Ë
    øŒjµ“  ã                   óz  — d Z ddlZddlZddlZddlZddlZddl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 ddl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 ddlZddlm Z  ddl!Z!ddl"m#Z#m$Z$m%Z% ddl&m'Z' ddl(m)Z)m*Z*m+Z+m,Z,m-Z-m.Z. ddl/mZ0 ddl1m2Z2m3Z3m4Z4m5Z5m6Z6m7Z7m8Z8m9Z9 ddl:m;Z;m<Z< ddl=m>Z>m?Z? ddl@m@Z@  e «         ee ej‚                  dd«      ¬«      ZB edd¬«      ZCeBeCjˆ                  _B        eCj‹                  ee«       eCj�                  edgddgdg¬«       eCj�                  e>ddg¬«        ej‚                  d d!«      ZG e#eGd"d#d$¬%«      ZH e%eHe$d"¬&«      ZI e'«       ZJ G d'„ d(eJ«      ZK G d)„ d*e;«      ZL G d+„ d,e;«      ZMd-„ ZNdaOd.„ ZP ej¢                  d/«      ZR ej¢                  d0«      ZS e2«       ZTd1„ ZUd2eVd3eVfd4„ZWd5e$d2eVd3e	eK   fd6„ZXd5e$d2eVd7eYd8eVd3eKf
d9„ZZd2eVd3eVfd:„Z[d2eVd3eVfd;„Z\d2eVd3eYfd<„Z]d€d2eVd=e^d3d,fd>„Z_d?ed@eVd3efdA„Z`dBed@eVd3efdC„ZadDedEe3d@eVd3e4fdF„Zbd�dEe3d@eVd3e4fdG„ZcdH„ ZdeCjË                  dIed«       eCjÍ                  dJ«      eBjÏ                  dK«      dLefdM„«       «       ZheCjÍ                  dN«      eBjÏ                  dO«      dLefdP„«       «       ZieCjÍ                  dQ«      eBjÏ                  dR«       edSdT¬U«       edSdV¬U«       eddWd0dX¬Y«       edddZd[¬\«       edddZd]¬\«       edd^¬U«       edd_¬U«       edd`¬U«       edda¬U«       edddZdb¬\«       eddc¬U«       eddd¬U«      fdLedeeVdfeVdge	ej   dhe	ek   die	ek   dje	eV   dke	eV   dle	eV   dme	eV   dne	ek   doe	eV   dpe^fdq„«       «       ZleCjÛ                  dr«      eBjÏ                  ds«      dLedteLfdu„«       «       ZneCjÍ                  dv«      eBjÏ                  dO«      dLedweVfdx„«       «       ZoeCjÍ                  dy«      eBjÏ                  dK«      dLefdz„«       «       ZpeCjã                  d{«      eBjÏ                  d|«       ed"d}¬U«      fdLed~e^fd„«       «       Zry)‚zä
main.py - FastAPI application with endpoints, database, and video generation
Multilingual support with Korean and English translation
SCALABLE VERSION: Async database, Redis caching, rate limiting, thread-pool video generation
é    N)ÚDictÚListÚOptionalÚAny)Údatetime)ÚFastAPIÚQueryÚHTTPExceptionÚRequestÚBackgroundTasks)ÚResponseÚJSONResponse)ÚCORSMiddleware)ÚLimiterÚ_rate_limit_exceeded_handler)Úget_remote_address)ÚRateLimitExceeded)Úload_dotenv)Úcreate_async_engineÚAsyncSessionÚasync_sessionmaker)Údeclarative_base)ÚColumnÚStringÚLargeBinaryÚDateTimeÚIntegerÚselect)Úasyncio)ÚWorkoutDatabaseÚUserProfileÚWeeklyPlanUIÚ
ExerciseUIÚWorkoutDayUIÚnormalize_blood_typeÚparse_duration_to_minutesÚparse_calories)Ú	BaseModelÚField)ÚLanguageMiddlewareÚget_language_from_request)Útranslation_serviceÚ	REDIS_URLúredis://localhost:6379)Úkey_funcÚstorage_uriú3Personalized Blood Type Workout Planner with Videosz8.0.0)ÚtitleÚversionÚ*T)Úallow_originsÚallow_credentialsÚallow_methodsÚallow_headersÚenÚko)Úsupported_languagesÚDATABASE_URLz(sqlite+aiosqlite:///./exercise_videos.dbFé   é(   )ÚechoÚ	pool_sizeÚmax_overflow)Úclass_Úexpire_on_commitc                   óú   — e Zd ZdZdZ eedd¬«      Z eeddd¬«      Z	 eeddd¬«      Z
 eed¬«      Z eed¬«      Z eed¬«      Z eeej"                  ¬«      Z eeej"                  ¬«      Z eed	¬«      Zy
)ÚExerciseVideoz*Database model for storing exercise videosÚexercise_videosT)Úprimary_keyÚautoincrementF)ÚuniqueÚindexÚnullable)rK   )Údefaultr   N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__Ú__tablename__r   r   Úidr   Úexercise_nameÚexercise_hashr   Ú
video_dataÚprompt_usedÚ	file_sizer   r   ÚutcnowÚ
created_atÚlast_accessedÚaccess_count© ó    ú8/var/www/html/Fitness-lenito-AI-main/workout_app/main.pyrE   rE   K   sˆ   „ Ù4Ø%€Má	� T¸Ô	>€BÙ˜6¨$°dÀUÔK€MÙ˜6¨$°dÀUÔK€MÙ˜¨eÔ4€JÙ˜¨%Ô0€KÙ�w¨Ô/€IÙ˜¨(¯/©/Ô:€JÙ˜8¨X¯_©_Ô=€MÙ˜'¨1Ô-�Lr]   rE   c                   óJ   — e Zd ZU  edd¬«      Zeed<    edd¬«      Zeed<   y)	ÚVideoGenerationRequest.zName of the exercise©ÚdescriptionrS   FzForce regenerate even if cachedÚforce_regenerateN)	rM   rN   rO   r)   rS   ÚstrÚ__annotations__rc   Úboolr\   r]   r^   r`   r`   Y   s'   … Ù˜sÐ0FÔG€M�3ÓGÙ" 5Ð6WÔXÐ�dÔXr]   r`   c                   ó†   — e Zd ZU eed<   dZee   ed<   dZee   ed<   eed<   eed<   e	ed<   dZ
ee   ed<   dZee   ed	<   y)
ÚVideoResponserS   NÚvideo_base64Ú	video_urlÚstatusÚ
from_cacherW   rY   Úmessage)rM   rN   rO   rd   re   ri   r   rj   rf   ÚintrY   r   rm   r\   r]   r^   rh   rh   ]   sQ   … ØÓØ"&€L�(˜3‘-Ó&Ø#€Iˆx˜‰}Ó#ØƒKØÓØƒNØ%)€J�˜Ñ"Ó)Ø!€GˆX�c‰]Ô!r]   rh   c               ƒ   óþ   K  — t         j                  «       4 ƒd {  –—† } | j                  t        j                  j
                  «      ƒ d {  –—†  d d d «      ƒd {  –—†  y 7 ŒF7 Œ7 Œ	# 1 ƒd {  –—†7  sw Y   y xY w­w©N)ÚengineÚbeginÚrun_syncÚBaseÚmetadataÚ
create_all)Úconns    r^   Úinit_dbrx   h   sI   è ø€ Ü�|‰|�~Œ~ Ø�m‰mœDŸM™M×4Ñ4Ó5×5Ð5÷ �~‰~øØ5øð ø�~�~‰~üsV   ‚A=›A"œA=Ÿ-A(ÁA$ÁA(ÁA=ÁA&ÁA=Á$A(Á&A=Á(A:Á.A1Á/A:Á6A=c               ƒ   ó�   K  — t         €5t        j                  dd«      } t        j                  | d¬«      ƒ d {  –—† a t         S 7 Œ
­w)Nr-   r.   T)Údecode_responses)Ú_redis_clientÚosÚgetenvÚaioredisÚfrom_url)Ú	redis_urls    r^   Ú	get_redisr�   o   s>   è ø€ äÐÜ—I‘I˜kÐ+CÓDˆ	Ü&×/Ñ/°	ÈDÔQ×QˆÜÐð Rús   ‚7A¹AºAiè  éd   c                 óŽ   K  — t        «       4 ƒd {  –—† } | ­–— d d d «      ƒd {  –—†  y 7 Œ7 Œ# 1 ƒd {  –—†7  sw Y   y xY w­wrp   )ÚAsyncSessionLocal)Úsessions    r^   Úget_dbr†   ~   s/   è ø€ Ü ×"Ô" gØ‹÷ #×"Ñ"øÐ"ø×"×"Ñ"üs8   ‚A‘,’A•0›A¦.§A®A°A¶9·A¾ArS   Úreturnc                 óœ   — t        j                  | j                  «       j                  «       j	                  «       «      j                  «       S rp   )ÚhashlibÚmd5ÚlowerÚstripÚencodeÚ	hexdigest)rS   s    r^   Úget_exercise_hashr�   ‚   s5   € Ü�;‰;�}×*Ñ*Ó,×2Ñ2Ó4×;Ñ;Ó=Ó>×HÑHÓJÐJr]   Údbc              ƒ   ó   K  — t        |«      }| j                  t        t        «      j	                  t        j
                  |k(  «      «      ƒ d {  –—† }|j                  «       }|rbt        j                  «       |_	        |xj                  dz  c_
        | j                  «       ƒ d {  –—†  t        d|› d|j                  › d�«       |S 7 Œz7 Œ$­w)Né   zCache HIT for 'z' (accessed z times))r�   Úexecuter   rE   ÚwhererT   Úscalar_one_or_noner   rX   rZ   r[   ÚcommitÚprint)r�   rS   rT   ÚresultÚvideo_records        r^   Úget_cached_videorš   …   s³   è ø€ Ü% mÓ4€MØ—:‘:ÜŒ}Ó×#Ñ#¤M×$?Ñ$?À=Ñ$PÓQó÷ €Fð ×,Ñ,Ó.€LÙÜ%-§_¡_Ó%6ˆÔ"Ø×!Ò! QÑ&Õ!Ø�i‰i‹k×ÐÜ� ˜¨l¸<×;TÑ;TÐ:UÐU\Ð]Ô^ØÐðøð 	ús%   ‚ACÁC
