
    ڵ1i/                         d dl Z d dlZd dl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  e	         ej&                  ej(                  
        ej*                  e      Z G d d      Zy)    N)ListTupleDictAny)load_dotenv)datetime   )CVStorageService)ResumeParserService)JobMatchingService)JobAPIService)levelc                       e Zd ZdZd Zdedeeef   fdZde	dededefdZ
d	eee	ef      deeef   fd
Zd	eee	ef      dedeeef   fdZd	eee	ef      defdZy)ResumeUploadServicez?Service for handling resume uploads with background AI analysisc                 ^    t               | _        t               | _        t	               | _        y N)r   resume_parserr   job_matching_servicer   job_api_service)selfs    B/var/www/html/drjob-dev/drjob-ai/services/resume_upload_service.py__init__zResumeUploadService.__init__   s"    02$6$8!,    
job_emp_idreturnc                 "   	 | j                   j                  |      }|j                  d      sdd| d|j                  dd       dS |j                  di       }t        |t              r	dd| d	dS t        |t
              rbd
|v r|d
   }|j                  dd      xs: |j                  dd      xs& |j                  dd      xs |j                  dd      }|s	dd| ddS ddiS # t        $ r}ddt        |       dcY d}~S d}~ww xY w)z
        Validate job_emp_id by checking if job exists using JobAPIService
        
        Args:
            job_emp_id: The employer job ID to validate
            
        Returns:
            Dict with validation result
        successFzInvalid job_emp_id: z - errorzJob not foundvalidr   dataz% - Job ID not found or does not existjob	job_title titlepositionjob_positionz# - Job data incomplete or corruptedr    TzError validating job_emp_id: N)r   get_employer_jobget
isinstancelistdict	Exceptionstr)r   r   resultjob_datar#   es         r   validate_job_emp_idz'ResumeUploadService.validate_job_emp_id   sL   *	)):::FF::i("3J<s6::gWfCgBhi  zz&"-H(D)"3J<?de  (D)H$'H &\\+r: <#<<4<#<<
B7< $<<; 
 !!&#7
|Cf!g 
 T?" 	8QA 	s1   AC* 	*C* 4A1C* &C* *	D3D	D	Dfile_contentfilenamec           	         	 t         j                  j                  t        j                         dd|      }t        j                  |d       t        j                         j                  d      }t         j                  j                  |      d   }| d|j                  dd       }t         j                  j                  ||      }t        |d	      5 }	|	j                  |       d
d
d
       t         j                  j                  dd||      }
t        j                  d|
        |
S # 1 sw Y   ExY w# t        $ r.}t        j                  d| dt!        |              Y d
}~yd
}~ww xY w)a2  
        Save uploaded file to local storage and return file path
        
        Args:
            file_content: File content bytes
            filename: Original filename
            job_emp_id: Job employer ID for organizing files
            
        Returns:
            str: Saved file path
        uploadsresumesT)exist_okz%Y%m%d_%H%M%Sr	   _ wbNFile saved successfully: zError saving file : r$   )ospathjoingetcwdmakedirsr   nowstrftimesplitextreplaceopenwriteloggerinfor-   r   r.   )r   r3   r4   r   base_upload_dir	timestampfile_extensionsafe_filename	file_pathfrelative_pathr1   s               r   save_uploaded_filez&ResumeUploadService.save_uploaded_fileR   s+   	 ggll299;	9jYOKK$7 !//@IWW--h7:N(k8+;+;C+E*FGM_mDI i& &!%& GGLLIz=YMKK3M?CD  & &  	LL-hZr#a&BC	s1   CD7 D+'AD7 +D40D7 7	E. $E))E.
files_datac                 f    h d}|D ]$  \  }t        fd|D              rdd ddc S  ddiS )	z
        Validate uploaded file types
        
        Args:
            files_data: List of (file_content, filename) tuples
            
        Returns:
            Dict with validation result
        >   .doc.pdf.docxc              3   \   K   | ]#  }j                         j                  |       % y wr   )lowerendswith).0extr4   s     r   	<genexpr>z:ResumeUploadService.validate_file_types.<locals>.<genexpr>   s#     T#x~~'005Ts   ),FzFile z; is not supported. Only PDF and Word documents are allowed.r   r    T)any)r   rS   allowed_extensionsr3   r4   s       @r   validate_file_typesz'ResumeUploadService.validate_file_typesx   sT     7&0 	"L(TASTT"$XJ.ij 	 r   c                    	 t        j                  | j                  ||fd      }|j                          dd| t	        |      ddS # t
        $ r9}t        j                  dt        |              dt        |      dcY d	}~S d	}~ww xY w)
a  
        Start background processing for resume uploads
        
        Args:
            files_data: List of (file_content, filename) tuples
            job_emp_id: The employer job ID
            
        Returns:
            Dict with processing start confirmation
        T)targetargsdaemonz&Resume processing started for job ID: processing_started)r   messagetotal_filesstatusz"Error starting resume processing: F)r   r   N)		threadingThread_background_resume_processingstartlenr-   rI   r   r.   )r   rS   r   threadr1   s        r   process_resume_uploads_asyncz0ResumeUploadService.process_resume_uploads_async   s    	%%99 *-F
 LLN  CJ<P":.	   	LL=c!fXFG Q 	s   AA	 		B.B BBc           
         	 t         j                  d| dt        |              t               }d}|D ]  \  }}	 t         j                  d|        | j	                  |||      }|st         j                  d|        Nt         j                  d|        	 | j                  j                  ||      }t         j                  d| d|rt        |      nd        |r|j                         dk(  rt         j                  d| d       t         j                  d|        	 | j                  j                  |      }
