o
    sbh]                     @   sl  d Z ddlZddlZddlZddlZddlZddlZ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 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" ddl#m$Z$ ddl%m&Z&m'Z'm(Z( e Z)dde*de+de*fddZ,de*fddZ-		dde*de*de*fddZ.dde*d e+d!e*de*fd"d#Z/d$e*de*fd%d&Z0dde*d(e+dee* fd)d*Z1dd+e
d,e*de*fd-d.Z2d/e*dee
 fd0d1Z3d2e*de4fd3d4Z5d+e
de*fd5d6Z6d7e*de*fd8d9Z7d7e*de*fd:d;Z8dd=e+de9fd>d?Z:d@ee* de9fdAdBZ;dd7e*dDe*de*fdEdFZ<ddGe*defdHdIZ=ddJede*fdKdLZ>ddJedNe9defdOdPZ?dQedefdRdSZ@ddJedUe*de*defdVdWZAdXe*de9fdYdZZBd[e*de9fd\d]ZCd^e*de9fd_d`ZDdae*dee*ef fdbdcZEdde!de*fdedfZFdde!de*fdgdhZGdde!de9fdidjZHdde!dke*de*fdldmZIdd^e*dnee* de*fdodpZJddre+dse+defdtduZK	ddve*dwe*dxe*dyedze*de9fd{d|ZLdde+de*fd~dZMdde*de*de*fddZN		ddJe*de*de+de+de*f
ddZOddde*de*fddZP	M		ddJede9de*de+dede"fddZQddJede*de"fddZRdde*dede+de"fddZSdS )z~Core Utilities

This module contains utility functions, helpers, and common operations
used across the entire Adtlas project.
    N)urlparse)datetime	timedelta)AnyDictListOptional)settings)timezone)slugify)	send_mail)
strip_tags)get_user_model)gettext)HttpRequestJsonResponse)render_to_string)	Paginator	EmptyPagePageNotAnInteger    prefixlengthreturnc                 C   s&   t |d| }| r|  | S |S )zGenerate a unique identifier with optional prefix.
    
    Args:
        prefix: Optional prefix for the ID
        length: Length of the random part
    
    Returns:
        Unique identifier string
    Nsecretstoken_urlsafe)r   r   Zrandom_part r   EC:\Users\vibe-look\OneDrive\Desktop\Adtlas_V\src\apps\common\utils.pygenerate_unique_id%   s   
r    c                   C   s   t t S )zDGenerate a UUID4 string.
    
    Returns:
        UUID4 string
    N)struuiduuid4r   r   r   r   generate_uuid3   s   r$   slugtext
field_namec                 C   sf   t | }|}d}	 |jjdi ||i}|r |jr |j|jd}| s'	 |S | d| }|d7 }q	)a)  Generate a unique slug for a model field.
    
    Args:
        text: Text to slugify
        model_class: Model class to check uniqueness against
        field_name: Field name to check uniqueness
        instance: Current instance (for updates)
    
    Returns:
        Unique slug string
       T)pk-Nr   )r   objectsfilterr)   excludeexists)r&   model_classr'   instanceZ	base_slugr%   counterquerysetr   r   r   slugify_unique<   s   
r3   d   ...
max_lengthsuffixc                 C   s(   t | |kr| S | d|t |  | S )zTruncate text to specified length with suffix.
    
    Args:
        text: Text to truncate
        max_length: Maximum length
        suffix: Suffix to add if truncated
    
    Returns:
        Truncated text
    Nlen)r&   r6   r7   r   r   r   truncate_text_   s   r:   html_contentc                 C   s   t |  S )zClean HTML content by removing tags.
    
    Args:
        html_content: HTML content to clean
    
    Returns:
        Clean text without HTML tags
    N)r   strip)r;   r   r   r   
clean_htmlp   s   	r=      
min_lengthc                    s:   t |  }td|}tt fdd|D }t|S )zExtract keywords from text.
    
    Args:
        text: Text to extract keywords from
        min_length: Minimum keyword length
    
    Returns:
        List of keywords
    z\b\w+\bc                 3   s     | ]}t | kr|V  qd S Nr8   ).0wordr?   r   r   	<genexpr>   s    z#extract_keywords.<locals>.<genexpr>N)r=   lowerrefindalllistsetsorted)r&   r?   Z
clean_textwordskeywordsr   rC   r   extract_keywords|   s   rM   dtformat_stringc                 C   s    | sdS |r|  |S |  dS )zFormat datetime with default or custom format.
    
    Args:
        dt: Datetime to format
        format_string: Custom format string
    
    Returns:
        Formatted datetime string
    r   %Y-%m-%d %H:%M:%SN)strftime)rN   rO   r   r   r   format_datetime   s
   


rR   date_stringc              	   C   s:   g d}|D ]}z	t | |W   S  ty   Y qw dS )zParse datetime string with multiple format attempts.
    
    Args:
        date_string: Date string to parse
    
    Returns:
        Parsed datetime or None
    )	rP   z%Y-%m-%d %H:%Mz%Y-%m-%dz%d/%m/%Y %H:%M:%Sz%d/%m/%Y %H:%Mz%d/%m/%Yz%m/%d/%Y %H:%M:%Sz%m/%d/%Y %H:%Mz%m/%d/%YN)r   strptime
ValueError)rS   formatsfmtr   r   r   parse_datetime   s   	rX   periodc           
      C   sF  t  }| }| dkr||fS | dkr|tdd }||fS | dkr0|t| d }||fS | dkrJ|t| d d }|tdd }||fS | d	krX|jdd
}||fS | dkrs|jdd
}|tdd }|jdd
}||fS | dkr|jddd}||fS | dkr|jd }	|j|	ddd}|j|	ddd}||fS ||fS )zGet date range for common periods.
    
    Args:
        period: Period name (today, yesterday, this_week, last_week, etc.)
    
    Returns:
        Tuple of (start_date, end_date)
    today	yesterdayr(   )daysZ	this_weekZ	last_week      Z
this_month)dayZ
last_month	this_year)monthr_   	last_year)yearra   r_         N)r
   nowdater   weekdayreplacerc   )
rY   rf   rZ   r[   startendZfirst_this_monthZlast_month_endZlast_month_startrb   r   r   r   get_date_range   s>   	
rl   c                 C   s$  | sdS t  }||  }|jdkr'|jd }| d|dkr"d dS d dS |jdkrB|jd }| d|dkr=d dS d dS |jd	krZ|j d
|jdkrUd dS d dS |jdkru|jd }| d|dkrpd dS d dS |jdkr|jd }| d|dkrd dS d dS dS )zGet human-readable time ago string.
    
    Args:
        dt: Datetime to compare
    
    Returns:
        Human-readable time ago string
    r   im  z yearr(   sz ago   z monthr   z dayi  z hour<   z minutezJust nowN)r
   rf   r\   seconds)rN   rf   diffyearsmonthshoursminutesr   r   r   time_ago   s&   	

"

"
&

"

"rv   filenamec                 C   s   t j| d dd  S )zGet file extension from filename.
    
    Args:
        filename: Name of the file
    
    Returns:
        File extension (without dot)
    r(   N)ospathsplitextrE   )rw   r   r   r   get_file_extension  s   	r{   c                 C   s   t | \}}|p
dS )z{Get MIME type for a file.
    
    Args:
        filename: Name of the file
    
    Returns:
        MIME type string
    zapplication/octet-streamN)	mimetypes
guess_type)rw   Z	mime_type_r   r   r   get_mime_type%  s   	r   
   max_size_mbc                 C   s   |d d }| j |kS )zValidate file size.
    
    Args:
        file: File object
        max_size_mb: Maximum size in MB
    
    Returns:
        True if valid, False otherwise
    i   N)size)filer   Zmax_size_bytesr   r   r   validate_file_size2  s   

r   allowed_typesc                 C   s   t | j}||v S )zValidate file type.
    
    Args:
        file: File object
        allowed_types: List of allowed MIME types
    
    Returns:
        True if valid, False otherwise
    N)r   name)r   r   Z	file_typer   r   r   validate_file_type@  s   

r   uploadsfolderc                 C   s:   t |}t  d| }t d}| d| d| S )zGenerate upload path for files.
    
    Args:
        instance: Model instance
        filename: Original filename
        folder: Upload folder
    
    Returns:
        Upload path
    .z%Y/%m/%d/N)r{   r$   r
   rf   rQ   )r0   rw   r   extZunique_filenameZ	date_pathr   r   r   generate_upload_pathN  s   r   json_stringc              	   C   s*   zt | W S  t jtfy   | Y S w )zSafely load JSON string.
    
    Args:
        json_string: JSON string to parse
        default: Default value if parsing fails
    
    Returns:
        Parsed JSON data or default value
    N)jsonloadsJSONDecodeError	TypeError)r   defaultr   r   r   safe_json_loadsi  s
   
r   datac              	   C   s0   zt j| tdW S  ttfy   |pd Y S w )zSafely dump data to JSON string.
    
    Args:
        data: Data to serialize
        default: Default value if serialization fails
    
    Returns:
        JSON string or default value
    )r   z{}N)r   dumpsr!   r   rU   )r   r   r   r   r   safe_json_dumpsy  s
   
r   Tremove_emptyc                 C   sF   i }|   D ]\}}|du rq|r|s|dkr|durq|||< q|S )zClean dictionary by removing None values and optionally empty values.
    
    Args:
        data: Dictionary to clean
        remove_empty: Whether to remove empty values
    
    Returns:
        Cleaned dictionary
    Nr   F)items)r   r   cleanedkeyvaluer   r   r   
clean_dict  s   

r   dictsc                  G   s&   i }| D ]}t |tr|| q|S )zMerge multiple dictionaries.
    
    Args:
        *dicts: Dictionaries to merge
    
    Returns:
        Merged dictionary
    N)
isinstancedictupdate)r   resultdr   r   r   merge_dicts  s   	

r   r   	separatorc                 C   sX   i }|   D ]#\}}|r| | | n|}t|tr%|t||| q|||< q|S )zFlatten nested dictionary.
    
    Args:
        data: Dictionary to flatten
        separator: Key separator
        prefix: Key prefix
    
    Returns:
        Flattened dictionary
    N)r   r   r   r   flatten_dict)r   r   r   r   r   r   Znew_keyr   r   r   r     s   

r   emailc                 C   s   d}t t|| S )zValidate email address format.
    
    Args:
        email: Email address to validate
    
    Returns:
        True if valid, False otherwise
    z0^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$N)boolrF   match)r   patternr   r   r   validate_email  s   	r   phonec                 C   s*   t dd| }dt|  kodkS   S )zValidate phone number format.
    
    Args:
        phone: Phone number to validate
    
    Returns:
        True if valid, False otherwise
    z\Dr   r]      N)rF   subr9   )r   Zdigits_onlyr   r   r   validate_phone  s   
r   urlc                 C   s0   zt | }t|j|jgW S  ty   Y dS w )z~Validate URL format.
    
    Args:
        url: URL to validate
    
    Returns:
        True if valid, False otherwise
    FN)r   allschemenetloc	Exception)r   r   r   r   r   validate_url  s   	r   passwordc                 C   s~  dg ddd}t | dk r|d d d|d	< n|d
  d7  < td| s3|d d d|d	< n|d
  d7  < td| sM|d d d|d	< n|d
  d7  < td| sg|d d d|d	< n|d
  d7  < td| s|d d d|d	< n|d
  d7  < |d
 dkrd|d< |S |d
 dkrd|d< |S |d
 dkrd|d< |S |d
 dkrd|d< |S d|d< |S )zValidate password strength.
    
    Args:
        password: Password to validate
    
    Returns:
        Dictionary with validation results
    Tr   weak)is_validerrorsscorestrengthr   r   z+Password must be at least 8 characters longFr   r   r(   z[a-z]z3Password must contain at least one lowercase letterz[A-Z]z3Password must contain at least one uppercase letterz\dz)Password must contain at least one numberz&[!@#$%^&*()_+\-=\[\]{};\':"\\|,.<>\/?]z4Password must contain at least one special character   Zvery_strongr      Zstrongr>   medium   Z	very_weakN)r9   appendrF   search)r   r   r   r   r   validate_password_strength  sN   






r   requestc                 C   s2   | j d}|r|dd }|S | j d}|S )zGet client IP address from request.
    
    Args:
        request: HTTP request object
    
    Returns:
        Client IP address
    HTTP_X_FORWARDED_FOR,r   REMOTE_ADDRN)METAgetsplit)r   Zx_forwarded_foripr   r   r   get_client_ip?  s   	r   c                 C   s   | j ddS )zGet user agent from request.
    
    Args:
        request: HTTP request object
    
    Returns:
        User agent string
    HTTP_USER_AGENTr   Nr   r   r   r   r   r   get_user_agentP  s   	r   c                 C   s   | j ddkS )zCheck if request is AJAX.
    
    Args:
        request: HTTP request object
    
    Returns:
        True if AJAX request, False otherwise
    ZHTTP_X_REQUESTED_WITHZXMLHttpRequestNr   r   r   r   r   is_ajax_request\  s   	r   ry   c                 C   s
   |  |S )zBuild absolute URI from request and path.
    
    Args:
        request: HTTP request object
        path: Relative path
    
    Returns:
        Absolute URI
    N)build_absolute_uri)r   ry   r   r   r   r   h  s   

r   allowed_hostsc                 C   s<   | sdS t | }|js| S |pttdg }|j|v r| S dS )zValidate and return safe redirect URL.
    
    Args:
        url: URL to validate
        allowed_hosts: List of allowed hosts
    
    Returns:
        Safe URL or default URL
    r   ALLOWED_HOSTSN)r   r   getattrr	   )r   r   parsedr   r   r   safe_redirect_urlu  s   

r      pageper_pagec                 C   s   t | |}z||}W n ty   |d}Y n ty(   ||j}Y nw |j||| | | r;| nd| rD|	 nd|j
|j|j| | dS )zPaginate queryset and return pagination data.
    
    Args:
        queryset: Django queryset
        page: Page number
        per_page: Items per page
    
    Returns:
        Dictionary with pagination data
    r(   N)r+   page_obj	paginatorhas_previoushas_nextprevious_page_numbernext_page_numbercurrent_pagetotal_pagestotal_countstart_index	end_index)r   r   r   r   	num_pagesobject_listr   r   r   r   numbercountr   r   )r2   r   r   r   r   r   r   r   paginate_queryset  s*   
r   to_emailsubjecttemplate_namecontext
from_emailc              
   C   sv   z |pi }|p	t j}t||}t|}t|||| g|dd W dS  ty: } ztd|  W Y d}~dS d}~ww )a)  Send notification email using template.
    
    Args:
        to_email: Recipient email
        subject: Email subject
        template_name: Template name
        context: Template context
        from_email: Sender email
    
    Returns:
        True if sent successfully, False otherwise
    F)r   messager   recipient_listhtml_messagefail_silentlyTzEmail sending failed: N)r	   DEFAULT_FROM_EMAILr   r   r   r   print)r   r   r   r   r   r;   Ztext_contenter   r   r   send_notification_email  s&   

	r       c                 C   s
   t | S )z|Generate secure random token.
    
    Args:
        length: Token length
    
    Returns:
        Secure random token
    Nr   )r   r   r   r   generate_token  s   
	r   sha256	algorithmc                 C   s"   t |}|| d | S )zHash string using specified algorithm.
    
    Args:
        text: Text to hash
        algorithm: Hash algorithm
    
    Returns:
        Hashed string
    zutf-8N)hashlibnewr   encode	hexdigest)r&   r   Zhash_objr   r   r   hash_string  s   

r   *r   	mask_charvisible_startvisible_endc                 C   sf   t | || kr|t |  S | d| }|dkr| | d nd}|t | | |  }| | | S )a5  Mask sensitive data showing only start and end characters.
    
    Args:
        data: Data to mask
        mask_char: Character to use for masking
        visible_start: Number of visible characters at start
        visible_end: Number of visible characters at end
    
    Returns:
        Masked data
    Nr   r   r8   )r   r  r  r  rj   rk   middler   r   r   mask_sensitive_data  s   r  adtlas)r   c                 G   s"   dd |D }d | g| }|S )zGenerate cache key from arguments.
    
    Args:
        *args: Arguments to include in key
        prefix: Key prefix
    
    Returns:
        Cache key
    c                 S   s   g | ]}t |qS r   )r!   )rA   argr   r   r   
<listcomp>+  s    z&generate_cache_key.<locals>.<listcomp>:N)join)r   argsZ	key_partsr   r   r   r   generate_cache_key!  s   
r     successr   statusr   c                 C   s$   ||| d}|r||d< t ||dS )a  Create standardized JSON response.
    
    Args:
        data: Response data
        success: Success status
        message: Response message
        status: HTTP status code
        errors: Error details
    
    Returns:
        JsonResponse object
    )r  r   r   r   )r  N)r   )r   r  r   r  r   Zresponse_datar   r   r   json_response4  s   r  Successc                 C   s   t | d|ddS )zCreate success JSON response.
    
    Args:
        data: Response data
        message: Success message
    
    Returns:
        JsonResponse object
    Tr  )r   r  r   r  Nr  )r   r   r   r   r   success_responseO  s   
r  Error  c                 C   s   t d| ||dS )zCreate error JSON response.
    
    Args:
        message: Error message
        errors: Error details
        status: HTTP status code
    
    Returns:
        JsonResponse object
    F)r  r   r   r  Nr  )r   r   r  r   r   r   error_response\  s   r  )r   r   )r%   N)r4   r5   )r>   r@   )r   )r   )T)r   r   )r   )NN)r   )r   )r   r   r   )NTr   r  N)Nr  )r  Nr  )T__doc__rx   rF   r   r"   r   r   r|   urllib.parser   r   r   typingr   r   r   r   django.confr	   django.utilsr
   django.utils.textr   django.core.mailr   django.utils.htmlr   django.contrib.authr   django.utils.translationr   r~   django.httpr   r   django.template.loaderr   django.core.paginatorr   r   r   Userr!   intr    r$   r3   r:   r=   rM   rR   rX   tuplerl   rv   r{   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r  r   r   r   r   <module>   s    	
#-%C")
,

 