
    Jjq                     x   d Z ddlZddlZddlZej                            ej                            e                    Zej        	                    de           ddl
ZddlZddlZdZd Zd Zd Z	 	 d-dZd Zd	 Zd
 Zd Zd Zd.dZd Zd Zd Zd Zd Zd Z d Z!d/dZ"d Z#d Z$e%dk    r ej&                    Z'dD ]gZ( e#e'de(          Z) e*d e)d         +                                D                       Z, e-de)d         e)d          e)d!         e)d"         e,fz             he'.                    d#          /                                d$         Z0 ej1        dd%d&           ej1        dd%d'          cZ2Z3 e-d(e2d)e3d* ee'e0e2e3          d+         d,           e'4                                 dS dS )0u  
PLANNING TEAM - le planning reel, date.

Assemble les trois briques : le cycle de reference (base.py), le calendrier et ses
jours feries (calendrier.py), les absences (conges.py).

    planning effectif d'une semaine  =  cycle  -  absences  -  jours feries

Une absence porte toujours sur des JOURNEES entieres. Ce qui change, c'est le
decompte : soit des jours de conges payes, soit des heures a deduire — et dans ce
second cas, les heures se calculent toutes seules a partir de ce que la personne
aurait travaille. C'est exactement la question que pose le cabinet chaque mois :
« combien d'heures a deduire pour l'arret du 26/06 au 05/07 ».
    Nu  
-- Une journee retouchee a la main sur une VRAIE date. Quand une ligne existe ici, elle
-- remplace entierement ce que dit le cycle pour cette personne ce jour-la.
CREATE TABLE IF NOT EXISTS creneau_jour (
    id            INTEGER PRIMARY KEY,
    salarie_id    INTEGER NOT NULL REFERENCES salarie(id) ON DELETE CASCADE,
    date          TEXT NOT NULL,
    debut_min     INTEGER NOT NULL,
    fin_min       INTEGER NOT NULL,
    hors_comptoir INTEGER NOT NULL DEFAULT 0,
    ajpp          INTEGER NOT NULL DEFAULT 0
);

-- Le marqueur du jour retouche. Il est indispensable : sans lui, une journee videe a
-- la main serait indiscernable d'une journee jamais touchee, et le cycle reprendrait
-- le dessus.
CREATE TABLE IF NOT EXISTS jour_modifie (
    salarie_id INTEGER NOT NULL REFERENCES salarie(id) ON DELETE CASCADE,
    date       TEXT NOT NULL,
    modifie_le TEXT NOT NULL,
    PRIMARY KEY (salarie_id, date)
);

-- L'etat de reference d'une journee : ce qu'elle valait AVANT que Jerome ne la
-- retouche. Pose par l'import du planning 2026, et par la premiere retouche d'une
-- journee qui n'en avait pas encore. C'est la que le bouton ↺ va chercher.
CREATE TABLE IF NOT EXISTS creneau_jour_origine (
    id            INTEGER PRIMARY KEY,
    salarie_id    INTEGER NOT NULL REFERENCES salarie(id) ON DELETE CASCADE,
    date          TEXT NOT NULL,
    debut_min     INTEGER NOT NULL,
    fin_min       INTEGER NOT NULL,
    hors_comptoir INTEGER NOT NULL DEFAULT 0,
    ajpp          INTEGER NOT NULL DEFAULT 0
);

-- Le marqueur : une journee peut avoir un etat de reference VIDE (personne ne
-- travaillait), qu'il faut distinguer de « pas d'etat de reference du tout ».
CREATE TABLE IF NOT EXISTS jour_origine (
    salarie_id INTEGER NOT NULL REFERENCES salarie(id) ON DELETE CASCADE,
    date       TEXT NOT NULL,
    pose_le    TEXT NOT NULL,
    PRIMARY KEY (salarie_id, date)
);

CREATE INDEX IF NOT EXISTS idx_creneau_jour ON creneau_jour (date, salarie_id);
CREATE INDEX IF NOT EXISTS idx_jour_origine ON creneau_jour_origine (date, salarie_id);
c                     t          j        | d          rdS |                     t                     |                                  dS )uK  
    Cree les tables si elles manquent — et surtout, ne fait RIEN sinon.

    `executescript` valide implicitement la transaction en cours. Appele au milieu
    d'une simulation (voir demandes.impact), il rendait definitive l'absence qu'on
    s'appretait a annuler. D'ou le controle prealable, qui ne coûte qu'une lecture.
    )creneau_jourjour_modifiecreneau_jour_originejour_origineN)cgtables_completesexecutescriptSCHEMAcommit)cxs    "/opt/planning-team/travail/reel.pycreer_schemar   O   sM     
