ó
    5ûojî/  ã                  ó–  • S r SSKJr  SSKrSSKJr  SSKJrJrJ	r	J
r
JrJr  SSKJr  SSKJrJr  SSKJrJrJrJrJr  S	r\" 1 S
k5      r " S S\5      r " S S\5      r " S S\5      r " S S\
5      r " S S\
5      rSS jr SS jr!SS jr"S S jr#      S!S jr$S"S jr%            S#S jr&              S$S jr'g)%a=  Reconcile the values printed in a document's figures, with per-value provenance.

The scan reads each figure into prose, so a chart's numbers reach the document
with no per-value geometry and nothing checking them. This module gives every
plotted value a second, independent read and a trace to the page:

- The parse's positioned text cells are the fragment source. A born-digital
  page carries every printed chart value as a native cell with a box; a page
  docling read from its image carries the same cells from OCR, marked so.
- A local reader looks at the page image with the figure's fragments and
  reports each printed value with the fragment ids that ground it.
- A parser turns the scan's prose reading into the same value shape.
- A deterministic tie matches the two within each page and assigns statuses
  from the cell-status registry: agreement grounded in a fragment is
  `reconciled` with the fragment's box; a positional disagreement is
  `value_misread`; a one-sided value is `value_unreconciled` with the
  direction in the note.

