
    P1iF:             %       A   d dl mZ d dlmZmZmZmZ d dlmZ d dl	m
Z
 d dlmZmZ 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mZ d dlmZ d dlmZ d dlmZmZm Z m!Z!m"Z"m#Z#m$Z$ d dlm%Z% d dl&m'Z'm(Z(m)Z) d dl*m+Z, ddl%m-Z-m.Z.m/Z/m0Z0m1Z1m2Z2m3Z3m4Z4m5Z5m6Z6m7Z7m8Z8m9Z9m:Z:m;Z;m<Z<m=Z=m>Z>m?Z?m@Z@mAZAmBZBmCZCmDZDmEZEmFZFmGZGmHZH d dlImIZImJZJmKZK d dlLmMZM d dlNmOZO d dlPmQZQ d dlRmSZS d dlTmUZU d dlVmWZW d dlXmYZZmOZ[ d dl\m]Z] d dl^m_Z_ d dl`maZa d dlbmcZc d dldmeZe d d lfmgZg d d!lhmiZi d d"ljmkZk d d#llmmZm d d$llmnZn d d%l&moZo d d&lpZpd d'lqmrZr d d(lsmtZt d d)lumvZv d d*lwmxZx d d+lymzZz d dlRmSZS d dlTmUZU  epj                  e|      Z} G d, d-ee      Z~ e       Z G d. d/e%j                         Z G d0 d1e%j                         Z G d2 d3e%j                         Z G d4 d5e%j                         Z G d6 d7e%j                         Z G d8 d9e%j                         Z G d: d;e%j                         Z G d< d=e%j                         Z G d> d?e%j                         Z G d@ dAe%j                         Z edBe"j                   edCeej                  dDdE e dFdGH      gI       edJeej                  dDdK e dLdMH      gI       edNeej                  dDdO e dPdQH      gI       edReej                  dDdS e dTdQH      gI       edUeej                  dVdW e dXdYH      gI       edZeej                  dVd[ e d\d]H      gI       ed^eej                  dVd_ e d`daH      gI       edbeej                  dVdc e dddeH      gI       edfeej                  dVdg e dhdiH      gI       edjeej                  dVdk e dld H      gI       edmeej                  dVdn e dodpH      gI       edqeej                  dVdr e dsdtH      gI      g e!e-du e dvddGdMdYd]dadedid dpdtdwdxdydydzH      g{       e!d| e d}d~gg ddgd~gdgdgdgdH      g      d e ddGdMdQdQdYd]dadedid dpdtdwddD      g       G d dej                               Z edddddddddddi       G d dej"                               Z ed e$d e%j&                  d       e%j&                  d      d       e$d e%j&                  d       e%j&                  d       e%j(                  d       e%j&                  d      d       e!d       e!d      d e dvdddddVddddD      g       G d dej"                               Z ede= e$d e.        e%j&                  d       e%j&                  d       e%j&                  d      d       e!d       e!d       e!d      d e ddGdMddD       e ddGdMdddYd]dadedd dddVdddD       e dddddddddddʜd˜dD       e dddGdMdYd]dadedd dddVddxdddddD       e dddddddddddd&dVddxddҜdD      g       G dӄ dej"                               Z ed e$dd e%j&                  dج      i       e$d e%j&                  dڬ       e%j&                  d۬      dܜ       e!dݬ      dޜ߫       G d dej"                               Z edddddddidi       G d dej"                               Z edddddddidi       G d dej"                               Z ede: e!dddddiddꬥ       e!ddddidddddiddddd       e!d񬥫      d e ddddddD       e dvddidDdg       e d}dgdgddDdg      g       G d dej"                               Z ed e!e.d  e dvddGdMdYd]dadedd dpdtdVddxdDdg      g{       e!d񬥫      dޜ       G d dej6                               Z eUeSd       G d de
             Z e# edd ed	eej                  d
