Ë
    ð(j2á  ã            
      óÊ  — U d Z ddlmZ ddlZddl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mZmZmZ dd	l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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.m/Z/ d~d„Z0dd„Z1d€d„Z2dd„Z3d�d„Z4h d£Z5h d£Z6h d£Z7h d£Z8d‚d„Z9d‚d„Z:dƒd„Z;d„d„Z<d‚d„Z=d…d„Z>dZ?de@d <   d!ZAd„d"„ZBd„d#„ZCd„d$„ZDd†d%„ZEd‡d&„ZFdˆd'„ZGd‰d(„ZHddd)œ	 	 	 	 	 	 	 	 	 	 	 	 	 dŠd*„ZIddd+d+d,œ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d‹d-„ZJdŒd.„ZKd�d/„ZLd‚d0„ZMd…d1„ZNd2ZOd„d3„ZPdŽd5„ZQd�d6„ZRd�d7„ZSd‰d8„ZTd‘d9„ZUddd)œ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d’d:„ZVddd+d+d,œ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d“d;„ZWd”d<„ZXd„d=„ZYd•d>„ZZdd?œd–d@„Z[dd?œ	 	 	 	 	 	 	 d—dA„Z\d˜dB„Z]dC„ Z^	 	 	 	 	 	 	 	 d™dD„Z_dšdE„Z`d„dF„ZadGZbd„dH„Zcd‚dI„Zdd›dJ„ZedœdK„Zfd�dL„ZgdždM„Zh	 dŸ	 	 	 	 	 	 	 	 	 d dN„Zid+dOœ	 	 	 	 	 	 	 d¡dP„Zje*jÖ                  dQe*jØ                  d4e*jÚ                  dRe*jÜ                  dSe*jÞ                  dTiZpdUe@dV<   dWZqd„dX„ZrdYZsd„dZ„Ztd¢d[„Zud£d\„Zvd¤d]„Zwd¥d^„Zxd¦d_„Zyd+d`œ	 	 	 	 	 d§da„Zzd¨db„Z{d©dc„Z|dªdd„Z}dªde„Z~dªdf„Zd«dg„Z€	 	 	 	 	 	 	 	 	 	 	 	 	 	 d¬dh„Z�e#�j                  die#�j                  djiZ„dke@dl<   dmZ…dnZ†d„do„Z‡d­dp„Zˆd®dq„Z‰d¯dr„ZŠd°ds„Z‹d±dt„ZŒd²du„Z�d³dv„ZŽd´dw„Z�d+dxœ	 	 	 	 	 	 	 	 	 	 	 dµdy„Z�d¶dz„Z‘d·d{„Z’d¸d|„Z“d¹d}„Z”y)ºz¢
Admin panel business logic.

Sections:
  - Shared uploads & DB sync
  - Community posts
  - Rewards
  - Member notifications
  - Content (Privacy / Terms / FAQ)
é    )ÚannotationsN)ÚdatetimeÚtimezone)ÚZoneInfo)ÚPath)ÚHTTPExceptionÚ
UploadFileÚstatus)ÚinspectÚor_ÚtextÚfunc)ÚEngine)ÚSessionÚ
joinedload)Úget_settings)ÚUserÚUserRole)ÚCommunityPostÚCommunityPostCommentÚCommunityPostLikeÚCommunityPostShareÚContentPageÚContentPageSlugÚFaqÚ	FaqStatusÚMemberPointsÚMemberSessionProgressÚMemberSessionProgressImageÚMemberNotificationÚMemberNotificationTypeÚRewardÚRewardClaim)Ú MemberSessionProgressSaveRequestÚRewardClaimRequestc                ó  — 	 | j                  t        d«      d|i«      j                  «       }|syt        |j                  «      t        |j                  «      t        |j                  «      dœS # t        $ r | j	                  «        Y yw xY w)z¦Return the member_points row for a user if present.

    Returns a dict with keys: user_id, assigned_points_total, available_points
    or None if no row exists.
    z_SELECT user_id, assigned_points_total, available_points FROM member_points WHERE user_id = :uidÚuidN©Úuser_idÚassigned_points_totalÚavailable_points)	Úexecuter   ÚfetchoneÚ	ExceptionÚrollbackÚintr)   r*   r+   )Údbr)   Úrows      ú?/var/www/html/kelly/kelly-backend/app/services/admin_service.pyÚget_member_pointsr4   .   s­   € ðØ�j‰jœÐó  Að  DIð  KRð  CSó  T÷  ]ñ  ]ó  _ˆñ ØÜ˜3Ÿ;™;Ó'Ä#Àc×F_ÑF_ÓB`ÔvyÐz}÷  {Oñ  {Oó  wPñ  Qð  Qøô ò Ø
�‰ŒÙðús   ‚+A/ Á/BÂ
Bc                ó¢  — |xs g D �cg c]  }t        |«      dkD  sŒt        |«      ‘Œ }}|si S 	 | j                  t        d«      d|i«      j                  «       }i }|D ]F  }t        |j                  «      }|t        |j                  «      t        |j                  «      dœ||<   ŒH |S c c}w # t        $ r | j                  «        i cY S w xY w)z=Bulk read cached points from member_points table (fast path).r   zeSELECT user_id, assigned_points_total, available_points FROM member_points WHERE user_id = ANY(:uids)Úuidsr(   )	r0   r,   r   Úfetchallr.   r/   r)   r*   r+   )r1   Úuser_idsr'   ÚidsÚrowsÚoutÚrs          r3   Úget_member_points_bulkr=   >   sÜ   € à (¢¨B Ó
@¡˜´3°s³8¸a³<Œ3ˆs�8 €CÐ
@ÙØˆ	ð
Ø�z‰zÜð@óð �SˆMó
÷ ‰(‹*ð 	ð €CÛˆÜ�!—)‘)‹nˆàÜ%(¨×)@Ñ)@Ó%AÜ # A×$6Ñ$6Ó 7ñ
ˆˆCŠð ð €Jùò- Aøô ò Ø
�‰ŒØŠ	ðús   ‰B+�B+±+B0 Â0CÃCc                óP  — | j                  t        j                  t        j                  t        j
                  «      d«      «      j                  t        j                  |k(  «      j                  «       xs d}| j                  t        j                  t        j                  t        j                  «      d«      «      j                  t        j                  |k(  t        t        j                  «      «      j                  «       xs d}t        |«      }|t        |«      z
  }|||dœS )Nr   r(   )Úqueryr   ÚcoalesceÚsumr   Úassigned_pointsÚfilterr)   Úscalarr#   Útotal_pointsÚ_reward_claim_reserves_pointsr
   r0   )r1   r)   Úassigned_sumÚclaimed_sumr*   r+   s         r3   Úcalculate_member_pointsrI   Y   sî   € Ø—8‘8œDŸM™M¬$¯(©(Ô3H×3XÑ3XÓ*YÐ[\Ó]Ó^×eÑeÜ×%Ñ%¨Ñ0óç�fƒhòàð ð —(‘(œ4Ÿ=™=¬¯©´+×2JÑ2JÓ)KÈQÓOÓP×WÑWÜ×Ñ˜wÑ&Ü%¤k×&8Ñ&8Ó9ó÷ �fƒhòð ð ô
   Ó-ÐØ,¬s°;Ó/?Ñ?ÐàØ!6Ø,ñð ó    c           	     óþ  — |xs g D �cg c]  }t        |«      dkD  sŒt        |«      ‘Œ }}|si S t        | |«      }|D �cg c]	  }||vsŒ|‘Œ }}|s|S | j                  t        j                  t        j                  t        j                  t        j                  «      d«      «      j                  t        j                  j                  |«      «      j                  t        j                  «      j                  «       }|D ��ci c]  \  }}t        |«      t        |xs d«      “Œ  }}}| j                  t        j                  t        j                  t        j                  t        j                  «      d«      «      j                  t        j                  j                  |«      t        t        j                   «      «      j                  t        j                  «      j                  «       }	|	D ��ci c]  \  }}t        |«      t        |xs d«      “Œ  }
}}i |¥}|D ]D  }t        |j#                  |d«      «      }|t        |
j#                  |d«      «      z
  }|||dœ||<   ŒF |S c c}w c c}w c c}}w c c}}w )z>Compute points for many users in 2 aggregate queries (no N+1).r   r(   )r0   r=   r?   r   r)   r   r@   rA   rB   rC   Úin_Úgroup_byÚallr#   rE   rF   r
   Úget)r1   r8   r'   r9   ÚcachedÚmissingÚassigned_rowsÚtotalÚassigned_by_userÚclaimed_rowsÚclaimed_by_userr;   r*   r+   s                 r3   Úcalculate_member_points_bulkrW   k   s"  € à (¢¨B Ó
@¡˜´3°s³8¸a³<Œ3ˆs�8 €CÐ
@ÙØˆ	ô $ B¨Ó,€FÙ!Ó7™c�s S°Ò%6Šs˜c€GÐ7ÙØˆð 	�‰Ô&×.Ñ.´·±¼d¿h¹hÔG\×GlÑGlÓ>mÐopÓ0qÓrß	‰Ô%×-Ñ-×1Ñ1°'Ó:Ó	;ß	‰Ô'×/Ñ/Ó	0ß	‰‹ð	 ñ ERÔRÁM±j°c¸5œ˜C›¤# e¢j¨q£/Ñ1ÀMÐÑRð 	�‰”×$Ñ$¤d§m¡m´D·H±H¼[×=UÑ=UÓ4VÐXYÓ&ZÓ[ß	‰Ü×Ñ×#Ñ# GÓ,Ü)¬+×*<Ñ*<Ó=ó

÷ 
‰”+×%Ñ%Ó	&ß	‰‹ð ñ DPÔPÁ<±Z°S¸%”s˜3“x¤ U¢Z¨a£Ñ0À<€OÑPà%˜f˜:€CÛˆÜ #Ð$4×$8Ñ$8¸¸aÓ$@Ó AÐØ0´3°×7JÑ7JÈ3ÐPQÓ7RÓ3SÑSÐàØ%:Ø 0ñ
ˆˆCŠð ð €JùòM Aùò 8ùó Sùó Qs"   ‰I)�I)Á 	I.Á
I.Ä#I3Ç4#I9c                ó
  — t        | |«      }| j                  t        «      j                  t        j                  |k(  «      j                  «       }|€t        |¬«      }| j                  |«       |d   |_        |d   |_        |S )z?Recalculate and cache a member's assigned and available points.©r)   r*   r+   )	rI   r?   r   rC   r)   ÚfirstÚaddr*   r+   )r1   r)   Úpointsr2   s       r3   Úrefresh_member_pointsr]   –   sv   € ä$ R¨Ó1€Fà
