§
    	Êjð4  ã                  ó  — U 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	m
Z
 ddlmZ ddlmZ  ej        e¦  «        Z e
dd¬¦  «        Zd	ed<   d
ZdZdddœZdZddddœZdZg e¢d‘R Zd(d„Zd)d „Z G d!„ d"¦  «        Zd#Zd*d'„ZdS )+a  Request log: one SQLite row per ``/v1/*`` request, for the dashboard and ``/admin/stats``.

Rows are written by ``api.middleware.RequestLogMiddleware``. Code deeper in the
stack (the gateway) adds to the in-flight row through ``current_record``. Async
image/video submits return 200 straight away, so the task's outcome is written back
onto the submit row when it finishes (``finish_task``) and counts as an error. All
database work runs in a worker thread; a failed write is logged and dropped,
never surfaced to the client.
é    )ÚannotationsN)Ú
ContextVar)ÚPath)ÚAnyÚcurrent_record)Údefaultz!ContextVar[dict[str, Any] | None])ÚtsÚmethodÚpathÚmodelÚkey_idÚkey_nameÚ
account_idÚstatus_codeÚ
latency_msÚstreamÚpollÚerrorÚ	client_ipÚ
user_agentÚtask_ida#  
CREATE TABLE IF NOT EXISTS requests (
    id          INTEGER PRIMARY KEY AUTOINCREMENT,
    ts          REAL    NOT NULL,
    method      TEXT    NOT NULL,
    path        TEXT    NOT NULL,
    model       TEXT,
    key_id      TEXT,
    key_name    TEXT,
    account_id  TEXT,
    status_code INTEGER NOT NULL,
    latency_ms  INTEGER NOT NULL,
    stream      INTEGER NOT NULL DEFAULT 0,
    poll        INTEGER NOT NULL DEFAULT 0,
    error       TEXT,
    client_ip   TEXT,
    user_agent  TEXT,
    task_id     TEXT
);
CREATE INDEX IF NOT EXISTS idx_requests_ts ON requests (ts);
CREATE INDEX IF NOT EXISTS idx_requests_key ON requests (key_id, ts);
CREATE INDEX IF NOT EXISTS idx_requests_account ON requests (account_id, ts);
CREATE INDEX IF NOT EXISTS idx_requests_task ON requests (task_id);
ÚTEXTÚINTEGER)Útask_statusÚtask_msz.(status_code >= 400 OR task_status = 'failed'))i  é<   )é€Q i  )i€:	 i0*  )Ú1hÚ24hÚ7d)Ú2xxÚ3xxÚ4xxÚ5xxÚfailedr   ÚstrÚreturnÚNonec                óJ   — t                                ¦   «         }|�| |d<   dS dS )zORecord the account serving the current request (the last one wins on failover).Nr   )r   Úget)r   Úrecords     ú9C:\Projects\Muse2API\src\muse2api\services\request_log.pyÚnote_accountr-   F   s1   € å×ÒÑ!Ô!€FØÐØ)ˆˆ|ÑÐÐð Ðó    Úsorted_valuesú	list[int]ÚqÚfloatú
int | Nonec                óÀ   — | sd S t          dt          t          | ¦  «        dz
  t          j        |t          | ¦  «        z  ¦  «        dz
  ¦  «        ¦  «        }| |         S )Nr   é   )ÚmaxÚminÚlenÚmathÚceil)r/   r1   Úidxs      r,   Ú_percentiler<   M   s]   € Øð Øˆtå