dV e ddH       e ddH      g       edeej                  ddV e ddH      g       edeej                  ddV e ddH       e ddH      g      gd e! e2dD      d e di ddddd d!d"d#d$d%d&d%d'd%d(d)d*d%d+d,d-d.d/d0d1d2d3dd4dd5d6      g{      i7       ed8d9d e!e2d: e d;i ddddd d!d"d#d$d%d&d%d'd%d(d)d*d%d+d,d-d.d/d0d1d2d3dd4dd5d6      g{      i<       ed=d>e3 e d?dd#d%d%d%d)d%d,d.d0d@dAdD      gdB e!e2dC e dDi ddddd d!d"d#d$d%d&d%d'd%d(d)d*d%d+d,d-d.d/d0d1d2d3dd4dd5d6      g{      iE       edFdGe3 e dHddId%d%d%d)d%d,dJdKd@dAdD      gL       edMdNe3 e dOdPdQdRdD      gL       edSdTU      V       eUeSd       G dW dXej<                                      Z e# edYdZ ed[eej                  d\dV e d]d^H       e d_d`H      g       edaeej                  dbdV e dcdYH      g       ed	eej                  dddV e ddH       e dedfH      g       edeej                  ddV e ddH      g       edeej                  ddV e ddH       e ddH      g      gd e! e5dD      dg e dhddidadeddjdkdkdldldmdd dmddldmdndldmdodpddq6      g{      i7       edrds edeejB                  dtdDu      ge"j                  e"j                  dv e dwdxdidadeddjdkdkddydzd{dDdg      g|       ed}d~e6e6ge"j                  e"j                  e"j                  d e ddidadedd%dddjddD       e dddddddddddddddddddgdDddddddddddddddddgdVdndddddddddgdVdddddddddddddgdVdddddd ddd ddd dgidddddd dgiddd ddd ddd ddd ddddgiddddgidodi dddddddg ddddi dd dddd dddddddddd dddddddƐddddddɐddʐdndːdddddlddddndddld̜dg d΢dg dТdѐddҐdӓdԐdldddndd՜dg dעdddِdړdg dܢdݐdndސddd d՜dg di dd dddd dddddd dddddddddddƐdޓdddȐddddddːdndldddlddސdddld	g dddddddddDdg       e dddddddddddddddddddgdDddddddddddddddddgdVdndddddddddgdVdddddddddddddgdVdddddd ddd ddd dgidddddd dgiddd ddd ddd ddd ddddgiddddgidodi dddddddg ddddi dd dddd dddddddddd dddddddƐddddddɐddʐdndːdddddlddddndddld̜dg d΢dg dТdѐddҐdӓdԐdldddndd՜dg dעdddِdړdg dܢdݐdndސddd d՜dg di dd dddd dddddd dddddddddddƐdޓdddȐddddddːdndldddlddސdddld	g dddddDdg       e d}ddidDdg      g       edde6 edeejB                  ddDu      ge"j                  e"j                  d e ddddedd%dddddD       e ddxdddedddkdddydzd{dDdg      g       edde6 edeejB                  ddDu      ge"j                  e"j                  d e dOddddD       e d dxddadedddkdddydzd{dDdg      g       edd edeejB                  ddDu      ge"jD                  e"j                  d7      V       eUeSd       G d dej<                                      Z G d d	e
      Z G d
 de
      Z edd edeej                  ddD e d eIjL                         jN                  H       e d eIjL                         jN                  dz   H      g      ge8 e!e9d e ddddddd ddddddddddnddddddddldddVdddd dd ddddVddndddd gd!dd"dd#ddddndddndddddddg d!ddd$gd%d&d'd(dg)      g{       e!d* e d+dd,idg)       e d-dd.idg)      g       e!d/ e d0dd1id2g)      g      dd3g4       G d5 d6e
             Z ed7e; e!dddddd8dd9dd:       e!d񬥫      dޜ e dd;d<idD       e dvd=dMd9dDdg      g       G d> d?ej"                               Z G d@ dAe
      Z edBdCgdDdEd e!e"j                  dF e dGdHdIdJdKdLdMdKdNdOdKdPdQdKdRdSdKdTdUdVdWdXdYdZd[dD       e d\d]d^d_dD      g{      i`       edadbgdcddg  e$de e%jV                  dVdVdfg       e%j&                  dVdDdhi       e%j&                  dVdDdji      dk       e dldVdWdVdmdD       e dndodpdDdmdD      g e!e"j                  dq e drdHdIdJdKdLdMdKdNdOdKdPdQdKdRdSdKdTdUdVdWdXdYdZd[dD      g{       e!e"j                  ds e dtdduidDv      g{       e!e"j                  dw e dxddwidDv      g{       e!e"j                  dy e dzd{d|d}dDv      g{      d~       e'dCdbg       e(eg       e)eWeQg      d                                    Z eddCgddd e!e"j                  dF e dGdHddddddddddVdWdddZd[dD       e d\d]d^d_dD      g{      i`       eddbgddg  e$d e%jV                  dVdVdfg       e%j&                  dVdDdhi       e%j&                  dVdDdji       e%j&                  dVddg      d       e dldVdWdVdmdD       e dndodpdDdmdD      g e!e"j                  dq e drdHddddddddddVdWdddZd[dD      g{       e!e"j                  ds e dtdduidDv      g{       e!e"j                  d e dxddidDv      g{       e!e"j                  dy e dzd{d|d}dDv      g{      d~       e'dCdbg       e(eg       e)eWeQg      d                                    Z ede> e$d e%j\                  d       e%j&                  d       e%j&                  d       e%jV                  d       e%j&                  d       e%j\                  ddD       e%jV                  d      d       e!d e d}dgdgddD      g      d e dddadedid ddD       e ddadidD       e di dD       e dvddddDdd&dDddD      g       G d dej"                               Z eUeSd       ed e$dd e%j&                  d      i       e$d e%jV                  d       e%j&                  d       e%j\                  d       e%jV                  d      d       e!d e dddidD      g       e!d e dddidD      g      d e dddidD       e dvdDdddDddD      g       G d dej"                                      Z eddde? e!dì       e!dĬ       e!d       e!dŬ       e!dƬ      dǜȫ       eUeSd       G dɄ dej                                      Z edːd̐de? e!dì       e!dĬ       e!d       e!dŬ       e!dƬ      dǜȫ       eUeSd       G d΄ dej                                      Z edАdѐde? e!dì       e!dĬ       e!d       e!dŬ       e!dƬ      dǜȫ       eUeSd       G dӄ dej                                      Zd dlmZmZmZ d dlmZmZmZmZ d dlmZ  edאd eDdD       e!d       e!dw      dٜ<       e'dCg       e)eWeQg       e(eg      dڄ                             Z edېdeC e!d       e!dݬ      dٜ<       e'dCg       e)eWeQg       e(eg      dބ                             Z edߐdeEeC e!d       e!dw      d       e'dbg       e)eWeQg       e(eg      d                             Z eddeF e$d e%j\                          eG        eG       d       e!d謥       e!d       e!dݬ       e!d鬥      d       e'dbg       e)eWeQg       e(eg      d                             Z edd e$d e%j\                  dVd       e%j&                  dVd      d       e$d e%j\                          eG        eG       d       e!d       e!d       e!d      d       e'dbg       e)eWeQg       e(eg      d                             Z edd eDdD       e!d       e!d      dٜ<       e'dCg       e)eWeQg       e(eg      d                             Z eddeC e!d       e!dݬ      dٜ<       e'dCg       e)eWeQg       e(eg      d                             Z edd eEeC e!d       e!d      d       e'dbg       e)eWeQg       e(eg      d                             Z eddeF e$d e%j\                          eG        eG       d       e!d謥       e!d       e!dݬ       e!d鬥      d       e'dbg       e)eWeQg       e(eg      d                             Z edd e$d e%j\                  dVd       e%j&                  dVd      d       e$d e%j\                          eG        eG       d       e!d       e!d       e!d      d       e'dbg       e)eWeQg       e(eg      d                             Z edd	d
geH e!d      dޜ       e'dCg       e)eWeQg       e(eg      d                             Zy&(      )render)genericsstatusviewsetsfilters)Response)APIView)AllowAnyIsAuthenticatedRefreshToken)get_user_modelauthenticatedefault_token_generator)	send_mail)Qsettings)ValidationError)extend_schemaOpenApiParameterOpenApiExampleOpenApiResponseOpenApiTypesextend_schema_viewinline_serializer)serializers)api_viewpermission_classesauthentication_classes)Person   )UserRegistrationSerializerUserSerializerPasswordResetRequestSerializerPasswordResetConfirmSerializerLoginSerializerLiuyaoSerializerLiuyaoRequestSerializerLiuyaoCalculatorSerializerBaziSerializerBaziRequestSerializerTongshuCalendarQuerySerializerCalendar10kQuerySerializerCalendar10kSerializerPasswordChangeSerializer AccountDeletionRequestSerializerTempLoginSerializerTempRegisterSerializerTempUserCreateSerializerReportSubmissionSerializerBaziReportSerializerNumberReportSerializerLiuYaoReportSerializerConversationSerializerConversationListSerializerCreateConversationSerializerSendMessageSerializerMessageSerializerConversationConfigSerializer)datetimedate	timedelta)
monthrange)liuyao)SessionAuthentication)csrf_exempt)method_decorator)!ActivityTrackingJWTAuthentication)bzrD   )evaluate_pair)NumberPower)utilstimezone)PageNumberPagination)render_to_string)reverse)
strip_tags)AccountDeletionRequestUserProfile)actionN)analyze_number)Http404)PermissionDenied)LLMServiceFactory)get_ai_configc                       e Zd ZdZdZdZd Zy)CustomPageNumberPaginationz
    Custom pagination class that allows specifying page size via query parameter.
    If not specified, uses the default from settings.
    	page_sized   c                    	 t        |j                  j                  | j                  d            }|dkD  rt	        || j
                        S 	 t        t        di       j                  dd      S # t        t        f$ r Y 2w xY w)Nr   PAGINATE_BYdefault   )
intquery_paramsgetpage_size_query_paramminmax_page_size	TypeError
ValueErrorgetattrr   )selfrequestr^   s      +/home/cursorai/projects/iching/api/views.pyget_page_sizez(CustomPageNumberPagination.get_page_sizeW   s    	G0044T5O5OQRSTI1}9d&8&899  x377	2FF :& 		s   A	A. .B ?B N)__name__
__module____qualname____doc__rg   ri   rp        ro   r]   r]   O   s     (MGrv   r]   c                   X    e Zd Z ej                  d      Z ej                  d      Zy)BaziComponentSerializeru   Heavenly stem (天干) index	help_textu   Earthly branch (地支) indexN)rq   rr   rs   r   IntegerFieldgeru   rv   ro   rx   rx   e   s(       +IJA   +JKArv   rx   c                       e Zd Z ed      Z ed      Z ed      Z ed      Z ej                  d      Z
 ej                  d      Zy)	BaziPillarSerializerzHour pillarry   z
Day pillarzMonth pillarzYear pillarzFormatted date and timezEmpty positions for divinationN)rq   rr   rs   rx   hourdaymonthyearr   	CharFieldrA   	DictFieldemptyru   rv   ro   r   r   i   sP    "];D
!L
9C#n=E"];D ;  +DED!K!!,LMErv   r   c                       e Zd Z ej                  d      Z ej                  d      Z ej                  d      Z ej                  d      Zy)PalaceSerializerzPalace numberry   PositionzShi positionzYin positionN)	rq   rr   rs   r   r{   palaceposshiyinru   rv   ro   r   r   q   sJ    %[%%@F
"+
"
"Z
8C
"+
"
"^
<C
"+
"
"^
<Crv   r   c                       e Zd Z ej                  d      Z ej                  d      Z ej                  d      Z ej                  d      Z ej                  d      Z	 ej                  d      Z
y)	YaoPositionsSerializerz
Position 1ry   z
Position 2z
Position 3z
Position 4z
Position 5z
Position 6N)rq   rr   rs   r   r{   y1y2y3y4y5y6ru   rv   ro   r   r   w   sn    	!	!	!L	9B	!	!	!L	9B	!	!	!L	9B	!	!	!L	9B	!	!	!L	9B	!	!	!L	9Brv   r   c                       e Zd Z ej                  d      Z ej                  d      Z ej                  d      Z e	d      Z
 ed      Z ed      Z ed      Z ed	      Zy
)GuaSerializerHexagram numberry   Six clash flagSix harmony flagPalace informationzHeavenly stems for each linezEarthly branches for each linez Line structure (broken/unbroken)zChanging lines informationN)rq   rr   rs   r   r{   guaBooleanFieldclash6harmony6r   r   r   r|   r}   yao	changeyaoru   rv   ro   r   r      sr    
"+
"
"->
?C%[%%0@AF'{''2DEH(<=F)GHA)IJA
 +M
NC&1MNIrv   r   c                       e Zd Z ej                  d      Z ej                  d      Z ej                  d      Z e	d      Z
y)ChangedGuaSerializerr   ry   r   r   r   N)rq   rr   rs   r   r{   r   r   r   r   r   r   ru   rv   ro   r   r      sF    
"+
"
"->
?C%[%%0@AF'{''2DEH(<=Frv   r   c                   0    e Zd Z ed      Z ed      Zy)LiuyaoStructureSerializerzOriginal hexagram datary   zChanged hexagram dataN)rq   rr   rs   r   oguar   cguaru   rv   ro   r   r      s    #;<D*ABDrv   r   c                   X    e Zd Z ej                  d      Z ej                  d      Zy)RelationshipsSerializerz"Relationships in original hexagramry   z!Relationships in changed hexagramN)rq   rr   rs   r   r   r   r   ru   rv   ro   r   r      s(     ;  +OPD ;  +NODrv   r   c                       e Zd Z ej                  d      Z ed      Z ej                  d      Z	 e
d      Z ed      Zy)GuaDataSerializer!The question asked for divinationry   u   Bazi (八字) informationu#   The Six Gods (六神) determinationzLiuyao structurezRelationships between elementsN)rq   rr   rs   r   r   questionr   rI   r{   god6r   lyr   relru   rv   ro   r   r      sI    ${$$/RSH	(C	DB#;##.STD	"-?	@B
!,L
MCrv   r   c                   j    e Zd Z ed      Z ej                  d      Z ej                  d      Zy) LiuyaoDetailedResponseSerializerz-Main object containing all divination resultsry   r   z+String representation of the hexagram linesN)	rq   rr   rs   r   r   r   r   r   r   ru   rv   ro   r   r      s3    
&U
VC${$$/RSH
+

*W
XCrv   r   zRegister a new userphoneTz7Phone number used for login (max length: 16 characters)zValid Phone
1234567890value)nametypelocationrequireddescriptionexamplesemailzCEmail address (required, max length: 50 characters, must be unique)zValid Emailzuser@example.compasswordzTPassword (must be at least 8 characters and meet Django's password validation rules)zValid Passwordsecurepass123	password2z+Password confirmation (must match password)zValid Password Confirmation
first_nameFz8User's first name (optional, max length: 150 characters)zValid First NameJohn	last_namez7User's last name (optional, max length: 150 characters)zValid Last NameDoegenderz=User's gender (M for male, F for female, N for not specified)zValid GenderM
birth_datez1User's birth date in YYYY-MM-DD format (optional)zValid Birth Datez
1990-01-01
birth_timez,User's birth time in HH:MM format (optional)zValid Birth Timez12:00	twin_typezTUser's twin type (0 for not a twin, 1 for elder twin, 2 for younger twin) (optional)zValid Twin Type
father_dobzPFather's birth date in YYYY-MM-DD format (required if twin_type is 1) (optional)zValid Father DOBz
1960-01-01
mother_dobzPMother's birth date in YYYY-MM-DD format (required if twin_type is 2) (optional)zValid Mother DOBz
1965-01-01zUser successfully registeredSuccess Responser   r   r   r   r   idr   r   r   r   r   profilez'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...useraccess_tokenrefresh_tokenresponser   r   zBad request (validation error)Validation ErrorzThis field is required.)zThis password is too common.Password fields didn't match.z"This password is entirely numeric.zEnter a valid email address.z/Father's birth date is required for elder twin.z+This field is required when twin_type is 1.z+This field is required when twin_type is 2.)r   r   r   r   r   r   r   r   r   )     zValid Registration)r   r   r   r   r   r   r   r   )r   request_only)r   rn   
parameters	responsesr   c                       e Zd ZeZegZd Zy)RegisterViewc           	         | j                  |j                        }|j                  d       |j                         }t	        j
                  |      }t        |j                        }i }	 t        j                  j                  |      \  }	}
|	j                  |	j                  |	j                  |	j                  |	j                  d}| j#                  |j                        }t%        |j&                  |j(                  |j*                  |j,                  |j.                  |d|t        |      dt0        j2                  |      S # t         $ r Y w xY w)	NdataTraise_exceptionr   r   )r   r   r   r   r   r   r   )r   headers)get_serializerr   is_validsaver   for_userstrr   rU   objectsget_or_creater   r   r   r   r   	Exceptionget_success_headersr   r   r   r   r   r   r   HTTP_201_CREATED)rm   rn   argskwargs
serializerr   refreshr   profile_datar   createdr   s               ro   createzRegisterView.create  s6   ((gll(;
D1  ''-7//0 	*22@@d@KGW%00%00$..%00%00L **:??;gg"oo!^^' ) \
 ))7< 	<  		s   ,AE 	EEN)rq   rr   rs   r$   serializer_classr
   r    r   ru   rv   ro   r   r      s    X 2"%<rv   r   zLogin user and get JWT tokens   objectstringzJWT refresh token)r   r   zJWT access tokenr   access)r   
properties)r   r   c                       e Zd ZefZeZd Zy)	LoginViewc                    | j                  |j                        }|j                  d       |j                  d   }|j                  d   }	 t        j
                  j                  |      }|j                  |      rNddlm	}  |||       t        j                  |      }t        t        |      t        |j                        d	      S |j                  d
      r	 t        j
                  j                  |dd        }|j                  |      rNddlm	}  |||       t        j                  |      }t        t        |      t        |j                        d	      S 	 t        ddit"        j$                        S # t        j                   $ r Y 2w xY w# t        j                   $ r |j                  d
      r	 t        j
                  j                  |dd        }|j                  |      rPddlm	}  |||       t        j                  |      }t        t        |      t        |j                        d	      cY S n# t        j                   $ r Y nw xY wt        ddit"        j$                        cY S w xY w)Nr   Tr   r   r   r   r   fire_jwt_authenticated_signalr   60r#   errorInvalid credentialsr   )r   r   r   validated_dataUserr   rf   check_passwordmain.signalsr  r   r   r   r   r   
startswithDoesNotExistr   HTTP_401_UNAUTHORIZED)rm   rn   r   r   r   r   r  r   s           ro   postzLoginView.post  sT   ((gll(;
D1))'2,,Z89	<<##%#0D""8,F-dG<&//5"7|!'"6"67!   %<<++%)+<D**84N5dGD"."7"7"=''*7|&)'*>*>&?)    5 /033  ((     	%<<++%)+<D**84N5dGD"."7"7"=''*7|&)'*>*>&?)    5 ((  /033 %	si   A>F F BE? !F ?FF FF $I;=BI>I;I;II;I I;:I;N)rq   rr   rs   r
   r    r(   r   r  ru   rv   ro   r  r    s     #&@rv   r  zLLogin for temporary users - migrates temp user data to existing user accountTempLoginRequestzPhone number for loginry   zPassword for login)r   r   r   fieldsTempLoginResponsezSummary of transferred datazSuccess messager   r   transfer_summarymessagez%Invalid credentials or user not found)r   z(User not authenticated as temporary user)r   r     z'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...   )bazi_recordsliuyao_recordsowner_flag_clearedKLogin successful. Your temporary data has been transferred to your account.r   r   response_only)r   rn   r   r   c                   &    e Zd ZdZefZegZeZ	d Z
y)TempLoginViewz
    Login endpoint for temporary users.
    Migrates temporary user's data to existing user account and returns new JWT tokens.
    c                    |j                   j                  st        ddit        j                        S t        |j                   dd      st        ddit        j                        S | j                  |j                        }|j                  d	       |j                  d
   }|j                  d   }|j                   }	 	 t        j                  j                  |      }|j!                  |      s|j                  d      rU	 t        j                  j                  |dd        }|j!                  |      st        ddit        j                        S |}nt        ddit        j                        S t        |dd      rt        ddit        j"                        S ddlm}m}	  |||      }
	  |	|       ddlm}  |||       t1        j2                  |      }t        t5        |j6                        t5        |      |
ddt        j8                        S # t        j                  $ r |j                  d      r]	 t        j                  j                  |dd        }nW# t        j                  $ r" t        ddit        j                        cY cY S w xY wt        ddit        j                        cY S Y w xY w# t        j                  $ r  t        ddit        j                        cY S w xY w# t*        $ r Y Iw xY w# t:        $ r3}t        ddt5        |       it        j<                        cY d }~S d }~ww xY w)Nr  5Authentication required - temporary user token neededr	  is_temporary_userF)This endpoint is only for temporary usersr   Tr   r   r   r  r  r#   r  z.Cannot login to another temporary user accountr   )transfer_user_datacleanup_temp_userr  r  r  zLogin failed: )r   is_authenticatedr   r   r  rl   HTTP_403_FORBIDDENr   r   r   r
  r  r   rf   r  r  r  HTTP_400_BAD_REQUESTaccounts.utilsr'  r(  rk   r  r  r   r   r   r   HTTP_200_OKr   HTTP_500_INTERNAL_SERVER_ERROR)rm   rn   r   r   r   	temp_usertarget_useralt_userr'  r(  r  r  r   r}   s                 ro   r  zTempLoginView.post6  s6   ||,,QR33 
 w||%8%@EF00  ((gll(;
D1))'2,,Z8LL	P	"ll..U.;$ --h7##D)#'<<#3#3%)#3#D'66x@#+!(*? @'-'C'C$  '/ $ "78%;;  {$7?NO!66  M1)[I!), C)+w? #++K8Gg223w<$4h	
 ((* *C $$ ##D)&*ll&6&6U12Y&6&G,, '$&;<#)#?#?   $ "78%;;  $	8  ,, '$&;<#)#?#?  4  $  	N3q6(34<< 	s    H ""L AK K L 6)L  L 2K7 :AL $J>?#I#"J>#0JJ>L J J>8L :L =J>>L 0K41L 3K44L 7	L L LL 	M(L>8M>MN)rq   rr   rs   rt   r
   r    rH   r!   r3   r   r  ru   rv   ro   r"  r"    s'    P #?@*frv   r"  zaConvert temporary user to permanent user - only phone & email required, all other fields optionalTempRegisterResponsez*JWT access token (new if password updated)z+JWT refresh token (new if password updated))r   r   r   r  zValidation errorszAuthentication required)r   r     r  zMinimal Registration)r   r   r   r   r   z*Full Registration with All Optional Fieldsnewpassword123z12:00:00z
1965-05-15z
1967-03-22)r   r   r   r   r   can_regenerate_aiz,Registration with Profile Only (No Password)
9876543210zprofile-user@example.comJaneSmithFz
1985-12-25z09:30:00z
1960-08-10)r   r   r   r   )r   r   r   r   r   r   z"Success Response with Full Profile   zOAccount successfully converted to permanent user with new authentication tokensz(Success Response without Password Update   0Account successfully converted to permanent userr   r  c                   &    e Zd ZdZefZegZeZ	d Z
y)TempRegisterViewz
    Convert temporary user to permanent user.
    Only phone & email are required - other fields are optional.
    Returns updated JWT tokens if password is changed.
    c           
      "   |j                   j                  st        ddit        j                        S t        |j                   dd      st        ddit        j                        S |j                   }| j                  |j                  d|i      }|j                  d	
       	 |j                  }|j                  dd       }|j                  dd       }|j                  dd        |j                         D ]  \  }}t        |||        d|_        d}	|r|j                  |       d	}	|j!                          |rmddlm}
 |
j&                  j)                  |      \  }}|j                         D ]  \  }}|	t        |||        |j!                          |j+                          |	r6t-        j.                  |      }t1        |j2                        }t1        |      }nd }d }	 t5        |      }|j                  }|dd}|	r||d<   ||d<   |dxx   dz  cc<   t        |t        jD                        S # t6        $ r}|j+                          	 t5        |      }|j                  }n\# t6        $ rP}|j8                  |j:                  |j<                  |j>                  |j@                  |jB                  d d}Y d }~nd }~ww xY wY d }~d }~ww xY w# t6        $ r3}t        ddt1        |       it        jF                        cY d }~S d }~ww xY w)Nr  r$  r	  r%  Fr&  r   )r   contextTr   r   r   r   r   rT   r   r   r=  r>  r   r   r  z with new authentication tokenszRegistration failed: )$r   r)  r   r   r  rl   r*  r   r   r   r
  popitemssetattrr%  set_passwordr   accounts.modelsrU   r   r   refresh_from_dbr   r   r   r   r%   r   r   r   r   r   r   r   r-  r.  )rm   rn   r/  r   r
  r   r   fieldr   password_updatedrU   r   r   r   r   r   user_serializer	user_datar}   e2response_datas                        ro   r  zTempRegisterView.post  s    ||,,QR33 
 w||%8%@EF00 
 LL	 ((gllVYDW(X
D1U	'66N)--i>L%))*d;H{D1 !/ 4 4 6u	5%0 !7 +0I'  %&&x0#' NN 7#.#6#6#D#D)#D#T $0$6$6$8LE5(6 %9  ))+  &//	:"7#7#78 #G  $ $"0";+00	( "MM  *6h'+8i(i(,MM(M&2D2DEE;  ))+&4Y&?O / 4 4I  
 (ll!*!*&/&:&:%.%8%8"+"2"2#'!I
>  	1#a&:;<< 	s{   &CK ;A+K 'H7 >8K 7	K K
I)(K
)	K2AJ=8K
=KK
K 
KK 	L(L	L	LN)rq   rr   rs   rt   r
   r    rH   r!   r4   r   r  ru   rv   ro   r@  r@    s'    f
 #?@-irv   r@  z)Refresh JWT token and track user activityTokenRefreshRequestr   zRefresh tokenTokenRefreshResponsezNew access tokenz'New refresh token (if rotation enabled))r   r   z Invalid or expired refresh tokenr   r3  )r   rn   r   c                       e Zd ZdZefZd Zy)CustomTokenRefreshViewz
    Custom JWT token refresh view that automatically tracks user activity.
    
    This extends the default Simple JWT token refresh functionality to fire
    activity tracking signals whenever a refresh token is used.
    c                    ddl m} ddlm} ddlm}m} ddlm} |j                  j                  d      }|st        ddit        j                  	      S 	  ||      }|j                  j                  d
      }	|	r*	 t        j                   j                  |	      }
 ||
|        |       }||_        d |_        |j+                  |      }|S # t        j"                  $ r  t        ddit        j$                  	      cY S w xY w# |$ r0}t        dt-        |      it        j$                  	      cY d }~S d }~wt.        $ r?}t0        j3                  d|        t        ddit        j4                  	      cY d }~S d }~ww xY w)Nr   )TokenRefreshViewr   )InvalidToken
TokenErrorr  r   r  zRefresh token is requiredr	  user_idr   User not foundzToken refresh error: zToken refresh failed)rest_framework_simplejwt.viewsrU  rest_framework_simplejwt.tokensr   #rest_framework_simplejwt.exceptionsrV  rW  r  r  r   rf   r   r   r+  payloadr  r   r  r  rn   format_kwargr  r   r   loggerr  r.  )rm   rn   rU  r   rV  rW  r  r   r   rX  r   refresh_viewr   r}   s                 ro   r  zCustomTokenRefreshView.post  sh   C@P>((35622 
$	"=1Goo)))4G
<<++w+7D 2$@ ,-L#*L (,L%#((1HO (( # "23%;;   	#a&!33   	LL04501<< 	sT   %D <)C %'D 0D =D ?D  D E>%D3-E>3E>?4E93E>9E>Nrq   rr   rs   rt   r
   r    r  ru   rv   ro   rS  rS    s    ( #1rv   rS  zRequest password reset emailr  c                       e Zd ZefZeZd Zy)PasswordResetRequestViewc           	         | j                  |j                        }|j                  d       	 	 t        j                  j                  |j                  d         }ddlm	} dd	l
m} dd
lm} ddlm}  |d|j                   i      }|j                         rV|j#                         |t$        j&                  dd|d d d}	 |j(                  di |	 t+        ddit,        j.                        S y # t        j                  $ r0 t        j                  j                  |j                  d         }Y w xY w# t        j                  $ r  t+        ddit,        j0                        cY S w xY w)Nr   Tr   r   )r   r  r   )PasswordResetFormr   )get_current_site)CustomPasswordResetFormz"accounts/password_reset_email.htmlz#accounts/password_reset_subject.txt)	use_httpstoken_generator
from_emailemail_template_namesubject_template_namern   html_email_template_nameextra_email_contextr  z#Password reset email has been sent.r	  r  z.No user found with this email or phone number.ru   )r   r   r   r  r   rf   r
  r  django.contrib.auth.formsrf  django.contrib.auth.tokensr   django.contrib.sites.shortcutsrg  accounts.formsrh  r   	is_securer   DEFAULT_FROM_EMAILr   r   r   r-  HTTP_404_NOT_FOUND)
rm   rn   r   r   rf  r   rg  rh  formoptss
             ro   r  zPasswordResetRequestView.post  s\   ((gll(;
D1(	R||''j.G.G.P'Q DJG ?*GTZZ+@AD}} ")!2!2!4'>"*"="=+O-R&04+/	 		!D! EF!--   $$ R||''j.G.G.P'QR@    	JK00 	s1   -C1 BD7 1A D41D7 3D44D7 70E*)E*N)rq   rr   rs   r
   r    r&   r   r  ru   rv   ro   rd  rd    s     #5,rv   rd  z!Confirm password reset with tokenc                       e Zd ZegZeZd Zy)PasswordResetConfirmViewc                    | j                  |j                        }|j                  d       |j                  d   }|j                  d   }|j                  d   }	 ddlm} dd	lm}  ||      j                         }t        j                  j                  |
      }	 |j                  |	|      r>|	j                  |       |	j                          t        ddit         j"                        S t        ddit         j$                        S # t&        t(        t*        t        j,                  f$ r  t        ddit         j$                        cY S w xY w)Nr   Tr   uidb64tokenr   r   )urlsafe_base64_decoder   pkr  z%Password has been reset successfully.r	  r  zInvalid or expired token.)r   r   r   r
  django.utils.httpr~  rq  r   decoder  r   rf   check_tokenrF  r   r   r   r-  r+  rj   rk   OverflowErrorr  )
rm   rn   r   r|  r}  r   r~  r   uidr   s
             ro   r  zPasswordResetConfirmView.post  s(   ((gll(;
D1**84))'2,,Z8	h?J'/668C<<##s#+D 3&224?!!(+		,S T]c]o]opp*E FvOjOjkk:}d6G6GH 	hW&AB6KfKfgg	hs   BD 0D A EEN)rq   rr   rs   r
   r    r'   r   r  ru   rv   ro   rz  rz    s     #5hrv   rz  z&Change password for authenticated userzPassword changed successfullyarrayr   z"Current password validation errors)r   rD  r   zNew password validation errors)current_passwordnew_passwordz&Unauthorized - Authentication required)r   r   r3  zRequest Examplezcurrent-passwordznew-password123)r  r  new_password2Password changed successfully.200r   r   r   status_codeszCurrent password is incorrect.r   400c                       e Zd ZegZeZd Zy)PasswordChangeViewc                    | j                  |j                        }|j                  d       |j                  }|j	                  |j
                  d          |j                          t        ddit        j                        S )Nr   Tr   r  r  r  r	  )
r   r   r   r   rF  r
  r   r   r   r-  )rm   rn   r   r   s       ro   r  zPasswordChangeView.postg  sx    ((gll(;
D1 ||*33NCD		 89%%
 	
rv   N)rq   rr   rs   r   r    r1   r   r  ru   rv   ro   r  r  3  s    b **/
rv   r  zView and update user profilez+User profile retrieved/updated successfullyc                       e Zd ZefZeZd Zy)UserProfileViewc                 .    | j                   j                  S N)rn   r   rm   s    ro   
get_objectzUserProfileView.get_object  s    ||   rv   N)rq   rr   rs   r   r    r%   r   r  ru   rv   ro   r  r  v  s    H *+%!rv   r  dispatch)r   c            "          e Zd ZeegZegZ edde	e	ge
 ed edddidg      g	      d
 eddddddddddddddddddddddddddddddddddddddddddd ddddddddddddddddddddd!didd dddddddddddddddddddddd"d#ddddd ddd$d%d&d'gd(d)gd*ii d%d+dd,d-d.d/g0      g1      d2        Z ed3d4e	 ed5 ed6dddddddddddddddddddddddddddd$d#ddddd ddd$d%i i d%d+dd,d7d8d/g      g	       ed9 ed:dddddddddddddddddddddddddddd$d#ddddd ddd$d%i i d%d+dd,d;d<dd=d=d>d?	d@g      g	       edA edddidg      g	      dB edCdDddEdddFdGdGdGdHdGdIdJd.K       edLdMd.dIdGdIdHdIdGdNd.K      gO      dP        ZyQ)RLiuyaoCalculatorAPIViewzCalculate Liuyao resultu%  
This endpoint calculates the Liuyao (六爻) divination result based on the provided input data.

**Response Structure:**

- `gua`: Main object containing all divination results
  - `question`: The question asked for divination
  - `bz`: Bazi (八字) information containing time pillars
    - `hour`, `day`, `month`, `year`: The four pillars with heavenly stems (`g`) and earthly branches (`e`)
    - `date`: Formatted date and time
    - `empty`: Empty positions for divination
  - `god6`: The Six Gods (六神) determination
  - `ly`: Liuyao structure
    - `ogua`: Original hexagram data
      - `gua`: Index of one of the 64 gua
      - `clash6`, `harmony6`: Six relationships flags
      - `palace`: Palace information for divination
        - `palace`: The palace where the gua belongs to, 0 (乾) to 7 (兑)
        - `pos`: The gua position in the palace, from 1 to 8
        - `shi`: The shi(世) position (1 to 6)
        - `yin`: The yin(应) position (1 to 6)
      - `g`, `e`: Heavenly stem and earthly branch for each line
      - `q`: The 6 relationship flags (六亲), 0: "兄弟", 1: "子孙", 2: "妻财", 3: "官鬼", 4: "父母"
      - `hg`, `he`, `hq`: Similar to `g`, `e` and `q`, only this is used for hidden gods (伏神); `hq` will only list out the yao that has hidden god, for eg: `"hq": {"y6": 1}` indicates the 6th yao is a yao with hidden god of "子孙"
      - `hqf`: while 'hq' only list out 六亲 that is not appear in the main gua, hqf (hq full) will list out the full hidden 六亲
      - `gb`: gua body (卦身), value range from 0 (子) to 11 (亥)
      - `sb`: shi body (世身), value range from 1 to 6 (which yao)
      - `yao`: Structure of 6 lines (yin/yang)
      - `changeyao`: Information on which line(s) are changing, 1 indicates change yao, 0 indicates non-changing yao
    - `cgua`: Changed hexagram data (same structure as ogua)
  - `rel`: Relationships between elements
    - `ogua`: Relationships in original hexagram
    - `cgua`: Relationships in changed hexagram

Additional fields may be present in the complete response.
zError messager   r  zInvalid date or time formatr  r   r   r  r   r   r   zComplete Response Exampleztest question      r|   r}   r#      z2025-03-07 21:48:46   	   )01)r   r   r   r   rA   r   r      F   r  )r   r   r   r   )r   r   r   r   r   r      
      r   )r   r   r   r   r|   r}   qhghehqhqfgbsbr   r   "   )r   r   r   r   )r   r   q1zcs12-3counterzcs12-7r   r   r   rI   r   r   r   z0|0|0|000|0|1r   r   r   Tr  r  summaryr   rn   r   r   r   c                    t        |j                        }|j                  d       |j                  }|j	                  dd      }	 |r$t        j                  t        j                        }nv|j	                  d      }|j	                  d      }	|j	                  d      }
|j	                  d	d
      }t        t        |j                  d            \  }}t        ||	|
||      }t        j                  |j                  |j                  |j                  |j                   |j"                        }|j%                  d      |d<   t        j&                  |d   d   |d   d         |d<   t)        j*                  |d   d         }|j	                  dd      }|j	                  dd      }|j	                  dd      }|j	                  dd      }|j	                  dd      }|j	                  dd      }t)        j,                  ||||||      }t)        j.                  ||      }|j	                  dd      }|||||d}|||j	                  dd       d|j	                  dd       d|j	                  dd       d|j	                  dd       d|j	                  dd       d|j	                  dd       d}t1        |t2        j4                        S # t6        $ r0}t1        dt9        |      it2        j:                        cY d }~S d }~ww xY w)Nr   Tr   usecurFr   r   r   time00:00:%Y-%m-%d %H:%M:%SrA   r|   r}   r   r    r   r   r   r   r   r   r  |r  r	  r  )r+   re   r   r
  rf   r@   nowbazigMYTimezonemaprd   splitgetDateTimeGodEarthStemr   r   r   r   minutestrftimecalcEarthEmptyliuyao_utilcalc6Godcalc6YaocalcRelationshipr   r   r-  r   r   r+  )rm   rn   r   r   r   r
  r  date_objr   r   r   time_strhoursminutesrI   r   r   r   r   r   r   r   r   r   r   gua_dataresultr}   s                               ro   rf   zLiuyaoCalculatorAPIView.get  sY   j 0W5I5IJ
D1#22  ##He48	S#<<(8(89%))&1&**73$((/)--fg>!$S(..*=!>w#D%eWE --x~~x||X]]HOOB "**+>?BvJ--binbinMBwK ''5	#7D  ##D"-B##D"-B##D"-B##D"-B##D"-B##D"-B%%b"b"b"=B ..r26C &))*b9H$H  $(,,T267q9K9KDRT9U8VVWXfXjXjkoqsXtWuuv  xF  xJ  xJ  KO  QS  xT  wU  UV  We  Wi  Wi  jn  pr  Ws  Vt  tu  vD  vH  vH  IM  OQ  vR  uS  TF F6+=+=>> 	SWc!f-f6Q6QRR	Ss   JK 	L%K?9L?Lz Calculate and save Liuyao resultu?  
This endpoint calculates the Liuyao (六爻) divination result and optionally saves it to the database.

**Authentication Behavior:**
- **Public users**: Calculation only (results returned but not saved)
- **Authenticated users**: Calculation and automatic saving to database

**Request Body:** All calculation parameters should be provided in JSON format instead of query parameters.

**Response Structure:** Same as GET method, but with additional fields for authenticated users:
- `id`: Record ID (only for authenticated users who saved the divination)
- `created_by`: User ID who created the divination (only for authenticated users)
- `created_at`: Creation timestamp (only for authenticated users)
- `updated_at`: Last update timestamp (only for authenticated users)
- `message`: Status message about calculation and saving
z/Calculation completed (public user - not saved)zPublic User Responsez,Liuyao divination calculated (login to save))r   r   r   r  z4Calculation completed and saved (authenticated user)zAuthenticated User Responser_   $3fa85f64-5717-4562-b3fc-2c963f66afa62025-03-07T15:23:38.757Z6Liuyao divination calculated and saved to your account)	r   r   r   r   uuid
created_by
created_at
updated_atr  201Validation error)r   r   r   zCalculate and Save Requestz!Will my business venture succeed?  z15:30r  000r  )r   r  r   r   r   r  r   r   r   r   r   r   r4  zCalculate Current Time Requestz!What should I do about my career?)r   r  r   r   r   r   r   r   )r  r   rn   r   r   c                 	   t        |j                        }|j                         s't        d|j                  it
        j                        S |j                  }|j                  dd      }	 |r$t        j                  t        j                        }nv|j                  d      }|j                  d      }	|j                  d      }
|j                  d	d
      }t        t        |j                  d            \  }}t        ||	|
||      }t        j                   |j"                  |j$                  |j&                  |j(                  |j*                        }|j-                  d      |d<   t        j.                  |d   d   |d   d         |d<   t1        j2                  |d   d         }|j                  dd      }|j                  dd      }|j                  dd      }|j                  dd      }|j                  dd      }|j                  dd      }t1        j4                  ||||||      }t1        j6                  ||      }|j                  dd      }|||||d}|||j                  dd       d|j                  dd       d|j                  dd       d|j                  dd       d|j                  dd       d|j                  dd       d}|j8                  j:                  r@t=        j>                  |      }tA        |j8                  d      xr |j8                  jB                  }tD        jF                  jI                  ||j8                  |||||||||d   |      }|jJ                  |d<   tM        |jN                        |d <   |j8                  jJ                  |d!<   |jP                  |d"<   |jR                  |d#<   d$|d%<   tA        |d&      r;tA        |j8                  d      r%|j8                  jB                  r|jT                  |d'<   t        |t
        jV                        S d(|d%<   t        |t
        jX                        S # tZ        $ r0}t        dtM        |      it
        j                        cY d)}~S d)}~ww xY w)*z?Calculate Liuyao result and optionally save it to the database.r   r  r	  r  Fr   r   r   r  r  r  r  rA   r|   r}   r   r   r  r   r   r   r   r   r   r  r  r  r%  r   )r  r   qdater   r   r   r   r   r   r   r   created_by_temp_userr   r  r  r  r  r  r  temp_user_tokenr/  z<Liuyao divination calculated (not saved - no authentication)N).r+   r   r   r   errorsr   r+  r
  rf   r@   r  r  r  r  rd   r  r  r   r   r   r   r  r  r  r  r  r  r  r   r)  rL   getUUIDhasattrr%  rD   r   r   r   r   r  r  last_modified_atr  r   r-  r   )rm   rn   r   r   r   r
  r  r  r   r   r   r  r  r  rI   r   r   r   r   r   r   r   r   r   r   r  r  
uuid_valueis_temp_user
liuyao_objr}   s                                  ro   r  zLiuyaoCalculatorAPIView.post\  s   H 0W\\B
""$Wj&7&78A\A\]]#22  ##He4a	S#<<(8(89%))&1&**73$((/)--fg>!$S(..*=!>w#D%eWE --x~~x||X]]HOOB "**+>?BvJ--binbinMBwK ''5	#7D  ##D"-B##D"-B##D"-B##D"-B##D"-B##D"-B%%b"b"b"=B ..r26C &))*b9H$H  $(,,T267q9K9KDRT9U8VVWXfXjXjkoqsXtWuuv  xF  xJ  xJ  KO  QS  xT  wU  UV  We  Wi  Wi  jn  pr  Ws  Vt  tu  vD  vH  vH  IM  OQ  vR  uS  TF ||,,"]]73
  'w||5HIlgllNlNl $^^22# "%)5 3 
   *}}t!$Z__!5v'1'9'9|$'1'<'<|$'1'B'B|$$\y! 7$5677<<Qd;ejqjvjv  kI  kI*1*A*AF;'v/F/FGG %cy!v/A/ABB 	SWc!f-f6Q6QRR	Ss%   -N>Q ,Q 	R%R :R RN)rq   rr   rs   rH   rE   r!   r
   r    r   r+   r   r   r   rf   r  ru   rv   ro   r  r    sa   ?AVW")#H +&
 2 +"/#%B ',W
  0 %4*+"$5)*#4+,1%5*+!$4$9+,1%5 !" (**/,156qST*U,-QaqPQYZ%[,-QarQRZ[%\,-QaqPQYZ%[-.aqQRZ[&\-.aqQS[\&]'+Qi./qRS[\']&'&'./qRS[\']45QaqXYab-c%$ (**/,156qST*U	%%6 !%,4:.7-B'"% %' I-\ !0*a1d ##Wk68
srfASgrfASF 2  + M"3 -<23",=12+<341-=23!,<,A341-='" )* 022749=>qQR[\2]	-& 022749=>qQR[\2]	-&'" -/,.("3$< )8#2'UC"F ',WK&(+X !R": -<23",=12+<341-=23!,<,A341-='" )* 022749=>qQR[\2]	-& 022749=>qQR[\2]	-&'" -/,.("3$< )8#2"%$J*+*D*D'_M'P ',WU+-0b !."/#%B ',W}j
X 1 C# # "!$ 5 C"	 "'!
}`BmSC`BmSrv   r  zList all Liuyao objectszRetrieve a list of all Liuyao objects created by the current user. Results can be sorted by any field using the 'ordering' query parameter and paginated using 'page' and 'page_size' parameters.orderinga   Comma-separated list of fields to order by. Prefix with '-' for descending order. Available fields: id, qdate (question date, the date that is used to calculate the bazi and liuyao result), created_at, last_modified_at. If not specified, it will be ordered by created_at descending order.z Sort by creation date descending-created_atz=Sort by question date descending then created date descendingz-qdate,-created_at)r   r   r   r   r   r   pagezPage number (1-based)z
First pager^   zKNumber of items per page. If not specified, uses the default from settings.z10 items per pager  z25 items per pagerc   manyzList of Liuyao objectszLiuyao List Exampler   r_   r  r  r  r  r   zTest questionr   r  r   r   r   r  r   r   r  readingzInterpretation textfeedbackzUser feedbackr   zsSee the /api/liuyao/calc endpoint for the complete data structure. This field contains the full calculation result.r  r  r   )r   r   r  r   r   r   zRetrieve a Liuyao objectz2Get details of a specific Liuyao object by its ID.zLiuyao object detailszLiuyao Detail Example)r  r   r   zCreate a Liuyao objectzmAdd a new Liuyao object to the system. The user field is automatically populated with the authenticated user.zCreate Liuyao Examplez6The JSON data as returned when calling api/liuyao/calc)r  r   r   r   r   r   r   r   r  r  r   r   zCreated Liuyao objectzCreated Liuyao Example)r  r   rn   r   r   zUpdate a Liuyao objectzWUpdate an existing Liuyao object by its ID. The user field is automatically maintained.zUpdate Liuyao ExamplezUpdated questionzUpdated interpretationzUpdated feedback)r  r   rn   r   z Partially update a Liuyao objectzaPartially update an existing Liuyao object by its ID. Only provide the fields you want to update.zPartial Update ExamplezUpdated question onlyzUpdated reading only)r   r  zDelete a Liuyao objectz*Delete a specific Liuyao object by its ID.)r  r   )listretriever   updatepartial_updatedestroyc                        e Zd Zej                  j                         ZeZe	e
gZegZej                  gZg dZdgZeZd Zd Zd Z fdZ xZS )LiuyaoViewSet)r   r  r  r  r  c                 6    | j                   dv rt        S t        S )zUUse LiuyaoRequestSerializer for create/update operations to allow optional data field)r   r  r  )rV   r*   r)   r  s    ro   get_serializer_classz"LiuyaoViewSet.get_serializer_classM  s    ;;@@**rv   c                    t        j                  | j                        }| j                  j                  }|j                  r|j
                  nd }|j                  xr t        |d      xr |j                  }|j                  |||       y )Nr%  )r  rX  r  )	rL   r  rn   r   r)  r   r  r%  r   )rm   r   r  r   rX  r  s         ro   perform_createzLiuyaoViewSet.perform_createS  sr    ]]4<<0
 ||  !22$'' ,,n?R1SnX\XnXn 	Z|\rv   c                     | j                   j                  }|j                  r t        j                  j                  |      S t        j                  j                         S )zZ
        Filter queryset to return only records belonging to the requesting user.
        r   )rn   r   r)  rD   r   filternone)rm   r   s     ro   get_querysetzLiuyaoViewSet.get_queryseta  sI     ||    >>((d(33 >>&&((rv   c                     t         |          }| j                  j                  }| j                  dk(  r1|j
                  st        d      |j                  |k7  rt        d      |S )zLOverride to add additional permission checking for individual record access.r  z%Authentication required for deletion.z1You do not have permission to delete this object.)superr  rn   r   rV   r)  rY   )rm   objr   	__class__s      ro   r  zLiuyaoViewSet.get_objectl  sa    g "||   ;;)#((&'NOO xx4&'Z[[
rv   )rq   rr   rs   rD   r   allquerysetr)   r   rH   rE   r!   r
   r    r   OrderingFilterfilter_backendsordering_fieldsr  r]   pagination_classr  r  r   r  __classcell__r  s   @ro   r  r  m  sl    n ~~!!#H'?AVW"--.OGOH1 ]	) rv   r  zList all Bazi chartszRetrieve a list of all Bazi charts created by the current user. Results can be sorted by any field using the 'ordering' query parameter and paginated using 'page' and 'page_size' parameters.ownerzdFilter by owner status. Set to 'true' to get only your own record, 'false' to get all other records.zGet own recordtruezGet other recordsfalser   z/Filter by name (case-insensitive partial match)zFilter by namezComma-separated list of fields to order by. Prefix with '-' for descending order. Available fields: name, birth_date, created_at, updated_at. If not specified, it will be ordered by created_at descending order.z1Sort by birth date descending then name ascendingz-birth_date,namezList of Bazi chartszBazi List ExamplezExample PersonzExample notesz2023-01-01T12:00:00Zr  godearthr  r   r   r   r   zFRefer the response structure in the /api/bazi/bazi (POST) for details.)r   r   r   r   r   notesr  r  bazi_resultnumber_resultr  zRetrieve a Bazi chartzGet details of a specific Bazi chart by its ID. The response includes the fully calculated Bazi chart details. Refer the response structure in the /api/bazi/bazi (POST) for details.z>A unique integer value identifying the Bazi chart to retrieve.r   r   r   r   r   )r     zBazi Detail Example'   zqSee the /api/bazi/bazi (POST) endpoint for the complete data structure. This field contains the full bazi result.z|See the /api/bazi/bazi (POST) endpoint for the complete data structure. This field contains the full number analysis result.)r   r   r   r   r   r  r  r  r  r  numberr  r   r   r   r   z*Calculate and optionally save a Bazi chartu  
Calculate a Bazi (八字) chart based on the provided birth information.

**Response Structure:**

- `name`: Person's name
- `gender`: Gender (M/男, F/女, N/不指定)
- `birth_date`: Birth date in YYYY-MM-DD format
- `birth_time`: Birth time in HH:MM:SS format (optional)
- `notes`: Additional notes about this chart (optional)
- `result`: The calculated Bazi chart with detailed analysis
  - `year`, `month`, `day`, `hour`: The four pillars with:
    - `god`: Heavenly stem (天干) in Chinese character
    - `earth`: Earthly branch (地支) in Chinese character
    - `earth_element`, `god_element`: Element of the earthly branch (wood/fire/earth/metal/water)
    - `ten_god`: Ten Gods (十神) relationship to day stem
    - `hidden_gods`: Array of hidden gods (藏干) with:
      - `god`: Hidden heavenly stem
      - `ten_god`: Ten Gods relationship to day stem
    - `empty`: Whether this pillar is in an empty position (空亡)
    - `god_idx`, `earth_idx`: Numeric indices for stems and branches
    - `star_luck_idx`, `self_luck_idx`: Numeric indices for 12长生, star_luck is for the 星运, while self_luck is for the 自坐
      - The 12长生:
        ```
        0: "长生", 1: "沐浴", 2: "冠带", 3: "临官", 4: "帝旺", 5: "衰", 6: "病", 7: "死", 8: "墓", 9: "绝", 10: "胎", 11: "养"
        ```
  - `sha_gods`: Sha Gods (神煞) information for each pillar:
    - `year`, `month`, `day`, `hour`: The four pillars with:
        - `gods` with:
            - `name`: Name of the sha god
            - `kindness`: Whether the god is auspicious (0), inauspicious (1) or neutral (2)
  - `sha_god_locations`: Sha Gods (神煞) location information:
    - `gid`: the sha god identifier
        - List of god id:
            ```
            0: "tian_yi" (天乙), 1: "tian_de" (天德), 2: "yue_de" (月德), 3: "taiji" (太极), 4: "lushen" (禄神), 5: "wenchang" (文昌), 6: "yima" (驿马), 7: "taohua" (桃花), 8: "hongluan" (红鸾), 9: "tianxi" (天喜), 10: "jiangxing" (将星), 11: "fuxing" (福星), 12: "huagai" (华盖), 13: "jiesha" (劫煞), 14: "wangshen" (亡神), 15: "yangren" (羊刃), 16: "zaisha" (灾煞), 17: "guchen" (孤辰), 18: "guashu" (寡宿), 19: "yinchayangcuo" (阴差阳错), 20: "tianyi" (天医), 21: "feiren" (飞刃), 22: "gejiao" (隔角), 23: "shieda" (十恶大败), 24: "liuxiu" (六秀), 25: "jinyu" (金舆)
            ```
    - `god`: the sha god shortname
    - `loc`: the sha god location, this will map to the god stem (0-9) / earth branch (0-11) index.
        - List of god stem / earth branch:
            ```
            God stem: 0: "甲", 1: "乙", 2: "丙", 3: "丁", 4: "戊", 5: "己", 6: "庚", 7: "辛", 8: "壬", 9: "癸"
            Earth branch: 0: "子", 1: "丑", 2: "寅", 3: "卯", 4: "辰", 5: "巳", 6: "午", 7: "未", 8: "申", 9: "酉", 10: "戌", 11: "亥"
            ```
        - The information on the type of location (either god or branch) will be available in the `ge`. This can be multiple items, and each item will map accordingly to the `ge`, `pillar` and `type`. For eg, if `loc` = {0, 8}, `ge` = {'e', 'e'}, `pillar` = {'d', 'y'} it means the first item 0 is 子 (`ge` = 'e' means earth branch) on the day pillar, while the second item 8 is 申 on the year pillar.
    - `ge`: the type of location, either god stem (g) or earth branch (e)
    - `pillar`: indicates the location of where the sha god exists, it can be either y/m/d/h, indicating year/month/day/hour.
    - `upc`: unique pillar count, if the sha god exists in more than pillar, for eg in both the day and year pillar, then this `upc` = 2.
    - `type`: the type of sha god, currently only applies to tian_yi, where y1 = 阳, y0 = 阴.
  - `life_number`: the life number of the person
  - `life_palace`: the life palace of the person
- `number`: The numerology profile based on the date of birth
    - `formula`: Birthdate formula string (e.g., "01011990")
    - `number`: The main life number
    - `number_5e`: Five Element name of the number (wood/fire/earth/metal/water)
    - `number_5el`: Ordered five element cycle starting from the number's element, so that it starts from the number_5e, the cycle is wood(1)>fire(2)>earth(3)>metal(4)>water(5), so if the number_5e = "metal", the number_5el will become 4,5,1,2,3
    - `head`: First digit (起头数) of the number string
    - `mingpan`: 命盘, dictionary with keys `a` to `x`, `north`, `south`, `east`, `west`.
    - `have_nos`: List of present numbers
    - `no_nos`: List of missing numbers
    - `zodiac`: Zodiac index (0–11)
    - `zodiac_boost`: Number boost from zodiac
    - `number_5ec`: Dict of element index (1–5) and their counts
    - `number_5es`: Ordered element counts starting from `number_5e`, if follow above eg where number_5e="metal", and the number_5es={4,3,2,0,3}, it means the user has metal=4,water=3,wood=2,fire=0,earth=3
    - `year_number`: Annual number
    - `year_5e`: Five Element of the year
    - `year_5el`: Ordered year element cycle
    - `year_5ec`: Year element counts by index
    - `year_5es`: Ordered year element counts
    - `liunian`: 流年数盘 (a to x, north & south)
    - `pairs81`: List of 13 strings mapping to:
        1. 事业基因
        2. 事业过程
        3. 事业过程
        4. 事业结果
        5. 感情基因
        6. 感情过程
        7. 感情过程
        8. 感情结果
        9. 人生基因
        10. 人生过程
        11. 人生过程
        12. 人生结果
        13. 隐藏禀赋
- `id`: Record ID (only for authenticated users who saved the chart)
- `created_by`: User ID who created the chart (only for authenticated users)
- `created_at`: Creation timestamp (only for authenticated users)
- `updated_at`: Last update timestamp (only for authenticated users)
- `message`: Status message about the calculation and saving status

Authentication behavior:
- Public users can calculate charts (results will be returned but not saved)
- Authenticated users automatically have their charts saved
)r   r   r   zCalculate Bazi Examplez
1970-01-01z
1971-12-31r   r   r   r   r   r   r   r  z Success Response (Authenticated)2025Nz
2025-02-03z22:11:00r     乙   巳u   火u   食神u   丙u   正财)r  ten_god   庚u   正印   戊u   正官r  )r  r  earth_elementr!  hidden_godsr   god_idx	earth_idx   寅u   木   甲u   伤官   癸u   卯u   比肩r  r  u   亥u   水u   壬u   劫财r  godsu   驿马)r   kindnessu   太极贵人u   天乙贵人u   亡神u   金舆u   福星u   将星u   文昌u   灾煞u   羊刃)r   r   r   r   sha_godsformula01011990r  	number_5efire
number_5el)r  r  r#   r  r  headmingpanabcdr}   fr|   hijklmnopr  )rstuvwxnorthsouthwesteasthave_nos)r#   r  r  r  no_nos)r  r  r  r  r  zodiaczodiac_boost6
number_5ec)r  2345
number_5es)r  r#   r  r  r  year_numberyear_5ewateryear_5el)r  r  r  r  r#   year_5ecr  year_5es)r  r  r  r   r  )	rC  rD  rE  rF  rG  rH  rI  rJ  rK  )112123r`  336191r_  911213rd  235134459426)liunianpairs81   z2025-03-08T10:30:28.790974Zz2025-03-08T10:30:28.790989Z/Bazi chart calculated and saved to your account)r   r   r   r   r  r  r  r   r  r  r  r  r  zSuccess Response (Public User)z%Bazi chart calculated (login to save))r   r   r   r   r  r  r  r  r  z.Invalid date format or missing required fieldsr  zUpdate a Bazi chartzUpdate an existing Bazi chart by its ID. The response includes the fully calculated Bazi chart details. Refer the response structure in the /api/bazi/bazi (POST) for details.z<A unique integer value identifying the Bazi chart to update.r  zUpdate Bazi ExamplezUpdated Personz14:00:00z
1970-12-31zUpdated noteszUpdated Bazi Responsez2023-01-01T15:00:00ZzPartially update a Bazi chartzPartially update an existing Bazi chart by its ID. Only provide the fields you want to update. The response includes the fully calculated Bazi chart details. Refer the response structure in the /api/bazi/bazi (POST) for details.zRenamed Personz!Only these fields will be updated)r   r  zPartially Updated Bazi Responsez2023-01-01T15:30:00ZzDelete a Bazi chartz'Delete a specific Bazi chart by its ID.z<A unique integer value identifying the Bazi chart to delete.)   r  c                      e Zd ZdZeZeegZe	gZ
ej                  gZg dZdgZeZg dZd Zd Zd Zd Zd	 Zd
 Zd Zd Zd Z eddgddd eej>                  d e dddddddddddd	d d!d"d#d$d%d&d'(       e d)d*d+id'(      g,      i-       ed.d/gd0d1g  e!d2 e"jF                  d3d'd45       e"jF                  d3d'd65       e"jF                  d3d7d89       e"jH                  d3d3d:9      d;<       e d=d!d"d3d>d'?       e d@dAdBd'd>d'?      g eej>                  dC e dDddddddddddd	d d!d"d#d$d%d&d'(      g,       eej>                  dE e dFdGdHdIJ      g,       eej>                  dK e dLdGdMdIJ      g,      dNO       e%d'dPdQgdRS      d~dU                     Z& edVdW e'dXejP                  e'jR                  d3dYZ       e'd[ejP                  e'jR                  d3d\Z      g e!d] e"jT                  d^_       e"jV                  d3d'd`5       e"jV                  d3d'da5       e"jX                   e!db e"jT                  dc_       e"jF                  dd_       e"jZ                  dedfgdgh       e"j\                  di_       e"j^                  d3djk       e"j`                  dl_       e"jX                   e"j`                         d3dmn       e"jX                   e"j`                         d3don       e"jT                  dp_       e"jT                  dq_      dr
