o
    )iw                    @   s  d dl Z d dlmZ d dlmZmZ d dlmZmZm	Z	m
Z
mZmZmZmZmZmZmZmZmZmZmZmZmZm
Z
mZmZmZ d dlmZmZmZmZm Z mZm!Z!m"Z"m#Z#m$Z$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z* d dl+m,Z,m-Z-m.Z. d dlmZm/Z/ d dl0m1Z1 d d	l2m3Z3m4Z4 d d
l5m6Z6 d dl7m8Z8m9Z9 d dl:m;Z; d dl<m=Z= d dl+m>Z>m-Z-m?Z? d dl@mAZA d dlBmCZC d dlDmEZE d dlFmGZGmHZHmIZI d dlJmKZKmLZLmMZMmNZNmOZOmPZPmQZQmRZR d dlSmTZTmUZUmVZV d dlWmXZX d dlYmZZZ d dl[m\Z\ d dl]m^Z^ d dl2m_Z_m`Z` d dlambZbmcZc d dldmeZe d dlFmIZImHZH d dlfmgZg d dlhmiZi d dljmkZk d d llmmZm d d!lhmnZn d d"lompZp d d#lqmrZr d d$lsmtZtmuZu d d%lvmwZwmxZx d d&lvmyZymwZwmxZx d d'lzm{Z{ d d(l|m}Z} d d)l~mZmZ d d*lmZ d d+lmZmZ d d,lmZ d d-lmZ d d.lmZ d d(l|m}Z} d d/lmZ d dlZd d0lmZ d d1lmZmZ d d-lmZ d d2lmZmZmZ d d3lmZ d dlZd d4lmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZ d d5lmZ d dlZd dlZd dlZd dlZd dlZd dlZd dlZd d6lzmZ d d7lmZ d d8lBmZ d d9lmZmZmZmZ d d:lmZ d d;lmZ d d<l:mZ d d=lFmZ d d>lmZ d dlZe_ Zd?d@ Z	 edAgeegdBdC ZedAgeegdDdE ZedAgeegdFdG Z	 	 d dHlmZ dIdJ ZedKgeegeeegdLdM Z	 	 edAdNgeegeeege{jdOdP ZedKgeegeegdQdR ZedKgeegeegdSdT ZedUgeegdVdW ZedKgeegdXdY ZedKgeegdZd[ ZedKgeegd\d] Z	 edKgeegd^d_ ZedKgeegd`da ZedKgeegdbdc Z	 edKgeegddde Z	 edAgeegdfdg ZedAgeegdhdi ZedAgeegdjdk ZedAgeegdldm Zdndo ZedAgeegdpdq Z	 edAgeegdrds Z	 d dlZd dtl+mZ edAdNgeege{jdudv Z	 edKgeege{jdwdx ZedAdKgeegdydz Zegd{d| Z	 e{jedUgeegd}d~ ZedKgeegdd Z	 edAgeegdd Z	 edAgeegdd ZG dd deZedAgeegdd Z	 	 	 edAgeegdd ZedAgeegdd Z	 d dtl+mZ d dlZdddZdd Zdd Zdd Zg dZd dlJmZ dd ZedKgeegeeegdd Z	 	 	 edAgeegdd Z	 	 d dlJmZ edNgeegeeege{jdd Z	 d dlmZ dd ZedAdKgeegeeege{jdd ZedAdKgeegdd Z	 	 	 	 	 dd Z	 	 d dlzm Z  d d7lmZ edNgeegdd Z	 d dlZd dlBmZ 	 	 edUgeegdd Z	 edKgeegeegdd ZedKgeegeegdd Z	 	 edAgeegdd Z	 dddZdd ZmedAdKgeegdd Z	 	 	 	 dd ZmedAdKgeegdd Z	edAdKgeegdd Z
dd Zm	 edAdKgeegdd Z	 edAdKgeegddĄ Z	 d dlmZ edAdKgeegddǄ ZedAgeegddɄ ZedAdKgeegdd˄ Z	 	 	 edKgeegdd̈́ ZedKgeegddτ ZedKgeegddф ZedKgeegddӄ ZedKgeegddՄ Zddׄ Zddل ZdS )    N)count)renderredirect)UserClientCompany	ClientLogMenuUserPermissionsCities	CountriesStateAssocClientCompanyAirlinesLoginOTPClientPassportClientDocumentClientTravelInsuranceClientFrequentFlyer
ClientVisar   
EmailSetupClientDataViewCompanyDataView)CompanyFormUserFormCustomLoginFormUserCreateFormUserUpdateFormr   CompanyFormSetOTPVerificationFormForgotPasswordFormResetPasswordFormFrequentFlyerFormClientPassportFormAssocClientCompanyFormOtherDocumentFormClientVisaFormClientTravelInsuranceForm)HttpResponseRedirectHttpResponseHttpResponseBadRequest)r   get_object_or_404)login_required)authenticatelogin)messages)	LoginView
LogoutView)reverse_lazy)url_has_allowed_host_and_scheme)JsonResponser)   StreamingHttpResponse)render_to_string)
make_aware)	Paginator)QValueF)generate_otpsend_login_otpsend_forgot_password_otpget_assigned_menunormalize_client_payload_find_non_serializablesconvert_datetimeserialize_company_with_related)serialize_instancebuild_changesnormalize_value)get_random_string)View)	send_mail)settings)get_user_modelupdate_session_auth_hash)check_passwordmake_password)Concat)r;   r:   )csrf_exempt)require_http_methods)method_decorator)
parse_date)require_POST)
DateFormat)escape)formset_factoryinlineformset_factory)get_module_permsrequire_menu_perm)build_session_permissionsrY   rZ   )transactionResponse)IsAuthenticatedAllowAny)Token)RefreshToken
TokenError)TokenAuthentication)APIViewstatus)DjangoJSONEncoder)
JSONParser)MultiPartParser
FormParser)api_viewpermission_classesparser_classes)PageNumberPagination)CompanySerializerCompanydataSerializerCountrySerializerStateSerializerCitySerializerClientDataSerializerCompanyDataSerializerFullClientSerializerAirlinesSerializerAddFamilyMemberSerializerEmailSetupSerializerrv   ClientSerializerClientTravelInsuranceSerializerClientPassportSerializerClientVisaSerializerOtherDocumentSerializerAssocClientCompanySerializerFrequentflyerSerializerClientDeleteSerializerClientExportSerializer)AnonymousUser)
connection)timezone)	localtime)datetime	timedeltadatetime)
ClientForm)get_user_assigned_menus)reverse)Prefetch)call_commandc                 C   s2   zt jddj| |dW S  t jy   Y d S w )Nref_userref_menu)r   ref_menu_id)r
   objectsselect_relatedgetDoesNotExist)usermenu_id r   I/var/www/html/myvaluetrips/my_value_trip_new/my_value_trip/users/views.pyget_menu_permissionso   s
   r   GETc                 C   s   d}t | j|}tj }t|dd}|j}|jdd }|jdd }t	d||||r:|j
|j|j|j|jdni dtjd	S )
N   Tmanyactive)company_statusinactivecan_viewcan_addcan_edit
can_delete
can_export)successdatar   r   permissionsrf   )r   r   r   r   allrq   r   filterr   r^   r   r   r   r   r   rg   HTTP_200_OK)requestr   perms	companies
serializerr   active_countinactive_countr   r   r   company_list   s0   
r   c                 C   s&  | j dd }t|dk rtg dddS tjt|dt|dB t|dB t|d	B t|d
B t|dB t|dB t|dB 	ddd}g }|D ]6}|
|j|j|jpXd|jp\d|j|jre|jjnd|jrm|jjnd|jru|jjnd|jdkr}dndd	 qLtd|| | dtjdS )Nsearch_text    r   )r   recordsTotalrecordsFiltered)company_name__icontains)gst_name__icontains)gst_no__icontains)company_address__icontains)company_city__name__icontains)company_state__name__icontains) company_country__name__icontains)+company_account_concerned_person__icontainscompany_citycompany_statecompany_countryr   ActiveInactive)	idcompany_namegst_namegst_nocompany_addresscitystatecountryrg   T)r   r   r   r   rf   )r   r   striplenr4   r   r   r   r9   r   appendr   r   r   r   r   r   namer   r   r   r   rg   r   )r   r   r   r   companyr   r   r   search_company   sV   

