
     ix                     	   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mZ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 d
dlmZmZmZmZmZmZm Z 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+  edgd ed      e ed      d       G d dejX                               Z- edgded ed      d       G d d ej\                               Z/ ed!gd" ed       ed#      d$       G d% d&ej`                               Z1 ed!gd'ee ed(       ed)      d*+       G d, d-ejd                               Z3 ed!gd. ed/       ed)      d$       G d0 d1ejh                               Z5 ed!gd2e ed3       ed(      d4+       G d5 d6ejd                               Z6 ed!gd7d8d9ie ed:      d$+       G d; d<ejn                               Z8 ed=gd>ee  ed(      d4+       G d? d@ejd                               Z9 ed=gdAee  ed(      d4+       G dB dCejd                               Z: ed=gdDe  edE      d$       G dF dGejv                               Z< ed=gdHe! edI       ed(       edE      dJ+       G dK dLe             Z= ed=gdMdN e d      i       G dO dPej`                               Z> ed!gdQdRdN e"d      i edSej~                  ej                  dTdUV       ed8ej                  ej                  dWdUV      gX       G dY dZej`                               ZB ed=gd[d\ ed]       ed^       edE      d_`       G da dbejh                               ZC ed=gdcdde  ede       edE      d_`       G df dgejn                               ZD ed=gdhdie  edj       edk       edE      dl`       G dm dnej                               ZF ed=gdodpdN e d      i edqej                  ej                  drdUV       edSej~                  ej                  dsdUV       edtej                  ej                  dudUV      gX       G dv dwej`                               ZGy)x    )genericsstatus)IsAuthenticated)APIView)Response)action)extend_schemaOpenApiResponseOpenApiParameter)OpenApiTypes)User)IntegrityError   )	StructureStructureUser
Invitation)	StructureSerializerStructureUserSerializerCreateStructureUserSerializerAddExistingUsersSerializerStructureUserListSerializerSendInvitationSerializerInvitationSerializerAcceptInvitationSerializerUserWithStructureSerializer)UserProfiletimezone)api_view)	timedelta)send_welcome_email	structurezList and create structuresT)many	Forbidden)description)        )tagssummary	responsesc                   "    e Zd ZegZeZd Zd Zy)StructureListCreateViewc                 j    t         j                  j                  | j                  j                        S N)userr   objectsfilterrequestr0   selfs    /backend/structures/views.pyget_querysetz$StructureListCreateView.get_queryset%   &      ''T\\->->'??    c                 $    |j                          y )N)save)r6   
serializers     r7   perform_createz&StructureListCreateView.perform_create)   s    r:   N)	__name__
__module____qualname__r   permission_classesr   serializer_classr8   r>    r:   r7   r-   r-      s     ***@r:   r-   z(Retrieve, update, and delete a structureN)r&      r(   c                       e Zd ZegZeZd Zy)"StructureRetrieveUpdateDestroyViewc                 j    t         j                  j                  | j                  j                        S r/   r1   r5   s    r7   r8   z/StructureRetrieveUpdateDestroyView.get_queryset;   r9   r:   N)r?   r@   rA   r   rB   r   rC   r8   rD   r:   r7   rG   rG   .   s     ***@r:   rG   zstructure-users)Get all users from a particular structureStructure not found)r&     c                   2     e Zd ZdZeZegZd Z fdZ	 xZ
S )StructureUsersListViewrI   c                 `    | j                   d   }t        j                  j                  |      S )Nstructure_idrO   )kwargsr   r2   r3   )r6   rO   s     r7   r8   z#StructureUsersListView.get_querysetL   s*    {{>2$$+++FFr:   c                     	 t         j                  j                  |      }t        |   |g|i |S # t         j                  $ r t	        ddid      cY S w xY w)NiderrorrJ   rK   r   )r   r2   getDoesNotExistr   superlist)r6   r4   rO   argsrQ   r"   	__class__s         r7   rW   zStructureUsersListView.getP   si    	J!))--->I w|G5d5f55 %% 	JW&;<SII	Js    6 "AA)r?   r@   rA   __doc__r   rC   r   rB   r8   rW   __classcell__)r\   s   @r7   rM   rM   ?   s'     42)*G
6 
6r:   rM   z(Create new user for particular structurezBad requestzStructure or User not found)r'     rK   )r)   r*   r4   r+   c                        e Zd ZdZeZegZd Zy)CreateStructureUserViewz$Create user for particular structurec                 V   	 t         j                  j                  |      }| j                  |j                        }|j                  d       |j                  d	   }|j                  d
   }	 t        j                  j                  ||||j                        }	t        |	      }
