ó
    À‘´jû6  ã                  ó  • S r SSKJr  SSKJrJrJrJrJr  SSK	J
r
Jr  \\\4   r " S S\
5      r " S S\
5      r " S	 S
\
5      r\S   r " S S\
5      r " S S\
5      r " S S\
5      r " S S\
5      r " S S\
5      rg)ah  What the figure workflow produces: one record per scanned page, and one per figure.

A `FigureRun` is the run record, written as `<base>.figures.json`. It holds one
`PageScan` for every page the run scanned, including a page that came back
with nothing. A page whose scan or reading failed has no `PageScan` and is
reported in `errors` instead. Either way, a nominated page never disappears
between nomination and output. A chart in a financial document is often where
a number appears that appears nowhere else, so a page that was nominated and
produced nothing has to be visible.

A `FigureRecord` is one figure the scan returned, with the box the scan drew
around it. On the dpt-2 path its text is the scan's reading exactly as
returned. On the dpt-3 path it is the figure's description followed by its
value tables as markdown rows (`dpt3.digest.figure_text`). The text is not
parsed into fields: a chart description has no fixed shape, and two bar charts
in one response used different field names in a different order, so a parser
written against what has been seen encodes one shape and misreads the next.

A `ScannedTable` is one table the scan returned. Unlike a chart, a table is
kept as structure: a grid, with a box for each cell where the scan gave one.
Only the grids the capture step takes go on to the correction and grounding
every other table goes through (`capture.capture_tables`).

`ChartContext` is the rest of the page's returned text: on the dpt-2 path every
text and marginalia chunk, on the dpt-3 path every node with text that is
neither a figure nor a table. That is where the chart titles and the notes at
the foot of the page are, alongside the page's body text. It is kept as
returned and attached to no chart. One note commonly serves several charts on
a page, and matching a superscript to the note it points at is a separate
step, the same way it is for tables.
é    )Úannotations)ÚDictÚListÚLiteralÚOptionalÚTuple)Ú	BaseModelÚFieldc                  ó¦   • \ rS rSr% Sr\" SS9rS\S'   \" SS9rS\S	'   \" S
SS9r	S\S'   \" S
SS9r
S\S'   \" S
SS9rS\S'   \" S
SS9rS\S'   Srg
)ÚFigureRecordé/   zOne chart a page scan returned.z+1-based source page the chart is printed on©ÚdescriptionÚintÚpagez;The scan's reading of the chart, stored exactly as returnedÚstrÚtextNz+Normalized box around the chart on the page©Údefaultr   úOptional[Box]ÚboxzCThe scan's own id for this figure, for tracing back to the responseúOptional[str]Úchunk_idú@The scan job that produced this record (see the page's PageScan)Újob_idz�The picture in the refined parse this record was grafted onto. Absent when the figure overlapped no picture, which the run reports as an error.Úpicture_ref© )Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__r
   r   Ú__annotations__r   r   r   r   r   Ú__static_attributes__r   ó    Ú7/home/mande/repo/quber/src/quber/core/figures/models.pyr   r   /   sƒ   ‡ Ù)áÐ"OÑP€Dˆ#ÓPÙÐ"_Ñ`€Dˆ#Ó`Ù tÐ9fÑg€CˆÓgÙ#ØÐ"gñ€Hˆmó ñ "ØÐ"dñ€FˆMó ñ "'ØðOñ"€K�ö r%   r   c                  óì   • \ rS rSr% Sr\" SS9rS\S'   \" SS9rS\S	'   \" S
S9r	S\S'   \" SSS9r
S\S'   \" SSS9rS\S'   \" SSS9rS\S'   \" SSS9rS\S'   \" SSS9rS\S'   \" SSS9rS\S'   Srg)ÚScannedTableéD   u4  One table a page scan returned, as a grid with a box on every cell.

A table comes back shaped like a table: an HTML grid with an id on every
cell, and a box for each of those ids. That is a different shape from a
chart, which comes back as a description with no internal locations at all,
and it is the right shape for the element it belongs to.

