U
    vhEa                     @   s   d 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 ddlmZ dd	lmZmZmZ dd
lmZmZ G dd deeeZG dd deeeZG dd deeZdS )aA  
Adtlas Activities Models

This module contains the activity tracking and audit logging models for the Adtlas DAI Management System.
It provides comprehensive activity monitoring, user action tracking, and audit trail functionality.

Features:
    - User activity tracking with detailed context
    - System event logging and monitoring
    - Activity categorization and filtering
    - Audit trail management with retention policies
    - Activity analytics and reporting
    - Real-time activity feeds

Author: Adtlas Development Team
Version: 1.0.0
Last Updated: 2025-01-27
    )models)reversetimezone)RegexValidator)gettext_lazyContentType)GenericForeignKey)	UUIDModelTimestampedModelSoftDeleteModel)ActivityManagerActivityCategoryManagerc                	   @   sL  e Zd ZdZejddededdZejddedd	ged
eddZ	ej
dededdZejddeddgededdZejddededdZejdededdZejdeded dZejd!ed"ed#dZejd$ejddd%ed&ed'd(ZG d)d* d*Ze Ze Zd+d, Zd-d. Zd/d0 Zd4d2d3Zd1S )5ActivityCategoryas  
    Activity Category model for organizing and classifying different types of activities.
    
    This model allows for better organization of activities by grouping them into
    logical categories such as authentication, content management, system administration, etc.
    
    Attributes:
        name (str): Human-readable name of the category
        code (str): Unique code for programmatic access
        description (str): Detailed description of the category
        color (str): Hex color code for UI representation
        icon (str): CSS icon class for UI representation
        is_system (bool): Whether this is a system-defined category
        is_active (bool): Whether this category is currently active
        retention_days (int): Number of days to retain activities in this category
        parent (ForeignKey): Parent category for hierarchical organization
    d   TzCategory Namez,Human-readable name of the activity category)
max_lengthuniqueverbose_name	help_text2   z	^[a-z_]+$z,Code must be lowercase with underscores onlyzCategory Codez=Unique code for programmatic access (e.g., 'auth', 'content'))r   r   
validatorsr   r   Descriptionz?Detailed description of what activities belong to this categoryblankr   r      z#007bffz^#[0-9A-Fa-f]{6}$zColor must be a valid hex codeZColorz6Hex color code for UI representation (e.g., '#007bff'))r   defaultr   r   r   zfas fa-activityZIconz:CSS icon class for UI representation (e.g., 'fas fa-user'))r   r   r   r   FzIs System Categoryz@Whether this is a system-defined category that cannot be deletedr   r   r   z	Is Activez)Whether this category is currently activeim  zRetention DayszBNumber of days to retain activities in this category (0 = forever)selfchildrenzParent Categoryz-Parent category for hierarchical organization	on_deletenullr   related_namer   r   c                   @   sN   e Zd ZdZdgZedZedZej	dgdej	dgdej	dgdgZ
d	S )
zActivityCategory.MetaZactivities_categoriesnamezActivity CategoryzActivity Categoriescodefields	is_active	is_systemN__name__
__module____qualname__db_tableordering_r   verbose_name_pluralr   Indexindexes r4   r4   1/var/www/html/Focus/src/apps/activities/models.pyMeta{   s   r6   c                 C   s   | j S )z/String representation of the activity category.)r$   r   r4   r4   r5   __str__   s    zActivityCategory.__str__c                 C   s   t dd| jidS )z'Get the absolute URL for this category.zactivities:category_detailpkkwargsr   r9   r7   r4   r4   r5   get_absolute_url   s    z!ActivityCategory.get_absolute_urlc                 C   s8   t | jjdd}| jjddD ]}||  q |S )z
        Get all child categories recursively.
        
        Returns:
            list: List of all child ActivityCategory instances
        T)r(   )listr   filterextendget_all_children)r   r   childr4   r4   r5   rA      s    z!ActivityCategory.get_all_childrenNc                 C   s:   | j  }|dk	r2t tj|d }|j|d}| S )a  
        Get the count of activities in this category.
        
        Args:
            days (int, optional): Number of days to look back. If None, count all activities.
        
        Returns:
            int: Number of activities in this category
        N)days)created_at__gte)
activitiesallr   now	timedeltar?   count)r   rC   querysetcutoff_dater4   r4   r5   get_activity_count   s
    

z#ActivityCategory.get_activity_count)N)r+   r,   r-   __doc__r   	CharFieldr0   r$   r   r%   	TextFielddescriptioncolorZiconBooleanFieldr)   r(   PositiveIntegerFieldZretention_days
ForeignKeyCASCADEparentr6   Managerobjectsr   activer8   r=   rA   rL   r4   r4   r4   r5   r   "   s~   
	

