
    ȃ_i
                    (   d dl Z d dlZd dlmZmZmZmZ d dlZd dlm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 ddlmZ dd	lmZ dd
lmZ ddlmZ  e         ej2                  ej4                          ej6                  e      Z G d d      Zy)    N)DictAnyListOptional)datetime)load_dotenv)CodecOptionsDatetimeConversion   )JobAPIService)OpenSearchService)Embedder)mongo_service)	AIService)levelc                      e Zd ZdZd Zdedeeef   fdZdede	e
   deeef   fdZdefdZdede	e
   fd	Zdedeeeef      fd
Zd0deeef   de	e   de	eeef      fdZd0deeef   de	e   de	eeef      fdZdeeef   de	eeef      fdZdededefdZdeeef   defdZde	e   dede	eeef      fdZdeeef   deeef   deeeef      fdZdedeeeef      fdZdeeef   deeef   deeef   defdZdededeeef   fd Zd0dedeeef   d!eeef   deeef   fd"Zd#edefd$Zd0dedeeef   d!eeef   deeef   fd%Zded&ed'e	eeef      fd(Zd1ded#e
d)eeef   d*e
deeef   f
d+Z d2ded#e
d!eeef   d*e
deeef   deeef   fd,Z!deeef   deeef   deeef   deeeef      fd-Z"d#e
deeef   fd.Z#d)eeef   d#e
deeef   fd/Z$y)3JobMatchingServicezKService for job matching with candidate screening and background processingc                 |    t               | _        t               | _        t	               | _        t               | _        y N)r   job_api_servicer   opensearch_servicer   embedderr   
ai_service)selfs    A/var/www/html/drjob-dev/drjob-ai/services/job_matching_service.py__init__zJobMatchingService.__init__   s)    ,"3"5 
#+    
job_emp_idreturnc                 
   	 t        j                  | j                  |fd      }|j                          dd| |ddS # t        $ r:}t
        j                  dt        |              dt        |      |dcY d	}~S d	}~ww xY w)
a	  
        Start background job matching process and return immediately
        
        Args:
            job_emp_id: The employer job ID to process
            
        Returns:
            Dict with success status - actual processing happens in background
        Ttargetargsdaemonz)Job matching process started for job ID: processing_started)successmessagejob_idstatusz%Error starting job matching process: Fr&   errorr(   N)	threadingThread_background_job_matchingstart	Exceptionloggerr+   str)r   r   threades       r   process_job_matching_asyncz-JobMatchingService.process_job_matching_async!   s    	%%44 ]F
 LLN  FzlS$.	   	LL@QIJ Q$ 	s   <? 	B/A=7B=Bjobseeker_idsc                     	 t        j                  | j                  ||fd      }|j                          dd| |t	        |      ddS # t
        $ r:}t        j                  dt        |              dt        |      |dcY d	}~S d	}~ww xY w)
aM  
        Start background processing for specific candidate analysis
        
        Args:
            job_emp_id: The employer job ID to process
            jobseeker_ids: List of specific jobseeker IDs to analyze
            
        Returns:
            Dict with success status - actual processing happens in background
        Tr!   z0Specific candidate analysis started for job ID: r%   )r&   r'   r(   jobseeker_countr)   z,Error starting specific candidate analysis: Fr*   N)	r,   r-   (_background_specific_candidates_analysisr/   lenr0   r1   r+   r2   )r   r   r6   r3   r4   s        r   !process_specific_candidates_asyncz4JobMatchingService.process_specific_candidates_asyncC   s    	%%DD -0F
 LLN  Mj\Z$#&}#5.   	LLGAxPQ Q$ 	s   AA
 
	B/BBBc           
      2   	 t         j                  d|        | j                  |      }|st         j                  d|        y| j	                  |      }|st         j                  d|        yd}|D ]  }	 | j                  ||      }|ri|j                  dd      dk\  r| j                  ||||       |dz  }n:t         j                  d	|j                  d
       d|j                  d       d        t         j                  d| d| d       y# t        $ r=}t         j                  d|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)z
        Background process for job matching - runs in separate thread
        
        Args:
            job_emp_id: The employer job ID to process
        z-Starting background job matching for job ID: +Could not retrieve job details for job ID: Nz)No matching candidates found for job ID: r   matching_percentage      I@r   z
Candidate 	resume_idz below 50% threshold: %zError processing candidate : z#Job matching completed for job ID: z. Processed z candidates.z,Error in background job matching for job ID )
r1   info_get_job_detailsr+   _search_candidates_generate_candidate_analysisget_store_screening_resultr0   r2   )r   r   job_details
candidatesprocessed_count	candidateanalysisr4   s           r   r.   z+JobMatchingService._background_job_matchingg   s   '	`KKG
|TU //
;KJ:,WX 00=JG
|TU  O' 	#@@iXH#<<(=qATI 88YPXZef+q0O"KK*Y]];5O4PPfgogsgs  uJ  hK  gL  LM  )N  O$ KK=j\VeUffrst	 ! LL#>y}}[?Y>ZZ\]`ab]c\d!ef  	`LLG
|SUVYZ[V\U]^__	`sO   AE +E 2E :A=D7E 	E3EE EE 	F($FFc           
         	 t         j                  d| dt        |              | j                  |      }|st         j	                  d|        yt         j                  d|        |j                  d      r!t         j                  d|d    d       d|d<   d	}d	}|D ]  }	 t        |      }| j                  |      st         j                  d
|        9|dz  }| j                  |      }|st         j                  d|        j|d	d	d	d	d	d}	| j                  ||	      }
|
r| j                  ||	|
|       |dz  } t         j                  d| d| 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}~yd}~ww xY w)z
        Background process for specific candidate analysis - runs in separate thread
        
        Args:
            job_emp_id: The employer job ID to process
            jobseeker_ids: List of specific jobseeker IDs to analyze
        z1Starting specific candidate analysis for job ID: z, candidates: r=   Nz>Type 1 specific analysis - country filtering disabled for job country_header_codezOriginal job country: z - will be ignored for type 1r   z)Could not ensure indexing for jobseeker: r   z/Could not get candidate details for jobseeker: r@   keyword_scorekeyword_percentagevector_scorevector_percentagecombined_scorez$Error processing specific candidate rB   z2Specific candidate analysis completed for job ID: z. Indexed: z, Processed: z;Error in background specific candidate analysis for job ID )r1   rC   r:   rD   r+   rG   r2   _ensure_jobseeker_indexedwarning_get_candidate_detailsrF    _store_specific_screening_resultr0   )r   r   r6   rI   rK   indexed_countjobseeker_idjobseeker_id_strcandidate_datarL   rM   r4   s               r   r9   z;JobMatchingService._background_specific_candidates_analysis   s1   >	oKKKJ<Wefijwfxeyz{ //
;KJ:,WX KKXYcXdef454[AV5W4XXuvw5912  OM - &%'*<'8$  99:JK)RScRd'ef !Q&M &*%@%@AQ%RN))XYiXj'kl  &6)*./()-.*+!I  $@@iXH==j)U]_jk'1,E&P KKLZLXcdqcrr  AP  @Q  R  S	 ! LL#G~UWX[\]X^W_!`a  	oLLVWaVbbdehijekdlmnn	osa   AF AF &4E#F 0E#F 6E# F #	F,$FF FF 	G&$GGc                 	   	 | j                   j                  |      }|j                  d      s(t        j	                  d|j                  d              y|j                  di       }t        |t              rt        j	                  d| d       yt        |t              s&t        j	                  d| dt        |       d	       yd
|v r|d
   }||j                  dd      xs: |j                  dd      xs& |j                  dd      xs |j                  dd      |j                  dd      xs: |j                  dd      xs& |j                  dd      xs |j                  dd      |j                  dd      xs& |j                  dd      xs |j                  dd      |j                  dd      |j                  dd      xs& |j                  dd      xs |j                  dd      |j                  dd      xs& |j                  dd      xs |j                  dd      |j                  dd      xs& |j                  dd      xs |j                  d d      |j                  d!      |j                  d"      xs$ |j                  d#      xs |j                  d$      dd%}|j                  d!      r	 t        t        j                  &      }t        j                  }|t        j                  j                     j!                  |'      }|d(   }|j#                  d)|d!   id*d*d*d+      }	|	rI|	j                  d,      r8|	d,   j%                         |d-<   t        j'                  d.|d-    d/|d!           nt        j)                  d0|d!           |j                  d-      rt        j'                  d3| d4|d-           nt        j)                  d5| d6       |j                  d      st        j	                  d| d7       yt        j'                  d8|        |S # t*        $ rW}
t        j	                  d1t-        |
              t        j)                  d2|j                  d!              d|d-<   Y d}
~
d}
~
ww xY w# t*        $ r.}
t        j	                  d9| d4t-        |
              Y d}
~
yd}
~
ww xY w):z
        Get job details from job API and extract required fields
        
        Args:
            job_emp_id: The employer job ID
            
        Returns:
            Dictionary with job details or None if failed
        r&   zFailed to get job details: r+   NdatazInvalid job ID z-: API returned empty list instead of job dataz: API returned z instead of dictionaryjob	job_title titlepositionjob_positionjob_descriptionjob_descdescriptionrI   
skill_listskillsrequired_skillsskill_list_arworkplace_type	work_typeremote_typejob_typeemployment_typetypecountry_namecountrylocation
country_id
company_idemployer_idemployer_user_id)r   ra   rg   ri   rl   rm   rp   rs   rv   rw   rO   )datetime_conversion)codec_optionscountry_updateidr   )r}   nameheader_coder   rO   zFound country header code: z for country_id: z%No header_code found for country_id: z#Error getting country header code: z8Could not determine country_header_code for country_id: z"Country filtering enabled for job rB   zNo country filtering for job z - will search all countriesz$: No job title found in API responsez/Successfully retrieved job details for job ID: z%Error getting job details for job ID )r   get_employer_jobrG   r1   r+   
isinstancelistdictrr   r	   r
   DATETIME_AUTOr   clientdbr~   with_optionsfind_onelowerrC   rW   r0   r2   )r   r   resultjob_datarI   r{   r   r   country_collectioncountry_docr4   s              r   rD   z#JobMatchingService._get_job_details   s   a	)):::FF::i(:6::g;N:OPQzz&"-H (D)zl:ghi h-zl/$x.IYYopq  #E? )&ll;; =$LL"5=$LLR8= %LL<%\\*;R@ ;#<<
B7;#<<r:; $<<r:'||L"=  A%\\(B7 A%\\*;R@!)or!B#+<<0@"#E $A!)k2!>$A!)mR!@%\\*b9 4#<<(92>4#<<3!)nb!A "<'||Ir:"<'||J;&ll<8'||L9  >%\\-8 >%\\*<='+7K> |,>$0EWEeEe$fM +11F 0 0 5 56CCR_C`B)+,<)=& #5"="={<89 !A>#K #{}'E=H=W=]=]=_$9:&A+NcBdAeev  xC  DP  xQ  wR  %S  T)N{[gOhNi'jk 45@B{[pOqNrst!>zlJfgh ??;/zl:^_`KKI*VW% ! >LL#Fs1vh!OPNN%]^i^m^mnz^{]|#}~9=K 56	>(  	LL@BsSTvhWX	s^   AR ;R 5R G4R =CP( A3R R (	R1AR>R RR 	S$R==SNrI   c           	         	 | j                  |      }t        j                  d| d       t        j                  d       |rft        j                  dt        |       d|        | j                  j                  |      }t        j                  d|rt        |      nd d       nA| j                  j                  |      }t        j                  d|rt        |      nd d	       t        t        j                  d
d            }|r&|D cg c]  }|j                  dd      |k\  s| c}ng }t        j                  d| dt        |              |r|D cg c]%  }|j                  d      s|j                  d      ' }}t        j                  dt        |       d       | j                  j                  ||      }	t        j                  d|	rt        |	j                  dg             nd d       | j                  ||	j                  dg       |      }
|
S t        j                  d       g S c c}w c c}w # t        $ r-}t        j                  dt!        |              g cY d}~S d}~ww xY w)aQ  
        Search for candidates without country filtering (for type 1 and type 3)
        
        Args:
            job_details: Job details dictionary
            jobseeker_ids: Optional list of specific jobseeker IDs to analyze
            
        Returns:
            List of matching candidates without country restrictions
        z/Search query built without country filtering: ''z0Country filtering disabled for type 1 and type 3Searching specific  candidates: OpenSearch returned r    specific candidates resultsJOB_MATCHING_ABSOLUTE_MIN_SCORE333333?rQ   OpenSearch results above  threshold: r@   Performing vector search on 4 pre-filtered candidates without country restrictionVector search returned r   ! results from filtered candidatesz+No candidates met minimum quality thresholdz3Error searching candidates without country filter: N_build_search_queryr1   rC   r:   r   search_by_idssearchfloatosgetenvrG   r   search_filtered_candidates_merge_and_rank_resultsr0   r+   r2   r   rI   r6   search_querykeyword_resultsabsolute_min_scorerfiltered_keyword_resultsfiltered_resume_idsvector_resultsfinal_candidatesr4   s               r   )_search_candidates_without_country_filterz<JobMatchingService._search_candidates_without_country_filterL  sw   +	33K@LKKI,WXYZ KKJK 1#m2D1E]S`Rabc"&"9"9"G"G"V2?33G`a2bbvwx #'"9"9"@"@"N2?33G`a2bbjkl "'ryy1RTW'X!Y {J?'vaaeeO]^F_cuFu'v  PR$KK34F3G|TWXpTqSrst (C[&ra_`_d_dep_qquu['9&r#&r:3?R;S:T  UI  J  K "&!I!I,Xk!l5_mc.:L:LXWY:Z6[st5u  vW  X  Y $(#?#?@XZhZlZlmuwyZz  }H  $I ''IJ	% (w 's  	LLNsSTvhWXI	O   DI H9*H9./I H>4H>BI "I 9
