U
    h                     @   s  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Zddl	Z	ddl
Z
ddlmZmZ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 ddlmZmZ 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* ddl+m,Z, ddl-m.Z. ddl/m0Z0m1Z1 ddl2m3Z3 ddl4mZ ddl5m6Z6 ddl7m8Z8 ddl9m:Z: ddl;m<Z= ddl>m?Z? ddl@mAZA ddlBmCZCmDZD ddlEmFZFmGZG ddlHZHe
IeJZKeLeMdddZNeLeeLef dddZOdeLeeL eLd d!d"ZPeeL eMd#d$d%ZQdeLeLeLd'd(d)ZRdeLeSeLeLd,d-d.ZTdeLeSeSeeL d1d2d3ZUdeeSeVef eLeLd5d6d7ZWdeLeeL ee d8d9d:ZXdeLeeeef d;d<d=ZYeeLd>d?d@ZZdeLeLeLdBdCdDZ[deLeLeLdFdGdHZ\eeL eLeLdIdJdKZ]eSeLdLdMdNZ^deSeLdPdQdRZ_deLeLeeLeLf dSdTdUZ`eLeLeLeMdVdWdXZadeLeLeLdYdZd[ZbdeLeLeLd\d]d^ZceLd_d`daZddeSeLdcdddeZeeLdfdgdhZfdeLeeeL eLeLeMdidjdkZgeeL eeeL eeL f dldmdnZhdeeLeLeSeeFdqdrdsZideDeSeSedvdwdxZjdee eeL eSedzd{d|ZkdeDeeL eeL eDd}d~dZleedddZmeedddZndeDeLeeL eLdddZodeLeeLeLf edddZpdd Zqdd Zrdd ZsdS )a  
Enterprise-Ready Utility Functions Module

This module provides a comprehensive collection of utility functions and helpers
that are commonly used across the application. It includes data processing,
validation, formatting, security, and performance optimization utilities.

Features:
- Data validation and sanitization utilities
- File processing and manipulation functions
- Security and encryption helpers
- Performance optimization utilities
- Date and time manipulation functions
- String processing and formatting utilities
- Email and communication helpers
- API and web service utilities
- Database optimization functions
- Caching and memoization decorators

Author: Focus Development Team
Version: 2.0.0
License: Proprietary
    N)datetime	timedeltatimezone)DecimalInvalidOperation)AnyDictListOptionalUnionTupleCallable)wraps	lru_cache)urlparseurljoin)Path)settings)cache)ValidationError)validate_emailURLValidator)default_storage)ContentFile)	send_mailEmailMultiAlternatives)render_to_string)r   )slugify)
strip_tags)	force_str)gettext)get_user_model)transaction)QQuerySet)JsonResponseHttpResponse)phonereturnc                 C   sR   t dd| }t|dk s&t|dkr*dS ddg}|D ]}t || r6 dS q6dS )	z
    Validate phone number format.
    
    Args:
        phone: Phone number string to validate
        
    Returns:
        Boolean indicating if phone number is valid
    z\D       Fz ^\+?1?[2-9]\d{2}[2-9]\d{2}\d{4}$z^\+?[1-9]\d{1,14}$T)resublenmatch)r'   Zdigits_onlypatternspattern r2   ,/var/www/html/Focus/src/apps/common/utils.pyvalidate_phone_numberE   s    r4   )passwordr(   c              	   C   s  ddg ddddddd}t | dkrDd|d d< |d	  d
