Ë
    wÍ'jCË  ã                  ó”  — d dl mZ d dlmZ d dlmZmZ d dlmZm	Z	m
Z
 d dlmZmZmZmZmZ d dlmZ d dlmZmZmZmZmZmZmZmZmZ d dlmZmZm Z  d d	l!m"Z"m#Z#m$Z$m%Z%m&Z&m'Z'm(Z(m)Z) d d
l*m+Z+m,Z, e G d„ d«      «       Z-d<d„Z.d=d„Z/d>d„Z0	 	 	 	 	 	 	 	 d?d„Z1	 	 	 	 	 	 d@d„Z2	 	 	 	 	 	 dAd„Z3	 	 	 	 	 	 dBd„Z4	 	 	 	 	 	 dCd„Z5	 	 	 	 	 	 	 	 	 	 dDd„Z6	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dEd„Z7	 	 	 	 	 	 dFd„Z8	 	 	 	 	 	 	 	 	 	 dGd„Z9	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dHd„Z:	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dId„Z;dJd„Z<	 	 	 	 	 	 	 	 	 	 	 	 dKd„Z=	 	 	 	 	 	 	 	 	 	 	 	 dLd„Z>dMd„Z?dNd„Z@d d!œ	 	 	 	 	 	 	 	 	 dOd"„ZAd d d#œ	 	 	 	 	 	 	 	 	 dPd$„ZBd d!œ	 	 	 	 	 	 	 dQd%„ZCdRd&„ZD	 	 	 	 	 	 	 	 	 	 	 	 dKd'„ZEd d(d)œ	 	 	 	 	 	 	 	 	 	 	 	 	 dSd*„ZFd d+d(d,œ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dTd-„ZGd.d/œdUd0„ZH	 	 	 	 	 	 	 	 	 	 	 	 	 	 dVd1„ZI	 	 	 	 	 	 	 	 	 	 	 	 	 	 dWd2„ZJ	 	 	 	 	 	 	 	 dXd3„ZK	 	 	 	 	 	 	 	 dYd4„ZL	 	 	 	 	 	 	 	 	 	 	 	 dZd5„ZM	 	 	 	 	 	 	 	 	 	 	 	 	 	 d[d6„ZNd7d8œ	 	 	 	 	 	 	 	 	 	 	 d\d9„ZO	 	 	 	 	 	 	 	 	 	 d]d:„ZPd^d;„ZQy )_é    )Úannotations)Ú	dataclass)ÚHTTPExceptionÚstatus)ÚSessionÚ
joinedloadÚselectinload)ÚUserÚUserCompletedAssessmentÚUserCompletedSessionÚUserRegisterStepÚUserTrackTrace)ÚMemberSessionProgress)	ÚProgrammeAssessmentÚProgrammeLevelÚProgrammeModuleÚProgrammeSessionÚProgrammeWeekÚnormalize_programme_level_slugÚnormalize_programme_module_kindÚprogramme_level_slug_strÚprogramme_module_kind_str)Ú_CompletionBatchÚ_ensure_assessment_doneÚ_ensure_session_marker_done)ÚProgrammeTrackResponseÚTrackAssessmentOutÚTrackCompleteRequestÚTrackLevelOutÚTrackModuleOutÚTrackProgressStatusÚTrackSessionOutÚTrackWeekOut)Úbuild_register_step_stateÚget_user_register_stepc                  ó6   — e Zd ZU ded<   ded<   ded<   ded<   y	)
Ú_TrackSessionRefr   Úsessionr   Úweekr   Úmoduler   ÚlevelN)Ú__name__Ú
__module__Ú__qualname__Ú__annotations__© ó    úD/var/www/html/kelly/kelly-backend/app/services/user_track_service.pyr'   r'   *   s   … àÓØ
ÓØÓØÔr1   r'   c                óš   — | �| j                  «       sy 	 t        | «      S # t        $ r!}t        t        j
                  d¬«      |‚d }~ww xY w)Nzlevel slug is required©Ústatus_codeÚdetail)Ústripr   Ú
ValueErrorr   r   ÚHTTP_400_BAD_REQUEST)r+   Úexcs     r2   Ú_parse_level_slugr;   2   sP   € Ø€}˜EŸK™KœMØðÜ-¨eÓ4Ð4øÜò ÜÜ×3Ñ3Ø+ô
ð ð	ûðúó   •
   	A
©AÁA
c                óš   — | �| j                  «       sy 	 t        | «      S # t        $ r!}t        t        j
                  d¬«      |‚d }~ww xY w)Nzmodule_kind is requiredr4   )r7   r   r8   r   r   r9   )Úmodule_kindr:   s     r2   Ú_parse_module_kindr?   >   sT   € ØÐ +×"3Ñ"3Ô"5ØðÜ.¨{Ó;Ð;øÜò ÜÜ×3Ñ3Ø,ô
ð ð	ûðúr<   c                ó¦  — | j                  t        «      j                  t        j                  |k(  «      j	                  «       D �ch c]  }|j
                  ’Œ }}| j                  t        «      j                  t        j                  |k(  «      j	                  «       D �ch c]  }|j                  ’Œ }}| j                  t        «      j                  t        j                  |k(  t        j                  j                  d«      «      j	                  «       }|D ]*  }|j                  €Œ|j                  |j                  «       Œ, ||fS c c}w c c}w )NT)Úqueryr   ÚfilterÚuser_idÚallÚassessment_idr   Ú
session_idr   Úis_completedÚis_Úadd)ÚdbrC   ÚrowÚcompleted_assessmentsÚcompleted_sessionsÚprogress_rowss         r2   Ú_load_completed_setsrO   J   s,  € ð —8‘8Ô3Ó4×;Ñ;Ô<S×<[Ñ<[Ð_fÑ<fÓg×kÑkÔmóámˆCð 	×ÓØmð ð ð —8‘8Ô0Ó1×8Ñ8Ô9M×9UÑ9UÐY`Ñ9`Óa×eÑeÔgóágˆCð 	�‹Øgð ð ð
 	�‰Ô&Ó'ß	‰Ô%×-Ñ-°Ñ8Ô:O×:\Ñ:\×:`Ñ:`ÐaeÓ:fÓ	gß	‰‹ð ó
 ˆØ�>‰>Ñ%Ø×"Ñ" 3§>¡>Õ2ð ð !Ð"4Ð4Ð4ùò!ùòs   ÁE	Â Ec               ó|   ‡— t        | j                  d„ ¬«      }|rt        ˆfd„|D «       «      S | j                  |v S )Nc                ó   — | j                   S ©N©Ú
sort_order©Úitems    r2   Ú<lambda>z'_session_is_completed.<locals>.<lambda>d   ó   € ¸t¿ºr1   ©Úkeyc              3  ó:   •K  — | ]  }|j                   ‰v –— Œ y ­wrR   ©Úid©Ú.0Ú
assessmentrL   s     €r2   Ú	<genexpr>z(_session_is_completed.<locals>.<genexpr>f   s   øè ø€ ÐXÉK¸j�:—=‘=Ð$9Ô9ÉKùó   ƒ)ÚsortedÚassessmentsrD   r]   )r(   rL   rM   rd   s    `  r2   Ú_session_is_completedre   ^   s<   ø€ ô ˜×,Ñ,Ñ2NÔO€KÙÜÓXÉKÓXÓXÐXØ�:‰:Ð+Ð+Ð+r1   c               óT  — g }t        | j                  d„ ¬«      }|�'|D �cg c]  }t        |j                  «      |k(  r|‘Œ }}|D ]Z  }t        |j                  d„ ¬«      D ]=  }t        |j
                  d„ ¬«      D ]   }|j                  t        |||| ¬«      «       Œ" Œ? Œ\ |S c c}w )Nc                ó   — | j                   S rR   rS   rU   s    r2   rW   z'_ordered_session_refs.<locals>.<lambda>p   s   € °T·_²_r1   rY   c                ó   — | j                   S rR   ©Ú
week_indexrU   s    r2   rW   z'_ordered_session_refs.<locals>.<lambda>y   ó   € ¸$¿/º/r1   c                ó   — | j                   S rR   ©Úsession_numberrU   s    r2   rW   z'_ordered_session_refs.<locals>.<lambda>z   ó
   € À$×BUÒBUr1   )r(   r)   r*   r+   )rc   Úmodulesr   ÚkindÚweeksÚsessionsÚappendr'   )r+   r>   Úrefsrp   r*   r)   r(   s          r2   Ú_ordered_session_refsrv   j   s´   € ð
 $&€DÜ�U—]‘]Ñ(DÔE€GØÐñ "ó
á!�Ü(¨¯©Ó5¸ÒDò Ø!ð 	ð 
ó ˆÜ˜6Ÿ<™<Ñ-I×JˆDÜ! $§-¡-Ñ5U×V�Ø—‘Ü$¨W¸4ÈÐV[Ô\õñ Wñ Kð ð €Kùò
s   ¡!B%c                óº   — | j                   }|r|j                  nd }|€y|D ]4  }|j                  D ]#  }|j                  |j                  k(  sŒ||fc c S  Œ6 d |fS )N©NN)r)   r*   rp   r]   )r(   Úprogramme_levelsr)   r*   r+   Úmods         r2   Ú_level_context_for_sessionr{   �   s`   € ð �<‰<€DÙ ˆT�[Š[ d€FØ€~ØÛ!ˆØ—=”=ˆCØ�v‰v˜Ÿ™Ó"Ø˜c�zÔ!ñ !ð "ð �ˆ<Ðr1   c                ój   — |€yt        | «      D ]"  \  }}|j                  j                  |k(  sŒ |c S  y)Nr   )Ú	enumerater(   r]   )Úsession_refsÚanchor_session_idÚindexÚrefs       r2   Ú_anchor_session_indexr‚   �   s;   € ð Ð ØÜ Ö-‰
ˆˆsØ�;‰;�>‰>Ð.Ó.ØŠLð .ð r1   c                ód   ‡— ‰�‰j                   €yt        ˆfd„| D «       «      r‰j                   S y)zKOnly use the registration anchor when it belongs to this level/module view.Nc              3  ód   •K  — | ]'  }|j                   j                  ‰j                  k(  –— Œ) y ­wrR   )r(   r]   rF   )r_   r�   Úregister_steps     €r2   ra   z0_registration_anchor_for_refs.<locals>.<genexpr>£   s%   øè ø€ Ð
NÁ¸#ˆ3�;‰;�>‰>˜]×5Ñ5Õ5Áùs   ƒ-0)rF   Úany)r~   r…   s    `r2   Ú_registration_anchor_for_refsr‡   œ   s6   ø€ ð
 Ð × 8Ñ 8Ð @ØÜ
