Spaces:
Runtime error
Runtime error
Upload 2 files
Browse files- __init__.py +0 -0
- main.py +214 -0
__init__.py
ADDED
File without changes
|
main.py
ADDED
@@ -0,0 +1,214 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import FastAPI, HTTPException
|
2 |
+
from fastapi.middleware.cors import CORSMiddleware
|
3 |
+
from pydantic import BaseModel
|
4 |
+
import databases
|
5 |
+
import sqlalchemy
|
6 |
+
from sqlalchemy.sql import select, and_
|
7 |
+
from fuzzywuzzy import process
|
8 |
+
import urllib.parse
|
9 |
+
import os
|
10 |
+
from dotenv import load_dotenv
|
11 |
+
import json
|
12 |
+
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, DateTime, Boolean, Text, JSON
|
13 |
+
import time
|
14 |
+
from datetime import datetime
|
15 |
+
|
16 |
+
load_dotenv()
|
17 |
+
|
18 |
+
class JobQuery(BaseModel):
|
19 |
+
budget: str
|
20 |
+
experience: str
|
21 |
+
startDate: str
|
22 |
+
workType: str
|
23 |
+
semanticSearchString: str
|
24 |
+
|
25 |
+
username = urllib.parse.quote_plus('trial_user')
|
26 |
+
password = urllib.parse.quote_plus('trial_user_12345#')
|
27 |
+
host = '35.224.61.48'
|
28 |
+
port = '3306'
|
29 |
+
database_name = 'MERCOR_TRIAL_SCHEMA'
|
30 |
+
|
31 |
+
DATABASE_URL = f"mysql://{username}:{password}@{host}:{port}/{database_name}"
|
32 |
+
database = databases.Database(DATABASE_URL)
|
33 |
+
|
34 |
+
metadata = sqlalchemy.MetaData()
|
35 |
+
|
36 |
+
skills = sqlalchemy.Table(
|
37 |
+
"Skills",
|
38 |
+
metadata,
|
39 |
+
sqlalchemy.Column("skillId", sqlalchemy.String, primary_key=True),
|
40 |
+
sqlalchemy.Column("skillName", sqlalchemy.String),
|
41 |
+
sqlalchemy.Column("skillValue", sqlalchemy.String, unique=True),
|
42 |
+
)
|
43 |
+
|
44 |
+
jobs = sqlalchemy.Table(
|
45 |
+
"MercorUsers",
|
46 |
+
metadata,
|
47 |
+
sqlalchemy.Column("userId", sqlalchemy.String, primary_key=True),
|
48 |
+
sqlalchemy.Column("email", sqlalchemy.String, unique=True),
|
49 |
+
sqlalchemy.Column("name", sqlalchemy.String),
|
50 |
+
sqlalchemy.Column("phone", sqlalchemy.String),
|
51 |
+
sqlalchemy.Column("residence", JSON),
|
52 |
+
sqlalchemy.Column("profilePic", Text),
|
53 |
+
sqlalchemy.Column("createdAt", DateTime),
|
54 |
+
sqlalchemy.Column("lastLogin", DateTime),
|
55 |
+
sqlalchemy.Column("notes", Text),
|
56 |
+
sqlalchemy.Column("referralCode", sqlalchemy.String, unique=True),
|
57 |
+
sqlalchemy.Column("isGptEnabled", sqlalchemy.Boolean),
|
58 |
+
sqlalchemy.Column("preferredRole", sqlalchemy.String),
|
59 |
+
sqlalchemy.Column("fullTimeStatus", sqlalchemy.String),
|
60 |
+
sqlalchemy.Column("workAvailability", sqlalchemy.String),
|
61 |
+
sqlalchemy.Column("fullTimeSalaryCurrency", sqlalchemy.String),
|
62 |
+
sqlalchemy.Column("fullTimeSalary", sqlalchemy.String),
|
63 |
+
sqlalchemy.Column("partTimeSalaryCurrency", sqlalchemy.String),
|
64 |
+
sqlalchemy.Column("partTimeSalary", sqlalchemy.String),
|
65 |
+
sqlalchemy.Column("fullTime", sqlalchemy.Boolean),
|
66 |
+
sqlalchemy.Column("fullTimeAvailability", sqlalchemy.Integer),
|
67 |
+
sqlalchemy.Column("partTime", sqlalchemy.Boolean),
|
68 |
+
sqlalchemy.Column("partTimeAvailability", sqlalchemy.Integer),
|
69 |
+
sqlalchemy.Column("w8BenUrl", JSON),
|
70 |
+
sqlalchemy.Column("tosUrl", Text),
|
71 |
+
sqlalchemy.Column("policyUrls", JSON),
|
72 |
+
sqlalchemy.Column("isPreVetted", sqlalchemy.Boolean),
|
73 |
+
sqlalchemy.Column("isActive", sqlalchemy.Boolean),
|
74 |
+
sqlalchemy.Column("isComplete", sqlalchemy.Boolean),
|
75 |
+
sqlalchemy.Column("summary", Text),
|
76 |
+
sqlalchemy.Column("preVettedAt", DateTime)
|
77 |
+
)
|
78 |
+
|
79 |
+
MercorUserSkills = sqlalchemy.Table(
|
80 |
+
"MercorUserSkills",
|
81 |
+
metadata,
|
82 |
+
sqlalchemy.Column("userId", sqlalchemy.String, sqlalchemy.ForeignKey("MercorUsers.userId"), primary_key=True),
|
83 |
+
sqlalchemy.Column("skillId", sqlalchemy.String, sqlalchemy.ForeignKey("Skills.skillId"), primary_key=True),
|
84 |
+
sqlalchemy.Column("isPrimary", sqlalchemy.Boolean, default=False),
|
85 |
+
sqlalchemy.Column("order", Integer, default=0)
|
86 |
+
)
|
87 |
+
|
88 |
+
UserResume = sqlalchemy.Table(
|
89 |
+
"UserResume",
|
90 |
+
metadata,
|
91 |
+
sqlalchemy.Column("resumeId", sqlalchemy.String, primary_key=True),
|
92 |
+
sqlalchemy.Column("userId", sqlalchemy.String, sqlalchemy.ForeignKey("MercorUsers.userId")),
|
93 |
+
sqlalchemy.Column("url", Text),
|
94 |
+
sqlalchemy.Column("filename", String),
|
95 |
+
sqlalchemy.Column("createdAt", DateTime),
|
96 |
+
sqlalchemy.Column("updatedAt", DateTime),
|
97 |
+
sqlalchemy.Column("source", String),
|
98 |
+
sqlalchemy.Column("ocrText", Text),
|
99 |
+
sqlalchemy.Column("ocrEmail", String),
|
100 |
+
sqlalchemy.Column("ocrGithubUsername", String),
|
101 |
+
sqlalchemy.Column("resumeBasedQuestions", Text),
|
102 |
+
sqlalchemy.Column("isInvitedToInterview", Boolean),
|
103 |
+
sqlalchemy.Column("reminderTasksIds", JSON)
|
104 |
+
)
|
105 |
+
|
106 |
+
WorkExperience = sqlalchemy.Table(
|
107 |
+
"WorkExperience",
|
108 |
+
metadata,
|
109 |
+
sqlalchemy.Column("workExperienceId", sqlalchemy.String, primary_key=True),
|
110 |
+
sqlalchemy.Column("company", String),
|
111 |
+
sqlalchemy.Column("role", String),
|
112 |
+
sqlalchemy.Column("startDate", String),
|
113 |
+
sqlalchemy.Column("endDate", String),
|
114 |
+
sqlalchemy.Column("description", Text),
|
115 |
+
sqlalchemy.Column("locationCity", String),
|
116 |
+
sqlalchemy.Column("locationCountry", String),
|
117 |
+
sqlalchemy.Column("resumeId", String, sqlalchemy.ForeignKey("UserResume.resumeId"))
|
118 |
+
)
|
119 |
+
|
120 |
+
app = FastAPI()
|
121 |
+
# Configure CORS
|
122 |
+
origins = [
|
123 |
+
"http://localhost:3000", # Adjust this to the URL of your frontend
|
124 |
+
"http://127.0.0.1:3000"
|
125 |
+
]
|
126 |
+
|
127 |
+
app.add_middleware(
|
128 |
+
CORSMiddleware,
|
129 |
+
allow_origins=origins,
|
130 |
+
allow_credentials=True,
|
131 |
+
allow_methods=["*"],
|
132 |
+
allow_headers=["*"],
|
133 |
+
)
|
134 |
+
|
135 |
+
@app.on_event("startup")
|
136 |
+
async def startup():
|
137 |
+
await database.connect()
|
138 |
+
|
139 |
+
@app.on_event("shutdown")
|
140 |
+
async def shutdown():
|
141 |
+
await database.disconnect()
|
142 |
+
|
143 |
+
@app.post("/query-jobs/")
|
144 |
+
async def query_jobs(query: JobQuery):
|
145 |
+
start_time = time.time() # Start timing
|
146 |
+
|
147 |
+
# Fetch and match skills
|
148 |
+
all_skills = await database.fetch_all(select(skills.c.skillId, skills.c.skillName))
|
149 |
+
skills_dict = {skill['skillName']: skill['skillId'] for skill in all_skills}
|
150 |
+
matching_skills = process.extract(query.semanticSearchString, skills_dict.keys(), limit=10)
|
151 |
+
matching_skill_ids = [skills_dict[skill[0]] for skill in matching_skills if skill[1] > 70]
|
152 |
+
|
153 |
+
if not matching_skill_ids:
|
154 |
+
return {"message": "No skills matched your query.", "query": query.semanticSearchString}
|
155 |
+
|
156 |
+
user_skill_query = select(MercorUserSkills.c.userId).where(MercorUserSkills.c.skillId.in_(matching_skill_ids)).distinct()
|
157 |
+
user_ids = await database.fetch_all(user_skill_query)
|
158 |
+
user_ids = [user['userId'] for user in user_ids]
|
159 |
+
|
160 |
+
if not user_ids:
|
161 |
+
return {"message": "No users found with the matching skills."}
|
162 |
+
|
163 |
+
user_query = (
|
164 |
+
select(jobs)
|
165 |
+
.where(jobs.c.userId.in_(user_ids), jobs.c.fullTimeSalary <= int(query.budget))
|
166 |
+
.order_by(jobs.c.fullTimeSalary.desc()) # Sorting by fullTimeSalary descending
|
167 |
+
.limit(4) # Limiting to 4 results
|
168 |
+
)
|
169 |
+
users_with_skills = await database.fetch_all(user_query)
|
170 |
+
|
171 |
+
result = []
|
172 |
+
for user in users_with_skills:
|
173 |
+
user_skills_query = select(skills.c.skillName).join(MercorUserSkills, skills.c.skillId == MercorUserSkills.c.skillId).where(MercorUserSkills.c.userId == user['userId'])
|
174 |
+
user_skills = await database.fetch_all(user_skills_query)
|
175 |
+
user_skills_list = [skill['skillName'] for skill in user_skills]
|
176 |
+
|
177 |
+
# Fetch resume ID for user
|
178 |
+
resume_query = select(UserResume.c.resumeId).where(UserResume.c.userId == user['userId'])
|
179 |
+
resume_id = await database.fetch_one(resume_query)
|
180 |
+
|
181 |
+
# Fetch and process work experience
|
182 |
+
experience_query = select(WorkExperience).where(WorkExperience.c.resumeId == resume_id['resumeId'])
|
183 |
+
experiences = await database.fetch_all(experience_query)
|
184 |
+
companies = set()
|
185 |
+
total_experience = 0
|
186 |
+
|
187 |
+
for exp in experiences:
|
188 |
+
companies.add(exp['company'])
|
189 |
+
# Calculate duration
|
190 |
+
start_date = datetime.strptime(exp['startDate'], '%Y') if exp['startDate'] else None
|
191 |
+
end_date = datetime.strptime(exp['endDate'], '%Y') if exp['endDate'] else datetime.now()
|
192 |
+
if start_date:
|
193 |
+
duration_years = (end_date.year - start_date.year)
|
194 |
+
total_experience += duration_years
|
195 |
+
|
196 |
+
experiences_list = [{'company': exp['company'], 'role': exp['role'], 'startDate': exp['startDate'], 'endDate': exp['endDate']} for exp in experiences]
|
197 |
+
|
198 |
+
result.append({
|
199 |
+
"user_id": user['userId'],
|
200 |
+
"name": user['name'],
|
201 |
+
"email": user['email'],
|
202 |
+
"skills": user_skills_list,
|
203 |
+
"full_time_salary": user['fullTimeSalary'],
|
204 |
+
"companies": list(companies),
|
205 |
+
"total_experience_years": total_experience
|
206 |
+
})
|
207 |
+
|
208 |
+
execution_time = time.time() - start_time # Calculate execution time
|
209 |
+
|
210 |
+
return {"users": result, "execution_time": execution_time}
|
211 |
+
|
212 |
+
if __name__ == "__main__":
|
213 |
+
import uvicorn
|
214 |
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|