+
    `ojT                        ^ RI t ^ RIt^ RIt^ RIt^ RIHtHt ^ RIHt ^ RIH	t	H
t
 ^ RIHt ^ RIHt ^ RIHtHtHtHtHt ]P*                  ! ]4      tRtR	tR
tRtRtRtRtRt]P@                  ! R]PB                  4      t"]P@                  ! R4      t#R R lt$R R lt%R R lt&R R lt'R R lt(R R lt)0 R0mt*^t+R1t,R R lt-R  R! lt.R" R# lt/R$ R% lt0R2R& R' llt1R( R) lt2R* R+ lt3R, R- lt4R. R/ lt5R# )3    N)datedatetime)Any)BeautifulSoupTag)fuzz)settings)build_clientextract_aspnet_statehttp_get	http_postrandom_delayzDhttps://www.citius.mj.pt/portal/consultas/ConsultasDistribuicao.aspxz&ctl00$ContentPlaceHolder1$ddlTribunaisz*ctl00$ContentPlaceHolder1$txtCalendarDesdez(ctl00$ContentPlaceHolder1$txtCalendarAtez"ctl00$ContentPlaceHolder1$txtPartez%ctl00$ContentPlaceHolder1$txtNEntradaz#ctl00$ContentPlaceHolder1$btnSearch!ctl00_ContentPlaceHolder1_grdViewz(\d+)\s+Processos\s+encontradosz(\d{1,2})/(\d{1,2})/(\d{4})c                L    V ^8  d   QhR\         R,          R\        R,          /# )   sNreturn)strr   )formats   "app/scrapers/distribuicao.py__annotate__r   $   s"     
 
cDj 
TD[ 
    c                    V '       g   R # \         P                  V 4      pV'       g   R # VP                  4       w  r#p \        \	        V4      \	        V4      \	        V4      4      P                  4       #   \         d     R # i ; iN)DATE_REsearchgroupsr   intr   
ValueError)r   mdmmys   &    r   _parse_pt_dater$   $   sf    qAxxzHA1ABQ05577 s   5A4 4BBc                \    V ^8  d   QhR\         \        \        \        3,          ,          /# )r   r   )listdictr   )r   s   "r   r   r   1   s       tDcN'; r   c                    "   \        4       ;_uu_4       GR j  xL
 p \        V \        4      G R j  xL
 pR R R 4      GR j  xL
  \        XP                  R4      pVP                  RR\        /4      p. pV'       g   V# VP                  R4       F  pVP                  R4      ;'       g    RP                  4       pVP                  4       ;'       g    RP                  4       pV'       g   K^  VR8w  g   Kg  V'       g   Kq  VP                  R4      '       d   K  VP                  RVR	V/4       K  	  V#  EL EL L  + GR j  xL 
 '       g   i     EL; i5i)
Nlxmlselectnameoptionvalue 0-label)r
   r   URLr   textfindF_TRIBUNAISfind_allgetstripget_text
startswithappend)clientrsoupr*   outoptvalr1   s           r   discover_tribunal_codesrB   1   s     ~~~63'' ~(DYYx&+!67F "C
x(www%%2,,.%%2,,.33#:%%0@0@0E0EJJgu56	 )
 J ' ~~~s~   ED6ED>D9D>ED<A)E,'EE/E8EEE9D><E>E	E
E	E		Ec                J    V ^8  d   QhR\         R\        R\        R,          /# )r   rowsuffixr   N)r   r   )r   s   "r   r   r   A   s%      s C C$J r   c                     V P                  R 4       FT  pVP                  R4      ;'       g    RpVP                  V4      '       g   K7  VP                  RRR7      ;'       g    Ru # 	  R# )spanidr.    Tr8   N)r6   r7   endswithr9   )rD   rE   rG   sids   &&  r   _grid_label_by_suffixrM   A   sY    V$hhtn""<<==D=199T9 % r   c                h    V ^8  d   QhR\         R\        \        \        \        3,          ,          /# )r   rD   r   )r   r&   r'   r   )r   s   "r   r   r   I   s'      3 4S#X#7 r   c                    . pR pV P                  R4       F9  pVP                  R4      ;'       g    RpVP                  R4      '       g   K7  Tp M	  V'       g   V# VP                  RRR7       F  pR pR pVP                  R4       F4  pVP                  R4      ;'       g    Rp	RV	9   d   TpK)  RV	9   g   K2  TpK6  	  V'       d"   VP                  R	R
7      P	                  R4      MR;'       g    Rp
V'       d   VP                  RR	R
7      MR;'       g    RpV'       g   K  VP                  RV
RV/4       K  	  V# )NrG   rH   r.   	_DataListF)	recursivelblDesignacaolblNomeIntervTrJ   :rI   roler+   )r6   r7   rK   r9   rstripr;   )rD   partiesdatalistrG   rL   sub	role_span	name_spanlbllidrU   r+   s   &           r   _parties_from_rowr^   I   s+   $&GHV$hhtn""<<$$H	 %
   5 9		<<'C''$-%%2C#%	 C'	 ( ?H	""".55c:RVVTV7@	""3d"3bOOR4NNFD&$78 : Nr   c                R    V ^8  d   QhR\         R\        \         \        3,          /# )r   htmlr   )r   r'   r   )r   s   "r   r   r   d   s&     @ @# @$sCx. @r   c                    \        V R 4      pVP                  R\        R7      p\        P	                  VP                  4       ;'       g    R4      pV'       d   \        VP                  ^4      4      M^ p. pVf	   RVRVRR/# VP                  R4       F  pVP                  R	4      '       d   K  VP                  R
4      p\        V4      ^8  d   K?  R\        VR4      R\        VR4      R\        VR4      R\        VR4      R\        VR4      R\        VR4      R\        VR4      R\        V4      /pVR,          '       g   K  VP                  V4       K  	  \        VP                  RR7      4      p	RVRVRV	/# )r)   table)rH   r.   totalrowshas_nextFtrthtdprocess_number_lblNProcesso
unorganica_lblUnOrganicaespecie_lblEspeciedata_entrada_lblDataEntradadata_distribuicao_lblDataDistribvalor	_lblValorobservacoes_lblObservacoesrW   (ctl00_ContentPlaceHolder1_Pager1_lnkNext)r   r4   GRID_IDCOUNT_REr   r9   r   groupr6   lenrM   r^   r;   bool)
r`   r>   rb   count_matchrc   rd   rf   cellsentryre   s
   &         r   parse_distribuicao_pager   d   sg   v&DIIg'I*E//$--/"7"7R8K)4C!!!$%!E!#D}j%@@nnT"774==D!u:>3BH/4DE,R?1"6GH!6r;L!M*2{;05FG(,	!
 !""KK% #& DII!KILMHUFD*h??r   c                ,   V ^8  d   QhR\         \        \        \        3,          ,          R\         \        \        \        3,          ,          R\        R\
        \        \        \        3,          \        \        \        3,          3,          R,          /# )r   rW   	companies	thresholdr   N)r&   r'   r   r   floattuple)r   s   "r   r   r      si      $sCx.!DcN#  4S>4S>)*T1	r   c                   V  F  pVP                  R4      ;'       g    RpV'       g   K(  RpV Fx  pVP                  R4      ;'       g    RpV'       g   K(  \        P                  ! VP                  4       VP                  4       4      R,          pVe   W^ ,          8  g   Ku  W3pKz  	  V'       g   K  V^ ,          V8  g   K  V^,          V3u # 	  R# )z(Return (company, matched_party) or None.r+   r.   N