ÁACÂ'CÂ(#CÃCrU   Úpromptc           	   ƒ   óx  K  — t        |«      }| j                  t        t        «      j	                  t        j
                  |k(  «      «      ƒ d {  –—† }|j                  «       }|r`||_        ||_        t        |«      |_
        t        j                  «       |_        | j                  «       ƒ d {  –—†  t        d|› d�«       |S t        ||||t        |«      ¬«      }| j!                  |«       | j                  «       ƒ d {  –—†  | j#                  |«      ƒ d {  –—†  t        d|› d�«       |S 7 Œâ7 Œƒ7 Œ27 Œ­w)NzUpdated cache for 'Ú')rS   rT   rU   rV   rW   zSaved new video to cache for ')r�   r“   r   rE   r”   rT   r•   rU   rV   ÚlenrW   r   rX   rZ   r–   r—   ÚaddÚrefresh)r�   rS   rU   r›   rT   r˜   ÚexistingÚ	new_videos           r^   Úsave_video_to_cacher£   ’   s  è ø€ Ü% mÓ4€MØ—:‘:ÜŒ}Ó×#Ñ#¤M×$?Ñ$?À=Ñ$PÓQó÷ €Fð ×(Ñ(Ó*€HÙØ(ˆÔØ%ˆÔÜ  ›_ˆÔÜ!)§¡Ó!2ˆÔØ�i‰i‹k×ÐÜÐ# M ?°!Ð4Ô5Øˆä!Ø'Ø'Ø!ØÜ˜*“oô
ˆ	ð 	�‰ˆyÔØ�i‰i‹k×ÐØ�j‰j˜Ó#×#Ð#ÜÐ.¨}¨o¸QÐ?Ô@ØÐð1øð 	øð 	øØ#úsJ   ‚AD:ÁD2ÁA D:Â0D4Â1AD:ÄD6ÄD:ÄD8ÄD:Ä4D:Ä6D:Ä8D:c              ƒ   óö  K  — t         j                  €t         j                  «        d| › d�}	 t        4 ƒd{  –—†  t         j                  j	                  |«      ƒ d{  –—† }ddd«      ƒd{  –—†  j
                  j                  «       }|| k7  rt        d| › d|› d�«       |S 7 Œn7 ŒI7 Œ;# 1 ƒd{  –—†7  sw Y   ŒKxY w# t        $ r+}t        d| › d|› �«       | j                  «       cY d}~S d}~ww xY w­w)	zHUse LLM to validate and clean exercise name without changing its meaningNz�You are an exercise name validator. Your job is to clean and standardize exercise names WITHOUT changing their meaning.

    Exercise Name: "a]  "

    Rules:
    1. Fix obvious typos and spelling errors
    2. Standardize capitalization (Title Case for exercise names)
    3. Remove extra spaces, special characters that don't belong
    4. Keep the core exercise name EXACTLY the same
    5. Don't add or remove words that change the exercise type
    6. If the name is already valid, return it as-is

    Examples:
    - "push  up" -> "Push Up"
    - "burpee's" -> "Burpees"
    - "mountain-climbers" -> "Mountain Climbers"
    - "JUMPING JACKS" -> "Jumping Jacks"
    - "squat" -> "Squat"

    Return ONLY the cleaned exercise name, nothing else.z
Cleaned: 'z' -> 'r�   zValidation error for 'z': )	Ú
workout_dbÚvalidation_llmÚinitialize_llmsÚLLM_SEMAPHOREÚainvokeÚcontentrŒ   r—   Ú	Exception)rS   Úvalidation_promptÚresponseÚcleaned_nameÚes        r^   Ú validate_and_clean_exercise_namer°   ¯   så   è ø€ ä× Ñ Ð(Ü×"Ñ"Ô$ðà"�Oð $9ð<Ðð*	%ß –=Ü'×6Ñ6×>Ñ>Ð?PÓQ×QˆH÷ !—=à×'Ñ'×-Ñ-Ó/ˆØ˜=Ò(Ü�J˜}˜o¨V°L°>ÀÐCÔDØÐð !øØQøð !ø—=—=‘=ûô ò %ÜÐ& } o°S¸¸Ð<Ô=Ø×"Ñ"Ó$Õ$ûð%üs‹   ‚+C9®
C ¸B'¹C ¼#B-ÁB)Á B-Á$C Á/B+Á06C Â&C9Â'C Â)B-Â+C Â-B?Â3B6Â4B?Â;C Ã	C6Ã C1Ã+C6Ã,C9Ã1C6Ã6C9c                 ó2   — | j                  «       }d|› d�}|S )NzA person performing z} exercise, proper form, fitness demonstration, athletic movement, gym environment, professional fitness video, clear movement)rŒ   )rS   Ú
clean_namer›   s      r^   Úcreate_exercise_promptr³   Õ   s*   € Ø×$Ñ$Ó&€JØ# J <ð  0mð  n€FØ€Mr]   c           	   ƒ   óp  ‡K  — t        j                  d«      }|st        dd¬«      ‚t        | «      ƒ d{  –—† }t	        |«      Št        d|› d�«       t        d‰› �«       t        j                  «       }t        4 ƒd{  –—†  	 |j                  dˆfd	„«      ƒ d{  –—† }|j                  «       }t        d
|› dt        |«      › d�«       |cddd«      ƒd{  –—†  S 7 Œ«7 Œb7 ŒF7 Œ# t        $ r+}dt        |«      › �}t        |«       t        d|¬«      ‚d}~ww xY w# 1 ƒd{  –—†7  sw Y   yxY w­w)z@Generate video using Replicate's SeedDance model (async wrapper)ÚREPLICATE_API_TOKENéô  z"REPLICATE_API_TOKEN not configured©Ústatus_codeÚdetailNzGenerating video for 'z'...z
  Prompt: c            
      ó@   •— t        j                  dd‰ dddddœ¬«      S )	Nzbytedance/seedance-1-pro-fasté   é   Ú480pz16:9T)Úfpsr›   ÚdurationÚ
resolutionÚaspect_ratioÚcamera_fixed)Úinput)Ú	replicateÚrun)r›   s   €r^   Ú<lambda>z5generate_video_with_replicate_async.<locals>.<lambda>ê   s(   ø€ œ	Ÿ™Ø3à!Ø"(Ø$%Ø&,Ø(.Ø(,ñõ
r]   zGenerated video for 'z' (z bytes)zReplicate API error: )r|   r}   r
   r°   r³   r—   r   Úget_running_loopÚVIDEO_SEMAPHOREÚrun_in_executorÚreadrž   r«   rd   )	rS   Úreplicate_api_keyr®   ÚloopÚoutputrU   r¯   Ú	error_msgr›   s	           @r^   Ú#generate_video_with_replicate_asyncrÏ   Ú   s&  øè ø€ äŸ	™	Ð"7Ó8ÐÙÜ¨Ð4XÔYÐYä9¸-ÓH×H€LÜ# LÓ1€FÜ	Ð" < .°Ð
5Ô6Ü	ˆJ�v�hÐ
Ô ä×#Ñ#Ó%€DßŽð	CØ×/Ñ/Øó
ó÷ ˆFð  Ÿ™›ˆJÜÐ)¨,¨°s¼3¸z»?Ð:KÈ7ÐSÔTØ÷% �‰ð Iøð øðøð ùô& ò 	CØ/´°A³¨xÐ8ˆIÜ�)ÔÜ¨C¸	ÔBÐBûð	Cú÷' �‰üsˆ   ƒ3D6¶C"·A
D6ÂC$ÂD6ÂD!ÂC*ÂC&Â 0C*ÃD6ÃC(ÃD6Ã$D6Ã&C*Ã(D6Ã*	DÃ3&DÄDÄD!Ä!D3Ä'D*Ä(D3Ä/D6rc   c              ƒ   ó˜  K  — t        | «      ƒ d{  –—† }t        «       2 3 d{  –—† }|szt        ||«      ƒ d{  –—† }|rdt        j                  |j
                  «      j                  d«      }t        ||d|j                  › �dd|j                  |j                  d¬«      c S t        |«      ƒ d{  –—† }t        |«      }t        ||||«      ƒ d{  –—† }t        j                  |«      j                  d«      }t        ||d|j                  › �dd	t        |«      |j                  d
¬«      c S 7 �Œ!7 �Œ7 Œý7 Œ†7 Œg6 t        d«      ‚­w)z0Get video from cache or generate new one (async)Nzutf-8z/workout/api/exercise_video/ÚcachedTzRetrieved from cache)rS   ri   rj   rk   rl   rW   rY   rm   Ú	generatedFzGenerated and cachedzDatabase session not available)r°   r†   rš   Úbase64Ú	b64encoderU   Údecoderh   rT   rW   rY   rÏ   r³   r£   rž   ÚRuntimeError)	rS   rc   r®   r�   rÑ   ri   rU   r›   Úsaveds	            r^   Úget_or_generate_video_asyncrØ   þ   sG  è ø€ ä9¸-ÓH×H€LÜ”H÷ 
ˆbÙÜ+¨B°Ó=×=ˆFÙÜ%×/Ñ/°×0AÑ0AÓB×IÑIÈ'ÓR�Ü$Ø".Ø!-Ø <¸V×=QÑ=QÐ<RÐSØ#Ø#Ø$×.Ñ.Ø%×0Ñ0Ø2ô	ò 	ô ?¸|ÓL×Lˆ
Ü'¨Ó5ˆÜ)¨"¨l¸JÈÓO×OˆÜ×'Ñ'¨
Ó3×:Ñ:¸7ÓCˆÜØ&Ø%Ø4°U×5HÑ5HÐ4IÐJØØÜ˜*“oØ×'Ñ'Ø*ô	
ò 		
ð) Iùð
ùà=øð MøàOøð# ô< Ð7Ó
8Ð8ùse   ‚E
‘D2’E
 D>¤D5¥D>¨E
ºD8»A8E
Â3D:Â4 E
ÃD<ÃAE
Ä5D>Ä8E
Ä:E
Ä<E
Ä>E
ÚexerciseÚlanguagec                 ó(  — |dk(  st        j                  «       s| S 	 d| v r!| d   rt        j                  | d   |«      | d<   d| v r~| d   ry| d   }t        j                  d|«      }|rC|j                  d«      }|j                  d«      }t        j                  ||«      }|› d|› �| d<   nt        j                  ||«      | d<   d| v r~| d   ry| d   }t        j                  d|«      }|rC|j                  d«      }|j                  d«      }t        j                  ||«      }|› d|› �| d<   nt        j                  ||«      | d<   d	| v r~| d	   ry| d	   }t        j                  d|«      }|rC|j                  d«      }|j                  d«      }t        j                  ||«      }|› d|› �| d	<   nt        j                  ||«      | d	<   d
| v r!| d
   rt        j                  | d
   |«      | d
<   | S # t        $ r}	t        d|	› �«       Y d }	~	| S d }	~	ww xY w)Nr9   Únamer¿   z(\d+(?:\.\d+)?)\s*(.+)r’   é   Ú ÚsetsÚcaloriesÚvideo_statuszExercise translation error: )r,   Úis_translation_enabledÚtranslate_textÚreÚmatchÚgroupr«   r—   )
rÙ   rÚ   r¿   rå   ÚnumberÚunitÚtranslated_unitrß   rà   r¯   s
             r^   Útranslate_exerciserê   "  s>  € Ø�4ÒÔ2×IÑIÔKØˆð$2Ø�XÑ (¨6Ò"2Ü2×AÑAÀ(È6ÑBRÐT\Ó]ˆH�VÑØ˜Ñ! h¨zÒ&:Ø 
Ñ+ˆHÜ—H‘HÐ6¸ÓAˆEÙØŸ™ Q›�Ø—{‘{ 1“~�Ü"5×"DÑ"DÀTÈ8Ó"T�Ø*0¨°°?Ð2CÐ'D�˜Ò$ä':×'IÑ'IÈ(ÐT\Ó']�˜Ñ$Ø�XÑ (¨6Ò"2Ø˜FÑ#ˆDÜ—H‘HÐ6¸Ó=ˆEÙØŸ™ Q›�Ø—{‘{ 1“~�Ü"5×"DÑ"DÀTÈ8Ó"T�Ø&, X¨Q¨Ð.?Ð#@�˜Ò ä#6×#EÑ#EÀdÈHÓ#U�˜Ñ Ø˜Ñ! h¨zÒ&:Ø 
Ñ+ˆHÜ—H‘HÐ6¸ÓAˆEÙØŸ™ Q›�Ø—{‘{ 1“~�Ü"5×"DÑ"DÀTÈ8Ó"T�Ø*0¨°°?Ð2CÐ'D�˜Ò$ä':×'IÑ'IÈ(ÐT\Ó']�˜Ñ$Ø˜XÑ%¨(°>Ò*BÜ':×'IÑ'IÈ(ÐSaÑJbÐdlÓ'mˆH�^Ñ$ð €Oøô ò 2ÜÐ,¨Q¨CÐ0×1Ð1Ø€Oûð2ús   �GG/ Ç/	HÇ8HÈHÚ	plan_datac                 óF  — |dk(  st        j                  «       s| S 	 g d¢}|D ])  }|| v sŒ| |   sŒt        j                  | |   |«      | |<   Œ+ d| v rLt        | d   t        «      r9g }| d   D ]*  }|sŒ|j                  t        j                  ||«      «       Œ, || d<   d| v r¨| d   r£| d   }d|v rI|d   }d|v r@|j                  dd«      j                  «       }t        j                  d|«      }	|› d|	› �|d<   |D ]L  }
t        ||
   t        «      sŒ|
dk7  sŒ||
   j                  «       rŒ1t        j                  ||
   |«      ||
<   ŒN d	| v �r�| d	   �rŠ| d	   D �]�  }d
|v rt        j                  |d
   |«      |d
<   d|v rt        j                  |d   |«      |d<   d|v rt        j                  |d   |«      |d<   d|v rc|d   }d|v rA|j                  dd«      j                  «       }t        j                  d|«      }	|› d|	› �|d<   nt        j                  ||«      |d<   d|v rc|d   }d|v rA|j                  dd«      j                  «       }t        j                  d|«      }	|› d|	› �|d<   nt        j                  ||«      |d<   d|v r|d   D ]  }t        ||«       Œ d|v r|d   D ]  }t        ||«       Œ d|v s�Œl|d   D ]  }t        ||«       Œ �Œ„ || d<   t        j                  «       | d<   | S # t        $ r}t        d|› �«       Y d }~| S d }~ww xY w)Nr9   )Ú
body_shapeÚactivity_levelÚworkout_levelÚ	main_goalÚbmi_categoryÚfocus_areasÚplan_summaryÚaverage_durationÚMinÚ rÞ   Úweekly_workoutsr2   Ú	intensityÚcategoryr¿   Útotal_caloriesÚCaloriesÚwarmup_exercisesÚmain_exercisesÚcooldown_exercisesrÚ   r,   z Workout plan translation error: )r,   râ   rã   Ú
isinstanceÚlistÚappendÚreplacerŒ   rd   Úisdigitrê   r«   r—   )rë   rÚ   Úprofile_fieldsÚfieldÚtranslated_areasÚareaÚsummaryÚavg_durationrç   ré   ÚkeyÚworkout_dayr¿   rà   rÙ   r¯   s                   r^   Útranslate_workout_planr  L  sÁ  € Ø�4ÒÔ2×IÑIÔKØÐð<6ò6ˆã#ˆEØ˜	Ò! i°Ó&6Ü#6×#EÑ#EÀiÐPUÑFVÐX`Ó#a�	˜%Ò ð $ð ˜IÑ%¬*°Y¸}Ñ5MÌtÔ*TØ!ÐØ! -Ô0�ÚØ$×+Ñ+Ô,?×,NÑ,NÈtÐU]Ó,^Õ_ð 1ð (8ˆI�mÑ$Ø˜YÑ&¨9°^Ò+DØ Ñ/ˆGØ! WÑ,Ø&Ð'9Ñ:�Ø˜LÑ(Ø)×1Ñ1°%¸Ó<×BÑBÓD�FÜ&9×&HÑ&HÈÐPXÓ&Y�OØ5;°H¸A¸oÐ=NÐ2O�GÐ.Ñ/Û�Ü˜g c™l¬CÕ0°SÐ<NÓ5NØ" 3™<×/Ñ/Õ1Ü':×'IÑ'IÈ'ÐRUÉ,ÐX`Ó'a˜ šð ð  	Ò)¨iÐ8IÓ.JØ(Ð):Õ;�Ø˜kÑ)Ü+>×+MÑ+MÈkÐZaÑNbÐdlÓ+m�K Ñ(Ø +Ñ-Ü/B×/QÑ/QÐR]Ð^iÑRjÐltÓ/u�K Ñ,Ø Ñ,Ü.A×.PÑ.PÐQ\Ð]gÑQhÐjrÓ.s�K 
Ñ+Ø Ñ,Ø*¨:Ñ6�HØ Ñ(Ø!)×!1Ñ!1°%¸Ó!<×!BÑ!BÓ!D˜Ü*=×*LÑ*LÈUÐT\Ó*]˜Ø5;°H¸A¸oÐ=NÐ2O˜ JÒ/ä2E×2TÑ2TÐU]Ð_gÓ2h˜ JÑ/Ø# {Ñ2Ø*Ð+;Ñ<�HØ! XÑ-Ø!)×!1Ñ!1°*¸bÓ!A×!GÑ!GÓ!I˜Ü*=×*LÑ*LÈZÐYaÓ*b˜Ø;A¸(À!ÀOÐCTÐ8U˜Ð$4Ò5ä8K×8ZÑ8ZÐ[cÐemÓ8n˜Ð$4Ñ5Ø%¨Ñ4Ø$/Ð0BÔ$C˜Ü*¨8°XÕ>ð %Dà# {Ñ2Ø$/Ð0@Ô$A˜Ü*¨8°XÕ>ð %Bà'¨;Ó6Ø$/Ð0DÔ$E˜Ü*¨8°XÕ>ò %Fð=  <ð@ !)ˆ	�*ÑÜ+>×+UÑ+UÓ+Wˆ	Ð'Ñ(ð Ðøô ò 6ÜÐ0°°Ð4×5Ð5ØÐûð6úsB   �K> «K> ±AK> Á3BK> ÄK> ÄK> Ä-FK> Ë5K> Ë>	L ÌLÌL Ú	plan_dictÚuser_profilec              ƒ   ó4  K  — g }g d¢}d}d}|D �]u  }|| vrŒ	| |   }|}	|dk(  rdddddd	d
dœ}
|
j                  ||«      }	g }d}d}|j                  dg «      D �]  }t        |j                  dd«      j                  «       «      ƒ d{  –—† }t        ||j                  dd«      |j                  dd«      |j                  dd«      ¬«      }|t	        |j
                  «      z  }|t        |j                  «      z  }|j                  r[t        |«      ƒ d{  –—† }|j                  |_
        |j                  |_        |j                  r|dz  }n|j                  dk(  r|dz  }|j                  |«       �Œ g }d}d}|j                  dg «      D �]  }t        |j                  dd«      j                  «       «      ƒ d{  –—† }t        ||j                  dd«      |j                  dd«      |j                  dd«      ¬«      }|t	        |j
                  «      z  }|t        |j                  «      z  }|j                  r[t        |«      ƒ d{  –—† }|j                  |_
        |j                  |_        |j                  r|dz  }n|j                  dk(  r|dz  }|j                  |«       �Œ g }d}d}|j                  dg «      D �]  }t        |j                  dd«      j                  «       «      ƒ d{  –—† }t        ||j                  dd«      |j                  dd«      |j                  dd«      ¬«      }|t	        |j
                  «      z  }|t        |j                  «      z  }|j                  r[t        |«      ƒ d{  –—† }|j                  |_
        |j                  |_        |j                  r|dz  }n|j                  dk(  r|dz  }|j                  |«       �Œ ||z   |z   }||z   |z   }|j                  d|› d�«      }|dk(  rt        j                   ||«      }t#        |	||› d�|j                  dd«      |› d�|j                  d d!«      |||¬"«	      }|j                  |«       t%        d#|	› d$|› d%|› d&�«       �Œx t'        d'„ |D «       «      }t)        |«      ||r!t'        d(„ |D «       «      t)        |«      z  › d�ndt'        d)„ |D «       «      t'        d*„ |D «       «      t'        d+„ |D «       «      d,œd-œ}t+        |j,                  |j.                  |j0                  |j3                  «       |j5                  «       |j6                  |j8                  |j:                  |j<                  |j>                  ||||¬.«      S 7 �Œþ7 �Œs7 �ŒÍ7 �ŒB7 �Œœ7 �Œ­w)/zWConvert LLM output to UI format with video generation and auto-corrected totals (async))ÚMondayÚTuesdayÚ	WednesdayÚThursdayÚFridayÚSaturdayÚSundayr   r:   u	   ì›”ìš”ì�¼u	   í™”ìš”ì�¼u	   ìˆ˜ìš”ì�¼u	   ëª©ìš”ì�¼u	   ê¸ˆìš”ì�¼u	   í† ìš”ì�¼u	   ì�¼ìš”ì�¼ÚwarmuprÜ   rö   Nr¿   z0 Minrß   z1 Setrà   z
