
    -hVU                        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mZ ddl	m	Z	 ddl
mZmZmZmZ ddlZddlmZmZmZmZ ej*                  j-                  dd      Zej*                  j-                  d	d
      Zej*                  j-                  dd      ZdZ ed      ZdZ ed      Zej=                  dd        ed      Zej=                  dd        ed      Z e j=                  dd       e dz  Z!e dz  Z"e dz  Z#ej*                  j-                  dd      Z$dZ%dZ&dZ' ejP                  d      Z)e)jU                  ejV                         	 e!jX                  j=                  dd       e!j[                  d       e#j[                  d       e"j[                  d        ej^                   e0e!            Z1e1jU                  ejV                          ejd                  d      Z3e1ji                  e3       e)jk                  e1        ej^                   e0e#            Z6e6jU                  ejn                         e6ji                  e3       e)jk                  e6        ejp                         Z9e9jU                  ejV                         e9ji                  e3       e)jk                  e9       d e0fd!Z:d"efd#Z;d ee<   fd$Z=d%e<fd&Z>d'e0fd(Z?d e0fd)Z@dJd e0fd*ZAd+e0d ee0   fd,ZBd-ed ee0   fd.ZCd/e0d e<fd0ZDd/e0d eee0   ee0   f   fd1ZEd2e0d e0fd3ZFd4e0d5ed eee0f   fd6ZG	 dKd7e0d8ed9e0d:e0d eeHe0f   f
d;ZI eeJ      ZKd"e<d ee0   fd<ZLeKj                  d=d>g?      d@        ZNeKj                  dAdBg?      dC        ZOeJdDk(  r7e)j                  dE       	 ej                  dF       eKj                  dGdHI       yy# e.$ r Y !w xY w# e.$ r Y (w xY w)LaX  
zoho_webhook.py (modified per request)

Behavior:
- Given ATS_Record_ID: fetch ATS record, extract email and attachment IDs
- Log & return the found email
- Download attachments into /var/www/zoho/attache (persistent)
- Search Candidates by email and upload found attachments to candidate (category "Resume")
- Debug endpoint: /debug_ats/<id>
    N)Path)datetime)OptionalListTupleAny)FlaskrequestjsonifyabortZOHO_CLIENT_IDz#1000.6XRABP1ZPTQ2M0TOCLMUA3764Y0B1ZZOHO_CLIENT_SECRET*2da33cfb7e3babc22ff3c25438e12cacd6fde224cdZOHO_AUTH_CODE zhttps://www.zoho.comz/var/www/zoho/tokens.jsonz#https://recruit.zoho.com/recruit/v2z/var/www/zoho/tmpTparentsexist_okz/var/www/zoho/attachez/var/www/zoho/Logzwebhook_transfer.logzwebhook_received.logzwebhook_error.logZOHO_WEBHOOK_SECRETchange_this_shared_secret      x   zoho_webhook)r   z'%(asctime)s [%(levelname)s] %(message)sreturnc                  H    t        j                         j                  d      S )Nz%Y-%m-%d %H:%M:%S)r   nowstrftime     /var/www/zoho/newupload.pycurrent_timestampr"   ]   s    <<>""#677r    payloadc                 *   	 t               | d}t        j                  dd      5 }|j                  t	        j
                  |d      dz          d d d        y # 1 sw Y   y xY w# t        $ r$}t        j                  d| d	
       Y d }~y d }~ww xY w)N)tsr#   autf-8encodingF)ensure_ascii