r   c                "   @   s  e Zd ZdZdedfdedfdedfded	fd
edfdedfdedfdedfdedfdedfdedfdedfdedfdedfdedfd ed!fd"ed#fd$ed%fd&ed'fd(ed)fd*ed+fd,ed-fd.ed/fd0ed1fd2ed3fd4ed5fd6ed7fd8ed9fd:ed;fd<ed=fd>ed?fd@edAfg ZdBdCdDdEdFdGdHgZejdIej	dJdJdKedLedMdNZ
ejeej	dJdJdKedOedPdNZejdQeedRedSdTZejedUedVdWZejeej	dJdJedXedYdZZejdJdJed[ed\d]Zed^d_ZejdJdJed`edad]ZejdJedbedcddZejdedJedfedgdhZejdidJedjedkdhZejdledJedmedndoZejdJedpedqdrZejdJedsedtddZej e!dJeduedvdwZ"ej#dJdJedxedyd]Z$e% Z&G dzd{ d{Z'e(d|d} Z)e(d~d Z*dd Z+dd Z,dd Z-dd Z.dd Z/dd Z0dddZ1e2dddZ3e2dddZ3e4dd Z5dS )Activitya  
    Activity model for tracking user actions and system events.
    
    This model provides comprehensive activity tracking with support for
    categorization, detailed context, and audit trail functionality.
    
    Attributes:
        user (ForeignKey): User who performed the activity
        category (ForeignKey): Category this activity belongs to
        action (str): Type of action performed
        description (str): Human-readable description of the activity
        content_type (ForeignKey): Content type of the related object
        object_id (int): ID of the related object
        content_object (GenericForeignKey): Generic relation to any model
        ip_address (GenericIPAddressField): IP address of the user
        user_agent (str): User agent string from the request
        session_key (str): Session key for tracking user sessions
        request_path (str): URL path of the request
        request_method (str): HTTP method of the request
        is_successful (bool): Whether the activity completed successfully
        error_message (str): Error message if the activity failed
        metadata (JSONField): Additional metadata about the activity
        duration_ms (int): Duration of the activity in milliseconds
    ZLOGINZLoginZLOGOUTZLogoutZPASSWORD_CHANGEzPassword ChangeZPASSWORD_RESETzPassword ResetZACCOUNT_LOCKEDzAccount LockedZACCOUNT_UNLOCKEDzAccount UnlockedCREATEZCreateREADZReadUPDATEZUpdateDELETEDeleteZRESTOREZRestoreZUPLOADZUploadZDOWNLOADZDownloadZEXPORTZExportZIMPORTImportZCAMPAIGN_STARTzCampaign StartZCAMPAIGN_STOPzCampaign StopZCAMPAIGN_PAUSEzCampaign PauseZCAMPAIGN_RESUMEzCampaign ResumeZSYSTEM_BACKUPzSystem BackupZSYSTEM_RESTOREzSystem RestoreZSYSTEM_MAINTENANCEzSystem MaintenanceZCONFIGURATION_CHANGEzConfiguration ChangeZPERMISSION_GRANTEDzPermission GrantedZPERMISSION_REVOKEDzPermission RevokedZROLE_ASSIGNEDzRole AssignedZROLE_REMOVEDzRole RemovedZSECURITY_VIOLATIONzSecurity ViolationZREPORT_GENERATEDzReport GeneratedZANALYTICS_VIEWEDzAnalytics ViewedZDATA_EXPORTEDzData ExportedZOTHEROther)GETrb   )POSTrc   )PUTrd   )PATCHre   )r^   r^   )HEADrf   )OPTIONSrg   accounts.UserTrE   Userz User who performed this activityr    Categoryz!Category this activity belongs tor   Actionz!Type of action that was performed)r   choicesr   r   r   z+Human-readable description of what happenedr   r   zContent Typez'Type of object this activity relates to)r!   r"   r   r   r   z	Object IDz)ID of the object this activity relates tor"   r   r   r   content_type	object_idz
IP Addressz0IP address from which the activity was performedz
User Agentz Browser/client user agent stringr   (   zSession Keyz&Session key for tracking user sessions)r   r   r   r   i  zRequest Pathz$URL path where the activity occurred
   zRequest Methodz HTTP method used for the request)r   rl   r   r   r   zIs Successfulz+Whether the activity completed successfullyr   zError Messagez$Error message if the activity failedMetadataz-Additional structured data about the activityr   r   r   r   zDuration (ms)z(Duration of the activity in millisecondsc                
   @   s   e Zd ZdZdgZedZedZej	ddgdej	ddgdej	ddgdej	d	d
gdej	dgdej	dgdej	dgdej	dgdgZ
dS )zActivity.MetaZactivities_activitiesz-created_atrZ   Z
Activitiesuserr&   categoryactionro   rp   
ip_addresssession_keyis_successfulNr*   r4   r4   r4   r5   r6     s   r6   c                 C   s   | j r| j d S dS )zCalculate duration in seconds.g     @@Nduration_msr7   r4   r4   r5   duration_seconds  s    
zActivity.duration_secondsc                 C   s*   ddl m} | |jdd }| j|kS )z3Check if activity is recent (within last 24 hours).r   r      )hours)django.utilsr   rG   rH   
created_at)r   r   cutoffr4   r4   r5   	is_recent  s    zActivity.is_recentc                 C   s2   | j r.| jr.z| j j| jdW S    Y dS X dS )zGet the related content object.)r9   N)ro   rp   get_object_for_this_typer7   r4   r4   r5   get_content_object  s    zActivity.get_content_objectc                 C   s6   | j rt| j nd}| jd}| d| j d| S )z&String representation of the activity.Z	Anonymousz%Y-%m-%d %H:%M - )ru   strr   strftimerw   )r   user_strZformatted_dater4   r4   r5   r8     s    zActivity.__str__c                 C   s   t dd| jidS )z'Get the absolute URL for this activity.zactivities:activity_detailr9   r:   r<   r7   r4   r4   r5   r=     s    zActivity.get_absolute_urlc                 C   s0   | j rt| j S | jr,| jj d| j dS dS )z
        Get a human-readable representation of the related object.
        
        Returns:
            str: String representation of the related object
        z (ID: )zNo related object)content_objectr   ro   r$   rp   r7   r4   r4   r5   get_object_display  s
    
zActivity.get_object_displayc                 C   sn   | j dkrdS | j dk r$| j  dS | j dk r@| j d ddS | j d }| j d d }| d|ddS dS )	z
        Get a human-readable representation of the activity duration.
        
        Returns:
            str: Formatted duration string
        NUnknowni  msi`  z.1fszm r{   )r   minutessecondsr4   r4   r5   get_duration_display  s    



