o
    j֢                     @  s  d Z ddlm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 ddlmZmZ ddlmZ ddl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m Z m!Z! ddl"m#Z#m$Z$ dVddZ%ddddWddZ&dXd"d#Z'dYd%d&Z(dZd(d)Z)d[d+d,Z*d\d/d0Z+d]d3d4Z,d^d7d8Z-d_d:d;Z.d`d?d@Z/d`dAdBZ0dadGdHZ1dbdIdJZ2edKgeegdcdMdNZ3edOdKgeegdPdQ Z4edOgeegdRdS Z5edOgeegdTdU Z6dS )day  
Campaign endpoints expected by the React app (/api/campaigns/...).

These must run on the existing Django API host (localhost:8000) so the frontend
doesn't need a base URL change.

Data sources (matches backend/apps/cluster/dynamic_tables.py):
- Cluster DB: `{business_id}_campaigns`, `{business_id}_campaign_contacts`, `{business_id}_call_history`
- Master DB: `did_numbers`
    )annotationsN)Path)load_dotenv)datetime	timedelta)ZoneInfo)Any)close_old_connectionsconnections)api_viewpermission_classes)IsAuthenticated)Response)_q_ident_table_namesensure_business_cluster_tables)_legacy_profile_for_usercluster_table_existsreturn
int | Nonec                 C  sJ   t | j}|r|dnd }z|d urt|W S d W S  ty$   Y d S w )Nbusiness_id)r   usergetint	Exception)requestlegacybid r   =/var/www/html/livekitdocker/backend/config/campaigns_views.py_resolve_business_id$   s   
r       i'  min_vmax_vqr   keystrdefaultr   r#   r$   c                C  s<   z
t | ||}W n ty   |}Y nw t|t||S N)r   r   r   maxmin)r%   r&   r(   r#   r$   vr   r   r   
_parse_int-   s   r-   r,   boolc                 C  s&   t | tr| S t|   }|dv S )N)1trueonyes)
isinstancer.   r'   striplower)r,   sr   r   r   _php_truthy5   s   
r7   r   c                   C  s   t tdS )NAsia/Kolkata)r   nowr   r   r   r   r   _now_ist<   s   r:   dtc                 C  s4   | j d u r| jtdd} n| td} | dS )Nr8   tzinfo%Y-%m-%d %H:%M:%S)r=   replacer   
astimezonestrftime)r;   r   r   r   _fmt_dt@   s   

rB   rawc              	   C  s   | du st |  dkrt S t |  }dD ] }zt|dd|}|jtddW   S  ty7   Y qw zt|dd}|j	du rO|jtdd}|
tdW S  tyb   t  Y S w )zU
    Accepts ISO strings or 'Y-m-d H:i:s'. Best-effort; falls back to now (IST).
    N )r>   z%Y-%m-%dT%H:%M:%Sz%Y-%m-%dT%H:%M:%S.%fZr8   r<   z+00:00)r'   r4   r:   r   strptimer?   r   r   fromisoformatr=   r@   )rC   r6   fmtr;   r   r   r   _parse_dt_anyH   s$   

rI   r   	list[int]c              	   C  s  t d  }|d| g | }|r|d nd}W d   n1 s$w   Y  |dv r/g S t|trGzt|}W n tyF   g  Y S w t|t	sNg S g }|
 D ]*\}}zt|ttfskt|ddd rt|tt| W qT ty~   Y qTw |S )	z]
    businesses.retry_attempt is JSON (object). Extract numeric values preserving order.
    r(   zCSELECT retry_attempt FROM businesses WHERE business_id = %s LIMIT 1r   NNrD   .rD   r!   )r
   cursorexecutefetchoner3   r'   jsonloadsr   dictitemsr   floatr?   isdigitappend)r   currowrC   out_kr,   r   r   r   _fetch_retry_attempt_values`   s2   