The grid is dense â€” one entry per row and column, blank where a spanning
cell covers a position â€” so it drops straight into the same correction and
grounding steps every other table in the document goes through.
z+1-based source page the table is printed onr   r   r   z$The returned grid, rows of cell textzList[List[str]]ÚcellszTNormalized box per cell, shaped exactly like `cells`; absent where none was returnedzList[List[Optional[Box]]]Ú
cell_boxesNz%Normalized box around the whole tabler   r   r   z The scan's own id for this tabler   r   r   r   z–The table in the refined parse this grid replaced. Absent when the returned table overlapped no table in the parse, which the run reports as an error.Ú	table_refz¸The picture in the parse this grid was printed over, when the parse detected the region but filed it as a picture rather than a table. The grid becomes a new table beside that picture.r   z6The extracted table this grid produced, by its addressÚtable_idr   )r   r   r    r!   r"   r
   r   r#   r*   r+   r   r   r   r,   r   r-   r$   r   r%   r&   r(   r(   D   sË   ‡ ñ
ñ Ð"OÑP€Dˆ#ÓPÙ"Ð/UÑV€Eˆ?ÓVÙ,1Øjñ-€JÐ)ó ñ  tÐ9`Ña€CˆÓaÙ#¨DÐ>`Ña€HˆmÓaÙ!ØÐ"dñ€FˆMó ñ  %ØðYñ €Iˆ}ó ñ "'Øð-ñ"€K�ó ñ $ØÐ"Zñ€Hˆmö r%   r(   c                  óŒ   • \ rS rSr% Sr\" SS9rS\S'   \" SS9rS\S	'   \" S
S9r	S\S'   \" SSS9r
S\S'   \" SSS9rS\S'   Srg)ÚChartContextéo   zKA title or note the scanned page returned, kept as returned and unattached.z*1-based source page the text is printed onr   r   r   z@The chunk type the scan assigned, as returned (text, marginalia)r   Úkindz$The text, stored exactly as returnedr   Nz*Normalized box around the text on the pager   r   r   z The scan's own id for this chunkr   r   r   )r   r   r    r!   r"   r
   r   r#   r1   r   r   r   r$   r   r%   r&   r/   r/   o   sY   ‡ ÙUáÐ"NÑO€Dˆ#ÓOÙÐ"dÑe€Dˆ#ÓeÙÐ"HÑI€Dˆ#ÓIÙ tÐ9eÑf€CˆÓfÙ#¨DÐ>`Ña€HˆmÖar%   r/   )ÚfiguresÚtablesÚemptyÚdroppedc                  óT  • \ rS rSr% Sr\" SS9rS\S'   S\S'   \" S	S
S9rS\S'   \" \	SS9r
S\S'   \" \	SS9rS\S'   \" SSS9rS\S'   \" SSS9rS\S'   \" SSS9rS\S'   \" SSS9rS\S'   \" SS S9rS!\S"'   \" SS#S9rS\S$'   \" \	S%9rS&\S''   \" \	S%9rS(\S)'   \" \	S%9rS*\S+'   S,rg)-ÚPageScané|   añ  One nominated page and what became of it.

`status` says which: `figures` for a scanned page that returned at least one
figure, `tables` for one that returned no figure but at least one table,
`empty` for a scanned page that returned neither, `dropped` for a page the
filter answered no on, which is never scanned and so is never billed.

`figures` does not promise a chart. Nothing here filters on what a figure
contains, and a page's logo comes back as a figure of its own, so a page can
reach `figures` on the strength of its letterhead alone.

