
    j;                    x   d Z ddlmZ ddlZddlZddlZddlZddlZddl	Z	ddl
Z
ddlZddlmZ ddlmZ ddlmZ ddlZdZdZe G d	 d
                      Zd9dZd:dZd;dZd<dZd=dZd>dZd=dZd?dZd@dAd"ZdBd#ZdCd%Z dDd'Z!dEd*Z"dFd,Z#dGd2Z$dFd3Z%dHd5Z&dId7Z'e(d8k    r e) e'                      dS )Jus   Budget Transfer Bot Argentina — Twitter/X MVP.

Dry-run by default. Posts only with --post and ENABLE_POSTING=1.
    )annotationsN)	dataclass)Path)Anyz@https://www.presupuestoabierto.gob.ar/api/v1/credito?format=jsoni  c                  P    e Zd ZU ded<   ded<   ded<   ded<   ded<   d	Zd
ed<   d	S )	Candidatestrtopicintscoretweetdict[str, Any]payloadsource_rowsNzlist[str] | Nonethread)__name__
__module____qualname____annotations__r        budget_transfer_bot.pyr   r      sV         JJJJJJJJJ#F######r   r   returnr	   c                 v    t          j        dd                                          } | st          d          | S )NPRESUPUESTO_ABIERTO_TOKEN z!Missing PRESUPUESTO_ABIERTO_TOKEN)osgetenvstrip
SystemExit)tokens    r   require_tokenr"   $   s<    I1266<<>>E ><===Lr   r!   r   r   list[dict[str, Any]]c                x   t          j        t          d|  ddd|d          }|j        dk    r=|j        d d                             | d          }t          d	|j         d
|           |                                }t          |t                    s$t          dt          |          j                   |S )NzBearer zapplication/json)AuthorizationzContent-TypeAccept<   )headersjsontimeout   i   z
[REDACTED]zPresupuesto Abierto API error z: zUnexpected API response: )requestspostAPI_URLstatus_codetextreplaceRuntimeErrorr)   
isinstancelisttyper   )r!   r   rbodydatas        r   api_postr9   +   s    "3E"3"3EWcuvv		 	 	A 	}vdsd|##E<88SAMSSTSSTTT6688DdD!! NLtDzz7JLLMMMKr   dt.datec                     t           j                            t          j        t          j        d                                                              S )N)hours)dtdatetimenowtimezone	timedeltadater   r   r   today_arrD   ;   s9    ;??2;r|"'='='=>>??DDFFFr   dc                *    |                                  S N)	isoformat)rE   s    r   date_strrI   ?   s    ;;==r   valuefloatc                0   t          | pd          }|dk    rd|dz  dd                    dd          S |dk    rA|dz  }|d	k    rd|d
d                    dd          S d|dd                    dd          S d|d
d                    dd          S )z?Format Presupuesto Abierto amount expressed in millions of ARS.r   i@B $z.2fz	 billones.,i  d   z,.0fz mil millones.1fz	 millones)rK   r1   )rJ   vns      r   fmt_millionsrT   C   s    ejqAI~~-1Y;----55c3???EzzI88,q,,,,44S#>>>'1''''//S999 q    ((c222r   numdenc                V    |r|dk    rdS | |z  dz  dd                     dd          S )Nr   zN/ArP   rQ   %rN   rO   r1   )rU   rV   s     r   pctrZ   P   sA     #((uCi#o$$$$,,S#666r   c                2    | d                     dd          S )NrQ   rN   rO   rY   )rJ   s    r   one_decimalr\   V   s    >>!!#s+++r   r0   c                >   d                     |                                           } t          |           t          k    r| S g d}|D ]7\  }}|                     ||          } t          |           t          k    r| c S 8| d d                                         dz   S )N ))zUniversidad de Buenos AiresUBA)zse registraron pagosu   registró pagos)zuniversidades nacionaleszuniv. nacionales)zHospitales universitarioszHosp. universitarios)u   crédito vigenteu   créd. vigente)zPresupuesto AbiertoPAi  u   …)joinsplitlenMAX_TWEET_LENr1   rstrip)r0   replacementsoldnews       r   shorten_tweetri   Z   s    88DJJLL!!D
4yyM!!  L !  S||C%%t99%%KKK &:&&r        @@min_total_millionsc           	     L   dt                      j        gddgddddddd	dd
dddgdddgd}t          | |          }|st          d          |D ]<}t	          |                    d          pd          |k    r|d         dd         c S =|d         d         dd         S )a1  Pick latest material university-payment day.

    Presupuesto Abierto can publish tiny adjustment rows after the last large payroll/payment day.
    For social output we want the latest day whose aggregated paid amount is material, not a $1M
    correction that would be technically true but useless.
    u   Últimos pagos universidadesimpacto_presupuestario_fechacredito_pagado	inciso_idequal5columnoperatorrJ   greater_than0parcial_desclike%universidades nacionales%descrs   ordertitle