<      dst      du<       e!dvdw e"jF                  dxy      i<      dz{       e%d3dPgd|S      d}               Z1yT)BaziViewSetz
    ViewSet for Bazi chart objects providing CRUD operations and calculations.
    Public users can calculate charts (results will be returned but not saved).
    Authenticated users automatically have their charts saved.
    )r   r   r  r  r  )rf   r  putpatchdeleter3  optionsc                    | j                   j                  }|j                  st        j                  j                         S t        j                  j                  |      }| j                   j                  j                  dd      }|&	 |j                         dk(  }|j                  |      }| j                   j                  j                  dd      }||j                  |      }|S # t        $ r Y Gw xY w)z
        This view returns a list of Bazi charts with optional filtering.
        - For authenticated users: Return user's records only
        - For unauthenticated users: Return empty queryset
        r  r  Nr  r  r   name__icontainsrn   r   r)  
BaziPersonr   r  r  re   rf   lowerrk   rm   r   r  r  
owner_boolr   s         ro   r   zBaziViewSet.get_queryset
  s     ||  $$%%**,,%%,,,= ))--gt<"[[]f4
#???<
 ||((,,VT:t<H     
%C+ +	C76C7c                     | j                         }| j                  |      }|.| j                  |d      }| j                  |j                        S | j                  |d      }t        |j                        S )zi
        Override list to apply user-specific filtering while keeping get_queryset open for DRF.
        Tr  )_get_user_filtered_querysetpaginate_querysetr   get_paginated_responser   r   )rm   rn   r   r   r  r  r   s          ro   r  zBaziViewSet.list
  sx    
 335 %%h/,,T,=J..z??(((=

((rv   c                    | j                   j                  }|j                  st        j                  j                         S t        j                  j                  |      }| j                   j                  j                  dd      }|&	 |j                         dk(  }|j                  |      }| j                   j                  j                  dd      }||j                  |      }|S # t        $ r Y Gw xY w)zV
        Get queryset filtered by user authentication and request parameters.
        ru  r  Nr  rv  r   rw  ry  r|  s         ro   r  z'BaziViewSet._get_user_filtered_queryset
  s     ||  $$%%**,,%%,,,= ))--gt<"[[]f4