I 	I9"I4.I94I9c           	         	 | j                  |      }t        j                  d| d       t        j                  d       |rft        j                  dt        |       d|        | j                  j                  |      }t        j                  d|rt        |      nd d       nA| j                  j                  |      }t        j                  d|rt        |      nd d	       t        t        j                  d
d            }|r&|D cg c]  }|j                  dd      |k\  s| c}ng }t        j                  d| dt        |              |r|D cg c]%  }|j                  d      s|j                  d      ' }}t        j                  dt        |       d       | j                  j                  ||      }	t        j                  d|	rt        |	j                  dg             nd d       | j                  ||	j                  dg       |      }
|
S t        j                  d       g S c c}w c c}w # t        $ r-}t        j                  dt!        |              g cY d}~S d}~ww xY w)aX  
        Search for candidates for specific analysis without country filtering (type 1)
        
        Args:
            job_details: Job details dictionary
            jobseeker_ids: Optional list of specific jobseeker IDs to analyze
            
        Returns:
            List of matching candidates without country restrictions
        z+Search query built for specific analysis: 'r   z9Country filtering disabled for specific analysis (type 1)r   r   r   r   r   r   r   r   rQ   r   r   r@   r   r   r   r   r   zANo candidates met minimum quality threshold for specific analysisz2Error searching candidates for specific analysis: Nr   r   s               r   (_search_candidates_for_specific_analysisz;JobMatchingService._search_candidates_for_specific_analysis  sw   +	33K@LKKEl^STUV KKST 1#m2D1E]S`Rabc"&"9"9"G"G"V2?33G`a2bbvwx #'"9"9"@"@"N2?33G`a2bbjkl "'ryy1RTW'X!Y {J?'vaaeeO]^F_cuFu'v  PR$KK34F3G|TWXpTqSrst (C[&ra_`_d_dep_qquu['9&r#&r:3?R;S:T  UI  J  K "&!I!I,Xk!l5_mc.:L:LXWY:Z6[st5u  vW  X  Y $(#?#?@XZhZlZlmuwyZz  }H  $I ''_`	% (w 's  	LLMcRSfXVWI	r   c           
         	 | j                  |      }t        j                  d| d       |j                  d      }|j                  dd      }|rt        j                  d|        nt        j	                  d       |rt        j                  d| d	       |rK| j
                  j                  |||
      }t        j                  d| d| d|rt        |      nd d       nA| j
                  j                  |      }t        j                  d|rt        |      nd d       | j                  |j                  d      d|r|ng        t        t        j                  dd            }|r&|D cg c]  }|j                  dd      |k\  s| c}ng }t        j                  d| dt        |              |r:|D cg c]%  }|j                  d      s|j                  d      ' }	}t        j                  dt        |	       d       | j                  j                  ||	|      }
t        j                  d|
rt        |
j                  dg             nd dt        |	       d       | j                  |j                  d      d |
r|
j                  dg       ng        |
r|
j                  d      st        j	                  d!|j                  d              t        j	                  d"       g S t        j                  d#       dg i}
| j                  |j                  d      d g        t        j	                  d$|j                  d              g S | j                  ||
      }t        j                  d%t        |       d&|d           |S c c}w c c}w # t         $ r-}t        j#                  d't%        |              g cY d(}~S d(}~ww xY w))a  
        Search for candidates using both OpenSearch and Vector search with country filtering
        
        Args:
            job_details: Job details dictionary
            
        Returns:
            List of matching candidates with combined scores
        zSearch query built: 'r   rO   ra   rb   z0Country filtering enabled for screening type 2: z@No country_header_code found - search will include all countrieszJob title filtering enabled: 'z'' - will prioritize relevant candidates)ra   z OpenSearch with country filter (z) and job title ('z') returned r   r   r   r   
opensearch)r   search_typeresultsr   r   rQ   r   r   r@   r   z pre-filtered candidates)country_coder   r   z results from vector_searchzHVector search returned no results - stopping candidate matching for job zLBOTH OpenSearch and Vector search must return results for candidate matchingzANo candidates passed OpenSearch filtering, skipping vector searchz<No OpenSearch results - stopping candidate matching for job zFound z matching candidates for job: zError searching candidates: N)r   r1   rC   rG   rW   r   search_with_country_filterr:   r   _log_search_resultsr   r   r   r   r   _combine_and_filter_resultsr0   r+   r2   )r   rI   r   rO   ra   r   r   r   r   r   r   rJ   r4   s                r   rE   z%JobMatchingService._search_candidates  s3   Z	33K@LKK/~Q?@ #.//2G"H#R8I"NObNcdeab<YKGnop #"&"9"9"T"T '' #U #
 >?R>SSefoepp|  Ve  ~A  BQ  ~R  kl  }m  mu  v  w"&"9"9"@"@"N2?33G`a2bbjkl $$&??<8(+: %  "'ryy1RTW'X!Y {J?'vaaeeO]^F_cuFu'v  PR$KK34F3G|TWXpTqSrst (C[&ra_`_d_dep_qquu['9&r#&r:3?R;S:TTlmn "&!I!I '!4 "J "
 5_mc.:L:LXWY:Z6[st5u  vD  EH  I\  E]  D^  ^v  w  x ((*|< /@NN..x<TV )  &^-?-?-INN%mnyn}n}  K  oL  nM  $N  ONN#qrI_`"*B ((*|< / )  !]^i^m^mnz^{]|}~	 99:RTbcJKK&Z 11OP[\gPhOijka (w 'sX  	LL7Ax@AI	sV   E2N# 4NN0N# NN-DN# 9A$N# :N# 
N# #	O,"OOOra   rg   c                    |r|j                         nd}|r|j                         nd}| d|dd  g }t        fddD              r*|j                  g d       t        j	                  d       n}t        fd	d
D              r*|j                  g d       t        j	                  d       n?t        fddD              r*|j                  g d       t        j	                  d       nt        fddD              r*|j                  g d       t        j	                  d       nt        fddD              r*|j                  g d       t        j	                  d       nt        fddD              r*|j                  g d       t        j	                  d       nGt        fddD              r*|j                  g d       t        j	                  d        n	t        fd!d"D              r)|j                  g d#       t        j	                  d$       nt        fd%d&D              r)|j                  g d'       t        j	                  d(       nt        fd)d*D              r)|j                  g d+       t        j	                  d,       nRt        fd-d.D              r)|j                  g d/       t        j	                  d0       nt        j	                  d1       dj                  |      S )2a  
        Detect job category from job title and description to filter relevant candidates
        
        Args:
            job_title: Job title
            job_desc: Job description
            
        Returns:
            Category keywords string for filtering
        rb    Ni  c              3   &   K   | ]  }|v  
 y wr    .0keywordcombined_texts     r   	<genexpr>z:JobMatchingService._detect_job_category.<locals>.<genexpr>4  s       NGw-'  N   )	developer
programmersoftwarepythonjava
javascriptreactangularznode.jsphprubyzc++zc#z.netiosandroidflutterkotlinswiftbackendfrontendz
full stack	fullstackz
mobile appzweb developmentsoftware engineerdevopszcloud engineerawsazuregcp)zsoftware developmentprogrammingcodingr   r   zIT professionalz-Detected IT/Software Development job categoryc              3   &   K   | ]  }|v  
 y wr   r   r   s     r   r   z:JobMatchingService._detect_job_category.<locals>.<genexpr>9  s       BgM)  Br   )zdata scientistzdata analystmachine learningzai engineerzdeep learningnlpdata engineerzbig data	analyticstableauzpower bi
tensorflowpytorchzscikit-learn)zdata sciencezdata analysisr   r   r   z,Detected Data Science/Analytics job categoryc              3   &   K   | ]  }|v  
 y wr   r   r   s     r   r   z:JobMatchingService._detect_job_category.<locals>.<genexpr>>  s       CgM)  Cr   )salesbusiness developmentzaccount managerzsales managerzsales executivezsales representativezsales consultantzbusiness analystcrmzlead generationzclient acquisition)r   r   zaccount managementzclient relationshipzsales professionalz0Detected Sales/Business Development job categoryc              3   &   K   | ]  }|v  
 y wr   r   r   s     r   r   z:JobMatchingService._detect_job_category.<locals>.<genexpr>C  s       ygM)  yr   )	marketingdigital marketingseosemzcontent marketingzsocial mediazbrand managerzmarketing managerz
google adszfacebook adszemail marketingzgrowth hacking)r   r   zbrand managementadvertisingzmarketing professionalz1Detected Marketing/Digital Marketing job categoryc              3   &   K   | ]  }|v  
 y wr   r   r   s     r   r   z:JobMatchingService._detect_job_category.<locals>.<genexpr>H  s       PgM)  Pr   )
hrzhuman resource	recruiterrecruitmenttalent acquisitionz
hr managerzhr executivezemployee relationspayrollbenefits)zhuman resourcesr   r   zhr professionalzpeople managementz$Detected HR/Recruitment job categoryc              3   &   K   | ]  }|v  
 y wr   r   r   s     r   r   z:JobMatchingService._detect_job_category.<locals>.<genexpr>M  s       WgM)  Wr   )
accountantfinance
accountingzfinancial analystauditorbookkeepingtaxcpazchartered accountantzfinancial planningbudget)r   r   zfinancial managementauditingzfinance professionalz(Detected Finance/Accounting job categoryc              3   &   K   | ]  }|v  
 y wr   r   r   s     r   r   z:JobMatchingService._detect_job_category.<locals>.<genexpr>R  s       TgM)  Tr   )designergraphic designui/uxzui designerzux designerzweb designercreativeillustrator	photoshopfigmasketchadobe)designr  r  zcreative professionalzvisual designz%Detected Design/Creative job categoryc              3   &   K   | ]  }|v  
 y wr   r   r   s     r   r   z:JobMatchingService._detect_job_category.<locals>.<genexpr>W  s       OgM)  Or   )customer servicecustomer supporttechnical supportz	help deskzcall centerzcustomer carezsupport engineerzsupport specialist)r  r  r  zclient supportz.Detected Customer Service/Support job categoryc              3   &   K   | ]  }|v  
 y wr   r   r   s     r   r   z:JobMatchingService._detect_job_category.<locals>.<genexpr>\  s       zgM)  zr   )
