1000jaus commited on
Commit
68ec74d
·
1 Parent(s): dcc1c1c

edited rol logic

Browse files
project/cv_matcher.py CHANGED
@@ -218,8 +218,8 @@ class CVMatcher:
218
  weighted_experience += similarity * exp['years']
219
 
220
  # Get min and max experience from offer
221
- min_exp = float(offer_dict.get('experience', {}).get('min', 0))
222
- max_exp = float(offer_dict.get('experience', {}).get('max', min_exp + 5)) # Default range if max not specified
223
 
224
  # Calculate experience percentage (capped at 1.0)
225
  if min_exp > 0:
@@ -293,6 +293,27 @@ class CVMatcher:
293
  # Get role experience details
294
  min_exp, max_exp, total_exp, exp_score = self.role_experience_similarity(offer_dict, cv_dict)
295
  role = offer_dict.get("role", "")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
296
 
297
  # Get sector information
298
  sector_similarity = self.sector_similarity(offer_dict, cv_dict)
@@ -301,88 +322,84 @@ class CVMatcher:
301
 
302
  # Get education information
303
  education_score = self.education_final_score(offer_dict, cv_dict)
304
- min_education = float(offer_dict.get("education", {}).get("number", 0))
305
  min_education_level = offer_dict.get("education", {}).get("min", "No especificado")
306
  min_education_field = offer_dict.get("education", {}).get("field", "No especificado")
307
 
308
- # Find same level education and higher education
309
- same_level_education = []
310
- higher_education = []
311
-
312
- for edu in cv_dict.get("education", []):
313
- try:
314
- edu_level = float(edu.get("number", 0))
315
- if edu_level == min_education:
316
- same_level_education.append(edu)
317
- elif edu_level > min_education:
318
- higher_education.append(edu)
319
- except (ValueError, TypeError):
320
- continue
321
-
322
- # Prepare education explanation
323
- same_level_text = ""
324
- if same_level_education:
325
- same_level_text = f"The candidate has the same required education level: {same_level_education[0].get('degree', 'Similar level')}"
 
 
 
 
 
 
326
  else:
327
- same_level_text = "The candidate does not have the same required education level"
 
 
 
 
328
 
329
- higher_education_text = ""
330
- if higher_education:
331
- higher_education_text = ", ".join([edu.get('degree', '') for edu in higher_education])
332
- else:
333
- higher_education_text = "No higher education"
334
-
335
- # Format the return dictionary with all the processed information
 
 
 
 
 
 
336
  result = {
337
- # Scores
338
  "technical_skills_score": int(np.round(100 * tech_score, 2)),
339
  "soft_skills_score": int(np.round(100 * soft_score, 2)),
340
  "role_experience_score": int(np.round(100 * exp_score, 2)),
341
  "education_score": int(np.round(100 * education_score, 2)),
342
  "sector_score": int(np.round(100 * sector_similarity, 2)),
343
 
344
- # Detailed information
345
  "technical_skills": tech_skills,
346
  "soft_skills": soft_skills,
347
 
348
- # Role experience details
349
  "role_experience": {
350
- "explanation": f"You have approximately {round(total_exp, 1)} years of experience in roles similar to '{role}'. "
351
- f"The offer is looking for between {min_exp} and {max_exp} years of experience.",
352
  "details": {
353
- "role": role,
354
- "min_years": min_exp,
355
- "max_years": max_exp,
356
- "total_experience": round(total_exp, 1)
357
  }
358
  },
359
 
360
- # Education details
361
  "education": {
362
- "explanation": f"The offer requires at least {min_education_level}. {same_level_text}.",
363
- "details": {
364
- "minimum_required_level": min_education_level,
365
- "minimum_required_field": min_education_field,
366
- "equivalent_level_cv": same_level_education[0].get('degree', 'Not available') if same_level_education else 'Not available',
367
- "equivalent_field_cv": same_level_education[0].get('field', 'Not available') if same_level_education else 'Not available',
368
- "higher_education_degrees": [edu.get('degree', '') for edu in higher_education],
369
- "higher_education_fields": [edu.get('field', '') for edu in higher_education],
370
- "meets_requirement": education_score >= 0.5
371
- }
372
  },
373
 
374
- # Sector information
375
  "sector": {
376
- "explanation": f"The offer's sector is '{offer_sector}' and your main sector is '{cv_sector}'. "
377
  f"The similarity between both sectors is {round(sector_similarity * 100, 1)}%.",
378
  "details": {
379
- "offer_sector": offer_sector,
380
- "cv_sector": cv_sector,
381
- "similarity": round(sector_similarity * 100, 1)
382
  }
383
  }
384
  }