t	        dd|
j                  dd      S # t         j                  $ r t	        ddid      cY S w xY w# t        $ r t	        ddid      cY S t        j                  $ r t	        ddid      cY S w xY w)NrS   rU   rJ   rK   rV   dataTraise_exceptionuser_idrole)r"   rg   rh   
created_byz$User added to structure successfullysuccessmessagerd   r'   z'User is already added to this structurer_   zUser not found)r   r2   rW   rX   r   get_serializerrd   is_validvalidated_datar   creater0   r   r   r   )r6   r4   rO   r[   rQ   r"   r=   rg   rh   structure_userresponse_serializers              r7   rp   zCreateStructureUserView.createk   sK   	J!))--->I ((gll(;
D1++I6((0	E*2299#"<<	 : N #:."IA+00 	 % %% 	JW&;<SII	J0  	B     	EW&67DD	Es*    C /AC* "C'&C'*D(!D('D(N)	r?   r@   rA   r]   r   rC   r   rB   rp   rD   r:   r7   ra   ra   \   s     /4)* Er:   ra   %Delete user from particular structurezUser removed successfullyc                       e Zd ZdZegZd Zy)DeleteStructureUserViewrs   c                     	 t         j                  j                  ||      }|j                          t	        dddd      S # t         j
                  $ r t	        ddid	      cY S w xY w)
NrO   rg   Tz(User removed from structure successfullyrk   rl   r&   rV   rU    User not found in this structurerK   )r   r2   rW   deleter   rX   )r6   r4   rO   rg   r[   rQ   rq   s          r7   rz   zDeleteStructureUserView.delete   s    	*2266) 7 N !!#E  
 )) 	; 	s   A A "A('A(N)r?   r@   rA   r]   r   rB   rz   rD   r:   r7   ru   ru      s     0)*r:   ru   z3Add multiple existing users to particular structurezUsers added successfully)r'   r_   c                        e Zd ZdZeZegZd Zy)AddExistingUsersToStructureViewz*Add existing users to particular structurec           	         	 t         j                  j                  |      }| j                  |j                        }|j                  d       |j                  d	   }|j                  d
   }g }	g }
