Skip to content

Commit 3b7abc3

Browse files
committed
Added missing analysis config state for full otconfig re-deserialization
1 parent 8fe3b8c commit 3b7abc3

18 files changed

Lines changed: 363 additions & 123 deletions

‎OTAnalytics/application/application.py‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
)
1010
from OTAnalytics.application.datastore import Datastore
1111
from OTAnalytics.application.key_prefix import S3KeyPrefix
12+
from OTAnalytics.application.parser.config_parser import StaticAnalysisConfig
1213
from OTAnalytics.application.project import SvzMetadata
1314
from OTAnalytics.application.state import (
1415
ActionState,
1516
CurrentKeyPrefix,
17+
CurrentStaticAnalysisConfigState,
1618
FileState,
1719
FlowState,
1820
SectionState,
@@ -160,6 +162,7 @@ def __init__(
160162
get_current_remark: GetCurrentRemark,
161163
update_count_plots: CountPlotsUpdater,
162164
current_key_prefix: CurrentKeyPrefix,
165+
current_static_analysis_config_state: CurrentStaticAnalysisConfigState,
163166
) -> None:
164167
self._datastore: Datastore = datastore
165168
self.track_state: TrackState = track_state
@@ -208,6 +211,9 @@ def __init__(
208211
self._get_current_remark = get_current_remark
209212
self._update_count_plots = update_count_plots
210213
self._current_key_prefix = current_key_prefix
214+
self._current_static_analysis_config_state = (
215+
current_static_analysis_config_state
216+
)
211217

212218
def connect_observers(self) -> None:
213219
"""
@@ -317,6 +323,14 @@ def get_current_key_prefix(self) -> S3KeyPrefix | None:
317323
"""
318324
return self._current_key_prefix.get()
319325

326+
def get_current_static_analysis_config(self) -> StaticAnalysisConfig:
327+
"""Get the current static analysis configuration.
328+
329+
Returns:
330+
StaticAnalysisConfig: the current static analysis configuration.
331+
"""
332+
return self._current_static_analysis_config_state.get()
333+
320334
def load_otconfig(self, file: Path) -> None:
321335
"""Load an otconfig, blocking. Outside an event loop only."""
322336
self._load_otconfig.load(file)

‎OTAnalytics/application/config.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
DEFAULT_SAVE_SUFFIX = ""
5050
DEFAULT_EVENT_FORMATS = {"csv"}
5151
DEFAULT_LOG_FILE = Path("logs")
52+
DEFAULT_NUM_PROCESSES = 1
5253

5354

5455
OS: str = platform.system()

‎OTAnalytics/application/parser/config_parser.py‎

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,18 @@ class AnalysisConfig:
3232
logfile: Path
3333

3434

35+
@dataclass(frozen=True)
36+
class StaticAnalysisConfig:
37+
"""The subset of the AnalysisConfig that stays fixed while the
38+
application runs."""
39+
40+
do_events: bool
41+
do_counting: bool
42+
export_config: ExportConfig
43+
num_processes: int
44+
logfile: Path
45+
46+
3547
@dataclass(frozen=True)
3648
class SubstitutedFile:
3749
"""A reference an otconfig named, and the file loaded in its place.
@@ -97,8 +109,8 @@ def parse_from_dict(self, data: dict, base_folder: Path) -> OtConfig:
97109
def serialize(
98110
self,
99111
project: Project,
112+
analysis_config: AnalysisConfig,
100113
video_files: Iterable[Video],
101-
track_files: Iterable[Path],
102114
sections: Iterable[Section],
103115
flows: Iterable[Flow],
104116
file: Path,
@@ -110,8 +122,9 @@ def serialize(
110122
111123
Args:
112124
project (Project): description of the project
125+
analysis_config (AnalysisConfig): analysis settings to store,
126+
including the track files to reference
113127
video_files (Iterable[Video]): video files to reference
114-
track_files (Iterable[Path]): track files to reference
115128
sections (Iterable[Section]): sections to store
116129
flows (Iterable[Flow]): flows to store
117130
file (Path): output file
@@ -139,8 +152,8 @@ def serialize_from_config(self, config: OtConfig, file: Path) -> None:
139152
def convert(
140153
self,
141154
project: Project,
155+
analysis_config: AnalysisConfig,
142156
video_files: Iterable[Video],
143-
track_files: Iterable[Path],
144157
sections: Iterable[Section],
145158
flows: Iterable[Flow],
146159
file: Path,

‎OTAnalytics/application/state.py‎

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,28 @@
66
from typing import Callable, Generic, Optional
77

88
from OTAnalytics.application.analysis.traffic_counting import CountImage
9-
from OTAnalytics.application.config import DEFAULT_TRACK_OFFSET
9+
from OTAnalytics.application.analysis.traffic_counting_specification import (
10+
CountingEvent,
11+
)
12+
from OTAnalytics.application.config import (
13+
DEFAULT_COUNTING_EVENT,
14+
DEFAULT_COUNTING_INTERVAL_IN_MINUTES,
15+
DEFAULT_DO_COUNTING,
16+
DEFAULT_DO_EVENTS,
17+
DEFAULT_EVENT_FORMATS,
18+
DEFAULT_LOG_FILE,
19+
DEFAULT_NUM_PROCESSES,
20+
DEFAULT_SAVE_NAME,
21+
DEFAULT_SAVE_SUFFIX,
22+
DEFAULT_TRACK_OFFSET,
23+
)
1024
from OTAnalytics.application.datastore import Datastore
1125
from OTAnalytics.application.key_prefix import S3KeyPrefix
26+
from OTAnalytics.application.parser.config_parser import (
27+
AnalysisConfig,
28+
ExportConfig,
29+
StaticAnalysisConfig,
30+
)
1231
from OTAnalytics.application.playback import SkipTime
1332
from OTAnalytics.application.use_cases.section_repository import GetSectionsById
1433
from OTAnalytics.domain.date import DateRange
@@ -810,3 +829,69 @@ def __init__(self) -> None:
810829

811830
def reset(self) -> None:
812831
self.last_saved_config.set(None)
832+
833+
834+
class CurrentExportConfigState:
835+
def __init__(self) -> None:
836+
self.save_name: str
837+
self.save_suffix: str
838+
self.event_formats: set[str]
839+
self.count_intervals: set[int]
840+
self.counting_event: CountingEvent
841+
self.reset()
842+
843+
def get(self) -> ExportConfig:
844+
return ExportConfig(
845+
save_name=self.save_name,
846+
save_suffix=self.save_suffix,
847+
event_formats=self.event_formats,
848+
count_intervals=self.count_intervals,
849+
counting_event=self.counting_event,
850+
)
851+
852+
def set(self, export_config: ExportConfig) -> None:
853+
self.save_name = export_config.save_name
854+
self.save_suffix = export_config.save_suffix
855+
self.event_formats = export_config.event_formats
856+
self.count_intervals = export_config.count_intervals
857+
self.counting_event = export_config.counting_event
858+
859+
def reset(self) -> None:
860+
self.save_name = DEFAULT_SAVE_NAME
861+
self.save_suffix = DEFAULT_SAVE_SUFFIX
862+
self.event_formats = DEFAULT_EVENT_FORMATS
863+
self.count_intervals = {DEFAULT_COUNTING_INTERVAL_IN_MINUTES}
864+
self.counting_event = CountingEvent.parse(DEFAULT_COUNTING_EVENT)
865+
866+
867+
class CurrentStaticAnalysisConfigState:
868+
def __init__(self) -> None:
869+
self.do_events: bool
870+
self.do_counting: bool
871+
self.export_config: CurrentExportConfigState = CurrentExportConfigState()
872+
self.num_processes: int
873+
self.logfile: Path
874+
self.reset()
875+
876+
def get(self) -> StaticAnalysisConfig:
877+
return StaticAnalysisConfig(
878+
do_events=self.do_events,
879+
do_counting=self.do_counting,
880+
export_config=self.export_config.get(),
881+
num_processes=self.num_processes,
882+
logfile=self.logfile,
883+
)
884+
885+
def set_from_analysis_config(self, analysis_config: AnalysisConfig) -> None:
886+
self.do_events = analysis_config.do_events
887+
self.do_counting = analysis_config.do_counting
888+
self.export_config.set(analysis_config.export_config)
889+
self.num_processes = analysis_config.num_processes
890+
self.logfile = analysis_config.logfile
891+
892+
def reset(self) -> None:
893+
self.do_events = DEFAULT_DO_EVENTS
894+
self.do_counting = DEFAULT_DO_COUNTING
895+
self.export_config.reset()
896+
self.num_processes = DEFAULT_NUM_PROCESSES
897+
self.logfile = DEFAULT_LOG_FILE

‎OTAnalytics/application/use_cases/config.py‎

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
from OTAnalytics.application.parser.config_parser import ConfigParser
66
from OTAnalytics.application.state import ConfigurationFile, CurrentKeyPrefix, FileState
77
from OTAnalytics.application.upload_otconfig import UploadOtconfig
8+
from OTAnalytics.application.use_cases.get_current_analysis_config import (
9+
GetCurrentAnalysisConfig,
10+
)
811
from OTAnalytics.application.use_cases.get_current_remark import GetCurrentRemark
912

1013

@@ -24,13 +27,15 @@ def __init__(
2427
state: FileState,
2528
get_current_remark: GetCurrentRemark,
2629
current_key_prefix: CurrentKeyPrefix,
30+
get_current_analysis_config: GetCurrentAnalysisConfig,
2731
otconfig_upload: UploadOtconfig,
2832
) -> None:
2933
self._datastore = datastore
3034
self._config_parser = config_parser
3135
self._state = state
3236
self._get_current_remark = get_current_remark
3337
self._current_key_prefix = current_key_prefix
38+
self._get_current_analysis_config = get_current_analysis_config
3439
self._otconfig_upload = otconfig_upload
3540

3641
async def __call__(self, file: Path) -> None:
@@ -47,18 +52,18 @@ async def __call__(self, file: Path) -> None:
4752
raise ConfigValidationError(errors)
4853

4954
video_files = self._datastore.get_all_videos()
50-
track_files = self._datastore._track_file_repository.get_all()
5155
sections = self._datastore.get_all_sections()
5256
flows = self._datastore.get_all_flows()
5357
remark = self._get_current_remark.get()
5458
# The same value reaches `convert` below. Passing it to only one of the
5559
# two would make every project report itself as permanently unsaved.
5660
s3_key_prefix = self._current_key_prefix.get()
61+
analysis_config = self._get_current_analysis_config.get()
5762
logger().info(f"Saving otconfig to '{file}'")
5863
self._config_parser.serialize(
5964
project=project,
65+
analysis_config=analysis_config,
6066
video_files=video_files,
61-
track_files=track_files,
6267
sections=sections,
6368
flows=flows,
6469
file=file,
@@ -69,14 +74,14 @@ async def __call__(self, file: Path) -> None:
6974
ConfigurationFile(
7075
file,
7176
self._config_parser.convert(
72-
project,
73-
video_files,
74-
track_files,
75-
sections,
76-
flows,
77-
file,
78-
remark,
79-
s3_key_prefix,
77+
project=project,
78+
analysis_config=analysis_config,
79+
video_files=video_files,
80+
sections=sections,
81+
flows=flows,
82+
file=file,
83+
remark=remark,
84+
s3_key_prefix=s3_key_prefix,
8085
),
8186
)
8287
)

‎OTAnalytics/application/use_cases/config_has_changed.py‎

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
from OTAnalytics.application.parser.flow_parser import FlowParser
33
from OTAnalytics.application.state import ConfigurationFile, CurrentKeyPrefix, FileState
44
from OTAnalytics.application.use_cases.flow_repository import GetAllFlows
5+
from OTAnalytics.application.use_cases.get_current_analysis_config import (
6+
GetCurrentAnalysisConfig,
7+
)
58
from OTAnalytics.application.use_cases.get_current_project import GetCurrentProject
69
from OTAnalytics.application.use_cases.get_current_remark import GetCurrentRemark
710
from OTAnalytics.application.use_cases.section_repository import GetAllSections
8-
from OTAnalytics.application.use_cases.track_repository import GetAllTrackFiles
911
from OTAnalytics.application.use_cases.video_repository import GetAllVideos
1012

1113

@@ -17,18 +19,18 @@ def __init__(
1719
get_flows: GetAllFlows,
1820
get_current_project: GetCurrentProject,
1921
get_videos: GetAllVideos,
20-
get_track_files: GetAllTrackFiles,
2122
get_remark: GetCurrentRemark,
2223
current_key_prefix: CurrentKeyPrefix,
24+
get_current_analysis_config: GetCurrentAnalysisConfig,
2325
):
2426
self._config_parser = config_parser
2527
self._get_sections = get_sections
2628
self._get_flows = get_flows
2729
self._get_current_project = get_current_project
2830
self._get_videos = get_videos
29-
self._get_track_files = get_track_files
3031
self._get_remark = get_remark
3132
self._current_key_prefix = current_key_prefix
33+
self._get_current_analysis_config = get_current_analysis_config
3234

3335
def has_changed(self, prev_config: ConfigurationFile) -> bool:
3436
"""
@@ -41,14 +43,14 @@ def has_changed(self, prev_config: ConfigurationFile) -> bool:
4143
bool: True if the OTConfig file has changed, False otherwise.
4244
"""
4345
current_content = self._config_parser.convert(
44-
self._get_current_project.get(),
45-
self._get_videos.get(),
46-
self._get_track_files(),
47-
self._get_sections(),
48-
self._get_flows.get(),
49-
prev_config.file,
50-
self._get_remark.get(),
51-
self._current_key_prefix.get(),
46+
project=self._get_current_project.get(),
47+
analysis_config=self._get_current_analysis_config.get(),
48+
video_files=self._get_videos.get(),
49+
sections=self._get_sections(),
50+
flows=self._get_flows.get(),
51+
file=prev_config.file,
52+
remark=self._get_remark.get(),
53+
s3_key_prefix=self._current_key_prefix.get(),
5254
)
5355
return prev_config.content != current_content
5456

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from OTAnalytics.application.parser.config_parser import AnalysisConfig
2+
from OTAnalytics.application.state import CurrentStaticAnalysisConfigState
3+
from OTAnalytics.application.use_cases.track_repository import GetAllTrackFiles
4+
5+
6+
class GetCurrentAnalysisConfig:
7+
"""Provide the analysis configuration currently held by the application.
8+
9+
Args:
10+
static_analysis_config_state (CurrentStaticAnalysisConfigState): the state
11+
holding the analysis settings that are not derived from repositories.
12+
get_all_track_files (GetAllTrackFiles): use case to retrieve all track files.
13+
"""
14+
15+
def __init__(
16+
self,
17+
static_analysis_config_state: CurrentStaticAnalysisConfigState,
18+
get_all_track_files: GetAllTrackFiles,
19+
) -> None:
20+
self._static_analysis_config_state = static_analysis_config_state
21+
self._get_all_track_files = get_all_track_files
22+
23+
def get(self) -> AnalysisConfig:
24+
"""Get the current analysis configuration.
25+
26+
Returns:
27+
AnalysisConfig: The current analysis configuration.
28+
"""
29+
return AnalysisConfig(
30+
do_events=self._static_analysis_config_state.do_events,
31+
do_counting=self._static_analysis_config_state.do_counting,
32+
track_files=self._get_all_track_files(),
33+
export_config=self._static_analysis_config_state.export_config.get(),
34+
num_processes=self._static_analysis_config_state.num_processes,
35+
logfile=self._static_analysis_config_state.logfile,
36+
)

0 commit comments

Comments
 (0)