Coverage for src / local_deep_research / database / models / reports.py: 96%

53 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2026-01-11 00:51 +0000

1""" 

2Report generation and storage models. 

3""" 

4 

5from sqlalchemy import ( 

6 JSON, 

7 Boolean, 

8 Column, 

9 Float, 

10 ForeignKey, 

11 Integer, 

12 String, 

13 Text, 

14) 

15from sqlalchemy.orm import relationship 

16from sqlalchemy_utc import UtcDateTime, utcnow 

17 

18from .base import Base 

19 

20 

21class Report(Base): 

22 """ 

23 Generated research reports. 

24 Can be in various formats and languages. 

25 """ 

26 

27 __tablename__ = "reports" 

28 

29 id = Column(Integer, primary_key=True) 

30 research_task_id = Column( 

31 Integer, ForeignKey("research_tasks.id", ondelete="CASCADE") 

32 ) 

33 

34 # Report metadata 

35 title = Column(String(500)) 

36 subtitle = Column(String(500)) 

37 abstract = Column(Text) # Summary/abstract 

38 content = Column(Text) # Full report content (usually markdown) 

39 

40 # Format and presentation 

41 format = Column(String(50), default="markdown") # markdown, html, pdf, docx 

42 template = Column(String(100)) # Template used for generation 

43 style = Column(String(100)) # Style/theme applied 

44 language = Column(String(10), default="en") 

45 

46 # Statistics 

47 word_count = Column(Integer) 

48 section_count = Column(Integer) 

49 reference_count = Column(Integer) 

50 image_count = Column(Integer) 

51 

52 # Generation metadata 

53 generation_params = Column(JSON) # Parameters used for generation 

54 generation_model = Column(String(100)) # AI model used 

55 generation_time_seconds = Column(Float) 

56 

57 # Versioning 

58 version = Column(Integer, default=1) 

59 is_draft = Column(Boolean, default=False) 

60 

61 # Timestamps 

62 created_at = Column(UtcDateTime, default=utcnow()) 

63 updated_at = Column(UtcDateTime, default=utcnow(), onupdate=utcnow()) 

64 published_at = Column(UtcDateTime) 

65 

66 # Relationships 

67 research_task = relationship("ResearchTask", back_populates="reports") 

68 sections = relationship( 

69 "ReportSection", 

70 back_populates="report", 

71 cascade="all, delete-orphan", 

72 order_by="ReportSection.section_order", 

73 ) 

74 

75 def __repr__(self): 

76 return f"<Report(title='{self.title}', format='{self.format}', draft={self.is_draft})>" 

77 

78 

79class ReportSection(Base): 

80 """ 

81 Individual sections within a report. 

82 Allows for structured document generation. 

83 """ 

84 

85 __tablename__ = "report_sections" 

86 

87 id = Column(Integer, primary_key=True) 

88 report_id = Column(Integer, ForeignKey("reports.id", ondelete="CASCADE")) 

89 

90 # Section metadata 

91 title = Column(String(500)) 

92 subtitle = Column(String(500)) 

93 content = Column(Text) 

94 

95 # Structure 

96 section_order = Column(Integer, nullable=False) # Order within report 

97 section_type = Column( 

98 String(50) 

99 ) # introduction, methodology, findings, conclusion, references, appendix 

100 section_level = Column(Integer, default=1) # 1=H1, 2=H2, etc. 

101 parent_section_id = Column( 

102 Integer, ForeignKey("report_sections.id") 

103 ) # For nested sections 

104 

105 # References and citations 

106 references = Column(JSON) # List of SearchResult IDs used 

107 citations = Column(JSON) # Formatted citations 

108 

109 # Generation metadata 

110 auto_generated = Column(Boolean, default=True) 

111 edited = Column(Boolean, default=False) 

112 

113 # Timestamps 

114 created_at = Column(UtcDateTime, default=utcnow()) 

115 updated_at = Column(UtcDateTime, default=utcnow(), onupdate=utcnow()) 

116 

117 # Relationships 

118 report = relationship("Report", back_populates="sections") 

119 subsections = relationship( 

120 "ReportSection", backref="parent_section", remote_side=[id] 

121 ) 

122 

123 def __repr__(self): 

124 return f"<ReportSection(title='{self.title}', order={self.section_order}, type='{self.section_type}')>"