7  < n|d td d|d< td| rd|d d< |d	  d
7  < n|d td d|d< td| rd|d d< |d	  d
7  < n|d td d|d< td| rd|d d< |d	  d
7  < n|d td d|d< td| rXd|d d< |d	  d
7  < n|d td d|d< ddddg}|D ]@}t||  r|d td |d	  d
8  <  qĐq|S )z
    Validate password strength and return detailed feedback.
    
    Args:
        password: Password string to validate
        
    Returns:
        Dictionary with validation results and feedback
    Tr   F)length	uppercase	lowercasedigitspecial)is_validscorefeedbackrequirements_met   r>   r6   r<      r=   z+Password must be at least 8 characters longr;   z[A-Z]r7   z3Password must contain at least one uppercase letterz[a-z]r8   z3Password must contain at least one lowercase letterz\dr9   z(Password must contain at least one digitz[!@#$%^&*(),.?":{}|<>]r:   z4Password must contain at least one special characterZ123456r5   ZqwertyZabc123z!Password contains common patterns)r.   append_r,   searchlower)r5   resultZcommon_patternsr1   r2   r2   r3   validate_password_strengthc   s\    
rF   )html_contentallowed_tagsr(   c                 C   sV   |dkr.dddddddd	d
ddddddddg}ddgddddgd}t j| ||ddS )z
    Sanitize HTML content to prevent XSS attacks.
    
    Args:
        html_content: HTML content to sanitize
        allowed_tags: List of allowed HTML tags
        
    Returns:
        Sanitized HTML content
    NpbrZstrongZemuZolulZliZh1h2Zh3Zh4Zh5Zh6
blockquoteaimghreftitlesrcaltwidthheight)rO   rP   T)tags
attributesstrip)bleachclean)rG   rH   Zallowed_attributesr2   r2   r3   sanitize_html   s8                  
r\   )allowed_typesr(   c                 C   s\   | sdS t | j\}}||kr$dS tj| jd  }ddddddd	d
}|||kS )z
    Validate file type based on MIME type.
    
    Args:
        file: File object to validate
        allowed_types: List of allowed MIME types
        
    Returns:
        Boolean indicating if file type is allowed
    FTr@   z
image/jpegz	image/pngz	image/gifzapplication/pdfzapplication/mswordzGapplication/vnd.openxmlformats-officedocument.wordprocessingml.document)z.jpgz.jpegz.pngz.gifz.pdfz.docz.docx)	mimetypes
guess_typenameospathsplitextrD   get)filer]   	mime_typerB   Zfile_extensionZextension_mappingr2   r2   r3   validate_file_type   s    
rg   slug)text
field_namer(   c                 C   sJ   t | }|sd}|}d}|jjf ||i rF| d| }|d7 }q|S )a  
    Generate a unique slug for a model instance.
    
    Args:
        text: Text to create slug from
        model_class: Model class to check uniqueness against
        field_name: Field name for the slug
        
    Returns:
        Unique slug string
    itemr@   -)r   objectsfilterexists)ri   model_classrj   Z	base_slugrh   counterr2   r2   r3   generate_unique_slug   s    
rr   d   ...)ri   
max_lengthsuffixr(   c                 C   s(   t | |kr| S | d|t |  | S )z
    Truncate text to specified length with suffix.
    
    Args:
        text: Text to truncate
        max_length: Maximum length of text
        suffix: Suffix to add when truncating
        
    Returns:
        Truncated text
    Nr.   )ri   ru   rv   r2   r2   r3   truncate_text  s    rx      
   )ri   
min_lengthmax_keywordsr(   c              2      s   t |  }tdt| 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dddd d!d"d#d$d%d&d'd(d)d*d+d,d-d.d/d0d1d2d3d4h2  fd5d6|D }d7d8lm} ||}d9d6 ||D S ):z
    Extract keywords from text.
    
    Args:
        text: Text to extract keywords from
        min_length: Minimum keyword length
        max_keywords: Maximum number of keywords to return
        
    Returns:
        List of extracted keywords
    z\b[a-zA-Z]{z,}ZtheandorZbutinonattoforofwithbyfromupZaboutZintothroughduringbeforeafterZaboveZbelowZbetweenZamongthisthatZtheseZthoseisarewaswerebeZbeenZbeinghaveZhasZhaddoZdoesZdidZwillZwouldZcouldZshouldmayZmightZmustZcanZshallc                    s   g | ]}| kr|qS r2   r2   ).0wordZ