ˆa••S˜Ñ'Ô'¨!Ñ+­T¬Y°q½3¸}Ñ;MÔ;MÑ7MÑ-NÔ-NÐQRÑ-RÑSÔSÑ
TÔ
T€CØ˜ÔÐr.   c            	      ó„   — e Zd Zd-d„Zd.d„Zd„ Zd/d	„Zd/d
„Zd0d„Z	 d1d2d„Z	d3d„Z
d4d„Zdddddddd d!œd5d)„Zd6d+„Zd7d,„ZdS )8Ú
RequestLogr   r   r'   r(   c                ó`   — || _         d | _        t          j        ¦   «         | _        i | _        d S ©N)r   Ú_connÚ	threadingÚLockÚ_lockÚ_early)Úselfr   s     r,   Ú__init__zRequestLog.__init__V   s*   € ØˆŒ	Ø04ˆŒ
å”^Ñ%Ô%ˆŒ
à(*ˆŒˆˆr.   úsqlite3.Connectionc                ó
  — | j         €ö| j        j                             dd¬¦  «         t	          j        | j        dd ¬¦  «        }t          j        |_        |                     d¦  «         |                     d¦  «         | 	                    t          ¦  «         d„ |                     d¦  «        D ¦   «         }t                               ¦   «         D ]$\  }}||vr|                     d	|› d
