Coverage for src / local_deep_research / storage / database_with_file_backup.py: 98%
40 statements
« prev ^ index » next coverage.py v7.12.0, created at 2026-01-11 00:51 +0000
« prev ^ index » next coverage.py v7.12.0, created at 2026-01-11 00:51 +0000
1"""Storage that always uses database as primary storage with optional file backup."""
3from typing import Dict, Any, Optional
4from loguru import logger
5from sqlalchemy.orm import Session
7from .base import ReportStorage
8from .database import DatabaseReportStorage
9from .file import FileReportStorage
12class DatabaseWithFileBackupStorage(ReportStorage):
13 """
14 Storage that always saves to database and optionally backs up to file system.
16 Database is the primary storage and is always used.
17 File storage is optional for external access/backup purposes.
18 """
20 def __init__(self, session: Session, enable_file_storage: bool = False):
21 """
22 Initialize combined storage.
24 Args:
25 session: SQLAlchemy database session
26 enable_file_storage: Whether to also save reports to file system
27 """
28 self.db_storage = DatabaseReportStorage(session)
29 self.file_storage = FileReportStorage() if enable_file_storage else None
30 self.enable_file_storage = enable_file_storage
32 def save_report(
33 self,
34 research_id: str,
35 content: str,
36 metadata: Optional[Dict[str, Any]] = None,
37 username: Optional[str] = None,
38 ) -> bool:
39 """Save report to database and optionally to file."""
40 # Always save to database first (primary storage)
41 success = self.db_storage.save_report(
42 research_id, content, metadata, username
43 )
45 if not success:
46 logger.error(f"Failed to save report {research_id} to database")
47 return False
49 # Optionally save to file system
50 if self.file_storage:
51 try:
52 file_success = self.file_storage.save_report(
53 research_id, content, metadata, username
54 )
55 if not file_success:
56 logger.warning(
57 f"Failed to save report {research_id} to file, "
58 "but database save was successful"
59 )
60 except Exception as e:
61 logger.warning(
62 f"Error saving report {research_id} to file: {e}, "
63 "but database save was successful"
64 )
66 return success
68 def get_report(
69 self, research_id: str, username: Optional[str] = None
70 ) -> Optional[str]:
71 """Get report from database (never from file)."""
72 # Always read from database for consistency
73 return self.db_storage.get_report(research_id, username)
75 def delete_report(
76 self, research_id: str, username: Optional[str] = None
77 ) -> bool:
78 """Delete report from database and file if it exists."""
79 # Delete from database
80 db_success = self.db_storage.delete_report(research_id, username)
82 # Also delete from file if enabled
83 if self.file_storage and db_success:
84 try:
85 self.file_storage.delete_report(research_id, username)
86 except Exception as e:
87 logger.warning(f"Error deleting report file {research_id}: {e}")
89 return db_success
91 def list_reports(self, username: Optional[str] = None) -> list:
92 """List reports from database only."""
93 # Always list from database for consistency
94 return self.db_storage.list_reports(username)
96 def get_report_with_metadata(
97 self, research_id: str, username: Optional[str] = None
98 ) -> Optional[Dict[str, Any]]:
99 """Get report with metadata from database."""
100 # Always read from database for consistency
101 return self.db_storage.get_report_with_metadata(research_id, username)
103 def report_exists(
104 self, research_id: str, username: Optional[str] = None
105 ) -> bool:
106 """Check if report exists in database."""
107 # Always check database for consistency
108 return self.db_storage.report_exists(research_id, username)