+
    mej3!                         R t ^ RIt^ RIt^ RIHt ^ RIHt ^ RIHt ^ RI	H
t
 ^ RIHt ^ RIHtHtHt ^ RIHt ^ R	IHt ^ R
IHt ^ RIHtHt ]P4                  ! ]4      tR R ltR R ltR R ltRR R llt R# )zOn-demand company analysis.

Given a NIF (+ optional name), ensure a `companies` row exists (creating one
with monitoring_type='analysis' if missing), run scrapers synchronously for
that single company, then compute metrics + risk score.
N)Any)text)AsyncSession)AsyncSessionLocal)scrape_cire_for_nif)discover_tribunal_codesscrape_distribuicao_rangeshort_search_term)
lookup_nif)upsert_process)is_valid_nif)compute_riskupdate_company_riskc                    V ^8  d   QhR\         R\        R\        R,          R\        \        \        \        3,          \
        3,          /# )   sessionnif	name_hintNreturn)r   strtupledictr   bool)formats   "app/services/analysis.py__annotate__r      sB     . .. #.03d
.
4S>4 .    c                R  "   V P                  \        R4      RV/4      G Rj  xL
 P                  4       pV'       d)   RV^ ,          RV^,          RV^,          RV^,          /R3# \        V4      G Rj  xL
 pT;'       g    VP	                  R4      ;'       g    R	V 2pVP	                  R
4      R8X  d   VP	                  R4      MRpT P                  \        R4      RTRTRVP	                  R4      RVP	                  R4      RVP	                  R4      RV'       d   \
        P                  ! V4      MRR\        V4      /4      G Rj  xL
 P                  4       pV P                  4       G Rj  xL
  RV^ ,          RV^,          RV^,          RV^,          /R3#  ELz EL, LX L45i)z-Return ({id, nif, legal_name}, created_flag).zOSELECT id::text, nif, legal_name, monitoring_type FROM companies WHERE nif = :nnNidr   
legal_namemonitoring_typeFzNIF sourceptdatarawa  
                INSERT INTO companies
                    (nif, legal_name, cae, address, status, ptdata_payload,
                     ptdata_fetched_at, monitored, monitoring_type)
                VALUES (:nif, :ln, :cae, :addr, :st, CAST(:pl AS JSONB),
                        CASE WHEN :pt THEN now() ELSE NULL END, TRUE, 'analysis')
                RETURNING id::text, nif, legal_name, monitoring_type
                lncaeaddraddressststatusplptT)	executer   firstr
   getjsondumpsr   commit)r   r   r   rowdatalegalpayloadcreateds   &&&     r   _ensure_companyr8      s    
 oobc#J
 	
 eg  3q65#a&,A@QSVWXSYZ
 	

 C D??,/??T#<E!%(!3x!?dhhuoTGoo	 setxx+dhhx(Wdjj)$d7m
 	
* eg- . ..
	wqz5'!*lGAJHY[bcd[ef M	
 !	
, sW   !F'FF'6F'4F 5F'F'B/F'
F#%F'0F%1-F' F'#F'%F'c                J    V ^8  d   QhR\         \        \        3,          RR/# )r   companyr   N)r   r   r   )r   s   "r   r   r   N   s&     3[ 3[d38n 3[ 3[r   c                
  a aaaaaa"    \        S R,          S R,          RR7      G Rj  xL
 pV'       da   \        4       ;_uu_4       GRj  xL
 pV F  p\        W#4      G Rj  xL
  K  	  VP                  4       G Rj  xL
  RRR4      GRj  xL
  M  ^ RIp^ RIH	p \        4       G Rj  xL
 p\        S R,          4      oS'       g   R# TP                  VP                  4      o^ R	IHpHp	 VP#                  4       V	! RR7      ,
          oVP#                  4       o. oR
 VV VVVV3R lloVP$                  ! V3R lV 4       !  G Rj  xL
  S'       db   \        4       ;_uu_4       GRj  xL
 pS F  p
