ó
    ¦(ÒjÊ=  ã                   ó„  • S r SSKrSSKrSSKrSSKJrJr  SSKrSSKr	Sr
\R                  " S5      r/ SQ/ SQS.r\" \R!                  \S	   \S
   -   5      5      rSrS rSr " S S\5      rS rS rS rS rS rS rS rS rS rS r S r!S r"S r#S r$S r%S r&S  r'S! r(S" r)S# r*S$ r+S+S% jr,S& r-S' r.S( r/S) r0S* r1g),zúAccounts and tenancy: admin (platform) -> parent (tenant) -> kid (sub-tenant).

Every parent owns only the kids whose parent_id is theirs; a parent may own at most max_kids kids.
A suspended parent blocks its own login and the logins of all its kids.é    N)Úgenerate_password_hashÚcheck_password_hash)ÚadminÚparentÚkidz^[a-z0-9][a-z0-9._-]{2,29}$)u   ðŸ�±u   ðŸ�¶u   ðŸ¦Šu   ðŸ�¼u   ðŸ�¨u   ðŸ�¯u   ðŸ¦�u   ðŸ�¸u   ðŸ�µu   ðŸ¦„u   ðŸ�°u   ðŸ�»u   ðŸ�§u   ðŸ¦‰u   ðŸ�™u   ðŸ¦–u   ðŸ�¬u   ðŸ¦‹u   ðŸ�¢õ   ðŸš€õ   âš½u   ðŸŒŸ)u   ðŸ§‘â€�ðŸš€u   ðŸ¦¸u   ðŸ¥·u   ðŸ§™u   ðŸ�ºu   ðŸ¦…u   ðŸ�‰u   ðŸ¦ˆu   ðŸŽ§u   ðŸŽ®u   ðŸ�€r	   u   ðŸŽ¸u   ðŸ›¹u   ðŸ“·u   ðŸ§­u   âš¡u   ðŸ”¥u   ðŸŒ�r   u   ðŸ�†u   ðŸŽ¯)ÚkidsÚjuniorr
   r   é   c                 óÈ   • [         R                  " U 5      (       a&  [         R                  " U 5      R                  SS5      OSn[        R                  U[        S   5      S   $ )zcFirst avatar of the age band of the kid's course level (a cat for 8 years old, an explorer for 11).Úbandr
   r   )ÚCURÚlevelÚgetÚAVATARS_BY_BAND)r   r   s     Ú F:\Yaseen\AI\French2\accounts.pyÚdefault_avatarr      sJ   € ä36·9²9¸U×3CÑ3CŒ3�9Š9�UÓ×Ñ ¨Ô/È€DÜ×Ñ˜t¤_°VÑ%<Ó=¸aÑ@Ð@ó    aë  
