Skip to content

Commit cac72d6

Browse files
committed
Merge remote-tracking branch 'origin/bug/8343-await-all-methods-decorated-with-actions' into bug/8343-await-all-methods-decorated-with-actions
2 parents 71731f5 + 42a7c25 commit cac72d6

8 files changed

Lines changed: 156 additions & 26 deletions

File tree

‎OTAnalytics/application/config.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
DEFAULT_LOG_DIR = Path("logs").absolute()
77
"""The log save directory."""
88

9-
ALLOWED_TRACK_SIZE_PARSING = 5
9+
ALLOWED_TRACK_SIZE_PARSING = 0
1010
TRACK_LENGTH_LIMIT = 12000
1111
GEOMETRY_CACHE_SIZE: int = 20000
1212
CUTTING_SECTION_MARKER: str = "#cut"

‎OTAnalytics/application/export_formats/event_list.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
DIRECTION_VECTOR_Y = "vector_px_y"
1919
VIDEO_NAME = event.VIDEO_NAME
2020
INTERPOLATED_OCCURRENCE = event.INTERPOLATED_OCCURRENCE
21+
INTERPOLATED_EVENT_COORDINATE = event.INTERPOLATED_EVENT_COORDINATE
2122
INTERPOLATED_EVENT_COORDINATE_X = "interpolated_coordinate_px_x"
2223
INTERPOLATED_EVENT_COORDINATE_Y = "interpolated_coordinate_px_y"
2324

‎OTAnalytics/plugin_prototypes/eventlist_exporter/eventlist_exporter.py‎

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,27 @@
55
from OTAnalytics.application.config import DEFAULT_EVENTLIST_FILE_TYPE
66
from OTAnalytics.application.datastore import EventListParser
77
from OTAnalytics.application.export_formats import event_list
8+
from OTAnalytics.application.export_formats.event_list import (
9+
DATE_TIME_FORMAT,
10+
DIRECTION_VECTOR_X,
11+
DIRECTION_VECTOR_Y,
12+
EVENT_COORDINATE_X,
13+
EVENT_COORDINATE_Y,
14+
EVENT_TYPE,
15+
FRAME_NUMBER,
16+
HOSTNAME,
17+
INTERPOLATED_EVENT_COORDINATE_X,
18+
INTERPOLATED_EVENT_COORDINATE_Y,
19+
INTERPOLATED_OCCURRENCE,
20+
OCCURRENCE,
21+
OCCURRENCE_DATE,
22+
OCCURRENCE_TIME,
23+
ROAD_USER_ID,
24+
ROAD_USER_TYPE,
25+
SECTION_ID,
26+
SECTION_NAME,
27+
VIDEO_NAME,
28+
)
829
from OTAnalytics.application.export_formats.export_mode import INITIAL_MERGE
930
from OTAnalytics.application.logger import logger
1031
from OTAnalytics.application.use_cases.export_events import (
@@ -24,6 +45,44 @@
2445
OTC_CSV_FORMAT_NAME = "CSV (OpenTrafficCam)"
2546
OTC_OTEVENTS_FORMAT_NAME = "OTEvents (OpenTrafficCam)"
2647

48+
OCCURRENCE_SEC = f"{OCCURRENCE}_sec"
49+
50+
NUMBER_ROUNDED_COLUMNS = {
51+
event_list.EVENT_COORDINATE_X: 1,
52+
event_list.EVENT_COORDINATE_Y: 1,
53+
event_list.INTERPOLATED_EVENT_COORDINATE_X: 1,
54+
event_list.INTERPOLATED_EVENT_COORDINATE_Y: 1,
55+
event_list.DIRECTION_VECTOR_X: 4,
56+
event_list.DIRECTION_VECTOR_Y: 4,
57+
}
58+
59+
DATETIME_ROUNDED_COLUMNS = {
60+
event_list.OCCURRENCE: "ms",
61+
event_list.INTERPOLATED_OCCURRENCE: "ms",
62+
}
63+
64+
EXPORT_COLUMNS = [
65+
ROAD_USER_ID,
66+
ROAD_USER_TYPE,
67+
HOSTNAME,
68+
OCCURRENCE,
69+
FRAME_NUMBER,
70+
SECTION_ID,
71+
EVENT_TYPE,
72+
VIDEO_NAME,
73+
OCCURRENCE_SEC,
74+
EVENT_COORDINATE_X,
75+
EVENT_COORDINATE_Y,
76+
DIRECTION_VECTOR_X,
77+
DIRECTION_VECTOR_Y,
78+
SECTION_NAME,
79+
OCCURRENCE_DATE,
80+
OCCURRENCE_TIME,
81+
INTERPOLATED_OCCURRENCE,
82+
INTERPOLATED_EVENT_COORDINATE_X,
83+
INTERPOLATED_EVENT_COORDINATE_Y,
84+
]
85+
2786

2887
class EventListDataFrameBuilder:
2988
def __init__(self, events: Iterable[Event], sections: Iterable[Section]):
@@ -39,7 +98,20 @@ def build(self) -> pd.DataFrame:
3998
self._split_columns_with_lists()
4099
self._add_section_names()
41100
self._add_detailed_date_time_columns()
42-
return self._df
101+
self._round()
102+
return self._df.loc[:, EXPORT_COLUMNS]
103+
104+
def _round(self) -> None:
105+
for column, decimals in NUMBER_ROUNDED_COLUMNS.items():
106+
if column in self._df.columns:
107+
self._df.loc[:, column] = self._df.loc[:, column].round(decimals)
108+
for column, freq in DATETIME_ROUNDED_COLUMNS.items():
109+
if column in self._df.columns:
110+
self._df.loc[:, column] = (
111+
pd.to_datetime(self._df.loc[:, column])
112+
.dt.round(freq)
113+
.dt.strftime(DATE_TIME_FORMAT)
114+
)
43115

44116
def _add_detailed_date_time_columns(self) -> None:
45117
occurrence_column = pd.to_datetime(self._df[event_list.OCCURRENCE])
@@ -69,8 +141,21 @@ def _split_columns_with_lists(self) -> None:
69141
self._df[event_list.DIRECTION_VECTOR].tolist(), index=self._df.index
70142
)
71143
)
144+
self._df[
145+
[
146+
event_list.INTERPOLATED_EVENT_COORDINATE_X,
147+
event_list.INTERPOLATED_EVENT_COORDINATE_Y,
148+
]
149+
] = pd.DataFrame(
150+
self._df[event_list.INTERPOLATED_EVENT_COORDINATE].tolist(),
151+
index=self._df.index,
152+
)
72153
self._df = self._df.drop(
73-
columns=[event_list.EVENT_COORDINATE, event_list.DIRECTION_VECTOR]
154+
columns=[
155+
event_list.EVENT_COORDINATE,
156+
event_list.DIRECTION_VECTOR,
157+
event_list.INTERPOLATED_EVENT_COORDINATE,
158+
]
74159
)
75160

