
    j#                       d Z ddlmZ ddlmZmZmZ ddlmZ ddl	m
Z
mZ ddlmZmZmZmZ ddlmZ dd	lmZmZmZ dd
lmZmZmZmZ  edg      Z G d de
      Z G d de
      Z G d de
      Z G d de
      Z ejC                  d       ee       ee      fd-d       Z"ejG                  d       ee       ee      fd.d       Z$ejC                  d       ee       ee      fd/d       Z%ejG                  d       ee       ee      fd0d       Z&ejC                  d       ee       ee      fd/d       Z'ejC                  d       ee       ee      fd/d       Z(ejC                  d        ee       ee      fd/d!       Z)ejC                  d"       ee       ee      fd1d#       Z*d2d$Z+ejG                  d        ee       ee      fd3d%       Z,ejG                  d&       ee       ee      fd1d'       Z-ejG                  d(       ee       ee      fd4d)       Z.ejC                  d*      d+ ee       ee      fd5d,       Z/y+)6z5Centers, zones, members and versioned configurations.    )annotations)	APIRouterDependsHTTPException)
Connection)	BaseModelField   )Usercurrent_userrequire_adminrequire_center)replay_config)active_configconfig_by_idseed_default)Jsonbfetchallfetchoneget_conncenters)tagsc                      e Zd ZU  ed      Zded<    edd      Zded<   d	Zd
ed<   dZded<   dZ	ded<    edd      Z
ded<   y	)CenterInz^[a-z0-9-]{2,40}$)patternstrslugr
   x   
min_length
max_lengthnameN
str | Noneafp_center_idzAmerica/Los_AngelestimezoneenlocaleNACz^(NAC|SAC)$)defaultr   danger_scale)__name__
__module____qualname__r	   r   __annotations__r"   r$   r%   r'   r*        0/var/www/avy-guidance/api/app/routers/centers.pyr   r      sR    12D#2s3D#3 $M:$)Hc)FCe]CL#Cr0   r   c                  H    e Zd ZU  edd      Zded<   dZded<   dZd	ed
<   y)ZoneIn   r   r   r   r"   Nr#   afp_zone_iddict[str, str] | Noneband_labels)r+   r,   r-   r	   r"   r.   r5   r7   r/   r0   r1   r3   r3      s(    s3D#3"K")-K&-r0   r3   c                  ~    e Zd ZU dZded<   dZded<   dZded<   dZded	<   dZd
ed<   dZ	ded<    e
d      Zded<   y)ConfigDraftInNz
int | None	parent_idzdict[str, list[int]] | Nonedanger_tablez!dict[str, list[list[int]]] | Nonegradient_tablez dict[str, dict[str, int]] | Nonelikelihood_matrixzbool | Noneshow_danger_suggestionr6   r7   
   )r    r   	rationale)r+   r,   r-   r:   r.   r;   r<   r=   r>   r7   r	   r@   r/   r0   r1   r9   r9       sS     Iz 04L-48<N5<:>7>*.K.)-K&-b)Is)r0   r9   c                      e Zd ZU dZded<   y)	ApproveInNr#   r@   )r+   r,   r-   r@   r.   r/   r0   r1   rB   rB   +   s     Iz r0   rB   z/centersc                b    |j                   rt        | d      S t        | d|j                  f      S )Nz#SELECT * FROM centers ORDER BY namezoSELECT c.*, m.role FROM centers c JOIN memberships m ON m.center_id = c.id WHERE m.user_id = %s ORDER BY c.name)is_adminr   id)connusers     r1   list_centersrH   /   s3    }}CDDy	
 r0   c                    t        |       t        |d| j                               }t        ||d   |j                         |S )NzINSERT INTO centers (slug, name, afp_center_id, timezone, locale, danger_scale)
           VALUES (%(slug)s, %(name)s, %(afp_center_id)s, %(timezone)s, %(locale)s, %(danger_scale)s) RETURNING *rE   )r   r   