r   c                 C   sJ   t t|d}t|d| id}d|jr|jjnd i}td|j|dtjdS )Nr   r   contextgst_document_fileT)r   r   company_datarf   )	r+   r   rv   r   urlr^   r   rg   r   )r   
company_idr   r   r   r   r   r   company_view   s   r   )model_to_dictc                 C   s   i }t |tst|dd |jjD d}| r(t | ts(t| dd | jjD d} t| r/|  ng | }|D ]=}| rB| |nd }|rK||nd }t |t	jj
jr\|rZ|jnd }t |t	jj
jrk|ri|jnd }||krv||d||< q9|S )Nc                 S      g | ]}|j qS r   r   .0fieldr   r   r   
<listcomp>`      z)build_changes_company.<locals>.<listcomp>fieldsc                 S   r   r   r   r   r   r   r   r   b  r   oldnew)
isinstancedictr   _metar   setkeysunionr   modelsfiles	FieldFiler   )old_datanew_datachangesall_keyskey	old_value	new_valuer   r   r   build_changes_company]  s"   
r   POSTc                 C   sp  | j }g }d}	 d| d}i }d}| D ]\}}||r2|| dddd}	|||	< d}q|s6n| jj|d< t |d	< || |d
7 }q|sXt	dddt
jdS t|dd| id}
|
 r|
jddd}g }|D ]#}|t||jj|jdttjtd t|td| jt d qptj| t	ddt|dd| idj dt
jdS t	d|
jdt
jdS )Nr   Tz
companies[]F[r   
created_by
created_at   z No company data found in requestr   messagerf   r   r   r   r   r   )r   	is_activezCOMPANY CREATEDcls)ref_company_idref_table_nameref_idaction_typechanged_dataperformed_byperformed_atz)Company or companies created successfullyr   r   r   r  r   r   errors)r   items
startswithreplacer   r   r   nowr   r^   rg   HTTP_400_BAD_REQUESTrp   is_validsaver   r   db_tablepkjsonloadsdumpsrE   rC   rh   r   bulk_createHTTP_201_CREATEDr  )r   r   r   indexprefixr   has_datar   valuer   r   	instanceslogsinstancer   r   r   company_create  sz   


r)  PUTc                 C   s   t t|d}| jdkrM| j}d|vr| }|j|d< t||dd| id}| rB|j| j	d}t
dd|j|jt|jd	tjd
S t
d|jdtjd
S t|}t
d|j|j|jdtjd
S )Nr   r*  r   Tr   )r(  r   partialr   
updated_byzCompany updated successfully)r   r  r   r   r   rf   Fr  )r   r   r   r   )r+   r   methodr   copyr   rp   r  r  r   r^   r   rg   r   r  r  )r   r   r   r   r   updated_companyr   r   r   company_edit[  sB   

r1  c                    s  | j d}|stdditjdS z|jdrt|nt	|  
t d  W n tyJ } ztdd| itjdW  Y d }~S d }~ww ddd	d
ddd} j|dd g d} fdd|D }|rvtdd| itjdS ttjjddd}t }g }d}	  D ]O\}
}|drt|d  nd}d}|sd}n||v rd}n||v rd}|| ||
d ||d|d	|d
|d|d|d |rd}	qt||	dS )NfileerrorNo file uploaded.rf   .csvError reading file: r   r   r   r   r   company_pincodezCompany NameAddressCityr   CountryPincodeTcolumnsinplacer   r   r   r   r   r7  c                       g | ]	}| j vr|qS r   r>  r   coldfr   r   r         z*company_preview_import.<locals>.<listcomp>Missing columns: flatFr   Company name is requiredzDuplicate company in fileCompany already existsr   )
row_numberr   r   r   r   r   r7  r3  )rows
has_errors)FILESr   r^   rg   r  r   endswithpdread_csv
read_excelwherenotnull	Exceptionrenamer   r   r   values_listiterrowsstrr   addr   )r   r2  eCOLUMN_MAPPINGrequiredmissingexisting_names
seen_namespreview_datarO  idxrowr   r3  r   rE  r   company_preview_import  sd    $

rf  c                    s  | j d}|stdditjdS z|jdrt|nt	|  
t d  W n tyJ } ztdd| itjdW  Y d }~S d }~ww ddd	d
ddd} j|dd g d} fdd|D }|rvtdd| itjdS dg }}  D ]\}}	zt  t|	d  }
|
stdtjj|
d rtdztjj|	d	d}W n tjy   td|	d	 dw ztjj|	d
d}W n tjy   td|	d
 dw ztjj|	dd}W n tjy   td|	d dw tjj|
|	d||||	dd |d7 }W d    n	1 s&w   Y  W q tyO } z||d |	dt|d W Y d }~qd }~ww td|t||d tjdS )!Nr2  r3  r4  rf   r5  r6  r   r   r   r   r   r7  r8  Tr=  r@  c                    rA  r   rB  rC  rE  r   r   r     rG  z"company_import.<locals>.<listcomp>rH  r   rK  r   rL  r   zCity 'z' not foundzState 'z	Country 'r  r   )rM  r   r3  rg   successfully_importedfailed_countfailures) rP  r   r^   rg   r  r   rQ  rR  rS  rT  rU  rV  rW  rX  rZ  r\   atomicr[  r   
ValueErrorr   r   r   existsr   r   r   r   creater   r   r   )r   r2  r]  column_mappingr_  r`  r   failedrd  re  r   city_instancestate_instancecountry_instancer   rE  r   company_import  s    $



 ru  DELETEc              
   C   s   z t jj|d}| j|_|jdgd |  tdddddW S  t jy3   td	d
ddd Y S  t	yO } ztd	t
|dddW  Y d }~S d }~ww )Nr   r-  update_fieldsTzCompany deleted successfullyrg   r     rf   FzCompany not found    )r   r   r   r   r-  r  deleter^   r   rW  r[  )r   r   r   r]  r   r   r   delete_company$  s   "r~  c           
      C   s  | j d}| j d}|r|stdddtjdS ztjj|d}||s1tdd	itjdW S W n tj	yE   tdd
itjd Y S w |j
d u }g d}|j|v sW|jrt|}tjj|dd}dd |D }tddd|t|jt||j|j|j|j|j|j|j|j|d	dtjdS t }	tjj||	dt dd t|j|	 |j| jd< t|}tjj|dd}dd |D }tddd|t|jt||j|j|j|j|j|j|j|ddtjdS )NemailpasswordFEmail and password are requiredr  rf   r  r3  Incorrect passwordInvalid email or password)zkush.dave@ammrs.co.inzatul@myvaluetrip.comzshachi@myvaluetrip.comzkunael.aneja@ammrs.co.inzvimlesh.kumhar@ammrs.co.inzkush.fsdfd@yopmail.comr   r   c              
   S   F   g | ]}|j r|j jnd |j r|j jnd |j|j|j|j|jdqS N)r   	menu_namer   r   r   r   r   r   r   r  r   r   r   r   r   r   permr   r   r   r   t      
z custom_login.<locals>.<listcomp>Tz#Login successful (OTP not required))	r   r  r   designationcontactrg   is_user_accessis_superuserr   r   r  otp_sentfirst_loginaccessrefreshr   otpis_verifiedr  r   defaultsotp_user_idc              
   S   r  r  r  r  r   r   r   r     r  OTP sent to your emailr   r  r   r  r  rg   r  r   ) r   r   r^   rg   r  r   r   rM   HTTP_401_UNAUTHORIZEDr   
last_loginr  r  rb   for_userr
   r   r   r[  access_tokenr   usernamer  
contact_nor   r<   r   update_or_creater   r  r=   session)
r   r  r  r   is_first_loginADMIN_BYPASS_EMAILSr  user_permissionspermissions_datar  r   r   r   custom_loginF  s   


	



r  c           	      C   sb  | j d}| j d}|r|stdddtjdS ztjj|d}||s1tdd	itjdW S W n tj	yE   tdd
itjd Y S w |j
d u }|d urt }tjj||dt dd t|j| |j| jd< t|}tjj|dd}dd |D }tddd|t|jt||j|j|j|j|j|j|j|ddtjdS tdd
ddtjdS )Nr  r  Fr  r  rf   r  r3  r  r  r  r  r  r  r   c              
   S   r  r  r  r  r   r   r   r     r  z$custom_login_old.<locals>.<listcomp>Tr  r  r  )r   r  r  ) r   r   r^   rg   r  r   r   rM   r  r   r  r<   r   r  r   r  r=   r  r   r  rb   r  r
   r   r   r[  r  r  r  r  r  r   )	r   r  r  r   r  r  r  r  r  r   r   r   custom_login_old  s   




r  c                 C   s   | j d}| j d}|r|stdditjdS z	tjj|d}W n tjy4   tddidd Y S w |j|kr[|	 s[d	|_
|  |j}t| | td	d
|j|jdtjdS tddiddS )Nr   r  r3  zUser ID and OTP are requiredrf   )user_idzOTP not found  TzOTP verified successfully)r   r  r  r  Invalid or expired OTP)r   r   r^   rg   r  r   r   r   r  
is_expiredr  r  r   r.   r   r  HTTP_202_ACCEPTED)r   r  	otp_inputotp_objr   r   r   r   
verify_otp*  s.   
r  c                 C   s   | j d}|stdddtjdS z	tjj|d}W n tjy/   tdddtjd Y S w t	 }t
jj||dt dd	d
\}}t|j| |j| jd< tddd|j|j|jddtjdS )Nr  FEmail is requiredr  rf   r  zNo user found with this emailforgot_password)r  purposer  r  r  r  Tr  )r   r  r  )r   r  r  r   )r   r   r^   rg   r  r   r   r   HTTP_404_NOT_FOUNDr<   r   r  r   r  r>   r  r   r  r  r   )r   r  r   r  r  createdr   r   r   r  p  sP   

	r  c                 C   s   | j d}| jdp| j d}|r|s tdddtjdS ztjj|ddd	d	}W n tj
yB   tdd
dtjd Y S w |j|krj| sjd|_|  || jd< | jdd  tddddtjdS tdddtjdS )Nr  r   FzOTP and user_id are requiredr  rf   r  )r  r  r  r  zOTP not found or expiredTreset_user_idr  z-OTP verified. You can now reset your password)r   r  r   r  )r   r   r  r^   rg   r  r   r   r   latestr   r  r  r  r  r  popr   )r   r  r  r  r   r   r   verify_otp_forgot_pass  sR   

r  c                 C   s   | j d}| j d}|r|stdddtjdS ztjj|d}|| |  tdd	dtj	dW S  tj
yI   tdd
dtjd Y S w )Nnew_passwordr   Fz%New password and user id are requiredr  rf   r   TzPassword reset successfulzUser not found)r   r   r^   rg   r  r   r   set_passwordr  r   r   r  )r   r  r  r   r   r   r   reset_password  s6   

