File size: 8,556 Bytes
63bfc55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
"""
SAAP Document Upload API
Handles file uploads, parsing, and privacy detection for multi-agent chat
"""
import logging
from typing import Optional
from fastapi import APIRouter, UploadFile, File, HTTPException, Form
from fastapi.responses import JSONResponse
from pydantic import BaseModel

from services.document_parser import document_parser
from services.privacy_detector import privacy_detector

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/api/v1/documents", tags=["documents"])


class DocumentUploadResponse(BaseModel):
    """Response model for document upload"""
    success: bool
    document_id: str
    filename: str
    file_type: str
    file_size: int
    char_count: int
    has_sensitive_data: bool
    sensitive_data_types: list[str]
    content_preview: str
    full_content: str  # πŸ“„ Full document content for chat context
    error: Optional[str] = None


@router.post("/upload", response_model=DocumentUploadResponse)
async def upload_document(
    file: UploadFile = File(...),
    user_message: Optional[str] = Form(None)
):
    """
    Upload and parse a document (PDF, DOCX, TXT)
    
    - Extracts text content from document
    - Analyzes for sensitive/private data
    - Returns parsed content and privacy analysis
    
    Args:
        file: Uploaded file (PDF, DOCX, TXT)
        user_message: Optional user message with context about the document
    
    Returns:
        DocumentUploadResponse with parsed content and privacy analysis
    """
    try:
        logger.info(f"πŸ“€ Document upload started: {file.filename}")
        
        # Read file content
        file_content = await file.read()
        
        # Parse document
        parse_result = document_parser.parse_document(
            file_data=file_content,
            filename=file.filename,
            mime_type=file.content_type
        )
        
        if not parse_result["success"]:
            logger.error(f"❌ Document parsing failed: {parse_result['error']}")
            raise HTTPException(
                status_code=400,
                detail=f"Failed to parse document: {parse_result['error']}"
            )
        
        extracted_text = parse_result["content"]
        metadata = parse_result["metadata"]
        
        # Analyze for sensitive data
        from services.privacy_detector import analyze_document_privacy
        privacy_level, privacy_details = analyze_document_privacy(
            document_text=extracted_text,
            filename=file.filename
        )
        
        # Convert privacy level to boolean + types for response
        has_sensitive_data = privacy_level.value in ["private", "confidential"]
        
        # Extract detected types from details
        sensitive_types = []
        if privacy_details.get("keyword_matches"):
            sensitive_types.extend([cat for cat, _ in privacy_details["keyword_matches"]])
        if privacy_details.get("pattern_matches"):
            sensitive_types.extend([cat for _, cat in privacy_details["pattern_matches"]])
        if privacy_details.get("document_indicators"):
            sensitive_types.extend(privacy_details["document_indicators"])
        
        # Remove duplicates
        sensitive_types = list(set(sensitive_types))
        
        # Create content preview (first 500 characters)
        content_preview = extracted_text[:500]
        if len(extracted_text) > 500:
            content_preview += "..."
        
        # Generate document ID (simple hash for now)
        import hashlib
        doc_id = hashlib.sha256(file_content).hexdigest()[:16]
        
        logger.info(
            f"βœ… Document processed successfully: {file.filename} "
            f"({metadata['char_count']} chars, "
            f"sensitive: {has_sensitive_data})"
        )
        
        return DocumentUploadResponse(
            success=True,
            document_id=doc_id,
            filename=metadata["filename"],
            file_type=metadata["file_type"],
            file_size=metadata["file_size"],
            char_count=metadata["char_count"],
            has_sensitive_data=has_sensitive_data,
            sensitive_data_types=sensitive_types,
            content_preview=content_preview,
            full_content=extracted_text,  # πŸ“„ Full document content for chat context
            error=None
        )
    
    except HTTPException:
        raise
    
    except Exception as e:
        logger.error(f"❌ Document upload error: {e}", exc_info=True)
        raise HTTPException(
            status_code=500,
            detail=f"Internal server error: {str(e)}"
        )


@router.post("/analyze")
async def analyze_document_content(
    file: UploadFile = File(...),
):
    """
    Analyze document for sensitive data without full parsing
    
    Quick privacy check before full processing
    
    Args:
        file: Uploaded file
    
    Returns:
        Privacy analysis results
    """
    try:
        logger.info(f"πŸ” Document privacy analysis: {file.filename}")
        
        # Read file content
        file_content = await file.read()
        
        # Parse document
        parse_result = document_parser.parse_document(
            file_data=file_content,
            filename=file.filename,
            mime_type=file.content_type
        )
        
        if not parse_result["success"]:
            raise HTTPException(
                status_code=400,
                detail=f"Failed to parse document: {parse_result['error']}"
            )
        
        # Analyze for sensitive data
        from services.privacy_detector import analyze_document_privacy
        privacy_level, privacy_details = analyze_document_privacy(
            document_text=parse_result["content"],
            filename=file.filename
        )
        
        # Convert privacy level to boolean
        has_sensitive_data = privacy_level.value in ["private", "confidential"]
        
        # Extract detected types
        sensitive_types = []
        if privacy_details.get("keyword_matches"):
            sensitive_types.extend([cat for cat, _ in privacy_details["keyword_matches"]])
        if privacy_details.get("pattern_matches"):
            sensitive_types.extend([cat for _, cat in privacy_details["pattern_matches"]])
        if privacy_details.get("document_indicators"):
            sensitive_types.extend(privacy_details["document_indicators"])
        sensitive_types = list(set(sensitive_types))
        
        logger.info(
            f"βœ… Privacy analysis complete: {file.filename} "
            f"(sensitive: {has_sensitive_data})"
        )
        
        return JSONResponse(content={
            "success": True,
            "filename": file.filename,
            "privacy_level": privacy_level.value,
            "has_sensitive_data": has_sensitive_data,
            "sensitive_data_types": sensitive_types,
            "reason": privacy_details.get("reason", "unknown"),
            "details": privacy_details
        })
    
    except HTTPException:
        raise
    
    except Exception as e:
        logger.error(f"❌ Document analysis error: {e}", exc_info=True)
        raise HTTPException(
            status_code=500,
            detail=f"Internal server error: {str(e)}"
        )


@router.get("/supported-formats")
async def get_supported_formats():
    """
    Get list of supported document formats
    
    Returns:
        List of supported file formats and their MIME types
    """
    return JSONResponse(content={
        "success": True,
        "supported_formats": [
            {
                "extension": "pdf",
                "mime_type": "application/pdf",
                "description": "Adobe PDF Document",
                "max_size_mb": 10
            },
            {
                "extension": "docx",
                "mime_type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
                "description": "Microsoft Word Document (2007+)",
                "max_size_mb": 10
            },
            {
                "extension": "doc",
                "mime_type": "application/msword",
                "description": "Microsoft Word Document (Legacy)",
                "max_size_mb": 10
            },
            {
                "extension": "txt",
                "mime_type": "text/plain",
                "description": "Plain Text Document",
                "max_size_mb": 10
            }
        ],
        "max_file_size_bytes": document_parser.MAX_FILE_SIZE
    })