Spaces:
Sleeping
Sleeping
Commit ·
3d40e5f
1
Parent(s): 15145f6
prepare for huggingface deployment
Browse files- .dockerignore +34 -0
- .gitignore +1 -0
- Dockerfile +28 -0
- backend/.dockerignore +0 -73
- backend/Dockerfile +0 -24
- backend/routers/admin.py +59 -0
- backend/schemas/admin.py +20 -1
- backend/services/chatbot_service.py +261 -0
- resume-jd-verification-2026-04-10T05-15-44-248Z.pdf +0 -646
.dockerignore
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Environment
|
| 2 |
+
.env
|
| 3 |
+
|
| 4 |
+
# Python cache
|
| 5 |
+
__pycache__/
|
| 6 |
+
*.pyc
|
| 7 |
+
*.pyo
|
| 8 |
+
*.pyd
|
| 9 |
+
|
| 10 |
+
# Virtual environments
|
| 11 |
+
venv/
|
| 12 |
+
.venv/
|
| 13 |
+
env/
|
| 14 |
+
inter/
|
| 15 |
+
|
| 16 |
+
# Node / frontend (not needed for backend deploy)
|
| 17 |
+
frontend/
|
| 18 |
+
node_modules/
|
| 19 |
+
|
| 20 |
+
# Uploads (runtime data)
|
| 21 |
+
uploads/
|
| 22 |
+
|
| 23 |
+
# Git
|
| 24 |
+
.git/
|
| 25 |
+
.gitignore
|
| 26 |
+
|
| 27 |
+
# OS / IDE junk
|
| 28 |
+
.DS_Store
|
| 29 |
+
Thumbs.db
|
| 30 |
+
.idea/
|
| 31 |
+
.vscode/
|
| 32 |
+
*.swp
|
| 33 |
+
|
| 34 |
+
backend/.env
|
.gitignore
CHANGED
|
@@ -15,3 +15,4 @@ WORKFLOW.md
|
|
| 15 |
voice_name_list_xtts.txt
|
| 16 |
|
| 17 |
|
|
|
|
|
|
| 15 |
voice_name_list_xtts.txt
|
| 16 |
|
| 17 |
|
| 18 |
+
SYSTEM_ARCHITECTURE.md
|
Dockerfile
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.10-slim
|
| 2 |
+
|
| 3 |
+
ENV PYTHONUNBUFFERED=1
|
| 4 |
+
ENV PYTHONDONTWRITEBYTECODE=1
|
| 5 |
+
|
| 6 |
+
WORKDIR /app
|
| 7 |
+
|
| 8 |
+
RUN apt-get update && apt-get install -y \
|
| 9 |
+
gcc \
|
| 10 |
+
ffmpeg \
|
| 11 |
+
libglib2.0-0 \
|
| 12 |
+
libsm6 \
|
| 13 |
+
libxext6 \
|
| 14 |
+
libxrender-dev \
|
| 15 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 16 |
+
|
| 17 |
+
# Copy ONLY backend requirements
|
| 18 |
+
COPY backend/requirements.txt .
|
| 19 |
+
|
| 20 |
+
RUN pip install --no-cache-dir --upgrade pip \
|
| 21 |
+
&& pip install --no-cache-dir -r requirements.txt
|
| 22 |
+
|
| 23 |
+
# Copy backend code into container
|
| 24 |
+
COPY backend/ .
|
| 25 |
+
|
| 26 |
+
EXPOSE 7860
|
| 27 |
+
|
| 28 |
+
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
|
backend/.dockerignore
DELETED
|
@@ -1,73 +0,0 @@
|
|
| 1 |
-
# Environments
|
| 2 |
-
.env
|
| 3 |
-
|
| 4 |
-
# Byte-compiled / optimized / DLL files
|
| 5 |
-
__pycache__/
|
| 6 |
-
*.py[cod]
|
| 7 |
-
*$py.class
|
| 8 |
-
|
| 9 |
-
# Virtual environments
|
| 10 |
-
venv/
|
| 11 |
-
.venv/
|
| 12 |
-
env/
|
| 13 |
-
.env/
|
| 14 |
-
|
| 15 |
-
# Distribution / packaging
|
| 16 |
-
build/
|
| 17 |
-
develop-eggs/
|
| 18 |
-
dist/
|
| 19 |
-
downloads/
|
| 20 |
-
eggs/
|
| 21 |
-
.eggs/
|
| 22 |
-
lib/
|
| 23 |
-
lib64/
|
| 24 |
-
parts/
|
| 25 |
-
sdist/
|
| 26 |
-
var/
|
| 27 |
-
wheels/
|
| 28 |
-
share/python-wheels/
|
| 29 |
-
*.egg-info/
|
| 30 |
-
.installed.cfg
|
| 31 |
-
*.egg
|
| 32 |
-
|
| 33 |
-
# PyInstaller
|
| 34 |
-
*.manifest
|
| 35 |
-
*.spec
|
| 36 |
-
|
| 37 |
-
# Installer logs
|
| 38 |
-
pip-log.txt
|
| 39 |
-
pip-delete-this-directory.txt
|
| 40 |
-
|
| 41 |
-
# Unit test / coverage reports
|
| 42 |
-
htmlcov/
|
| 43 |
-
.tox/
|
| 44 |
-
.nox/
|
| 45 |
-
.coverage
|
| 46 |
-
.coverage.*
|
| 47 |
-
.cache
|
| 48 |
-
nosetests.xml
|
| 49 |
-
coverage.xml
|
| 50 |
-
*.cover
|
| 51 |
-
*.py,cover
|
| 52 |
-
.hypothesis/
|
| 53 |
-
.pytest_cache/
|
| 54 |
-
cover/
|
| 55 |
-
|
| 56 |
-
# IDEs
|
| 57 |
-
.idea/
|
| 58 |
-
.vscode/
|
| 59 |
-
*.swp
|
| 60 |
-
*.swo
|
| 61 |
-
|
| 62 |
-
# OS generated files
|
| 63 |
-
.DS_Store
|
| 64 |
-
.DS_Store?
|
| 65 |
-
._*
|
| 66 |
-
.Spotlight-V100
|
| 67 |
-
.Trashes
|
| 68 |
-
ehthumbs.db
|
| 69 |
-
Thumbs.db
|
| 70 |
-
|
| 71 |
-
# Git
|
| 72 |
-
.git/
|
| 73 |
-
.gitignore
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/Dockerfile
DELETED
|
@@ -1,24 +0,0 @@
|
|
| 1 |
-
# Use the official Python base image
|
| 2 |
-
FROM python:3.10-slim
|
| 3 |
-
|
| 4 |
-
# Set environment variables
|
| 5 |
-
ENV PYTHONDONTWRITEBYTECODE=1
|
| 6 |
-
ENV PYTHONUNBUFFERED=1
|
| 7 |
-
|
| 8 |
-
# Set the working directory in the container
|
| 9 |
-
WORKDIR /app
|
| 10 |
-
|
| 11 |
-
# Copy the requirements file into the container
|
| 12 |
-
COPY requirements.txt .
|
| 13 |
-
|
| 14 |
-
# Install dependencies
|
| 15 |
-
RUN pip install --no-cache-dir -r requirements.txt
|
| 16 |
-
|
| 17 |
-
# Copy the rest of the application code
|
| 18 |
-
COPY . .
|
| 19 |
-
|
| 20 |
-
# Expose port 8000
|
| 21 |
-
EXPOSE 8000
|
| 22 |
-
|
| 23 |
-
# Command to run the application
|
| 24 |
-
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/routers/admin.py
CHANGED
|
@@ -563,7 +563,66 @@ from services.chatbot_service import (
|
|
| 563 |
process_chatbot_query,
|
| 564 |
update_student_info,
|
| 565 |
generate_excel,
|
|
|
|
| 566 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 567 |
|
| 568 |
|
| 569 |
@router.post("/chatbot/query")
|
|
|
|
| 563 |
process_chatbot_query,
|
| 564 |
update_student_info,
|
| 565 |
generate_excel,
|
| 566 |
+
filter_students_structured,
|
| 567 |
)
|
| 568 |
+
from schemas.admin import StudentFilterRequest, StudentFilterExportRequest
|
| 569 |
+
|
| 570 |
+
|
| 571 |
+
@router.post("/students/filter")
|
| 572 |
+
async def students_filter(
|
| 573 |
+
request: StudentFilterRequest,
|
| 574 |
+
current_user: dict = Depends(require_role("admin")),
|
| 575 |
+
):
|
| 576 |
+
"""Structured student filter — no AI, direct params (sort + date range + multi-test)."""
|
| 577 |
+
try:
|
| 578 |
+
result = await filter_students_structured(
|
| 579 |
+
group_test_ids=request.group_test_ids,
|
| 580 |
+
jd_id=request.jd_id,
|
| 581 |
+
start_date=request.start_date,
|
| 582 |
+
end_date=request.end_date,
|
| 583 |
+
top_k=request.top_k,
|
| 584 |
+
min_score=request.min_score,
|
| 585 |
+
sort_fields=request.sort_fields,
|
| 586 |
+
sort_orders=request.sort_orders,
|
| 587 |
+
)
|
| 588 |
+
return result
|
| 589 |
+
except Exception as e:
|
| 590 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 591 |
+
|
| 592 |
+
|
| 593 |
+
@router.post("/students/export-excel")
|
| 594 |
+
async def students_export_excel(
|
| 595 |
+
request: StudentFilterExportRequest,
|
| 596 |
+
current_user: dict = Depends(require_role("admin")),
|
| 597 |
+
):
|
| 598 |
+
"""Generate styled Excel for structured student filter results."""
|
| 599 |
+
try:
|
| 600 |
+
bio = generate_excel(
|
| 601 |
+
rows=request.rows,
|
| 602 |
+
topic_columns=request.topic_columns,
|
| 603 |
+
group_test_name=request.group_test_name,
|
| 604 |
+
)
|
| 605 |
+
safe_name = request.group_test_name.replace(" ", "_").replace("/", "-")[:40]
|
| 606 |
+
filename = f"{safe_name}_students.xlsx"
|
| 607 |
+
return StreamingResponse(
|
| 608 |
+
bio,
|
| 609 |
+
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
| 610 |
+
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
|
| 611 |
+
)
|
| 612 |
+
except Exception as e:
|
| 613 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 614 |
+
|
| 615 |
+
|
| 616 |
+
@router.patch("/students")
|
| 617 |
+
async def update_student(
|
| 618 |
+
request: ChatbotStudentUpdate,
|
| 619 |
+
current_user: dict = Depends(require_role("admin")),
|
| 620 |
+
):
|
| 621 |
+
"""Admin corrects a student's reg_no or name (shared by both filter endpoints)."""
|
| 622 |
+
try:
|
| 623 |
+
return await update_student_info(request.user_id, request.reg_no, request.name)
|
| 624 |
+
except ValueError as e:
|
| 625 |
+
raise HTTPException(status_code=400, detail=str(e))
|
| 626 |
|
| 627 |
|
| 628 |
@router.post("/chatbot/query")
|
backend/schemas/admin.py
CHANGED
|
@@ -111,7 +111,7 @@ class RoleRequirementResponse(BaseModel):
|
|
| 111 |
level: str
|
| 112 |
|
| 113 |
|
| 114 |
-
# ── Chatbot ──────────────────────────────────────────────────────────
|
| 115 |
|
| 116 |
class ChatbotQueryRequest(BaseModel):
|
| 117 |
query: str
|
|
@@ -128,3 +128,22 @@ class ChatbotStudentUpdate(BaseModel):
|
|
| 128 |
user_id: str
|
| 129 |
reg_no: Optional[str] = None
|
| 130 |
name: Optional[str] = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
level: str
|
| 112 |
|
| 113 |
|
| 114 |
+
# ── Chatbot (legacy) ──────────────────────────────────────────────────────────
|
| 115 |
|
| 116 |
class ChatbotQueryRequest(BaseModel):
|
| 117 |
query: str
|
|
|
|
| 128 |
user_id: str
|
| 129 |
reg_no: Optional[str] = None
|
| 130 |
name: Optional[str] = None
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
# ── Structured Student Filter ─────────────────────────────────────────────────
|
| 134 |
+
|
| 135 |
+
class StudentFilterRequest(BaseModel):
|
| 136 |
+
group_test_ids: Optional[List[str]] = None # None = all tests
|
| 137 |
+
jd_id: Optional[str] = None
|
| 138 |
+
start_date: Optional[str] = None # "YYYY-MM-DD"
|
| 139 |
+
end_date: Optional[str] = None # "YYYY-MM-DD"
|
| 140 |
+
top_k: Optional[int] = None
|
| 141 |
+
min_score: Optional[float] = None
|
| 142 |
+
sort_fields: List[str] = ["time"] # ordered priority: "time"|"score"|"duration"
|
| 143 |
+
sort_orders: List[str] = ["desc"] # matching orders: "asc"|"desc"
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
class StudentFilterExportRequest(BaseModel):
|
| 147 |
+
rows: List[dict]
|
| 148 |
+
topic_columns: List[dict]
|
| 149 |
+
group_test_name: str
|
backend/services/chatbot_service.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
| 3 |
import json
|
| 4 |
import re
|
| 5 |
from collections import defaultdict
|
|
|
|
| 6 |
from io import BytesIO
|
| 7 |
|
| 8 |
from bson import ObjectId
|
|
@@ -403,3 +404,263 @@ def generate_excel(rows: list[dict], topic_columns: list[dict], group_test_name:
|
|
| 403 |
wb.save(bio)
|
| 404 |
bio.seek(0)
|
| 405 |
return bio
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
import json
|
| 4 |
import re
|
| 5 |
from collections import defaultdict
|
| 6 |
+
from datetime import datetime, timezone, timedelta
|
| 7 |
from io import BytesIO
|
| 8 |
|
| 9 |
from bson import ObjectId
|
|
|
|
| 404 |
wb.save(bio)
|
| 405 |
bio.seek(0)
|
| 406 |
return bio
|
| 407 |
+
|
| 408 |
+
|
| 409 |
+
# ─── Structured Student Filter (no Gemini) ───────────────────────────────────
|
| 410 |
+
|
| 411 |
+
def _parse_date(date_str: str | None, end_of_day: bool = False):
|
| 412 |
+
"""Parse YYYY-MM-DD string to UTC-aware datetime, or None."""
|
| 413 |
+
if not date_str:
|
| 414 |
+
return None
|
| 415 |
+
try:
|
| 416 |
+
dt = datetime.strptime(date_str.strip(), "%Y-%m-%d").replace(tzinfo=timezone.utc)
|
| 417 |
+
if end_of_day:
|
| 418 |
+
dt = dt + timedelta(days=1)
|
| 419 |
+
return dt
|
| 420 |
+
except ValueError:
|
| 421 |
+
return None
|
| 422 |
+
|
| 423 |
+
|
| 424 |
+
def _compute_duration(started_at, completed_at) -> float | None:
|
| 425 |
+
"""Duration in minutes, rounded to 1 dp. Returns None if either timestamp missing."""
|
| 426 |
+
if started_at is None or completed_at is None:
|
| 427 |
+
return None
|
| 428 |
+
try:
|
| 429 |
+
# Handle both datetime objects and ISO strings
|
| 430 |
+
if isinstance(started_at, str):
|
| 431 |
+
started_at = datetime.fromisoformat(started_at.replace("Z", "+00:00"))
|
| 432 |
+
if isinstance(completed_at, str):
|
| 433 |
+
completed_at = datetime.fromisoformat(completed_at.replace("Z", "+00:00"))
|
| 434 |
+
delta = completed_at - started_at
|
| 435 |
+
return round(max(delta.total_seconds(), 0) / 60, 1)
|
| 436 |
+
except Exception:
|
| 437 |
+
return None
|
| 438 |
+
|
| 439 |
+
|
| 440 |
+
async def filter_students_structured(
|
| 441 |
+
group_test_ids: list[str] | None,
|
| 442 |
+
jd_id: str | None,
|
| 443 |
+
start_date: str | None,
|
| 444 |
+
end_date: str | None,
|
| 445 |
+
top_k: int | None,
|
| 446 |
+
min_score: float | None,
|
| 447 |
+
sort_fields: list[str] | None = None,
|
| 448 |
+
sort_orders: list[str] | None = None,
|
| 449 |
+
) -> dict:
|
| 450 |
+
"""Structured student filter — no Gemini, explicit params, composable."""
|
| 451 |
+
db = get_db()
|
| 452 |
+
|
| 453 |
+
# ── Fetch all group tests for metadata ────────────────────────────────────
|
| 454 |
+
gt_cursor = db[GROUP_TESTS].find({}).sort("created_at", -1)
|
| 455 |
+
gt_docs = await gt_cursor.to_list(length=300)
|
| 456 |
+
all_group_tests: dict[str, dict] = {
|
| 457 |
+
str(d["_id"]): {
|
| 458 |
+
"id": str(d["_id"]),
|
| 459 |
+
"name": d.get("name", ""),
|
| 460 |
+
"topic_ids": d.get("topic_ids") or [],
|
| 461 |
+
}
|
| 462 |
+
for d in gt_docs
|
| 463 |
+
}
|
| 464 |
+
|
| 465 |
+
# ── Fetch JD info if provided ─────────────────────────────────────────────
|
| 466 |
+
jd_content, jd_req_skills = (None, [])
|
| 467 |
+
if jd_id:
|
| 468 |
+
jd_content, jd_req_skills = await _jd_skills(jd_id, db)
|
| 469 |
+
use_jd_ranking = bool(jd_req_skills)
|
| 470 |
+
|
| 471 |
+
# ── Build MongoDB query ───────────────────────────────────────────────────
|
| 472 |
+
results_filter: dict = {}
|
| 473 |
+
|
| 474 |
+
if group_test_ids:
|
| 475 |
+
results_filter["group_test_id"] = {"$in": group_test_ids}
|
| 476 |
+
|
| 477 |
+
start_dt = _parse_date(start_date, end_of_day=False)
|
| 478 |
+
end_dt = _parse_date(end_date, end_of_day=True)
|
| 479 |
+
if start_dt or end_dt:
|
| 480 |
+
date_filter: dict = {}
|
| 481 |
+
if start_dt:
|
| 482 |
+
date_filter["$gte"] = start_dt
|
| 483 |
+
if end_dt:
|
| 484 |
+
date_filter["$lt"] = end_dt
|
| 485 |
+
results_filter["started_at"] = date_filter
|
| 486 |
+
|
| 487 |
+
results_cursor = db[GROUP_TEST_RESULTS].find(results_filter)
|
| 488 |
+
results_docs = await results_cursor.to_list(length=3000)
|
| 489 |
+
|
| 490 |
+
# ── Collect topic columns from selected group tests ───────────────────────
|
| 491 |
+
selected_gt_ids: set[str] = (
|
| 492 |
+
set(group_test_ids) if group_test_ids
|
| 493 |
+
else {str(d["_id"]) for d in gt_docs}
|
| 494 |
+
)
|
| 495 |
+
|
| 496 |
+
topic_columns: list[dict] = []
|
| 497 |
+
topic_seen: set[str] = set()
|
| 498 |
+
group_test_name = "All Group Tests"
|
| 499 |
+
|
| 500 |
+
if group_test_ids and len(group_test_ids) == 1:
|
| 501 |
+
gt = all_group_tests.get(group_test_ids[0])
|
| 502 |
+
if gt:
|
| 503 |
+
group_test_name = gt["name"]
|
| 504 |
+
for tid in gt["topic_ids"]:
|
| 505 |
+
if tid not in topic_seen:
|
| 506 |
+
topic_seen.add(tid)
|
| 507 |
+
try:
|
| 508 |
+
t = await db[TOPICS].find_one({"_id": ObjectId(tid)})
|
| 509 |
+
except Exception:
|
| 510 |
+
t = None
|
| 511 |
+
if t:
|
| 512 |
+
topic_columns.append({"id": tid, "name": t.get("name", tid)})
|
| 513 |
+
elif group_test_ids and len(group_test_ids) > 1:
|
| 514 |
+
names = [all_group_tests[gid]["name"] for gid in group_test_ids if gid in all_group_tests]
|
| 515 |
+
group_test_name = ", ".join(names) if names else "Multiple Tests"
|
| 516 |
+
for gid in group_test_ids:
|
| 517 |
+
gt = all_group_tests.get(gid)
|
| 518 |
+
if not gt:
|
| 519 |
+
continue
|
| 520 |
+
for tid in gt["topic_ids"]:
|
| 521 |
+
if tid not in topic_seen:
|
| 522 |
+
topic_seen.add(tid)
|
| 523 |
+
try:
|
| 524 |
+
t = await db[TOPICS].find_one({"_id": ObjectId(tid)})
|
| 525 |
+
except Exception:
|
| 526 |
+
t = None
|
| 527 |
+
if t:
|
| 528 |
+
topic_columns.append({"id": tid, "name": t.get("name", tid)})
|
| 529 |
+
|
| 530 |
+
# ── Group by (user_id, group_test_id) → pick best attempt ────────────────
|
| 531 |
+
attempts_map: dict[tuple, list] = defaultdict(list)
|
| 532 |
+
for r in results_docs:
|
| 533 |
+
uid = r.get("user_id", "")
|
| 534 |
+
gtid = r.get("group_test_id", "")
|
| 535 |
+
if uid:
|
| 536 |
+
attempts_map[(uid, gtid)].append(r)
|
| 537 |
+
|
| 538 |
+
rows: list[dict] = []
|
| 539 |
+
|
| 540 |
+
for (uid, gt_id), attempts in attempts_map.items():
|
| 541 |
+
best = None
|
| 542 |
+
for attempt in attempts:
|
| 543 |
+
attempt = await _refresh_topic_statuses(attempt, db)
|
| 544 |
+
score = attempt.get("overall_score") or 0
|
| 545 |
+
if best is None or score > (best.get("overall_score") or 0):
|
| 546 |
+
best = attempt
|
| 547 |
+
|
| 548 |
+
user = await _user_info(uid, db)
|
| 549 |
+
|
| 550 |
+
# Per-topic scores
|
| 551 |
+
topic_scores: dict[str, dict] = {}
|
| 552 |
+
for tr in best.get("topic_results") or []:
|
| 553 |
+
tid = tr.get("topic_id", "")
|
| 554 |
+
topic_scores[tid] = {
|
| 555 |
+
"topic_name": tr.get("topic_name", ""),
|
| 556 |
+
"score": tr.get("overall_score"),
|
| 557 |
+
"status": tr.get("status", "pending"),
|
| 558 |
+
}
|
| 559 |
+
|
| 560 |
+
# JD skill match
|
| 561 |
+
skill_match: float | None = None
|
| 562 |
+
if use_jd_ranking:
|
| 563 |
+
skills_doc = await db[SKILLS].find_one({"user_id": uid})
|
| 564 |
+
student_skills = (skills_doc or {}).get("skills") or []
|
| 565 |
+
skill_match = _skill_match_pct(student_skills, jd_req_skills)
|
| 566 |
+
|
| 567 |
+
# Attempt time & duration
|
| 568 |
+
started_at = best.get("started_at")
|
| 569 |
+
completed_at = best.get("completed_at")
|
| 570 |
+
attempt_time: str | None = None
|
| 571 |
+
if started_at is not None:
|
| 572 |
+
try:
|
| 573 |
+
attempt_time = started_at.isoformat() if isinstance(started_at, datetime) else str(started_at)
|
| 574 |
+
except Exception:
|
| 575 |
+
attempt_time = None
|
| 576 |
+
|
| 577 |
+
# Collect topic columns dynamically when showing all tests
|
| 578 |
+
if not group_test_ids:
|
| 579 |
+
gt = all_group_tests.get(gt_id)
|
| 580 |
+
if gt:
|
| 581 |
+
for tid in gt["topic_ids"]:
|
| 582 |
+
if tid not in topic_seen:
|
| 583 |
+
topic_seen.add(tid)
|
| 584 |
+
try:
|
| 585 |
+
t = await db[TOPICS].find_one({"_id": ObjectId(tid)})
|
| 586 |
+
except Exception:
|
| 587 |
+
t = None
|
| 588 |
+
if t:
|
| 589 |
+
topic_columns.append({"id": tid, "name": t.get("name", tid)})
|
| 590 |
+
|
| 591 |
+
gt_info = all_group_tests.get(gt_id, {})
|
| 592 |
+
|
| 593 |
+
row = {
|
| 594 |
+
"user_id": uid,
|
| 595 |
+
"reg_no": user["reg_no"],
|
| 596 |
+
"name": user["name"],
|
| 597 |
+
"email": user["email"],
|
| 598 |
+
"group_test_id": gt_id,
|
| 599 |
+
"group_test_name": best.get("group_test_name") or gt_info.get("name", ""),
|
| 600 |
+
"overall_score": round(best.get("overall_score") or 0, 1),
|
| 601 |
+
"total_attempts": len(attempts),
|
| 602 |
+
"status": best.get("status", "in_progress"),
|
| 603 |
+
"topic_scores": topic_scores,
|
| 604 |
+
"skill_match": skill_match,
|
| 605 |
+
"rank": 0,
|
| 606 |
+
"attempt_time": attempt_time,
|
| 607 |
+
"duration_minutes": _compute_duration(started_at, completed_at),
|
| 608 |
+
}
|
| 609 |
+
rows.append(row)
|
| 610 |
+
|
| 611 |
+
# ── Min score filter ──────────────────────────────────────────────────────
|
| 612 |
+
if min_score is not None:
|
| 613 |
+
rows = [r for r in rows if (r["overall_score"] or 0) >= min_score]
|
| 614 |
+
|
| 615 |
+
# ── Multi-sort ────────────────────────────────────────────────────────────
|
| 616 |
+
_fields = sort_fields if sort_fields else ["time"]
|
| 617 |
+
_orders = sort_orders if sort_orders else ["desc"]
|
| 618 |
+
_INF = float("inf")
|
| 619 |
+
_NEG_INF = float("-inf")
|
| 620 |
+
|
| 621 |
+
def _row_key(r: dict, field: str, order: str):
|
| 622 |
+
"""Return a comparable key, handling None with order-aware sentinel."""
|
| 623 |
+
desc = order.lower() == "desc"
|
| 624 |
+
if field == "score":
|
| 625 |
+
v = r.get("overall_score") or 0
|
| 626 |
+
return -v if desc else v
|
| 627 |
+
elif field == "duration":
|
| 628 |
+
v = r.get("duration_minutes")
|
| 629 |
+
if v is None:
|
| 630 |
+
return _INF # always sort None to end
|
| 631 |
+
return -v if desc else v
|
| 632 |
+
else: # "time"
|
| 633 |
+
v = r.get("attempt_time") or ""
|
| 634 |
+
# For strings, desc means we negate via reverse tuple trick below
|
| 635 |
+
return v
|
| 636 |
+
|
| 637 |
+
# Apply sorts in reverse priority order (stable sort)
|
| 638 |
+
paired = list(zip(_fields, _orders))
|
| 639 |
+
for field, order in reversed(paired):
|
| 640 |
+
desc = order.lower() == "desc"
|
| 641 |
+
if field == "time":
|
| 642 |
+
rows.sort(key=lambda r: r.get("attempt_time") or "", reverse=desc)
|
| 643 |
+
elif field == "score":
|
| 644 |
+
rows.sort(key=lambda r: r.get("overall_score") or 0, reverse=desc)
|
| 645 |
+
elif field == "duration":
|
| 646 |
+
rows.sort(
|
| 647 |
+
key=lambda r: r["duration_minutes"] if r["duration_minutes"] is not None
|
| 648 |
+
else (_NEG_INF if desc else _INF),
|
| 649 |
+
reverse=desc,
|
| 650 |
+
)
|
| 651 |
+
|
| 652 |
+
# ── Assign ranks ──────────────────────────────────────────────────────────
|
| 653 |
+
for i, row in enumerate(rows):
|
| 654 |
+
row["rank"] = i + 1
|
| 655 |
+
|
| 656 |
+
# ── Top-K slice ───────────────────────────────────────────────────────────
|
| 657 |
+
if top_k and top_k > 0:
|
| 658 |
+
rows = rows[:top_k]
|
| 659 |
+
|
| 660 |
+
return {
|
| 661 |
+
"group_test_name": group_test_name,
|
| 662 |
+
"group_test_id": group_test_ids[0] if group_test_ids and len(group_test_ids) == 1 else None,
|
| 663 |
+
"topic_columns": topic_columns,
|
| 664 |
+
"rows": rows,
|
| 665 |
+
"total": len(rows),
|
| 666 |
+
}
|
resume-jd-verification-2026-04-10T05-15-44-248Z.pdf
DELETED
|
@@ -1,646 +0,0 @@
|
|
| 1 |
-
%PDF-1.3
|
| 2 |
-
%�߬�
|
| 3 |
-
3 0 obj
|
| 4 |
-
<</Type /Page
|
| 5 |
-
/Parent 1 0 R
|
| 6 |
-
/Resources 2 0 R
|
| 7 |
-
/MediaBox [0 0 595.2799999999999727 841.8899999999999864]
|
| 8 |
-
/Contents 4 0 R
|
| 9 |
-
>>
|
| 10 |
-
endobj
|
| 11 |
-
4 0 obj
|
| 12 |
-
<<
|
| 13 |
-
/Length 5607
|
| 14 |
-
>>
|
| 15 |
-
stream
|
| 16 |
-
0.200025 w
|
| 17 |
-
0 G
|
| 18 |
-
BT
|
| 19 |
-
/F2 16 Tf
|
| 20 |
-
18.3999999999999986 TL
|
| 21 |
-
0 g
|
| 22 |
-
40. 795.8899999999999864 Td
|
| 23 |
-
(Resume vs Job Description Verification) Tj
|
| 24 |
-
ET
|
| 25 |
-
BT
|
| 26 |
-
/F2 11 Tf
|
| 27 |
-
12.6499999999999986 TL
|
| 28 |
-
0 g
|
| 29 |
-
40. 771.8899999999999864 Td
|
| 30 |
-
(Verification ID:) Tj
|
| 31 |
-
ET
|
| 32 |
-
BT
|
| 33 |
-
/F1 11 Tf
|
| 34 |
-
12.6499999999999986 TL
|
| 35 |
-
0 g
|
| 36 |
-
130. 771.8899999999999864 Td
|
| 37 |
-
(b5519c4e-2ab1-4bbd-a5ca-ff133b558b5b) Tj
|
| 38 |
-
ET
|
| 39 |
-
BT
|
| 40 |
-
/F2 11 Tf
|
| 41 |
-
12.6499999999999986 TL
|
| 42 |
-
0 g
|
| 43 |
-
40. 753.8899999999999864 Td
|
| 44 |
-
(Saved At:) Tj
|
| 45 |
-
ET
|
| 46 |
-
BT
|
| 47 |
-
/F1 11 Tf
|
| 48 |
-
12.6499999999999986 TL
|
| 49 |
-
0 g
|
| 50 |
-
130. 753.8899999999999864 Td
|
| 51 |
-
(4/10/2026, 10:45:29 AM) Tj
|
| 52 |
-
ET
|
| 53 |
-
BT
|
| 54 |
-
/F2 11 Tf
|
| 55 |
-
12.6499999999999986 TL
|
| 56 |
-
0 g
|
| 57 |
-
40. 735.8899999999999864 Td
|
| 58 |
-
(Role:) Tj
|
| 59 |
-
ET
|
| 60 |
-
BT
|
| 61 |
-
/F1 11 Tf
|
| 62 |
-
12.6499999999999986 TL
|
| 63 |
-
0 g
|
| 64 |
-
130. 735.8899999999999864 Td
|
| 65 |
-
(Generative AI Engineer) Tj
|
| 66 |
-
ET
|
| 67 |
-
BT
|
| 68 |
-
/F2 16 Tf
|
| 69 |
-
18.3999999999999986 TL
|
| 70 |
-
0 g
|
| 71 |
-
40. 711.8899999999999864 Td
|
| 72 |
-
(Job Description Snapshot) Tj
|
| 73 |
-
ET
|
| 74 |
-
BT
|
| 75 |
-
/F2 11 Tf
|
| 76 |
-
12.6499999999999986 TL
|
| 77 |
-
0 g
|
| 78 |
-
40. 687.8899999999999864 Td
|
| 79 |
-
(JD Title:) Tj
|
| 80 |
-
ET
|
| 81 |
-
BT
|
| 82 |
-
/F1 11 Tf
|
| 83 |
-
12.6499999999999986 TL
|
| 84 |
-
0 g
|
| 85 |
-
130. 687.8899999999999864 Td
|
| 86 |
-
(AI Engineering Intern) Tj
|
| 87 |
-
ET
|
| 88 |
-
BT
|
| 89 |
-
/F2 11 Tf
|
| 90 |
-
12.6499999999999986 TL
|
| 91 |
-
0 g
|
| 92 |
-
40. 669.8899999999999864 Td
|
| 93 |
-
(Company:) Tj
|
| 94 |
-
ET
|
| 95 |
-
BT
|
| 96 |
-
/F1 11 Tf
|
| 97 |
-
12.6499999999999986 TL
|
| 98 |
-
0 g
|
| 99 |
-
130. 669.8899999999999864 Td
|
| 100 |
-
(-) Tj
|
| 101 |
-
ET
|
| 102 |
-
BT
|
| 103 |
-
/F2 11 Tf
|
| 104 |
-
12.6499999999999986 TL
|
| 105 |
-
0 g
|
| 106 |
-
40. 651.8899999999999864 Td
|
| 107 |
-
(Required Skills:) Tj
|
| 108 |
-
ET
|
| 109 |
-
BT
|
| 110 |
-
/F1 11 Tf
|
| 111 |
-
12.6499999999999986 TL
|
| 112 |
-
0 g
|
| 113 |
-
130. 651.8899999999999864 Td
|
| 114 |
-
(Basic understanding of Machine Learning concepts \(supervised/unsupervised learning\),) Tj
|
| 115 |
-
T* (Familiarity with Python and libraries like NumPy, Pandas, Scikit-learn, Knowledge of) Tj
|
| 116 |
-
T* (deep learning frameworks \(e.g., TensorFlow or PyTorch\) is a plus Strong analytical and) Tj
|
| 117 |
-
T* (problem-solving skills) Tj
|
| 118 |
-
ET
|
| 119 |
-
BT
|
| 120 |
-
/F2 11 Tf
|
| 121 |
-
12.6499999999999986 TL
|
| 122 |
-
0 g
|
| 123 |
-
40. 595.8899999999999864 Td
|
| 124 |
-
(JD Description:) Tj
|
| 125 |
-
ET
|
| 126 |
-
BT
|
| 127 |
-
/F1 11 Tf
|
| 128 |
-
12.6499999999999986 TL
|
| 129 |
-
0 g
|
| 130 |
-
130. 595.8899999999999864 Td
|
| 131 |
-
(Key Responsibilities) Tj
|
| 132 |
-
T* () Tj
|
| 133 |
-
T* (Assist in developing and implementing AI/ML models and algorithms) Tj
|
| 134 |
-
T* (Work on data preprocessing, cleaning, and analysis) Tj
|
| 135 |
-
T* (Support model training, evaluation, and optimization) Tj
|
| 136 |
-
T* (Conduct research on the latest AI trends and technologies) Tj
|
| 137 |
-
T* (Collaborate with engineers and product teams to integrate AI solutions) Tj
|
| 138 |
-
T* (Document experiments, processes, and results) Tj
|
| 139 |
-
T* (Participate in brainstorming and problem-solving sessions) Tj
|
| 140 |
-
ET
|
| 141 |
-
BT
|
| 142 |
-
/F2 16 Tf
|
| 143 |
-
18.3999999999999986 TL
|
| 144 |
-
0 g
|
| 145 |
-
40. 463.8899999999999864 Td
|
| 146 |
-
(Resume Snapshot) Tj
|
| 147 |
-
ET
|
| 148 |
-
BT
|
| 149 |
-
/F2 11 Tf
|
| 150 |
-
12.6499999999999986 TL
|
| 151 |
-
0 g
|
| 152 |
-
40. 439.8899999999999864 Td
|
| 153 |
-
(Resume File:) Tj
|
| 154 |
-
ET
|
| 155 |
-
BT
|
| 156 |
-
/F1 11 Tf
|
| 157 |
-
12.6499999999999986 TL
|
| 158 |
-
0 g
|
| 159 |
-
130. 439.8899999999999864 Td
|
| 160 |
-
(Resume.pdf) Tj
|
| 161 |
-
ET
|
| 162 |
-
BT
|
| 163 |
-
/F2 11 Tf
|
| 164 |
-
12.6499999999999986 TL
|
| 165 |
-
0 g
|
| 166 |
-
40. 421.8899999999999864 Td
|
| 167 |
-
(Candidate:) Tj
|
| 168 |
-
ET
|
| 169 |
-
BT
|
| 170 |
-
/F1 11 Tf
|
| 171 |
-
12.6499999999999986 TL
|
| 172 |
-
0 g
|
| 173 |
-
130. 421.8899999999999864 Td
|
| 174 |
-
(SAJITH J) Tj
|
| 175 |
-
ET
|
| 176 |
-
BT
|
| 177 |
-
/F2 11 Tf
|
| 178 |
-
12.6499999999999986 TL
|
| 179 |
-
0 g
|
| 180 |
-
40. 403.8899999999999864 Td
|
| 181 |
-
(Email:) Tj
|
| 182 |
-
ET
|
| 183 |
-
BT
|
| 184 |
-
/F1 11 Tf
|
| 185 |
-
12.6499999999999986 TL
|
| 186 |
-
0 g
|
| 187 |
-
130. 403.8899999999999864 Td
|
| 188 |
-
(jsajith76@gmail.com) Tj
|
| 189 |
-
ET
|
| 190 |
-
BT
|
| 191 |
-
/F2 11 Tf
|
| 192 |
-
12.6499999999999986 TL
|
| 193 |
-
0 g
|
| 194 |
-
40. 385.8899999999999864 Td
|
| 195 |
-
(Phone:) Tj
|
| 196 |
-
ET
|
| 197 |
-
BT
|
| 198 |
-
/F1 11 Tf
|
| 199 |
-
12.6499999999999986 TL
|
| 200 |
-
0 g
|
| 201 |
-
130. 385.8899999999999864 Td
|
| 202 |
-
(+91 8637440071) Tj
|
| 203 |
-
ET
|
| 204 |
-
BT
|
| 205 |
-
/F2 11 Tf
|
| 206 |
-
12.6499999999999986 TL
|
| 207 |
-
0 g
|
| 208 |
-
40. 367.8899999999999864 Td
|
| 209 |
-
(Location:) Tj
|
| 210 |
-
ET
|
| 211 |
-
BT
|
| 212 |
-
/F1 11 Tf
|
| 213 |
-
12.6499999999999986 TL
|
| 214 |
-
0 g
|
| 215 |
-
130. 367.8899999999999864 Td
|
| 216 |
-
(Coimbatore, India) Tj
|
| 217 |
-
ET
|
| 218 |
-
BT
|
| 219 |
-
/F2 11 Tf
|
| 220 |
-
12.6499999999999986 TL
|
| 221 |
-
0 g
|
| 222 |
-
40. 349.8899999999999864 Td
|
| 223 |
-
(Extracted Skills:) Tj
|
| 224 |
-
ET
|
| 225 |
-
BT
|
| 226 |
-
/F1 11 Tf
|
| 227 |
-
12.6499999999999986 TL
|
| 228 |
-
0 g
|
| 229 |
-
130. 349.8899999999999864 Td
|
| 230 |
-
(Python, SQL, RAG Pipelines, Semantic Search, Embedding Models, Vector Similarity) Tj
|
| 231 |
-
T* (Search, Prompt Engineering, LangChain, LangGraph, LangSmith, CNN, Transformers,) Tj
|
| 232 |
-
T* (BERT Fine-tuning, RNN, LSTM, GRU, Encoder Decoder, GAN, Pinecone, ChromaDB,) Tj
|
| 233 |
-
T* (MySQL, FastAPI, Docker, Git, Github, Sentence Transformers, Scikit-learn, Llama 4,) Tj
|
| 234 |
-
T* (Gemini API, E5 Multilingual Embeddings, OCR Based Extraction, PyTorch, BERT) Tj
|
| 235 |
-
ET
|
| 236 |
-
BT
|
| 237 |
-
/F2 11 Tf
|
| 238 |
-
12.6499999999999986 TL
|
| 239 |
-
0 g
|
| 240 |
-
40. 279.8899999999999864 Td
|
| 241 |
-
(Experience Summary:) Tj
|
| 242 |
-
ET
|
| 243 |
-
BT
|
| 244 |
-
/F1 11 Tf
|
| 245 |
-
12.6499999999999986 TL
|
| 246 |
-
0 g
|
| 247 |
-
130. 279.8899999999999864 Td
|
| 248 |
-
(AI & Data Science undergraduate with practical experience in architecting and) Tj
|
| 249 |
-
T* (deploying end-to-end AI systems, specializing in Deep Learning, RAG pipelines, and) Tj
|
| 250 |
-
T* (multimodal modeling.) Tj
|
| 251 |
-
ET
|
| 252 |
-
BT
|
| 253 |
-
/F2 16 Tf
|
| 254 |
-
18.3999999999999986 TL
|
| 255 |
-
0 g
|
| 256 |
-
40. 231.8899999999999864 Td
|
| 257 |
-
(Alignment Result) Tj
|
| 258 |
-
ET
|
| 259 |
-
BT
|
| 260 |
-
/F2 11 Tf
|
| 261 |
-
12.6499999999999986 TL
|
| 262 |
-
0 g
|
| 263 |
-
40. 207.8899999999999864 Td
|
| 264 |
-
(Fit Summary:) Tj
|
| 265 |
-
ET
|
| 266 |
-
BT
|
| 267 |
-
/F1 11 Tf
|
| 268 |
-
12.6499999999999986 TL
|
| 269 |
-
0 g
|
| 270 |
-
130. 207.8899999999999864 Td
|
| 271 |
-
(The student presents an exceptional fit for the Generative AI Engineer Intern role,) Tj
|
| 272 |
-
T* (showcasing a strong academic foundation in AI/Data Science, practical deployment) Tj
|
| 273 |
-
T* (experience, and highly specialized skills in Generative AI, RAG pipelines, and LLM) Tj
|
| 274 |
-
T* (development. Their demonstrated proficiency in PyTorch and MLOps tools directly) Tj
|
| 275 |
-
T* (aligns with the job's core responsibilities and 'plus' qualifications.) Tj
|
| 276 |
-
ET
|
| 277 |
-
BT
|
| 278 |
-
/F2 12 Tf
|
| 279 |
-
13.7999999999999989 TL
|
| 280 |
-
0 g
|
| 281 |
-
40. 137.8899999999999864 Td
|
| 282 |
-
(Meeting Expectations) Tj
|
| 283 |
-
ET
|
| 284 |
-
BT
|
| 285 |
-
/F1 11 Tf
|
| 286 |
-
12.6499999999999986 TL
|
| 287 |
-
0 g
|
| 288 |
-
46. 121.8899999999999864 Td
|
| 289 |
-
(- Strong foundation in Python and deep learning frameworks, specifically PyTorch, aligning with the) Tj
|
| 290 |
-
T* ('Knowledge of deep learning frameworks \(e.g., TensorFlow or PyTorch\) is a plus' requirement.) Tj
|
| 291 |
-
ET
|
| 292 |
-
BT
|
| 293 |
-
/F1 11 Tf
|
| 294 |
-
12.6499999999999986 TL
|
| 295 |
-
0 g
|
| 296 |
-
46. 93.8899999999999864 Td
|
| 297 |
-
(- Extensive experience with Machine Learning concepts and models, including CNN, Transformers,) Tj
|
| 298 |
-
T* (BERT, RNN, LSTM, GRU, Encoder Decoder, and GANs, demonstrating a robust understanding of AI/ML) Tj
|
| 299 |
-
T* (models and algorithms.) Tj
|
| 300 |
-
ET
|
| 301 |
-
endstream
|
| 302 |
-
endobj
|
| 303 |
-
5 0 obj
|
| 304 |
-
<</Type /Page
|
| 305 |
-
/Parent 1 0 R
|
| 306 |
-
/Resources 2 0 R
|
| 307 |
-
/MediaBox [0 0 595.2799999999999727 841.8899999999999864]
|
| 308 |
-
/Contents 6 0 R
|
| 309 |
-
>>
|
| 310 |
-
endobj
|
| 311 |
-
6 0 obj
|
| 312 |
-
<<
|
| 313 |
-
/Length 3330
|
| 314 |
-
>>
|
| 315 |
-
stream
|
| 316 |
-
0.200025 w
|
| 317 |
-
0 G
|
| 318 |
-
BT
|
| 319 |
-
/F1 11 Tf
|
| 320 |
-
12.6499999999999986 TL
|
| 321 |
-
0 g
|
| 322 |
-
46. 795.8899999999999864 Td
|
| 323 |
-
(- Direct and highly relevant skills in Generative AI, RAG Pipelines, Semantic Search, Embedding) Tj
|
| 324 |
-
T* (Models, Vector Similarity Search, and Prompt Engineering, which directly supports 'developing and) Tj
|
| 325 |
-
T* (implementing AI/ML models and algorithms' for a Generative AI role.) Tj
|
| 326 |
-
ET
|
| 327 |
-
BT
|
| 328 |
-
/F1 11 Tf
|
| 329 |
-
12.6499999999999986 TL
|
| 330 |
-
0 g
|
| 331 |
-
46. 753.8899999999999864 Td
|
| 332 |
-
(- Familiarity with key ML/DL libraries like Scikit-learn, which is explicitly mentioned as a required skill.) Tj
|
| 333 |
-
ET
|
| 334 |
-
BT
|
| 335 |
-
/F1 11 Tf
|
| 336 |
-
12.6499999999999986 TL
|
| 337 |
-
0 g
|
| 338 |
-
46. 739.8899999999999864 Td
|
| 339 |
-
(- Practical experience with MLOps and deployment tools such as FastAPI, Docker, Git, and Github,) Tj
|
| 340 |
-
T* (indicating the ability to 'integrate AI solutions'.) Tj
|
| 341 |
-
ET
|
| 342 |
-
BT
|
| 343 |
-
/F1 11 Tf
|
| 344 |
-
12.6499999999999986 TL
|
| 345 |
-
0 g
|
| 346 |
-
46. 711.8899999999999864 Td
|
| 347 |
-
(- Experience with LLMs like Llama 4, Gemini API, and fine-tuning BERT, showing proactive 'research on) Tj
|
| 348 |
-
T* (the latest AI trends and technologies'.) Tj
|
| 349 |
-
ET
|
| 350 |
-
BT
|
| 351 |
-
/F1 11 Tf
|
| 352 |
-
12.6499999999999986 TL
|
| 353 |
-
0 g
|
| 354 |
-
46. 683.8899999999999864 Td
|
| 355 |
-
(- Skills in managing data for AI, including Pinecone, ChromaDB, MySQL, and OCR Based Extraction,) Tj
|
| 356 |
-
T* (relevant to 'data preprocessing, cleaning, and analysis' and 'model training, evaluation, and optimization'.) Tj
|
| 357 |
-
ET
|
| 358 |
-
BT
|
| 359 |
-
/F1 11 Tf
|
| 360 |
-
12.6499999999999986 TL
|
| 361 |
-
0 g
|
| 362 |
-
46. 655.8899999999999864 Td
|
| 363 |
-
(- The resume summary highlights 'architecting and deploying end-to-end AI systems', which implies) Tj
|
| 364 |
-
T* (strong analytical and problem-solving skills, as well as the ability to 'collaborate with engineers and) Tj
|
| 365 |
-
T* (product teams'.) Tj
|
| 366 |
-
ET
|
| 367 |
-
BT
|
| 368 |
-
/F2 12 Tf
|
| 369 |
-
13.7999999999999989 TL
|
| 370 |
-
0 g
|
| 371 |
-
40. 607.8899999999999864 Td
|
| 372 |
-
(Missing Expectations) Tj
|
| 373 |
-
ET
|
| 374 |
-
BT
|
| 375 |
-
/F1 11 Tf
|
| 376 |
-
12.6499999999999986 TL
|
| 377 |
-
0 g
|
| 378 |
-
46. 591.8899999999999864 Td
|
| 379 |
-
(- While likely used, specific mention of 'NumPy' and 'Pandas' as explicit skills is absent from the resume.) Tj
|
| 380 |
-
ET
|
| 381 |
-
BT
|
| 382 |
-
/F1 11 Tf
|
| 383 |
-
12.6499999999999986 TL
|
| 384 |
-
0 g
|
| 385 |
-
46. 577.8899999999999864 Td
|
| 386 |
-
(- The resume could more explicitly detail experience in 'data preprocessing, cleaning, and analysis' for) Tj
|
| 387 |
-
T* (diverse datasets, beyond what's implied by 'RAG Pipelines' and 'OCR Based Extraction'.) Tj
|
| 388 |
-
ET
|
| 389 |
-
BT
|
| 390 |
-
/F2 12 Tf
|
| 391 |
-
13.7999999999999989 TL
|
| 392 |
-
0 g
|
| 393 |
-
40. 543.8899999999999864 Td
|
| 394 |
-
(Improvement Suggestions) Tj
|
| 395 |
-
ET
|
| 396 |
-
BT
|
| 397 |
-
/F1 11 Tf
|
| 398 |
-
12.6499999999999986 TL
|
| 399 |
-
0 g
|
| 400 |
-
46. 527.8899999999999864 Td
|
| 401 |
-
(- Add 'NumPy' and 'Pandas' to your skills list if you have experience with them, as they are foundational) Tj
|
| 402 |
-
T* (for data manipulation in Python.) Tj
|
| 403 |
-
ET
|
| 404 |
-
BT
|
| 405 |
-
/F1 11 Tf
|
| 406 |
-
12.6499999999999986 TL
|
| 407 |
-
0 g
|
| 408 |
-
46. 499.8899999999999864 Td
|
| 409 |
-
(- Prepare specific examples from past projects where you handled significant 'data preprocessing,) Tj
|
| 410 |
-
T* (cleaning, and analysis' challenges, detailing the techniques used and the impact.) Tj
|
| 411 |
-
ET
|
| 412 |
-
BT
|
| 413 |
-
/F1 11 Tf
|
| 414 |
-
12.6499999999999986 TL
|
| 415 |
-
0 g
|
| 416 |
-
46. 471.8899999999999864 Td
|
| 417 |
-
(- When discussing projects, explicitly highlight your contributions to 'documenting experiments,) Tj
|
| 418 |
-
T* (processes, and results' and examples of 'collaborating with engineers and product teams' to showcase) Tj
|
| 419 |
-
T* (teamwork and communication skills.) Tj
|
| 420 |
-
ET
|
| 421 |
-
BT
|
| 422 |
-
/F1 11 Tf
|
| 423 |
-
12.6499999999999986 TL
|
| 424 |
-
0 g
|
| 425 |
-
46. 429.8899999999999864 Td
|
| 426 |
-
(- Quantify your experience where possible \(e.g., 'deployed X RAG pipelines serving Y users', 'improved) Tj
|
| 427 |
-
T* (model performance by Z%'\), to demonstrate impact and scale.) Tj
|
| 428 |
-
ET
|
| 429 |
-
endstream
|
| 430 |
-
endobj
|
| 431 |
-
1 0 obj
|
| 432 |
-
<</Type /Pages
|
| 433 |
-
/Kids [3 0 R 5 0 R ]
|
| 434 |
-
/Count 2
|
| 435 |
-
>>
|
| 436 |
-
endobj
|
| 437 |
-
7 0 obj
|
| 438 |
-
<<
|
| 439 |
-
/Type /Font
|
| 440 |
-
/BaseFont /Helvetica
|
| 441 |
-
/Subtype /Type1
|
| 442 |
-
/Encoding /WinAnsiEncoding
|
| 443 |
-
/FirstChar 32
|
| 444 |
-
/LastChar 255
|
| 445 |
-
>>
|
| 446 |
-
endobj
|
| 447 |
-
8 0 obj
|
| 448 |
-
<<
|
| 449 |
-
/Type /Font
|
| 450 |
-
/BaseFont /Helvetica-Bold
|
| 451 |
-
/Subtype /Type1
|
| 452 |
-
/Encoding /WinAnsiEncoding
|
| 453 |
-
/FirstChar 32
|
| 454 |
-
/LastChar 255
|
| 455 |
-
>>
|
| 456 |
-
endobj
|
| 457 |
-
9 0 obj
|
| 458 |
-
<<
|
| 459 |
-
/Type /Font
|
| 460 |
-
/BaseFont /Helvetica-Oblique
|
| 461 |
-
/Subtype /Type1
|
| 462 |
-
/Encoding /WinAnsiEncoding
|
| 463 |
-
/FirstChar 32
|
| 464 |
-
/LastChar 255
|
| 465 |
-
>>
|
| 466 |
-
endobj
|
| 467 |
-
10 0 obj
|
| 468 |
-
<<
|
| 469 |
-
/Type /Font
|
| 470 |
-
/BaseFont /Helvetica-BoldOblique
|
| 471 |
-
/Subtype /Type1
|
| 472 |
-
/Encoding /WinAnsiEncoding
|
| 473 |
-
/FirstChar 32
|
| 474 |
-
/LastChar 255
|
| 475 |
-
>>
|
| 476 |
-
endobj
|
| 477 |
-
11 0 obj
|
| 478 |
-
<<
|
| 479 |
-
/Type /Font
|
| 480 |
-
/BaseFont /Courier
|
| 481 |
-
/Subtype /Type1
|
| 482 |
-
/Encoding /WinAnsiEncoding
|
| 483 |
-
/FirstChar 32
|
| 484 |
-
/LastChar 255
|
| 485 |
-
>>
|
| 486 |
-
endobj
|
| 487 |
-
12 0 obj
|
| 488 |
-
<<
|
| 489 |
-
/Type /Font
|
| 490 |
-
/BaseFont /Courier-Bold
|
| 491 |
-
/Subtype /Type1
|
| 492 |
-
/Encoding /WinAnsiEncoding
|
| 493 |
-
/FirstChar 32
|
| 494 |
-
/LastChar 255
|
| 495 |
-
>>
|
| 496 |
-
endobj
|
| 497 |
-
13 0 obj
|
| 498 |
-
<<
|
| 499 |
-
/Type /Font
|
| 500 |
-
/BaseFont /Courier-Oblique
|
| 501 |
-
/Subtype /Type1
|
| 502 |
-
/Encoding /WinAnsiEncoding
|
| 503 |
-
/FirstChar 32
|
| 504 |
-
/LastChar 255
|
| 505 |
-
>>
|
| 506 |
-
endobj
|
| 507 |
-
14 0 obj
|
| 508 |
-
<<
|
| 509 |
-
/Type /Font
|
| 510 |
-
/BaseFont /Courier-BoldOblique
|
| 511 |
-
/Subtype /Type1
|
| 512 |
-
/Encoding /WinAnsiEncoding
|
| 513 |
-
/FirstChar 32
|
| 514 |
-
/LastChar 255
|
| 515 |
-
>>
|
| 516 |
-
endobj
|
| 517 |
-
15 0 obj
|
| 518 |
-
<<
|
| 519 |
-
/Type /Font
|
| 520 |
-
/BaseFont /Times-Roman
|
| 521 |
-
/Subtype /Type1
|
| 522 |
-
/Encoding /WinAnsiEncoding
|
| 523 |
-
/FirstChar 32
|
| 524 |
-
/LastChar 255
|
| 525 |
-
>>
|
| 526 |
-
endobj
|
| 527 |
-
16 0 obj
|
| 528 |
-
<<
|
| 529 |
-
/Type /Font
|
| 530 |
-
/BaseFont /Times-Bold
|
| 531 |
-
/Subtype /Type1
|
| 532 |
-
/Encoding /WinAnsiEncoding
|
| 533 |
-
/FirstChar 32
|
| 534 |
-
/LastChar 255
|
| 535 |
-
>>
|
| 536 |
-
endobj
|
| 537 |
-
17 0 obj
|
| 538 |
-
<<
|
| 539 |
-
/Type /Font
|
| 540 |
-
/BaseFont /Times-Italic
|
| 541 |
-
/Subtype /Type1
|
| 542 |
-
/Encoding /WinAnsiEncoding
|
| 543 |
-
/FirstChar 32
|
| 544 |
-
/LastChar 255
|
| 545 |
-
>>
|
| 546 |
-
endobj
|
| 547 |
-
18 0 obj
|
| 548 |
-
<<
|
| 549 |
-
/Type /Font
|
| 550 |
-
/BaseFont /Times-BoldItalic
|
| 551 |
-
/Subtype /Type1
|
| 552 |
-
/Encoding /WinAnsiEncoding
|
| 553 |
-
/FirstChar 32
|
| 554 |
-
/LastChar 255
|
| 555 |
-
>>
|
| 556 |
-
endobj
|
| 557 |
-
19 0 obj
|
| 558 |
-
<<
|
| 559 |
-
/Type /Font
|
| 560 |
-
/BaseFont /ZapfDingbats
|
| 561 |
-
/Subtype /Type1
|
| 562 |
-
/FirstChar 32
|
| 563 |
-
/LastChar 255
|
| 564 |
-
>>
|
| 565 |
-
endobj
|
| 566 |
-
20 0 obj
|
| 567 |
-
<<
|
| 568 |
-
/Type /Font
|
| 569 |
-
/BaseFont /Symbol
|
| 570 |
-
/Subtype /Type1
|
| 571 |
-
/FirstChar 32
|
| 572 |
-
/LastChar 255
|
| 573 |
-
>>
|
| 574 |
-
endobj
|
| 575 |
-
2 0 obj
|
| 576 |
-
<<
|
| 577 |
-
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
|
| 578 |
-
/Font <<
|
| 579 |
-
/F1 7 0 R
|
| 580 |
-
/F2 8 0 R
|
| 581 |
-
/F3 9 0 R
|
| 582 |
-
/F4 10 0 R
|
| 583 |
-
/F5 11 0 R
|
| 584 |
-
/F6 12 0 R
|
| 585 |
-
/F7 13 0 R
|
| 586 |
-
/F8 14 0 R
|
| 587 |
-
/F9 15 0 R
|
| 588 |
-
/F10 16 0 R
|
| 589 |
-
/F11 17 0 R
|
| 590 |
-
/F12 18 0 R
|
| 591 |
-
/F13 19 0 R
|
| 592 |
-
/F14 20 0 R
|
| 593 |
-
>>
|
| 594 |
-
/XObject <<
|
| 595 |
-
>>
|
| 596 |
-
>>
|
| 597 |
-
endobj
|
| 598 |
-
21 0 obj
|
| 599 |
-
<<
|
| 600 |
-
/Producer (jsPDF 4.2.1)
|
| 601 |
-
/CreationDate (D:20260410104544+05'30')
|
| 602 |
-
>>
|
| 603 |
-
endobj
|
| 604 |
-
22 0 obj
|
| 605 |
-
<<
|
| 606 |
-
/Type /Catalog
|
| 607 |
-
/Pages 1 0 R
|
| 608 |
-
/OpenAction [3 0 R /FitH null]
|
| 609 |
-
/PageLayout /OneColumn
|
| 610 |
-
>>
|
| 611 |
-
endobj
|
| 612 |
-
xref
|
| 613 |
-
0 23
|
| 614 |
-
0000000000 65535 f
|
| 615 |
-
0000009330 00000 n
|
| 616 |
-
0000011155 00000 n
|
| 617 |
-
0000000015 00000 n
|
| 618 |
-
0000000152 00000 n
|
| 619 |
-
0000005811 00000 n
|
| 620 |
-
0000005948 00000 n
|
| 621 |
-
0000009393 00000 n
|
| 622 |
-
0000009518 00000 n
|
| 623 |
-
0000009648 00000 n
|
| 624 |
-
0000009781 00000 n
|
| 625 |
-
0000009919 00000 n
|
| 626 |
-
0000010043 00000 n
|
| 627 |
-
0000010172 00000 n
|
| 628 |
-
0000010304 00000 n
|
| 629 |
-
0000010440 00000 n
|
| 630 |
-
0000010568 00000 n
|
| 631 |
-
0000010695 00000 n
|
| 632 |
-
0000010824 00000 n
|
| 633 |
-
0000010957 00000 n
|
| 634 |
-
0000011059 00000 n
|
| 635 |
-
0000011405 00000 n
|
| 636 |
-
0000011491 00000 n
|
| 637 |
-
trailer
|
| 638 |
-
<<
|
| 639 |
-
/Size 23
|
| 640 |
-
/Root 22 0 R
|
| 641 |
-
/Info 21 0 R
|
| 642 |
-
/ID [ <95A654D90B03BE650BD8733007BC1C07> <95A654D90B03BE650BD8733007BC1C07> ]
|
| 643 |
-
>>
|
| 644 |
-
startxref
|
| 645 |
-
11595
|
| 646 |
-
%%EOF
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|