2  H I I VIIKKKKK    c                     i }t          j        | |          D ]9}|                    |d         |d         fg                               |           :|S )z>{ (salarie_id, jour) : [creneaux] } pour une semaine du cycle.
salarie_idjour)bdcreneaux
setdefaultappend)r   cycleindexcs       r   _index_creneauxr   ^   s]    E[U## E E!L/1V95r::AA!DDDDLr   c                    t          |            i }|                     d|                                |                                f          D ]F}|                    |d         |d         fg                               t          |                     Gd |                     d|                                |                                f          D             }||fS )z
    Les journees retouchees a la main sur la periode.
    Renvoie ({ (salarie_id, date) : [creneaux] }, { (salarie_id, date) })
    zSELECT salarie_id, date, debut_min, fin_min, hors_comptoir, ajpp
                 FROM creneau_jour WHERE date BETWEEN ? AND ?
                ORDER BY debut_minr   datec                 <    i | ]}|d          |d         f|d         S )r   r   
modifie_le .0ls     r   
<dictcomp>z%_index_exceptions.<locals>.<dictcomp>s   s7     2 2 2!<!F),ao 2 2 2r   zPSELECT salarie_id, date, modifie_le FROM jour_modifie WHERE date BETWEEN ? AND ?)r   execute	isoformatr   r   dict)r   lundisamedicreneaux_jourr#   modifiess         r   _index_exceptionsr,   f   s    
 MZZ&(-(9(96;K;K;M;M'NP P S S 	  !L/1V9!=rBBII$q''RRRR2 2rzzZ			F,,../H1 H1 2 2 2H (""r   c                 X   ||                                 f}|Nt          |            |                     d||                                 f                                          du}n||v }|rL||                    |g           S d |                     d||                                 f          D             S t          j        |          \  }}	t          j        ||	          }
|r||
         nt          | |
          }|                    ||	                                dz   fg           S )zw
    Ce que la personne travaille reellement ce jour-la : la journee retouchee si elle
    existe, le cycle sinon.
    N<SELECT 1 FROM jour_modifie WHERE salarie_id = ? AND date = ?c                 ,    g | ]}t          |          S r    )r'   r!   s     r   
<listcomp>z&creneaux_effectifs.<locals>.<listcomp>   s0     H H HAQ H H Hr   zSELECT id, salarie_id, debut_min, fin_min, hors_comptoir, ajpp
                 FROM creneau_jour WHERE salarie_id = ? AND date = ?
                ORDER BY debut_min   )
r&   r   r%   fetchonegetcal
semaine_desemaine_cycler   weekday)r   r   r   index_cycle
exceptionsr+   clemodifie	annee_isonumeror   r   s               r   creneaux_effectifsr>   y   sN    t~~''
(CR**J))*, ,,4HJJdC / H!>>#r***H H&(2DNN4D4D'E"G "G H H H 	H
 t,,Ivi00E"-MK?2u3M3ME99j$,,..1"45r:::r   c                 
   t          |            |                     d||                                f                                          rdS t	          | ||          D ]H}|                     d||                                |d         |d         |d         |d         f           I|                     d||                                t
          j                                                            d	
          f           dS )zFFige la journee du cycle dans creneau_jour, pour pouvoir la retoucher.r.   NINSERT INTO creneau_jour
                 (salarie_id, date, debut_min, fin_min, hors_comptoir, ajpp)
               VALUES (?, ?, ?, ?, ?, ?)	debut_minfin_minhors_comptoirajppzHINSERT INTO jour_modifie (salarie_id, date, modifie_le) VALUES (?, ?, ?)secondstimespec)r   r%   r&   r2   r>   datetimenow)r   r   r   r   s       r   _materialiserrJ      s   	zzPt~~//02 22:(**J55 - -


