Ë
    ‘¨jS  ã                  ó„   — d Z ddlmZ ddlmZ ddlmZ ddlmZ ddl	m
Z
 ddlmZ d	Zdd
„Zdd„Zdd„Z	 	 d	 	 	 	 	 dd„Zy)uW  Find past forecasts similar to a draft ("analogs").

Structured similarity only â€” no embeddings: problem-type overlap, closeness of
likelihood / size / location for shared problems, time of season, and zone.
Danger is deliberately NOT part of the score, so analogs don't anchor the
forecaster; their danger distribution is shown separately.
é    )Úannotations)ÚCounter)Údate)Ú
Connectioné   )Úfetchall)ÚBANDSé<   c                óP   — | s|sy| r|syt        | |z  «      t        | |z  «      z  S )Ng      ð?ç        )Úlen)ÚaÚbs     ú*/var/www/avy-guidance/api/app/retrieval.pyÚ_jaccardr      s-   € Ù‘QØÙ‘AØÜˆq�1‰u‹:œ˜A ™E›
Ñ"Ð"ó    c                óÈ   — t        | j                  «       j                  |j                  «       j                  z
  «      }t        |d|z
  «      }t	        dd|t
        z  z
  «      S )Nim  r   r   )ÚabsÚ	timetupleÚtm_ydayÚminÚmaxÚMAX_SEASON_DAYS)Úd1Úd2Údiffs      r   Ú_season_closenessr      sP   € Üˆr�|‰|‹~×%Ñ%¨¯©«×(>Ñ(>Ñ>Ó?€DÜˆt�S˜4‘ZÓ €DÜˆs�A˜œÑ.Ñ.Ó/Ð/r   c           
     ó*  ‡— | D �ch c]  }|d   ’Œ	 }}|D �ch c]  }|d   ’Œ	 }}dt        ||«      z  }||z  }	|	�r)d}
|	D �]  Št        ˆfd„| D «       «      }t        ˆfd„|D «       «      }|
t        t        |j                  d«      xs g «      t        |j                  d«      xs g «      «      z  }
|j                  d«      r=|j                  d«      r,|
dt	        t        |d   «      t        |d   «      z
  «      z  z  }
|j                  d	«      €ŒÓ|j                  d	«      €Œå|
dt	        t        |d	   «      t        |d	   «      z
  «      z  z  }
�Œ ||
t        |	«      z  z  }|d
t        ||«      z  z  }|r|dz  }t        |d«      S c c}w c c}w )NÚtypeg      @r   c              3  ó4   •K  — | ]  }|d    ‰k(  sŒ|–— Œ y­w©r   N© ©Ú.0ÚpÚts     €r   ú	<genexpr>zscore.<locals>.<genexpr>,   s   øè ø€ Ò8˜1¨¨6©°a«”QÑ8ùó   ƒ‘c              3  ó4   •K  — | ]  }|d    ‰k(  sŒ|–— Œ y­wr!   r"   r#   s     €r   r'   zscore.<locals>.<genexpr>-   s   øè ø€ Ò7˜1¨¨&©	°Q«”QÑ7ùr(   Ú	locationsÚ
likelihoodgš™™™™™Ù?Úsize_maxg      ø?g      à?é   )
r   ÚnextÚsetÚgetr   ÚintÚfloatr   r   Úround)ÚqueryÚcandÚqdateÚcdateÚ
zone_matchr%   ÚqtypesÚctypesÚsÚsharedÚsubÚqÚcr&   s                @r   Úscorer@   $   s’  ø€ Ø!&Ö'˜Aˆa�‹iÐ'€FÐ'Ø!%Ö&˜Aˆa�‹iÐ&€FÐ&ØŒh�v˜vÓ&Ñ&€AØ�f‰_€FÚØˆØó 	NˆAÜÓ8 Ô8Ó8ˆAÜÓ7 Ô7Ó7ˆAØ”8œC §¡ kÓ 2Ò 8°bÓ9¼3¸q¿u¹uÀ[Ó?QÒ?WÐUWÓ;XÓYÑYˆCØ�u‰u�\Ô" q§u¡u¨\Ô':Ø�sœS¤ Q |¡_Ó!5¼¸A¸l¹OÓ8LÑ!LÓMÑMÑM�Ø�u‰u�ZÓ Ñ,°·±°zÓ1BÑ1NØ�sœS¤ q¨¡}Ó!5¼¸aÀ
¹mÓ8LÑ!LÓMÑMÑM’ð	Nð 	
ˆS”3�v“;ÑÑˆØˆÔ  ¨Ó.Ñ	.Ñ.€AÙØ	ˆS‰ˆÜ��A‹;Ðùò% (ùÚ&s
   †F˜FNc           
     ó€  ‡— t        | d|f«      }|xs
 t        «       }g }|D ]?  }	|	d   |v rŒt        ||	d   ||	d   |duxr |	d   |k(  «      |	d<   |j                  |	«       ŒA |j	                  d„ ¬	«       |d| }