r  c                 C   s  | j d}|stddiddS tt|d}tjj|ddd		d
ddddddddd
}i }zt
jjdd}|j|j|j|jg d||j< W n
 t
jyQ   Y nw |D ]q}|j}|jsi|jsi|jsi|jsi|jsiqT|jd u r|j|vr|j|j|j|jg |j|j|j|j|jdd||j< qT|j}|j|vr|j|j|j|jg d||j< |j|j|j|j|j|j|j|jdd}	||j d |	 qTt| dd d}
|
D ]}t|d dd d|d< qtd|
iS ) Nr  r3  zuser_id is requiredr  rf   r   r  r   ref_menu__ref_menur   r   r   r   r   ref_menu__idref_menu__menu_nameref_menu__menu_urlref_menu__menu_order_noref_menu__ref_menu_id	DASHBOARD)menu_action)r   r   r   ordersubmenusr   )r   r   r   r  r  r   )r   r   r   r   r  c                 S      | d S )Nr  r   )mr   r   r   <lambda>q      z$assigned_menus_api.<locals>.<lambda>)r   c                 S   r  )Nr   r   )smr   r   r   r  u  r  menus)r   r   r^   r+   r   r
   r   r   r   onlyr	   r   r  menu_urlmenu_order_nor   r   r   r   r   r   r   r   r   sortedvalues)r   r  r   r   r  	dashboardr  menuparentsubmenu_dictsorted_menusr  r   r   r   assigned_menus_api  s   





r  c                 C   s6   t j ddd}t|dd}td|jdtjdS )Nr   r   country_codeTr   r   r   rf   )	r   r   r   r  rr   r^   r   rg   r   )r   	countriesr   r   r   r   get_countries  s   r  c                 C   s\   | j d}|stdditjdS tjj|dddd}t	|dd	}td|j
d
tjdS )N
country_idr3  zcountry_id is requiredrf   )r  r   r   Tr   r  )query_paramsr   r^   rg   r  r   r   r   r  rs   r   r   )r   r  statesr   r   r   r   
get_states  s   r  c              	   C   s   | j d}|stdddtjdS zt|}W n ttfy-   tdddtjd Y S w tj	j
|ddd	d}t|d
d}td
|jdtjdS )Nstate_idFzstate_id is requiredr   r3  rf   z"Invalid state_id, must be a number)r  r   r   Tr   r  )r  r   r^   rg   r  intrm  	TypeErrorr   r   r   r  rt   r   r   )r   r  citiesr   r   r   r   
get_cities  s(   

r  c                 C   s   t i S Nr]   r   r   r   r   r    s   	r  c                 C   sP   |   }t }|D ]}t }|D ]}|tdi | d|iO }q||M }q	|S )z
    Splits query_string into words and builds a Q object:
    - All words must match somewhere (AND)
    - Within each word, it can match in any field (OR)
    __icontainsNr   )splitr9   )query_stringr   wordsq_objectwordword_qr   r   r   r   build_multiword_query!  s   
r  c                 C   sx  | j dkrtdddS | jdd }t|dk r%tdddtjd	S g g g g g g g g d
}g d}tj	
t|| }|D ]&}|jpGd d|jpMd d|jpSd  }|d |j||j|jd qAg d}tj	
t|| }|D ]*}	|d |	j|	j|	j|	jr|	jj|	jjpd d|	jjpd  dnd d qyg d}
tj	
t||
 }|D ]6}|d |j|j|jrd|jjind |j|j|jr|jj|jjpd d|jjpd  dnd d qg d}tj	
t|| }|D ]-}|d |j|j |j!|jr%|jj|jjpd d|jjpd  dnd d qg d}t"j	
t|| }|D ]:}|d |j#rOd|j#j$pMdind |j%|j&|jrp|jj|jjpad d|jjpid  dnd d q<g d}t'j	
t|| }|D ]8}|d  |j(rd|j(j)pdind |j*|jr|jj|jjpd d|jjpd  dnd d! qd"d#g}t+j	
t|| }|D ]*}|d$ |j,|jr|jj|jjpd d|jjpd  dnd d% qg d&}t-j	
t|| }|D ]}|d' |j|j$|j.d( qt/|0 s2tdd)|d*tj1d	S td+|d,tj2d	S )-Nr   FInvalid request method.ry  searchr   r   z#Please enter at least 3 characters.rf   )clientsr   	passportsvisas	insuranceassoc_company
freq_flyer	documents)client_first_nameclient_middle_nameclient_last_namer  r  client_coderesidential_addressresidential_city__nameresidential_state__nameresidential_country__namedobgenderanniversary_datereference_frompreferred_contact_method
aadhaar_nopan_noref_preferred_airline__airlineseat_preferenceseat_preference_othermeal_preferencefare_preferencestar_ratingstay_preferenceroom_preferenceextra_amenities r  )r   r   r  r  )passport_nopassport_expiry_dateref_client__client_idr  r   r   )r   r  r  
ref_client)	visa_typevisa_from_datevisa_to_dater  ref_visa_country__namer  r   )r   r  visa_countryr  r  r  )insurance_from_dateinsurance_to_dater  r  -)r   r  r   r  )ref_company__company_namer  primary_companyr  r  r   )ref_companyr#  r  r  )ref_airline__airliner  ff_nor  )ref_airliner&  r  other_document_namer  r  )r(  r  )r   r   r   company_city__namecompany_state__namecompany_country__nameaccount_concerned_persontravel_concerned_personr   r   r   r   zNo records found.)rg   r  r   T)rg   r   )3r.  r4   r   r   r   r   rg   r  r   r   r   r  distinctr  r  r  r   	client_idr  r  r   r   r  r  r  r   r  ref_visa_countryr   r  r  r   r  r   r   r$  r   r#  r  r   r'  airliner&  r   r(  r   r   anyr  r  r   )r   queryresultsclient_fieldsr  c	full_namepassport_fieldsr  pvisas_fieldsr  v
ins_fieldsr  insassoc_fieldsr  assoc	ff_fieldsr  ffdocs_fieldsr  docscompany_fieldsr   compr   r   r   search_records1  s  
*
 
 
$

$$$

rG  c                 C   sH  | j dd}d }|r|dr|dd }d}t| j|}| jdd }tj	
 }|rF|t|dt|d	B t|d
B t|dB }|jddd }|jdd }g }	t|ddD ]"\}
}|	|j|jt|ddt|dd|j|j|jr|dndd q_td| | |	|||r|j|j|j|j|jdni dtjdS )NHTTP_AUTHORIZATIONr   zBearer r  r  r   zsearch[value])username__icontainsemail__icontainscontact_no__icontains)designation__icontainsTF)r  r  r  startr  r  r   r   )r   r  r  r  r  r  rg   r   )r   r   r   r   r   r   r   rf   )METAr   r  r  r   r   r   r   r   r   r   r   r9   r   	enumerater   r   r  getattrr  r  r  r^   r   r   r   r   r   rg   r   )r   auth_headertokenr   r   r   usersr   r   r   r"  r   r   r   r   	user_listK  sd   




rX  )	QueryDictc              
      s  t t|d}g d}| jdkr\|j|j|j|jpd|jpd|j|j	d}g }t
