+
    4Yej&                         R t ^ RIt^ RIt^ RIHt ^ RIHt ^ RIHt ^ RI	H
t
Ht ^ RIHt ]P                  ! ]4      tRR R lltR	 R
 ltR# )u
  Persistência dos anúncios CIRE varridos nacionalmente.

Guarda o anúncio, as suas partes, e — quando o acto abre prazo de reclamação —
os campos extraídos do PDF. O cruzamento com a watchlist vive em
`insolvency_matching.py`, chamado a seguir a cada lote.
N)Any)text)AsyncSession)compute_deadlineparse_announcement_text)announcement_hashc                <    V ^8  d   QhR\         R\        R\        /# )   sessiononly_missingreturn)r   boolint)formats   "(app/services/cire_announcement_ingest.py__annotate__r      s'     S S, Sd SVY S    c                 F  a
"   T P                  \        RV'       d   RMR R24      4      G Rj  xL
 P                  4       p^ pV EFq  w  rErg\        V4      o
S
P	                  R4      fC   \
        ;QJ d    V
3R lR" 4       F  '       g   K   RM	  RM! V
3R lR" 4       4      '       g   Kg  \        VS
P	                  R4      S
P	                  R4      V4      w  rV P                  \        R4      RVRS
P	                  R4      RS
P	                  R4      RS
P	                  R4      RVRV	RS
P	                  R
4      RS
P	                  R4      RS
P	                  R4      RS
P	                  R4      RS
P	                  R4      RS
P	                  R4      RS
P	                  R	4      /4      G Rj  xL
  V^,          pEKt  	  V P                  \        R 4      4      G Rj  xL
  V P                  4       G Rj  xL
  \        P                  R!V\        V4      4       V#  EL Lq LC L-5i)#u'  Reaplica o parser ao `pdf_text` já guardado, sem voltar ao CITIUS.

Os anúncios variam de tribunal para tribunal e o parser vai ganhando padrões
com o tempo; sem isto, cada melhoria só valeria para o que fosse varrido a
partir daí, e o histórico ficava com prazos por extrair para sempre.
z
                SELECT id::text, pdf_text, pub_date, opens_claim_window
                FROM cire_announcements
                WHERE pdf_text IS NOT NULL
                  zAND claim_days IS NULL z
                N
claim_daysc              3   F   <"   T F  pSP                  V4      x  K  	  R # 5i)N)get).0kdetails   & r   	<genexpr>)reparse_stored_details.<locals>.<genexpr>.   s      8
#QaFJJqMM#Qs   !
debtor_nif	admin_nifinsolvency_dateTFeditos_daysa  
                UPDATE cire_announcements SET
                    claim_days = COALESCE(:claim_days, claim_days),
                    claim_days_source = COALESCE(:src, claim_days_source),
                    editos_days = COALESCE(:editos, editos_days),
                    claim_deadline = COALESCE(:deadline, claim_deadline),
                    claim_deadline_estimated = CASE
                        WHEN :claim_days IS NOT NULL THEN :estimated
                        ELSE claim_deadline_estimated END,
                    insolvency_date = COALESCE(:idate, insolvency_date),
                    debtor_nif = COALESCE(:dnif, debtor_nif),
                    debtor_name = COALESCE(:dname, debtor_name),
                    debtor_address = COALESCE(:daddr, debtor_address),
                    admin_name = COALESCE(:aname, admin_name),
                    admin_email = COALESCE(:aemail, admin_email),
                    admin_nif = COALESCE(:anif, admin_nif)
                WHERE id = :id
                idsrcclaim_days_sourceeditosdeadline	estimatedidatednifdnamedebtor_namedaddrdebtor_addressaname
admin_nameaemailadmin_emailanifz
            UPDATE insolvency_hits h
            SET claim_deadline = a.claim_deadline
            FROM cire_announcements a
            WHERE a.id = h.announcement_id
              AND h.claim_deadline IS DISTINCT FROM a.claim_deadline
            u6   reparse de anúncios: %d actualizados de %d candidatos)r   r   r   )executer   allr   r   anyr   commitloggerinfolen)r
   r   rowsupdatedann_idpdf_textpub_dateopensr%   r&   r   s   &&        @r   reparse_stored_detailsr?      s     oo 0<+D E		
 		
 
ce 	 G-1)((2::l#+CC 8
#Q8
CCC 8
#Q8
 5
 5
 .fjj.

=0I5
 oo* ffjj6vzz"56&**]3HY$56

<0M2$45L1&**]3

;/+$
 $	
 $	
J 	1] .2b //	

 
 
 ..

KKH'SVW[S\]NW		
,$	
P
 sY   ,H!HAH!6H!H!+C>H!)H*/H!HH!1H2'H!H!H!H!c                    V ^8  d   QhR\         R\        \        \        3,          R\        \        \
        3,          /# )r	   r
   rowr   )r   dictstrr   tupler   )r   s   "r   r   r   m   s:     p% p%p% $S#Xp%
39p%r   c                n  "   \        VP                  R4      VP                  R4      VP                  R4      VP                  R4      4      pVP                  R4      ;'       g    / p\        VP                  R4      4      p\        VP                  R4      VP                  R4      VP                  R4      V4      w  rVT P	                  \        R	4      / RVP                  R4      bR
VP                  R
4      bRVP                  R4      bRVP                  R4      bRVP                  R4      bRVP                  R4      bRVP                  R4      bRVP                  R4      bRVP                  R4      bR\        P                  ! VP                  R4      ;'       g    / R\        R7      bRVP                  R4      bR\        VP                  R4      4      bRVP                  R4      bRVP                  R4      bRVP                  R4      bRVP                  R4      bRVbRVRVRVP                  R4      RVP                  R4      RVP                  R4      RVP                  R4      RVP                  R4      RVP                  R4      RVP                  R4      R V/
C4      G R!j  xL
 pVP                  4       w  rV	'       d   VP                  R"4      ;'       g    .  F  p