operations	logisticssupply chain	warehouse	inventoryzoperations managerzlogistics managerprocurement)r  r  r  zoperations managementz*Detected Operations/Logistics job categoryc              3   &   K   | ]  }|v  
 y wr   r   r   s     r   r   z:JobMatchingService._detect_job_category.<locals>.<genexpr>a  s       rgM)  rr   )
doctornurse	physician
healthcaremedicalhospitalclinic
pharmacist	therapist	paramedic)r  r  clinicalzhealthcare professionalz(Detected Healthcare/Medical job categoryc              3   &   K   | ]  }|v  
 y wr   r   r   s     r   r   z:JobMatchingService._detect_job_category.<locals>.<genexpr>f  s       fgM)  fr   )	teacher
instructor	professoreducatortrainertutoracademic	educationteaching)r,  r-  trainingr+  r(  z(Detected Education/Teaching job categoryz7No specific job category detected - will search broadly)r   anyextendr1   rC   join)r   ra   rg   job_title_lowerjob_desc_lowercategory_keywordsr   s         @r   _detect_job_categoryz'JobMatchingService._detect_job_category"  s    09)//+b-5)2*+1^DS-A,BC   N  8M  N  N$$  &L  MKKGI   B  :A  B  B$$%xyKKFH   C  :B  C  C$$  &J  KKKJL   y  :x  y  y$$  &E  FKKKM   P  :O  P  P$$  &F  GKK>@   W  :V  W  W$$%z{KKBD   T  :S  T  T$$%tuKK?A   O  :N  O  O$$%tuKKHJ   z  :y  z  z$$%ijKKDF   r  :q  r  r$$%efKKBD   f  :e  f  f$$%bcKKBD KKQSxx)**r   c                    g }|j                  d      r|j                  |d          | j                  |j                  dd      |j                  dd            }|r|j                  |       |j                  d      r|j                  |d          |j                  d      r|d   dd }|j                  |       dj                  |      S )z
        Build search query from job details with job category detection
        
        Args:
            job_details: Job details dictionary
            
        Returns:
            Combined search query string with category-specific keywords
        ra   rb   rg   ri   N   r   )rG   appendr5  r1  )r   rI   query_partsjob_category_keywordsrg   s        r   r   z&JobMatchingService._build_search_queryo  s      ??;'{;78 !% 9 9OOK,OOJ+!
 !45 ??<({<89 ??:&":.t4Hx(xx$$r   r   r   c                    i }|rt         j                  dt        |       d       |r)t        |D cg c]  }|j	                  dd       c}      nd}t         j                  d|        |D ]M  }|j	                  d      }|j	                  dd      }|dkD  r||z  dz  nd}	|s8||t        |	d      dd|	d	||<   O t         j                  d
t        |       d       nt         j                  d       |r|j	                  dg       ng }
|
rt         j                  dt        |
       d       t        |
D cg c]  }|j	                  dd       c}      }t         j                  d|        t        t        j                  dd            }i }|
D ]  }|j	                  di       j	                  d      }|j	                  dd      }|dkD  r||z  dz  nd}||v r||   d   }t        ||z   dz  d      }||k\  rI|||   d   ||t        |d      |d	||<   t         j                  d| d| d| dt        |d       d	       t         j                  d| d| d| d       t         j                  d| d       ||k\  s|dd|t        |d      t        |d      d	||<    |}t         j                  dt        |       d        nt         j                  d!       i }t        |j                               }|j                  d" d#$       t         j                  d%t        j                  dd&       d't        |       d(       |S c c}w c c}w ))a  
        Combine pre-filtered OpenSearch and Vector search results, apply percentage threshold
        
        Args:
            keyword_results: Pre-filtered results from OpenSearch (already above absolute minimum)
            vector_results: Results from Qdrant vector search on filtered candidates
            
        Returns:
            List of filtered candidates with combined scores
        zProcessing z( OpenSearch results for reference scoresrQ   r   zMax OpenSearch score: r@   d      rP   zStored z. OpenSearch candidates for Vector DB filteringzNo OpenSearch results receivedr   z= Vector DB results - THESE WILL BE FINAL SCREENING CANDIDATESscorezMax vector score: JOB_MATCHING_MIN_PERCENTAGEr?   payloadrR   u   ✓ Candidate z: combined z% (keyword z% + vector z%)u   ✗ Rejected z% below z% thresholdzVector candidate z, not in OpenSearch - using vector score onlyu   ✓✓✓ FINAL: u>    candidates from Vector DB will proceed to screening ✓✓✓z+No vector results - clearing all candidatesc                     | d   S )NrU   r   )xs    r   <lambda>z@JobMatchingService._combine_and_filter_results.<locals>.<lambda>  s    !,<*= r   T)keyreversez#After double filtering (absolute + z50.0z%): z candidates)r1   rC   r:   maxrG   roundr   r   r   rW   r   valuessort)r   r   r   rJ   r   max_keyword_scorer   r@   r>  match_percentagevector_datamax_vector_scoremin_thresholdr   vector_match_percentagerR   combinedcandidate_lists                     r   r   z.JobMatchingService._combine_and_filter_results  s    
 KK+c/&:%;;cde]l$X1QUU?A%>$X YrsKK01B0CDE * "JJ{3	

?A6HY\]H]E,=$=#Dcd %.)..34Da.H()-.*:-Jy) KK'#j/!22`abKK89 ;In((26bKK+c+&6%77tuv"{#K!AEE'1$5#KLKK,-=,>?@!")),I4"PQM!% %"JJy"599+F	

7A.N^abNb53C+Cs*Jhi' 
*)3I)>?S)T&$&8;R&RVW%WYZ[H  =0)2-7	-B?-S2D,1167NPQ1R.67(3 nYK{8*T_`r_ss~  @E  F]  _`  @a  b  bd  %e  fmI;k(S[\i[jju$vw NN%6ykAm#no.-?)2-.23,1167NPQ1R.34KQ.O7(3=%P *JKK+C
O+<<z{|KKEFJ j//12 =tL9"))Daci:j9kkops  uC  qD  pE  EP  Q  	Rk %Y8 $Ls   M:MrL   c           	         	 | j                  |d         }|st        j                  d|d    d       y| j                  |||      }d}| j                  j                  ||d|r|j                  d      ndd	      }| j                  ||d
         }|S # t        $ r=}t        j                  d|j                  d       dt        |              Y d}~yd}~ww xY w)a9  
        Generate AI analysis for a candidate including strengths, weaknesses, and justification
        
        Args:
            job_details: Job details dictionary
            candidate: Candidate information with scores
            
        Returns:
            Analysis dictionary or None if failed
        r@   z%No database candidate data found for z!, will use direct analysis methodNtYou are an expert HR analyst. Analyze the candidate's fit for the job and provide detailed, professional assessment.  rw   	screeningquestionsystem_prompt
max_tokensrw   featurerU   z(Error generating candidate analysis for rB   )rX   r1   rC   _create_analysis_promptr   ai_responserG   _parse_ai_analysisr0   r+   r2   )	r   rI   rL   r]   promptrX  analysis_textparsed_analysisr4   s	            r   rF   z/JobMatchingService._generate_candidate_analysis  s     	!88;9OPN "CIkDZC[[|}~ 11+~yYF SM OO77+<G;??<8T# 8 M #55mYO_E`aO"" 	LLCIMMR]D^C__abefgbhaijk	s   2B AB 	C3CCr@   c                    	 t        j                  |      }|r|j                  d      set        j	                  d| d       t        |      }t        j                  d      j                  d|i      }|st        j                  d| d       yt        j                  d	      j                  d
|i      }t        t        j                  d      j                  d
|i            }t        t        j                  d      j                  d
|i            }||xs i ||g g g |j                  dd      |j                  dd      |j                  dd      |j                  dd      |j                  dd      d}t        j	                  d|        |S # t        $ r.}t        j                  d| dt        |              Y d}~yd}~ww xY w)z
        Get detailed candidate information from MongoDB
        
        Args:
            resume_id: Resume/jobseeker ID
            
        Returns:
            Candidate details or None if not found
        basic_detailszAggregation returned empty for z, trying direct queries
jobseekersr}   
Jobseeker  not found or inactiveNjobseeker_basic_detailsr[   jobseeker_employment_detailsjobseeker_education_detailsemailrb   mobile_numberrv   r)   r   indexedr   )r}   rb  employment_detailseducation_detailscertification_detailscourse_detailsproject_detailsri  rj  rv   r)   rk  z&Retrieved data via direct queries for z$Error getting candidate details for rB   )r   get_jobseeker_complete_datarG   r1   rC   intget_collectionr   rW   r   findr0   r+   r2   )	r   r@   r]   jobseeker_id_int	jobseekerrb  rl  rm  r4   s	            r   rX   z)JobMatchingService._get_candidate_details'  s   8	*FFyQN "););O)L=i[H_`a#&y>  *88FOO+,	 !NNZ	{:P#QR !. < <=V W ` `#%56!
 &*-*F*FGe*f*k*k#%56+ &"
 %))E)EFc)d)i)i#%56* %! +%2%8b*<):-/&(')&]]7B7%.]]?B%G"+--b"A'mmHa8(}}Y:" DYKPQ!! 	LL?	{"SQRVHUV	s   BF D F 	G	$GG	r]   candidate_scoresc                    	 |r|j                  di       ni }|r|j                  dg       ng }|r|j                  dg       ng }d}|rt        |t              r|dd D ]  }t        |t              r5|j                  d      r$|j                  d      r|d	|d    d
|d    dz  }Ht        |t              sY|j                  d      sk|j                  d      s}|d	|d    d
|d    dz  } d}	|rat        |t              rQ|dd D ]I  }
t        |
t              s|
j                  d      s&|
j                  d      s8|	d	|
d    d|
d    dz  }	K |r|j                  dd      nd}|r|j                  dd      nd}|r|j                  dd      nd}|r|j                  dd      nd}|r|j                  dd      nd}|r|j                  dd      nd}|r|j                  dd      nd}|j                  dd      xs |j                  dd      }d}|rt        |t              r|D ]  }t        |t              s|j                  dd      }t        |t        t
        f      r||z  }n>t        |t              r.|j                  dd      j                         r|t        |      z  }|j                  d|j                  dd            }|j                  d|j                  dd            }|j                  d|j                  dd             dd! }|d"| d
| d#| d$z  } g }|j                  d%d      }|rt        |t              r:|j                  |D cg c]  }|st        |      j                           c}       n]t        |t              rM|j                  |j                  d&      D cg c]#  }|j                         s|j                         % c}       |rt        |t              r|D ]  }t        |t              s|j                  dd      xs |j                  dd      }|r|j                  |       |j                  dd      xs |j                  dd      }t        |t              rpg }|D ]X  }t        |t              r,|j                  |D cg c]  }|st        |       c}       ?|j                  t        |             Z d'j                  |      }t        |t              s|s
|j                  d(d'      j                  d)d'      j                  d&d'      j                         }|D ]^  }|j                         }|s|d   j                         sd|v s.|j                  d*      }t!        |      dkD  sN|j                  |       `  t        t#        |            }|rd+j                  |      nd,} d-| d.|dk7  r|nd d/|j                  d0d       d'|j                  d1d       d2| d3| d4|j                  d%d       d5|j                  d6      r|j                  d6d      dd7 nd d8|  d9|r|nd: d;|	r|	nd< d=|  d>}!|!S c c}w c c}w c c}w # t$        $ rp}"t&        j)                  d?t        |"              t&        j)                  d@|        t&        j)                  dA|        t&        j)                  dB|        |"d}"~"ww xY w)Ca  
        Create prompt for AI analysis
        
        Args:
            job_details: Job requirements
            candidate_data: Candidate information
            candidate_scores: Matching scores
            
        Returns:
            Formatted prompt string
        rb  rl  rm  rb   N   rolecompanyz- z at 
