
    hi<                         d Z ddlZddlmZmZmZmZ ddlmZ ddl	m
Z
mZmZmZ ddlmZ ddlmZ ddlmZmZ  ej(                  e      Z G d	 d
      Z e       Zy)z-
Service de recherche et matching de profils
    N)ListDictOptionalTuple)Session)or_and_funcString)datetime)User)ProfileSearchRequestProfileMatchScorec                      e Zd ZdZdededeee   e	f   fdZ
dededefdZdefdZd	ed
edee   dedeeee   f   f
dZdefdZd	ed
ededeeee   ee	   f   fdZd	ed
ededefdZdee   dee	   fdZdede	fdZy)ProfileSearchServicez.Service pour rechercher et matcher des profilsdbcriteriareturnc                    	 |j                  t              j                  t        j                  dk(        }|j                  r<|j                  t        j                  j                  d|j                   d            }|j                         }t        j                  dt        |       d       g }|D ];  }	 | j                  ||      }|d   |j                  k\  r|j                  ||d       = |j!                  d
 d       t        |      }	||j"                  |j"                  |j$                  z    }
t        j                  d|	 dt        |
       d       |
|	fS # t        $ r/}t        j                  d|j                   d|        Y d	}~d	}~ww xY w# t        $ r }t        j                  d| d        d	}~ww xY w)u   
        Recherche des profils selon les critères
        
        Retourne: (liste de profils avec scores, nombre total)
        T%u   🔍 Recherche sur z profilsoverall_score)usermatch_scoreu   ❌ Erreur scoring profil z: Nc                     | d   d   S )Nr   r    )xs    K/home/www/therecruiter.miabetepe.com/app/services/profile_search_service.py<lambda>z6ProfileSearchService.search_profiles.<locals>.<lambda>@   s    a.?     )keyreverseu   ✅ u    profils trouvés, u    retournésu   ❌ Erreur recherche profils: )exc_info)queryr   filterregistration_completedlocationilikeallloggerinfolencalculate_match_scorematch_thresholdappend	Exceptionerroridsortoffsetlimit)selfr   r   r#   	all_usersscored_profilesr   
score_dataetotal	paginateds              r   search_profilesz$ProfileSearchService.search_profiles   s   2	HHTN))++t3E
   MM''!H,=,=+>a(@A
 		IKK-c)n-=XFG !O! !%!;!;D(!KJ "/2h6N6NN'..$(+50    ? ! 
 (E ((..8XYIKK$ug%8Y8HTUe### ! LL#=dggYb!LM&  	LL9!=LM	sC   B6F 98E!1A/F !	F*%FF FF 	G%G  Gr   c                    dddg g dd}dddd}|j                   xs i }|j                  di       }|j                  xs i }|j                  d	i       }|j                  r2| j	                  |||j                  |j
                        \  |d
<   |d<   nd|d
<   |j                  s$|j                  s|j                  s|j                  r!| j                  |||      \  |d<   |d<   |d<   nd|d<   |j                  s|j                  r| j                  |||      |d<   nd|d<   |d
   |d
   z  |d   |d   z  z   |d   |d   z  z   }	t        |	d      t        |d
   d      t        |d   d      t        |d   d      |d   |d   |d   dS )u   
        Calcule le score de matching pour un utilisateur
        
        Retourne un dict avec les scores détaillés
                N)skills
experience	educationmatched_skillsmatched_companiestotal_years_experienceg?gffffff?g      ?)r?   r@   rA   parsed_contentscraped_datar?   rB         ?r@   rC   rD   rA      )r   skills_scoreexperience_scoreeducation_scorerB   rC   rD   )cv_datagetlinkedin_profiler?   _score_skillsmatch_all_skillsmin_years_experiencemax_years_experience	companies	positions_score_experienceeducation_degreeseducation_institutions_score_educationround)
r5   r   r   scoresweightsrL   	parsed_cvlinkedin_datascraped_linkedinr   s
             r   r,   z*ProfileSearchService.calculate_match_scoreQ   s     !#&*
 
 ,,$"KK 0"5	--3(,,^R@ ??9=9K9K ))	:6F8f%56  #F8 ((H,I,IXM_M_ckcucubfbxbx c_F< &)<"=vF^?_ $'F<  %%)H)H"&"7"7 #F; #&F; 8wx00< 7<#889;'+"667 	 #=!4!&"2A6 %f\&:A >$VK%8!<$%56!'(;!<&,-E&F
 	