t
        D �‡ci c]   Š‰t        t        ˆfd
„|
D «       «      «      “Œ" }}|
|t        |«      dœS c c}w )z?query: [{type, locations, likelihood, size_max}] for the draft.at  SELECT f.id, f.valid_date, f.season, f.guidance_era, f.source, f.zone_id, f.title,
                  f.danger_upper, f.danger_middle, f.danger_lower, left(f.bottom_line, 600) AS bottom_line,
                  coalesce(json_agg(json_build_object('type', p.problem_type, 'rank', p.rank, 'locations', p.locations,
                      'likelihood', p.likelihood, 'size_min', p.size_min, 'size_max', p.size_max) ORDER BY p.rank)
                      FILTER (WHERE p.id IS NOT NULL), '[]') AS problems
           FROM forecasts f LEFT JOIN forecast_problems p ON p.forecast_id = f.id
           WHERE f.center_id = %s GROUP BY f.idÚidÚproblemsÚ
valid_dateNÚzone_idr@   c                ó6   — | d    | d   j                  «        fS )Nr@   rD   )Ú	toordinal)Úrs    r   ú<lambda>zfind_analogs.<locals>.<lambda>O   s!   €   '¡
˜{¨Q¨|©_×-FÑ-FÓ-HÐ,HÐI€ r   )Úkeyc              3  ó@   •K  — | ]  }|d ‰› �   €Œ|d ‰› �   –— Œ y­w)Údanger_Nr"   )r$   rH   r   s     €r   r'   zfind_analogs.<locals>.<genexpr>Q   s.   øè ø€ Ò#b¸ÀQÈÐQRÐPSÀ}ÑEUÑEa A¨°¨s mÕ$4Ñ#bùs   ƒ‘)ÚitemsÚdanger_distributionÚ
candidates)	r   r/   r@   ÚappendÚsortr	   Údictr   r   )ÚconnÚ	center_idr4   rD   rE   ÚkÚexclude_idsÚrowsÚscoredrH   Útopr   Údistributions              ` r   Úfind_analogsr[   :   sî   ø€ ô Øð	3ð 
ˆó
€Dð Ò&¤£€KØ€FØò ˆØˆT‰7�kÑ!ØÜ˜5 ! J¡-°¸Q¸|¹_ÈgÐ]aÐNaÒN}ÐfgÐhqÑfrÐv}Ñf}Ó~ˆˆ'‰
Ø�‰�aÕð	ð
 ‡K�KÑI€KÔJØ
��!ˆ*€CÜmr×sÐhi�A”tœGÓ#b¸cÔ#bÓbÓcÑcÐs€LÐsØ°ÌSÐQUËYÑWÐWùò ts   Â%B;)r   r/   r   r/   Úreturnr2   )r   r   r   r   r\   r2   )r4   ú
list[dict]r5   r]   r6   r   r7   r   r8   Úboolr\   r2   )Né   N)rS   r   rT   r1   r4   r]   rD   r   rE   z
int | NonerU   r1   rV   zset[int] | Noner\   rR   )Ú__doc__Ú
__future__r   Úcollectionsr   Údatetimer   Úpsycopgr   Údbr   Úenginer	   r   r   r   r@   r[   r"   r   r   ú<module>rg      s]   ðñõ #å Ý å å Ý à€ó#ó0óð, osØ<@ðXØðXØ*9ðXØEIôXr   