`reused` marks a page whose records come from a scan a previous run already
paid for. `credits` is what that scan cost when it was made, so the two
together separate what this run spent from what it read.
ú1-based source pager   r   r   Ú
PageStatusÚstatusÚ z0Why a page was dropped; empty for a scanned pager   r   Úreasonz,The picture classes that nominated this page©Údefault_factoryr   ú	List[str]Úpicture_classeszETables in the parse that nominated this page, read off the page imageÚ
table_refsNz)The scan job id, absent on a dropped pager   r   z The model requested for the scanÚmodelz#The model version the scan reportedÚversionz,Credits the scan was billed when it was madezOptional[float]ÚcreditsFz>True when the records come from a scan an earlier run paid forÚboolÚreusedz:Filename of the raw response this page's records came fromÚresponse_artifact©r?   úList[FigureRecord]r2   úList[ScannedTable]r3   zList[ChartContext]Úcontextr   )r   r   r    r!   r"   r
   r   r#   r=   ÚlistrA   rB   r   rC   rD   rE   rG   rH   r2   r3   rL   r$   r   r%   r&   r7   r7   |   s  ‡ ññ  Ð"7Ñ8€Dˆ#Ó8ØÓÙ Ð0bÑc€FˆCÓcÙ!&ØÐ*Xñ"€O�Yó ñ "ØØ[ñ€J�	ó ñ "¨$Ð<gÑh€FˆMÓhÙ ¨Ð;]Ñ^€Eˆ=Ó^Ù"¨4Ð=bÑc€Gˆ]ÓcÙ$¨TÐ?mÑn€Gˆ_ÓnÙØÐ#cñ€FˆDó ñ (-ØÐ"^ñ(Ð�}ó ñ #(¸Ñ"=€GÐÓ=Ù!&°tÑ!<€FÐÓ<Ù"'¸Ñ"=€GÐÖ=r%   r7   c                  óv   • \ rS rSr% Sr\" SS9rS\S'   \" SS9rS\S	'   \" S
SS9r	S\S'   \" SSS9r
S\S'   Srg
)ÚRemovedTexté¦   uè  One text the run took out of the parse because a figure had been read over it.

Recorded so a completeness check can tell a deliberate removal from a silent
loss. Both look the same from outside: text the page prints that the document
no longer holds. The check reads the source PDF's own text layer, which is the
layer this text came out of, so the same printed line is present in both
readings and matches on its words and on where it sits.

The box is the normalized top-left frame the rest of the workflow states
positions in, so no caller converts. Text alone would be too loose â€” a page
printing `0.0%` as a gridline and again in a footnote gives one string for two
places, and forgiving the gridline would forgive losing the footnote.
z"The 1-based page it was printed onr   r   r   zWhat it said, verbatimr   r   Nz=Where it sat: (x1, y1, x2, y2), 0..1 from the page's top-leftr   z+Optional[Tuple[float, float, float, float]]r   r<   z-Why it was taken to be the figure's furniturer=   r   )r   r   r    r!   r"   r
   r   r#   r   r   r=   r$   r   r%   r&   rO   rO   ¦   sU   ‡ ññ Ð"FÑG€Dˆ#ÓGÙÐ":Ñ;€Dˆ#Ó;Ù7<ØÐ"añ8€CÐ	4ó ñ  Ð0_Ñ`€FˆCÖ`r%   rO   c                  ó  • \ rS rSr% Sr\" SS9rS\S'   \" SSS	9rS
\S'   \" SSS	9r	S\S'   \" SS9r
S\S'   \" SSS	9rS\S'   \" SS9rS\S'   \" SS9rS\S'   \" SSS	9rS
\S'   \" \SS9rS\S'   \" SSS	9rS\S '   S!rg)"ÚFigureValueé½   a?  One plotted value read off a figure, reconciled and traced to the page.

Two independent readers produce the inputs: the page scan's prose reading
and a local read of the page image grounded in the parse's positioned text
cells. A value both agree on, whose cited fragment prints it, is
`reconciled` and carries that fragment's box. A positional disagreement is
`value_misread`; a value only one reader produced is `value_unreconciled`
with the direction in the note. Statuses come from the cell-status
registry, so review surfaces treat figure values and table cells alike.
r9   r   r   r   Nz5The parse picture the value's figure was grafted ontor   r   r   r<   z#The figure's printed title, as readr   Úchart_titlez+Category or axis label the value belongs toÚlabelz-Series name when the figure has more than oneÚseriesz7The value as printed, including currency and sign marksÚvaluez$A code from the cell-status registryr;   z!One line of evidence or directionÚnotez-Ids of the page fragments grounding the valuer>   r@   Úfragment_idsz1Normalized box of the fragment printing the valuer   r   r   )r   r   r    r!   r"   r
   r   r#   r   rT   rU   rV   rW   r;   rX   rM   rY   r   r$   r   r%   r&   rR   rR   ½   sÃ   ‡ ñ	ñ Ð"7Ñ8€Dˆ#Ó8Ù!&ØÐ"Yñ"€K�ó ñ  RÐ5ZÑ[€K�Ó[ÙÐ#PÑQ€Eˆ3ÓQÙ Ð0_Ñ`€FˆCÓ`ÙÐ#\Ñ]€Eˆ3Ó]ÙÐ$JÑK€FˆCÓKÙ¨Ð:]Ñ^€Dˆ-Ó^Ù#ØÐ*Yñ€L�)ó ñ  tÐ9lÑm€CˆÖmr%   rR   c                  ó„   • \ rS rSr% Sr\" SS9rS\S'   \" \S9r	S\S	'   \" \S9r
