+
    Vej                         R t ^ RIt^ RIt^ RIHt ^ RIHtHt ^ RIt^ RI	H
t
HtHtHtHt ^ RIHtHtHtHtHtHtHtHtHtHtHtHtHtHtHt ]P>                  ! ] 4      t!Rt"]PF                  ! R]PH                  4      t%]PF                  ! R4      t&]PF                  ! R	4      t'R
 R lt(R R lt)R R lt* ! R R4      t+R# )u1  Sweep nacional dos anúncios CIRE do CITIUS.

O `cire.py` pesquisa por NIF, um de cada vez, e só encontra o que envolve
empresas já registadas. Este módulo faz o inverso: varre **todos** os anúncios
publicados num intervalo de datas, sem filtro de entidade, para depois cruzar as
partes contra a watchlist. É isso que permite detectar a insolvência de um
devedor que ninguém tinha adicionado à plataforma — e o cruzamento é retroactivo,
porque os anúncios ficam guardados mesmo quando não dão match no momento.

Verificado ao vivo em 2026-07-25:
  * a pesquisa aceita termo vazio, filtrada só por datas (`dd-mm-aaaa`);
  * ~110 documentos/dia em todos os grupos, arquivo até pelo menos 2020;
  * paginação por `__EVENTTARGET=…Pager1$lnkNext`, 10 blocos por página;
  * cada bloco traz um `queryString` opaco que dá acesso ao PDF do anúncio,
    **desde que se use a mesma sessão HTTP da pesquisa** — com cookies novos o
    CITIUS devolve uma página de erro. Daí este módulo ser uma classe que segura
    o cliente, em vez de funções soltas.
N)date)AnyAsyncIterator)build_clientextract_aspnet_statehttp_get	http_postrandom_delay)BASEF_ACTOSF_ATEF_BTNF_DESDEF_DIASF_GRUPOF_NUMERO
F_PESQUISAF_TIPOF_TRIBUNAISURLparse_cire_pageparse_pt_datesplit_processi  u   (sent(en[çc]a)?\s*(de\s*)?(declara[çc][ãa]o\s*)?.*insolv|insolv.*sent(en[çc]a)?|cita[çc][ãa]o\s*(de\s*)?credores|credores\s*/\s*desp|lista\s*provis[óo]ria\s*de\s*cr[ée]ditos|verifica[çc][ãa]o\s*(ulterior\s*)?(de\s*)?cr[ée]ditos)z)<iframe[^>]*id="ifrDoc"[^>]*src="([^"]+)"zid="lblTotal"[^>]*>(\d+)c                0    V ^8  d   QhR\         R\        /# )   dreturn)r   str)formats   ""app/scrapers/cire_announcements.py__annotate__r    H   s     " "D "S "    c                 $    V P                  R 4      # )z%d-%m-%Y)strftime)r   s   &r   _fmtr$   H   s    ::j!!r!   c                >    V ^8  d   QhR\         R,          R\        /# )r   atoNr   )r   bool)r   s   "r   r    r    L   s     2 2C$J 24 2r!   c                R    \        T ;'       d    \        P                  V 4      4      # )uJ   True quando o acto é do tipo que fixa prazo de reclamação de créditos.)r'   CLAIM_ACT_REsearch)r&   s   &r   opens_claim_windowr+   L   s     00++C011r!   c                    V ^8  d   QhR\         \        \        3,          R\        R,          R\         \        \        3,          /# )r   entrytokenNr   )dictr   r   )r   s   "r   r    r    Q   s5      4S> #* c3h r!   c                   V P                  R4      ;'       g    . pV P                  4        UUu/ uF  w  r4VR8w  g   K  W4bK  	  ppp\        V P                  R4      ;'       g    R4      w  rgV P                  R4      p\        V P                  R4      4      p	RV P                  R4      ;'       g    V P                  R4      RV P                  R4      R	TRTR
T;'       g    RRV P                  R4      ;'       g    V P                  R4      RT	R\        V P                  R4      4      RTRV U
u. uF  p
V
P                  R4      ;'       g    RP	                  4       '       g   K4  RV
P                  R4      ;'       g    RR,          ;'       g    RRV
P                  R4      ;'       g    RR,          RV
P                  R4      ;'       g    RP	                  4       ;'       g    R/NK  	  up
RVR\        V4      /# u uppi u up
i )u   Bloco parseado -> linha pronta a persistir. As partes ficam de fora do
`raw` de propósito: vão para tabela própria e duplicá-las aqui custaria ~4x
em disco (o `processes.raw` chega a 83 kB numa insolvência com muitos
credores)._partiesprocesso r&   data
referenciau   referênciatribunaljuizoprocess_numberNspeciesu   espécieespeciepub_dateaction_dateu   data da propositura da açãorawpartiesnamerole:Nd   N:Ni  Nnif	pdf_token	wants_pdf)getitemsr   r   stripr+   )r-   r.   r>   kvr=   r8   r7   r&   r;   ps   &&         r   normalize_blockrK   Q   s   
 ii