76161
def _add_section_names(self) -> None:

‎pyproject.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ dependencies = [
3535
"openpyxl==3.1.5",
3636
"pandas==2.2.3",
3737
"pillow==11.1.0",
38+
"polars==1.32.3",
3839
"pywin32==310; sys_platform == 'win32'",
3940
"requests==2.32.4",
4041
"seaborn==0.13.2",

‎tests/OTAnalytics/plugin_parser/test_streaming_parser.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,8 @@ def test_parse_chunks(
168168
second_chunk = next(stream).as_list()
169169

170170
assert len(first_chunk) == 4
171-
assert len(second_chunk) == 2
172-
assert len(expected) == 6
171+
assert len(second_chunk) == 3
172+
assert len(expected) == 7
173173
assert set(self.ids_of(expected)) == set(
174174
self.ids_of(first_chunk + second_chunk)
175175
)

‎tests/regression_otanalytics.py‎

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@
88
from OTAnalytics.application.parser.flow_parser import FlowParser
99
from OTAnalytics.plugin_cli.cli_application import OtAnalyticsCliApplicationStarter
1010
from OTAnalytics.plugin_parser.otvision_parser import OtFlowParser
11-
from tests.utils.assertions import assert_two_files_equal_sorted
11+
from tests.utils.assertions import (
12+
assert_equal_count_files,
13+
assert_equal_event_files,
14+
assert_two_files_equal_sorted,
15+
)
1216
from tests.utils.builders.run_configuration import create_run_config
1317

1418

@@ -48,7 +52,7 @@ def otflow_file(test_data_dir: Path) -> str:
4852

4953
@pytest.fixture(scope="module")
5054
def cli_chunk_size() -> int:
51-
return 5
55+
return 1000
5256

5357

5458
@pytest.fixture
@@ -59,16 +63,11 @@ def otflow_parser() -> FlowParser:
5963
class TestRegressionCompleteApplication:
6064

6165
@pytest.mark.skip
62-
@pytest.mark.parametrize(
63-
"cli_mode",
64-
[CliMode.STREAM, CliMode.BULK],
65-
)
6666
def test_15_min_recreate_test_data(
6767
self,
6868
otflow_file: str,
6969
all_track_files_test_dataset: list[Path],
7070
otflow_parser: FlowParser,
71-
cli_mode: CliMode,
7271
cli_chunk_size: int,
7372
) -> None:
7473
for test_file in tqdm(all_track_files_test_dataset, desc="test data file"):
@@ -83,7 +82,7 @@ def test_15_min_recreate_test_data(
8382
test_interval=test_interval,
8483
otflow_parser=otflow_parser,
8584
event_formats=("csv", "otevents"),
86-
cli_mode=cli_mode,
85+
cli_mode=CliMode.BULK,
8786
cli_chunk_size=cli_chunk_size,
8887
)
8988

@@ -268,14 +267,6 @@ def _execute_test(
268267
cli_chunk_size,
269268
)
270269

271-
actual_events_file = Path(test_data_tmp_dir / save_name).with_suffix(
272-
".events.csv"
273-
)
274-
expected_events_file = Path(test_data_dir / save_name).with_suffix(
275-
".events.csv"
276-
)
277-
assert_two_files_equal_sorted(actual_events_file, expected_events_file)
278-
279270
actual_counts_file = (
280271
Path(test_data_tmp_dir / save_name)
281272
.with_suffix(f".counts_{count_interval}min.csv")
@@ -286,7 +277,16 @@ def _execute_test(
286277
.with_suffix(f".counts_{count_interval}min.csv")
287278
.absolute()
288279
)
289-
assert_two_files_equal_sorted(actual_counts_file, expected_counts_file)
280+
assert_equal_count_files(actual_counts_file, expected_counts_file)
281+
282+
actual_events_file = Path(test_data_tmp_dir / save_name).with_suffix(
283+
".events.csv"
284+
)
285+
expected_events_file = Path(test_data_dir / save_name).with_suffix(
286+
".events.csv"
287+
)
288+
assert_two_files_equal_sorted(actual_events_file, expected_events_file)
289+
assert_equal_event_files(actual_events_file, expected_events_file)
290290

291291
def _run_otanalytics(
292292
self,

‎tests/utils/assertions.py‎

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22
from typing import Iterable
33
from unittest.mock import Mock
44

5+
import polars as pl
6+
from polars.testing import assert_frame_equal
7+
58
from OTAnalytics.domain.track import Detection, Track
69
from OTAnalytics.domain.track_dataset.track_dataset import TrackDataset
10+
from OTAnalytics.plugin_prototypes.eventlist_exporter import eventlist_exporter
711

812

913
def assert_equal_detection_properties(actual: Detection, expected: Detection) -> None:
@@ -66,12 +70,10 @@ def assert_two_files_equal_sorted(
6670
) -> None:
6771

6872
with open(actual_counts_file, mode="r") as actual:
69-
act_lines = sorted(actual.readlines())
70-
act_lines = sorted([line.split(",", 1)[1] for line in actual.readlines()])
73+
act_lines = sorted([line for line in actual.readlines()])
7174

7275
with open(expected_counts_file, mode="r") as expected:
73-
exp_lines = sorted(expected.readlines())
74-
exp_lines = sorted([line.split(",", 1)[1] for line in expected.readlines()])
76+
exp_lines = sorted([line for line in expected.readlines()])
7577

7678
assert set(act_lines) == set(exp_lines), (
7779
f"Sets are not equal [act len: {len(act_lines)}, "
@@ -99,3 +101,28 @@ def assert_two_files_equal_sorted(
99101
f"{([a for e, a in zip(exp_lines, act_lines) if e != a] + [None])[0]}\n"
100102
f"\n"
101103
)
104+
105+
106+
def assert_equal_event_files(
107+
actual_counts_file: Path, expected_counts_file: Path
108+
) -> None:
109+
actual = _read_event_csv(actual_counts_file)
110+
expected = _read_event_csv(expected_counts_file)
111+
assert_frame_equal(actual, expected)
112+
113+
114+
def assert_equal_count_files(
115+
actual_counts_file: Path, expected_counts_file: Path
116+
) -> None:
117+
actual = _read_counts_csv(actual_counts_file)
118+
expected = _read_counts_csv(expected_counts_file)
119+
assert_frame_equal(actual, expected)
120+
121+
122+
def _read_counts_csv(file: Path) -> pl.DataFrame:
123+
return pl.read_csv(file)
124+
125+
126+
def _read_event_csv(file: Path) -> pl.DataFrame:
127+
data = pl.read_csv(file)
128+
return data.sort(by=eventlist_exporter.EXPORT_COLUMNS)

‎uv.lock‎

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)