r   c                    t        |t              r_t        |j                  dd      xs& |j                  dd      xs |j                  dd            j	                         j                         S t        |t              r|j	                         j                         S t        |      j	                         j                         S )u2   Normalise une compétence (peut être str ou dict)name skilltitle)
isinstancedictstrrM   lowerstrip)r5   rb   s     r   _normalize_skillz%ProfileSearchService._normalize_skill   s    eT"uyy,`		'20F`%))T[]_J`aggiooqqs#;;=&&((u:##%++--r   r\   r^   required_skills	match_allc                    	 |j                  dg       }|j                  dg       }|D cg c]  }|s| j                  |       }}|D cg c]  }|s| j                  |       }	}|D cg c]  }|s|	 }}|	D cg c]  }|s|	 }	}t        ||	z         }
|D cg c]   }|j                         j	                         " }}g }|D ]$  }|
D ]  }||v s||v s|j                  |        $ & t        t        |            }|r|rt        |      t        |      z  nd}n|rdnd}||fS c c}w c c}w c c}w c c}w c c}w # t        $ r&}t        j                  d|        dg fcY d}~S d}~ww xY w)u   Score les compétencesr?   r>   rG   u   ❌ Erreur _score_skills: N)rM   ri   setrg   rh   r.   listr+   r/   r)   r0   )r5   r\   r^   rj   rk   cv_skills_rawlinkedin_skills_raws	cv_skillslinkedin_skills
all_skillsrequired_skills_lowermatched	req_skill
user_skillscorer9   s                    r   rO   z"ProfileSearchService._score_skills   s   '	%MM(B7M"2"6"6x"D <INaA..q1NINATZAXYt44Q7ZOZ %.3q3I3*9?QQq?O?Y89J AP$P1QWWY__%6$P!$P G2 	", J J.*	2Iy1 3w<(G?NGs?';;TW  'C'>!; OZ 4?
 %Q*  	LL5aS9:7N	s|   (E D,D,E D1D1&E ,D64D68E >D;D;
E %E E AE ,E 	E4E/)E4/E4c                     |yt        |t              r>dD ]8  }||v s||   st        ||         j                         j	                         c S  yt        |      j                         j	                         S )u*   Normalise un texte (gère str, dict, None)ra   )	r`   rc   companycompanyNamepositiondegreeinstitutionschool
schoolName)rd   re   rf   rg   rh   )r5   textr    s      r   _normalize_textz$ProfileSearchService._normalize_text   so    <dD! :$;49tCy>//17799: 4y &&((r   c                    	 d}g }|j                  dg       }|j                  dg       }||z   }| j                  |      }	g }
|j                  |j                  |j                  xs d}|j                  xs d}|	f||	cxk  r|k  rn n|
j	                  d       nW|	|k  r/|
j	                  t        dd||	z
  t        |d      z  z
               n#|
j	                  d       n|
j	                  d       |j                  r|j                  D cg c]   }|j                         j                         " }}|D ]  }| j                  |j                  d	      xs |j                  d
            s9t        fd|D              sN|j	                  |j                  d	      xs |j                  d
d              |j                  r!t        |      t        |j                        z  nd}|
j	                  |       |j                  r|j                  D cg c]   }|j                         j                         " }}d}|D ]P  }| j                  |j                  d      xs |j                  d            s9t        fd|D              sNd} n |
j	                  |rdnd       |
rt        |
      t        |
      z  nd}|||	fS c c}w c c}w # t        $ r'}t        j!                  d|        dg dfcY d}~S d}~ww xY w)u#   Score l'expérience professionneller>   r@   Nr   d   rG      g      ?r{   r|   c              3   2   K   | ]  }|v xs |v   y wNr   ).0targetr{   s     r   	<genexpr>z9ProfileSearchService._score_experience.<locals>.<genexpr>  s$     &lRXv'8'MGv<M'M&l   ra   Fr}   rc   c              3   2   K   | ]  }|v xs |v   y wr   r   )r   r   r}   s     r   r   z9ProfileSearchService._score_experience.<locals>.<genexpr>  s$     'oU[((:(Ph&>P(P'or   Tu   ❌ Erreur _score_experience: )rM   _calculate_total_experiencerQ   rR   r.   maxrS   rg   rh   r   anyr+   rT   sumr/   r)   r0   )r5   r\   r^   r   ry   rC   cv_explinkedin_expall_exptotal_years
sub_scores	min_years	max_yearsccompanies_lowerexpcompany_scoreppositions_lowerposition_matchr9   r{   r}   s                        @@r   rU   z&ProfileSearchService._score_experience   s   =	!E " ]]<4F+//bAL|+G ::7CKJ ,,8H<Y<Y<e$99>Q	$99@S	* K<9<"))#. '2&--c#si+>UY\]fhiYj=j7j.kl&--c2%%c* !!>F>P>P"Q1779??#4"Q"Q" cC"2237793E3_Q^I_`G3&l\k&l#l)001C1asww}^`Gabc
 U]TfTf$5 6X=O=O9P Plo!!-0 !!>F>P>P"Q1779??#4"Q"Q!&" C#33CGGJ4G4[377SZK[\HC'o_n'o$o)-	 !!#SA :DC