385
-
386
 
387
  return result
388
 
 
218
  weighted_experience += similarity * exp['years']
219
 
220
  # Get min and max experience from offer
221
+ min_exp = float(offer_dict.get('experience', {}).get('min', 0.0))
222
+ max_exp = float(offer_dict.get('experience', {}).get('max', 9999.0)) # Default range if max not specified
223
 
224
  # Calculate experience percentage (capped at 1.0)
225
  if min_exp > 0:
 
293
  # Get role experience details
294
  min_exp, max_exp, total_exp, exp_score = self.role_experience_similarity(offer_dict, cv_dict)
295
  role = offer_dict.get("role", "")
296
+
297
+ # --- NUEVA LÓGICA CLARA Y ROBUSTA PARA EL TEXTO DE EXPERIENCIA ---
298
+ min_exp_raw = offer_dict.get('experience', {}).get('min', 0)
299
+ max_exp_raw = offer_dict.get('experience', {}).get('max', 9999.0)
300
+
301
+ # Convert to float for consistent comparison
302
+ min_exp = float(min_exp_raw) if min_exp_raw is not None else 0
303
+ max_exp = float(max_exp_raw) if max_exp_raw is not None else 9999.0
304
+
305
+ experience_requirement_text = ""
306
+ # Caso 1: No se especifica experiencia mínima o es 0.
307
+ if min_exp == 0:
308
+ experience_requirement_text = "There's not any experience required for this role."
309
+ # Caso 2: Se especifica un mínimo pero no un máximo (o máximo muy alto).
310
+ elif max_exp >= 9999.0:
311
+ experience_requirement_text = f"The offer is looking for someone with more than {int(min_exp)} years of experience."
312
+ # Caso 3: Se especifican ambos, mínimo y máximo.
313
+ else:
314
+ experience_requirement_text = f"The offer is looking for between {int(min_exp)} and {int(max_exp)} years of experience."
315
+
316
+ full_explanation = f"You have approximately {round(total_exp, 1)} years of experience in roles similar to '{role}'. {experience_requirement_text}"
317
 
318
  # Get sector information
319
  sector_similarity = self.sector_similarity(offer_dict, cv_dict)
 
322
 
323
  # Get education information
324
  education_score = self.education_final_score(offer_dict, cv_dict)
325
+ min_education = float(offer_dict.get("education", {}).get("number", 0))
326
  min_education_level = offer_dict.get("education", {}).get("min", "No especificado")
327
  min_education_field = offer_dict.get("education", {}).get("field", "No especificado")
328
 
329
+ # Get candidate's education and find the highest degree
330
+ cv_education_list = cv_dict.get("education", [])
331
+ highest_cv_degree = None
332
+ if cv_education_list:
333
+ sorted_cv_education = sorted(cv_education_list, key=lambda x: float(x.get('number', 0)), reverse=True)
334
+ highest_cv_degree = sorted_cv_education[0]
335
+
336
+ education_details = {}
337
+ education_explanation = ""
338
+
339
+ # SCENARIO 1: The offer does NOT specify a minimum education level
340
+ if min_education == 0:
341
+ education_explanation = "The offer does not specify a minimum education level. The candidate's highest degree is shown for reference."
342
+ education_details = {
343
+ "minimum_required_level": "Not specified",
344
+ "minimum_required_field": min_education_field if min_education_field != "No especificado" else "Not specified",
345
+ # USAMOS LAS CLAVES ORIGINALES para no romper el HTML
346
+ "equivalent_level_cv": highest_cv_degree.get('degree', 'Not available') if highest_cv_degree else 'Not available',
347
+ "equivalent_field_cv": highest_cv_degree.get('field', 'Not available') if highest_cv_degree else 'Not available',
348
+ # Devolvemos una lista vacía, el JS lo mostrará como 'None'
349
+ "higher_education_degrees": [],
350
+ "meets_requirement": True
351
+ }
352
+ # SCENARIO 2: The offer DOES specify a minimum education level
353
  else:
354
+ same_level_education = [edu for edu in cv_education_list if float(edu.get('number', 0)) == min_education]
355
+ higher_education = [edu for edu in cv_education_list if float(edu.get('number', 0)) > min_education]
356
+
357
+ match_text = "The candidate meets the minimum requirement." if same_level_education or higher_education else "The candidate does not meet the minimum requirement."
358
+ education_explanation = f"The offer requires at least {min_education_level}. {match_text}"
359
 
360
+ # Find the most relevant degree to show as "equivalent"
361
+ equivalent_education = same_level_education[0] if same_level_education else (highest_cv_degree if higher_education else {})
362
+
363
+ education_details = {
364
+ "minimum_required_level": min_education_level,
365
+ "minimum_required_field": min_education_field,
366
+ "equivalent_level_cv": equivalent_education.get('degree', 'Not available'),
367
+ "equivalent_field_cv": equivalent_education.get('field', 'Not available'),
368
+ "higher_education_degrees": [edu.get('degree', '') for edu in higher_education],
369
+ "meets_requirement": education_score >= 0.5
370
+ }
371
+
372
+ # Format the final return dictionary with all the processed information
373
  result = {
 
374
  "technical_skills_score": int(np.round(100 * tech_score, 2)),
375
  "soft_skills_score": int(np.round(100 * soft_score, 2)),
376
  "role_experience_score": int(np.round(100 * exp_score, 2)),
377
  "education_score": int(np.round(100 * education_score, 2)),
378
  "sector_score": int(np.round(100 * sector_similarity, 2)),
379
 
 
380
  "technical_skills": tech_skills,
381
  "soft_skills": soft_skills,
382
 
 
383
  "role_experience": {
384
+ "explanation": full_explanation, # Usamos la variable que acabamos de crear
 
385
  "details": {
386
+ "role": role, "min_years": min_exp, "max_years": max_exp, "total_experience": round(total_exp, 1)
 
 
 
387
  }
388
  },
389
 
 
390
  "education": {
391
+ "explanation": education_explanation,
392
+ "details": education_details
 
 
 
 
 
 
 
 
393
  },
394
 
 
395
  "sector": {
396
+ "explanation": f"The offer's sector is '{offer_sector}' and your main sector is '{' and '.join(cv_sector) if isinstance(cv_sector, list) else cv_sector}'. "
397
  f"The similarity between both sectors is {round(sector_similarity * 100, 1)}%.",
398
  "details": {
399
+ "offer_sector": offer_sector, "cv_sector": ' and '.join(cv_sector) if isinstance(cv_sector, list) else cv_sector, "similarity": round(sector_similarity * 100, 1)
 
 
400
  }
401
  }
402
  }
 
403
 
404
  return result
405
 
project/gemini_api.py CHANGED
@@ -64,10 +64,10 @@ def read_cv(file_path: str) -> str:
64
  for b in blocks_sorted:
65
  cv += b[4]
66
 
67
- # if len(cv) > 10000:
68
- # return -1
69
- # elif len(cv) < 10:
70
- # return -2
71
 
72
  return cv
73
 
 
64
  for b in blocks_sorted:
65
  cv += b[4]
66
 
67
+ if len(cv) > 10000:
68
+ return -1
69
+ elif len(cv) < 10:
70
+ return -2
71
 
72
  return cv
73
 