designationcompany_namer=  degreeinstitutionz from ra   zN/Ari   rp   rm   rs   rg   rU   r   current_desigationcurrent_designationjob_experience_duration.rf   zNo descriptionr7  u   • z
  Responsibilities: z...


key_skills,r   ()z.,;:!?z, z#No specific technical skills listedzN
Analyze this candidate's CV against the job post below.

Job Post:
Position: z
Description: z

Candidate CV:
Name: 
first_name	last_namez
Current Designation: z
Total Experience: ~z years
Key Skills: z
Profile Summary: profile_summaryi,  zM

**CANDIDATE'S COMPLETE TECHNICAL SKILLS LIST (Extracted from entire CV):**
z

Work Experience:
z%No detailed experience data availablez

Education:
zNo education data availableu\  

**CRITICAL INSTRUCTION - READ CAREFULLY:**

Step 1: Identify the PRIMARY TECHNICAL SKILL required for this role from the job title and description.
- For "Python Developer" → Python is MANDATORY
- For "Java Developer" → Java is MANDATORY  
- For "React Developer" → React is MANDATORY
- For "Sales Manager" → Sales experience is MANDATORY
- And so on...

Step 2: Check if candidate HAS this primary technical skill in their CV.
**IMPORTANT: Search for the EXACT WORD in the "CANDIDATE'S COMPLETE TECHNICAL SKILLS LIST" provided above.**

**EXACT MATCHING RULES (MANDATORY):**
1. Search for the EXACT skill name (e.g., "Python") in the skills list
2. "Node.js" does NOT contain "Python" - they are completely different
3. "JavaScript" does NOT contain "Java" - they are completely different
4. "React.js" is NOT "React Native" - they are different
5. Job title "Node.js Developer" does NOT mean the person knows Python
6. Backend experience does NOT automatically mean Python knowledge
7. "SQL" or "PL/SQL" does NOT mean "Python" - completely different languages

**HOW TO CHECK:**
- Read the skills list: "u  "
- Search for the PRIMARY SKILL word (e.g., "Python") in this exact list
- If "Python" appears as a separate word → FOUND
- If "Python" does NOT appear → NOT FOUND (even if Node.js, JavaScript, Java, SQL appear)
- DO NOT infer, DO NOT assume, DO NOT hallucinate

Step 3: Calculate EXACT score using this formula:

**If PRIMARY SKILL MISSING:**
- Maximum score = 35%
- Calculate based on: (Transferable skills × 20%) + (Education relevance × 15%)
- Example: Good education + some relevant experience = 25-30%
- Example: No relevant background at all = 0-15%

**If PRIMARY SKILL PRESENT:**
Base Score Calculation:
A. PRIMARY SKILL presence = 40 points (mandatory)
B. Experience with PRIMARY SKILL:
   - 0-1 years = +5 points (Total: 45%)
   - 1-2 years = +10 points (Total: 50%)
   - 2-3 years = +15 points (Total: 55%)
   - 3-5 years = +20 points (Total: 60%)
   - 5-7 years = +25 points (Total: 65%)
   - 7-10 years = +30 points (Total: 70%)
   - 10+ years = +35 points (Total: 75%)

C. SECONDARY SKILLS match (calculate percentage):
   - Count how many required secondary skills candidate has
   - 0-25% match = +0 points
   - 25-50% match = +5 points
   - 50-75% match = +10 points
   - 75-100% match = +15 points

D. EDUCATION relevance:
   - Relevant degree (CS, IT, Engineering) = +5 points
   - Other degree or no degree = +0 points

E. PROJECT/WORK quality:
   - Strong relevant projects mentioned = +5 points
   - Basic/no relevant projects = +0 points

**Final Score = A + B + C + D + E** (Maximum 100%)

**CRITICAL:** Each candidate MUST get different scores based on their actual experience years and secondary skills match. DO NOT give everyone the same score!

**COMMON MISTAKES TO AVOID:**
 WRONG: "Python - FOUND (Node.js Developer)" - Node.js is NOT Python
 WRONG: "Python - FOUND (Backend Developer)" - Backend ≠ Python
 WRONG: "Python - FOUND (JavaScript, Node.js)" - Neither is Python
 CORRECT: "Python - NOT FOUND (skills: Node.js, JavaScript, React.js - NO Python)"
 CORRECT: "Python - FOUND (appears as: Python, Django, Flask)"

**SCORING RULES (MANDATORY - DO NOT VIOLATE):**
1. If PRIMARY TECHNICAL SKILL is NOT in "CANDIDATE'S COMPLETE TECHNICAL SKILLS LIST" → MAXIMUM score = 35%
2. DO NOT give credit for "transferable skills" or "similar technologies" when primary skill is missing
3. DO NOT assume candidate can learn the skill - score based on CURRENT skills only
4. Node.js experience does NOT qualify for Python roles (SQL ≠ Python, PL/SQL ≠ Python)
5. Backend experience does NOT qualify for Frontend roles (and vice versa)
6. Sales experience does NOT qualify for IT roles (and vice versa)
7. DO NOT hallucinate - if skill is not in the list, it is NOT FOUND

Compare the CV against the Job Description:
1. **PRIMARY TECHNICAL SKILL** - Does candidate have the exact skill mentioned in job title/description?
2. Secondary technical skills and tools
3. Years of relevant experience  
4. Industry/domain knowledge
5. Education and certifications

**OUTPUT FORMAT - YOU MUST USE THIS EXACT STRUCTURE:**

MATCH_SCORE: [Write only the number, e.g., 75% or 20%]

MATCH_JUSTIFICATION:
PRIMARY SKILL CHECK: [Skill Name] - [FOUND/NOT FOUND]. [Explain the score calculation in 2-3 sentences. For FOUND: Base score 40 + Experience points + Secondary skills match + Education + Projects = Final score. For NOT FOUND: Explain why max 35% and how you calculated the actual score based on transferable skills and education.]

STRENGTHS:
[Point 1 - Specific achievement or skill]
[Point 2 - Specific achievement or skill]
[Point 3 - Specific achievement or skill]
[Point 4 - Specific achievement or skill]

WEAKNESSES:
[Point 1 - If primary skill missing, write: "Missing [Skill] - the primary technical requirement for this role"]
[Point 2 - Specific gap or concern]
[Point 3 - Specific gap or concern]
[Point 4 - Specific gap or concern]

TOP_KEYWORDS:
[Keyword1, Keyword2, Keyword3, Keyword4, Keyword5]

**EXAMPLE OUTPUT FOR 80% MATCH:**
MATCH_SCORE: 80%

MATCH_JUSTIFICATION:
PRIMARY SKILL CHECK: Python - FOUND. Base score: 40 points for Python presence. Experience: 5 years of Python development = +25 points. Secondary skills: Django and Flask present (60% match) = +10 points. Education: B.Tech CS = +5 points. Strong projects mentioned = +0. Final Score: 80%.

STRENGTHS:
Strong Python development experience with 5+ years
Expertise in Django and Flask frameworks
Relevant Computer Science degree
Good understanding of web development concepts

WEAKNESSES:
Limited experience with React.js mentioned in job requirements
No AWS cloud experience mentioned
Could benefit from more DevOps knowledge
Missing Kubernetes expertise

TOP_KEYWORDS:
Python, Django, Flask, Web Development, Backend

**EXAMPLE OUTPUT FOR 25% MATCH (Primary Skill Missing):**
MATCH_SCORE: 25%

MATCH_JUSTIFICATION:
PRIMARY SKILL CHECK: PHP - NOT FOUND. The candidate has customer service and hospitality experience but lacks the primary technical requirement (PHP). Maximum possible score is 35%. Transferable skills (team leadership, customer service) = 20%. No relevant technical education = 0%. Final Score: 20%.

STRENGTHS:
Strong customer service background
Excellent team leadership abilities
Experience in upscale hospitality settings
Good communication and interpersonal skills

WEAKNESSES:
Missing PHP - the primary technical requirement for this role
No programming or technical development experience
No relevant IT or Computer Science education
Career background in hospitality, not software development

TOP_KEYWORDS:
Customer Service, Team Leadership, Hospitality, Bartending, Communication

**CRITICAL REMINDERS:**
- MATCH_SCORE must be on its own line with the header "MATCH_SCORE:"
- MATCH_JUSTIFICATION must be on its own line with the header "MATCH_JUSTIFICATION:"
- STRENGTHS, WEAKNESSES, TOP_KEYWORDS must have their section headers
- If primary skill is MISSING, maximum score is 35%
z Error creating analysis prompt: zJob details: zCandidate data: zCandidate scores: )rG   r   r   r   rr  r   r2   replaceisdigitr0  stripsplitr8  r1  isupperrstripr:   setr0   r1   r+   )#r   rI   r]   rw  rb  rl  rm  experience_textempeducation_textedura   ri   rp   rm   rs   rg   rU   candidate_current_designationtotal_experience_yearsdurationr{  r}  candidate_skills_listr  sjob_desc_text	flat_listitemsubitemwordswordcandidate_skills_strr^  r4   s#                                      r   r[  z*JobMatchingService._create_analysis_promptk  s+   m	GUN..C[]MQ_!3!34H"!MegO] 2 23F Kce !O!j1CT&J-bq1 `C!#t,SWWYEW'RF}DY@PPR+SS#C.377=3IcggVdNe'RM0B/C4NH[G\\^+__	`  N Z0A4%H,Ra0 [C!#t,1Bsww}G]&Bs8}oVCDVCWWY*ZZ[
 @KU;PUIALu=RWJ=H{z59eHIT[__-=uEZ_NEP;??>5AV[L=H{z59eH K[-112BAF`aN -:,=,=>RTV,W  -K[h[l[l  nC  EJ  \K) &'"!j1CT&J- tC!#t,#&77+Da#H%he=2h>2'#68;K;KCQS;T;\;\;^2eHoE2 #&''.#'')U:S"T&)ggmSWWVU=S&T#&77:sww?PRb7c#deifi#j'T+d7)Kabjakkr+sst" %'! '**<<Jj$/)00*1ZQXY#a&,,.1Z[
C0)00ZEUEUVYEZ1h^_^e^e^g!'')1hi "j1CT&J-  KC!#t,&)ggmR&@&WCGGFTVDW&188E ),
B(?(a377K\^`Ca &mT:(*I(5 @#-dD#9$-$4$4RV5bwZac'l5b$c$-$4$4SY$?	@
 -0HHY,?M%mS9m %2$9$9#s$C$K$KCQT$U$]$]^acf$g$m$m$oE(- K'+zz|#'T!W__->#++/;;x+@D'*4y1}(=(D(DT(JK3 KF %)-B)C$D!G\499-B#C  cH  + "e+h7 8 r*+1]->->{B-O,P Q34 5*+ ,|U34 5GTGXGXYjGk-##$5u=dsCqvw x    $)P Q R "'D E F0 // I0[vFn MI 2[1h( 6c`  	LL;CF8DELL=67LL+N+;<=LL-.>-?@AG	s   B3Y- 6Y- Y- A Y- Y- -Y- ?C>Y- >DY- Y!Y<6Y- 2Y#Y#1Y- BY- !Y()Y(6AY- =Y- AY- Y- 1Y- CY- Y- -	[&6A+[!![&r_  fallback_percentagec                 d   	 g g |dg d}|j                  d      }|D ]  }|j                         }|j                  d      r|j                  dd      j                         }|j                  d      D cg c]  }|j                         s|j                         j                  d      r4|j                         j	                  d      rT|j                         j                  d      j                          }}||d	<   |j                  d
      r|j                  d
d      j                         }	|	j                  d      D 
cg c]  }
|
j                         s|
j                         j                  d      r4|
j                         j	                  d      rT|
j                         j                  d      j                          }}
||d<   |j                  d      s|j                  d      rq|j                  dd      j                  dd      j                         }ddl}|j                  d|      }|s#t        dt        dt        |d                     |d<   J|j                  d      r%|j                  dd      j                         |d<   |j                  d      s|j                  dd      j                         }d|v r[|j                  d      D cg c]@  }|j                         s|j                         j                  d      j                         B }}n|j                  d      D cg c]  }|j                         s|j                         j                  d      r4|j                         j	                  d      rT|j                         j                  d      j                          }}|r|dd ng |d<    |d   s|j                  d      D ]  }|j                         }|j                  d      s%|j                  dd      j                         }d|v rf|j                  d      D cg c]@  }|j                         s|j                         j                  d      j                         B }}|r|dd ng |d<    n |d	   rB|d   r=|d   r8t        |d	         dk(  s't        |d         dk(  s|d   j                         dk(  ryt        j                  d       t        j                  dt        |d	          dt        |d          d|d   j                         sdnd        t        j                  d |        y|S c c}w c c}
w c c}w c c}w c c}w # t        $ r+}t        j                  d!t!        |              Y d}~yd}~ww xY w)"a  
        Parse AI analysis response into structured data
        
        Args:
            analysis_text: Raw AI response
            fallback_percentage: Fallback percentage if parsing fails
            
        Returns:
            Structured analysis dictionary
        rb   )	strengths