$r[   retry_logiccustom_attempt_valuesc              	   C  s   |dkrt | }|rddd |D S dS |dkrWt|tr!|s#dS g }|D ]%}z|d ur2t|nd}W n ty@   d}Y nw |tt|d  q'dd	d |D S |d
kr]dS dS )Nbusiness_logic,c                 s      | ]}t |V  qd S r)   r'   ).0r,   r   r   r   	<genexpr>~       z/_compute_waiting_time_values.<locals>.<genexpr>rD   custom_logicg        <   c                 s  s     | ]}|d krt |V  qdS r   Nra   )rb   mr   r   r   rc      s    	not_retry)	r[   joinr3   listrT   r   rV   r   round)r   r\   r]   valsminutesr,   nr   r   r   _compute_waiting_time_values{   s$   rp   did_nobot_idc           	      C  sJ  |sdS t d  }|d | du r|W  d   S |d | du}|d | du}d}|r=|d7 }|d	7 }| |g}|d
vrT|d7 }|t| |rZ|d7 }|d7 }||| | }|ss|W  d   S |rt|dkr|d dvrt|d W  d   S t|d W  d   S 1 sw   Y  dS )z
    Mirrors Laravel fetch of exe_number/did_no from did_numbers (direction='outbound' when exists).
    Falls back to provided did_no.
    rD   r(   SHOW TABLES LIKE 'did_numbers'N.SHOW COLUMNS FROM did_numbers LIKE 'direction'z/SHOW COLUMNS FROM did_numbers LIKE 'exe_number'zSELECT did_noz, exe_numberz8 FROM did_numbers WHERE business_id = %s AND did_no = %s)NrD   r   z AND bot_id = %s AND direction = 'outbound'z LIMIT 1r!   rK   r   )r
   rM   rN   rO   rV   r   lenr'   )	r   rq   rr   rW   has_directionhas_exe_numbersqlparamsrX   r   r   r   _fetch_executive_number   s@   




$r{   tablec                 C  sd   z't d  }|d| g | d uW  d    W S 1 s w   Y  W d S  ty1   Y dS w )Nr(   zSHOW TABLES LIKE %sF)r
   rM   rN   rO   r   )r|   rW   r   r   r   _master_table_exists   s   
(r}   contentbytes-tuple[list[dict[str, str]], list[str]] | Nonec                   s  z| j ddd}dd |dD }t|dk rW dS g }d	}d	}g }g d
}t|D ]\}}	|	 }	|	s6q+g }
|D ](}|dkrUddl}ddl}t|j|	|	|d}
n|	|}
t|
dkrb nq:dd |
D }
|dkr|
}t|D ]$\}}|
   t fdddD r|}t fdddD r|}qtq+|dkr|dkr|t|
k r|
| nd}|t|
k r|
| nd}|s|r|||d q+|d	ks|d	ks|sW dS ||fW S  ty   Y dS w )z
    Laravel-like ultra-robust parsing:
    - tries delimiters: ',', '\t', ';', '|'
    - detects name + contact columns by header variations
    utf-8r?   )errorsc                 S     g | ]
}|  d kr|qS rD   r4   )rb   lnr   r   r   
<listcomp>       z2_parse_contacts_from_csv_bytes.<locals>.<listcomp>
   N)r_   	;|r_   r   )	delimiterc                 S  s   g | ]	}t |d qS )z 	
 "'r'   r4   rb   xr   r   r   r          c                 3      | ]}| v V  qd S r)   r   rb   kro   r   r   rc      rd   z1_parse_contacts_from_csv_bytes.<locals>.<genexpr>namefullname	full_namecustomer_namec                 3  r   r)   r   r   r   r   r   rc      
    
contactphonemobilenumberphone_numbermobile_numberrD   r   r   )decodesplitrv   	enumerater4   csvionextreaderStringIOr5   anyrV   r   )r~   textlinesheadersname_idxcontact_idxcontacts
delimitersilinerX   dr   r   jhr   r   r   r   r   _parse_contacts_from_csv_bytes   sb   

r   c                   s  zdd l }dd l}|j|| ddd}||jd  }|jdd}t|d }|s+W d S dd |D }d}d}	t|D ]$\}
}| 	  t
 fdd	d