\        W*4      G Rj  xL
  K  	  VP                  4       G Rj  xL
  RRR4      GRj  xL
  R# R#  EL ELu EL` ELG EL:  + GRj  xL 
 '       g   i     ELP; i  \         d*   p\
        P                  RS R,          T4        Rp?ELRp?ii ; i ELr L L L L Lv  + GRj  xL 
 '       g   i     R# ; i  \         d*   p\
        P                  RS R,          T4        Rp?R# Rp?ii ; i5i)u?   Run CIRE + Distribuição for a single company, ingest into DB.r   r   todos)daysNz#analysis CIRE failed for nif=%s: %s)settingsr    )date	timedeltac                J    V ^8  d   QhR\         \        \        3,          RR/# )r   tr   N)r   r   )r   s   "r   r   (_scrape_and_ingest.<locals>.__annotate__l   s"     	 	DcN 	t 	r   c                 |  <"   S;_uu_4       GR j  xL
   \        V R,          V R,          SSS.SR7      G R j  xL
 pSP                  V4       R R R 4      GR j  xL
  R #  LP L)  \         d)   p\        P	                  RT R,          T4        R p?LDR p?ii ; i L@  + GR j  xL 
 '       g   i     R # ; i5i)Nvaluelabel)party_filterz$analysis Dist tribunal=%s failed: %s)r   extend	Exceptionloggerwarning)	rB   meall_matchesr:   	date_fromdate_tosearchsems	   &  r   worker"_scrape_and_ingest.<locals>.workerl   s     ss	7'
AgJ	7 	 A  &&q) ss
 ! NN>'