z!Could not write to received log: Texc_info)	r"   RECEIVED_LOGopenwritejsondumps	Exceptionloggererror)r#   entryfes       r!   append_received_logr9   a   s    M(*w?sW5 	BGGDJJu59D@A	B 	B 	B M8<tLLMs4   $A% *AA% A"A% "A% %	B.BBc                      t         j                         sy 	 t        j                  t         j	                  d            S # t
        $ r"} t        j                  d|         Y d } ~ y d } ~ ww xY w)Nr'   r(   zfailed to read tokens.json: )TOKENS_FILEexistsr1   loads	read_textr3   r4   warning)r8   s    r!   load_tokensr@   m   sZ    zz+///ABB 5aS9:s   (A   	A+	A&&A+tokensc                 &   t         j                  j                  dd       t               xs i }|j	                         }|j                  |        d| vrd|v r|d   |d<   t        j                  t        t         j                              \  }}	 t        j                  |dd      5 }|j                  t        j                  |d	             d d d        t        j                  |t                y # 1 sw Y   $xY w# t        $ r}t         j#                  d
| d       	 t         j%                  t        j                  |d	      d       n5# t        $ r$}t         j#                  d| d       Y d }~nd }~ww xY wY d }~y Y d }~y d }~ww xY w)NTr   refresh_token)dirwr'   r(      )indentz#Failed to write tokens atomically: r,   zFallback write failed: )r;   parentmkdirr@   copyupdatetempfilemkstempstrosfdopenr0   r1   r2   replacer3   r4   r5   
write_text)rA   oldmergedfdtmp_pathr7   r8   e2s           r!   save_tokensrX   w   sR   TD9
-
2CXXZF
MM&f$C)?"%o"6##K,>,>(?@LB	HYYr31 	2QGGDJJva01	2


8[)	2 	2  H:1#>N	H""4::fQ#?'"R 	HLL22$7$LGG	HG SHsZ   D  *'C4"D  4C=9D   	F	F$,EF	E>E94F9E>>FF	auth_codec                 *   d}t         t        d| t        d}t        j                  ||d      }|j
                  dk7  r%t        d|j
                   d|j                         |j                         }t        |       t        j                  d	       |S )
N(https://accounts.zoho.com/oauth/v2/tokenauthorization_code)	client_idclient_secret
grant_typecoderedirect_uri   datatimeout   zAuth code exchange failed (z): z$Auth code exchanged and tokens saved)	CLIENT_IDCLIENT_SECRETREDIRECT_URIrequestspoststatus_coder3   textr1   rX   r4   info)rY   urlrd   rrA   s        r!   exchange_auth_code_for_tokensrq      s    
4C&*$D 	cb1A}}5amm_CxPQQVVXF
KK67Mr    c                      t               } | j                  d      }d}|t        t        dd}t	        j
                  ||d      }|j                          |j                         }t        |       |d   S )NrC   r[   )rC   r]   r^   r_   rb   rc   access_token)	r@   getrg   rh   rj   rk   raise_for_statusr1   rX   )rA   rC   ro   rd   rp   