project/offer.txt ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ En PwC te ofrecemos la posibilidad de iniciar tu carrera profesional trabajando desde tu incorporación para clientes de primer nivel (tanto nacionales como internacionales). Formarás parte de un equipo de trabajo con gran variedad de perfiles profesionales que potenciarán tu aprendizaje.
2
+
3
+ Apostamos por jóvenes sin experiencia, con pasión por el mundo de la Consultoría, que quieran desarrollar su carrera profesional con nosotros. Apostamos por ti y tus capacidades para formar parte de un equipo de personas con gran talento.
4
+
5
+ Tendrás un completo programa de formación adaptado a tu día a día. A través de La Academia de PwC, nuestra Universidad Corporativa interna, podrás acceder a un amplio catálogo de formaciones técnicas, sectoriales y de habilidades.
6
+
7
+ En nuestra línea de Consultoría de Negocio, ayudamos a nuestros clientes a optimizar sus procesos de negocio y a incrementar sus resultados. Ofrecemos servicios de consultoría y asesoramiento empresarial en los ámbitos de Función Financiera, Supply Chain Management, Customer, Consultoría de IT, Data&Analytics y People&Organization.
8
+
9
+ Las competencias más valoradas entre los profesionales Junior de PwC son:
10
+
11
+ Capacidad de trabajo en equipo.
12
+ Capacidad de análisis de problemas.
13
+ Capacidad de aprendizaje
14
+ Capacidad de comunicación
15
+
16
+ Construir relaciones y crear valor forman parte nuestro ADN. ¡Te esperamos!
17
+
18
+ Requisitos:
19
+
20
+ Buscamos estudiantes de último curso de Grado/Postgrado o perfiles de recién graduados, abarcando un amplio abanico de titulaciones:
21
+
22
+ Ingenierías (Industrial, Aeronáutica, Informática, Telecomunicaciones, etc.).
23
+ Dobles titulaciones ADE + Ingeniería, ADE + Derecho (o similares).
24
+ Grado en Matemáticas, Estadística, Física.
25
+
26
+ Es IMPRESCINDIBLE tener la titulación finalizada (incluyendo el Proyecto Fin de Grado o Máster) para poder incorporarse en agosto/septiembre de 2025. No obstante, ten en cuenta que puedes participar en nuestro proceso de selección sin haber finalizado los estudios. El proceso estará abierto de octubre de 2024 a julio de 2025, aunque te recomendamos que no demores tu participación.
27
+
28
+ Otros requisitos:
29
+
30
+ Nivel de inglés alto oral y escrito (se evaluará durante el proceso de selección)
31
+ Valoraremos positivamente un buen expediente académico y carrera cursada según cronología del plan de estudios.
32
+ Valoraremos prácticas en empresa, estancias internacionales (Erasmus) y conocimiento de otros idiomas.
33
+ No requerimos experiencia, pero sí ganas de aprender.
project/prompts/prompt_offer.txt CHANGED
@@ -10,7 +10,7 @@ Tareas:
10
  3. Extrae todas las habilidades técnicas relevantes.
11
  4. Extrae la experiencia mínima / máxima requerida
12
  - si no hay mínima, establecer la mínima como 0
13
- - si no hay máxima, establecer la máxima como mínima
14
  5. Extrae la educación mínima requerida y en caso de que haya varias opciones, coger la mínima siguiendo el orden:
15
 
16
  1. Educación secundaria/bachillerato
 
10
  3. Extrae todas las habilidades técnicas relevantes.
11
  4. Extrae la experiencia mínima / máxima requerida
12
  - si no hay mínima, establecer la mínima como 0
13
+ - si no hay máxima, establecer la máxima 9999
14
  5. Extrae la educación mínima requerida y en caso de que haya varias opciones, coger la mínima siguiendo el orden:
15
 
16
  1. Educación secundaria/bachillerato
project/static/index.html CHANGED
@@ -1303,12 +1303,21 @@
1303
 
1304
  const data = await response.json();
1305
  console.log('Received data:', data);
1306
-
 
1307
  if (data.error) {
1308
  throw new Error(data.error);
1309
  }
 
 
 
 
 
 
 
 
1310
 
1311
- // Mostrar resultados con animación
1312
  setTimeout(() => {
1313
  displayResults(data);
1314
  showNotification('Analysis completed successfully!', 'success');
 
1303
 
1304
  const data = await response.json();
1305
  console.log('Received data:', data);
1306
+
1307
+ // Esta parte ya maneja los errores que envía explícitamente el backend
1308
  if (data.error) {
1309
  throw new Error(data.error);
1310
  }
1311
+
1312
+ // --- AÑADE ESTA NUEVA COMPROBACIÓN AQUÍ ---
1313
+ // Si el objeto de respuesta no contiene las puntuaciones clave,
1314
+ // asumimos que el PDF no se pudo procesar correctamente.
1315
+ if (data.technical_skills_score === undefined || data.technical_skills_score === null) {
1316
+ throw new Error("The PDF could not be read correctly or is empty. Please check the file.");
1317
+ }
1318
+ // --- FIN DE LA NUEVA COMPROBACIÓN ---
1319
 
1320
+ // Si todo está bien, mostramos los resultados
1321
  setTimeout(() => {
1322
  displayResults(data);
1323
  showNotification('Analysis completed successfully!', 'success');