�(‰(”<Ó
 ×
'Ñ
'¬×(<Ñ(<ÀÑ(GÓ
H×
NÑ
NÓ
P€CØ
€{Ü 7Ô+ˆØ
�‰ˆsŒà &Ð'>Ñ ?€CÔØ!Ð"4Ñ5€CÔØ€JrJ   >   ú.movú.mp4ú.webmú.gifú.jpgú.pngú.jpegú.webp>   ú	video/mp4ú
video/webmúvideo/quicktimeú	image/gifú	image/pngú
image/jpegú
image/webp>   ra   rb   rc   rd   re   >   ri   rj   rk   rl   c                 óV   — t        t        «      j                  «       j                  d   S )Né   )r   Ú__file__ÚresolveÚparents© rJ   r3   Ú	repo_rootrs   ´   s    € Ü”‹>×!Ñ!Ó#×+Ñ+¨AÑ.Ð.rJ   c                 óÌ   — t        «       } | j                  xs dj                  «       }t        |«      }|j	                  «       st        «       |z  }|j                  dd¬«       |S )NÚuploadsT©rq   Úexist_ok)r   Úcommunity_upload_dirÚstripr   Úis_absoluters   Úmkdir)ÚsettingsÚrawÚbases      r3   Úuploads_base_dirr   ¸   sX   € Ü‹~€HØ×(Ñ(Ò5¨I×
<Ñ
<Ó
>€CÜ�‹9€DØ×ÑÔÜ‹{˜TÑ!ˆØ‡J�J�t d€JÔ+Ø€KrJ   c                óº   — | sy t        «       }|j                  j                  d«      }|j                  j	                  d«      }|› d|› d| j                  d«      › �S )NÚ/)r   Úhttp_api_prefixÚrstripÚcommunity_uploads_url_segmentry   Úlstrip)Úrelative_pathr|   ÚprefixÚuploads_segments       r3   Úmedia_public_urlr‰   Â   s_   € ÙØÜ‹~€HØ×%Ñ%×,Ñ,¨SÓ1€FØ×<Ñ<×BÑBÀ3ÓG€OØˆX�Q�Ð' q¨×)=Ñ)=¸cÓ)BÐ(CÐDÐDrJ   c                ó  — t        | «       t        | «       t        | «       t        | «       t	        | «       t        | «       ddlm}  || «       t        | «       t        | «       ddl
m}m} ddlm}  || «        || «        || «       y)z,Run all admin table sync helpers on startup.r   )Úsync_admin_notifications_schema)Ú%sync_member_inactive_followups_schemaÚ sync_users_last_active_at_column)Úsync_users_profile_image_columnN)Úsync_community_post_columnsÚsync_rewards_schemaÚsync_reward_claims_schemaÚ#sync_member_session_progress_schemaÚ*sync_member_session_progress_images_schemaÚ sync_member_notifications_schemaÚ'app.services.admin_notification_servicer‹   Úsync_content_schemaÚsync_member_points_schemaÚ-app.services.member_inactive_followup_servicerŒ   r�   Ú'app.services.user_profile_image_servicerŽ   )Úenginer‹   rŒ   r�   rŽ   s        r3   Úsync_admin_schemasr›   Ë   sp   € ä Ô'Ü˜ÔÜ˜fÔ%Ü'¨Ô/Ü.¨vÔ6Ü$ VÔ,ÝWá# FÔ+Ü˜ÔÜ˜fÔ%÷õ Xá$ VÔ,Ù# FÔ+Ù)¨&Õ1rJ   c                 óF   — t        «       dz  } | j                  dd¬«       | S )NÚ	communityTrv   ©r   r{   ©Údests    r3   rx   rx   æ   s$   € ÜÓ Ñ+€DØ‡J�J�t d€JÔ+Ø€KrJ   c           	   ƒ  óº  K  — t        «       }| j                  xs dj                  d«      d   j                  «       j	                  «       }| j
                  xs d}t        |«      j                  j	                  «       }|s)|j                  d«      rd|j                  dd«      d   z   }|t        vr:t        t        j                  dd	j                  t        t        «      «      › �¬
«      ‚|r#|t        vrt        t        j                  d¬
«      ‚| j!                  «       ƒ d {  –—† }|j"                  dz  dz  }t%        |«      |kD  r)t        t        j                  d|j"                  › d�¬
«      ‚|st        t        j                  d¬
«      ‚t'        j(                  «       j*                  › |› �}t-        «       |z  }|j/                  |«       d|› �|xs dfS 7 Œµ­w)NÚ Ú;r   úimage/Ú.r�   é   z!Unsupported media type. Allowed: ú, ©Ústatus_codeÚdetailzUnsupported media content typeé   zMedia file too large (max ú MB)zEmpty media filez
community/úapplication/octet-stream)r   Úcontent_typeÚsplitry   ÚlowerÚfilenamer   ÚsuffixÚ
startswithÚ"ALLOWED_COMMUNITY_MEDIA_EXTENSIONSr   r
   ÚHTTP_400_BAD_REQUESTÚjoinÚsortedÚ%ALLOWED_COMMUNITY_MEDIA_CONTENT_TYPESÚreadÚcommunity_max_upload_mbÚlenÚuuidÚuuid4Úhexrx   Úwrite_bytes)	Úmediar|   r®   r±   ÚextÚdataÚ	max_bytesÚstored_namer    s	            r3   Úsave_community_mediarÅ   ì   s°  è ø€ Ü‹~€HØ×&Ñ&Ò,¨"×3Ñ3°CÓ8¸Ñ;×AÑAÓC×IÑIÓK€LØ�~‰~Ò# €HÜ
ˆx‹.×
Ñ
×
%Ñ
%Ó
'€CÙ�<×*Ñ*¨8Ô4Ø�L×&Ñ& s¨AÓ.¨qÑ1Ñ1ˆØ
Ô4Ñ4ÜÜ×3Ñ3Ø6°t·y±yÄÔHjÓAkÓ7lÐ6mÐnô
ð 	
ñ ˜Ô,QÑQÜ¬×(CÑ(CÐLlÔmÐmà—‘“×€DØ×0Ñ0°4Ñ7¸$Ñ>€IÜ
ˆ4ƒy�9ÒÜÜ×3Ñ3Ø/°×0PÑ0PÐ/QÐQUÐVô
ð 	
ñ Ü¬×(CÑ(CÐL^Ô_Ð_ä—Z‘Z“\×%Ñ%Ð& s eÐ,€KÜÓ! KÑ/€DØ×Ñ�TÔØ˜�}Ð% |Ò'QÐ7QÐQÐQð úó   ‚D!GÄ#GÄ$B6G))Útitlez VARCHAR(255) NOT NULL DEFAULT '')Ú	post_typez&VARCHAR(64) NOT NULL DEFAULT 'general')ÚcontentzTEXT NOT NULL DEFAULT '')Ú
media_pathzVARCHAR(512))Úmedia_content_typezVARCHAR(128))Úcreated_by_idÚINTEGER)Ú
created_atú&TIMESTAMP WITH TIME ZONE DEFAULT NOW())Ú
updated_atrÏ   ztuple[tuple[str, str], ...]Ú_COMMUNITY_POST_COLUMN_DDLSaú  
CREATE TABLE IF NOT EXISTS community_posts (
    id SERIAL PRIMARY KEY,
    title VARCHAR(255) NOT NULL DEFAULT '',
    post_type VARCHAR(64) NOT NULL DEFAULT 'general',
    content TEXT NOT NULL DEFAULT '',
    media_path VARCHAR(512),
    media_content_type VARCHAR(128),
    created_by_id INTEGER,
    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_community_posts_id ON community_posts (id);
c           
     ó:  — 	 t        | «      }d|j                  «       vry |j                  d«      D �ch c]  }|d   ’Œ	 }}| j                  j
                  dk(  }| j                  «       5 }t        D ]N  \  }}||v rŒ|r!|j                  t        d|› d|› �«      «       Œ.	 |j                  t        d|› d|› �«      «       ŒP 	 d d d «       |sy 	 t        | «      }|j                  d«      D �ch c]  }|d   ’Œ	 }	}d|	vr4	 | j                  «       5 }|j                  t        d«      «       d d d «       t        | «       y c c}w # t        $ r Y y w xY w# t        $ r Y Œíw xY w# 1 sw Y   Œ xY wc c}w # t        $ r Y y w xY w# 1 sw Y   ŒXxY w# t        $ r Y Œfw xY w)	NÚcommunity_postsÚnameÚ
postgresqlz5ALTER TABLE community_posts ADD COLUMN IF NOT EXISTS Ú z'ALTER TABLE community_posts ADD COLUMN Ú"community_posts_created_by_id_fkeyzó
                        ALTER TABLE community_posts
                        ADD CONSTRAINT community_posts_created_by_id_fkey
                        FOREIGN KEY (created_by_id) REFERENCES users(id) ON DELETE SET NULL
                        )r   Úget_table_namesÚget_columnsr.   ÚdialectrÔ   ÚbeginrÑ   r,   r   Úget_foreign_keysÚ_fix_post_type_enum_to_varchar)
rš   ÚinspÚcÚcolsÚis_pgÚconnrÔ   ÚddlÚfkÚfkss
             r3   Ú_sync_community_post_columnsræ   &  s»  € ðÜ�v‹ˆØ D×$8Ñ$8Ó$:Ñ:ØØ#'×#3Ñ#3Ð4EÔ#FÓGÑ#F˜a��&“	Ð#FˆÐGð �N‰N×Ñ <Ñ/€EØ	�‰Œ˜4ß4‰IˆD�#Ø�t‰|ØÙØ—‘œTÐ$YÐZ^ÐY_Ð_`ÐadÐ`eÐ"fÓgÕhðØ—L‘L¤Ð(OÐPTÈvÐUVÐWZÐV[Ð&\Ó!]Õ^ñ 5÷ 
ñ ØðÜ�v‹ˆØ$(×$9Ñ$9Ð:KÔ$LÓMÑ$L˜bˆr�&‹zÐ$LˆÐMð ,°3Ñ6ð	Ø—‘” 4Ø—‘Üðóô÷  ô # 6Õ*ùòS HøÜò Ùðûô !ò Ùðú÷ 
ˆüò" NøÜò Ùðú÷
  �ûô ò 	Ùð	ús¤   ‚E  E ³D?¿E Á*5E"Â  EÃ E"ÃE3 Ã-E.Ã9E3 Ä F ÄFÄ+F Ä?E Å	EÅEÅ	EÅE"ÅEÅE"Å"E+Å.E3 Å3	E?Å>E?ÆFÆF Æ	FÆFc                óº  — | j                   j                  dk7  ry 	 | j                  «       5 }|j                  t	        d«      «      j                  «       }d d d «       r|d   dk7  ry | j                  «       5 }|j                  t	        d«      «       |j                  t	        d«      «       d d d «       y # 1 sw Y   ŒbxY w# t        $ r Y y w xY w# 1 sw Y   y xY w)NrÕ   zâ
                    SELECT udt_name FROM information_schema.columns
                    WHERE table_schema = 'public' AND table_name = 'community_posts'
                      AND column_name = 'post_type'
                    r   ÚcommunityposttypezYALTER TABLE community_posts ALTER COLUMN post_type TYPE VARCHAR(64) USING post_type::textzHALTER TABLE community_posts ALTER COLUMN post_type SET DEFAULT 'general')rÚ   rÔ   Úconnectr,   r   r-   r.   rÛ   )rš   râ   r2   s      r3   rÝ   rÝ   W  sÉ   € Ø‡~�~×Ñ˜lÒ*ØðØ�^‰^Ô Ø—,‘,Üðóó÷ ‰h‹jð ÷ ñ �#�a‘&Ð/Ò/ØØ	�‰Œ˜4Ø�‰ÜØkóô	
ð
 	�‰”TÐdÓeÔf÷ 
ˆ÷ Ðûô ò Ùðú÷ 
ˆús4   œC ¬)B6ÁC Á85CÂ6B?Â;C Ã	CÃCÃCc                ó´  — | j                   j                  dk(  }	 t        | «      }d|j                  «       v }|s:|r8| j                  «       5 }|j                  t        t        «      «       d d d «       n8|s6ddl	m
} ddlm} |j                  j                  d   j                  | d¬«       t!        | «       ddlm} |j                  j                  d	   j                  | d¬«       |j                  j                  d
   j                  | d¬«       |j                  j                  d   j                  | d¬«       y # t        $ r d}Y �Œw xY w# 1 sw Y   Œ«xY w)NrÕ   rÓ   Fr   ©Úmodels_admin©ÚBaseT©ÚbindÚ