ejercicioscolumnsfiltersr|   z&No university paid-transfer rows foundr   N
   )rD   yearr9   r2   rK   get)r!   rk   r   rowsrows        r   last_available_university_dayr   m   s     0zz'24DE"#FF'^cRR%6D`aa

 <fMMN
 
G E7##D ECDDD < <)**/a004FFF56ss;;;; G7123B377r   c                x   t                      j        }t          |           }dddddddddd|dd	d
ddg}t          | d|gg d|d          d         }t          | d|gg d|dddgd          }t          | d|gg d|dd
ddgz   d          }t          | d|gg dddddd	d
ddgd          }t	          d |D                       }t	          d |D                       }	|r|d         ni }
t	          d |D                       }|                    d          p|d                             d          pdd d          }t          |          d k    r|d!d          d"z   |d#d$         z   nd%}|d!d          d"z   |d#d$         z   }d&t          |d                    d'| d(t          |
                    dd                     d)t          ||	           d*	}t          d+| d,          t          d-t          ||	           d.          t          |	r9d/t          |           d0t          |	           d1t          ||	z  d2z             d3nd4          t          d5| d6t          |           d7          t          d8          g}t          d9d2t          |          ||d:||d d          ||d;|<          S )=Nro   rp   rq   rr   rn   ru   rv   rm   rw   rx   ry   u'   Universidades nacionales pagos del día)rm   rw   rn   ultima_actualizacion_fecha)r~   r   r   r   r   u    Top universidades pagos del día)rm   subparcial_descrn   rz   r{   r}   u(   Hospitales universitarios pagos del día)rm   actividad_descrn   r   r   z%Hospitales Universitarios%z(Universidades nacionales acumulado anual)rw   rn   credito_vigenter   c              3  ^   K   | ](}t          |                    d           pd          V  )dS rn   r   NrK   r   .0r6   s     r   	<genexpr>z&build_universidades.<locals>.<genexpr>   s<      JJaeAEE"2338q99JJJJJJr   c              3  ^   K   | ](}t          |                    d           pd          V  )dS )r   r   Nr   r   s     r   r   z&build_universidades.<locals>.<genexpr>   s<      JJaU155!2338q99JJJJJJr   c              3  ^   K   | ](}t          |                    d           pd          V  )dS r   r   r   s     r   r   z&build_universidades.<locals>.<genexpr>   s<      FFAE!%% 0116Q77FFFFFFr   r   r   r      /      s/dub   Universidades: tener presupuesto ≠ haber cobrado. En plena marcha, Presupuesto Abierto registra z pagados el z" a universidades nacionales. UBA: u   . ¿Qué significa ejecución u   ? Abro hilo 👇z&1/ Fuente: Presupuesto Abierto, corte zb. Montos nominales en pesos. El dato mide pagos registrados, no anuncios ni necesidades estimadas.uw   2/ Cómo leer el %: crédito vigente = plata autorizada; pagado = plata efectivamente registrada como pago. Ejecución uA    significa que se pagó ese porcentaje del crédito vigente 2026.z)3/ Universidades nacionales 2026: pagado u    sobre crédito vigente z?. Lectura simple: de cada $100 autorizados, figuran pagos por $rP   rN   uZ   3/ Universidades nacionales: crédito vigente informado en cero; no se calcula porcentaje.z!4/ Hospitales universitarios: el z figuran pagos por zp. Ojo: no todo pago universitario es hospitalario; salarios, funcionamiento y hospitales son partidas distintas.u   5/ Qué NO dice el dato: no mide inflación, suficiencia del presupuesto ni intencionalidad política. Sí muestra ritmo de pagos oficiales respecto del crédito vigente.universidades)dayr   )totaltop
hospitalesannual)r
   r   r   r   r   r   )rD   r   r   r9   sumr   rc   rT   rZ   ri   r\   r   )r!   r   r   r   r   r   hospr   annual_paid
annual_vigtop1	hosp_paidcutcut_dmday_dmr   r   s                    r   build_universidadesr      sj   ::?D
'
.
.CGcBB##NN1wQTUU!v@\]]	G U:fsss	   
 	
E 53fXXX-??@   C E;fuuu)9vXuvvww	   D e;ffff"#FF%6D`aa
	   F JJ6JJJJJKJJ6JJJJJJ 3q66bDFFFFFFFI99122gfQimmD`6a6agegiljlil
mC+.s88r>>S2Y_s1Q3x''uF2Y_s1Q3x'F	V9EeL\F]9^9^	V 	Vlr	V 	V,8BRTU9V9V,W,W	V 	V (+;
'C'C	V 	V 	V 
 	UV U U U	
 	
 	zk:66z z z	
 	
 	 | w[8Q8Q w wkw  yC  lD  lD w wLWXcdnXnorXrLsLsw w w w{	
 	

 	~ ~ ~<XaKbKb ~ ~ ~	
 	
 	R	
 	
%F. E""T**#CHDTZ[[   r   Candidate | Nonec                   t                      j        }t          t                      t          j        d          z
            }ddddddd	dd
d|dddddg}t          | d|gg d|d
dddddgd          }|sd S |d         }|d
         d d         }|dd         dz   |dd         z   }|                    d          pdd d         }t          |          dk    r|dd         dz   |dd         z   nd}	d| d|                    dd           d t          |                    dd                     d!|	 d"	}
t          d#          t          d$|	 d%          g}t          d&d't          |
          d(|id)|i|*          S )+N   )daysro   rp   rq   rr   rn   ru   rv   rm   greater_equal_thanr   rx   z%Aportes del Tesoro Nacional%zATN recientes)rm   r   ubicacion_geografica_descrn   r   rz   r{   r}   r   r   r   r   r   r   r   r   r   z*ATN: Presupuesto Abierto registra pago el z a r   z por uH   . Contexto: sigue la discusión por reparto de ATN a provincias. Corte: rN   u   1/ ATN = Aportes del Tesoro Nacional. Son transferencias discrecionales a provincias/municipios, separadas de coparticipación automática.z&2/ Fuente: Presupuesto Abierto, corte uI   . El monto publicado es crédito pagado registrado, no anuncio político.atnF   sincer   )r   )rD   r   rI   r>   rB   r9   r   rc   rT   ri   r   )r!   r   r   r   r   r6   r   r   r   r   r   r   s               r   	build_atnr      sJ   ::?DXZZ",B"7"7"7788EGcBB##NN1?S^cdd#Babb	G E f S  S  S;fMMZju{O|O|}   D  tQA
*
+CRC
0C2Y_s1Q3x'F55-..4"crc
:C+.s88r>>S2Y_s1Q3x''uF	JV 	J 	JFabg@h@h 	J 	JAEE"215566	J 	J AG	J 	J 	J 

 	  d  	e  	e  Qv  Q  Q  Q  	R  	RF UBe 4 4w6FPQ
[abbbbr   r
   c                D   |dk    rt          |           S |dk    r"t          |           }|st          d          |S |dk    rOt          |           g}t          |           }|r|                    |           t	          |d d          d         S t          |          )	Nr   r   zNo ATN candidate foundautoc                    | j         S rG   )r   )cs    r   <lambda>z pick_candidate.<locals>.<lambda>  s     r   T)keyreverser   )r   r   r2   appendsorted
ValueError)r!   r
   r   
candidatesr   s        r   pick_candidater      s    "5)))~~e 	97888 *%001
 	#c"""j&7&7FFFqII
U

r   pathsqlite3.Connectionc                X    t          j        |           }|                    d           |S )NzCREATE TABLE IF NOT EXISTS generated_posts (
        id TEXT PRIMARY KEY,
        created_at TEXT NOT NULL,
        topic TEXT NOT NULL,
        tweet TEXT NOT NULL,
        posted INTEGER NOT NULL DEFAULT 0,
        metadata TEXT NOT NULL
    ))sqlite3connectexecute)r   conns     r   
db_connectr     s3    ?4  DLL			 	 	 Kr   r   c                    t          j        |                     d                                                    d d         S )Nzutf-8   )hashlibsha256encode	hexdigest)r   s    r   post_idr     s3    >%,,w//00::<<SbSAAr   r   candpostedbooltuple[str, bool]c                   t          |j                  }	 |                     d|t          j                                                                        |j        |j        t          |          t          j
        |j        |j        dd          f           |                                  |dfS # t          j        $ r |dfcY S w xY w)Nz`INSERT INTO generated_posts(id, created_at, topic, tweet, posted, metadata) VALUES (?,?,?,?,?,?))r   r   F)ensure_asciiT)r   r   r   r>   r?   utcnowrH   r
   r   r)   dumpsr   r   commitr   IntegrityError)r   r   r   pids       r   save_candidater   #  s    