S
\S'   \SS j5       r\SS j5       rSrg)ÚFigureValueRunéÙ   zLEvery reconciled figure value one run produced, written as its own artifact.úThe source document's base namer   r   ÚdocumentrI   zList[FigureValue]Úvaluesr@   Úerrorsc                ó:   • [        S U R                   5       5      $ )Nc              3  óH   #   • U  H  oR                   S :X  d  M  Sv •  M     g7f©Ú
reconciledé   N©r;   ©Ú.0Úvs     r&   Ú	<genexpr>Ú,FigureValueRun.reconciled.<locals>.<genexpr>â   ó   é € ÐFšk˜¯X©X¸Ñ-E—1‘1škùó   ‚"™	"©Úsumr_   ©Úselfs    r&   rd   ÚFigureValueRun.reconciledà   ó   € äÑF˜dŸkškÓFÓFÐFr%   c                ó:   • [        S U R                   5       5      $ )Nc              3  óH   #   • U  H  oR                   S :w  d  M  Sv •  M     g7frc   rf   rg   s     r&   rj   Ú)FigureValueRun.flagged.<locals>.<genexpr>æ   rl   rm   rn   rp   s    r&   ÚflaggedÚFigureValueRun.flaggedä   rs   r%   r   N©Úreturnr   )r   r   r    r!   r"   r
   r^   r#   rM   r_   r`   Úpropertyrd   rw   r$   r   r%   r&   r[   r[   Ù   s^   ‡ ÙVáÐ&GÑH€HˆcÓHÙ %°dÑ ;€FÐÓ;Ù¨dÑ3€FˆIÓ3àóGó ðGð óGó óGr%   r[   c                  ó  • \ rS rSr% Sr\" SS9rS\S'   \" \S9r	S\S	'   \" \S