jj|dD ]#}|j|j|j|j|j|jdtfdd	|D d
< | q-td||dtjdS | jdkrt| j|d}| swtd|jitjdS |jdd}| j|_d| jv rt| jd   dkrd|_d|_!n#t| jd   dkrd|_d|_!ntdd| jd  ddtjdS |  i }d| jv rt"| jd t#r| jd D ]' t $d}	|	sqӈ $d
drdd |D ||	< qӇ fdd|D ||	< qnH| j% D ]B\}
}|
&drBz
|
'd\}}	}W n t(y    Y qw ||vr(q|	|vr6d d |D ||	< t|  d!v ||	 |< q|% D ]1\}	}t
jj)||	d"d |% D d#\}}|sx|% D ]\}}t*|d$| | qe|  qHg }t
jj|dD ]$}|j|j|j|j|j|jdtfd%d	|D d
< | qtdd&|j d'|j|j|j|jpd|jpd|jp|j!rd(nd)d*|d+tjdS d S ),Nr   viewr\  editr}  exportr   r   )r   r  r  r  r  rg   r  r  )r   r[  r\  r\  r}  r]  c                 3       | ]} | V  qd S r  r   r   a	perm_dictr   r   	<genexpr>      zupdate_user.<locals>.<genexpr>r   T)r   r   r   rf   r*  )r(  r  Fcommitrg   r   r   zInvalid status 'z'. Allowed: active, inactiver  r   r   c                 S      i | ]}|d qS )Tr   r_  r   r   r   
<dictcomp>  r   zupdate_user.<locals>.<dictcomp>c                    s   i | ]}|t  |d qS F)boolr   r_  )r  r   r   rh        menu__c                 S   rg  ri  r   r_  r   r   r   rh    r   )1trueyesc                 S   s   i | ]
\}}d | |qS )can_r   )r   r`  valr   r   r   rh  '      )r   r   r  rq  c                 3   r^  r  r   r_  ra  r   r   rc  :  rd  User 'z' updated successfully.r   r   )r   r  r  r  r  rg   )r   r  r   r   )+r+   r   r.  r   r  r  r  r  rg   r  r
   r   r   r   r   r   r   r   r   r   r   r^   r   r   r   r  r  r  r  r   r-  r[  lowerr  r   listr   r  r  r  rm  get_or_createsetattr)r   r  r   actions	user_datar   upformpermissions_dictr   r   r%  rm  action	new_permsobjr  rr  existing_permsr   )r  rb  r   update_user  s   

	



	

r  c           
         sn  g d}| j jdkrtdditjdS | j}t|d}| r|jdd}| j |_	t
jt
j  d	 fd
dtdD }|| t|drJd|_|  tdd|j d| dd|jgdd |dg }g }|D ],}|d}	|	suqk|t||	|dd|dd|dd|dd|ddd qk|rtj| tdd|j ddtjdS t|jtjdS )NrZ  Tr3  z!Only admins can create the users.rf   r   Fre  r   c                 3   s    | ]}t  V  qd S r  )secretschoice)r   rm  
charactersr   r   rc    s    zcreate_user.<locals>.<genexpr>   r  z)My Value Trip! | Account Has Been CreatedzHello zW,

Welcome to My Value Trip!

Your account has been created.
Your default password is: z4

Please login and change your password immediately.zno-reply@myvaluetrip.com)subjectr  
from_emailrecipient_listfail_silentlyr   r   r[  r\  r\  r}  r]  )r   r   r   r   r   r   r   rt  z' created successfullyr  )r   r  r^   rg   HTTP_403_FORBIDDENr   r   r  r  r   stringascii_lettersdigitsjoinranger  hasattrr  rI   r  r  r   r   r
   r   r   r!  r  r  )
r   ry  r   r   r   default_passwordr  upsr  r   r   r  r   create_user  s^   











r  c                 C   sP  t t|d}| jdkr| jd|j|_| jd|j|_| jd|j|_| jd}| jd}| jd}|s>|s>|r|rD|rD|sNtd	d
dt	j
dS ||s]td	ddt	j
dS ||krktd	ddt	j
dS || |  t| | tdddt	jdS |  tdddt	jdS d|j|j|j|j|jdd}t|t	jdS )Nr   r   	user_namer  r  old_passwordr  confirm_passwordFz!All password fields are required.r  rf   zOld password is incorrect.z/New password and confirm password do not match.TzPassword updated successfully.zProfile updated successfully.)r   r  r  r  r  )r   r   )r+   r   r.  r   r   r  r  r  r^   rg   r  rM   r  r  rL   r   r   r  )r   r  r   r  r  r  r   r   r   r   user_profile_view  s<   




r  c                 C   s:   t t|d}| jd}||rtdddS tdddS )Nr   r  TF)safe)r+   r   r   r   rM   r4   )r   r  r   r  r   r   r   password_validate	  s
   
r  c                 C   sT   | j dv r tt|d}|j}|  tdd| ddtjdS tdd	d
tjdS )N)r   rv  r   Trt  z
' deleted.r  rf   Fr  r  )	r.  r+   r   r  r}  r4   rg   r   r  )r   r  r   r  r   r   r   delete_user>	  s   
r  c                 C   sr   | j d}|stdddtjdS zt|}|  tdddtjdW S  ty8   tdddtjd Y S w )	Nr  FzRefresh token is required.ry  rf   TzLogged out successfully.z!Invalid or expired refresh token.)	r   r   r^   rg   r  rb   	blacklistHTTP_205_RESET_CONTENTrc   )r   refresh_tokenrV  r   r   r   logout_viewa	  s&   
r  c                 C   s
  | j dd }|stdg iS tjt|dt|dB t|dB t|dB t|dB t|d	B  }g }t	|d
dD ]A\}}|
||jpHd|j d|jpPd d|jpVd  |jp^d|jpbd|jri|jjnd|jpnd|jrw|jdnd|jd	 q=td|iS )Nqr   r5  client_first_name__icontains)client_middle_name__icontainsclient_last_name__icontainsrJ  rL  )!residential_city__name__icontainsr  rP  r  %d-%m-%Y)	r   r  r8  r  r  residential_citytyper  client_status)r   r   r   r4   r   r   r   r9   r/  rS  r   r  r  r  r  r  r  r  r   client_typer  strftimer  )r   r4  r  r   iclientr   r   r   client_search	  s@   	$
r  c                 C   sf  zd}t | j|}tjjdddddddd	d
tdtjddddtdtjjdddddddd	d
tdtjddddd}t	|dd| id}tjj
tjdtjdddtjdtjdddtdd}td|j|d |d |d |r|j|j|j|j|jdni dtjdW S  ty } ztdd t| d!tjdW  Y d }~S d }~ww )"N   Tref_client__isnullr  r  residential_stateresidential_countryref_preferred_airliner   r-  client_companiesref_company__company_countryref_company__company_stateref_company__company_cityquerysetrelated_clientsFr   r  r  r  rO  r   r   r   r   total_countr   r   r  r   )r   r   r   r   total_clientsr   rf   Internal server error: r  )r   r   r   r   r   r   prefetch_relatedr   r   r   	aggregater   Countr9   r^   r   r   r   r   r   r   rg   r   rW  r[  HTTP_500_INTERNAL_SERVER_ERROR)r   r   r   primary_clientsr   countsr]  r   r   r   client_exportQ  s   )r  c                   @   s   e Zd ZdZdZdZdS )CustomPageNumberPagination
   limitd   N)__name__
__module____qualname__	page_sizepage_size_query_parammax_page_sizer   r   r   r   r    s    r  c                 C   s  zt  }| jdd }|r"z
tt|d|_W n	 ty!   Y nw | jdd }| jdd }d}t| j|}t	j
jdd}|rz|t|dt|d	B t|d
B t|dB t|ddB t|dB t|ddB t|ddB t|ddB t|dB }|dddddddtdtj
ddddtdt	j
jdddddddddtdtj
ddddd}|r=zp| }|| }	||	 d  |	 }
|d!krd }nG|d"krtd |
}n=|d#krt| jd$d }t|d  |
}n(|d%krt| jd$d }td |d  }nt|}|d k s||
krtd&| j }t||d< || j_W n ty<   | j }d'|d< || j_Y nw ||| }t|dd(| id)}t	j
jtjd*tjd d+d,tjd*tjd-d+d,td*d.}td|j|jj j|jj!|jj j"|# |$ |d/ |d0 |d1 |r|j%|j&|j'|j(|j)d2ni |jd3t*j+d4W S  t,y } ztdd5t| d6t*j-d4W  Y d }~S d }~ww )7N	max_limiti  r  pager  Tr  )client_code__icontainsrL  rJ  r  F)r  client_middle_name__isnullr  )r  residential_city__isnull)$residential_country__name__icontainsresidential_country__isnull)"residential_state__name__icontainsresidential_state__isnull)client_type__icontainsr  r  r  r  r  r   r-  r  r  r  r  r  r  r  firstlastnextpage_numberpreviouszInvalid page numberrn  r   r  r  rO  r  r   r  r   r   r  r   )r   r   r   current_pagetotal_pagesr  r  r   r   r  r   r  rf   r  r  ).r  r  r   minr  r  rm  r   r   r   r   r   r9   r   r  r   r   r   get_page_sizemaxr/  r[  _requestr   paginate_querysetr{   r  r   r  r^   r   r  	paginatornumber	num_pagesget_next_linkget_previous_linkr   r   r   r   r   rg   r   rW  r  )r   r  r  search_querypage_actionr   r   r  r  r  r  r  r  r  paginated_clientsr   r  r]  r   r   r   client_list  s  



	
)



r  c                    s  t jddjd j|d }|stt |d}i d|jd|jp"d d	|j	p(d 
 d
|jd|jrC|jrCd|jj d|j n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rdd | dD ng |j|jr|jjnd |j |j!r|j!jnd |j"r|j"j#|j"j$dnd |j%|j&|j'|j(|j)|j*|j+|j,|j-d}t.jd d!d"d#d$d%d&j|d'}d(d |D |d)< t/t0jj|d'1 |d*< t2jd+d!d,d-d.j|d'}d/d |D |d0< t/t jj|jd'1 |d1< d2d |d1 D }t/t0jj|d31 |d4< |d1 D ]}|d   fd5d|d4 D |d*< q'|d4= t/t3jj|d'1 |d6< t/t4jj|d'1 |d7< t/t5jj|d'1 |d8< t6d9|d:t7j8d;S )<Nr  r  )"r0  r  r  r  country_code__country_coder  is_prepaymentr  r  r  r  r  marital_statusr  reference_idreference_remarkr   
occupationr  r	  aadhaar_card_filer
  pan_card_filer  r  r  r  r  r  r  r  r  ref_preferred_airline__idr  r0  r  r0  client_namer   r  r  full_contact_no+z - r  r  r  dateOfBirthr  r  r  r  r  r  ResidentialAddressr  r  c                 S   s   g | ]}|  qS r   )r   r   r<  r   r   r   r         zclient_view.<locals>.<listcomp>,r  )r	  r  r
  r  r  r  r  r  r  r  r  r  r  r  r$  r   r  r#  ref_company__idr"  ref_company__gst_noref_client_idc              	   S   s>   g | ]}|j |j|j|jr|jj |jj|jjd nddqS )r.  N)r   r  r#  r   )r   r  r#  r$  r   r   r   r7  r   r   r   r     s    
companies_datapassport_datar'  r&  ref_airline__idr%  c                 S   s4   g | ]}|j |j|jr|jj |jjd nddqS )r  N)r   r&  r2  )r   r&  r'  r2  r   fr   r   r   r   (  s    
flyer_datafamily_datac                 S      g | ]}|d  qS r  r   )r   memberr   r   r   r   L  r  )ref_client_id__infamily_passport_datac                    s   g | ]
}|d   kr|qS r	  r   )r   passportfamily_member_idr   r   r   U  s
    holding_visatravel_insuranceclients_documentsT)r   client_datarf   )9r   r   r   r  r   r  r+   r0  r  r  r   r  r  r  r  r  r  r  r  r  r  r  r  r  r   r  r  $get_preferred_contact_method_displayr  r	  r  r   r
  r  r  r   r2  r  r  r  r  r  r  r  r  r  r   rv  r   r  r   r   r   r   r^   rg   r   )r   r0  r  r  r   flyersfamily_member_idsfamily_memberr   r  r   client_view  s   	
-



r"  c              
   C   s|   zt j dddd}t|dd}td|jdtj	dW S  t
y= } ztdt|d	tjdW  Y d }~S d }~ww )
Nr   r2  iataTr   r  rf   Fr  )r   r   r   r  order_byrx   r^   r   rg   r   rW  r[  r  )r   airlinesr   r]  r   r   r   airlines_listq  s$   r&  c                    s    pg  t | trRi }|  D ]@\}}| v rHt |tr%t| g||< qt |tr6 fdd|D ||< q|du r?g ||< qt| g||< qt| ||< q|S t | trvt| dkrmt | d ttfsmt| d  S  fdd| D S | dv r|dS | S )z
    Normalize form data:
    - Convert empty strings and "null" to None
    - Flatten single-element lists into scalars for non-nested fields
    - Preserve lists for skip_keys (nested many=True fields)
    - Preserve None values explicitly
    c                       g | ]}|d urt | qS r  normalize_formdatar   x	skip_keysr   r   r   C  rk  z&normalize_formdata.<locals>.<listcomp>Nr  r   c                    r'  r  r(  r*  r,  r   r   r   P  rk  r   nullN)r   r   r  r)  rv  r   )r  r-  new_dictkr<  r   r,  r   r)  2  s*   




r)  c                 C   s8   t | trdd |  D S t | trdd | D S | S )z?
    Recursively remove keys with None, "", [], {} values.
    c                 S   s*   i | ]\}}|d dg i fvr|t |qS Nr   
deep_cleanr   r1  r<  r   r   r   rh    s
    zdeep_clean.<locals>.<dictcomp>c                 S   s$   g | ]}|d dg i fvrt |qS r2  r3  r*  r   r   r   r     s   $ zdeep_clean.<locals>.<listcomp>)r   r   r  rv  r  r   r   r   r4    s   

r4  c                 C   sj   i }|   D ]*\}}|ddd}|}|dd D ]}||i }q|dv r*dn|||d < qt|S )zG
    Convert flat form-data keys into nested dictionary structure.
    r   r   r   Nr.  )r  r  r  
setdefaultconvert_numeric_dicts_to_lists)r   resultr   r%  partscurrentpartr   r   r   parse_nested_formdata  s   r>  c                    sn   t  tr)tdd   D r  fddttt  D S dd   D S t  tr5dd  D S  S )z\
    Convert numerically indexed dictionaries to lists for proper nested data handling.
    c                 s   s    | ]}|  V  qd S r  )isdigit)r   r1  r   r   r   rc    rd  z1convert_numeric_dicts_to_lists.<locals>.<genexpr>c                    s   g | ]
}t  t| qS r   )r9  r[  )r   r  r  r   r   r     rs  z2convert_numeric_dicts_to_lists.<locals>.<listcomp>c                 S   s   i | ]	\}}|t |qS r   r9  r5  r   r   r   rh    rG  z2convert_numeric_dicts_to_lists.<locals>.<dictcomp>c                 S   s   g | ]}t |qS r   r@  )r   itemr   r   r   r     r  )	r   r   r   r   r  mapr  r  rv  r  r   r  r   r9    s   
 
r9  )	client_passportclient_visar  r  client_documentsclient_frequent_flyersfamily_membersfam_passportsrelationsWithOthers)serialize_client_with_relatedc                 C   s*  i }t |tst|dd |jjD d}| r(t | ts(t| dd | jjD d} |D ]}t || tjjjrC|| r?|| jnd ||< q*| rb| D ]}t | | tjjjra| | r]| | jnd | |< qHt	| ri| 
 ng |
 }|D ]}| r|| |nd }|r||nd }||kr||d||< qs|S )Nc                 S   r   r   r   r   r   r   r   r   N  r   z(build_changes_client.<locals>.<listcomp>r   c                 S   r   r   r   r   r   r   r   r   P  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   build_changes_clientJ  s,   
rK  c                 C   s   t i | j| j}t|td}t|}t|d| id}| rZ|j| j	| j	d}t
jj||jj|jdttjtd t|td|jt d tdt|d| id	jd
tjdS td|jdtjdS )Nr,  r   )r   r   )r   r-  zCLIENT CREATEDr  r
  r
  r  r  r  r  r  Tr   r  rf   Fr  )r>  r   rP  r)  	SKIP_KEYSr4  rw   r  r  r   r   r   ro  r   r  r  r  r  r  rE   rJ  rh   r   r   r  r^   rg   r!  r  r  )r   normalized_datar   r  r   r   r   client_createc  sB   rO  c              	      st  zt jdddddddj|d}W n t jy&   td	d
dtjd Y S w fdd dd i d|jd|j	d|j
d|jd|jd|jd|jd|jd|jg dd|jd|jd|jd|jd|jd|jd |jd!|ji d"|jd#|jd$|jd%|jr| d&ng d'|jd( |j d)|j!d* |j"d|j#d+d,gd|j$d+d-gd|j%d+d-gd|j&d+d-gd.|j'd/|j(d0|j)d1|j*d2|j+|j,|j-|j.|j/d3} fd4d5t0jj1|d6D |d7<  fd8d5t2jj1|d6D |d9<  fd:d5t3jj1|d6D |d;<  fd<d5t4jj1|d6D |d=< d>d5 t5jj1|d6D |d?< fd@d5t6jj1|d6D |dA<  fdBd5t jj1|dCD |dD< dED ] }|| r}t7|| t8s}td	dF| dGdtj9d  S q^|dD D ],}dED ]&}|| rt7|| t8std	dF| dH|d-  dtj9d    S qqtdI|dJtj:dS )Kz
    Fetch single client with related passports, visas, travel insurance,
    companies, documents, frequent flyers, and family member data,
    including file URLs. Optimized with select_related and prefetch_related.
    r  r  r  r  r  r   r-  r  FClient not foundr  rf   c                    s   | r  | jS dS )zAHelper to return absolute URL for a file field, or None if empty.N)build_absolute_urir   )
file_fieldr  r   r   get_absolute_url  s   z'client_detail.<locals>.get_absolute_urlc                    s    sdS  fdd|D S )z*Helper to extract foreign key data safely.Nc                    s   i | ]	}|t  |d qS r  )rT  r   r6  r   r   rh    rG  z?client_detail.<locals>.get_foreign_key_data.<locals>.<dictcomp>r   )r  r   r   r6  r   get_foreign_key_data  s   z+client_detail.<locals>.get_foreign_key_datar0  r  r  client_salutationr  r  r  r  )r   r  r   r  r  r  r  r   r  r  r  r  r  r  r  , r	  r  r
  r  r   r2  r   residential_pincoder  r  r  r  )r  r  r  r  c                    s,   g | ]}|j |jj|j |j|jd qS ))r   r
  r  passport_filer  )r   r  r0  r  rX  r  )r   r:  rS  r   r   r     s    z!client_detail.<locals>.<listcomp>r	  rC  c              
      s@   g | ]}|j |jj|jr|jj nd |j |j|j|jdqS )N)r   r
  r1  r  passport_size_photographr  r  )r   r  r0  r1  r  rZ  r  r  r  rY  r   r   r     s    
rD  c                    s,   g | ]}|j |jj |j|j|jd qS ))r   r
  insurance_documentr  r   )r   r  r0  r[  r  r   )r   trY  r   r   r     s    r  c                    s(   g | ]}|j |jj|j |jd qS ))r   r
  r(  other_document)r   r  r0  r(  r]  )r   cdrY  r   r   r   )  s    rE  c                 S   s0   g | ]}|j |jj|jr|jj nd |jdqS )N)r   r
  r'  r&  )r   r  r0  r'  r&  )r   rB  r   r   r   r   3  s    rF  c                    s2   g | ]}|j |jj |jd dg|j|jdqS )r   r   )r   r
  r$  r  r#  )r   r  r0  r$  r  r#  r_  )rT  r   r   r   =  s    r  c                    sZ   g | ])}|j |j d |j |j |j|j |j|j|j|j|j|j	|j
|jdqS )r  )r   r   r	  r  r
  r  r  r  r  r  r  r  r  )r0  r  r  r	  r  r
  r  r  r  r  r  r  )r   famrY  r   r   r   I  s"    )r  rG  )r	  r
  zInvalid z formatz format for family member Tr  );r   r   r   r   r   r^   rg   r  r0  r  r  rU  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  rW  r  r  r  r  r  r  r  r  r   r   r   r   r   r   r   r   r[  r  r   )r   r0  r  r  r   r_  r   )rS  rT  r   r   client_detail  s  

	


 !"#$%&
-
















r`  c                 C   sd  | j jstdddtjdS ztjddddd	j|d
}W n8 tj	y6   tdd| ddtj
d Y S  tyV } ztddt| dtjdW  Y d}~S d}~ww t|}|j}|dd}|dd}| d|  }ti | j| j}	t|	g dd}	t||	dd| id}
|
 r&|