Ó
NÁÓ
NÔNØ×'Ñ'Ð'Ør1   c               ó¬   — | D ]O  }|j                   |j                   k\  r yt        |d ¬«      }|sŒ.|D ]  }t        |j                  ||¬«      rŒ  y ŒQ y)NT©r>   ©rL   rM   F)rT   rv   re   r(   )ÚlevelsÚ	level_rowrL   rM   r+   ru   r�   s          r2   Ú_prior_levels_completer�   ¨   s`   € ó ˆØ×Ñ˜y×3Ñ3Ò3ÙÜ$ U¸Ô=ˆÙØÛˆCÜ(Ø—‘Ø&;Ø#5öò
 ñ ð ð r1   c               ó$   — t        ||||¬«      syy)zALevel is playable when every prior level's sessions are complete.rŠ   FT)r�   )rJ   rC   rŒ   r…   ry   rL   rM   s          r2   Ú_level_is_accessibler�   ¿   s"   € ô "ØØØ3Ø-õ	ð ð
 r1   c                ód   — |€y | j                   D ]  }t        |j                  «      |k(  sŒ|c S  y rR   )rp   r   rq   )rŒ   r>   rz   s      r2   Ú_module_row_for_kindr‘   Ø   s8   € ð ÐØØ× Ô ˆÜ$ S§X¡XÓ.°+Ó=ØŠJð !ð r1   c               ó  — t        | j                  d„ ¬«      }|D ]b  }|j                  |j                  k\  r yt        | t	        |j
                  «      ¬«      }|sŒA|D ]  }t        |j                  ||¬«      rŒ  y Œd y)Nc                ó   — | j                   S rR   rS   rU   s    r2   rW   z)_prior_modules_complete.<locals>.<lambda>ë   ó   € ¸¿ºr1   rY   Tr‰   rŠ   F)rc   rp   rT   rv   r   rq   re   r(   )rŒ   Ú
module_rowrL   rM   rp   rz   ru   r�   s           r2   Ú_prior_modules_completer–   ä   s€   € ô �Y×&Ñ&Ñ,HÔI€GÛˆØ�>‰>˜Z×2Ñ2Ò2ÙÜ$ØÜ1°#·(±(Ó;ô
ˆñ ØÛˆCÜ(Ø—‘Ø&;Ø#5öò
 ñ ð ð  r1   c          	     óJ   — t        | ||||||¬«      syt        ||||¬«      syy)zQModule is playable only when the level and every prior module in it are complete.rŠ   FT)r�   r–   )rJ   rC   rŒ   r•   r…   ry   rL   rM   s           r2   Ú_module_is_accessibler˜   ÿ   sD   € ô  Ø
ØØØØØ3Ø-õð Ü"ØØØ3Ø-õ	ð ð
 r1   c          
     ó¾  — t        ||¬«      }t        | ||||||¬«      sy|�"t        ||«      }	|	�t        | |||	||||¬«      syt	        ||«      }
t        | |||¬«      \  }}|�ê|j                  }|r|j                  nd}|r|j                  nd}|�)|j                  |j                  kD  rt        |||||
¬«      S |�‘|j                  |j                  k(  rx|rt        |j                  «      nd}|�9|�7t        ||«      }|�)|j                  |j                  kD  rt        |||||
¬«      S |�||k(  r|j                  |r|j                  fS dfS t        |||||
¬«      S )z©
    Resolve the open session/assessment for a specific level (and optional module).
    Prior levels must be fully complete before anything in this level can open.
    r‰   rŠ   rx   N©ry   )r>   rL   rM   r   )rv   r�   r‘   r˜   r‡   Úresolve_programme_open_positionr)   r*   r+   rT   Ú_resolve_open_targetsr]   r   rq   )rJ   rC   rŒ   r>   r…   rL   rM   ry   r~   r•   r   Úglobal_sessionÚglobal_assessmentr)   r*   Úglobal_levelÚmodule_kind_strÚfiltered_modules                     r2   Ú_resolve_open_for_track_viewr¢   #  sÀ  € ô )¨ÀÔL€LÜØ
ØØØØØ3Ø-õð àÐÜ)¨)°[ÓAˆ
ØÐÔ%:ØØØØØØØ"7Ø1õ	&
ð ä5°lÀMÓRÐÜ(GØ
ØØØ)ô	)Ñ%€NÐ%ð Ð!Ø×"Ñ"ˆÙ $�—’¨$ˆÙ'-�v—|’|°4ˆØÐ#¨×(?Ñ(?À)×BVÑBVÒ(VÜ(ØØ'Ø&;Ø#5Ø"3ôð ð Ð#¨¯©¸9¿<¹<Ò(GÙHNÔ7¸¿¹ÔDÐTXˆOØÐ&¨6Ð+=Ü"6°yÀ+Ó"N�Ø"Ð.°6×3DÑ3DÀ×GaÑGaÒ3aÜ0Ø!Ø$/Ø.CØ+=Ø*;ôð ð Ð" k°_Ò&DØ%×(Ñ(ÑBSÐ*;×*>Ñ*>Ð]Ð]ÐY]Ð]Ð]ä ØØØ3Ø-Ø+ôð r1   c                óÂ   — |j                   dk7  ry| j                  ry| j                  xs dj                  «       j	                  «       }|dv xs |j                  d«      S )zJIntro-week rows seeded without real assessments (e.g. HB intro session 1).r   FÚ >   ú	session 1ú	session 2ú	session 3r¤   zsession )rj   rd   Útitler7   ÚlowerÚ
startswith)r(   r)   r¨   s      r2   Ú_is_intro_placeholder_sessionr«   x  sZ   € à‡�˜!ÒØØ×ÒØØ�]‰]Ò ˜b×'Ñ'Ó)×/Ñ/Ó1€EØÐ?Ð?Ò_À5×CSÑCSÐT^ÓC_Ð_r1   c               ó~  — t        |«      }t        |«      }t        | ||¬«      }t        | |«      \  }}	t	        | |«      }
t        ||¬«      D ]^  }t        |j                  |j                  «      sŒ$t        |j                  ||	¬«      rŒ=t        | ||j                  j                  |
«       Œ` |
j                  «        y )N©Ú
level_slugr…   r‰   rŠ   )r;   r?   Ú_load_levelrO   r   rv   r«   r(   r)   re   r   r]   Úflush)rJ   rC   r®   r>   r…   Úlevel_slug_enumÚmodule_kind_enumrŒ   rL   rM   Úbatchr�   s               r2   Ú"_repair_intro_placeholder_sessionsr´   ‚  s©   € ô (¨
Ó3€OÜ)¨+Ó6ÐÜ˜B¨?È-ÔX€IÜ0DÀRÈÓ0QÑ-ÐÐ-Ü˜R Ó)€EÜ$ YÐ<L×MˆÜ,¨S¯[©[¸#¿(¹(ÔCØÜ Ø�K‰KØ"7Ø1õ
ð
 Ü# B¨°·±·±ÀÕGð Nð 
‡K�K…Mr1   c               ó‚  — t        | |¬«      }t        ||«      }t        |«      D ]˜  \  }}||k  rŒ|j                  }	t	        |	j
                  d„ ¬«      }
|
r2|
D ],  }|j                  |vsŒ|	j                  |j                  fc c S  Œdt        |	||¬«      rŒst        |	|j                  «      rŒŠ|	j                  dfc S  y)z›
    First incomplete assessment (preferred) or session without assessments.
    Everything before the registration anchor is treated as already done.
    r‰   c                ó   — | j                   S rR   rS   rU   s    r2   rW   z'_resolve_open_targets.<locals>.<lambda>°  ó   € À4Ç?Â?r1   rY   rŠ   Nrx   )
rv   r‚   r}   r(   rc   rd   r]   re   r«   r)   )r+   r>   rL   rM   r   r~   Úanchor_indexr€   r�   r(   rd   r`   s               r2   rœ   rœ   œ  s¾   € ô )¨¸KÔH€LÜ(¨Ð7HÓI€Lä Ö-‰
ˆˆsØ�<ÒØà—+‘+ˆÜ˜W×0Ñ0Ñ6RÔSˆÙÛ)�
Ø—=‘=Ð(=Ò=Ø"Ÿ:™: z§}¡}Ð4Ô4ñ *ô 'ØØ"7Ø1ö
ô
 -¨W°c·h±hÔ?ØØ—:‘:˜tÐ#Ò#ð# .ð& r1   c                óZ  — | j                  t        «      j                  t        t        j                  «      j                  t
        j                  «      j                  t        j                  «      j                  t        j                  «      t        t        j                  «      j                  t
        j                  «      j                  t        j                  «      j                  t
        j                  «      «      j                  t        j                  «      j!                  «       S rR   )rA   r   Úoptionsr	   rp   r   rr   r   rs   r   rd   r   r*   r+   Úorder_byrT   rD   )rJ   s    r2   Ú_load_programme_levels_orderedr¼   Á  s³   € à