, ))1[>1Y<6,		- 	- 	- 	- JJR	T^^%%					 	 	*	*I	*	>	>	@A A A A Ar   c           
      ~   t           j                            |          }t          | ||           t	          | ||           |                     dt           j                                                             d          ||f           |                     d||||f                                          }|r|                     d|d         f           |d         |k     r!t          | |||d         |||d                    |d	z   |d
         k     r$t          | |||d	z   |d
         ||d                    | 
                                 dS |                     d||f                                          d         }t          | ||||d	z   ||rdnd           t          | ||           | 
                                 dS )z
    Ajoute ou retire une demi-heure sur une VRAIE date, sans toucher au cycle.
    La premiere retouche fige la journee ; les suivantes la modifient.
    zHUPDATE jour_modifie SET modifie_le = ? WHERE salarie_id = ? AND date = ?rE   rF   zSELECT id, debut_min, fin_min, hors_comptoir, ajpp FROM creneau_jour
            WHERE salarie_id = ? AND date = ? AND debut_min <= ? AND fin_min > ?%DELETE FROM creneau_jour WHERE id = ?idrA   rD      rB   FzXSELECT MIN(ajpp) AS tous FROM creneau_jour
            WHERE salarie_id = ? AND date = ?tousr1   r   T)rH   r   fromisoformatfiger_originerJ   r%   rI   r&   r2   _inserer_jourr   _fusionner_jour)r   r   date_isominuter   lignerestes          r   basculer_creneau_reelrX      s   
 =&&x00D "j(+++"j$''' JJY!%%''1191EEzS[\^ ^ ^ JJ	T	Xvv.0 0 19

 

  	


:U4[NKKK&&"j(E+4Ff/ / /B;y)))"j(FRKyAQf/ / /
		uJJ	13=x2HJ JJR(**U[]E "j(FFRK#!!!% % %B
H---IIKKK4r   c           
          |                                 dz   }t          |||          D ]"\  }}	}
|                     d||||	|
|f           #d S )Nr1   r@   )r7   _decouper_ouverturer%   )r   r   rT   debutfinr   rD   numero_jourdfhorss              r   rR   rR      sq    ,,..1$K)+ucBB 6 6
1d


, 1at4		6 	6 	6 	66 6r   c                    g ||fg}}t           j        |          D ]\  }}g }|D ]\  }}	t          ||          t          |	|          }}
|
|k     rS|                    |
|df           ||
k     r|                    ||
f           ||	k     r|                    ||	f           ~|                    ||	f           |}|                    d |D                        t          |          S )zGSepare la part ouverte de la part hors ouverture (temps administratif).r   c              3   &   K   | ]\  }}||d fV  dS r1   Nr    )r"   rdrfs      r   	<genexpr>z&_decouper_ouverture.<locals>.<genexpr>   s,      44FBRQK444444r   )r   	OUVERTUREmaxminr   extendsorted)r]   r[   r\   morceauxrW   odofsuiterd   re   id_if_s               r   rZ   rZ      s   E3<.eH,{+  B 		' 		'FB2r{{CBKKCSyyc1...88LL"c+++88LL#r+++b"X&&&&OO44e444444(r   c                    |                      d||f                                          }d }|D ]}|r|d         |d         k    r|d         |d         k    rz|d         |d         k    rh|                      d|d         |d         f           |                      d|d         f           |d         |d         |d         |d         |d         d	}|}d S )
NzSELECT id, debut_min, fin_min, hors_comptoir, ajpp FROM creneau_jour
            WHERE salarie_id = ? AND date = ? ORDER BY debut_minrB   rA   rC   rD   z0UPDATE creneau_jour SET fin_min = ? WHERE id = ?rM   rL   )rM   rA   rB   rC   rD   )r%   fetchall)r   r   rT   lignes
precedenter#   s         r   rS   rS      s   ZZ	D	X    !)

  J   
	:i0AkNBB/1_3EEEv&!F)33JJI)j&679 9 9JJ>4
KKK *4 0z+?V%&y\AoDV"#F)- -JJ JJ r   Fc                    t          |            |                     d||f                                          }|r|sdS |                     d||f           t          | |t          j                            |                    D ]6}|                     d|||d         |d         |d         |d         f           7|                     d	||t          j                                                            d
          f           dS )z
    Enregistre l'etat de reference d'une journee : ce qu'elle vaut a cet instant.
    Appele par l'import du planning 2026 (forcer=True) et avant la premiere retouche
    manuelle d'une journee qui n'en a pas encore.
    <SELECT 1 FROM jour_origine WHERE salarie_id = ? AND date = ?FzBDELETE FROM creneau_jour_origine WHERE salarie_id = ? AND date = ?zINSERT INTO creneau_jour_origine
                 (salarie_id, date, debut_min, fin_min, hors_comptoir, ajpp)
               VALUES (?, ?, ?, ?, ?, ?)rA   rB   rC   rD   zINSERT INTO jour_origine (salarie_id, date, pose_le) VALUES (?, ?, ?)
           ON CONFLICT(salarie_id, date) DO UPDATE SET pose_le = excluded.pose_lerE   rF   T)	r   r%   r2   r>   rH   r   rP   rI   r&   )r   r   rT   forcerdejar   s         r   rQ   rQ     s4    ::T!8,. ..6hjj 	 F uJJSH%' ' 'J0K0KH0U0UVV - -


, 1[>1Y<6,		- 	- 	- 	- JJ	U	Xx04466@@)@TTUW W W 4r   c                 `   t          |            |                     d||f                                          }|                     d||f                                          }|                     d||f           |rp|                     d||f                                          }|D ]6}|                     d|||d         |d         |d         |d	         f           7d
 |D             }n|                     d||f           d}|                                  d |D             ||ng k    S )ug  
    Le bouton ↺ : la journee revient a son etat AVANT retouche.

    S'il existe un etat de reference — pose par l'import du planning 2026, ou juste
    avant la premiere retouche — on le remet. Sinon la journee redevient simplement
    celle du cycle. Autrement dit : on annule ce que Jerome a change, pas ce que
    disait le planning d'origine.
    rw   zlSELECT debut_min, fin_min FROM creneau_jour
            WHERE salarie_id = ? AND date = ? ORDER BY debut_minz:DELETE FROM creneau_jour WHERE salarie_id = ? AND date = ?zSELECT debut_min, fin_min, hors_comptoir, ajpp FROM creneau_jour_origine
                WHERE salarie_id = ? AND date = ? ORDER BY debut_minzINSERT INTO creneau_jour
                     (salarie_id, date, debut_min, fin_min, hors_comptoir, ajpp)
                   VALUES (?, ?, ?, ?, ?, ?)rA   rB   rC   rD   c                 .    g | ]}|d          |d         fS )rA   rB   r    r!   s     r   r0   z#rendre_au_cycle.<locals>.<listcomp>B  s%    @@@A!K.!I,/@@@r   z:DELETE FROM jour_modifie WHERE salarie_id = ? AND date = ?Nc                 ,    g | ]}t          |          S r    )tupler!   s     r   r0   z#rendre_au_cycle.<locals>.<listcomp>I  s    $$$E!HH$$$r   )r   r%   r2   rs   r   )r   r   rT   origineavantrt   r#   apress           r   rendre_au_cycler      s    jjF	X    (

  JJ	D	X    !)

 

 JJKH%' ' '  H"$ $ %-HJJ 	  	1 	1AJJ0 Xq~q|?#QvY0	1 1 1 1 A@@@@


O)	+ 	+ 	+IIKKK$$e$$$%2CLLr   c                 (   i }t          j        | |                                |                                          D ]}|d         dk    r|d         dk    rt          j                            |d                   }t          j                            |d                   }t          ||          }|t          ||          k    rJ|||d         |                                f<   |t          j        d          z  }|t          ||          k    J|S )	z5{ (salarie_id, date iso) : absence } sur une periode.statutrefuseer~   import
date_debutdate_finr   r1   )	r   absencesr&   rH   r   rP   rh   ri   	timedelta)r   r(   r)   r   ar[   r\   r   s           r   _absences_par_jourr   L  s   E[U__..0@0@0B0BCC * *X;)## Y<8##++AlO<<m))!J-885%  c#v&&&&9:E1\?DNN$4$456H&q)))D c#v&&&& Lr   c                 `   t          j        t          |j        dz
  |j        dz                       }t	          | d          t	          | d          d}t          | ||          \  }}dg }	}|}
|
|k    r|
                                dk     rt|
|vrpt          d t          | ||
|||          D                       }|rC||dz  z  }|		                    |

                                t          |dz  d          d           |
t          j        d          z  }
|
|k    t          |d          |	d	S )
z
    Les heures que la personne aurait travaillees entre deux dates, d'apres le cycle,
    jours feries deduits. C'est le chiffre que le cabinet reclame pour les arrets.
    r1      r1   r              c              3   8   K   | ]}|d          |d         z
  V  dS rB   rA   Nr    r"   r   s     r   rf   z!heures_prevues.<locals>.<genexpr>m  sK       M M I,;7 M M M M M Mr         N@)r   heuresr   detail)r4   tous_les_feriesrangeyearr   r,   r7   sumr>   r   r&   roundrH   r   )r   r   r[   r\   feriesr   r9   r+   totalr   r   minutess               r   heures_prevuesr   `  se   
  uzA~sx!|!D!DEEFA&&?2q+A+ABBE,R<<J6ED
#++<<>>A$f"4"4 M M#5b*dE6@($L $LM M M M MG  ^4't~~'7'75SWYZC[C[\\]]]"1%%% #++ E1oo888r   c                 r    |                                 dk    rdS t          t          | ||                    S )zPLa personne a-t-elle des creneaux ce jour-la, d'apres le cycle ou une retouche ?   F)r7   boolr>   )r   r   r   s      r   travaille_ler   w  s5    ||~~u"2z488999r   c                 N    t          j        ||          }t          |          |fS )uq  
    Le nombre de jours de conges payes d'une periode : les jours ouvrables, lundi ->
    samedi, jours feries exclus. Un jour ou la personne ne travaille pas compte quand
    meme — c'est le decompte legal en jours ouvrables.

    Une variante avait ete envisagee : ne pas compter les jours non travailles situes
    en BORDURE de periode. Elle a ete confrontee aux 43 periodes du classeur des
    conges. Verdict sans appel :

        decompte strict en jours ouvrables ....... 38 concordances sur 43  (88 %)
        avec la regle des bordures ............... 20 concordances sur 43  (47 %)

    La variante corrigeait quatre cas et en cassait vingt-trois. Elle est ecartee.
    Les cinq ecarts qui subsistent viennent vraisemblablement de libelles dont la date
    de debut reelle differe de celle qu'on en a lue.

    Renvoie (nombre de jours, liste des dates retenues).
    )r4   jours_ouvrableslen)r   r   r[   r\   jourss        r   
compter_cpr   ~  s'    & s++Eu::ur   c           
         t           j                            |          }|                     d|||f                                          }|rf|d         |k    s|d         |k    r"t          d|d         d|d         d          t          j        | |d                    |d         |k    rd	d
dS d t          j        D             }||vrt          d|z            |dk    rdnd}d\  }}	|dk    rt          | |||          d         }nt          | |||          d         }	t          j        | |||||||	          }
d||
|dS )u  
    Marque ou demarque UNE journee, directement depuis la grille des semaines reelles.

    C'est le pendant date du bouton AJPP du cycle : un clic sur « CP » ou « AJPP »
    pose l'absence de la journee, elle se hachure aussitot sur le planning et
    apparait dans l'onglet Absences. Un second clic la retire.

    Les CP se decomptent en jours ouvrables, le reste en heures - et les heures sont
    calculees a partir de ce que la personne aurait travaille ce jour-la.
    zzSELECT id, motif, date_debut, date_fin FROM absence
            WHERE salarie_id = ? AND date_debut <= ? AND date_fin >= ?r   r   u.   cette journée fait partie d’une absence du  au u)    : à modifier depuis l’onglet AbsencesrM   motifFN)poser   c                      i | ]}|d          |S )r   r    )r"   ms     r   r$   z!basculer_jour.<locals>.<dictcomp>  s    ,,,Q1q,,,r   zmotif inconnu : %scpr   )NNr   )decompter   r   T)r   r   rM   r   )rH   r   rP   r%   r2   
ValueErrorr   supprimer_absenceMOTIFSr   r   poser_absence)r   r   rT   r   r   	existante	referencer   r   r   
absence_ids              r   basculer_jourr     s    =&&x00D

	J	Xx(* * +3(** 
  2\"h..)J2G82S2S* \***Ij,A,A,ACD D D 	R4111W&&!D111,,"),,,II.6777}}tt(HMFE8Jd;;HE2z466q9!"j(He+3F%Q Q QJ5
fMMMr   c                    t          |            t          | d          t          | d          d}t          | ||          \  }}t          j        t          |j        dz
  |j        dz                       }d t          j        |           D             }i }t          |          D ]\  }	}
|	|v rt          j                            |
          }||cxk    r|k    rn n|                                dk    s||v rWt          j        |          \  }}t          j        ||          }t!          d ||                             |	|                                dz   fg           D                       }t!          d |                    |	|
fg           D                       }||z
  dz  }t%          |          d	k     r|                    |	d
g d          }|dxx         |z  cc<   |d                             |
t+          |dz  d          t+          |dz  d          t+          |d          d           d t          j        |           D             }g }|                                D ]e\  }	}|                    |	          }|s|                    |	|d         |d         |d         t+          |d         d          |d         d           f|                    d            |S )a  
    L'ecart entre ce qui a ete reellement planifie et ce que prevoit le cycle, sur une
    periode. Une heure retiree a la main creuse le compteur, une heure ajoutee le
    remplit. Positif = heures a recuperer, negatif = heures dues.

    Les absences ne comptent pas ici : un conge ou un arret se decompte ailleurs, en
    jours de CP ou en heures a deduire pour le cabinet. Seules les retouches du
    planning alimentent la recuperation.
    r1   r   r   c                 .    h | ]}|d          
|d         S )paye_a_l_heurerM   r    r"   ss     r   	<setcomp>z(compteur_recuperation.<locals>.<setcomp>  s'    HHHAA6F4GH$HHHr   r   c              3   8   K   | ]}|d          |d         z
  V  dS r   r    r   s     r   rf   z(compteur_recuperation.<locals>.<genexpr>  sK       U U iL1[>1 U U U U U Ur   c              3   8   K   | ]}|d          |d         z
  V  dS r   r    r   s     r   rf   z(compteur_recuperation.<locals>.<genexpr>  sK       H H Y<!K.0 H H H H H Hr   r   gMbP?r   r   r   r   )r   prevufaitecartc                      i | ]}|d          |S rM   r    r   s     r   r$   z)compteur_recuperation.<locals>.<dictcomp>      0001AdGQ000r   prenominitialecouleur)r   r   r   r   r   r   c                     | d         S )Nr   r    )rs    r   <lambda>z'compteur_recuperation.<locals>.<lambda>  s
    ( r   key)r   r   r,   r4   r   r   r   r   salariesrk   rH   r   rP   r7   r5   r6   r   r3   absr   r   r   itemssort)r   r[   r\   r   r9   r+   r   a_lheuretotauxr   rT   r   r<   r=   r   r   r   r   blocgensresultatr   s                         r   compteur_recuperationr     sU    A&&?2q+A+ABBE,R<<J uzA~sx!|!D!DEEF IHRHHHHF &x 0 0 Y Y
H!!}**844$$$$$$$$$!););tv~~N400	6!)V44 U U"5\--z4<<>>A;M.NPRSSU U U U U H H&NNJ+A2FFH H H H H%u::  r-J-JKKX%Xx%a:P:P',TD[!'<'<uUTUX X 	Y 	Y 	Y 	Y 10B000DH"LLNN  
DHHZ   	$(*!I,DNA..$x.
 
 	 	 	 	
 MM++M,,,Or   c                     t          j        t           j        t           j                  }|p3t          j                                        t	          j        d          z   }d t          | ||          D             S )u   
    { salarie_id : heures } — l'ecart au cycle accumule depuis le demarrage du planning
    (semaine 36 de 2026) jusqu'a aujourd'hui. C'est ce qui alimente la recuperation.
    i  )daysc                 ,    i | ]}|d          |d         S )r   r   r    )r"   r   s     r   r$   z"ecarts_cumules.<locals>.<dictcomp>  s"    XXXQAlOQx[XXXr   )	r4   lundi_deDEBUT_ANNEEDEBUT_SEMAINErH   r   todayr   r   )r   r\   r[   s      r   ecarts_cumulesr     sg    
 L#*;<<E

K(-%%''(*<'*J*J*JJCXX2GESV2W2WXXXXr   c                 	  #$ t          j        ||          }t          j        ||          }|t          j        d          z   }t          j        |           }d |D             #d |D             $t          | d          t          | d          d}t          | ||          \  }}	t          | ||          }
t          j
        t          |j        dz
  |j        dz                       }g i }}t          d          D ]T}|t          j        |          z   }|                    |          }g }|D ]}t          | |d         ||||	          }|
                    |d         |                                f          }t!          d	 |D                       d
z  }||                             |d         |dz   fg           }|                    |d         ||t%          t!          d |D                       d
z  d          t%          |d          t'          |          p|du||rdn|r|d         nd|d         |                                f|	v t)          d |	                    |d         |                                f          |r|d         ndfD             pdg          d
           |sd|b|D ]_}|d         rt          |d         |d         d          D ]6}|                    |dz   |fg                               |d                    7`|                    |                                |dz   t           j        |         ||d           Vi }t          d          D ]}|dz   }t/          t          j        |                    }t          j        |          }||         d         du}g }t          j                    D ]}||v o| }|                    ||fg           } t!          #fd| D                       }!#fd| D             }"|                    ||$fd| D             t7          |           t7          |"          |!|o|!dk    |o"t7          |"          t
          j        k     o||k    d           |||<   !||||                                |                                ||t;          | |||          t=          | ||          d	S )z
    Le planning effectif d'une semaine ISO : qui travaille quand, qui est absent,
    et ou les regles ne sont plus tenues.
    r   c                 ,    i | ]}|d          |d         S )rM   fonctionr    r   s     r   r$   zsemaine.<locals>.<dictcomp>  "    666A4!J-666r   c                 ,    i | ]}|d          |d         S )rM   r   r    r   s     r   r$   zsemaine.<locals>.<dictcomp>  r   r   r1   r   r   r   rM   c              3   8   K   | ]}|d          |d         z
  V  dS r   r    r   s     r   rf   zsemaine.<locals>.<genexpr>  s/      NN19+6NNNNNNr   r   c              3   8   K   | ]}|d          |d         z
  V  dS r   r    r   s     r   rf   zsemaine.<locals>.<genexpr>&  s/      NN!)q~5NNNNNNr   Nu   Fériér   c                     g | ]}||S r    r    )r"   xs     r   r0   zsemaine.<locals>.<listcomp>/  s3     "C "C "C@A"C! "C "C "Cr   cree_le)
r   r   creneaux_cycleheures_cycler   absentabsencer   r;   	change_lerC   rA   rB   rN   )r   r   nomferiert   r   c              3   b   K   | ])}                     |          t          j        k    %d V  *dS rc   )r3   r   PHARMACIENSr"   r   	fonctionss     r   rf   zsemaine.<locals>.<genexpr>G  s;       V Vq9==3C3Cr~3U3U3U3U3U3U V Vr   c                 V    g | ]%}                     |          t          j        v#|&S r    )r3   r   HORS_COMPTOIRr   s     r   r0   zsemaine.<locals>.<listcomp>H  s2    VVVY]]1-=-=REU-U-U1-U-U-Ur   c                      g | ]
}|         S r    r    )r"   r   	initialess     r   r0   zsemaine.<locals>.<listcomp>L  s    888qil888r   r   )rU   ouvertr   nbcomptoirpharmaciensmanque_pharmaciensous_effectif)	anneer=   r   r(   r)   r   presenceremplacantsrecuperation)r4   r6   r   rH   r   r   r   r   r,   r   r   r   r   r3   r>   r&   r   r   r   r   rh   r   JOURSsetcreneaux_ouverturederniere_demi_heurecreneaux_griller   MINI_COMPTOIRr  r   )%r   r  r=   r   r(   r)   r   r   r9   r+   	abs_indexr   r   occupeir   r   rt   r   r*   r   r   r   r   rU   r  r   ouvertsderniereferme_toute_la_journeepasr   idsnb_pharmaciensau_comptoirr   r   s%                                      @@r   semainer    s   
 eV,,EL''EX'***F;r??D66666I66666IA&&?2q+A+ABBE,R??J"2uf55I uzA~v{Q!G!GHHF6E1XX &N &Nx)!,,,

4   	O 	OA.r1T7D%/98E EMmmQtWdnn.>.>$?@@GNNNNNNNQUUF #5\--qwA.>CCNMMg)"0 %NN~NNNNNQUUWX!Z !Z**u++<)<"+0]77\ww7G7GX\dGT^^%5%56(B ! "C "CLL!D'4>>+;+;!<==*1;GI&&t.= "C "C "C "MGKfN N    $  OW_& O OA) ! "'+)b"I"I O O))1q5&/2>>EEagNNNNO 	dnn..A Yq\EVM M 	N 	N 	N 	N H1XX  1ub+D1122)$//!&q'!2$!>(** 	 	F'G1G-GF**dF^R00C  V V V VC V V VVVNVVVVcVVVKJJ  8888C888#hh,,-%+%C!0C"( #9S-=-=@P-P #9&,&8
 
 
 
 
 
  &5""f.>.>.@.@H"2uh>>-b%@@  r   c                 >   d t          j        |           D             }d t          j        |           D             }g }|D ]Z}|d         }d ||         D             }	|	s t          d |	D                       }
d |d         D             }d |d         D             }g }|                                D ]\  }}||v s||v r|d	         t           j        v r#|
r|d	         t           j        k    r<|                    ||d
         |d         |d	         |d         t          |	                    |d          d          |d         d           |
                    d            |                    ||d         t          |	          |
|dd         d           \|S )u  
    Pour chaque jour en alerte, qui pourrait couvrir.

    Deux criteres, ceux retenus au cadrage :
      1. le bon statut — s'il manque un pharmacien, on ne propose que des pharmaciens ;
      2. qui est sous son contrat sur le cycle — on rattrape au lieu de creuser.

    On ne propose que des gens libres ce jour-la et non absents. La rayonniste en est
    exclue d'office : les alertes portent sur la tenue du COMPTOIR, qu'elle n'assure
    pas — la proposer ne reglerait rien.
    c                 @    i | ]}|d          |d         |d         ndS )r   r   Nr   r    r   s     r   r$   zremplacants.<locals>.<dictcomp>k  s?     ) ) ) oaj.D'

# ) ) )r   c                      i | ]}|d          |S r   r    r   s     r   r$   zremplacants.<locals>.<dictcomp>m  r   r   r   c                 2    g | ]}|d          s|d         |S )r   r   r    r"   ps     r   r0   zremplacants.<locals>.<listcomp>r  s-    ]]]/0B]aH[F\]1]]]r   c              3   &   K   | ]}|d          V  dS )r   Nr    r  s     r   rf   zremplacants.<locals>.<genexpr>u  s(      HH1"5 6HHHHHHr   c                 >    h | ]}|d          
|d         |d         S )r   r   r   r    r!   s     r   r   zremplacants.<locals>.<setcomp>w  s@     9 9 9q
m9,-hK91\? 9 9 9r   rt   c                 .    h | ]}|d          
|d         S )r   r   r    r!   s     r   r   zremplacants.<locals>.<setcomp>y  s%    UUUQ8U<UUUr   r   r   r   r   r   r   r   )r   r   r   r   r   r   r   c                 "    | d         | d         fS )Nr   r   r    )r   s    r   r   zremplacants.<locals>.<lambda>  s    a(8&91W:%F r   r   r   Nr   )r   r   creneaux_en_alertebesoin_pharmacien	candidats)r   	compteursr   anyr   r   r   r   r   r3   r   r   )r   r   r  r   ecartsr   propositions	jour_blocr   alertesr!  occupesindisponiblesr"  sidr   s                   r   r  r  _  s   ) )|B'') ) )F00B000DL # #	 ]]htn]]] 	HHHHHHH9 9Ih,? 9 9 9UU)H2EUUU	jjll 	 	FCg~~!5!5} 000  Qz]bn%D%D!Qx[a
mjMa	lvzz#s33Q77"#$4"5	      	FFGGGf%"%g,,!2"2A2
 
 	 	 	 	 r   __main__)%   &   i  c              #   D   K   | ]}|D ]}|d          s|d         dV  dS )r   r   r1   Nr    )r"   jr  s      r   rf   rf     sn       R RA R RAo$6R:;<O:PRa R R R R R R Rr   r  u/   Semaine %d (S%d) — du %s au %s : %d alerte(s)r=   r   r(   r)   z+SELECT id FROM salarie WHERE initiale = 'E'rM   	         u   
Élodie, du r   z : r   u    h prévues)NNN)F)N)5__doc__rH   ossyspathdirnameabspath__file__BASEinsertbaser   
calendrierr4   congesr   r   r   r   r,   r>   rJ   rX   rR   rZ   rS   rQ   r   r   r   r   r   r   r   r   r  r  __name__	connexionr   r=   r   r   valuesr(  printr%   r2   r+  r   d1d2closer    r   r   <module>rG     s_     				 



	wrwx0011 4               /
d    # # #& :>15; ; ; ;:A A A&& & &R6 6 6  (  (   8)M )M )MX  (9 9 9.: : :  .)N )N )NX4 4 4nY Y Y YZ Z Zz5 5 5p z	B M MGBf%%# R R:!5!5!7!7 R R R R R@8aj!G*ak7KL 	M 	M 	M 	M **B
C
C
L
L
N
Nt
TCX]4A&&dAr(B(BFB	EERR^^BR44X>>>@ A A AHHJJJJJ r   