CREATE TABLE users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    username TEXT UNIQUE NOT NULL,
    role TEXT NOT NULL CHECK(role IN ('admin','parent','kid')),
    password_hash TEXT NOT NULL,
    display_name TEXT NOT NULL,
    parent_id INTEGER,
    max_kids INTEGER DEFAULT 3,
    active INTEGER DEFAULT 1,
    avatar TEXT DEFAULT '',
    unlock_all INTEGER DEFAULT 0,
    course_level INTEGER DEFAULT 1,
    created_at TEXT DEFAULT (datetime('now','localtime')),
    last_login TEXT
)c                   ó   • \ rS rSrSrSrg)ÚAccountErroré.   z:A user-facing problem (shown in Arabic in the dashboards).© N)Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__Ú__static_attributes__r   r   r   r   r   .   s   † ÜDr   r   c                  óN  • [         R                  " 5       n U R                  S5      R                  5       =(       d    SS0S   =(       d    SnSU;  GaC  U R                  S5      R	                  5        Vs/ s H  o"S   PM	     nnU R                  S5        U R                  [
        5        S Vs/ s H  oDU;   d  M
  UPM     nnU R                  S	S
R                  U5       SS
R                  U5       S35        U R                  S5        U R                  S5      R                  5       nU(       a  U R                  SUS   45        U R                  S5      R                  5       nU(       a  US   S:X  a  U R                  S5        U R                  5         SU R                  S5      R	                  5        Vs/ s H  o"S   PM	     sn;  a  U R                  S5        U R                  S[        S   45        U R                  5         U R                  5         gs  snf s  snf s  snf )zZUpgrade the old users table (kid/parent only) to the tenancy schema, keeping ids and data.z0SELECT sql FROM sqlite_master WHERE name='users'ÚsqlÚ z'admin'zPRAGMA table_info(users)Únamez%ALTER TABLE users RENAME TO users_old)ÚidÚusernameÚroleÚpassword_hashÚdisplay_nameÚ
created_atzINSERT INTO users (Ú,z	) SELECT z FROM users_oldzDROP TABLE users_oldz<SELECT id FROM users WHERE role='parent' ORDER BY id LIMIT 1z-UPDATE users SET parent_id=? WHERE role='kid'r$   z1SELECT value FROM settings WHERE key='unlock_all'ÚvalueÚ1z.UPDATE users SET unlock_all=1 WHERE role='kid'Úcourse_levelz;ALTER TABLE users ADD COLUMN course_level INTEGER DEFAULT 1zLUPDATE users SET avatar=? WHERE role='kid' AND (avatar IS NULL OR avatar='')r   N)
ÚdbÚget_dbÚexecuteÚfetchoneÚfetchallÚSCHEMAÚjoinÚcommitÚAVATARSÚclose)Úcr!   ÚrÚold_colsÚxÚkeepÚparÚrows           r   Úmigrater?   2   sÅ  € ä
�	Š	‹€AØ�9‰9ÐGÓH×QÑQÓS×bÐX]Ð_aÐWbÐdiÑ
j×
pÐnp€CØ˜ÔØ'(§y¡yÐ1KÓ'L×'UÑ'UÔ'WÓXÒ'W !�f”IÑ'WˆÐXØ	�	‰	Ð9Ô:Ø	�	‰	”&ÔÙdÓvÒd�aÐmuÑhu—ÑdˆÐvØ	�	‰	Ð'¨¯©°«Ð'7°yÀÇÁÈ$ÃÐ@PÐP_Ð`ÔaØ	�	‰	Ð(Ô)à�i‰iÐVÓW×`Ñ`ÓbˆÞØ�I‰IÐEÈÈDÉ	À|ÔTØ�i‰iÐKÓL×UÑUÓWˆÞ�3�w‘< 3Ó&Ø�I‰IÐFÔGØ	�‰Œ
Ø°·±Ð;UÓ1V×1_Ñ1_Ô1aÓbÒ1a¨A œiÑ1aÑbÓbØ	�	‰	ÐOÔPØ‡I�IÐ\Ô_fÐghÑ_iÐ^kÔlØ‡H�H„JØ‡G�G…Iùò% Yùò wùò cs   Á4HÂ.	HÂ;HÆ8H"c                 ó   • [        U SS9$ )Nzpbkdf2:sha256:600000)Úmethod)r   ©Úpws    r   Úhash_pwrD   M   s   € Ü! "Ð-CÑDÐDr   c                 ón   • [         R                  " SU -   R                  S5      5      R                  5       $ )Nzfr2:zutf-8)ÚhashlibÚsha256ÚencodeÚ	hexdigestrB   s    r   Ú_legacyrJ   Q   s)   € Ü�>Š>˜6 B™;×.Ñ.¨wÓ7Ó8×BÑBÓDÐDr   c                 óš   • U S   n[         R                  " SU5      (       a   U[        U5      :X  a  [        U S   U5        gg[	        X!5      $ )Nr'   z[0-9a-f]{64}r$   TF)ÚreÚ	fullmatchrJ   Úset_passwordr   )ÚuserrC   Úhs      r   Úcheck_pwrQ   U   sH   € Øˆ_Ñ€AÜ	‡|‚|�O Q×'Ñ'Ø”˜“ÓÜ˜˜d™ RÔ(ØØÜ˜qÓ%Ð%r   c                  ó   • / SQn [        S5       Vs/ s H9  n[        R                  " U 5      R                  SS5      R                  SS5      PM;     nnUS    SUS    S[        R                  " S	5      S
-    3$ s  snf )uJ   Easy for a child to type: 2 short words + 2 digits, e.g. Â«chat-lune-47Â».)ÚchatÚluneÚbolu   clÃ©ÚsacÚmotoÚlacÚrobeÚtaxiÚyoyoÚkiwiÚoliveÚpianou   zÃ¨breÚcubeÚjusé   ô   Ã©Úeô   Ã¨r   Ú-é   éZ   é
   )ÚrangeÚsecretsÚchoiceÚreplaceÚ	randbelow)ÚwordsÚ_Úws      r   Úgen_passwordrq   _   s|   € ò M€EÜNSÐTUÌhÓWÊhÈŒ�Š˜Ó	×	&Ñ	& t¨SÓ	1×	9Ñ	9¸$ÀÖ	DÉh€AÐWØ�‰dˆV�1�Q�q‘T�F˜!œG×-Ò-¨bÓ1°BÑ6Ð7Ð8Ð8ùò 	Xs   ’A A;c                 óz   • U (       a  [        U 5      S:  a  [        S5      e[        U 5      S:”  a  [        S5      eg )Né   uG   ÙƒÙ„Ù…Ø© Ø§Ù„Ù…Ø±ÙˆØ± ÙŠØ¬Ø¨ Ø£Ù† ØªÙƒÙˆÙ† 6 Ø£Ø­Ø±Ù� Ø¹Ù„Ù‰ Ø§Ù„Ø£Ù‚Ù„éd   u)   ÙƒÙ„Ù…Ø© Ø§Ù„Ù…Ø±ÙˆØ± Ø·ÙˆÙŠÙ„Ø© Ø¬Ø¯Ø§Ù‹)Úlenr   rB   s    r   Úcheck_new_passwordrv   f   s7   € Þ”�R“˜1“ÜÐdÓeÐeÜ
ˆ2ƒw�ƒ}ÜÐFÓGÐGð r   c                 ó¶   • [         R                  " 5       nUR                  SU 45      R                  5       nUR	                  5         U(       a  [        U5      $ S $ )NzSELECT * FROM users WHERE id=?)r.   r/   r0   r1   r7   Údict)Úuidr8   r9   s      r   r   r   n   sC   € Ü
�	Š	‹€AØ	�	‰	Ð2°S°FÓ;×DÑDÓF€AØ‡G�G„IÞŒ4�‹7Ð!˜TÐ!r   c                 ó   • [         R                  " 5       nUR                  SU =(       d    SR                  5       R	                  5       45      R                  5       nUR                  5         U(       a  [        U5      $ S $ )Nz$SELECT * FROM users WHERE username=?r"   )r.   r/   r0   ÚstripÚlowerr1   r7   rx   )r%   r8   r9   s      r   Úby_usernamer}   u   sZ   € Ü
�	Š	‹€AØ	�	‰	Ð8¸H¿NÈ×;QÑ;QÓ;S×;YÑ;YÓ;[Ð:]Ó^×gÑgÓi€AØ‡G�G„IÞŒ4�‹7Ð!˜TÐ!r   c                 óP   • S Vs0 s H  oU R                  U5      _M     sn$ s  snf )zDWhat the dashboards may see of an account (never the password hash).)r$   r%   r&   r(   Ú	parent_idÚmax_kidsÚactiveÚavatarÚ
unlock_allr-   r)   Ú
last_login)r   )ÚuÚks     r   Úpublicr‡   |   s;   € ñ "só  tò  "s˜Aˆq�u‰u�Q‹xŠKñ  "sñ  tð  tùò  ts   …#c                 ó˜   • U (       a
  U S   (       d  gU S   S:X  a/  U S   (       a%  [        U S   5      n[        U=(       a    US   5      $ g)z/Active account, and for a kid an active parent.r�   Fr&   r   r   T)r   Úbool)r…   Úps     r   Ú	can_loginr‹   �   sE   € æ�A�h—KØØˆ�y�EÓ˜a ŸnÜ��+‘ÓˆÜ�A×%˜!˜H™+Ó&Ð&Ør   c                 ó  • [        U 5      nU(       a  [        X!=(       d    S5      (       d  g[        U5      (       d  g[        R                  " 5       nUR                  SUS   45        UR                  5         UR                  5         US 4$ )Nr"   )NuD   Ø§Ø³Ù… Ø§Ù„Ù…Ø³ØªØ®Ø¯Ù… Ø£Ùˆ ÙƒÙ„Ù…Ø© Ø§Ù„Ù…Ø±ÙˆØ± ØºÙŠØ± ØµØ­ÙŠØ­Ø©)Nu?   Ù‡Ø°Ø§ Ø§Ù„Ø­Ø³Ø§Ø¨ Ù…ÙˆÙ‚ÙˆÙ�. ØªÙˆØ§ØµÙ„ Ù…Ø¹ Ø§Ù„Ù…Ø³Ø¤ÙˆÙ„.zBUPDATE users SET last_login=datetime('now','localtime') WHERE id=?r$   )r}   rQ   r‹   r.   r/   r0   r5   r7   )r%   Úpasswordr…   r8   s       r   ÚauthenticaterŽ   ‹   sk   € Ü�HÓ€AÞ”H˜Q§¨B×/Ñ/Ø[Ü�Q�<‰<ØVÜ
�	Š	‹€AØ‡I�IÐRÐUVÐW[ÑU\ÐT^Ô_Ø‡H�H„JØ‡G�G„IØˆdˆ7€Nr   c           	      óT  • [         R                  " 5       nUR                  SU  S3U5      R                  5       nUR	                  5         / nU HV  n[        [        U5      5      nUR                  US   [        US   S-  5      US   US   US   S	9  UR                  U5        MX     U$ )
zUKids with their totals in one query: xp, finished activities, last activity, minutes.a÷  
        SELECT u.*, COALESCE(a.xp,0) AS xp, COALESCE(a.secs,0) AS secs, a.last_at AS last_activity,
               COALESCE(p.done,0) AS done, COALESCE(p.stars,0) AS stars
        FROM users u
        LEFT JOIN (SELECT user_id, SUM(points_earned) xp, SUM(seconds) secs, MAX(created_at) last_at FROM attempts GROUP BY user_id) a ON a.user_id=u.id
        LEFT JOIN (SELECT user_id, SUM(completed) done, SUM(stars) stars FROM progress GROUP BY user_id) p ON p.user_id=u.id
        WHERE u.role='kid' AND z' ORDER BY u.display_name COLLATE NOCASEÚxpÚsecsé<   Úlast_activityÚdoneÚstars)r�   Úminutesr“   r”   r•   )
r.   r/   r0   r2   r7   r‡   rx   ÚupdateÚroundÚappend)ÚwhereÚargsr8   ÚrowsÚoutr9   Úds          r   Úkid_stats_sqlrŸ   ˜   s»   € ä
�	Š	‹€AØ�9‰9ð  ð !&˜wÐ&MðQð SWóX÷ YaÑX`ÓXbð 	ð ‡G�G„IØ
€CÛˆÜ”4˜“7‹OˆØ	�‰�A�d‘G¤U¨1¨V©9°r©>Ó%:È!ÈOÑJ\ÐcdÐekÑclÐtuÐv}Ñt~ˆÑØ�
‰
�1Žñ ð €Jr   c                 ó   • [        SU 45      $ )Nzu.parent_id=?©rŸ   ©r   s    r   Úkids_ofr£   «   s   € Ü˜¨9¨,Ó7Ð7r   c                  ó   • [        SS5      $ )Nz1=1r   r¡   r   r   r   Úall_kidsr¥   ¯   s   € Ü˜ Ó#Ð#r   c            
      óô   • [         R                  " 5       n U R                  S5      R                  5       nU R	                  5         U Vs/ s H"  n[        [        [        U5      5      US   S9PM$     sn$ s  snf )Nz¼SELECT u.*, (SELECT COUNT(*) FROM users k WHERE k.parent_id=u.id AND k.role='kid') AS kids
                        FROM users u WHERE u.role='parent' ORDER BY u.display_name COLLATE NOCASEr
   )r
   )r.   r/   r0   r2   r7   rx   r‡   )r8   rœ   r9   s      r   Úparentsr§   ³   sb   € Ü
�	Š	‹€AØ�9‰9ð eó fßfnÑfnÓfpð 	à‡G�G„IÙ;?Ó@º4°aŒD”œ˜Q›“ q¨¡yÔ1¹4Ñ@Ð@ùÒ@s   Á	)A5c                 ó˜   • [         R                  " 5       nUR                  SU 45      R                  5       S   nUR	                  5         U$ )Nz=SELECT COUNT(*) n FROM users WHERE role='kid' AND parent_id=?Ún)r.   r/   r0   r1   r7   )r   r8   r©   s      r   Ú
count_kidsrª   »   s?   € Ü
�	Š	‹€AØ	�	‰	ÐQÐT]ÐS_Ó`×iÑiÓkÐloÑp€AØ‡G�G„IØ€Hr   c                  ón  ^• [         R                  " 5       mSU4S jjn [        U " S5      U " S5      U " S5      U " S5      U " S5      U " S5      [        U " S5      S	-  5      U " S
5      TR	                  S5      R                  5        Vs0 s H  oS   US   _M     snS9	nTR                  5         U$ s  snf )Nc                 óH   >• TR                  X5      R                  5       S   $ )Nr   )r0   r1   )ÚsÚar8   s     €r   Ú<lambda>Úsummary.<locals>.<lambda>Ä   s   ø€ ˜Ÿ	™	 !›×0Ñ0Ó2°1Ò5r   z.SELECT COUNT(*) FROM users WHERE role='parent'z+SELECT COUNT(*) FROM users WHERE role='kid'z;SELECT COUNT(*) FROM users WHERE role='parent' AND active=1z[SELECT COUNT(DISTINCT user_id) FROM attempts WHERE date(created_at)=date('now','localtime')zdSELECT COUNT(DISTINCT user_id) FROM attempts WHERE created_at>=datetime('now','localtime','-7 days')zUSELECT COUNT(*) FROM attempts WHERE created_at>=datetime('now','localtime','-7 days')zeSELECT COALESCE(SUM(seconds),0) FROM attempts WHERE created_at>=datetime('now','localtime','-7 days')r’   z?SELECT COALESCE(SUM(max_kids),0) FROM users WHERE role='parent'zOSELECT course_level, COUNT(*) FROM users WHERE role='kid' GROUP BY course_levelr   rf   )	r§   r
   Úactive_parentsÚ
kids_todayÚ	kids_weekÚattempts_weekÚminutes_weekÚseatsÚ	per_level)r   )r.   r/   rx   r˜   r0   r2   r7   )Úqr9   r�   r8   s      @r   Úsummaryr¹   Â   sð   ø€ Ü
�	Š	‹€AÞ5€AÜ
‘qÐIÓJÑQRð  TAó  RBÙÐ ]Ó^ÙÐyÓzÙð  Bó  CÙÐvÓwÜ!¡!ð  %Ló  #Mð  PRñ  #Ró  SÙÐXÓYØ./¯i©ið  9Jó  /K÷  /Tñ  /Tô  /Vó  Wò  /V¨˜A™$  !¡š*ñ  /Vñ  WñX€Cð ‡G�G„IØ€Jùò Ws   Â	B2c                 óÜ   • U =(       d    SR                  5       R                  5       n[        R                  U5      (       d  [	        S5      e[        U5      (       a  [	        SU S35      eU$ )Nr"   uŸ   Ø§Ø³Ù… Ø§Ù„Ù…Ø³ØªØ®Ø¯Ù…: 3 Ø¥Ù„Ù‰ 30 Ø­Ø±Ù�Ø§Ù‹ Ø¥Ù†Ø¬Ù„ÙŠØ²ÙŠØ§Ù‹ ØµØºÙŠØ±Ø§Ù‹ Ø£Ùˆ Ø£Ø±Ù‚Ø§Ù…Ø§Ù‹ (ÙˆÙŠÙ�Ø³Ù…Ø­ Ø¨Ù€ . _ -)ØŒ ÙˆÙŠØ¨Ø¯Ø£ Ø¨Ø­Ø±Ù� Ø£Ùˆ Ø±Ù‚Ù…u   Ø§Ø³Ù… Ø§Ù„Ù…Ø³ØªØ®Ø¯Ù… Â«u8   Â» Ù…Ø³ØªØ®Ø¯Ù… Ù…Ù† Ù‚Ø¨Ù„ØŒ Ø§Ø®ØªØ± Ø§Ø³Ù…Ø§Ù‹ Ø¢Ø®Ø±)r{   r|   ÚUSER_REÚmatchr   r}   )r%   r…   s     r   Ú_clean_usernamer½   Ò   se   € Ø	��R×ÑÓ ×&Ñ&Ó(€AÜ�=‰=˜×ÑÜð  }ó  ~ð  	~Ü�1‡~�~ÜÐ7¸°sÐ:rÐsÓtÐtØ€Hr   c                 óz   • U =(       d    SR                  5       nU(       a  [        U5      S:”  a  [        S5      eU$ )Nr"   é(   uH   Ø§Ù„Ø§Ø³Ù… Ø§Ù„Ù…Ø¹Ø±ÙˆØ¶ Ù…Ø·Ù„ÙˆØ¨ (40 Ø­Ø±Ù�Ø§Ù‹ Ø¹Ù„Ù‰ Ø§Ù„Ø£ÙƒØ«Ø±))r{   ru   r   )r#   r©   s     r   Ú_clean_namerÀ   Û   s2   € Ø	��×ÑÓ€AÞ”�A“˜“ÜÐeÓfÐfØ€Hr   c                 ó�   •  [        U 5      n SU s=::  a  S::  d  O  [        S5      eU $ ! [        [        4 a    [        S5      ef = f)NuD   Ø¹Ø¯Ø¯ Ø§Ù„Ø£Ø·Ù�Ø§Ù„ Ø§Ù„Ù…Ø³Ù…ÙˆØ­ ÙŠØ¬Ø¨ Ø£Ù† ÙŠÙƒÙˆÙ† Ø±Ù‚Ù…Ø§Ù‹r   é2   u2   Ø¹Ø¯Ø¯ Ø§Ù„Ø£Ø·Ù�Ø§Ù„ Ø§Ù„Ù…Ø³Ù…ÙˆØ­ Ø¨ÙŠÙ† 0 Ùˆ50)ÚintÚ	TypeErrorÚ
ValueErrorr   )Úvs    r   Ú
_clean_maxrÇ   â   sS   € ðcÜ�‹Fˆð ��<�R�<ÜÐOÓPÐPØ€Høô	 ”zÐ"ó cÜÐaÓbÐbðcús	   ‚) ©Ac                 óî   •  [        U 5      n U [        R
                  ;  a5  [        R                  " U 5      n[        U(       a  SUS    S35      eS5      eU $ ! [        [        4 a    [        S5      ef = f)Nu&   Ø§Ø®ØªØ± Ø§Ù„Ù…Ø³ØªÙˆÙ‰ Ø§Ù„Ø¯Ø±Ø§Ø³ÙŠô   Â«Útitle_aru   Â» ØºÙŠØ± Ù…ØªØ§Ø­ Ø¨Ø¹Ø¯u'   Ù…Ø³ØªÙˆÙ‰ Ø¯Ø±Ø§Ø³ÙŠ ØºÙŠØ± Ù…Ø¹Ø±ÙˆÙ�)rÃ   rÄ   rÅ   r   r   Ú	AVAILABLEr   )rÆ   Úlvs     r   Ú_clean_levelrÍ   ì   sz   € ðEÜ�‹Fˆð 	”—‘ÓÜ�YŠY�q‹\ˆÜÎR˜R  :¡Ð/Ð/HÐIÓÐÐU~ÓÐØ€Høô ”zÐ"ó EÜÐCÓDÐDðEús   ‚A ÁA4c                 óš  • U [         ;   d   e[        U5      [        U5      p˜[        U5        U S:X  a]  [	        U5      n
U
(       a	  U
S   S:w  a  [        S5      e[        U5      U
S   :¼  a  [        SU
S    S35      eU(       a  U[        ;  a  SnU S:X  a  [        Ub  UO[        5      OS	nU S:X  a  [        U5      OS
n[        R                  " 5       nUR                  SX€[        U5      X�S:X  a  UOS X¶=(       d    U S:X  a  [        U5      OSU45      nUR!                  5         UR"                  nUR%                  5         [	        U5      $ )Nr   r&   r   u#   ÙˆÙ„ÙŠ Ø§Ù„Ø£Ù…Ø± ØºÙŠØ± Ù…ÙˆØ¬ÙˆØ¯r€   u9   ÙˆØµÙ„ ÙˆÙ„ÙŠ Ø§Ù„Ø£Ù…Ø± Ø¥Ù„Ù‰ Ø§Ù„Ø­Ø¯ Ø§Ù„Ù…Ø³Ù…ÙˆØ­ (u6    Ø£Ø·Ù�Ø§Ù„). ÙŠÙ…ÙƒÙ† Ù„Ù„Ù…Ø³Ø¤ÙˆÙ„ Ø±Ù�Ø¹ Ø§Ù„Ø­Ø¯.r"   r   rf   zƒINSERT INTO users (username, role, password_hash, display_name, parent_id, max_kids, avatar, course_level) VALUES (?,?,?,?,?,?,?,?))ÚROLESr½   rÀ   rv   r   r   rª   r6   rÇ   ÚDEFAULT_MAX_KIDSrÍ   r.   r/   r0   rD   r   r5   Ú	lastrowidr7   )r&   r%   r(   r�   r   r€   r‚   r-   r…   r©   r=   ÚmkÚlvlr8   Úcurry   s                   r   ÚcreaterÕ   ÷   s`  € Ø”5‹=Ðˆ=Ü˜8Ó$¤k°,Ó&?€qÜ�xÔ Øˆuƒ}Ü�)‹nˆÞ�c˜&‘k XÓ-ÜÐDÓEÐEÜ�iÓ  C¨
¡OÓ3ÜÐ!ZÐ[^Ð_iÑ[jÐZkð  lbð   có  dð  dÞ�f¤GÓ+ØˆFØOSÐW_ÓO_Œ Ñ 4‘HÔ:JÔ	KÐef€BØ(,°«Œ,�|Ô
$¸1€CÜ
�	Š	‹€AØ
�)‰)ð  ZØœg hÓ/°ÈÃ±IÐTXÐZ\÷  _Wð  AEð  INó  ANÔiwÐx{Ôi|ð  TVð  Y\ð  ]ó^€Cà‡H�H„JØ
�-‰-€CØ‡G�G„IÜˆs‹8€Or   c                 ó  • [        U 5      nU(       d  [        S5      e0 nSU;   a  [        US   5      US'   SU;   a  US   (       a  SOSUS'   SU;   a)  US   S:X  a   US   [        ;  a  [        S	5      eUS   US'   S
U;   a  US   S:X  a  [	        US
   5      US
'   SU;   a  US   S:X  a  US   (       a  SOSUS'   SU;   a:  US   S:X  a1  [        US   5      n[        U 5      nXE:  a  [        SU S35      eXCS'   U(       at  [        R                  " 5       nUR                  SSR                  S U 5       5       S3/ UR                  5       QU P75        UR                  5         UR                  5         [        U 5      $ )Nu   Ø§Ù„Ø­Ø³Ø§Ø¨ ØºÙŠØ± Ù…ÙˆØ¬ÙˆØ¯r(   r�   rf   r   r‚   r&   r   u'   ØµÙˆØ±Ø© Ø±Ù…Ø²ÙŠØ© ØºÙŠØ± Ù…Ø¹Ø±ÙˆÙ�Ø©r-   rƒ   r€   r   u   Ù„Ø¯Ù‰ ÙˆÙ„ÙŠ Ø§Ù„Ø£Ù…Ø± uK    Ø£Ø·Ù�Ø§Ù„ Ø­Ø§Ù„ÙŠØ§Ù‹ØŒ Ù„Ø§ ÙŠÙ…ÙƒÙ† Ø¬Ø¹Ù„ Ø§Ù„Ø­Ø¯ Ø£Ù‚Ù„ Ù…Ù† Ø°Ù„ÙƒzUPDATE users SET z, c              3   ó*   #   • U  H	  oS -   v •  M     g7f)z=?Nr   )Ú.0r†   s     r   Ú	<genexpr>Úupdate.<locals>.<genexpr>'  s   é € Ð/GÂ$¸Q°D¶Â$ùs   ‚z WHERE id=?)r   r   rÀ   r6   rÍ   rÇ   rª   r.   r/   r0   r4   Úvaluesr5   r7   )ry   Úfr…   ÚsetsrÒ   Úhaver8   s          r   r—   r—     s–  € ÜˆC‹€AÞÜÐ;Ó<Ð<Ø€DØ˜ÓÜ*¨1¨^Ñ+<Ó=ˆˆ^ÑØ�1ƒ}Ø Ÿk™¨qˆˆX‰Ø�1ƒ}˜˜6™ eÓ+ØˆX‰;œgÓ%ÜÐHÓIÐIØ˜8™ˆˆX‰Ø˜Ó˜q ™y¨EÓ1Ü+¨A¨nÑ,=Ó>ˆˆ^ÑØ�qÓ˜Q˜v™Y¨%Ó/Ø"# L§/™Q°qˆˆ\ÑØ�Qƒ˜1˜V™9¨Ó0Ü˜˜*™Ó&ˆÜ˜#‹ˆØ‹9ÜÐ!:¸4¸&ð  ALð   Mó  Nð  NØˆZÑÞÜ�IŠI‹KˆØ	�	‰	Ð% d§i¡iÑ/GÁ$Ó/GÓ&GÐ%HÈÐTÐVkÐX\×XcÑXcÓXeÐVkÐgjÑVkÔlØ	�‰Œ
Ø	�‰Œ	Üˆs‹8€Or   c                 ó¾   • [        U5        [        R                  " 5       nUR                  S[	        U5      U 45        UR                  5         UR                  5         g )Nz+UPDATE users SET password_hash=? WHERE id=?)rv   r.   r/   r0   rD   r5   r7   )ry   rC   r8   s      r   rN   rN   -  s?   € Ü�rÔÜ
�	Š	‹€AØ‡I�IÐ;¼gÀb»kÈ3Ð=OÔPØ‡H�H„JØ‡G�G…Ir   c                 ó  • [        U 5      nU(       a	  US   S:X  a  [        S5      eU /nUS   S:X  a[  [        R                  " 5       nX#R	                  SU 45      R                  5        Vs/ s H  oDS   PM	     sn-  nUR                  5         [        R                  " 5       nU H6  nS H  nUR	                  SU S	3U45        M     UR	                  S
U45        M8     UR                  5         UR                  5         [        U5      $ s  snf )z_Delete an account with everything it owns (a parent takes its kids and their progress with it).r&   r   u(   Ù„Ø§ ÙŠÙ…ÙƒÙ† Ø­Ø°Ù� Ù‡Ø°Ø§ Ø§Ù„Ø­Ø³Ø§Ø¨r   z&SELECT id FROM users WHERE parent_id=?r$   )ÚprogressÚattemptsÚbadgesÚdayszDELETE FROM z WHERE user_id=?zDELETE FROM users WHERE id=?)	r   r   r.   r/   r0   r2   r7   r5   ru   )ry   r…   Úidsr8   r9   ÚiÚts          r   Údeleterè   5  sí   € äˆC‹€AÞ��&‘	˜WÓ$ÜÐEÓFÐFØˆ%€CØˆ�y�HÓÜ�IŠI‹KˆØ§¡Ð+SÐVYÐU[Ó!\×!eÑ!eÔ!gÓhÒ!g˜A�$”Ñ!gÑhÑhˆØ	�‰Œ	Ü
�	Š	‹€AÛˆÛ;ˆAØ�I‰I˜ Q CÐ'7Ð8¸1¸$Ö?ñ <à	�	‰	Ð0°1°$Ö7ñ ð ‡H�H„JØ‡G�G„IÜˆs‹8€Oùò is   Á+D
c                 ó„   • U (       a  U(       a	  US   S:w  a  gU S   S:H  =(       d    U S   S:H  =(       a    US   U S   :H  $ )z/Admin manages every kid; a parent only its own.r&   r   Fr   r   r   r$   r   )Úactorr   s     r   Úcan_manage_kidrë   J  sH   € æž˜s 6™{¨eÓ3ØØ�‰=˜GÑ#×f¨¨f©¸Ñ(A×(eÀcÈ+ÑFVÐZ_Ð`dÑZeÑFeÐfr   c                 ó  • U R                  S5      =(       d    SR                  5       n[        U5      nU(       d{  U R                  S5      =(       d    [        R                  " S5      n[        SXR                  S5      =(       d    SU5        U R                  S5      (       d  [        SU SU S	3S
S9  O=U R                  S5      S:X  a(  U R                  S5      (       a  [        US   U S   5        U R                  S5      =(       d    SR                  5       U R                  S5      =(       d    SR                  5       pTU(       aN  [        U5      (       d>  U R                  S5      (       a(  [        SX@R                  S5      =(       d    UU S   5        U(       a  [        U5      OSnU(       a[  U(       aS  [        U5      (       dB  U R                  S5      (       a+  [        SXPR                  S5      =(       d    UU S   US   S9  ggggg)zôCreate the admin and the first family from the environment when they do not exist yet.
Passwords set here are only initial ones: later changes from the dashboards are never overwritten,
except the admin's when ADMIN_RESET_PASSWORD=1 (recovery).ÚADMIN_USERNAMEr   ÚADMIN_PASSWORDé   ÚADMIN_DISPLAY_NAMEÚAdminz$*** admin account created: username=z
 password=z4 (set ADMIN_PASSWORD in .env to choose your own) ***T)ÚflushÚADMIN_RESET_PASSWORDr,   r$   ÚPARENT_USERNAMEr"   ÚKID_USERNAMEÚPARENT_PASSWORDr   ÚPARENT_DISPLAY_NAMENÚKID_PASSWORDr   ÚKID_DISPLAY_NAMEr¢   )r   r|   r}   rj   Útoken_urlsaferÕ   ÚprintrN   )ÚenvÚadmin_ur®   rC   ÚpuÚkurŠ   s          r   Ú	bootstrapr   R  s§  € ð �w‰wÐ'Ó(×3¨G×:Ñ:Ó<€GÜ�GÓ€AÞØ�W‰WÐ%Ó&×C¬'×*?Ò*?ÀÓ*CˆÜˆw˜§¡Ð)=Ó!>×!IÀ'È2ÔNØ�w‰wÐ'×(Ñ(ÜÐ8¸¸	ÀÈBÈ4ð  PDð  Eð  MQò  RøØ	�‰Ð'Ó	(¨CÓ	/°C·G±GÐ<L×4MÑ4MÜ�Q�t‘W˜cÐ"2Ñ3Ô4Ø�g‰gÐ'Ó(×.¨B×5Ñ5Ó7¸#¿'¹'À.Ó:Q×:WÐUW×9^Ñ9^Ó9`ˆÞ	”+˜b—/‘/ c§g¡gÐ.?×&@Ñ&@Üˆx˜ŸW™WÐ%:Ó;×A¸rÀ3ÐGXÑCYÔZÞŒ�BŒ 4€AÞ	Žaœ BŸ™¨C¯G©G°N×,CÑ,CÜˆu�bŸ'™'Ð"4Ó5×;¸¸SÀÑ=PÐ\]Ð^bÑ\cÓdð -D˜ˆa€rr   )NNr"   rf   )2r   rL   rj   rF   Úwerkzeug.securityr   r   r.   Ú
curriculumr   rÏ   Úcompiler»   r   Úlistrx   Úfromkeysr6   rÐ   r   r3   Ú	Exceptionr   r?   rD   rJ   rQ   rq   rv   r   r}   r‡   r‹   rŽ   rŸ   r£   r¥   r§   rª   r¹   r½   rÀ   rÇ   rÍ   rÕ   r—   rN   rè   rë   r   r   r   r   Ú<module>r     s  ðñKó 
Û Û ß IÛ 	Û à"€Ø
�*Š*Ð3Ó
4€ò |ò Dñ€ñ ˆt�}‰}˜_¨VÑ4°ÀxÑ7PÑPÓQÓ
R€ØÐ òAð

€ô$E�9ô Eòò6EòEò&ò9òHò"ò"òtò
ò
òò&8ò$òAòòò òòòôò.ò>òò*góer   