stop_wordsr2   r3   
<listcomp>A  s      z$extract_keywords.<locals>.<listcomp>r   )Counterc                 S   s   g | ]\}}|qS r2   r2   )r   r   countr2   r2   r3   r   G  s     )r   rD   r,   findallstrcollectionsr   most_common)ri   r{   r|   Z
clean_textwordskeywordsr   Zword_countsr2   r   r3   extract_keywords%  sr                                                	r   USD)amountcurrencyr(   c              	   C   sn   zt t| } W n ttfk
r*   Y dS X ddddd}|||}|dkr\| | dS | | d	S d
S )z
    Format currency amount with proper symbols and formatting.
    
    Args:
        amount: Amount to format
        currency: Currency code
        
    Returns:
        Formatted currency string
    z0.00$u   €   £   ¥)r   ZEURZGBPJPYr   z,.0fz,.2fN)r   r   r   
ValueErrorrd   )r   r   Zcurrency_symbolssymbolr2   r2   r3   format_currencyJ  s    r   )date_stringformatsr(   c              	   C   sX   |dkrdddddddd	d
g	}|D ]0}zt | |W   S  tk
rP   Y q"Y q"X q"dS )z
    Parse date string with multiple format attempts.
    
    Args:
        date_string: Date string to parse
        formats: List of date formats to try
        
    Returns:
        Parsed datetime object or None
    Nz%Y-%m-%dz%Y-%m-%d %H:%M:%Sz%Y-%m-%d %H:%Mz%m/%d/%Yz%m/%d/%Y %H:%M:%Sz%d/%m/%Yz%d/%m/%Y %H:%M:%Sz%Y-%m-%dT%H:%M:%Sz%Y-%m-%dT%H:%M:%SZ)r   strptimer   )r   r   fmtr2   r2   r3   parse_date_stringm  s"    r   )period
start_dater(   c                 C   sR  |dkrt  jddddd}| dkr@|jddddd}||fS | dkrp|td	d
 }|jddddd}||fS | dkr| }|t|d
 }|tdddddd }||fS | dkr
|jd	d}|jdkr|j|jd	 d	d}n|j|jd	 d}|td	d }	||	fS | dkr@|jd	d	d}
|jddddddd}|
|fS td|  dS )z
    Get date range for common periods.
    
    Args:
        period: Period type ('today', 'yesterday', 'week', 'month', 'year')
        start_date: Starting date (defaults to today)
        
    Returns:
        Tuple of (start_date, end_date)
    Nr   )hourminutesecondmicrosecondtoday   ;   i?B 	yesterdayr@   )daysweek   )r   hoursminutessecondsmicrosecondsmonth)day   )yearr   )r   )r   r   )r   r      )r   r   r   r   r   r   zUnknown period: )r   nowreplacer   weekdayr   r   r   )r   r   end_dater   Zdays_since_monday
week_startZweek_endZmonth_start
next_monthZ	month_endZ
year_startZyear_endr2   r2   r3   get_date_range  s4    


r   )	date_timer(   c                 C   s  t  }||  }|jdkr@|jd }| d|dkr6dnd dS |jdkrp|jd }| d|dkrfdnd dS |jd	kr|j d
|jdkrdnd dS |jdkr|jd }| d|dkrdnd dS |jdk r|jd }| d|dkrdnd dS dS dS )z
    Format datetime as 'time ago' string.
    
    Args:
        date_time: Datetime to format
        
    Returns:
        Formatted time ago string
    im  z yearr@   sr)   z ago   z monthr   z dayi  z hour<   z minutezJust nowN)r   r   r   r   )r   r   diffyearsmonthsr   r   r2   r2   r3   format_time_ago  s"    





 