model_dumpr   rE   )bodyrF   rG   rows       r1   create_centerrM   :   sB    $
	u	C s4y$''*Jr0   z/centers/{center_id}c                    t        ||        t        |d| f      }|st        dd      t        |d| f      }i |||j	                  |       dS )Nz#SELECT * FROM centers WHERE id = %si  zCenter not foundz6SELECT * FROM zones WHERE center_id = %s ORDER BY name)zonesrole)r   r   r   r   role_in)	center_idrF   rG   centerrO   s        r1   
get_centerrT   G   s\    4#dAI<PFC!344TSV_UabEFfFudll9.EFFr0   z/centers/{center_id}/zonesc           
         t        || d       t        |d| |j                  |j                  |j                  rt        |j                        f      S d f      S )NTadminzaINSERT INTO zones (center_id, name, afp_zone_id, band_labels) VALUES (%s, %s, %s, %s) RETURNING *)r   r   r"   r5   r7   r   )rR   rK   rF   rG   s       r1   create_zonerX   Q   s^    4$/k	DIIt//DL\L\t7G7G1Hg  cgg r0   z/centers/{center_id}/membersc                :    t        || d       t        |d| f      S )NTrV   zSELECT u.id, u.email, u.name, u.last_login_at, m.role FROM memberships m JOIN users u ON u.id = m.user_id
           WHERE m.center_id = %s ORDER BY u.namer   r   rR   rF   rG   s      r1   list_membersr\   [   s(    4$/	5		 r0   z/centers/{center_id}/configc                2    t        ||        t        ||       S )N)r   r   r[   s      r1   get_active_configr^   f   s    4#y))r0   z/centers/{center_id}/configsc                6    t        ||        t        |d| f      S )Na{  SELECT cc.id, cc.version, cc.status, cc.rationale, cc.parent_id, cc.created_at, cc.proposed_at, cc.approved_at,
                  cu.name AS created_by_name, au.name AS approved_by_name
           FROM center_configs cc
           LEFT JOIN users cu ON cu.id = cc.created_by LEFT JOIN users au ON au.id = cc.approved_by
           WHERE cc.center_id = %s ORDER BY cc.version DESCrZ   r[   s      r1   list_configsr`   l   s(    4#	?
 
 r0   z/configs/{config_id}c                <    t        ||       }t        ||d          |S )NrR   )r   r   	config_idrF   rG   rL   s       r1   
get_configrd   z   s"    
tY
'C4[)*Jr0   c           	        | d   }t        | d         xr t        |j                  dg             }dD ]V  }|j                  |      }t        |t              r!t        |      |k7  st	        d |D              sEt        dd| d| d	       | d
   j                         D ]5  \  }}t	        d |j                         D              s't        dd| d       y )NdangerTablesizes1)rh   2345c              3  d   K   | ](  }t        |t               xs d |cxk  xr dk  nc   * yw)r      N
isinstanceint.0vs     r1   	<genexpr>z#_validate_tables.<locals>.<genexpr>   s4     ?wnoJqRUDV@V@i^_cd^ihi^iZi@i?w   .0  zDanger table row z must have z levels between 0 and 5.likelihoodMatrixc              3  d   K   | ](  }t        |t               xs d |cxk  xr dk  nc   * yw)r4   rn   Nro   rr   s     r1   ru   z#_validate_tables.<locals>.<genexpr>   s,     OQ:a%%8Q![q[8Orv   zLikelihood matrix row z must contain levels 1-5.)lengetrp   listanyr   itemsvalues)cfgtablecolslkrL   dists         r1   _validate_tablesr      s    Es7|8UYYsB%7!8D' hiim#t$CD(8C?wsv?w<w'8KvMe%fggh +,224 _	cO#**,OO'=dVC\%]^^_r0   c           
        t        || d       |j                  rt        ||j                        nt        ||       }|j                  r|d   | k7  rt	        dd      t        |d         }|j                  |j                  |d<   |j                  |j                  |d<   |j                  |j                  |d	<   |j                  |j                  |d
<   |j                  |j                  |d<   t        |       t        |d| f      d   }t        |d| |t        |      |j                  |d   |j                  f      S )zdCreate a new DRAFT version from a parent (default: the active one). Never changes the active config.TrV   rR   rw   z Parent belongs to another centerconfigrf   gradientTablerx   showDangerSuggestion
bandLabelszRSELECT coalesce(max(version), 0) + 1 AS v FROM center_configs WHERE center_id = %srt   zINSERT INTO center_configs (center_id, version, status, config, rationale, parent_id, created_by)
           VALUES (%s, %s, 'draft', %s, %s, %s, %s) RETURNING id, version, statusrE   )r   r:   r   r   r   dictr;   r<   r=   r>   r7   r   r   r   r@   rE   )rR   rK   rF   rG   parentr   versions          r1   create_draftr      sP    4$/37>>\$/}UY[dGeF~~&-:C!CDD
vh
 C$!..M&#22O)"&"8"8"".&*&A&A"## ,,LStqt}s  A  BE  FG	U	GU3ZtwwO	 r0   z/configs/{config_id}/proposec                    t        ||       }t        ||d   d       |d   dk7  rt        dd      |j                  d| f       d	diS )
NrR   TrV   statusdraft  zOnly drafts can be proposedzPUPDATE center_configs SET status = 'proposed', proposed_at = now() WHERE id = %sok)r   r   r   executerb   s       r1   proposer      sV    
tY
'C4[)6
8}C!>??LLcfoeqr$<r0   z/configs/{config_id}/approvec                `   t        ||       }t        ||d   d       |d   dk7  rt        dd      |d   |j                  k(  r|j                  st        d	d
      |j                  d|d   f       |j                  d|j                  |j                  rd|j                   nd| f       ddiS )z^Activate a proposed version. Needs a DIFFERENT center admin (or a site admin) than its author.rR   TrV   r   proposedr   z&Only proposed versions can be approved
created_byi  z2A different center admin must approve this versionzWUPDATE center_configs SET status = 'retired' WHERE center_id = %s AND status = 'active'zUPDATE center_configs SET status = 'active', approved_by = %s, approved_at = now(),
               rationale = coalesce(rationale, '') || coalesce(%s, '') WHERE id = %sz

Approval note: Nr   )r   r   r   rE   rD   r   r@   )rc   rK   rF   rG   rL   s        r1   approver      s     tY
'C4[)6
8}
"C!IJJ
<DGG#DMMC!UVVLLjmpq|m}l  ALL	X	DNN''78PTV_`
 $<r0   z/configs/{config_id}/replayNc                    t        ||       }t        ||d          t        ||d         }t        ||d   |d   |d   |      S )zjBacktest a config against imported forecasts: agreement vs the active config and which suggestions change.rR   r   )r   r   r   r   )rc   holdout_seasonrF   rG   rL   actives         r1   replayr      sR     tY