�‰”Ó ß	‰Üœ×/Ñ/Ó0ß‰\œ/×/Ñ/Ó0ß‰\œ-×0Ñ0Ó1ß‰\Ô*×6Ñ6Ó7Üœ×/Ñ/Ó0ß‰\œ/×/Ñ/Ó0ß‰Zœ×,Ñ,Ó-ß‰Zœ×-Ñ-Ó.ó	

÷ 
‰”.×+Ñ+Ó	,ß	‰‹ðr1   c                óP   — g }| D ]  }|j                  t        |d ¬«      «       Œ  |S )Nr‰   )Úextendrv   )r‹   ru   r+   s      r2   Ú _ordered_session_refs_for_levelsr¿   Ó  s*   € Ø#%€DÛˆØ�‰Ô)¨%¸TÔBÕCð à€Kr1   Nrš   c               ó¼  — |�|j                   sy|�|n
t        | «      }|syt        |«      }|syt        | |«      \  }}t	        ||j
                  «      }t        |«      D ]z  \  }	}
|	|k  rŒ|
j                  }t        |j                  d„ ¬«      }|r|D ]  }|j                  |vsŒ||fc c S  ŒPt        |||¬«      rŒ_t        ||
j                  «      rŒv|dfc S  y)z­
    First incomplete session/assessment from the member's registered level onward.
    When a module or level is fully complete, the position advances to the next one.
    Nrx   c                ó   — | j                   S rR   rS   rU   s    r2   rW   z1resolve_programme_open_position.<locals>.<lambda>ø  r·   r1   rY   rŠ   )rG   r¼   r¿   rO   r‚   rF   r}   r(   rc   rd   r]   re   r«   r)   )rJ   rC   r…   ry   r‹   r~   rL   rM   r¸   r€   r�   r(   rd   r`   s                 r2   r›   r›   Ú  sø   € ð Ð M×$>Ò$>Øà!1Ð!=ÑÔCaÐbdÓCe€FÙØä3°FÓ;€LÙØä0DÀRÈÓ0QÑ-ÐÐ-Ü(¨°}×7OÑ7OÓP€Lä Ö-‰
ˆˆsØ�<ÒØà—+‘+ˆÜ˜W×0Ñ0Ñ6RÔSˆÙÛ)�
Ø—=‘=Ð(=Ò=Ø" JÐ.Ô.ñ *ô 'ØØ"7Ø1ö
ô
 -¨W°c·h±hÔ?ØØ˜D�=Ò ð# .ð( r1   ©r+   r*   c               óà  — | j                   }|xs |r|j                  nd }|xs |r|j                  nd }|rt        |j                  «      nd |r|j
                  nd |rt        |j                  «      nd |r|j
                  nd |r|j                  nd |r|j                  nd | j                  | j                  | j                  | j                  |r|j                  nd |r|j                  dœS d dœS )N)r®   Úlevel_display_namer>   Úmodule_display_namerj   Ú
week_labelrn   Úsession_titleÚsession_admin_checkedrF   rE   Úassessment_title)r)   r*   r+   r   ÚslugÚdisplay_namer   rq   rj   Údisplay_labelrn   r¨   Úadmin_checkedr]   )r(   r`   r+   r*   r)   rz   rŒ   s          r2   Ú_programme_summary_from_sessionrÎ   
  s×   € ð �<‰<€DØ
Ò
3¡D�T—[’[¨d€CØÒ5¡s˜#Ÿ)š)°€IáBKÔ.¨y¯~©~Ô>ÐQUÙ8A˜i×4Ò4ÀtÙ>AÔ0°·±Ô:ÀtÙ36˜s×/Ò/¸DÙ)-�d—o’o°4Ù,0�d×(Ò(°dØ!×0Ñ0Ø Ÿ™Ø!(×!6Ñ!6Ø—j‘jÙ*4˜Ÿš¸$Ù0:˜J×,Ñ,ñð ð AEñð r1   c               óÌ   — ddl m} |�|n
t        | «      }t        | |«      }t	        | |j
                  ||¬«      \  }}|�t        ||«      \  }}	t        ||||	¬«      S  ||«      S )Nr   )Úload_user_programme_summaryrš   rÂ   )Ú(app.programme.services.programme_servicerÐ   r¼   r%   r›   r]   r{   rÎ   )
rJ   Úuserry   rÐ   r‹   r…   r(   r`   rŒ   r•   s
             r2   Úresolve_user_programme_summaryrÓ   $  sƒ   € õ Uà!1Ð!=ÑÔCaÐbdÓCe€FÜ*¨2¨tÓ4€MÜ9Ø
ˆD�G‰G�]°VôÑ€GˆZð ÐÜ :¸7ÀFÓ KÑˆ	�:Ü.ØØØØô	
ð 	
ñ ' tÓ,Ð,r1   c                ó  — t        | |«      }t        | «      }t        | |j                  ||¬«      \  }}|€y |j                  |_        |r|j                  nd |_        |€y t        ||«      \  }}|j                  }|�"|j                  |_        |j                  |_
        |�"|j                  |_        |j                  |_        |�"|j                  |_        |j                  |_        |j                  |_        |j"                  |_        y )Nrš   )r%   r¼   r›   r]   Úprogramme_session_idÚprogramme_assessment_idr{   r)   Úlevel_idrË   Ú
level_nameÚ	module_idÚmodule_nameÚweek_idrÌ   Ú	week_namerF   r¨   Úsession_name)	rJ   rÒ   r…   r‹   r(   r`   rŒ   r•   r)   s	            r2   Úsync_user_programme_pointerrÞ   <  sõ   € Ü*¨2¨tÓ4€MÜ+¨BÓ/€FÜ9Ø
ˆD�G‰G�]°VôÑ€GˆZð €ØØ '§
¡
€DÔÙ4> :§=¢=ÀD€DÔ ØÐØä6°wÀÓGÑ€IˆzØ�<‰<€DØÐØ!*§¡ˆÔØ#,×#9Ñ#9ˆÔ ØÐØ",§-¡-ˆÔØ$.×$;Ñ$;ˆÔ!ØÐØ $§¡ˆÔØ"&×"4Ñ"4ˆÔØ&Ÿz™z€MÔØ!(§¡€MÕr1   c               óþ  ‡— t        |«      }t        |«      }t        | ||¬«      }|€E|rC|j                  �7| j	                  t
        |j                  «      }|�t        |j                  «      }t        | |«      \  Š}	t        | |«      }
