Ë
    |Ñj„  ã                  óö   — d Z ddlmZ ddlmZmZmZ ddlmZmZ ddl	m
Z
 ddlmZmZ ddlmZmZ ddlmZ dd	lmZ dd
lmZ dZdZdd„Zdd„Zddœdd„Zdd„Zedœdd„Zeddddœ	 	 	 	 	 	 	 	 	 	 	 dd„Zdd„Zy) uD   Members inactive for N days â€” stored for admin Need Follow-Up tab.é    )Úannotations)ÚdatetimeÚ	timedeltaÚtimezone)ÚinspectÚtext)ÚEngine)ÚSessionÚ
joinedload)ÚUserÚUserRole)ÚMemberInactiveFollowUp)Úuser_programme_load_options)Úserialize_admin_memberé
   aˆ  
CREATE TABLE IF NOT EXISTS member_inactive_followups (
    id SERIAL PRIMARY KEY,
    user_id INTEGER NOT NULL UNIQUE REFERENCES users(id) ON DELETE CASCADE,
    last_active_at TIMESTAMP WITH TIME ZONE,
    days_inactive INTEGER NOT NULL DEFAULT 0,
    follow_up_sent_at TIMESTAMP WITH TIME ZONE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL
);
CREATE INDEX IF NOT EXISTS ix_member_inactive_followups_user_id ON member_inactive_followups (user_id);
CREATE INDEX IF NOT EXISTS ix_member_inactive_followups_days_inactive ON member_inactive_followups (days_inactive);
c                ó–  — | j                   j                  dk(  }	 t        | «      }d|j                  «       v }|s:|r8| j                  «       5 }|j                  t        t        «      «       d d d «       y |s7ddl	m
} ddlm} |j                  j                  d   j                  | d¬«       y y # t        $ r d}Y Œƒw xY w# 1 sw Y   y xY w)	NÚ
postgresqlÚmember_inactive_followupsFr   )Úmodels_admin)ÚBaseT)ÚbindÚ
checkfirst)ÚdialectÚnamer   Úget_table_namesÚ	ExceptionÚbeginÚexecuter   Ú$_CREATE_MEMBER_INACTIVE_FOLLOWUPS_PGÚappr   Úapp.databaser   ÚmetadataÚtablesÚcreate)ÚengineÚis_pgÚinspÚtable_existsÚconnr   r   s          úR/var/www/html/kelly/kelly-backend/app/services/member_inactive_followup_service.pyÚ%sync_member_inactive_followups_schemar+   !   s´   € Ø�N‰N×Ñ <Ñ/€EðÜ�v‹ˆØ2°d×6JÑ6JÓ6LÐLˆñ ™EØ�\‰\Œ^˜tØ�L‰LœÔBÓCÔD÷ àáÝ$Ý%à�‰×ÑÐ8Ñ9×@Ñ@ÀfÐY]Ð@Õ^ð	 øô ò ØŠðú÷ àús   ›B. ÁB?Â.B<Â;B<Â?Cc                óÀ  — 	 t        | «      }d|j                  «       vry |j                  d«      D �ch c]  }|d   ’Œ	 }}d|v ry | j                  j
                  dk(  }| j                  «       5 }|r|j                  t        d«      «       n	 |j                  t        d«      «       d d d «       y c c}w # t        $ r Y y w xY w# t        $ r Y Œ(w xY w# 1 sw Y   y xY w)NÚusersr   Úlast_active_atr   zRALTER TABLE users ADD COLUMN IF NOT EXISTS last_active_at TIMESTAMP WITH TIME ZONEz4ALTER TABLE users ADD COLUMN last_active_at DATETIME)	r   r   Úget_columnsr   r   r   r   r   r   )r%   r'   ÚcÚcolsr&   r)   s         r*   Ú sync_users_last_active_at_columnr2   5   sá   € ðÜ�v‹ˆØ˜$×.Ñ.Ó0Ñ0ØØ#'×#3Ñ#3°GÔ#<Ó=Ñ#<˜a��&“	Ð#<ˆÐ=ð ˜4ÑØà�N‰N×Ñ <Ñ/€EØ	�‰Œ˜4ÙØ�L‰LÜð>óõðØ—‘œTÐ"XÓYÔZ÷ 
