You've already forked RekomenciBackend
не помнб
This commit is contained in:
@@ -6,3 +6,14 @@ class ExperienceType(StrEnum):
|
||||
BETWEEN_1_AND_3 = "between1And3"
|
||||
BETWEEN_3_AND_6 = "between3And6"
|
||||
MORE_THAN_6 = "moreThan6"
|
||||
|
||||
|
||||
class EducationGrade(StrEnum):
|
||||
BASIC_GENERAL_EDUCATION = "basic_general_education"
|
||||
SECONDARY_GENERAL_EDUCATION = "secondary_general_education"
|
||||
SECONDARY_PROFESSIONAL_EDUCATION = "secondary_professional_education"
|
||||
BACHELOR = "bachelor"
|
||||
SPECIALIST = "specialist"
|
||||
MASTER = "master"
|
||||
POSTGRADUATE_STUDIES = "postgraduate_studies"
|
||||
OTHER = "other"
|
||||
|
||||
@@ -4,8 +4,11 @@ from typing import Protocol
|
||||
|
||||
from template_project.application.resume.entity import (
|
||||
Resume,
|
||||
ResumeEducation,
|
||||
ResumeExperience,
|
||||
ResumeId,
|
||||
ResumePrediction,
|
||||
ResumeProject,
|
||||
)
|
||||
from template_project.application.user.entity import UserId
|
||||
|
||||
@@ -32,3 +35,21 @@ class ResumePredictionDataGateway(Protocol):
|
||||
@abstractmethod
|
||||
async def load_by_resume_id(self, resume_id: ResumeId) -> ResumePrediction | None:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class ResumeExperienceDataGateway(Protocol):
|
||||
@abstractmethod
|
||||
async def load_by_resume_id(self, resume_id: ResumeId) -> Sequence[ResumeExperience]:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class ResumeEducationDataGateway(Protocol):
|
||||
@abstractmethod
|
||||
async def load_by_resume_id(self, resume_id: ResumeId) -> Sequence[ResumeEducation]:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class ResumeProjectDataGateway(Protocol):
|
||||
@abstractmethod
|
||||
async def load_by_resume_id(self, resume_id: ResumeId) -> Sequence[ResumeProject]:
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -6,19 +6,22 @@ from uuid import UUID
|
||||
from uuid_utils.compat import uuid7
|
||||
|
||||
from template_project.application.common.entity import Entity, to_entity
|
||||
from template_project.application.common.enums import ExperienceType
|
||||
from template_project.application.common.enums import EducationGrade, ExperienceType
|
||||
from template_project.application.user.entity import UserId
|
||||
|
||||
ResumeId = NewType("ResumeId", UUID)
|
||||
ResumeEmbeddingId = NewType("ResumeEmbeddingId", UUID)
|
||||
ResumePredictionId = NewType("ResumePredictionId", UUID)
|
||||
ResumeExperienceId = NewType("ResumeExperienceId", UUID)
|
||||
ResumeEducationId = NewType("ResumeEducationId", UUID)
|
||||
ResumeProjectId = NewType("ResumeProjectId", UUID)
|
||||
|
||||
|
||||
@to_entity
|
||||
class Resume(Entity[ResumeId]):
|
||||
user_id: UserId
|
||||
position: str
|
||||
# location: str
|
||||
location: str
|
||||
about_me: str
|
||||
key_skills: list[str]
|
||||
experience_type: ExperienceType
|
||||
@@ -30,6 +33,7 @@ class Resume(Entity[ResumeId]):
|
||||
cls,
|
||||
user_id: UserId,
|
||||
position: str,
|
||||
location: str,
|
||||
about_me: str,
|
||||
key_skills: list[str],
|
||||
experience_type: ExperienceType,
|
||||
@@ -41,6 +45,7 @@ class Resume(Entity[ResumeId]):
|
||||
created_at=datetime.now(tz=UTC),
|
||||
user_id=user_id,
|
||||
position=position,
|
||||
location=location,
|
||||
about_me=about_me,
|
||||
key_skills=key_skills,
|
||||
experience_type=experience_type,
|
||||
@@ -74,4 +79,78 @@ class ResumePrediction(Entity[ResumePredictionId]):
|
||||
from_salary: Decimal
|
||||
to_salary: Decimal
|
||||
recommended_skills: list[str]
|
||||
# common_recommended: str # TODO
|
||||
|
||||
|
||||
@to_entity
|
||||
class ResumeExperience(Entity[ResumeExperienceId]):
|
||||
resume_id: ResumeId
|
||||
place: str
|
||||
description: str
|
||||
months_duration: int
|
||||
|
||||
@classmethod
|
||||
def factory(
|
||||
cls,
|
||||
resume_id: ResumeId,
|
||||
place: str,
|
||||
description: str,
|
||||
months_duration: int,
|
||||
) -> Self:
|
||||
return cls(
|
||||
id=ResumeExperienceId(uuid7()),
|
||||
created_at=datetime.now(tz=UTC),
|
||||
resume_id=resume_id,
|
||||
place=place,
|
||||
description=description,
|
||||
months_duration=months_duration,
|
||||
)
|
||||
|
||||
|
||||
@to_entity
|
||||
class ResumeEducation(Entity[ResumeEducationId]):
|
||||
resume_id: ResumeId
|
||||
place: str
|
||||
grade: EducationGrade
|
||||
specialization: str
|
||||
description: str | None = None
|
||||
|
||||
@classmethod
|
||||
def factory(
|
||||
cls,
|
||||
resume_id: ResumeId,
|
||||
place: str,
|
||||
grade: EducationGrade,
|
||||
specialization: str,
|
||||
description: str | None = None,
|
||||
) -> Self:
|
||||
return cls(
|
||||
id=ResumeEducationId(uuid7()),
|
||||
created_at=datetime.now(tz=UTC),
|
||||
resume_id=resume_id,
|
||||
place=place,
|
||||
grade=grade,
|
||||
specialization=specialization,
|
||||
description=description,
|
||||
)
|
||||
|
||||
|
||||
@to_entity
|
||||
class ResumeProject(Entity[ResumeProjectId]):
|
||||
resume_id: ResumeId
|
||||
name: str
|
||||
description: str
|
||||
|
||||
@classmethod
|
||||
def factory(
|
||||
cls,
|
||||
resume_id: ResumeId,
|
||||
name: str,
|
||||
description: str,
|
||||
) -> Self:
|
||||
return cls(
|
||||
id=ResumeProjectId(uuid7()),
|
||||
created_at=datetime.now(tz=UTC),
|
||||
resume_id=resume_id,
|
||||
name=name,
|
||||
description=description,
|
||||
)
|
||||
|
||||
@@ -1,8 +1,36 @@
|
||||
from template_project.application.common.enums import ExperienceType
|
||||
from template_project.application.common.data_structure import to_data_structure
|
||||
from template_project.application.common.enums import EducationGrade, ExperienceType
|
||||
from template_project.application.common.identity_provider import IdentityProvider
|
||||
from template_project.application.common.interactor import to_interactor
|
||||
from template_project.application.common.unit_of_work import UnitOfWork
|
||||
from template_project.application.resume.entity import Resume, ResumeId
|
||||
from template_project.application.resume.entity import (
|
||||
Resume,
|
||||
ResumeEducation,
|
||||
ResumeExperience,
|
||||
ResumeId,
|
||||
ResumeProject,
|
||||
)
|
||||
|
||||
|
||||
@to_data_structure
|
||||
class ExperienceInput:
|
||||
place: str
|
||||
description: str
|
||||
months_duration: int
|
||||
|
||||
|
||||
@to_data_structure
|
||||
class EducationInput:
|
||||
place: str
|
||||
grade: EducationGrade
|
||||
specialization: str
|
||||
description: str | None = None
|
||||
|
||||
|
||||
@to_data_structure
|
||||
class ProjectInput:
|
||||
name: str
|
||||
description: str
|
||||
|
||||
|
||||
@to_interactor
|
||||
@@ -13,15 +41,20 @@ class AddResumeInteractor:
|
||||
async def execute(
|
||||
self,
|
||||
position: str,
|
||||
location: str,
|
||||
about_me: str,
|
||||
key_skills: list[str],
|
||||
experience_type: ExperienceType,
|
||||
experience: list[ExperienceInput] | None = None,
|
||||
education: list[EducationInput] | None = None,
|
||||
projects: list[ProjectInput] | None = None,
|
||||
) -> ResumeId:
|
||||
user = await self.identity_provider.get_current_user()
|
||||
|
||||
resume = Resume.factory(
|
||||
user_id=user.id,
|
||||
position=position,
|
||||
location=location,
|
||||
about_me=about_me,
|
||||
key_skills=key_skills,
|
||||
experience_type=experience_type,
|
||||
@@ -29,9 +62,38 @@ class AddResumeInteractor:
|
||||
up_resume_id=None,
|
||||
)
|
||||
|
||||
# TODO: тут надо сделать запуск фоновой таски для вычитывания подходящей вакансии
|
||||
|
||||
await self.unit_of_work.add(resume)
|
||||
|
||||
if experience:
|
||||
for exp in experience:
|
||||
resume_experience = ResumeExperience.factory(
|
||||
resume_id=resume.id,
|
||||
place=exp.place,
|
||||
description=exp.description,
|
||||
months_duration=exp.months_duration,
|
||||
)
|
||||
await self.unit_of_work.add(resume_experience)
|
||||
|
||||
if education:
|
||||
for edu in education:
|
||||
resume_education = ResumeEducation.factory(
|
||||
resume_id=resume.id,
|
||||
place=edu.place,
|
||||
grade=edu.grade,
|
||||
specialization=edu.specialization,
|
||||
description=edu.description,
|
||||
)
|
||||
await self.unit_of_work.add(resume_education)
|
||||
|
||||
if projects:
|
||||
for proj in projects:
|
||||
resume_project = ResumeProject.factory(
|
||||
resume_id=resume.id,
|
||||
name=proj.name,
|
||||
description=proj.description,
|
||||
)
|
||||
await self.unit_of_work.add(resume_project)
|
||||
|
||||
await self.unit_of_work.commit()
|
||||
|
||||
return resume.id
|
||||
|
||||
@@ -1,20 +1,77 @@
|
||||
from template_project.application.common.data_structure import to_data_structure
|
||||
from template_project.application.common.enums import ExperienceType
|
||||
from template_project.application.common.enums import EducationGrade, ExperienceType
|
||||
from template_project.application.common.identity_provider import IdentityProvider
|
||||
from template_project.application.common.interactor import to_interactor
|
||||
from template_project.application.common.unit_of_work import UnitOfWork
|
||||
from template_project.application.resume.data_gateway import ResumeDataGateway
|
||||
from template_project.application.resume.entity import Resume, ResumeId
|
||||
from template_project.application.resume.data_gateway import (
|
||||
ResumeDataGateway,
|
||||
ResumeEducationDataGateway,
|
||||
ResumeExperienceDataGateway,
|
||||
ResumeProjectDataGateway,
|
||||
)
|
||||
from template_project.application.resume.entity import (
|
||||
Resume,
|
||||
ResumeEducation,
|
||||
ResumeExperience,
|
||||
ResumeId,
|
||||
ResumeProject,
|
||||
)
|
||||
from template_project.application.resume.errors import ResumeDoesBelongUserError
|
||||
|
||||
|
||||
@to_data_structure
|
||||
class _Response:
|
||||
class ExperienceInput:
|
||||
place: str
|
||||
description: str
|
||||
months_duration: int
|
||||
|
||||
|
||||
@to_data_structure
|
||||
class EducationInput:
|
||||
place: str
|
||||
grade: EducationGrade
|
||||
specialization: str
|
||||
description: str | None = None
|
||||
|
||||
|
||||
@to_data_structure
|
||||
class ProjectInput:
|
||||
name: str
|
||||
description: str
|
||||
|
||||
|
||||
@to_data_structure
|
||||
class ExperienceItemResponse:
|
||||
place: str
|
||||
description: str
|
||||
months_duration: int
|
||||
|
||||
|
||||
@to_data_structure
|
||||
class EducationItemResponse:
|
||||
place: str
|
||||
grade: EducationGrade
|
||||
specialization: str
|
||||
description: str | None
|
||||
|
||||
|
||||
@to_data_structure
|
||||
class ProjectItemResponse:
|
||||
name: str
|
||||
description: str
|
||||
|
||||
|
||||
@to_data_structure
|
||||
class EditResumeResponse:
|
||||
id: ResumeId
|
||||
position: str
|
||||
location: str
|
||||
about_me: str
|
||||
key_skills: list[str]
|
||||
experience_type: ExperienceType
|
||||
experience: list[ExperienceItemResponse]
|
||||
education: list[EducationItemResponse]
|
||||
projects: list[ProjectItemResponse]
|
||||
|
||||
|
||||
@to_interactor
|
||||
@@ -22,21 +79,29 @@ class EditResumeInteractor:
|
||||
unit_of_work: UnitOfWork
|
||||
identity_provider: IdentityProvider
|
||||
resume_data_gateway: ResumeDataGateway
|
||||
resume_experience_data_gateway: ResumeExperienceDataGateway
|
||||
resume_education_data_gateway: ResumeEducationDataGateway
|
||||
resume_project_data_gateway: ResumeProjectDataGateway
|
||||
|
||||
async def execute(
|
||||
self,
|
||||
resume_id: ResumeId,
|
||||
position: str | None,
|
||||
location: str | None,
|
||||
about_me: str | None,
|
||||
key_skills: list[str] | None,
|
||||
experience_type: ExperienceType | None,
|
||||
) -> _Response:
|
||||
experience: list[ExperienceInput] | None = None,
|
||||
education: list[EducationInput] | None = None,
|
||||
projects: list[ProjectInput] | None = None,
|
||||
) -> EditResumeResponse:
|
||||
user = await self.identity_provider.get_current_user()
|
||||
old_resume = await self.resume_data_gateway.load(resume_id)
|
||||
if old_resume.user_id != user.id:
|
||||
raise ResumeDoesBelongUserError
|
||||
|
||||
new_position = position if position is not None else old_resume.position
|
||||
new_location = location if location is not None else old_resume.location
|
||||
new_about_me = about_me if about_me is not None else old_resume.about_me
|
||||
new_key_skills = key_skills if key_skills is not None else old_resume.key_skills
|
||||
new_experience_type = experience_type if experience_type is not None else old_resume.experience_type
|
||||
@@ -44,6 +109,7 @@ class EditResumeInteractor:
|
||||
new_resume = Resume.factory(
|
||||
user_id=user.id,
|
||||
position=new_position,
|
||||
location=new_location,
|
||||
about_me=new_about_me,
|
||||
key_skills=new_key_skills,
|
||||
experience_type=new_experience_type,
|
||||
@@ -53,15 +119,74 @@ class EditResumeInteractor:
|
||||
|
||||
await self.unit_of_work.add(new_resume)
|
||||
|
||||
if experience is not None:
|
||||
for exp in experience:
|
||||
resume_experience = ResumeExperience.factory(
|
||||
resume_id=new_resume.id,
|
||||
place=exp.place,
|
||||
description=exp.description,
|
||||
months_duration=exp.months_duration,
|
||||
)
|
||||
await self.unit_of_work.add(resume_experience)
|
||||
|
||||
if education is not None:
|
||||
for edu in education:
|
||||
resume_education = ResumeEducation.factory(
|
||||
resume_id=new_resume.id,
|
||||
place=edu.place,
|
||||
grade=edu.grade,
|
||||
specialization=edu.specialization,
|
||||
description=edu.description,
|
||||
)
|
||||
await self.unit_of_work.add(resume_education)
|
||||
|
||||
if projects is not None:
|
||||
for proj in projects:
|
||||
resume_project = ResumeProject.factory(
|
||||
resume_id=new_resume.id,
|
||||
name=proj.name,
|
||||
description=proj.description,
|
||||
)
|
||||
await self.unit_of_work.add(resume_project)
|
||||
|
||||
if old_resume.up_resume_id is None:
|
||||
old_resume.up_resume_id = new_resume.id
|
||||
|
||||
await self.unit_of_work.commit()
|
||||
|
||||
return _Response(
|
||||
new_experiences = await self.resume_experience_data_gateway.load_by_resume_id(new_resume.id)
|
||||
new_educations = await self.resume_education_data_gateway.load_by_resume_id(new_resume.id)
|
||||
new_projects = await self.resume_project_data_gateway.load_by_resume_id(new_resume.id)
|
||||
|
||||
return EditResumeResponse(
|
||||
id=new_resume.id,
|
||||
position=new_resume.position,
|
||||
location=new_resume.location,
|
||||
about_me=new_resume.about_me,
|
||||
key_skills=new_resume.key_skills,
|
||||
experience_type=new_resume.experience_type,
|
||||
experience=[
|
||||
ExperienceItemResponse(
|
||||
place=exp.place,
|
||||
description=exp.description,
|
||||
months_duration=exp.months_duration,
|
||||
)
|
||||
for exp in new_experiences
|
||||
],
|
||||
education=[
|
||||
EducationItemResponse(
|
||||
place=edu.place,
|
||||
grade=edu.grade,
|
||||
specialization=edu.specialization,
|
||||
description=edu.description,
|
||||
)
|
||||
for edu in new_educations
|
||||
],
|
||||
projects=[
|
||||
ProjectItemResponse(
|
||||
name=proj.name,
|
||||
description=proj.description,
|
||||
)
|
||||
for proj in new_projects
|
||||
],
|
||||
)
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
from decimal import Decimal
|
||||
|
||||
from template_project.application.common.data_structure import to_data_structure
|
||||
from template_project.application.common.enums import ExperienceType
|
||||
from template_project.application.common.enums import EducationGrade, ExperienceType
|
||||
from template_project.application.common.identity_provider import IdentityProvider
|
||||
from template_project.application.common.interactor import to_interactor
|
||||
from template_project.application.resume.data_gateway import ResumeDataGateway, ResumePredictionDataGateway
|
||||
from template_project.application.resume.data_gateway import (
|
||||
ResumeDataGateway,
|
||||
ResumeEducationDataGateway,
|
||||
ResumeExperienceDataGateway,
|
||||
ResumePredictionDataGateway,
|
||||
ResumeProjectDataGateway,
|
||||
)
|
||||
from template_project.application.resume.entity import ResumeId
|
||||
from template_project.application.resume.errors import ResumeDoesBelongUserError
|
||||
|
||||
@@ -17,12 +23,37 @@ class ResumePredictionResponse:
|
||||
|
||||
|
||||
@to_data_structure
|
||||
class _Response:
|
||||
class ExperienceItemResponse:
|
||||
place: str
|
||||
description: str
|
||||
months_duration: int
|
||||
|
||||
|
||||
@to_data_structure
|
||||
class EducationItemResponse:
|
||||
place: str
|
||||
grade: EducationGrade
|
||||
specialization: str
|
||||
description: str | None
|
||||
|
||||
|
||||
@to_data_structure
|
||||
class ProjectItemResponse:
|
||||
name: str
|
||||
description: str
|
||||
|
||||
|
||||
@to_data_structure
|
||||
class GetResumeResponse:
|
||||
id: ResumeId
|
||||
position: str
|
||||
location: str
|
||||
about_me: str
|
||||
key_skills: list[str]
|
||||
experience_type: ExperienceType
|
||||
experience: list[ExperienceItemResponse]
|
||||
education: list[EducationItemResponse]
|
||||
projects: list[ProjectItemResponse]
|
||||
prediction: ResumePredictionResponse | None
|
||||
|
||||
|
||||
@@ -31,11 +62,14 @@ class GetResumeInteractor:
|
||||
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,
|
||||
) -> _Response:
|
||||
) -> GetResumeResponse:
|
||||
user = await self.identity_provider.get_current_user()
|
||||
|
||||
resume = await self.resume_data_gateway.load(resume_id)
|
||||
@@ -53,12 +87,41 @@ class GetResumeInteractor:
|
||||
else:
|
||||
prediction = None
|
||||
|
||||
return _Response(
|
||||
experiences = await self.resume_experience_data_gateway.load_by_resume_id(resume.id)
|
||||
educations = await self.resume_education_data_gateway.load_by_resume_id(resume.id)
|
||||
projects = await self.resume_project_data_gateway.load_by_resume_id(resume.id)
|
||||
|
||||
return GetResumeResponse(
|
||||
id=resume.id,
|
||||
position=resume.position,
|
||||
location=resume.location,
|
||||
about_me=resume.about_me,
|
||||
key_skills=resume.key_skills,
|
||||
experience_type=resume.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,
|
||||
)
|
||||
|
||||
@@ -67,6 +130,7 @@ class GetResumeInteractor:
|
||||
class ResumeListItemResponse:
|
||||
id: ResumeId
|
||||
position: str
|
||||
location: str
|
||||
about_me: str
|
||||
key_skills: list[str]
|
||||
experience_type: ExperienceType
|
||||
@@ -86,6 +150,7 @@ class GetResumeListInteractor:
|
||||
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,
|
||||
@@ -112,6 +177,7 @@ class GetResumeHistoryInteractor:
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user