j| j d}t|}|jpd d|jpd  }dd | D }dd | D }t||}|r||d}z(tjj||jj|j dt!"t!j#|t$d|j%t&' d t(d |j  d!|  W n t)y } zt(d"|j  d!|  W Y d}~nd}~ww tdd#t|d| id$jd%tj*dS tdd&|
j+d'tj,dS )(a  
    Update a Client instance with nested relationships and file uploads.

    Expected Input (multipart/form-data or JSON):
    - client_first_name, client_code, etc. (scalars, not lists)
    - Nested fields: client_passport, client_visa, family_members, etc. (list of dicts)
    - Files: aadhaar_card_file, pan_card_file, family_members[0][aadhaarCard], etc.
    - Choice fields (e.g., gender, marital_status) should match model choices (e.g., "Male", "Married")

    Response:
    - 200: Success with updated client data
    - 400: Validation errors with detailed messages
    - 401: Unauthorized if user is not authenticated
    - 404: Client not found
    - 500: Server error for invalid configurations
    FzUser is not authenticatedr  rf   r  r  r  r  r  r  zClient with ID z
 not foundz(Invalid prefetch_related configuration: Nr  r   r  r  )	rC  rD  r  r  rE  rF  rG  fam_passportrI  r,  Tr   )r   r+  r   r,  c                 S      i | ]\}}|d vr||qS )r  r  
