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.
Files changed (63) hide show
  1. lenexpy/__init__.py +129 -0
  2. lenexpy/decoder/__init__.py +4 -0
  3. lenexpy/decoder/decoder.py +15 -0
  4. lenexpy/decoder/encoder.py +14 -0
  5. lenexpy/decoder/lef_decoder.py +10 -0
  6. lenexpy/decoder/lef_encoder.py +19 -0
  7. lenexpy/decoder/lxf_decoder.py +21 -0
  8. lenexpy/decoder/lxf_encoder.py +24 -0
  9. lenexpy/models/__init__.py +0 -0
  10. lenexpy/models/agedate.py +20 -0
  11. lenexpy/models/agegroup.py +32 -0
  12. lenexpy/models/athelete.py +63 -0
  13. lenexpy/models/bank.py +13 -0
  14. lenexpy/models/base.py +24 -0
  15. lenexpy/models/club.py +48 -0
  16. lenexpy/models/common.py +34 -0
  17. lenexpy/models/constructor.py +14 -0
  18. lenexpy/models/contact.py +21 -0
  19. lenexpy/models/course.py +15 -0
  20. lenexpy/models/currency.py +32 -0
  21. lenexpy/models/entry.py +49 -0
  22. lenexpy/models/event.py +73 -0
  23. lenexpy/models/facility.py +16 -0
  24. lenexpy/models/fee.py +23 -0
  25. lenexpy/models/gender.py +7 -0
  26. lenexpy/models/handicap.py +34 -0
  27. lenexpy/models/heat.py +31 -0
  28. lenexpy/models/judge.py +39 -0
  29. lenexpy/models/lenex.py +31 -0
  30. lenexpy/models/meet.py +96 -0
  31. lenexpy/models/meetinfoentry.py +27 -0
  32. lenexpy/models/meetinforecord.py +33 -0
  33. lenexpy/models/nation.py +214 -0
  34. lenexpy/models/official.py +24 -0
  35. lenexpy/models/pointtable.py +12 -0
  36. lenexpy/models/pool.py +22 -0
  37. lenexpy/models/qualify.py +22 -0
  38. lenexpy/models/ranking.py +13 -0
  39. lenexpy/models/reactiontime.py +5 -0
  40. lenexpy/models/record.py +28 -0
  41. lenexpy/models/recordlist.py +30 -0
  42. lenexpy/models/relaymeet.py +33 -0
  43. lenexpy/models/relayposition.py +23 -0
  44. lenexpy/models/relayrecord.py +25 -0
  45. lenexpy/models/result.py +41 -0
  46. lenexpy/models/session.py +60 -0
  47. lenexpy/models/split.py +11 -0
  48. lenexpy/models/stroke.py +20 -0
  49. lenexpy/models/swimstyle.py +27 -0
  50. lenexpy/models/swimtime.py +73 -0
  51. lenexpy/models/timestandard.py +12 -0
  52. lenexpy/models/timestandardlist.py +35 -0
  53. lenexpy/models/timestandardref.py +14 -0
  54. lenexpy/models/timing.py +9 -0
  55. lenexpy/models_st/__init__.py +0 -0
  56. lenexpy/models_st/athelete.py +29 -0
  57. lenexpy/models_st/entry.py +26 -0
  58. lenexpy/models_st/heat.py +42 -0
  59. lenexpy/models_st/result.py +31 -0
  60. lenexpy/strenum.py +14 -0
  61. lenexpy-3.0.1.dist-info/METADATA +115 -0
  62. lenexpy-3.0.1.dist-info/RECORD +63 -0
  63. 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,4 @@
1
+ from .decoder import decode as load
2
+ from .encoder import encode as save
3
+ from .lef_decoder import decode_lef_bytes
4
+ from .lxf_decoder import decode_lxf_bytes
@@ -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,10 @@
1
+ from lenexpy.models.lenex import Lenex
2
+
3
+
4
+ def decode_lef(filename: str) -> Lenex:
5
+ with open(filename, "rb") as file:
6
+ return decode_lef_bytes(file.read())
7
+
8
+
9
+ def decode_lef_bytes(data: bytes) -> Lenex:
10
+ return Lenex.from_xml(data)
@@ -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)
@@ -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)
@@ -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"
@@ -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