0 Calories)rÜ   r¿   rß   rà   r’   rÒ   ÚmainÚcooldownr2   z Workoutz Minrø   ÚMediumz	 Caloriesrù   ÚGENERAL)	Údayr2   r¿   rø   rú   rù   rü   rý   rþ   Ú
z: z min, z calc              3   óF   K  — | ]  }t        |j                  «      –— Œ y ­wrp   )r'   rú   ©Ú.0Úws     r^   Ú	<genexpr>z'convert_to_ui_format.<locals>.<genexpr>ý  s   è ø€ ÐZÉ/ÀQ¤¨q×/?Ñ/?× @É/ùó   ‚!c              3   óF   K  — | ]  }t        |j                  «      –— Œ y ­wrp   )r&   r¿   r  s     r^   r"  z'convert_to_ui_format.<locals>.<genexpr>  s   è ø€ Ð"bÑRaÈQÔ#<¸Q¿Z¹Z×#HÑRaùr#  c              3   ó>   K  — | ]  }d |j                   v sŒd–— Œ y­w)ÚHighr’   N©rø   r  s     r^   r"  z'convert_to_ui_format.<locals>.<genexpr>  s   è ø€ ÐL¡?˜a°fÀÇÁÒ6Kœ¡?ùó   ‚–c              3   ó>   K  — | ]  }d |j                   v sŒd–— Œ y­w)r  r’   Nr'  r  s     r^   r"  z'convert_to_ui_format.<locals>.<genexpr>  s   è ø€ ÐP¡_ ¸ÀAÇKÁKÒ8Oœ!¡_ùr(  c              3   ó>   K  — | ]  }d |j                   v sŒd–— Œ y­w)ÚLowr’   Nr'  r  s     r^   r"  z'convert_to_ui_format.<locals>.<genexpr>  s   è ø€ ÐJ¡/˜Q°U¸a¿k¹kÒ5I”q¡/ùr(  )ÚhighÚmediumÚlow)Útotal_workoutsÚtotal_calories_per_weekrô   Úintensity_distribution)Úuser_idÚ
blood_typeÚageÚbmirñ   rí   rî   rð   rï   rò   ró   r÷   Úvideos_generatedÚvideos_cached) Úgetr°   rŒ   r#   r&   r¿   r'   rà   Úgenerate_videosrØ   rj   rk   rá   rl   r  r,   rã   r$   r—   Úsumrž   r"   r2  r3  r4  Úcalculate_bmiÚget_bmi_categoryrí   rî   rð   rï   rò   )r  r  rÚ   r÷   Ú
days_orderr6  r7  r  Úday_dataÚdisplay_dayÚday_translationsr  Úwarmup_total_durationÚwarmup_total_caloriesÚexr®   Úexercise_uiÚvideo_responser  Úmain_total_durationÚmain_total_caloriesr  Úcooldown_total_durationÚcooldown_total_caloriesÚtotal_durationrú   r2   r  Útotal_weekly_caloriesró   s                                 r^   Úconvert_to_ui_formatrL  �  s½  è ø€ à€OÚ_€JØÐØ€MäˆØ�iÑØØ˜S‘>ˆàˆØ�tÒà%°+ÈKØ'°;ÈKÐcnñ Ðð +×.Ñ.¨s°CÓ8ˆKàˆØ !ÐØ !ÐØ—,‘,˜x¨×,ˆBÜ!AÀ"Ç&Á&ÈÐQSÓBT×BZÑBZÓB\Ó!]×]ˆLÜ$Ø!ØŸ™ 
¨GÓ4Ø—V‘V˜F GÓ,ØŸ™ 
¨LÓ9ô	ˆKð "Ô%>¸{×?SÑ?SÓ%TÑTÐ!Ø!¤^°K×4HÑ4HÓ%IÑIÐ!Ø×+Ò+Ü'BÀ<Ó'P×!P�Ø(6×(@Ñ(@�Ô%Ø+9×+@Ñ+@�Ô(Ø!×,Ò,Ø! QÑ&‘MØ#×*Ñ*¨kÒ9Ø$¨Ñ)Ð$Ø�M‰M˜+Ö&ð% -ð( ˆØÐØÐØ—,‘,˜v r×*ˆBÜ!AÀ"Ç&Á&ÈÐQSÓBT×BZÑBZÓB\Ó!]×]ˆLÜ$Ø!ØŸ™ 
¨GÓ4Ø—V‘V˜F GÓ,ØŸ™ 
¨LÓ9ô	ˆKð  Ô#<¸[×=QÑ=QÓ#RÑRÐØ¤>°+×2FÑ2FÓ#GÑGÐØ×+Ò+Ü'BÀ<Ó'P×!P�Ø(6×(@Ñ(@�Ô%Ø+9×+@Ñ+@�Ô(Ø!×,Ò,Ø! QÑ&‘MØ#×*Ñ*¨kÒ9Ø$¨Ñ)Ð$Ø�K‰K˜Ö$ð% +ð( ˆØ"#ÐØ"#ÐØ—,‘,˜z¨2×.ˆBÜ!AÀ"Ç&Á&ÈÐQSÓBT×BZÑBZÓB\Ó!]×]ˆLÜ$Ø!ØŸ™ 
¨GÓ4Ø—V‘V˜F GÓ,ØŸ™ 
¨LÓ9ô	ˆKð $Ô'@À×AUÑAUÓ'VÑVÐ#Ø#¤~°k×6JÑ6JÓ'KÑKÐ#Ø×+Ò+Ü'BÀ<Ó'P×!P�Ø(6×(@Ñ(@�Ô%Ø+9×+@Ñ+@�Ô(Ø!×,Ò,Ø! QÑ&‘MØ#×*Ñ*¨kÒ9Ø$¨Ñ)Ð$Ø�O‰O˜KÖ(ð% /ð( /Ð1DÑDÐG^Ñ^ˆØ.Ð1DÑDÐG^Ñ^ˆà—‘˜W¨¨¨XÐ&6Ó7ˆØ�tÒÜ'×6Ñ6°u¸hÓGˆEä"ØØØ&Ð' tÐ,Ø—l‘l ;°Ó9Ø,Ð-¨YÐ7Ø—\‘\ *¨iÓ8Ø#ØØ'ô

