Spaces:
Sleeping
Sleeping
Added ARS
#1
by
Nattyboi
- opened
- Ars/ai_functions.py +5 -5
- Ars/controllers.py +17 -42
- Ars/repositories.py +3 -60
- Ars/routes.py +5 -21
- Dockerfile +1 -1
- README.md +3 -0
- controller/imports.py +1 -1
- gamification/pointLogic.py +11 -0
- requirements.txt +11 -64
- security/__init__.py +0 -0
- security/authDependency.py +0 -14
Ars/ai_functions.py
CHANGED
@@ -1,18 +1,18 @@
|
|
1 |
from pdfminer.high_level import extract_text_to_fp
|
2 |
from io import BytesIO
|
3 |
-
from
|
4 |
-
from
|
5 |
from fastapi import UploadFile
|
|
|
6 |
def extract_text_from_bytes(pdf_bytes: bytes) -> str:
|
7 |
output_string = BytesIO()
|
8 |
with BytesIO(pdf_bytes) as input_stream:
|
9 |
extract_text_to_fp(input_stream, output_string)
|
10 |
return output_string.getvalue().decode()
|
11 |
|
12 |
-
|
13 |
|
14 |
-
async def resume_analysis(
|
15 |
-
|
16 |
resume = extract_text_from_bytes(pdf_bytes=contents)
|
17 |
if resume:
|
18 |
prompt = f"""
|
|
|
1 |
from pdfminer.high_level import extract_text_to_fp
|
2 |
from io import BytesIO
|
3 |
+
from objects import ai,ResumeData,AutomationRiskResult,AutomationRiskInput,RealWorldQuestion,SkillDepthResult,SkillDepthInput,BreakDownByDomainUpdate,FlaggedRiskAreasUpdate,BoostSuggestionsUpdate,AICollabReadinessInput
|
4 |
+
from embedDoc import search_pinecone_text
|
5 |
from fastapi import UploadFile
|
6 |
+
|
7 |
def extract_text_from_bytes(pdf_bytes: bytes) -> str:
|
8 |
output_string = BytesIO()
|
9 |
with BytesIO(pdf_bytes) as input_stream:
|
10 |
extract_text_to_fp(input_stream, output_string)
|
11 |
return output_string.getvalue().decode()
|
12 |
|
|
|
13 |
|
14 |
+
async def resume_analysis(upload_file:UploadFile) -> ResumeData:
|
15 |
+
contents = await upload_file.read()
|
16 |
resume = extract_text_from_bytes(pdf_bytes=contents)
|
17 |
if resume:
|
18 |
prompt = f"""
|
Ars/controllers.py
CHANGED
@@ -1,44 +1,19 @@
|
|
1 |
-
from
|
2 |
-
from
|
3 |
from fastapi import UploadFile
|
4 |
-
from
|
5 |
-
from
|
6 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
|
8 |
-
|
9 |
-
# Ensure consistent encoding (e.g., UTF-8) before hashing
|
10 |
-
document_bytes = document_content.encode('utf-8')
|
11 |
-
hasher = hashlib.sha256()
|
12 |
-
hasher.update(document_bytes)
|
13 |
-
return hasher.hexdigest()
|
14 |
-
|
15 |
-
async def resilience_analysis(file:UploadFile,userId:str):
|
16 |
-
contents = await file.read()
|
17 |
-
resume_text = extract_text_from_bytes(pdf_bytes=contents)
|
18 |
-
hashed_doc = get_document_hash(resume_text)
|
19 |
-
|
20 |
-
check = await get_document_by_hashed_doc(hashed_doc=hashed_doc)
|
21 |
-
if check==None:
|
22 |
-
resume= await resume_analysis(contents)
|
23 |
-
risk = calculate_automation_risk(resume)
|
24 |
-
risk = AutomationRiskResult(**risk.model_dump())
|
25 |
-
skill_depth = calculate_skill_depth(resume)
|
26 |
-
skill_depth= SkillDepthResult(**skill_depth.model_dump())
|
27 |
-
ai_readiness = calculate_Ai_collab_readiness(resume)
|
28 |
-
ai_readiness = AICollabReadiness(**ai_readiness.model_dump())
|
29 |
-
ResilienceScore = ((1-(risk.result/100))*0.5+(skill_depth.result/100)*0.3+(ai_readiness.result/100)*0.2)
|
30 |
-
flagged_risk =generate_flagged_risk_areas(resume=resume,skil_depth=skill_depth,risk=risk,ai_readiness=ai_readiness)
|
31 |
-
boost_suggestion = generate_boost_suggestion(resume=resume,skil_depth=skill_depth,risk=risk,ai_readiness=ai_readiness)
|
32 |
-
domain_breakdown = generate_domain_breakdown(resume=resume,skil_depth=skill_depth,risk=risk,ai_readiness=ai_readiness)
|
33 |
-
final_analysis_response ={"overall score": ResilienceScore,"flagged Risk": flagged_risk.model_dump(),"boost suggestion":boost_suggestion.model_dump(),"domain breakdown":domain_breakdown.model_dump(),"resume":resume.model_dump(),"skil_depth":skill_depth.model_dump(),"risk":risk.model_dump(),"ai_readiness":ai_readiness.model_dump()}
|
34 |
-
resultId =await create_new_hashed_doc_entry(data={"hashed_doc":hashed_doc,"resume":final_analysis_response,"userId":userId})
|
35 |
-
return final_analysis_response
|
36 |
-
else:
|
37 |
-
result_hash =await get_document_by_hashed_doc_complete(hashed_doc)
|
38 |
-
if result_hash['hashed_doc']==hashed_doc and result_hash['userId']== userId:
|
39 |
-
|
40 |
-
await create_new_hashed_doc_entry(data={"hashed_doc":hashed_doc,"resume":check,"userId":userId})
|
41 |
-
return check
|
42 |
-
|
43 |
-
await create_new_hashed_doc_entry(data={"hashed_doc":hashed_doc,"resume":check,"userId":userId})
|
44 |
-
return check
|
|
|
1 |
+
from core import r
|
2 |
+
from repositories import create_boost_suggestions,create_breakdown_by_domain,create_flagged_risk_areas,create_user_resilience
|
3 |
from fastapi import UploadFile
|
4 |
+
from ai_functions import resume_analysis,calculate_automation_risk,calculate_Ai_collab_readiness,calculate_skill_depth,generate_boost_suggestion,generate_domain_breakdown,generate_flagged_risk_areas
|
5 |
+
from objects import AICollabReadiness,SkillDepthResult,AutomationRiskResult
|
6 |
+
async def resilience_analysis(file:UploadFile):
|
7 |
+
resume= await resume_analysis(file)
|
8 |
+
risk = calculate_automation_risk(resume)
|
9 |
+
risk = AutomationRiskResult(**risk.model_dump())
|
10 |
+
skill_depth = calculate_skill_depth(resume)
|
11 |
+
skill_depth= SkillDepthResult(**skill_depth.model_dump())
|
12 |
+
ai_readiness = calculate_Ai_collab_readiness(resume)
|
13 |
+
ai_readiness = AICollabReadiness(**ai_readiness.model_dump())
|
14 |
+
ResilienceScore = ((1-(risk.result/100))*0.5+(skill_depth.result/100)*0.3+(ai_readiness.result/100)*0.2)
|
15 |
+
flagged_risk =generate_flagged_risk_areas(resume=resume,skil_depth=skill_depth,risk=risk,ai_readiness=ai_readiness)
|
16 |
+
boost_suggestion = generate_boost_suggestion(resume=resume,skil_depth=skill_depth,risk=risk,ai_readiness=ai_readiness)
|
17 |
+
domain_breakdown = generate_domain_breakdown(resume=resume,skil_depth=skill_depth,risk=risk,ai_readiness=ai_readiness)
|
18 |
|
19 |
+
return {"overall score": ResilienceScore,"flagged Risk": flagged_risk,"boost suggestion":boost_suggestion,"domain breakdown":domain_breakdown,"resume":resume,"skil_depth":skill_depth,"risk":risk,"ai_readiness":ai_readiness}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ars/repositories.py
CHANGED
@@ -1,68 +1,11 @@
|
|
1 |
-
from
|
2 |
from bson import ObjectId
|
3 |
from fastapi import HTTPException
|
4 |
from typing import Optional, List
|
5 |
from motor.motor_asyncio import AsyncIOMotorDatabase
|
6 |
-
from
|
7 |
-
|
8 |
|
9 |
-
async def create_new_hashed_doc_entry(data: dict):
|
10 |
-
|
11 |
-
try:
|
12 |
-
# Attempt to insert the document, ensuring hashed_doc is unique due to the index
|
13 |
-
result = await db.resume_resilience_analysis.insert_one(document=data)
|
14 |
-
return {"inserted_id": result.inserted_id,"error":None} # Return the inserted ID
|
15 |
-
except pymongo.errors.DuplicateKeyError:
|
16 |
-
# Handle the case where a document with the same 'hashed_doc' already exists
|
17 |
-
return {"error": "Document with this 'hashed_doc' already exists"}
|
18 |
-
except Exception as e:
|
19 |
-
# Catch other exceptions (e.g., database connection issues)
|
20 |
-
return {"error": str(e)}
|
21 |
-
|
22 |
-
async def get_document_by_hashed_doc(hashed_doc: str):
|
23 |
-
try:
|
24 |
-
# Find the document using the hashed_doc field
|
25 |
-
document = await db.resume_resilience_analysis.find_one({"hashed_doc": hashed_doc})
|
26 |
-
|
27 |
-
if document:
|
28 |
-
return document['resume']
|
29 |
-
else:
|
30 |
-
return None
|
31 |
-
|
32 |
-
except Exception as e:
|
33 |
-
# Handle any other errors, like database issues
|
34 |
-
return {"error": str(e)}
|
35 |
-
|
36 |
-
async def get_document_by_hashed_doc_complete(hashed_doc: str):
|
37 |
-
try:
|
38 |
-
# Find the document using the hashed_doc field
|
39 |
-
document = await db.resume_resilience_analysis.find_one({"hashed_doc": hashed_doc})
|
40 |
-
|
41 |
-
if document:
|
42 |
-
return document
|
43 |
-
else:
|
44 |
-
return None
|
45 |
-
|
46 |
-
except Exception as e:
|
47 |
-
# Handle any other errors, like database issues
|
48 |
-
return {"error": str(e)}
|
49 |
-
|
50 |
-
|
51 |
-
async def get_document_by_userId(userId: str):
|
52 |
-
try:
|
53 |
-
# Find the document using the hashed_doc field
|
54 |
-
document = await db.resume_resilience_analysis.find_one({"userId": userId})
|
55 |
-
|
56 |
-
if document:
|
57 |
-
return document['resume']
|
58 |
-
else:
|
59 |
-
return None
|
60 |
-
|
61 |
-
except Exception as e:
|
62 |
-
# Handle any other errors, like database issues
|
63 |
-
return {"error": str(e)}
|
64 |
-
|
65 |
-
|
66 |
async def create_user_resilience( data: UserResilienceScoreCreate) -> UserResilienceScoreOut:
|
67 |
"""
|
68 |
Create a new UserResilienceScore in the database.
|
|
|
1 |
+
from core import db
|
2 |
from bson import ObjectId
|
3 |
from fastapi import HTTPException
|
4 |
from typing import Optional, List
|
5 |
from motor.motor_asyncio import AsyncIOMotorDatabase
|
6 |
+
from objects import UserResilienceScoreCreate, UserResilienceScoreOut,BreakDownByDomainCreate,BreakDownByDomainOut,FlaggedRiskAreasCreate,FlaggedRiskAreasOut,BoostSuggestionsCreate,BoostSuggestionsOut,BoostSuggestionsUpdate,UserResilienceScoreUpdate,FlaggedRiskAreasUpdate,BreakDownByDomainUpdate
|
7 |
+
|
8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
async def create_user_resilience( data: UserResilienceScoreCreate) -> UserResilienceScoreOut:
|
10 |
"""
|
11 |
Create a new UserResilienceScore in the database.
|
Ars/routes.py
CHANGED
@@ -1,31 +1,15 @@
|
|
1 |
import base64
|
2 |
-
from fastapi import FastAPI, File, UploadFile,HTTPException
|
3 |
-
from
|
4 |
-
from
|
5 |
-
from security.authDependency import verifyAccessToken
|
6 |
-
from Ars.embedDoc import upsert_text_with_chunks,search_pinecone_text
|
7 |
-
import hashlib
|
8 |
-
|
9 |
-
|
10 |
ARS = FastAPI()
|
11 |
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
@ARS.post("/risk-analysis")
|
17 |
-
async def perform_risk_analysis(
|
18 |
if file.content_type != "application/pdf":
|
19 |
return HTTPException(status_code=400, detail={"error": "File must be a PDF."})
|
20 |
-
|
21 |
-
ResilienceScore = await resilience_analysis(file=file,userId=user['userId'])
|
22 |
return ResilienceScore
|
23 |
|
24 |
|
25 |
|
26 |
-
@ARS.get("/risk-analysis")
|
27 |
-
async def perform_risk_analysis(user=Depends(verifyAccessToken) ):
|
28 |
-
ResilienceScore = await get_document_by_userId(userId=user['userId'])
|
29 |
-
|
30 |
-
return ResilienceScore
|
31 |
-
|
|
|
1 |
import base64
|
2 |
+
from fastapi import FastAPI, File, UploadFile,HTTPException
|
3 |
+
from controllers import resilience_analysis
|
4 |
+
from embedDoc import upsert_text_with_chunks,search_pinecone_text
|
|
|
|
|
|
|
|
|
|
|
5 |
ARS = FastAPI()
|
6 |
|
|
|
|
|
|
|
|
|
7 |
@ARS.post("/risk-analysis")
|
8 |
+
async def perform_risk_analysis(file: UploadFile = File(...)):
|
9 |
if file.content_type != "application/pdf":
|
10 |
return HTTPException(status_code=400, detail={"error": "File must be a PDF."})
|
11 |
+
ResilienceScore = await resilience_analysis(file=file)
|
|
|
12 |
return ResilienceScore
|
13 |
|
14 |
|
15 |
|
|
|
|
|
|
|
|
|
|
|
|
Dockerfile
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
# Read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
|
2 |
# you will also find guides on how best to write your Dockerfile
|
3 |
|
4 |
-
FROM python:3.
|
5 |
|
6 |
RUN useradd -m -u 1000 user
|
7 |
USER user
|
|
|
1 |
# Read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
|
2 |
# you will also find guides on how best to write your Dockerfile
|
3 |
|
4 |
+
FROM python:3.9
|
5 |
|
6 |
RUN useradd -m -u 1000 user
|
7 |
USER user
|
README.md
CHANGED
@@ -8,4 +8,7 @@ pinned: false
|
|
8 |
license: apache-2.0
|
9 |
---
|
10 |
|
|
|
|
|
11 |
if you are changing the connection string in the env to a new one ensure you populate the levels table with levels up to level9 for a default career path and default level name else points won't be calculated properly
|
|
|
|
8 |
license: apache-2.0
|
9 |
---
|
10 |
|
11 |
+
<<<<<<< HEAD
|
12 |
+
=======
|
13 |
if you are changing the connection string in the env to a new one ensure you populate the levels table with levels up to level9 for a default career path and default level name else points won't be calculated properly
|
14 |
+
>>>>>>> master
|
controller/imports.py
CHANGED
@@ -12,7 +12,7 @@ import fitz
|
|
12 |
from gamification.routes import gamification
|
13 |
from controller.scraper import scrapeCourse
|
14 |
import asyncio
|
15 |
-
from google import genai
|
16 |
from typing import Optional,List
|
17 |
from pydantic import BaseModel
|
18 |
import re
|
|
|
12 |
from gamification.routes import gamification
|
13 |
from controller.scraper import scrapeCourse
|
14 |
import asyncio
|
15 |
+
from google import genai
|
16 |
from typing import Optional,List
|
17 |
from pydantic import BaseModel
|
18 |
import re
|
gamification/pointLogic.py
CHANGED
@@ -93,6 +93,16 @@ def get_all_simple_points_func(userId) -> SimpleIndividualUserLevel:
|
|
93 |
db = client[db_name]
|
94 |
collection = db[collection_name]
|
95 |
dreamJob = get_dream_job(userId=userId)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
96 |
print(dreamJob)
|
97 |
point_cursor = collection.find({"userId": userId})
|
98 |
try:
|
@@ -102,6 +112,7 @@ def get_all_simple_points_func(userId) -> SimpleIndividualUserLevel:
|
|
102 |
|
103 |
particularLevelInfo = get_particular_level(dreamJob=dreamJob,totalPoints=totalPoints)
|
104 |
print(particularLevelInfo)
|
|
|
105 |
points = SimpleIndividualUserLevel(totalpoints=totalPoints,levelName=particularLevelInfo[0].levelName,maxPoints=particularLevelInfo[0].maxPoints,minPoints=particularLevelInfo[0].minPoints,levelNumber=particularLevelInfo[0].levelNumber)
|
106 |
except:
|
107 |
totalPoints = 0
|
|
|
93 |
db = client[db_name]
|
94 |
collection = db[collection_name]
|
95 |
dreamJob = get_dream_job(userId=userId)
|
96 |
+
<<<<<<< HEAD
|
97 |
+
|
98 |
+
point_cursor = collection.find({"userId": userId})
|
99 |
+
try:
|
100 |
+
points_list = list(point_cursor)
|
101 |
+
|
102 |
+
totalPoints = sum([point['numOfPoints'] for point in points_list])
|
103 |
+
particularLevelInfo = get_particular_level(dreamJob=dreamJob,totalPoints=totalPoints)
|
104 |
+
|
105 |
+
=======
|
106 |
print(dreamJob)
|
107 |
point_cursor = collection.find({"userId": userId})
|
108 |
try:
|
|
|
112 |
|
113 |
particularLevelInfo = get_particular_level(dreamJob=dreamJob,totalPoints=totalPoints)
|
114 |
print(particularLevelInfo)
|
115 |
+
>>>>>>> master
|
116 |
points = SimpleIndividualUserLevel(totalpoints=totalPoints,levelName=particularLevelInfo[0].levelName,maxPoints=particularLevelInfo[0].maxPoints,minPoints=particularLevelInfo[0].minPoints,levelNumber=particularLevelInfo[0].levelNumber)
|
117 |
except:
|
118 |
totalPoints = 0
|
requirements.txt
CHANGED
@@ -1,75 +1,22 @@
|
|
1 |
-
# Core web stack
|
2 |
fastapi[all]
|
3 |
-
uvicorn
|
4 |
-
gunicorn
|
5 |
requests
|
6 |
python-dotenv
|
7 |
-
|
8 |
-
# PDF, Docx handling
|
9 |
pymupdf
|
10 |
-
|
11 |
-
|
12 |
-
pdfminer.six # Use only this (newest), remove old 'pdfminer'
|
13 |
-
|
14 |
-
# MongoDB
|
15 |
motor
|
16 |
-
|
17 |
-
|
18 |
-
# AI and embedding
|
19 |
-
openai
|
20 |
-
sentence-transformers
|
21 |
pinecone
|
22 |
-
|
23 |
-
langchain-core
|
24 |
-
transformers
|
25 |
-
torch
|
26 |
einops
|
27 |
-
|
28 |
-
# Google GenAI
|
29 |
-
google-generativeai
|
30 |
-
google-auth
|
31 |
-
google-api-python-client
|
32 |
google-genai
|
33 |
-
|
34 |
-
|
|
|
35 |
bcrypt
|
36 |
-
passlib[bcrypt]
|
37 |
python-jose[cryptography]
|
38 |
-
|
39 |
-
|
|
|
40 |
redis
|
41 |
-
|
42 |
-
# Frontend templating and security
|
43 |
-
jinja2
|
44 |
-
itsdangerous
|
45 |
-
|
46 |
-
# Other utilities
|
47 |
-
python-multipart
|
48 |
-
pydantic-settings
|
49 |
-
pydantic-extra-types
|
50 |
-
|
51 |
-
# Windows only (skip on Linux/mac)
|
52 |
-
# Remove pywin32==308, use the latest
|
53 |
-
|
54 |
-
# Optional tools
|
55 |
-
tqdm
|
56 |
-
beautifulsoup4
|
57 |
-
httpx
|
58 |
-
httpcore
|
59 |
-
|
60 |
-
# ML / Data
|
61 |
-
scikit-learn
|
62 |
-
pandas
|
63 |
-
numpy
|
64 |
-
matplotlib-inline
|
65 |
-
joblib
|
66 |
-
scipy
|
67 |
-
|
68 |
-
# Optional for resume parsing
|
69 |
-
pyresparser
|
70 |
-
email-validator
|
71 |
-
|
72 |
-
# Logging, async, extras
|
73 |
-
rich
|
74 |
-
watchfiles
|
75 |
-
nest-asyncio
|
|
|
|
|
1 |
fastapi[all]
|
|
|
|
|
2 |
requests
|
3 |
python-dotenv
|
|
|
|
|
4 |
pymupdf
|
5 |
+
<<<<<<< HEAD
|
6 |
+
=======
|
|
|
|
|
|
|
7 |
motor
|
8 |
+
>>>>>>> master
|
|
|
|
|
|
|
|
|
9 |
pinecone
|
10 |
+
sentence-transformers
|
|
|
|
|
|
|
11 |
einops
|
|
|
|
|
|
|
|
|
|
|
12 |
google-genai
|
13 |
+
python-docx
|
14 |
+
beautifulsoup4
|
15 |
+
pymongo
|
16 |
bcrypt
|
|
|
17 |
python-jose[cryptography]
|
18 |
+
passlib[bcrypt]
|
19 |
+
uvicorn
|
20 |
+
gunicorn # Add this for production
|
21 |
redis
|
22 |
+
cryptography
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
security/__init__.py
DELETED
File without changes
|
security/authDependency.py
DELETED
@@ -1,14 +0,0 @@
|
|
1 |
-
from fastapi.security import HTTPBearer
|
2 |
-
from fastapi import Depends
|
3 |
-
from controller.imports import *
|
4 |
-
|
5 |
-
token_auth_scheme = HTTPBearer()
|
6 |
-
|
7 |
-
def verifyAccessToken(token:str =Depends(token_auth_scheme)):
|
8 |
-
credentials = token.credentials
|
9 |
-
decoded_user_id,decoded_access_token = decode_jwt(credentials)
|
10 |
-
is_valid = verify_access_token(db_uri=MONGO_URI, user_id=decoded_user_id, access_token=decoded_access_token)
|
11 |
-
if is_valid != True: # Example check
|
12 |
-
raise HTTPException(status_code=401, detail="Invalid token")
|
13 |
-
else:
|
14 |
-
return {"userId":decoded_user_id}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|