hytek-parser 2.0.0__tar.gz → 2.2.0__tar.gz
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.
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/PKG-INFO +1 -1
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/hytek_parser/__init__.py +3 -1
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/hytek_parser/hy3/_utils.py +13 -1
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/hytek_parser/hy3/line_parsers/c_team_parsers.py +22 -5
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/hytek_parser/hy3/line_parsers/d_swimmer_parsers.py +9 -0
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/hytek_parser/hy3/line_parsers/e_event_parsers.py +23 -2
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/hytek_parser/hy3/line_parsers/f_relay_parsers.py +18 -2
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/hytek_parser/hy3/schemas.py +67 -3
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/pyproject.toml +1 -1
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/LICENSE.md +0 -0
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/README.md +0 -0
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/hytek_parser/_utils.py +0 -0
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/hytek_parser/export_xls/__init__.py +0 -0
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/hytek_parser/export_xls/_utils.py +0 -0
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/hytek_parser/export_xls/schemas.py +0 -0
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/hytek_parser/hy3/__init__.py +0 -0
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/hytek_parser/hy3/enums.py +0 -0
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/hytek_parser/hy3/line_parsers/__init__.py +0 -0
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/hytek_parser/hy3/line_parsers/a_file_parsers.py +0 -0
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/hytek_parser/hy3/line_parsers/b_meet_parsers.py +0 -0
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/hytek_parser/hy3/line_parsers/g_split_parsers.py +0 -0
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/hytek_parser/hy3/line_parsers/h_dq_parsers.py +0 -0
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/hytek_parser/hy3_parser.py +0 -0
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/hytek_parser/hyv/__init__.py +0 -0
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/hytek_parser/hyv/enums.py +0 -0
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/hytek_parser/hyv/schemas.py +0 -0
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/hytek_parser/py.typed +0 -0
- {hytek_parser-2.0.0 → hytek_parser-2.2.0}/hytek_parser/types.py +0 -0
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
"""A Hytek hy3 file parser."""
|
|
2
|
+
from importlib.metadata import version
|
|
3
|
+
|
|
2
4
|
from .export_xls import ExportXlsParseError, parse_event_export_xls
|
|
3
5
|
from .hy3 import HY3_LINE_PARSERS
|
|
4
6
|
from .hy3_parser import parse_hy3
|
|
@@ -15,4 +17,4 @@ __all__ = [
|
|
|
15
17
|
]
|
|
16
18
|
__author__ = "Nino Maruszewski"
|
|
17
19
|
__license__ = "MIT"
|
|
18
|
-
__version__ =
|
|
20
|
+
__version__ = version(__package__)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Union
|
|
1
|
+
from typing import Optional, Union
|
|
2
2
|
|
|
3
3
|
from hytek_parser._utils import safe_cast, select_from_enum
|
|
4
4
|
from hytek_parser.hy3.enums import ReplacedTimeTimeCode
|
|
@@ -18,3 +18,15 @@ def parse_time(raw_time: str) -> Union[float, ReplacedTimeTimeCode]:
|
|
|
18
18
|
return casted
|
|
19
19
|
else:
|
|
20
20
|
return select_from_enum(ReplacedTimeTimeCode, raw_time)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def parse_time_or_none(raw_time: str) -> Optional[float]:
|
|
24
|
+
"""Parse a timing field where 0.00/blank means 'not recorded'.
|
|
25
|
+
|
|
26
|
+
Unlike parse_time (which returns 0.0 for "0.00" and a ReplacedTimeTimeCode
|
|
27
|
+
for blank/non-numeric input), this returns None unless the value is a
|
|
28
|
+
positive float. Used for pad and backup-button times, where an unused
|
|
29
|
+
slot is written as 0.00 and should surface as None rather than 0.0.
|
|
30
|
+
"""
|
|
31
|
+
val = parse_time(raw_time)
|
|
32
|
+
return val if isinstance(val, float) and val > 0.0 else None
|
|
@@ -26,8 +26,24 @@ def c1_parser(
|
|
|
26
26
|
# Then truncate to 5 chars
|
|
27
27
|
team_code = "".join(TEAM_CODE_REGEX.findall(team_name.upper()))[:5]
|
|
28
28
|
|
|
29
|
+
# LSC code at C1 cols 54-55 (2 chars, e.g. 'NE', 'PC', 'CA');
|
|
30
|
+
# populates the previously-unset Team.region field.
|
|
31
|
+
# Bug fix: was extract(54, 3), which grabbed the first letter of the contact
|
|
32
|
+
# name at col 56, corrupting ~19% of teams' LSC codes.
|
|
33
|
+
region = extract(line, 54, 2) or None
|
|
34
|
+
|
|
35
|
+
# C1 contact name slots: cols 56-85 (contact_name_1) and 86-115 (contact_name_2).
|
|
36
|
+
# Often identical; blank slot → None.
|
|
37
|
+
contact_name_1 = extract(line, 56, 30) or None
|
|
38
|
+
contact_name_2 = extract(line, 86, 30) or None
|
|
39
|
+
|
|
29
40
|
file.meet.get_or_create_team(
|
|
30
|
-
name=team_name,
|
|
41
|
+
name=team_name,
|
|
42
|
+
short_name=team_short_name,
|
|
43
|
+
code=team_code,
|
|
44
|
+
region=region,
|
|
45
|
+
contact_name_1=contact_name_1,
|
|
46
|
+
contact_name_2=contact_name_2,
|
|
31
47
|
)
|
|
32
48
|
return file
|
|
33
49
|
|
|
@@ -39,7 +55,11 @@ def c2_parser(
|
|
|
39
55
|
# Get the last team
|
|
40
56
|
team_code, team = file.meet.last_team
|
|
41
57
|
|
|
42
|
-
|
|
58
|
+
# C2 has two 30-char address lines: line 1 (cols 3-32) is a free-text
|
|
59
|
+
# "c/o"/attention line (often a contact person, sometimes the org), line 2
|
|
60
|
+
# (cols 33-62) is the street. They were previously read as one 60-char blob.
|
|
61
|
+
team.address_1 = extract(line, 3, 30)
|
|
62
|
+
team.address_2 = extract(line, 33, 30)
|
|
43
63
|
team.city = extract(line, 63, 30)
|
|
44
64
|
team.state = extract(line, 93, 2)
|
|
45
65
|
team.zip_code = extract(line, 95, 10)
|
|
@@ -49,9 +69,6 @@ def c2_parser(
|
|
|
49
69
|
else:
|
|
50
70
|
team.country = opts["default_country"]
|
|
51
71
|
|
|
52
|
-
# TODO: Team region
|
|
53
|
-
team.region = "Unknown"
|
|
54
|
-
|
|
55
72
|
file.meet.last_team = (team_code, team)
|
|
56
73
|
return file
|
|
57
74
|
|
{hytek_parser-2.0.0 → hytek_parser-2.2.0}/hytek_parser/hy3/line_parsers/d_swimmer_parsers.py
RENAMED
|
@@ -24,6 +24,15 @@ def d1_parser(
|
|
|
24
24
|
swimmer.date_of_birth = date_or_none(extract(line, 89, 8))
|
|
25
25
|
swimmer.age = int(extract(line, 97, 3))
|
|
26
26
|
|
|
27
|
+
# previously dropped D1 fields. Capture both per spec.
|
|
28
|
+
# citizenship: cols 113-115 (3 chars). Observed: 'USA' or blank.
|
|
29
|
+
# status: col 125 (1 char). Athlete status; observed 'N' (Normal) or blank.
|
|
30
|
+
swimmer.citizenship = extract(line, 113, 3) or None
|
|
31
|
+
swimmer.status = extract(line, 125, 1) or None
|
|
32
|
+
# class_year: cols 100-101 (2 chars). "Fr"/"So"/"Jr"/"Sr" school
|
|
33
|
+
# class in HS-meet exports; other data / blank in club exports.
|
|
34
|
+
swimmer.class_year = extract(line, 100, 2) or None
|
|
35
|
+
|
|
27
36
|
swimmer.team_code = team_code
|
|
28
37
|
|
|
29
38
|
file.meet.add_swimmer(swimmer)
|
|
@@ -2,7 +2,7 @@ from datetime import datetime
|
|
|
2
2
|
from typing import Any
|
|
3
3
|
|
|
4
4
|
from hytek_parser._utils import extract, get_age_group, safe_cast, select_from_enum
|
|
5
|
-
from hytek_parser.hy3._utils import parse_time
|
|
5
|
+
from hytek_parser.hy3._utils import parse_time, parse_time_or_none
|
|
6
6
|
from hytek_parser.hy3.enums import (
|
|
7
7
|
Course,
|
|
8
8
|
DisqualificationCode,
|
|
@@ -63,6 +63,10 @@ def e1_parser(
|
|
|
63
63
|
entry_converted_seed_time = parse_time(extract(line, 43, 8))
|
|
64
64
|
entry_converted_seed_time_course = event.course
|
|
65
65
|
|
|
66
|
+
# Meet Division: col 77-79 in most MM versions, col 92-93 in MM4/MM5-7.0Fa.
|
|
67
|
+
# The two are mutually exclusive; col 77 takes precedence.
|
|
68
|
+
meet_division = extract(line, 77, 3) or extract(line, 92, 2) or None
|
|
69
|
+
|
|
66
70
|
event.get_or_create_entry(
|
|
67
71
|
swimmers=entry_swimmers,
|
|
68
72
|
relay=False,
|
|
@@ -71,6 +75,7 @@ def e1_parser(
|
|
|
71
75
|
seed_course=entry_seed_course,
|
|
72
76
|
converted_seed_time=entry_converted_seed_time,
|
|
73
77
|
converted_seed_time_course=entry_converted_seed_time_course,
|
|
78
|
+
meet_division=meet_division,
|
|
74
79
|
)
|
|
75
80
|
|
|
76
81
|
# Update event
|
|
@@ -100,7 +105,17 @@ def e2_parser(
|
|
|
100
105
|
heat_place = safe_cast(int, extract(line, 27, 3))
|
|
101
106
|
overall_place = safe_cast(int, extract(line, 30, 4))
|
|
102
107
|
|
|
103
|
-
#
|
|
108
|
+
# previously-dropped E2 timing fields.
|
|
109
|
+
# See spec for column-offset derivation and failure-mode evidence
|
|
110
|
+
# (touchpad turn-touch misattribution in short SCY sprints).
|
|
111
|
+
# parse_time_or_none returns None for "0.00" and blank/non-numeric fields.
|
|
112
|
+
pad_time = parse_time_or_none(extract(line, 63, 12))
|
|
113
|
+
button_1_time = parse_time_or_none(extract(line, 39, 8))
|
|
114
|
+
button_2_time = parse_time_or_none(extract(line, 47, 8))
|
|
115
|
+
button_3_time = parse_time_or_none(extract(line, 55, 8))
|
|
116
|
+
backup_4_time = parse_time_or_none(extract(line, 75, 8))
|
|
117
|
+
alt_time_code = extract(line, 96, 1) or None # observed: 'A' / 'K' / blank
|
|
118
|
+
|
|
104
119
|
raw_date = extract(line, 88, 8).strip()
|
|
105
120
|
date_ = datetime.strptime(raw_date, "%m%d%Y").date() if raw_date else None
|
|
106
121
|
|
|
@@ -128,6 +143,12 @@ def e2_parser(
|
|
|
128
143
|
setattr(entry, f"{prefix}_heat_place", heat_place)
|
|
129
144
|
setattr(entry, f"{prefix}_overall_place", overall_place)
|
|
130
145
|
setattr(entry, f"{prefix}_date", date_)
|
|
146
|
+
setattr(entry, f"{prefix}_pad_time", pad_time)
|
|
147
|
+
setattr(entry, f"{prefix}_button_1_time", button_1_time)
|
|
148
|
+
setattr(entry, f"{prefix}_button_2_time", button_2_time)
|
|
149
|
+
setattr(entry, f"{prefix}_button_3_time", button_3_time)
|
|
150
|
+
setattr(entry, f"{prefix}_backup_4_time", backup_4_time)
|
|
151
|
+
setattr(entry, f"{prefix}_alt_time_code", alt_time_code)
|
|
131
152
|
|
|
132
153
|
event.last_entry = entry
|
|
133
154
|
file.meet.last_event = (event_num, event)
|
|
@@ -2,7 +2,7 @@ from datetime import datetime
|
|
|
2
2
|
from typing import Any
|
|
3
3
|
|
|
4
4
|
from hytek_parser._utils import extract, get_age_group, safe_cast, select_from_enum
|
|
5
|
-
from hytek_parser.hy3._utils import parse_time
|
|
5
|
+
from hytek_parser.hy3._utils import parse_time, parse_time_or_none
|
|
6
6
|
from hytek_parser.hy3.enums import (
|
|
7
7
|
Course,
|
|
8
8
|
DisqualificationCode,
|
|
@@ -107,7 +107,17 @@ def f2_parser(
|
|
|
107
107
|
heat_place = safe_cast(int, extract(line, 27, 3))
|
|
108
108
|
overall_place = safe_cast(int, extract(line, 30, 4))
|
|
109
109
|
|
|
110
|
-
#
|
|
110
|
+
# previously-dropped F2 timing fields. The five timing-column
|
|
111
|
+
# offsets are IDENTICAL to e2_parser; only alt_time_code differs because F2
|
|
112
|
+
# has a 15-column gap before its date field (date at col 103, not 88).
|
|
113
|
+
# F2 alt_time_code lives at col 111, not col 96.
|
|
114
|
+
pad_time = parse_time_or_none(extract(line, 63, 12))
|
|
115
|
+
button_1_time = parse_time_or_none(extract(line, 39, 8))
|
|
116
|
+
button_2_time = parse_time_or_none(extract(line, 47, 8))
|
|
117
|
+
button_3_time = parse_time_or_none(extract(line, 55, 8))
|
|
118
|
+
backup_4_time = parse_time_or_none(extract(line, 75, 8))
|
|
119
|
+
alt_time_code = extract(line, 111, 1) or None # F2 offset; observed: 'A'/'K'/blank
|
|
120
|
+
|
|
111
121
|
raw_date = extract(line, 103, 8).strip()
|
|
112
122
|
date_ = datetime.strptime(raw_date, "%m%d%Y").date() if raw_date else None
|
|
113
123
|
|
|
@@ -135,6 +145,12 @@ def f2_parser(
|
|
|
135
145
|
setattr(entry, f"{prefix}_heat_place", heat_place)
|
|
136
146
|
setattr(entry, f"{prefix}_overall_place", overall_place)
|
|
137
147
|
setattr(entry, f"{prefix}_date", date_)
|
|
148
|
+
setattr(entry, f"{prefix}_pad_time", pad_time)
|
|
149
|
+
setattr(entry, f"{prefix}_button_1_time", button_1_time)
|
|
150
|
+
setattr(entry, f"{prefix}_button_2_time", button_2_time)
|
|
151
|
+
setattr(entry, f"{prefix}_button_3_time", button_3_time)
|
|
152
|
+
setattr(entry, f"{prefix}_backup_4_time", backup_4_time)
|
|
153
|
+
setattr(entry, f"{prefix}_alt_time_code", alt_time_code)
|
|
138
154
|
|
|
139
155
|
event.last_entry = entry
|
|
140
156
|
file.meet.last_event = (event_num, event)
|
|
@@ -36,6 +36,17 @@ class Swimmer:
|
|
|
36
36
|
usa_swimming_id: str
|
|
37
37
|
team_code: str # Team 5-letter code
|
|
38
38
|
|
|
39
|
+
# D1 fields previously dropped by the parser
|
|
40
|
+
citizenship: Optional[str] = None
|
|
41
|
+
# col 125; athlete status (Hytek MM "Status" field). Raw 1-char code — only
|
|
42
|
+
# "N" (Normal) or blank observed across ~9.25M records; other GUI statuses
|
|
43
|
+
# (Impaired/Exhibition/Foreigner/Rookie) never seen in data, so left as a
|
|
44
|
+
# raw code rather than an enum.
|
|
45
|
+
status: Optional[str] = None
|
|
46
|
+
# cols 100-101; "Fr"/"So"/"Jr"/"Sr" school class in HS-meet exports,
|
|
47
|
+
# other data / blank in club exports.
|
|
48
|
+
class_year: Optional[str] = None
|
|
49
|
+
|
|
39
50
|
|
|
40
51
|
@define
|
|
41
52
|
class Team:
|
|
@@ -53,13 +64,15 @@ class Team:
|
|
|
53
64
|
state: str
|
|
54
65
|
zip_code: str
|
|
55
66
|
country: str
|
|
56
|
-
region: str
|
|
67
|
+
region: Optional[str]
|
|
57
68
|
|
|
58
69
|
# Contact info
|
|
59
70
|
daytime_phone: str
|
|
60
71
|
evening_phone: str
|
|
61
72
|
fax: str
|
|
62
73
|
email: str
|
|
74
|
+
contact_name_1: Optional[str] = None
|
|
75
|
+
contact_name_2: Optional[str] = None
|
|
63
76
|
|
|
64
77
|
# Swimmers
|
|
65
78
|
swimmers: dict[int, Swimmer] = Factory(dict)
|
|
@@ -99,6 +112,14 @@ class EventEntry:
|
|
|
99
112
|
prelim_heat_place: Optional[int] = None
|
|
100
113
|
prelim_overall_place: Optional[int] = None
|
|
101
114
|
prelim_date: Optional[date] = None
|
|
115
|
+
# E2 fields previously dropped (pad + 3 buttons + backup_4 + alt code)
|
|
116
|
+
prelim_pad_time: Optional[float] = None
|
|
117
|
+
prelim_button_1_time: Optional[float] = None
|
|
118
|
+
prelim_button_2_time: Optional[float] = None
|
|
119
|
+
prelim_button_3_time: Optional[float] = None
|
|
120
|
+
prelim_backup_4_time: Optional[float] = None
|
|
121
|
+
# col 96; semantics unverified — observed 'A'/'K'/blank
|
|
122
|
+
prelim_alt_time_code: Optional[str] = None
|
|
102
123
|
|
|
103
124
|
# Swimoff Time info
|
|
104
125
|
swimoff_time: Optional[Union[float, ReplacedTimeTimeCode]] = None
|
|
@@ -111,6 +132,14 @@ class EventEntry:
|
|
|
111
132
|
swimoff_heat_place: Optional[int] = None
|
|
112
133
|
swimoff_overall_place: Optional[int] = None
|
|
113
134
|
swimoff_date: Optional[date] = None
|
|
135
|
+
# E2 fields previously dropped (pad + 3 buttons + backup_4 + alt code)
|
|
136
|
+
swimoff_pad_time: Optional[float] = None
|
|
137
|
+
swimoff_button_1_time: Optional[float] = None
|
|
138
|
+
swimoff_button_2_time: Optional[float] = None
|
|
139
|
+
swimoff_button_3_time: Optional[float] = None
|
|
140
|
+
swimoff_backup_4_time: Optional[float] = None
|
|
141
|
+
# col 96; semantics unverified — observed 'A'/'K'/blank
|
|
142
|
+
swimoff_alt_time_code: Optional[str] = None
|
|
114
143
|
|
|
115
144
|
# Finals time info
|
|
116
145
|
finals_time: Optional[Union[float, ReplacedTimeTimeCode]] = None
|
|
@@ -123,11 +152,26 @@ class EventEntry:
|
|
|
123
152
|
finals_heat_place: Optional[int] = None
|
|
124
153
|
finals_overall_place: Optional[int] = None
|
|
125
154
|
finals_date: Optional[date] = None
|
|
155
|
+
# E2 fields previously dropped (pad + 3 buttons + backup_4 + alt code)
|
|
156
|
+
finals_pad_time: Optional[float] = None
|
|
157
|
+
finals_button_1_time: Optional[float] = None
|
|
158
|
+
finals_button_2_time: Optional[float] = None
|
|
159
|
+
finals_button_3_time: Optional[float] = None
|
|
160
|
+
finals_backup_4_time: Optional[float] = None
|
|
161
|
+
# col 96; semantics unverified — observed 'A'/'K'/blank
|
|
162
|
+
finals_alt_time_code: Optional[str] = None
|
|
126
163
|
|
|
127
164
|
# Relay attribution (moved from Event — set by f1_parser)
|
|
128
165
|
relay_team_id: Optional[str] = None
|
|
129
166
|
relay_swim_team_code: Optional[str] = None
|
|
130
167
|
|
|
168
|
+
# Meet Division. The host-configured division label for the
|
|
169
|
+
# entry: 'JV'/'VR' (varsity-style), classification codes ('A'/'AA'/'AAA'/'BB'),
|
|
170
|
+
# HS enrollment classes ('5A'/'4A'/'3A'), age-group/level codes ('AG'/'SR'),
|
|
171
|
+
# or numeric ('0'/'1'/'2'/'3'). Stored at cols 77-79 in most MM versions,
|
|
172
|
+
# cols 92-93 in MM4/MM5-7.0Fa (read with col-77 precedence).
|
|
173
|
+
meet_division: Optional[str] = None
|
|
174
|
+
|
|
131
175
|
def __init__(
|
|
132
176
|
self,
|
|
133
177
|
swimmers: dict[int, Swimmer],
|
|
@@ -139,6 +183,7 @@ class EventEntry:
|
|
|
139
183
|
converted_seed_time_course: Course,
|
|
140
184
|
relay_team_id: Optional[str] = None,
|
|
141
185
|
relay_swim_team_code: Optional[str] = None,
|
|
186
|
+
meet_division: Optional[str] = None,
|
|
142
187
|
) -> None:
|
|
143
188
|
self.swimmers = swimmers
|
|
144
189
|
self.relay = relay
|
|
@@ -149,6 +194,7 @@ class EventEntry:
|
|
|
149
194
|
self.converted_seed_time_course = converted_seed_time_course
|
|
150
195
|
self.relay_team_id = relay_team_id
|
|
151
196
|
self.relay_swim_team_code = relay_swim_team_code
|
|
197
|
+
self.meet_division = meet_division
|
|
152
198
|
|
|
153
199
|
for course in ("prelim", "swimoff", "finals"):
|
|
154
200
|
setattr(self, f"{course}_time", None)
|
|
@@ -161,6 +207,12 @@ class EventEntry:
|
|
|
161
207
|
setattr(self, f"{course}_heat_place", None)
|
|
162
208
|
setattr(self, f"{course}_overall_place", None)
|
|
163
209
|
setattr(self, f"{course}_date", None)
|
|
210
|
+
setattr(self, f"{course}_pad_time", None)
|
|
211
|
+
setattr(self, f"{course}_button_1_time", None)
|
|
212
|
+
setattr(self, f"{course}_button_2_time", None)
|
|
213
|
+
setattr(self, f"{course}_button_3_time", None)
|
|
214
|
+
setattr(self, f"{course}_backup_4_time", None)
|
|
215
|
+
setattr(self, f"{course}_alt_time_code", None)
|
|
164
216
|
|
|
165
217
|
self.prelim_dq_info = None
|
|
166
218
|
self.swimoff_dq_info = None
|
|
@@ -230,6 +282,7 @@ class Event:
|
|
|
230
282
|
converted_seed_time_course: Course,
|
|
231
283
|
relay_team_id: Optional[str] = None,
|
|
232
284
|
relay_swim_team_code: Optional[str] = None,
|
|
285
|
+
meet_division: Optional[str] = None,
|
|
233
286
|
) -> EventEntry:
|
|
234
287
|
"""Get an event entry or create one if needed."""
|
|
235
288
|
entry = EventEntry(
|
|
@@ -242,6 +295,7 @@ class Event:
|
|
|
242
295
|
converted_seed_time_course=converted_seed_time_course,
|
|
243
296
|
relay_team_id=relay_team_id,
|
|
244
297
|
relay_swim_team_code=relay_swim_team_code,
|
|
298
|
+
meet_division=meet_division,
|
|
245
299
|
)
|
|
246
300
|
if self.entries and self.entries[-1].same_swimmer_entry_as(entry):
|
|
247
301
|
# P/F entries always listed together: a swimmer (individuals) or a
|
|
@@ -306,7 +360,15 @@ class Meet:
|
|
|
306
360
|
self.swimmers[swimmer.meet_id] = swimmer
|
|
307
361
|
self.teams[swimmer.team_code].swimmers[swimmer.meet_id] = swimmer
|
|
308
362
|
|
|
309
|
-
def get_or_create_team(
|
|
363
|
+
def get_or_create_team(
|
|
364
|
+
self,
|
|
365
|
+
name: str,
|
|
366
|
+
short_name: str,
|
|
367
|
+
code: str,
|
|
368
|
+
region: Optional[str] = None,
|
|
369
|
+
contact_name_1: Optional[str] = None,
|
|
370
|
+
contact_name_2: Optional[str] = None,
|
|
371
|
+
) -> Team:
|
|
310
372
|
"""Get a team or create if needed."""
|
|
311
373
|
if team := self.teams.get(code):
|
|
312
374
|
return team
|
|
@@ -319,13 +381,15 @@ class Meet:
|
|
|
319
381
|
address_2="N/A",
|
|
320
382
|
city="N/A",
|
|
321
383
|
country="N/A",
|
|
322
|
-
region=
|
|
384
|
+
region=region,
|
|
323
385
|
state="N/A",
|
|
324
386
|
zip_code="N/A",
|
|
325
387
|
daytime_phone="N/A",
|
|
326
388
|
evening_phone="N/A",
|
|
327
389
|
fax="N/A",
|
|
328
390
|
email="N/A",
|
|
391
|
+
contact_name_1=contact_name_1,
|
|
392
|
+
contact_name_2=contact_name_2,
|
|
329
393
|
)
|
|
330
394
|
|
|
331
395
|
# Add team, this also updates the teams dict
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|