ˆð 	×Ñ˜{Ô+Ü��;�-˜r .Ð!1°¸Ð7GÀtÐLÖMðK ôN  ÑZÉ/ÓZÓZÐä˜oÓ.Ø#8ñ ETœsÑ"bÑRaÓ"bÓbÔfiÐjyÓfzÑzÐ{Ð{ñ  Að  ZaäÑL¡?ÓLÓLÜÑP¡_ÓPÓPÜÑJ¡/ÓJÓJñ#
ñ		€Lô Ø×$Ñ$Ø×*Ñ*Ø×ÑØ×&Ñ&Ó(Ø!×2Ñ2Ó4Ø×*Ñ*Ø#×2Ñ2Ø×(Ñ(Ø"×0Ñ0Ø ×,Ñ,Ø!Ø'Ø)Ø#ôð ðE ^ùð "Qùð ^ùð "Qùð ^ùð "Qûsr   ‚BVÂVÂ	BVÄV	ÄB(VÆ?VÇ BVÉVÉB(VË6VË7BVÎVÎHVÖ	VÖVÖVÖVÖVc              ƒ   ó¢  K  — t         j                  €t         j                  «        t         j                  | «      }t         j	                  «       }|t         j                  z  t         j                  «       z  }t        dd› �«       t        d| j                  › �«       t        d| j                  › �«       t        d|› �«       | j                  rt        d| j                  › �«       | j                  rt        d| j                  › �«       | j                  rt        d	| j                  › �«       | j                  rt        d
| j                  › �«       | j                  r't        ddj                  | j                  «      › �«       t        dt        |«      › d�«       t        d› d�«       	 t         4 ƒd{  –—†  |j#                  d|i«      ƒ d{  –—† }ddd«      ƒd{  –—†  t%        | |«      ƒ d{  –—† S 7 ŒD7 Œ+7 Œ# 1 ƒd{  –—†7  sw Y   Œ-xY w7 Œ# t&        $ r)}t        d|› �«       t)        dt+        |«      ¬«      ‚d}~ww xY w­w)z<Generate personalized workout plan (async, with Redis cache)Nr  ú<============================================================zGenerating plan for zBlood Type: z
Language: zAge: zBody Shape: zActivity Level: zMain Goal: zFocus Areas: z, zContext length: z charactersÚpersonalized_contextzError: r¶   r·   )r¥   Úllmr§   Úcreate_personalized_contextÚcreate_personalized_promptÚWorkoutPlanOutputParserr—   r2  r3  r4  rí   rî   rð   rò   Újoinrž   r¨   r©   rL  r«   r
   rd   )r  rÚ   Úcontextr›   Úchainr  r¯   s          r^   Úgenerate_personalized_planrW    sñ  è ø€ ô ‡~�~ÐÜ×"Ñ"Ô$ô ×4Ñ4°\ÓB€GÜ×2Ñ2Ó4€Fð ”Z—^‘^Ñ#¤j×&HÑ&HÓ&JÑJ€Eä	ˆBˆvˆhˆ-ÔÜ	Ð  ×!5Ñ!5Ð 6Ð
