Coverage for src / quber / core / figures / dpt3 / models.py: 100%

49 statements  

« prev     ^ index     » next       coverage.py v7.14.0, created at 2026-09-23 22:14 -0400

1"""What the dpt-3 track's digestion produces: one record per page, figure and table. 

2 

3A `PageDigest` is one scanned page read out of a dpt-3 response. It holds more 

4than the graft takes: the graft receives each figure's text, id and rectangle, 

5and everything else stays here for the consumers built after the track — the 

6axis and legend checks, estimate classification, and crop-level disagreement 

7checks. 

8 

9A `DigestedFigure` keeps the response's own separation of a figure into what 

10kind of figure it is, what the model said about it, and the values it read as a 

11small table. Each value cell carries a provenance marker separating a number 

12printed on the page from one estimated off the drawing's geometry; digestion 

13writes every cell `unclassified`, and classifying them is a later change. A 

14resolved footnote attaches to the specific cell its marker's label matches, so 

15an annotation qualifies the value it is printed on rather than the whole chart. 

16 

17A `DigestedTable` is an image table with the response's own cell grid: every 

18cell arrives with its row, column and rectangle, which is what lets a 

19disagreement check read a crop of one cell instead of the whole page. 

20""" 

21 

22from __future__ import annotations 

23 

24from typing import List, Literal, Optional 

25 

26from pydantic import BaseModel, Field 

27 

28from quber.core.figures.models import Box 

29 

30#: Whether a figure value is printed on the page or estimated from the drawing. 

31#: Digestion writes every value `unclassified`; classification is a later step. 

32Provenance = Literal["printed", "estimate", "unclassified"] 

33 

34 

35class PageDigest(BaseModel): 

36 """One scanned page, digested from a dpt-3 response.""" 

37 

38 page: int = Field(description="1-based source page the submission came from") 

39 job_id: Optional[str] = Field(default=None, description="The scan job that produced the response") 

40 model: Optional[str] = Field(default=None, description="The model requested for the scan") 

41 version: Optional[str] = Field(default=None, description="The model version the response reported") 

42 credits: Optional[float] = Field(default=None, description="Credits the scan was billed") 

43 figures: List[DigestedFigure] = Field(default_factory=list) 

44 tables: List[DigestedTable] = Field(default_factory=list) 

45 context: List[DigestedText] = Field( 

46 default_factory=list, 

47 description="The page's other nodes — text and page furniture — kept as context", 

48 ) 

49 

50 

51class DigestedFigure(BaseModel): 

52 """One figure node, with the parts the response separates kept separate.""" 

53 

54 page: int = Field(description="1-based source page the figure is printed on") 

55 kind: str = Field( 

56 default="", 

57 description="What the response typed the figure as: chart, diagram, illustration", 

58 ) 

59 description: str = Field(default="", description="The model's description blocks, in order") 

60 box: Optional[Box] = Field(default=None, description="Normalized box around the figure") 

61 values: List[FigureValueTable] = Field( 

62 default_factory=list, 

63 description="The value tables the model read off the figure, as returned", 

64 ) 

65 footnotes: List[ResolvedNote] = Field( 

66 default_factory=list, 

67 description="The figure's footnotes once resolution has run; empty before it", 

68 ) 

69 node_id: Optional[str] = Field(default=None, description="The response's own id for the node") 

70 

71 

72class DigestedTable(BaseModel): 

73 """One table node, as a dense grid with the response's box on every cell.""" 

74 

75 page: int = Field(description="1-based source page the table is printed on") 

76 cells: List[List[str]] = Field(description="The grid, rows of cell text") 

77 cell_boxes: List[List[Optional[Box]]] = Field( 

78 description="Normalized box per cell, shaped exactly like `cells`; absent where none was returned" 

79 ) 

80 box: Optional[Box] = Field(default=None, description="Normalized box around the whole table") 

81 node_id: Optional[str] = Field(default=None, description="The response's own id for the node") 

82 

83 

84class DigestedText(BaseModel): 

85 """A text or page-furniture node, kept as returned and attached to nothing.""" 

86 

87 page: int = Field(description="1-based source page the text is printed on") 

88 kind: str = Field(description="The node type as returned: text, marginalia, logo") 

89 text: str = Field(description="The node's text, exactly as returned") 

90 box: Optional[Box] = Field(default=None, description="Normalized box around the text") 

91 node_id: Optional[str] = Field(default=None, description="The response's own id for the node") 

92 

93 

94class FigureValueTable(BaseModel): 

95 """One value table read off a figure. The rows are kept exactly as returned; 

96 no shape is imposed, because two charts in one response lay their values out 

97 differently and a fixed reading of one layout misreads the next.""" 

98 

99 rows: List[List[FigureValueCell]] = Field(default_factory=list) 

100 

101 

102class FigureValueCell(BaseModel): 

103 """One cell of a figure's value table.""" 

104 

105 text: str = Field(description="The cell's text, exactly as returned") 

106 provenance: Provenance = Field( 

107 default="unclassified", 

108 description="Printed on the page, estimated from the drawing, or not yet classified", 

109 ) 

110 footnotes: List[PlacedNote] = Field( 

111 default_factory=list, 

112 description="Resolved notes whose marker sits on this cell's label", 

113 ) 

114 

115 

116class PlacedNote(BaseModel): 

117 """One resolved footnote attached to the specific value its marker annotates.""" 

118 

119 marker: str = Field(description="The marker as printed on the label") 

120 text: str = Field(description="The resolved note text") 

121 

122 

123class ResolvedNote(BaseModel): 

124 """One footnote resolution outcome for a figure. 

125 

126 `sources` names where the text came from: `agent` for the correction 

127 agent's read of the page image, `document` for the parse's own footnote 

128 text on the page, `headings` for a section reference resolved to a heading 

129 pointer. Two sources that agree corroborate each other; a disagreement is 

130 carried as a flag by the run, never silently settled. 

131 """ 

132 

133 marker: str = Field(default="", description="The marker as printed; empty for an unmarked note") 

134 text: str = Field(description="The note text a reader should see") 

135 sources: List[str] = Field(default_factory=list) 

136 corroborated: bool = Field( 

137 default=False, description="True when the agent read and the document text agree" 

138 ) 

139 placed: bool = Field(default=False, description="True when the note attached to a specific value cell")