ishaq101 commited on
Commit
2216e64
·
1 Parent(s): e4b8ed7

[NOTICKET] Feat: get profiles by criteria_id

Browse files
externals/databases/pg_crud.py CHANGED
@@ -1,7 +1,7 @@
1
  from typing import Optional, List
2
  from uuid import UUID
3
 
4
- from sqlalchemy import select, and_, update, delete
5
  from sqlalchemy.ext.asyncio import AsyncSession
6
  from externals.databases.pg_models import (
7
  CVUser,
@@ -274,6 +274,136 @@ async def get_profiles(
274
  result = await db.execute(stmt)
275
  return result.scalars().all()
276
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
  async def get_profile_by_id(
278
  db: AsyncSession,
279
  profile_id: str,
@@ -338,16 +468,16 @@ async def get_filter(
338
 
339
  # --- ARRAY fields (exact match) ---
340
  if filter.hardskills:
341
- conditions.append(CVFilter.hardskills == filter.hardskills)
342
 
343
  if filter.softskills:
344
- conditions.append(CVFilter.softskills == filter.softskills)
345
 
346
  if filter.certifications:
347
- conditions.append(CVFilter.certifications == filter.certifications)
348
 
349
  if filter.business_domain:
350
- conditions.append(CVFilter.business_domain == filter.business_domain)
351
 
352
  # ⛔ Prevent full table scan
353
  if not conditions:
 
1
  from typing import Optional, List
2
  from uuid import UUID
3
 
4
+ from sqlalchemy import select, and_, update, delete, or_
5
  from sqlalchemy.ext.asyncio import AsyncSession
6
  from externals.databases.pg_models import (
7
  CVUser,
 
274
  result = await db.execute(stmt)
275
  return result.scalars().all()
276
 
277
+ from sqlalchemy import func
278
+
279
+ from sqlalchemy import select, and_, func, cast, String
280
+ from sqlalchemy.ext.asyncio import AsyncSession
281
+ from sqlalchemy.dialects.postgresql import ARRAY
282
+ from typing import List
283
+
284
+ async def get_profiles_by_criteria_id(
285
+ db: AsyncSession,
286
+ criteria_id: str,
287
+ current_user: CVUser,
288
+ ) -> List[CVProfile]:
289
+
290
+ filter = await get_filter_by_id(db, criteria_id)
291
+
292
+ if not filter:
293
+ return []
294
+
295
+ conditions = []
296
+
297
+ # --- GPA ---
298
+ if filter.gpa_edu_1 is not None:
299
+ conditions.append(CVProfile.gpa_edu_1 >= filter.gpa_edu_1)
300
+
301
+ if filter.gpa_edu_2 is not None:
302
+ conditions.append(CVProfile.gpa_edu_2 >= filter.gpa_edu_2)
303
+
304
+ if filter.gpa_edu_3 is not None:
305
+ conditions.append(CVProfile.gpa_edu_3 >= filter.gpa_edu_3)
306
+
307
+ # --- University (case-insensitive) ---
308
+ if filter.univ_edu_1:
309
+ conditions.append(
310
+ func.lower(CVProfile.univ_edu_1) ==
311
+ filter.univ_edu_1.lower()
312
+ )
313
+
314
+ if filter.univ_edu_2:
315
+ conditions.append(
316
+ func.lower(CVProfile.univ_edu_2) ==
317
+ filter.univ_edu_2.lower()
318
+ )
319
+
320
+ if filter.univ_edu_3:
321
+ conditions.append(
322
+ func.lower(CVProfile.univ_edu_3) ==
323
+ filter.univ_edu_3.lower()
324
+ )
325
+
326
+ # --- Major (case-insensitive) ---
327
+ if filter.major_edu_1:
328
+ conditions.append(
329
+ func.lower(CVProfile.major_edu_1) ==
330
+ filter.major_edu_1.lower()
331
+ )
332
+
333
+ if filter.major_edu_2:
334
+ conditions.append(
335
+ func.lower(CVProfile.major_edu_2) ==
336
+ filter.major_edu_2.lower()
337
+ )
338
+
339
+ if filter.major_edu_3:
340
+ conditions.append(
341
+ func.lower(CVProfile.major_edu_3) ==
342
+ filter.major_edu_3.lower()
343
+ )
344
+
345
+ # --- Others ---
346
+ if filter.domicile:
347
+ conditions.append(
348
+ func.lower(CVProfile.domicile) ==
349
+ filter.domicile.lower()
350
+ )
351
+
352
+ if filter.yoe is not None:
353
+ conditions.append(CVProfile.yoe >= filter.yoe)
354
+
355
+ # --- ARRAY fields (Postgres-safe cast) ---
356
+ if filter.hardskills:
357
+ conditions.append(
358
+ CVProfile.hardskills.overlap(
359
+ cast(filter.hardskills, ARRAY(String))
360
+ )
361
+ )
362
+
363
+ if filter.softskills:
364
+ conditions.append(
365
+ CVProfile.softskills.overlap(
366
+ cast(filter.softskills, ARRAY(String))
367
+ )
368
+ )
369
+
370
+ if filter.certifications:
371
+ conditions.append(
372
+ CVProfile.certifications.overlap(
373
+ cast(filter.certifications, ARRAY(String))
374
+ )
375
+ )
376
+
377
+ if filter.business_domain:
378
+ conditions.append(
379
+ CVProfile.business_domain.overlap(
380
+ cast(filter.business_domain, ARRAY(String))
381
+ )
382
+ )
383
+
384
+ if not conditions:
385
+ return []
386
+
387
+ # 🔐 Tenant-safe JOIN
388
+ stmt = (
389
+ select(CVProfile)
390
+ .join(CVFile, CVProfile.file_id == CVFile.file_id)
391
+ .join(CVUser, CVFile.user_id == CVUser.user_id)
392
+ .where(
393
+ CVUser.tenant_id == current_user.tenant_id,
394
+ *conditions
395
+ )
396
+ )
397
+
398
+ compiled = stmt.compile(
399
+ dialect=db.bind.dialect,
400
+ compile_kwargs={"literal_binds": True}
401
+ )
402
+ print(compiled)
403
+ result = await db.execute(stmt)
404
+ return result.scalars().all()
405
+
406
+
407
  async def get_profile_by_id(
408
  db: AsyncSession,
409
  profile_id: str,
 
468
 
469
  # --- ARRAY fields (exact match) ---
470
  if filter.hardskills:
471
+ conditions.append(CVFilter.hardskills.overlap(filter.hardskills))
472
 
473
  if filter.softskills:
474
+ conditions.append(CVFilter.softskills.overlap(filter.softskills))
475
 
476
  if filter.certifications:
477
+ conditions.append(CVFilter.certifications.overlap(filter.certifications))
478
 
479
  if filter.business_domain:
480
+ conditions.append(CVFilter.business_domain.overlap(filter.business_domain))
481
 
482
  # ⛔ Prevent full table scan
483
  if not conditions:
interfaces/api/profile.py CHANGED
@@ -1,3 +1,5 @@
 
 
1
  from externals.databases.pg_models import CVUser
2
  from externals.databases.database import get_db
3
  from fastapi import APIRouter, Depends, status, HTTPException
@@ -35,15 +37,14 @@ async def extract_profile(
35
  @router.get("/{profile_id}")
36
  async def get_profile(
37
  profile_id: str,
 
38
  current_user: CVUser = Depends(get_current_user),
39
  ):
40
  """
41
  Get extracted profile by ID
42
  """
43
- knowledge_service = KnowledgeService(user=current_user)
44
-
45
  profile = await knowledge_service.profile.get_profile(profile_id)
46
-
47
  if not profile:
48
  raise HTTPException(
49
  status_code=status.HTTP_404_NOT_FOUND,
@@ -56,7 +57,31 @@ async def get_profile(
56
  status_code=status.HTTP_403_FORBIDDEN,
57
  detail="Not authorized to access this profile",
58
  )
59
-
60
  return profile
61
 
62
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import profile
2
+
3
  from externals.databases.pg_models import CVUser
4
  from externals.databases.database import get_db
5
  from fastapi import APIRouter, Depends, status, HTTPException
 
37
  @router.get("/{profile_id}")
38
  async def get_profile(
39
  profile_id: str,
40
+ db=Depends(get_db),
41
  current_user: CVUser = Depends(get_current_user),
42
  ):
43
  """
44
  Get extracted profile by ID
45
  """
46
+ knowledge_service = KnowledgeService(db=db, user=current_user)
 
47
  profile = await knowledge_service.profile.get_profile(profile_id)
 
48
  if not profile:
49
  raise HTTPException(
50
  status_code=status.HTTP_404_NOT_FOUND,
 
57
  status_code=status.HTTP_403_FORBIDDEN,
58
  detail="Not authorized to access this profile",
59
  )
 
60
  return profile
61
 
62
 
63
+
64
+
65
+ @router.get("/profiles/{criteria_id}")
66
+ async def get_profiles(
67
+ criteria_id: str,
68
+ db=Depends(get_db),
69
+ current_user: CVUser = Depends(get_current_user),
70
+ ):
71
+ """
72
+ Get profiles based on criteria id
73
+ """
74
+
75
+ knowledge_service = KnowledgeService(db=db, user=current_user)
76
+
77
+ profiles = await knowledge_service.profile.get_profiles_by_criteria(criteria_id=criteria_id)
78
+ logger.info(f"retrieved profiles: {profiles}")
79
+
80
+ if not profiles:
81
+ raise HTTPException(
82
+ status_code=status.HTTP_404_NOT_FOUND,
83
+ detail="Profiles not found",
84
+ )
85
+
86
+ return profiles
87
+
services/agentic/profile_scoring.py CHANGED
@@ -491,7 +491,7 @@ class AgenticScoringService:
491
  # Get profile data all
492
  all_profiles = await get_profiles(self.db)
493
  print(f"Found {len(all_profiles)} profiles to be scored")
494
- all_profiles = all_profiles[:5] # FIXME: DELETE LATER
495
 
496
  _weight:CVWeight = await self.weight_service.get_weight_by_weight_id(weight_id=weight_id)
497
  print(f"Found weight: {_weight}")
@@ -572,4 +572,4 @@ class AgenticScoringService:
572
  except Exception as E:
573
  logger.error(f"profile scoring error, {E}")
574
  traceback.print_exc()
575
- raise
 
491
  # Get profile data all
492
  all_profiles = await get_profiles(self.db)
493
  print(f"Found {len(all_profiles)} profiles to be scored")
494
+ # all_profiles = all_profiles[:5] # FIXME: DELETE LATER
495
 
496
  _weight:CVWeight = await self.weight_service.get_weight_by_weight_id(weight_id=weight_id)
497
  print(f"Found weight: {_weight}")
 
572
  except Exception as E:
573
  logger.error(f"profile scoring error, {E}")
574
  traceback.print_exc()
575
+ raise
services/knowledge/get_profile.py CHANGED
@@ -1,4 +1,4 @@
1
- from externals.databases.pg_crud import get_profile_by_filename, get_profile_by_id
2
  from externals.databases.pg_models import CVUser, CVProfile
3
  from services.models.data_model import Profile
4
  from sqlalchemy.ext.asyncio import AsyncSession
@@ -71,4 +71,15 @@ class KnowledgeGetProfileService:
71
  return profiles
72
  except Exception as E:
73
  logger.error(f"❌ get profile error, {E}")
74
- return {}
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from externals.databases.pg_crud import get_profile_by_filename, get_profile_by_id, get_profiles_by_criteria_id
2
  from externals.databases.pg_models import CVUser, CVProfile
3
  from services.models.data_model import Profile
4
  from sqlalchemy.ext.asyncio import AsyncSession
 
71
  return profiles
72
  except Exception as E:
73
  logger.error(f"❌ get profile error, {E}")
74
+ return {}
75
+
76
+
77
+ async def get_profiles_by_criteria(self, criteria_id: str) -> List[CVProfile]:
78
+ try:
79
+ profiles: List[CVProfile] = await get_profiles_by_criteria_id(self.db, criteria_id=criteria_id, current_user=self.user)
80
+ logger.info(f"get_profiles_by_criteria/profiles: {profiles}")
81
+
82
+ return profiles
83
+ except Exception as E:
84
+ logger.error(f"❌ get profiles by criteria id '{criteria_id}' error, {E}")
85
+ return []