7Ô8Ü	ˆL˜×0Ñ0Ð1Ð
2Ô3Ü	ˆJ�x�jÐ
!Ô"Ø×ÒÜ��l×&Ñ&Ð'Ð(Ô)Ø×ÒÜ�˜\×4Ñ4Ð5Ð6Ô7Ø×"Ò"ÜÐ  ×!<Ñ!<Ð =Ð>Ô?Ø×ÒÜ�˜L×2Ñ2Ð3Ð4Ô5Ø×ÒÜ�˜dŸi™i¨×(@Ñ(@ÓAÐBÐCÔDÜ	ÐœS ›\˜N¨+Ð
6Ô7Ü	ˆVˆH�Bˆ-Ôð<ß –=Ø#Ÿm™mÐ-CÀWÐ,MÓN×NˆI÷ !—=ä)¨)°\À8ÓL×LÐLð !øØNøð !ø—=—=‘=úàLùÜò <Ü�˜�sˆmÔÜ¨´C¸³FÔ;Ð;ûð<üs�   ‚F+IÆ.
H Æ8G=Æ9H Æ<HÇG?ÇHÇH Ç#HÇ$H Ç8HÇ9H Ç<IÇ=H Ç?HÈH ÈHÈ	HÈ
HÈH È	IÈ#$IÉIÉIc               ƒ   óØ  K  — t        d«       t        d«       t        d«       t        d«       t        «       ƒ d {  –—†  t        d«       t        d«       t        j                  «        t        d«       t        j	                  «        t        d«       t        d«       t        j                  «       r(t        d	«       t        d
t
        j                  › �«       y t        d«       y 7 Œ«­w)NrN  z5Starting Personalized Workout Planner v1.0 (Scalable)z