D rQ|
}t
 fdd	dD r^|
}	q:|dksg|	dkrjW d S g }|D ]@}t|ptg }|t|k r|| d urt|| 	 nd}|	t|k r||	 d urt||	 	 nd}|s|r|||d qn|  |sW d S ||fW S  ty   Y d S w )Nr   T)	read_only	data_only)values_onlyc                 S  s$   g | ]}|d urt | ndqS rK   r   rb   cr   r   r   r        $ z3_parse_contacts_from_xlsx_bytes.<locals>.<listcomp>r   c                 3  r   r)   r   r   r   r   r   rc     rd   z2_parse_contacts_from_xlsx_bytes.<locals>.<genexpr>r   c                 3  r   r)   r   r   r   r   r   rc     r   r   rD   r   )r   openpyxlload_workbookBytesIO
sheetnames	iter_rowsr   r   r5   r4   r   rk   rv   r'   rV   closer   )r~   r   r   wbws	rows_iter
header_rowr   r   r   r   r   r   rrX   r   r   r   r   r   _parse_contacts_from_xlsx_bytes  sR   
,
r   r   campaign_rowdict[str, Any]tuple[int, bool]c                 C  s   t |dpddk}t|dpd}|sdS |dkrdS t|dp%d }|rCd	d
 |dD }|r?t|d dfS ddfS |dkrYt| }|rUt|d dfS ddfS dS )a  
    Port of Laravel storeCampaignContacts logic (simplified to match our campaign creation):
    - run_now=0 => set retry now based on waiting_time, unless retry_logic=not_retry
    - run_now=1 => retry=0 (scheduled will be set at start)
    Returns (retry_value, is_run_now).
    run_nowr   r\   rD   )r   Fri   )r!   Twaiting_timec                 S  s2   g | ]}|  r|  d dd r|  qS )rL   rD   r!   )r4   r?   rU   )rb   pr   r   r   r   Q  s   2 z3_retry_value_for_contact_insert.<locals>.<listcomp>r_   r!   Tr^   )r   r   r'   r4   r   rv   r[   )r   r   
is_run_nowr\   waitingpartsrm   r   r   r   _retry_value_for_contact_insert?  s   r   c                 C  s,  |du rdS zt |W S  ty   Y nw t| }|r#| dkr%dS ztt |  W n	 ty6   Y nw t |  d}zMtd  <}|dt	| d|||g |
 }|ro|d durxt |d W  d   W S W d   W dS W d   W dS 1 sw   Y  W dS  ty   Y dS w )zVMap campaign ``bot_id`` column (numeric or bot display name) to ``{bid}_bots.bot_id``.Nr(   _botsclusterzSELECT bot_id FROM z@ WHERE bot_name = %s OR agent_name = %s OR agent_id = %s LIMIT 1r   )r   r   r'   r4   r5   r   r
   rM   rN   r   rO   )r   rC   labelr|   rW   rX   r   r   r   _resolve_cluster_bot_pk_django\  sJ   




r   POSTcampaign_idc                   sl  t | }|stddiddS t| | d}| d t|s(tddid	dS t s4tdd
id	dS | jd}|sHtdddgidddS |jr^t|jdkr^tdddgidddS t|ddped	 }|
 }d}|drxt|}nt|}|stdddddS |\}}	td  }
|
dt| dt|g |
 }|stddid	dW  d   S dd |
jD }tt||}t|dpd}t||\}}tt }g }|D ]9}|dpd }|d pd }|
d!t  d"|t|||||||g ||
j|d#|t||d$ qt|}|| }|
d%t| d&|t|g W d   n	1 s;w   Y  dd'dB fd3d4}zLd5d |D }|d6}|d7v ra|d8}t|pfd }| rrd9}n|pvd9}|rtj |t|t||||d:d;d<| d=| d>!  W n
 t"y   Y nw td?t| d@|||||t|d;dAS )Ca  
    Endpoint expected by the React app:
    POST /api/campaigns/{id}/contacts
    multipart/form-data with field `file`.

    Stores contacts into cluster table `{business_id}_campaign_contacts` and increments
    `{business_id}_campaigns.total_count`.
    detail(No business profile found for this user.  status
_campaigns_campaign_contactsmessagezCampaign not foundi  z&Campaign contacts table does not existfileValidation failedrequiredr   r     i   zmax 10MBr   rD   Nz.xlsxzNo valid contact data foundF)r   successr   SELECT * FROM  WHERE campaign_id = %s LIMIT 1c                 S     g | ]}|d  qS r   r   r   r   r   r   r         z,campaign_contacts_upload.<locals>.<listcomp>total_countr   r   z
                INSERT INTO z
                (business_id, campaign_id, name, number, source, status, `retry`, created_at, updated_at)
                VALUES (%s, %s, %s, %s, %s, 'pending', %s, %s, %s)
                pending)idr   r   r   r   r   UPDATE z, SET total_count = %s WHERE campaign_id = %s)campaign_bot_idr   r   r   contact_idsrJ   
agent_namer'   r   r   r   Nonec           "        s  t   ttt jjd dd tt| |}t	dp#t	dp#d
 d}t	dp/d	
 d}t	d