A  ssss}   B<A#B<B#A'A%A'B<BB<%A''B2BBBBB<B9	%B(&
B9	1B9	3	B<c              3   4   <"   T F  pS! V4      x  K  	  R # 5iN ).0rB   rS   s   & r   	<genexpr>%_scrape_and_ingest.<locals>.<genexpr>y   s     <)Qvayy)s   u-   analysis Distribuição failed for nif=%s: %si!  )r   r   r   r2   rI   rJ   rK   asyncio
app.configr>   r   r	   	SemaphoreSCRAPE_CONCURRENCYdatetimer?   r@   todaygather)r:   matchessr3   rM   r[   	_settings	tribunalsr?   r@   rrN   rO   rP   rQ   rR   rS   s   f          @@@@@@r   _scrape_and_ingestrg   N   s    Q+GENGDMPWXX(***a"C(000 #hhj   +** %[4133	"7<#89	 < <=,JJL9'#::	**,,.	 	 nn<)<===(***a$A(... %hhj   +** S Y*0  +***  Q<genaPPQ 42 	> +.  +***  [FPUXYZZ[s  JG+ F?G+ G+ GG+ G%G
&GGGG+ GG+ JI 0H"1I JA>I H%I I 2H'3I 6H/
H)
H/&H+'H/+I 6H-7I ;J?G+ G+ GGG+ G(	G
G(	 G(	"G+ &J(G+ +H6HJHJ"I %I 'I )H/+H/-I /I		5H86
I		I		I J	I J I;5J;J  Jc                ^    V ^8  d   QhR\         R\        R\        \        \        3,          /# )r   r   
company_idr   )r   r   r   r   )r   s   "r   r   r      s,     4 44'*4	#s(^4r   c                 j  "   V P                  \        R 4      RV/4      G Rj  xL
 P                  4       pV P                  \        R4      RV/4      G Rj  xL
 P                  4       pV Uu/ uF  qD^ ,          V^,          bK  	  ppV P                  \        R4      RV/4      G Rj  xL
 P                  4       pV Uu. uF   pRV^ ,          R\	        V^,          4      /NK"  	  pp\        W4      G Rj  xL
 w  rRV^ ,          RV^,          R	V^,          R
VRVRVRV	/#  L Lu upi  Lu upi  L;5i)aK  
                SELECT
                  count(*) AS total,
                  count(*) FILTER (WHERE date_filed >= (now() - interval '30 days')::date) AS d30,
                  count(*) FILTER (WHERE date_filed >= (now() - interval '180 days')::date) AS d180
                FROM processes WHERE company_id = :cid
                cidNzNSELECT source, count(*) FROM processes WHERE company_id = :cid GROUP BY sourceaY  
                SELECT to_char(date_trunc('month', date_filed), 'YYYY-MM') AS month,
                       count(*) AS c
                FROM processes
                WHERE company_id = :cid
                  AND date_filed >= (now() - interval '24 months')::date
                GROUP BY month
                ORDER BY month
                monthcounttotal_processeslast_30dlast_6m	by_sourcetimeline
risk_scorerisk_reasons)r-   r   r.   allintr   )
r   ri   totalsby_source_rowsrf   rq   timeline_rowsrr   rs   rt   s
   &&        r   _compute_metricsrz      s\     oo J
 	
 eg  oo` J	
 	
 
ce  &44^1qt^I4oo
 J
 	
 
ce   >KK]!A$QqT3]HK%1'%FFJ6!9F1I6!9YHj Q	
	
 5	
 LFs]   !D3D!2D3D#D3-D%"D3(D*)D3?&D,%D35D16,D3#D3%D3,D3c                l    V ^8  d   QhR\         R\         R,          R\        \         \        3,          /# )r   r   r   Nr   )r   r   r   )r   s   "r   r   r      s-     7 7C 7C$J 7$sCx. 7r   c                 @  "   \        V 4      '       g   \        R 4      h\        4       ;_uu_4       GRj  xL
 p\        W V4      G Rj  xL
 w  r4RRR4      GRj  xL
  \        4       ;_uu_4       GRj  xL
 pVP	                  \        R4      R\        P                  ! RV RXR,          /4      /4      G Rj  xL
 P                  4       ^ ,          pVP                  4       G Rj  xL
  RRR4      GRj  xL
   \        X4      G Rj  xL
  \        4       ;_uu_4       GRj  xL
 p\        W#R,          4      G Rj  xL
 p\        W#R,          4      G Rj  xL
  VP	                  \        R4      RXRVR	,          /4      G Rj  xL
  VP                  4       G Rj  xL
  RRR4      GRj  xL
  RTR,          RTR,          RTR,          RTR,          RXRX/#  EL EL EL  + GRj  xL 
 '       g   i     EL; i EL ELN EL$ EL  + GRj  xL 
 '       g   i     EL/; i EL# EL L L L L L  + GRj  xL 
 '       g   i     L; i  \         d   p\        P                  R
T4       \        4       ;_uu_4       GRj  xL 
 pTP	                  \        R4      RXR\!        T4      R,          /4      G Rj  xL 
  TP                  4       G Rj  xL 
  RRR4      GRj  xL 
  h   + GRj  xL 
 '       g   i     h ; iRp?ii ; i5i)zinvalid NIFNz
                    INSERT INTO scraping_logs (source, started_at, status, params)
                    VALUES ('analysis', now(), 'running',
                            CAST(:p AS JSONB))
                    RETURNING id::text
                    pr   ri   r   zZUPDATE scraping_logs SET finished_at = now(), status = 'ok', rows_seen = :n WHERE id = :idr   rn   zanalysis failed: %szYUPDATE scraping_logs SET finished_at = now(), status = 'error', error = :e WHERE id = :idrM   :Ni  Nr    r!   metricsr7   )r   
ValueErrorr   r8   r-   r   r0   r1   r.   r2   rg   rz   r   rI   rJ   	exceptionr   )r   r   r   r:   r7   log_idr~   rM   s   &&      r   run_analysisr      s    '' """g!0y!II #" !"""g// djj%lGDM!JKL
 
 %'! nn #"  )))$&&&',WdmDDG%gt}===//p vsG,=$>?	   ..""" '&. 	gdmwu~gl+7#4577 Y #I #""" #
 	 #"""" 	*&D= # '&&&  
.2$&&&'//o vsCF5M2	   ..""" '&& 	 '&& 	
s  3LGLG(	G"
G(LG%L6H7L:A H:H;,H'H(H,L7H8L=I H.I &H1'I *H> H4H>H6-H>H8	H> H:!H>%I 0H<1I 5+L"G(%L(H	.G1/
H	:H	<
LHHLH+	H
H+	#H+	%	L.I 1I 4H>6H>8H>:H><I >I	I
I	I	I LI L%,LJ
L3K:	K

K:"K%
#K:(L3K64L:L L
LLLLLrV   )!__doc__r0   loggingtypingr   
sqlalchemyr   sqlalchemy.ext.asyncior   app.dbr   app.scrapers.cirer   app.scrapers.distribuicaor   r   r	   app.services.nif_lookupr
   app.services.ingestr   app.services.nifr   app.services.riskr   r   	getLogger__name__rJ   r8   rg   rz   r   rW   r   r   <module>r      sf        / $ 1 
 / . ) ?			8	$.b3[l4n7 7r   