t        ||¬«      D ]V  }|j                  }t        |j                  d„ ¬«      }|sŒ*t        ˆfd„|D «       «      sŒ?t        | ||j                   |
«       ŒX |
j#                  «        y)zVMark sessions done when all assessments are already completed (fixes stuck open step).r­   Nr‰   c                ó   — | j                   S rR   rS   rU   s    r2   rW   z3_repair_stale_session_completions.<locals>.<lambda>n  r·   r1   rY   c              3  ó:   •K  — | ]  }|j                   ‰v –— Œ y ­wrR   r\   r^   s     €r2   ra   z4_repair_stale_session_completions.<locals>.<genexpr>q  ó   øè ø€ ÐTÉ¸*ˆz�}‰}Ð 5Ô5Éùrb   )r;   r?   r¯   rÙ   Úgetr   r   rq   rO   r   rv   r(   rc   rd   rD   r   r]   r°   )rJ   rC   r®   r>   r…   r±   r²   rŒ   r•   Ú_r³   r�   r(   rd   rL   s                 @r2   Ú!_repair_stale_session_completionsrå   X  sä   ø€ ô (¨
Ó3€OÜ)¨+Ó6ÐÜ˜B¨?È-ÔX€IàÐ¡M°m×6MÑ6MÐ6YØ—V‘VœO¨]×-DÑ-DÓEˆ
ØÐ!Ü8¸¿¹ÓIÐä3°B¸Ó@ÑÐ˜1Ü˜R Ó)€EÜ$ YÐ<L×MˆØ—+‘+ˆÜ˜W×0Ñ0Ñ6RÔSˆÙØÜÓTÉÓTÕTÜ'¨¨G°W·Z±ZÀÕGð Nð 
‡K�K…Mr1   F)r`   rG   c               ó  — |xs |j                   }| j                  t        «      j                  t        j                  |k(  t        j
                  |j                  k(  «      j                  «       }|€t        |¬«      }| j                  |«       |r|j                  nd|_	        |r|j                  nd|_        |r|j                  nd|_        |j                  |_        |j                  |_        |j                  |_        ||_        |S )z?Save progress to member_session_progress for dashboard / trace.N)rC   )r)   rA   r   rB   rC   rF   r]   ÚfirstrI   rÛ   rÌ   rÜ   rj   r¨   rÝ   rn   rG   )rJ   rC   r(   r)   r`   rG   Úweek_rowrK   s           r2   Ú_upsert_track_traceré   v  sÖ   € ð Ò#�w—|‘|€Hà
�‰Ô&Ó'ß	‰Ü!×)Ñ)¨WÑ4Ü!×,Ñ,°·
±
Ñ:ó

÷ 
‰‹ð ð €{Ü#¨GÔ4ˆØ
�‰ˆsŒá!)�(—+’+¨t€C„KÙ.6�H×*Ò*¸D€C„MÙ,4�X×(Ò(¸$€C„NØ—Z‘Z€C„NØ—}‘}€CÔØ ×/Ñ/€CÔØ#€CÔØ€Jr1   Úassessment_completed)r`   ÚactionÚsession_fully_completedc               ó´  — |xs |j                   }|r|j                  nd}|r|j                  nd}	t        di d|“d|	r|	j                  nd“d|	rt        |	j                  «      nd“d|	r|	j                  nd“d|r|j                  nd“d|rt        |j                  «      nd“d|r|j                  nd“d	|r|j                  nd“d
|r|j                  nd“d|r|j                  nd“d|j                  “d|j                  “d|j                  “d|r|j                  nd“d|r|j                  nd“d|“d|“Ž}
| j                  |
«       | j                  «        |
S )zMAppend one row to user_track_traces when user clicks complete on Tracks page.NrC   r×   r®   rØ   rÙ   r>   rÚ   rÛ   rÜ   rj   rF   rÝ   rn   rE   rÉ   rë   rì   r0   )r)   r*   r+   r   r]   r   rÊ   rË   r   rq   rÌ   rj   r¨   rn   rI   r°   )rJ   rC   r(   r)   r`   rë   rì   rè   r*   r+   rK   s              r2   Ú_insert_user_track_tracerî   —  s€  € ð Ò#�w—|‘|€HÙ8@ X§_¢_Àd€FÙ39 6§<¢<¸t€Eä
ò Ùðá"�—’¨øðñ <AÔ+¨E¯J©JÔ7Àdøðñ */�5×%Ò%°Døð	ñ
  &�&—)’)¨4øðñ ?EÔ-¨f¯k©kÔ:È$øðñ ,2�F×'Ò'°tøðñ  (�—’¨Tøðñ -5�(×(Ò(¸$øðñ +3�8×&Ò&¸øðð —:’:ðð —]’]ðð ×-Ò-ðñ (2�j—m’m°tøðñ .8˜×)Ò)¸Tøðñ  ð!ñ" !8ð#€Cð& ‡F�Fˆ3„KØ‡H�H„JØ€Jr1   éd   )Úlimitc               ó6  — | j                  t        «      j                  t        j                  |k(  «      j	                  t        j
                  j                  «       t        j                  j                  «       «      j                  |«      j                  «       S rR   )
rA   r   rB   rC   r»   Ú
created_atÚdescr]   rð   rD   )rJ   rC   rð   s      r2   Úlist_user_track_tracesrô   ¾  sb   € à
�‰”Ó ß	‰”×&Ñ&¨'Ñ1Ó	2ß	‰”.×+Ñ+×0Ñ0Ó2´N×4EÑ4E×4JÑ4JÓ4LÓ	Mß	‰ˆu‹ß	‰‹ðr1   c               óò   — | |v rt         j                  S t        |||¬«      rt         j                  S || k(  rt         j                  S ||j                  k(  r|€t         j                  S t         j
                  S )NrŠ   )r!   Ú	completedre   Úopenr]   Úlocked)rE   r(   Úopen_session_idÚopen_assessment_idrL   rM   s         r2   Ú_assessment_statusrû   È  sv   € ð Ð-Ñ-Ü"×,Ñ,Ð,ÜØØ3Ø-õô
 #×,Ñ,Ð,Ø˜]Ò*Ü"×'Ñ'Ð'Ø˜'Ÿ*™*Ò$Ð);Ð)CÜ"×'Ñ'Ð'Ü×%Ñ%Ð%r1   c               ó¶  ‡ ‡‡‡‡— t        ‰ ‰‰¬«      rt        j                  S |st        j                  S t	        ‰ j
                  d„ ¬«      }|r\t        ˆˆˆˆˆ fd„|D «       «      rt        j                  S t        ˆfd„|D «       «      rt        j                  S t        j                  S ‰‰ j                  k(  rt        j                  S t        j                  S )NrŠ   c                ó   — | j                   S rR   rS   rU   s    r2   rW   z!_session_status.<locals>.<lambda>ñ  rX   r1   rY   c           	   3  óv   •K  — | ]0  }t        |j                  ‰‰‰‰‰¬ «      t        j                  k(  –— Œ2 y­w)©rù   rú   rL   rM   N)rû   r]   r!   r÷   )r_   r`   rL   rM   rú   rù   r(   s     €€€€€r2   ra   z"_session_status.<locals>.<genexpr>ó  sH   øè ø€ ð 
ñ *�
ô Ø—‘ØØ /Ø#5Ø&;Ø#5ôô #×'Ñ'õ(ñ *ùs   ƒ69c              3  ó:   •K  — | ]  }|j                   ‰v –— Œ y ­wrR   r\   r^   s     €r2   ra   z"_session_status.<locals>.<genexpr>   râ   rb   )
re   r!   rö   rø   rc   rd   r†   r÷   rD   r]   )r(   rù   rú   rL   rM   Úprior_session_donerd   s   `````  r2   Ú_session_statusr  à  s¸   ü€ ô ØØ3Ø-õô
 #×,Ñ,Ð,ÙÜ"×)Ñ)Ð)Ü˜×,Ñ,Ñ2NÔO€KÙÜ÷ 
ñ *ó
ô 
ô '×+Ñ+Ð+ÜÓTÉÓTÔTÜ&×0Ñ0Ð0Ü"×)Ñ)Ð)Ø˜'Ÿ*™*Ò$Ü"×'Ñ'Ð'Ü×%Ñ%Ð%r1   c               ó^  — |r"t        d„ |D «       «      rt        j                  S |s"|rt        j                  S t        j                  S |st        j                  S t	        d„ |D «       «      rt        j
                  S t	        d„ |D «       «      rt        j
                  S t        j                  S )Nc              3  óB   K  — | ]  }|t         j                  k(  –— Œ y ­wrR   ©r!   rö   ©r_   rV   s     r2   ra   z_week_status.<locals>.<genexpr>  s   è ø€ ÐcÑRbÈ$ Ô(;×(EÑ(EÕ EÑRbùó   ‚c              3  óB   K  — | ]  }|t         j                  k(  –— Œ y ­wrR   )r!   r÷   r  s     r2   ra   z_week_status.<locals>.<genexpr>  s   è ø€ Ð
IÑ8H°ˆ4Ô&×+Ñ+Õ+Ñ8Hùr  c              3  óB   K  — | ]  }|t         j                  k(  –— Œ y ­wrR   r  r  s     r2   ra   z_week_status.<locals>.<genexpr>  s   è ø€ Ð
NÑ=M°Tˆ4Ô&×0Ñ0Õ0Ñ=Mùr  )rD   r!   rö   rø   r†   r÷   )r)   Úsession_statusesÚprior_week_dones      r2   Ú_week_statusr    sœ   € ñ œCÑcÑRbÓcÔcÜ"×,Ñ,Ð,Ùñ ô  ×)Ñ)ð	
ô %×+Ñ+ð	
ñ
 Ü"×)Ñ)Ð)Ü
Ñ
IÑ8HÓ
IÔIÜ"×'Ñ'Ð'Ü
Ñ
NÑ=MÓ
NÔNÜ"×'Ñ'Ð'Ü×%Ñ%Ð%r1   c               ó|  — | j                  t        «      j                  t        t        j                  «      j                  t
        j                  «      j                  t        j                  «      j                  t        j                  «      «      j                  t        j                  «      }|�#|j                  t        j                  |k(  «      }n:|r8|j                  �,|j                  t        j                   |j                  k(  «      }|j#                  «       }|€t%        t&        j(                  d¬«      ‚|S )NzProgramme level not foundr4   )rA   r   rº   r	   rp   r   rr   r   rs   r   rd   r»   rT   rB   rÊ   r×   r]   rç   r   r   ÚHTTP_404_NOT_FOUND)rJ   r®   r…   rA   rŒ   s        r2   r¯   r¯     sé   € ð 	�‰”Ó ß	‰Üœ×/Ñ/Ó0ß‰\œ/×/Ñ/Ó0ß‰\œ-×0Ñ0Ó1ß‰\Ô*×6Ñ6Ó7ó	

÷ 
‰”.×+Ñ+Ó	,ð 
ð ÐØ—‘œ^×0Ñ0°JÑ>Ó?‰Ù	˜=×1Ñ1Ð=Ø—‘œ^×.Ñ.°-×2HÑ2HÑHÓIˆà—‘“€IØÐÜ¬×(AÑ(AÐJeÔfÐfØÐr1   c               ó
  — t        | |j                  |||¬«       t        | |j                  |||¬«       |�|j                  rt	        | |«       | j
                  s| j                  s| j                  r| j                  «        yy)z\Lightweight stale-session repair on track preview (reseed runs only via POST /track/reseed).©r®   r>   r…   N)	rå   r]   r´   rG   rÞ   ÚnewÚdirtyÚdeletedÚcommit)rJ   rÒ   r…   r®   r²   s        r2   Ú_sync_track_progressr  :  sw   € ô &Ø
Ø�‰ØØ$Ø#õô 'Ø
Ø�‰ØØ$Ø#õð Ð  ]×%?Ò%?Ü# B¨Ô-Ø	‡v‚v�—’˜RŸZšZØ
�	‰	�ð (r1   c          
     ó¾  ‡— t        | |j                  «      \  }}t        | «      }t        | |j                  ||||||¬«      \  Š}	‰€i S t	        ˆfd„t        ||¬«      D «       d «      }
|
€i S |	rdnd|	‰|
j                  j                  |
j                  j                  t        |
j                  j                  «      |j                  t        |j                  «      dœS )N©r>   r…   rL   rM   ry   c              3  óT   •K  — | ]  }|j                   j                  ‰k(  r|–— Œ! y ­wrR   ©r(   r]   ©r_   r�   rù   s     €r2   ra   z%_peek_current_open.<locals>.<genexpr>o  s)   øè ø€ ð 	
áU�Ø�{‰{�~‰~ Ò0ô ÙUùó   ƒ%(r‰   r`   r(   ©ÚtyperE   rF   rÛ   rÙ   r>   r×   r®   )rO   r]   r¼   r¢   Únextrv   r)   r*   r   rq   r   rÊ   )rJ   rÒ   r…   rŒ   r®   r²   rL   rM   ry   rú   Úsession_refrù   s              @r2   Ú_peek_current_openr   W  sî   ø€ ô 1EÀRÈÏÉÓ0QÑ-ÐÐ-Ü5°bÓ9ÐÜ*FØ
Ø�‰ØØ$Ø#Ø3Ø-Ø)ô	+Ñ'€OÐ'ð ÐØˆ	Üó	
ä,¨YÐDTÕUó	
ð
 	ó€Kð ÐØˆ	á 2‘¸	Ø+Ø%Ø×#Ñ#×&Ñ&Ø ×'Ñ'×*Ñ*Ü0°×1CÑ1C×1HÑ1HÓIØ—L‘LÜ.¨y¯~©~Ó>ñ	ð 	r1   T)Úsyncc               ó*  ‡1— t        | |«      }t        |«      }|j                  st        dd d ¬«      S t	        |«      }t        |«      }t        | ||¬«      }	|€E|rC|j                  �7| j                  t        |j                  «      }
