ishaq101 commited on
Commit
028a629
Β·
1 Parent(s): 56dd37e

[NOTICKET] Feat: score card

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, or_
5
  from sqlalchemy.ext.asyncio import AsyncSession
6
  from externals.databases.pg_models import (
7
  CVUser,
@@ -200,6 +200,30 @@ async def get_file_by_filename(
200
  return result.scalar_one_or_none()
201
 
202
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
  async def mark_file_extracted(db: AsyncSession, file_id: UUID):
204
  await db.execute(
205
  update(CVFile)
@@ -262,8 +286,13 @@ async def create_profile(
262
  async def get_profile_by_filename(
263
  db: AsyncSession,
264
  filename: str,
 
265
  ) -> Optional[CVProfile]:
266
- stmt = select(CVProfile).where(CVProfile.filename == filename)
 
 
 
 
267
  result = await db.execute(stmt)
268
  return result.scalar_one_or_none()
269
 
 
1
  from typing import Optional, List
2
  from uuid import UUID
3
 
4
+ from sqlalchemy import select, and_, update, delete, or_, func
5
  from sqlalchemy.ext.asyncio import AsyncSession
6
  from externals.databases.pg_models import (
7
  CVUser,
 
200
  return result.scalar_one_or_none()
201
 
202
 
203
+ async def count_files_by_user(
204
+ db: AsyncSession,
205
+ user_id: str,
206
+ ) -> int:
207
+ """Return number of CVFile rows belonging to a user."""
208
+ result = await db.execute(
209
+ select(func.count(CVFile.file_id)).where(CVFile.user_id == user_id)
210
+ )
211
+ return int(result.scalar_one())
212
+
213
+
214
+ async def count_profiles_by_user(
215
+ db: AsyncSession,
216
+ user_id: str,
217
+ ) -> int:
218
+ """Return number of CVProfile rows that are associated with files of a user."""
219
+ result = await db.execute(
220
+ select(func.count(CVProfile.profile_id))
221
+ .join(CVFile, CVProfile.file_id == CVFile.file_id)
222
+ .where(CVFile.user_id == user_id)
223
+ )
224
+ return int(result.scalar_one())
225
+
226
+
227
  async def mark_file_extracted(db: AsyncSession, file_id: UUID):
228
  await db.execute(
229
  update(CVFile)
 
286
  async def get_profile_by_filename(
287
  db: AsyncSession,
288
  filename: str,
289
+ current_user: CVUser,
290
  ) -> Optional[CVProfile]:
291
+ stmt = (
292
+ select(CVProfile)
293
+ .join(CVFile, CVProfile.file_id == CVFile.file_id)
294
+ .where(CVProfile.filename == filename, CVFile.user_id == current_user.user_id)
295
+ )
296
  result = await db.execute(stmt)
297
  return result.scalar_one_or_none()
298
 
externals/storages/azure_blob.py CHANGED
@@ -50,8 +50,6 @@ async def get_container_client() -> ContainerClient:
50
  )
51
  return container_client
52
 
53
-
54
-
55
  # async def get_container_client() -> ContainerClient:
56
  # service = await get_blob_service_client()
57
  # container = service.get_container_client(
 
50
  )
51
  return container_client
52
 
 
 
53
  # async def get_container_client() -> ContainerClient:
54
  # service = await get_blob_service_client()
55
  # container = service.get_container_client(
interfaces/api/file.py CHANGED
@@ -51,7 +51,7 @@ async def upload_knowledge_many(
51
  }
52
 
53
  @router.get(
54
- "/{user_id}",
55
  status_code=status.HTTP_200_OK,
56
  summary="Get file metadata by user ID",
57
  )
@@ -105,3 +105,41 @@ async def delete_knowledge_file(
105
  "deleted_by": current_user.email,
106
  }
107
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  }
52
 
53
  @router.get(
54
+ "/user/{user_id}",
55
  status_code=status.HTTP_200_OK,
56
  summary="Get file metadata by user ID",
57
  )
 
105
  "deleted_by": current_user.email,
106
  }
107
 
108
+
109
+
110
+ @router.get(
111
+ "/score_card",
112
+ status_code=status.HTTP_200_OK,
113
+ summary="Get score card data for dashboard summary",
114
+ )
115
+ async def get_score_card(
116
+ db=Depends(get_db),
117
+ current_user=Depends(get_current_user),
118
+ ):
119
+ """
120
+ Retrieve all file metadata uploaded by a specific user.
121
+
122
+ Return:
123
+ - total file
124
+ - total profile extracted
125
+ - percent profile extracted
126
+ """
127
+ try:
128
+ knowledge_service = KnowledgeService(
129
+ db=db,
130
+ user=current_user,
131
+ )
132
+
133
+ data = await knowledge_service.score_card.get_score_card()
134
+
135
+ return {
136
+ "status": "success",
137
+ "message": "Score card retrieved successfully",
138
+ "data": data,
139
+ }
140
+ except Exception as E:
141
+ logger.error(f"get score card error: {E}")
142
+ raise HTTPException(
143
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
144
+ detail=f"get score card error: {E}"
145
+ )
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}")
 
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
+
495
 
496
  _weight:CVWeight = await self.weight_service.get_weight_by_weight_id(weight_id=weight_id)
497
  print(f"Found weight: {_weight}")
services/knowledge/get_profile.py CHANGED
@@ -21,14 +21,10 @@ class KnowledgeGetProfileService:
21
 
22
  logger.info(f"πŸ“„ Fetching profile: {filename}")
23
 
24
- profile = await get_profile_by_filename(self.db, filename)
25
  if not profile:
26
  return None
27
 
28
- # πŸ” Tenant isolation
29
- if profile.tenant_id != self.user.tenant_id:
30
- raise PermissionError("Access denied")
31
-
32
  return profile
33
 
34
 
 
21
 
22
  logger.info(f"πŸ“„ Fetching profile: {filename}")
23
 
24
+ profile = await get_profile_by_filename(self.db, filename, current_user=self.user)
25
  if not profile:
26
  return None
27
 
 
 
 
 
28
  return profile
29
 
30
 
services/knowledge/knowledge_setup.py CHANGED
@@ -16,6 +16,7 @@ from services.knowledge.upload_file import KnowledgeFileService
16
  from services.knowledge.get_profile import KnowledgeGetProfileService
17
  from services.knowledge.delete_file import KnowledgeDeleteService
18
  from services.knowledge.extract_profile import KnowledgeExtractService
 
19
 
20
 
21
  class KnowledgeService:
@@ -31,3 +32,4 @@ class KnowledgeService:
31
  self.profile = KnowledgeGetProfileService(db, user)
32
  self.delete = KnowledgeDeleteService(db, user)
33
  self.extract = KnowledgeExtractService(db, user)
 
 
16
  from services.knowledge.get_profile import KnowledgeGetProfileService
17
  from services.knowledge.delete_file import KnowledgeDeleteService
18
  from services.knowledge.extract_profile import KnowledgeExtractService
19
+ from services.knowledge.score_card import KnowledgeScoreCardService
20
 
21
 
22
  class KnowledgeService:
 
32
  self.profile = KnowledgeGetProfileService(db, user)
33
  self.delete = KnowledgeDeleteService(db, user)
34
  self.extract = KnowledgeExtractService(db, user)
35
+ self.score_card = KnowledgeScoreCardService(db, user)
services/knowledge/score_card.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from externals.databases.pg_crud import (
2
+ count_files_by_user,
3
+ count_profiles_by_user,
4
+ )
5
+ from externals.databases.pg_models import CVUser
6
+ from sqlalchemy.ext.asyncio import AsyncSession
7
+ from utils.logger import get_logger
8
+
9
+ logger = get_logger("score card")
10
+
11
+
12
+ class KnowledgeScoreCardService:
13
+
14
+ def __init__(self, db: AsyncSession, user: CVUser):
15
+ self.db = db
16
+ self.user = user
17
+
18
+ async def get_score_card(self) -> dict:
19
+ """
20
+ Retrieve extracted profile securely
21
+ """
22
+ # Count total files for this user (from cv_file table)
23
+ total_file = await count_files_by_user(self.db, self.user.user_id)
24
+
25
+ # Count total extracted profiles associated with this user's files
26
+ total_extracted = await count_profiles_by_user(self.db, self.user.user_id)
27
+
28
+ # percent as 0-1, safely handle division by zero
29
+ percent_extracted = round(total_extracted / total_file, 2) if total_file else 0.0
30
+
31
+ score_card = {
32
+ "total_file": total_file,
33
+ "total_extracted": total_extracted,
34
+ "percent_extracted": percent_extracted*100,
35
+ }
36
+
37
+ return score_card
services/knowledge/upload_file.py CHANGED
@@ -90,8 +90,6 @@ class KnowledgeFileService:
90
  if existing:
91
  return existing
92
 
93
-
94
-
95
 
96
  async def get_files_by_user(self, user_id: str):
97
  """
 
90
  if existing:
91
  return existing
92
 
 
 
93
 
94
  async def get_files_by_user(self, user_id: str):
95
  """