checkfirstÚcommunity_post_likesÚcommunity_post_commentsÚcommunity_post_shares)rÚ   rÔ   r   rØ   r.   rÛ   r,   r   Ú_CREATE_COMMUNITY_POSTS_PGÚapprì   Úapp.databaserî   ÚmetadataÚtablesÚcreateræ   ©rš   rá   rÞ   Útable_existsrâ   rì   rî   s          r3   r�   r�   r  s#  € Ø�N‰N×Ñ <Ñ/€EðÜ�v‹ˆØ(¨D×,@Ñ,@Ó,BÐBˆñ ™EØ�\‰\Œ^˜tØ�L‰LœÔ8Ó9Ô:÷ ˆ^áÝ$Ý%à�‰×ÑÐ.Ñ/×6Ñ6¸FÈtÐ6ÔTä  Ô(å!à‡M�M×ÑÐ/Ñ0×7Ñ7¸VÐPTÐ7ÔUØ‡M�M×ÑÐ2Ñ3×:Ñ:ÀÐSWÐ:ÔXØ‡M�M×ÑÐ0Ñ1×8Ñ8¸fÐQUÐ8ÕVøô% ò Ø‹ðú÷ ˆ^ús   ›D< ÁEÄ<EÅ
EÅEc                óÒ   — | j                   | j                  | j                  | j                  r| j                  j                  nd | j
                  | j                  | j                  dœS )N)ÚidÚpost_idr)   Ú	user_nameÚcommentrÎ   rÐ   )rþ   rÿ   r)   ÚuserÚ	full_namer  rÎ   rÐ   )r  s    r3   Úserialize_community_commentr  Œ  sO   € à�j‰jØ—?‘?Ø—?‘?Ø/6¯|ª|�W—\‘\×+Ò+ÀØ—?‘?Ø×(Ñ(Ø×(Ñ(ñð rJ   c                ó¤  — i d| j                   “d| j                  xs d“d| j                  “d| j                  “dt	        | j
                  «      “d| j                  “dt        | j
                  «      “d	| j                  “d
| j                  r| j                  j                  nd “d| j                  “d| j                  “dd“dd“dd“di “dd“dg “S )Nrþ   rÇ   r¢   rÈ   rÉ   Ú	media_urlrË   Ú	has_mediarÌ   Úcreated_by_namerÎ   rÐ   Úlikes_countr   Úcomments_countÚshares_countÚshare_countsÚliked_by_meFÚcomments)rþ   rÇ   rÈ   rÉ   r‰   rÊ   rË   ÚboolrÌ   Ú
created_byr  rÎ   rÐ   )Úposts    r3   Úserialize_community_postr  ˜  s"  € ðØˆd�g‰gðà�—‘Ò!˜rðð 	�T—^‘^ðð 	�4—<‘<ð	ð
 	Ô% d§o¡oÓ6ðð 	˜d×5Ñ5ðð 	”T˜$Ÿ/™/Ó*ðð 	˜×+Ñ+ðð 	¸¿º˜4Ÿ?™?×4Ò4ÈTðð 	�d—o‘oðð 	�d—o‘oðð 	�qðð 	˜!ðð 	˜ðð 	˜ðð  	�uð!ð" 	�Bð#ð rJ   c                óê   — | j                  t        «      j                  t        t        j                  «      «      j                  t        j                  j                  «       «      j                  «       S ©N)	r?   r   Úoptionsr   r  Úorder_byrÎ   ÚdescrN   ©r1   s    r3   Úlist_community_postsr  ®  sH   € à
�‰”Óß	‰”œM×4Ñ4Ó5Ó	6ß	‰”-×*Ñ*×/Ñ/Ó1Ó	2ß	‰‹ð	rJ   c                óŒ   — | sy t        «       t        | «      j                  z  }|j                  «       r|j	                  «        y y r  )rx   r   rÔ   Úis_fileÚunlink©r†   Úpaths     r3   Údelete_community_media_filer  ·  s7   € ÙØÜÓ!¤D¨Ó$7×$<Ñ$<Ñ<€DØ‡|�|„~Ø�‰�ð rJ   )rÊ   rË   c                ó  — t        |j                  «       |j                  «       |j                  «       |||j                  ¬«      }| j                  |«       | j	                  «        | j                  |«       ||_        |S )N)rÇ   rÈ   rÉ   rÊ   rË   rÌ   )r   ry   rþ   r[   ÚcommitÚrefreshr  )r1   ÚadminrÇ   rÈ   rÉ   rÊ   rË   r  s           r3   Úcreate_community_postr$  ¿  se   € ô Ø�k‰k‹mØ—/‘/Ó#Ø—‘“ØØ-Ø—h‘hô€Dð ‡F�Fˆ4„LØ‡I�I„KØ‡J�JˆtÔØ€D„OØ€KrJ   F)rÊ   rË   Úreplace_mediaÚclear_mediac        	        ó:  — |s|rt        |j                  «       |j                  «       |_        |j                  «       |_        |j                  «       |_        |r||_        ||_        n|rd |_        d |_        | j                  «        | j                  |«       |S r  )	r  rÊ   ry   rÇ   rÈ   rÉ   rË   r!  r"  )	r1   r  rÇ   rÈ   rÉ   rÊ   rË   r%  r&  s	            r3   Úupdate_community_postr(  Ø  s{   € ñ ™Ü# D§O¡OÔ4Ø—‘“€D„JØ—_‘_Ó&€D„NØ—=‘=“?€D„LÙØ$ˆŒØ"4ˆÕÙ	ØˆŒØ"&ˆÔØ‡I�I„KØ‡J�JˆtÔØ€KrJ   c                ó¢   — | j                  t        |«      }|syt        |j                  «       | j	                  |«       | j                  «        y©NFT)rO   r   r  rÊ   Údeleter!  )r1   rÿ   r  s      r3   Údelete_community_postr,  ô  s;   € Ø�6‰6”- Ó)€DÙØÜ §¡Ô0Ø‡I�Iˆd„OØ‡I�I„KØrJ   c                óx   — | j                  t        |«      }|sy| j                  |«       | j                  «        y)z?Admin-only: permanently delete one community_post_comments row.FT)rO   r   r+  r!  )r1   Ú
comment_idr2   s      r3   Údelete_community_commentr/  þ  s0   € à
�&‰&Ô% zÓ
2€CÙØØ‡I�Iˆc„NØ‡I�I„KØrJ   c                 óF   — t        «       dz  } | j                  dd¬«       | S )NÚrewardsTrv   rž   rŸ   s    r3   Úreward_upload_dirr2    s$   € ÜÓ 	Ñ)€DØ‡J�J�t d€JÔ+Ø€KrJ   c           	   ƒ  óº  K  — | j                   xs dj                  d«      d   j                  «       j                  «       }| j                  xs d}t        |«      j                  j                  «       }|s)|j                  d«      rd|j                  dd«      d   z   }|t        vr:t        t        j                  dd	j                  t        t        «      «      › �¬
«      ‚|r#|t        vrt        t        j                  d¬
«      ‚t        «       }| j!                  «       ƒ d {  –—† }|j"                  dz  dz  }t%        |«      |kD  r)t        t        j                  d|j"                  › d�¬
«      ‚|st        t        j                  d¬
«      ‚t'        j(                  «       j*                  › |› �}t-        «       |z  }|j/                  |«       d|› �|xs dfS 7 Œµ­w)Nr¢   r£   r   r¤   r¥   r�   r¦   ú!Unsupported image type. Allowed: r§   r¨   úUnsupported image content typer«   úImage too large (max r¬   úEmpty image filezrewards/r­   )r®   r¯   ry   r°   r±   r   r²   r³   ÚALLOWED_REWARD_IMAGE_EXTENSIONSr   r
   rµ   r¶   r·   Ú"ALLOWED_REWARD_IMAGE_CONTENT_TYPESr   r¹   rº   r»   r¼   r½   r¾   r2  r¿   )	rÀ   r®   r±   rÁ   r|   rÂ   rÃ   rÄ   r    s	            r3   Úsave_reward_mediar:    s¯  è ø€ Ø×&Ñ&Ò,¨"×3Ñ3°CÓ8¸Ñ;×AÑAÓC×IÑIÓK€LØ�~‰~Ò# €HÜ
ˆx‹.×
Ñ
×
%Ñ
%Ó
'€CÙ�<×*Ñ*¨8Ô4Ø�L×&Ñ& s¨AÓ.¨qÑ1Ñ1ˆØ
Ô1Ñ1ÜÜ×3Ñ3Ø6°t·y±yÄÔHgÓAhÓ7iÐ6jÐkô
ð 	
ñ ˜Ô,NÑNÜ¬×(CÑ(CÐLlÔmÐmä‹~€HØ—‘“×€DØ×0Ñ0°4Ñ7¸$Ñ>€IÜ
ˆ4ƒy�9ÒÜÜ×3Ñ3Ø*¨8×+KÑ+KÐ*LÈDÐQô
ð 	
ñ Ü¬×(CÑ(CÐL^Ô_Ð_ä—Z‘Z“\×%Ñ%Ð& s eÐ,€KÜÓ Ñ,€DØ×Ñ�TÔØ�k�]Ð# \Ò%OÐ5OÐOÐOð úrÆ   aí  
CREATE TABLE IF NOT EXISTS rewards (
    id SERIAL PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    description TEXT NOT NULL DEFAULT '',
    points_required INTEGER NOT NULL,
    category VARCHAR(64) NOT NULL,
    media_path VARCHAR(512),
    media_content_type VARCHAR(128),
    created_by_id INTEGER,
    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_rewards_id ON rewards (id);
c                óÈ  — | j                   j                  dk(  }	 t        | «      }d|j                  «       v }|s:|r8| j                  «       5 }|j                  t        t        «      «       d d d «       n8|s6ddl	m
} ddlm} |j                  j                  d   j                  | d¬«       |r|sy 	 t        | «      }|j!                  d«      D �ch c]  }|d	   ’Œ	 }}d
|vr5	 | j                  «       5 }|j                  t        d«      «       d d d «       y y # t        $ r d}Y Œíw xY w# 1 sw Y   Œ†xY wc c}w # t        $ r Y y w xY w# 1 sw Y   y xY w# t        $ r Y y w xY w)NrÕ   r1  Fr   rë   rí   Trï   rÔ   Úrewards_created_by_id_fkeyzã
                        ALTER TABLE rewards
                        ADD CONSTRAINT rewards_created_by_id_fkey
                        FOREIGN KEY (created_by_id) REFERENCES users(id) ON DELETE SET NULL
                        )rÚ   rÔ   r   rØ   r.   rÛ   r,   r   Ú_CREATE_REWARDS_PGrö   rì   r÷   rî   rø   rù   rú   rÜ   )	rš   rá   rÞ   rü   râ   rì   rî   rä   rå   s	            r3   r�   r�   A  sW  € Ø�N‰N×Ñ <Ñ/€EðÜ�v‹ˆØ  D×$8Ñ$8Ó$:Ð:ˆñ ™EØ�\‰\Œ^˜tØ�L‰LœÔ0Ó1Ô2÷ ˆ^áÝ$Ý%à�‰×Ñ˜YÑ'×.Ñ.°FÀtÐ.ÔLá™ØðÜ�v‹ˆØ$(×$9Ñ$9¸)Ô$DÓEÑ$D˜bˆr�&‹zÐ$DˆÐEð $¨3Ñ.ð	Ø—‘” 4Ø—‘Üðóô÷  �ð /øô+ ò ØŠðú÷ ˆ^üò FøÜò Ùðú÷
  �ûô ò 	Ùð	úsq   ›D ÁD)Â2D: ÃD5ÃD: Ã#E Ã3E	ÄE ÄD&Ä%D&Ä)D2Ä5D: Ä:	EÅEÅ	EÅE ÅE Å	E!Å E!r"   c                ód  — | j                   | j                  | j                  | j                  | j                  t        | j                  «      | j                  t        | j                  «      | j                  | j                  r| j                  j                  nd | j                  | j                  dœS )N)rþ   rÔ   ÚdescriptionÚpoints_requiredÚcategoryr  rË   r  rÌ   r  rÎ   rÐ   )rþ   rÔ   r?  r@  rA  r‰   rÊ   rË   r  rÌ   r  r  rÎ   rÐ   )Úrewards    r3   Úserialize_rewardrC  k  s�   € à�i‰iØ—‘Ø×)Ñ)Ø!×1Ñ1Ø—O‘OÜ% f×&7Ñ&7Ó8Ø$×7Ñ7Ü˜&×+Ñ+Ó,Ø×-Ñ-Ø:@×:KÒ:K˜6×,Ñ,×6Ò6ÐQUØ×'Ñ'Ø×'Ñ'ñð rJ   c                óà  — | j                   xs dj                  «       xs d}| j                  r(| j                  j                  xs dj                  «       nd}| j                  xs dj                  «       }|xs |xs d }i d| j                  “d| j
                  “d| j                  “d| j                  “d| j                  “d| j                  “d	|“d
| j                   “d|“d| j                  “d| j                  “d| j                  rt        | j                  j                  «      nd “dt        | dd «      “dt        | dd «      “dt        | dd «      “d| j                   “d| j"                  “S )Nr¢   ÚAdminrþ   Ú	reward_idÚproduct_namer)   r   ÚemailÚphone_numberÚlocationÚsent_torE   r
   Úproduct_image_urlÚremaining_pointsr*   r+   rÎ   rÐ   )rJ  ry   r  rI  rþ   rF  rG  r)   r   rH  rE   r
   rB  r‰   rÊ   ÚgetattrrÎ   rÐ   )ÚclaimrK  Ú
