Ë
    Iá'jy7  ã                   ó\	  — U d dl 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	 d dl
mZmZmZ d dl
mZ d dlmZ 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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l(m)Z) d dl*m+Z+m,Z,m-Z-m.Z.m/Z/m0Z0m1Z1m2Z2m3Z3 d dl4m5Z5m6Z6m7Z7m8Z8  eddg¬«      Z9i Z:e;e<e=e>e&f   f   e?d<   dld„Z@dedeAdeAdeBe   fd„ZCe9j‰                  de&d¬«      e9j‰                  de&d¬«       ee«       ee«       ed d ¬«       ed d!d"¬#«       edd$¬%«       ed&d d'¬#«      fded(edeAdeAd)edz  d*eAfd+„«       «       ZEe9j‰                  d,eBe'   d-¬«       ee«       ee«       ed d ¬«       ed d!d"¬#«       ed.d d'¬#«      fded(edeAdeAd*eAf
d/„«       ZFe9j‰                  d0e'd1¬«      e9j‰                  d2e'd3¬«      e9j‰                  d4e'd5¬«       ed6d!d7¬8«       ee«       ee«      fd9eAded(efd:„«       «       «       ZGe9j‘                  d;e'ej’                  ¬<«       ee«       ee«      fd=e$ded(efd>„«       ZJe9j‘                  d?e'¬@«       ee«       ee«      fd=e#ded(efdA„«       ZKe9j‘                  dBe'dC¬«       ee«       ee«      fd=eded(efdD„«       ZLe9j‘                  dEe'dF¬«       ee«       ee«      fd=eded(efdG„«       ZMe9j‰                  dHedI¬«       ee«       ee«       ed d ¬«       ed d!d"¬#«       eddJ¬%«       edKdL¬%«      fded(edeAdeAdMe<dz  dNe<fdO„«       ZNe9j‰                  dPedQ¬«       ed6d!¬«       ee«       ee«      fd9eAded(efdR„«       ZOe9j‘                  dSedT¬«       ee«       ee«      fd=eded(efdU„«       ZPe9j‘                  dVe'dW¬«       ee«       ee«      fd=e"ded(efdX„«       ZQe9j‰                  dYedZ¬«       ee«       ee«       ee5d!d[d\¬]«       ed d ¬«       ed d!d"¬#«       ed^d_¬%«      fded(ed`eAdeAdeAdaeRfdb„«       ZSe9j‘                  dce!dd¬«       ee«       ee«       ee5d!d[¬#«      fded(ed`eAfde„«       ZTe9j‘                  dfe!dg¬«       ee«       ee«      fd=e ded(efdh„«       ZUe9j‘                  die!¬@«       ee«       ee«      fd=e%dedjefdk„«       ZVy)mé    )Útime)Ú	APIRouterÚDependsÚHTTPExceptionÚQueryÚstatus)ÚSession)Úselectinload)ÚProgrammeModuleÚProgrammeSessionÚProgrammeWeek)ÚProgrammeLevel)Úcalculate_member_points_bulk)Úget_db)Úrequire_admin_token)ÚUserÚUserRole)ÚAdminMemberAccountUpdateRequestÚ#AdminMemberGoogleDriveUpdateRequestÚ$AdminMemberPersonalInfoUpdateRequestÚMemberGoogleDriveEntryOutÚMemberGoogleDriveListResponseÚ"MemberInactiveFollowUpListResponseÚMemberInactiveFollowUpOutÚ#MemberInactiveFollowUpUserIdRequestÚMessageÚUpdateUserProgrammeRequestÚUpdateUserRequestÚ
UserCreateÚUserIdRequestÚAdminUsersListResponseÚ
UserPublic)Úhash_password)	Ú_load_member_userÚassign_user_programmeÚlist_members_google_drive_linksÚserialize_admin_memberÚserialize_admin_member_lightÚserialize_admin_member_minimalÚ#serialize_member_google_drive_entryÚupdate_member_google_drive_linkÚupdate_member_personal_info)ÚDEFAULT_INACTIVE_DAYSÚlist_member_inactive_followupsÚ#mark_member_inactive_follow_up_sentÚsync_member_inactive_followupsz/userszadmin-users)ÚprefixÚtagsÚ_LIST_USERS_CACHEÚreturnNc                  ó,   — t         j                  «        y ©N)r3   Úclear© ó    ú</var/www/html/kelly/kelly-backend/app/routers/admin/users.pyÚinvalidate_list_users_cacher;   8   s   € Ü×ÑÕr9   ÚdbÚskipÚlimitc                 ó  — | j                  t        «      j                  t        j                  j	                  «       t        j
                  j	                  «       «      j                  |«      j                  |«      j                  «       S r6   )	Úqueryr   Úorder_byÚ