weaknessesr>   match_justificationtop_keywordsz

z
STRENGTHS:r|  []u   -•[]r  zWEAKNESSES:r  zMATCH_SCORE:zMATCHING_PERCENTAGE:r   Nz	\d+\.?\d*g      Y@g        r>   zMATCH_JUSTIFICATION:r  zTOP_KEYWORDS:r     r  z+AI analysis incomplete - skipping candidatez!Parsed analysis state: strengths=z items, weaknesses=z items, match_justification=emptypresentzAnalysis content: zError parsing AI analysis: )r  r  
startswithr  endswithlstriprefindallminrF  r   r:   r1   rW   r0   r+   r2   )r   r_  r  rM   sectionssectionstrengths_textr  strengths_listweaknesses_textwweaknesses_listpercentage_textr  numberskeywords_textkkeywordsliner4   s                       r   r]  z%JobMatchingService._parse_ai_analysis  s>   M	 ':') "H %**62H# "P!--/%%l3%,__\2%F%L%L%NNR`RfRfgkRl  &}Qpqpwpwpy  CD  CJ  CJ  CL  CW  CW  X[  C\  ef  el  el  en  ew  ew  x{  e|aggi&6&6x&@&F&F&H  &}N  &},:H[)''6&-oomR&H&N&N&POSbShShimSn  'arsryryr{  EF  EL  EL  EN  EY  EY  Z]  E^  gh  gn  gn  gp  gy  gy  z}  g~qwwy'7'7'A'G'G'I  'O  '-<H\*''77;M;MNd;e&-oonb&I&Q&QRhjl&m&s&s&uO jjGG:=eSeT[\]T^N_E`:a!67''(>?6=ooF\^`6a6g6g6iH23''8$+OOOR$H$N$N$PMm+P]PcPcdgPh#v1lmlslsluAGGI$4$4X$>$D$D$F#v#v Q^PcPcdhPi  $z1mnmtmtmv  @A  @G  @G  @I  @T  @T  UX  @Y  bc  bi  bi  bk  bt  bt  ux  byAGGI$4$4X$>$D$D$F  $z  $z @Hx|RH^,E"PJ N+)//5 D::<D7(,_b(I(O(O(Q-/TaTgTghkTl'zqpqpwpwpy	(8(8(B(H(H(J'zH'zGOx|UWH^4 [)\*23H[)*a/H\*+q0./5572=LM!B3xP[G\C]B^^qruv~  @L  wM  sN  rO  Ok  {C  DY  {Z  {`  {`  {b  ls  hq  kr   s  t!3H:>?Om &}
 '" $w $z ({$  	LL6s1vh?@	s   A3U; 5U"U"+U"/U":AU; U'U'<U'/U'A3U;  A.U; 07U; 'U,=/U,,U; U1U18U1/U1AU; 7U; 
U6 /U6CU;  U; "U; ;	V/!V**V/rM   c                 H   	 ddl m}  |       }|d   }|j                  |t        |d         dd      }|r|j	                  dd      }	t        |d   d      }
t        |	|
z
        dkD  rnt        j                  d|d    d	|	 d
|
 d       |j                  d|d   id|
|d   |d   |d   |j	                  dg       t        j                         di       yt        j                  d|d    d| d       y| j                  |d         }g }|r-|j	                  di       }|j	                  dg       }|j	                  dg       }|j	                  dg       }|j	                  dg       }|j	                  dg       }|j	                  dg       }|j	                  dg       }|j	                  dg       }|j	                  d g       }|j	                  d!g       }|j	                  d"g       }g }|r1i d#|j	                  d$d%       d&|j	                  d'd%       j                         d$|j	                  d$d%      d'|j	                  d'd%      d(|j	                  d(d%      xs |j	                  d(d%      d)|j	                  d*d%      xs |j	                  d)d%      d*|j	                  d*d%      d+|j	                  d+d%      d,|j	                  d,d%      d-|j	                  d-d%      d.|j	                  d.d%      d/|j	                  d/d%      d0|j	                  d1d%      xs |j	                  d0d%      d2|j	                  d2d%      d3|j	                  d3d%      d4|j	                  d4d%      d5|j	                  d3d%       d&|j	                  d4d%       j                         d6|j	                  d6d%      i d7|j	                  d7d%      d8|j	                  d8d%      d9|j	                  d9      r!|j	                  d9d%      j                  d:      ng d;|j	                  d;g       d<|j	                  d<d%      d=|j	                  d=d%      d>|j	                  d>d%      d?|j	                  d?d%      d@|j	                  d@d%      dA|j	                  dAd%      dB|j	                  dBd%      dC|j	                  dCd%      dD|j	                  dDd%      dE|j	                  dEd%      dF|j	                  dFd%      dG|j	                  dGd%      dH|j	                  dHd%      i dI|j	                  dId%      dJ|j	                  dJd%      dK|j	                  dKd%      dL|j	                  dLd%      dM|j	                  dMd%      dN|j	                  dNd%      dO|j	                  dOd%      dP|j	                  dPd%      dQ|j	                  dQd%      dR|j	                  dRd%      dS|j	                  dSd%      dT|j	                  dTd%      dU|j	                  dUg       dV|j	                  dVd%      dW|j	                  dWd%      dX|j	                  dXd%      dY|j	                  dYd%      |j	                  dZd%      |j	                  d[d%      |j	                  d\d%      |j	                  d]d%      |j	                  d^d%      |j	                  d_d%      |j	                  d`d%      |j	                  dad%      |j	                  dbd%      |j	                  dcd%      dd
}|j                  |       i d|de|r|ng df|r|ng dg|r|ng dh|r|ng di|r|ng dj|r|ng dk|r|ng dl|r|ng dm|r|ng dn|r|ng do|r|ng d(|j	                  d(d%      d*|j	                  d*d%      dp|j	                  dpd%      dq|j	                  dqd%      d_|j	                  d_d%      |j	                  drd%      |j	                  dFd%      |j	                  dsd      |j!                         D ci c]  \  }}|dtvr|| c}}du}|j                  |       |t        |d         ddv|d   |d   t        |d   d      |d   |j	                  dg       |||r|j	                  dw      ndxt        j                         t        j                         dy}|j#                  |      }t        j                  dz|d    d|        yc c}}w # t$        $ r+} t        j'                  d{t)        |               Y dx} ~ y|dx} ~ ww xY w)}aE  
        Store screening result in MongoDB screenings collection
        Check for duplicates before inserting
        
        Args:
            job_emp_id: Job employer ID
            candidate: Candidate information
            analysis: AI analysis result
            job_details: Complete job details dictionary
        r   get_database
screeningsr@   r=  r(   r[   rr   r>   z!Updating screening for candidate  - score changed from % to rA   _id$setr  r  r  r  r>   r  r  r  r  
updated_atz'Screening already exists for candidate 	 and job &, skipping (score within 2% threshold)Trb  rl  rm  rn  ro  rp  language_detailshobby_detailsreference_detailsportfolio_detailssocial_media_detailspublic_profilesr~   r  rb   r   r  ri  phonerj  
birth_date	gender_idmarital_statusnationality_idreligion_idr  r  current_companytotal_experience_yeartotal_experience_monthtotal_experienceindustry_idfunctional_area_idprofile_type_idr  r  key_skills_idr  cv_headlinecurrent_salarycurrent_salary_periodcurrent_salary_currencyexpected_salaryexpected_salary_periodexpected_salary_currencycurrent_locationprefered_locationrv   com_addressvisa_status_idvisa_residence_locationvisa_validity_monthvisa_validity_yearlicense_optprofile_completionprofile_updated_datesystem_original_resumesystem_resume_fileoriginal_file_nameresume_fileresume_added_dateprofile_picknown_language_idknown_languagescv_push
cv_push_idcv_download_countapplied_job_countconnection_countprofile_uploadresume_uploades_pushr)   
is_deleted
created_atr  r}   
r  r  r   r  r  r)   r  r  r  r}   
experiencer,  certificationscoursesprojects	languageshobbies
references	portfoliosocial_mediajobseeker_public_profilesrs   rO   rk  is_subscribedrb  rl  rm  rn  ro  rp  r  r  r  r  r  rk  rv   r  additional_infor   rw   Nr(   r[   rr   r)   r  r  r>   r  r  jobseeker_detailsrI   rw   r  processed_atz7Successfully stored new screening result for candidate z Error storing screening result: Fservices.databaser  r   rr  rG   rG  absr1   rC   
update_oner   utcnowrX   r  r  r8  items
insert_oner0   r+   r2   !r   r   rL   rM   rI   r  r   screenings_collectionexisting_screeningexisting_percentagenew_percentager]   r  rb  rl  rm  rn  ro  rp  r  r  r  r  r  r  basic_details_arraybasic_detailr  vjobseeker_detailscreening_datar   r4   s!                                    r   rH   z*JobMatchingService._store_screening_result   s   N	6B$&|$4! "7!?!?$ #Ik$: ;A " " '9&<&<=RTU&V#!&x0E'F!J*^;<q@KK"CIkDZC[[q  sF  rG  GL  M[  L\  \]  !^  _)44 25 9:"7E-5k-B.6|.D7?@U7V08^R0P.6oo.?%	  KK"I)T_J`Iaajkujv  w]  !^  _ "88;9OPN " . 2 2?B G%3%7%78Lb%Q"$2$6$67JB$O!(6(:(:;RTV(W%!/!3!34Db!I"0"4"45F"K#1#5#56H"#M  . 2 2?B G$2$6$67JB$O!$2$6$67JB$O!'5'9'9:PRT'U$"0"4"45F"K ')# O$=#4#4\2#F"GqIZIZ[fhjIkHl m s s uO$ %m&7&7b&IO$ $]%6%6{B%G	O$
  !3!3GR!@!bMDUDUV]_aDbO$  !3!3OR!H!jML]L]^egiLjO$ (););OR)PO$ %m&7&7b&IO$ $]%6%6{B%GO$ )-*;*;<Lb*QO$ )-*;*;<Lb*QO$ &}'8'8'KO$ .}/@/@AUWY/Z  0K^k^o^o  qF  HJ  _KO$  *=+<+<=NPR+S!O$" 01B1BCZ\^1_#O$$ 1-2C2CD\^`2a%O$& +}/@/@AXZ\/].^^_`m`q`q  sK  MO  aP  `Q  -R  -X  -X  -Z'O$( &}'8'8'K)O$* -m.?.?@TVX.Y+O$, *=+<+<=NPR+S-O$2 %XeXiXijvXwm&7&7b&I&O&OPS&T}3O$4 ():):?B)O5O$6 *=+<+<=NPR+S7O$8 &}'8'8'K9O$> )-*;*;<Lb*Q?O$@ 01B1BCZ\^1_AO$B 2=3D3DE^`b3cCO$D *=+<+<=NPR+SEO$F 1-2C2CD\^`2aGO$H 3M4E4EF`bd4eIO$N +M,=,=>PRT,UOO$P ,]->->?RTV-WQO$R %m&7&7b&ISO$T &}'8'8'KUO$Z )-*;*;<Lb*Q[O$\ 2=3D3DE^`b3c]O$^ .}/@/@AVXZ/[_O$` -m.?.?@TVX.YaO$b &}'8'8'KcO$h -m.?.?@TVX.YiO$j /0A0ABXZ\0]kO$l 1-2C2CD\^`2amO$n -m.?.?@TVX.YoO$p -m.?.?@TVX.YqO$r &}'8'8'KsO$t ,]->->?RTV-WuO$v &}'8'8'KwO$| ,]->->?RTV-W}O$~ *=+<+<=NPR+SO$D "=#4#4Y#CEO$F %m&7&7b&IGO$H ,]->->?RTV-WIO$J .;->->?RTV-W,9,=,=>PRT,U*7*;*;<Lb*Q)6):):?B)O#0#4#4Y#C"/"3"3Hb"A&3&7&7b&I&3&7&7b&I&3&7&7b&I+//b9]O$L` (..|< $#%8 $ 8J"4PR $  6G!2R $ %?T&;Z\	 $
 ~R $ ? $  5E!12 $ }2 $ !7H"3b $  6G!2R $ #<P$8VX $ 0OY[ $ ^//< $  $^%7%7%L! $" #N$6$6~r$J# $$ *>+=+=>SUW+X% $& n002>' $(  .11)R@"0"4"4\2"F%3%7%7%K *8)=)=)?(!%A %P P 1(3 $ B "(()9: % #Ik$: ;%k2&|4',X6K-La'P'/0E'F (^R @%6*?Jkool;PT&oo/ ( 1N$ +55nEFKKQR[\gRhQiirs}r~  AA(D  	LL;CF8DE	8   Cc- c- 7\c- c'%Cc- 'c- -	d!6!dd!r[   c           	         	 t        |      }t        j                  d      j                  |dd      }|st        j                  d| d       y|j                  d      dk(  rt        j                  d| d       y	| j                  j                  |      }|rt        j                  d