user_phoneÚclaim_phonerI  s        r3   Úserialize_reward_claimrR  |  s¨  € Ø�~‰~Ò# ×*Ñ*Ó,Ò7°€GØ<A¿JºJ�%—*‘*×)Ñ)Ò/¨R×6Ñ6Ô8ÈB€JØ×%Ñ%Ò+¨×2Ñ2Ó4€KØÒ4 Ò4°€LðØˆe�h‰hðà�U—_‘_ðð 	˜×*Ñ*ðð 	�5—=‘=ð	ð
 	�U—_‘_ðð 	�—‘ðð 	˜ðð 	�E—N‘Nðð 	�7ðð 	˜×*Ñ*ðð 	�%—,‘,ðð 	È%Ï,Ê,Ô-¨e¯l©l×.EÑ.EÔFÐ\`ðð 	œG EÐ+=¸tÓDðð 	 ¤¨Ð0GÈÓ!Nðð 	œG EÐ+=¸tÓDðð  	�e×&Ñ&ð!ð" 	�e×&Ñ&ð#ð rJ   c                ón   — t        | |j                  «      }|d   |_        |d   |_        |d   |_        |S )Nr*   r+   )rI   r)   r*   r+   rM  )r1   rO  r\   s      r3   Úattach_claim_pointsrT  –  sA   € Ü$ R¨¯©Ó7€FØ"(Ð)@Ñ"A€EÔØ#Ð$6Ñ7€EÔØ#Ð$6Ñ7€EÔØ€LrJ   c                óŒ   — | sy t        «       t        | «      j                  z  }|j                  «       r|j	                  «        y y r  )r2  r   rÔ   r  r  r  s     r3   Údelete_reward_media_filerV  ž  s7   € ÙØÜÓ¤ mÓ!4×!9Ñ!9Ñ9€DØ‡|�|„~Ø�‰�ð rJ   c                óê   — | j                  t        «      j                  t        t        j                  «      «      j                  t        j                  j                  «       «      j                  «       S r  )	r?   r"   r  r   r  r  rÎ   r  rN   r  s    r3   Úlist_rewardsrX  ¦  sH   € à
�‰”Óß	‰”œF×-Ñ-Ó.Ó	/ß	‰”&×#Ñ#×(Ñ(Ó*Ó	+ß	‰‹ð	rJ   c           	     ó  — t        |j                  «       |j                  «       ||j                  «       |||j                  ¬«      }| j                  |«       | j	                  «        | j                  |«       ||_        |S )N)rÔ   r?  r@  rA  rÊ   rË   rÌ   )r"   ry   rþ   r[   r!  r"  r  )	r1   r#  rÔ   r?  r@  rA  rÊ   rË   rB  s	            r3   Úcreate_rewardrZ  ¯  sl   € ô Ø�Z‰Z‹\Ø×%Ñ%Ó'Ø'Ø—‘Ó!ØØ-Ø—h‘hô€Fð ‡F�Fˆ6„NØ‡I�I„KØ‡J�JˆvÔØ€FÔØ€MrJ   c        
        óH  — |s|	rt        |j                  «       |j                  «       |_        |j                  «       |_        ||_        |j                  «       |_        |r||_        ||_        n|	rd |_        d |_        | j                  «        | j                  |«       |S r  )
rV  rÊ   ry   rÔ   r?  r@  rA  rË   r!  r"  )
r1   rB  rÔ   r?  r@  rA  rÊ   rË   r%  r&  s
             r3   Úupdate_rewardr\  Ê  s‹   € ñ ™Ü  ×!2Ñ!2Ô3Ø—*‘*“,€F„KØ$×*Ñ*Ó,€FÔØ,€FÔØ—n‘nÓ&€F„OÙØ&ˆÔØ$6ˆÕ!Ù	Ø ˆÔØ$(ˆÔ!Ø‡I�I„KØ‡J�JˆvÔØ€MrJ   c                ó¢   — | j                  t        |«      }|syt        |j                  «       | j	                  |«       | j                  «        yr*  )rO   r"   rV  rÊ   r+  r!  )r1   rF  rB  s      r3   Údelete_rewardr^  è  s>   € Ø�V‰V”F˜IÓ&€FÙØÜ˜V×.Ñ.Ô/Ø‡I�IˆfÔØ‡I�I„KØrJ   c           
     óæ  — 	 t        | «      }d|j                  «       v }|s7ddlm} ddlm} |j                  j                  d   j                  | d¬«       y 	 t        | «      j                  d«      D �ch c]  }|d   ’Œ	 }}g }d	|vr|j                  d
«       d|vr|j                  d«       |sy | j                  «       5 }|D ]`  \  }	}
| j                  j                  dk(  r!|j                  t!        d|	› d|
› �«      «       Œ@	 |j                  t!        d|	› d|
› �«      «       Œb 	 d d d «       y # t        $ r d}Y �Œ!w xY w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Úreward_claimsFr   rë   rí   Trï   rÔ   rJ  )rJ  zVARCHAR(255)rM  )rM  rÍ   rÕ   z3ALTER TABLE reward_claims ADD COLUMN IF NOT EXISTS rÖ   z%ALTER TABLE reward_claims ADD COLUMN )r   rØ   r.   rö   rì   r÷   rî   rø   rù   rú   rÙ   ÚappendrÛ   rÚ   rÔ   r,   r   )rš   rÞ   rü   rì   rî   rß   rà   Úto_addrâ   Úcol_namerã   s              r3   r‘   r‘   ò  s}  € ðÜ�v‹ˆØ&¨$×*>Ñ*>Ó*@Ð@ˆñ Ý$Ý%à�‰×Ñ˜_Ñ-×4Ñ4¸&ÈTÐ4ÔRØðÜ#*¨6£?×#>Ñ#>¸Ô#OÓPÑ#O˜a��&“	Ð#OˆÐPð
 €FØ˜ÑØ�‰Ð2Ô3Ø Ñ%Ø�‰Ð5Ô6áØà	�‰Œ˜4Û#‰MˆH�cØ�~‰~×"Ñ" lÒ2Ø—‘œTÐ$WÐX`ÐWaÐabÐcfÐbgÐ"hÓiÕjðØ—L‘L¤Ð(MÈhÈZÐWXÐY\ÐX]Ð&^Ó!_Õ`ñ $÷ 
ˆøô3 ò Ø‹ðüò QøÜò Ùðûô( !ò Ùðú÷ 
ˆúsf   ‚D2 ÁE	 Á5EÂE	 ÃAE'Ä EÄ&E'Ä2EÅ EÅE	 Å		EÅEÅ	E$Å!E'Å#E$Å$E'Å'E0c                ó¢  — | j                  t        |j                  «      }|st        d«      ‚| j	                  t
        «      j                  t
        j                  |j                  k(  t
        j                  |j                  k(  t        t
        j                  «      «      j                  «       }|rt        d«      ‚| j	                  t        j                  t        j                  t        j                   «      d«      «      j                  t        j                  |j                  k(  «      j#                  «       xs d}| j	                  t        j                  t        j                  t
        j$                  «      d«      «      j                  t
        j                  |j                  k(  t        t
        j                  «      «      j#                  «       xs d}t'        |«      t'        |«      z
  }|j$                  �t'        |j$                  «      nt'        |j(                  «      }||kD  rt        d|› d|› �«      ‚|j*                  xs |j*                  xs dj-                  «       xs d }	t        |j                  |j                  |j.                  |j0                  xs d|j2                  |	|j4                  xs dj-                  «       xs d |t'        ||z
  «      d¬«
      }
| j7                  |
«       | j9                  «        t;        | |j                  «      }| j=                  «        | j?                  |
«       ||
_         ||
_!        |jD                  |
_"        |jF                  |
_#        ||z
  }	 tI        |
d	t'        |«      «       dd
l&m'}  || ||
jP                  t'        |
j$                  «      |
j                  ¬«       |
S # tJ        $ r Y ŒGw xY w)NzReward not foundz%You have already claimed this reward.r   z Insufficient points. Available: z, requested: r¢   Úpending)
rF  r)   rG  r   rH  rI  rJ  rE   rM  r
   rM  )Únotify_admin_reward_claim)rG  r\   Úclaim_id))rO   r"   rF  Ú
ValueErrorr?   r#   rC   r)   rþ   rF   r
   rZ   r   r@   rA   r   rB   rD   rE   r0   r@  rI  ry   rÔ   r  rH  rJ  r[   Úflushr]   r!  r"  rB  r  r*   r+   Úsetattrr.   r•   rf  rG  )r1   r  ÚbodyrB  Úexisting_claimrG   rH   Ú	availableÚ	requestedrI  rO  r\   Ú	remainingrf  s                 r3   Úcreate_reward_claimrp    s  € Ø�V‰V”F˜DŸN™NÓ+€FÙÜÐ+Ó,Ð,ð 	�‰”Óß	‰Ü×Ñ 4§7¡7Ñ*Ü×!Ñ! V§Y¡YÑ.Ü)¬+×*<Ñ*<Ó=ó

÷
 
‰‹ð ñ ÜÐ@ÓAÐAð —8‘8œDŸM™M¬$¯(©(Ô3H×3XÑ3XÓ*YÐ[\Ó]Ó^×eÑeÜ×%Ñ%¨¯©Ñ0óç�fƒhòàð ð
 —(‘(œ4Ÿ=™=¬¯©´+×2JÑ2JÓ)KÈQÓOÓP×WÑWÜ×Ñ˜tŸw™wÑ&Ü%¤k×&8Ñ&8Ó9ó÷ �fƒhòð ð ô
 �LÓ!¤C¨Ó$4Ñ4€IØ*.×*;Ñ*;Ð*G”�D×%Ñ%Ô&ÌSÐQW×QgÑQgÓMh€Ià�9ÒäÐ;¸I¸;ÀmÐT]ÐS^Ð_Ó`Ð`à×%Ñ%Ò@¨×):Ñ):Ò@¸b×GÑGÓIÒQÈT€LäØ—)‘)Ø—‘Ø—[‘[Ø—.‘.Ò& BØ�j‰jØ!Ø—-‘-Ò% 2×,Ñ,Ó.Ò6°$ØÜ˜Y¨Ñ2Ó3Øô€Eð ‡F�Fˆ5„MØ‡H�H„JÜ" 2 t§w¡wÓ/€FØ‡I�I„KØ‡J�JˆuÔØ€E„LØ€E„JØ"(×">Ñ">€EÔØ#×4Ñ4€EÔð ˜IÑ%€IðÜ�Ð)¬3¨y«>Ô:õ RáØ
ØØ×'Ñ'Ü�5×%Ñ%Ó&Ø—‘õð €Løô ò Ùðús   Í0O Ï	OÏO)Ústatus_filterc               óÊ  — | j                  t        «      j                  t        t        j                  «      t        t        j
                  «      «      }|r"|j                  t        j                  |k(  «      }|j                  t        j                  j                  «       t        j                  j                  «       «      j                  «       }|D ]  }t        | |«       Œ |S r  )r?   r#   r  r   rB  r  rC   r
   r  rÎ   r  rþ   rN   rT  )r1   rq  r?   ÚclaimsrO  s        r3   Úlist_reward_claimsrt  h  s£   € Ø�H‰H”[Ó!×)Ñ)Ü”;×%Ñ%Ó&Ü”;×#Ñ#Ó$ó€Eñ Ø—‘œ[×/Ñ/°=Ñ@ÓAˆØ�^‰^œK×2Ñ2×7Ñ7Ó9¼;¿>¹>×;NÑ;NÓ;PÓQ×UÑUÓW€FÛˆÜ˜B Õ&ð à€MrJ   c               óà  — | j                  t        «      j                  t        t        j                  «      t        t        j
                  «      «      j                  t        j                  |k(  «      }|r"|j                  t        j                  |k(  «      }|j                  t        j                  j                  «       t        j                  j                  «       «      j                  «       S )z3Reward claims for one member (user My Claims page).)r?   r#   r  r   rB  r  rC   r)   r
   r  rÎ   r  rþ   rN   )r1   r)   rq  r?   s       r3   Úlist_user_reward_claimsrv  u  s    € ð 	�‰”Óß	‰”œK×.Ñ.Ó/´¼K×<LÑ<LÓ1MÓ	Nß	‰”×#Ñ# wÑ.Ó	/ð 
ñ
 Ø—‘œ[×/Ñ/°=Ñ@ÓAˆØ�>‰>œ+×0Ñ0×5Ñ5Ó7¼¿¹×9LÑ9LÓ9NÓO×SÑSÓUÐUrJ   c                ó~   — ddddddddœ}| xs dj                  «       j                  «       }|j                  ||«      S )NÚacceptedÚrejectedre  )Úacceptrx  ÚapproveÚapprovedÚrejectry  re  r¢   )ry   r°   rO   )Ústatus_valueÚaliasesÚ
