Ë
    
Á'j¯…  ã                  óÜ  — 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 d dlmZmZmZmZmZmZmZmZmZmZmZmZ d*d„Zd+d„Zd,d„Zd-d	„Zd+d
„Zd+d„Zddœd.d„Z ddœd.d„Z!d/d„Z"ddœd.d„Z#ddœd.d„Z$d0d„Z%ddœ	 	 	 	 	 d1d„Z&	 	 	 	 	 	 	 	 	 	 	 	 d2d„Z'd3d„Z(	 	 	 	 	 	 	 	 	 	 	 	 	 	 d4d„Z) G d„ d«      Z*d5d„Z+	 d6	 	 	 	 	 	 	 	 	 d7d„Z,	 d6	 	 	 	 	 	 	 	 	 d8d„Z-	 d6	 	 	 	 	 	 	 	 	 d9d„Z.	 d6	 	 	 	 	 	 	 	 	 d:d „Z/	 d6	 	 	 	 	 	 	 	 	 d;d!„Z0	 d6	 	 	 	 	 	 	 	 	 d<d"„Z1	 d6	 	 	 	 	 	 	 	 	 	 	 	 	 d=d#„Z2	 d6	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d>d$„Z3	 d6	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d?d%„Z4	 	 	 	 	 	 	 	 	 	 d@d&„Z5dAd'„Z6d(„ Z7dBd)„Z8y)Cé    )Úannotations)ÚfuncÚtext)ÚSessionÚ
joinedloadÚselectinload)ÚUserÚUserCompletedAssessmentÚUserCompletedSession)ÚCSV_LEVEL_SLUGSÚLEVEL_DISPLAY_DEFAULTSÚProgrammeAssessmentÚProgrammeLevelÚProgrammeModuleÚProgrammeSessionÚProgrammeWeekÚnormalize_programme_module_kindÚparse_upp_assessment_csvÚprogramme_level_slug_strÚprogramme_module_kind_strÚresolve_upp_csv_pathc                ó.  — ddl m} 	  || «      }t        |j                  «       «      }d|v r&|j                  d«      D �ch c]  }|d   ’Œ	 }}d|vryd|v r•|j                  d«      D �ch c]  }|d   ’Œ	 }}d	|vry	 |j                  d«      }|D ]X  }|j                  d«      xs d
j                  «       }|j                  d«      xs d
j                  «       }	|dk(  s
d|	v sŒSd|	v sŒX y y# t        $ r Y yw xY wc c}w c c}w # t        $ r g }Y Œ„w xY w)z¾
    True if programme tables exist but do not match current models (e.g. missing `kind`
    on programme_modules). `create_all()` never ALTERs old tables, so we must drop and recreate.
    r   )ÚinspectFÚprogramme_modulesÚnameÚkindTÚprogramme_sessionsÚadmin_checkedÚ ÚsqltextÚ!ck_programme_session_number_rangeÚsession_numberz<= 3)	Ú
sqlalchemyr   ÚsetÚget_table_namesÚ	ExceptionÚget_columnsÚget_check_constraintsÚgetÚlower)
Úenginer   ÚinspÚnamesÚcÚcolsÚchecksÚcheckr   r    s
             úM/var/www/html/kelly/kelly-backend/app/programme/services/programme_service.pyÚprogramme_schema_outdatedr3      sK  € õ
 #ðÙ�v‹ˆÜ�D×(Ñ(Ó*Ó+ˆð ˜eÑ#Ø#'×#3Ñ#3Ð4GÔ#HÓIÑ#H˜a��&“	Ð#HˆÐIØ˜ÑØà˜uÑ$Ø#'×#3Ñ#3Ð4HÔ#IÓJÑ#I˜a��&“	Ð#IˆÐJØ $Ñ&Øð	Ø×/Ñ/Ð0DÓEˆFó ˆEØ—I‘I˜fÓ%Ò+¨×2Ñ2Ó4ˆDØ—y‘y Ó+Ò1¨r×8Ñ8Ó:ˆGØÐ:Ò:Ø  GÒ+°¸'Ò0Aáð ð øô1 ò Ùðüò Jùò
 Køô
 ò 	ØŠFð	ús/   ˆ!C- Á C<Á*DÁ>D Ã-	C9Ã8C9ÄDÄDc                óŽ  — ddl m}m} t        | j                  «      j                  «       }	  || «      }d|j                  «       vry|j                  d«      D �ch c]  }|d   ’Œ	 }}| j                  «       5 }d|vr4d|v r|j                   |d«      «       n	 |j                   |d	«      «       d
|vr4d|v r|j                   |d«      «       n	 |j                   |d«      «       ddd«       d|vry	  || «      }t        |j                  «       «      }d|vsd|vryd}	d}
| j                  «       5 }|j                   ||	«      «       |j                   ||
«      «       ddd«       yc c}w # t        $ r Y yw xY w# t        $ r Y ŒÛw xY w# t        $ r Y Œ²w xY w# 1 sw Y   Œ·xY w# t        $ r Y yw xY w# 1 sw Y   yxY w)z¥
    Add users.programme_session_id and users.programme_assessment_id when the table
    predates those ORM fields. create_all() does not ALTER existing tables.
    r   ©r   r   ÚusersNr   Úprogramme_session_idÚ
postgresqlzGALTER TABLE users ADD COLUMN IF NOT EXISTS programme_session_id INTEGERz9ALTER TABLE users ADD COLUMN programme_session_id INTEGERÚprogramme_assessment_idzJALTER TABLE users ADD COLUMN IF NOT EXISTS programme_assessment_id INTEGERz<ALTER TABLE users ADD COLUMN programme_assessment_id INTEGERr   Úprogramme_assessmentsaæ  
        DO $$ BEGIN
            IF NOT EXISTS (
                SELECT 1 FROM pg_constraint c
                JOIN pg_class t ON c.conrelid = t.oid
                WHERE t.relname = 'users' AND c.conname = 'users_programme_session_id_fkey'
            ) THEN
                ALTER TABLE users ADD CONSTRAINT users_programme_session_id_fkey
                FOREIGN KEY (programme_session_id) REFERENCES programme_sessions(id) ON DELETE SET NULL;
            END IF;
        END $$;
    aò  
        DO $$ BEGIN
            IF NOT EXISTS (
                SELECT 1 FROM pg_constraint c
                JOIN pg_class t ON c.conrelid = t.oid
                WHERE t.relname = 'users' AND c.conname = 'users_programme_assessment_id_fkey'
            ) THEN
                ALTER TABLE users ADD CONSTRAINT users_programme_assessment_id_fkey
                FOREIGN KEY (programme_assessment_id) REFERENCES programme_assessments(id) ON DELETE SET NULL;
            END IF;
        END $$;
    )r#   r   r   ÚstrÚurlr*   r%   r'   r&   ÚbeginÚexecuter$   )r+   r   r   r<   r,   r.   r/   Úconnr-   Ú