r   r)   )filename	subfolderr(   c                 C   s`   | dd  }t j d| }t d}|rL| d| d| S d| d| S dS )z
    Generate file upload path with organization.
    
    Args:
        instance: Model instance
        filename: Original filename
        subfolder: Optional subfolder
        
    Returns:
        Generated file path
    .z%Y/%m/%d/zuploads/N)splitrD   uuiduuid4hexr   r   strftime)instancer   r   extZunique_filenameZ	date_pathr2   r2   r3   generate_file_path  s    r   md5)	file_path	algorithmr(   c              	      sJ   t |}t| d( t fdddD ]}|| q(W 5 Q R X | S )z
    Calculate hash of a file.
    
    Args:
        file_path: Path to the file
        algorithm: Hash algorithm ('md5', 'sha1', 'sha256')
        
    Returns:
        File hash as hexadecimal string
    rbc                      s
     dS )Ni   )readr2   fr2   r3   <lambda>
      z%calculate_file_hash.<locals>.<lambda>r   )hashlibnewopeniterupdate	hexdigest)r   r   Zhash_objchunkr2   r   r3   calculate_file_hash  s
    
r   )
file_pathsoutput_pathr(   c              	   C   sN   t |dt j4}| D ](}tj|rtj|}||| qW 5 Q R X |S )z
    Compress multiple files into a ZIP archive.
    
    Args:
        file_paths: List of file paths to compress
        output_path: Path for the output ZIP file
        
    Returns:
        Path to the created ZIP file
    w)zipfileZipFileZIP_DEFLATEDra   rb   ro   basenamewrite)r   r   zipfr   arcnamer2   r2   r3   compress_files  s    r   )
size_bytesr(   c                 C   s\   | dkrdS dddddg}d}| dkrH|t |d	 k rH| d
 } |d	7 }q| dd||  S )z
    Convert file size in bytes to human-readable format.
    
    Args:
        size_bytes: File size in bytes
        
    Returns:
        Human-readable file size string
    r   z0 BBKBMBGBTB   r@   g      @z.1f rw   )r  Z
size_namesir2   r2   r3   get_file_size_human%  s    

r
      )r6   r(   c                 C   s   ddl }|| S )z
    Generate a cryptographically secure random token.
    
    Args:
        length: Length of the token
        
    Returns:
        Secure random token
    r   N)secretstoken_urlsafe)r6   r  r2   r2   r3   generate_secure_token@  s    
r  )r5   saltr(   c                 C   s4   |dkrt d}td|  | d}| |fS )z
    Hash password with salt using PBKDF2.
    
    Args:
        password: Password to hash
        salt: Optional salt (generated if not provided)
        
    Returns:
        Tuple of (hashed_password, salt)
    N   sha256順 )r  r   pbkdf2_hmacencoder   )r5   r  Zhashedr2   r2   r3   hash_passwordN  s    r  )r5   hashed_passwordr  r(   c                 C   s   t | |\}}||kS )z
    Verify password against hash.
    
    Args:
        password: Password to verify
        hashed_password: Stored hash
        salt: Salt used for hashing
        
    Returns:
        Boolean indicating if password is correct
    )r  )r5   r  r  Z	test_hashrB   r2   r2   r3   verify_passworda  s    r  )datakeyr(   c           	      C   s   ddl m} ddlm} ddlm} ddl}|dkr:tj}||	 dddd	}|
|| }||}||  }|
| S )
z
    Encrypt data using Fernet symmetric encryption.
    
    Args:
        data: Data to encrypt
        key: Encryption key (uses Django SECRET_KEY if not provided)
        
    Returns:
        Encrypted data as base64 string
    r   Fernethashes
PBKDF2HMACNr     salt_r  r   r6   r  
iterations)cryptography.fernetr  cryptography.hazmat.primitivesr  )cryptography.hazmat.primitives.kdf.pbkdf2r  base64r   
SECRET_KEYSHA256urlsafe_b64encodederiver  Zencryptdecode)	r  r  r  r  r  r&  kdfr   encrypted_datar2   r2   r3   encrypt_dataq  s     r.  )r-  r  r(   c           
      C   s   ddl m} ddlm} ddlm} ddl}|dkr:tj}||	 dddd	}|
|| }||}||  }||}	|	 S )
z
    Decrypt data using Fernet symmetric encryption.
    
    Args:
        encrypted_data: Encrypted data as base64 string
        key: Decryption key (uses Django SECRET_KEY if not provided)
        
    Returns:
        Decrypted data
    r   r  r  r  Nr  r   r  r!  )r#  r  r$  r  r%  r  r&  r   r'  r(  r)  r*  r  urlsafe_b64decodedecryptr+  )
r-  r  r  r  r  r&  r,  r   Zencrypted_bytesdecrypted_datar2   r2   r3   decrypt_data  s"    
r2  )r(   c                  O   sv   g }| D ]8}t |dr2||jj d|j  q|t| qt| D ]\}}|| d|  qNd|S )z
    Generate cache key from arguments.
    
    Args:
        *args: Positional arguments
        **kwargs: Keyword arguments
        
    Returns:
        Generated cache key
    pkrB   )	hasattrrA   	__class____name__r3  r   sorteditemsjoin)argskwargsZ	key_partsargr  valuer2   r2   r3   cache_key_generator  s    
r>  ,  )timeout
key_prefixc                    s   t t d fdd}|S )z
    Decorator to cache function results.
    
    Args:
        timeout: Cache timeout in seconds
        key_prefix: Prefix for cache key
        
    Returns:
        Decorated function
    funcr(   c                    s   t   fdd}|S )Nc                     sN    d j  dt| | }t|}|d k	r2|S  | |}t|| |S )NrB   )r6  r>  r   rd   set)r:  r;  	cache_keyrE   )rC  rA  r@  r2   r3   wrapper  s    

z3cached_function.<locals>.decorator.<locals>.wrapperr   rC  rF  rA  r@  rC  r3   	decorator  s    z"cached_function.<locals>.decorator)r   )r@  rA  rK  r2   rI  r3   cached_function  s    rL  )r1   c                 C   sZ   z6ddl m} tt|r4tj jtj |   W n tk
rT   t	
d Y nX dS )zo
    Invalidate cache keys matching a pattern.
    
    Args:
        pattern: Pattern to match cache keys
    r   )
RedisCachezBPattern cache invalidation not supported for current cache backendN)Z django.core.cache.backends.redisrM  
isinstancer   _cacheZ
get_clientdeletekeysImportErrorloggerwarning)r1   rM  r2   r2   r3   invalidate_cache_pattern  s    	
 rU  )template_namecontext	to_emailssubject
from_emailr(   c           	   
   C   s   z\|dkrt j}td|  d|}td|  d|}t||||d}||d |  W dS  tk
r } ztd|  W Y d	S d}~X Y nX dS )
a]  
    Send email using template.
    
    Args:
        template_name: Template name (without extension)
        context: Template context
        to_emails: List of recipient emails
        subject: Email subject
        from_email: Sender email (uses DEFAULT_FROM_EMAIL if not provided)
        
    Returns:
        Boolean indicating success
    Nzemails/z.htmlz.txt)rY  bodyrZ  r   z	text/htmlTzError sending email: F)	r   DEFAULT_FROM_EMAILr   r   attach_alternativesend	ExceptionrS  error)	rV  rW  rX  rY  rZ  rG   text_contentmsger2   r2   r3   send_template_email  s"    rd  )emailsr(   c              	   C   sP   g }g }| D ]:}zt | || W q tk
rD   || Y qX q||fS )z
    Validate list of email addresses.
    
    Args:
        emails: List of email addresses to validate
        
    Returns:
        Tuple of (valid_emails, invalid_emails)
    )r   rA   r   )re  Zvalid_emailsZinvalid_emailsemailr2   r2   r3   validate_email_list/  s    
rg  success   )r  messagestatusstatus_codeerrorsr(   c                 C   s.   ||| t   d}|r"||d< t||dS )a.  
    Create standardized API response.
    
    Args:
        data: Response data
        message: Response message
        status: Response status ('success', 'error', 'warning')
        status_code: HTTP status code
        errors: Error details
        
    Returns:
        JsonResponse object
    )rk  rj  r  	timestamprm  )rk  )r   r   	isoformatr%   )r  rj  rk  rl  rm  response_datar2   r2   r3   create_api_responseJ  s    
rq  r@      )querysetpageper_pager(   c              
   C   s   ddl m}m}m} || |}z||}W n< |k
rJ   |d}Y n  |k
rh   ||j}Y nX t||j|j|j||	 |
 |	 r| nd|
 r| ndddS )z
    Paginate queryset and return pagination info.
    
    Args:
        queryset: QuerySet to paginate
        page: Page number
        per_page: Items per page
        
    Returns:
        Dictionary with pagination info
    r   )	Paginator	EmptyPagePageNotAnIntegerr@   N)Zcurrent_pageZtotal_pagesZtotal_itemsru  has_nexthas_previous	next_pageZprevious_page)rm   
pagination)django.core.paginatorrv  rw  rx  rt  	num_pageslistnumberr   ry  rz  next_page_numberprevious_page_number)rs  rt  ru  rv  rw  rx  	paginatorpage_objr2   r2   r3   paginate_querysetf  s&    
r    )	data_listunique_fields
batch_sizer(   c                    s$  d}d}d}t   tdt||D ]}||||  }|D ]Ɖ z fdd|D }	z@| jjf |	}
  D ]\}}t|
|| qn|
  |d7 }W n, | j	k
r   | jj
f  }
|d7 }Y nX W q> tk
r } ztd|  |d7 }W 5 d}~X Y q>X q>q&W 5 Q R X |||t|dS )a8  
    Bulk update or create model instances.
    
    Args:
        model_class: Model class
        data_list: List of data dictionaries
        unique_fields: Fields to use for uniqueness check
        batch_size: Batch size for processing
        
    Returns:
        Dictionary with operation statistics
    r   c                    s   i | ]}| kr| | qS r2   r2   r   fieldr  r2   r3   
<dictcomp>  s       z)bulk_update_or_create.<locals>.<dictcomp>r@   z Error in bulk_update_or_create: N)createdupdatedrm  Ztotal_processed)r"   atomicranger.   rm   rd   r8  setattrsaveDoesNotExistcreater_  rS  r`  )rp   r  r  r  Zcreated_countZupdated_counterror_countr	  batchlookup_kwargsobjr  r=  rc  r2   r  r3   bulk_update_or_create  s4    
(r  )rs  select_relatedprefetch_relatedr(   c                 C   s    |r| j | } |r| j| } | S )a  
    Optimize queryset with select_related and prefetch_related.
    
    Args:
        queryset: QuerySet to optimize
        select_related: Fields for select_related
        prefetch_related: Fields for prefetch_related
        
    Returns:
        Optimized QuerySet
    )r  r  )rs  r  r  r2   r2   r3   optimize_queryset  s
    

