U
    hN                     @   s   d dl 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 G d
d deeeZG dd deZG dd deZG dd deZG dd deZG dd deZdS )    N)models)timezone)RegexValidator)gettext_lazy)AbstractBaseUserPermissionsMixin
Permission)reverse)	BaseModel)UserManagerc                
       s,  e Zd ZdZejedddddZejddddddZ	ejddd	d
Z
ejdddd
ZejeddddedededdgdZejdddZejdddZejdededdZejddededdZejddededdZejejddZejddd d!Zejddd"d!Zejd#d$dZejddd%d!Zejejd&dZejdd'dZejd(dd)Z ejdd*Z!e" Z#d+Z$d,d-d.gZ%G d/d0 d0Z&d1d2 Z'd3d4 Z(d5d6 Z)d7d8 Z*d9d: Z+dTd<d=Z,d>d? Z-d@dA Z.dBdC Z/dDdE Z0dFdG Z1dHdI Z2dJdK Z3dLdM Z4 fdNdOZ5 fdPdQZ6dRdS Z7  Z8S )UUserz<
    Custom user model with email-based authentication.
    zEmail AddressTz%User's email address (used for login))uniquedb_index	help_text   z&Optional username for display purposes)
max_lengthr   nullblankr   zUser's first namer   r   r   zUser's last namezPhone Number   zUser's phone numberz^\+?1?\d{9,15}$zRPhone number must be entered in the format: '+999999999'. Up to 15 digits allowed.)regexmessage)r   r   r   r   
validatorsz8Designates whether this user should be treated as activedefaultr   Fz7Designates whether the user can log into the admin sitezIs Verifiedz$Whether the user's email is verifiedr   verbose_namer   zLast Login IPzIP address of last loginr   r   r   r   zEmail Verified AtzWhen the email was verifiedz&Date when the user account was createdzLast time the user logged inr   r   r   z+Last time the user was active in the systemr   z+Number of consecutive failed login attemptszAccount lockout expiration timez"When the password was last changedz.Whether multi-factor authentication is enabledd   )r   r   )r   emailusername
first_name	last_namec                   @   sP   e Zd ZedZedZdZdgZej	dgdej	ddgdej	d	gdgZ
d
S )z	User.Metar   ZUsersZaccounts_usersz-date_joinedr    )fields	is_activeis_verifieddate_joinedN)__name__
__module____qualname___r   verbose_name_pluraldb_tableorderingr   Indexindexes r1   r1   //var/www/html/Focus/src/apps/accounts/models.pyMeta   s   r3   c                 C   s.   | j r(| jr(| j  d| j d| j dS | jS )z
        String representation of the user.
        
        Returns:
            str: User's full name or email if name is not available
         z ())r"   r#   r    selfr1   r1   r2   __str__   s    zUser.__str__c                 C   s.   | j r(| jr(| j  d| j  p&| jS | jS )z
        Get the user's full name.
        
        Returns:
            str: User's full name or email if name is not available
        r4   )r"   r#   stripr!   r    r6   r1   r1   r2   get_full_name   s    zUser.get_full_namec                 C   s   | j p
| jS )z
        Get the user's short name.
        
        Returns:
            str: User's first name or email if first name is not available
        )r"   r!   r6   r1   r1   r2   get_short_name   s    zUser.get_short_namec                 C   s   | j rt | j k S dS )z)Check if the account is currently locked.F)account_locked_untilr   nowr6   r1   r1   r2   is_account_locked   s    zUser.is_account_lockedc                 C   s    d| _ d| _| jddgd dS )zUnlock the user account.Nr   r<   failed_login_attemptsupdate_fields)r<   r?   saver6   r1   r1   r2   unlock_account   s    zUser.unlock_account   c                 C   s(   t  t j|d | _| jdgd dS )z$Lock account for specified duration.)minutesr<   r@   N)r   r=   	timedeltar<   rB   )r7   Zduration_minutesr1   r1   r2   lock_account   s    zUser.lock_accountc                 C   s2   |  j d7  _ | j dkr |   | jdgd dS )z Increment failed login attempts.      r?   r@   N)r?   rG   rB   r6   r1   r1   r2   increment_failed_login   s    