fk_sessionÚfk_assessments              r2   Úsync_users_programme_columnsrB   <   sÒ  € ÷
 )ä
ˆf�j‰j‹/×
Ñ
Ó
!€CðÙ�v‹ˆØ˜$×.Ñ.Ó0Ñ0ØØ#'×#3Ñ#3°GÔ#<Ó=Ñ#<˜a��&“	Ð#<ˆÐ=ð 
�‰Œ˜4Ø!¨Ñ-Ø˜sÑ"Ø—‘™TÐ"kÓlÕmðØ—L‘L¡Ð&aÓ!bÔcð %¨DÑ0Ø˜sÑ"Ø—‘™TÐ"nÓoÕpðØ—L‘L¡Ð&dÓ!eÔf÷ 
ð$ ˜3ÑØðÙ�v‹ˆÜ�D×(Ñ(Ó*Ó+ˆð  5Ñ(Ð,CÈ5Ñ,PØð€Jð€Mð 
�‰Œ˜4Ø�‰‘T˜*Ó%Ô&Ø�‰‘T˜-Ó(Ô)÷ 
ˆùòs >øÜò Ùðûô !ò Ùðûô !ò Ùðú÷ 
ˆûô0 ò Ùðú÷: 
ˆús•   ­E3 ÁE3 ÁE.Á'E3 Á9!F ÂFÂ2 F ÃFÃ8!F, Ä6/F;Å.E3 Å3	E?Å>E?Æ	FÆF ÆFÆF Æ	FÆF ÆFÆF Æ F)Æ,	F8Æ7F8Æ;Gc                ó¼   — ddl m} | j                  «       5 }|j                   |d«      ||dœ«      j	                  «       }d d d «       r|d   S d S # 1 sw Y   ŒxY w)Nr   ©r   zï
                SELECT udt_name
                FROM information_schema.columns
                WHERE table_schema = 'public'
                  AND table_name = :table_name
                  AND column_name = :column_name
                )Ú
table_nameÚcolumn_name)r#   r   Úconnectr>   Úfirst)r+   ÚtableÚcolumnr   r?   Úrows         r2   Ú_postgres_column_udtrL   †   sc   € Ýà	�‰Ô	˜TØ�l‰lÙðóð !°Ñ8ó
÷ ‰%‹'ð 	÷ 
ñ ˆ3ˆq‰6Ð"˜dÐ"÷ 
Ð	ús   —*AÁAc                óÒ   — ddl m} | j                  «       5 }|j                   |d«      «      j	                  «       }d d d «       D �cg c]  }|d   ‘Œ	 c}S # 1 sw Y   ŒxY wc c}w )Nr   rD   a¿  
                SELECT t.typname
                FROM pg_catalog.pg_type t
                JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
                WHERE n.nspname = 'public'
                  AND t.typtype = 'e'
                  AND (
                    t.typname ILIKE '%programme%'
                    OR t.typname ILIKE '%modulekind%'
                    OR t.typname ILIKE '%levelslug%'
                  )
                )r#   r   rG   r>   Úfetchall)r+   r   r?   ÚrowsrK   s        r2   Ú_postgres_programme_enum_typesrP   ™   se   € Ýà	�‰Ô	˜TØ�|‰|Ùðóó
÷ ‰(‹*ð 	÷ 
ñ" #Ó#™d�sˆC�‹F˜dÑ#Ð#÷# 
Ð	üò" $s   —&AÁ	A$ÁA!c                ó„  ‡ ‡
‡‡‡‡— ddl }ddlm}mŠ |j	                  t
        «      Št        ‰ j                  «      j                  «       }d|v xs d|v Šd|v xs d|v }	  |‰ «      Št        ‰j                  «       «      }dˆfd	„Š
dˆ
ˆ ˆfd
„}dˆ ˆˆfd„}d|v r  |dd«      r‰r
 |dd«       n|r	 |dd«       d|v r  |dd«      r‰r
 |dd«       n|r	 |dd«       ‰syt        ‰ «      D ]  }	 |d|	› �d|	› d�«       Œ dD ]  }	 |d|	› �d|	› d�«       Œ y# t        $ r}‰j                  d|«       Y d}~yd}~ww xY w)uˆ  
    Allow custom module kinds and level slugs in existing databases.

    Older deployments stored programme_modules.kind and programme_levels.slug as
    SQLAlchemy/PostgreSQL enums (foundations/developmental/creativity only).
    After admins add custom values like test_modules, reads fail with LookupError
    until columns are plain VARCHAR â€” this runs that migration on startup.
    r   Nr5   r8   ÚpostgresÚmysqlÚmariadbz9programme string-column sync skipped (inspect failed): %sc                óº   •— 	 ‰j                  | «      D ]5  }|d   |k(  sŒt        |j                  dd«      «      j                  «       c S  	 y # t        $ r Y y w xY w)Nr   Útyper   )r'   r;   r)   r*   r&   )rI   rJ   Úcolr,   s      €r2   Ú_column_typez3sync_programme_string_columns.<locals>._column_typeÉ   sa   ø€ ð	Ø×'Ñ'¨Ö.�Ø�v‘; &Ó(Ü˜sŸw™w v¨rÓ2Ó3×9Ñ9Ó;Ò;ñ /ð
 øô ò 	Ùð	ús   ƒA  *A ÁA Á	AÁAc                óp   •— ‰r#t        ‰| |«      xs dj                  «       }|dvS  ‰| |«      xs d}d|v S )Nr   )Úvarcharzcharacter varyingr   ÚbpcharÚenum)rL   r*   )rI   rJ   ÚudtÚcol_typerX   r+   Úis_postgress       €€€r2   Ú_needs_varchar_migrationz?sync_programme_string_columns.<locals>._needs_varchar_migrationÒ   sJ   ø€ ÙÜ'¨°°vÓ>ÒDÀ"×KÑKÓMˆCØÐPÐPÐPÙ  vÓ.Ò4°"ˆØ˜Ð!Ð!ó    c                óö   •— 	 ‰j                  «       5 }|j                   ‰|«      «       d d d «       ‰j                  d| «       y # 1 sw Y   ŒxY w# t        $ r}‰j	                  d| |«       Y d }~y d }~ww xY w)Nzprogramme migration ok: %sz$programme migration skipped (%s): %s)r=   r>   Úinfor&   Úwarning)ÚlabelÚsqlr?   Úexcr+   Úloggerr   s       €€€r2   Ú_runz+sync_programme_string_columns.<locals>._runÙ   sg   ø€ ð	OØ—‘” 4Ø—‘™T #›YÔ'÷  à�K‰KÐ4°eÕ<÷  �ûô ò 	OØ�N‰NÐAÀ5È#×NÑNûð	Oús-   ƒA “A«A ÁAÁA Á	A8ÁA3Á3A8r   r   z!programme_modules.kind -> varcharzQALTER TABLE programme_modules ALTER COLUMN kind TYPE VARCHAR(64) USING kind::textz>ALTER TABLE programme_modules MODIFY kind VARCHAR(64) NOT NULLÚprogramme_levelsÚslugz programme_levels.slug -> varcharzPALTER TABLE programme_levels ALTER COLUMN slug TYPE VARCHAR(64) USING slug::textz=ALTER TABLE programme_levels MODIFY slug VARCHAR(64) NOT NULLz
drop type zDROP TYPE IF EXISTS "z	" CASCADE)ÚprogrammemodulekindÚprogrammelevelslugÚprogramme_module_kindÚprogramme_level_slugzDROP TYPE IF EXISTS z CASCADE©rI   r;   rJ   r;   Úreturnú
str | None)rI   r;   rJ   r;   rq   Úbool)re   r;   rf   r;   rq   ÚNone)Úloggingr#   r   r   Ú	getLoggerÚ__name__r;   r<   r*   r$   r%   r&   rd   rP   )r+   ru   r   r<   Úis_mysqlÚtablesrg   r`   ri   Ú	enum_namerX   r,   r_   rh   r   s   `         @@@@@r2   Úsync_programme_string_columnsr{   °   s„  ý€ ó ç(à×ÑœxÓ(€FÜ
ˆf�j‰j‹/×
Ñ
Ó
!€CØ #Ð%Ò:¨°sÐ):€KØ˜#ˆ~Ò1 ¨cÐ!1€HðÙ�v‹ˆÜ�T×)Ñ)Ó+Ó,ˆõ
÷"÷Oð ˜fÑ$Ñ)AÐBUÐW]Ô)^ÙÙØ3ðFõñ
 ÙØ3ØPôð
 ˜VÑ#Ñ(@ÐASÐU[Ô(\ÙÙØ2ðFõñ
 ÙØ2ØOôñ
 Øä3°FÖ;ˆ	Ùˆz˜)˜Ð%Ð)>¸y¸kÈÐ'SÕTð <óˆ	ñ 	ˆz˜)˜Ð%Ð)=¸i¸[ÈÐ'QÕRñøôy ò Ø�‰ÐRÐTWÔXÜûðús   Á !D Ä	D?Ä#D:Ä:D?c                 ó�  — ddl m}  t        | j                  «      j	                  «       }| j                  «       5 }	 |j                  t        d«      «       ddd«       d|v r@g d¢}| j                  «       5 }|D ]  }|j                  t        |«      «       Œ 	 ddd«       yg d¢}| j                  «       5 }|j                  t        d«      «       |D ]  }|j                  t        |«      «       Œ |j                  t        d	«      «       ddd«       y# t        $ r Y ŒÊw xY w# 1 sw Y   ŒÏxY w# 1 sw Y   yxY w# 1 sw Y   yxY w)