|
�t        |
j                  «      }|rt        | ||||¬«       t        | |j                  «      \  }}t        | «      }t!        | |j                  |	||||¬«      }d}|�/t#        |	|«      }
|
d uxr t%        | |j                  |	|
||||¬«      }|xr |}t'        |	|¬«      }t)        ||«      }t+        ||«      }d }|r2|�0d|cxk  rt-        |«      k  rn n||   j.                  j0                  }t3        | |j                  |	|||||¬	«      \  Š1}d }‰1�“t5        ˆ1fd
„t'        |	|¬«      D «       d «      }|�q|rdnd|‰1|j.                  j                  |j6                  j                  t        |j6                  j                  «      |	j                  t9        |	j:                  «      dœ}g }d}d}d}t=        |	j>                  d„ ¬«      }|�+|D �cg c]  }t        |j                  «      |k(  r|‘Œ }}|r|}|D �]\  }g }d} d}!d}"t=        |j@                  d„ ¬«      D �]–  }#g }$d}%g }&d}'t=        |#jB                  d„ ¬«      D �]Œ  }(g })|xr |d uxr ||k  }*|*rtD        jF                  }+n$|stD        jH                  }+ntK        |(‰1||||'¬«      }+|&jM                  |+«       t=        |(jN                  d„ ¬«      D ]  },|*rtD        jF                  }-n.|stD        jH                  }-ntQ        |,j                  |(‰1|||¬«      }-|)jM                  tS        |,j                  |,jT                  |,jV                  |-¬«      «       Œ� |+tD        jF                  k(  r|%dz  }%| dz  } |dz  }|!dz  }!|dz  }|+tD        jF                  k(  }'|dz  }|$jM                  tY        |(j                  |(jZ                  |(jV                  |(j\                  |+t-        |)«      |)¬«      «       �Œ� |stD        jH                  }.nu|&se|�c|#j0                  |k  rtD        jF                  }.nQ|#j0                  |kD  rtD        jH                  }.n1|"rtD        j^                  ntD        jH                  }.nta        |#|&|"¬«      }.|.tD        jF                  k(  }"|jM                  tc        |#j                  |#j0                  |#jd                  |.t-        |$«      |%|$¬«      «       �Œ™ tD        jH                  }/|rEtg        d„ |D «       «      rtD        jF                  }/n"ti        d„ |D «       «      rtD        j^                  }/|jM                  tk        |j                  t        |j                  «      |jl                  |/|| |!¬«      «       �Œ_ d }0|�[|jn                  |jp                  |j                  |jr                  |jt                  |jv                  |jx                  |jz                  dœ}0t        d||||0t}        |	j                  t9        |	j:                  «      |	jl                  |||¬«      ¬«      S c c}w ) NF)Úregistration_completedÚcurrent_openr+   r­   ©r®   r²   rŠ   Tr‰   r   r  c              3  óT   •K  — | ]  }|j                   j                  ‰k(  r|–— Œ! y ­wrR   r  r  s     €r2   ra   z&get_programme_track.<locals>.<genexpr>Õ  s)   øè ø€ ð áY�CØ—;‘;—>‘> _Ò4ô ÙYùr  r`   r(   r  c                ó   — | j                   S rR   rS   rU   s    r2   rW   z%get_programme_track.<locals>.<lambda>í  r”   r1   rY   c                ó   — | j                   S rR   ri   rU   s    r2   rW   z%get_programme_track.<locals>.<lambda>þ  rk   r1   c                ó   — | j                   S rR   rm   rU   s    r2   rW   z%get_programme_track.<locals>.<lambda>  ro   r1   )rù   rú   rL   rM   r  c                ó   — | j                   S rR   rS   rU   s    r2   rW   z%get_programme_track.<locals>.<lambda>  s   € ÈtÏÊr1   rÿ   )r]   rT   r¨   r   é   )r]   rn   r¨   rÍ   r   Úassessments_countrd   )r  )r]   rj   rÌ   r   Úsessions_countÚcompleted_sessions_countrs   c              3  óV   K  — | ]!  }|j                   t        j                  k(  –— Œ# y ­wrR   )r   r!   rö   ©r_   r)   s     r2   ra   z&get_programme_track.<locals>.<genexpr>b  s!   è ø€ ÐVÉIÀD�4—;‘;Ô"5×"?Ñ"?Õ?ÉIùó   ‚')c              3  óV   K  — | ]!  }|j                   t        j                  k(  –— Œ# y ­wrR   )r   r!   r÷   r0  s     r2   ra   z&get_programme_track.<locals>.<genexpr>d  s!   è ø€ ÐSÉÀ�T—[‘[Ô$7×$<Ñ$<Õ<Éùr1  )r]   rq   rË   r   rr   rM   Útotal_sessions)r×   rØ   rÙ   rÚ   rÛ   rÜ   rF   rÝ   )r]   rÊ   rË   rp   rM   r3  )r#  Úlevel_unlockedÚmodule_unlockedr$  Úregistration_selectionr+   )?r%   r$   rG   r   r;   r?   r¯   rÙ   rã   r   r   rq   r  rO   r]   r¼   r�   r‘   r˜   rv   r‡   r‚   Úlenr)   rj   r¢   r  r*   r   rÊ   rc   rp   rr   rs   r!   rö   rø   r  rt   rd   rû   r   rT   r¨   r"   rn   rÍ   r÷   r  r#   rÌ   rD   r†   r    rË   r×   rØ   rÚ   rÛ   rÜ   rF   rÝ   r   )2rJ   rÒ   r+   r>   r!  r…   Úregistration_stater®   r²   rŒ   r•   rL   rM   ry   r4  r5  Úcontent_unlockedr~   r   r¸   Úanchor_week_indexrú   r$  r  Úmodules_outÚlevel_completedÚlevel_totalÚglobal_session_indexrp   r*   Úfiltered_modulesÚ	weeks_outÚmodule_completedÚmodule_totalr  r)   Úsessions_outÚweek_completedr
  r  r(   Úassessments_outÚbefore_anchorÚsession_statusr`   Úassessment_statusÚweek_statusÚmodule_statusr6  rù   s2                                                    @r2   Úget_programme_trackrK  „  sä  ø€ ô +¨2¨tÓ4€MÜ2°=ÓAÐØ×*Ò*Ü%Ø#(ØØô
ð 	
ô # 5Ó)€JÜ)¨+Ó6ÐÜ˜B¨:À]ÔS€IàÐ¡M°m×6MÑ6MÐ6YØ—V‘VœO¨]×-DÑ-DÓEˆ
ØÐ!Ü8¸¿¹ÓIÐáÜØØØØ!Ø-õ	
ô 1EÀRÈÏÉÓ0QÑ-ÐÐ-Ü5°bÓ9ÐÜ)Ø
Ø�‰ØØØØ3Ø-ô€Nð €OØÐ#Ü)¨)Ð5EÓFˆ
Ø$¨DÐ0ò 	
Ô5JØØ�G‰GØØØØØ"7Ø1ô	6
ˆð &Ò9¨/Ðä(¨Ð@PÔQ€LÜ5°lÀMÓRÐÜ(¨Ð7HÓI€LØ$(ÐÙÐ)Ð5¸!¸|Ô:_ÌcÐR^ÓN_Õ:_Ø(¨Ñ6×;Ñ;×FÑFÐÜ*FØ
Ø�‰ØØ$Ø#Ø3Ø-Ø)ô	+Ñ'€OÐ'ð !%€LØÐ"Üóä0°ÐHXÕYóð
 ó
ˆð Ð"á(:™À	Ø!3Ø-Ø&×+Ñ+×.Ñ.Ø(×/Ñ/×2Ñ2Ü8¸×9KÑ9K×9PÑ9PÓQØ%ŸL™LÜ6°y·~±~ÓFñ	ˆLð )+€KØ€OØ€KØÐä�Y×&Ñ&Ñ,HÔI€GØÐ#ñ "ó
á!�Ü(¨¯©Ó5Ð9IÒIò Ø!ð 	ð 
ñ Ø&ˆGäˆØ(*ˆ	ØÐØˆØˆä˜6Ÿ<™<Ñ-I×JÐJˆDØ24ˆLØˆNØ:<ÐØ!%Ðä! $§-¡-Ñ5U×VÐV�Ø<>�à$ò <Ø)°Ð5ò<à,¨|Ñ;ð ñ !Ü%8×%BÑ%B‘NÙ)Ü%8×%?Ñ%?‘Nä%4ØØ(7Ø+=Ø.CØ+=Ø+=ô&�Nð !×'Ñ'¨Ô7ä"(¨×)<Ñ)<ÑB^×"_�JÙ$Ü,?×,IÑ,IÑ)Ù-Ü,?×,FÑ,FÑ)ä,>Ø&ŸM™MØ#Ø,;Ø/AØ2GØ/Aô-Ð)ð $×*Ñ*Ü*Ø)Ÿ}™}Ø'1×'<Ñ'<Ø",×"2Ñ"2Ø#4ô	õð #`ð. "Ô%8×%BÑ%BÒBØ" aÑ'�NØ$¨Ñ)Ð$Ø# qÑ(�OØ Ñ!�Ø˜qÑ �Ø%3Ô7J×7TÑ7TÑ%TÐ"Ø$¨Ñ)Ð$à×#Ñ#Ü#Ø"Ÿ:™:Ø'.×'=Ñ'=Ø%Ÿm™mØ&-×&;Ñ&;Ø-Ü*-¨oÓ*>Ø$3ôö
ðo WñF $Ü1×8Ñ8‘Ù%Ð*;Ð*GØ—?‘?Ð%6Ò6Ü"5×"?Ñ"?‘KØ—_‘_Ð'8Ò8Ü"5×"<Ñ"<‘Ká>MÔ"5×":Ò":ÔSf×SmÑSm‘Kä*¨4Ð1AÐSbÔc�Ø)Ô-@×-JÑ-JÑJˆOà×ÑÜØ—w‘wØ#Ÿ™Ø"&×"4Ñ"4Ø&Ü#& |Ó#4Ø-;Ø)ôö
ðm KôD ,×2Ñ2ˆÙÜÑVÉIÓVÔVÜ 3× =Ñ =‘ÜÑSÉÓSÔSÜ 3× 8Ñ 8�à×ÑÜØ—9‘9Ü.¨v¯{©{Ó;Ø#×0Ñ0Ø$ØØ#3Ø+ôö
	
ð_ ðv +/ÐØÐ à%×.Ñ.Ø'×2Ñ2Ø&×0Ñ0Ø(×4Ñ4Ø$×,Ñ,Ø&×0Ñ0Ø'×2Ñ2Ø)×6Ñ6ñ	"
Ðô "Ø#Ø%Ø'Ø!Ø5ÜØ�|‰|Ü)¨)¯.©.Ó9Ø"×/Ñ/ØØ.Ø&ô
ôð ùòc
s   É!!Zc                ór  ‡— |sy | j                  t        «      j                  t        t        j                  «      «      j                  t        j                  |k(  «      j                  «       }|€y t        |j                  d„ ¬«      }|r-t        ˆfd„|D «       «      rt        | ||j                  «       y y y )Nc                ó   — | j                   S rR   rS   rU   s    r2   rW   z:_maybe_complete_session_after_assessment.<locals>.<lambda>¡  rX   r1   rY   c              3  ó:   •K  — | ]  }|j                   ‰v –— Œ y ­wrR   r\   r^   s     €r2   ra   z;_maybe_complete_session_after_assessment.<locals>.<genexpr>¢  s   øè ø€ Ð`ÑT_Àj˜:Ÿ=™=Ð,AÔAÑT_ùrb   )rA   r   rº   r	   rd   rB   r]   rç   rc   rD   r   )rJ   rC   rF   rL   r(   rd   s      `  r2   Ú(_maybe_complete_session_after_assessmentrO  ‘  s–   ø€ ñ Øà
�‰Ô!Ó"ß	‰”Ô.×:Ñ:Ó;Ó	<ß	‰Ô ×#Ñ# zÑ1Ó	2ß	‰‹ð	 ð €ØÜ˜×,Ñ,Ñ2NÔO€KÙ”sÓ`ÑT_Ó`Ô`Ü# B¨°·±Õ<ð a€{r1   c           
     óÄ  ‡— t        | |«      }|�|j                  st        t        j                  d¬«      ‚|j
                  }|j                  }|€C|j                  �7| j                  t        |j                  «      }|�t        |j                  «      }|€C|j                  �7| j                  t        |j                  «      }|�t        |j                  «      }|j                   €'|j"                  €t        t        j                  d¬«      ‚|j                   �'|j"                  �t        t        j                  d¬«      ‚t%        |«      }t'        |«      }	|	€C|j                  �7| j                  t        |j                  «      }|�t        |j                  «      }	t)        | ||¬«      }t+        | «      }