r  rB  c                    s   t   fdd}|S )z
    Decorator to measure function execution time.
    
    Args:
        func: Function to measure
        
    Returns:
        Decorated function
    c                     sF   t  } | |}t  }||  }td j d|dd |S )N	Function z executed in z.4fz seconds)r   r   total_secondsrS  infor6  )r:  r;  
start_timerE   end_timeZexecution_timerJ  r2   r3   rF    s    
z'measure_execution_time.<locals>.wrapperrG  rH  r2   rJ  r3   measure_execution_time  s    
	r  c                    s   t   fdd}|S )z
    Decorator to monitor memory usage of a function.
    
    Args:
        func: Function to monitor
        
    Returns:
        Decorated function
    c            	         s   zvdd l }dd l}|| }| jd d } | |}| jd d }|| }td j d|dd |W S  t	k
r   t
d  | | Y S X d S )Nr   r  r  z memory usage: z.2fz MBz*psutil not available for memory monitoring)psutilra   ProcessgetpidZmemory_inforssrS  r  r6  rR  rT  )	r:  r;  r  ra   processZmemory_beforerE   Zmemory_afterZmemory_diffrJ  r2   r3   rF    s    

z%memory_usage_monitor.<locals>.wrapperrG  rH  r2   rJ  r3   memory_usage_monitor  s    
r  )rs  r   fieldsr(   c           
   	   C   s   |dkrdd | j jjD }tjt |}t|ddddb}t	
