lenexpy 3.0.1__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.
- lenexpy/__init__.py +129 -0
- lenexpy/decoder/__init__.py +4 -0
- lenexpy/decoder/decoder.py +15 -0
- lenexpy/decoder/encoder.py +14 -0
- lenexpy/decoder/lef_decoder.py +10 -0
- lenexpy/decoder/lef_encoder.py +19 -0
- lenexpy/decoder/lxf_decoder.py +21 -0
- lenexpy/decoder/lxf_encoder.py +24 -0
- lenexpy/models/__init__.py +0 -0
- lenexpy/models/agedate.py +20 -0
- lenexpy/models/agegroup.py +32 -0
- lenexpy/models/athelete.py +63 -0
- lenexpy/models/bank.py +13 -0
- lenexpy/models/base.py +24 -0
- lenexpy/models/club.py +48 -0
- lenexpy/models/common.py +34 -0
- lenexpy/models/constructor.py +14 -0
- lenexpy/models/contact.py +21 -0
- lenexpy/models/course.py +15 -0
- lenexpy/models/currency.py +32 -0
- lenexpy/models/entry.py +49 -0
- lenexpy/models/event.py +73 -0
- lenexpy/models/facility.py +16 -0
- lenexpy/models/fee.py +23 -0
- lenexpy/models/gender.py +7 -0
- lenexpy/models/handicap.py +34 -0
- lenexpy/models/heat.py +31 -0
- lenexpy/models/judge.py +39 -0
- lenexpy/models/lenex.py +31 -0
- lenexpy/models/meet.py +96 -0
- lenexpy/models/meetinfoentry.py +27 -0
- lenexpy/models/meetinforecord.py +33 -0
- lenexpy/models/nation.py +214 -0
- lenexpy/models/official.py +24 -0
- lenexpy/models/pointtable.py +12 -0
- lenexpy/models/pool.py +22 -0
- lenexpy/models/qualify.py +22 -0
- lenexpy/models/ranking.py +13 -0
- lenexpy/models/reactiontime.py +5 -0
- lenexpy/models/record.py +28 -0
- lenexpy/models/recordlist.py +30 -0
- lenexpy/models/relaymeet.py +33 -0
- lenexpy/models/relayposition.py +23 -0
- lenexpy/models/relayrecord.py +25 -0
- lenexpy/models/result.py +41 -0
- lenexpy/models/session.py +60 -0
- lenexpy/models/split.py +11 -0
- lenexpy/models/stroke.py +20 -0
- lenexpy/models/swimstyle.py +27 -0
- lenexpy/models/swimtime.py +73 -0
- lenexpy/models/timestandard.py +12 -0
- lenexpy/models/timestandardlist.py +35 -0
- lenexpy/models/timestandardref.py +14 -0
- lenexpy/models/timing.py +9 -0
- lenexpy/models_st/__init__.py +0 -0
- lenexpy/models_st/athelete.py +29 -0
- lenexpy/models_st/entry.py +26 -0
- lenexpy/models_st/heat.py +42 -0
- lenexpy/models_st/result.py +31 -0
- lenexpy/strenum.py +14 -0
- lenexpy-3.0.1.dist-info/METADATA +115 -0
- lenexpy-3.0.1.dist-info/RECORD +63 -0
- lenexpy-3.0.1.dist-info/WHEEL +4 -0
lenexpy/__init__.py
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
from .decoder import load as fromfile, save as tofile
|
|
2
|
+
from .models.agedate import TypeAgeDate, AgeDate
|
|
3
|
+
from .models.agegroup import Calculate, AgeGroup
|
|
4
|
+
from .models.athelete import Athlete
|
|
5
|
+
from .models.base import LenexBaseXmlModel
|
|
6
|
+
from .models.club import TypeClub, Club
|
|
7
|
+
from .models.constructor import Constructor
|
|
8
|
+
from .models.contact import Contact
|
|
9
|
+
from .models.course import Course
|
|
10
|
+
from .models.currency import Currency
|
|
11
|
+
from .models.entry import Status, Entry
|
|
12
|
+
from .models.event import Round, TypeEvent, Event
|
|
13
|
+
from .models.fee import TypeFee, Fee
|
|
14
|
+
from .models.gender import Gender
|
|
15
|
+
from .models.handicap import HandicapClass, Handicap
|
|
16
|
+
from .models.heat import Final, StatusHeat, Heat
|
|
17
|
+
from .models.judge import Role as JudgeRole, Judge
|
|
18
|
+
from .models.lenex import Lenex
|
|
19
|
+
from .models.meet import EntryType, Meet
|
|
20
|
+
from .models.meetinfoentry import MeetInfoEntry
|
|
21
|
+
from .models.meetinforecord import Role as MeetInfoRecordRole, MeetInfoRecord
|
|
22
|
+
from .models.nation import Nation
|
|
23
|
+
from .models.official import Official
|
|
24
|
+
from .models.pointtable import PointTable
|
|
25
|
+
from .models.pool import TypePool, Pool
|
|
26
|
+
from .models.qualify import Conversion, Qualify
|
|
27
|
+
from .models.ranking import Ranking
|
|
28
|
+
from .models.reactiontime import ReactionTime
|
|
29
|
+
from .models.record import Record
|
|
30
|
+
from .models.recordlist import RecordList
|
|
31
|
+
from .models.relaymeet import RelayMeet
|
|
32
|
+
from .models.relayposition import (
|
|
33
|
+
StatusRelayPosition as RelayPositionStatus,
|
|
34
|
+
RelayPosition,
|
|
35
|
+
)
|
|
36
|
+
from .models.relayrecord import (
|
|
37
|
+
StatusRelayPosition as RelayRecordStatus,
|
|
38
|
+
RelayRecord,
|
|
39
|
+
)
|
|
40
|
+
from .models.result import StatusResult, Result
|
|
41
|
+
from .models.session import Session
|
|
42
|
+
from .models.split import Split
|
|
43
|
+
from .models.stroke import Stroke
|
|
44
|
+
from .models.swimstyle import Technique, SwimStyle
|
|
45
|
+
from .models.swimtime import SwimTimeAttr, SwimTime
|
|
46
|
+
from .models.timestandard import TimeStandard
|
|
47
|
+
from .models.timestandardlist import TypeTimeStandardList, TimeStandardList
|
|
48
|
+
from .models.timestandardref import TimeStandardRef
|
|
49
|
+
from .models.timing import Timing
|
|
50
|
+
from .models_st.athelete import Athlete as AthleteST
|
|
51
|
+
from .models_st.entry import Status as StatusST, Entry as EntryST
|
|
52
|
+
from .models_st.heat import Final as FinalST, StatusHeat as StatusHeatST, Heat as HeatST
|
|
53
|
+
from .models_st.result import StatusResult as StatusResultST, Result as ResultST
|
|
54
|
+
|
|
55
|
+
__all__ = [
|
|
56
|
+
"fromfile",
|
|
57
|
+
"tofile",
|
|
58
|
+
"TypeAgeDate",
|
|
59
|
+
"AgeDate",
|
|
60
|
+
"Calculate",
|
|
61
|
+
"AgeGroup",
|
|
62
|
+
"Athlete",
|
|
63
|
+
"LenexBaseXmlModel",
|
|
64
|
+
"TypeClub",
|
|
65
|
+
"Club",
|
|
66
|
+
"Constructor",
|
|
67
|
+
"Contact",
|
|
68
|
+
"Course",
|
|
69
|
+
"Currency",
|
|
70
|
+
"Status",
|
|
71
|
+
"Entry",
|
|
72
|
+
"Round",
|
|
73
|
+
"TypeEvent",
|
|
74
|
+
"Event",
|
|
75
|
+
"TypeFee",
|
|
76
|
+
"Fee",
|
|
77
|
+
"Gender",
|
|
78
|
+
"HandicapClass",
|
|
79
|
+
"Handicap",
|
|
80
|
+
"Final",
|
|
81
|
+
"StatusHeat",
|
|
82
|
+
"Heat",
|
|
83
|
+
"JudgeRole",
|
|
84
|
+
"Judge",
|
|
85
|
+
"Lenex",
|
|
86
|
+
"EntryType",
|
|
87
|
+
"Meet",
|
|
88
|
+
"MeetInfoEntry",
|
|
89
|
+
"MeetInfoRecordRole",
|
|
90
|
+
"MeetInfoRecord",
|
|
91
|
+
"Nation",
|
|
92
|
+
"Official",
|
|
93
|
+
"PointTable",
|
|
94
|
+
"TypePool",
|
|
95
|
+
"Pool",
|
|
96
|
+
"Conversion",
|
|
97
|
+
"Qualify",
|
|
98
|
+
"Ranking",
|
|
99
|
+
"ReactionTime",
|
|
100
|
+
"Record",
|
|
101
|
+
"RecordList",
|
|
102
|
+
"RelayMeet",
|
|
103
|
+
"RelayPositionStatus",
|
|
104
|
+
"RelayPosition",
|
|
105
|
+
"RelayRecordStatus",
|
|
106
|
+
"RelayRecord",
|
|
107
|
+
"StatusResult",
|
|
108
|
+
"Result",
|
|
109
|
+
"Session",
|
|
110
|
+
"Split",
|
|
111
|
+
"Stroke",
|
|
112
|
+
"Technique",
|
|
113
|
+
"SwimStyle",
|
|
114
|
+
"SwimTimeAttr",
|
|
115
|
+
"SwimTime",
|
|
116
|
+
"TimeStandard",
|
|
117
|
+
"TypeTimeStandardList",
|
|
118
|
+
"TimeStandardList",
|
|
119
|
+
"TimeStandardRef",
|
|
120
|
+
"Timing",
|
|
121
|
+
"AthleteST",
|
|
122
|
+
"StatusST",
|
|
123
|
+
"EntryST",
|
|
124
|
+
"FinalST",
|
|
125
|
+
"StatusHeatST",
|
|
126
|
+
"HeatST",
|
|
127
|
+
"StatusResultST",
|
|
128
|
+
"ResultST",
|
|
129
|
+
]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from lenexpy.decoder.lef_decoder import decode_lef
|
|
5
|
+
from lenexpy.decoder.lxf_decoder import decode_lxf
|
|
6
|
+
from lenexpy.models.lenex import Lenex
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def decode(filename: str) -> Lenex:
|
|
10
|
+
suffix = Path(filename).suffix.lower()
|
|
11
|
+
if suffix in (".xml", ".lef"):
|
|
12
|
+
return decode_lef(filename)
|
|
13
|
+
if suffix == ".lxf":
|
|
14
|
+
return decode_lxf(filename)
|
|
15
|
+
raise TypeError("The file type must be .lxf, .lef, .xml")
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from lenexpy.models.lenex import Lenex
|
|
4
|
+
from .lef_encoder import encode_lef
|
|
5
|
+
from .lxf_encoder import encode_lxf
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def encode(lenex: Lenex, filename: str):
|
|
9
|
+
suffix = Path(filename).suffix.lower()
|
|
10
|
+
if suffix in (".xml", ".lef"):
|
|
11
|
+
return encode_lef(lenex, filename)
|
|
12
|
+
if suffix == ".lxf":
|
|
13
|
+
return encode_lxf(lenex, filename)
|
|
14
|
+
raise TypeError("The file type must be .lxf, .lef, .xml")
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from lenexpy.models.lenex import Lenex
|
|
2
|
+
|
|
3
|
+
ENCODING = 'utf-8'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def encode_lef_bytes(lenex: Lenex) -> bytes:
|
|
7
|
+
xml_value = lenex.to_xml(encoding=ENCODING, xml_declaration=True)
|
|
8
|
+
if isinstance(xml_value, str):
|
|
9
|
+
return xml_value.encode(ENCODING)
|
|
10
|
+
return xml_value
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def encode_lef(lenex: Lenex, filename: str):
|
|
14
|
+
if not filename.endswith(('.lef', '.xml')):
|
|
15
|
+
raise TypeError('The file type must be .lef, .xml')
|
|
16
|
+
|
|
17
|
+
xml_string = encode_lef_bytes(lenex)
|
|
18
|
+
with open(filename, "wb") as f:
|
|
19
|
+
f.write(xml_string)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from io import BytesIO
|
|
2
|
+
from zipfile import ZipFile
|
|
3
|
+
|
|
4
|
+
from lenexpy.decoder.lef_decoder import decode_lef_bytes
|
|
5
|
+
from lenexpy.models.lenex import Lenex
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def decode_lxf(filename: str) -> Lenex:
|
|
9
|
+
with open(filename, "rb") as file:
|
|
10
|
+
return decode_lxf_bytes(file.read())
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def decode_lxf_bytes(data: bytes) -> Lenex:
|
|
14
|
+
with ZipFile(BytesIO(data)) as zp:
|
|
15
|
+
if not zp.filelist:
|
|
16
|
+
raise TypeError("Incorrect lenex file")
|
|
17
|
+
|
|
18
|
+
lef_members = [m for m in zp.filelist if m.filename.lower().endswith(".lef")]
|
|
19
|
+
member = lef_members[0] if lef_members else zp.filelist[0]
|
|
20
|
+
with zp.open(member) as file:
|
|
21
|
+
return decode_lef_bytes(file.read())
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from io import BytesIO
|
|
3
|
+
from zipfile import ZipFile, ZIP_DEFLATED
|
|
4
|
+
|
|
5
|
+
from lenexpy.models.lenex import Lenex
|
|
6
|
+
from .lef_encoder import encode_lef_bytes
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def encode_lxf(lenex: Lenex, filename: str):
|
|
10
|
+
if not filename.endswith('.lxf'):
|
|
11
|
+
raise TypeError('The file type must be .lxf')
|
|
12
|
+
|
|
13
|
+
lxf_bytes = encode_lxf_bytes(lenex, Path(filename).name)
|
|
14
|
+
with open(filename, "wb") as f:
|
|
15
|
+
f.write(lxf_bytes)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def encode_lxf_bytes(lenex: Lenex, filename: str = "lenex.lxf") -> bytes:
|
|
19
|
+
fn = Path(filename).name[:-4] + ".lef"
|
|
20
|
+
xml_bytes = encode_lef_bytes(lenex)
|
|
21
|
+
buffer = BytesIO()
|
|
22
|
+
with ZipFile(buffer, "w", compression=ZIP_DEFLATED) as zf:
|
|
23
|
+
zf.writestr(fn, xml_bytes)
|
|
24
|
+
return buffer.getvalue()
|
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from datetime import date
|
|
2
|
+
|
|
3
|
+
from lenexpy.strenum import StrEnum
|
|
4
|
+
from pydantic_xml import attr
|
|
5
|
+
|
|
6
|
+
from .base import LenexBaseXmlModel
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TypeAgeDate(StrEnum):
|
|
10
|
+
YEAR = "YEAR"
|
|
11
|
+
DATE = "DATE"
|
|
12
|
+
POR = "POR"
|
|
13
|
+
CAN_FNQ = "CAN.FNQ"
|
|
14
|
+
LUX = "LUX"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# TODO: confirm root tag for AgeDate.
|
|
18
|
+
class AgeDate(LenexBaseXmlModel, tag="AGEDATE"):
|
|
19
|
+
type: TypeAgeDate = attr(name="type")
|
|
20
|
+
value: date = attr(name="value")
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from lenexpy.strenum import StrEnum
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
from pydantic_xml import attr, wrapped, element
|
|
4
|
+
|
|
5
|
+
from .base import LenexBaseXmlModel
|
|
6
|
+
|
|
7
|
+
from .ranking import Ranking
|
|
8
|
+
from .gender import Gender
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Calculate(StrEnum):
|
|
12
|
+
TOTAL: str = 'TOTAL'
|
|
13
|
+
SINGLE: str = 'SINGLE'
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# TODO: confirm root tag for AgeGroup.
|
|
17
|
+
class AgeGroup(LenexBaseXmlModel, tag="AGEGROUP"):
|
|
18
|
+
id: Optional[int] = attr(name="agegroupid", default=None)
|
|
19
|
+
agemax: int = attr(name="agemax")
|
|
20
|
+
agemin: int = attr(name="agemin")
|
|
21
|
+
gender: Optional[Gender] = attr(name="gender", default=None)
|
|
22
|
+
calculate: Optional[Calculate] = attr(name="calculate", default=None)
|
|
23
|
+
handicap: Optional[int] = attr(name="handicap", default=None)
|
|
24
|
+
levelmax: Optional[int] = attr(name="levelmax", default=None)
|
|
25
|
+
levelmin: Optional[int] = attr(name="levelmin", default=None)
|
|
26
|
+
levels: Optional[str] = attr(name="levels", default=None)
|
|
27
|
+
name: Optional[str] = attr(name="name", default=None)
|
|
28
|
+
rankings: List[Ranking] = wrapped(
|
|
29
|
+
"RANKINGS",
|
|
30
|
+
element(tag="RANKING"),
|
|
31
|
+
default_factory=list,
|
|
32
|
+
)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from datetime import date
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
|
|
4
|
+
from pydantic import model_validator
|
|
5
|
+
from pydantic_xml import attr, element, wrapped
|
|
6
|
+
|
|
7
|
+
from lenexpy.strenum import StrEnum
|
|
8
|
+
|
|
9
|
+
from .base import LenexBaseXmlModel
|
|
10
|
+
from .entry import Entry
|
|
11
|
+
from .gender import Gender
|
|
12
|
+
from .handicap import Handicap
|
|
13
|
+
from .nation import Nation
|
|
14
|
+
from .result import Result
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class AthleteStatus(StrEnum):
|
|
18
|
+
EXHIBITION = "EXHIBITION"
|
|
19
|
+
FOREIGNER = "FOREIGNER"
|
|
20
|
+
ROOKIE = "ROOKIE"
|
|
21
|
+
STARTSUISSE = "SUI.STARTSUISSE"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# required_params = {'birthday', 'gender', 'firstname', 'lastname'}/
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# TODO: confirm root tag for Athlete.
|
|
28
|
+
class Athlete(LenexBaseXmlModel, tag="ATHLETE"):
|
|
29
|
+
athleteid: int = attr(name="athleteid")
|
|
30
|
+
birthdate: date = attr(name="birthdate")
|
|
31
|
+
entries: List[Entry] = wrapped(
|
|
32
|
+
"ENTRIES",
|
|
33
|
+
element(tag="ENTRY"),
|
|
34
|
+
default_factory=list,
|
|
35
|
+
)
|
|
36
|
+
firstname: str = attr(name="firstname")
|
|
37
|
+
firstname_en: Optional[str] = attr(name="firstname.en", default=None)
|
|
38
|
+
gender: Gender = attr(name="gender")
|
|
39
|
+
handicap: Optional[Handicap] = element(tag="HANDICAP", default=None)
|
|
40
|
+
lastname: str = attr(name="lastname")
|
|
41
|
+
lastname_en: Optional[str] = attr(name="lastname.en", default=None)
|
|
42
|
+
level: Optional[str] = attr(name="level", default=None)
|
|
43
|
+
license: Optional[str] = attr(name="license", default=None)
|
|
44
|
+
license_dbs: Optional[str] = attr(name="license_dbs", default=None)
|
|
45
|
+
license_dsv: Optional[str] = attr(name="license_dsv", default=None)
|
|
46
|
+
license_ipc: Optional[int] = attr(name="license_ipc", default=None)
|
|
47
|
+
nameprefix: Optional[str] = attr(name="nameprefix", default=None)
|
|
48
|
+
nation: Optional[Nation] = attr(name="nation", default=None)
|
|
49
|
+
passport: Optional[str] = attr(name="passport", default=None)
|
|
50
|
+
results: List[Result] = wrapped(
|
|
51
|
+
"RESULTS",
|
|
52
|
+
element(tag="RESULT"),
|
|
53
|
+
default_factory=list,
|
|
54
|
+
)
|
|
55
|
+
status: Optional[AthleteStatus] = attr(name="status", default=None)
|
|
56
|
+
swrid: Optional[int] = attr(name="swrid", default=None)
|
|
57
|
+
|
|
58
|
+
@model_validator(mode="before")
|
|
59
|
+
@classmethod
|
|
60
|
+
def _map_id(cls, data):
|
|
61
|
+
if isinstance(data, dict) and "id" in data and "athleteid" not in data:
|
|
62
|
+
data["athleteid"] = data.pop("id")
|
|
63
|
+
return data
|
lenexpy/models/bank.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from pydantic_xml import attr
|
|
4
|
+
|
|
5
|
+
from .base import LenexBaseXmlModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Bank(LenexBaseXmlModel, tag="BANK"):
|
|
9
|
+
accountholder: str = attr(name="accountholder")
|
|
10
|
+
bic: Optional[str] = attr(name="bic", default=None)
|
|
11
|
+
iban: str = attr(name="iban")
|
|
12
|
+
name: Optional[str] = attr(name="name", default=None)
|
|
13
|
+
note: Optional[str] = attr(name="note", default=None)
|
lenexpy/models/base.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from pydantic import ConfigDict, model_validator
|
|
4
|
+
from pydantic_xml import BaseXmlModel
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class LenexBaseXmlModel(BaseXmlModel):
|
|
8
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
9
|
+
|
|
10
|
+
@model_validator(mode="before")
|
|
11
|
+
@classmethod
|
|
12
|
+
def _normalize_empty_strings(cls, data: Any):
|
|
13
|
+
def normalize(value: Any):
|
|
14
|
+
if isinstance(value, str) and value == "":
|
|
15
|
+
return None
|
|
16
|
+
if isinstance(value, list):
|
|
17
|
+
return [normalize(item) for item in value]
|
|
18
|
+
if isinstance(value, dict):
|
|
19
|
+
return {key: normalize(val) for key, val in value.items()}
|
|
20
|
+
return value
|
|
21
|
+
|
|
22
|
+
if isinstance(data, (dict, list)):
|
|
23
|
+
return normalize(data)
|
|
24
|
+
return data
|
lenexpy/models/club.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from lenexpy.strenum import StrEnum
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
from pydantic_xml import attr, element, wrapped
|
|
4
|
+
|
|
5
|
+
from .base import LenexBaseXmlModel
|
|
6
|
+
|
|
7
|
+
from .nation import Nation
|
|
8
|
+
from .official import Official
|
|
9
|
+
from .relaymeet import RelayMeet
|
|
10
|
+
from .athelete import Athlete
|
|
11
|
+
from .contact import Contact
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TypeClub(StrEnum):
|
|
15
|
+
CLUB = "CLUB",
|
|
16
|
+
NATIONALTEAM = "NATIONALTEAM",
|
|
17
|
+
REGIONALTEAM = "REGIONALTEAM",
|
|
18
|
+
UNATTACHED = "UNATTACHED"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# TODO: confirm root tag for Club.
|
|
22
|
+
class Club(LenexBaseXmlModel, tag="CLUB"):
|
|
23
|
+
contact: Optional[Contact] = element(tag="CONTACT", default=None)
|
|
24
|
+
code: Optional[str] = attr(name="code", default=None)
|
|
25
|
+
athletes: List[Athlete] = wrapped(
|
|
26
|
+
"ATHLETES",
|
|
27
|
+
element(tag="ATHLETE"),
|
|
28
|
+
default_factory=list,
|
|
29
|
+
)
|
|
30
|
+
name: str = attr(name="name")
|
|
31
|
+
name_en: Optional[str] = attr(name="name.en", default=None)
|
|
32
|
+
nation: Optional[Nation] = attr(name="nation", default=None)
|
|
33
|
+
number: Optional[int] = attr(name="number", default=None)
|
|
34
|
+
officials: List[Official] = wrapped(
|
|
35
|
+
"OFFICIALS",
|
|
36
|
+
element(tag="OFFICIAL"),
|
|
37
|
+
default_factory=list,
|
|
38
|
+
)
|
|
39
|
+
region: Optional[str] = attr(name="region", default=None)
|
|
40
|
+
relays: List[RelayMeet] = wrapped(
|
|
41
|
+
"RELAYS",
|
|
42
|
+
element(tag="RELAY"),
|
|
43
|
+
default_factory=list,
|
|
44
|
+
)
|
|
45
|
+
shortname: Optional[str] = attr(name="shortname", default=None)
|
|
46
|
+
shortname_en: Optional[str] = attr(name="shortname.en", default=None)
|
|
47
|
+
swrid: Optional[str] = attr(name="swrid", default=None)
|
|
48
|
+
type: Optional[TypeClub] = attr(name="type", default=None)
|
lenexpy/models/common.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from lenexpy.strenum import StrEnum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class StartMethod(StrEnum):
|
|
5
|
+
ONE_START = "1"
|
|
6
|
+
TWO_STARTS = "2"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TouchpadMode(StrEnum):
|
|
10
|
+
ONESIDE = "ONESIDE"
|
|
11
|
+
BOTHSIDE = "BOTHSIDE"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class StatusMeet(StrEnum):
|
|
15
|
+
ENTRIES = "ENTRIES"
|
|
16
|
+
SEEDED = "SEEDED"
|
|
17
|
+
RUNNING = "RUNNING"
|
|
18
|
+
OFFICIAL = "OFFICIAL"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class StatusSession(StrEnum):
|
|
22
|
+
ENTRIES = "ENTRIES"
|
|
23
|
+
SEEDED = "SEEDED"
|
|
24
|
+
RUNNING = "RUNNING"
|
|
25
|
+
UNOFFICIAL = "UNOFFICIAL"
|
|
26
|
+
OFFICIAL = "OFFICIAL"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class StatusEvent(StrEnum):
|
|
30
|
+
ENTRIES = "ENTRIES"
|
|
31
|
+
SEEDED = "SEEDED"
|
|
32
|
+
RUNNING = "RUNNING"
|
|
33
|
+
UNOFFICIAL = "UNOFFICIAL"
|
|
34
|
+
OFFICIAL = "OFFICIAL"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from pydantic_xml import attr, element
|
|
4
|
+
|
|
5
|
+
from .base import LenexBaseXmlModel
|
|
6
|
+
from .contact import Contact
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# TODO: confirm root tag for Constructor.
|
|
10
|
+
class Constructor(LenexBaseXmlModel, tag="CONSTRUCTOR"):
|
|
11
|
+
contact: Contact = element(tag="CONTACT")
|
|
12
|
+
name: str = attr(name="name")
|
|
13
|
+
registration: Optional[str] = attr(name="registration", default=None)
|
|
14
|
+
version: str = attr(name="version")
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from pydantic_xml import attr
|
|
4
|
+
|
|
5
|
+
from .base import LenexBaseXmlModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# TODO: confirm root tag for Contact.
|
|
9
|
+
class Contact(LenexBaseXmlModel, tag="CONTACT"):
|
|
10
|
+
city: Optional[str] = attr(name="city", default=None)
|
|
11
|
+
country: Optional[str] = attr(name="country", default=None)
|
|
12
|
+
email: str = attr(name="email")
|
|
13
|
+
fax: Optional[str] = attr(name="fax", default=None)
|
|
14
|
+
internet: Optional[str] = attr(name="internet", default=None)
|
|
15
|
+
name: Optional[str] = attr(name="name", default=None)
|
|
16
|
+
mobile: Optional[str] = attr(name="mobile", default=None)
|
|
17
|
+
phone: Optional[str] = attr(name="phone", default=None)
|
|
18
|
+
state: Optional[str] = attr(name="state", default=None)
|
|
19
|
+
street: Optional[str] = attr(name="street", default=None)
|
|
20
|
+
street2: Optional[str] = attr(name="street2", default=None)
|
|
21
|
+
zip: Optional[str] = attr(name="zip", default=None)
|
lenexpy/models/course.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from lenexpy.strenum import StrEnum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Course(StrEnum):
|
|
5
|
+
LCM = "LCM",
|
|
6
|
+
SCM = "SCM",
|
|
7
|
+
SCY = "SCY",
|
|
8
|
+
SCM16 = "SCM16",
|
|
9
|
+
SCM20 = "SCM20",
|
|
10
|
+
SCM33 = "SCM33",
|
|
11
|
+
SCY20 = "SCY20",
|
|
12
|
+
SCY27 = "SCY27",
|
|
13
|
+
SCY33 = "SCY33",
|
|
14
|
+
SCY36 = "SCY36",
|
|
15
|
+
OPEN = "OPEN"
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from lenexpy.strenum import StrEnum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Currency(StrEnum):
|
|
5
|
+
AUD = "Australian dollar"
|
|
6
|
+
BRL = "Brazilian real"
|
|
7
|
+
CAD = "Canadian dollar"
|
|
8
|
+
CHF = "Swiss franc"
|
|
9
|
+
DKK = "Danish krone"
|
|
10
|
+
DZD = "Algerian dinar"
|
|
11
|
+
GBP = "British pound"
|
|
12
|
+
DR = "Indonesian rupiah"
|
|
13
|
+
EUR = "Euro"
|
|
14
|
+
HRK = "Croatian Kuna"
|
|
15
|
+
INR = "Indian rupee"
|
|
16
|
+
IQD = "Iraqi dinar"
|
|
17
|
+
IRR = "Iranian rial"
|
|
18
|
+
JPY = "Japanese yen"
|
|
19
|
+
KRW = "Korea won"
|
|
20
|
+
KWD = "Kuwaiti dinar"
|
|
21
|
+
MXP = "Mexican peso"
|
|
22
|
+
NGN = "Nigerian naira"
|
|
23
|
+
NOK = "Norwegian krone"
|
|
24
|
+
NZD = "New Zealand dollar"
|
|
25
|
+
PHP = "Philippine peso"
|
|
26
|
+
PKR = "Pakistan rupee"
|
|
27
|
+
PYG = "Paraguay guarani"
|
|
28
|
+
RUR = "Russian rouble"
|
|
29
|
+
SAR = "Saudi Arabian riyal"
|
|
30
|
+
SEK = "Swedish krona"
|
|
31
|
+
TND = "Tunisian dinar"
|
|
32
|
+
USD = "US dollar"
|
lenexpy/models/entry.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from datetime import time
|
|
2
|
+
from typing import List, Optional, Union
|
|
3
|
+
|
|
4
|
+
from lenexpy.strenum import StrEnum
|
|
5
|
+
from pydantic import field_validator
|
|
6
|
+
from pydantic_xml import attr, element, wrapped
|
|
7
|
+
|
|
8
|
+
from .base import LenexBaseXmlModel
|
|
9
|
+
|
|
10
|
+
from .course import Course
|
|
11
|
+
from .handicap import HandicapClass
|
|
12
|
+
from .meetinfoentry import MeetInfoEntry
|
|
13
|
+
from .relayposition import RelayPosition
|
|
14
|
+
from .swimtime import SwimTime
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Status(StrEnum):
|
|
18
|
+
EXH = "EXH"
|
|
19
|
+
RJC = "RJC"
|
|
20
|
+
SICK = "SICK"
|
|
21
|
+
WDR = "WDR"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# TODO: confirm root tag for Entry.
|
|
25
|
+
class Entry(LenexBaseXmlModel, tag="ENTRY"):
|
|
26
|
+
agegroupid: Optional[int] = attr(name="agegroupid", default=None)
|
|
27
|
+
entrycourse: Optional[Course] = attr(name="entrycourse", default=None)
|
|
28
|
+
entrydistance: Optional[int] = attr(name="entrydistance", default=None)
|
|
29
|
+
entrytime: Optional[SwimTime] = attr(name="entrytime", default=None)
|
|
30
|
+
eventid: int = attr(name="eventid")
|
|
31
|
+
handicap: Optional[HandicapClass] = attr(name="handicap", default=None)
|
|
32
|
+
heatid: Optional[int] = attr(name="heatid", default=None)
|
|
33
|
+
lane: Optional[int] = attr(name="lane", default=None)
|
|
34
|
+
meetinfo: Optional[MeetInfoEntry] = element(tag="MEETINFO", default=None)
|
|
35
|
+
relay_positions: List[RelayPosition] = wrapped(
|
|
36
|
+
"RELAYPOSITIONS",
|
|
37
|
+
element(tag="RELAYPOSITION"),
|
|
38
|
+
default_factory=list,
|
|
39
|
+
)
|
|
40
|
+
status: Optional[Status] = attr(name="status", default=None)
|
|
41
|
+
|
|
42
|
+
@field_validator("entrytime", mode="before")
|
|
43
|
+
@classmethod
|
|
44
|
+
def _parse_entrytime(cls, value):
|
|
45
|
+
if value is None or isinstance(value, SwimTime):
|
|
46
|
+
return value
|
|
47
|
+
if isinstance(value, (str, time)):
|
|
48
|
+
return SwimTime._parse(value)
|
|
49
|
+
return value
|