zActivity.get_duration_displayc                 C   s.   t | jtsi | _|| j|< | jdgd dS )z
        Add a key-value pair to the activity metadata.
        
        Args:
            key (str): Metadata key
            value: Metadata value (must be JSON serializable)
        metadata)update_fieldsN)
isinstancer   dictsave)r   keyvaluer4   r4   r5   add_metadata  s    
zActivity.add_metadataNc                 C   s   t | jts|S | j||S )z
        Get a value from the activity metadata.
        
        Args:
            key (str): Metadata key
            default: Default value if key doesn't exist
        
        Returns:
            Metadata value or default
        )r   r   r   get)r   r   r   r4   r4   r5   get_metadata  s    zActivity.get_metadata c              
   C   sP   ddl m} ||||||||	|
d	}|rB|j||d< |j|d< | jjf |S )zLog an activity.r   r   )	ru   rw   rP   rv   rx   
user_agentry   rz   r|   ro   rp   )"django.contrib.contenttypes.modelsr	   rX   get_for_modelr9   create)clsru   rw   rP   rv   r   rx   r   ry   rz   r|   r	   activity_datar4   r4   r5   log_activity  s    
zActivity.log_activityc
                 K   s   t |tr8ztjj|d}W n tjk
r6   d}Y nX i }|rn|| ||jdd|j	j
|j|jd ||||||p~d|	pi d||
}|r||d< | jjf |S )a  
        Convenience method to log an activity.
        
        Args:
            user: User who performed the activity
            action (str): Action type
            description (str): Activity description
            category: ActivityCategory instance or code
            content_object: Related model instance
            request: Django request object
            is_successful (bool): Whether the activity was successful
            error_message (str): Error message if failed
            metadata (dict): Additional metadata
            **kwargs: Additional fields to set on the activity
        
        Returns:
            Activity: Created activity instance
        )r%   NHTTP_USER_AGENTr   )rx   r   ry   request_pathrequest_method)ru   rw   rP   rv   rz   error_messager   r   )r   r   r   rX   r   DoesNotExistupdate_get_client_ipMETAsessionry   pathmethodr   )r   ru   rw   rP   rv   r   requestrz   r   r   r;   Zrequest_datar   r4   r4   r5   r     s8    


