vsp-demo / src /vsp /app /model /linkedin /linkedin_models.py
pquiggles's picture
fix after merge
819725d
from typing import Any, List
from pydantic import BaseModel, ConfigDict, Field, field_validator
from pydantic.alias_generators import to_camel
class BaseSchema(BaseModel):
model_config = ConfigDict(
alias_generator=to_camel,
populate_by_name=True,
from_attributes=True,
)
class DateComponent(BaseSchema):
"""
Represents a date component with year, month, and day.
Attributes:
year (int | None): The year component of the date.
month (int | None): The month component of the date. Defaults to None.
day (int | None): The day component of the date. Defaults to None.
"""
year: int | None = None
month: int | None = None
day: int | None = None
model_config = {"frozen": True} # This makes the model immutable and hashable
@field_validator("year", "month", "day", mode="before")
@classmethod
def zero_to_none(cls, v: int | None) -> int | None:
return None if v == 0 else v
# Function to format date components
class StartEndMixin(BaseSchema):
"""
A mixin class for including start and end dates in other Pydantic models.
This class is designed to be inherited by other classes that require start and end date attributes.
"""
start: DateComponent | None = None
end: DateComponent | None = None
@field_validator("start", "end", mode="before")
@classmethod
def validate_date(cls, v: dict[str, str] | DateComponent) -> dict[str, str] | DateComponent | None:
if isinstance(v, DateComponent):
return v
if not v or v.get("year") is None:
return None
return v
class Geo(BaseSchema):
country: str | None = None
city: str | None = None
full: str | None = None
class Language(BaseSchema):
name: str | None = None
proficiency: str | None = None
class Education(StartEndMixin):
field_of_study: str | None = None
degree: str | None = None
grade: str | None = None
school_name: str | None = None
description: str | None = None
activities: str | None = None
model_config = {"frozen": True} # This makes the model immutable and hashable
class Position(StartEndMixin):
company_name: str | None = None
company_username: str | None = None
company_url: str | None = None
company_industry: str | None = None
company_description: str | None = None
company_staff_count_range: str | None = None
title: str | None = None
location: str | None = None
description: str | None = None
employment_type: str | None = None
model_config = {"frozen": True} # This makes the model immutable and hashable
class Skill(BaseSchema):
name: str | None = None
class Course(BaseSchema):
name: str | None = None
number: str | None = None
class CertificationCompany(BaseSchema):
name: str | None = None
universal_name: str | None = None
logo: str | None = None
class Certification(StartEndMixin):
name: str | None = None
authority: str | None = None
company: CertificationCompany | None = None
time_period: StartEndMixin | None = None
class LinkedinProfile(BaseSchema):
"""
Represents a comprehensive profile, encompassing various sections such as articles,
accomplishments, experiences, education, certifications, courses, test scores,
and more personal and professional details.
"""
first_name: str | None = None
last_name: str | None = None
is_open_to_work: bool | None = None
is_hiring: bool | None = None
profile_picture: str | None = None
summary: str | None = None
headline: str | None = None
geo: Geo | None = None
languages: List[Language] | None = []
educations: List[Education] = []
positions: List[Position] = Field(default=[], alias="position")
full_positions: List[Position] = Field(default=[])
skills: List[Skill] | None = []
courses: List[Course] | None = []
certifications: List[Certification] | None = []
@staticmethod
def profile_from_json(json: dict[str, Any]) -> "LinkedinProfile":
"""
Create a Profile instance from the given JSON data.
:param json: The JSON data to create a Profile instance from.
:return: A Profile instance created from the given JSON data.
"""
profile = LinkedinProfile.model_validate(json)
profile.positions = profile.full_positions
return profile