|        y	t        j                  d|        y# t        $ r.}t        j                  d| dt        |              Y d}~yd}~ww xY w)z
        Ensure jobseeker is indexed in OpenSearch, index if needed
        
        Args:
            jobseeker_id: Jobseeker ID to check/index
            
        Returns:
            bool: True if indexed successfully, False otherwise
        rc  r   )r}   r)   rd  re  Frk  z already indexedTzSuccessfully indexed jobseeker zFailed to index jobseeker zError ensuring jobseeker z is indexed: N)rr  r   rs  r   r1   rW   rG   rC   r   
add_resumer+   r0   r2   )r   r[   ru  rv  r&   r4   s         r   rV   z,JobMatchingService._ensure_jobseeker_indexed  s    	"<0%44\BKK'15I L>9OPQ }}Y'1,j6FGH --88FG=l^LM9,HI 	LL4\N-PSTUPVxXY	s*   AC -C >5C 4C 	D$C??Dc                 H   	 ddl m}  |       }|d   }|j                  |t        |d         dd      }|r|j	                  dd      }	t        |d   d      }
t        |	|
z
        dkD  rnt        j                  d	|d    d
|	 d|
 d       |j                  d|d   id|
|d   |d   |d   |j	                  dg       t        j                         di       yt        j                  d|d    d| d       y| j                  |d         }g }|r-|j	                  di       }|j	                  dg       }|j	                  dg       }|j	                  dg       }|j	                  dg       }|j	                  dg       }|j	                  dg       }|j	                  dg       }|j	                  d g       }|j	                  d!g       }|j	                  d"g       }|j	                  d#g       }g }|r1i d$|j	                  d%d&       d'|j	                  d(d&       j                         d%|j	                  d%d&      d(|j	                  d(d&      d)|j	                  d)d&      xs |j	                  d)d&      d*|j	                  d+d&      xs |j	                  d*d&      d+|j	                  d+d&      d,|j	                  d,d&      d-|j	                  d-d&      d.|j	                  d.d&      d/|j	                  d/d&      d0|j	                  d0d&      d1|j	                  d2d&      xs |j	                  d1d&      d3|j	                  d3d&      d4|j	                  d4d&      d5|j	                  d5d&      d6|j	                  d4d&       d'|j	                  d5d&       j                         d7|j	                  d7d&      i d8|j	                  d8d&      d9|j	                  d9d&      d:|j	                  d:      r!|j	                  d:d&      j                  d;      ng d<|j	                  d<g       d=|j	                  d=d&      d>|j	                  d>d&      d?|j	                  d?d&      d@|j	                  d@d&      dA|j	                  dAd&      dB|j	                  dBd&      dC|j	                  dCd&      dD|j	                  dDd&      dE|j	                  dEd&      dF|j	                  dFd&      dG|j	                  dGd&      dH|j	                  dHd&      dI|j	                  dId&      i dJ|j	                  dJd&      dK|j	                  dKd&      dL|j	                  dLd&      dM|j	                  dMd&      dN|j	                  dNd&      dO|j	                  dOd&      dP|j	                  dPd&      dQ|j	                  dQd&      dR|j	                  dRd&      dS|j	                  dSd&      dT|j	                  dTd&      dU|j	                  dUd&      dV|j	                  dVg       dW|j	                  dWd&      dX|j	                  dXd&      dY|j	                  dYd&      dZ|j	                  dZd&      |j	                  d[d&      |j	                  d\d&      |j	                  d]d&      |j	                  d^d&      |j	                  d_d&      |j	                  d`d&      |j	                  dad&      |j	                  dbd&      |j	                  dcd&      |j	                  ddd&      de
}|j                  |       i d|df|r|ng dg|r|ng dh|r|ng di|r|ng dj|r|ng dk|r|ng dl|r|ng dm|r|ng dn|r|ng do|r|ng dp|r|ng d)|j	                  d)d&      d+|j	                  d+d&      dq|j	                  dqd&      dr|j	                  drd&      d`|j	                  d`d&      |j	                  dsd&      |j	                  dGd&      |j	                  dtd      |j!                         D ci c]  \  }}|duvr|| c}}dv}|j                  |       |t        |d         dd|d   |d   t        |d   d      |d   |j	                  dg       |||r|j	                  dw      ndxt        j                         t        j                         dy}|j#                  |      }t        j                  dz|d    d|        yc c}}w # t$        $ r+} t        j'                  d{t)        |               Y dx} ~ y|dx} ~ ww xY w)}a  
        Store screening result for specific candidate analysis with type=1
        Check for duplicates before inserting
        
        Args:
            job_emp_id: Job employer ID
            candidate: Candidate information
            analysis: AI analysis result
        r   r  r  r@   r   r  r>   r=  z(Updating type 1 screening for candidate r  r  rA   r  r  r  r  r  r  r  z.Type 1 screening already exists for candidate r  r  Trb  rl  rm  rn  ro  rp  r  r  r  r  r  r  r~   r  rb   r   r  ri  r  rj  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  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  r  r	  r
  r  r  r  r  r  rs   rO   rk  r  r  r  rw   Nr  z<Successfully stored specific screening result for candidate z)Error storing specific screening result: Fr  r  s!                                    r   rY   z3JobMatchingService._store_specific_screening_result  s   N	6B$&|$4! "7!?!?$ #Ik$: ;A " " '9&<&<=RTU&V#!&x0E'F!J*^;<q@KK"J9U`KaJbbx  zM  yN  NS  Tb  Sc  cd  !e  f)44 25 9:"7E-5k-B.6|.D7?@U7V08^R0P.6oo.?%	  KK"PQZ[fQgPhhqr|q}  ~d  !e  f "88;9OPN " . 2 2?B G%3%7%78Lb%Q"$2$6$67JB$O!(6(:(:;RTV(W%!/!3!34Db!I"0"4"45F"K#1#5#56H"#M  . 2 2?B G$2$6$67JB$O!$2$6$67JB$O!'5'9'9:PRT'U$"0"4"45F"K ')# O$=#4#4\2#F"GqIZIZ[fhjIkHl m s s uO$ %m&7&7b&IO$ $]%6%6{B%G	O$
  !3!3GR!@!bMDUDUV]_aDbO$  !3!3OR!H!jML]L]^egiLjO$ (););OR)PO$ %m&7&7b&IO$ $]%6%6{B%GO$ )-*;*;<Lb*QO$ )-*;*;<Lb*QO$ &}'8'8'KO$ .}/@/@AUWY/Z  0K^k^o^o  qF  HJ  _KO$  *=+<+<=NPR+S!O$" 01B1BCZ\^1_#O$$ 1-2C2CD\^`2a%O$& +}/@/@AXZ\/].^^_`m`q`q  sK  MO  aP  `Q  -R  -X  -X  -Z'O$( &}'8'8'K)O$* -m.?.?@TVX.Y+O$, *=+<+<=NPR+S-O$2 %XeXiXijvXwm&7&7b&I&O&OPS&T}3O$4 ():):?B)O5O$6 *=+<+<=NPR+S7O$8 &}'8'8'K9O$> )-*;*;<Lb*Q?O$@ 01B1BCZ\^1_AO$B 2=3D3DE^`b3cCO$D *=+<+<=NPR+SEO$F 1-2C2CD\^`2aGO$H 3M4E4EF`bd4eIO$N +M,=,=>PRT,UOO$P ,]->->?RTV-WQO$R %m&7&7b&ISO$T &}'8'8'KUO$Z )-*;*;<Lb*Q[O$\ 2=3D3DE^`b3c]O$^ .}/@/@AVXZ/[_O$` -m.?.?@TVX.YaO$b &}'8'8'KcO$h -m.?.?@TVX.YiO$j /0A0ABXZ\0]kO$l 1-2C2CD\^`2amO$n -m.?.?@TVX.YoO$p -m.?.?@TVX.YqO$r &}'8'8'KsO$t ,]->->?RTV-WuO$v &}'8'8'KwO$| ,]->->?RTV-W}O$~ *=+<+<=NPR+SO$D "=#4#4Y#CEO$F %m&7&7b&IGO$H ,]->->?RTV-WIO$J .;->->?RTV-W,9,=,=>PRT,U*7*;*;<Lb*Q)6):):?B)O#0#4#4Y#C"/"3"3Hb"A&3&7&7b&I&3&7&7b&I&3&7&7b&I+//b9]O$L` (..|< $#%8 $ 8J"4PR $  6G!2R $ %?T&;Z\	 $
 ~R $ ? $  5E!12 $ }2 $ !7H"3b $  6G!2R $ #<P$8VX $ 0OY[ $ ^//< $  $^%7%7%L! $" #N$6$6~r$J# $$ *>+=+=>SUW+X% $& n002>' $(  .11)R@"0"4"4\2"F%3%7%7%K *8)=)=)?(!%A %P P 1(3 $ B "(()9: % #Ik$: ;%k2&|4',X6K-La'P'/0E'F (^R @%6*?Jkool;PT&oo/ ( 1N$ +55nEFKKVW`alWmVnnw  yC  xD  E  FA(D  	LLDSVHMN	r(  r   r   c           
      P   	 ddl m}  |       }|d   }|||t        |      t        j                         d}|j                  |       t        j                  d| d| dt        |       d       y