|}|| | D ]B}g }|D ]*}t||d}	|	dkrd}	|t|	 qd|| qXW 5 Q R X |S )z
    Export queryset to CSV file.
    
    Args:
        queryset: QuerySet to export
        filename: Output filename
        fields: Fields to include (all fields if None)
        
    Returns:
        Path to created CSV file
    Nc                 S   s   g | ]
}|j qS r2   )r`   r  r2   r2   r3   r   *  s     z*export_queryset_to_csv.<locals>.<listcomp>r   r)   utf-8)newlineencoding)model_metar  ra   rb   r9  tempfile
gettempdirr   csvwriterwriterowgetattrrA   r   )
rs  r   r  r   csvfiler  r  rowr  r=  r2   r2   r3   export_queryset_to_csv  s    

r  )csv_file_pathfield_mappingr(   c                 C   s  d}d}g }t |ddd}t|}t|ddD ]\}}	zbi }
|	 D ].\}}|r`|||n|}t| |rH||
|< qH| f |
}|  |  |d7 }W q2 t	k
r } z<|d7 }|
d| d	t|  td
| d	|  W 5 d}~X Y q2X q2W 5 Q R X |||dS )a  
    Import CSV data to model.
    
    Args:
        model_class: Model class to import to
        csv_file_path: Path to CSV file
        field_mapping: Mapping of CSV columns to model fields
        
    Returns:
        Dictionary with import statistics
    r   rr  )r     )startr@   zRow z: zError importing row N)success_countr  rm  )r   r  