|D ]Z  }	 t        j                  j                  ||||j                  d      \  }}|r|	j                  |       n|
j                  |       \ t	        ddt        |	       d|	|
t        |	      dd      S # t         j                  $ r t	        ddid      cY S w xY w# t        j                  $ r |
j                  |       Y w xY w)NrS   rU   rJ   rK   rV   rc   Tre   user_idsrh   )rh   ri   )r"   rg   defaultszAdded z users to structure)rk   rl   added_usersskipped_userstotal_addedr'   )r   r2   rW   rX   r   rm   rd   rn   ro   r   get_or_creater0   appendr   len)r6   r4   rO   r[   rQ   r"   r=   r~   rh   r   r   rg   rq   createds                 r7   rp   z&AddExistingUsersToStructureView.create   si   	J!))--->I ((gll(;
D1,,Z8((0G.*7*?*?*M*M'# $&-ll +N +' &&w/!((1  & K 011DE&*{+
  	? %% 	JW&;<SII	J8 $$ .$$W-.s$    C: 8AD":"DD"$E	E	N)	r?   r@   rA   r]   r   rC   r   rB   rp   rD   r:   r7   r|   r|      s     51)*(r:   r|   zUpdate user role in structurerh   AdminzUser not found in structurec                        e Zd ZdZeZegZd Zy)UpdateStructureUserRoleViewz(Update user role in particular structurec                    	 t         j                  j                  ||      }|j
                  j                  d      }|t         j                  D cg c]  }|d   	 c}vrt	        ddid	      S ||_        |j                          | j                  |      }	t	        d
d|	j
                  d      S # t         j                  $ r t	        ddid      cY S w xY wc c}w )Nrw   rU   ry   rK   rV   rh   r   z.Invalid role. Must be Admin, Editor, or Viewerr_   TzUser role updated successfullyrj   )
r   r2   rW   rX   r   rd   ROLE_CHOICESrh   r<   rm   )
r6   r4   rO   rg   r[   rQ   rq   new_rolechoicer=   s
             r7   patchz!UpdateStructureUserRoleView.patch   s    	*2266) 7 N <<##F+M4N4NO4N&F1I4NOOI  '((8
7OO
  	 )) 	; 	 Ps   !B1 C1"CCN)	r?   r@   rA   r]   r   rC   r   rB   r   rD   r:   r7   r   r      s     3.)*r:   r   invitationsz.Send invitation to user for specific structurec                        e Zd ZdZeZegZd Zy)SendStructureInvitationViewz,Send invitation to join a specific structurec                     | j                  |j                  ||d      }|j                  d       |j                         }t	        |      }t        dd|j                  dd      S )	Nr4   rO   rd   contextTre   zInvitation sent successfullyrj   r'   rV   rm   rd   rn   r<   r   r   )r6   r4   rO   r[   rQ   r=   
invitationrr   s           r7   rp   z"SendStructureInvitationView.create  sv    (( 'F ) 

 	D1__&
2:>5',,
 	 	r:   N	r?   r@   rA   r]   r   rC   r   rB   rp   rD   r:   r7   r   r     s     7/)*r:   r   z6Send general system invitation (no specific structure)c                        e Zd ZdZeZegZd Zy)SendGeneralInvitationViewz*Send general invitation to join the systemc                     | j                  |j                  |d d      }|j                  d       |j                         }t	        |      }t        dd|j                  dd      S )	Nr   r   Tre   z$General invitation sent successfullyrj   r'   rV   r   )r6   r4   r[   rQ   r=   r   rr   s          r7   rp   z SendGeneralInvitationView.create7  sv    (( '> ) 

 	D1__&
2:>=',,
 	 	r:   Nr   rD   r:   r7   r   r   )  s     5/)*r:   r   zGet invitation detailsInvitation not foundc                   R    e Zd ZdZej
                  j                         ZeZ	g Z
d Zy)InvitationDetailViewzGet invitation details by IDc                     	 t         j                  j                  |      }|j                  r|j	                          | j                  |      }t        d|j                  d      S # t         j                  $ r t        dddd      cY S w xY w)	NrS   T)rk   rd   Fr   rx   rK   rV   )	r   r2   rW   
is_expiredexpirerm   r   rd   rX   r6   r4   invitation_idr[   rQ   r   r=   s          r7   rW   zInvitationDetailView.getT  s    	#++//=/AJ $$!!#,,Z8J"  
 && 	 1  	s   A$A' '#BBN)r?   r@   rA   r]   r   r2   allquerysetr   rC   rB   rW   rD   r:   r7   r   r   F  s,     '!!%%'H+r:   r   z)Accept invitation and create user accountz$Invitation accepted and user created)r&   r_   rK   c                       e Zd ZdZg Zd Zy)AcceptInvitationViewz
    Accept an invitation and create a user account
    
    Validates:
    - Invitation exists
    - Invitation is in pending status
    - Invitation hasn't expired
    - No user account exists with the invitation email
    c           
      b	   ddl m} ddlm} 	 t        j
                  j                  |      }|j                  dk(  rJt        d	d
|j                  r|j                  j                  d      nd dddt        j                        S |j                  dk(  rt        ddddt        j                        S |j                  dk(  s*|j                  r|j                   |j                         k  rp|j                  dk7  rd|_	        |j!                          t        dd|j                  r|j                  j                  d      nd dddt        j                        S t"        j
                  j%                  |j&                        j)                         r-t        dd|j&                   dddt        j                        S t+        |j,                        }|j/                         s%t        |j0                  t        j                        S 	 |j3                         5  t"        j
                  j5                  |j6                  d   |j&                  |j6                  j                  dd      |j6                  j                  dd      |j6                  d          }t8        j
                  j;                  ||j<                  d!"       |j>                  rAt@        j
                  j;                  |j>                  ||jB                  |j<                  #       d|_	         |j                         |_        ||_"        |j!                          tG        ||j>                  |j>                  r|jI                         nd        t        d$|jJ                  |jL                  |j&                  |jN                  |jP                  d%|j>                  r-|j>                  jJ                  |j>                  jR                  d&nd |j>                  r|jI                         nd d't        jT                        cd d d        S # t        j                  $ r  t        ddit        j                        cY S w xY w# 1 sw Y   y xY w# tV        $ r1}t        d(tY        |      d)t        jZ                        cY d }~S d }~ww xY w)*Nr   r   )transactionrS   rU   zInvitation not found.rV   acceptedz*This invitation has already been accepted.zUser account was created on z%B %d, %Y at %I:%M %pzN/A.)rU   detailr   	cancelledz#This invitation has been cancelled.z6Please contact the administrator for a new invitation.expiredzThis invitation has expired.zInvitation expired on )emailzUser account already exists.zAn account with email 'z'' already exists. Please login instead.user_existsrc   username