normalizeds      r3   Ú_normalize_reward_claim_statusr�  †  sM   € àØØØØØØñ€Gð Ò$ "×+Ñ+Ó-×3Ñ3Ó5€JØ�;‰;�z :Ó.Ð.rJ   c                óp   — t        j                  t        j                  | «      «      j                  d«      S )N)re  rx  )r   r°   ÚtrimrL   )Ústatus_columns    r3   rF   rF   ”  s&   € Ü�:‰:”d—i‘i Ó.Ó/×3Ñ3Ð4KÓLÐLrJ   c                ó¦  — | j                  t        «      j                  t        t        j                  «      t        t        j
                  «      «      j                  t        j                  |k(  «      j                  «       }|sy t        |«      }|dvrt        t        j                  d¬«      ‚||_        | j                  «        t        | |j                  «      }| j                  «        |j                   |_        | j%                  «        | j'                  |«       |j(                  |_        |j                   |_        |j                   |_        |S )N>   re  rx  ry  z2status must be one of: pending, accepted, rejectedr¨   )r?   r#   r  r   rB  r  rC   rþ   rZ   r�  r   r
   rµ   ri  r]   r)   r+   rM  r!  r"  r*   )r1   rg  r~  rO  Únormalized_statusr\   s         r3   Úupdate_reward_claim_statusr‡  ˜  s  € ð 	�‰”Óß	‰”œK×.Ñ.Ó/´¼K×<LÑ<LÓ1MÓ	Nß	‰”—‘ (Ñ*Ó	+ß	‰‹ð	 
ñ ØÜ6°|ÓDÐØÐ CÑCÜÜ×3Ñ3ØGô
ð 	
ð
 %€E„LØ‡H�H„JÜ" 2 u§}¡}Ó5€FØ‡H�H„JØ#×4Ñ4€EÔØ‡I�I„KØ‡J�JˆuÔØ"(×">Ñ">€EÔØ#×4Ñ4€EÔØ#×4Ñ4€EÔØ€LrJ   c                óÈ   — | j                  t        |«      }|sy|j                  }| j                  |«       | j	                  «        t        | |«       | j                  «        yr*  )rO   r#   r)   r+  ri  r]   r!  )r1   rg  rO  r)   s       r3   Údelete_reward_claimr‰  ·  sL   € Ø�F‰F”; Ó)€EÙØØ�m‰m€GØ‡I�IˆeÔØ‡H�H„JÜ˜"˜gÔ&Ø‡I�I„KØrJ   c                ó&  — 	 t        | «      }d|j                  «       v }|s7ddlm} ddlm} |j                  j                  d   j                  | d¬«       y 	 t        | «      j                  d«      D �ch c]  }|d   ’Œ	 }}| j                  «       5 }d	|vrO| j                  j                  d
k(  r|j                  t        d«      «       n	 |j                  t        d«      «       d|vrO| j                  j                  d
k(  r|j                  t        d«      «       n	 |j                  t        d«      «       d d d «       y # t        $ r d}Y �Œ2w xY wc c}w # t        $ r Y y w xY w# t        $ r Y Œ�w xY w# t        $ r Y ŒIw xY w# 1 sw Y   y xY w)NÚmember_session_progressFr   rë   rí   Trï   rÔ   Ú
image_pathrÕ   zTALTER TABLE member_session_progress ADD COLUMN IF NOT EXISTS image_path VARCHAR(512)zFALTER TABLE member_session_progress ADD COLUMN image_path VARCHAR(512)Úfeedback_notifiedzmALTER TABLE member_session_progress ADD COLUMN IF NOT EXISTS feedback_notified BOOLEAN NOT NULL DEFAULT FALSEz[ALTER TABLE member_session_progress ADD COLUMN feedback_notified BOOLEAN NOT NULL DEFAULT 0)r   rØ   r.   rö   rì   r÷   rî   rø   rù   rú   rÙ   rÛ   rÚ   rÔ   r,   r   )rš   rÞ   rü   rì   rî   rß   rà   râ   s           r3   r’   r’   Ã  s“  € ðÜ�v‹ˆØ0°D×4HÑ4HÓ4JÐJˆñ Ý$Ý%à�‰×ÑÐ6Ñ7×>Ñ>ÀFÐW[Ð>Ô\ØðÜ#*¨6£?×#>Ñ#>Ð?XÔ#YÓZÑ#Y˜a��&“	Ð#YˆÐZð 
�‰Œ˜4Ø˜tÑ#Ø�~‰~×"Ñ" lÒ2Ø—‘ÜðKóõðØ—L‘L¤Ð&nÓ!oÔpð  dÑ*Ø�~‰~×"Ñ" lÒ2Ø—‘ÜðdóõðØ—L‘LÜðVóô÷3 
ˆøô ò Ø‹ðüò [øÜò Ùðûô !ò Ùðûô& !ò Ùðú÷? 
ˆúsƒ   ‚E ÁE Á5EÂE Â9FÃE)Ã'8FÄ E8ÅEÅEÅE Å	E&Å%E&Å)	E5Å2FÅ4E5Å5FÅ8	FÆFÆFÆFÆFa  
CREATE TABLE IF NOT EXISTS member_session_progress_images (
    id SERIAL PRIMARY KEY,
    progress_id INTEGER NOT NULL REFERENCES member_session_progress(id) ON DELETE CASCADE,
    image_path VARCHAR(512) NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL
);
CREATE INDEX IF NOT EXISTS ix_member_session_progress_images_progress_id
  ON member_session_progress_images (progress_id);
CREATE INDEX IF NOT EXISTS ix_member_session_progress_images_id
  ON member_session_progress_images (id);
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)	NrÕ   Úmember_session_progress_imagesFr   rë   rí   Trï   )rÚ   rÔ   r   rØ   r.   rÛ   r,   r   Ú)_CREATE_MEMBER_SESSION_PROGRESS_IMAGES_PGrö   rì   r÷   rî   rø   rù   rú   rû   s          r3   r“   r“     s·   € Ø�N‰N×Ñ <Ñ/€EðÜ�v‹ˆØ7¸4×;OÑ;OÓ;QÐQˆñ ™EØ�\‰\Œ^˜tØ�L‰LœÔGÓHÔI÷ àáÝ$Ý%à�‰×ÑÐ=Ñ>×EÑEÈ6Ð^bÐEÔcØð øô ò ØŠðú÷ àús   ›B. ÁB?Â.B<Â;B<Â?Cc                 óF   — t        «       dz  } | j                  dd¬«       | S )Nzmember-progressTrv   rž   rŸ   s    r3   Úmember_progress_upload_dirr’    s%   € ÜÓÐ 1Ñ1€DØ‡J�J�t d€JÔ+Ø€KrJ   c           	   ƒ  ó°  K  — | j                   xs dj                  dd«      d   j                  «       j                  «       }| j                  xs d}t        |«      j                  j                  «       }|s)|j                  d«      rd|j                  dd«      d   z   }|t        vr:t        t        j                  dd	j                  t        t        «      «      › �¬
«      ‚|r#|t        vrt        t        j                  d¬
«      ‚t        «       }| j!                  «       ƒ d {  –—† }|j"                  dz  dz  }t%        |«      |kD  r)t        t        j                  d|j"                  › d�¬
«      ‚|st        t        j                  d¬
«      ‚t'        j(                  «       j*                  › |› �}t-        «       |z  }|j/                  |«       d|› �S 7 Œ¯­w)Nr¢   r£   r¦   r   r¤   r¥   r�   r4  r§   r¨   r5  r«   r6  r¬   r7  zmember-progress/)r®   r¯   ry   r°   r±   r   r²   r³   r8  r   r
   rµ   r¶   r·   r9  r   r¹   rº   r»   r¼   r½   r¾   r’  r¿   )	Úimager®   r±   rÁ   r|   rÂ   rÃ   rÄ   r    s	            r3   Úsave_member_progress_imager•  "  s¦  è ø€ Ø×&Ñ&Ò,¨"×3Ñ3°C¸Ó;¸AÑ>×DÑDÓF×LÑLÓN€LØ�~‰~Ò# €HÜ
ˆx‹.×
Ñ
×
%Ñ
%Ó
'€CÙ�<×*Ñ*¨8Ô4Ø�L×&Ñ& s¨AÓ.¨qÑ1Ñ1ˆØ
Ô1Ñ1ÜÜ×3Ñ3Ø6°t·y±yÄÔHgÓAhÓ7iÐ6jÐkô
ð 	
ñ ˜Ô,NÑNÜ¬×(CÑ(CÐLlÔmÐmä‹~€HØ—‘“×€DØ×0Ñ0°4Ñ7¸$Ñ>€IÜ
ˆ4ƒy�9ÒÜÜ×3Ñ3Ø*¨8×+KÑ+KÐ*LÈDÐQô
ð 	
ñ Ü¬×(CÑ(CÐL^Ô_Ð_ä—Z‘Z“\×%Ñ%Ð& s eÐ,€KÜ%Ó'¨+Ñ5€DØ×Ñ�TÔØ˜k˜]Ð+Ð+ð ús   ‚D"GÄ$GÄ%B0Gc                óð  — g }t        | dd «      xs g }|D �cg c]%  }t        |dd «      sŒt        |j                  «      ‘Œ' }}| j                  r-t        | j                  «      }|r||vr|j                  d|«       i d| j                  “d| j
                  “d| j                  “d| j                  “d| j                  “d	| j                  “d
| j                  “d| j                  “d| j                  “d| j                  “dt        | j                  «      “d|“d| j                  “d| j                  “d| j                   r| j                   j"                  nd “d| j$                  “d| j&                  “S c c}w )NÚimagesrŒ  r   rþ   r)   Úweek_idÚ	week_nameÚ
week_indexÚ
session_idÚsession_nameÚsession_numberrB   ÚfeedbackÚ	image_urlÚ
image_urlsÚis_completedÚassigned_by_idÚassigned_by_namerÎ   rÐ   )rN  r‰   rŒ  Úinsertrþ   r)   r˜  r™  rš  r›  rœ  r�  rB   rž  r¡  r¢  Úassigned_byr  rÎ   rÐ   )r2   r   Ú
extra_rowsr<   Úprimary_urls        r3   Ú!serialize_member_session_progressr¨  A  s«  € Ø€JÜ˜˜h¨Ó-Ò3°€JÙ:DÓg¹*°QÌÐPQÐS_ÐaeÕHfÔ" 1§<¡<Õ0¸*€JÐgØ
‡~‚~Ü& s§~¡~Ó6ˆÙ˜;¨jÑ8Ø×Ñ˜a Ô-ðØˆc�f‰fðà�3—;‘;ðð 	�3—;‘;ðð 	�S—]‘]ð	ð
 	�c—n‘nðð 	�c—n‘nðð 	˜×(Ñ(ðð 	˜#×,Ñ,ðð 	˜3×.Ñ.ðð 	�C—L‘Lðð 	Ô% c§n¡nÓ5ðð 	�jðð 	˜×(Ñ(ðð 	˜#×,Ñ,ðð 	¸¿º˜CŸO™O×5Ò5Èdðð  	�c—n‘nð!ð" 	�c—n‘nð#ð ùò hs
   ˜E3«E3c               ó  — |xs g D �cg c]'  }|xs dj                  «       sŒ|j                  «       ‘Œ) }}|sy|D �cg c]  }t        ||¬«      ‘Œ }}| j                  |«       | j                  «        t	        |«      S c c}w c c}w )Nr¢   r   ©Úprogress_idrŒ  )ry   r   Úadd_allr!  r»   )r1   r«  Úimage_pathsÚpÚcleanedr:   s         r3   Ú"add_member_session_progress_imagesr°  _  sƒ   € Ø#.Ò#4°"Ð#4ÓKÑ#4˜Q¸!º'¸r¿¹Õ9Jˆq�w‰w�yÐ#4€GÐKÙØÙW^Ó_ÑW^ÐRSÔ&°;È1ÖMÐW^€DÐ_Ø‡J�JˆtÔØ‡I�I„KÜˆt‹9Ðùò Lùò `s   ‰B £B ¾Bc                ó2   — | xs dj                  «       xs d S )Nr¢   )ry   ©Úvalues    r3   Ú_clean_optional_textr´  i  s   € ØŠK�R×ÑÓ Ò( DÐ(rJ   c                ó¼  — | j                  t        |j                  «      }|r|j                  t        j
                  k7  rt        d«      ‚| j                  t        «      j                  t        j                  |j                  k(  «      }|j                  �-|j                  t        j                  |j                  k(  «      }n»|j                  �U|j                  �I|j                  t        j                  |j                  k(  t        j                  |j                  k(  «      }nZ|j                  t        j                  t        |j                  «      k(  t        j                  t        |j                  «      k(  «      }|j!                  «       }|€'t        |j                  ¬«      }| j#                  |«       |j$                  |_        t        |j                  «      |_        |j                  |_        |j                  |_
        t        |j                  «      |_        |j                  |_        |j&                  r;t)        |j*                  xs d«      }|t)        |j*                  xs d«      z   |_        n|j*                  |_        t        |j,                  «      |_        |j.                  |_        |j0                  |_        | j5                  «        |�I|j6                  s||_        | j#                  t9        |j0                  |¬«      «       | j5                  «        t;        | |j                  «       | j=                  «        | j?                  |«       ||_         |S )NúMember not foundrY   r   rª  )!rO   r   r)   Úroler   r  rh  r?   r   rC   r›  r�  rš  r™  r´  rœ  rZ   r[   r˜  Úadd_to_existingr0   rB   rž  r¡  rþ   r¢  ri  rŒ  r   r]   r!  r"  r¥  )r1   r#  rk  rŒ  Úmemberr?   r2   Úexisting_pointss           r3   Úsave_member_session_progressr»  m  sr  € ð �V‰V”D˜$Ÿ,™,Ó'€FÙ�V—[‘[¤H§M¡MÒ1ÜÐ+Ó,Ð,à�H‰HÔ*Ó+×2Ñ2Ô3H×3PÑ3PÐTX×T`ÑT`Ñ3`Óa€EØ‡�Ð"Ø—‘Ô2×=Ñ=ÀÇÁÑPÓQ‰Ø	×	Ñ	Ð	(¨T¯_©_Ð-HØ—‘Ü!×,Ñ,°·±Ñ?Ü!×0Ñ0°D×4GÑ4GÑGó