t         j                  d|        |
rnt         j                  d|        	 |j                  |||
||      }t         j                  d| d
|j                  d              |j                  d      r|d   }t         j                  d|        t         j                  d|        	 | j                  j                  |||
d      }t         j                  d| d
|j                  d              j                  d      rt         j                  d|        |dz  }not         j                  d | d
|j                  d!              nDt         j                  d"| d
|j                  d!              nt         j                  d#| d$        t         j                  d&| d'| d(t        |       d)       y# t        $ r/}	t         j                  d	| d
t        |	              Y d}	~	d}	~	ww xY w# t        $ r/}t         j                  d| d
t        |              Y d}~Jd}~ww xY w# t        $ r/}t         j                  d| d
t        |              Y d}~d}~ww xY w# t        $ r/}t         j                  d| d
t        |              Y d}~d}~ww xY w# t        $ r/}t         j                  d%| d
t        |              Y d}~d}~ww xY w# t        $ r.}t         j                  d*| d
t        |              Y d}~yd}~ww xY w)+a`  
        Background function to:
        1. Parse and store resume files
        2. Generate AI analysis (strengths, weaknesses, matching_percentage) 
        3. Store results in screenings collection with type=3
        
        Args:
            files_data: List of (file_content, filename) tuples
            job_emp_id: The employer job ID
        z2Starting background resume processing for job ID: z	, files: r   zProcessing file: zFailed to save file: r<   zText extraction successful for z
, length: zText extraction failed for r=   Nr$   z"Could not extract text from file: z - empty contentzStarting resume parsing for zResume parsing successful for zResume parsing failed for zStarting database storage for )r4   raw_textparsed_datar   rO   zDatabase storage completed for r   zDatabase storage failed for jobseeker_idz2Successfully stored resume data for jobseeker ID: z'Starting AI analysis for jobseeker ID:    )r   rs   rr   analysis_typez(AI analysis completed for jobseeker ID: z'AI analysis exception for jobseeker ID z5Successfully completed AI analysis for jobseeker ID: r	   z%AI analysis failed for jobseeker ID: r   zStorage failed for file zParsing failed for file z: No data returnedzError processing file z(Resume processing completed for job ID: z. Successfully processed /z files.z1Error in background resume processing for job ID )rI   rJ   rm   r
   rR   r   r   extract_text_from_filer-   r.   stripparse_resumestore_complete_cv_datar)   r   #analyze_single_candidate_for_upload)r   rS   r   
cv_storageprocessed_countr3   r4   saved_file_pathextracted_textextract_errorrr   parse_errorstorage_resultstorage_errorrs   ai_analysis_resultai_errorr1   s                     r   rk   z1ResumeUploadService._background_resume_processing   s   ]	eKKLZLXabefpbqarst)+JO*4 R&hQKK"3H: >? '+&=&=lHV`&aO*'<XJ%GH KK";O;L MN!)-););)R)RS_ai)j&EhZzqZ]^lZm  FG  ZH  %I  J
 *^-A-A-Cr-I'I(Sc%de KK">xj IJ!&*&8&8&E&En&U&DXJ$OP
 #&DXJ$OP%-7-N-N)1)7,7+5*9 .O .N #KK*I(SUVdVhVhirVsUt(uv
 *--i8+9.+IL"KK*\]i\j(kl #KK*QR^Q_(`a	x595N5N5r5r/91=0;23	 6s 6" 2 !'.VWcVddfgyg}g}  H  hI  gJ  -K  !L
  255i@ &.cdpcq,r s /1 4 &/TUaTbbdewe{e{  }D  fE  eF  .G  !H"LL+CH:RP^PbPbcjPkOl)mn'?zI[%\]]Rh KKB:,Nghwgxxyz}  I  {J  zK  KR  S  TI % !'B8*BsS`OaNb%cd ! % !'A(2cR]N^M_%`a !"  ) %"LL+GzQSTWXeTfSg)hi$%& $- x &/VWcVddfgjksgtfu-v w wx ! LL#9(2c!fX!NO  	eLLLZLXZ[^_`[aZbcdd	es  9P7 AO<P7 O<AL.O<P7 O<'3MO<6A N6AO<=A	OBO<$+P7 	M$M=O<P7 MO<	N$M>8O<<P7 >NO<	N>$N93O<7P7 9N>>O<	O9
$O4.O<4O99O<<	P4$P/)P7 /P44P7 7	Q. $Q))Q.N)__name__
__module____qualname____doc__r   r.   r   r   r2   bytesrR   r   r   r`   ro   rk    r   r   r   r      s    I/
4c 4d38n 4l$u $ $QT $Y\ $Ld53D.E $sTWx. * tE%*<M7N  \_  dhilnqiqdr  DheU5#:=N8O he]` her   r   )r>   ri   loggingtypingr   r   r   r   dotenvr   r   r|   r
   r   r   r   r   r   r   basicConfigINFO	getLoggerr   rI   r   r   r   r   <module>r      sc    	   ) )   ( . 4 *    ',, '			8	$Ce Cer   