ˆùò >øÜò Ùðûô$ ò Ùðú÷ 
ˆúsP   ‚B6  B6 ³B1¿B6 Á/CÂCÂ1B6 Â6	CÃCÃ	CÃCÃCÃCÃCT)Úcommitc               ó\  — |j                   t        j                  k7  ryt        j                  t
        j                  «      }|j                  }|�C|j                  € |j                  t
        j                  ¬«      }||z
  j                  «       dk  ry||_        |r| j                  «        yy)z8Record member activity (login or authenticated API use).N©Útzinfoi„  )Úroler   Úuserr   Únowr   Úutcr.   r6   ÚreplaceÚtotal_secondsr3   )Údbr8   r3   r9   Úlasts        r*   Útouch_user_last_activer?   Q   s‡   € à‡y�y”H—M‘MÒ!ØÜ
�,‰,”x—|‘|Ó
$€CØ×Ñ€DØÐØ�;‰;ÐØ—<‘<¤x§|¡|�<Ó4ˆDØ�$‰J×%Ñ%Ó'¨#Ò-ØØ€DÔÙØ
�	‰	�ð ó    c                óz   — | j                   �| j                   S | j                  �| j                  S | j                  S ©N)r.   Ú
updated_atÚ
created_at)r8   s    r*   Ú_effective_last_activerE   a   s8   € Ø×ÑÐ&Ø×"Ñ"Ð"Ø‡�Ð"Ø�‰ÐØ�?‰?Ðr@   ©Úinactive_daysc               ó  ‡— t        dt        |«      «      }t        j                  t        j
                  «      t        |¬«      z
  }| j                  t        «      j                  t        j                  t        j                  k(  t        j                  j                  d«      «      j                  «       }t!        «       }|D ]]  }t#        |«      }|€Œ|j$                  € |j'                  t        j
                  ¬«      }||k  sŒC|j)                  |j*                  «       Œ_ | j                  t,        «      j                  «       }|D �ci c]  }|j.                  |“Œ }	}t        j                  t        j
                  «      }
|D ]¯  Št1        ˆfd„|D «       d«      }|€Œt#        |«      }|r,|j$                  € |j'                  t        j
                  ¬«      }t        ||r|
|z
  j2                  n|«      }|	j5                  ‰«      }|€| j)                  t-        ‰||¬«      «       Œ¢||_        ||_        Œ± |	j;                  «       D ]  \  Š}‰|vsŒ| j=                  |«       Œ | j?                  «        tA        |«      S c c}w )zy
    Rebuild member_inactive_followups from current user activity.
    Returns count of rows currently in the table.
    é   )ÚdaysTNr5   c              3  óB   •K  — | ]  }|j                   ‰k(  sŒ|–— Œ y ­wrB   )Úid)Ú.0ÚmÚuser_ids     €r*   Ú	<genexpr>z1sync_member_inactive_followups.<locals>.<genexpr>†   s   øè ø€ Ð=¡'˜Q¨Q¯T©T°W«_”q¡'ùs   ƒ˜)rO   r.   Údays_inactive)!ÚmaxÚintr   r9   r   r:   r   Úqueryr   Úfilterr7   r   r8   Ú	is_activeÚis_ÚallÚsetrE   r6   r;   ÚaddrL   r   rO   ÚnextrJ   Úgetr.   rQ   ÚitemsÚdeleter3   Úlen)r=   rG   ÚcutoffÚmembersÚinactive_user_idsÚmemberr>   Úexisting_rowsÚrowÚexisting_by_userr9   rJ   rO   s               @r*   Úsync_member_inactive_followupsrg   i   s  ø€ ô
 ˜œ3˜}Ó-Ó.€MÜ�\‰\œ(Ÿ,™,Ó'¬)¸Ô*GÑG€Fð 	�‰”‹ß	‰”—	‘	œXŸ]™]Ñ*¬D¯N©N×,>Ñ,>¸tÓ,DÓ	Eß	‰‹ð ô #&£%ÐÛˆÜ% fÓ-ˆØˆ<ØØ�;‰;ÐØ—<‘<¤x§|¡|�<Ó4ˆDØ�6‹>Ø×!Ñ! &§)¡)Õ,ð ð —H‘HÔ3Ó4×8Ñ8Ó:€MÙ4AÓB±M¨S˜Ÿ™ SÑ(°MÐÐBä
�,‰,”x—|‘|Ó
$€CÛ$ˆÜÓ=¡'Ó=¸tÓDˆØˆ>ØÜ% fÓ-ˆÙ�D—K‘KÐ'Ø—<‘<¤x§|¡|�<Ó4ˆDÜ�=±t 3¨¡:×"3Ò"3ÀÓOˆà×"Ñ" 7Ó+ˆØˆ;Ø�F‰FÜ&Ø#Ø#'Ø"&ôõð "&ˆCÔØ $ˆCÕð) %ð, )×.Ñ.Ö0‰ˆ�ØÐ+Ò+Ø�I‰I�c�Nð 1ð ‡I�I„KÜÐ Ó!Ð!ùò= Cs   Å	I?é2   )rG   ÚskipÚlimitÚsyncc               ó0  — |rt        | |¬«       | j                  t        «      j                   t	        t        j
                  «      j                  t        «       Ž «      j                  t        j                  j                  «       t        j                  j                  «       «      }|j                  «       }|j                  |«      j                  |«      j                  «       }g }|D ]¦  }	|	j
                  }