new_tokenss         r!   refresh_access_tokenrw      sm    ]FJJ/M
4C&&%	D 	cb1AJ
n%%r    c                     t               }|s#t        r| rt        t              }nt        d      |j	                  d      }|s
t               }|S )Nz1tokens.json not found and AUTH_CODE not provided.rs   )r@   	AUTH_CODErq   r3   rt   rw   )allow_exchange_if_missingrA   rs   s      r!   get_access_tokenr{      sG    ]F229=FOPP::n-L+-r    emailc                    | sy t        d      D ]  }t        |dk(        }dd| i}d|  d}t         dt        j                  j                  |       }	 t        j                  ||d	
      }|j                  dk(  r9|j                         j                  d      }|r|d   j                  d      c S  y |j                  dk(  r y |j                  dk(  rt                t        j                  d|j                   d|j                           y  y # t        $ r%}t        j                  d| d       Y d }~ y d }~ww xY w)NrF   r   )rz   AuthorizationZoho-oauthtoken z(Email:equals:)z/Candidates/search?criteria=rb   headersre   z*search_candidate_by_email request failed: Tr,   rf   rd   id     zsearch candidate returned : )ranger{   RECRUIT_API_BASErj   utilsquotert   r3   r4   r5   rl   r1   rw   r?   rm   )	r|   attempttokenr   criteriaro   rp   r8   rd   s	            r!   search_candidate_by_emailr      s:   8  GqLJ"&6ug$>?#E7!,!"">x~~?S?ST\?]>^_	S'2>A ==C668<<'DAw{{4((==C==C "3AMM?"QVVHMN+,   	LLEaSITXLY	s   D	ED??Eobjc                    g }t        | t              r{| j                         D ]f  \  }}t        |t        t        f      r,|j                         dv r|j                  t        |             t        |t              r|j                         dv r|D ]  }t        |t              r|j                  d      xs7 |j                  d      xs$ |j                  d      xs |j                  d      }|r|j                  t        |             z|j                  t        |             t        |t        t        f      s|j                  t        |              t        |t        t        f      sM|j                  t        |             i n1t        | t              r!| D ]  }|j                  t        |              t        t        j                  |            S )N)r   attachment_idattachmentidfile_idfileid)attachmentsfiles	documentsr   r   r   attachmentId)
isinstancedictitemsrN   intlowerappendlistrt   extend extract_attachment_ids_from_jsonfromkeys)r   foundkvitemaids         r!   r   r      sm   E#tIIK 	BDAq!c3Z(QWWY:v-vSV$!T"qwwy4Y'Y 0D!$-"hhtn|0I|TXXV_M`|dhdldlm{d|!LLS2!LL)I$)OP#D3*5SY/0 !dD\*=a@A	B 
C	 	ADLL9$?@	Ae$%%r    ats_record_idc                 $   t               }dd| i}t         d|  }	 t        j                  ||d      }|j                  d	k(  rT|j                         j                  d
      xs g }t        |t              r	|r|d   }|S t        |t              r|}|S i }|S |j                  dk(  rt
        j                  d|  d       i S |j                  dk(  r+t
        j                  d       	 t                t        |       S t
        j                  d|j                   d|j                           i S # t        $ r&}t
        j                  d| d       i cY d }~S d }~ww xY w# t        $ r&}t
        j                  d| d       i cY d }~S d }~ww xY w)Nr~   r   z/ATS/rb   r   z!fetch_ats_record request failed: Tr,   rf   rd   r   r   zATS record z returned 204 (no content)r   z=fetch_ats_record: got 401, refreshing token and retrying oncezrefresh failed: zfetch_ats_record returned r   )r{   r   rj   rt   r3   r4   r5   rl   r1   r   r   r   rn   rw   fetch_ats_recordr?   rm   )r   r   r   ro   rp   r8   rd   records           r!   r   r      s   E"25' :;GeM?
3CLLgr: 	}}vvx||F#)r&tT2ta KUUY[_J`$ gi	
#	k-0JKL		
#	ST	 "#M22
 	3AMM?"QVVHMN	)  8<tL	  	LL+A3/$L?I	s;   D. (E  .	E7EEE 	F)F
