ó
    9°MjG  ã                  óâ   • 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	  SSK
Jr  SSKJr  SSKJr  SS	KJrJr  SS
KJr  SSKJrJrJrJr  SSKJr  SrSS jrSS jr                SS jrg)uc  Detect and repair Camelot capture drops before structure correction.

Camelot occasionally drops printed values during cell assignment even though
its text extraction captured them: a totals row typeset on a raised baseline
splits into two row bands at the default grouping tolerance, and the numbers'
vertical center lands exactly on the band boundary, failing the strict
containment test on both sides â€” the row's label and dollar signs survive,
its numbers vanish from the grid.

The repair loop keeps authority deterministic and puts the model only where
judgement is needed:

1. DETECT (deterministic): numeric tokens present in the table region's text
   layer but absent from the grid. Same numeric normalization as the
   correction-stage grounding guard.
2. RECOMMEND (agent): the capture advisor sees the cropped table image, the
   extracted grid, and the dropped tokens with their printed lines, and
   proposes one bounded retry adjustment.
3. RETRY + ACCEPT (deterministic): re-run the region pass with the advised
   knobs. The retry replaces the original ONLY if it recovers every missing
   numeric and loses none the original had. Anything else keeps the original
   grid, so a wrong recommendation costs one extra Camelot pass and nothing
   else.

Every detection and every verdict is logged â€” a capture drop on a financial
document must never be silent.
é    )ÚannotationsN)ÚPath)ÚListÚOptionalÚSet)Úlogger)ÚCaptureAdvisor)Ú
page_words)ÚCamelotCandidateÚgrid_to_markdown)Únumeric_keys)ÚWordBoxÚbbox_to_top_leftÚcrop_region_pngÚtable_crop_box)Úcamelot_targetedg       @c                óŽ   • [        SR                  S U 5       5      5      n[        SR                  S U  5       5      5      nX#-
  $ )zFNumeric tokens in the region's text layer that appear in no grid cell.Ú c              3  ó*   #   • U  H	  oS    v •  M     g7f)é   N© )Ú.0Úws     ÚE/home/mande/repo/quber/src/quber/core/extractors/camelot/recapture.pyÚ	<genexpr>Ú'dropped_numeric_keys.<locals>.<genexpr>:   s   é € Ð!=²¨1 A¦$²ùs   ‚c              3  ó6   #   • U  H  o  H  o"v •  M     M     g 7f©Nr   ©r   ÚrowÚcs      r   r   r   ;   s   é € Ð Aªe s»S¸¤¹S¡ªeùó   ‚)r   Újoin)ÚcellsÚregion_wordsÚlayerÚgrids       r   Údropped_numeric_keysr(   8   s<   € ä˜Ÿ™Ñ!=±Ó!=Ó=Ó>€EÜ˜Ÿ™Ñ A©eÓ AÓAÓB€DØ‰<Ðó    c                óü   ^• / n[        U5       Hi  nU  HK  m[        TS   5      U1:X  d  M  SR                  U4S jU  5       5      nUR                  SU SU 35          MR     UR                  SU S35        Mk     U$ )uo   For each missing token, the full printed line it sits on â€” the evidence
the advisor grounds its diagnosis in.r   r   c              3  ób   >#   • U  H$  n[        US    TS    -
  5      S:  d  M  US   v •  M&     g7f)é   é   r   N)Úabs)r   Úxr   s     €r   r   Ú printed_lines.<locals>.<genexpr>F   s0   øé € ÐSªl¨¼cÀ!ÀAÁ$ÈÈ1ÉÁ+Ó>NÐQRÑ>R£  !¦ªlùs   ƒ/£/Ú'z' on printed line: z' (word not isolated on a line))Úsortedr   r#   Úappend)r%   ÚmissingÚlinesÚkeyÚliner   s        @r   Úprinted_linesr8   ?   s…   ø€ ð €EÜ�gŽˆÛˆAÜ˜A˜a™DÓ! c UÕ*Ø—x‘xÔS©lÓSÓS�Ø—‘˜q  Ð%8¸¸Ð?Ô@Úñ	 ð �L‰L˜1˜S˜EÐ!@ÐAÖBñ ð €Lr)   c           
   ƒ  óº  #   • Ub  U R                   b  Uc  U $ [        R                  " [        [	        U5      U5      I Sh  v•N u  pxn	[        U R                   U[        S9u  p«pÍU	 Vs/ s H0  oîS   U