#???<
 ||((,,VT:t<H  r~  c                 \   d|_         |j                         }	 |j                  rt        j                  j                  |j                  d      j                  d      j                         }|rP|j                  |j                  k7  r6|j                  r)|j                  r|j                  j                  di       j                  d      }|j                  j                  di       j                  d      }|j                  j                  di       j                  d      }|j                  j                  di       j                  d      }|m|j|g|dt        ||||      }g }	|j                  |j                  d      k7  r'|j                  d      |_        |	j                  d	       |j                  |j                  d
      k7  r'|j                  d
      |_        |	j                  d       t        |j                  d      xs g       }
t        |j                  d
      xs g       }|j                   |
k7  r|
|_        |	j                  d       |j"                  |k7  r||_        |	j                  d       |	r>ddlm} |j)                         |_        |	j                  d       |j-                  |	       |j                  |j0                  |j2                  |j4                  |j6                  |j8                  |j:                  |j<                  |j>                  |j@                  ||jB                  |jD                  |jF                  |j                  r|j                  j                  ndd}|j                  xs g |d	<   |j                  xs g |d<   |S # t.        $ r Y w xY w)z
        Helper method to format the response with calculated Bazi details.
        Always calculates the full BaZi result to ensure all fields are populated.
        Tr  r  r  r   r  r  Ngoodrelation_goodbadrelation_badrelation_good_countrelation_bad_countr   rM   relation_updated_atupdate_fields)r   r   r   r   r   r   r   r   r  r  r  r  r  r  r  )$_skip_bazi_calculationcalculate_bazir  rz  r   r  order_byfirstr   r  rf   rJ   r  appendr  lenr  r  django.utilsrN   r  r  r   r   r   r   r   r   r   r   r   r  r  r  r  r  )rm   personr  r  ogoepgper   	to_update
good_count	bad_countdj_tzrN  s                 ro   _format_bazi_responsez!BaziViewSet._format_bazi_response
  s    )-%++-$	  &&--9J9JRV-WXm,UW 
 UXX2u7H7HVM_M_**..ub9==eDB**..ub9==gFB++//r:>>uEB++//r:>>wGB~".R^PRP^+BB;$&	!//3776?B36776?F0%,,_=!..#''%.@25''%.F/%,,^<%()>B%?
$'(<"$=	!55C9CF6%,,-BC!44	A8AF5%,,-AB$F9>F6%,,-BC"KKiK@
 ))KKmm ++ ++)) ++ ++\\\\!** ++ ++282C2C&++..
& *0)=)=)Co&(.(;(;(Arn%3  		s   J(N 	N+*N+c                 Z    | j                         }| j                  |      }t        |      S )zN
        Override to include calculated Bazi details in the response.
        )r  r  r   )rm   rn   r   r   instancerN  s         ro   r  zBaziViewSet.retrieveF  s+     ??$228<&&rv   c                 J    |j                   }d|_        |j                          y)zM
        Override to prevent signal interference during API updates.
        TN)r  _skip_relation_calculationr   )rm   r   r  s      ro   perform_updatezBaziViewSet.perform_updateN  s!     &&.2+rv   c                 H   |j                  dd      }| j                         }| j                  ||j                  |      }|j	                  d       | j                  |       | j                         }| j                  |      }t        |dd      ri |_        t        |      S )z[
        Override to include calculated Bazi details in the response after update.
        partialF)r   r  Tr   _prefetched_objects_cacheN)
rC  r  r   r   r   r  r  rl   r  r   )	rm   rn   r   r   r  r  r   updated_instancerN  s	            ro   r  zBaziViewSet.updateX  s     **Y.??$((g(V
D1J'  ??,223CD88$? 24H.&&rv   c                 8    d|d<    | j                   |g|i |S )zR
        Override to ensure partial updates use the same response format.
        Tr  )r  )rm   rn   r   r   s       ro   r  zBaziViewSet.partial_updatem  s)     !yt{{74T4V44rv   c                 v   t        |j                        }|j                         s't        d|j                  it
        j                        S |j                  }t        |d   |d   |d   |j                  d      |j                  dd	      |j                  d
      |j                  d      |j                  dd            }|j                  dd	      }|dk(  r1|j                  d
      s t        dd
dgiit
        j                        S |dk(  r1|j                  d      s t        dddgiit
        j                        S d|_
        |j                         }|j                  |j                  |j                  |j                  |j                   |j"                  |j$                  |j&                  |d	}	|j(                  j*                  rP|j(                  |_        t/        |j(                  d      r|j(                  j0                  rd|_        t        j4                  j7                  |j(                        }
|
j9                          |_        |j=                          |j>                  |	d<   |j@                  |	d<   |j,                  j@                  |	d<   |jB                  |	d<   |jD                  |	d<   d|	d<   t/        |d      r;t/        |j(                  d      r%|j(                  j0                  r|jF                  |	d<   t        |	t
        jH                        S d |	d<   t        |	t
        jJ                        S )!z
        Create action handling both calculation and optional saving.
        - Public users: Calculate only
        - Authenticated users: Calculate and save
        r   r  r	  r   r   r   r   r   r   r   r   r  r  r  r#   u$   双大需要填写父亲出生日期r  u$   双小需要填写母亲出生日期T)	r   r   r   r   r   r   r   r  r  r%  ru  r  r   r  r  r  rl  r  r  r/  z5Bazi chart calculated (not saved - no authentication))&r-   r   r   r   r  r   r+  r
  rz  rf   r  r  r   r   r   r   r   r   r   r  r   r)  r  r  r%  r  r   r  existsr  r   r  r   r  r  r  r   r-  )rm   rn   r   r   r   r
  r  r   r  rN  existing_recordss              ro   r   zBaziViewSet.createt  s    +=
""$Wj&7&78A\A\]] $22 '!(+%l3%)),7$((a8%)),7%)),7 $$Wb1	
 #&&{A6	>."4"4\"BW|6\5]&^_"("="=? ?!^N$6$6|$DW|6\5]&^_"("="=? ? )-% ++- KKmm ++ ++)) ++ ++\\!

 <<(( 'Fw||%89gll>\>\.2+  *1188GLL8Q  06688FLKKM&,&:&:M(#"())M$*0*;*;*>*>M,'*0*;*;M,'*0*;*;M,''XM)$ w 12ww||M`7afmfrfr  gE  gE-4-D-Dk*M&2I2IJJ (_M)$M&2D2DEErv   bazi_analysis_getGETz Get AI analysis for a BaZi chartzu
Retrieve the AI analysis for a BaZi chart if it exists.
If no analysis exists, returns a status of "not-available".
r   3Analysis retrieved successfully or status indicatedCompleted Analysis	completedu   包含性格分析的文本...u$   命局结构与五行平衡分析...u   事业与财富展望分析...u$   人际关系与婚姻模式分析...u   健康考量分析...u   人生轨迹分析...u$   当前大运与流年影响分析...u   特殊格局与神煞分析...u   实用建议总结...)	personality_analysislife_structurecareer_wealthrelationshipshealthlife_trajectorycurrent_influencesspecial_elementspractical_recommendationsuK   请分析以下八字命盘，提供详细的命理解读和人生建议...groqllama3.3-70bu?   我需要分析这个八字命盘，首先整理基本信息...)bazi_analysispromptprovidermodelthink2023-05-30T12:34:56.789Zr   analysis	timestampTr  No Analysis Availabler   not-availabler   operation_idmethodsr  r   r   bazi_analysis_postPOSTz%Generate AI analysis for a BaZi chartaE  
Generate a new analysis (or regenerate an existing one).

## Request Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `provider` | string | No | LLM provider to use. Possible values: `groq`, `openai`. If not specified, uses the default from settings. |
| `model` | string | No | Specific model to use. See available models below. If not specified, uses the default for the chosen provider. |
| `language` | string | No | Language for the analysis. Possible values: `zh-hans` (Chinese Simplified), `en` (English). Default is `zh-hans`. |
| `regenerate` | boolean | No | Set to `true` to force regeneration of an existing analysis. Default is `false`. |

## Permission Requirements

Regeneration requires special permissions:
- Only users with the `can_regenerate_ai` privilege can regenerate analyses
- Without this privilege, users can only generate an analysis once per chart

## Available Providers and Models

1. **Groq Models**:
   - `llama3.3-70b`: Llama 3.3 70B Versatile - Best quality with 128k context
   - `llama4-maverick`: Llama 4 Maverick 17B - Good balance with 128k context 
   - `llama3.1-8b`: Llama 3.1 8B Instant - Faster model with 128k context

2. **OpenAI Models**:
   - `gpt-4o`: GPT-4o - Latest version, best quality
   - `gpt-4-turbo`: GPT-4 Turbo - Fast with good quality
   - `gpt-3.5-turbo`: GPT-3.5 Turbo - Fastest, good for simpler tasks
   - `gpt-4`: GPT-4 - Original GPT-4 model
   - `gpt-4.1`: GPT-4.1
   - `gpt-5`: GPT-5

If no provider or model is specified, the system will use the default values from settings.
BaziAnalysisRequestFiLLM provider to use. Possible values: 'groq', 'openai'. If not specified, uses the default from settings.r   
allow_nullrz   zSpecific model identifier (e.g., 'llama3.3-70b', 'gpt-4o'). If not specified, uses the default model for the selected provider.zh-hansqLanguage for the analysis. Possible values: 'zh-hans' (Chinese Simplified), 'en' (English). Default is 'zh-hans'.r   rb   rz   tSet to true to force regeneration of an existing analysis. Requires 'can_regenerate_ai' privilege. Default is false.)r  r  language
regenerater  Generate Analysis Requestr  r  r  r4  Regenerate Analysis Requestopenaigpt-4ozAnalysis successfully generatedAnalysis Created"Permission denied for regenerationPermission Errorr  J   您没有重新生成AI分析的权限。请联系管理员获取权限。r   r  r   Error generating analysisAnalysis Errorz4Failed to connect to AI provider: Connection timeout)r   r    r  r  r  r   r   rn   r   r   rf   r  r  )detailr  url_pathNc                    | j                         }|j                  dk(  r|j                  r|j                  dk(  rt	        i ddd|j                  d|j
                  d|j                  d|j                  d|j                  d	|j                  d
|j                  d|j                  d|j                  d|j                  d|j                  d|j                  d|j                  d|j                   d|j"                  d|j$                  d|j&                  i      S t	        ddi      S |j                  dk(  r|j(                  j+                  d      }|j(                  j+                  d      }|j(                  j+                  dd      }ddlm}  ||      }	 t1        d      }|s|j+                  d      }|s|j+                  d      }|j(                  j+                  dd       }
t9        |
t:              r|
}n|
j=                         d!k(  }|j                  rN|j                  dk(  r>|st	        i ddd|j                  d|j
                  d|j                  d|j                  d|j                  d	|j                  d
|j                  d|j                  d|j                  d|j                  d|j                  d|j                  d|j                  d|j                   d|j"                  d|j$                  d|j&                  i      S |j>                  }d"}|j@                  r$tC        |d#      r|jD                  jF                  rd$}|st	        d%d&d'd()      S |jH                  s#|jK                          |jM                  d*g+       	 dd,l'm(} d-|_        tS        jT                         |_        |jM                  dd.g+        |||||/      }d|_        |jM                  dg+       t	        i ddd|d|j
                  d|j                  d|j                  d|j                  d	|j                  d