#))rG!KKM
=MDAQ*_414MC
=)%))J*?*E*E2FN
))E
CUYYv./Heii.II%))L2IEIIj)s.00D599Z(@@EIIi,@H}UYY/N%OPs 
 f##**,v,,"d3;;tv,,"d3e**113;;t
 
 	U',) 	 >
s:   G?G?1H*H*'H* H*H*,!H*H*&H*c                      a  ] tR t^st o RtRV 3R lR lltV 3R lR ltV 3R lR lt]V 3R lR	 l4       t	V 3R
 lR lt
V 3R lR ltRtV tR# )CireAnnouncementSweeperu%  Segura a sessão HTTP durante todo o sweep.

Uso:
    async with CireAnnouncementSweeper() as s:
        async for page in s.pages(d1, d2):
            for row in page["rows"]:
                if row["wants_pdf"]:
                    row["pdf_text"] = await s.fetch_pdf_text(row["pdf_token"])
c                $   < V ^8  d   QhRS[ RR/# )r   grupor   N)r   )r   __classdict__s   "r   r    $CireAnnouncementSweeper.__annotate__~   s     6 6c 64 6r!   c                     Wn         R V n        R # N)rO   _client)selfrO   s   &&r   __init__ CireAnnouncementSweeper.__init__~   s    
15r!   c                   < V ^8  d   QhRR/# )r   r   rM    )r   rP   s   "r   r    rQ      s      "; r!   c                t   "   \        4       V n        V P                  P                  4       G R j  xL
  V #  L5irS   )r   rT   
__aenter__rU   s   &r   r[   "CireAnnouncementSweeper.__aenter__   s.     #~ll%%''' 	(s   -868c                $   < V ^8  d   QhRS[ RR/# )r   excr   N)r   )r   rP   s   "r   r    rQ      s        C  D  r!   c                   "   V P                   e,   V P                   P                  ! V!  G R j  xL
  R V n         R # R #  L5irS   )rT   	__aexit__)rU   r_   s   &*r   ra   !CireAnnouncementSweeper.__aexit__   s6     <<#,,((#...DL $.s   ,A >A c                4   < V ^8  d   QhRS[ P                  /# )r   r   )httpxAsyncClient)r   rP   s   "r   r    rQ      s      )) r!   c                L    V P                   f   \        R4      hV P                   # )Nz0CireAnnouncementSweeper usado fora do async with)rT   RuntimeErrorr\   s   &r   clientCireAnnouncementSweeper.client   s"    <<QRR||r!   c          	      R   < V ^8  d   QhRS[ RS[ RS[S[S[S[3,          ,          /# )r   	date_fromdate_tor   )r   r   r/   r   r   )r   rP   s   "r   r    rQ      s2     0 00(,0	tCH~	&0r!   c               ,  "   \        V P                  \        4      G Rj  xL
 p/ \        VP                  4      C\
        R\        R\        R\        R\        V P                  \        R\        R\        \        V4      \        \        V4      \         R/
Cp\#        4       G Rj  xL
  \%        V P                  \        VR\        /R7      G Rj  xL
 p^p \'        VP                  4      pVR,          p\)        VR	,          4       U	U
u. uF(  w  r\+        Y\-        V4      8  d	   W,          MR4      NK*  	  pp	p
R
VRVR,          RV/5x  VR,          '       g   R# V\.        8  d   \0        P3                  RW\.        4       R# / \        VP                  4      CRRRR/CpVP5                  \         R4       \#        4       G Rj  xL
  \%        V P                  \        VR\        /R7      G Rj  xL
 pV^,          pEK,   EL ELa EL9u up
p	i  LJ L!5i)uy   Percorre as páginas de resultados do intervalo, uma de cada vez, para
o chamador poder persistir e commitar por página.NrB   r3   todos	PesquisarReferer)r4   headerstokensblockspagetotalrowshas_nextu:   cire_announcements %s..%s: tecto de páginas atingido (%d)__EVENTTARGETz(ctl00$ContentPlaceHolder1$Pager1$lnkNext__EVENTARGUMENT)r   rh   r   r   textr   r   r   r   r   rO   r   r   r   r$   r   r   r	   r   r   	enumeraterK   lenPAGE_CAPloggerwarningpop)rU   rk   rl   initialpayloadrrt   r4   rr   ibrv   pagers   &&&          r   pagesCireAnnouncementSweeper.pages   s    
 !c22