updated_atr  r   r5  r   r   r   rh    
    z!client_update.<locals>.<dictcomp>c                 S   rb  rc  r   r5  r   r   r   rh    re  )descriptionr  zCLIENT UPDATEDr  rL  z&Created CLIENT UPDATED log for client z: z-Error creating CLIENT UPDATED log for client zClient updated successfullyr   r  zValidation failed)r   r  r  )-r   is_authenticatedr^   rg   r  r   r   r   r   r   r  rm  r[  r  rJ  r  r   r>  r   rP  r)  rw   r  r  r  r  r  rE   r   ro  r   r  r  r  r  r  rh   r-  r   r  printrW  r   r  r  )r   r0  r  r]  r   
old_status	old_firstold_lastold_namerN  r   updated_clientr   new_nameold_data_filterednew_data_filteredr   log_datar   r   r   client_update  s   





	"rr  )defaultdictc           	      C   s6  t t}|  D ]\}}|dr|d}t|d dd }t|dkr5|d dd }||| |< qt|dkrt|d dd }|d	 dd }d
|| vrYg || d
< t|| d
 |krv|| d
 i  t|| d
 |ksc|dv r}d}n|dv rd}n|dv rd}||| d
 | |< qt|	 S )z
    Convert form-data like family_members[0][field]
    into proper list of dicts for serializer.
    Handles nested passports properly.
    rG  r   r  Nr7  r   r      r  rC  )
passportNor  r  )r  expiryr  )passportFilerX  rX  )
rs  r   r  r  r  r  r   r   rv  r  )	r   r  r   r%  r;  	fam_indexr   passport_index	sub_fieldr   r   r   normalize_family_members%  s2   

r{  c                 C   sn  t t|dd}| jdkrtdt|d| idjdtjdS | jd	kr| j	 }| j
D ]	}| j
| ||< q*t|}t|d| || jd
d}| rz>| }t|d|jd}|D ]"}	tjj|	|	jj|	jdttjtd t|	td| jt d qWtdd|jdtjdW S  t y }
 ztdddt!|
 idtj"dW  Y d }
~
S d }
~
ww td|j#dtj"dS d S )NzPrimary Member)r  r  r   Tr   r   )r   primary_memberrf   r   )r   primary_clientr   r  r  zFAMILY MEMBER ADDEDr  rL  z#Family member(s) added successfullyr  FdetailzFailed to save family members: r  )$r+   r   r.  r^   rw   r   rg   r   r   r/  rP  r{  ry   r   r  r  r   r   r   ro  r   r  r  r  r  r  rE   rJ  rh   r   r  r!  rW  r[  r  r  )r   r0  r}  combined_datar   family_members_datar   rG  response_serializerr!  r]  r   r   r   add_family_member  s   



r  c                 C   s  d}t | j|}	 tjjddjtdtddtddtddd	d
d	d}t
jjdd	ddd}| jd}| jd}| jd}| jd}g }	|r^t|tr[|}	n|g}	g }
|rmt|trj|}
n|g}
t }|	ry|t|	dO }|
r|t|
dO }|rtj|ntj }|rzt|d}|j| d}W n ty   tddddd Y S w |rzt|d}|j| d}W n ty   tddddd Y S w |dd	dd d  }g }|D ]6}||j|jr|jjnd |jr|jjnd |j|j|jr|jj nd |j!rt"|j!#d!nd |j$d" qtdt|t|||r:|j%|j&|j'|j(|j)d#ni d$t*j+dS )%N   TrO  rU  r  r  r  r  )r  r0  r  r  r   r  performedByfromDatetoDate)ref_client_id__client_id__in)performed_by_id__in%Y-%m-%d)performed_at__date__gteFzInvalid fromDate formatr  r  rf   )performed_at__date__ltezInvalid toDate formatr  r
  z-performed_atr  z%d/%m/%Y %H:%M)r   r0  r  r~  rf  
perform_byperformed_ontabler   )r   r  rW  r'  r   ),r   r   r   r   r   annotaterO   r:   r  r$  r   r   r   r   rv  r9   r   r   r   strptimer   rm  r^   r   r   r   r
  r0  r8  r  r  r  r  r  r   r  r
  r   r   r   r   r   rg   r   )r   r   r   r  rW  client_inputperformed_by_input	date_fromdate_to
client_idsuser_idsfilterslogs_qsdate_from_objdate_to_objr'  logr   r   r   
change_log  s   		


r  c              	   C   >   | sdS dD ]}zt | | W   S  ty   Y qw dS )z=Try parsing date in multiple formats, return None if invalid.N)r  z%d/%m/%Yr   r  r   rm  )date_strfmtr   r   r   parse_date_safe     r  )r   c                 C   s   t t|d}| jd}|dvrtdddddS |j}|d u r'|r$d	nd
}nt|}||_||_| j|_	|
  tdd|j d|j d|j|j d|j |ddtjdS )Nr  rg   )r   r  0rn  NFz$Invalid status. Allowed values: 1, 0r  r  rf   r   r  TzClient 'r  z' status updated successfully.)r   r   rg   )r   r  r  )r+   r   r   r   r^   r  r  r  r   r-  r  r  r  r0  rg   r   )r   r0  r  status_valueri  
new_statusr   r   r   client_active_status  s,   	r  )r  c                 C   s   z/t jj|d}| j|_|jdgd |j d|j  }|	  t
d| ddtjdW S  t jyC   t
d	d
dtjd Y S w )Nr  r-  rw  r  Tz deleted successfullyr  rf   FrP  r  )r   r   r   r   r-  r  r  r  r   r}  r4   rg   r   r   r  )r   r0  r  r  r   r   r   client_deletex  s(   
r  c                    s^  z| j d}|stddiddW S |jdd  }|dvr*tdd	iddW S |jd
r6t| nt	| dd  j
D  _
 t d ddddddddddddddd} j|dd g d} fd d|D }|rtdd!| iddW S ttjjddd"}t }g }d#}	  D ]p\}
}|drt|d nd}z|drtjj|dd$jnd}W n tjy   d}Y n tjy   d}Y nw d}|sd%}n||v rd&}n||v rd'}|d}|d}|d}|s|rd(}|s
|s
|r
d)}|s|r|rd*}|r |s |r d+}z|dr7tjj|d  d, nd }W n tjtjfyJ   d-}Y nw z|drbtjj|d  d, nd }W n tjtjfyu   d.}Y nw z|drtjj|d  d, nd }W n tjtjfy   d/}Y nw || |i d0|
d1 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| |rd}	qt||	d2d3d4dW S  ty. } ztdt|id5dW  Y d }~S d }~ww )6Nr2  r3  No file uploadedr  rf   .r7  xlsxlsxcsvUnsupported file formatr5  c                 S       g | ]}|   d dqS r  rm  r   ru  r  rC  r   r   r   r          z)client_import_preview.<locals>.<listcomp>r   rU  r  r  r  r  r  r
  r  r  r   r  r  r  rW  
salutation
first_name	last_namer  r  r  primary_member_coder  r  addressr   r   r   pincodeTr=  r  r  r  r  r  c                    rA  r   rB  r  rE  r   r   r     rG  Missing required fields: rI  F)r  Client code is requiredzDuplicate client in fileClient already existszcountry is required for statez'country and state are required for cityzcountry is required zstate is required r   zCountry Does Not Existzstate Does Not Existzcity Does Not ExistrM  r   zPreview generated successfully)rN  rO  r  rz  r|  )rP  r   r4   r   r  ru  rQ  rR  rS  rT  r>  rU  rV  rX  r   r   r   rY  rZ  r[  r   r0  r   MultipleObjectsReturnedr   r   r   r   r\  r   rW  )r   r2  extrp  required_fieldsmissing_fieldsexisting_client_codesseen_client_codesrc  rO  rd  re  r  r
  r3  r   r   r   r]  r   rE  r   client_import_preview  s   
 (


444





	







 r  c                    s@  z| j d}|stddiddW S |jdd  }|dvr*tdd	iddW S |jd
r6t| nt	| dd  j
D  _
 t d ddddddddddddddd} j|dd g d} fd d|D }|rtdd!| iddW S ttjjddd"}d#}g }  D ]a\}	}