V
P                  R#4      ;'       g    R$P                  4       ;'       g    R!pV'       d)   VP                  4       '       d   \        V4      ^	8w  d   R!pV P	                  \        R%4      R&VR'V
P                  R'4      R(V
P                  R(4      R#V/4      G R!j  xL
  K  	  \        V4      \        V	4      3#  EL L!5i))u   Insere ou actualiza um anúncio. Devolve (id, is_new).

Ao reencontrar um anúncio já conhecido só se toca no `last_seen_at` e nos
campos do PDF — que podem chegar numa corrida posterior, se o detalhe não
foi puxado à primeira.

referenciaprocess_numberator=   r   	wants_pdfr   r    u  
            INSERT INTO cire_announcements (
                referencia, tribunal, juizo, ato, ato_group, process_number, species,
                pub_date, action_date, raw, pdf_token, has_pdf, pdf_text,
                detail_fetched_at, claim_days, claim_days_source, editos_days,
                claim_deadline, claim_deadline_estimated, opens_claim_window,
                insolvency_date, debtor_nif, debtor_name, debtor_address,
                admin_name, admin_email, admin_nif, dedup_hash
            ) VALUES (
                :referencia, :tribunal, :juizo, :ato, :ato_group, :process_number, :species,
                :pub_date, :action_date, CAST(:raw AS JSONB), :pdf_token, :has_pdf, :pdf_text,
                CASE WHEN :has_pdf THEN now() ELSE NULL END,
                :claim_days, :claim_days_source, :editos_days,
                :claim_deadline, :claim_deadline_estimated, :opens_claim_window,
                :insolvency_date, :debtor_nif, :debtor_name, :debtor_address,
                :admin_name, :admin_email, :admin_nif, :dedup_hash
            )
            ON CONFLICT (dedup_hash) DO UPDATE SET
                last_seen_at = now(),
                -- só enriquecemos: nunca apagamos um detalhe já obtido com um NULL
                pdf_text = COALESCE(EXCLUDED.pdf_text, cire_announcements.pdf_text),
                has_pdf = cire_announcements.has_pdf OR EXCLUDED.has_pdf,
                detail_fetched_at = COALESCE(
                    EXCLUDED.detail_fetched_at, cire_announcements.detail_fetched_at),
                claim_days = COALESCE(EXCLUDED.claim_days, cire_announcements.claim_days),
                claim_days_source = COALESCE(
                    EXCLUDED.claim_days_source, cire_announcements.claim_days_source),
                editos_days = COALESCE(EXCLUDED.editos_days, cire_announcements.editos_days),
                claim_deadline = COALESCE(
                    EXCLUDED.claim_deadline, cire_announcements.claim_deadline),
                claim_deadline_estimated = CASE
                    WHEN EXCLUDED.claim_days IS NOT NULL THEN EXCLUDED.claim_deadline_estimated
                    ELSE cire_announcements.claim_deadline_estimated END,
                insolvency_date = COALESCE(
                    EXCLUDED.insolvency_date, cire_announcements.insolvency_date),
                debtor_nif = COALESCE(EXCLUDED.debtor_nif, cire_announcements.debtor_nif),
                debtor_name = COALESCE(EXCLUDED.debtor_name, cire_announcements.debtor_name),
                debtor_address = COALESCE(
                    EXCLUDED.debtor_address, cire_announcements.debtor_address),
                admin_name = COALESCE(EXCLUDED.admin_name, cire_announcements.admin_name),
                admin_email = COALESCE(EXCLUDED.admin_email, cire_announcements.admin_email),
                admin_nif = COALESCE(EXCLUDED.admin_nif, cire_announcements.admin_nif)
            RETURNING id::text, (xmax = 0) AS is_new
            tribunaljuizo	ato_groupspeciesaction_daterawF)ensure_asciidefault	pdf_tokenhas_pdfr<   r#   claim_deadlineclaim_deadline_estimatedopens_claim_windowr   r   r*   r,   r.   r0   r   
dedup_hashNpartiesnifr   z
                    INSERT INTO cire_announcement_parties (announcement_id, role, name, nif)
                    VALUES (:aid, :role, :name, :nif)
                    aidrolename)r   r   r   r   r2   r   jsondumpsrC   firststripisdigitr8   )r
   rA   hr   r>   r%   r&   resultr;   is_newprY   s   &&          r   upsert_announcementrf   m   s     	sww'78#''%.#''R\J]	A WWX$$"F%&E*
VZZ5vzz-7PRWH ??+-	
\	
#'',/	

+	
 SWWW%	
 3775>		

 -	
 cgg&67	
 swwy)	
 
+	
 377=1	
 4::cggen22PST	
 -	
 tCGGJ/0	
 
+	
 &**\2	
  ,?!@	
  6::m4!	
" h#	
$ '	 %vzz*;<&**\26::m4fjj)9:&**\26::m4K0!7	
_L LFZ \\^NF#))r)A55<%%2,,.66$CCKKMMSX]// fvquuV}eUXY  	 * v;V$$}LhsC   J+N5-N0.5N5$N5N5
N5%N5;AN5N3N53N5)T)__doc__r]   loggingtypingr   
sqlalchemyr   sqlalchemy.ext.asyncior   %app.services.cire_announcement_parserr   r   app.services.dedupr   	getLogger__name__r6   r?   rf    r   r   <module>rq      sB        / 1			8	$Slp%r   