'C4[)*4[!12Fs;/Xx@PR`aar0   )rF   r   rG   r   )rK   r   rF   r   rG   r   )rR   rq   rF   r   rG   r   )rR   rq   rK   r3   rF   r   rG   r   )rc   rq   rF   r   rG   r   )r   r   returnNone)rR   rq   rK   r9   rF   r   rG   r   )rc   rq   rK   rB   rF   r   rG   r   )rc   rq   r   r#   rF   r   rG   r   )0__doc__
__future__r   fastapir   r   r   psycopgr   pydanticr   r	   authr   r   r   r   calibrationr   configsr   r   r   dbr   r   r   r   routerr   r3   r9   rB   r{   rH   postrM   rT   rX   r\   r^   r`   rd   r   r   r   r   r   r/   r0   r1   <module>r      s   ; " 5 5  % D D ' ? ? 4 4		$Dy D.Y .*I *!	 ! J$+H$5GLDY   Z5<X5FU\]iUj 	 	 "#29(2CRYZfRg G $G )*AHARahiuav  + *+4;H4ET[\hTi  , )*9@9JY`amYn * +*
 *+4;H4ET[\hTi 
 ,
 "#29(2CRYZfRg  $	_ +,IPQYIZipq}i~  -6 +,/6x/@wWcOd  - +,@G@Q`ght`u  -" )*8<QXYaQbqx  zF  rG b +br0   