t-        | |j.                  «      \  Š}t1        | |j.                  |||
‰|¬«      st        t        j2                  d¬«      ‚d }|j"                  �¶| j5                  t6        «      j9                  t;        t6        j<                  «      j;                  t>        j@                  «      «      jC                  t6        j.                  |j"                  k(  «      jE                  «       }|��|j<                  �ù|j<                  j@                  }nâ|j                   �Ö| j                  tF        |j                   «      }|�´| j5                  t6        «      j9                  t;        t6        j<                  «      j;                  t>        j@                  «      «      jC                  t6        j.                  |j"                  k(  «      jE                  «       }|�"|j<                  �|j<                  j@                  }|�8tI        | |j.                  ||||
‰|¬«      st        t        j2                  d¬«      ‚tK        | ||||	¬	«       tM        | |||||	¬	«      }|j                   ��³t-        | |j.                  «      \  Š}|j                   ‰v rtO        | |||d
¬«      S |j                  d«      }||j                   k7  rvtQ        | |j.                  |||¬«       | jS                  «        tM        | |||||	¬	«      }|j                  d«      }||j                   k7  rt        t        j                  d¬«      ‚tU        | |j.                  |j                   «       | jS                  «        t-        | |j.                  «      \  Š}| j                  tF        |j                   «      }| j5                  t6        «      j9                  t;        t6        j<                  «      j;                  t>        j@                  «      j;                  t        j
                  «      t;        t6        jV                  «      «      jC                  t6        j.                  |r|j"                  ndk(  «      jE                  «       }|�„tY        |jV                  d„ ¬«      }t[        |xr t]        ˆfd„|D «       «      «      }t_        | |j.                  ||j<                  ||¬«       ta        | |j.                  ||j<                  |d|¬«       tc        | |j.                  |j                  d«      xs d‰«       �nç| j5                  t6        «      j9                  te        t6        jV                  «      t;        t6        j<                  «      j;                  t>        j@                  «      j;                  t        j
                  «      «      jC                  t6        j.                  |j"                  k(  «      jE                  «       }|€t        t        jf                  d¬«      ‚tY        |jV                  d„ ¬«      }t-        | |j.                  «      \  Š}|r/t]        ˆfd„|D «       «      sTt        t        j                  d¬«      ‚|j                  d«      |j"                  k7  rt        t        j                  d¬«      ‚ti        | |j.                  |j"                  «       t_        | |j.                  ||j<                  d¬«       ta        | |j.                  ||j<                  dd¬«       tk        | |«       | jm                  «        	 dd l7m8}  |«        dd!l:m;} |j                   �g�e|r|jx                  r|jx                  nd xs0 |r|jz                  rd"|jz                  › �nd xs d#|j                   › �} || |||j                   ¬$«       nt|j"                  �h|�f|jx                  r|jx                  j}                  «       n)|jz                  rd"|jz                  › �nd"|j"                  › �} || |||j"                  ¬$«       tO        | |||d