"7<<0
EGTZZRbT)_4=;
 nDKK7YPSDTUU"166*D(^F &d8n55DA  CK69TJ5   4$w-FF
##xP &qvv.!K!2E
 IIeT".  SuyRUFVWWAAIDW 3 	U( !Wse   HHBH#H$*HH:H	.H
7BHH*H0H1HHH
HHc                B   < V ^8  d   QhRS[ R,          RS[R,          /# )r   r.   Nr   )r   bytes)r   rP   s   "r   r    rQ      s#      3: %$, r!   c                >  "   V'       g   R#  \        V P                  \         RV 2R\        /R7      G Rj  xL
 p\        P                  TP                  4      pT'       d    \        TP                  ^4      4      ^ 8X  d   R# \        P                  TP                  4      pT'       g   R# \        R,           TP                  ^4      P                  RR4      ,           p \        T P                  TR\         R	2/R7      G Rj  xL
 pTP                  P                  R4      '       g   R# TP                  #  EL  \         d"   p\
        P                  RT4        Rp?R# Rp?ii ; i Le  \         d"   p\
        P                  R
T4        Rp?R# Rp?ii ; i5i)u   Token do bloco -> bytes do PDF, ou None quando o anúncio não tem
documento associado (`lblTotal` = 0, o que acontece em ~1/3 dos casos).NzViewer/MostraPdf.aspx?q=rp   )rq   z%cire_announcements: viewer falhou: %szViewer/z&amp;&zViewer/MostraPdf.aspxz.cire_announcements: download do PDF falhou: %ss   %PDF)r   rh   r
   r   	Exceptionr~   infoLBL_TOTAL_REr*   rz   intgroupIFRAME_SRC_REreplacecontent
startswith)rU   r.   viewereru   srcurldocs   &&      r   fetch_pdf_bytes'CireAnnouncementSweeper.fetch_pdf_bytes   sM     	#v%=eWE"C( F ##FKK0EKKN+q0""6;;/Y1!5!5gs!CC	 S9>S6T*U C {{%%g..{{3  	KK?C	  	KKH!L	s   F*D= D:D= &F$AF,5F"$E. E,E.  F,F:D= =E)E$F$E))F,E. .F9FFFF)rT   rO   N)r3   )__name__
__module____qualname____firstlineno____doc__rV   r[   ra   propertyrh   r   r   __static_attributes____classdictcell__)rP   s   @r   rM   rM   s   sQ     6 6 
   
  
0 0d r!   rM   ),r   loggingredatetimer   typingr   r   rd   app.scrapers.baser   r   r   r   r	   app.scrapers.cire_commonr
   r   r   r   r   r   r   r   r   r   r   r   r   r   r   	getLoggerr   r~   r}   compile
IGNORECASEr)   r   r   r$   r+   rK   rM   rY   r!   r   <module>r      s   $  	  %      $ 
		8	$
 zzB MM 

GHzz56"2
Dp pr!   