z9|
drt|
d nd }|
d$rt|
d$ nd}z|
drtjjt|
dd%jnd }W n tjtjfy   d }Y nw z|
drtjj|
d  d& nd }W n tjtjfy   d }Y nw z|
dr tjj|
d  d& nd }W n tjtjfy3   d }Y nw z|
drKtjj|
d  d& nd }W n tjtjfy^   d }Y nw |sftd'||v rotd(tjjd2i d|d|
dd|
dd|
dd|
dpdd|
dd|d$|d|
dd|
dd|
dd|d|d|d|
dd)d*d+d |d,7 }|| W q t y } z|!|	d- |
dt|d. W Y d }~qd }~ww td|t"||d/d0dW S  t y } ztdt|id1dW  Y d }~S d }~ww )3Nr2  r3  r  r  rf   r  r7  r  r  r5  c                 S   r  r  r  rC  r   r   r   r   Y  r  z!client_import.<locals>.<listcomp>r   rU  r  r  r  r  r  r
  r  r  r   r  r  r  rW  r  Tr=  r  c                    rA  r   rB  r   rE  r   r   r   r  rG  r  rI  r   relationr  r   r  r  r  r   r  r  r   )rM  r  r3  rh  rz  r|  r   )#rP  r   r4   r   r  ru  rQ  rR  rS  rT  r>  rU  rV  rX  r   r   r   rY  rZ  r[  r   r  r0  r   r  r   r   r   r   rm  ro  r\  rW  r   r   )r   r2  r  rp  r  r  r  success_countfailed_rowsrd  re  r  r  r
  r   r   r   r]  r   rE  r   client_importE  s   
  ,244





	



 r  c                 C   s   | j dkr:| jd}| jd}| jd}| jd}t ||||}d|i}t|jd}tdd	|d
S tdddddS )Nr   time_periodr  report_from_datereport_to_datevisa_reportzutf-8Tr   )rg   r  DataFzInvalid requestry  r  rf   )	r.  r   r   r   get_visa_expiry_reportr^   contentdecoder4   )r   r  r  r  r  r  r   report_datar   r   r   visa_expiry_reportC  s    
'r  r[  c                 C   s<   zt jj| |d}t|d| dW S  t jy   Y dS w )z=
    Check if user has given permission for a menuaction
    )r   ref_menu__menu_actionrq  F)r
   r   r   rT  r   )r   
menuactionr~  r  r   r   r   has_menu_permission  s   r  c              	   C   r  )8Convert string (DD-MM-YYYY or YYYY-MM-DD) to date safelyN)r  r  r  r%  r  r   r   r   rS     r  rS   c                 C   s  d}t | j|}| jdp| jd}| jdp| jd}| jdp+| jd}| jdp7| jd}t  }tj	j
dddd	}|d
kr[|j||tdd gd}n||dkrm|j||tdd gd}nj|dkr|j||tdd gd}nX|dkr|j||tdd gd}nF|dkr|j||tdd gd}n4|dkr|j||tdd gd}n"|dkr|j||tdd gd}n|dkr|r|r|j||gd}|r|j|d}g }	|dD ]D}
|
jsq|
jr|
j| jnd}|	|
j|
jjpd d|
jjp	d  |
jr|
jjnd|
j|
j|
j||
jr%|
jjndd qtd |	r2dnd!|	|rD|j|j|j |j!|j"d"ni d#t#j$d$S )%z
    Visa Expiry Report API
    - Filters: today, 1Week, 2Weeks, 3Weeks, 1Month, 3Months, 6Months, 1Year, custom
    - Optional filter by visa country
    - Returns visa expiry details
       r  r  r  	countryIdN)r  r  r1  1Week   days)visa_to_date__range2Weeks   3Weeks   1Month   3MonthsZ   6Months   1Yearm  custom)ref_visa_country_idr  r   r  )r   r  country_namer  r  r  remaining_daysrZ  Tz,No visa records found for the given criteriar   r   r  r   r   rf   )%r   r   r   r   r  r   r  r   r   r   excluder   r   r   r$  r  r  r  r   r   r  r  r   r1  r   r  r  rZ  r   r^   r   r   r   r   r   rg   r   )r   r   r   r  r  r  r1  todayqsr   r<  r  r   r   r   visa_expiry  sr   
$
r  c                 C   sX   | sdS z	t | d W S  ty+   zt | d W  Y S  ty*   Y Y dS w w )z*Convert string (YYYY-MM-DD) to date safelyNr  r  r  )r%  r   r   r   rS     s   c              
   C   s  d}t | j|}| jdp| jdp| jd}| jdp+| jdp+| jd}| jdp=| jdp=| jd}t|trG| n|}t|trR| n|}t	 
 }tjd}	|rt|  d	d
}
|
dkr|	j||tdd gd}	n||
dkr|	j||tdd gd}	nj|
dkr|	j||tdd gd}	nX|
dkr|	j||tdd gd}	nF|
dkr|	j||tdd gd}	n4|
dkr|	j||tdd gd}	n"|
dkr|	j||tdd gd}	n|
dkr|r|r|	j||gd}	g }|	jdddD ]5}|jsq|jr|j| jnd }||jj|jjp&d  d	|jjp.d   |j|j|d! qtd|rDd nd"||rV|j|j|j|j |j!d#ni d$t"j#d%S )&Nr  
timePeriodr  	startDater  endDater  r  r  rm  1weekr  )weeks)passport_expiry_date__range2weeksr   3weeksr   1monthr  r  3monthsr  6monthsr  1yearr  r  T)passport_expiry_date__isnullr  r   )r0  r  r  r  r  z)No passports found for the given criteriar   r  rf   )$r   r   r   r   r  r   r[  r   r   r  r   r   r   r   ru  r  r   r   r  r$  r  r  r  r   r0  r  r  r  r^   r   r   r   r   r   rg   r   )r   r   r   r  
start_dateend_dater  r  r  r  tpr   r:  r  r   r   r   passports_expiry_report  sn   $$$$r  c                 C   sr  d}t | j|}| jdp| jd}| jdp| jd}| jdp+| jd}t  }tjj	dd	
d
}|dkrG|j|d	}n|dkrY|j||tdd gd}n||dkrk|j||tdd gd}nj|dkr}|j||tdd gd}nX|dkr|j||tdd gd}nF|dkr|j||tdd gd}n4|dkr|j||tdd gd}n"|dkr|j||tdd gd}n|dkr|r|r|j||gd}g }|dD ];}	|	jsq|	jr|	j| jnd}
||	j|	jj|	jjpd d|	jjpd  |	jr|	jjnd|	j|	j|
d  qtd!|rdnd"||r1|j|j|j|j|jd#ni d$t j!d%S )&z
    Travel Insurance Expiry Report API
    - Filters: today, 1Week, 2Weeks, 3Weeks, 1Month, 3Months, 6Months, 1Year, custom
    - Extra filters: expired, valid
    r  r  r  r  r  r  r  N)r   r  r  r  r  r  )insurance_to_date__ranger  r  r  r  r  r  r  r  r  r  r  r  r  r   r   r  )r   r0  r  r[  r  r   r  Tz8No travel insurance records found for the given criteriar   r  rf   )"r   r   r   r   r   r  r   r   r   r  r   r   r   r$  r  r   r  r   r   r0  r  r  r   r[  r   r  r^   r   r   r   r   r   rg   r   )r   r   r   r  r  r  r  r  r   r>  r  r   r   r   travel_insurance_expiry-  sn   		"

r  c              	   C   r  )r  N)z%d-%mz%m-%dr  r  r   r   r   rS     r  c              
   C   s  d}t | j|}| jdp| jdp| jd}| jdp+| jdp+| jd}| jdp=| jdp=| jd}t  }tj	j
dd	}|}|td
d }	d}
d }d }z|rt|  dd}|dkrq|}	n|dkr}|tdd }	n|dkr|tdd }	n|dkr|tdd }	n|dkr|tdd }	nu|dkr|tdd }	ni|dkr|tdd }	n]|dkr|td
d }	nQ|dkr|r|rz t|d }t|d }|j|jf}|j|jf}d}
W n% ty   |j|jf}|td
d j|td
d jf}d}
Y nw nd}
W n ty*   |}|td
d }	Y nw g }|D ]}|jj}|jj}||f}|
rc|rc|rc||krY||  koU|kn  }n,||kpa||k}n"t|j||}||k rxt|jd ||}||  ko|	kn  }|rt|j||}||k rt|jd ||}|| j}||j|jpd  d|jpd   |j|j|j|d! q/td|rd nd"||r|j |j!|j"|j#|j$d#ni d$t%j&d%S )&N	   r  r  r  r  r  r  T)dob__isnullr  r  Fr  rm  r  r  r  r     r  r  r  r  r  r  r  r  r  r  r  r  r   )r0  r  r  r  r  r  z2No upcoming birthdays found for the given criteriar   r  rf   )'r   r   r   r   r  r   r  r   r   r   r  r   r[  r   ru  r  r   r  monthdayrm  rW  r  yearr  r   r0  r  r  r  r  r^   r   r   r   r   r   rg   r   )r   r   r   r  r  r  r  r  start_filter
end_filtercustom_rangestart_tuple	end_tupler  start_dtend_dtr   r7  birth_month	birth_daydob_mdin_rangedob_nextdob_upcomingr  r   r   r   birth_report  s   





 



 	r  c              
   C   s  d}t | j|}| jdp| jdp| jd}| jdp+| jdp+| jd}| jdp=| jdp=| jd}t  }tj	j