¬«      S # tr        $ r Y �Œ	w xY w)%Nz;Complete registration steps before updating track progress.r4   z!Send assessment_id or session_id.z-Send only one of assessment_id or session_id.r­   rŠ   zGComplete all sessions in the previous level before updating this level.zIComplete all sessions in the previous module before updating this module.r%  F)r+   r>   r!  rE   r  z-This assessment is not the current open step.r   c                ó   — | j                   S rR   rS   rU   s    r2   rW   z%complete_track_item.<locals>.<lambda>@  s   € ÀtÇÂr1   rY   c              3  ó:   •K  — | ]  }|j                   ‰v –— Œ y ­wrR   r\   )r_   rV   rL   s     €r2   ra   z&complete_track_item.<locals>.<genexpr>C  s   øè ø€ ÐQÁ[¸T˜Ÿ™Ð#8Ô8Á[ùrb   )r(   r)   r`   rG   rê   )r(   r)   r`   rë   rì   rF   zSession not foundc                ó   — | j                   S rR   rS   rU   s    r2   rW   z%complete_track_item.<locals>.<lambda>h  r·   r1   c              3  ó:   •K  — | ]  }|j                   ‰v –— Œ y ­wrR   r\   r^   s     €r2   ra   z&complete_track_item.<locals>.<genexpr>k  s   øè ø€ Ð\ÑP[À*�z—}‘}Ð(=Ô=ÑP[ùrb   z/Complete all assessments in this session first.z*This session is not the current open step.T)r(   r)   rG   Úsession_completed)r(   r)   rë   rì   )Úinvalidate_list_users_cache)Únotify_admin_track_submitzSession zAssessment )Ú
step_labelÚreference_id)?r%   rG   r   r   r9   r+   r>   r×   rã   r   r   rÊ   rÙ   r   r   rq   rE   rF   r;   r?   r¯   r¼   rO   r]   r�   ÚHTTP_403_FORBIDDENrA   r   rº   r   r)   r   r*   rB   rç   r   r˜   r  r   rK  rå   r°   r   rd   rc   ÚboolrD   ré   rî   rO  r	   r  r   rÞ   r  Úapp.routers.admin.usersrV  Ú	ExceptionÚ'app.services.admin_notification_servicerW  r¨   rn   r7   )rJ   rÒ   Úbodyr…   Úlevel_filterÚmodule_filterrŒ   r•   r±   r²   ry   rM   Útarget_module_rowÚsession_rowÚassessment_rowr$  rä   Úexpected_idr`   r(   rd   Úall_donerV  rW  Úsession_labelrL   s                            @r2   Úcomplete_track_itemrh  ¦  s·  ø€ Ü*¨2¨tÓ4€MØÐ M×$>Ò$>ÜÜ×3Ñ3ØPô
ð 	
ð
 —:‘:€LØ×$Ñ$€MØÐ × 6Ñ 6Ð BØ—F‘Fœ>¨=×+AÑ+AÓBˆ	ØÐ Ü3°I·N±NÓCˆLØÐ ×!8Ñ!8Ð!DØ—V‘VœO¨]×-DÑ-DÓEˆ
ØÐ!Ü5°j·o±oÓFˆMà×ÑÐ! d§o¡oÐ&=ÜÜ×3Ñ3Ø6ô
ð 	
ð ×ÑÐ%¨$¯/©/Ð*EÜÜ×3Ñ3ØBô
ð 	
ô
 (¨Ó5€OÜ)¨-Ó8ÐØÐ M×$;Ñ$;Ð$GØ—V‘VœO¨]×-DÑ-DÓEˆ
