Spaces:
Build error
Build error
Download api/routes/export.py from abinazebinoy/bharatgraph: direct link, hf CLI and curl.
- Browser
- Download file 3.71 kB
-
https://huggingface.co/spaces/abinazebinoy/bharatgraph/resolve/main/api/routes/export.py
- Command line
-
hf download hf://spaces/abinazebinoy/bharatgraph/api/routes/export.py
-
curl -L -o export.py https://huggingface.co/spaces/abinazebinoy/bharatgraph/resolve/main/api/routes/export.py
3.71 kB
| import os | |
| import sys | |
| sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) | |
| from datetime import datetime | |
| from fastapi import APIRouter, Depends, HTTPException | |
| from fastapi.responses import FileResponse | |
| from loguru import logger | |
| from api.dependencies import get_db, get_driver | |
| from ai.dossier_generator import DossierGenerator | |
| from ai.report_hasher import ReportHasher | |
| from ai.multi_investigator import MultiInvestigator | |
| router = APIRouter() | |
| _hasher = None | |
| def get_generator() -> DossierGenerator: | |
| """BUG-08 FIX: was caching generator with stale driver. | |
| Now creates a fresh generator per request -- driver is always fresh.""" | |
| return DossierGenerator(driver=get_driver()) | |
| def get_hasher() -> ReportHasher: | |
| global _hasher | |
| if _hasher is None: | |
| _hasher = ReportHasher() | |
| return _hasher | |
| def export_pdf(entity_id: str, driver=Depends(get_db)): | |
| logger.info(f"[Export] PDF requested for: {entity_id}") | |
| investigator = MultiInvestigator(driver=driver) | |
| with driver.session() as session: | |
| row = session.run( | |
| "MATCH (n {id: $id}) RETURN n.name AS name", | |
| id=entity_id | |
| ).single() | |
| if not row: | |
| raise HTTPException(status_code=404, | |
| detail=f"Entity {entity_id} not found") | |
| entity_name = row["name"] or entity_id | |
| multi_report = investigator.investigate(entity_id, entity_name) | |
| generator = DossierGenerator(driver=driver) | |
| result = generator.generate( | |
| entity_id, | |
| multi_report=multi_report, | |
| ) | |
| output_path = result.get("pdf_path") or result.get("html_path") | |
| if not output_path or not os.path.exists(output_path): | |
| raise HTTPException(status_code=500, | |
| detail="Dossier generation failed") | |
| # H-07 / CodeQL fix: use file-anchored absolute path. | |
| # P-1 fix: was its own separate hardcoded <project_root>/data/processed | |
| # computation, independent of (and narrower than) dossier_generator.py's | |
| # own guard -- see get_dossier_output_dir()'s docstring in | |
| # config/settings.py for why that latent inconsistency mattered. | |
| from config.settings import get_dossier_output_dir | |
| allowed_dir = os.path.realpath(get_dossier_output_dir()) | |
| real_out = os.path.realpath(output_path) | |
| if not real_out.startswith(allowed_dir + os.sep): | |
| raise HTTPException(status_code=400, detail="Invalid output path") | |
| media_type = ( | |
| "application/pdf" | |
| if real_out.endswith(".pdf") | |
| else "text/html" | |
| ) | |
| # BUG-17 FIX: wire blockchain audit trail to dossier export. | |
| # AuditChain was fully implemented but never called anywhere. | |
| # README claimed "SHA-256 blockchain audit trail" -- now true. | |
| try: | |
| from blockchain.audit_chain import store_daily_root | |
| from api.dependencies import get_driver as _get_driver | |
| _drv = _get_driver() | |
| if _drv: | |
| store_daily_root(_drv) | |
| except Exception as _bc_err: | |
| logger.debug(f"[Export] Blockchain audit step (optional): {_bc_err}") | |
| # CodeQL #16-17 FIX: use validated real_out path for all file operations | |
| return FileResponse( | |
| real_out, | |
| media_type=media_type, | |
| filename=os.path.basename(real_out), | |
| headers={"X-Report-Hash": result["report_hash"]}, | |
| ) | |
| def verify_report(report_hash: str, driver=Depends(get_db)): | |
| hasher = get_hasher() | |
| result = hasher.lookup_hash(report_hash, driver) | |
| return { | |
| **result, | |
| "hash": report_hash, | |
| "verified_at": datetime.now().isoformat(), | |
| } | |