‰ð
 —‘Ü!×+Ñ+Ô/CÀDÇNÁNÓ/SÑSÜ!×.Ñ.Ô2FÀt×GXÑGXÓ2YÑYó
ˆð
 �+‰+‹-€CØ
€{Ü#¨D¯L©LÔ9ˆØ
�‰ˆsŒà—,‘,€C„KÜ(¨¯©Ó8€C„MØ—_‘_€C„NØ—_‘_€C„NÜ+¨D×,=Ñ,=Ó>€CÔØ×,Ñ,€CÔØ×ÒÜ˜c×1Ñ1Ò6°QÓ7ˆØ-´°D×4HÑ4HÒ4MÈAÓ0NÑNˆÕà"×2Ñ2ˆÔÜ'¨¯©Ó6€C„LØ×(Ñ(€CÔØŸ™€CÔà‡H�H„JØÐà�~Š~Ø'ˆCŒNØ
�‰Ô)°c·f±fÈÔTÔUØ
�‰Œ
Ü˜"˜dŸl™lÔ+Ø‡I�I„KØ‡J�Jˆs„OØ€C„Oà€JrJ   )Úfeedback_onlyc               ón  — ddl m} | j                  t        «      j	                  t        t        j                  «      t        t        j                  «      «      j                  t        j                  |k(  «      }|j                  t        j                  j                  «       j                  «       t        j                  j                  «       j                  «       t        j                  j                  «       «      j!                  «       }|r#|D �cg c]  } ||j"                  «      sŒ|‘Œ c}S |S c c}w )Nr   )Úis_coach_feedback_text)Úapp.member_feedbackr¾  r?   r   r  r   r¥  r—  rC   r)   r  rš  ÚascÚ	nullslastr�  rÎ   rN   rž  )r1   r)   r¼  r¾  r?   r:   r2   s          r3   Úlist_member_session_progressrÂ  ¨  sê   € õ ;ð 	�‰Ô&Ó'ß	‰”Ô1×=Ñ=Ó>Ä
ÔK`×KgÑKgÓ@hÓ	iß	‰Ô%×-Ñ-°Ñ8Ó	9ð 
ð
 �>‰>Ü×(Ñ(×,Ñ,Ó.×8Ñ8Ó:Ü×,Ñ,×0Ñ0Ó2×<Ñ<Ó>Ü×(Ñ(×,Ñ,Ó.ó÷ 
�cƒeð	 	ñ
 Ù#ÓL™t˜Ñ'=¸c¿l¹lÕ'K’˜tÑLÐLØ€Kùò Ms   ÄD2Ä)D2ÚAnnouncementÚFeedbackÚSystemÚAchievementz!dict[MemberNotificationType, str]Ú _MEMBER_NOTIFICATION_TYPE_LABELSaÿ  
CREATE TABLE IF NOT EXISTS member_notifications (
    id SERIAL PRIMARY KEY,
    user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    notification_type VARCHAR(64) NOT NULL,
    title VARCHAR(255) NOT NULL,
    message TEXT NOT NULL,
    is_read BOOLEAN NOT NULL DEFAULT FALSE,
    admin_cleared BOOLEAN NOT NULL DEFAULT FALSE,
    sent_by_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL
);
CREATE INDEX IF NOT EXISTS ix_member_notifications_user_id ON member_notifications (user_id);
CREATE INDEX IF NOT EXISTS ix_member_notifications_notification_type ON member_notifications (notification_type);
CREATE INDEX IF NOT EXISTS ix_member_notifications_id ON member_notifications (id);
c                óü  — | j                   j                  dk(  }	 t        | «      }d|j                  «       v }|s:|r8| j                  «       5 }|j                  t        t        «      «       d d d «       n8|s6ddl	m
} ddlm} |j                  j                  d   j                  | d¬«       |sy 	 t        | «      j!                  d«      D �ch c]  }|d	   ’Œ	 }}d
|v ry | j                  «       5 }|r|j                  t        d«      «       n	 |j                  t        d«      «       d d d «       y # t        $ r d}Y �Œw xY w# 1 sw Y   Œ xY wc c}w # t        $ r Y y w xY w# t        $ r Y ŒFw xY w# 1 sw Y   y xY w)NrÕ   Úmember_notificationsFr   rë   rí   Trï   rÔ   Úadmin_clearedzfALTER TABLE member_notifications ADD COLUMN IF NOT EXISTS admin_cleared BOOLEAN NOT NULL DEFAULT FALSEzTALTER TABLE member_notifications ADD COLUMN admin_cleared BOOLEAN NOT NULL DEFAULT 0)rÚ   rÔ   r   rØ   r.   rÛ   r,   r   Ú_CREATE_MEMBER_NOTIFICATIONS_PGrö   rì   r÷   rî   rø   rù   rú   rÙ   )	rš   rá   rÞ   rü   râ   rì   rî   rß   rà   s	            r3   r”   r”   Ü  so  € Ø�N‰N×Ñ <Ñ/€EðÜ�v‹ˆØ-°×1EÑ1EÓ1GÐGˆñ ™EØ�\‰\Œ^˜tØ�L‰LœÔ=Ó>Ô?÷ ˆ^áÝ$Ý%à�‰×ÑÐ3Ñ4×;Ñ;ÀÐTXÐ;ÔYáØðÜ#*¨6£?×#>Ñ#>Ð?UÔ#VÓWÑ#V˜a��&“	Ð#VˆÐWð ˜$ÑØà	�‰Œ˜4ÙØ�L‰LÜð\óõðØ—‘ÜðNóô÷ 
ˆøô/ ò Ø‹ðú÷ ˆ^üò XøÜò Ùðûô, ò Ùðú÷! 
ˆúsk   ›D1 ÁEÂ0E ÃEÃE Ã/E2ÄE#Ä1E Ä?E ÅEÅE Å	E ÅE Å#	E/Å,E2Å.E/Å/E2Å2E;a…  
CREATE TABLE IF NOT EXISTS member_points (
    id SERIAL PRIMARY KEY,
    user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    assigned_points_total INTEGER NOT NULL DEFAULT 0,
    available_points INTEGER NOT NULL DEFAULT 0,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL
);
CREATE INDEX IF NOT EXISTS ix_member_points_user_id ON member_points (user_id);
c           
     óÎ  — | j                   j                  dk(  }	 t        | «      }d|j                  «       v }|s:|r8| j                  «       5 }|j                  t        t        «      «       ddd«       n9|s7ddl	m
} ddlm} |j                  j                  d   j                  | d¬	«       y	 t        | «      j!                  d«      D �ch c]  }|d
   ’Œ	 }}g }	d|vr|	j#                  d«       d|vr|	j#                  d«       d|vr%|r|	j#                  d«       n|	j#                  d«       |	sy| j                  «       5 }|	D ]I  \  }
}|r!|j                  t        d|
› d|› �«      «       Œ)	 |j                  t        d|
› d|› �«      «       ŒK 	 ddd«       y# t        $ r d}Y �Œow xY w# 1 sw Y   �ŒxY w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)zHCreate or alter the `member_points` table used to cache per-user points.rÕ   Úmember_pointsFNr   rë   rí   Trï   rÔ   r*   )r*   úINTEGER NOT NULL DEFAULT 0r+   )r+   rÎ  rÐ   )rÐ   z/TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL)rÐ   ÚDATETIMEz3ALTER TABLE member_points ADD COLUMN IF NOT EXISTS rÖ   z%ALTER TABLE member_points ADD COLUMN )rÚ   rÔ   r   rØ   r.   rÛ   r,   r   Ú_CREATE_MEMBER_POINTS_PGrö   rì   r÷   rî   rø   rù   rú   rÙ   ra  )rš   rá   rÞ   rü   râ   rì   rî   rß   rà   rb  rc  rã   s               r3   r—   r—     sÙ  € à�N‰N×Ñ <Ñ/€EðÜ�v‹ˆØ&¨$×*>Ñ*>Ó*@Ð@ˆñ ™EØ�\‰\Œ^˜tØ�L‰LœÔ6Ó7Ô8÷ ˆ^áÝ$Ý%à�‰×Ñ˜_Ñ-×4Ñ4¸&ÈTÐ4ÔRØðÜ#*¨6£?×#>Ñ#>¸Ô#OÓPÑ#O˜a��&“	Ð#OˆÐPð €FØ dÑ*Ø�‰ÐMÔNØ Ñ%Ø�‰ÐHÔIØ˜4ÑÙØ�M‰MÐ[Õ\à�M‰MÐ4Ô5áØà	�‰Œ˜4Û#‰MˆH�cÙØ—‘œTÐ$WÐX`ÐWaÐabÐcfÐbgÐ"hÓiÕjðØ—L‘L¤Ð(MÈhÈZÐWXÐY\ÐX]Ð&^Ó!_Õ`ñ $÷ 
ˆøôA ò Ø‹ðú÷ ‰^üò QøÜò Ùðûô0 !ò Ùðú÷ 
ˆúsq   ›F ÁF+Â.F= Ã
F8ÃF= Å ,GÅ- GÆGÆF(Æ'F(Æ+F5Æ8F= Æ=	G	ÇG	Ç	GÇGÇGÇGÇG$c                ó  — | j                  «       j                  «       j                  dd«      }	 t        |«      S # t        $ r< dj                  d„ t        D «       «      }t        t        j                  d|› �¬«      d ‚w xY w)NrÖ   Ú_r§   c              3  ó4   K  — | ]  }|j                   –— Œ y ­wr  r²  )Ú.0Úts     r3   Ú	<genexpr>z6_normalize_member_notification_type.<locals>.<genexpr>O  s   è ø€ ÐDÑ-C¨˜AŸG�GÑ-Cùs   ‚z$Invalid notification type. Allowed: r¨   )	ry   r°   Úreplacer!   rh  r¶   r   r
   rµ   )r}   ÚkeyÚalloweds      r3   Ú#_normalize_member_notification_typerÚ  J  s|   € Ø
�)‰)‹+×
Ñ
Ó
×
%Ñ
% c¨3Ó
/€CðÜ% cÓ*Ð*øÜò Ø—)‘)ÑDÕ-CÓDÓDˆÜÜ×3Ñ3Ø9¸'¸ÐCô
ð ð	ðús
   °