F
Fc                    t        |       }|sd g fS d }dD ]F  }|j                  |      }t        |t              s%|j	                         s6|j	                         } n |sx|j                         D ]e  \  }}t        |t              sdD ]F  }|j                  |      }t        |t              s%|j	                         s6|j	                         } n |se n g }|j                         D ]^  }|j                         dk(  s |j                         j                  d      s7|j                  |      }	t        |	t              rdD ]  }
|	j                  |
      }t        |t              s%|D ]  }t        |t              rh|j                  d      xs7 |j                  d      xs$ |j                  d      xs |j                  d	      }|s`|j                  t        |             {t        |t        t        f      s|j                  t        |               3t        |	t              sE|j                  t        |	             a |st        |      }t        t        j                  |            }||fS )
N)Emailr|   Applicant_Emailapplicant_emailcandidate_emailApplicantEmail)r|   r   othersother)r   Attachmentsr   Filesr   	Documentsr   r   r   r   )r   rt   r   rN   stripr   r   keysr   
startswithr   r   r   r   r   r   )r   r   r|   keyr   r   kkevr   r   fk	files_valr7   fids                 r!   "get_email_and_attachments_from_atsr     s   m,FRx El JJsOa!'')GGIE	 LLN 	DAq!T", BrB!"c*rxxz "
	
 	  K[[] M779 AGGI$8$8$AZZ]F&$'d 	;B &

2I!)T2!* ;A)!T2&'eeDk&xQUU?5K&xquuU^O_&xcdchchiwcx#&$/$6$6s3x$@!+ASz!: + 2 23q6 :;	; FD)""#CF#KLM$ 6v>t}}[12K+r    namec                     | j                         } t        j                  dd|       } t        j                  dd|       } | d d } | S )Nz[:\\/]+_z[^0-9A-Za-z._- ]+r   rf   )r   resub)r   s    r!   sanitize_filenamer   D  sB    ::<D66*c4(D66&D1D:DKr    r   dest_dirc           
      >   | st        d      t        t              D ]  }t               }t         d|  d}dd| i}t        j                  ||t              }|j                  dk(  r|j                  j                  dd	      }t        j                  d
|      }|r|j                  d      n|  d}	t        |	      }	|j                  dd       |t        t!        j                                 d|	 z  }
	 t#        |
d      5 }|j%                  |j&                         ddd       |
|	fc S |j                  dk(  rt/                +|j                  dv rRt1        d|dz   z  d      }t*        j-                  d|  d|j                   d| d       t!        j2                  |       t)        d|  d|j                   d|j4                          t)        d|  d      # 1 sw Y   xY w# t(        $ r'}t*        j-                  d|
 d| d       Y d}~d}~ww xY w) zF
    Download file and save to dest_dir. Returns (path, filename)
    zfile_id requiredz/ATS/Attachments/z	/downloadr~   r   r   rf   zContent-Dispositionr   zfilename="([^"]+)"   z.binTr   r   wbNzCould not write to disk r   r,   r   )  i    
   r   z	download z
 returned z. retrying after szFailed to download  z after retries)
ValueErrorr   MAX_DOWNLOAD_RETRIESr{   r   rj   rt   REQUEST_TIMEOUTrl   r   r   searchgroupr   rI   r   timer/   r0   contentr3   r4   r?   rw   minsleeprm   )r   r   r   r   ro   r   rp   cdmfilenameout_pathr7   r8   waits                 r!   download_file_from_ats_to_diskr   M  s    +,,-. S "!""3G9IF"&6ug$>?LLgG==C4b9B		/4A%&qwwqzwit,<H(2HNN4$N7S%5$6az"BBHZ(D) 'QGGAII&'
 X%%==C "==O+rWq[)3/DNNYwiz!--HYZ^Y__`abJJt-gYbqQRR7S8 )'.A
BB' ' Z!9(2aSITXYYZs0   7G,G G, G)	%G,,	H5HHcandidate_id	file_path	file_namecategoryc           	         t          d|  d}d}d}|t        k  rot               }dd| i}d||j                  d      fi}	d	|i}
	 t	        j
                  |||	|
t        
      }|}|j                  dv r|j                  |j                  fS |j                  dk(  rt                |dz  }|j                  dk(  rIt        d|dz   z  d      }t        j!                  d| d| d       t        j                  |       |dz  }d|j                  cxk  rdk  rZn nWt        d|dz   z  d      }t        j!                  d|j                   d| d| d       t        j                  |       |dz  }a|j                  |j                  fS ||j                  nd||j                  fS dfS # t        $ rR}t        j                  d| d| d       t        j                  t        d|dz   z  d             |dz  }Y d}~d}~ww xY w)z9
    Upload a file on disk to candidate attachments.
    z/Candidates/z/Attachmentsr   Nr~   r   filerbattachments_category)r   r   rd   re   zupload exception for r   Tr,   <   r   iX  )rf      r   r   i  z429 Too Many Requests for z. Sleeping r   r   rb   i,  zServer error z for r   )r   MAX_UPLOAD_RETRIESr{   r/   rj   rk   r   r3   r4   r5   r   r   r   rl   rm   rw   r?   )r   r   r   r   ro   retries	last_respr   r   r   rd   rp   r8   	wait_times                 r!   upload_file_to_candidater   r  s    
 l<.