# t        $ r+}t        j                  d	t        |              Y d
}~y
d
}~ww xY w)z
        Log search results to ai_search_logs collection
        
        Args:
            job_emp_id: Job employer ID
            search_type: Type of search ("opensearch" or "vector_search")
            results: Array of search results
        r   r  ai_search_logs)r   rr   r   total_resultsr  zLogged z results for job rB   r   zError logging search results: N)r  r  r:   r   r  r  r1   rC   r0   r+   r2   )	r   r   r   r   r  r   ai_search_logs_collectionlog_datar4   s	            r   r   z&JobMatchingService._log_search_results  s    	6B(*+;(<% )#"!$W&oo/H &00:KK'+.?
|2cRYl^[cde 	LL9#a&BC	s   A.A1 1	B%:!B  B%parsed_dataanalysis_typec                 L   	 | j                  |      }|sdd| dS t        |      dddddd}| j                  |      }|s+t        j	                  d| d       | j                  ||      }t        j                  d|        | j                  |||      }|rB| j                  ||||||	      }	|	r$t        j                  d
| d| d|        d||dS dddS dddS # t        $ r9}
t        j                  dt        |
              dt        |
      dcY d}
~
S d}
~
ww xY w)a  
        Analyze a single candidate for upload with AI analysis and store with specified type
        
        Args:
            job_emp_id: The employer job ID
            jobseeker_id: The jobseeker ID from jobseekers_bulk_upload_cv.id
            parsed_data: Parsed resume data
            analysis_type: Type of analysis (3 for upload analysis)
            
        Returns:
            Dict: Analysis result with success status
        FzFailed to get job details for )r&   r+   r   rP   z6Could not fetch complete candidate data for jobseeker z, using parsed data onlyz2Using direct analysis method for upload candidate )r   r[   rM   r2  r]   rI   z2Successfully analyzed and stored upload candidate z	 for job  with type T)r&   r[   rM   zFailed to store analysis resultzFailed to generate AI analysisz-Error analyzing single candidate for upload: N)rD   r2   (_get_complete_bulk_upload_candidate_datar1   rW   _format_parsed_data_for_storagerC   #_generate_candidate_analysis_direct_store_upload_screening_resultr0   r+   )r   r   r[   r1  r2  rI   rL   complete_candidate_dataanalysis_resultstorage_successr4   s              r   #analyze_single_candidate_for_uploadz6JobMatchingService.analyze_single_candidate_for_upload  s   B	//
;K$=j\J  !.!"&' !%&"#I '+&S&ST`&a#*!WXdWee}~*.*N*N{\h*i' KKL\N[\"FF{TkmvwO"&"E"E)!-,"/#: + #F # #KK"TUaTbbklvkw  xC  DQ  CR  !S  T#'(4$3  $)!B   %= 
  	LLHQQR Q 	s/   C! B9C! C! C! !	D#*.DD#D#c                    	 ddl m}  |       }|d   }	|	j                  |||d      }
|
r t        j	                  d| d| d| d       y	g }|r|d
k(  r|j                  di       }|j                  dg       }|j                  dg       }|j                  dg       }|j                  dg       }|j                  dg       }|j                  dg       }|j                  dg       }|j                  dg       }|j                  dg       }|j                  dg       }d}d}	 ddl m}  |       }|d   j                  d|i      }|rC|j                  dd      }|j                  dd      }t        j	                  d| d| d|        nt        j                  d|        |s&|j                  dd      xs |j                  dd      }|s&|j                  dd      xs |j                  d"d      }g }|s|rL|r|j                  d#d      nd}|r|j                  d$d      nd}|s0|s-|r*|j                  d%i       }|j                  d&d      }|r|j                  d'      s|j                  d(d      j                  d)d      j                  d*d      j                  d+d,      j                  d-d,      j                         } | j                         }!t        |!      d.k\  r|!d   }d,j                  |!d/d!       }|j                  di       }"|s#|"j                  d#      r|"j                  d#d      }|s#|"j                  d$      r|"j                  d$d      }|}#|}$|#s&|r$|j                  di       }"|"j                  dd      }#|$s&|r$|j                  di       }"|"j                  d"d      }$i d0| d,| j                         d#|d$|d|#d"|$d|$d1|j                  d1d      d2|j                  d2d      d3|j                  d3d      d4|j                  d4d      d5|j                  d5d      d6|j                  d7d      xs |j                  d6d      d8|j                  d8d      d9|j                  d9d      d:|j                  d:d      d;|j                  d9d       d,|j                  d:d       j                         d<|j                  d<d      i d=|j                  d=d      d>|j                  d>d      d?|j                  d?      r!|j                  d?d      j                  d@      ng dA|j                  dAg       dB|j                  dBd      dC|j                  dCd      dD|j                  dDd      dE|j                  dEd      dF|j                  dFd      dG|j                  dGd      dH|j                  dHd      dI|j                  dId      dJ|j                  dJd      dK|j                  dKd      dL|j                  dLd      dM|j                  dMd      dN|j                  dNd      i dO|j                  dOd      dP|j                  dPd      dQ|j                  dQd      dR|j                  dRd      dS|j                  dSd      dT|j                  dTd      dU|j                  dUd      dV|j                  dVd      dW|j                  dWd      dX|j                  dXd      dY|j                  dYd      dZ|j                  dZd      d[|j                  d[g       d\|j                  d\d      d]|j                  d]d      d^|j                  d^d      d_|j                  d_d      |j                  d`d      |j                  dad      |j                  dbd      |j                  dcd      |j                  ddd      |j                  ded      |j                  dfd      |j                  dgd      |j                  dhd      |j                  dd      di
}%|j!                  |%       i d|dj|r|ng dk|r|ng dl|r|ng dm|r|ng dn|r|ng do|r|ng dp|r|ng dq|r|ng dr|r|ng ds|r|ng dtg d#d$du|j                  dud      dv|j                  dvd      de|j                  ded      |j                  dwd      |j                  dxd      |j                  dLd      |j#                         D &'ci c]  \  }&}'|&dyvr|&|' c}'}&dz}(|j!                  |(       |||d/|d{   |d|   t%        |d}   d.      |d~   |j                  dg       ||r|j                  d      nd!t'        j(                         t'        j(                         d})|d
k(  r|r||)d<   n
|d
k(  rg |)d<   |	j+                  |)      }*t        j	                  d| d| d|        y	# t        $ r/}t        j                  d| d t        |              Y d!}~d!}~ww xY wc c}'}&w # t        $ r+}t        j                  dt        |              Y d!}~yd!}~ww xY w)a  
        Store screening result for upload analysis with jobseeker_details for type 3
        Check for duplicates before inserting
        
        Args:
            job_emp_id: Job employer ID
            jobseeker_id: Jobseeker ID from bulk upload collection
            analysis: AI analysis result
            analysis_type: Type of analysis (3 for upload)
            candidate_data: Candidate data to store in jobseeker_details array
        r   r  r  r  z.Upload screening already exists for candidate r  r4  z, skipping insertTry  rb  rl  rm  rn  ro  rp  r  r  r  r  r  rb   jobseekers_bulk_upload_cvr}   ri  rj  z%Found bulk upload data for jobseeker z: email=z	, mobile=z(No bulk upload data found for jobseeker z-Error getting bulk upload data for jobseeker rB   Nr  r  r  r  cv_filenamezuploads/.pdfz.docz.docx_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  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  r	  r
  r  r  r  r  r  rs   rO   r  rk  r  )r  rk  rv   r  r  r  r>   r  r  rw   )r(   r[   rr   r)   r  r  r>   r  r  rI   rw   r  r  r  z:Successfully stored upload screening result for candidate z'Error storing upload screening result: F)r  r  r   r1   rC   rG   rW   r0   r+   r2   r  r  r  r  r:   r1  r8  r  rG  r   r  r  )+r   r   r[   rM   r2  r]   rI   r  r   r  r   r  rb  rl  rm  rn  ro  rp  r  r  r  r  r  ri  mobilebulk_upload_datar4   r#  r  r  r  r?  name_from_file
name_partsparsed_basicfinal_emailfinal_mobiler$  r  r%  r&  r'  r   s+                                              r   r8  z1JobMatchingService._store_upload_screening_resultN  s   u	6B$&|$4! "7!?!?$ ,%A " "L\NZcdncooz  |I  {J  J[  \  ] !#-1"4 . 2 2?B G%3%7%78Lb%Q"$2$6$67JB$O!(6(:(:;RTV(W%!/!3!34Db!I"0"4"45F"K#1#5#56H"#M  . 2 2?B G$2$6$67JB$O!$2$6$67JB$O!'5'9'9:PRT'U$ k>%B')*E'F'O'OQUWcPd'e$' 0 4 4Wb A!1!5!5or!J&KL>Yabgahhqrxqy$z{)QR^Q_'`a
 *..w;]}?P?PQXZ\?]E+//DfHYHYZaceHfF ')# NHU!2!2<!D[]JFS 1 1+r BY[I &iN*8*<*<=NPR*S&5&9&9-&L '{/E/Ej/Q-8-@-@-L-T-TU[]_-`-h-hiprt-u-}-}  B  DG  .H  .P  .P  QT  VY  .Z  .`  .`  .bN)7)=)=)?J":!3-7]
,/HHZ^,D	 (6'9'9/2'N)l.>.>|.L)5)9)9,)KJ(\-=-=k-J(4(8(8b(II #(K#)L&>'5'9'9/2'N&2&6&6w&C'N'5'9'9/2'N'3'7'7'DO$:,a	{ ; A A CO$ %jO$ $Y	O$
  O$  O$ (O$ %m&7&7b&IO$ $]%6%6{B%GO$ )-*;*;<Lb*QO$ )-*;*;<Lb*QO$ &}'8'8'KO$ .}/@/@AUWY/Z  0K^k^o^o  qF  HJ  _KO$  *=+<+<=NPR+S!O$" 01B1BCZ\^1_#O$$ 1-2C2CD\^`2a%O$& +}/@/@AXZ\/].^^_`m`q`q  sK  MO  aP  `Q  -R  -X  -X  -Z'O$( &}'8'8'K)O$* -m.?.?@TVX.Y+O$, *=+<+<=NPR+S-O$2 %XeXiXijvXwm&7&7b&I&O&OPS&T}3O$4 ():):?B)O5O$6 *=+<+<=NPR+S7O$8 &}'8'8'K9O$> )-*;*;<Lb*Q?O$@ 01B1BCZ\^1_AO$B 2=3D3DE^`b3cCO$D *=+<+<=NPR+SEO$F 1-2C2CD\^`2aGO$H 3M4E4EF`bd4eIO$N +M,=,=>PRT,UOO$P ,]->->?RTV-WQO$R %m&7&7b&ISO$T &}'8'8'KUO$Z )-*;*;<Lb*Q[O$\ 2=3D3DE^`b3c]O$^ .}/@/@AVXZ/[_O$` -m.?.?@TVX.YaO$b &}'8'8'KcO$h -m.?.?@TVX.YiO$j /0A0ABXZ\0]kO$l 1-2C2CD\^`2amO$n -m.?.?@TVX.YoO$p -m.?.?@TVX.YqO$r &}'8'8'KsO$t ,]->->?RTV-WuO$v &}'8'8'KwO$| ,]->->?RTV-W}O$~ *=+<+<=NPR+SO$D "=#4#4Y#CEO$F %m&7&7b&IGO$H ,]->->?RTV-WIO$J .;->->?RTV-W,9,=,=>PRT,U*7*;*;<Lb*Q)6):):?B)O#0#4#4Y#C"/"3"3Hb"A&3&7&7b&I&3&7&7b&I&3&7&7b&I+//b9]O$L` (..|< $#%8 $ 8J"4PR $  6G!2R $ %?T&;Z\	 $
 ~R $ ? $  5E!12 $ }2 $ !7H"3b $  6G!2R $ #<P$8VX $ 0 $ [ $  $\! $" #N$6$6~r$J# $$ *>+=+=>SUW+X% $& n002>' $( &4%7%7%K-11)R@"0"4"4\2"F *8)=)=)?(!%A %P P 1(3 $ B "(()9: % ,%%k2&|4',X6K-La'P'/0E'F (^R @*?Jkool;PT&oo/ ( 1N" !n6G23!#6823 +55nEFKKTUaTbbklvkw  xC  DQ  CR  S  TG ! kLL#PQ]P^^`adefag`h!ijjkz(P  	LLB3q6(KL	sX   Ah4 Ch4 !B g3 !]5h4 h.'Ch4 3	h+<$h& h4 &h++	h4 4	i(=!i##i(c           	         	 t         j                  d|r|j                  dd      nd        t         j                  dt        |              t         j                  dt        |              t         j                  d|        | j	                  |||      }t         j                  d|j                  dd              d	}| j
                  j                  ||d