|› �¦  «         Œ%|| _         | j         S )NT)ÚparentsÚexist_okF)Úcheck_same_threadÚisolation_levelzPRAGMA journal_mode=WALzPRAGMA synchronous=NORMALc                ó   — h | ]
}|d          ’ŒS )r5   © ©Ú.0Úrs     r,   ú	<setcomp>z!RequestLog._db.<locals>.<setcomp>f   s   € ÐNÐNÐN˜Q�A�a”DÐNÐNÐNr.   zPRAGMA table_info(requests)z ALTER TABLE requests ADD COLUMN ú )rA   r   ÚparentÚmkdirÚsqlite3ÚconnectÚRowÚrow_factoryÚexecuteÚexecutescriptÚ_SCHEMAÚ_ADDED_COLUMNSÚitems)rF   ÚconnÚhaveÚcolÚkinds        r,   Ú_dbzRequestLog._db^   s  € ØŒ:ÐØŒIÔ×"Ò"¨4¸$Ð"Ñ?Ô?Ð?Ý”? 4¤9ÀÐW[Ð\Ñ\Ô\ˆDÝ&œ{ˆDÔØ�LŠLÐ2Ñ3Ô3Ð3Ø�LŠLÐ4Ñ5Ô5Ð5Ø×Ò�wÑ'Ô'Ð'ØNÐN $§,¢,Ð/LÑ"MÔ"MÐNÑNÔNˆDÝ+×1Ò1Ñ3Ô3ð Rð R‘	��TØ˜d�?�?Ø—L’LÐ!PÀCÐ!PÐ!PÈ$Ð!PÐ!PÑQÔQÐQøØˆDŒJØŒzÐr.   c                óz   — | j         5   ||                      ¦   «         g|¢R Ž cd d d ¦  «         S # 1 swxY w Y   d S r@   )rD   rd   )rF   ÚfnÚargss      r,   Ú_callzRequestLog._callm   s‹   € ØŒZð 	)ð 	)Ø�2�d—h’h‘j”jÐ( 4Ð(Ð(Ð(ð	)ð 	)ð 	)ð 	)ñ 	)ô 	)ð 	)ð 	)ð 	)ð 	)ð 	)ð 	)øøøð 	)ð 	)ð 	)ð 	)ð 	)ð 	)s   ˆ0°4·4c              ƒ  ó°   K  — 	 t          j        | j        d„ ¦  «        ƒ d {V —† d S # t          $ r$ t                               d| j        ¦  «         Y d S w xY w)Nc                ó   — d S r@   rO   )Údbs    r,   ú<lambda>z!RequestLog.open.<locals>.<lambda>s   s   € ¸4€ r.   zfailed to open request log %s)ÚasyncioÚ	to_threadrh   Ú	ExceptionÚlogÚ	exceptionr   ©rF   s    r,   ÚopenzRequestLog.openq   sz   è è € ð	FÝÔ# D¤J°°Ñ@Ô@Ð@Ð@Ð@Ð@Ð@Ð@Ð@Ð@Ð@øÝð 	Fð 	Fð 	Få�MŠMÐ9¸4¼9ÑEÔEÐEÐEÐEÐEð	Føøøs   „!' §*AÁAc              ƒ  óL   ‡ K  — dˆ fd„}t          j        |¦  «        ƒ d {V —† d S )Nr'   r(   c                 ó–   •— ‰ j         5  ‰ j        � ‰ j                             ¦   «          d ‰ _        d d d ¦  «         d S # 1 swxY w Y   d S r@   )rD   rA   Úcloserr   s   €r,   Ú_closez RequestLog.close.<locals>._closey   s’   ø€ Ø”ð &ð &Ø”:Ð)Ø”J×$Ò$Ñ&Ô&Ð&Ø!%�D”Jð&ð &ð &ñ &ô &ð &ð &ð &ð &ð &ð &ð &øøøð &ð &ð &ð &ð &ð &s   ‰(>¾AÁA©r'   r(   )rm   rn   )rF   rw   s   ` r,   rv   zRequestLog.closex   sP   øè è € ð	&ð 	&ð 	&ð 	&ð 	&ð 	&õ Ô Ñ'Ô'Ð'Ð'Ð'Ð'Ð'Ð'Ð'Ð'Ð'r.   r+   údict[str, Any]c           	   ƒ  óÜ  ‡ ‡‡‡‡K  — dddœ‰¥Št          ˆfd„t          D ¦   «         ¦  «        Šdd                     t          ¦  «        › dd                     dt          t          ¦  «        z  ¦  «        › d�Š‰d	         rd n‰                     d
¦  «        Šdˆˆ ˆˆfd„}	 t          j        ‰ j        |¦  «        ƒ d {V —† d S # t          $ r t           
                    d¦  «         Y d S w xY w)Nr   )r   r   c              3  óB   •K  — | ]}‰                      |¦  «        V — Œd S r@   )r*   )rQ   Úcr+   s     €r,   ú	<genexpr>z!RequestLog.add.<locals>.<genexpr>„   s-   øè è € Ð3Ð3 a�F—J’J˜q‘M”MÐ3Ð3Ð3Ð3Ð3Ð3r.   zINSERT INTO requests (z, z
) VALUES (ú?ú)r   r   rk   rH   r'   r(   c                ó°   •— |                       ‰‰¦  «         ‰r:‰j                             ‰d ¦  «        x}r|                       t          |¦  «         d S d S d S r@   )r[   rE   ÚpopÚ_FINISH_SQL)rk   ÚearlyÚrowrF   Úsqlr   s     €€€€r,   ÚrunzRequestLog.add.<locals>.runˆ   sk   ø€ Ø�JŠJ�s˜CÑ Ô Ð ð ð / T¤[§_¢_°W¸dÑ%CÔ%CÐC˜Eð /Ø—
’
�;¨Ñ.Ô.Ð.Ð.Ð.ð/ð /ð /ð /r.   zfailed to write request log row©rk   rH   r'   r(   )ÚtupleÚCOLUMNSÚjoinr8   r*   rm   rn   rh   ro   rp   rq   )rF   r+   r†   r„   r…   r   s   `` @@@r,   ÚaddzRequestLog.add‚   s2  øøøøøè è € Ø qÐ3Ð3¨FÐ3ˆÝÐ3Ð3Ð3Ð3­7Ð3Ñ3Ô3Ñ3Ô3ˆØe t§y¢yµÑ'9Ô'9ÐeÐeÀTÇYÂYÈsÕUXÕY`ÑUaÔUaÑOaÑEbÔEbÐeÐeÐeˆØ  œ.ÐC�$�$¨f¯jªj¸Ñ.CÔ.Cˆð	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð	=ÝÔ# D¤J°Ñ4Ô4Ð4Ð4Ð4Ð4Ð4Ð4Ð4Ð4Ð4øÝð 	=ð 	=ð 	=Ý�MŠMÐ;Ñ<Ô<Ð<Ð<Ð<Ð<ð	=øøøs   Â! C Ã$C+Ã*C+Nr   r&   ÚstatusÚfinished_atr2   r   ú
str | Noner   c              ƒ  óÈ   ‡ ‡‡K  — ||||‰fŠdˆˆ ˆfd„}	 t          j        ‰ j        |¦  «        ƒ d{V —† dS # t          $ r t                               d‰¦  «         Y dS w xY w)	zAWrite an async task's outcome onto the request that submitted it.rk   rH   r'   r(   c                ó6  •— |                       t          ‰¦  «        j        su‰‰j        ‰<   t	          ‰j        ¦  «        dk    rU‰j                             t          t          ‰j        ¦  «        ¦  «        ¦  «         t	          ‰j        ¦  «        dk    °Qd S d S d S )NéÈ   )r[   r‚   ÚrowcountrE   r8   r�   ÚnextÚiter)rk   rg   rF   r   s    €€€r,   r†   z#RequestLog.finish_task.<locals>.run™   s”   ø€ Ø—:’:�k¨4Ñ0Ô0Ô9ð =ð (,�”˜GÑ$Ý˜$œ+Ñ&Ô&¨Ò,Ð,Ø”K—O’O¥D­¨d¬kÑ):Ô):Ñ$;Ô$;Ñ<Ô<Ð<õ ˜$œ+Ñ&Ô&¨Ò,Ð,Ð,Ð,ð	=ð =ð -Ð,r.   Nz#failed to record outcome of task %sr‡   )rm   rn   rh   ro   rp   rq   )rF   r   rŒ   r�   r   r   r†   rg   s   ``     @r,   Úfinish_taskzRequestLog.finish_task”   s³   øøøè è € ð ˜ U¨J¸Ð@ˆð	=ð 	=ð 	=ð 	=ð 	=ð 	=ð 	=ð 	=ð	JÝÔ# D¤J°Ñ4Ô4Ð4Ð4Ð4Ð4Ð4Ð4Ð4Ð4Ð4øÝð 	Jð 	Jð 	JÝ�MŠMÐ?ÀÑIÔIÐIÐIÐIÐIð	Jøøøs   – 8 ¸%A!Á A!Útasksú	list[Any]c              ƒ  óâ   ‡‡K  — d„ |D ¦   «         Š‰sdS t           dz   Š	 t          j        | j        ˆˆfd„¦  «        ƒ d{V —† dS # t          $ r t
                               d¦  «         Y dS w xY w)zFFill in outcomes for submit rows logged before outcomes were recorded.c                ó†   — g | ]>}|j         ¯	|j        j        |j        |j        pi                      d ¦  «        d|j        f‘Œ?S )ÚmessageN)ÚfinishedrŒ   ÚvalueÚ
