You've already forked RekomenciBackend
fix(): update schemas
This commit is contained in:
@@ -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
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user