p;d	
 d}t	dpGd
 }	|	rU|	dsUd|	 }	t	dp[d
 }
|
ri|
dsid|
 }
|rq| |
 nd	}d}d}td  *}|dt  d | d u}|dt  d | d u}W d    n1 sw   Y  t|D ]\}}ztd  }|dt  dt|t|t| g | }|s	 W d    W qt|d pd	
 }|s&|r
|dt  ddtt t|g n|dt  dtt t|g 	 W d    W qd| d| }|r_|rJ|dt  d|dtt t|g n@|dt  d|tt t|g n+|rw|dt  d dtt t|g n|dt  d!tt t|g W d    n	1 sw   Y  |r| |	 d| nd	}||| d"}|}|d ur| d| }n	|d#vr||d$< |rt|
  d%vrt|
  s||d&< |r||d'< |r||d(< t|d)}tjj||d*d+id,d-}z]tjj|d.d/J}| }z
t |!d)}W n t"y'   i }Y nw t|#d0p0d	
 }t|#d1p<d2
 }t$|t%rMt&|#d3nd}W d    n	1 sZw   Y  W n1 tj'j(y }  zd}d4t)| d5d6 }d	}W Y d } ~ nd } ~ w t"y   d}d7}d	}Y nw td  }!|r|r|r|r|!dt  d8||d d9 tt t|g nt|!dt  d:|tt t|g n_|r|!dt  d;|d d9 tt t|g nC|!dt  d<tt t|g n/|r |!dt  d|d d9 tt t|g n|!dt  dtt t|g W d    n	1 s>w   Y  W n
 t"yN   Y nw |t*|d k r]t+,d= qt   d S )>Nz.envT)overrideMCUBE_LOCAL_OUTBOUND_URLMCUBE_OUTBOUND_CALL_URLz-http://127.0.0.1:8088/api/mcube/outbound-call/MCUBE_PUBLIC_BASE_URLrD   MCUBE_PUBLIC_WS_URL_BASEMCUBE_WS_PATH_PREFIXz/wsMCUBE_WEBHOOK_PATHz/webhooks/mcubeFr   SHOW COLUMNS FROM z LIKE 'call_id'z LIKE 'dialstatus'zSELECT id, number FROM z@ WHERE id = %s AND campaign_id = %s AND business_id = %s LIMIT 1r!   r   z> SET status='failed', dialstatus=%s, updated_at=%s WHERE id=%smissing_numberz/ SET status='failed', updated_at=%s WHERE id=%scmp_	_contact_zK SET status='calling', call_id=%s, dialstatus=%s, updated_at=%s WHERE id=%scallingz< SET status='calling', call_id=%s, updated_at=%s WHERE id=%sz? SET status='calling', dialstatus=%s, updated_at=%s WHERE id=%sz0 SET status='calling', updated_at=%s WHERE id=%s)tocall_idr   rK   r   )r(   rD   r   callback_urlrefurlr   zcontent-typezapplication/jsonr   )datar   methodg      4@)timeoutmcube_call_sidr   	initiatedokhttp_code  request_errorzM SET status='initiated', call_id=%s, dialstatus=%s, updated_at=%s WHERE id=%s   z> SET status='initiated', call_id=%s, updated_at=%s WHERE id=%szA SET status='initiated', dialstatus=%s, updated_at=%s WHERE id=%sz2 SET status='initiated', updated_at=%s WHERE id=%sg       @)-r	   r   r   __file__resolveparentr   r   osgetenvr4   rstrip
startswithr
   rM   rN   r   rO   r   r'   rB   r:   r5   rU   rP   dumpsencodeurllibr   RequesturlopenreadrQ   r   r   r   r3   rR   r.   error	HTTPErrorgetattrrv   timesleep)"r   r   r   r   r   resolved_bot_pkoutbound_basepublic_base	public_ws	ws_prefixwebhook_pathcallback_basehas_call_idhas_dialstatuscur2idxcidcur3rX   r   refid	refurl_wspayloadoutbound_urlr
  reqresprC   	resp_json	mcube_sid
status_txtr  ecur4contacts_tabler   r   _autocall_worker  sD  

	2

 	#

