fix(): update schemas

This commit is contained in:
gitgernit
2025-11-23 04:25:59 +03:00
parent d1c7641698
commit f92e3d3372
3 changed files with 258 additions and 33 deletions
@@ -136,19 +136,43 @@ class ResumeListItemResponse:
about_me: str about_me: str
key_skills: list[str] key_skills: list[str]
experience_type: ExperienceType experience_type: ExperienceType
experience: list[ExperienceItemResponse]
education: list[EducationItemResponse]
projects: list[ProjectItemResponse]
prediction: ResumePredictionResponse | None
@to_interactor @to_interactor
class GetResumeListInteractor: class GetResumeListInteractor:
identity_provider: IdentityProvider identity_provider: IdentityProvider
resume_data_gateway: ResumeDataGateway 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]: async def execute(self, limit: int, offset: int) -> list[ResumeListItemResponse]:
user = await self.identity_provider.get_current_user() 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) resumes = await self.resume_data_gateway.list_latest_by_user_id(user.id, limit=limit, offset=offset)
return [ 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( ResumeListItemResponse(
id=r.id, id=r.id,
position=r.position, position=r.position,
@@ -156,15 +180,45 @@ class GetResumeListInteractor:
about_me=r.about_me, about_me=r.about_me,
key_skills=r.key_skills, key_skills=r.key_skills,
experience_type=r.experience_type, experience_type=r.experience_type,
experience=[
ExperienceItemResponse(
place=exp.place,
description=exp.description,
months_duration=exp.months_duration,
) )
for r in resumes 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,
)
)
return result
@to_interactor @to_interactor
class GetResumeHistoryInteractor: class GetResumeHistoryInteractor:
identity_provider: IdentityProvider identity_provider: IdentityProvider
resume_data_gateway: ResumeDataGateway 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]: async def execute(self, resume_id: ResumeId) -> list[ResumeListItemResponse]:
user = await self.identity_provider.get_current_user() user = await self.identity_provider.get_current_user()
@@ -177,7 +231,23 @@ class GetResumeHistoryInteractor:
history = await self.resume_data_gateway.get_history(resume_id) history = await self.resume_data_gateway.get_history(resume_id)
return [ 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( ResumeListItemResponse(
id=r.id, id=r.id,
position=r.position, position=r.position,
@@ -185,6 +255,32 @@ class GetResumeHistoryInteractor:
about_me=r.about_me, about_me=r.about_me,
key_skills=r.key_skills, key_skills=r.key_skills,
experience_type=r.experience_type, experience_type=r.experience_type,
experience=[
ExperienceItemResponse(
place=exp.place,
description=exp.description,
months_duration=exp.months_duration,
) )
for r in history 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,
)
)
return result
@@ -41,23 +41,21 @@ def _filter_and_sort_vacancies(
suitable_vacancies: list[SuitableVacancy], suitable_vacancies: list[SuitableVacancy],
limit: int = 50, limit: int = 50,
) -> list[SuitableVacancy]: ) -> list[SuitableVacancy]:
def is_suitable(vacancy: SuitableVacancy) -> bool: def calculate_priority(vacancy: SuitableVacancy) -> float:
experience_match = resume.experience_type == vacancy.vacancy.experience_type priority = vacancy.resume_similarity
if resume.experience_type == vacancy.vacancy.experience_type:
priority += 0.1
if resume.key_skills:
skills_matching = _calculate_skills_matching(resume.key_skills, vacancy.vacancy.key_skills) skills_matching = _calculate_skills_matching(resume.key_skills, vacancy.vacancy.key_skills)
skills_match = skills_matching >= 0.5 priority += skills_matching * 0.2
return experience_match and skills_match
filtered = [v for v in suitable_vacancies if is_suitable(v)] return priority
if len(filtered) >= limit: sorted_vacancies = sorted(suitable_vacancies, key=calculate_priority, reverse=True)
filtered.sort(key=lambda v: v.resume_similarity, reverse=True)
return filtered[:limit]
remaining = [v for v in suitable_vacancies if v not in filtered] return sorted_vacancies[:limit]
remaining.sort(key=lambda v: v.resume_similarity, reverse=True)
total_needed = limit - len(filtered)
return filtered + remaining[:total_needed]
@to_data_structure @to_data_structure
@@ -227,6 +227,10 @@ class ResumeListItem(BaseModel):
about_me: str = Field(description="About me section") about_me: str = Field(description="About me section")
key_skills: list[str] = Field(description="List of key skills") key_skills: list[str] = Field(description="List of key skills")
experience_type: ExperienceType = Field(description="Experience type") 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 = { model_config = {
"json_schema_extra": { "json_schema_extra": {
@@ -236,6 +240,27 @@ class ResumeListItem(BaseModel):
"about_me": "Experienced Python developer with 5 years of experience", "about_me": "Experienced Python developer with 5 years of experience",
"key_skills": ["Python", "FastAPI", "PostgreSQL"], "key_skills": ["Python", "FastAPI", "PostgreSQL"],
"experience_type": "between3And6", "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": [ "resumes": [
{ {
"position": "Python Developer", "position": "Python Developer",
"location": "Moscow",
"about_me": "Experienced Python developer", "about_me": "Experienced Python developer",
"key_skills": ["Python", "FastAPI"], "key_skills": ["Python", "FastAPI"],
"experience_type": "between3And6", "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": [ "resumes": [
{ {
"position": "Python Developer", "position": "Python Developer",
"location": "Moscow",
"about_me": "Experienced Python developer", "about_me": "Experienced Python developer",
"key_skills": ["Python", "FastAPI"], "key_skills": ["Python", "FastAPI"],
"experience_type": "between3And6", "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, about_me=r.about_me,
key_skills=r.key_skills, key_skills=r.key_skills,
experience_type=r.experience_type, 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 for r in interactor_response
] ]
@@ -588,6 +688,37 @@ async def get_resume_history(
about_me=r.about_me, about_me=r.about_me,
key_skills=r.key_skills, key_skills=r.key_skills,
experience_type=r.experience_type, 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 for r in interactor_response
] ]