hrxlib 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
hrx/__init__.py ADDED
@@ -0,0 +1,71 @@
1
+ """HRX Python SDK public API."""
2
+
3
+ from .api import HRX
4
+ from .errors import (
5
+ HrxError,
6
+ HrxFileError,
7
+ HrxParseError,
8
+ HrxSchemaError,
9
+ HrxValidationError,
10
+ HrxVersionError,
11
+ )
12
+ from .models import (
13
+ Award,
14
+ Bibliography,
15
+ BibliographyType,
16
+ Candidate,
17
+ Civility,
18
+ Contact,
19
+ ContactType,
20
+ ContractType,
21
+ Credentials,
22
+ Education,
23
+ Experience,
24
+ HrxMetadata,
25
+ Identity,
26
+ Level,
27
+ Location,
28
+ Mobility,
29
+ Preferences,
30
+ Project,
31
+ Reference,
32
+ RemoteType,
33
+ SkillDomain,
34
+ SkillItem,
35
+ Skills,
36
+ )
37
+
38
+ __all__ = [
39
+ "HRX",
40
+ "HrxError",
41
+ "HrxFileError",
42
+ "HrxParseError",
43
+ "HrxSchemaError",
44
+ "HrxValidationError",
45
+ "HrxVersionError",
46
+ "Candidate",
47
+ "HrxMetadata",
48
+ "Identity",
49
+ "Contact",
50
+ "Location",
51
+ "Skills",
52
+ "SkillDomain",
53
+ "SkillItem",
54
+ "Experience",
55
+ "Project",
56
+ "Education",
57
+ "Credentials",
58
+ "Award",
59
+ "Reference",
60
+ "Bibliography",
61
+ "Preferences",
62
+ "Civility",
63
+ "ContactType",
64
+ "Mobility",
65
+ "Level",
66
+ "ContractType",
67
+ "RemoteType",
68
+ "BibliographyType",
69
+ ]
70
+
71
+ __version__ = "0.2.0"
hrx/__init__.pyi ADDED
@@ -0,0 +1,39 @@
1
+ """Stubs du package HRX: réexporte l'API publique du SDK."""
2
+ from .api import HRX # type: ignore
3
+ from .errors import (
4
+ HrxError, # type: ignore
5
+ HrxFileError, # type: ignore
6
+ HrxParseError, # type: ignore
7
+ HrxSchemaError, # type: ignore
8
+ HrxValidationError, # type: ignore
9
+ HrxVersionError, # type: ignore
10
+ )
11
+ from .models import (
12
+ Award, # type: ignore
13
+ Bibliography, # type: ignore
14
+ BibliographyType, # type: ignore
15
+ Candidate, # type: ignore
16
+ Civility, # type: ignore
17
+ Contact, # type: ignore
18
+ ContactType, # type: ignore
19
+ ContractType, # type: ignore
20
+ Credentials, # type: ignore
21
+ Education, # type: ignore
22
+ Experience, # type: ignore
23
+ HrxMetadata, # type: ignore
24
+ Identity, # type: ignore
25
+ Level, # type: ignore
26
+ Location, # type: ignore
27
+ Mobility, # type: ignore
28
+ Preferences, # type: ignore
29
+ Project, # type: ignore
30
+ Reference, # type: ignore
31
+ RemoteType, # type: ignore
32
+ SkillDomain, # type: ignore
33
+ SkillItem, # type: ignore
34
+ Skills, # type: ignore
35
+ )
36
+
37
+ __all__: list[str]
38
+
39
+ __version__: str
hrx/api.py ADDED
@@ -0,0 +1,171 @@
1
+ """High-level API for reading and writing HRX documents."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+ from typing import Any, cast
8
+
9
+ from pydantic import ValidationError
10
+
11
+ from .errors import HrxFileError, HrxParseError, HrxSchemaError, HrxValidationError, HrxVersionError
12
+ from .models import Candidate
13
+
14
+
15
+ class HRX:
16
+ """Load, validate and serialize an HRX document."""
17
+
18
+ def __init__(self, source: Path | str | dict[str, Any] | Candidate) -> None:
19
+ self.candidate = self._load_candidate(source)
20
+
21
+ @classmethod
22
+ def from_path(cls, path: Path | str) -> "HRX":
23
+ """
24
+ Load an HRX document from a file.
25
+
26
+ Args:
27
+ path: Path to the HRX file.
28
+
29
+ Returns:
30
+ HRX document.
31
+ """
32
+ if isinstance(path, str):
33
+ path = Path(path)
34
+ return cls(path)
35
+
36
+ @classmethod
37
+ def from_json(cls, payload: str) -> "HRX":
38
+ """
39
+ Load an HRX document from a JSON string.
40
+
41
+ Args:
42
+ payload: JSON string representing the HRX document.
43
+
44
+ Returns:
45
+ HRX document.
46
+ """
47
+ return cls(payload)
48
+
49
+ @classmethod
50
+ def from_dict(cls, payload: dict[str, Any]) -> "HRX":
51
+ """
52
+ Load an HRX document from a dictionary.
53
+
54
+ Args:
55
+ payload: Dictionary representing the HRX document.
56
+
57
+ Returns:
58
+ HRX document.
59
+ """
60
+ return cls(payload)
61
+
62
+ @staticmethod
63
+ def _load_candidate(source: Path | str | dict[str, Any] | Candidate) -> Candidate:
64
+ if isinstance(source, Candidate):
65
+ candidate = source
66
+ elif isinstance(source, Path):
67
+ candidate = HRX._load_from_path(source)
68
+ elif isinstance(source, dict):
69
+ candidate = HRX._load_from_dict(source)
70
+ else:
71
+ source_path = Path(source)
72
+ if source_path.exists():
73
+ candidate = HRX._load_from_path(source_path)
74
+ else:
75
+ candidate = HRX._load_from_json(source)
76
+
77
+ version = candidate.hrx.version
78
+ if version != "1.0":
79
+ raise HrxVersionError(
80
+ f"Unsupported HRX version '{version}'. Only version '1.0' is currently supported.",
81
+ )
82
+ return candidate
83
+
84
+ @staticmethod
85
+ def _load_from_path(path: Path) -> Candidate:
86
+ try:
87
+ payload = path.read_text(encoding="utf-8")
88
+ except OSError as exc:
89
+ raise HrxFileError(f"Cannot read HRX file: {path}") from exc
90
+ return HRX._load_from_json(payload)
91
+
92
+ @staticmethod
93
+ def _load_from_json(payload: str) -> Candidate:
94
+ try:
95
+ parsed = json.loads(payload)
96
+ except json.JSONDecodeError as exc:
97
+ raise HrxParseError(f"Invalid HRX JSON payload: {exc}") from exc
98
+ if not isinstance(parsed, dict):
99
+ raise HrxParseError("HRX payload must be a JSON object.")
100
+ return HRX._load_from_dict(cast(dict[str, Any], parsed))
101
+
102
+ @staticmethod
103
+ def _load_from_dict(payload: dict[str, Any]) -> Candidate:
104
+ try:
105
+ return Candidate.model_validate(payload)
106
+ except ValidationError as exc:
107
+ raise HrxValidationError(
108
+ "HRX payload does not match the expected model.",
109
+ details=exc.errors()
110
+ ) from exc
111
+
112
+ def to_dict(self) -> dict[str, Any]:
113
+ """
114
+ Serialize the HRX document to a dictionary.
115
+
116
+ Args:
117
+ None
118
+
119
+ Returns:
120
+ Dictionary representing the HRX document.
121
+ """
122
+ return self.candidate.model_dump(mode="json", by_alias=True, exclude_none=True)
123
+
124
+ def to_json(self, *, indent: int = 2) -> str:
125
+ """
126
+ Serialize the HRX document to a JSON string.
127
+
128
+ Args:
129
+ indent: Number of spaces for indentation in the JSON string.
130
+
131
+ Returns:
132
+ JSON string representing the HRX document.
133
+ """
134
+ return self.candidate.model_dump_json(by_alias=True, exclude_none=True, indent=indent)
135
+
136
+ def validate_against_schema(self, schema: dict[str, Any] | str | Path) -> None:
137
+ """
138
+ Validate the HRX document against a JSON schema.
139
+
140
+ Args:
141
+ schema: JSON schema to validate against.
142
+
143
+ Returns:
144
+ None
145
+ """
146
+ try:
147
+ from jsonschema import validate as jsonschema_validate #pylint: disable=import-outside-toplevel
148
+ from jsonschema.exceptions import ValidationError as JsonSchemaValidationError #pylint: disable=import-outside-toplevel
149
+ except ImportError as exc:
150
+ raise HrxSchemaError(
151
+ "jsonschema package is required to validate against a JSON schema."
152
+ ) from exc
153
+
154
+ if isinstance(schema, dict):
155
+ schema_data = schema
156
+ else:
157
+ schema_path = Path(schema)
158
+ try:
159
+ schema_data = json.loads(schema_path.read_text(encoding="utf-8"))
160
+ except OSError as exc:
161
+ raise HrxSchemaError(f"Cannot read schema file: {schema_path}") from exc
162
+ except json.JSONDecodeError as exc:
163
+ raise HrxSchemaError(f"Invalid schema JSON: {exc}") from exc
164
+
165
+ try:
166
+ jsonschema_validate(instance=self.to_dict(), schema=schema_data)
167
+ except JsonSchemaValidationError as exc:
168
+ raise HrxSchemaError(
169
+ "HRX payload does not match the provided JSON schema.",
170
+ details=exc.message
171
+ ) from exc
hrx/api.pyi ADDED
@@ -0,0 +1,17 @@
1
+ from pathlib import Path
2
+ from typing import Any
3
+
4
+ from .models import Candidate
5
+
6
+ class HRX:
7
+ candidate: Candidate
8
+ def __init__(self, source: Path | str | dict[str, Any] | Candidate) -> None: ...
9
+ @classmethod
10
+ def from_path(cls, path: Path | str) -> HRX: ...
11
+ @classmethod
12
+ def from_json(cls, payload: str) -> HRX: ...
13
+ @classmethod
14
+ def from_dict(cls, payload: dict[str, Any]) -> HRX: ...
15
+ def to_dict(self) -> dict[str, Any]: ...
16
+ def to_json(self, *, indent: int = 2) -> str: ...
17
+ def validate_against_schema(self, schema: dict[str, Any] | str | Path) -> None: ...
hrx/errors.py ADDED
@@ -0,0 +1,33 @@
1
+ """Custom exceptions for the HRX Python SDK."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+
8
+ class HrxError(Exception):
9
+ """Base exception for all SDK errors."""
10
+
11
+ def __init__(self, message: str, *, details: Any | None = None) -> None:
12
+ super().__init__(message)
13
+ self.details = details
14
+
15
+
16
+ class HrxFileError(HrxError):
17
+ """Raised when a file cannot be read or written."""
18
+
19
+
20
+ class HrxParseError(HrxError):
21
+ """Raised when HRX payload is not valid JSON."""
22
+
23
+
24
+ class HrxValidationError(HrxError):
25
+ """Raised when payload does not match the Pydantic HRX model."""
26
+
27
+
28
+ class HrxSchemaError(HrxError):
29
+ """Raised when payload does not match a JSON schema."""
30
+
31
+
32
+ class HrxVersionError(HrxError):
33
+ """Raised when document version is not supported."""
hrx/errors.pyi ADDED
@@ -0,0 +1,11 @@
1
+ from typing import Any
2
+
3
+ class HrxError(Exception):
4
+ details: Any | None
5
+ def __init__(self, message: str, *, details: Any | None = None) -> None: ...
6
+
7
+ class HrxFileError(HrxError): ...
8
+ class HrxParseError(HrxError): ...
9
+ class HrxValidationError(HrxError): ...
10
+ class HrxSchemaError(HrxError): ...
11
+ class HrxVersionError(HrxError): ...
hrx/models.py ADDED
@@ -0,0 +1,222 @@
1
+ """Pydantic models aligned with HRX v1 JSON schema."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from datetime import date
6
+ from enum import Enum
7
+
8
+ from pydantic import BaseModel, ConfigDict, Field, HttpUrl, StringConstraints
9
+ from typing_extensions import Annotated
10
+
11
+ YearMonth = Annotated[str, StringConstraints(pattern=r"^\d{4}-\d{2}$")]
12
+ CountryCode = Annotated[str, StringConstraints(pattern=r"^[A-Z]{2}$")]
13
+ LanguageCode = Annotated[str, StringConstraints(pattern=r"^[a-z]{2}$")]
14
+ VersionCode = Annotated[str, StringConstraints(pattern=r"^\d+\.\d+$")]
15
+
16
+
17
+ class StrictModel(BaseModel):
18
+ """Shared model config: no unknown keys and alias support."""
19
+
20
+ model_config = ConfigDict(extra="forbid", populate_by_name=True)
21
+
22
+
23
+ class Civility(str, Enum):
24
+ """Common civility titles."""
25
+ MR = "Mr."
26
+ MRS = "Mrs."
27
+ DR = "Dr."
28
+ PROF = "Prof."
29
+ MX = "Mx."
30
+ ME = "Me"
31
+ OTHER = "other"
32
+
33
+
34
+ class ContactType(str, Enum):
35
+ """Types of contact methods."""
36
+ EMAIL = "email"
37
+ PHONE = "phone"
38
+ LINKEDIN = "linkedin"
39
+ GITHUB = "github"
40
+ X = "x"
41
+ FACEBOOK = "facebook"
42
+ INSTAGRAM = "instagram"
43
+ WEBSITE = "website"
44
+ OTHER = "other"
45
+
46
+
47
+ class Mobility(str, Enum):
48
+ """Types of mobility."""
49
+ LOCAL = "local"
50
+ REGIONAL = "regional"
51
+ NATIONAL = "national"
52
+ INTERNATIONAL = "international"
53
+
54
+
55
+ class Level(str, Enum):
56
+ """Proficiency levels."""
57
+ BEGINNER = "beginner"
58
+ INTERMEDIATE = "intermediate"
59
+ ADVANCED = "advanced"
60
+ EXPERT = "expert"
61
+
62
+
63
+ class ContractType(str, Enum):
64
+ """Types of contract."""
65
+ PERMANENT = "permanent"
66
+ FIXED_TERM = "fixed-term"
67
+ FREELANCE = "freelance"
68
+ MISSION = "mission"
69
+ INTERNSHIP = "internship"
70
+ APPRENTICESHIP = "apprenticeship"
71
+
72
+
73
+ class RemoteType(str, Enum):
74
+ """Types of remote work."""
75
+ NO = "no"
76
+ PARTIAL = "partial"
77
+ FULL = "full"
78
+
79
+
80
+ class BibliographyType(str, Enum):
81
+ """Types of bibliography entries."""
82
+ ARTICLE = "article"
83
+ BOOK = "book"
84
+ CONFERENCE = "conference"
85
+ REPORT = "report"
86
+ OTHER = "other"
87
+
88
+
89
+ class HrxMetadata(StrictModel):
90
+ """HRX metadata."""
91
+ version: VersionCode
92
+ schema_uri: HttpUrl = Field(alias="schema")
93
+ issuer: str | None = None
94
+ date: date
95
+ lang: LanguageCode | None = None
96
+
97
+
98
+ class Contact(StrictModel):
99
+ """Contact information."""
100
+ type: ContactType
101
+ value: str
102
+ primary: bool | None = None
103
+
104
+
105
+ class Location(StrictModel):
106
+ """Location information."""
107
+ city: str | None = None
108
+ postal_code: str | None = None
109
+ country: CountryCode | None = None
110
+ mobility: Mobility | None = None
111
+
112
+
113
+ class Identity(StrictModel):
114
+ """Identity information."""
115
+ civility: Civility | None = None
116
+ last_name: str
117
+ first_name: str
118
+ contacts: list[Contact] | None = None
119
+ location: Location | None = None
120
+
121
+
122
+ class SkillItem(StrictModel):
123
+ """Skill item."""
124
+ label: str
125
+ level: Level | None = None
126
+
127
+
128
+ class SkillDomain(StrictModel):
129
+ """Skill domain."""
130
+ domain: str
131
+ items: list[SkillItem]
132
+
133
+
134
+ class Skills(StrictModel):
135
+ """Skills information."""
136
+ hard: list[SkillDomain] | None = None
137
+ soft: list[SkillDomain] | None = None
138
+
139
+
140
+ class Period(StrictModel):
141
+ """Period information."""
142
+ start: YearMonth
143
+ end: YearMonth | None = None
144
+
145
+
146
+ class Project(StrictModel):
147
+ """Project information."""
148
+ title: str
149
+ description: str | None = None
150
+ role: str | None = None
151
+ period: Period | None = None
152
+ url: HttpUrl | None = None
153
+
154
+
155
+ class Experience(StrictModel):
156
+ """Experience information."""
157
+ position: str
158
+ organisation: str
159
+ sector: str | None = None
160
+ period: Period
161
+ description: str | None = None
162
+ achievements: list[str] | None = None
163
+ projects: list[Project] | None = None
164
+
165
+
166
+ class Education(StrictModel):
167
+ """Education information."""
168
+ title: str
169
+ institution: str
170
+ year: int = Field(ge=1900, le=2100)
171
+ certification: bool | None = None
172
+
173
+
174
+ class Award(StrictModel):
175
+ """Award information."""
176
+ title: str
177
+ issuer: str | None = None
178
+ year: int | None = None
179
+ description: str | None = None
180
+
181
+
182
+ class Reference(StrictModel):
183
+ """Reference information."""
184
+ name: str
185
+ position: str | None = None
186
+ organisation: str | None = None
187
+ contact: str | None = None
188
+
189
+
190
+ class Bibliography(StrictModel):
191
+ """Bibliography information."""
192
+ title: str
193
+ authors: list[str] | None = None
194
+ year: int | None = None
195
+ type: BibliographyType | None = None
196
+ url: HttpUrl | None = None
197
+
198
+
199
+ class Credentials(StrictModel):
200
+ """Credentials information."""
201
+ awards: list[Award] | None = None
202
+ references: list[Reference] | None = None
203
+ bibliography: list[Bibliography] | None = None
204
+
205
+
206
+ class Preferences(StrictModel):
207
+ """Preferences information."""
208
+ availability: date | None = None
209
+ contracts: list[ContractType] | None = None
210
+ remote: RemoteType | None = None
211
+ salary_min: int | None = None
212
+
213
+
214
+ class Candidate(StrictModel):
215
+ """Candidate information."""
216
+ hrx: HrxMetadata = Field(alias="$hrx")
217
+ identity: Identity
218
+ skills: Skills | None = None
219
+ experiences: list[Experience] | None = None
220
+ education: list[Education] | None = None
221
+ credentials: Credentials | None = None
222
+ preferences: Preferences | None = None
hrx/models.pyi ADDED
@@ -0,0 +1,200 @@
1
+ """Stubs des modèles Pydantic utilisés par le SDK HRX.
2
+
3
+ Ce fichier décrit l'API de type des modèles définis dans `models.py`.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from datetime import date
9
+ from enum import Enum
10
+ from typing import List, Optional, ClassVar
11
+
12
+ from pydantic import BaseModel, HttpUrl, ConfigDict
13
+
14
+ # Types simples (les contraintes sont appliquées au runtime dans `models.py`).
15
+ YearMonth = str
16
+ CountryCode = str
17
+ LanguageCode = str
18
+ VersionCode = str
19
+
20
+
21
+ class StrictModel(BaseModel): # pylint: disable=C0115
22
+ model_config: ClassVar[ConfigDict]
23
+
24
+
25
+ class Civility(str, Enum): # pylint: disable=C0115
26
+ MR: str
27
+ MRS: str
28
+ DR: str
29
+ PROF: str
30
+ MX: str
31
+ ME: str
32
+ OTHER: str
33
+
34
+
35
+ class ContactType(str, Enum): # pylint: disable=C0115
36
+ EMAIL: str
37
+ PHONE: str
38
+ LINKEDIN: str
39
+ GITHUB: str
40
+ X: str
41
+ FACEBOOK: str
42
+ INSTAGRAM: str
43
+ WEBSITE: str
44
+ OTHER: str
45
+
46
+
47
+ class Mobility(str, Enum): # pylint: disable=C0115
48
+ LOCAL: str
49
+ REGIONAL: str
50
+ NATIONAL: str
51
+ INTERNATIONAL: str
52
+
53
+
54
+ class Level(str, Enum): # pylint: disable=C0115
55
+ BEGINNER: str
56
+ INTERMEDIATE: str
57
+ ADVANCED: str
58
+ EXPERT: str
59
+
60
+
61
+ class ContractType(str, Enum): # pylint: disable=C0115
62
+ PERMANENT: str
63
+ FIXED_TERM: str
64
+ FREELANCE: str
65
+ MISSION: str
66
+ INTERNSHIP: str
67
+ APPRENTICESHIP: str
68
+
69
+
70
+ class RemoteType(str, Enum): # pylint: disable=C0115
71
+ NO: str
72
+ PARTIAL: str
73
+ FULL: str
74
+
75
+
76
+ class BibliographyType(str, Enum): # pylint: disable=C0115
77
+ ARTICLE: str
78
+ BOOK: str
79
+ CONFERENCE: str
80
+ REPORT: str
81
+ OTHER: str
82
+
83
+
84
+ class HrxMetadata(StrictModel): # pylint: disable=C0115
85
+ version: VersionCode
86
+ schema_uri: HttpUrl
87
+ issuer: Optional[str]
88
+ date: date
89
+ lang: Optional[LanguageCode]
90
+
91
+
92
+ class Contact(StrictModel): # pylint: disable=C0115
93
+ type: ContactType
94
+ value: str
95
+ primary: Optional[bool]
96
+
97
+
98
+ class Location(StrictModel): # pylint: disable=C0115
99
+ city: Optional[str]
100
+ postal_code: Optional[str]
101
+ country: Optional[CountryCode]
102
+ mobility: Optional[Mobility]
103
+
104
+
105
+ class Identity(StrictModel): # pylint: disable=C0115
106
+ civility: Optional[Civility]
107
+ last_name: str
108
+ first_name: str
109
+ contacts: Optional[List[Contact]]
110
+ location: Optional[Location]
111
+
112
+
113
+ class SkillItem(StrictModel): # pylint: disable=C0115
114
+ label: str
115
+ level: Optional[Level]
116
+
117
+
118
+ class SkillDomain(StrictModel): # pylint: disable=C0115
119
+ domain: str
120
+ items: List[SkillItem]
121
+
122
+
123
+ class Skills(StrictModel): # pylint: disable=C0115
124
+ hard: Optional[List[SkillDomain]]
125
+ soft: Optional[List[SkillDomain]]
126
+
127
+
128
+ class Period(StrictModel): # pylint: disable=C0115
129
+ start: YearMonth
130
+ end: Optional[YearMonth]
131
+
132
+
133
+ class Project(StrictModel): # pylint: disable=C0115
134
+ title: str
135
+ description: Optional[str]
136
+ role: Optional[str]
137
+ period: Optional[Period]
138
+ url: Optional[HttpUrl]
139
+
140
+
141
+ class Experience(StrictModel): # pylint: disable=C0115
142
+ position: str
143
+ organisation: str
144
+ sector: Optional[str]
145
+ period: Period
146
+ description: Optional[str]
147
+ achievements: Optional[List[str]]
148
+ projects: Optional[List[Project]]
149
+
150
+
151
+ class Education(StrictModel): # pylint: disable=C0115
152
+ title: str
153
+ institution: str
154
+ year: int
155
+ certification: Optional[bool]
156
+
157
+
158
+ class Award(StrictModel): # pylint: disable=C0115
159
+ title: str
160
+ issuer: Optional[str]
161
+ year: Optional[int]
162
+ description: Optional[str]
163
+
164
+
165
+ class Reference(StrictModel): # pylint: disable=C0115
166
+ name: str
167
+ position: Optional[str]
168
+ organisation: Optional[str]
169
+ contact: Optional[str]
170
+
171
+
172
+ class Bibliography(StrictModel): # pylint: disable=C0115
173
+ title: str
174
+ authors: Optional[List[str]]
175
+ year: Optional[int]
176
+ type: Optional[BibliographyType]
177
+ url: Optional[HttpUrl]
178
+
179
+
180
+ class Credentials(StrictModel): # pylint: disable=C0115
181
+ awards: Optional[List[Award]]
182
+ references: Optional[List[Reference]]
183
+ bibliography: Optional[List[Bibliography]]
184
+
185
+
186
+ class Preferences(StrictModel): # pylint: disable=C0115
187
+ availability: Optional[date]
188
+ contracts: Optional[List[ContractType]]
189
+ remote: Optional[RemoteType]
190
+ salary_min: Optional[int]
191
+
192
+
193
+ class Candidate(StrictModel): # pylint: disable=C0115
194
+ hrx: HrxMetadata
195
+ identity: Identity
196
+ skills: Optional[Skills]
197
+ experiences: Optional[List[Experience]]
198
+ education: Optional[List[Education]]
199
+ credentials: Optional[Credentials]
200
+ preferences: Optional[Preferences]
hrx/py.typed ADDED
File without changes
@@ -0,0 +1,64 @@
1
+ Metadata-Version: 2.4
2
+ Name: hrxlib
3
+ Version: 0.1.0
4
+ Summary: HRX SDK for Python — types and helpers for HRX models
5
+ Author-email: HRX Project <rverschuur@audit-io.fr>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://example.com/hrx
8
+ Project-URL: Source, https://example.com/hrx/repo
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3 :: Only
11
+ Classifier: Programming Language :: Python :: 3.8
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Requires-Python: >=3.8
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: pydantic<3,>=2
22
+ Requires-Dist: jsonschema<5,>=4
23
+ Provides-Extra: test
24
+ Requires-Dist: pytest<9,>=8; extra == "test"
25
+ Dynamic: license-file
26
+
27
+ # HRX Python SDK (hrxlib)
28
+
29
+ Paquet Python fournissant les modeles Pydantic et helpers pour le format HRX.
30
+
31
+ Installation (locally):
32
+
33
+ ```bash
34
+ pip install build
35
+ cd src/hrx/sdk/python
36
+ python -m build
37
+ pip install dist/hrxlib-0.1.0-py3-none-any.whl
38
+ ```
39
+
40
+ Usage:
41
+
42
+ ```python
43
+ from pathlib import Path
44
+
45
+ from hrx import HRX
46
+
47
+ doc = HRX.from_path(Path("candidate.hrx"))
48
+ candidate = doc.candidate
49
+ payload_dict = doc.to_dict()
50
+ payload_json = doc.to_json(indent=2)
51
+
52
+ # Optional: strict JSON schema validation
53
+ doc.validate_against_schema(Path("../../models/hrx-schema-v1.json"))
54
+ ```
55
+
56
+ Erreurs SDK:
57
+
58
+ - `HrxFileError`: probleme de lecture/ecriture fichier
59
+ - `HrxParseError`: JSON invalide
60
+ - `HrxValidationError`: payload non conforme aux modeles Pydantic
61
+ - `HrxSchemaError`: payload non conforme au schema JSON
62
+ - `HrxVersionError`: version HRX non supportee
63
+
64
+ Licence: MIT
@@ -0,0 +1,14 @@
1
+ hrx/__init__.py,sha256=xcqbalz3Uhzz0nNf-dabBY8lH3UZP7U0nOlCqsk4C4M,1144
2
+ hrx/__init__.pyi,sha256=29QQ_PoP3j91PI_J-NaQ5f0j8p_dv1FBRyXNCKC5Jz8,1147
3
+ hrx/api.py,sha256=GyO98Pv_BpUSZwqYQobY9Zxr_wde3SQEoR-ca3j2Z-U,5507
4
+ hrx/api.pyi,sha256=hbVodQpxfz8olKsTlAzIK7an5Gc-wz2DDIww7LqjYaU,601
5
+ hrx/errors.py,sha256=6e6WLCmBQJFdF4MBfsNqYAHNwpWq2rVATDnQ6Fn7ZkA,792
6
+ hrx/errors.pyi,sha256=rX-osCmOG17XWxNYvoqJ0NwpKcwhCE1slS7kqOsc9Mk,339
7
+ hrx/models.py,sha256=EMszzMD-02R0lA3TLD_1ZLYTb_TqguuvnGV8RPPJ4-Q,5254
8
+ hrx/models.pyi,sha256=jE4HlUYihW4b5xBih1o9QycjQGW2Yhd1AbPUpIT9SO4,4472
9
+ hrx/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ hrxlib-0.1.0.dist-info/licenses/LICENSE,sha256=czMQ3BDspljuUYTS6sLnwgfUQKuHF4M4UrGJWD98WSU,1070
11
+ hrxlib-0.1.0.dist-info/METADATA,sha256=-b4xmn2055XjG3wAJYa4GsrblCruQHdUuSBGuA66E3s,1831
12
+ hrxlib-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
13
+ hrxlib-0.1.0.dist-info/top_level.txt,sha256=Q2RvrDWn0Q4C4N8PutqhNV2IzvRoDHXQ70VjraGxB5E,4
14
+ hrxlib-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ # MIT License
2
+
3
+ Copyright (c) 2026 HRX Project
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ hrx