z2campaign_contacts_upload.<locals>._autocall_workerc                 S  s(   g | ]}| d durt| d qS )r   N)r   r   r   r   r   r   r     s   ( rr   rK   bot_namer(   )r   r   r   r   r   Tcampaign_autocall__)targetkwargsdaemonr   zSuccessfully parsed z	 contacts)r   contacts_importedr   previous_countcontacts_datainserted_contactsr   r   )r   r   r   r   r   rJ   r   r'   r   r   r   r   )#r    r   r   r   FILESr   sizer   r$  r5   r!  endswithr   r   r
   rM   rN   r   rO   descriptionrR   zipr   rB   r:   r4   rV   	lastrowidrv   r'   rU   	threadingThreadstartr   )r   r   r   campaigns_tableupfilenamer~   parsedr   _headersrW   camp_row	camp_colscampaigncurrent_totalretry_value_is_run_nownow_srK  r   r   r   	new_countupdated_totalrA  r   raw_botraw_sr   r   r?  r   campaign_contacts_uploadz  s   





7 :




re  GETc           2        s<  t | }|stddiddS | jdkr<t| jtr| jni }|dp%d }|s6tddd	gid
ddS |d}t|dd}|rMt	|d}nt
 }|tdd }t|dd}|rcdnd}	t|dpld}
|dg }t||
|}t|dpd }|d}t|pd dkrtdddgid
ddS t|||}t| | d}t|stdddddS td   }|d!t| d" | d u}|d!t| d# | d u}|d!t| d$ | d u}|d!t| d% | d u}g d&}||||rd'nd(d|pd|t|t||d)|d)|	|r/dndd*d+tt
 tt
 g}|ra|d, z|tt|  W n  ty`   || Y nw |rr|d |t|  |r|d || |r|d ||
 d-d.d/ |D }d-d0gt| }|d1t| d2| d3| d4| |j}|d5t| d6|g d7d8 |jD }tt|| }W d    n	1 sw   Y  |r*z9t d9rtd:  !}|d;||t|t|t|tt
 tt
 g W d    n	1 sw   Y  n	 W n
 ty)   Y nw |d<|d=< td>|||d?d@dS | d}t|sRtg dddAddBdCS | j!dDpZd }| j!dEped }t"| j!dFd}t"| j!dGdAddHdI} |d |  }!g }"g }#|r|"dJ dK| dK}$|##|$|$|$|$g |r|dLkr|"dM |#| |"rdNdO|" nd}%| dP}&t|&}'| dQ}(t|(})d}*|)rtd   }|d!t|( dR | d u}*W d    n	1 sw   Y  td   }|dSt| |% |# t| pdgd pd}+|d5t| |% dT|#| |!g  dUd8 |jD   fdVd8|$ D },g }-|,D ]}.t|.d<pMd}/|/|.d=< d|.dW< d|.dX< d|.dY< t|.dZpgd }0|0dkrud|.d[< ntd\d8 |0%d]D |.d[< |'r|/r|dSt|& d^|/g t| pdgd pd|.dX< |dSt|& d_|/g t| pdgd pd|.dW< |)r|*r|/r|dSt|( d`t|/g t| pdgd pd|.dY< |-|. qCW d    n	1 s w   Y  | r|+|  d |  nd}1t|-||1| |+dBdCS )aNr   r   r   r   r   r   rD   r   r   r   r   rO  r   F
start_timei  )dayscallback_enabledTr!   r   r\   r]   exeuctive_numberrB  u`   Select a bot — the cluster bot_id is stored on the campaign and used for MCube outbound calls.r   z7Campaign table not found. Please contact administrator.zTable does not exist)r   r"  r  r   r  z LIKE 'bot_id'z LIKE 'exeuctive_number'z LIKE 'retry_logic'z LIKE 'bot_name')r   r   rO  r   r   r   didrg  end_time
start_dateend_date
retry_timer   
batch_sizebatch_interval
created_at
updated_at	scheduledactivez%Y-%m-%d
      rr   z, c                 s  r`   r)   )r   r   r   r   r   rc   ?  rd   z"campaigns_index.<locals>.<genexpr>z%szINSERT INTO z (z
) VALUES ()r   r   c                 S  r   r   r   r   r   r   r   r   M  r   z#campaigns_index.<locals>.<listcomp>scheduled_campaignsr(   aD  
                            INSERT INTO scheduled_campaigns
                            (campaign_id, business_id, start_time, end_time, execute_at, delay_seconds, status, error_message, created_at, updated_at)
                            VALUES (%s, %s, %s, %s, %s, 0, 'pending', NULL, %s, %s)
                            r   r   zCampaign created successfully)r   r\  r   r         )current_page	last_pageper_pagetotal)r
  