dd	}|}|td
d }	d}
d }d }z|rt|  dd}|dkrq|}	n|dkr}|tdd }	n|dkr|tdd }	n|dkr|tdd }	n|dkr|tdd }	nu|dkr|tdd }	ni|dkr|tdd }	n]|dkr|td
d }	nQ|dkr|r|rz t|d }t|d }|j|jf}|j|jf}d}
W n% ty   |j|jf}|td
d j|td
d jf}d}
Y nw nd}
W n ty*   |}|td
d }	Y nw g }|D ]}|js7q/|jj}|jj}||f}d}|
rk|rk|rk||kra||  ko]|kn  }n,||kpi||k}n"t|j||}||k rt|jd ||}||  ko|	kn  }|rt|j||}||k rt|jd ||}|| j}||j|jpd  d|jpd   |j|j|j|d! q/td|rd nd"||r|j |j!|j"|j#|j$d#ni d$t%j&d%S )&Nr  r  r  r  r  r  r  T)anniversary_date__isnullr  r  Fr  rm  r  r  r  r  r	  r  r  r  r  r  r  r  r  r  r  r  r  r   )r0  r  r  r  r  r  z6No upcoming anniversaries found for the given criteriar   r  rf   )'r   r   r   r   r  r   r  r   r   r   r  r   r[  r   ru  r  r   r  r
  r  rm  rW  r  r  r  r   r0  r  r  r  r  r^   r   r   r   r   r   rg   r   )r   r   r   r  r  r  r  r  r  r  r  r  r  r  r  r  r   r7  
anni_monthanni_dayanni_mdr  r  r  r  r   r   r   anniversary_report  s   





 



 	r  )r   c                 C   s  d}t | j|}| jdkr| jn| j}dd }dd }||d}||d}||d	}||d
}	||d}
||d}||d}||d}||d}||d}|rg||nd }|ro||nd }tj }|r|r|j	t
|tjt
|tjfd}n|r|j	t
|tjd}n|r|j	t
|tjd}|r|j	|d}|	r|j	|	d}|
r|j	|
d}|r|j	|d}|r|j	|d}|r|j	|d}|r|j	|d}|r|j	|d}| }g }|D ]r}dt	d |j|j|jg}dd |j D }||j|dd d |D dt	d |jr$|jjnd!|jr-|jjnd!|jr6|jjnd!g|	||j|jrGt|jnd!|jrQt|jnd!|jr[t|jnd!|jd" qtd#d!||rt|d$d%t|d&d%t|d'd%t|d(d%t|d)d%d*ni d+tj d,S )-N   r   c                 S   s   | dvr| S d S )N)Nr   r/  	undefinedr   )rr  r   r   r   	normalizeP  s   z report_client.<locals>.normalizec              
   S   sl   z| r|    dv rW d S t|   d W S  ty5 } ztd|  d|  W Y d }~d S d }~ww )N)r   r/  r!  r  u!   ❌ Date parse failed for value 'z': )r   ru  r   r  r   rW  rh  )rr  r]  r   r   r   safe_parse_dateS  s   z&report_client.<locals>.safe_parse_dater  r  r   r  r  r  status_filterr  r  city_id)created_at__range)created_at__gte)created_at__lte) client_companies__ref_company_id)reference_from__icontainsr	  )reference_remark__icontains)r  )residential_country_id)residential_state_id)residential_city_idr  c                 S   s*   g | ]}|j r|j jnd |j|jdqS )r   )r   r  r#  )r$  r   r  r#  )r   r@  r   r   r   r     s    z!report_client.<locals>.<listcomp>rV  c                 S   r  rg  r   r  r   r   r   r     r  r   )r   r  r   	referencer  r  rg   r   r   r   r  Tr   Fr   r   r   r   r   r  rf   )!r   r   r.  r  r   r   r   r   r   r   r   combiner   r  r/  r  r  r  r  r  r   r0  r  r  r  r[  r  r  r  r^   rT  rg   r   )r   r   r   paramsr"  r#  r  r  r   r  r  r  r$  r  r  r%  r  r   r  r8  r   r   r   r   report_clientG  s   	
&





r2  c                 C   s,   t jd }t|dd}td|jdS )NemailsTr   r  )r   r   r  r   rz   r^   r   )r   setupsr   r   r   r   list_email_setups  s   r5  c                 C   s   | j dkr| jdn| jd}|stdddtjdS tjj	|d\}}| j dkr8td|j
|jd	tjdS | j d
krc| jd}|sOtdddtjdS ||_|  td|j
|jd	tjdS d S )Nr   report_typeFzreport_type is requiredr  rf   )r6  T)r   r6  r  r   r  r  )r.  r  r   r   r^   rg   r  r   r   rw  r6  r  r   r  r!  )r   r6  setupr  r  r   r   r   email_setup  s@   "

r8  c                 C   s   t d tdddtjdS )Nsend_anniversary_emailTzAnniversary email triggeredr  rf   )r   r^   rg   r   r  r   r   r   trigger_anniversary_reportN   s   r:  c              
   C   s^   zt d tdddtjdW S  ty. } ztdd| dtjdW  Y d}~S d}~ww )	zN
    Trigger the birthday report email by calling the management command.
    send_birthday_emailTzBirthday email triggeredr  rf   Fz"Failed to trigger birthday email: N)r   r^   rg   r   rW  r  r   r]  r   r   r   trigger_birthday_reportU   s   &r=  c              
   C   b   zt d tdddtjdW S  ty0 } ztddt| dtjdW  Y d}~S d}~ww )	zI
    Trigger the passport expiry email report via management command
    send_passport_expiry_emailTz4Passport expiry email report triggered successfully.r  rf   Fz*Failed to trigger passport expiry report: Nr   r^   rg   r   rW  r[  r  r<  r   r   r   trigger_passport_expiry_reportb   s"   rA  c              
   C   r>  )	zQ
    Trigger the travel insurance expiry email report via management command
    "send_travel_insurance_expiry_emailTz<Travel insurance expiry email report triggered successfully.r  rf   Fz2Failed to trigger travel insurance expiry report: Nr@  r<  r   r   r   trigger_travel_insurance_reportu   s"   rC  c              
   C   sb   zt d tdddtjdW S  ty0 } ztddt| dtjdW  Y d }~S d }~ww )Nsend_visa_expiry_emailTz0Visa expiry email report triggered successfully.r  rf   Fz&Failed to trigger visa expiry report: r@  r<  r   r   r   trigger_visa_expiry_report   s"   rE  c                 C      t | dddS )Nz Views/errors/html/error_404.htmlr{  rf   r   r  r   r   r   error_404_view      rH  c                 C   rF  )Nz Views/errors/html/error_400.htmlr  rf   rG  r  r   r   r   error_400_view   rI  rJ  r  )r[  (  io	itertoolsr   django.shortcutsr   r   users.modelsr   r   r   r   r	   r
   r   r   r   r   r   r   r   r   r   r   r   r   r   r   users.formsr   r   r   r   r   r   r   r    r!   r"   r#   r$   r%   r&   r'   django.httpr(   r)   r*   r+   django.contrib.auth.decoratorsr,   django.contrib.authr-   r.   django.contribr/   django.contrib.auth.viewsr0   r1   django.urlsr2   django.utils.httpr3   r4   r5   django.template.loaderr6   django.utils.timezoner7   django.core.paginatorr8   django.db.modelsr9   r:   r;   users.utilsr<   r=   r>   r?   r@   rA   rB   rC   users.signalsrD   rE   rF   django.utils.cryptorG   django.viewsrH   django.core.mailrI   django.confrJ   rK   rL   django.contrib.auth.hashersrM   rN   django.db.models.functionsrO   django.views.decorators.csrfrP   django.views.decorators.httprQ   django.utils.decoratorsrR   django.utils.dateparserS   rT   django.utils.dateformatrU   django.utils.htmlrV   django.formsrW   rX   users.permissionsrY   rZ   r[   	django.dbr\   rest_framework.responser^   rest_framework.permissionsr_   r`   rest_framework.authtoken.modelsra   rest_framework_simplejwt.tokensrb   rc   rest_framework.authenticationrd   rest_framework.viewsre   rest_frameworkrg   django.core.serializers.jsonrh   r  rest_framework.parsersri   rj   rk   rest_framework.decoratorsrl   rm   rn   rest_framework.paginationro   r  users.serializersrp   rq   rr   rs   rt   ru   rv   rw   rx   ry   rz   r{   r|   r}   r~   r   r   r   r   r   django.contrib.auth.modelsr   openpyxlrandompandasrR  r  r  r   django.utilsr   r   r   r   r   r   r   users.helperr   r   r   django.core.managementr   jwtr   r   r   r   django.forms.modelsr   r   r)  rl  r1  rf  ru  r~  r  r  r  r  r  r  r  r  r  r  r  r  rG  rX  rY  r  r  r  r  r  r  r  r  r  r  r"  r&  rer)  r4  r>  r9  rM  rJ  rK  rO  r`  rr  collectionsrs  r{  r  r  r  r   r  r  r  r  r  r  r  r  r  r  r  r  r2  r5  r8  r:  r=  rA  rC  rE  rH  rJ  r   r   r   r   <module>   s   \H(\$*\`
D5
"
,CP  Z/3*,_J "w5=
 F
W0
 >/  lO ,:d P /( 
]3 I
"A S 8   v
r 
$

Fo_%$9b!#MY$) zK68
S\3*

LU
G 
? 
|
$*#