z¿
    Drop all programme_* tables so `init_db()` / `create_all()` can recreate them from ORM.

    Use when Postgres already had partial/old programme tables (e.g. missing `kind` column).
    r   )r+   zLUPDATE users SET programme_session_id = NULL, programme_assessment_id = NULLNr8   )z7DROP TABLE IF EXISTS user_completed_assessments CASCADEz4DROP TABLE IF EXISTS user_completed_sessions CASCADEz2DROP TABLE IF EXISTS programme_assessments CASCADEz/DROP TABLE IF EXISTS programme_sessions CASCADEz,DROP TABLE IF EXISTS programme_weeks CASCADEz.DROP TABLE IF EXISTS programme_modules CASCADEz-DROP TABLE IF EXISTS programme_levels CASCADE)z/DROP TABLE IF EXISTS user_completed_assessmentsz,DROP TABLE IF EXISTS user_completed_sessionsz*DROP TABLE IF EXISTS programme_assessmentsz'DROP TABLE IF EXISTS programme_sessionsz$DROP TABLE IF EXISTS programme_weeksz&DROP TABLE IF EXISTS programme_modulesz%DROP TABLE IF EXISTS programme_levelszPRAGMA foreign_keys=OFFzPRAGMA foreign_keys=ON)	Úapp.databaser+   r;   r<   r*   r=   r>   r   r&   )r+   r<   r?   ÚdropsÚstmts        r2   Úrecreate_programme_schemar€   
  s  € õ $ä
ˆf�j‰j‹/×
Ñ
Ó
!€Cà	�‰Œ˜4ð	Ø�L‰LÜÐcÓdô÷ 
ð �sÑò
ˆð �\‰\Œ^˜tÛ�Ø—‘œT $›ZÕ(ñ ÷ ð 	ò€Eð 
�‰Œ˜4Ø�‰”TÐ3Ó4Ô5ÛˆDØ�L‰Lœ˜d›Õ$ð à�‰”TÐ2Ó3Ô4÷	 
ˆøô7 ò 	Ùð	ú÷ 
ˆú÷$ ð 	ú÷ 
ˆúsA   ºD$¼DÁ6"D0Â6AD<Ä	D!ÄD$Ä D!Ä!D$Ä$D-Ä0D9Ä<EF©Úprogressc               ó   — t        | |¬«      S )zUInsert programme tree from CSV; programme tables must exist and match current models.r�   )Ú_seed_programme_tables_from_csv©Údbr‚   s     r2   Úimport_programmes_from_csvr‡   ;  s   € ä*¨2¸ÔAÐAra   c               ód   — | j                  t        «      j                  «       �yt        | |¬«      S )z�
    If no programme_levels rows exist, import all UPP CSV files from the repo root.
    Returns True if seeding ran, False if data was already present.
    Fr�   )Úqueryr   rH   r„   r…   s     r2   Ú!seed_programmes_from_csv_if_emptyrŠ   @  s,   € ð
 
‡x�x”Ó×%Ñ%Ó'Ð3ØÜ*¨2¸ÔAÐAra   c                ó*  — | j                  t        «      j                  d¬«       | j                  t        «      j                  d¬«       | j                  t        «      j                  d¬«       | j                  t
        «      j                  d¬«       | j                  t        «      j                  d¬«       | j                  t        «      j                  d¬«       | j                  t        «      j                  d¬«       | j                  «        y)z^Remove all programme hierarchy rows (users' programme FKs become NULL via ON DELETE SET NULL).F)Úsynchronize_sessionN)
r‰   r
   Údeleter   r   r   r   r   r   Úcommit)r†   s    r2   Úclear_programme_tablesr�   J  sÅ   € à‡H�HÔ$Ó%×,Ñ,ÀÐ,ÔGØ‡H�HÔ!Ó"×)Ñ)¸eÐ)ÔDØ‡H�HÔ Ó!×(Ñ(¸UÐ(ÔCØ‡H�HÔÓ×%Ñ%¸%Ð%Ô@Ø‡H�HŒ]Ó×"Ñ"°uÐ"Ô=Ø‡H�HŒ_Ó×$Ñ$¸Ð$Ô?Ø‡H�HŒ^Ó×#Ñ#¸Ð#Ô>Ø‡I�I…Kra   c               ó2   — t        | «       t        | |¬«      S )z^Wipe programme tables and import again from CSV files. Returns True if any level was imported.r�   )r�   r„   r…   s     r2   Úreseed_programmes_from_csvr‘   V  s   € ä˜2ÔÜ*¨2¸ÔAÐAra   c               ó  — d}d}t         D �]+  }t        |«      }|€Œd}|rt        d|›d�d¬«       t        |t	        j
                  ||j                  dd«      j                  «       «      |¬	«      }| j                  |«       | j                  «        t        |«      }d}|D �]Œ  \  }	}