updated_atr   r*   Úid)rQ   Úts     r,   ú
<listcomp>z-RequestLog.backfill_tasks.<locals>.<listcomp>¨   sW   € ð .ð .ð .Ø !¤*ð.�”” ¤°´°¸2×/BÒ/BÀ9Ñ/MÔ/MÈtÐUVÔUYÐZð .ð .ð .r.   Nz AND task_status IS NULLc                ó0   •— |                       ‰‰¦  «        S r@   )Úexecutemany)rk   Úrowsr…   s    €€r,   rl   z+RequestLog.backfill_tasks.<locals>.<lambda>®   s   ø€ ¸2¿>º>È#ÈtÑ;TÔ;T€ r.   z failed to backfill task outcomes)r‚   rm   rn   rh   ro   rp   rq   )rF   r–   r£   r…   s     @@r,   Úbackfill_taskszRequestLog.backfill_tasks¦   s´   øøè è € ð.ð .Øð.ñ .ô .ˆàð 	ØˆFÝÐ6Ñ6ˆð	>ÝÔ# D¤JÐ0TÐ0TÐ0TÐ0TÐ0TÑUÔUÐUÐUÐUÐUÐUÐUÐUÐUÐUøÝð 	>ð 	>ð 	>Ý�MŠMÐ<Ñ=Ô=Ð=Ð=Ð=Ð=ð	>øøøs    $A Á$A.Á-A.Úretention_daysÚintc              ƒ  ó8  ‡K  — t          j         ¦   «         |dz  z
  Š	 t          j        | j        ˆfd„¦  «        ƒ d {V —†}n+# t          $ r t
                               d¦  «         Y dS w xY w|j        r!t
                               d|j        |¦  «         |j        S )Nr   c                ó2   •— |                       d‰f¦  «        S )Nz!DELETE FROM requests WHERE ts < ?)r[   )rk   Úcutoffs    €r,   rl   z"RequestLog.prune.<locals>.<lambda>¶   s   ø€  r§z¢zÐ2UÐX^ÐW`Ñ'aÔ'a€ r.   zfailed to prune request logr   z-pruned %d request log rows older than %s days)	Útimerm   rn   rh   ro   rp   rq   r’   Úinfo)rF   r¥   Úcurr©   s      @r,   ÚprunezRequestLog.prune²   sÐ   øè è € Ý”‘”˜~°Ñ5Ñ5ˆð	ÝÔ)Ø”