zUser.increment_failed_loginc                 C   s   d| _ | jdgd dS )z0Reset failed login attempts on successful login.r   r?   r@   N)r?   rB   r6   r1   r1   r2   reset_failed_login   s    zUser.reset_failed_loginc                 C   s   t jj| dddS )z+Get all active roles assigned to this user.T)Zuserrole__userZuserrole__is_activer%   )Roleobjectsfilterr6   r1   r1   r2   	get_roles   s
    zUser.get_rolesc                 C   s   |   j|d S )z
        Check if the user has a specific role.
        
        Args:
            role (str): Role to check
            
        Returns:
            bool: True if user has the specified role
        )code)rO   rN   exists)r7   Z	role_coder1   r1   r2   has_role   s    
zUser.has_rolec                 C   s   |   d S )z%Get the user"s highest priority role.level)rO   order_byfirstr6   r1   r1   r2   get_highest_role   s    zUser.get_highest_rolec                 C   s(   |   }|D ]}|d|r dS qdS )z(Check if user can access a specific app.viewTF)rO   has_permission)r7   	app_labelZrolesroler1   r1   r2   can_access_app   s
    zUser.can_access_appc                 C   s$   d| _ t | _| jddgd dS )zMark user"s email as verified.Tr&   email_verified_atr@   N)r&   r   r=   r\   rB   r6   r1   r1   r2   verify_email   s    