DictReader	enumerater8  rd   r4  
full_cleanr  r_  rA   r   rS  r`  )rp   r  r  r  r  rm  r  readerZrow_numr  Z
model_dataZ	csv_fieldr=  model_fieldr  rc  r2   r2   r3   import_csv_to_modelA  s0    



4r  c                 C   s   | j ddkS )z
    Check if the request is an AJAX request.
    
    Args:
        request: Django request object
        
    Returns:
        bool: True if it's an AJAX request
    zX-Requested-WithXMLHttpRequest)headersrd   requestr2   r2   r3   is_ajax_requesto  s    
r  c                 C   s0   | j d}|r |dd }n| j d}|S )z
    Get the client's IP address from the request.
    
    Args:
        request: Django request object
        
    Returns:
        str: Client IP address
    HTTP_X_FORWARDED_FOR,r   REMOTE_ADDR)METArd   r   )r  x_forwarded_foripr2   r2   r3   get_client_ip|  s
    
r  c                 C   s   t | | jdddS )zGet client IP and user agent.HTTP_USER_AGENTr)   )
ip_address
user_agent)r  r  rd   r  r2   r2   r3   get_client_info  s    r  )N)rh   )rs   rt   )ry   rz   )r   )N)N)r)   )r   )r  )N)N)N)r?  r)   )N)Nr)   rh  ri  N)r@   rr  )r  )NN)N)N)t__doc__r,   ra   jsonr   r   r^   r  r   r  loggingr   r   r   Zdt_timezonedecimalr   r   typingr   r   r	   r
   r   r   r   	functoolsr   r   urllib.parser   r   pathlibr   django.confr   django.core.cacher   django.core.exceptionsr   Zdjango.core.validatorsr   r   django.core.files.storager   django.core.files.baser   django.core.mailr   r   django.template.loaderr   django.utilsdjango.utils.textr   django.utils.htmlr   django.utils.encodingr   django.utils.translationr    rB   django.contrib.authr!   	django.dbr"   django.db.modelsr#   r$   django.httpr%   r&   rZ   	getLoggerr6  rS  r   boolr4   rF   r\   rg   rr   intrx   r   floatr   r   r   r   r   r   r   r
  r  r  r  r.  r2  r>  rL  rU  rd  rg  rq  r  r  r  r  r  r  r  r  r  r  r2   r2   r2   r3   <module>   s   $
P'%#!0#!&   )$      +   4  
 (  & 
 .