|j                  d|j                  d|j                  d|j                  d|j                  d|j                  d|j                  d|j                   d|j"                  d|j$                  d|j&                  id0)      S y # t2        $ r#}	t4        j7                  d|	        Y d }	~	9d }	~	ww xY w# tV        $ r}	tY        |	      }d%|_        |jM                  dg+       d1|v rd2}nDd3|v rd4}n=d5|v rd6}n6d7|v rd8}n/d9|v rd:}n(d;|v rd<}n!d=|v rd>}nd?|v rd@}ndA|v rdB}ndC|v rdD}ndE| }t4        j[                  dF|j\                   dG|        t	        d%||dHdI)      cY d }	~	S d }	~	wt2        $ rg}	tY        |	      }d%|_        |jM                  dg+       t4        j[                  dJ|j\                   dG| d$K       t	        d%dL|dHdI)      cY d }	~	S d }	~	ww xY w)MNr  r  r   r  r  analysis_statusbazi_analysis_reportedbazi_report_statusbazi_report_categorybazi_report_messagebazi_report_admin_notesbazi_report_timestampbazi_report_resolved_atnumber_analysis_reportednumber_report_statusnumber_report_categorynumber_report_messagenumber_report_admin_notesnumber_report_timestampnumber_report_resolved_atr  r  r  r  r  r  r   validate_languager  z*Could not get default AI config for bazi: r  r  r  Fr   Tr  r  r  r  r	  r  r  )analyze_bazipendinganalysis_timestamp	model_keyr  r  r   zFailed to calculate BaZi datau   无法计算八字数据，请确保出生日期和时间正确。如果您是双胞胎，请确保填写了父亲或母亲的出生日期。z%BaZi data is missing required pillarsuK   八字数据缺少必要的柱位，请确保出生日期和时间正确。zBaZi data has None foruK   八字数据计算出现问题，请重新检查出生信息是否准确。z Could not initialize LLM serviceu,   AI服务暂时不可用，请稍后再试。zCould not change modelu8   所选AI模型暂时不可用，请尝试其他模型。zCould not prepare BaZi promptuc   准备分析提示时出错，可能是八字数据格式有问题。请重新检查出生信息。z)Error getting completion from LLM serviceu;   从AI服务获取分析结果时出错，请稍后再试。zError processing LLM responseuU   处理AI回复时出错，AI可能返回了意外的格式。请尝试其他模型。z#Error preparing analysis dictionaryu>   准备分析结果时出错，请尝试使用其他AI模型。z!Error saving analysis to databaseu0   保存分析结果时出错，请稍后再试。u   分析生成失败: zBaZi analysis error for person z: )r   r  r  r  z-Unexpected error in BaZi analysis for person exc_infouE   分析过程中发生意外错误，请稍后再试或联系客服。)/r  methodai_analysisr  r   r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r   rf   ai.utils.i18nr  r[   r   r`  warning
isinstanceboolr{  r   r)  r  r   r6  r  r  r   ai.utils.bazir  rN   r  rk   r   r  r   )rm   rn   r  r  r  r  r  r  configr}   regenerate_valuer  r   can_regenerater  r  error_messageuser_messages                     ro   r  zBaziViewSet.analysis  s   H ??$ >>U"##(@(@K(O !k! 4 4!  !<!<! &x'?'?	!
 -h.M.M! )(*E*E! +H,I,I! *8+G+G! .x/O/O! ,X-K-K! .x/O/O! /0Q0Q! +H,I,I! -h.M.M! ,X-K-K!  01S1S!!" .x/O/O#!$ 01S1S%!  ,  o!  
 ^^v%||''
3HLL$$W-E||''
I>H 8(2HQ&v.%zz*5H"JJw/E  '||//gF*D1-
-335?
 ##(@(@K(O!+3 5k5 4 45  !<!<5 &x'?'?	5
 -h.M.M5 )(*E*E5 +H,I,I5 *8+G+G5 .x/O/O5 ,X-K-K5 .x/O/O5 /0Q0Q5 +H,I,I5 -h.M.M5 ,X-K-K5  01S1S!5" .x/O/O#5$ 01S1S%5 , %, ||!&((WT9-E$,,JhJh%)N%#")!m% "# # '''')]O<T6 ,5(.6lln+->@T,UV &h%(]ef ,7(->,?@ !k!!  !<!<! &x'?'?	!
 -h.M.M! )(*E*E! +H,I,I! *8+G+G! .x/O/O! ,X-K-K! .x/O/O! /0Q0Q! +H,I,I! -h.M.M! ,X-K-K!  01S1S!!" .x/O/O#!$ 01S1S%!& ' q &"  Q!KA3OPPQv  $ #A+2(->,?@ 3mC $sL<M#pL->#pL7=H#QL->#]L4E $IL@MQ#`L4E#zL:mK#cL8MI#UL%9-#IL>x{{m2m_]^%)+! 	 
   #A+2(->,?@LX[[MY[\i[jkvz{%d+! 	 sK   &1S) $ET )	T2TT	X?!B%WX?X?AX:4X?:X?z#Get BaZi records with relationshipsu{  
Returns paginated list of BaZi records that have pairwise relationship data with the logged-in user.
This endpoint filters out records with no relationships for cleaner pairwise relationship analysis.

## Purpose
This endpoint is specifically designed for the **Pairwise Relations** tab in the relations page,
showing only people who have meaningful relationships (good or bad) with the current user.

## Filtering Logic
- **Owner Records**: Excluded (only shows other people's records)
- **Relationship Requirement**: Must have `relation_good_count > 0` OR `relation_bad_count > 0`
- **Ordering**: By creation date (newest first)

## Response Structure

### Paginated Response
```json
{
  "count": 25,
  "next": "http://example.com/api/bazi/bazi/with-relations/?page=2",
  "previous": null,
  "results": [
    {
      "id": 123,
      "name": "张三",
      "gender": "M",
      "birth_date": "1990-01-01",
      "birth_time": "10:00:00",
      "bazi_result": {
        "year": {"god": "庚", "earth": "午"},
        "month": {"god": "己", "earth": "丑"},
        "day": {"god": "甲", "earth": "子"},
        "hour": {"god": "乙", "earth": "丑"}
      },
      "relation_good": [
        {
          "t": "r",
          "c": 0,
          "i": null,
          "5e": null
        },
        {
          "t": "r", 
          "c": 2,
          "i": null,
          "5e": 2
        }
      ],
      "relation_bad": [
        {
          "t": "r",
          "c": 3,
          "i": null,
          "5e": null
        }
      ],
      "relation_good_count": 2,
      "relation_bad_count": 1
    }
  ]
}
```

### Non-Paginated Response
```json
{
  "count": 5,
  "results": [...]
}
```

## Relationship Data Structure

### Good Relationships (`relation_good`)
Array of positive relationship reasons (mix of relations and sha gods):

**Relations (`t = "r"`):**
- **六合 (Liu He - Six Harmony)**: `{"t": "r", "c": 0}`
- **半三合 (Half Triad)**: `{"t": "r", "c": 1}`
- **天干合 (Heavenly Stems Harmony)**: `{"t": "r", "c": 2, "5e": <element_index>}`

**Sha Gods (`t = "s"`):**
- **天乙贵人 (Tian Yi Noble)**: `{"t": "s", "i": 0}`
- **文昌贵人 (Wen Chang Noble)**: `{"t": "s", "i": 5}`
- **桃花 (Peach Blossom)**: `{"t": "s", "i": 7}`

### Bad Relationships (`relation_bad`)
Array of negative relationship reasons (relations only):

**Relations (`t = "r"`):**
- **六冲 (Liu Chong - Six Clash)**: `{"t": "r", "c": 3}`
- **六害 (Liu Hai - Six Harm)**: `{"t": "r", "c": 4}`
- **刑 (Punishment)**: `{"t": "r", "c": 5}`

### Type Field (`t`) Explanation
- `"r"`: **Relations** - Direct BaZi interactions (harmony, clash, etc.)
- `"s"`: **Sha Gods** - Auspicious/inauspicious deities based on traditional calculations

### Index Mappings

- Relation Codes (`c` when `t = "r"`): `0: 六合, 1: 半三合, 2: 天干合, 3: 六冲, 4: 六害, 5: 刑`

- Sha God Indices (`i` when `t = "s"`) (from `gShagodNames` in `bzshagod.py`): `0: 天乙, 1: 天德, 2: 月德, 3: 太极, 4: 禄神, 5: 文昌, 6: 驿马, 7: 桃花, 8: 红鸾, 9: 天喜, 10: 将星, 11: 福星, 12: 华盖, 13: 劫煞, 14: 亡神, 15: 羊刃, 16: 灾煞, 17: 孤辰, 18: 寡宿, 19: 阴差阳错, 20: 天医, 21: 飞刃, 22: 隔角, 23: 十恶大败, 24: 六秀, 25: 金舆`

- Five Elements (`5e`) — only for 天干合 (`t = "r"`, `c = 2`): `0: 木, 1: 火, 2: 土, 3: 金, 4: 水`

## Pagination
- **Default page size**: Follows `PAGINATE_BY['default']` setting
- **Page parameter**: `?page=N` where N is the page number
- **Page size parameter**: `?page_size=N` to override default

## Use Cases
1. **Relations Page**: Display pairwise relationships in the first tab
2. **Relationship Analysis**: Show only people with meaningful connections
3. **Data Filtering**: Avoid displaying empty relationship records
4. **Performance**: Efficient pagination for large datasets

## Authentication
Requires authenticated user. Returns empty results for unauthenticated requests.
        r  z'Page number for pagination (default: 1))r   r   r   r   r   r^   zANumber of results per page (default: follows PAGINATE_BY setting)BaziWithRelationsResponsez/Total number of BaZi records with relationshipsry   z+URL for next page of results (if available)z/URL for previous page of results (if available)BaziPersonWithRelationsz	Person IDzPerson namer   r:  zGender: M=Male, F=Female)choicesrz   zBirth date in YYYY-MM-DD formatz+Birth time in HH:MM:SS format (may be null)r   rz   z$Four pillars BaZi calculation resultz"Array of good relationship reasons)childr   rz   z!Array of bad relationship reasonsz"Number of good relationships foundz!Number of bad relationships found)
r   r   r   r   r   r  r  r  r  r  z3Array of BaZi person records with relationship data)r  rz   )countnextpreviousresultsUnauthorizedResponser  z-Authentication credentials were not provided.)rb   rQ  r  zwith-relationsc                 @   |j                   r|j                   j                  st        g ddd      S t        j                  j                  |j                   d      j                  t        d      t        d      z        j                  d	      }| j                  |      }|:g }|D ]"  }|j                  | j                  |             $ | j                  |      S g }|D ]"  }|j                  | j                  |             $ t        |t        |      d      S )
z
        Return only BaZi records that have pairwise relationships (relation_good_count > 0 or relation_bad_count > 0).
        r   )r  r  r   r	  Fr  )relation_good_count__gt)relation_bad_count__gtr  )r   r)  r   rz  r   r  r   r  r  r  r  r  r  )rm   rn   r  r  formatted_resultsr  s         ro   with_relationszBaziViewSet.with_relationsL  s   ` ||7<<#@#@Q7DD %%,,|| - 
 &a(1A+FF

(=
! 	 %%h/ "!(()C)CF)KL  ../@AA F$$T%?%?%GH  $5DU@VWXXrv   r  )2rq   rr   rs   rt   r,   r   rH   rE   r!   r
   r    r   r  r  r	  r  r]   r
  http_method_namesr   r  r  r  r  r  r  r  r   r   r   r   OBJECTr   r   r   r   r   rV   r  r   INTQUERYr{   URLField	ListFieldChoiceField	DateField	TimeFieldr   r  ru   rv   ro   ro  ro  |  sZ   X
 &?AVW"--.OHOH1T8) 4HT''*5RFj (2
 %,,Q"1&1 =]6\5U5[.E7N:`8XAX
2" +x,2)7)j)" *D'* '+/2 #4$o '+5!%'
0b )7"F !&1K11"# J
 /.."# `
 2K11"% R
 7k66"! U!
4 0 &+"'
 " 2 (%"&
 "
* !%,,="/&1 =]6\5U5[.E7N:`8XAX
2" +x,2)7)j)" *D'* '+/> !%,,@"*&-%q !%,,7"(&-%[[:
kPb 4%:F~ GcPc0F~@ 5wr !%%)//E  !%%)//_
" #05[55"S 1K00!&#'"O
 !5 4 4!&#'"S!
  5{44/!:&>k&>&>&U(=(=(=(V*A+*A*A-0#J.H+" /Dk.C.CNo.p.Ck.C.C-2.[/" 0E{/D/D.T0" 2G1F1F*?+*?*?*A-2.R2"
 1F0E0E*?+*?*?*A-2.Q1"
 8P{7O7O.R8" 7Ok6N6N.Q7"9$"F #XI% 47p #+3k33<kls?
UJV 55'4DEY FWJXYrv   ro  c                   F   e Zd ZdZegZ edd edeej                  dd       edeej                  d	d      ge
j                  e
j                  d
 edddddi ddddddddddddddddddddddd d!d"ddd#d$d%d&d'd(d)d*d+d,d-d.dd/d0d1dd2dd3d4i dd5dd6dddddddddddd7d8dddddd d9d:ddd#d$d;d&d<d(d=d*d+d,d>d.dd/d:d1dd2dd3d4ggd<d?d:d@dAdBdd@gdd:dCddDdCdEddFgG       edHdIdJiddKgG      gL      dM        ZyN)OTongshuCalendarAPIViewz.API view for retrieving Chinese calendar data.zGet monthly calendar datau
	  
Retrieve Chinese calendar data for a specific month, including:
- Lunar dates
- Solar terms
- Heavenly stems and earthly branches
- Five elements
- Zodiac animals
- Navigation links to adjacent months

If no year or month is provided, the current month is returned.

**Response Structure:**

- `year`: Requested year (integer)
- `month`: Requested month (integer, 1-12)
- `month_god`: Month's heavenly stem (天干) in Chinese, calculated using the 15th day
- `month_earth`: Month's earthly branch (地支) in Chinese, calculated using the 15th day
- `days`: Two-dimensional array of day objects (organized by weeks)
  - Each day object contains:
    - `date`: ISO date string (YYYY-MM-DD)
    - `day`: Day of month (integer)
    - `is_adjacent_month`: Whether this day belongs to adjacent month (boolean)
    - `is_today`: Whether this day is today (boolean)
    - `bazi`: Four pillars information
      - `year`, `month`, `day`, `hour`: Each pillar contains:
        - `god`: Heavenly stem (天干) in Chinese
        - `earth`: Earthly branch (地支) in Chinese
    - `lunar`: Raw lunar date information
      - `d`: Lunar day (integer)
      - `m`: Lunar month (integer)
      - `y`: Lunar year (integer)
      - `leap`: Whether this is a leap month (boolean)
    - `lunar_formatted`: Full lunar date in Chinese characters
    - `lunar_title`: Lunar month in Chinese characters
    - `lunar_desc`: Lunar day in Chinese characters
    - `lunar_leap`: Leap month indicator, if applicable
    - `jianchu`: Jianchu (建除) cycle value in Chinese
    - `has_solar_term`: Whether this day has a solar term (boolean)
    - `solar_term_index`: Index of the solar term (0-23)
    - `month_earth`: Month's earthly branch in Chinese
    - `month_god`: Month's heavenly stem in Chinese
    - `month_element`: Month's five element in English (wood, fire, earth, metal, water)
- `solar_terms`: Array of solar terms in this month
  - Each solar term contains:
    - `name`: Name of the solar term in Chinese
    - `date`: Date and time (ISO format)
    - `index`: Index of the solar term
- `prev_month`: Reference to previous month
  - `year`: Year of previous month
  - `month`: Month number of previous month
- `next_month`: Reference to next month
  - `year`: Year of next month
  - `month`: Month number of next month
r   /Year (defaults to current year if not provided)Fr  r   7Month (1-12, defaults to current month if not provided)r  r   r  r  r#  r(  rA   
2025-02-24r      is_adjacent_monthTis_todayr  r  r   r  r)  u   子r"  u   午r  lunar   r#   r8  r?  yleaplunar_formattedu   正月廿七lunar_titleu   正月
lunar_descu   廿七
lunar_leapr  jianchuu   开has_solar_termsolar_term_indexr   month_earth	month_godmonth_elementwood
2025-03-05r  r*  u   酉r  r  u   惊蛰 16:07u   惊蛰16:07u   破z2025-03-05T08:07:16r   rA   indexu   春分z2025-03-20T09:01:29r   r   r  r   r   r9  r8  dayssolar_terms
prev_month
next_monthr  r  Error Responser  (Invalid month. Must be between 1 and 12.r  r  c           	      ~   t        |j                        }|j                         s't        d|j                  it
        j                        S |j                  }t        j                         j                         }|j                  d|j                        }|j                  d|j                        }d|cxk  rdk  sn t        ddit
        j                        S d	d
lm}	m}
 	  |	||      } |
||      }|D ]s  }d|v r)t#        |d   t              r|d   j%                         |d<   d|v s5|d   s;|d   D ]1  }d|v st#        |d   t&              s|d   j%                         |d<   3 u |D ]k  }||   }d|v rNt#        |d   t(              r;t'        j*                  |d   j-                  dd            }|j%                         |d<   d|vs_d|v sd|d   |d<   m g }d|v rG|j/                  |d   d   |d   j                  d|d   d         |d   j                  dd	      d       d|v rG|j/                  |d   d   |d   j                  d|d   d         |d   j                  dd	      d       |dk(  r	|dz
  dd}n||dz
  d}|dk(  r	|dz   dd}n||dz   d}t1        j2                  ||d      }||t0        j4                  |d   d      t0        j6                  |d   d      ||||d}t        |      S # t8        $ r3}t        ddt)        |       it
        j:                        cY d}~S d}~ww xY w)z<Get calendar data for a specific month or the current month.r   r  r	  r   r   r#      rG  r   )get_month_calendarget_solar_termsrA   rC  r8  Zz+00:00r  r7  r;  r>  secondr@     r|   r}   rA  !Error calculating calendar data: N)r.   re   r   r   r  r   r+  r
  rN   r  rA   rf   r   r   tongshu.viewsrJ  rK  r  	isoformatr@   r   fromisoformatreplacer  r  calcBazigGodstem
gEarthstemr   r.  )rm   rn   r   r   r   r
  todayr   r   rJ  rK  	days_datarC  day_dataterm	term_namedtsolar_terms_listrD  rE  