zUser.verify_emailc                 C   s   || _ | jdgd dS )z$Update user"s last login IP address.last_login_ipr@   N)r^   rB   )r7   
ip_addressr1   r1   r2   update_last_login_ip   s    zUser.update_last_login_ipc                    s    t    | jr| j | _dS )zValidate the model data.N)supercleanr    lowerr6   	__class__r1   r2   rb      s    
z
User.cleanc                    s   |    t j|| dS )z"Override save to add custom logic.N)
full_cleanra   rB   )r7   argskwargsrd   r1   r2   rB      s    z	User.savec                 C   s   t dd| jidS )z-Return the absolute URL for the user profile.zaccounts:profilepk)rh   )r	   ri   r6   r1   r1   r2   get_absolute_url  s    zUser.get_absolute_url)rD   )9r(   r)   r*   __doc__r   
EmailFieldr+   r    	CharFieldr!   r"   r#   r   Zphone_numberBooleanFieldr%   is_staffr&   GenericIPAddressFieldr^   DateTimeFieldr\   r   r=   r'   
last_loginlast_activityPositiveIntegerFieldr?   r<   Zpassword_changed_atZmfa_enabledZemail_verification_tokenZreceive_notificationsr   rM   USERNAME_FIELDREQUIRED_FIELDSr3   r8   r:   r;   r>   rC   rG   rJ   rK   rO   rR   rV   r[   r]   r`   rb   rB   rj   __classcell__r1   r1   rd   r2   r      s   
	
r   c                   @   s|  e Zd ZdZejeejdededdZ	ej
edddded	d
ZejedddeddZejddededdZejedddeddZejedddeddZejdededdZejddddZejddd dZejd!d"d#d$gd%d&d'Zejdd(d)Zejd*ed+ed,d-Zejded.ed/d-Zejd!d0d1d2gd3d4ZG d5d6 d6Zd7d8 Zd9d: Zd;d< Z d=S )>Profilez
    Extended user profile model for additional user information.
    
    This model stores additional user data that doesn"t belong
    in the core User model, following the principle of separation of concerns.
    Zprofiler   zAssociated user account)	on_deleterelated_namer   r   ZAvatarzavatars/%Y/%m/TzUser's profile picture)	upload_tor   r   r   Z	Biographyi  zUser's biography or descriptionr   z
Birth DatezUser's birth dater   z	Job Titler   zUser job title or positionZCompanyzUser company or organizationZWebsitezUser's website URLr   r   r   2   UTCzUser's preferred timezone)r   r   r   
   enzUser's preferred language   )lightzLight Theme)darkz
Dark Theme)autozAuto (System)r   zUser's preferred UI themer   choicesr   r   z Whether to receive notificationsr   FzSMS NotificationszReceive SMS notificationsr   zEmail NotificationszReceive email notifications)publicZPublic)privateZPrivate)ZcontactszContacts Onlyr   )r   r   r   c                   @   s&   e Zd ZedZedZdgZdZdS )zProfile.Metarx   ZProfiles-created_atZaccounts_profileN)r(   r)   r*   r+   r   r,   r.   r-   r1   r1   r1   r2   r3   |  s   r3   c                 C   s   d| j  p| j j S )z*String representation of the user profile.zProfile for )userr:   r    r6   r1   r1   r2   r8     s    zProfile.__str__c                 C   s@   | j r<t  }|j| j j |j|jf| j j| j jfk  S dS )z,Calculate user"s age based on date of birth.N)
birth_dater   r=   dateyearmonthday)r7   todayr1   r1   r2   get_age  s    zProfile.get_agec                 C   s   | j r| j jS dS )z!Get avatar URL or default avatar.z!/static/images/default-avatar.png)avatarurlr6   r1   r1   r2   get_avatar_url  s    zProfile.get_avatar_urlN)!r(   r)   r*   rk   r   OneToOneFieldr   CASCADEr+   r   
ImageFieldr   	TextFieldZbio	DateFieldr   rm   Z	job_titleZcompanyURLFieldZwebsiter   languageZthemern   Znotifications_enabledZsms_notificationsZemail_notificationsZprofile_visibilityr3   r8   r   r   r1   r1   r1   r2   rx     s   	
	rx   c                   @   s  e Zd ZdZdddgZejddededd	Zejd
de	ddgddZ
ejdededdZejdedddZejdejdddddZejedededdZejdededdZejded d!dZejd"d#d$ZG d%d& d&Zd'd( Zd)d* Zd.d,d-Zd+S )/rL   z
    Role model for dynamic role management.
    
    This model allows creating custom roles with specific permissions
    that can be assigned to users for flexible access control.
    )systemzSystem Role)customzCustom Role)ZprojectzProject Roler   Tz	Role NamezGUnique name for the role (e.g., 'Campaign Manager', 'Analytics Viewer'))r   r   r   r   r}   z	^[a-z_]+$z,Code must be lowercase with underscores onlyz<Role code for programmatic access (e.g., 'campaign_manager'))r   r   r   r   DescriptionzCDetailed description of the role's responsibilities and permissionsr|   r   r   zType of role for categorizationr   r7   Zchild_roleszParent role for inheritancery   r   r   rz   r   ZPermissionsz*Specific permissions assigned to this rolez	Is Activez%Whether this role is currently activer   rH   Priorityz*Role hierarchy level (1=highest, 5=lowest)Fz,Whether this is a default role for new usersr   c                   @   s(   e Zd ZedZedZdZddgZdS )z	Role.MetarL   ZRolesZaccounts_rolesrS   nameN)r(   r)   r*   r+   r   r,   r-   r.   r1   r1   r1   r2   r3     s   r3   c                 C   s   | j  d| j dS )z"String representation of the role.z (Level r5   )r   rS   r6   r1   r1   r2   r8     s    zRole.__str__c                 C   s(   t | j }| jr$|| j  |S )z9Get all permissions including inherited from parent roles)setpermissionsallparent_roleupdateget_all_permissions)r7   r   r1   r1   r2   r     s    zRole.get_all_permissionsNc                 C   sH   |   }|D ]6}|r2|j|krB|jj|krB dS q|j|kr dS qdS )z%Check if role has specific permissionTF)r   codenamecontent_typerY   )r7   Zpermission_codenamerY   r   permr1   r1   r2   rX     s    
zRole.has_permission)N)r(   r)   r*   rk   Z
ROLE_TYPESr   rm   r+   r   r   rP   r   descriptionZ	role_type
ForeignKeyr   r   ManyToManyFieldr   r   rn   r%   rt   rS   Z
is_defaultr3   r8   r   rX   r1   r1   r1   r2   rL     sx   

rL   c                   @   s   e Zd ZdZejeejddZeje	ejddZ
ejeejdddddZejejd	d
Zejejdd
ZejddddZejddd
ZejdddZG dd dZdd Zdd ZdS )UserRolez
    Junction model for User-Role relationships with temporal aspects.
    Allows users to have multiple roles with validity periods.
    zUser assigned to this role)ry   r   zRole assigned to the userTZassigned_roleszUser who assigned this roler   zWhen this role was assignedr   z'When this role assignment becomes validz!When this role assignment expiresr   z0Whether this role assignment is currently activez+Additional notes about this role assignmentr   r   c                   @   s&   e Zd ZdZddgZdZdZdgZdS )zUserRole.MetaZaccounts_user_rolesr   rZ   z	User Rolez
User Rolesz-assigned_atN)r(   r)   r*   r-   unique_togetherr   r,   r.   r1   r1   r1   r2   r3   2  s
   r3   c                 C   s    | j  p| j j d| jj S )N - )r   r:   r    rZ   r   r6   r1   r1   r2   r8   9  s    zUserRole.__str__c                 C   s,   t  }| jo*| j|ko*| jdkp*| j|kS )z0Check if the role assignment is currently valid.N)r   r=   r%   
valid_fromvalid_until)r7   r=   r1   r1   r2   is_valid<  s    zUserRole.is_validN)r(   r)   r*   rk   r   r   r   r   r   rL   rZ   SET_NULLZassigned_byrq   r   r=   Zassigned_atr   r   rn   r%   r   Znotesr3   r8   r   r1   r1   r1   r2   r     sV   r   c                   @   s   e Zd ZdZejeejdddZej	ddddZ
ejddd	d
ZejdddZej	ddddZejdddZejejddZejddZG dd dZdd Zdd ZdS )UserSessionzB
    Track user sessions for security and analytics purposes.
    sessionsz!User associated with this sessionry   rz   r   (   TzDjango session key)r   r   r   zIP address of the sessionr   zBrowser user agent stringr      z"Geographic location (if available)r   z'Whether the session is currently activer   zLast activity timestampzWhen the session expiresr   c                   @   s   e Zd ZdZdZdZdgZdS )zUserSession.MetaZaccounts_user_sessionszUser SessionzUser Sessionsz-last_activityNr(   r)   r*   r-   r   r,   r.   r1   r1   r1   r2   r3   p  s   r3   c                 C   s   | j j d| jd d  dS )Nr      z...)r   r    session_keyr6   r1   r1   r2   r8   v  s    zUserSession.__str__c                 C   s   t  | jkS )z!Check if the session has expired.r   r=   
expires_atr6   r1   r1   r2   
is_expiredy  s    zUserSession.is_expiredN)r(   r)   r*   rk   r   r   r   r   r   rm   r   rp   r_   r   Z
user_agentlocationrn   r%   rq   r   r=   rs   r   r3   r8   r   r1   r1   r1   r2   r   F  sP   r   c                   @   s   e Zd ZdZejeejdddZej	e
jdddZejdd	Zejd
ddZejddddZG dd dZdd Zdd Zdd ZdS )PasswordResetTokenz7
    Secure password reset tokens with expiration.
    Zreset_tokenszUser requesting password resetr   TzUnique reset token)r   r   r   zWhen the token expiresr   FzWhether the token has been usedr   z)IP address from which reset was requestedr   c                   @   s   e Zd ZdZdZdZdgZdS )zPasswordResetToken.MetaZaccounts_password_reset_tokenszPassword Reset TokenzPassword Reset Tokensr   Nr   r1   r1   r1   r2   r3     s   r3   c                 C   s   d| j j S )NzReset token for )r   r    r6   r1   r1   r2   r8     s    zPasswordResetToken.__str__c                 C   s   t  | jkS )zCheck if the token has expired.r   r6   r1   r1   r2   r     s    zPasswordResetToken.is_expiredc                 C   s   | j  o|   S )z$Check if the token is valid for use.)is_usedr   r6   r1   r1   r2   r     s    zPasswordResetToken.is_validN)r(   r)   r*   rk   r   r   r   r   r   	UUIDFielduuiduuid4tokenrq   r   rn   r   rp   r_   r3   r8   r   r   r1   r1   r1   r2   r   ~  s8   r   )r   	django.dbr   django.utilsr   Zdjango.core.validatorsr   django.utils.translationr   r+   django.contrib.auth.modelsr   r   r   django.urlsr	   Zapps.common.modelsr
   Zapps.accounts.managersr   r   rx   rL   r   r   r   r1   r1   r1   r2   <module>   s     z nB8