Matching is value-first with label confirmation, consuming pairs so repeated
values resolve by count. Values are compared in a normalized form; the stored
value keeps the printed form. Boxes are normalized 0..1, top-left origin, the
same frame every other figure box in this package uses.
é    )ÚannotationsN)ÚPath)ÚDictÚListÚOptionalÚProtocolÚSequenceÚTuple)Úlogger)Ú	BaseModelÚField)ÚBoxÚFigureRecordÚFigureValueÚFigureValueRunÚPageScang      à?>   ÚaÚbyÚinÚofÚtoÚandÚperÚthec                  óT   • \ rS rSr% SrS\S'   S\S'   S\S'   SrS	\S
'   SrS\S'   Srg)ÚPageFragmenté,   zHOne positioned text cell from the parse, in the page's normalized frame.ÚstrÚidÚtextr   ÚboxFÚboolÚfrom_ocrç      ð?ÚfloatÚ
confidence© N)	Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__Ú__annotations__r#   r&   Ú__static_attributes__r'   ó    Ú7/home/mande/repo/quber/src/quber/core/figures/values.pyr   r   ,   s(   ‡ ÙRàƒGØ
ƒIØ	ƒHØ€HˆdÓØ€J�Ör/   r   c                  óP   • \ rS rSr% Sr\" SS9rS\S'   \" SS9rS\S'   S\S'   S	r	g
)ÚParsedValueé6   zGOne labeled value read out of the scan's prose description of a figure.Ú ©Údefaultr   ÚlabelÚseriesÚvaluer'   N)
r(   r)   r*   r+   r,   r   r7   r-   r8   r.   r'   r/   r0   r2   r2   6   s(   ‡ ÙQá˜rÑ"€Eˆ3Ó"Ù Ñ#€FˆCÓ#Ø†Jr/   r2   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rg)Ú	ReadValueé>   z9One printed value the local reader saw on the page image.r4   r5   r   Úchart_titler7   r8   r9   )Údefault_factoryz	List[str]Úfragment_idsr'   N)r(   r)   r*   r+   r,   r   r=   r-   r7   r8   Úlistr?   r.   r'   r/   r0   r;   r;   >   sF   ‡ ÙCá RÑ(€K�Ó(Ù˜rÑ"€Eˆ3Ó"Ù Ñ#€FˆCÓ#ØƒJÙ#°DÑ9€L�)Ö9r/   r;   c                  ó"   • \ rS rSrSrSS jrSrg)ÚScanValueParseréH   z3Turns a figure's prose reading into labeled values.c              ƒ  ó   #   • g 7f©Nr'   )ÚselfÚfigure_textsÚpages      r0   ÚparseÚScanValueParser.parseK   s   é € ÐX[ùó   ‚r'   N)rG   zSequence[str]rH   ÚintÚreturnzList[ParsedValue])r(   r)   r*   r+   r,   rI   r.   r'   r/   r0   rB   rB   H   s   † Ù=ç[r/   rB   c                  ó2   • \ rS rSrSr        SS jrSrg)ÚLocalValueReaderéN   zJReads a page image's figure values, citing the fragments that ground them.c              ƒ  ó   #   • g 7frE   r'   )rF   Ú
image_pathÚ	fragmentsrH   s       r0   ÚreadÚLocalValueReader.readQ   s   é € àùrK   r'   N)rR   r   rS   úSequence[PageFragment]rH   rL   rM   zList[ReadValue])r(   r)   r*   r+   r,   rT   r.   r'   r/   r0   rO   rO   N   s*   † ÙTðØðØ+AðØILðà	÷r/   rO   c                óî  • [        U S   5      =(       d    Sn[        U S   5      =(       d    Sn/ nU S   n[        U R                  S/ 5      5       H   u  pV[        UR                  SS5      5      R	                  5       nU(       d  M7  US   u  p‰p«UR                  [        S	U S
U 3UX�-  X’-  X¡-  X²-  S.[        UR                  SS5      5      [        UR                  SS5      5      S95        M¢     U$ )zïThe parse's cells for one page, normalized to the 0..1 top-left frame.

`cells_page` is one entry of the document's cells artifact: page_no,
width, height, and cells each holding text, box in top-left PDF points,
from_ocr, and confidence.
Úwidthr$   ÚheightÚpage_noÚcellsr    r4   r!   Úpz.c)ÚleftÚtopÚrightÚbottomr#   Fr&   )r   r    r!   r#   r&   )r%   Ú	enumerateÚgetr   ÚstripÚappendr   r"   )Ú
cells_pagerX   rY   rS   rZ   ÚiÚcellr    r]   r^   r_   r`   s               r0   Úpage_fragmentsrh   V   sû   € ô �*˜WÑ%Ó&×-¨#€EÜ�:˜hÑ'Ó(×/¨C€FØ$&€IØ˜Ñ#€GÜ˜ZŸ^™^¨G°RÓ8Ö9‰ˆÜ�4—8‘8˜F BÓ'Ó(×.Ñ.Ó0ˆÞÙØ#'¨¡;Ñ ˆ�5Ø×ÑÜØ�w�i˜r ! Ð%Øà ™LØ™<Ø"™]Ø$™oñ	ô ˜dŸh™h z°5Ó9Ó:Ü  §¡¨,¸Ó!<Ó=ñö	
ñ :ð& Ðr/   c           
     óv  • / nU  H°  nUR                   n[        S[        US   US   5      [        US   US   5      -
  5      n[        S[        US   US   5      [        US   US   5      -
  5      n[        US   US   -
  US   US   -
  -  S5      nXV-  U-  [        :¼  d  MŸ  UR	                  U5        M²     U$ )z0Fragments whose area sits mostly inside the box.g        r_   r]   r`   r^   g•Ö&è.>)r!   ÚmaxÚminÚSCOPE_OVERLAPrd   )rS   r!   ÚhitsÚfragÚfÚinter_wÚinter_hÚareas           r0   Úfragments_in_boxrs   w   sÆ   € à€DÛˆØ�H‰HˆÜ�cœ3˜q ™z¨3¨w©<Ó8¼3¸qÀ¹yÈ#ÈfÉ+Ó;VÑVÓWˆÜ�cœ3˜q ™{¨C°©MÓ:¼SÀÀ5ÁÈ3ÈuÉ:Ó=VÑVÓWˆÜ�A�g‘J  6¡Ñ*¨q°©{¸Q¸u¹XÑ/EÑFÈÓMˆØÑ Ñ%¬Õ6Ø�K‰K˜Öñ ð €Kr/   c                ó.  • [        U 5      R                  5       R                  SS5      R                  SS5      nUR                  SS5      R                  SS5      nUR                  S5      =(       a    UR	                  S5      nU(       a  US	S
 nUR                  SS5      R                  SS5      R                  S5      R                  5       nUS;   a  g [        U5      nU(       a	  US:”  a  U* nUS $ ! [         a    UR                  5       s $ f = f)zDA value reduced to its comparable core: sign, digits, decimal point.Ú$r4   Ú,u   â€“Ú-u   â€”Ú(Ú)é   éÿÿÿÿÚ%>   r4   rw   ú--ÚDASHr   Úg)	r   rc   ÚreplaceÚ
startswithÚendswithÚrstripr%   Ú
ValueErrorÚlower)ÚrawÚsÚnegativeÚnumbers       r0   Ú
norm_valuerŠ   „   sõ   € äˆC‹�‰Ó× Ñ   bÓ)×1Ñ1°#°rÓ:€AØ	�	‰	�%˜Ó×%Ñ% e¨SÓ1€AØ�|‰|˜CÓ ×4 Q§Z¡Z°£_€HÞØˆa�ˆGˆØ	�	‰	�#�rÓ×"Ñ" 3¨Ó+×2Ñ2°3Ó7×=Ñ=Ó?€AØˆOÓØðÜ�q“ˆö �F˜Q“JØ�ˆØ�QˆZÐøô	 ó Ø�w‰w‹yÒðús   ÃC7 Ã7DÄDc            	     óÆ   • [        5       nU  H@  nUR                  [        R                  " S[	        U5      R                  5       5      5        MB     [        U[        -
  5      $ )Nz[a-z0-9/+.]+)ÚsetÚupdateÚreÚfindallr   r…   Ú	frozensetÚ
STOP_WORDS)ÚpartsÚwordsÚparts      r0   Úlabel_wordsr•   —   sE   € Ü‹E€EÛˆØ�‰”R—Z’Z ´°T³·±Ó1BÓCÖDñ ä�UœZÑ'Ó(Ð(r/   c                ó8  • U R                    Vs/ s H  o"U;   d  M
  X   PM     nn[        U R                  5      nU HD  nU R                  UR                  ;   d#  US:w  d  M%  U[        UR                  5      ;   d  M@  US4s  $    U(       a  US   S4$ SS4$ s  snf )a"  The cited fragment printing the value, and whether any cited one does.

Returns the first cited fragment whose text contains the value in either
printed or normalized form. When citations exist but none contains the
value, the first cited fragment anchors the position of the disagreement.
r~   Tr   NF)r?   rŠ   r9   r    )rT   Úfragments_by_idrf   ÚcitedÚtargetrn   s         r0   Úvalue_fragmentrš   ž   sš   € ð *.×):Ò):ÓSÒ): AÀ?Ñ>RÓˆ_ÔÑ):€EÐSÜ˜Ÿ
™
Ó#€FÛˆØ�:‰:˜Ÿ™Ó" v°Õ'7¸FÄjÐQU×QZÑQZÓF[Õ<[Ø˜�:Òñ ö ˆE�!‰H¨%Ð/Ð/ 4¨%Ð/Ð/ùò Ts
   �	BœBc                óF  • Ub°  US   US   -   S-  nUS   US   -   S-  nSnU  H„  nUR                   nUb  UR                  c  M!  US   Us=::  a  US   ::  d  M6  O  M:  US   Us=::  a  US   ::  d  MO  O  MS  US   US   -
  US   US   -
  -  nUb
  X…S   :  d  Mw  X†R                  4nM†     Ub  US   $ U(       ab  U  Vs1 s H2  ofR                  (       d  M  X&R                  ;   d  M&  UR                  iM4     n	n[        U	5      S:X  a  [	        [        U	5      5      $ gs  snf )	uˆ  The picture a value belongs to, or None when no figure claims it.

A boxed value belongs to the figure whose box contains the value's
center, the smallest such figure when they nest. An unboxed value
belongs to the one figure whose scan text prints it, and to no figure
when the value appears in several or in none â€” a wrong anchor misleads
where an absent one just falls back to the page.
Nr]   r_   é   r^   r`   r   rz   )r!   Úpicture_refr    ÚlenÚnextÚiter)
Úfiguresr!   Ú	raw_valueÚcxÚcyÚbestÚfigÚfbrr   Úholderss
             r0   Úpicture_for_valuer©   ¯   s/  € ð �Ø�&‰k˜C ™LÑ(¨AÑ-ˆØ�%‰j˜3˜x™=Ñ(¨AÑ-ˆØ6:ˆÛˆCØ—‘ˆBØ‰z˜SŸ_™_Ñ4ÙØ�&‰z˜RÕ. 2 g¡;×.Ñ.°2°e±9ÀÕ3RÀbÈÁl×3RÑ3RØ˜7™ b¨¡jÑ0°R¸±\ÀBÀuÁIÑ5MÑN�Ø‘< 4¨q©'¥>Ø §/¡/Ð2’Dñ ð ÑØ˜‘7ˆNÞÙ.5Ócªg s¿½“?ÈY×ZbÑZbÑMb“?�3—?”?©gˆÐcÜˆw‹<˜1ÓÜœ˜W›Ó&Ð&Øùò ds   Â?DÃDÃ(Dc                ó  ^• U Vs0 s H  oUR                   U_M     nn[        U5      m/ nSU4S jjnU Hê  n	U" U	SS9=(       d    U" U	SS9n
[        X–5      u  p¼U
(       a  U(       a  Ub  Su  pÞO.U
(       a  Ub  SnSUR                  < S	3nOU
(       a  S
nSnOS
nSnUb  UR                  OSnUR                  [        U [        XU	R                  5      U	R                  U	R                  U	R                  U	R                  UU[        U	R                  5      US9
5        Mì     T HT  nUR                  [        U [        USUR                  5      UR                  UR                  UR                  S
SS95        MV     U$ s  snf )zAMatch the two readings of one page's figures and assign statuses.c                ó”  >• [        U R                  5      n[        U R                  U R                  5      nT HŒ  n[        UR                  5      U:w  a  M  [        UR                  UR                  5      n[        X5-  5      nU(       a	  U(       d  M[  U(       d  U(       a  U(       a	  U(       d  My  TR                  U5        Us  $    g rE   )rŠ   r9   r•   r7   r8   r"   Úremove)rT   Úrequire_labelr™   Ú
read_wordsÚ	candidateÚcandidate_wordsÚoverlapÚ	remainings          €r0   Ú
take_matchÚtie_page.<locals>.take_matchÙ   s“   ø€ Ü˜DŸJ™JÓ'ˆÜ  §¡¨T¯[©[Ó9ˆ
Û"ˆIÜ˜)Ÿ/™/Ó*¨fÓ4ÙÜ)¨)¯/©/¸9×;KÑ;KÓLˆOÜ˜:Ñ7Ó8ˆGÞ¦WÙÞ ¦_¾ÎGÙØ×Ñ˜YÔ'ØÒñ #ð r/   T)r­   FN)Ú
reconciledNÚvalue_misreadzthe page prints z2 where this value should appear - possible misreadÚvalue_unreconciledzKcorroborated by both readings, but not anchored to printed text on the pagezSone measurement, read from the page; uncontradicted, not independently corroborated)
rH   r�   r=   r7   r8   r9   ÚstatusÚnoter?   r!   zbone measurement, from the figure description; uncontradicted, not corroborated on the printed page)rH   r�   r7   r8   r9   r¸   r¹   )rT   r;   r­   r"   rM   zOptional[ParsedValue])r   r@   rš   r    r!   rd   r   r©   r9   r=   r7   r8   r?   )rH   r¡   Úscan_valuesÚlocal_valuesrS   ro   r—   Úoutr³   rT   ÚmatchedÚfragmentÚcontainsr¸   r¹   r!   Úleftoverr²   s                    @r0   Útie_pagerÁ   Í   su  ø€ ñ )2Ó2ª	 1—t‘t˜Q’w©	€OÐ2Ü�[Ó!€IØ€C÷ó  ˆÙ˜T°Ñ6×_¹*ÀTÐY^Ñ:_ˆÜ+¨DÓBÑˆÞ–x HÑ$8Ø-‰LˆF�DÞ˜Ñ-Ø$ˆFØ% h§m¡mÑ%6Ð6hÐi‰DÞØ)ˆFØ`‰Dà)ˆFØhˆDØ&Ñ2ˆh�lŠl¸ˆØ�
‰
ÜØÜ-¨g¸D¿J¹JÓGØ ×,Ñ,Ø—j‘jØ—{‘{Ø—j‘jØØÜ! $×"3Ñ"3Ó4Øñö	
ñ ó< ˆØ�
‰
ÜØÜ-¨g°t¸X¿^¹^ÓLØ—n‘nØ—‘Ø—n‘nØ+Øyñö
	
ñ ð €Jùò} 3s   †E?c           
   ƒ  ód  #   • [        US9nU  GH   nUR                  (       d  M  UR                  nUR                  U5      n	UR                  U5      n
U	b  U
c!  UR                  R                  SU S35        Ml  [        U	5      n UR                  UR                   Vs/ s H  oÌR                  PM     snU5      I Sh  v•N nUR                  X«U5      I Sh  v•N nUR                  R                  [        X‡R                  XÞU5      5        GM     UR                   UR"                  S.n[        R$                  " S['        S	 U  5       5      U5        U$ s  snf  N› Nƒ! [         aD  nUR                  R                  SU SU 35        [        R                  " SX�5         SnAGMš  SnAff = f7f)
zïReconcile every scanned figure page's values.

In: the run's page scans, the parse's cells keyed by page, a rendered
image per scanned page, and the two readers. Out: one FigureValueRun with
a FigureValue per value either reader produced.
)ÚdocumentNzpage z0: missing cells or render; figure values skippedz: figure-value read failed: zfigure values page {}: {})rµ   Úflaggedzfigure values: {} pages -> {}c              3  óJ   #   • U  H  oR                   (       d  M  S v •  M     g7f)rz   N)r¡   )Ú.0r‡   s     r0   Ú	<genexpr>Ú%read_figure_values.<locals>.<genexpr>8  s   é € Ð4SÂ¸1ÏÍ·Q±QÂùs   ‚#š	#)r   r¡   rH   rb   Úerrorsrd   rh   rI   r    rT   Ú	Exceptionr   ÚwarningÚvaluesÚextendrÁ   rµ   rÄ   ÚinfoÚsum)ÚscansÚcells_pagesÚpage_imagesÚparserÚreaderrÃ   ÚrunÚscanrH   re   ÚimagerS   ro   rº   r»   ÚexcÚcountss                    r0   Úread_figure_valuesrÚ     sb  é € ô  (Ñ
+€CÜˆØ�|�|ÙØ�y‰yˆØ —_‘_ TÓ*ˆ
Ø—‘ Ó%ˆØÑ ¡Ø�J‰J×Ñ  d VÐ+[Ð\Ô]ÙÜ" :Ó.ˆ	ð	Ø &§¡¸d¿lºlÓ-Kºl¸¯f¬f¹lÑ-KÈTÓ R×RˆKØ!'§¡¨U¸tÓ!D×DˆLð
 	�
‰
×Ñœ( 4¯©°{ÐR[Ó\×]ñ# ð$  ŸN™N°s·{±{ÑC€FÜ
‡K‚KÐ/´Ñ4SÁÓ4SÓ1SÐU[Ô\Ø€Jùò .LÑRÙDøÜó 	Ø�J‰J×Ñ  d VÐ+GÈÀuÐMÔNÜ�NŠNÐ6¸ÔBÞûð	üsa   ‚BF0ÂEÂE
Â4EÂ?EÃ EÃEÃEÃA8F0ÅEÅEÅ
F-Å)8F(Æ!F0Æ(F-Æ-F0)re   ÚdictrM   úList[PageFragment])rS   rV   r!   r   rM   rÜ   )r†   r   rM   r   )r’   r   rM   r�   )rT   r;   r—   zDict[str, PageFragment]rM   z#Tuple[Optional[PageFragment], bool])r¡   úSequence[FigureRecord]r!   zOptional[Box]r¢   r   rM   zOptional[str])rH   rL   r¡   rÝ   rº   zSequence[ParsedValue]r»   zSequence[ReadValue]rS   rV   rM   zList[FigureValue])rÐ   zSequence[PageScan]rÑ   zDict[int, dict]rÒ   zDict[int, Path]rÓ   rB   rÔ   rO   rÃ   r   rM   r   )(r,   Ú
__future__r   rŽ   Úpathlibr   Útypingr   r   r   r   r	   r
   Úlogurur   Úpydanticr   r   Úquber.core.figures.modelsr   r   r   r   r   rl   r�   r‘   r   r2   r;   rB   rO   rh   rs   rŠ   r•   rš   r©   rÁ   rÚ   r'   r/   r0   Ú<module>rä      s<  ðñõ0 #ã 	Ý ß B× Bå ß %ç ^Õ ^ð €ñ ÒIÓJ€
ô�9ô ô�)ô ô:�	ô :ô\�hô \ô�xô ôôB
ôô&)ð0Ø
ð0Ø&=ð0à(ô0ô"ð<FØ
ðFà#ðFð 'ðFð &ð	Fð
 &ðFð ôFðR#Øð#à ð#ð !ð#ð ð	#ð
 ð#ð ð#ð õ#r/   