Initializing database...zDatabase readyz
Loading workout databases...z
Initializing AI...zAI initialized!z 
Checking translation service...zTranslation service enabledz   Supported languages: zTranslation service disabled)r—   rx   r¥   Úload_and_parse_all_pdfsr§   r,   râ   r;   r\   r]   r^   Ústartup_eventrZ  B  s¯   è ø€ Ü	ˆ&„MÜ	Ð
AÔBÜ	ˆ&„MÜ	Ð
&Ô'Ü
‹)‡O€OÜ	Ð
ÔÜ	Ð
*Ô+Ü×&Ñ&Ô(Ü	Ð
 Ô!Ü×ÑÔ Ü	Ð
ÔÜ	Ð
-Ô.Ü×1Ñ1Ô3ÜÐ+Ô,ÜÐ(Ô)<×)PÑ)PÐ(QÐRÕSäÐ,Õ-ð ús   ‚:C*¼C(½B,C*ÚstartupÚ/z	30/minuteÚrequestc              ƒ   ó  K  — t        | «      }t        «       2 3 d{  –—† }|j                  t        t        «      «      ƒ d{  –—† }t        |j                  «       «      } dd|t        j                  «       ddd› d�dd	d
dœddddddœg d¢ddgddœdœ}|dk7  rÓt        j                  «       r¿t        j                  |d   |«      |d<   |d   j                  «       D ]'  \  }}|dk7  sŒt        j                  ||«      |d   |<   Œ) ddddddd œ}|dk(  r(|j                  «       D ]  \  }	}
|	|d!   v sŒ|
|d!   |	<   Œ |d"   d#   D �cg c]  }t        j                  ||«      ‘Œ c}|d"   d#<   t        |¬$«      S 7 �Œ^7 �Œ;6 �Œ"c c}w ­w)%zAPI DocumentationNr1   z8.0.0 (Scalable)z'AI-generated personalized workout planszMComprehensive user profiling (body shape, activity level, goals, focus areas)z)Exercise videos using Replicate SeedDancez videos cachedz"LLM-powered exercise name cleaningz3Goal-based and focus-area-driven workout generationzSupports English and Korean)Úworkout_plansÚuser_profilingÚvideo_generationÚvideo_cachingÚname_validationÚsmart_planningÚmultilingualz/workout/weekly_workout_plan_uiz/workout/api/generate_videoz+/workout/api/exercise_video/{exercise_hash}z/workout/api/video_cache/statsz/workout/health)Úweekly_workout_plan_uiÚgenerate_exercise_videoÚget_exercise_videoÚvideo_cache_statsÚhealth)Úquery_paramÚheaderÚcookier9   r:   )Údetection_methodsr;   Údefault_language)Úservicer3   rÚ   Útranslation_enabledÚfeaturesÚ	endpointsÚlanguage_supportrp  rr  rb  u   ì£¼ê°„ ìš´ë�™ ê³„íš� ìƒ�ì„±u   ìš´ë�™ ë¹„ë””ì˜¤ ìƒ�ì„±u   ìš´ë�™ ë¹„ë””ì˜¤ ì¡°íšŒu   ìš´ë�™ ì�´ë¦„ ê²€ì¦�u   ë¹„ë””ì˜¤ ìº�ì‹œ í†µê³„u   ì„œë²„ ìƒ�íƒœ í™•ì�¸)rf  rg  rh  Úvalidate_exercise_nameri  rj  rs  rt  rn  ©rª   )r+   r†   r“   r   rE   rž   Úallr,   râ   rã   Úitemsr   )r]  rÚ   r�   r˜   Úvideo_countÚbase_responseÚfeature_keyÚfeature_descÚendpoint_descriptionsÚendpointÚkorean_descÚmethods               r^   Úrootr�  Y  sî  è ø€ ô )¨Ó1€HÜ”H÷ ˆbØ—z‘z¤&¬Ó"7Ó8×8ˆÜ˜&Ÿ*™*›,Ó'ˆØàHØ%ØÜ2×IÑIÓKàFØmØ KØ +˜}¨NÐ;ØCØSØ9ñ
ð 'HØ'DØ"OØ!AØ'ñ
ò "EØ$(¨$ <Ø $ñ
ñ+€Mð6 �4ÒÔ/×FÑFÔHÜ#6×#EÑ#EÀmÐT]ÑF^Ð`hÓ#iˆ�iÑ Ø)6°zÑ)B×)HÑ)HÖ)JÑ%ˆK˜Ø˜oÓ-Ü9L×9[Ñ9[Ð\hÐjrÓ9s�˜jÑ)¨+Ò6ð *Kð 'DØ'@Ø";Ø&<Ø!:Ø,ñ!
Ðð �tÒØ)>×)DÑ)DÖ)FÑ%�˜+Ø˜}¨[Ñ9Ò9Ø;F�M +Ñ.¨xÒ8ð *Gð
 (Ð(:Ñ;Ð<OÒPóB
áP�ô  ×.Ñ.¨v°xÕ@ØPñB
ˆÐ(Ñ)Ð*=Ñ>ô  Ô.Ð.ðiùØ8ùð ûò`B
ùsW   ‚F˜F œE:�F  "FÁE=ÁB&FÃ*A	FÄ4FÅFÅ%FÅ:F Å=FÆ FÆFz/healthz	60/minutec              ƒ   ó4  K  — t        | «      }t        «       2 3 d {  –—† }|j                  t        t        «      «      ƒ d {  –—† }t        |j                  «       «      } d|t        t        j                  «      t        j                  d ut        j                  d ut        t        j                  d«      «      t        j                  «       ddgdœ	}|dk7  r-t        j                  «       rt        j                   d|«      |d<   t#        |¬«      S 7 Œõ7 ŒÑ6 Œ·­w)NÚhealthyrµ   r9   r:   )	rk   rÚ   Údata_loadedÚ	llm_readyÚvalidation_llm_readyÚvideo_cache_sizeÚreplicate_configuredrq  r;   rk   rv  )r+   r†   r“   r   rE   rž   rw  rf   r¥   Úparsed_workout_datarP  r¦   r|   r}   r,   râ   rã   r   )r]  rÚ   r�   r˜   ry  Úhealth_datas         r^   rj  rj  ”  së   è ø€ ô )¨Ó1€HÜ”H÷ ˆbØ—z‘z¤&¬Ó"7Ó8×8ˆÜ˜&Ÿ*™*›,Ó'ˆØàØÜœJ×:Ñ:Ó;Ü—^‘^¨4Ð/Ü *× 9Ñ 9ÀÐ EØ'Ü $¤R§Y¡YÐ/DÓ%EÓ FÜ2×IÑIÓKØ $ d˜|ñ
€Kð �4ÒÔ/×FÑFÔHÜ 3× BÑ BÀ9ÈhÓ Wˆ�HÑÜ Ô,Ð,ð#øØ8øð ús8   ‚D˜DœD�D "DÁDÁCDÄDÄDÄDz/weekly_workout_plan_uiz	10/minute.zUser IDra   zBlood Type (O, A, B, AB)é
   ÚAge)ÚgeÚlerb   i,  zWeight (kg))ÚgtrŽ  rb   zHeight (cm)z,Body Shape: Medium, Flabby, Skinny, MuscularzIActivity Level: Sedentary, Lightly Active, Moderately Active, Very Activez>Workout Level: Easy To Start, Break A Light Sweat, Challengingz-Main Goal: Lose Weight, Gain Muscle, Stay FitzDesired Weight (kg)z@Focus Areas (comma-separated): Arms, Upper Body, Abs, Butt, LegszGenerate videos for exercisesr2  r3  r4  ÚweightÚheightrí   rî   rï   rð   Údesired_weightrò   r9  c              ƒ   óæ  K  — t        | «      }|dvrd}t        |«      }g d¢}||vrd}|dk(  rd}t        d|¬«      ‚g }|r=|j                  d	«      D �cg c]#  }|j	                  «       sŒ|j	                  «       ‘Œ% }}t        |||||||||	|
||¬
«      }t        ||«      ƒ d {  –—† }|j                  «       }|dk7  r t        j                  «       rt        ||«      }t        |¬«      S c c}w 7 ŒJ­w)N)r9   r:   r9   )ÚOÚAÚBÚABz+Invalid blood type. Must be O, A, B, or AB.r:   uE   ìž˜ëª»ë�œ í˜ˆì•¡í˜•ìž…ë‹ˆë‹¤. O, A, B, AB ì¤‘ í•˜ë‚˜ì—¬ì•¼ í•©ë‹ˆë‹¤.i�  r·   Ú,)r2  r3  r4  r�  r‘  rí   rî   rï   rð   r’  rò   r9  rv  )r+   r%   r
   ÚsplitrŒ   r!   rW  Údictr,   râ   r  r   )r]  r2  r3  r4  r�  r‘  rí   rî   rï   rð   r’  rò   r9  rÚ   Úvalid_blood_typesrÎ   Úfocus_areas_listr  r  Úplanr  s                        r^   Úget_weekly_workout_plan_uirž  «  s  è ø€ ô" )¨Ó1€HØ�|Ñ#Øˆä% jÓ1€JÚ-ÐØÐ*Ñ*ØAˆ	Ø�tÒØ_ˆIÜ¨°IÔ>Ð>àÐÙØ5@×5FÑ5FÀsÔ5KÓ\Ñ5K¨TÈtÏzÉzÍ|˜DŸJ™J�LÐ5KÐÐ\äØØØØØØØ%Ø#ØØ%Ø$Ø'ô€Lô ,¨L¸(ÓC×C€DØ—	‘	“€IØ�4ÒÔ/×FÑFÔHÜ*¨9°hÓ?ˆ	Ü 	Ô*Ð*ùò+ ]ð" Dús%   ‚AC1ÁC*Á*C*Á<(C1Â$C/Â%AC1z/api/generate_videoz	20/minuteÚvideo_requestc              ƒ   ó  K  — t        | «      }t        |j                  |j                  ¬«      ƒ d {  –—† }|dk7  rEt	        j
                  «       r1|j                  r%t	        j                  |j                  |«      |_        |S 7 ŒP­w)N)rc   r9   )r+   rØ   rS   rc   r,   râ   rm   rã   )r]  rŸ  rÚ   r­   s       r^   rg  rg  â  sw   è ø€ ô )¨Ó1€HÜ0Ø×#Ñ#Ø&×7Ñ7ô÷ €Hð �4ÒÔ/×FÑFÔHÈX×M]ÒM]Ü.×=Ñ=¸h×>NÑ>NÐPXÓYˆÔØ€Oðús   ‚0B²B³ABz#/api/exercise_video/{exercise_hash}rT   c              ƒ   óØ  K  — t        «       2 3 d {  –—† }|j                  t        t        «      j	                  t        j
                  |k(  «      «      ƒ d {  –—† }|j                  «       }|st        dd¬«      ‚t        j                  «       |_
        |xj                  dz  c_        |j                  «       ƒ d {  –—†  |j                  } t        ddd|› d�i¬	«      S 7 ŒÐ7 ŒŒ7 Œ)6 Œ­w)
Ni”  zVideo not foundr·   r’   z	video/mp4zContent-Dispositionzinline; filename=z.mp4)rª   Ú
media_typeÚheaders)r†   r“   r   rE   r”   rT   r•   r
   r   rX   rZ   r[   r–   rU   r   )r]  rT   r�   r˜   r™   rU   s         r^   Úget_exercise_video_by_hashr¤  î  sß   è ø€ ô ”H÷ ˆbØ—z‘zÜ”=Ó!×'Ñ'¬×(CÑ(CÀ}Ñ(TÓUó
÷ 
ˆð ×0Ñ0Ó2ˆÙÜ¨CÐ8IÔJÐJÜ%-§_¡_Ó%6ˆÔ"Ø×!Ò! QÑ&Õ!Ø�i‰i‹k×ÐØ!×,Ñ,ˆ
ØÜØØØ&Ð*;¸M¸?È$Ð(OÐPôð ðøð
øð 	øð úsK   ‚C*�C(‘C"’C(•AC*ÁC$ÁA$C*Â<C&Â=%C*Ã"C(Ã$C*Ã&C*Ã(C*z/api/video_cache/statsc              ƒ   óŒ  K  — t        | «      }t        «       2 3 d {  –—† }|j                  t        t        «      «      ƒ d {  –—† }|j                  «       j                  «       }t        |«      }t        d„ |D «       «      }t        |d„ d¬«      d d }t        |d„ d¬«      d d } t        dz  d«      |d	kD  rt        ||z  d
z  d«      nd	D �	cg c]&  }	|	j                  |	j                  |	j                  dœ‘Œ( c}	D �	cg c]3  }	|	j                  |	j                  t        |	j                  d
z  d«      dœ‘Œ5 c}	dœ}
|dk7  r`t!        j"                  «       rL|
d   D ]  }t!        j$                  |d   |«      |d<   Œ  |
d   D ]  }t!        j$                  |d   |«      |d<   Œ  t'        |
¬«      S 7 �Œ•7 �Œr6 �Œc c}	w c c}	w ­w)Nc              3   ó4   K  — | ]  }|j                   –— Œ y ­wrp   )rW   )r   Úvs     r^   r"  z(get_video_cache_stats.<locals>.<genexpr>  s   è ø€ Ð:©z¨!˜!Ÿ+�+©zùs   ‚c                 ó   — | j                   S rp   )r[   ©r§  s    r^   rÆ   z'get_video_cache_stats.<locals>.<lambda>  s   € ¸¿ºr]   T)r
  Úreverser‹  c                 ó   — | j                   S rp   )rY   r©  s    r^   rÆ   z'get_video_cache_stats.<locals>.<lambda>  s   € ¸¿ºr]   i   rÝ   r   i   )rÙ   r[   rZ   )rÙ   rY   Úfile_size_kb)Útotal_videosÚtotal_size_mbÚaverage_size_kbÚmost_accessedÚrecent_additionsr9   r°  rÙ   r±  rv  )r+   r†   r“   r   rE   Úscalarsrw  rž   r:  ÚsortedÚroundrS   r[   rZ   rY   rW   r,   râ   rã   r   )r]  rÚ   r�   r˜   Ú