created_atÚdescÚidÚoffsetr>   Úall)r<   r=   r>   s      r:   Ú_fetch_usersrG   <   sN   € à
�‰”‹ß	‰”$—/‘/×&Ñ&Ó(¬$¯'©'¯,©,«.Ó	9ß	‰�‹ß	‰ˆu‹ß	‰‹ðr9   z/list-userszList users (paginated))Úresponse_modelÚsummaryz/get-all-usersz-Get all users (paginated, same as list-users))Úgeé2   é   iô  )rJ   Úlez+Filter by role (e.g. user for members only))Údescriptioné
   i,  Ú_ÚroleÚcache_secondsc                 ó\  — |xs d› d|› d|› �}|dkD  r+t         j                  |«      }|r|\  }}	t        «       |k  r|	S | j                  t        «      }
|�"|
j                  t        j                  |k(  «      }
|
j                  «       }|
j                  t        t        j                  «      j                  t        j                  «      j                  t        j                  «      j                  t        j                   «      t        t        j"                  «      «      j%                  t        j&                  j)                  «       t        j*                  j)                  «       «      j-                  |«      j/                  |«      j1                  «       }ddlm} |D �cg c]  }|j*                  ‘Œ }}t7        | |«      } || «      }t9        ||D �cg c]*  }t;        || ||j                  |j*                  «      ¬«      ‘Œ, c}¬«      }	|dkD  rt        «       |z   |	ft         |<   |	S c c}w c c}w )NrF   Ú:r   )Ú_load_programme_levels_ordered)r<   Úprogramme_levelsÚpoints)ÚtotalÚusers)r3   Úgetr   r@   r   ÚfilterrQ   ÚcountÚoptionsr
   Úprogramme_sessionr   Úweekr   Úmoduler   ÚlevelÚprogramme_assessmentrA   rB   rC   rD   rE   r>   rF   Úapp.services.user_track_servicerU   r   r!   r(   )r<   rP   r=   r>   rQ   rR   Ú	cache_keyÚcachedÚ