$*

Cn"+$$&&0022DJ
CPVKKY]Ycptp|  NR  N^  e_  e_  ns  Zt  Zt  Zt  u	
 	
 	
 	Dy!   Ezs   BB0 0CCc                L   t          j        d          dk    rt          d          t          j        dd| gddd          }	 t          j        |j                  }t          |	                    di           	                    d	          pd
          S # t          $ r Y d
S w xY w)NENABLE_POSTING1 ENABLE_POSTING must be 1 to postxurlr-   Tcheckr0   capture_outputr8   idr   r   r   r2   
subprocessrunr)   loadsstdoutr	   r   	Exception)r   procr8   s      r   	xurl_postr   0  s    	y!""c))=>>>>6651DY]^^^Dz$+&&488FB''++D117R888   rrs   AB 
B#"B#	parent_idc                N   t          j        d          dk    rt          d          t          j        dd| |gddd          }	 t          j        |j                  }t          |	                    di           	                    d	          pd
          S # t          $ r Y d
S w xY w)Nr   r   r   r   replyTr   r8   r   r   r   )r   r   r   r8   s       r   
xurl_replyr   ;  s    	y!""c))=>>>>67Iu=TPTeijjjDz$+&&488FB''++D117R888   rrs   AB 
B$#B$r   c                    t          j                    } |                     dg dd           |                     ddd           |                     d	dd
           |                     ddd           |                     dt          j        dd                     |                     dd           |                                 }t                      }t          ||j                  }t          |j
                  t          k    r$t          dt          |j
                             d}g }|j        ry|j        srt          |j
                  }|r|                    |           d}|j        r>|j        r7|}|j        D ]-}|s n(t%          ||          }|r|                    |           .t'          |j                  }	t+          |	||          \  }
}|
||||j        t          |j
                  |j
        |j        pg d |j        pg D             |j        d
}|j        r%t1          t/          j        |dd                     nt1          d|j         dt          |j
                   d| d|            t1          |j
                   |j        rOt1          d           t5          |j        d          D ]*\  }}t1          d | d!t          |           d"|            +d#S )$Nz--topic)r   r   r   r   )choicesdefaultz--post