all_videosr­  Útotal_bytesr°  Úrecent_videosÚvideoÚ
stats_dataÚitems               r^   Úget_video_cache_statsr»    sì  è ø€ ô )¨Ó1€HÜ”H÷ ˆbØ—z‘z¤&¬Ó"7Ó8×8ˆØ—^‘^Ó%×)Ñ)Ó+ˆ
Ü˜:“ˆÜÑ:©zÓ:Ó:ˆÜ˜zÑ/GÐQUÔVÐWZÐXZÐ[ˆÜ˜zÑ/EÈtÔTÐUXÐVXÐYˆØð %Ü˜{¨kÑ:¸AÓ>ØLXÐ[\ÒL\œ5 +°Ñ"<ÀÑ!DÀaÔHÐbcñ 'ó
ñ '�ð	 "×/Ñ/Ø %× 2Ñ 2Ø!&×!4Ñ!4óð
 'ñ
ñ 'ó
ñ '�ð	 "×/Ñ/Ø#×.Ñ.Ü % e§o¡o¸Ñ&<¸aÓ @óð
 'ñ
ñ€Jð, �4ÒÔ/×FÑFÔHØ˜Ô/ˆDÜ2×AÑAÀ$ÀzÑBRÐT\Ó]ˆD�Òð 0àÐ1Ô2ˆDÜ2×AÑAÀ$ÀzÑBRÐT\Ó]ˆD�Òð 3ô  
Ô+Ð+ðKùØ8ùð ûò
ùò
ùsW   ‚G˜F7œF1�F7 "GÁF4ÁBGÃ+F:
Ã=GÄ8F?Ä;A6GÆ1F7Æ4GÆ7GÆ:
Gz/api/video_cache/clearz5/minutezConfirm deletionÚconfirmc              ƒ   ó&  K  — t        | «      }|s0d}d}|dk(  rt        j                  «       rd}d}t        ||dœ¬«      S t	        «       2 3 d {  –—† }|j                  t        t        «      «      ƒ d {  –—† }|j                  «       j                  «       }t        |«      }|D ]  }	|j                  |	«      ƒ d {  –—†  Œ |j                  «       ƒ d {  –—†   d}|dk(  rt        j                  «       rd	}t        |d
œ¬«      S 7 Œ¸7 Œ”7 ŒO7 Œ76 Œ6­w)Nz-Add ?confirm=true to actually clear the cachez"This will delete all cached videosr:   uA   ìº�ì‹œë¥¼ ì‹¤ì œë¡œ ì§€ìš°ë ¤ë©´ ?confirm=trueë¥¼ ì¶”ê°€í•˜ì„¸ìš”u;   ì�´ ìž‘ì—…ì�€ ëª¨ë“  ìº�ì‹œë�œ ë¹„ë””ì˜¤ë¥¼ ì‚­ì œí•©ë‹ˆë‹¤)rm   Úwarningrv  zCache cleared successfullyu/   ìº�ì‹œê°€ ì„±ê³µì �ìœ¼ë¡œ ì‚­ì œë�˜ì—ˆìŠµë‹ˆë‹¤)rm   Úvideos_deleted)r+   r,   râ   r   r†   r“   r   rE   r²  rw  rž   Údeleter–   )
r]  r¼  rÚ   rm   r¾  r�   r˜   rµ  Údeletedr¸  s
             r^   Úclear_video_cacherÂ  .  s  è ø€ ô )¨Ó1€HÙØAˆØ6ˆØ�tÒÔ 3× JÑ JÔ LØYˆGØSˆGÜ°ÀGÑ$LÔMÐMä”H÷ ˆbØ—z‘z¤&¬Ó"7Ó8×8ˆØ—^‘^Ó%×)Ñ)Ó+ˆ