ÐaÐaÐaÐañcô cð cð cð cð cð cð cˆCˆCøåð 	ð 	ð 	Ý�MŠMÐ7Ñ8Ô8Ð8Ø�1�1ð	øøøð Œ<ð 	dÝ�HŠHÐDÀcÄlÐTbÑcÔcÐcØŒ|Ðs   ž#A Á$A*Á)A*Féd   r   )r   r   rŒ   r   ÚsinceÚ
hide_pollsÚlimitÚoffsetr   r¯   úfloat | Noner°   Úboolr±   r²   c             ƒ  óh  ‡‡‡‡K  — dgg c}	Š|r*|	                      d¦  «         ‰                      |¦  «         |r*|	                      d¦  «         ‰                      |¦  «         |dk    r|	                      t          ¦  «         n@|t          v r7t          |d         ¦  «        dz  }
|	                      d¦  «         ‰|
|
dz   gz  Š|rk|	                      d¦  «         |                     d	d
¦  «                             dd¦  «                             dd¦  «        }‰                      |dz   ¦  «         |r*|	                      d¦  «         ‰                      |¦  «         |r|	                      d¦  «         d                     |	¦  «        Šdˆˆˆˆfd„}t          j        | j        |¦  «        ƒ d {V —†S )Nz1=1z
key_id = ?zaccount_id = ?r%   r   r®   z$status_code >= ? AND status_code < ?zpath LIKE ? ESCAPE '\'ú\z\\ú%z\%Ú_z\_zts >= ?zpoll = 0z AND rk   rH   r'   ry   c                óè   •— |                       d‰› �‰¦  «                             ¦   «         d         }|                       d‰› d�g ‰¢‰‘‰‘¦  «                             ¦   «         }d„ |D ¦   «         |dœS )Nz$SELECT COUNT(*) FROM requests WHERE r   zSELECT * FROM requests WHERE z" ORDER BY id DESC LIMIT ? OFFSET ?c                ó,   — g | ]}t          |¦  «        ‘ŒS rO   )Ú_rowrP   s     r,   r    z1RequestLog.query.<locals>.run.<locals>.<listcomp>à   s   € Ð3Ð3Ð3¨�T !™WœWÐ3Ð3Ð3r.   )ÚdataÚtotal)r[   ÚfetchoneÚfetchall)rk   r½   r£   rg   Úcondr±   r²   s      €€€€r,   r†   zRequestLog.query.<locals>.runÛ   sŽ   ø€ Ø—J’JÐLÀdÐLÐLÈdÑSÔS×\Ò\Ñ^Ô^Ð_`ÔaˆEØ—:’:ØX°ÐXÐXÐXØ&�$Ð&˜Ð&˜vÐ&ñ(ô (ç(0ª©
¬
ð ð 4Ð3¨dÐ3Ñ3Ô3¸eÐDÐDÐDr.   ©rk   rH   r'   ry   )	ÚappendÚFAILEDÚSTATUS_CLASSESr¦   ÚreplacerŠ   rm   rn   rh   )rF   r   r   rŒ   r   r¯   r°   r±   r²   ÚwhereÚlowÚescapedr†   rg   rÀ   s          ``    @@r,   ÚqueryzRequestLog.query¿   sò  øøøøè è € ð �g˜rˆˆˆtØð 	 Ø�LŠL˜Ñ&Ô&Ð&Ø�KŠK˜ÑÔÐØð 	$Ø�LŠLÐ)Ñ*Ô*Ð*Ø�KŠK˜
Ñ#Ô#Ð#Ø�XÒÐØ�LŠL�Ñ Ô Ð Ð Ø•~Ð%Ð%Ý�f˜Q”i‘.”. 3Ñ&ˆCØ�LŠLÐ?Ñ@Ô@Ð@Ø�S˜# ™)Ð$Ñ$ˆDØð 	'Ø�LŠLÐ2Ñ3Ô3Ð3Ø—l’l 4¨Ñ0Ô0×8Ò8¸¸eÑDÔD×LÒLÈSÐRWÑXÔXˆGØ�KŠK˜ #™Ñ&Ô&Ð&Øð 	Ø�LŠL˜Ñ#Ô#Ð#Ø�KŠK˜ÑÔÐØð 	%Ø�LŠL˜Ñ$Ô$Ð$Ø�|Š|˜EÑ"Ô"ˆð	Eð 	Eð 	Eð 	Eð 	Eð 	Eð 	Eð 	Eð 	Eõ Ô& t¤z°3Ñ7Ô7Ð7Ð7Ð7Ð7Ð7Ð7Ð7r.   Úwindowc              ƒ  ó  ‡‡‡‡‡‡	‡
K  — t           ‰         \  }Št          j        ¦   «         }||z
  ‰z  ‰z  ‰z   Š