legal_nameg      Y@)r7   r   token_set_ratiolower)	rW   r   r   partyr+   bestclegalscores	   &&&      r   _best_matchr      s     yy &&B48AEE,'--2E((u{{}EME|uAwz  4DGy(7E>!  r   c                0    V ^8  d   QhR\         R\        /# )r   name_lor   r   r|   )r   s   "r   r   r      s     ; ; ; ;r   c                    a  \         ;QJ d#    V 3R  l\         4       F  '       g   K   R# 	  R# ! V 3R  l\         4       4      # )c              3   ,   <"   T F	  qS9   x  K  	  R # 5ir    ).0indr   s   & r   	<genexpr>&_has_corp_indicator.<locals>.<genexpr>   s     :)9#g~)9s   TF)any_CORP_INDICATORS)r   s   fr   _has_corp_indicatorr      s.    3:)9:33:3:3:)9:::r   c                0    V ^8  d   QhR\         R\         /# )r   r   r   r   )r   s   "r   r   r      s      # # r   c                    V '       g   R# RP                  R \        P                  ! RV P                  4       4       4       4      # )u   Lowercase + strip diacritics. Makes "Segurança" match "Seguranca"
and "Securité" match "securite". Tribunals sometimes drop accents.r.   c              3   `   "   T F$  p\         P                  ! V4      R 8w  g   K   Vx  K&  	  R# 5i)MnN)unicodedatacategory)r   chs   & r   r   _normalize.<locals>.<genexpr>   s,      <r#t+ 	<s   .
.NFD)joinr   	normalizer   )r   s   &r   
_normalizer      s;     77  **5!'')<  r   c                <    V ^8  d   QhR\         R\         R\        /# )r   
party_namedistinctiver   r   )r   s   "r   r   r      s!      3  3  33  34  3r   c                >   V '       d	   V'       g   R# \        V 4      P                  R4      p\        V4      pVP                  V4      '       g   R# V\        V4      R pV'       d    V^ ,          P	                  4       '       d   R# \        V P                  4       4      # )u  Does `party_name` refer to the monitored company identified by
`distinctive`?

Three conditions:
  1. After diacritic normalization, `party_name` *starts with* `distinctive`
     (the distintivo — e.g. "SEGUNOR", "IGPS PROTEK", "CARLOS SILVA").
     Using startswith (not substring anywhere) rejects names like
     "Medicina Laboratorial Carlos Silva Torres, S.A." where the company
     label appears deep inside an unrelated company name.
  2. `party_name` contains a corporate-form indicator (LDA, S.A., Sucursal,
     Unipessoal, SGPS, …).
  3. The character right after the distintivo is a word boundary (space,
     comma, dash, quote, end-of-string). This protects companies whose
     distintivo is a common prefix (e.g. "FIR" must not match "FIRMINO").

Rejects homónimos humanos (individuals lack corp suffix) AND companies
that merely *contain* the distintivo as part of a longer phrase.
F "'N)r   lstripr:   r{   isalphar   r   )r   r   	name_norm	dist_normtails   &&   r   is_company_partyr      s{    & [:&--f5I;'I	**S^_%DQ!! z//122r   c                0    V ^8  d   QhR\         R\         /# )r   r   r   r   )r   s   "r   r   r      s     27 27# 27# 27r   c                   V '       g   R# T pR F2  pW!9   g   K  VP                  V^4      ^ ,          P                  4       p M	  VP                  4        Uu. uF.  q3P                  4       P                  R4      \        9  g   K,  VNK0  	  ppV'       g   R# \	        V4      ^8  d   RP                  VR,          4      p\	        VP                  RR4      4      \        8  d   V# \        ^\	        V4      ^,           4       F?  pRP                  VRV 4      p\	        VP                  RR4      4      \        8  g   K=  Vu # 	  R# V^ ,          p\	        V4      \        8  d   V# R# u upi )u  Reduce a legal_name to a short, distinctive search term for Distribuição's
substring filter.

Strategy: take the part before the first separator ("-", "–", ","), drop
corporate suffixes, then use the first token if it's long enough (>=6
chars). Otherwise use the first two tokens. Short term = robust against
spelling variations further down the name (e.g. Gardiennage vs Gradiennage).

Returns "" when the derived term has fewer than MIN_SEARCH_CHARS
non-whitespace chars — a too-short term (e.g. "J M" from "J M F L - …")
would substring-match every "João Miguel", "Jorge Mendes", etc., creating
tens of thousands of false positives. Callers must skip companies with an
empty search term (or the user must add a distinctive alias).

Examples:
  'SEGUNOR - SEGURANÇA PRIVADA, LDA'     -> 'SEGUNOR'
  'IGPS PROTEK INTERVENTION …'           -> 'IGPS PROTEK'
  'A & B, S.A.'                          -> 'A & B'
  'J M F L - SEGURANÇA …'                -> '' (too short/ambiguous — skip)
r.   z,.rI   :Nr   NN)z - u    – ,)	splitr8   upper_CORP_SUFFIXr{   r   replaceMIN_SEARCH_CHARSrange)r   headsepttokenstermntoks   &       r   short_search_termr      s-   * D$;::c1%a(..0D % SA)>l)RaaFS 6{axxr
#t||C$%)99Kq#f+/*A88F2AJ'D4<<R()-== +  )Cc(..36B6- Ts   )E'E'c                    V ^8  d   QhR\         R\         R\        R\        R\        \        \         \        3,          ,          R\         R,          R\        \        \         \        3,          ,          /# )	r   tribunal_valuetribunal_label	date_fromdate_tor   party_filterNr   r   r   r&   r'   r   )r   s   "r   r   r     su     \ \\\ \ 	\
 DcN#\ *\ 
$sCx.\r   c                $  "   . p\        4       ;_uu_4       GRj  xL
 p\        V\        4      G Rj  xL
 p\        VP                  4      p	/ V	C\
        T \        VP                  R4      \        VP                  R4      \        T;'       g    R\        R\        R/Cp
\        4       G Rj  xL
  \        V\        V
R\        /R7      G Rj  xL
 p^p. p \        VP                  4      pVP                  VR,          4       VR,          '       g   MV^28  d   \         P#                  R	W4       Ms\        VP                  4      p/ VCR
RRR/CpVP%                  \        R4       \        4       G Rj  xL
  \        V\        VR\        /R7      G Rj  xL
 pV^,          pK  RRR4      GRj  xL
  \         P'                  RWW5\)        X4      4       V EF  pVP+                  R4      ;'       g    . pV'       dQ   RpV F2  p\-        VP+                  R4      ;'       g    RV4      '       g   K0  Tp M	  V'       g   Kk  V^ ,          TppM*\/        VV\0        P2                  4      pV'       g   K  Vw  pp\5        VP+                  R4      4      ;'       g%    \5        VP+                  R4      4      ;'       g    TpTP7                  RVR,          RRRVR,          RTRVP+                  R4      RVP+                  R4      RVP+                  R4      ;'       g    RP9                  4       ;'       g    RRVR\:        P<                  ! VR R!7      R"R/
4       EK  	  V#  ELC EL. EL EL EL EL EL  + GRj  xL 
 '       g   i     EL; i5i)#z5Scrape one tribunal for a date range, return matches.N%d-%m-%Yr.   	PesquisarRefererdataheadersrd   re   z1distribuicao %s: pagination safety cap at page %d__EVENTTARGET(ctl00$ContentPlaceHolder1$Pager1$lnkNext__EVENTARGUMENTzUdistribuicao tribunal=%s range=%s..%s party=%r -> %d rows (total parsed across pages)rW   r+   rq   ro   
company_idrH   sourcedistribuicaori   tribunaljuizork   speciesrm   role_in_processrU   
date_filedraw_jsonF)ensure_asciiraw_html)r
   r   r2   r   r3   r5   F_DESDEstrftimeF_ATEF_PARTE
F_NENTRADAF_BTNr   r   r   extendloggerwarningpopinfor{   r7   r   r   r	   FUZZY_MATCH_THRESHOLDr$   r;   r8   jsondumps)r   r   r   r   r   r   matchesr<   initialvspayloadr=   pageall_rowsparsedvs_nextpager_payloadr   rW   matched_partypcompanyr   r    r   s   &&&&&&                   r   scrape_distribuicao_ranger     s     %'G~~~ --!',,/

Y''
37##J/\''R;
 nFCg	3?OPP)+,QVV4FOOF6N+*%%rzG *1662G!K!2M
 eT*.  -)UXIYZZAAIDI ~L KK_7#h-
 ))I&,,"
 !M#AEE&M$7$7RFF$%M  !&q\=UGUGY0N0NOANGU599%89:  eii78  	
 	gdm. %(8"9N<0599Y/!EIIf$5$;$;#B#B#D#L#LjDJJu5AD	
3 N Ne - 	P* !ZG ~~~s   NMNM3M!AM3M3,M$- M3M'B&M34M*5 M3M-M3%N0M01AN4
N?#N#N3NAN!N<ANN3,N!M3$M3'M3*M3-M30N3N	9M<:
N	N		Nc                    V ^8  d   QhR\         R\         R\        R\        \        \         \        3,          ,          R\        \        \         \        3,          ,          /# )r   r   r   dayr   r   r   )r   s   "r   r   r   |  sT       
 DcN#	
 
$sCx.r   c                 8   "   \        WW"V4      G R j  xL
 #  L5ir   )r   )r   r   r   r   s   &&&&r   scrape_distribuicao_for_dayr   |  s%      +)   s   c                    V ^8  d   QhR\         \        \        \        3,          ,          R\        \        \        \        \        3,          3,          /# )r   r   r   )r&   r'   r   r   )r   s   "r   r   r     s;      DcN#	#tCH~
r   c           	        / pV  F  p\        VP                  R4      ;'       g    R4      pV'       g   K1  \        V4      pWA9   d>   \        P	                  RWBP                  R4      W,          P                  R4      4       K  W!V&   K  	  V# )u  Pre-compute the normalised distintivo for each company once. Skips
companies whose name is too ambiguous for a safe distintivo (short_search_term
returns ''). Returns {normalised_distintivo: company_dict}.

If two companies collapse to the same distintivo (e.g. a parent and a
subsidiary with the same leading name), the first one wins — very rare
in the security-industry corpus but flagged by a warning.r   r.   z3duplicate distintivo %r for %s (already held by %s))r   r7   r   r   r   )r   r?   r   r!   keys   &    r   build_distintivos_mapr    s}     &(CaeeL177R8m:NNEUU<(#(,,|*D C  Jr   c                   V ^8  d   QhR\         \        \        3,          R\         \        \         \        \        3,          3,          R\        \        \         \        \        3,          \         \        \        3,          3,          ,          /# )r   rD   distintivos_mapr   )r'   r   r   r&   r   )r   s   "r   r   r     s_      	c3h#tCH~-. 
%S#XS#X.
/0r   c                t   . p\        4       pV P                  R4      ;'       g    . pV EF	  pVP                  R4      ;'       g    RpV'       g   K)  \        V4      P                  R4      pVP	                  4       p\        V4      '       g   Kf  VP                  4        F  w  rV
R,          V9   d   K  VP                  V	4      '       g   K.  V\        V	4      R pV'       d    V^ ,          P                  4       '       d   Kc  VP                  W34       VP                  V
R,          4        EK  	  EK  	  V# )u  Given one parsed process row, return (company, matching_party) tuples
for every monitored company whose distintivo matches some party in that
row. Normally 0 or 1 tuple; rarely >1 when co-réus are both monitored
(legitimate, stored as separate processes rows).rW   r+   r.   r   rH   N)setr7   r   r   r   r   itemsr:   r{   r   r;   add)rD   r  hitsseen_companiesrW   r   r+   r   r   r   r   r   s   &&          r   match_row_to_companiesr    s    9;D"uNggi &&BGyy &&Bt$++F3	**,"7++"1"7"7"9It}.''	22S^_-DQ))KK()wt}- #: & Kr   c                    V ^8  d   QhR\         R\         R\        R\        R\        \        \         \        3,          ,          /# )r   r   r   r   r   r   r   )r   s   "r   r   r     sN     Y YYY Y 	Y
 
$sCx.Yr   c                  "   . p\        4       ;_uu_4       GRj  xL
 p\        V\        4      G Rj  xL
 p\        VP                  4      p/ VC\
        V \        VP                  R4      \        VP                  R4      \        R\        R\        R/Cp\        4       G Rj  xL
  \        V\        VR\        /R7      G Rj  xL
 p	^p
Rp^ p \        V	P                  4      pV
^8X  d
   VR	,          pVR
,           F~  pWR&   \        VP!                  R4      4      ;'       g%    \        VP!                  R4      4      ;'       g    TVR&   \        VP!                  R4      4      VR&   VP#                  V4       K  	  VR,          '       g   MV
^d8  d   \$        P'                  RW4       RpMt\        V	P                  4      p/ VCRRRR/CpVP)                  \        R4       \        4       G Rj  xL
  \        V\        VR\        /R7      G Rj  xL
 p	V
^,          p
EKU  RRR4      GRj  xL
  X'       d@   \+        V4      V8  d0   \$        P-                  RYT\+        V4      TX'       d   RMR4       V# \$        P/                  RWV\+        V4      4       V#  EL ELm EL EL L L L~  + GRj  xL 
 '       g   i     L; i5i)u  Devolve **todas** as linhas distribuídas no tribunal, no intervalo dado.

Um POST por página do grid, sem filtro `parte` do lado do servidor — sempre
foi assim, porque o cruzamento por distintivo é local e o filtro do CITIUS é
demasiado solto. O que mudou é o fim da história: antes só saíam daqui as
linhas que davam match e o resto morria em memória, e como a fonte só serve
~180 dias isso era informação perdida para sempre. O cruzamento passou para
o `services/court_filings.py`, onde pode voltar a correr quantas vezes for
preciso.

Cada linha vem como o parser a deu — `process_number`, `especie`, `valor`,
`unorganica`, `observacoes`, datas e `parties` — mais o `tribunal`, que não
está na página porque é o que se escolheu na consulta.
Nr   r.   r   r   r   FTrc   rd   r   rq   ro   data_distribuicao_ddata_entrada_dre   z7distribuicao sweep %s: pagination safety cap at page %dr   r   r   z=distribuicao sweep %s %s..%s: guardadas %d de %d anunciadas%su    (tecto de páginas)z6distribuicao sweep tribunal=%s range=%s..%s: %d linhas)r
   r   r2   r   r3   r5   r   r   r   r   r   r   r   r   r   r$   r7   r;   r   r   r   r{   errorr   )r   r   r   r   rd   r<   r   r   r   r=   r   truncado	esperadosr   r   r   r   s   &&&&             r   "scrape_distribuicao_tribunal_sweepr    s    ( "$D~~~ --!',,/

Y''
37##J/R;
 nFCg	3?OPP	,QVV4Fqy #7O	$2j!"599-@#AB ! !%eii&?@! !  +,
 +9>9R*S&'E" ( *%%s{M"  *1662G!K!2M
 eT*.  -)UXIYZZAAIDg ~r SY*KwD	9&."B	
 K	 	DwD		
 KI - 	PH !Ze ~~~s   KJKJ/J A.J/"J## J/J&BJ/AJ/A#J/1J)2 J/J+J/#K.J-/K;A#K J/#J/&J/)J/+J/-K/K	5J86
K	K	K>   SACRLINCLDACORPSGPS	SOCIEDADE
UNIPESSOALCRL.INC.LDA.S.A.SOC.CORP.S.R.L.S.G.P.S.)z ldaz,ldau    ldªz ld.z s.az sa,z,saz s a z s a,
unipessoalsgpszs.g.p.ssucursalz suc.z suc 	sociedadez soc.z soc z crlzcrl.cooperativazs.r.lz s.l.z s.l z ltdz ltdagmbhz b.v.za.c.ez ace r   )6r   loggingrer   r   r   typingr   bs4r   r   	rapidfuzzr   
app.configr	   app.scrapers.baser
   r   r   r   r   	getLogger__name__r   r2   r5   r   r   r   r   r   rx   compile
IGNORECASEry   r   r$   rB   rM   r^   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r   r   r   <module>r7     s      	  #  "    
		8	$L6
62
.4
-
-::8"--H
**3
4
 6@>.  
  ; 3F27j\~*4BYr   