ECGI
'
' ""&6ug$>?)Y^^D%9:;&1	c7%dTcdA 	==J&==!&&((==C "qLG==CB'A+.4INN7	{+i[XYZ[JJy!qLG!--%#%B'A+.4INN]1==/ykU^T__`abJJy!qLG}}aff$$%.%:I!!'3INN= =9;= =3  	LL02aSADLQJJs21-s34qLG		s   F+ +	H4AHHc           	          g }dD ]  }|| v s| j                  |      }t        |t              r,|j                  |D cg c]  }|st	        |       c}       Ut        |t              sf|j                         sw|j                  |j                  d      D cg c]#  }|j                         s|j                         % c}        | j                  d      }t        |t              rl|D ]g  }t        |t              s|j                  d      xs$ |j                  d      xs |j                  d      }|sN|j                  t	        |             i t        t        j                  |            S c c}w c c}w )N)attachment_idsfile_idsAttachment_IDsattachmentIdsr   attachment_ids_csv,r   r   r   r   )
rt   r   r   r   rN   r   splitr   r   r   )	r#   outr   valr   pattr&   r   s	            r!   parse_attachment_idsr     s    
Cu M'>++c"C#t$

C5q1CF56C%#))+

syy~K!AGGIKLM ++m
$C#t 	)A!T"eeDkTQUU?%;Tquu^?TJJs3x(		)
 c"## 6Ks   E6
E6
E;
0E;
z/zoho_webhookPOST)methodsc                  	   i } 	 t         j                  rt        j                         } nit         j                  j	                  d      } t        | j                               D ]-  \  }}t        |t
              st        |      dk(  s&|d   | |<   / t        |        t        j!                  d|         t         j"                  j%                  d      xs t         j"                  j%                  d      }| j%                  d      xs |}t&        r7|t&        k7  r.t        j)                  d       t        d| i       t+        dd       | j%                  d      xs7 | j%                  d      xs$ | j%                  d      xs | j%                  d      }|s$t        j                  d       t        ddi      dfS | j%                  d      xs$ | j%                  d      xs | j%                  d      }| j%                  d      xs$ | j%                  d       xs | j%                  d!      }t-        |       }	|	sbt        j!                  d"| d#       	 t/        |      \  }
}t        j!                  d$|
        t        j!                  d%|        |
r|s|
}|r|}	|	s)t        j!                  d,|        t        d-||d.      d/fS |sX|rVt        j!                  d0|        t3        |      }|rt        j!                  d1|        nt        j                  d2|        g }|	D ]  }	 t        j!                  d3| d4       t5        |t6              \  }}t        j!                  d5|        d }|rMt        j!                  d6| d7|        t9        |||d89      \  }}||d:}t        j!                  d;| d'|        |j;                  |t        |      ||d<        t        d?||||d@      d/fS # t        $ r[}t        j                  dd       t        dt        j                  d	      i       t        d
t        |      d      dfcY d }~S d }~ww xY w# t        $ rL}t        j1                  d&| d'|        t        |t        |      d(       t        d)|d*      d+fcY d }~S d }~ww xY w# t        $ rC}t        j1                  d=| d'|        |j;                  |t        |      d>       Y d }~d }~ww xY w)ANF)flatr   r   z Failed to parse incoming payloadTr,   raw)as_textbad_payload)statusr5   i  zWebhook received: zX-Webhook-SecretzX-ZOHO-SECRETwebhook_secretzInvalid webhook secretinvalid_secret_payloadi  	ForbiddenATS_Record_ID	record_idats_id	Record_IDzNo ATS record id in payloadr  missing_ats_idCandidate_IDr   candidateIdr   Candidate_Emailr|   zFetching ATS record z! to extract email and attachmentszFetched email from ATS: zFetched attachments from ATS: zError reading ATS r   )list_attachments_error_forr5   error_listing_attachments)r  r   r   zNo attachments found for ATS no_attachments)r  r   r|   rf   zSearching candidate by email: zCandidate found: zNo candidate found with email zDownloading attachment z to diskzSaved attachment to z
Uploading z to candidate Resume)r   )rl   responsezUpload result for )r   
saved_path
saved_nameuploadzError processing attachment )r   r5   done)r  r   r   r   results)r
   is_jsonget_jsonformto_dictr   r   r   lenr3   r4   r5   r9   get_datar   rN   rn   r   rt   WEBHOOK_SECRETr?   r   r   r   	exceptionr   r   