t          |‰
z
  ‰z  ¦  «        dz   Š	d‰
fcŠŠdˆˆfd	„Šdˆˆˆˆˆ	ˆ
ˆfd„}t          j        | j        |¦  «        ƒ d {V —†S )Nr5   zts >= ? AND poll = 0rk   rH   Úcolsr&   r'   úlist[dict[str, Any]]c                ó�   •— |                       d|› dt          › d‰› d|› d�	‰¦  «                             ¦   «         }d„ |D ¦   «         S )NzSELECT z, COUNT(*) AS count, SUM(z ) AS errors FROM requests WHERE z
 GROUP BY z ORDER BY count DESC LIMIT 50c                ó,   — g | ]}t          |¦  «        ‘ŒS rO   ©ÚdictrP   s     r,   r    z5RequestLog.stats.<locals>.grouped.<locals>.<listcomp>ò   ó   € Ð*Ð*Ð* •D˜‘G”GÐ*Ð*Ð*r.   ©r[   rÃ   r¿   )rk   rÌ   r£   rg   rÀ   s      €€r,   Úgroupedz!RequestLog.stats.<locals>.groupedí   sƒ   ø€ Ø—:’:ð[˜$ð [ð [½ð [ð [Ø'+ð[ð [Ø7;ð[ð [ð [àñô ÷ ’h‘j”jð ð +Ð* TÐ*Ñ*Ô*Ð*r.   ry   c                óX  •— |                       dt          › d‰› �‰	¦  «                             ¦   «         \  }}}d„ |                       d‰› d�‰	¦  «        D ¦   «         }ˆ
ˆfd„t          ‰¦  «        D ¦   «         }|                       dt          › d‰› d	�‰‰
g‰	¢R ¦  «        D ]-\  }}}d
|cxk    r‰k     rn Œ||c||         d<   ||         d<   Œ.‰‰‰
||||r||z  ndt	          |d¦  «        t	          |d¦  «        dœ ‰| d¦  «         ‰| d¦  «         ‰| d¦  «         ‰| d¦  «        |dœS )NzSELECT COUNT(*), COALESCE(SUM(z@), 0), COALESCE(SUM(status_code >= 500), 0) FROM requests WHERE c                ó   — g | ]
}|d          ‘ŒS )r   rO   rP   s     r,   r    z1RequestLog.stats.<locals>.run.<locals>.<listcomp>ù   s.   € ð \ð \ð \ !˜˜1œð \ð \ð \r.   z&SELECT latency_ms FROM requests WHERE z ORDER BY latency_msc                ó(   •— g | ]}‰|‰z  z   d d dœ‘ŒS )r   )rŸ   ÚcountÚerrorsrO   )rQ   ÚiÚbucketÚstarts     €€r,   r    z1RequestLog.stats.<locals>.run.<locals>.<listcomp>û   s,   ø€ ÐcÐcÐcÈQ˜E A¨¡JÑ.¸ÀaÐHÐHÐcÐcÐcr.   z9SELECT CAST((ts - ?) / ? AS INTEGER) AS b, COUNT(*), SUM(z) FROM requests WHERE z GROUP BY br   rØ   rÙ   g        g      à?gffffffî?)Úp50Úp95zkey_id, key_namer   r   r   )rÊ   r¯   Úbucket_secondsr½   rÙ   Úserver_errorsÚ
error_rater   Úby_keyÚ
by_accountÚby_modelÚ	by_statusÚseries)r[   rÃ   r¾   Úranger<   )rk   r½   rÙ   rà   Ú	latenciesræ   r;   rØ   Úerrsrg   rÛ   rÀ   rÔ   Ú	n_bucketsrÜ   rÊ   s            €€€€€€€r,   r†   zRequestLog.stats.<locals>.runô   só  ø€ Ø+-¯:ª:ðSµð Sð SØLPðSð Sàñ,ô ,÷ ’h‘j”jñ )ˆE�6˜=ð\ð \ r§z¢zØS¸ÐSÐSÐSÐUYñ([ô ([ð \ñ \ô \ˆIàcÐcÐcÐcÐcÕRWÐXaÑRbÔRbÐcÑcÔcˆFØ$&§J¢JðKÝ!ðKð KØ9=ðKð Kð Kà˜FÐ* TÐ*Ð*ñ%,ô %,ð Nð NÑ ��U˜Dð ˜Ð'Ð'Ò'Ð'˜iÒ'Ð'Ð'Ð'Ð'ØBGÈÐ?�F˜3”K Ñ(¨&°¬+°hÑ*?øà ØØ"(ØØ Ø!.Ø05Ð>˜f u™n˜n¸3Ý&1°)¸TÑ&BÔ&BÝ&1°)¸TÑ&BÔ&BðDð Dà!˜' "Ð&8Ñ9Ô9Ø%˜g b¨,Ñ7Ô7Ø#˜G B¨Ñ0Ô0Ø$˜W R¨Ñ7Ô7Ø ðð ð r.   )rk   rH   rÌ   r&   r'   rÍ   rÁ   )ÚWINDOWSrª   r¦   rm   rn   rh   )rF   rÊ   ÚlengthÚnowr†   rg   rÛ   rÀ   rÔ   rê   rÜ   s    `   @@@@@@r,   ÚstatszRequestLog.statsä   sí   øøøøøøøè è € Ý  œ‰ˆ�ÝŒi‰kŒkˆà�v‘ &Ñ(¨6Ñ1°FÑ:ˆÝ˜˜u™¨Ñ/Ñ0Ô0°1Ñ4ˆ	à+¨e¨Xˆ
ˆˆdð	+ð 	+ð 	+ð 	+ð 	+ð 	+ð 	+ð	ð 	ð 	ð 	ð 	ð 	ð 	ð 	ð 	ð 	ð 	ð 	õ> Ô& t¤z°3Ñ7Ô7Ð7Ð7Ð7Ð7Ð7Ð7Ð7r.   c              ƒ  ó†   ‡K  — t          j         ¦   «         dz
  Šd	ˆfd„}‰t          j        | j        |¦  «        ƒ d{V —†dœS )
zÁPer-key request counts in one GROUP BY: everything kept (see retention), the
        last 24h, and the failures among those (4xx/5xx or a failed task). Task polls are excluded, as in ``stats``.r   rk   rH   r'   rÍ   c                ó‚   •— |                       dt          › d�‰‰f¦  «                             ¦   «         }d„ |D ¦   «         S )NzŒSELECT key_id, key_name, MAX(ts) AS last_request_at, COUNT(*) AS total, COALESCE(SUM(ts >= ?), 0) AS requests_24h, COALESCE(SUM(ts >= ? AND zk), 0) AS errors_24h FROM requests WHERE poll = 0 AND key_id IS NOT NULL GROUP BY key_id ORDER BY total DESCc                ó,   — g | ]}t          |¦  «        ‘ŒS rO   rÐ   rP   s     r,   r    z5RequestLog.key_usage.<locals>.run.<locals>.<listcomp>$  rÒ   r.   rÓ   )rk   r£   r¯   s     €r,   r†   z!RequestLog.key_usage.<locals>.run  s\   ø€ ð —:’:ð6å,2ð6ð 6ð 6ð
 ˜�ñ ô  ÷ !)¢¡
¤
ð ð +Ð* TÐ*Ñ*Ô*Ð*r.   N)r¯   r¼   )rk   rH   r'   rÍ   )rª   rm   rn   rh   )rF   r†   r¯   s     @r,   Ú	key_usagezRequestLog.key_usage  sh   øè è € õ ”	‘”˜eÑ#ˆð
	+ð 
	+ð 
	+ð 
	+ð 
	+ð 
	+ð ­gÔ.?ÀÄ