|r|j                  d      ndd      }| j                  |d      }|S # t        $ rD}|r|j                  dd      nd}	t         j                  d|	 dt        |              Y d}~yd}~ww xY w)aa  
        Generate AI analysis for a candidate using direct data (for upload scenarios)
        
        Args:
            job_details: Job details dictionary
            candidate_data: Direct candidate data
            candidate: Candidate information with scores
            
        Returns:
            Analysis dictionary or None if failed
        z#Starting AI analysis for candidate r@   unknownzNone candidatezJob details available: zCandidate data available: zCandidate object: z(Generated analysis prompt for candidate rS  rT  rw   NrU  rV  r?   z/Error generating direct candidate analysis for rB   )r1   rC   rG   boolr[  r   r\  r]  r0   r+   r2   )
r   rI   r]   rL   r^  rX  r_  r`  r4   candidate_ids
             r   r7  z6JobMatchingService._generate_candidate_analysis_directQ  s_   &	KK=gpimmKYb>c  wG  >H  I  JKK1${2C1DEFKK4T.5I4JKLKK,YK89 11+~yYF KKB9==Q\^gChBijk SM OO77+<G;??<8T# 8 M #55mTJO"" 	DM9==i@S\LLLJ<.XZ[^_`[aZbcd		s   D	D 	E:EEc                    	 ddl m}  |       }i g g g g g g g g g g d}|d   j                  d|i      }|ri|j                  dd      |d<   |j                  dd      |d<   |j                  d	d      |d	<   |j                  d
d      |d
<   |j                  dd      |d<   |d   j                  d|i      }|r||d<   t	        |d   j                  d|i            }||d<   t	        |d   j                  d|i            }||d<   t	        |d   j                  d|i            }	|	|d<   t	        |d   j                  d|i            }
|
|d<   t	        |d   j                  d|i            }||d<   t        j                  d|        |S # t        $ r.}t        j                  d| dt        |              Y d}~yd}~ww xY w)z
        Get complete candidate data from all bulk upload related tables
        
        Args:
            jobseeker_id: The jobseeker ID from bulk upload collection
            
        Returns:
            Complete candidate data dictionary
        r   r  r  r>  r}   ri  rb   rj  r?  cvfile_store_pathlast_login_date&jobseeker_basic_details_bulk_upload_cvr[   rb  +jobseeker_employment_details_bulk_upload_cvrl  *jobseeker_education_details_bulk_upload_cvrm  'jobseeker_course_details_bulk_upload_cvro  (jobseeker_project_details_bulk_upload_cvrp  .jobseeker_certification_details_bulk_upload_cvrn  z;Successfully fetched complete candidate data for jobseeker z5Error fetching complete candidate data for jobseeker rB   N)r  r  r   rG   r   rt  r1   rC   r0   r+   r2   )r   r[   r  r   r]   main_cv_datarb  rl  rm  ro  rp  rn  r4   s                r   r5  z;JobMatchingService._get_complete_bulk_upload_candidate_data  sR   :	6B "$&(%')+"$#%$&!#%'%'(*N 9:CCT<DXYL*6*:*:7B*Gw'2>2B2B?TV2W/0<0@0@PR0S}-6B6F6FGZ\^6_234@4D4DEVXZ4[01 GHQQSacoRpqM2?/ "&b)V&W&\&\^lnz]{&|!}3EN/0 !%R(T%U%Z%Z\jlx[y%z {2CN./ ""%N"O"T"TVdfrUs"tuN/=N+, #2&P#Q#V#VXfhtWu#vwO0?N,- %),\)])b)bdr  uA  dB  *C  %D!6KN23KKUVbUcde!! 	LLPQ]P^^`adefag`hij	s   FF	 		G $F;;G c                    	 i d||j                  d      r#|j                  dd      j                         d   nd|j                  d      r^t        |j                  dd      j                               dkD  r2dj                  |j                  dd      j                         dd       nd|j                  dd      |j                  dd      |j                  d	d      |j                  d
      r!dj                  |j                  d
g             nd|j                  d      r|j                  dd      nd|j                  d      |j                  d      |j                  dg       dd|j                  dd      |j                  dd      dd|j                  dd      d|j                  d	d      dg dg dg dg dg d|j                  dd      |j                  d	d      d|j                  dd       dd| d|j                  dd      ddg d g d!g d"g d#g d$g d|j                  dd      d|j                  d	d      }|j                  d%      rt	        |d%   t
              r|d%   D ]  }t	        |t              s||j                  d&d      xs |j                  d'd      |j                  d(d      |j                  d)      |j                  d*      |j                  d+d      xs |j                  d,d      |j                  d-g       d.}|d   j                  |        |j                  d/      rt	        |d/   t
              r|d/   D ]  }t	        |t              s||j                  d0d      |j                  d1d      |j                  d2d      |j                  d)      |j                  d*      |j                  d3      |j                  d4      d5}|d   j                  |        |j                  d6      rt	        |d6   t
              r|d6   D ]  }t	        |t              s||j                  dd      xs |j                  d7d      |j                  d8d      xs |j                  d9d      |j                  d:      |j                  d;      |j                  d<      |j                  d=      d>}	|d   j                  |	        |j                  d?      rt	        |d?   t
              r|d?   D ]  }
t	        |
t              s||
j                  d7d      xs |
j                  dd      |
j                  d9d      xs |
j                  d(d      |
j                  d,d      |
j                  d=d      |
j                  d)      |
j                  d*      |
j                  d@g       dA}|d   j                  |        t        j                  dB|        |S # t        $ r^}t        j                  dC| dDt        |              i g g g g g g g g g g |j                  dd      |j                  d	d      dEcY d}~S d}~ww xY w)Fa  
        Format parsed data to match the expected storage structure
        
        Args:
            parsed_data: The parsed resume data
            jobseeker_id: The jobseeker ID
            
        Returns:
            Formatted candidate data dictionary
        rb  r~   rb   r   r   r   Nri  r  rj   r  r  date_of_birthnationalityr
  r  r  )r[   r  r  	full_nameri  r  r  r  rY  rZ  r
  r)   r  r  r  rj  rl  rm  rn  ro  rp  r  zParsed Resume - Unknownr@  zuploads/parsed/z/resume.pdf)ri  rj  r?  rO  rP  r  r  r  r  r  r  rz  rd   r{  
start_dateend_daterf   rh   responsibilities)r[   r}  r~  r]  r^  rf   r_  r,  r  r  field_of_studycompletion_yeargrade)r[   r  institute_namer`  r]  r^  ra  rb  r  rc   issuerorganization
issue_dateexpiry_datecredential_idurl)r[   certificate_namerc  rf  rg  rh  ri  r	  technologies)r[   project_titleorganization_nameproject_descriptionproject_urlr]  r^  rk  z1Successfully formatted parsed data for jobseeker z+Error formatting parsed data for jobseeker rB   )rb  rl  rm  rn  ro  rp  r  r  r  r  r  ri  rj  )rG   r  r:   r1  r   r   r   r8  r1   rC   r0   r+   r2   )r   r1  r[   formatted_candidate_dataexpformatted_expr  formatted_educertformatted_certprojformatted_projr4   s                r   r6  z2JobMatchingService._format_parsed_data_for_storage  s   ~	)($0LWOO\bLc+//&""="C"C"Ea"HikVaVeVeflVmru  wB  wF  wF  GM  OQ  wR  wX  wX  wZ  s[  ^_  s_+//&"*E*K*K*Mab*Q!R  eg!,!<(__Wb9(__Wb9MX__]eMf#((;??8R+H"IlnQ\Q`Q`arQs{7H"'My{%0___%E#.??=#A!,b!A"#"-//,"C"-//,"C")($ "5%)(&  "!=')(( %b))(* $R+)(, (-)(. !"/)(0 "21)(2 "(__Wb9%0__Wb%A%5koofi6X5YY]#^+:<.)T'2|R'H$3)(B "2C)(D #BE)(F  G)(H $RI)(J $RK)(L 'M)(N "5O)(P  "!=Q)($X |,K<UW[1\&|4 ]C!#t,,8+.7762+>+Y#''*VXBY,/GGIr,B*-'',*?(+
(;/2ww7H"/M/kQTQXQXYfhjQk038JB0O) 11EFMMm\] {+
;{;SUY0Z&{3 \C!#t,,8&)ggh&;.1ggmR.H.1gg6F.K*-'',*?(+
(;/2ww7H/I%(WWW%5	) 11DELL][\ /0ZL\@]_c5d'(89 aD!$-,8040D0]QXZ\H].2hhx.D.dQ_acHd*.((<*@+/88M+B-1XXo-F#'88E?* 11HIPPQ_`a z*z+j:QSW/X'
3 [D!$-,8-1XXgr-B-ZdhhvWYFZ15."1M1hQUQYQYZcegQh3788M23N+/88E2+>*.((<*@(,(<,0HH^R,H	* 11BCJJ>Z[ KKKL>Z[++ 	LLF|nTVWZ[\W]V^_`!#&(%')+"$#%$&!#%'%'(*$"5!,"!= 	s;   JW' C"W' +C	W' 5C W' CW' '	Y0AY	Y	Yr   )ry  )NN)%__name__
__module____qualname____doc__r   r2   r   r   r5   r   rr  r;   r.   r9   r   rD   r   r   rE   r5  r   r   rF   rX   r[  r   r]  rH   rL  rV   rY   r   r<  r8  r7  r5  r6  r   r   r   r   r      s   U& S  T#s(^  D"C "PTUXPY "^bcfhkck^l "H.`3 .``Fo3 FoW[\_W` FoPk3 k8DcN3K kZ6T#s(^ 6dhildm 6y}  C  DG  IL  DL  M  zN 6p6DcN 6cghkcl 6x|  ~B  CF  HK  CK  ~L  yM 6pdd38n dd3PS8nAU dLK+c K+S K+S K+Z!%tCH~ !%# !%Ff4: fW[ f`deijmorjres`t fP+S#X +SWX[]`X`Sa +fnostwy|t|o}f~ +ZB Bc3h8P BHy4S> ySWX[]`X`Sa yuyz}  @C  {C  vD y  IL yv	X X% XTXY\^aYaTb XtY# Y$sCx. Y\`adfiai\j Yy}  B  DG  G  zH Yv%c %d %NX3 X4PSUXPX> Xeijmorjres X  CG  HK  MP  HP  CQ Xtc  dSWX[]`X`SaNb BOc OQT Ocghkmphpcq O  CF O  OS  TW  Y\  T\  O] ObA AC A[_`ceh`h[i Az} A  PT  UX  Z]  U]  P^ A  tx  y|  ~A  yA  tB AF2tCH~ 2_cdgildl_m 2z~  @C  EH  @H  {I 2  NV  W[  \_  ad  \d  We  Nf 2hDS DTRUWZRZ^ DLI4S> IY\ Iaefiknfnao Ir   r   )asyncior,   typingr   r   r   r   loggingr   r   pymongodotenvr   bson.codec_optionsr	   r
   r   r   r   r   
embeddingsr   r   r   r   basicConfigINFO	getLoggerrx  r1   r   r   r   r   <module>r     sm      , ,   	   ? * 1   ( !    ',, '			8	$|$ |$r   