expires_atÚpayloadÚ
base_queryrX   rY   rU   ÚuÚidsÚpoints_by_userrV   s                     r:   Ú
list_usersrl   F   sà  € ð$ ’=˜5�/  4 &¨¨%¨Ð1€IØ�qÒÜ"×&Ñ& yÓ1ˆÙØ"(ÑˆJ˜Ü‹v˜
Ò"Ø�à—‘œ$“€JØÐØ×&Ñ&¤t§y¡y°DÑ'8Ó9ˆ
Ø×ÑÓ€Eð 	×ÑÜœ×/Ñ/Ó0ß‰\Ô*×/Ñ/Ó0ß‰\œ-×.Ñ.Ó/ß‰\œ/×/Ñ/Ó0Üœ×2Ñ2Ó3ó	
÷ 
‰”$—/‘/×&Ñ&Ó(¬$¯'©'¯,©,«.Ó	9ß	‰�‹ß	‰ˆu‹ß	‰‹ð 
õ OáÓ
™�Aˆ1�4‹4˜€CÐ
Ü1°"°cÓ:€NÙ5°bÓ9ÐÜ$Øñ
 ó	
ñ �ô )Ø�bÐ+;ÀN×DVÑDVÐWX×W[ÑW[ÓD\öð ñ	
ô€Gð �qÒÜ(,«°Ñ(>ÀÐ'HÔ˜)Ñ$Ø€Nùò  ùò

s   ÆH$Ç/H)
z/get-all-users-lightz3Get users (very fast: no programme/progress/points)é   c                 óø  — d|› d|› �}|dkD  r+t         j                  |«      }|r|\  }}t        «       |k  r|S | j                  t        «      j                  t        j                  j                  «       t        j                  j                  «       «      j                  |«      j                  |«      j                  «       }	|	D �
cg c]  }
t        |
«      ‘Œ }}
|dkD  rt        «       |z   |ft         |<   |S c c}
w )Nzlight:rT   r   )r3   rZ   r   r@   r   rA   rB   rC   rD   rE   r>   rF   r)   )r<   rP   r=   r>   rR   rd   re   rf   rg   rY   ri   s              r:   Úlist_users_lightro   …   sß   € ð ˜˜˜a ˜wÐ'€IØ�qÒÜ"×&Ñ& yÓ1ˆÙØ"(ÑˆJ˜Ü‹v˜
Ò"Ø�ð 	�‰”‹ß	‰”$—/‘/×&Ñ&Ó(¬$¯'©'¯,©,«.Ó	9ß	‰�‹ß	‰ˆu‹ß	‰‹ð 
ñ ;@Ó@¹%°QÔ-¨aÕ0¸%€GÐ@Ø�qÒÜ(,«°Ñ(>ÀÐ'HÔ˜)Ñ$Ø€Nùò As   ÃC7z	/get-userz#Get one user by id (query: user_id)z/get-user-dataz*Get one user by id (query: user_id, alias)z/member-personal-infoz@Get member personal info including phone_number (query: user_id).zUser primary key)rJ   rN   Úuser_idc                 ó€   — |j                  t        | «      }|st        t        j                  d¬«      ‚t        ||«      S )NúUser not found©Ústatus_codeÚdetail)rZ   r   r   r   ÚHTTP_404_NOT_FOUNDr'   )rp   r<   rP   ri   s       r:   Úget_userrw   ¦   s8   € ð( 	�‰Œt�WÓ€AÙÜ¬×(AÑ(AÐJZÔ[Ð[Ü! ! RÓ(Ð(r9   z/create-user)rH   rt   Úbodyc                 óP  — |j                  t        «      j                  t        j                  | j                  k(  «      j	                  «       rt        t        j                  d¬«      ‚t        t        | j                  «      | j                  t        | j                  «      | j                  j                  dk(  rt        j                  nt        j                   | j"                  ¬«      }|j%                  |«       |j'                  «        |j)                  |«       t+        ||«      S )NzEmail already registeredrs   Úadmin)ÚemailÚ	full_nameÚhashed_passwordrQ   Ú	is_active)r@   r   r[   r{   Úfirstr   r   ÚHTTP_409_CONFLICTÚstrr|   r#   ÚpasswordrQ   Úvaluer   rz   Úuserr~   ÚaddÚcommitÚrefreshr'   ©rx   r<   rP   ri   s       r:   Úcreate_userr‰   À   s»   € à	‡x�x”ƒ~×ÑœTŸZ™Z¨4¯:©:Ñ5Ó6×<Ñ<Ô>Ü¬×(@Ñ(@ÐIcÔdÐdÜÜ�$—*‘*‹oØ—.‘.Ü% d§m¡mÓ4Ø#Ÿy™yŸ™°'Ò9ŒX�^Š^¼x¿}¹}Ø—.‘.ô	€Að ‡F�Fˆ1„IØ‡I�I„KØ‡J�Jˆq„MÜ! ! RÓ(Ð(r9   z/update-user)rH   c                 óx  — |j                  t        | j                  «      }|st        t        j
                  d¬«      ‚| j                  �›| j                  |j                  k7  r‚|j                  t        «      j                  t        j                  | j                  k(  «      j                  «       rt        t        j                  d¬«      ‚t        | j                  «      |_        | j                  �| j                  |_        | j                  �| j                  |_        | j                  rt        | j                  «      |_        | j"                  �>| j"                  j$                  dk(  rt&        j(                  nt&        j*                  |_        |j-                  «        |j/                  |«       t1        ||«      S )Nrr   rs   úEmail in userz   )rZ   r   rp   r   r   rv   r{   r@   r[   r   r€   r�   r|   r~   r‚   r#   r}   rQ   rƒ   r   rz   r„   r†   r‡   r'   rˆ   s       r:   Úupdate_userrŒ   Ñ   s#  € ð 	�‰Œt�T—\‘\Ó"€AÙÜ¬×(AÑ(AÐJZÔ[Ð[Ø‡z�zÐ $§*¡*°·±Ò"7Ø�8‰8”D‹>× Ñ ¤§¡¨t¯z©zÑ!9Ó:×@Ñ@ÔBÜ¬F×,DÑ,DÈ^Ô\Ð\Ü�d—j‘j“/ˆŒØ‡~�~Ð!Ø—n‘nˆŒØ‡~�~Ð!Ø—n‘nˆŒØ‡}‚}Ü)¨$¯-©-Ó8ˆÔØ‡y�yÐØ#'§9¡9§?¡?°gÒ#=”—’Ä8Ç=Á=ˆŒØ‡I�I„KØ‡J�Jˆq„MÜ! ! RÓ(Ð(r9   z/update-member-accountz/Admin update member email and/or reset passwordc                 ó^  — | j                   €'| j                  €t        t        j                  d¬«      ‚|j                  t        | j                  «      }|st        t        j                  d¬«      ‚|j                  t        j                  k7  rt        t        j                  d¬«      ‚| j                   �§t        | j                   «      }||j                   k7  rƒ|j                  t        «      j                  t        j                   |k(  t        j                  |j                  k7  «      j!                  «       }|rt        t        j"                  d¬«      ‚||_         | j                  �t%        | j                  «      |_        |j)                  «        |j+                  |«       t-        ||«      S )Nz Send email or password to updaters   zMember not foundzTarget account is not a memberr‹   )r{   r‚   r   r   ÚHTTP_400_BAD_REQUESTrZ   r   rp   rv   rQ   r   r„   r�   r@   r[   rD   r   r€   r#   r}   r†   r‡   r'   )rx   r<   rP   ÚmemberÚ	new_emailÚexistings         r:   Úupdate_member_accountr’   ë   s;  € ð ‡z�zÐ˜dŸm™mÐ3ÜÜ×3Ñ3Ø5ô
ð 	
ð
 �V‰V”D˜$Ÿ,™,Ó'€FÙÜ¬×(AÑ(AÐJ\Ô]Ð]Ø‡{�{”h—m‘mÒ#Ü¬×(CÑ(CÐLlÔmÐmà‡z�zÐÜ˜Ÿ
™
“Oˆ	Ø˜Ÿ™Ò$Ø—x‘x¤“~×,Ñ,¬T¯Z©Z¸9Ñ-DÄdÇgÁgÐQW×QZÑQZÑFZÓ[×aÑaÓcˆHÙÜ#´×0HÑ0HÐQ_Ô`Ð`Ø$ˆFŒLà‡}�}Ð Ü!.¨t¯}©}Ó!=ˆÔà‡I�I„KØ‡J�JˆvÔÜ! &¨"Ó-Ð-r9   z/update-member-personal-infozXUpdate member personal info (name, phone_number, level, module, week, Google Drive link)c                 ó2   — t        || «      }t        ||«      S r6   )r,   r'   ©rx   r<   rP   r„   s       r:   Ú!update_member_personal_info_router•     s   € ô ' r¨4Ó0€DÜ! $¨Ó+Ð+r9   z/list-member-google-drive-linkszMList members with Google Drive links for WhatsApp video submission managementzFilter by member name or emailrF   zall | with_link | without_linkÚsearchÚlink_statusc           	      ó¤   — t        | ||||¬«      \  }}|D �cg c]!  }t        j                  t        || «      «      ‘Œ# }	}t	        ||	¬«      S c c}w )N)r=   r>   r–   r—   )rX   Úmembers)r&   r   Úmodel_validater*   r   )
r<   rP   r=   r>   r–   r—   r™   rX   ÚmÚ
serializeds
             r:   Ú$list_member_google_drive_links_router�     sl   € ô  5Ø
ØØØØô�N€GˆUñ óáˆAô 	"×0Ñ0Ô1TÐUVÐXZÓ1[Õ\Øð ð ô )¨u¸jÔIÐIùò	s   ˜&Az/member-google-drive-linkz=Get one member's Google Drive link and programme week contextc                 óX   — t        || «      }t        j                  t        ||«      «      S r6   )r$   r   rš   r*   )rp   r<   rP   r„   s       r:   Ú"get_member_google_drive_link_routerŸ   =  s+   € ô ˜R Ó)€DÜ$×3Ñ3Ô4WÐX\Ð^`Ó4aÓbÐbr9   z /update-member-google-drive-linkz:Attach or update a member's personalized Google Drive linkc                 ó‚   — t        || j                  | j                  «      }t        j                  t        ||«      «      S r6   )r+   rp   Úgoogle_drive_linkr   rš   r*   r”   s       r:   Ú%update_member_google_drive_link_router¢   K  s7   € ô +¨2¨t¯|©|¸T×=SÑ=SÓT€DÜ$×3Ñ3Ô4WÐX\Ð^`Ó4aÓbÐbr9   z/update-user-programmez>Assign programme level, module, week, and session for a memberc                 ó2   — t        || «      }t        ||«      S r6   )r%   r'   r”   s       r:   Úupdate_user_programme_router¤   Y  s   € ô !  TÓ*€DÜ! $¨Ó+Ð+r9   z/list-member-inactive-followupszPList members inactive for N days (Need Follow-Up tab). Syncs table on each call.im  zDays without activity)rJ   rM   rN   Tz/Rebuild inactive follow-up table before listingÚinactive_daysÚsyncc           
      ó’   — t        | ||||¬«      \  }}}t        |||D �	cg c]  }	t        j                  |	«      ‘Œ c}	¬«      S c c}	w )N)r¥   r=   r>   r¦   )rX   r¥   r™   )r.   r   r   rš   )
