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