resume-api / app.py
Nattyboi's picture
fixed byte12 error
37e73d6
raw
history blame
23.1 kB
from controller.imports import *
import logging
from datetime import datetime
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = FastAPI()
app.mount('/gamification',gamification)
@app.get("/courses",tags=["Scrape"])
def get_course(query):
# Example search query
results = google_search(query, API_KEY, CX)
content=[]
if results:
for item in results.get('items', []):
title = item.get('title')
link = item.get('link')
snippet = item.get('snippet')
provider = extract_provider(link)
content_structure={}
content_structure["courseTitle"]=title
content_structure["courseLink"]=link
content_structure["courseSnippet"]= snippet
content_structure["provider"]= provider
content_structure["scrapedCourseDetails"]= scrapeCourse(url=link)
content.append(content_structure)
return JSONResponse(content,status_code=200)
@app.post("/ai/upload",tags=["AI"])
async def upload_file(file: UploadFile = File(...),authorization: str = Header(...)):
# Extract the token from the Authorization header (Bearer token)
token = authorization.split("Bearer ")[-1]
decoded_user_id,decoded_access_token = decode_jwt(token)
is_valid = verify_access_token(db_uri=MONGO_URI, user_id=decoded_user_id, access_token=decoded_access_token)
if is_valid != True: # Example check
raise HTTPException(status_code=401, detail="Invalid token")
else:
content = await file.read() # Read the file content (this will return bytes)
sentences=[]
print(f"File name: {file.filename}")
print(f"File content type: {file.content_type}")
print(f"File size: {file.size} bytes")
if "pdf" == file.filename.split('.')[1]:
pdf_document = fitz.open(stream=BytesIO(content), filetype="pdf")
extracted_text = ""
for page_num in range(pdf_document.page_count):
page = pdf_document.load_page(page_num)
extracted_text += page.get_text()
elif "docx" == file.filename.split('.')[1]:
docx_file = BytesIO(content)
doc = docx.Document(docx_file)
extracted_text = ""
for para in doc.paragraphs:
extracted_text += para.text + "\n"
sentences = split_text_into_chunks(extracted_text,chunk_size=200)
docs = generate_embedding_for_user_resume(data=sentences,user_id=file.filename)
response= insert_embeddings_into_pinecone_database(doc=docs,api_key=PINECONE_API_KEY,name_space=decoded_user_id)
return {"name": file.filename,"response":str(response) }
@app.post("/ai/ask",tags=["AI"])
def ask_ai_about_resume(req:AiAnalysis,authorization: str = Header(...)):
# Retrieve context from your vector database
token = authorization.split("Bearer ")[-1]
decoded_user_id,decoded_access_token = decode_jwt(token)
is_valid = verify_access_token(db_uri=MONGO_URI, user_id=decoded_user_id, access_token=decoded_access_token)
if is_valid != True: # Example check
raise HTTPException(status_code=401, detail="Invalid token")
context = query_vector_database(query=req.Query, api_key=PINECONE_API_KEY, name_space=decoded_user_id)
# Ensure that an event loop is present in this thread.
try:
loop = asyncio.get_event_loop()
except RuntimeError:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
# Create the Gemini client after the event loop is set up
client = genai.Client(api_key=GEMINI_API_KEY)
response = client.models.generate_content(
model="gemini-2.0-flash",
contents=f"""
Answer this question using the context provided:
question: {req.Query}
context: {context}
"""
)
return {"Ai_Response":response.text}
@app.post("/ai/recommend",tags=["AI"])
def ask_ai_to_recommnd_courses(request:UserCourse,authorization:str=Header(...)):
"""
"""
# Extract the token from the Authorization header (Bearer token)
token = authorization.split("Bearer ")[-1]
decoded_user_id,decoded_access_token = decode_jwt(token)
is_valid = verify_access_token(db_uri=MONGO_URI, user_id=decoded_user_id, access_token=decoded_access_token)
if is_valid != True:
raise HTTPException(status_code=401, detail="Invalid token")
# Ensure that an event loop is present in this thread.
try:
loop = asyncio.get_event_loop()
except RuntimeError:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
# # Create the Gemini client after the event loop is set up
# client = genai.Client(api_key=GEMINI_API_KEY)
# response = client.models.generate_content(
# model="gemini-2.0-flash",
# contents=f"""
# please respond with a JSON object that contains the following keys as a response:
# - "coursename": the name of the recommended course,
# - "completiontime": an estimate of how long it would take to complete the course.
# Do not include any extra text.
# Recommend a course using this information below :
# Which of the following best describes you?: {request.employmentStatus}
# Would you like to prepare for an interim role to gain experience and income while pursuing your dream job?: {request.interimRole}
# What is your desired role?: {request.dreamRole}
# Why do you want to achieve this desired role?: {request.motivation}
# How do you prefer to learn new skills?: {request.learningPreference}
# How many hours per day can you dedicate to learning?: {request.timeCommitmentPerDay}
# What are the biggest challenges or obstacles you face in reaching your dream role?: {request.challenges}
# What is your ideal timeframe for achieving your dream role?: {request.timeframeToAchieveDreamRole}
# """
# )
questions=request.model_dump()
questions['userId']=decoded_user_id
create_questionaire(db_uri=MONGO_URI,db_name="crayonics",collection_name="Questionaire",document=questions)
# course_info = extract_course_info(response.text)
# courses = get_course_func(query=course_info.courseName)
return {"courseInfo":"course_info","courses":"courses"}
@app.post("/auth/login",tags=["Authentication"])
def login(user:UserBody):
user ={"email":user.email,"password":user.password,"firstName":user.firstName,"lastName":user.lastName}
print(user)
user_id= login_user(db_uri=MONGO_URI,db_name="crayonics",collection_name="users",document=user)
if user_id != False:
refreshToken=create_refreshToken(db_uri=MONGO_URI,user_id=user_id)
accessToken = create_accessToken(db_uri=MONGO_URI,user_id=user_id,refresh_token=refreshToken)
result = update_refreshTokenWithPreviouslyUsedAccessToken(db_uri=MONGO_URI,refresh_token=refreshToken,access_token=accessToken)
print(result)
access_token = encode_jwt(user_id=user_id,access_token=accessToken)
return {"refreshToken":refreshToken,"accessToken":access_token}
return JSONResponse(status_code=401,content={"detail":"Invalid login details"})
@app.post("/auth/signup",tags=["Authentication"])
def signUp(user:UserBody):
user ={"email":user.email,"password":user.password,"first_name":user.firstName,"last_name":user.lastName}
user_id= create_user(db_uri=MONGO_URI,db_name="crayonics",collection_name="users",document=user)
if user_id != False:
refreshToken=create_refreshToken(db_uri=MONGO_URI,user_id=user_id)
accessToken = create_accessToken(db_uri=MONGO_URI,user_id=user_id,refresh_token=refreshToken)
result = update_refreshTokenWithPreviouslyUsedAccessToken(db_uri=MONGO_URI,refresh_token=refreshToken,access_token=accessToken)
print(result)
access_token = encode_jwt(user_id=user_id,access_token=accessToken)
return {"refreshToken":refreshToken,"accessToken":access_token}
return JSONResponse(status_code=status.HTTP_226_IM_USED,content="user already Exists")
@app.post("/auth/logout",tags=["Authentication"])
def logout(refresh:Token,authorization: str = Header(...)):
token = authorization.split("Bearer ")[-1]
decoded_user_id,decoded_access_token = decode_jwt(token)
is_valid = verify_access_token(db_uri=MONGO_URI, user_id=decoded_user_id, access_token=decoded_access_token)
if is_valid != True: # Example check
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")
result = logout_func(db_uri=MONGO_URI,refresh_token= refresh.refreshToken)
if result ==True:
return {"content": f"successful"}
else:
return JSONResponse(status_code=status.HTTP_410_GONE,content={"content": f"unsuccessful"})
@app.post("/auth/refresh",tags=["Authentication"])
def refresh_access_token(refresh_token:Token, authorization: str = Header(...)):
token = authorization.split("Bearer ")[-1]
decoded_user_id,decoded_access_token = decode_jwt(token)
try:
is_valid = verify_refresh_access_token(db_uri=MONGO_URI, user_id=decoded_user_id, access_token=decoded_access_token,refresh_token=refresh_token.refreshToken)
if is_valid != True: # Example check
raise HTTPException(status_code=401, detail="Invalid token")
new_access_token = create_accessToken(db_uri=MONGO_URI,user_id=decoded_user_id,refresh_token=refresh_token.refreshToken)
update_refreshTokenWithPreviouslyUsedAccessToken(db_uri=MONGO_URI,refresh_token=refresh_token.refreshToken,access_token=new_access_token)
newly_encoded_access_token = encode_jwt(user_id=decoded_user_id,access_token=new_access_token)
return {"accessToken":newly_encoded_access_token}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Most likely because the refresh token is wrong {e}")
@app.get("/user/user-details",tags=["user"])
def get_user_details(authorization: str = Header(...)):
# Extract the token from the Authorization header (Bearer token)
token = authorization.split("Bearer ")[-1]
decoded_user_id,decoded_access_token = decode_jwt(token)
is_valid = verify_access_token(db_uri=MONGO_URI, user_id=decoded_user_id, access_token=decoded_access_token)
if is_valid != True: # Example check
raise HTTPException(status_code=401, detail="Invalid token")
doc = {"user_id":decoded_user_id}
user_info = user_details_func(db_uri=MONGO_URI,document=doc)
return { "userInfo": user_info}
@app.get("/protected-route")
def protected_route(authorization: str = Header(...)):
# Extract the token from the Authorization header (Bearer token)
token = authorization.split("Bearer ")[-1]
decoded_user_id,decoded_access_token = decode_jwt(token)
is_valid = verify_access_token(db_uri=MONGO_URI, user_id=decoded_user_id, access_token=decoded_access_token)
if is_valid != True: # Example check
raise HTTPException(status_code=401, detail="Invalid token")
return {"message": "Access granted", "verification": "verified"}
def create_leaderboard_ranking( document: LeaderBoardRanking) -> bool:
collection = db['LeaderBoard']
# Insert the document
result= collection.find_one_and_replace(filter={"userId":document.userId},replacement=document.model_dump())
print(result)
if result==None:
result = collection.insert_one(document.model_dump())
print("correctly inserted new document for",document.firstName)
return True
return False
def get_all_users(user_id =None) -> List:
client = MongoClient(MONGO_URI)
db = client.crayonics
collection = db['users']
# Insert the document
if user_id==None:
results= collection.find()
if results:
result = [result for result in results]
return result
client.close()
else:
result = collection.find_one(filter={"_id":ObjectId(user_id)})
return result
def get_user_id_from_docKey(dockId):
client = MongoClient(MONGO_URI)
db = client.crayonics
collection = db['Points']
# Insert the document
result = collection.find_one(filter={"_id":ObjectId(dockId)})
client.close()
return result['userId']
client = MongoClient(MONGO_URI)
db = client.crayonics
collection = db['Points']
def handle_change2(new_point):
logger.info(f"Extra info: {new_point}")
print("No leaderboard so creating one now")
users = get_all_users()
for user in users:
user_id = str(user['_id'])
print("Inserting user", f"User ID: {user_id}")
# Handle points retrieval safely
try:
points = get_all_simple_points_func(userId=user_id)
print("Points:", points)
except Exception as e:
logger.error(f"Error processing points for user {user_id}: {e}")
points = None # Default value to prevent errors
# Handle dream job retrieval safely
tempDreamJob = None
try:
tempDreamJob = get_dream_job(userId=user_id)
except Exception as e:
logger.error(f"Error retrieving dream job for user {user_id}: {e}")
# Assign default value if tempDreamJob is invalid
dreamJob = tempDreamJob if isinstance(tempDreamJob, str) else "IncompleteProfile"
# Try inserting into leaderboard
try:
create_leaderboard_ranking(LeaderBoardRanking(
userId=user_id,
firstName=user.get('first_name', 'Unknown'), # Safer dict access
lastName=user.get('last_name', 'Unknown'),
totalpoints=points.totalpoints if points else 0, # Prevent NoneType error
lastUpdated=datetime.now(),
careerPath=dreamJob,
))
except Exception as e:
logger.error(f"Error adding user {user_id} to leaderboard: {e}")
def handle_change3(userId:str):
if not userId:
raise ValueError("User ID not found in inserted document")
# Fetch user details
try:
leveleduser = get_all_users(user_id=userId)
except Exception as e:
leveleduser = {} # Default empty dict to prevent KeyError
# Fetch user points
try:
points = get_all_simple_points_func(userId=userId)
except Exception as e:
logger.error(f"Error fetching points for userId {userId}: {e}")
points = SimpleIndividualUserLevel(totalpoints=0)
# Fetch dream job
try:
tempDreamJob = get_dream_job(userId=userId)
print("========================================================")
print("Temp dream job", tempDreamJob)
except Exception as e:
logger.error(f"Error fetching dream job for userId {userId}: {e}")
tempDreamJob = None
dreamJob = tempDreamJob if isinstance(tempDreamJob, str) else "IncompleteProfile"
print("DreamJob", dreamJob)
# Insert into leaderboard
try:
create_leaderboard_ranking(LeaderBoardRanking(
userId=userId,
firstName=leveleduser.get('first_name', 'Unknown'),
lastName=leveleduser.get('last_name', 'Unknown'),
totalpoints=points.totalpoints,
lastUpdated=datetime.now(),
careerPath=dreamJob,
))
except Exception as e:
logger.error(f"Error adding user {userId} to leaderboard: {e}")
except Exception as e:
logger.error(f"Unexpected error processing change document: {e}")
# A function to handle changes
def handle_change(change=None):
print("Change detected in point making changes immediately")
logger.info(f"Change detected: {change}")
# add everybodies points and add it to the leaderboard table
collections = db.list_collection_names()
if "LeaderBoard" not in collections:
print("No leaderboard so creating one now")
users = get_all_users()
for user in users:
print("inserting user",f"user id {user['_id']}")
points = get_all_simple_points_func(userId=str(user['_id']))
tempDreamJob = get_dream_job(userId=str(user['_id']))
dreamJob = tempDreamJob if type(tempDreamJob)==str else "IncompleteProfile"
create_leaderboard_ranking(LeaderBoardRanking(userId=str(user['_id']),firstName=user['first_name'],lastName=user['last_name'],totalpoints=points.totalpoints,lastUpdated=datetime.now(),careerPath=dreamJob,))
else:
if change['operationType'] == 'insert':
try:
# Extract the full document
full_document = change.get('fullDocument', {})
user_id = full_document.get('userId')
print("========================================================")
print("Leveled User", user_id)
if not user_id:
raise ValueError("User ID not found in inserted document")
# Fetch user details
try:
leveleduser = get_all_users(user_id=user_id)
print("========================================================")
print("Leveled User", leveleduser)
except Exception as e:
logger.error(f"Error fetching user details for userId {user_id}: {e}")
leveleduser = {} # Default empty dict to prevent KeyError
# Fetch user points
try:
points = get_all_simple_points_func(userId=user_id)
print("========================================================")
print("Points", points)
except Exception as e:
logger.error(f"Error fetching points for userId {user_id}: {e}")
points = SimpleIndividualUserLevel(totalpoints=0)
# Fetch dream job
try:
tempDreamJob = get_dream_job(userId=user_id)
print("========================================================")
print("Temp dream job", tempDreamJob)
except Exception as e:
logger.error(f"Error fetching dream job for userId {user_id}: {e}")
tempDreamJob = None
dreamJob = tempDreamJob if isinstance(tempDreamJob, str) else "IncompleteProfile"
print("DreamJob", dreamJob)
# Insert into leaderboard
try:
create_leaderboard_ranking(LeaderBoardRanking(
userId=user_id,
firstName=leveleduser.get('first_name', 'Unknown'),
lastName=leveleduser.get('last_name', 'Unknown'),
totalpoints=points.totalpoints,
lastUpdated=datetime.now(),
careerPath=dreamJob,
))
except Exception as e:
logger.error(f"Error adding user {user_id} to leaderboard: {e}")
except Exception as e:
logger.error(f"Unexpected error processing change document: {e}")
elif change['operationType'] == 'update':
try:
# Extract the full document
doc_id = str( change.get('documentKey')['_id'])
print("========================================================")
print("document Id", doc_id)
user_id = get_user_id_from_docKey(doc_id)
if not user_id:
raise ValueError("User ID not found in inserted document")
# Fetch user details
try:
leveleduser = get_all_users(user_id=user_id)
print("========================================================")
print("Leveled User", leveleduser)
except Exception as e:
logger.error(f"Error fetching user details for userId {user_id}: {e}")
leveleduser = {} # Default empty dict to prevent KeyError
# Fetch user points
try:
points = get_all_simple_points_func(userId=user_id)
print("========================================================")
print("Points", points)
except Exception as e:
logger.error(f"Error fetching points for userId {user_id}: {e}")
points = SimpleIndividualUserLevel(totalpoints=0)
# Fetch dream job
try:
tempDreamJob = get_dream_job(userId=user_id)
print("========================================================")
print("Temp dream job", tempDreamJob)
except Exception as e:
logger.error(f"Error fetching dream job for userId {user_id}: {e}")
tempDreamJob = None
dreamJob = tempDreamJob if isinstance(tempDreamJob, str) else "IncompleteProfile"
print("DreamJob", dreamJob)
# Insert into leaderboard
try:
create_leaderboard_ranking(LeaderBoardRanking(
userId=user_id,
firstName=leveleduser.get('first_name', 'Unknown'),
lastName=leveleduser.get('last_name', 'Unknown'),
totalpoints=points.totalpoints,
lastUpdated=datetime.now(),
careerPath=dreamJob,
))
except Exception as e:
logger.error(f"Error adding user {user_id} to leaderboard: {e}")
except Exception as e:
logger.error(f"Unexpected error processing change document: {e}")
logger.info(f"Change detected:")
def watch_change_stream():
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor: # Limited to 10 threads, because there are only 10 server worker
with collection.watch() as stream:
for change in stream:
# Submit the handle_change task to the thread pool
executor.submit(handle_change, change)
# Start a background thread to watch the change stream
@app.on_event("startup")
def start_change_stream():
threading.Thread(target=watch_change_stream, daemon=True).start()