File size: 4,453 Bytes
3b993c4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49b13c6
3b993c4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c1f8477
 
 
 
3b993c4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49b13c6
3b993c4
 
 
 
 
 
 
9347485
3b993c4
 
 
 
 
49b13c6
3b993c4
 
 
 
 
819725d
3b993c4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9347485
3b993c4
 
 
 
145d03b
 
 
 
 
 
 
 
 
9347485
145d03b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
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