ÈCÑ.PÔ.PÐ(PÐ(PÐ(PÐ(PÐ(PÐ(PÐQÐQÐQr.   )r   r   r'   r(   )r'   rH   rx   )r+   ry   r'   r(   )NN)r   r&   rŒ   r&   r�   r2   r   rŽ   r   rŽ   r'   r(   )r–   r—   r'   r(   )r¥   r2   r'   r¦   )r   rŽ   r   rŽ   rŒ   rŽ   r   rŽ   r¯   r³   r°   r´   r±   r¦   r²   r¦   r'   ry   )rÊ   r&   r'   ry   )r'   ry   )Ú__name__Ú
__module__Ú__qualname__rG   rd   rh   rs   rv   r‹   r•   r¤   r­   rÉ   rî   rò   rO   r.   r,   r>   r>   U   s=  € € € € € ð+ð +ð +ð +ðð ð ð ð)ð )ð )ðFð Fð Fð Fð(ð (ð (ð (ð=ð =ð =ð =ð& NRðJð Jð Jð Jð Jð$
>ð 
>ð 
>ð 
>ð
ð 
ð 
ð 
ð 37ÐQUØ)-À$Ø*.À5Ø!$°Að#8ð #8ð #8ð #8ð #8ð #8ðJ/8ð /8ð /8ð /8ðbRð Rð Rð Rð Rð Rr.   r>   z°UPDATE requests SET task_status = ?, task_ms = CAST((? - ts) * 1000 AS INTEGER), error = COALESCE(error, ?), account_id = COALESCE(account_id, ?) WHERE task_id = ? AND poll = 0rR   úsqlite3.Rowry   c                ó†   — t          | ¦  «        }t          |d         ¦  «        t          |d         ¦  «        c|d<   |d<   |S )Nr   r   )rÑ   r´   )rR   Úds     r,   r»   r»   0  s<   € ÝˆQ‰Œ€AÝ! ! H¤+Ñ.Ô.µ°Q°v´Y±´Ð€A€h�K��6‘Ø€Hr.   )r   r&   r'   r(   )r/   r0   r1   r2   r'   r3   )rR   rö   r'   ry   ) Ú__doc__Ú
__future__r   rm   Úloggingr9   rW   rB   rª   Úcontextvarsr   Úpathlibr   Útypingr   Ú	getLoggerró   rp   r   Ú__annotations__r‰   r]   r^   rÃ   rë   rÄ   ÚSTATUS_FILTERSr-   r<   r>   r‚   r»   rO   r.   r,   ú<module>r     s¤  ððð ð ð #Ð "Ð "Ð "Ð "Ð "à €€€Ø €€€Ø €€€Ø €€€Ø Ð Ð Ð Ø €€€Ø "Ð "Ð "Ð "Ð "Ð "Ø Ð Ð Ð Ð Ð Ø Ð Ð Ð Ð Ð à€gÔ˜Ñ!Ô!€ð 5?°JÐ?OÐY]Ð4^Ñ4^Ô4^€Ð ^Ð ^Ð ^Ñ ^ðZ€ð€ð4 "(°IÐ>Ð>€ð 
:€ð  MÐ9NÐ
OÐ
O€à-€Ø,�>Ð, 8Ð,Ð,€ð*ð *ð *ð *ðð ð ð ðQRð QRð QRð QRð QRñ QRô QRð QRðj%ð ðð ð ð ð ð r.   