t        |j                  t        |	«      t        |	«      j                  dd«      j                  «       |¬
«      }| j                  |«       | j                  «        |dz  }t        |
«      D �]  \  }}t        |j                  ||d   d d ¬«      }| j                  |«       | j                  «        g }t!        |d   j#                  «       «      D ]H  \  }}t%        |j                  ||d   d d d¬«      }| j                  |«       |j'                  ||f«       ŒJ | j                  «        |D ]E  \  }}t        |d   «      D ]/  \  }}| j                  t)        |j                  ||d d ¬«      «       Œ1 ŒG �Œ �Œ� |dz  }�Œ. |s| j+                  «        y|rt        dd¬«       	 | j-                  «        y# t.        $ r | j+                  «        ‚ w xY w)Nr   FTu     â†’ importing level u    â€¦)ÚflushÚ_Ú )rk   Údisplay_nameÚ
sort_order)Úlevel_idr   r–   r—   é   Údisplay_labelé@   )Ú	module_idÚ
week_indexrš   ÚsessionsÚtitlei   )Úweek_idr"   rŸ   r   Úassessments)Ú
session_idr—   rŸ   u0     â†’ commit (sab rows DB me likh rahe hain) â€¦)r   r   Úprintr   r   r)   ÚreplacerŸ   Úaddr“   r   r   Úidr   Ú	enumerater   ÚsortedÚitemsr   Úappendr   ÚrollbackrŽ   r&   )r†   r‚   ÚorderÚ	any_levelrk   ÚpathÚlevelÚmodules_dataÚ	mod_orderr   Úweeks_payloadÚmodÚwiÚweekÚwkÚsessions_for_weekÚsnÚsdataÚsessÚaiÚatitles                        r2   r„   r„   \  su  € Ø€EØ€Iß�ˆÜ# DÓ)ˆØˆ<Øàˆ	ÙÜÐ*¨4¨(°$Ð7¸tÕDäØÜ/×3Ñ3°D¸$¿,¹,ÀsÈCÓ:P×:VÑ:VÓ:XÓYØô
ˆð
 	�‰ˆuŒØ
�‰Œ
ä/°Ó5ˆØˆ	Ü#/ÑˆD�-Ü!ØŸ™Ü.¨tÓ4Ü6°tÓ<×DÑDÀSÈ#ÓN×TÑTÓVØ$ô	ˆCð �F‰F�3ŒKØ�H‰HŒJØ˜‰NˆIä% m×4‘��DÜ"Ø!Ÿf™fØ!Ø"& Ñ"7¸¸Ð"<ô�ð
 —‘�r”
Ø—‘”
ØIKÐ!Ü!'¨¨ZÑ(8×(>Ñ(>Ó(@Ö!A‘I�B˜Ü+Ø "§¡Ø')Ø# G™n¨T¨cÐ2Ø&+ô	�Dð —F‘F˜4”LØ%×,Ñ,¨d°E¨]Õ;ð "Bð —‘”
Û#4‘K�D˜%Ü&/°°mÑ0DÖ&E™
˜˜FØŸ™Ü/Ø+/¯7©7Ø+-Ø&,¨T¨c lôõñ 'Fò $5ò' 5ð $0ðP 	�‰
Šðw  ñz Ø
�‰ŒØáÜÐ@ÈÕMðØ
�	‰	Œð øô ò Ø
�‰ŒØðús   ÉI. É.J	c                óˆ   — | j                  t        «      j                  t        j                  |k(  «      j	                  «       S ©N)r‰   r   Úfilterrk   rH   )r†   rk   s     r2   Úget_level_by_slugrÀ   «  s0   € Ø�8‰8”NÓ#×*Ñ*¬>×+>Ñ+>À$Ñ+FÓG×MÑMÓOÐOra   N)Ú
level_slugc               ó–  — | j                  t        «      j                  t        t        j                  «      j                  t
        j                  «      j                  t        j                  «      j                  t        j                  «      «      j                  t        j                  «      }|�"|j                  t        j                  |k(  «      }|j                  «       }d}d}d}d}g }|D �]@  }	g }
d}d}d}t!        |	j                  d„ ¬«      D �]§  }g }d}d}t!        |j                  d„ ¬«      D �]  }g }d}t!        |j                  d„ ¬«      D ]¢  }t!        |j                  d„ ¬«      }|D �cg c]&  }|j"                  |j                  |j$                  dœ‘Œ( }}|t'        |«      z  }|j)                  |j"                  |j*                  |j$                  |j,                  t'        |«      |dœ«       Œ¤ |t'        |«      z  }||z  }|j)                  |j"                  |j.                  |j0                  t'        |«      ||d	œ«       �Œ |t'        |«      z  }||z  }||z  }|
j)                  |j"                  t3        |j4                  «      |j6                  |j                  t'        |«      |||d
œ«       �Œª |t'        |
«      z  }||z  }||z  }||z  }|j)                  |	j"                  t9        |	j                  «      |	j6                  |	j                  t'        |
«      ||||
dœ	«       �ŒC t'        |«      |||||dœS c c}w )zHLoad the full programme hierarchy from SQL for frontend catalog screens.r   c                ó   — | j                   S r¾   ©r—   ©Úms    r2   Ú<lambda>z-serialize_programme_catalog.<locals>.<lambda>Ï  s   € ¸!¿,º,ra   ©Úkeyc                ó   — | j                   S r¾   ©r�   ©Úws    r2   rÇ   z-serialize_programme_catalog.<locals>.<lambda>Ô  s   € ¸1¿<º<ra   c                ó   — | j                   S r¾   ©r"   ©Úss    r2   rÇ   z-serialize_programme_catalog.<locals>.<lambda>Ø  s
   € À1×CSÒCSra   c                ó   — | j                   S r¾   rÄ   ©Úas    r2   rÇ   z-serialize_programme_catalog.<locals>.<lambda>Ù  s   € ÈAÏLÊLra   )r¦   r—   rŸ   )r¦   r"   rŸ   r   Úassessments_countr¡   )r¦   r�   rš   Úsessions_countrÕ   rž   )r¦   r   r–   r—   Úweeks_countrÖ   rÕ   Úweeks)	r¦   rk   r–   r—   Úmodules_countr×   rÖ   rÕ   Úmodules)Útotal_levelsÚtotal_modulesÚtotal_weeksÚtotal_sessionsÚtotal_assessmentsÚlevels)r‰   r   Úoptionsr   rÚ   r   rØ   r   rž   r   r¡   Úorder_byr—   r¿   rk   Úallr¨   r¦   rŸ   Úlenrª   r"   r   r�   rš   r   r   r–   r   )r†   rÁ   r‰   rà   rÜ   rÝ   rÞ   rß   Ú