; »AB c                ó†   — 	 t         t        | «         S # t        $ r# | j                  dd«      j	                  «       cY S w xY w)NrÒ  rÖ   )rÇ  r!   rh  r×  rÇ   )Únotification_types    r3   Ú_member_notification_type_labelrÝ  V  sD   € ð;Ü/Ô0FÐGXÓ0YÑZÐZøÜò ;Ø ×(Ñ(¨¨cÓ2×8Ñ8Ó:Ò:ð;ús   ‚ ”)A ¿A c                óÐ   — | €y | j                   € | j                  t        j                  ¬«      } | j	                  t        d«      «      j                  d«      j                  d«      S )N)ÚtzinfozEurope/Londonz%d %b, %Y %I:%M %pÚ0)rß  r×  r   ÚutcÚ
astimezoner   Ústrftimer…   )Údts    r3   Ú_format_notification_datetimerå  ]  sT   € Ø	€zØØ	‡y�yÐØ�Z‰ZœxŸ|™|ˆZÓ,ˆØ�=‰=œ /Ó2Ó3×<Ñ<Ð=QÓR×YÑYÐZ]Ó^Ð^rJ   c                ó^  — | j                   r,| j                   j                  r| j                   j                  nd| j                  › �}d }| j                  r-| j                  j                  r| j                  j                  }n| j                  rd| j                  › �}| j
                  | j                  ||| j                  t        | j                  «      | j                  | j                  | j                  | j                  | j                  || j                  t        | j                  «      dœS )NzUser #zAdmin #)rþ   r)   r   Ú	recipientrÜ  Ú
type_labelrÇ   ÚmessageÚis_readrÊ  Ú
sent_by_idÚsent_by_namerÎ   Úcreated_at_label)r  r  r)   Úsent_byrë  rþ   rÜ  rÝ  rÇ   ré  rê  rÊ  rÎ   rå  )r2   r   rì  s      r3   Úserialize_member_notificationrï  e  sã   € Ø&)§h¢h°3·8±8×3EÒ3E�—‘×"Ò"ÈVÐTW×T_ÑT_ÐS`ÐKa€IØ€LØ
‡{‚{�s—{‘{×,Ò,Ø—{‘{×,Ñ,‰Ø	�ŠØ  §¡Ð 0Ð1ˆà�f‰fØ—;‘;ØØØ ×2Ñ2Ü5°c×6KÑ6KÓLØ—‘Ø—;‘;Ø—;‘;Ø×*Ñ*Ø—n‘nØ$Ø—n‘nÜ9¸#¿.¹.ÓIñð rJ   c                ó„   — | j                  t        «      j                  t        j                  j	                  d«      «      S ©NF)r?   r    rC   rÊ  Úis_r  s    r3   Ú"_admin_visible_notifications_queryró  ~  s0   € Ø�8‰8Ô&Ó'×.Ñ.Ô/A×/OÑ/O×/SÑ/SÐTYÓ/ZÓ[Ð[rJ   )Úinclude_clearedc               ó¸  — | j                  t        «      }|s.|j                  t        j                  j	                  d«      «      }|j                  t        t        j                  «      t        t        j                  «      «      j                  t        j                  j                  «       t        j                  j                  «       «      j                  «       S rñ  )r?   r    rC   rÊ  rò  r  r   r  rî  r  rÎ   r  rþ   rN   )r1   rô  r?   s      r3   Úlist_member_notificationsrö  ‚  sš   € ð �H‰HÔ'Ó(€EÙØ—‘Ô/×=Ñ=×AÑAÀ%ÓHÓIˆà�‰ÜÔ)×.Ñ.Ó/ÜÔ)×1Ñ1Ó2ó	
÷ 
‰Ô$×/Ñ/×4Ñ4Ó6Ô8J×8MÑ8M×8RÑ8RÓ8TÓ	Uß	‰‹ðrJ   c                óx   — | j                  t        |«      }|sy| j                  |«       | j                  «        y)zJPermanently delete one notification row from the admin notifications list.FT)rO   r    r+  r!  ©r1   Únotification_idr2   s      r3   Údelete_member_notificationrú  ’  s0   € à
�&‰&Ô# _Ó
5€CÙØØ‡I�Iˆc„NØ‡I�I„KØrJ   c                ó    — | j                  t        |«      }|r|j                  ry d|_        | j	                  «        | j                  |«       |S )NT)rO   r    rÊ  rê  r!  r"  rø  s      r3   Úmark_member_notification_readrü  œ  sA   € Ø
�&‰&Ô# _Ó
5€CÙ�#×#Ò#ØØ€C„KØ‡I�I„KØ‡J�Jˆs„OØ€JrJ   c                óÖ   — t        | «      j                  t        j                  j	                  d«      «      j                  t        j                  did¬«      }| j                  «        |S )NFT©Úsynchronize_session)ró  rC   r    rê  rò  Úupdater!  ©r1   Úcounts     r3   Ú"mark_all_member_notifications_readr  ¦  sU   € ä*¨2Ó.ß	‰Ô"×*Ñ*×.Ñ.¨uÓ5Ó	6ß	‰Ô#×+Ñ+¨TÐ2ÈˆÓ	Nð 
ð
 ‡I�I„KØ€LrJ   c                ó~   — t        | «      j                  t        j                  did¬«      }| j	                  «        |S )NTFrþ  )ró  r   r    rÊ  r!  r  s     r3   Úclear_all_member_notificationsr  °  s9   € ä*¨2Ó.ß	‰Ô#×1Ñ1°4Ð8ÈeˆÓ	Tð 
ð ‡I�I„KØ€LrJ   c                óŒ   — t        | «      j                  t        j                  j	                  d«      «      j                  «       S )zZUnread count for admin bell badge (visible rows only: admin_cleared=false, is_read=false).F)ró  rC   r    rê  rò  r  r  s    r3   Ú'count_unread_admin_member_notificationsr  ¹  s4   € ô 	+¨2Ó.ß	‰Ô"×*Ñ*×.Ñ.¨uÓ5Ó	6ß	‰‹ðrJ   c                ó  — | j                  t        «      j                  t        j                  t        j
                  k(  t        j                  j                  d«      «      j                  t        j                  j                  «       t        j                  j                  «       «      j                  «       }|D �cg c]B  }|j                  |j                  j                  «       xs |j                  |j                  dœ‘ŒD c}S c c}w )NT)rþ   r  rH  )r?   r   rC   r·  r   r  Ú	is_activerò  r  r  rÀ  rþ   rN   ry   rH  )r1   ÚmembersÚms      r3   Úlist_notification_recipientsr  Â  s¸   € à
�‰”‹ß	‰”—	‘	œXŸ]™]Ñ*¬D¯N©N×,>Ñ,>¸tÓ,DÓ	Eß	‰”$—.‘.×$Ñ$Ó&¬¯©¯©«Ó	6ß	‰‹ð	 ñ óñ ˆAð	 —$‘$ØŸ™×*Ñ*Ó,Ò7°·±Ø—W‘Wó	
ð
 ñð ùò s   Â<ADc          
     ó|  — t        |«      }|�ˆ| j                  t        |«      }|r)|j                  t        j
                  k7  s|j                  st        t        j                  d¬«      ‚|g}|j                  j                  «       xs |j                  }	nœ| j                  t        «      j                  t        j                  t        j
                  k(  t        j                  j                  d«      «      j!                  t        j"                  j%                  «       «      j'                  «       }d}	|st        t        j(                  d¬«      ‚|D �
cg c]0  }
t+        |
j"                  |j,                  |||j"                  ¬«      ‘Œ2 }}
| j/                  |«       | j1                  «        t3        |«      |	|fS c c}
w )Nr¶  r¨   TzAll MemberszNo active members to notify)r)   rÜ  rÇ   ré  rë  )rÚ  rO   r   r·  r   r  r	  r   r
   ÚHTTP_404_NOT_FOUNDr  ry   rH  r?   rC   rò  r  rþ   rÀ  rN   rµ   r    r³  r¬  r!  r»   )r1   r#  r)   rÜ  rÇ   ré  ÚntypeÚtargetÚtargetsÚrecipient_labelr¹  r:   s               r3   Úsend_member_notificationr  Ó  s]  € ô 0Ð0AÓB€EàÐØ—‘œ˜gÓ&ˆÙ˜Ÿ™¬¯©Ò5¸V×=MÒ=MÜ¬F×,EÑ,EÐN`ÔaÐaØ�(ˆØ ×*Ñ*×0Ñ0Ó2ÒB°f·l±l‰ð �H‰H”T‹Nß‰V”D—I‘I¤§¡Ñ.´·±×0BÑ0BÀ4Ó0HÓIß‰X”d—g‘g—k‘k“mÓ$ß‰S‹Uð	 	ð (ˆáÜ¬×(CÑ(CÐLiÔjÐjñ ó	ñ ˆFô 	Ø—I‘IØ#Ÿk™kØØØ—x‘xö	
ð ð 	ð 	ð ‡J�JˆtÔØ‡I�I„KÜˆt‹9�o wÐ.Ð.ùò	s   Å5F9)zPrivacy Policyza# Privacy Policy

Last updated: April 28, 2026

## Introduction

Your privacy is important to us.)zTerms & Conditionszg# Terms & Conditions

Last updated: April 28, 2026

## Introduction

Please read these terms carefully.z&dict[ContentPageSlug, tuple[str, str]]Ú_DEFAULT_PAGESzó
CREATE TABLE IF NOT EXISTS content_pages (
    id SERIAL PRIMARY KEY,
    slug VARCHAR(64) NOT NULL UNIQUE,
    title VARCHAR(255) NOT NULL,
    body TEXT NOT NULL DEFAULT '',
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL
);
a’  
CREATE TABLE IF NOT EXISTS faqs (
    id SERIAL PRIMARY KEY,
    question VARCHAR(512) NOT NULL,
    answer TEXT NOT NULL,
    status VARCHAR(32) NOT NULL DEFAULT 'completed',
    view_count INTEGER NOT NULL DEFAULT 0,
    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_faqs_id ON faqs (id);
c                ó\  — | j                   j                  dk(  }	 t        | «      }t        |j	                  «       «      }|r^| j                  «       5 }d|vr|j                  t        t        «      «       d|vr|j                  t        t        «      «       d d d «       y ddlm} ddlm} d|vr*|j                  j                   d   j#                  | d¬«       d|vr+|j                  j                   d   j#                  | d¬«       y y # t
        $ r t        «       }Y Œßw xY w# 1 sw Y   y xY w)	NrÕ   Úcontent_pagesÚfaqsr   rë   rí   Trï   )rÚ   rÔ   r   ÚsetrØ   r.   rÛ   r,   r   Ú_CREATE_CONTENT_PAGES_PGÚ_CREATE_FAQS_PGrö   rì   r÷   rî   rø   rù   rú   )rš   rá   rÞ   rù   râ   rì   rî   s          r3   r–   r–   %  s  € Ø�N‰N×Ñ <Ñ/€EðÜ�v‹ˆÜ�T×)Ñ)Ó+Ó,ˆñ Ø�\‰\Œ^˜tØ fÑ,Ø—‘œTÔ":Ó;Ô<Ø˜VÑ#Ø—‘œT¤/Ó2Ô3÷	 ˆ^õ 	%Ý%à &Ñ(Ø�M‰M× Ñ  Ñ1×8Ñ8¸fÐQUÐ8ÔVØ˜ÑØ�M‰M× Ñ  Ñ(×/Ñ/°VÈÐ/ÕMð  øô ò Ü“Šðú÷ ˆ^ús   ›$D	 ÁAD"Ä	DÄDÄ"D+c                ó"  — | j                  «       j                  «       j                  dd«      }t        j                  t        j                  t        j
                  t        j
                  t        j
                  dœ}||vrt        d«      ‚||   S )NÚ-rÒ  )ÚprivacyÚprivacy_policyÚtermsÚterms_conditionsÚterms_and_conditionsz;Invalid content_type. Use: privacy_policy, terms_conditions)ry   r°   r×  r   r  r   rh  )r}   r€  r  s      r3   Ú_parse_content_slugr"  =  sx   € Ø—‘“×"Ñ"Ó$×,Ñ,¨S°#Ó6€Jä"×1Ñ1Ü)×8Ñ8Ü ×1Ñ1Ü+×<Ñ<Ü /× @Ñ @ñ€Gð ˜Ñ ÜÐVÓWÐWØ�:ÑÐrJ   c                óÈ   — | st         j                  S | j                  «       j                  «       }|dv rt         j                  S |dv rt         j                  S t        d«      ‚)N)Ú	completedÚcompleteÚ	published)Údraftre  z'Invalid status. Use: completed or draft)r   r$  ry   r°   r'  rh  )r}   r€  s     r3   Ú_parse_faq_statusr(  K  sY   € ÙÜ×"Ñ"Ð"Ø—‘“×"Ñ"Ó$€JØÐ;Ñ;Ü×"Ñ"Ð"ØÐ)Ñ)Ü�‰ÐÜ