store_truez1Actually post via xurl. Requires ENABLE_POSTING=1)actionhelpz--threadz4When posting, reply with the generated detail threadz	--dry-runzGenerate only; never postz--dbBOT_DB_PATHz./bot_state.sqlite3)r   z--json)r   zTweet too long: FTc                ,    g | ]}t          |          S r   )rc   )r   ts     r   
<listcomp>zmain.<locals>.<listcomp>q  s    <<<1A<<<r   )
r   rh   r   
posted_idsr
   rc   r   r   thread_lenssource   )r   indentztopic=z len=z new=z posted=z
THREAD:   [z] len=r^   r   )argparseArgumentParseradd_argumentr   r   
parse_argsr"   r   r
   rc   r   rd   r2   r-   dry_runr   r   r   r   r   dbr   r   r)   printr   	enumerate)apargsr!   r   r   r   r   reply_tor   r   r   is_newoutir   s                  r   mainr  F  s   		 	"	"BOOI'G'G'GQWOXXXOOH\8kOlllOOJ|:pOqqqOOK;VOWWWOOFBIm=R$S$SOTTTOOH\O222==??DOOE%,,D
4:&&?c$*oo??@@@FJy 0 0dj))	 	)i(((; 	04; 	0 H 0 0 E%h66 0%%h///dgD tV44KC  4:+#<<):<<<" C y 1dj5;;;<<<<VtzVVDJVVfVVfVVWWWdj; 	1+!$+q11 1 11/!//3q66//A//00001r   __main__)r   r	   )r!   r	   r   r   r   r#   )r   r:   )rE   r:   r   r	   )rJ   rK   r   r	   )rU   rK   rV   rK   r   r	   )r0   r	   r   r	   )rj   )r!   r	   rk   rK   r   r	   )r!   r	   r   r   )r!   r	   r   r   )r!   r	   r
   r	   r   r   )r   r	   r   r   )r   r	   r   r	   )r   r   r   r   r   r   r   r   )r   r	   r   r	   r   r	   )r   r   )*__doc__
__future__r   r  r?   r>   r   r)   r   r   r   sysdataclassesr   pathlibr   typingr   r,   r.   rd   r   r"   r9   rD   rI   rT   rZ   r\   ri   r   r   r   r   r   r   r   r   r   r  r   r    r   r   r   <module>r     s    # " " " " "        				      



 ! ! ! ! ! !             
L $ $ $ $ $ $ $ $       G G G G   
3 
3 
3 
37 7 7 7, , , ,' ' ' '&8 8 8 8 86P P P Pfc c c cD   &   B B B B
 
 
 
      7 7 7 7t z
*TTVV

 r   