ATTACH_DIRr   r   )r#   r   r   r8   secret_headerprovided_secretr   r   r   r   fetched_emailfetched_attachmentsr  r   r  r  upload_resultrl   resps                      r!   r   r     s   GH??&&(Gll***6GW]]_- &1a&3q6Q;!"1GAJ&  
KK$WI./OO''(:;cw?R?RSb?cMkk"23D}O/^;/05w?@c;KK0  BGKK4L  BPWP[P[\dPe  Bipitit  vA  jBM23"234c99;;~.k'++n2MkQXQ\Q\]jQkLkk"34nDU8VnZaZeZefmZnO)'2N *=/9Z[\	i1STa1b.M.KK2=/BCKK89L8MNO_"/"!4 3M?CD"2]]lmnpsss O4_4EFG0AKK+L>:;LL9/9JKL G D	DKK1#h?@%CC%T"J
KK.zl;< MjN<.QR$<\:Walt$u!T0; N0B{mLMNN!$!*o('	 D* &$*  	 Y  H7$GUG$4$4T$BCD-#a&ABCGGHF  	i1-1#FG}WZ[\W] ^_&ATabcehhh	iJ  	D;C51#FGNNS3q6BCC	Dsd   A4O* 9O* 
O* AQ !B4R)*	Q3AQ	Q	Q	R&AR!R&!R&)	S528S00S5z/debug_ats/<ats_id>GETc                    	 t        |       }|st	        dd| d      dfS t        |       \  }}t	        d| |||d      dfS # t        $ r9}t        j                  d       t	        t        |      d| d      dfcY d }~S d }~ww xY w)	Nzdebug_ats fetch failedF)r5   r<   r  r   z"Record not found with any endpointrf   T)r<   r  r|   r   record_preview)r   r3   r4   r  r   rN   r   )r  r   r8   r|   r   s        r!   	debug_atsr*    s    U!&) !EQVeklmorrr;FCE;%   	   U12Q5vNOQTTTUs   A 	B
.A>8B>B__main__z\Starting Zoho Webhook receiver (Flask built-in server). For production use Gunicorn + nginx.i  z0.0.0.0i  )hostport)T)r  )S__doc__r   rO   r1   r   loggingrL   pathlibr   r   typingr   r   r   r   rj   flaskr	   r
   r   r   environrt   rg   rh   ry   ri   r;   r   TMP_DIRrI   r   LOG_DIRLOG_FILEr.   	ERROR_LOGr  r   r   r   	getLoggerr4   setLevelINFOrH   touchr3   FileHandlerrN   fh	Formatterfh_fmtsetFormatter
addHandlerehERRORStreamHandlerchr"   r9   r   r@   rX   rq   rw   r{   r   r   r   r   r   r   r   r   __name__appr   router   r*  rn   chmodrunr   r    r!   <module>rK     s  	 
 	       - -  0 0
 JJNN+-RS	

35abJJNN+R0	%./8  "
# dT * )*
 
    - "
# dT * ++//))	  57RS   
 
		>	*  	OO$6NNDN!OOTO"% WX' GLL 			D	E     " WY( GMM      " W GLL      " 83 8M MXd^ H H(S $&c &$
 
 S Xc] 8&# &$s) &0C D :1c 1eHSMSWX[S\D\>] 1hC C "CC "C4 "CE$PS)DT "CL .6)=3 )=4 )=C )='*)=:?S/)=^ Ho$$ $49 $& ?VH-\ .\~  5'2 3* z
KKno GGG& I  		R  s%   AN? O ?OOOO