Ð>Ó
?Ð?rJ   c                ó,   — | €dS | j                  d«      S )Nr¢   z%d-%b-%Y)rã  )Úds    r3   Ú_format_updatedr+  V  s   € Ø�ˆ2Ð6 §
¡
¨:Ó 6Ð6rJ   c                óò   — t        | j                  d«      r| j                  j                  nt        | j                  «      | j                  | j
                  | j                  t        | j                  «      dœS )Nr³  )r®   rÇ   rÉ   Úlast_updatedÚlast_updated_label)ÚhasattrÚslugr³  ÚstrrÇ   rk  rÐ   r+  )Úpages    r3   Úserialize_content_pager3  Z  sR   € ä+2°4·9±9¸gÔ+F˜Ÿ	™	ŸšÌCÐPT×PYÑPYËNØ—‘Ø—9‘9ØŸ™Ü-¨d¯o©oÓ>ñð rJ   c           
     óv  — t        | j                  d«      r| j                  j                  nt        | j                  «      }| j                  | j
                  | j                  ||j                  dd«      j                  «       | j                  | j                  t        | j                  «      | j                  dœ	S )Nr³  rÒ  rÖ   )	rþ   ÚquestionÚanswerr
   Ústatus_labelÚ
view_countr-  r.  rÎ   )r/  r
   r³  r1  rþ   r5  r6  r×  rÇ   r8  rÐ   r+  rÎ   )ÚfaqÚ
status_vals     r3   Úserialize_faqr;  d  s„   € Ü%,¨S¯Z©Z¸Ô%A�—‘×!Ò!ÄsÈ3Ï:É:Ã€Jà�f‰fØ—L‘LØ—*‘*ØØ"×*Ñ*¨3°Ó4×:Ñ:Ó<Ø—n‘nØŸ™Ü-¨c¯n©nÓ=Ø—n‘nñ
ð 
rJ   c                ó,  — | j                  t        «      j                  t        j                  |k(  «      j	                  «       }|r|S t
        |   \  }}t        |||¬«      }| j                  |«       | j                  «        | j                  |«       |S )N)r0  rÇ   rk  )	r?   r   rC   r0  rZ   r  r[   r!  r"  )r1   r0  r2  rÇ   rk  s        r3   Úget_or_create_content_pager=  s  sx   € Ø�8‰8”KÓ ×'Ñ'¬×(8Ñ(8¸DÑ(@ÓA×GÑGÓI€DÙØˆÜ  Ñ&�K€Eˆ4Ü˜D¨°DÔ9€DØ‡F�Fˆ4„LØ‡I�I„KØ‡J�JˆtÔØ€KrJ   c                ó,   — t        | t        |«      «      S r  )r=  r"  )r1   r®   s     r3   Úget_content_pager?    s   € Ü% bÔ*=¸lÓ*KÓLÐLrJ   c               óÆ   — t        | t        |«      «      }|j                  «       |_        |j                  «       |_        | j                  «        | j                  |«       |S r  )r=  r"  ry   rÇ   rk  r!  r"  )r1   r®   rÇ   rÉ   r2  s        r3   Úsave_content_pagerA  ƒ  sH   € Ü% bÔ*=¸lÓ*KÓL€DØ—‘“€D„JØ—‘“€D„IØ‡I�I„KØ‡J�JˆtÔØ€KrJ   )Úpublished_onlyc               óH  — | j                  t        «      }|r0|j                  t        j                  t        j
                  k(  «      }|rm|j                  «       x}r[d|› d�}|j                  t        t        j                  j                  |«      t        j                  j                  |«      «      «      }|j                  «       }|j                  t        j                  j                  «       «      j                  |«      j!                  |«      j#                  «       }	|	|fS )NÚ%)r?   r   rC   r
   r   r$  ry   r   r5  Úiliker6  r  r  rÐ   r  ÚoffsetÚlimitrN   )
r1   ÚsearchÚskiprG  rB  ÚqÚtermÚpatternrS   r:   s
             r3   Ú	list_faqsrM  Œ  sÓ   € ð 	�‰”‹€AÙØ�H‰H”S—Z‘Z¤9×#6Ñ#6Ñ6Ó7ˆÙ˜6Ÿ<™<›>Ð)�4Ð)Ø�d�V˜1�+ˆØ�H‰H”SœŸ™×+Ñ+¨GÓ4´c·j±j×6FÑ6FÀwÓ6OÓPÓQˆØ�G‰G‹I€EØ�:‰:”c—n‘n×)Ñ)Ó+Ó,×3Ñ3°DÓ9×?Ñ?ÀÓF×JÑJÓL€DØ�ˆ;ÐrJ   c               óÐ   — t        |j                  «       |j                  «       t        |«      ¬«      }| j                  |«       | j	                  «        | j                  |«       |S )N)r5  r6  r
   )r   ry   r(  r[   r!  r"  )r1   r5  r6  r
   r9  s        r3   Ú
create_faqrO  Ÿ  sH   € Ü
�x—~‘~Ó'°·±³ÔGXÐY_ÓG`Ô
a€CØ‡F�Fˆ3„KØ‡I�I„KØ‡J�Jˆs„OØ€JrJ   c               óî   — | j                  t        |«      }|sy |j                  «       |_        |j                  «       |_        t        |«      |_        | j                  «        | j                  |«       |S r  )	rO   r   ry   r5  r6  r(  r
   r!  r"  )r1   Úfaq_idr5  r6  r
   r9  s         r3   Ú
update_faqrR  §  sX   € Ø
�&‰&”�fÓ
€CÙØØ—>‘>Ó#€C„LØ—‘“€C„JÜ" 6Ó*€C„JØ‡I�I„KØ‡J�Jˆs„OØ€JrJ   c                óx   — | j                  t        |«      }|sy| j                  |«       | j                  «        yr*  )rO   r   r+  r!  )r1   rQ  r9  s      r3   Ú
delete_faqrT  ³  s/   € Ø
�&‰&”�fÓ
€CÙØØ‡I�Iˆc„NØ‡I�I„KØrJ   c                ó.   — | j                  t        |«      S r  )rO   r   )r1   rQ  s     r3   Úget_faqrV  ¼  s   € Ø�6‰6”#�vÓÐrJ   )r1   r   r)   r0   Úreturnzdict | None)r1   r   r8   z	list[int]rW  zdict[int, dict])r1   r   r)   r0   rW  Údict)r1   r   r)   r0   rW  r   )rW  r   )r†   ú
str | NonerW  rY  )rš   r   rW  ÚNone)rÀ   r	   rW  ztuple[str, str])r  r   rW  rX  )r  r   rW  rX  )rW  zlist[CommunityPost])r†   rY  rW  rZ  )r#  r   rÇ   r1  rÈ   r1  rÉ   r1  rÊ   rY  rË   rY  rW  r   )r  r   rÇ   r1  rÈ   r1  rÉ   r1  rÊ   rY  rË   rY  r%  r  r&  r  rW  r   )r1   r   rÿ   r0   rW  r  )r1   r   r.  r0   rW  r  )rB  r"   rW  rX  )rO  r#   rW  rX  )r1   r   rO  r#   rW  r#   )rW  zlist[Reward])r#  r   rÔ   r1  r?  r1  r@  r0   rA  r1  rÊ   rY  rË   rY  rW  r"   )rB  r"   rÔ   r1  r?  r1  r@  r0   rA  r1  rÊ   rY  rË   rY  r%  r  r&  r  rW  r"   )r1   r   rF  r0   rW  r  )r1   r   r  r   rk  r%   rW  r#   )r1   r   rq  rY  rW  úlist[RewardClaim])r1   r   r)   r0   rq  rY  rW  r[  )r~  r1  rW  r1  )r1   r   rg  r0   r~  r1  rW  zRewardClaim | None)r1   r   rg  r0   rW  r  )r”  r	   rW  r1  )r2   r   rW  rX  )r1   r   r«  r0   r­  z	list[str]rW  r0   )r³  rY  rW  rY  r  )
r1   r   r#  r   rk  r$   rŒ  rY  rW  r   )r1   r   r)   r0   r¼  r  rW  zlist[MemberSessionProgress])r}   r1  rW  r!   )rÜ  r1  rW  r1  )rä  zdatetime | NonerW  rY  )r2   r    rW  rX  )r1   r   )r1   r   rô  r  rW  zlist[MemberNotification])r1   r   rù  r0   rW  r  )r1   r   rù  r0   rW  zMemberNotification | None)r1   r   rW  r0   )r1   r   rW  z
list[dict])r1   r   r#  r   r)   z
int | NonerÜ  r1  rÇ   r1  ré  r1  rW  ztuple[int, str, list[User]])r}   r1  rW  r   )r}   rY  rW  r   )rW  r1  )r2  r   rW  rX  )r9  r   rW  rX  )r1   r   r0  r   rW  r   )r1   r   r®   r1  rW  r   )
r1   r   r®   r1  rÇ   r1  rÉ   r1  rW  r   )r1   r   rH  rY  rI  r0   rG  r0   rB  r  rW  ztuple[list[Faq], int])
r1   r   r5  r1  r6  r1  r
   rY  rW  r   )r1   r   rQ  r0   r5  r1  r6  r1  r
   rY  rW  ú
Faq | None)r1   r   rQ  r0   rW  r  )r1   r   rQ  r0   rW  r\  )•Ú__doc__Ú
__future__r   r¼   r   r   Úzoneinfor   Úpathlibr   Úfastapir   r	   r
   Ú
sqlalchemyr   r   r   r   Úsqlalchemy.enginer   Úsqlalchemy.ormr   r   Ú
app.configr   Ú
app.modelsr   r   Úapp.models_adminr   r   r   r   r   r   r   r   r   r   r   r    r!   r"   r#   Úapp.schemasr$   r%   r4   r=   rI   rW   r]   r´   r¸   r8  r9  rs   r   r‰   r›   rx   rÅ   rÑ   Ú__annotations__rõ   ræ   rÝ   r�   r  r  r  r  r$  r(  r,  r/  r2  r:  r=  r�   rC  rR  rT  rV  rX  rZ  r\  r^  r‘   rp  rt  rv  r�  rF   r‡  r‰  r’   r�  r“   r’  r•  r¨  r°  r´  r»  rÂ  ÚannouncementrB  rž  ÚsystemÚachievementrÇ  rË  r”   rÐ  r—   rÚ  rÝ  rå  rï  ró  rö  rú  rü  r  r  r  r  r  r  r   r  r  r  r–   r"  r(  r+  r3  r;  r=  r?  rA  rM  rO  rR  rT  rV  rr   rJ   r3   Ú<module>rm     s¥  ðò	õ #ã ß 'Ý Ý ç 5Ñ 5ß /Ó /Ý $ß .å #ß %÷÷ ÷ ÷ ñ ÷" MóQó ó6ó$(óVò  &iÐ "ò)Ð %ò #MÐ Ú%[Ð "ó/óóEó2ó6óRð>	<Ð Ð8ó 	ðÐ ó .+óbgó6Wó4	óó,óð "Ø%)ñð ðð ð	ð
 ðð ðð ðð #ðð óð@ "Ø%)ØØñð ðð ð	ð
 ðð ðð ðð #ðð ðð ðð óó8óóóPð>Ð ó"'óTó"ó4óóð" "Ø%)ñð ðð ð	ð
 ðð ðð ðð ðð #ðð óðF "Ø%)ØØñð ðð ð	ð
 ðð ðð ðð ðð #ðð ðð ðð óó<ó%óPKð\ DHõ 
ð" !%ñ	VØðVàðVð ð	Vð
 óVó"/òMðØðØðØ.1ðàóó>	ó3ðl-Ð )óó*ó,ó>ó<ó)ð "ð	8Øð8àð8ð +ð8ð ð	8ð
 ó8ð~  ñ	Øðàðð ð	ð
 !óð6 ×'Ñ'¨Ø×!Ñ! 8Ø×#Ñ# ZØ×!Ñ! 8Ø×&Ñ&¨ðGÐ  Ð"Có ð#Ð ó$-ð`	Ð ó.ód	ó;ó_óó2\ð
 -2ñØðØ%)ðàóó óóóóóð")/Øð)/àð)/ð ð	)/ð
 ð)/ð ð)/ð ð)/ð !ó)/ð` ×"Ò"ð %ð ×$Ò$ð 'ð	:€Ð6ó 	ðÐ ð€óNó0ó@ó7óóó	óMóð !ñØðð ðð ð	ð
 ðð ðð óó&ó	óôrJ   