levels_outr¯   Úmodules_outÚlevel_weeksÚlevel_sessionsÚlevel_assessmentsÚmoduleÚ	weeks_outÚmodule_sessionsÚmodule_assessmentsrµ   Úsessions_outÚweek_assessmentsÚsessionr¡   Ú
assessmentÚassessments_outs                            r2   Úserialize_programme_catalogró   ¯  sL  € ð 	�‰”Ó ß	‰Üœ×/Ñ/Ó0ß‰\œ/×/Ñ/Ó0ß‰\œ-×0Ñ0Ó1ß‰\Ô*×6Ñ6Ó7ó	

÷ 
‰”.×+Ñ+Ó	,ð 
ð ÐØ—‘œ^×0Ñ0°JÑ>Ó?ˆà�Y‰Y‹[€FØ€MØ€KØ€NØÐØ€JäˆØ"$ˆØˆØˆØÐä˜UŸ]™]Ñ0F×GÐGˆFØ$&ˆIØˆOØ!"Ðä˜vŸ|™|Ñ1G×HÐH�Ø+-�Ø#$Ð ä% d§m¡mÑ9S×T�GÜ"(¨×)<Ñ)<ÑBXÔ"Y�Kñ +6ó'ñ +6˜Jð	 #-§-¡-Ø*4×*?Ñ*?Ø%/×%5Ñ%5óð
 +6ð $ð 'ð %¬¨OÓ(<Ñ<Ð$Ø ×'Ñ'à")§*¡*Ø.5×.DÑ.DØ%,§]¡]Ø-4×-BÑ-BÜ14°_Ó1EØ+:ñõ	ð  Uð,  ¤3 |Ó#4Ñ4�Ø"Ð&6Ñ6Ð"Ø× Ñ à"Ÿg™gØ&*§o¡oØ)-×);Ñ);Ü*-¨lÓ*;Ø-=Ø$0ñö	ð9 IðN œ3˜y›>Ñ)ˆKØ˜oÑ-ˆNØÐ!3Ñ3ÐØ×Ñà Ÿ)™)Ü5°f·k±kÓBØ$*×$7Ñ$7Ø"(×"3Ñ"3Ü#& y£>Ø&5Ø);Ø&ñ	öð_ Hðx 	œ˜[Ó)Ñ)ˆØ�{Ñ"ˆØ˜.Ñ(ˆØÐ.Ñ.ÐØ×Ñà—h‘hÜ0°·±Ó<Ø %× 2Ñ 2Ø#×.Ñ.Ü!$ [Ó!1Ø*Ø"0Ø%6Ø&ñ
ö	
ðM ôj ˜J›Ø&Ø"Ø(Ø.Øñð ùòG's   Å5+Mc               ó<  — t        |«      }| j                  t        «      j                  t        t        j
                  t        j                  k(  «      j                  t        t        j                  t        j                  k(  «      j                  t        t        j                  t        j                  k(  «      j                  t        j                  |k(  t        j                  |k(  t        j                  |k(  t        j                  |k(  «      j!                  «       S r¾   )r   r‰   r   Újoinr   r    r¦   r   rœ   r   r˜   r¿   rk   r   r�   r"   rH   )r†   rÁ   Úmodule_kindr�   r"   Úkind_strs         r2   Úresolve_sessionrø   '  sÃ   € ô /¨{Ó;€Hà
�‰Ô!Ó"ß	‰ŒmÔ-×5Ñ5¼×9IÑ9IÑIÓ	Jß	‰Œoœ}×6Ñ6¼/×:LÑ:LÑLÓ	Mß	‰Œnœo×6Ñ6¼.×:KÑ:KÑKÓ	Lß	‰Ü×Ñ :Ñ-Ü× Ñ  HÑ,Ü×$Ñ$¨
Ñ2Ü×+Ñ+¨~Ñ=ó	

÷ 
‰‹ðra   c                óÊ   — | j                  t        j                  t        j                  «      «      j                  t        j                  |k(  «      j                  «       xs dS ©Nr   )r‰   r   Úcountr   r¦   r¿   r¢   Úscalar)r†   r¢   s     r2   Úsession_assessment_countrý   ?  sL   € à
�‰”—‘Ô/×2Ñ2Ó3Ó4×;Ñ;Ô<O×<ZÑ<ZÐ^hÑ<hÓi×pÑpÓrò 	Øðra   c               óØ  — d|cxk  rdk  st        d«      ‚ t        d«      ‚|dk  rt        d«      ‚t        | ||||¬«      }|€t        d«      ‚t        | |j                  «      }|dkD  rd|€t        d|› d	|› d
�«      ‚| j	                  t
        |«      }|�|j                  |j                  k7  rt        d|› d|› d|› d|› d|› d�«      ‚||fS |�t        |› d|› d|› d|› d�«      ‚|d fS )Nr   é   z+week_index must be between 0 (intro) and 12r™   z)session_number must be a positive integer)rÁ   rö   r�   r"   z0No matching session for the given programme pathzThis session has zR assessment(s). Send assessment_id from GET /api/v1/admin/programme/catalog?level=zl&modules=true&weeks=true&sessions=true&assessments=true (use the id inside that session's assessments list).zassessment_id z is not valid for Ú/z/week_index=z	/session=zQ. Pick an id from that session in the programme catalog (not a random global id).z› has no assessments in the database. Remove assessment_id from the body, or choose another week/session that has assessments (see admin programme catalog).)Ú
ValueErrorrø   rý   r¦   r)   r   r¢   )	r†   rÁ   rö   r�   r"   Úassessment_idrð   ÚnrÔ   s	            r2   Ú!validate_user_programme_selectionr  F  sh  € ð �Ô!˜rÒ!ÜÐFÓGÐGð "ÜÐFÓGÐGØ˜ÒÜÐDÓEÐEäØ
ØØØØ%ô€Gð €ÜÐKÓLÐLä   W§Z¡ZÓ0€AØˆ1‚uØÐ ÜØ# A 3ð '=Ø=G¸LðGðHóð ð �F‰FÔ&¨Ó6ˆØˆ9˜Ÿ™¨¯
©
Ò2ÜØ   Ð/AØ�,˜a ˜}¨L¸¸ÀIÈnÐM]ð ^bðcóð ð
 ˜ˆzÐàÐ ÜØˆl˜!˜K˜=¨°Z°LÀ	È.ÐIYð ZOð Pó
ð 	
ð
 �Dˆ=Ðra   c                  ó0   — e Zd ZdZdd„Zdd„Zd	d„Zd