ØÐ!Ü8¸¿¹ÓIÐä˜B¨?È-ÔX€IÜ5°bÓ9ÐÜ0DÀRÈÏÉÓ0QÑ-ÐÐ-ÜØ
Ø�‰ØØØØ3Ø-õô Ü×1Ñ1Ø\ô
ð 	
ð
 15ÐØ‡�Ð"à�H‰HÔ%Ó&ß‰W”ZÔ 0× 5Ñ 5Ó6×AÑAÄ-×BVÑBVÓWÓXß‰VÔ$×'Ñ'¨4¯?©?Ñ:Ó;ß‰U‹Wð	 	ð Ñ" {×'7Ñ'7Ð'CØ +× 0Ñ 0× 7Ñ 7ÑØ	×	Ñ	Ð	'ØŸ™Ô 3°T×5GÑ5GÓHˆØÐ%à—‘Ô)Ó*ß‘œÔ$4×$9Ñ$9Ó:×EÑEÄm×FZÑFZÓ[Ó\ß‘Ô(×+Ñ+¨~×/HÑ/HÑHÓIß‘“ð	 ð Ð&¨;×+;Ñ+;Ð+GØ$/×$4Ñ$4×$;Ñ$;Ð!ØÐ$Ô-BØ
Ø�‰ØØØØØ3Ø-õ	.ô Ü×1Ñ1Ø^ô
ð 	
ô
 Ø
ØØØ"Ø)õô &Ø
ØØØØ"Ø)ô€Lð ×ÑÑ%Ü#7¸¸D¿G¹GÓ#DÑ Ð˜qØ×ÑÐ!6Ñ6Ü&Ø�D ¸-Èeôð ð #×&Ñ& Ó7ˆØ˜$×,Ñ,Ò,Ü-ØØ—‘Ø'Ø)Ø+õð �H‰HŒJÜ-ØØØØØ*Ø!1ôˆLð '×*Ñ*¨?Ó;ˆKØ˜d×0Ñ0Ò0Ü#Ü &× ;Ñ ;ØJôð ô 	   D§G¡G¨T×-?Ñ-?Ô@Ø
�‰Œ
Ü#7¸¸D¿G¹GÓ#DÑ Ð˜qØ—V‘VÔ/°×1CÑ1CÓDˆ
à�H‰HÔ%Ó&ß‰WÜÔ+×0Ñ0Ó1ß‘œM×0Ñ0Ó1ß‘œO×1Ñ1Ó2ÜÔ+×7Ñ7Ó8ó	÷ ‰VÔ$×'Ñ'ÁZ¨J×,AÒ,AÐUVÑWÓXß‰U‹Wð 	ð ÐÜ  ×!4Ñ!4Ñ:VÔWˆKÜØò RÜÓQÁ[ÓQÓQóˆHô  ØØ—‘ØØ—\‘\Ø%Ø%õô %ØØ—‘ØØ—\‘\Ø%Ø-Ø(0õô 	1Ø�—‘˜×)Ñ)¨,Ó7Ò<¸1Ð>Sö	
ð
 �H‰HÔ%Ó&ß‰WÜÔ-×9Ñ9Ó:ÜÔ+×0Ñ0Ó1ß‘œM×0Ñ0Ó1ß‘œO×1Ñ1Ó2ó	÷ ‰VÔ$×'Ñ'¨4¯?©?Ñ:Ó;ß‰U‹Wð 	ð ˆ?Ü¬F×,EÑ,EÐNaÔbÐbä˜W×0Ñ0Ñ6RÔSˆÜ#7¸¸D¿G¹GÓ#DÑ Ð˜qÙÜÓ\ÑP[Ó\Ô\Ü#Ü &× ;Ñ ;ØLôð ð ×Ñ˜lÓ+¨t¯©Ò>ÜÜ"×7Ñ7ØCôð ô
 	$ B¨¯©°·±ÔAÜØØ�G‰GØØ—‘Øõ	
ô 	!ØØ�G‰GØØ—‘Ø&Ø$(õ	
ô    DÔ)Ø‡I�I„KðÝGá#Ô%õ Rà×ÑÐ%¨*Ð*@á%¨'¯-ª-ˆW�]Š]¸Tò 2Ù7>À7×CYÒCY�(˜7×1Ñ1Ð2Ñ3Ð_cò2à˜T×/Ñ/Ð0Ð1ð 	ñ
 	"Ø� ¸T×=OÑ=Oö	
ð 
�‰Ð	$¨Ð)<ð �}Š}ð �M‰M×ÑÔ!à9@×9OÒ9O�H˜W×3Ñ3Ð4Ñ5ÐW_Ð`d×`oÑ`oÐ_pÐUqð 	ñ
 	" " d°}ÐSW×SbÑSbÕcäØ
ˆD˜°-Àeôð øô- ò Úðús   ãg ç	gçg)r+   ú
str | NoneÚreturnri  )r>   ri  rj  ri  )rJ   r   rC   Úintrj  ztuple[set[int], set[int]])r(   r   rL   úset[int]rM   rl  rj  r[  )r+   r   r>   ri  rj  úlist[_TrackSessionRef])r(   r   ry   úlist[ProgrammeLevel]rj  z4tuple[ProgrammeLevel | None, ProgrammeModule | None])r~   rm  r   ú
int | Nonerj  rk  )r~   rm  r…   úUserRegisterStep | Nonerj  ro  )
r‹   rn  rŒ   r   rL   rl  rM   rl  rj  r[  )rJ   r   rC   rk  rŒ   r   r…   rp  ry   rn  rL   rl  rM   rl  rj  r[  )rŒ   r   r>   ri  rj  úProgrammeModule | None)
rŒ   r   r•   r   rL   rl  rM   rl  rj  r[  )rJ   r   rC   rk  rŒ   r   r•   r   r…   rp  ry   rn  rL   rl  rM   rl  rj  r[  )rJ   r   rC   rk  rŒ   r   r>   ri  r…   rp  rL   rl  rM   rl  ry   rn  rj  útuple[int | None, int | None])r(   r   r)   r   rj  r[  )rJ   r   rC   rk  r®   ri  r>   ri  r…   rp  rj  ÚNone)r+   r   r>   ri  rL   rl  rM   rl  r   ro  rj  rr  )rJ   r   rj  rn  )r‹   rn  rj  rm  )
rJ   r   rC   rk  r…   rp  ry   úlist[ProgrammeLevel] | Nonerj  z:tuple[ProgrammeSession | None, ProgrammeAssessment | None])
r(   r   r`   úProgrammeAssessment | Noner+   zProgrammeLevel | Noner*   rq  rj  Údict)rJ   r   rÒ   r
   ry   rt  rj  zdict | None)rJ   r   rÒ   r
   rj  rs  )rJ   r   rC   rk  r(   r   r)   úProgrammeWeek | Noner`   ru  rG   r[  rj  r   )rJ   r   rC   rk  r(   r   r)   rw  r`   ru  rë   Ústrrì   r[  rj  r   )rJ   r   rC   rk  rð   rk  rj  zlist[UserTrackTrace])rE   rk  r(   r   rù   ro  rú   ro  rL   rl  rM   rl  rj  r!   )r(   r   rù   ro  rú   ro  rL   rl  rM   rl  r  r[  rj  r!   )r)   r   r
  zlist[TrackProgressStatus]r  r[  rj  r!   )rJ   r   r®   ri  r…   rp  rj  r   )rJ   r   rÒ   r
   r…   rp  r®   ri  r²   ri  rj  rs  )rJ   r   rÒ   r
   r…   rp  rŒ   r   r®   ri  r²   ri  rj  rv  )rJ   r   rÒ   r
   r+   ri  r>   ri  r!  r[  rj  r   )
rJ   r   rC   rk  rF   rk  rL   rl  rj  rs  )rJ   r   rÒ   r
   r_  r   rj  r   )RÚ
__future__r   Údataclassesr   Úfastapir   r   Úsqlalchemy.ormr   r   r	   Ú
app.modelsr
   r   r   r   r   Úapp.models_adminr   Úapp.programme.models_programmer   r   r   r   r   r   r   r   r   rÑ   r   r   r   Úapp.schemasr   r   r   r   r    r!   r"   r#   Ú'app.services.user_register_step_servicer$   r%   r'   r;   r?   rO   re   rv   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   rK  rO  rh  r0   r1   r2   Ú<module>r‚     sÌ  ðÝ "å !ç )ß <Ñ <ç lÕ lÝ 2÷
÷ 
õ 
÷ñ ÷
	÷ 	ó 	÷ð ÷ð ó ðó	ó	ó5ð(	,Øð	,ð $ð	,ð !ð		,ð
 
ó	,ðØðð ðð ó	ð.Øðà*ðð :óð	Ø(ð	à!ð	ð 	ó	ð	Ø(ð	à*ð	ð ó	ðØ ðàðð $ð	ð
 !ðð 
óð.Øðàðð ðð +ð	ð
 +ðð $ðð !ðð 
óð2	Øð	àð	ð ó	ðØðàðð $ð	ð
 !ðð 
óð6!Øð!àð!ð ð!ð  ð	!ð
 +ð!ð +ð!ð $ð!ð !ð!ð 
ó!ðHRØðRàðRð ðRð
 ðRð +ðRð $ðRð !ðRð +ðRð #óRój`ðØðàðð ð	ð
 ðð +ðð 
óð4"Øð"ð ð"ð $ð	"ð
 !ð"ð "ð"ð #ó"óJó$ð 59ñ-Øð-àð-ð +ð-ð
 2ð-ð @ó-ðh $(Ø%)ñØðà*ðð !ð	ð
 #ðð 
óð< 59ñ	-Øð-à
ð-ð 2ð	-ð
 ó-ó0/ð8Øðàðð ð	ð
 ðð +ðð 
óðH .2ØñØðàðð ð	ð
 ðð +ðð ðð óðN .2Ø(Ø$)ñ$Øð$àð$ð ð	$ð
 ð$ð +ð$ð ð$ð "ð$ð ó$ðN GJõ ð&Øð&àð&ð  ð	&ð
 #ð&ð $ð&ð !ð&ð ó&ð0%&Øð%&ð  ð%&ð #ð	%&ð
 $ð%&ð !ð%&ð ð%&ð ó%&ðP&Ø
ð&à/ð&ð ð	&ð
 ó&ð.Øðð ðð +ð	ð
 óð6Øðà
ðð +ðð
 ðð !ðð 
óð:*Øð*à
ð*ð +ð*ð ð	*ð ð*ð !ð*ð 
ó*ðf ñJØðJà
ðJð ð	Jð
 ðJð ðJð óJðZ=Øð=àð=ð ð=ð $ð	=ð
 
ó=ô*r1   