You've already forked RekomenciBackend
fix(): update schemas
This commit is contained in:
@@ -136,35 +136,89 @@ class ResumeListItemResponse:
|
||||
about_me: str
|
||||
key_skills: list[str]
|
||||
experience_type: ExperienceType
|
||||
experience: list[ExperienceItemResponse]
|
||||
education: list[EducationItemResponse]
|
||||
projects: list[ProjectItemResponse]
|
||||
prediction: ResumePredictionResponse | None
|
||||
|
||||
|
||||
@to_interactor
|
||||
class GetResumeListInteractor:
|
||||
identity_provider: IdentityProvider
|
||||
resume_data_gateway: ResumeDataGateway
|
||||
resume_prediction_data_gateway: ResumePredictionDataGateway
|
||||
resume_experience_data_gateway: ResumeExperienceDataGateway
|
||||
resume_education_data_gateway: ResumeEducationDataGateway
|
||||
resume_project_data_gateway: ResumeProjectDataGateway
|
||||
|
||||
async def execute(self, limit: int, offset: int) -> list[ResumeListItemResponse]:
|
||||
user = await self.identity_provider.get_current_user()
|
||||
|
||||
resumes = await self.resume_data_gateway.list_latest_by_user_id(user.id, limit=limit, offset=offset)
|
||||
|
||||
return [
|
||||
ResumeListItemResponse(
|
||||
id=r.id,
|
||||
position=r.position,
|
||||
location=r.location,
|
||||
about_me=r.about_me,
|
||||
key_skills=r.key_skills,
|
||||
experience_type=r.experience_type,
|
||||
result = []
|
||||
for r in resumes:
|
||||
resume_prediction = await self.resume_prediction_data_gateway.load_by_resume_id(r.id)
|
||||
if resume_prediction is not None:
|
||||
prediction = ResumePredictionResponse(
|
||||
from_salary=resume_prediction.from_salary,
|
||||
to_salary=resume_prediction.to_salary,
|
||||
recommended_skills=resume_prediction.recommended_skills,
|
||||
)
|
||||
else:
|
||||
prediction = None
|
||||
|
||||
experiences = await self.resume_experience_data_gateway.load_by_resume_id(r.id)
|
||||
educations = await self.resume_education_data_gateway.load_by_resume_id(r.id)
|
||||
projects = await self.resume_project_data_gateway.load_by_resume_id(r.id)
|
||||
|
||||
result.append(
|
||||
ResumeListItemResponse(
|
||||
id=r.id,
|
||||
position=r.position,
|
||||
location=r.location,
|
||||
about_me=r.about_me,
|
||||
key_skills=r.key_skills,
|
||||
experience_type=r.experience_type,
|
||||
experience=[
|
||||
ExperienceItemResponse(
|
||||
place=exp.place,
|
||||
description=exp.description,
|
||||
months_duration=exp.months_duration,
|
||||
)
|
||||
for exp in experiences
|
||||
],
|
||||
education=[
|
||||
EducationItemResponse(
|
||||
place=edu.place,
|
||||
grade=edu.grade,
|
||||
specialization=edu.specialization,
|
||||
description=edu.description,
|
||||
)
|
||||
for edu in educations
|
||||
],
|
||||
projects=[
|
||||
ProjectItemResponse(
|
||||
name=proj.name,
|
||||
description=proj.description,
|
||||
)
|
||||
for proj in projects
|
||||
],
|
||||
prediction=prediction,
|
||||
)
|
||||
)
|
||||
for r in resumes
|
||||
]
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@to_interactor
|
||||
class GetResumeHistoryInteractor:
|
||||
identity_provider: IdentityProvider
|
||||
resume_data_gateway: ResumeDataGateway
|
||||
resume_prediction_data_gateway: ResumePredictionDataGateway
|
||||
resume_experience_data_gateway: ResumeExperienceDataGateway
|
||||
resume_education_data_gateway: ResumeEducationDataGateway
|
||||
resume_project_data_gateway: ResumeProjectDataGateway
|
||||
|
||||
async def execute(self, resume_id: ResumeId) -> list[ResumeListItemResponse]:
|
||||
user = await self.identity_provider.get_current_user()
|
||||
@@ -177,14 +231,56 @@ class GetResumeHistoryInteractor:
|
||||
|
||||
history = await self.resume_data_gateway.get_history(resume_id)
|
||||
|
||||
return [
|
||||
ResumeListItemResponse(
|
||||
id=r.id,
|
||||
position=r.position,
|
||||
location=r.location,
|
||||
about_me=r.about_me,
|
||||
key_skills=r.key_skills,
|
||||
experience_type=r.experience_type,
|
||||
result = []
|
||||
for r in history:
|
||||
resume_prediction = await self.resume_prediction_data_gateway.load_by_resume_id(r.id)
|
||||
if resume_prediction is not None:
|
||||
prediction = ResumePredictionResponse(
|
||||
from_salary=resume_prediction.from_salary,
|
||||
to_salary=resume_prediction.to_salary,
|
||||
recommended_skills=resume_prediction.recommended_skills,
|
||||
)
|
||||
else:
|
||||
prediction = None
|
||||
|
||||
experiences = await self.resume_experience_data_gateway.load_by_resume_id(r.id)
|
||||
educations = await self.resume_education_data_gateway.load_by_resume_id(r.id)
|
||||
projects = await self.resume_project_data_gateway.load_by_resume_id(r.id)
|
||||
|
||||
result.append(
|
||||
ResumeListItemResponse(
|
||||
id=r.id,
|
||||
position=r.position,
|
||||
location=r.location,
|
||||
about_me=r.about_me,
|
||||
key_skills=r.key_skills,
|
||||
experience_type=r.experience_type,
|
||||
experience=[
|
||||
ExperienceItemResponse(
|
||||
place=exp.place,
|
||||
description=exp.description,
|
||||
months_duration=exp.months_duration,
|
||||
)
|
||||
for exp in experiences
|
||||
],
|
||||
education=[
|
||||
EducationItemResponse(
|
||||
place=edu.place,
|
||||
grade=edu.grade,
|
||||
specialization=edu.specialization,
|
||||
description=edu.description,
|
||||
)
|
||||
for edu in educations
|
||||
],
|
||||
projects=[
|
||||
ProjectItemResponse(
|
||||
name=proj.name,
|
||||
description=proj.description,
|
||||
)
|
||||
for proj in projects
|
||||
],
|
||||
prediction=prediction,
|
||||
)
|
||||
)
|
||||
for r in history
|
||||
]
|
||||
|
||||
return result
|
||||
|
||||
@@ -41,23 +41,21 @@ def _filter_and_sort_vacancies(
|
||||
suitable_vacancies: list[SuitableVacancy],
|
||||
limit: int = 50,
|
||||
) -> list[SuitableVacancy]:
|
||||
def is_suitable(vacancy: SuitableVacancy) -> bool:
|
||||
experience_match = resume.experience_type == vacancy.vacancy.experience_type
|
||||
skills_matching = _calculate_skills_matching(resume.key_skills, vacancy.vacancy.key_skills)
|
||||
skills_match = skills_matching >= 0.5
|
||||
return experience_match and skills_match
|
||||
def calculate_priority(vacancy: SuitableVacancy) -> float:
|
||||
priority = vacancy.resume_similarity
|
||||
|
||||
filtered = [v for v in suitable_vacancies if is_suitable(v)]
|
||||
if resume.experience_type == vacancy.vacancy.experience_type:
|
||||
priority += 0.1
|
||||
|
||||
if len(filtered) >= limit:
|
||||
filtered.sort(key=lambda v: v.resume_similarity, reverse=True)
|
||||
return filtered[:limit]
|
||||
if resume.key_skills:
|
||||
skills_matching = _calculate_skills_matching(resume.key_skills, vacancy.vacancy.key_skills)
|
||||
priority += skills_matching * 0.2
|
||||
|
||||
remaining = [v for v in suitable_vacancies if v not in filtered]
|
||||
remaining.sort(key=lambda v: v.resume_similarity, reverse=True)
|
||||
return priority
|
||||
|
||||
total_needed = limit - len(filtered)
|
||||
return filtered + remaining[:total_needed]
|
||||
sorted_vacancies = sorted(suitable_vacancies, key=calculate_priority, reverse=True)
|
||||
|
||||
return sorted_vacancies[:limit]
|
||||
|
||||
|
||||
@to_data_structure
|
||||
|
||||
@@ -227,6 +227,10 @@ class ResumeListItem(BaseModel):
|
||||
about_me: str = Field(description="About me section")
|
||||
key_skills: list[str] = Field(description="List of key skills")
|
||||
experience_type: ExperienceType = Field(description="Experience type")
|
||||
experience: list[ExperienceItem] = Field(description="Work experience list")
|
||||
education: list[EducationItem] = Field(description="Education list")
|
||||
projects: list[ProjectItem] = Field(description="Projects list")
|
||||
prediction: SalaryPrediction | None = Field(None, description="Salary prediction (can be null)")
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
@@ -236,6 +240,27 @@ class ResumeListItem(BaseModel):
|
||||
"about_me": "Experienced Python developer with 5 years of experience",
|
||||
"key_skills": ["Python", "FastAPI", "PostgreSQL"],
|
||||
"experience_type": "between3And6",
|
||||
"experience": [
|
||||
{
|
||||
"place": "T-bank",
|
||||
"description": "some description lorem ipsum",
|
||||
"months_duration": 12,
|
||||
}
|
||||
],
|
||||
"education": [
|
||||
{
|
||||
"place": "Central university",
|
||||
"grade": "bachelor",
|
||||
"specialization": "IT guy",
|
||||
"description": "optional field, if user want add something",
|
||||
}
|
||||
],
|
||||
"projects": [
|
||||
{
|
||||
"name": "Rekomenci fluon",
|
||||
"description": "fucking shit",
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -255,9 +280,31 @@ class GetResumeListResponse(BaseModel):
|
||||
"resumes": [
|
||||
{
|
||||
"position": "Python Developer",
|
||||
"location": "Moscow",
|
||||
"about_me": "Experienced Python developer",
|
||||
"key_skills": ["Python", "FastAPI"],
|
||||
"experience_type": "between3And6",
|
||||
"experience": [
|
||||
{
|
||||
"place": "T-bank",
|
||||
"description": "some description lorem ipsum",
|
||||
"months_duration": 12,
|
||||
}
|
||||
],
|
||||
"education": [
|
||||
{
|
||||
"place": "Central university",
|
||||
"grade": "bachelor",
|
||||
"specialization": "IT guy",
|
||||
"description": "optional field, if user want add something",
|
||||
}
|
||||
],
|
||||
"projects": [
|
||||
{
|
||||
"name": "Rekomenci fluon",
|
||||
"description": "fucking shit",
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -279,9 +326,31 @@ class GetResumeHistoryResponse(BaseModel):
|
||||
"resumes": [
|
||||
{
|
||||
"position": "Python Developer",
|
||||
"location": "Moscow",
|
||||
"about_me": "Experienced Python developer",
|
||||
"key_skills": ["Python", "FastAPI"],
|
||||
"experience_type": "between3And6",
|
||||
"experience": [
|
||||
{
|
||||
"place": "T-bank",
|
||||
"description": "some description lorem ipsum",
|
||||
"months_duration": 12,
|
||||
}
|
||||
],
|
||||
"education": [
|
||||
{
|
||||
"place": "Central university",
|
||||
"grade": "bachelor",
|
||||
"specialization": "IT guy",
|
||||
"description": "optional field, if user want add something",
|
||||
}
|
||||
],
|
||||
"projects": [
|
||||
{
|
||||
"name": "Rekomenci fluon",
|
||||
"description": "fucking shit",
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -379,6 +448,37 @@ async def get_resume_list(
|
||||
about_me=r.about_me,
|
||||
key_skills=r.key_skills,
|
||||
experience_type=r.experience_type,
|
||||
experience=[
|
||||
ExperienceItem(
|
||||
place=exp.place,
|
||||
description=exp.description,
|
||||
months_duration=exp.months_duration,
|
||||
)
|
||||
for exp in r.experience
|
||||
],
|
||||
education=[
|
||||
EducationItem(
|
||||
place=edu.place,
|
||||
grade=edu.grade,
|
||||
specialization=edu.specialization,
|
||||
description=edu.description,
|
||||
)
|
||||
for edu in r.education
|
||||
],
|
||||
projects=[
|
||||
ProjectItem(
|
||||
name=proj.name,
|
||||
description=proj.description,
|
||||
)
|
||||
for proj in r.projects
|
||||
],
|
||||
prediction=SalaryPrediction(
|
||||
from_salary=r.prediction.from_salary,
|
||||
to_salary=r.prediction.to_salary,
|
||||
recommended_skills=r.prediction.recommended_skills,
|
||||
)
|
||||
if r.prediction is not None
|
||||
else None,
|
||||
)
|
||||
for r in interactor_response
|
||||
]
|
||||
@@ -588,6 +688,37 @@ async def get_resume_history(
|
||||
about_me=r.about_me,
|
||||
key_skills=r.key_skills,
|
||||
experience_type=r.experience_type,
|
||||
experience=[
|
||||
ExperienceItem(
|
||||
place=exp.place,
|
||||
description=exp.description,
|
||||
months_duration=exp.months_duration,
|
||||
)
|
||||
for exp in r.experience
|
||||
],
|
||||
education=[
|
||||
EducationItem(
|
||||
place=edu.place,
|
||||
grade=edu.grade,
|
||||
specialization=edu.specialization,
|
||||
description=edu.description,
|
||||
)
|
||||
for edu in r.education
|
||||
],
|
||||
projects=[
|
||||
ProjectItem(
|
||||
name=proj.name,
|
||||
description=proj.description,
|
||||
)
|
||||
for proj in r.projects
|
||||
],
|
||||
prediction=SalaryPrediction(
|
||||
from_salary=r.prediction.from_salary,
|
||||
to_salary=r.prediction.to_salary,
|
||||
recommended_skills=r.prediction.recommended_skills,
|
||||
)
|
||||
if r.prediction is not None
|
||||
else None,
|
||||
)
|
||||
for r in interactor_response
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user