Ü�j“/ˆÛˆEØ—)‘)˜EÓ"×"Ñ"ð  à�i‰i‹k×ÐØà*€GØ�4ÒÔ/×FÑFÔHØCˆÜ¨GÀwÑ OÔPÐPðøØ8øð #øØøð úsa   ‚ADÁ
DÁDÁDÁ"DÁ4D	Á5ADÂ;DÂ<DÃDÃ1DÄDÄ	DÄDÄDÄD)F)r9   )srP   r|   rÓ   r‰   r   rä   Útypingr   r   r   r   r   Úfastapir   r	   r
   r   r   Úfastapi.responsesr   r   Úfastapi.middleware.corsr   Úslowapir   r   Úslowapi.utilr   Úslowapi.errorsr   ÚuvicornÚdotenvr   rÄ   Úsqlalchemy.ext.asyncior   r   r   Úsqlalchemy.ormr   Ú
sqlalchemyr   r   r   r   r   r   Úredisr~   Úworkout_app.workout_listr    r!   r"   r#   r$   r%   r&   r'   Úpydanticr(   r)   Ú
middlewarer*   r+   r,   r}   ÚlimiterÚappÚstateÚadd_exception_handlerÚadd_middlewarer<   rq   r„   rt   rE   r`   rh   rx   r{   r�   Ú	Semaphorer¨   rÈ   r¥   r†   rd   r�   rš   Úbytesr£   r°   r³   rÏ   rf   rØ   rê   r  rL  rW  rZ  Úadd_event_handlerr8  Úlimitr�  rj  rn   Úfloatrž  Úpostrg  r¤  r»  rÀ  rÂ  r\   r]   r^   Ú<module>rÞ     sÓ  ðñó 
Û Û Û Û 	ß ,Ó ,Ý ß KÕ Kß 4Ý 2ß 9Ý +Ý ,Û Ý Û ß XÑ XÝ +ß M× MÝ %÷	÷ 	ó 	÷ &÷ EÝ 3á „ñ Ð-¸9¸2¿9¹9À[ÐRjÓ;kÔ
l€áÐIÐSZÔ[€Ø€‡	�	Ô Ø × Ñ Ð+Ð-IÔ Jð × Ñ ØØ�%ØØ�%Ø�%ð ô ð × Ñ Ð%¸DÀ$¸<Ð Ô Hð ˆr�y‰y˜Ð)SÓT€Ù	˜\°ÀÐRTÔ	U€Ù& v°lÐUZÔ[Ð ÙÓ€ô.�Dô .ôY˜Yô Yô"�Iô "ò6ð
 €òð "�×!Ñ! $Ó'€Ø#�'×#Ñ# CÓ(€ñ Ó€
òðK Sð K¨Só Kð˜|ð ¸Cð ÀHÈ]ÑD[ó ð ,ð ¸sð ÐPUð Ð_bð Ðgtó ð:#%¸#ð #%À#ó #%ðL¨#ð °#ó ð
"C¸Sð "CÀUó "CñH!9°Sð !9ÈDð !9Ð]ló !9ðH( ð (°ð (¸ó (ðT@ dð @°cð @¸dó @ðFI¨$ð I¸kð IÐUXð IÐ]ió IñX$<°;ð $<È#ð $<ÐYeó $<òN.ð& × Ñ �i Ô /ð ‡�ˆƒØ‡�ˆ{Óð7/˜ò 7/ó ó ð7/ðr ‡�ˆÓØ‡�ˆ{Óð-˜'ò -ó ó ð-ð* ‡�Ð	"Ó#Ø‡�ˆ{Óñ ˜¨)Ô4Ù˜CÐ-GÔHÙ˜t¨¨sÀÔFÙ# D¨Q°3ÀMÔRÙ# D¨Q°3ÀMÔRÙ % dÐ8fÔ gÙ$)¨$ð  =Hô  %IÙ#(¨Ð;{Ô#|Ù$ TÐ7fÔgÙ&+¨D°Q¸3ÐLaÔ&bÙ!& tÐ9{Ô!|Ù! $Ð4SÔTñ2+Øð2+àð2+ð ð2+ð 
�#‰ð	2+ð
 �U‰Oð2+ð �U‰Oð2+ð ˜‘ð2+ð ˜S‘Mð2+ð ˜C‘=ð2+ð ˜‰}ð2+ð ˜U‘Oð2+ð ˜#‘ð2+ð ò2+ó ó $ð2+ðj ‡�Ð
Ó Ø‡�ˆ{Óð¨7ð ÐCYò ó ó !ðð ‡�Ð	.Ó/Ø‡�ˆ{Óð¨gð Àcò ó ó 0ðð& ‡�Ð	!Ó"Ø‡�ˆ{Óð',¨ò ',ó ó #ð',ðR ‡�Ð$Ó%Ø‡�ˆzÓñ ˜%Ð-?Ô@ñQØðQàòQó ó &ñQr]   