d„Zy)Ú_CompletionBatchzQBatch programme completion writes (avoids one SELECT per assessment on register).c                óÜ  — || _         || _        |j                  t        j                  «      j                  t        j                  |k(  «      j                  «       D �ch c]  }|d   ’Œ	 c}| _        |j                  t        j                  «      j                  t        j                  |k(  «      j                  «       D �ch c]  }|d   ’Œ	 c}| _
        g | _        g | _        y c c}w c c}w rú   )r†   Úuser_idr‰   r
   r  r¿   rã   Údone_assessmentsr   r¢   Údone_sessionsÚnew_assessmentsÚnew_sessions)Úselfr†   r  rK   s       r2   Ú__init__z_CompletionBatch.__init__|  sä   € ØˆŒØˆŒð —x‘xÔ 7× EÑ EÓFß‰VÔ+×3Ñ3°wÑ>Ó?ß‰S‹Uðó+
ñ�ð �‹Fðñ+
ˆÔð —x‘xÔ 4× ?Ñ ?Ó@ß‰VÔ(×0Ñ0°GÑ;Ó<ß‰S‹Uðó(
ñ�ð �‹Fðñ(
ˆÔð +-ˆÔØ')ˆÕùò+
ùò(
s   ÁC$ÃC)c                óŽ   — || j                   v ry | j                   j                  |«       | j                  j                  |«       y r¾   )r	  r¥   r  rª   )r  r  s     r2   Úensure_assessmentz"_CompletionBatch.ensure_assessmentŽ  s;   € Ø˜D×1Ñ1Ñ1ØØ×Ñ×!Ñ! -Ô0Ø×Ñ×#Ñ# MÕ2ra   c                óŽ   — || j                   v ry | j                   j                  |«       | j                  j                  |«       y r¾   )r
  r¥   r  rª   )r  r¢   s     r2   Úensure_sessionz_CompletionBatch.ensure_session”  s;   € Ø˜×+Ñ+Ñ+ØØ×Ñ×Ñ˜zÔ*Ø×Ñ× Ñ  Õ,ra   c           
     óü  — | j                   s| j                  sy | j                  j                  «       }|��|j                  j
                  dk(  ríddlm} | j                   rg| j                  j                   |t        «      j                  | j                   D �cg c]  }| j                  |dœ‘Œ c}«      j                  «       «       | j                  rê| j                  j                   |t        «      j                  | j                  D �cg c]  }| j                  |dœ‘Œ c}«      j                  «       «       n‚| j                   D ]2  }| j                  j                  t        | j                  |¬«      «       Œ4 | j                  D ]2  }| j                  j                  t        | j                  |¬«      «       Œ4 | j                   j                  «        | j                  j                  «        y c c}w c c}w )Nr8   r   )Úinsert©r  r  ©r  r¢   )r  r  r†   Úget_bindÚdialectr   Úsqlalchemy.dialects.postgresqlr  r>   r
   Úvaluesr  Úon_conflict_do_nothingr   r¥   Úclear)r  ÚbindÚ	pg_insertr  r¢   s        r2   r“   z_CompletionBatch.flushš  s˜  € Ø×#Ò#¨D×,=Ò,=ØØ�w‰w×ÑÓ!ˆØÑ §¡× 1Ñ 1°\Ò AÝJà×#Ò#Ø—‘—‘ÙÔ5Ó6ß‘Vð 26×1EÒ1Eóá1E ð )-¯©À}ÓUØ1Eñó÷ ,Ñ+Ó-ô	ð × Ò Ø—‘—‘ÙÔ2Ó3ß‘Vð /3×.?Ò.?óá.? 
ð )-¯©ÀJÓOØ.?ñó÷ ,Ñ+Ó-õ	ð "&×!5Ô!5�Ø—‘—‘Ü+°D·L±LÐP]Ô^õð "6ð #×/Ô/�
Ø—‘—‘Ô0¸¿¹ÐR\Ô]Õ^ð 0à×Ñ×"Ñ"Ô$Ø×Ñ×ÑÕ!ùò5ùòs   ÂG4ÄG9N)r†   r   r  Úintrq   rt   )r  r  rq   rt   )r¢   r  rq   rt   ©rq   rt   )rw   Ú
__module__Ú__qualname__Ú__doc__r  r  r  r“   © ra   r2   r  r  y  s   „ Ù[ó*ó$3ó-ô%"ra   r  c                óŠ   — | j                   D ]4  }t        |t        «      sŒ|j                  |k(  sŒ$|j                  |k(  sŒ4 y y)NTF)ÚnewÚ
isinstancer   r  r¢   )r†   r  r¢   Úobjs       r2   Ú_pending_user_completed_sessionr)  Â  s;   € Ø�vŒvˆä�sÔ0Õ1Ø—‘˜wÓ&Ø—‘ *Ó,áð ð ra   c                óÒ   — |�|j                  |«       y | j                  t        «      j                  ||¬«      j	                  «       }|s| j                  t        ||¬«      «       y y )Nr  )r  r‰   r
   Ú	filter_byrH   r¥   )r†   r  r  ÚbatchÚexistss        r2   Ú_ensure_assessment_doner.  Í  sg   € ð ÐØ×Ñ Ô.ØØ�X‰XÔ-Ó.×8Ñ8Ø }ð 9ó ç�eƒgð ñ Ø
