-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathserver.py
143 lines (108 loc) · 4.09 KB
/
server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
from __future__ import annotations
import dataclasses
import json
import os
from dataclasses import dataclass
from fastapi import FastAPI, UploadFile, File, Form, WebSocket, WebSocketException
from fastapi.middleware.cors import CORSMiddleware
from core.cv_evaluator import score_cv_eligibility_verbose, score_cv_eligibility
from core.cv_interface import create_structured_cv_from_path
from core.cv_structures import CriteriaCV
from core.cv_tools import cache_path
from core.utils import gen_uuid
app = FastAPI()
app.add_middleware(
CORSMiddleware, # type: ignore
allow_credentials=True,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
# todo: rewrite to use postgres instead of filesystem
@dataclass
class BatchResumeManualEvaluationTask:
batchId: str
criteria: str
resume_file_paths: list[str]
# todo: rewrite to use postgres instead of filesystem
@dataclass
class CompletedResumeEvaluation:
parentId: str # batch or requester
resume_file_path: str
is_eligible: bool
explanation: str | None # only available with verbose mode, compute-heavy
def serialize_evaluation(
evaluation_to_serialize: CompletedResumeEvaluation,
):
evaluation_json = dataclasses.asdict(evaluation_to_serialize)
return json.dumps(evaluation_json)
batch_evaluation_queue: list[BatchResumeManualEvaluationTask] = []
observers: list[WebSocket] = []
evaluations_filepath = "evaluations.json"
def load_evaluated_resumes() -> list[str]:
try:
with open(cache_path + evaluations_filepath, "r") as file:
dict_list = json.load(file)
return dict_list
except FileNotFoundError:
return []
def save_evaluated_resumes():
os.makedirs(os.path.dirname(cache_path), exist_ok=True)
with open(cache_path + evaluations_filepath, "w") as file:
serialized = json.dumps(evaluated_resumes)
file.write(serialized)
# storing as string for now as they'll be only ever read in that format for now
evaluated_resumes: list[str] = load_evaluated_resumes()
def add_evaluated_resume(evaluation: CompletedResumeEvaluation):
serialized_evaluation = serialize_evaluation(evaluation)
evaluated_resumes.append(serialized_evaluation)
save_evaluated_resumes()
for observer in observers:
try:
observer.send_text(serialized_evaluation)
except WebSocketException:
observer.close()
observers.remove(observer)
@app.post("/resume_manual_evaluation")
async def resume_manual_evaluation(
files: list[UploadFile] = File(...), criteria: str = Form(...)
):
batch_id = gen_uuid()
criteria_json = json.loads(criteria)
criteria_object = CriteriaCV().load(criteria_json)
verbose_mode = False
for file in files:
file_contents = await file.read()
file_name = file.filename
f = open(cache_path + file_name, "wb")
f.write(file_contents)
f.close()
structured_cv = create_structured_cv_from_path(file_name)
# todo: move to an async queue asap, this can't remain here!!!
if verbose_mode:
eligibility_output = score_cv_eligibility_verbose(
structured_cv, criteria_object
)
else:
eligibility_output = score_cv_eligibility(structured_cv, criteria_object)
add_evaluated_resume(
CompletedResumeEvaluation(
parentId=batch_id,
resume_file_path=file_name,
is_eligible=eligibility_output.is_eligible,
explanation=(
eligibility_output.decision_explanation
if "decision_explanation" in eligibility_output
else None
),
)
)
return {"status": "success"}
@app.websocket("/get_resume_evaluation_results")
async def get_resume_evaluation_results(websocket: WebSocket):
await websocket.accept()
# 1. send all evaluated resumes
# 2. send each new collected resume via completion callbacks
for resume in evaluated_resumes:
await websocket.send_text(resume)
observers.append(websocket)