first_name 	last_namepassword)r   r   r   r   r   F)r0   ri   super_admin)r"   r0   rh   ri   z!Invitation accepted successfully.)rT   r   r   r   r   )rT   name)rl   r0   r"   rh   zFailed to create user account.)rU   r   ).django.utilsr   	django.dbr   r   r2   rW   rX   r   r   HTTP_404_NOT_FOUNDaccepted_atstrftimeHTTP_400_BAD_REQUEST
expires_atnowr<   r   r3   r   existsr   rd   rn   errorsatomiccreate_userro   r   rp   
invited_byr"   r   rh   created_userr!   get_role_displayrT   r   r   r   r   HTTP_201_CREATED	ExceptionstrHTTP_500_INTERNAL_SERVER_ERROR)	r6   r4   r   r   r   r   r=   r0   es	            r7   postzAcceptInvitationView.post  sp   ))	#++//=/AJ 
*I <  zD  zP  zPZ=S=S=\=\]t=u  V[  =\  \]  ^(
 22  +BV)
 22  	)j.C.C
H]H]`l`h`l`l`nHn  I-$-
!!; 6r|  sH  sHz7L7L7U7UVm7n  NS  7T  TU  V'
 22  <<Z%5%56==?; 7
8H8H7IIpq+
 22  0W\\B
""$J--f6Q6QRRB	##%||//'66zB$**)88<<\2N(77;;KL'66zB 0  ##**)44 % +  ''!))00","6"6!'__#-#8#8	 1  %/
!)5
&*.
'! #((5?5I5IJ//1t  #F"&''(,%)ZZ*.//)-! (11 #-"6"6"9"9$.$8$8$=$=& 8<AKAUAU
 ; ; =[_ "22!O &%K && 	1200 	J &%t  	=!!f << 	sN    P2 Q4 H	Q((	Q4 20Q%$Q%(Q1-Q4 1Q4 4	R.=&R)#R.)R.N)r?   r@   rA   r]   rB   r   rD   r:   r7   r   r   h  s     Mr:   r   z)List all invitations sent by current userr&   c                        e Zd ZdZeZegZd Zy)MyInvitationsListViewz%List invitations sent by current userc                 j    t         j                  j                  | j                  j                        S N)r   r   r2   r3   r4   r0   r5   s    r7   r8   z"MyInvitationsListView.get_queryset  s&    !!((DLL4E4E(FFr:   N	r?   r@   rA   r]   r   rC   r   rB   r8   rD   r:   r7   r   r     s     0+)*Gr:   r   z&Get all users with their structure IDszXReturns a flat array of all users across all structures with their structure informationrO   zFilter by specific structure IDF)r   typelocationr%   requiredz&Filter by role (Admin, Editor, Viewer))r)   r*   r%   r+   
parametersc                        e Zd ZdZeZegZd Zy)AllStructureUsersViewz6Get all users with their structure IDs in a flat arrayc                 h   t         j                  j                         j                  dd      j	                  d      }| j
                  j                  j                  dd       }| j
                  j                  j                  dd       }|r|j                  |      }|r|j                  |      }|S )Nr0   r"   -created_atrO   rh   rP   )rh   )	r   r2   r   select_relatedorder_byr4   query_paramsrW   r3   )r6   r   rO   rh   s       r7   r8   z"AllStructureUsersView.get_queryset8  s     ((,,.==fkR[[\ij ||0044^TJ||((,,VT:LAHD1Hr:   N)	r?   r@   rA   r]   r   rC   r   rB   r8   rD   r:   r7   r   r     s    2 A2)*r:   r   Delete/Cancel an invitationzTDelete or cancel an invitation. Only the user who sent the invitation can delete it.zInvitation deleted successfullyz4Forbidden - not authorized to delete this invitation)r&   r(   rK   )r)   r*   r%   r+   c                   &    e Zd ZdZegZdZd Zd Zy)DeleteInvitationViewr   r   c                 j    t         j                  j                  | j                  j                        S r   r   r5   s    r7   r8   z!DeleteInvitationView.get_querysetX  s&    !!((DLL4E4E(FFr:   c                    	 t         j                  j                  |      }|j                  |j                  k7  rt        dddd      S |j                  dk(  rt        dddd	      S |j                  }|j                  r|j                  j                  nd
}|j                          t        dd| d| ddd      S # t         j                  $ r t        dddd      cY S w xY w)NrS   Fz0You are not authorized to delete this invitationrx   r(   rV   r   z$Cannot delete an accepted invitationr_   SystemTzInvitation to z for z has been deleted successfullyr&   r   rK   )r   r2   rW   r   r0   r   r   r   r"   r   rz   rX   )r6   r4   r   r[   rQ   r   r   structure_names           r7   rz   zDeleteInvitationView.delete\  s   !	#++//=/AJ $$4$Q!     J.$E!   $$E:D:N:NZ1166T\N +E7%7GGef  
 && 	 1  	s   AC C *AC #C'&C'N)	r?   r@   rA   r]   r   rB   lookup_fieldr8   rz   rD   r:   r7   r   r   I  s      &)*"LG"r:   r   zCancel an invitationzQCancel a pending invitation. Only the user who sent the invitation can cancel it.z4Forbidden - not authorized to cancel this invitationc                        e Zd ZdZegZeZd Zy)CancelInvitationViewzCancel a pending invitationc                    	 t         j                  j                  |      }|j                  |j                  k7  rt        dddd      S |j                  dk(  rt        dddd	      S |j                  d
k(  rt        dddd	      S d
|_        |j                          | j                  |      }t        dd|j                  dd      S # t         j                  $ r t        dddd      cY S w xY w)NrS   Fz0You are not authorized to cancel this invitationrx   r(   rV   r   z$Cannot cancel an accepted invitationr_   r   zInvitation is already cancelledTz*Invitation has been cancelled successfullyrj   r&   r   rK   )r   r2   rW   r   r0   r   r   r<   rm   rd   rX   r   s          r7   r   zCancelInvitationView.patch  s'   '	#++//=/AJ $$4$Q!     J.$E!  
   K/$@!   !,JOO,,Z8JG" 	  && 	 1  	s%   AC C *C 	AC #C21C2N)	r?   r@   rA   r]   r   rB   r   rC   r   rD   r:   r7   r   r     s     &)*+(r:   r   zResend an invitationzResend an invitation email to the user. Updates the expiration date to 15 days from now. Only pending or expired invitations can be resent.z)Bad request - invitation cannot be resentz4Forbidden - not authorized to resend this invitation)r&   r_   r(   rK   c                        e Zd ZdZegZeZd Zy)ResendInvitationViewzResend an invitation emailc                    	 t         j                  j                  |      }|j                  |j                  k7  rt        dddd      S |j                  dk(  rt        dddd	      S |j                  d
k(  rt        dddd	      S |j                  dk(  rd|_        t        j                         t        d      z   |_
        |j                          ddlm}  ||      }|st        dddd      S | j                  |      }t        dd|j                   |j                   dd      S # t         j"                  $ r t        dddd      cY S w xY w)NrS   Fz0You are not authorized to resend this invitationrx   r(   rV   r   z$Cannot resend an accepted invitationr_   r   zECannot resend a cancelled invitation. Please create a new invitation.r   pending   )daysr   )send_invitation_emailz>Invitation updated but failed to send email. Please try again.i  Tz"Invitation resent successfully to rj   r&   r   rK   )r   r2   rW   r   r0   r   r   r   r   r    r   r<   utilsr   rm   r   rd   rX   )	r6   r4   r   r[   rQ   r   r   
email_sentr=   s	            r7   r   zResendInvitationView.post  s   8	#++//=/AJ $$4$Q!     J.$E!  
   K/$f!     I-$-
! %-LLNYB5G$GJ!OO 5 /z:J$_!   ,,Z8J?
@P@P?QR" 	  && 	 1  	s+   AD. D. *D. 	A+D. 58D. .#EEN)	r?   r@   rA   r]   r   rB   r   rC   r   rD   r:   r7   r   r     s     %)*+9r:   r   z%List all invitations sent by any userz^Returns all invitations in the system regardless of who sent them. Includes filtering options.r   z8Filter by status (pending, accepted, expired, cancelled)zFilter by structure IDr   zFilter by invitee emailc                        e Zd ZdZeZegZd Zy)AllInvitationsListViewz;List all invitations sent by any user with optional filtersc                    t         j                  j                         j                  ddd      j	                  d      }| j
                  j                  j                  dd       }| j
                  j                  j                  dd       }| j
                  j                  j                  dd       }|r|j                  |      }|r|j                  |	      }|r|j                  |
      }|S )Nr   r"   r   r   r   rO   r   rV   rP   )email__icontains)	r   r2   r   r   r   r4   r   rW   r3   )r6   r   status_filterrO   email_filters        r7   r8   z#AllInvitationsListView.get_queryset(  s    %%))+::<Vdenno|} 1155hE||0044^TJ||0044WdCm<HLAHEHr:   Nr   rD   r:   r7   r   r     s    @ F+)*r:   r   )Hrest_frameworkr   r   rest_framework.permissionsr   rest_framework.viewsr   rest_framework.responser   rest_framework.decoratorsr   drf_spectacular.utilsr	   r
   r   drf_spectacular.typesr   django.contrib.auth.modelsr   r   r   modelsr   r   r   serializersr   r   r   r   r   r   r   r   r   users.modelsr   r   r   r   datetimer    structures.utilsr!   ListCreateAPIViewr-   RetrieveUpdateDestroyAPIViewrG   ListAPIViewrM   CreateAPIViewra   DestroyAPIViewru   r|   UpdateAPIViewr   r   r   RetrieveAPIViewr   r   r   INTQUERYSTRr   r   r   GenericAPIViewr   r   rD   r:   r7   <module>r     s   + 6 ( , , R R . + $ 8 8   % ! .  ! /
( d+ 5
h88 

 
6 5@)N)N @@ 
	7(d3)>?6X11 66* 
	6)$7)FG		%Eh44 %E	%EN 
	3)DE)FGh55 , 
	A&)CD7	-h&<&< --^ 
	+W$)FG	("8"8 @ 
<$!7	("8"8 ( 
D$!7	 6 6 ( 
$!)?@833 4 
7&)OP7)?@		Z7 Z	Zx 
7(d34
GH00 G
G 
	4j(d3 	!!%++9	
 	!!%++@	
0H00 10, 
)f)JK)_`)?@		+822 +	+Z 
"c!)_`)?@		-811 -	-^ 
" ^!)TU)_`)?@		
>822 >
>@ 
3p!t, 	!!%++R	
 	!!%++0	
 	!!%++1	
>X11 ?>r:   