c                 C   s0   | j d}|r |dd }n| j d}|S )z
        Get the client IP address from the request.
        
        Args:
            request: Django request object
        
        Returns:
            str: Client IP address
        ZHTTP_X_FORWARDED_FOR,r   REMOTE_ADDR)r   r   split)r   Zx_forwarded_foripr4   r4   r5   r   G  s
    zActivity._get_client_ip)N)r   NNNNNTN)	NNNNNNTNN)6r+   r,   r-   rM   r0   ZACTION_CHOICESZHTTP_METHOD_CHOICESr   rT   SET_NULLru   r   rv   rN   rw   rO   rP   r	   ro   rS   rp   r
   r   GenericIPAddressFieldrx   r   ry   r   r   rR   rz   r   	JSONFieldr   r   BigIntegerFieldr|   r   rX   r6   propertyr}   r   r   r8   r=   r   r   r   r   classmethodr   staticmethodr   r4   r4   r4   r5   rZ      sR  































4

		
	

	
                    :rZ   c                	   @   s^  e Zd ZdZejededdZejdej	dddeded	d
Z
ejeej	dddededd
ZejdededdZejdededdZejdededdZejdede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dededd ZG d!d" d"Zd#d$ Zed%d& Zed'd( Zd)S )*ActivitySummarya  
    Activity Summary model for storing aggregated activity statistics.
    
    This model stores pre-calculated activity statistics for improved
    performance when displaying activity analytics and reports.
    
    Attributes:
        date (DateField): Date for which the summary is calculated
        user (ForeignKey): User for user-specific summaries (optional)
        category (ForeignKey): Category for category-specific summaries (optional)
        total_activities (int): Total number of activities
        successful_activities (int): Number of successful activities
        failed_activities (int): Number of failed activities
        unique_users (int): Number of unique users (for global summaries)
        unique_ips (int): Number of unique IP addresses
        avg_duration_ms (float): Average activity duration in milliseconds
        peak_hour (int): Hour with the most activity (0-23)
        summary_data (JSONField): Additional summary statistics
    Datez)Date for which this summary is calculatedrm   rh   TZactivity_summariesri   z<User for user-specific summaries (null for global summaries)r    rj   zBCategory for category-specific summaries (null for all categories)r   zTotal Activitiesz.Total number of activities for this date/scoper   zSuccessful ActivitieszNumber of successful activitieszFailed ActivitieszNumber of failed activitieszUnique Usersz-Number of unique users (for global summaries)z
Unique IPszNumber of unique IP addresseszAverage Duration (ms)z)Average activity duration in millisecondsrn   z	Peak Hourz"Hour with the most activity (0-23)zSummary Dataz,Additional summary statistics and breakdownsrt   c                   @   s\   e Zd ZdZdgZedZedZdddgZe	j
dgde	j
ddgde	j
ddgdgZd	S )
zActivitySummary.MetaZactivities_summariesz-datezActivity SummaryzActivity Summariesdateru   rv   r&   N)r+   r,   r-   r.   r/   r0   r   r1   unique_togetherr   r2   r3   r4   r4   r4   r5   r6     s   
r6   c                 C   sL   t | jg}| jr$|d| j  | jr<|d| j  dd| S )z.String representation of the activity summary.zUser: z
Category: zActivity Summary - r   )r   r   ru   appendrv   join)r   Zscope_partsr4   r4   r5   r8     s    zActivitySummary.__str__c                 C   s   | j dkrdS | j| j  d S )z
        Calculate the success rate as a percentage.
        
        Returns:
            float: Success rate as a percentage (0-100)
        r   g        r   )total_activitiessuccessful_activitiesr7   r4   r4   r5   success_rate  s    
zActivitySummary.success_ratec                 C   s
   d| j  S )z
        Calculate the failure rate as a percentage.
        
        Returns:
            float: Failure rate as a percentage (0-100)
        g      Y@)r   r7   r4   r4   r5   failure_rate  s    zActivitySummary.failure_rateN)r+   r,   r-   rM   r   	DateFieldr0   r   rT   rU   ru   r   rv   rS   r   r   Zfailed_activitiesZunique_usersZ
unique_ips
FloatFieldZavg_duration_msPositiveSmallIntegerFieldZ	peak_hourr   r   Zsummary_datar6   r8   r   r   r   r4   r4   r4   r5   r   Z  s   

r   N)rM   	django.dbr   django.urlsr   r   r   Zdjango.core.validatorsr   django.utils.translationr   r0   r   r	   "django.contrib.contenttypes.fieldsr
   apps.common.modelsr   r   r   Zapps.activities.managersr   r   r   rZ   r   r4   r4   r4   r5   <module>   s        ,