�‰Ô&¨wÀmÔTÕUð ra   c                óî   — |�|j                  |«       y t        | ||«      ry | j                  t        «      j	                  ||¬«      j                  «       }|s| j                  t        ||¬«      «       y y )Nr  )r  r)  r‰   r   r+  rH   r¥   )r†   r  r¢   r,  r-  s        r2   Ú_ensure_session_marker_doner0  Ý  sv   € ð ÐØ×Ñ˜ZÔ(ØÜ& r¨7°JÔ?ØØ�X‰XÔ*Ó+×5Ñ5Ø Jð 6ó ç�eƒgð ñ Ø
�‰Ô#¨GÀ
ÔKÕLð ra   c                ó¨   — t        |j                  d„ ¬«      }|st        | ||j                  |«       y |D ]  }t	        | ||j                  |«       Œ y )Nc                ó   — | j                   S r¾   rÄ   rÓ   s    r2   rÇ   z)_complete_session_fully.<locals>.<lambda>õ  s   € ¸¿ºra   rÈ   )r¨   r¡   r0  r¦   r.  )r†   r  rº   r,  r¡   rÔ   s         r2   Ú_complete_session_fullyr3  ï  sH   € ô ˜×)Ñ)Ñ/EÔF€KÙÜ# B¨°·±¸%Õ@ãˆAÜ# B¨°·±°uÕ=ñ ra   c                óZ   — t        |j                  d„ ¬«      D ]  }t        | |||«       Œ y )Nc                ó   — | j                   S r¾   rÏ   rÐ   s    r2   rÇ   z'_complete_entire_week.<locals>.<lambda>  ó
   € °A×4DÒ4Dra   rÈ   )r¨   rž   r3  )r†   r  rµ   r,  rº   s        r2   Ú_complete_entire_weekr7  ý  s)   € ô �t—}‘}Ñ*D×EˆÜ  G¨T°5Õ9ñ Fra   c                óZ   — t        |j                  d„ ¬«      D ]  }t        | |||«       Œ y )Nc                ó   — | j                   S r¾   rË   rÌ   s    r2   rÇ   z)_complete_entire_module.<locals>.<lambda>  ó   € °·²ra   rÈ   )r¨   rØ   r7  )r†   r  rê   r,  r¶   s        r2   Ú_complete_entire_moduler;    s(   € ô �V—\‘\Ñ'=×>ˆÜ˜b '¨2¨uÕ5ñ ?ra   c                óZ   — t        |j                  d„ ¬«      D ]  }t        | |||«       Œ y )Nc                ó   — | j                   S r¾   rÄ   rÅ   s    r2   rÇ   z(_complete_entire_level.<locals>.<lambda>  ó   € °1·<²<ra   rÈ   )r¨   rÚ   r;  )r†   r  r¯   r,  r³   s        r2   Ú_complete_entire_levelr?    s)   € ô �e—m‘mÑ)?×@ˆÜ  G¨S°%Õ8ñ Ara   c                óp  — t        |j                  d„ ¬«      D ]›  }|j                  |j                  k  rt        | |||«       Œ+|j                  |j                  k(  rWt        |j
                  d„ ¬«      }|r;|�9|D ]4  }|j                  |j                  k  sŒt        | ||j                  |«       Œ6  y  y  y )Nc                ó   — | j                   S r¾   rÏ   rÐ   s    r2   rÇ   z(_complete_partial_week.<locals>.<lambda>#  r6  ra   rÈ   c                ó   — | j                   S r¾   rÄ   rÓ   s    r2   rÇ   z(_complete_partial_week.<locals>.<lambda>'  s   € ÀÇÂra   )r¨   rž   r"   r3  r¦   r¡   r—   r.  )	r†   r  rµ   Útarget_sessÚtarget_assessmentr,  rº   r¡   rÔ   s	            r2   Ú_complete_partial_weekrE    s    € ô �t—}‘}Ñ*D×EˆØ×Ñ ×!;Ñ!;Ò;Ü# B¨°°uÕ=Ø�W‰W˜Ÿ™Ò&Ü  ×!1Ñ!1Ñ7MÔNˆKÙÐ0Ð<Û$�AØ—|‘|Ð&7×&BÑ&BÓBÜ/°°G¸Q¿T¹TÀ5ÕIð %ñ áñ Fra   c           	     óæ   — t        |j                  d„ ¬«      D ]V  }|j                  |j                  k  rt        | |||«       Œ+|j                  |j                  k(  rt        | |||||«        y  y  y )Nc                ó   — | j                   S r¾   rË   rÌ   s    r2   rÇ   z*_complete_partial_module.<locals>.<lambda>:  r:  ra   rÈ   )r¨   rØ   r�   r7  r¦   rE  )r†   r  rê   Útarget_weekrC  rD  r,  r¶   s           r2   Ú_complete_partial_modulerI  1  sd   € ô �V—\‘\Ñ'=×>ˆØ�=‰=˜;×1Ñ1Ò1Ü! " g¨r°5Õ9Ø�U‰U�k—n‘nÒ$Ü" 2 w°°KÐARÐTYÔZÙáñ ?ra   c           
     óè   — t        |j                  d„ ¬«      D ]W  }|j                  |j                  k  rt        | |||«       Œ+|j                  |j                  k(  rt        | ||||||«        y  y  y )Nc                ó   — | j                   S r¾   rÄ   rÅ   s    r2   rÇ   z)_complete_partial_level.<locals>.<lambda>N  r>  ra   rÈ   )r¨   rÚ   r—   r;  r¦   rI  )	r†   r  r¯   Útarget_modulerH  rC  rD  r,  r³   s	            r2   Ú_complete_partial_levelrM  D  sm   € ô �e—m‘mÑ)?×@ˆØ�>‰>˜M×4Ñ4Ò4Ü# B¨°°eÕ<Ø�V‰V�}×'Ñ'Ò'Ü$Ø�G˜S +¨{Ð<MÈuôñ áñ Ara   c                óî  — |j                   }| j                  t        «      j                  t	        t        j
                  «      t	        t        j                  «      j	                  t        j                  «      j	                  t        j                  «      t	        t        j                  «      j	                  t        j                  «      j	                  t        j
                  «      «      j                  t        j                   |k(  «      j                  «       }|€y|j                  }|j                  }|j                  }| j                  t        «      j                  t	        t        j                  «      j	                  t        j                   «      j	                  t        j                  «      j	                  t        j
                  «      «      j#                  t        j$                  «      j'                  «       }	t)        | |«      }
|	D ]W  }|j$                  |j$                  k  rt+        | |||
«       Œ+|j                   |j                   k(  sŒEt-        | |||||||
«        n |
j/                  «        y)z¯
    Mark everything strictly before the user's chosen position as completed:
    all prior levels fully; within selected level, prior modules/weeks/sessions/assessments.
    N)r¦   r‰   r   rá   r   r¡   rµ   r   rê   r   r¯   rž   r¿   rH   r   rÚ   rØ   râ   r—   rã   r  r?  rM  r“   )r†   r  Útarget_sessionrD  ÚsidÚsess_rowÚtwÚtmÚtlrà   r,  r¯   s               r2   Ú seed_prior_programme_completionsrU  Z  sÇ  € ð ×
Ñ
€Cà
�‰Ô!Ó"ß	‰ÜÔ'×3Ñ3Ó4ÜÔ'×,Ñ,Ó-ß‰Zœ×,Ñ,Ó-ß‰Zœ×-Ñ-Ó.ÜÔ'×,Ñ,Ó-×8Ñ8¼×9OÑ9OÓP×[Ñ[Ô\l×\xÑ\xÓyó

÷ 
‰Ô ×#Ñ# sÑ*Ó	+ß	‰‹ð ð ÐØà	�‰€BØ	�‰€BØ	�‰€Bð 	�‰”Ó ß	‰Ü”~×-Ñ-Ó.ß‰Zœ×-Ñ-Ó.ß‰Zœ×.Ñ.Ó/ß‰ZÔ(×4Ñ4Ó5ó	

÷ 
‰”.×+Ñ+Ó	,ß	‰‹ð ô ˜R Ó)€EÛˆØ×Ñ˜bŸm™mÒ+Ü" 2 w°°uÕ=Ø�X‰X˜Ÿ™ÓÜ#Ø�G˜U B¨¨HÐ6GÈôñ ð ð 
‡K�K…Mra   c                óP  — | j                   €y| j                  }|€| j                   | j                  dœS |j                  }|r|j                  nd}|r|j
                  nd}| j                  }|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 )zXBuild a dict for UserPublic.programme from a User ORM instance (relationships optional).N)r¢   r  )rÁ   Úlevel_display_namerö   Úmodule_display_namer�   Ú
