Spaces:
Sleeping
Sleeping
File size: 2,525 Bytes
2e818da | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | """Per-project upload storage.
Each project gets its OWN folder -> no shared, rescanned "current uploads"
directory. A project's document set is exactly what's in its own folder,
nothing else, so a new project can never see another project's (or a prior
project's leftover) files. Duplication across projects is an accepted
tradeoff for that isolation.
"""
import os
import shutil
from typing import List
PROJECT_UPLOADS_ROOT = os.path.expanduser("~/.studybuddy/projects/uploads")
_SUPPORTED_EXTS = {".pdf", ".docx", ".txt"}
def project_upload_dir(project_id: str) -> str:
d = os.path.join(PROJECT_UPLOADS_ROOT, project_id)
os.makedirs(d, exist_ok=True)
return d
def delete_project_uploads(project_id: str) -> None:
folder = os.path.join(PROJECT_UPLOADS_ROOT, project_id)
shutil.rmtree(folder, ignore_errors=True)
def list_project_files(project_id: str) -> List[str]:
folder = project_upload_dir(project_id)
return sorted(
os.path.join(folder, f)
for f in os.listdir(folder)
if os.path.isfile(os.path.join(folder, f))
and os.path.splitext(f)[1].lower() in _SUPPORTED_EXTS
)
def add_file_to_project(project_id: str, file_path: str) -> str:
folder = project_upload_dir(project_id)
filename = os.path.basename(file_path)
dest = os.path.join(folder, filename)
shutil.copy2(file_path, dest)
return dest
async def remove_file_from_project(project_id: str, filename: str) -> bool:
from app.rag.chromadb_client import ChromaDBClient
from app.rag.evidence_ingestion import EvidenceIngestionService
folder = project_upload_dir(project_id)
file_path = os.path.join(folder, filename)
if not os.path.exists(file_path):
return False
try:
with open(file_path, "rb") as f:
content = f.read()
document_id = ChromaDBClient.file_hash(content)
except Exception:
document_id = None
# Remove derived state first. If this fails, keep the irreplaceable source
# PDF so the operation can be retried safely.
if document_id:
EvidenceIngestionService().remove_document(project_id, document_id)
from app.services.paper_metadata import PaperMetadataService
PaperMetadataService().forget_file_metadata(document_id)
os.remove(file_path)
# Cognee is cross-project student memory, not a mirror of project papers;
# paper deletion therefore does not mutate the durable learner profile.
return True
|