r<   rP   r¥   r=   r>   r¦   r™   rX   Údaysr›   s
             r:   Ú$list_member_inactive_followups_router©   g  sY   € ô :Ø
˜-¨d¸%ÀdôÑ€GˆU�Dô .ØØÙFMÓNÁgÀÔ*×9Ñ9¸!Õ<ÀgÑNôð ùò Os    Az/sync-member-inactive-followupsz=Rebuild member_inactive_followups table from current activityc                 óB   — t        | |¬«      }t        d|› d|› d�¬«      S )N)r¥   zSynced z% inactive member(s) (no activity for z+ days)©Úmessage)r0   r   )r<   rP   r¥   r\   s       r:   Ú$sync_member_inactive_followups_router­   ~  s.   € ô +¨2¸]ÔK€EÜ˜W U GÐ+PÐQ^ÐP_Ð_fÐgÔhÐhr9   z$/mark-member-inactive-follow-up-sentz4Mark that admin sent follow-up to an inactive memberc                 ó€   — t        || j                  «      }|st        t        j                  d¬«      ‚t        d¬«      S )Nz#Inactive follow-up record not foundrs   zFollow-up marked as sentr«   )r/   rp   r   r   rv   r   )rx   r<   rP   Úrows       r:   Ú)mark_member_inactive_follow_up_sent_router°   Œ  s7   € ô .¨b°$·,±,Ó
?€CÙÜ¬×(AÑ(AÐJoÔpÐpÜÐ5Ô6Ð6r9   z/delete-userrz   c                 ó>  — | j                   |j                  k(  rt        t        j                  d¬«      ‚|j                  t        | j                   «      }|st        t        j                  d¬«      ‚|j                  |«       |j                  «        t        d¬«      S )NzCannot delete yourselfrs   rr   zUser deletedr«   )rp   rD   r   r   rŽ   rZ   r   rv   Údeleter†   r   )rx   r<   rz   ri   s       r:   Údelete_userr³   œ  sp   € à‡|�|�u—x‘xÒÜ¬×(CÑ(CÐLdÔeÐeØ
�‰Œt�T—\‘\Ó"€AÙÜ¬×(AÑ(AÐJZÔ[Ð[Ø‡I�Iˆa„LØ‡I�I„KÜ˜>Ô*Ð*r9   )r4   N)Wr   Úfastapir   r   r   r   r   Úsqlalchemy.ormr	   r
   Úapp.programme.models_programmer   r   r   r   Úapp.services.admin_servicer   Úapp.databaser   Úapp.dependenciesr   Ú
app.modelsr   r   Úapp.schemasr   r   r   r   r   r   r   r   r   r   r   r   r    r!   r"   Úapp.securityr#   Ú!app.services.admin_member_servicer$   r%   r&   r'   r(   r)   r*   r+   r,   Ú-app.services.member_inactive_followup_servicer-   r.   r/   r0   Úrouterr3   Údictr�   ÚtupleÚfloatÚ__annotations__r;   ÚintÚlistrG   rZ   rl   ro   rw   ÚpostÚHTTP_201_CREATEDr‰   rŒ   r’   r•   r�   rŸ   r¢   r¤   Úboolr©   r­   r°   r³   r8   r9   r:   Ú<module>rÉ      su  ðÞ ç DÕ DÝ "å 'ç [Ñ [Ý 9Ý Cå Ý 0ß %÷÷ ÷ ÷ ñ õ" '÷
÷ 
õ 
÷ó ñ 
˜(¨-¨Ô	9€àEGÐ �4˜˜U 5Ð*@Ð#@ÑAÐAÑBÓ Góð�Wð  Cð °ð ¸¸T¹
ó ð ‡�ØØ)Ø$ð ó ð
 ‡�ØØ)Ø;ð ó ñ ˜&“/ÙÐ)Ó*Ù�a˜A”Ù�r˜a CÔ(Ù! $Ð4aÔbÙ˜r a¨CÔ0ñ2Øð2àð2ð ð2ð ð	2ð
 �T‰/ð2ð ò2óóð2ðj ‡�ØØ˜
Ñ#ØAð ó ñ ˜&“/ÙÐ)Ó*Ù�a˜A”Ù�r˜a CÔ(Ù˜r a¨CÔ0ñØðàðð ðð ð	ð
 òóð
ð8 ‡�ØØØ1ð ó ð
 ‡�ØØØ8ð ó ð
 ‡�ØØØNð ó ñ ˜ Ð0BÔCÙ˜&“/ÙÐ)Ó*ñ)Øð)àð)ð ò)óóóð)ð ‡�ˆ^¨JÀF×D[ÑD[€Ó\Ù07¸³É7ÐSfÓKgñ )�jð ) gð )ÀDò )ó ]ð)ð  ‡�ˆ^¨J€Ó7ñ ˜&“/ÙÐ)Ó*ñ)Ø