|
sŒt        |
| «      }|j!                  |	j                  |	j"                  |
j$                  xs |
j&                  |
j&                  |	j(                  |	j                  |	j*                  d u|	j*                  |j-                  «       dœ	«       Œ¨ |||fS )NrF   )	rL   rO   Ú	full_nameÚemailr.   rQ   Úfollow_up_sentÚfollow_up_sent_atrc   )rg   rT   r   Úoptionsr   r8   r   Úorder_byrQ   ÚdescrL   ÚcountÚoffsetrj   rX   r   ÚappendrO   rm   rn   r.   rp   Ú
model_dump)r=   rG   ri   rj   rk   rT   ÚtotalÚrowsÚ
serializedre   r8   Úmember_publics               r*   Úlist_member_inactive_followupsr|   £   sN  € ñ Ü& r¸ÕGð 	�‰Ô'Ó(ß	‰Ð@”Ô2×7Ñ7Ó8×@Ñ@ÔB]ÓB_Ð`Ó	aß	‰Ô(×6Ñ6×;Ñ;Ó=Ô?U×?XÑ?X×?]Ñ?]Ó?_Ó	`ð 
ð
 �K‰K‹M€EØ�<‰<˜Ó×#Ñ# EÓ*×.Ñ.Ó0€Dà€JÛˆØ�x‰xˆÙØÜ.¨t°RÓ8ˆØ×Ñà—f‘fØŸ;™;Ø!Ÿ^™^Ò9¨t¯z©zØŸ™Ø"%×"4Ñ"4Ø!$×!2Ñ!2Ø"%×"7Ñ"7¸tÐ"CØ%(×%:Ñ%:Ø'×2Ñ2Ó4ñ
õ	
ð ð$ �u˜mÐ+Ð+r@   c                ó$  — | j                  t        «      j                  t        j                  |k(  «      j	                  «       }|sy t        j                  t        j                  «      |_	        | j                  «        | j                  |«       |S rB   )rT   r   rU   rO   Úfirstr   r9   r   r:   rp   r3   Úrefresh)r=   rO   re   s      r*   Ú#mark_member_inactive_follow_up_sentr€   Ì   sf   € Ø
�(‰(Ô)Ó
*×
1Ñ
1Ô2H×2PÑ2PÐT[Ñ2[Ó
\×
bÑ
bÓ
d€CÙØÜ$ŸL™L¬¯©Ó6€CÔØ‡I�I„KØ‡J�Jˆs„OØ€Jr@   N)r%   r	   ÚreturnÚNone)r=   r
   r8   r   r3   Úboolr�   r‚   )r8   r   r�   zdatetime | None)r=   r
   rG   rS   r�   rS   )r=   r
   rG   rS   ri   rS   rj   rS   rk   rƒ   r�   ztuple[list[dict], int, int])r=   r
   rO   rS   r�   zMemberInactiveFollowUp | None) Ú__doc__Ú
__future__r   r   r   r   Ú
sqlalchemyr   r   Úsqlalchemy.enginer	   Úsqlalchemy.ormr
   r   Ú
app.modelsr   r   Úapp.models_adminr   Ú(app.programme.services.programme_servicer   Ú!app.services.admin_member_servicer   ÚDEFAULT_INACTIVE_DAYSr   r+   r2   r?   rE   rg   r|   r€   © r@   r*   Ú<module>r�      s°   ðÙ Jå "ç 2Ñ 2ç $Ý $ß .ç %Ý 3Ý PÝ DàÐ ð(Ð $ó_ó(ð8 GKõ ó ð I^õ 7"ðz /ØØØñ&,Øð&,ð ð&,ð ð	&,ð
 ð&,ð ð&,ð !ó&,ôRr@   