:¼  d  M  US   U::  d  M  US   U:¼  d  M#  US   U::  d  M.  UPM2     nn[        U R                  U5      nU(       d  U $ [        R                  " SU[        U5      [        U5      5        [        R                  " [        U[        U R                   U5      U5      I Sh  v•N nUR                  U[!        U R                  5      [#        UU5      U5      I Sh  v•N nUc  U $ [        R$                  " SUUR&                  UR(                  UR*                  UR,                  5        U R                   u  nnnn[/        UU5      S	 S
[1        UU5      S	 S
[1        UU5      S	 S
[/        UU5      S	 3n [        R                  " [2        UUUUUR&                  UR(                  UR*                  5      I Sh  v•N nUc  [        R                  " SU5        U $ [7        SR9                  S U R                   5       5      5      n[7        SR9                  S UR                   5       5      5      nUU-  nUU-
  nUU:X  a*  U(       d#  [        R$                  " SU[        U5      5        U$ [        R                  " SU[        U5      [        U5      [        U5      5        U $  GNÚs  snf  GN GNÍ Nõ! [4         a$  n[        R                  " SUU5        U s SnA$ SnAff = f7f)uñ   Return `cand`, or an advised retry that provably captures more.

No-op when the advisor is off, the candidate has no bbox, or no drop is
detected. The retry is scoped to the candidate's own bbox â€” the exact
frame the drop was measured in.
N)Úpadr   é   r,   r-   uj   page {}: capture drop detected â€” {} numeric token(s) in the table's text layer missing from the grid: {}zJpage {}: capture advisor recommends flavor={} row_tol={} column_tol={}: {}z.1fÚ,z?page {}: capture-repair retry failed; keeping original grid: {}z=page {}: capture-repair retry found no grid; keeping originalr   c              3  ó6   #   • U  H  o  H  o"v •  M     M     g 7fr   r   r   s      r   r   Ú!repair_capture.<locals>.<genexpr>�   s   é € Ð$J²*¨3ÃcÀ¤QÁc¡Q²*ùr"   c              3  ó6   #   • U  H  o  H  o"v •  M     M     g 7fr   r   r   s      r   r   r>   ‘   s   é € Ð#J²¨#ÃcÀ¤AÁc¡A²ùr"   uE   page {}: capture repair ACCEPTED â€” recovered {} token(s), lost nonezRpage {}: capture repair REJECTED (recovered {}/{}, lost {}); keeping original grid)ÚbboxÚasyncioÚ	to_threadr
   r   r   ÚDETECT_PAD_PTSr(   r$   r   ÚwarningÚlenr2   r   r   Ú	recommendr   r8   ÚinfoÚflavorÚrow_tolÚ
column_tolÚ	diagnosisÚminÚmaxr   Ú	Exceptionr   r#   )ÚcandÚsourceÚpageÚ
page_imageÚdpiÚordinalÚadvisorÚ_pwÚpage_hÚwordsÚleftÚtopÚrightÚbottomr   r%   r4   ÚcropÚadviceÚx1Úy1Úx2Úy2ÚareaÚretryÚexcÚoriginalÚretriedÚ	recoveredÚlosts                                 r   Úrepair_capturerj   N   s  é € ð �˜$Ÿ)™)Ñ+¨zÑ/AØˆÜ&×0Ò0´¼TÀ&»\È4ÓP×PÑ€C�Ü/°·	±	¸6Ä~ÑVÑ€DˆuÙ$Ójšu˜!¨!©°©“A¸¸1¹À¹“AÈ1ÈQÉ4ÐSVÉ;“AÐ[\Ð]^Ñ[_ÐciÑ[i—A™u€LÐjÜ" 4§:¡:¨|Ó<€GÞØˆÜ
‡N‚Nð	$àÜˆG‹Üˆw‹ôô ×"Ò"¤?°JÄÈtÏyÉyÐZ`Ó@aÐcfÓg×g€DØ×$Ñ$ØÔ˜tŸz™zÓ*¬M¸,ÈÓ,PÐRVó÷ €Fð �~ØˆÜ
‡K‚KØTØØ�‰Ø�‰Ø×ÑØ×Ñôð —Y‘Y�N€BˆˆB�Ü�"�b‹k˜#Ð˜a¤ B¨£¨CÐ0°´#°b¸"³+¸cÐ1BÀ!ÄCÈÈBÃKÐPSÐCTÐU€DðÜ×'Ò'ÜØØØØØ�M‰MØ�N‰NØ×Ñó	
÷ 	
ˆð �}Ü�ŠÐVÐX\Ô]Øˆä˜CŸH™HÑ$J°$·*²*Ó$JÓJÓK€HÜ˜3Ÿ8™8Ñ#J°·²Ó#JÓJÓK€GØ˜'Ñ!€IØ�gÑ€DØ�GÓ¦DÜ�ŠØSØÜ�	‹Nô	
ð
 ˆÜ
‡N‚NØ\ØÜˆI‹ÜˆG‹ÜˆD‹	ôð €KòG Qùâjò hòñ"	
øô ó Ü�ŠÐXÐZ^Ð`cÔdØ�ûðüsŸ   ‚>MÁ LÁ'MÁ(LÁ8LÂLÂLÂLÂB MÄL"Ä 8MÅL%ÅBMÇ0AL* È2L(È3L* È7C$MÌMÌ%MÌ(L* Ì*
MÌ4MÍMÍMÍMÍM)r$   zList[List[str]]r%   úList[WordBox]ÚreturnúSet[str])r%   rk   r4   rm   rl   z	List[str])rO   r   rP   ÚstrrQ   ÚintrR   zOptional[Path]rS   ro   rT   ro   rU   zOptional[CaptureAdvisor]rl   r   ) Ú__doc__Ú
__future__r   rA   Úpathlibr   Útypingr   r   r   Úlogurur   Úquber.agents.capture_advisorr	   Úquber.agents.completenessr
   Ú%quber.core.extractors.camelot.acquirer   r   Ú7quber.core.extractors.camelot.correspondence.correctionr   Ú5quber.core.extractors.camelot.correspondence.geometryr   r   r   r   Ú5quber.core.extractors.camelot.correspondence.recoveryr   rC   r(   r8   rj   r   r)   r   Ú<module>r{      s¥   ðñõ8 #ã Ý ß &Ñ &å å 7Ý 0ß TÝ P÷ó õ Sð €ôôðTØ
ðTàðTð ðTð ð	Tð
 
ðTð ðTð &ðTð õTr)   