S9r
S\S'   \" \SS9rS\S'   \SS j5       r\SS j5       r\SS j5       r\SS j5       r\SS j5       r\SS j5       r\SS j5       r\SS j5       rSrg)Ú	FigureRunéé   zMEverything one figure run produced, for the CLI to print and callers to read.r]   r   r   r^   rI   zList[PageScan]ÚscanszYSurfaced problems: a chart that matched no picture in the parse, a page whose scan failedr>   r@   r`   zdText taken out of the parse as a read figure's furniture, so a completeness check can account for itzList[RemovedText]Úremovedc                ó,   • [        U R                  5      $ )zÞPages with a scan record, the same count as `scanned`.

It is not the count of pages the parse nominated. It includes pages
added by `pages="all"` and stored dpt-3 pages, and leaves out a
nominated page whose scan failed.
)Úlenr   rp   s    r&   Ú	nominatedÚFigureRun.nominated÷   s   € ô �4—:‘:‹Ðr%   c                ó:   • [        S U R                   5       5      $ )zKPages with a scan record, whether this run paid for the scan or reused one.c              3  óH   #   • U  H  oR                   S :w  d  M  Sv •  M     g7f©r5   re   Nrf   ©rh   Úss     r&   rj   Ú$FigureRun.scanned.<locals>.<genexpr>  ó   é € ÐBšj˜¯H©H¸	Ñ,A—1‘1šjùrm   ©ro   r   rp   s    r&   ÚscannedÚFigureRun.scanned  ó   € ô ÑB˜dŸjšjÓBÓBÐBr%   c                ó:   • [        S U R                   5       5      $ )zšPages with a scan record that this run submitted.

A page that was billed but whose dpt-3 response could not be read has
no record, so it is not counted.
c              3  ón   #   • U  H+  oR                   S :w  d  M  UR                  (       a  M'  Sv •  M-     g7fr‡   )r;   rG   rˆ   s     r&   rj   Ú&FigureRun.submitted.<locals>.<genexpr>  s#   é € ÐSšj˜¯H©H¸	Ñ,A“1È!Ï(Í(—1‘1šjùs   ‚5™5¬	5rŒ   rp   s    r&   Ú	submittedÚFigureRun.submitted  s   € ô ÑS˜dŸjšjÓSÓSÐSr%   c                ó:   • [        S U R                   5       5      $ )zYPages whose status is `figures`, after the graft dropped the records over page furniture.c              3  óH   #   • U  H  oR                   S :X  d  M  Sv •  M     g7f)r2   re   Nrf   rˆ   s     r&   rj   Ú)FigureRun.with_figures.<locals>.<genexpr>  r‹   rm   rŒ   rp   s    r&   Úwith_figuresÚFigureRun.with_figures  r�   r%   c                ó:   • [        S U R                   5       5      $ )z/Scanned pages that returned at least one table.c              3  óJ   #   • U  H  oR                   (       d  M  S v •  M     g7f)re   N)r3   rˆ   s     r&   rj   Ú(FigureRun.with_tables.<locals>.<genexpr>  s   é € Ð5šj˜¯H­H—1‘1šjùs   ‚#š	#rŒ   rp   s    r&   Úwith_tablesÚFigureRun.with_tables  s   € ô Ñ5˜dŸjšjÓ5Ó5Ð5r%   c                óp   • U R                    VVs/ s H  oR                    H  o"PM     M     snn$ s  snnf )z4Every figure record the run produced, in page order.)r   r2   )rq   r‰   Úcs      r&   r2   ÚFigureRun.figures  s*   € ð  Ÿ:š:Ô9š:�a¯y­y¨!’©y‘™:Ò9Ð9ùÓ9ó   �2c                óp   • U R                    VVs/ s H  oR                    H  o"PM     M     snn$ s  snnf )z9Every table the run read off a page image, in page order.)r   r3   )rq   r‰   Úts      r&   r3   ÚFigureRun.tables  s*   € ð  Ÿ:š:Ô8š:�a¯x­x¨!’©x‘™:Ò8Ð8ùÓ8r¢   c                ó:   • [        S U R                   5       5      $ )zjCredits billed for the pages counted by `submitted`.

A reused scan was paid for by the run that made it.
c              3  óp   #   • U  H,  oR                   (       a  M  UR                  =(       d    S v •  M.     g7f)g        N)rG   rE   rˆ   s     r&   rj   Ú$FigureRun.credits.<locals>.<genexpr>)  s#   é € ÐHªZ¨¿x½xÓ#�1—9‘9×# Ô#ªZùs   ‚6š6rŒ   rp   s    r&   rE   ÚFigureRun.credits#  s   € ô ÑH¨T¯ZªZÓHÓHÐHr%   r   Nry   )rz   rJ   )rz   rK   )rz   Úfloat)r   r   r    r!   r"   r
   r^   r#   rM   r   r`   r€   r{   rƒ   r�   r“   r˜   r�   r2   r3   rE   r$   r   r%   r&   r}   r}   é   sý   ‡ ÙWáÐ&GÑH€HˆcÓHÙ!°$Ñ7€Eˆ>Ó7ÙØØoñ€FˆIó ñ "'ØØzñ"€GÐó ð
 óó ðð óCó ðCð óTó ðTð óCó ðCð ó6ó ð6ð ó:ó ð:ð ó9ó ð9ð óIó óIr%   r}   N)r"   Ú
__future__r   Útypingr   r   r   r   r   Úpydanticr	   r
   r   rª   ÚBoxr   r(   r/   r:   r7   rO   rR   r[   r}   r   r%   r&   Ú<module>r¯      s¤   ðñõ@ #ç 7Õ 7ç %ð ˆ3�ˆ:Ñ€ô�9ô ô*(�9ô (ôVb�9ô bð Ð<Ñ=€
ô'>ˆyô '>ôTa�)ô aô.n�)ô nô8G�Yô Gô @I�	õ @Ir%   