ð)àð)ð ò)ó 8ð)ð2 ‡�ØØØ=ð ó ñ ˜&“/ÙÐ)Ó*ñ.Ø
)ð.àð.ð ò.óð
.ðB ‡�Ø"ØØfð ó ñ ˜&“/ÙÐ)Ó*ñ,Ø
.ð,àð,ð ò,óð
,ð ‡�Ø%Ø0Ø[ð ó ñ ˜&“/ÙÐ)Ó*Ù�a˜A”Ù�r˜a CÔ(Ù˜tÐ1QÔRÙØØ4ôñJØðJàðJð ðJð ð	Jð
 �$‰JðJð òJóð
Jð2 ‡�ØØ,ØKð ó ñ ˜ Ô#Ù˜&“/ÙÐ)Ó*ñcØðcàðcð òcóð
cð ‡�Ø&Ø,ØHð ó ñ ˜&“/ÙÐ)Ó*ñcØ
-ðcàðcð òcóð
cð ‡�ØØØLð ó ñ ˜&“/ÙÐ)Ó*ñ,Ø
$ð,àð,ð ò,óð
,ð ‡�Ø%Ø5Ø^ð ó ñ ˜&“/ÙÐ)Ó*ÙÐ4¸¸sÐPgÔhÙ�a˜A”Ù�r˜a CÔ(Ù�tÐ)ZÔ[ñØðàðð ðð ð	ð
 ðð òóð
ð$ ‡�Ø%ØØKð ó ñ ˜&“/ÙÐ)Ó*ÙÐ4¸¸sÔCñiØðiàðið òióð
ið ‡�Ø*ØØBð ó ñ ˜&“/ÙÐ)Ó*ñ7Ø
-ð7àð7ð ò7óð
7ð ‡�ˆ^¨G€Ó4Ù3:¸6³?ÑRYÐZmÓRnñ +�mð +¨ð +È4ò +ó 5ñ+r9   