month_bazirN  r}   s                          ro   rf   zTongshuCalendarAPIView.get<  s   X 49M9MN
""$Wj&7&78A\A\]] $22##%!!&%**5""7EKK8 ERW&PQ"("="=? ? 	FE	I*47I)$6K &X%*Xf5Et*L'/'7'A'A'CHV$ H,-1H ( 7!T>jfx.P+/<+A+A+CDL !8 & )	"9-$;:d3i#=!//S	0A0A#x0PQB#%<<>DL%#+#'9DL )  "+% '''05'044V[=QRU=VW(155c1=) 
 ;& '''1#6'155fk(>STW>XY(266sA>)  z&*Qh<
&*UQY?
{&*Qh;
&*UQY?
 tUB7J !]]:g+>s+CD#z'/B3/GH!/((	M M** 	IW(I#a&&RS"("G"GI I	IsE   'AL  0L  6L  L  A:L  L  D(L   	L<	(L71L<7L<Nrq   rr   rs   rt   r
   r    r   r   rd   r  r   r  r   rf   ru   rv   ro   r%  r%  8  s^   8"+5n )//M )//U
" $$$$

 ' !&#(' &' %r' !4T' !+E	'
 !'/416-&
 0516.&
 0516,&
 0516-&)"'. !()+)*)-,1	*"/': !2>;'< !.x='> !-h?'@ !-bA'B !*5C'D !1%E'F !3AG'H !.uI'J !,UK'L !0M'P' &' %q' !4U' !+E	'
 !'/416-&
 0516.&
 0516,&
 0516-&)"'. !()*)*)-,1	*"/': !2>;'< !.x='> !-g?'@ !-bA'B !*5C'D !1$E'F !3AG'H !.uI'J !,UK'L !0M'SQSl %-$9%& %-$9%&$ !%!"#
 !%!"#Sm\ ##Wcrf %G ##Wi|
YITZIUITZIrv   r%  c                      e Zd ZdZegZ edd edeej                  dd       edeej                  d	d       ed
eej                  dd      ge
j                  e
j                  d edddg dddddg dg ddg ddgdddg dg ddg ddggdddg dg d dg d!d"gd#d"dg d$g d%d"g d&dgggdd'gdd(gd)d*d+g,       ed-d.d/id*d0g,      g1      d2        Zy3)4TongshuCalendarAPIViewV2zXAPI view for retrieving Chinese calendar data using the optimized TongshuCalendar model.z(Get optimized monthly calendar data (V2)u`
  
Retrieve Chinese calendar data for a specific month using the optimized TongshuCalendar model.
This endpoint provides the same data as the original endpoint but with improved performance.

Features:
- Optimized data structure for faster processing
- Compact arrays for reduced payload size
- Elements represented as indices (wood=0, fire=1, earth=2, metal=3, water=4)

If no year or month is provided, the current month is returned.

**Response Structure:**

- `year`: Requested year (integer)
- `month`: Requested month (integer, 1-12)
- `month_bazi`: Month's BaZi information [year_god_index, year_earth_index, month_god_index, month_earth_index]
- `month_5e`: Month's five element index (0=wood, 1=fire, 2=earth, 3=metal, 4=water)
- `days`: Two-dimensional array of day objects (organized by weeks)
  - Each day object contains:
    - `date`: ISO date string (YYYY-MM-DD)
    - `day`: Day of month (integer)
    - `is_today`: Whether this day is today (1=true, 0=false)
    - `bazi`: Three pillars information as indices [year_god, year_earth, month_god, month_earth, day_god, day_earth]
    - `lunar`: Lunar date information [day, month, year, is_leap]
    - `jianchu`: Jianchu (建除) cycle index (0: "建", 1: "除", 2: "满", 3: "平", 4: "定", 5: "执", 6: "破", 7: "危", 8: "成", 9: "收", 10: "开", 11: "闭")
    - `solar`: Solar term information with conditional time:
      - For regular days: [term_index, day_is_term, term_5element_index]
      - For solar term days: [term_index, day_is_term, term_5element_index, term_time]
      where:
        - term_index: Solar term index (0-23)
            - 0: "立春", 1: "雨水", 2: "惊蛰", 3: "春分", 4: "清明", 5: "谷雨", 6: "立夏", 7: "小满", 8: "芒种", 9: "夏至", 10: "小暑", 11: "大暑", 12: "立秋", 13: "处暑", 14: "白露", 15: "秋分", 16: "寒露", 17: "霜降", 18: "立冬", 19: "小雪", 20: "大雪", 21: "冬至", 22: "小寒", 23: "大寒"
        - day_is_term: Whether this day is a solar term (1=true, 0=false)
        - term_5element_index: Five element index (0=wood, 1=fire, 2=earth, 3=metal, 4=water)
        - term_time: Only present when day_is_term=1, contains the time of the solar term in "HH:MM" format
    - `ybp`: Yellow/Black Path (黄黑道) index (0-11):
        - God index mappings: 0: "青龙" (good), 1: "明堂" (good), 2: "天刑" (bad), 3: "朱雀" (bad), 4: "金匮" (good), 5: "天德" (good), 6: "白虎" (bad), 7: "玉堂" (good), 8: "天牢" (bad), 9: "玄武" (bad), 10: "司命" (good), 11: "勾陈" (bad)
- `prev_month`: Reference to previous month [year, month]
- `next_month`: Reference to next month [year, month]
r   r&  Fr  r   r'  
extra_dayszNumber of extra days to include before/after the month (default varies based on month layout, maximum 100, negative values treated as 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   r   z
2025-02-25rc   )r#   r  r  r  r#   r#   )   r#   r  r   r  r#   z
2025-03-01)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  r#   r   r=  r  r  r   r   r^  month_5erB  rD  rE  Tr  r  rF  r  rG  r  r  c                 d   t        |j                        }|j                         s't        d|j                  it
        j                        S |j                  }t        j                         j                         }|j                  d|j                        }|j                  d|j                        }|j                  j                  d      }	|	 	 t        |	      }	|	dk  rd}	t        |	d	      }	nft        ||d      }
|
j#                         }t%        ||      \  }}t        |||      }|j#                         }|}d|z
  }|dk  r|dz  }t'        ||      }	d|cxk  rdk  sn t        ddit
        j                        S ddlm} 	 |j-                  |||	d      }|d   |d   |d   |d   |d   |d   |d   d}t        |      S # t         $ r  t        dd
it
        j                        cY S w xY w# t.        $ r3}t        ddt1        |       it
        j2                        cY d}~S d}~ww xY w)zfGet calendar data for a specific month or the current month using the optimized TongshuCalendar model.r   r  r	  r   r   rb  Nr   r_   z"extra_days must be a valid integerr#   r  r  r  rI  rG  )TongshuCalendarF)rb  include_lunar_title_descr  r  r  rd  rO  )r.   re   r   r   r  r   r+  r
  rN   r  rA   rf   r   r   rd   rh   rk   weekdayrC   maxtongshu.modelsrg  get_month_datar   r   r.  )rm   rn   r   r   r   r
  rW  r   r   rb  	first_dayfirst_weekday_num_dayslast_daylast_weekdaydays_before
days_afterrg  calendar_datarN  r}   s                         ro   rf   zTongshuCalendarAPIViewV2.getf  s`   Z 49M9MN
""$Wj&7&78A\A\]] $22##%!!&%**5""7EKK8 ))--l;
!	B _
>!"J S1
 T5!,I%--/M$T51KAxD%2H#++-L (K\)J Qa
 [*5J ERW&PQ"("="=? ? 	3	I+::4S]x}:~M &a(&q)+A.)!,%a(+A.+A.M M**]  B*N O%+%@%@B BB^  	IW(I#a&&RS"("G"GI I	Is0   G >G3 &G0/G03	H/<(H*$H/*H/Nr_  ru   rv   ro   ra  ra  b  s   b":&P )//M )//U !)// g
0 $$$$

 ' ". ! *2q2DFVXZ\eghi)2q2DFVXZ\eghi *1a1C_VWYbdef)1a1C_VWYkmop	 $()#')+. ##W58 %G ##W;%
IjVHIWjVHIrv   ra  z Get yearly Chinese calendar datau	  
Retrieve comprehensive Chinese calendar data for an entire year.

**Access Control:**
- **Public users**: Limited to accessing only the previous year, current year, and next year
- **Authenticated users**: Can access calendar data for any year (range 1900-2100)

**Response Structure:**

- `year`: Requested year (integer)
- `year_data`: Information about the requested year
  - `element`: Year's element in English (wood/fire/earth/metal/water)
  - `animal`: Chinese zodiac animal (龙/蛇/马/羊/猴/鸡/狗/猪/鼠/牛/虎/兔)
  - `star`: Flying star (1白/2黑/3碧/4绿/5黄/6白/7赤/8白/9紫)
- `cycles`: Array of monthly cycles data (one entry per month, 12 total)
  - Each cycle contains:
    - `first`: First solar term data of the month
      - `i`: Solar term index (0-23)
      - `c`: Name of the solar term in Chinese
      - `d`: Date and time of the solar term (YYYY-MM-DD HH:MM:SS format)
      - `next`: Date and time of the next solar term (YYYY-MM-DD HH:MM:SS format)
      - `bz`: Bazi data (Chinese four pillars) for this solar term
        - `year`, `month`, `day`, `hour`: Each pillar contains indices:
          - `g`: Heavenly stem index (天干)
          - `e`: Earthly branch index (地支)
        - `month` also contains:
          - `element`: Element of the month in English (wood/fire/earth/metal/water)
      - `dates`: Array of days in this half of the month
        - Each day contains:
          - `lunar`: Lunar date information
            - `d`: Lunar day (integer)
            - `m`: Lunar month (integer)
            - `y`: Lunar year (integer)
            - `leap`: Whether this is a leap month (boolean)
          - `d`: Day of month (integer)
          - `m`: Month number (integer)
          - `w`: Weekday (0=Monday through 6=Sunday)
          - `jc12`: Jianchu (建除) cycle index, 0: "建", 1: "除", 2: "满", 3: "平", 4: "定", 5: "执", 6: "破", 7: "危", 8: "成", 9: "收", 10: "开", 11: "闭"
    - `second`: Second solar term data of the month (same structure as `first`)
    - `count`: Count of days in this month
    - `star`: Flying star for this month (1白/2黑/3碧/4绿/5黄/6白/7赤/8白/9紫)
- `paginate`: Pagination information
  - `prev`: Previous year (or false if at lower boundary)
  - `next`: Next year (or false if at upper boundary)
r   z5Year to retrieve calendar data for (range: 1900-2100)zCurrent YearzFuture Yearz$Successfully retrieved calendar datazExample Calendar10k Responser  u   蛇u   2黑elementanimalstaru   立春z2025-02-03 22:10:28z2025-02-18 18:06:34r  r;  )r|   r}   rw  )r   r   r   r   r.  )r,  r8  r?  rH  jc12)r;  r7  r8  r  rI   datesu   雨水z2025-03-05 16:07:16)r  rM  r  ry  i  i  r  prevr   	year_datacyclespaginater  zInvalid parameters providedzInvalid YearYear parameter is requiredzOut of Range!The request year is out of range.zHAccess forbidden - attempting to access a year outside the allowed rangezAccess RestrictedzRAccess to years outside of [2022, 2023, 2024] is restricted to authenticated users403calendar10k)r  r   r   rn   r   tagsc                       e Zd ZdZegZd Zy)Calendar10kAPIViewz]API view for retrieving calendar10k data for an entire year with access control restrictions.c           
      l
   	 t        |j                  j                  dd            }|dk(  rt        ddit        j
                        S 	 d|cxk  rdk  sn t        dd	it        j
                        S t        j                         j                  }|j                  j                  s<|d
z
  ||d
z   g}||vr-t        dd|d
z
   d| d|d
z    dit        j                        S 	 ddlm}m}m}	 |j%                  |d      }
i }t'        t)        |
            D ]  }d}|
|   j+                         D ]W  \  }}|dvrt-        j.                  |d   d      t1        d      z   |d<   t-        j.                  |d   d      t1        d      z   |d<   |d   }|j3                  |j                  |j4                  |j6                  |j8                  |j:                  |j<                        }||
|   |   d<   |j?                  |d   d         |
|   |   d   d   d<   |d   }g }t-        |j                  |j4                  |j6                        }t-        |j                  |j4                  |j6                        }d}||k  ri }|	jA                  |      |d<   |j6                  |d<   |j4                  |d<   |jC                         |d<   |r|d   d   }|d   d   }nd
z   dz  }d
z   dz  }d|id|id }|jE                  |j                  |j4                  |j6                  |      |d!<   d"}|jG                  |       |d
z  }|t1        d
#      z   }||k  r|d   jI                  d      |d<   |d   jI                  d      |d<   ||
|   |   d$<   Z ||
|   d%<   |jK                  |jM                  j                  |j4                              |
|   d&<    |j?                  |
d   d'   d   d   d         |d<   |jO                  |
d   d'   d   d   d         |d(<   |jK                  |jQ                  |            |d&<   |dkD  r|d
z
  nd"}|dk  r|d
z   nd"}||d)}||j?                  |
d   d'   d   d   d         |jO                  |
d   d'   d   d   d         |jK                  |jQ                  |            d*|
|d+}t        |      S # t        t        f$ r  t        ddit        j
                        cY S w xY w# tR        $ r0}t        dtU        |      it        jV                        cY d,}~S d,}~ww xY w)-z
        Get calendar data for an entire year with access control.
        
        Public users are limited to accessing previous year, current year, and next year.
        Authenticated users can access data for any year between 1900 and 2100.
        r   r   r  r  r	  zYear must be a valid integeril  i4  r  r#   zAccess to years outside of [z, z&] is restricted to authenticated users)rI   bz24lunar_calendarT)groupByMonth)r  rM  r8  r  r  r  r  rI   r   r}   rw  r,  r?  rH  r   rI  r  rz  F)rB  r{  r  ry  r  rx  r|  rv  r~  N),rd   re   rf   r   r   r+  rk   rj   rN   r  r   r   r)  r*  iching.utilsrI   r  r  calc24CycleAdjustedYearranger  rD  r@   strptimerB   getCalendar10kGodEarthStemr   r   r   r  rM  getEarthElementconvertLunarri  calcJianChu12Godr  r  getStarNamecalcMonthFlyingStargetEarthAnimalcalcYearFlyingStarr   r   r.  ) rm   rn   r   r   r   current_yearallowed_yearsrI   r  r  r  r  r;  mdxkeyvalrA   bzdate	next_dater{  curendr  r8  demedaybzprevyearnextyearr  rN  r}   s                                    ro   rf   zCalendar10kAPIView.get  s   	kw++//:;Dqy*F GPVPkPkll  $$W&IJSYSnSnoo  ||~**||,,)A-|\A=MNM=( <\!^<LB|n\^_klm_m^n  oU  V  W!44 V	]== 11$T1JFI3v;' &q	 1HC"55   (00S;NOR[bcRddCH"*"3"3CKAT"UXahiXj"jCKs8D::499djjRVRZRZ\`\e\egkgrgrtxtt  AF+1F1IcN4(?A?Q?QRXY`RabeRf?gF1IcN4(1)< !$FIE"499djj$((CC"9>>9??IMMRC E)%3%@%@%E'
!$#!$#!$# !!'s!3B!'!5B"$q&BB"$q&BB),b	S"I F$&$7$7#))SWWV[$\&	 %Qq!I1$55) ).  #3x001DECH"%f+"6"67J"KCK.3F1IcN7+] !2` &)q	'"$&NN23I3I$))UYU_U_3`$aq	&!g (j $&#5#5fQi6H6Nv6VWZ6[#\Ii "$"3"3F1Ig4Ft4LV4TUX4Y"ZIh "r/D/DT/J KIf "&tAv%H!%tAv%H ((;H !11&)G2DT2J62RSV2WX //q	'0B40H0PQT0UVNN2+@+@+FG
 !$	M M**K I& 	kW&DEfNiNijj	kN  	]Wc!f-f6[6[\\	]s8   AS %I*S: E7S: ,S76S7:	T3%T.(T3.T3N)rq   rr   rs   rt   r
   r    rf   ru   rv   ro   r  r    s    T h"t]rv   r  z/Request account deletion for authenticated userz!Email where confirmation was sentr  r   z'Deletion request initiated successfullyreasonz$Optional reason for account deletionMAccount deletion request initiated. Please check your email for confirmation.c                   $    e Zd ZeegZegZeZ	d Z
y)AccountDeletionRequestViewc           	         | j                  |j                        }|j                  d       t        j                         t        d      z   }t        j                  j                  |j                  |d|j                  j                  dd            }|j                  t        d	d
|j                  i            }|j                  j                  xs |j                  j                   }|j                  |t        j                         j"                  t%        t&        dd      d}t)        d|      }d}	t+        |	t-        |      t&        j.                  |g|d       t1        d|dt2        j4                        S )Nr   Tr   r)  r  r  r  )r   
expires_at	is_activer  zaccounts:confirm_deletionr}  )r   
SITE_TITLEu   易经)r   confirmation_urlr  
site_titlez2accounts/emails/account_deletion_confirmation.htmlu   确认删除您的账户F)subjectr  rk  recipient_listhtml_messagefail_silentlyr  r  r	  )r   r   r   rN   r  rB   rS   r   r   r   r
  rf   build_absolute_urirQ   r}  r   r   r   rl   r   rP   r   rR   ru  r   r   r-  )
rm   rn   r   r  deletion_requestr  r   rB  r  r  s
             ro   r  zAccountDeletionRequestView.post  sZ   ((gll(;
D1 \\^ib&99
199@@!,,002>	 A 
 #55/BRBXBX8YZ

 ""8gll&8&8 LL 0$LLN//!(L(C	
 ((\^ef,|,22!7%	
 f
 $$& 	&rv   N)rq   rr   rs   rH   rE   r!   r   r    r2   r   r  ru   rv   ro   r  r  ^  s&    F @AVW)*7,&rv   r  c                       e Zd ZegZd Zy)AppVersionViewc                     |dvrt        ddid      S t        j                  j                  |dddd	      }t        |      S )
N)iosandroidr  zInvalid platformr   r	  z0.0.0Fz!Version information not available)versionenforcedescr)r   r   APP_VERSIONSrf   )rm   rn   platformversion_infos       ro   rf   zAppVersionView.get  sQ    --W&89#FF,,008<
  %%rv   N)rq   rr   rs   r
   r    rf   ru   rv   ro   r  r    s    "
&rv   r  number_analysis_getr  z"Get AI analysis for a Number chartzs
Retrieve the AI analysis for a Number chart if it exists.
If no analysis exists, returns a status of "not_found".
r  r  r  u   个人数字意义up   您的个人数字是4，代表成功和稳定。这个数字通常与勤奋、责任感和实用性相关联...)titlecontentu   性格优势uO   作为数字4的人，您具有出色的组织能力和严谨的逻辑思维...u   生命挑战u?   您可能面临的主要挑战是过于固执或墨守成规...u   事业路径分析u<   您适合需要精确、结构和可靠性的职业领域...u   人际关系模式u<   在关系中，您往往是可靠和值得信赖的伙伴...)personal_number_meaningcharacter_strengthslife_challengescareer_pathrelationship_patternsuE   请分析以下数字命盘，提供详细的解读和人生建议...r  r  u?   我需要分析这个数字命盘，首先整理基本信息...)number_analysisr  r  r  r  r  r  r  	not_foundNo analysis availabler   r  r  number_analysis_postr  z'Generate AI analysis for a Number charta  
Generate a new analysis (or regenerate an existing one) for a Number chart.

## Request Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `regenerate` | boolean | No | Set to `true` to force regeneration of an existing analysis. Default is `false`. |
| `provider` | string | No | LLM provider to use. Possible values: `groq`, `openai`. If not specified, uses the default from settings. |
| `model` | string | No | Specific model to use. See available models in the `/api/bazi/{id}/analysis/` endpoint documentation. If not specified, uses the default for the chosen provider. |

## Permission Requirements

Regeneration requires special permissions:
- Only users with the `can_regenerate_ai` privilege can regenerate analyses
- Without this privilege, users can only generate an analysis once per chart

Refer to the `/api/bazi/{id}/analysis/` endpoint documentation for the list of available models.
NumberAnalysisRequestr  r  r  r  z^Specific model identifier. If not specified, uses the default model for the selected provider.)r  r  r  r  r  r  r  r  z,Analysis successfully generated or retrievedr  r  r  1You do not have permission to regenerate analysis)r   r   Person not foundzNot Found Errorr  r  Failed to generate analysisz0Error message with details about what went wrongr  r  )r   r  r  r  r  c                 V
   	 t         j                  j                  |      }|j                  rb| j                  j
                  sL| j                  j                  r| j                  |j                  k7  rt        ddit        j                        S | j                  dk(  r|j                  st        ddd      S t        i d	d
d|j                  d|j                  d|j                  d|j                  d|j                  d|j                   d|j"                  d|j$                  d|j&                  d|j(                  d|j*                  d|j,                  d|j.                  d|j0                  d|j2                  d|j4                  d|j6                  i      S | j                  dk(  r>| j8                  j                  dd      }t;        |t<              r|j?                         dv }|r| j                  j                  st        dd it        j                        S | j                  j
                  s=| j                  j@                  jB                  st        dd!it        j                        S | j8                  j                  d"      }| j8                  j                  d#      }	 tE        d$      }|s|j                  d"      }|s|j                  d#      }	 tM        |||      }|t        d&d'd(t        jN                        S t        i d	d
d|d|j                  d|j                  d|j                  d|j                  d|j                   d|j"                  d|j$                  d|j&                  d|j(                  d|j*                  d|j,                  d|j.                  d|j0                  d|j2                  d|j4                  d|j6                  i      S y # tF        $ r#}tH        jK                  d%|        Y d }~8d }~ww xY w# tF        $ rl}tH        jQ                  d)t=        |              d|_        |jS                  d*g+       t        d&t=        |      d(t        jN                        cY d }~S d }~ww xY w# t         jT                  $ r  t        dd,it        jV                        cY S tF        $ rR}tH        jQ                  d-t=        |              t        d.t=        |      d(t        jN                        cY d }~S d }~ww xY w)/NrY  r  z0You do not have permission to access this personr	  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  F)r  yesr  rE  z(Authentication required for regenerationr  r  r  r  z,Could not get default AI config for number: r  zAnalysis generation failedr  z"Error generating Number analysis: number_analysis_statusr  r  zError in number_analysis view: zInternal server error),rz  r   rf   r  r   is_staffr)  r   r   r*  r   number_ai_analysisnumber_analysis_timestampr  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r   r  r   r{  r   r6  r[   r   r`  r  rW   r.  r  r   r  rv  )	rn   	person_idr  r  r  r  r  r}   r  s	            ro   r  r    s   lx9##''9'5 W\\%:%:<<00GLLFDUDU4UO! 335 5 >>U",,)6!  
  +F55 V== "6#@#@	
 +F,K,K '(C(C )&*G*G ()E)E ,V-M-M *6+I+I ,V-M-M )&*G*G %f&?&? '(C(C &v'A'A  *6+I+I!" ()E)E#$ *6+I+I%  * ^^v% )),>J*c*'--/3LL
||44#!K%$779 9 ||,,W\\5I5I5[5[#!T%$779 9
 ||''
3HLL$$W-ES&x0%zz*5H"JJw/E
'A)&%B##!>">% %CCE E
   !k!!  !A!A! &v'D'D	!
 /0O0O! +F,G,G! -f.K.K! ,V-I-I! 01Q1Q! .v/M/M! 01Q1Q! -f.K.K! )&*C*C! +F,G,G! *6+E+E!  .v/M/M!!" ,V-I-I#!$ .v/M/M%!  U &8  S!MaSQRRSF  AA#a&JK07-+C*DE:!!f! !??A AA "" -'
++- 	-  96s1vh?@,!f
 779 	99s   BR )R :C+R &BR )AR <6R 31O5 %,P$ C!P$ 5	P!>PR P!!R $	R-A!RRR RR 0T(T(AT#T(#T(liuyao_analysis_getz'Get AI analysis for a LiuYao divinationzx
Retrieve the AI analysis for a LiuYao divination if it exists.
If no analysis exists, returns a status of "not_found".
zFull response text from the LLMu'   本卦含义及与问题的关联分析u   所有动爻的解释及意义u$   本卦与变卦之间的关系分析u!   时间指示及季节影响分析u'   根据解读给求卦者的实用建议u3   本次预测的特殊考虑因素或不寻常特点)hexagram_interpretationchanging_linesrelationship_analysistiming_analysispractical_advicespecial_considerationsz'Full text of the prompt sent to the LLMzAI's thinking process)liuyao_analysissectionsr  r  r  r  liuyao_analysis_postz,Generate AI analysis for a LiuYao divinationa  
Generate a new analysis (or regenerate an existing one) for a LiuYao divination.

## Request Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `regenerate` | boolean | No | Set to `true` to force regeneration of an existing analysis. Default is `false`. |
| `provider` | string | No | LLM provider to use. Possible values: `groq`, `openai`. If not specified, uses the default from settings. |
| `model` | string | No | Specific model to use. If not specified, uses the default for the chosen provider. |
| `language` | string | No | Language for the analysis. Possible values: `zh-hans` (Chinese Simplified), `en` (English). Default is `zh-hans`. |

## Permission Requirements

Regeneration requires special permissions:
- Only users with the `can_regenerate_ai` privilege can regenerate analyses
- Without this privilege, users can only generate an analysis once per divination
LiuYaoAnalysisRequestr  r  )r  r  r  r  LiuYao divination not foundc                    	 t         j                  j                  |      }| j                  dk(  r|j                  dk(  r|j                  r{t	        d|j                  |j                  |j                  |j                  |j                  |j                  |j                  |j                  |j                   |j"                  d      S |j                  dk(  rt	        dd	d
      S |j                  dk(  rt	        ddd
      S t	        ddd
      S | j                  dk(  r| j$                  j                  dd      }t'        |t(              r|}n|j+                         dk(  }|rn|j                  dk(  r_|j                  rSt-        | j.                  d      r | j.                  j0                  j2                  st	        ddit
        j4                        S | j$                  j                  dd      }| j$                  j                  dd      }| j$                  j                  dd      }ddlm}  ||      }	 t;        d      }	|s|	j                  d      }|s|	j                  d      }	 ddl!m"} d|_        |jG                  dg        |||||       }|r/t	        d||j                  |j                  |j                  d!      S t	        ddd
t
        jH                        S y# t         j                  $ r  t	        ddit
        j                        cY S w xY w# t<        $ r"}
t>        jA                  d|
        Y d}
~
d}
~
ww xY w# t<        $ rn}
t>        jK                  d"tM        |
       d#$       d|_        |jG                  dg       t	        d%tM        |
      d&t
        jH                        cY d}
~
S d}
~
ww xY w)'z4Get or generate AI analysis for a LiuYao divination.r  r  r  r	  r  r  )r   r  r  r  analysis_reportedreport_statusreport_categoryreport_messagereport_admin_notesreport_timestampreport_resolved_atr  zAnalysis is being generatedr  r  r  r  r  r  r  r  r   r  r  Nr  r  r  r   r  rD   z,Could not get default AI config for liuyao: )analyze_liuyaor  r  r  )r   r  r  r  r  z"Error generating LiuYao analysis: Tr  r  r  )'rD   r   rf   r  r   r   rv  r   r  r  r  r  r  r  r  r  r  r  r   r  r  r{  r  r   r   r6  r*  r  r  r[   r   r`  r  ai.utils.liuyao_analysisr  r   r.  r  r   )rn   r  r  r  r  r  r  r  r  r  r}   r  r  s                ro   r  r    s   Bd^^''2'.

 ~~%%49O9O%&22'::#-#=#=%/%A%A!+!9!9#-#=#=",";";&0&C&C$.$?$?&0&C&C   ''94#8   ''72!6  
 %2   
6	!"<<++L'B&-)J)//1V;J*44C
H^H^7<<37<<;O;O;a;aP! 335 5
 <<##J5  $/<<##J	: 	4$X.	O"8,F!::j1

7+
&	=? *3J&OO+<*=O> $J%(]efF ) &!+!>!>'1'A'A)3)E)E!    %:! !??A Aq 
"K  d"?@IbIbccdH  	ONNI!MNN	O<  	=LL=c!fXFQULV *1J&OO+<*=O> 6a& ;;= =	=sO    K" 41L &AM M "0LL	M!L>>M	N=A#N82N=8N=z-Create a temporary user and return JWT tokensTempUserCreateResponsezTemporary user IDz,JWT access token (permanent - never expires)z-JWT refresh token (permanent - never expires)z"Indicates this is a temporary userz?Access token expiry time in seconds (null for permanent tokens))rz   r  z5Indicates if the session is permanent (never expires)rX  r   r   is_temporaryr  
expires_inis_permanent_sessionz@Gender must be 'M' (male), 'F' (female), or 'N' (not specified).)r   r   zCreate Temp User with Full Datau   测试用户)r   r   r   r   r   z"Create Temp User with Minimal DatazSimple UserzCreate Empty Temp User{   #Temporary user created successfullyc                        e Zd ZdZegZeZd Zy)TempUserCreateViewa  
    Create a temporary user with optional profile data and return JWT tokens.
    
    This endpoint allows public access (no authentication required) and creates
    a temporary user account that can be used to store divination records.
    All input fields are optional.
    c           
         d|j                   v rt        ddgit        j                        S d|j                   v rt        ddgit        j                        S | j	                  |j                         }|j                  d       	 i }|j                  }d	D ]  }||v s||   ||<    d
|v r|d
   j                  d      |d
<   d|v r|d   j                  d      |d<   d|v r|d   j                  d      |d<   d|v r|d   j                  d      |d<   ddlm	}  ||      }ddl
m}  ||      }	t        |j                  |	d   |	d   dd|	d   |	d   dt        j                        S # t        $ rK}
t        j!                  d|
        t        ddt#        |
       it        j$                        cY d }
~
S d }
~
ww xY w)Nr   zSPhone field is not allowed. Phone numbers are system-generated for temporary users.r	  r   zUEmail field is not allowed. Email addresses are system-generated for temporary users.r   Tr   )r   r   r   
birth_cityrN   r   z%Y-%m-%dr   z%H:%Mr   r   r   )create_temporary_user)create_tokens_for_userr   r   r  r  is_permanentr  zError creating temporary user: r  z!Failed to create temporary user: )r   r   r   r+  r   r   r
  r  r,  r  main.tokensr  r   r   r   r`  r  r   r.  )rm   rn   r   rL  r
  rI  r  r/  r  
token_datar}   s              ro   r  zTempUserCreateView.post  s   gll"op113 3 gll"qr113 3 ((gll(;
D1.	I'66N SN*'5e'<Ie$ S
 ~-*8*F*O*OPZ*[	,'~-*8*F*O*OPW*X	,'~-*8*F*O*OPZ*[	,'~-*8*F*O*OPZ*[	,' =-i8I ;/	:J$<<$X.%i0 $@(6(2>(B --/ /  	LL:1#>?=c!fXFG<< 	s&   E+  C
E+ +	F?4A F:4F?:F?N)	rq   rr   rs   rt   r
   r    r5   r   r  ru   rv   ro   r  r  F  s    L #/=rv   r  z+Convert JWT token to session authenticationJWTToSessionRequestr   z&JWT access token to convert to sessionJWTToSessionResponsez%Indicates successful session creationzUser ID for the sessionz%Indicates if this is a temporary usersuccessr  rX  r  zInvalid or missing access tokenzMissing TokenAccess token is requiredInvalid or expired access tokenzInvalid TokenzConvert JWT to Session/Session authentication established successfullyc                       e Zd ZdZegZd Zy)JWTToSessionViewz
    Convert a JWT access token to session authentication.
    
    This endpoint allows converting JWT authentication to Django session 
    authentication for web applications that need both token and session support.
    c                    |j                   j                  d      }|st        ddit        j                        S 	 ddlm} ddlm}m	}  ||      }|j                  j                  d      }|st        dd	it        j                        S  |       }	 |j                  j                  |
      }	 |||	       t        dd|	j                  |	j                  dt        j                         S # |j                  $ r  t        ddit        j                        cY S w xY w# t"        $ r?}
t$        j'                  d|
        t        ddit        j                        cY d }
~
S d }
~
ww xY w)Nr   r  r  r	  r   )AccessToken)r   loginrX  zInvalid token payloadrY  rZ  Tr	  r  z!Error converting JWT to session: r  )r   rf   r   r   r+  r\  r  django.contrib.authr   r  r^  r  r   r  r   r%  r-  r   r`  r  )rm   rn   r   r  r   r  r}  rX  r  r   r}   s              ro   r  zJWTToSessionView.post&  sj   ||''73113 3$	4CA-Emm''	2G4! 668 8
 "#D8||''7'3 '4 L77 $ 6 6	
 ((* * $$ 8-! 668 88  	4LL<QC@A:224 4	4sH   AD D C- 0<D -,DD DD 	E'(4E"E'"E'Nrb  ru   rv   ro   r  r    s    B #,4rv   r  report_bazi_analysiszReport BaZi AnalysiszFSubmit a report for inappropriate or problematic BaZi analysis contentzReport submitted successfullyz+Invalid data or analysis cannot be reportedzAnalysis not foundz&Not authorized to report this analysis)r   r   r3  r  r  )r  r  r   rn   r   c                   :     e Zd ZdZeegZegZe	Z
d Z fdZ xZS )BaziReportViewz!Submit a report for BaZi analysisc                    | j                   d   }	 t        j                  j                  || j                  j
                        }|j                         st        d      |j                  d   }|j                  j                  dd      }|j                  ||| j                  j
                        }|st        d	      d
dlm}  ||d||      }|sddlm}	 t        |	dd      xs. dt        |	dd      v xs dt        |	dd      v xs t!        |	d      }
|
s:dd l} |j$                  t&              }|j)                  d|j*                   d       y y y # t        j                  $ r t        d      w xY w)Nr  r   r  zABaZi analysis not found or you don't have permission to report itzIThis BaZi analysis cannot be reported (not completed or already reported)categoryr  r  r  r  reported_byFailed to submit reportr#   send_admin_report_notificationr  r   r   TESTINGFtestEMAIL_BACKENDlocmemTEST_RUNNERz>Failed to send admin notification for BaZi report (Person ID: ))r   rz  r   rf   rn   r   r  r   can_report_bazi_analysisr
  r  rL   r  django.confr   rl   r  logging	getLoggerrq   r  r   rm   r   r  r  r  r  r  r  
email_sentr   should_be_silentr#  r`  s                ro   r  zBaziReportView.perform_createk  s   KK%		g''++yT\\EVEV+WF
 ..0!"mnn ,,Z8++//	2>-- )) . 
 !";<< 	:3FFHgV
 -)U3 1'(OR@@1GHorBB1 -0	  $***84!_`f`i`i_jjklm $ / && 	g!"eff	g   5E E4c                     	 t        |   |g|i |}t        dddt        j                        S # t
        $ r0}t        dt        |      it        j                        cY d }~S d }~ww xY w)NTz+BaZi analysis report submitted successfullyr  r  r	  r  r  r   r   r   r   r   r   r+  rm   rn   r   r   r   r}   r  s         ro   r   zBaziReportView.create  sy    		3w~g???HH --/ /  	3Q113 3	3   04 	A-%A("A-(A-rq   rr   rs   rt   rH   rE   r!   r   r    r6   r   r  r   r  r  s   @ro   r  r  W  s4     ,?AVW)*1*nX
3 
3rv   r  report_number_analysiszReport Number AnalysiszHSubmit a report for inappropriate or problematic Number analysis contentc                   :     e Zd ZdZeegZegZe	Z
d Z fdZ xZS )NumberReportViewz#Submit a report for Number analysisc                    | j                   d   }	 t        j                  j                  || j                  j
                        }|j                         st        d      |j                  d   }|j                  j                  dd      }|j                  ||| j                  j
                        }|st        d	      d
dlm}  ||d||      }|sddlm}	 t        |	dd      xs. dt        |	dd      v xs dt        |	dd      v xs t!        |	d      }
|
s:dd l} |j$                  t&              }|j)                  d|j*                   d       y y y # t        j                  $ r t        d      w xY w)Nr  r  zCNumber analysis not found or you don't have permission to report itzKThis Number analysis cannot be reported (not completed or already reported)r  r  r  r  r  r#   r  r  r   r   r  Fr  r  r  r  z@Failed to send admin notification for Number report (Person ID: r   )r   rz  r   rf   rn   r   r  r   can_report_number_analysisr
  r/  rL   r  r"  r   rl   r  r#  r$  rq   r  r   r%  s                ro   r  zNumberReportView.perform_create  s   KK%		i''++yT\\EVEV+WF
 002!"opp ,,Z8++//	2>// )) 0 
 !";<< 	:3FHhPWX
 -)U3 1'(OR@@1GHorBB1 -0	  $***84!abhbkbkallmno $ / && 	i!"ghh	ir(  c                     	 t        |   |g|i |}t        dddt        j                        S # t
        $ r0}t        dt        |      it        j                        cY d }~S d }~ww xY w)NTz-Number analysis report submitted successfullyr*  r	  r  r+  r,  s         ro   r   zNumberReportView.create  y    		3w~g???HJ --/ /  	3Q113 3	3r-  r.  r  s   @ro   r1  r1    s4     .?AVW)*1*pX
3 
3rv   r1  report_liuyao_analysiszReport LiuYao AnalysiszHSubmit a report for inappropriate or problematic LiuYao analysis contentc                   :     e Zd ZdZeegZegZe	Z
d Z fdZ xZS )LiuYaoReportViewz#Submit a report for LiuYao analysisc                    | j                   d   }	 t        j                  j                  || j                  j
                        }|j                         st        d      |j                  d   }|j                  j                  dd      }|j                  ||| j                  j
                        }|st        d	      d
dlm}  ||d||      }|sddlm}	 t        |	dd      xs. dt        |	dd      v xs dt        |	dd      v xs t!        |	d      }
|
s:dd l} |j$                  t&              }|j)                  d|j*                   d       y y y # t        j                  $ r t        d      w xY w)Nr  r   r   zCLiuYao analysis not found or you don't have permission to report itzKThis LiuYao analysis cannot be reported (not completed or already reported)r  r  r  r  r  r#   r  rD   r   r   r  Fr  r  r  r  z@Failed to send admin notification for LiuYao report (LiuYao ID: r   )r   rD   r   rf   rn   r   r  r   can_report_analysisr
  report_analysisrL   r  r"  r   rl   r  r#  r$  rq   r  r   )rm   r   	liuyao_idr  r  r  r  r  r&  r   r'  r#  r`  s                ro   r  zLiuYaoReportView.perform_create  s   KK%		i++yt||?P?P+QJ
 --/!"opp ,,Z8++//	2>,, )) - 
 !";<< 	:3J(T[\
 -)U3 1'(OR@@1GHorBB1 -0	  $***84!ablboboappqrs $ / "" 	i!"ghh	ir(  c                     	 t        |   |g|i |}t        dddt        j                        S # t
        $ r0}t        dt        |      it        j                        cY d }~S d }~ww xY w)NTz-LiuYao analysis report submitted successfullyr*  r	  r  r+  r,  s         ro   r   zLiuYaoReportView.create1  r5  r-  r.  r  s   @ro   r8  r8    s4     .?AVW)*1*tX
3 
3rv   r8  )ConversationMessageConversationSubject)create_conversationcreate_bazi_conversationcreate_liuyao_conversationsend_conversation_messagez$List conversations for a BaZi recordzTGet all conversations for a specific BaZi record (Person) for the authenticated user)r   r3  r  c                    	 t         j                  j                  || j                        }t        j                  d|      }t        j                  j                  || j                        j                  d      j                  d      }t        |d	
      }t        t        |j                         |j                   d      S # t         j                  $ r  t        ddit        j                        cY S w xY w)z(List all conversations for a BaZi recordr  r  :Person not found or you don't have permission to access itr	  r  r  r   messages-updated_atTr  r  r  )rz  r   rf   r   r  r   r   rv  rA  get_or_create_subjectr?  r  prefetch_relatedr  r;   r  r   )rn   r  r  r  conversationsr   s         ro   list_conversationsrO  D  s    
##''9'N "77	JG ((//\\ 0  z"88M#: 
 ,MEJZ__%??   "" 
RS,,
 	

   +C 0C76C7zGet conversation detailzBGet full conversation with all messages for a specific BaZi recordConversation not foundc                    	 t         j                  j                  || j                        }t        j                  d|      }	 t        j                  j                  ||| j                        }t        |      }t        |j                        S # t         j                  $ r  t        ddit        j                        cY S w xY w# t        j                  $ r  t        ddit        j                        cY S w xY w))Get conversation detail with all messagesr  r  rG  r	  r  r   r  r   rQ  )rz  r   rf   r   r  r   r   rv  rA  rL  r?  r:   r   )rn   r  conversation_idr  r  conversationr   s          ro   get_conversationrW  h  s    
##''9'N "77	JG

#++// 0 
 (5JJOO$$+ "" 
RS,,
 	

 $$ 
./,,
 	

#   +B ,C 0CC0C98C9zCreate conversationz2Create a new conversation thread for a BaZi record)r   r3  r  )r  r   rn   r   c                 V   	 t         j                  j                  || j                        }t        | j                  |d      }t        |      }t        |j                  t        j                        S # t         j                  $ r  t        ddit        j                        cY S w xY w)z+Create a new conversation for a BaZi recordr  r  rG  r	  N)r   r  r  )rz  r   rf   r   r  r   r   rv  rC  r:   r   r   )rn   r  r  rV  r   s        ro   create_conversation_viewrZ    s    
##''9'N ,\\L (5JJOOF,C,CDD! "" 
RS,,
 	

   +A5 50B('B(zSend messagez9Send a user message and get AI response in a conversationSendMessageResponserU  r  assistant_messagez)Validation error or message limit reachedz-Permission denied for provider/model override)r   r   r3  r  r  c           
      0   	 t         j                  j                  || j                        }t        j                  d|      }	 t        j                  j                  ||| j                        }t        | j                        }|j                         s%t        |j                  t        j                        S d	d
lm} |j%                         }|j'                         }	|	|k\  r!t        dd| dit        j                        S |j(                  j                  d      }
|j(                  j                  d      }|
s|rSt+        | j                  d      r | j                  j,                  j.                  st        ddit        j0                        S 	 t3        ||j(                  d   |
||j(                  j                  dd            }|j4                  j7                  d      j9                  d      j;                         }t        |j<                  t?        |      j                  t?        |      j                  d      S # t         j                  $ r  t        ddit        j                        cY S w xY w# t        j                  $ r  t        ddit        j                        cY S w xY w# t@        $ r}tB        jE                  dtG        |       d       |j4                  j7                  dd      j9                  d      j;                         }|rLt        |j<                  t?        |      j                  ddtG        |      d t        jH                        cY d}~S t        dtG        |      d!t        jH                        cY d}~S d}~ww xY w)"z Send a message in a conversationr  r  rG  r	  r  rT  rQ  r   r   ConversationConfig4Conversation has reached the maximum message limit (r   r  r  r   8You do not have permission to override AI provider/modelr  r  r  rV  r  r  r  r  r   roler  r]  $Error sending conversation message: Tr  failedrf  r   NFailed to send messagerU  r  r^  r  r  r  )%rz  r   rf   r   r  r   r   rv  rA  rL  r?  r=   r   r   r  r+  	ai.modelsra  get_max_messagesget_message_countr
  r  r   r6  r*  rE  rI  r  r  r  r   r>   r   r`  r  r   r.  )rn   r  rU  r  r  rV  r   ra  max_messagescurrent_message_countr  r  assistant_msguser_msgr}   failed_user_msgs                   ro   send_messagert    s3   0
##''9'N "77	JG

#++// 0 
 'GLL9J 
))&2M2MNN -%668L(::<,L\NZ[\]..
 	
 ((,,Z8H%%))'2E5w||Y/w||7K7K7]7]TU00 
$1%#229=..22:yI
  ((//V/<EEmTZZ\+-h7<<!2=!A!F!F
  	y "" 
RS,,
 	

 $$ 
./,,
 	

f  ;CF8DtT '//66F86T]]^klrrt#/?? 1/ B G G%)1a& ;;= = 2c!fE<< O   +I ,I: B.J0 0I76I7:0J-,J-0	N9B%NN$&N
NNzRetry failed messagezRetry sending a failed message. Allows editing the message content. Resends the message along with all previous messages to maintain context.RetryMessageRequestz?Message ID to retry (optional, defaults to last failed message)r  zMNew message content (optional, if not provided uses existing message content))
message_idr  RetryMessageResponser  z!Conversation or message not found)r   r   r3  r  c           
         	 t         j                  j                  || j                        }t        j                  d|      }	 t        j                  j                  ||| j                        }| j                  j                  d      }|r	 |j                  j                  |d	
      }nu|j                  j                  d	d      j                  d      j!                         }|s9|j                  j                  d	      j                  d      j!                         }|st        ddit        j"                        S | j                  j                  d|j$                        }|j                  j                  d      j                  d      j!                         }	|	r(|	j&                  r|	j&                  j                  dd      nd}
| j                  j                  d      }| j                  j                  d      }|s|	r|	j(                  }|s|	r|	j*                  }	 t-        |||||
|      }|j/                          t        |j0                  t3        |      j                  t3        |      j                  d      S # t         j                  $ r  t        ddit        j                        cY S w xY w# t        j                  $ r  t        ddit        j                        cY S w xY w# t        j                  $ r  t        ddit        j                        cY S w xY w# t4        $ r}t6        j9                  dt;        |       d       |j/                          |}|rLt        |j0                  t3        |      j                  ddt;        |      dt        j<                        cY d}~S t        dt;        |      dt        j<                        cY d}~S d}~ww xY w) zRetry sending a failed messager  r  rG  r	  r  rT  rQ  rw  r   r   rf  Message not foundrh  ri  r  re  No user message found to retryr  	assistantr  r  r  r  rV  r  r  r  r  existing_user_messager]  %Error retrying conversation message: Tr  NFailed to retry messagerk  r  )rz  r   rf   r   r  r   r   rv  rA  rL  r?  r   rI  r@  r  r  r  r+  r  metar  r  rE  rH  r   r>   r   r`  r  r   r.  )rn   r  rU  r  r  rV  rw  message_to_retrynew_message_contentlast_assistant_msgr  r  r  rq  r}   rs  s                   ro   retry_messager  $  s   :
##''9'N "77	JG

#++// 0 
 !!,/J	+4488JV8T (0077VH7U^^_lmssu+44;;;HQQR_`ffh67..
 	
 ",,**96F6N6NO &..55;5GPPQ^_eegEW\n\s\s!&&**:yA  zCH ||
+HLLW%E*%..'"(() 2%,"2
 	((*+-.>?DD!2=!A!F!F
  	Q "" 
RS,,
 	

 $$ 
./,,
 	

 ## 	-.00 	h  <SVHEPTU 	((* +#/?? 1/ B G G%)2a& ;;= = 3s1vF<< %a   +J ,J8 K. "AL$ 0J54J580K+*K+.0L! L!$	O!-A=O*O!0&OO!O!z&List conversations for a LiuYao recordzMGet all conversations for a specific LiuYao record for the authenticated userzLiuYao record not foundc                    	 t         j                  j                  || j                        }t        j                  d|      }t        j                  j                  || j                        j                  d      j                  d      }t        |d	
      }t        t        |j                         |j                   d      S # t         j                  $ r  t        ddit        j                        cY S w xY w)z*List all conversations for a LiuYao recordr:  r  ALiuYao record not found or you don't have permission to access itr	  rD   rH  rI  rJ  Tr  rK  )rD   r   rf   r   r  r   r   rv  rA  rL  r?  r  rM  r  r;   r  r   )rn   r=  r  r  rN  r   s         ro   list_liuyao_conversationsr    s    
^^''97<<'H
 "77)LG ((//\\ 0  z"88M#: 
 ,MEJZ__%??    
YZ,,
 	

rP  zGet LiuYao conversation detailzDGet full conversation with all messages for a specific LiuYao recordc                    	 t         j                  j                  || j                        }t        j                  d|      }	 t        j                  j                  ||| j                        }t        |      }t        |j                        S # t         j                  $ r  t        ddit        j                        cY S w xY w# t        j                  $ r  t        ddit        j                        cY S w xY w)rS  r:  r  r  r	  rD   rT  rQ  )rD   r   rf   r   r  r   r   rv  rA  rL  r?  r:   r   )rn   r=  rU  r  r  rV  r   s          ro   get_liuyao_conversationr    s    
^^''97<<'H
 "77)LG

#++// 0 
 (5JJOO$$+  
YZ,,
 	

 $$ 
./,,
 	

rX  zCreate LiuYao conversationz4Create a new conversation thread for a LiuYao recordc                 V   	 t         j                  j                  || j                        }t        | j                  |d      }t        |      }t        |j                  t        j                        S # t         j                  $ r  t        ddit        j                        cY S w xY w)z-Create a new conversation for a LiuYao recordr:  r  r  r	  N)r   rD   r  )rD   r   rf   r   r  r   r   rv  rD  r:   r   r   )rn   r=  r  rV  r   s        ro   create_liuyao_conversation_viewr    s    
^^''97<<'H
 .\\L (5JJOOF,C,CDD  
YZ,,
 	

r[  z Send LiuYao conversation messagez@Send a user message and get AI response in a LiuYao conversationc           
      0   	 t         j                  j                  || j                        }t        j                  d|      }	 t        j                  j                  ||| j                        }t        | j                        }|j                         s%t        |j                  t        j                        S d	d
lm} |j%                         }|j'                         }	|	|k\  r!t        dd| dit        j                        S |j(                  j                  d      }
|j(                  j                  d      }|
s|rSt+        | j                  d      r | j                  j,                  j.                  st        ddit        j0                        S 	 t3        ||j(                  d   |
||j(                  j                  dd            }|j4                  j7                  d      j9                  d      j;                         }t        |j<                  t?        |      j                  t?        |      j                  d      S # t         j                  $ r  t        ddit        j                        cY S w xY w# t        j                  $ r  t        ddit        j                        cY S w xY w# t@        $ r}tB        jE                  dtG        |       d       |j4                  j7                  dd      j9                  d      j;                         }|rLt        |j<                  t?        |      j                  ddtG        |      d t        jH                        cY d}~S t        dtG        |      d!t        jH                        cY d}~S d}~ww xY w)"z'Send a message in a LiuYao conversationr:  r  r  r	  rD   rT  rQ  r   r   r`  rb  r   r  r  r   rc  r  r  r  rd  r   re  r  r]  rg  Tr  rh  ri  Nrj  rk  r  )%rD   r   rf   r   r  r   r   rv  rA  rL  r?  r=   r   r   r  r+  rl  ra  rm  rn  r
  r  r   r6  r*  rE  rI  r  r  r  r   r>   r   r`  r  r   r.  )rn   r=  rU  r  r  rV  r   ra  ro  rp  r  r  rq  rr  r}   rs  s                   ro   send_liuyao_messager    s1   0
^^''97<<'H
 "77)LG

#++// 0 
 'GLL9J 
))&2M2MNN -%668L(::<,L\NZ[\]..
 	
 ((,,Z8H%%))'2E5w||Y/w||7K7K7]7]TU00 
$1%#229=..22:yI
  ((//V/<EEmTZZ\+-h7<<!2=!A!F!F
  	y  
YZ,,
 	

 $$ 
./,,
 	

f  ;CF8DtT '//66F86T]]^klrrt#/?? 1/ B G G%)1a& ;;= = 2c!fE<< ru  zRetry failed LiuYao messagezRetry sending a failed message in a LiuYao conversation. Allows editing the message content. Resends the message along with all previous messages to maintain context.c           
         	 t         j                  j                  || j                        }t        j                  d|      }	 t        j                  j                  ||| j                        }| j                  j                  d      }|r	 |j                  j                  |d	
      }nu|j                  j                  d	d      j                  d      j!                         }|s9|j                  j                  d	      j                  d      j!                         }|st        ddit        j"                        S | j                  j                  d|j$                        }|j                  j                  d      j                  d      j!                         }	|	r(|	j&                  r|	j&                  j                  dd      nd}
| j                  j                  d      }| j                  j                  d      }|s|	r|	j(                  }|s|	r|	j*                  }	 t-        |||||
|      }|j/                          t        |j0                  t3        |      j                  t3        |      j                  d      S # t         j                  $ r  t        ddit        j                        cY S w xY w# t        j                  $ r  t        ddit        j                        cY S w xY w# t        j                  $ r  t        ddit        j                        cY S w xY w# t4        $ r}t6        j9                  dt;        |       d       |j/                          |}|rLt        |j0                  t3        |      j                  ddt;        |      dt        j<                        cY d}~S t        dt;        |      dt        j<                        cY d}~S d}~ww xY w) z7Retry sending a failed message in a LiuYao conversationr:  r  r  r	  rD   rT  rQ  rw  r   rz  r{  rh  ri  r  re  r|  r  r}  r  r  r  r  r~  r]  r  Tr  Nr  rk  r  )rD   r   rf   r   r  r   r   rv  rA  rL  r?  r   rI  r@  r  r  r  r+  r  r  r  r  rE  rH  r   r>   r   r`  r  r   r.  )rn   r=  rU  r  r  rV  rw  r  r  r  r  r  r  rq  r}   rs  s                   ro   retry_liuyao_messager    s   :
^^''97<<'H
 "77)LG

#++// 0 
 !!,/J	+4488JV8T (0077VH7U^^_lmssu+44;;;HQQR_`ffh67..
 	
 ",,**96F6N6NO &..55;5GPPQ^_eegEW\n\s\s!&&**:yA  zCH ||
+HLLW%E*%..'"(() 2%,"2
 	((*+-.>?DD!2=!A!F!F
  	Q  
YZ,,
 	

 $$ 
./,,
 	

 ## 	-.00 	h  <SVHEPTU 	((* +#/?? 1/ B G G%)2a& ;;= = 3s1vF<< %r  zGet conversation configurationzNGet the current conversation configuration settings (e.g., max_messages limit)Conversations)r  r   r  r   c                     ddl m} |j                         }t        d|i      }t	        |j
                  t        j                        S )z*Get the current conversation configurationr   r`  ro  r	  )rl  ra  rm  r?   r   r   r   r-  )rn   ra  ro  r   s       ro   get_conversation_configr  
  sB     -%668L-/ J JOOF,>,>??rv   )django.shortcutsr   rest_frameworkr   r   r   r   rest_framework.responser   rest_framework.viewsr	   rest_framework.permissionsr
   r   r\  r   r  r   r   rq  r   django.core.mailr   django.db.modelsr   r"  r   django.core.exceptionsr   drf_spectacular.utilsr   r   r   r   r   r   r   r   rest_framework.decoratorsr   r    r!   bazi.modelsr"   rz  r$   r%   r&   r'   r(   r)   r*   r+   r,   r-   r.   r/   r0   r1   r2   r3   r4   r5   r6   r7   r8   r9   r:   r;   r<   r=   r>   r?   r@   rA   rB   calendarrC   liuyao.modelsrD   rest_framework.authenticationrE   django.views.decorators.csrfrF   django.utils.decoratorsrG   main.authenticationrH   r  rI   r  r  iching.utils.bazi_relationsrJ   iching.utils.numberpowerrK   main.visitorrL   r  rN   rest_framework.paginationrO   django.template.loaderrP   django.urlsrQ   django.utils.htmlrR   rG  rS   rU   rV   r#  ai.utils.number_analysisrW   django.httprX   rest_framework.exceptionsrY   ai.services.factoryrZ   ai.utils.configr[   r$  rq   r`  r]   r  
Serializerrx   r   r   r   r   r   r   r   r   r   r  r   r  rd   CreateAPIViewr   GenericAPIViewr  r   r   r"  r@  rS  rd  rz  r  RetrieveUpdateAPIViewr  r  ModelViewSetr  r  PATHNONEro  r%  ra  r  r   r  r  r  r   r  r  r{   r  r  r  r1  r8  rl  r?  r@  rA  ai.utils.conversationrB  rC  rD  rE  rO  rW  rZ  rt  r  r  r  r  r  r  r  ru   rv   ro   <module>r     s!3   # > > , ( @ 8 < > &    2 X  X  X & Z Z ,       > / .    ? 4 4 A : 5 0  ! : 3  ( 2 ' ,  3  6 1 ) 4 4 
		8	$G!5 G" 
Lk44 LN;11 N={-- =:[33 :OK** O>;11 >C 6 6 CPk44 PN.. NY{'='= Y
 %%++Q!&	
 	%++]!,	
 	%++n$)	
 	%++E1)	
 	%++R& 	
 	%++Q%	
 	%++W"	
 	%++K&&	
 	%++F&!	
 	%++n%	
 	%++j&&	
 	%++j&&	
a]~ /6& #$%1%7*0).&).:.5-..:.:(! )R)R#
8 8&";!<%
 #A!A#<"=&W%X'T&U'T&U
;2h 	 %++,$"".")!"".".  %	
ijV)<8)) )<WjV)<V /X$5HI#4FG6  D'' DDL ^*[**5MN---8LM
 $/+//:LM0;00;NO$9K$9$9Da$b0;00;LM	
 )PQ)ST 	#CD$%&'*/%
 i	 	
-&NoH++ oO&Nob s"'&(/+//:fg0;00;hi0;00;LM	
 )<=)BC)ST 	'%+ 	
 	=%+,-$"".",!"".".)." '	
* 	?%3$$".",!"".	 	
" 	5 )/"(!&!&2&0%&&2&2-2   DDl'* /	
2 	; )7"(!(!&2&0%&&2&*-2   N#& +	
Q_#qdsx.. seqdsj ;",{,,G
 '/+//:LM0;00;de
 )KL	&:X44 :'&:x .XH5FG6  0x66 00d 3XH5FG6  hx66 hhB 8$H=NO.  8	
 -4?Q  cG  %H)0FH;M^~ .  ,
 @
& 	"$6 1!2
 	
 	#>?		
 	#%E$F!@ A 	
!+/`
00 
a/`
& .#E+!-!3&,%*"%*6*4)**6*616$  #'"''
6 @
9"F!h44 !G"F! +J/HSg HS 0HSV 	) X)// ":+ #W2	" )//3"$  )//i"+  #+ 	?0
d )t44"2 ##%? #$J '	
 !# !# !# !% !# !# &'< ' #  %Z )*D /0J  #A!
iQ
d *H)3"4 ##%? #$J '	
 !# !# !# !% !# !# &'< ' #  %Z )*D /0J  #A!
 B ( D',7 /4 /T "
( )3"5 ##%? #$J '	
 !# !# !# !% !# !# &'< ' #  %Z )*D /0J  #A!
/4j (m',7 27 2T "
	0 !2w'- 75 "	
	 (@aTj +J/7H)) 7 0kTl7r 	& U)// C"($ #+%	" )//M"($ )// q":+ #K0	" )//3"$  )//i"+  #+ 	{N
` 'T21"0"#$4&)*6*4%4*@*@01A(>12Q)?/01'=01A(>	, .v*+!
eo
` ' L)..\
 $$$$

 *,!".",,"8"8"# R ] ##W!
!$J <]| &!
 $$$$$$
 -,!".",!$".".,	 " 7"!".", $)%*-2'/ ,1/7!"
 ,1/7!"
 ,1/7!", &*'()*+!0 $)%*-2'/ ,1/7!"
 ,1/7!"
 ,1/7!", &+'()*+"0 $)%*-2'/ ,1/7!", &+'()*   $)%*-2'/ ,1/7!"
 ,1/7!"	, &+'()+#!* !'0845%&
 1?45%&
 1?45%&)"%" !'0845%&
 1945%&	)"& !'0845%&
 1?45%&
 1945%&
 1945%&
 1945%&#)"$2 !'0845%&)"%k=%cO`/!:/ !/ $V/ %o	/
 / " $$$'$,/$47$<?$DG$$$'$,/$47$<?$DG$  $ %($ -0$ 58$ =@$ JK!"%&Aq$/ #L/ !// !!/  '!/" %!"!"!"!"!"'#/0 %o1/2 &q3/4 "75/6 #O7/8 #!"!"!"!"!"%9/F #OG/H$$$'$,/$47$<?$DG$$$'$,/$47$<?$DG$  $ %($ -0$ 58$ =@$ JK!"%&$$W/` "#"?"?PUKX ##W_Pb 5"!".", $)%*-2'/ ,1/7!"
 ,1/7!"
 ,1/7!", &*'()*+!0 $)%*-2'/ ,1/7!"
 ,1/7!"
 ,1/7!", &+'()*+"0 $)%*-2'/ ,1/7!", &+'()*   $)%*-2'/ ,1/7!"
 ,1/7!"	, &+'()+#!* !'0845%&
 1?45%&
 1?45%&)"%" !'0845%&
 1945%&	)"& !'0845%&
 1?45%&
 1945%&
 1945%&
 1945%&#)"$2 !'0845%&)"%k=%cO`/!:/ !/ $V/ %o	/
 / " $$$'$,/$47$<?$DG$$$'$,/$47$<?$DG$  $ %($ -0$ 58$ =@$ JK!"%&Aq$/ #L/ !// !!/  '!/" %!"!"!"!"!"'#/0 %o1/2 &q3/4 "75/6 #O7/8 #!"!"!"!"!"%9/F #OG/H$$$'$,/$47$<?$DG$$$'$,/$47$<?$DG$  $ %($ -0$ 58$ =@$ JK!"%&$$W/`  GMGP ##WWLZ 'M ##W[u
S_@ % E%)..Z
 $$$$

 *,!".",!$".".,	 " ,,!".",,"8"8"# R ] ##W!!
#3h !/ {%)..Z
 $$$$

 -,@ " 6,!".",@"8"8"# R ] ##W!
#-\ %=)..Z
 ""$$
qiT +J/OY('' OY 0UiVOYbhIW hIT	wIw wIr .,\ 	%++O"&(,,.-- !&(,,.--1		
& '*>7 $'-&+$*& *+)1)>,A 23131*
 23120*
 23127=2* 23121*+&, 6756598=	6. 23121245)* 6756598=	6. 23121245)*.&12*"h *+)1)>,A 23121*
 23130*
 23127=2* 23121*+&& .01+"4 *,(._PR#h %)$(%u^~ #(Cbdh
R 5'"$@A"'
 '"$GH"'
 b,"$xy"'	
sCH OHRx] x]SHRx]t A,$,=NO"*;^_.  B
 @
 	"@ 	
 	#j+ 	
!D1&!8!8 1&E!D1&f&W & &G0
 	_!((M-"- .B 0b<"
 .< 0A8"
 .</p4"
 .B/m0"
 .B/m:"#0, 'n(.%3%f5%8 &@=@ #'E#H 0"-#: #'K-1
3<z 'H5& $2+22 Q
 .-- F
 +[**z
* 	,"'#
 	
 	.$!"
 	
* !((F+"- .B 0b<"
 .< 0A8"
 .</p4"
 .B/m0"
 .B/m:"#0, 'n(.%3%f5%8 &@=@ #'E#%)
T !((<&!T #'
 !((*%!3 #'
 !((3$!>"T #'	
KSATj 
5&/XJ:<QRSy9 T   kT{<jy9v &G5
 	_!((M-"-/P;d2R9_3V4]:o) 'P(.%3%<% &@#& #'+. 0"-#: #'1 $
&/` 'H:$ $2+22 Q
 .-- F
 +[**z
 .--! N!
4 	,"'#
 	
 	.$!"
 	
* !((F+"-/P;d2R9_3V4]:o) 'P(.%3%<% &@#& #'+
: !((<&!T #'
 !((5%!> #'
 !((3$!>"T #'	
qFIKX 
5&/XJ:<QRSs= T   YKa/~s=j ?$)3;33>QR/+//:hi0;00;jk 8 8 8Cg h0;00;LM6k66  BC  PT  U(@(@(@  LC  )D
 ++'X&Y#e"f #'	
8 	2&*% 
	
 	5 	
 	)	

 	#CD $@"(, 	
1%=DJH00 HKDJHd +J/="1K11<de
 '3;33>ef0;00;LM3;33>WX 8 8 8Cj k	
 9(!; #'
 9(!B #'
-"H 	) I 	
 	#L $	 		
W>~54x.. 54> 0@54t '"X&)HI)VW)BC)=>)QR +J/<3X++ <3 0<3~ )$Z&)HI)VW)BC)=>)QR +J/<3x-- <3 0<3~ )$Z&)HI)VW)BC)=>)QR +J/<3x-- <3 0<3@ A @ G  G   2f'T2)BC);< 
5':<QRS_%& ' T 0 %T#)BC)AB 
5':<QRS_%&% ' T %8 !D(#)BC);<		 
6(:<QRS_%&E ' T 	E. K!&#;;#;#;#= 1 3%6%8
 )TU)BC)AB)XY	& 
6(:<QRS_%&W ' T ',Wt " `"2+22E  NO  P,{,,e  HW  X
 '#;;#;#;#= 1 3%6%8
 );<)BC)LM0 
6(:<QRS_%&f ' T 16fT 4_'T2)BC)BC 
5':<QRS_%& ' T 0 ,V#)BC)AB 
5':<QRS_%&% ' T %8 (F(#)BC)BC		 
6(:<QRS_%&E ' T 	E( .R!&#;;#;#;#= 1 3%6%8
 )TU)BC)AB)XY	& 
6(:<QRS_%&W ' T ',Wt ) y"2+22E  NO  P,{,,e  HW  X
 '#;;#;#;#= 1 3%6%8
 );<)BC)LM0 
6(:<QRS_%&f ' T 16fR ,`
	))BC	 
5':<QRS_%&
@ ' T 
@rv   