week_labelr"   Úsession_titleÚsession_admin_checkedr¢   r  Úassessment_title)r7   Úprogramme_sessionr9   rµ   rê   r¯   Úprogramme_assessmentr   rk   r–   r   r   r�   rš   r"   rŸ   r   r¦   )Úuserrº   rµ   r³   r¯   Úasss         r2   Úload_user_programme_summaryra  �  s  € à× Ñ Ð(ØØ×!Ñ!€DØ€|Ø"×7Ñ7È$×JfÑJfÑgÐgØ�9‰9€DÙˆ$�+Š+ 4€CÙˆC�IŠI $€EØ
×
#Ñ
#€Cá>CÔ.¨u¯z©zÔ:ÈÙ49˜e×0Ò0¸tÙ>AÔ0°·±Ô:ÀtÙ36˜s×/Ò/¸DÙ)-�d—o’o°4Ù,0�d×(Ò(°dØ×-Ñ-ØŸ™Ø!%×!3Ñ!3Ø—g‘gÙ#&˜Ÿš¨DÙ),˜CŸI™Iñð ð 37ñð ra   c                 ó  — t        t        j                  «      j                  t        j                  «      j                  t
        j                  «      j                  t        j                  «      t        t        j                  «      fS r¾   )
r   r	   r]  r   rµ   r   rê   r   r¯   r^  r$  ra   r2   Úuser_programme_load_optionsrc  «  sY   € ä”4×)Ñ)Ó*ß	‰Ô$×)Ñ)Ó	*ß	‰”M×(Ñ(Ó	)ß	‰”O×)Ñ)Ó	*Ü”4×,Ñ,Ó-ðð ra   c           	     ó   — ddl m}m}m} t	        | «      }|rø|j                  t        j                  t        j                  «      «      j                  t        j                  | j                  k(  «      j                  «       xs d}|j                  t        j                  t        j                  «      «      j                  t        j                  | j                  k(  «      j                  «       xs d}t        |«      |d<   t        |«      |d<   |r|j                  |«      nd } || j                  | j                   | j"                   || j$                  j&                  «      | j(                  | j*                  |¬«      S )Nr   )ÚRoleEnumÚUserProgrammeProgressPublicÚ
UserPublicÚ prior_completed_assessment_countÚ%prior_completed_session_markers_count)r¦   ÚemailÚ	full_nameÚroleÚ	is_activeÚ
created_atÚ	programme)Úapp.schemasre  rf  rg  ra  r‰   r   rû   r
   r  r¿   r¦   rü   r   r  Úmodel_validaterj  rk  rl  Úvaluerm  rn  )	r_  r†   re  rf  rg  ÚrawÚcaÚcsÚprogs	            r2   Úserialize_user_publicrw  µ  s'  € ßMÑMä
% dÓ
+€CÙ
à�H‰H”T—Z‘ZÔ 7× ?Ñ ?Ó@ÓAß‰VÔ+×3Ñ3°t·w±wÑ>Ó?ß‰V‹Xòð ð	 	ð �H‰H”T—Z‘ZÔ 4× <Ñ <Ó=Ó>ß‰VÔ(×0Ñ0°D·G±GÑ;Ó<ß‰V‹Xòð ð	 	ô 36°b³'ˆÐ.Ñ/Ü7:¸2³wˆÐ3Ñ4Ù>AÐ&×5Ñ5°cÔ:Àt€DÙØ�7‰7Ø�j‰jØ—.‘.Ù�d—i‘i—o‘oÓ&Ø—.‘.Ø—?‘?Øôð ra   )rq   rs   r   rp   )rq   z	list[str])r†   r   r‚   rs   rq   rs   )r†   r   rq   rt   )r†   r   rk   r;   rq   zProgrammeLevel | None)r†   r   rÁ   rr   rq   Údict)r†   r   rÁ   r;   rö   r;   r�   r  r"   r  rq   zProgrammeSession | None)r†   r   r¢   r  rq   r  )r†   r   rÁ   r;   rö   r;   r�   r  r"   r  r  z
int | Nonerq   z3tuple[ProgrammeSession, ProgrammeAssessment | None])r†   r   r  r  r¢   r  rq   rs   r¾   )
r†   r   r  r  r  r  r,  ú_CompletionBatch | Nonerq   rt   )
r†   r   r  r  r¢   r  r,  ry  rq   rt   )
r†   r   r  r  rº   r   r,  ry  rq   rt   )
r†   r   r  r  rµ   r   r,  ry  rq   rt   )
r†   r   r  r  rê   r   r,  ry  rq   rt   )
r†   r   r  r  r¯   r   r,  ry  rq   rt   )r†   r   r  r  rµ   r   rC  r   rD  úProgrammeAssessment | Noner,  ry  rq   rt   )r†   r   r  r  rê   r   rH  r   rC  r   rD  rz  r,  ry  rq   rt   )r†   r   r  r  r¯   r   rL  r   rH  r   rC  r   rD  rz  r,  ry  rq   rt   )
r†   r   r  r  rO  r   rD  rz  rq   rt   )r_  r	   rq   zdict | None)r_  r	   r†   r   rq   z'UserPublic')9Ú
__future__r   r#   r   r   Úsqlalchemy.ormr   r   r   Ú
app.modelsr	   r
   r   Úapp.programme.models_programmer   r   r   r   r   r   r   r   r   r   r   r   r3   rB   rL   rP   r{   r€   r‡   rŠ   r�   r‘   r„   rÀ   ró   rø   rý   r  r  r)  r.  r0  r3  r7  r;  r?  rE  rI  rM  rU  ra  rc  rw  r$  ra   r2   Ú<module>r     sü  ðÝ "ç !ß <Ñ <ç JÑ J÷÷ ÷ ó ó "óJG*óT#ó&$ó.WSót.5ðb AFõ Bð
 HMõ Bó	ð AFõ Bð FKõ Ló^Pð "ñuØðuð ðuð 
ó	uðpØðð ðð ð	ð
 ðð ðð óó0ð0Øð0ð ð0ð ð	0ð
 ð0ð ð0ð ð0ð 9ó0÷fF"ñ F"óRð &*ð	VØðVàðVð ðVð #ð	Vð
 
óVð( &*ð	MØðMàðMð ðMð #ð	Mð
 
óMð, &*ð	>Øð>àð>ð ð>ð #ð	>ð
 
ó>ð$ &*ð	:Øð:àð:ð ð:ð #ð	:ð
 
ó:ð &*ð	6Øð6àð6ð ð6ð #ð	6ð
 
ó6ð &*ð	9Øð9àð9ð ð9ð #ð	9ð
 
ó9ð  &*ðØðàðð ðð "ð	ð
 2ðð #ðð 
óð: &*ðØðàðð ðð ð	ð
 "ðð 2ðð #ðð 
óð6 &*ðØðàðð ðð #ð	ð
 ðð "ðð 2ðð #ðð 
óð,3Øð3àð3ð %ð3ð 2ð	3ð
 
ó3ólò6ôra   