paginationsearchr   pager~     r"   zF(name LIKE %s OR description LIKE %s OR did LIKE %s OR status LIKE %s)%allzstatus = %sz WHERE z AND r   _call_historyz LIKE 'refid'SELECT COUNT(*) FROM z, ORDER BY created_at DESC LIMIT %s OFFSET %sc                 S  r   r   r   r   r   r   r   r     r   c                   s   g | ]	}t t |qS r   )rR   rP  rb   r   colsr   r   r     r   successful_callstotal_callstotal_attemptsr   waiting_time_countc                 S  r   r   r   r   r   r   r   r     r   r_   z WHERE campaign_id = %sz- WHERE campaign_id = %s AND status = 'ANSWER'z WHERE refid = %s)&r    r   r  r3   r
  rR   r   r4   r7   rI   r:   r   r'   rp   r{   r   r   r
   rM   rN   r   rO   rB   rA   rV   r   r   rj   rv   rQ  rO  rP  r}   query_paramsr-   extendfetchallr   )2r   r   r
  r   rO  run_now_flagrT  endri  ro  r\   r]   waiting_time_valuesrj  rB  executive_numberrU  rW   
has_bot_idhas_exeuctive_number_colhas_retry_logic_colhas_bot_name_colinsert_colsinsert_valscols_sqlph_sqlr   r[  r\  r  r   r  r~  offsetwhere_partsrz   like	where_sqlr@  has_contactscall_history_tablehas_call_history	has_refidr  rowsrY   campr2  waiting_rawr}  r   r  r   campaigns_index  s  








U











""".r  c              	   C  s  t | }|stddiddS | d}| d}t|s(tddddddddS td	  }|d
t|  t| p@dgd pDd}|d
t| d t| pXdgd p\d}|d
t| d t| ppdgd ptd}d } }	}
t|r|d
t|  t| pdgd pd}|d
t| d t| pdgd pd}	|d
t| d t| pdgd pd}
W d    n1 sw   Y  t|||||	|
ddS )Nr   r   r   r   r   r   r   )total_campaignsactive_campaignscompleted_campaignsr  r  failed_callsaverage_call_durationr   r  z WHERE status = 'active'z WHERE status = 'completed'z WHERE status IN ('ANSWER')z WHERE status = 'failed')	r    r   r   r
   rM   rN   r   r   rO   )r   r   rU  r@  rW   r  r  r  r  r  r  r   r   r   campaigns_stats  sV   

r  c           	      C  s0  t | }|stddiddS | jd}z|d urt|nd }W n ty+   d }Y nw |s8tddg dddS td	  K}|d
 |	 d u rXtdg dW  d    S |d |	 d u}d}||g}|ro|d7 }|d7 }||| dd |
 D }W d    n1 sw   Y  td|dS )Nr   r   r   r   rr   Fzbot_id is required)r   r   r
  r(   rs   T)r   r
  rt   zNSELECT DISTINCT did_no FROM did_numbers WHERE business_id = %s AND bot_id = %sru   z ORDER BY did_no ASCc                 S  s$   g | ]}|r|d  dur|d  qS rg   r   r  r   r   r   r   4  r   z%executive_numbers.<locals>.<listcomp>)r    r   r  r   r   r   r
   rM   rN   rO   r  )	r   r   rr   
bot_id_intrW   rw   ry   rz   numsr   r   r   executive_numbers  s6   

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'   )rC   r   r   r   )r   r   r   rJ   )r   r   r\   r'   r]   r   r   r'   )r   r   rq   r'   rr   r   r   r'   )r|   r'   r   r.   )r~   r   r   r   )r   r   r   r   r   r   )r   r   rC   r   r   r   )r   r   )7__doc__
__future__r   rP   r  rR  r%  urllib.requestr  urllib.errorpathlibr   dotenvr   r   r   zoneinfor   typingr   	django.dbr	   r
   rest_framework.decoratorsr   r   rest_framework.permissionsr   rest_framework.responser   apps.cluster.dynamic_tablesr   r   r   config.reporting_utilsr   r   r    r-   r7   r:   rB   rI   r[   rp   r{   r}   r   r   r   r   re  r  r  r  r   r   r   r   <module>   sb    
	







'
	
H
8
  
C  "3