Oc*o5E+[881 #R #R  	!LL9!=>D= 	!sU   DK %K	4<K 1K BK %K;>K :K 9K 	
K 	LK>8L>Lc                    	 |j                  dg       }|j                  dg       }||z   }g }|j                  r|j                  D cg c]   }|j                         j                         " }	}d}
|D ]P  }| j	                  |j                  d      xs |j                  d            s9t        fd|	D              sNd}
 n |j                  |
rdnd       |j                  r|j                  D cg c]   }|j                         j                         " }}d}|D ]c  }| j	                  |j                  d	      xs$ |j                  d
      xs |j                  d            sLt        fd|D              sad} n |j                  |rdnd       |rt        |      t        |      z  }|S d}|S c c}w c c}w # t        $ r"}t        j                  d|        Y d}~yd}~ww xY w)zScore la formationrA   Fr~   
degreeNamec              3   2   K   | ]  }|v xs |v   y wr   r   )r   r   r~   s     r   r   z8ProfileSearchService._score_education.<locals>.<genexpr>A  s#     %gvf&6&J&F:J&J%gr   TrG   r>   r   r   r   c              3   2   K   | ]  }|v xs |v   y wr   r   )r   r   r   s     r   r   z8ProfileSearchService._score_education.<locals>.<genexpr>M  s%     *{^d6[+@+YKSYDY+Y*{r   u   ❌ Erreur _score_education: N)rM   rV   rg   rh   r   r   r.   rW   r   r+   r/   r)   r0   )r5   r\   r^   r   cv_edulinkedin_eduall_edur   ddegrees_lowerdegree_matcheduiinstitutions_lowerinstitution_matchry   r9   r~   r   s                    @@r   rX   z%ProfileSearchService._score_education-  s   $	]];3F+//R@L|+GJ ))<D<V<V Wq!2 W W$" C!11#''(2C2\sww|G\]F#%gYf%g"g'+	 !!#3? ..AIA`A`%aAaggioo&7%a"%a$)!" C"&"6"6sww}7M7{QTQXQXYaQb7{fifmfmnzf{"|K"s*{hz*{'{,0)	 !!):#D9CC
Oc*o5EL JMEL/ !X &b  	LL8<=	sU   AG 	%F;.>G -G 4G 6%G AG -G 4G 7G ;
G 	G0G++G0experiencesc                     |syd}|D ]3  }t        |j                  dd            }| j                  |      }||z  }5 |dkD  r|S dS )u/   Calcule le nombre total d'années d'expérienceNr   durationra   )rf   rM   _parse_duration)r5   r   r   r   duration_stryearss         r   r   z0ProfileSearchService._calculate_total_experienceZ  s`     	!Cswwz267L ((6E5 K	! *Ao{747r   r   c                 <   	 t        j                         j                  }|j                  dt	        |            }|j                  dt	        |            }|j                  dt	        |            }ddl}|j                  d|      }t        |      dk\  r+t        |d         }t        |d         }t        d||z
        S t        |      d	k(  rt        |d         }t        d||z
        S 	 y# t        $ r%}t        j                  d
| d|        Y d}~yd}~ww xY w)u0   Parse une durée et retourne le nombre d'annéesPresentpresentu   Présentr   Nz\d{4}   r   u   ⚠️ Erreur parsing durée  'z': )r   utcnowyearreplacerf   refindallr+   intr   r/   r)   warning)r5   r   current_yearr   r   startendr9   s           r   r   z$ProfileSearchService._parse_durationi  s   	S#??,11L'//	3|;LML'//	3|;LML'//
C<MNL JJx6E5zQE!H%)n1cEk**UqE!H1lU233 !   	SNN<\N#aSQRR	Ss   B=C-  *C- -	D6DDN)__name__
__module____qualname____doc__r   r   r   r   r   r   r<   r   r,   rf   ri   boolfloatrO   r   r   rU   rX   r   r   r   r   r   r   r      sY   8<< '< 
tDz3		<|M
M
 'M
 
	M
^. .// / c	/
 / 
ud3i	 /b
)s 
)D!D! D! '	D!
 
ud3i#.	/D!L++ + '	+
 
+Z8tDz 8hsm 8C C r   r   )r   loggingtypingr   r   r   r   sqlalchemy.ormr   
sqlalchemyr   r	   r
   r   r   
app.modelsr   app.schemasr   r   	getLoggerr   r)   r   profile_search_servicer   r   r   <module>r      sQ     . . " . .   ?			8	$q qj ./ r   