Skip to content

Commit

Permalink
Merge pull request #446 from OpenTrafficCam/bug/4209-could-not-load-o…
Browse files Browse the repository at this point in the history
…tconfig-file

bug/4209-could-not-load-otconfig-file
  • Loading branch information
briemla authored Jan 31, 2024
2 parents 96ef8e0 + 36a9322 commit 837a9a4
Show file tree
Hide file tree
Showing 8 changed files with 382 additions and 18 deletions.
54 changes: 54 additions & 0 deletions OTAnalytics/application/config_specification.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from abc import ABC, abstractmethod
from pathlib import Path


class OtConfigDefaultValueProvider(ABC):
@property
@abstractmethod
def do_events(self) -> bool:
raise NotImplementedError

@property
@abstractmethod
def do_counting(self) -> bool:
raise NotImplementedError

@property
@abstractmethod
def track_files(self) -> set[Path]:
raise NotImplementedError

@property
@abstractmethod
def event_formats(self) -> set[str]:
raise NotImplementedError

@property
@abstractmethod
def save_name(self) -> str:
raise NotImplementedError

@property
@abstractmethod
def save_suffix(self) -> str:
raise NotImplementedError

@property
@abstractmethod
def count_intervals(self) -> set[int]:
raise NotImplementedError

@property
@abstractmethod
def num_processes(self) -> int:
raise NotImplementedError

@property
@abstractmethod
def log_file(self) -> Path:
raise NotImplementedError

@property
@abstractmethod
def debug(self) -> bool:
raise NotImplementedError
81 changes: 81 additions & 0 deletions OTAnalytics/application/parser/cli_parser.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
from abc import ABC, abstractmethod
from dataclasses import dataclass
from pathlib import Path

from OTAnalytics.application.config import (
DEFAULT_COUNTING_INTERVAL_IN_MINUTES,
DEFAULT_EVENTLIST_FILE_TYPE,
DEFAULT_NUM_PROCESSES,
)
from OTAnalytics.application.config_specification import OtConfigDefaultValueProvider
from OTAnalytics.application.logger import DEFAULT_LOG_FILE

DEFAULT_SAVE_SUFFIX = ""
DEFAULT_SAVE_NAME = ""


class CliParseError(Exception):
Expand All @@ -23,6 +35,75 @@ class CliArguments:
log_file: str | None = None


class CliValueProvider(OtConfigDefaultValueProvider):
def __init__(self, cli_args: CliArguments) -> None:
self._cli_args = cli_args

@property
def do_events(self) -> bool:
return True

@property
def do_counting(self) -> bool:
return True

@property
def track_files(self) -> set[Path]:
if self._cli_args.track_files:
return {Path(path) for path in self._cli_args.track_files}
return set()

@property
def event_formats(self) -> set[str]:
return (
set(self._cli_args.event_formats)
if self._cli_args.event_formats
else {DEFAULT_EVENTLIST_FILE_TYPE}
)

@property
def save_name(self) -> str:
return (
self._cli_args.save_name if self._cli_args.save_name else DEFAULT_SAVE_NAME
)

@property
def save_suffix(self) -> str:
return (
self._cli_args.save_suffix
if self._cli_args.save_suffix
else DEFAULT_SAVE_SUFFIX
)

@property
def count_intervals(self) -> set[int]:
return (
set(self._cli_args.count_intervals)
if self._cli_args.count_intervals
else {DEFAULT_COUNTING_INTERVAL_IN_MINUTES}
)

@property
def num_processes(self) -> int:
return (
self._cli_args.num_processes
if self._cli_args.num_processes
else DEFAULT_NUM_PROCESSES
)

@property
def log_file(self) -> Path:
return (
Path(self._cli_args.log_file)
if self._cli_args.log_file
else DEFAULT_LOG_FILE
)

@property
def debug(self) -> bool:
return self._cli_args.debug


class CliParser(ABC):
@abstractmethod
def parse(self) -> CliArguments:
Expand Down
3 changes: 2 additions & 1 deletion OTAnalytics/application/run_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
DEFAULT_EVENTLIST_FILE_TYPE,
DEFAULT_NUM_PROCESSES,
)
from OTAnalytics.application.config_specification import OtConfigDefaultValueProvider
from OTAnalytics.application.datastore import FlowParser
from OTAnalytics.application.logger import DEFAULT_LOG_FILE
from OTAnalytics.application.parser.cli_parser import CliArguments
Expand All @@ -20,7 +21,7 @@ class RunConfigurationError(Exception):
pass


class RunConfiguration:
class RunConfiguration(OtConfigDefaultValueProvider):
def __init__(
self,
flow_parser: FlowParser,
Expand Down
57 changes: 53 additions & 4 deletions OTAnalytics/plugin_parser/otconfig_parser.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from abc import ABC, abstractmethod
from datetime import datetime, timezone
from pathlib import Path
from typing import Iterable

from OTAnalytics.application import project
from OTAnalytics.application.config_specification import OtConfigDefaultValueProvider
from OTAnalytics.application.datastore import FlowParser, VideoParser
from OTAnalytics.application.parser.config_parser import (
AnalysisConfig,
Expand Down Expand Up @@ -34,23 +36,70 @@
PATH = "path"


class OtConfigFormatFixer(ABC):
@abstractmethod
def fix(self, content: dict) -> dict:
raise NotImplementedError


class MultiFixer(OtConfigFormatFixer):
def __init__(self, fixer: list[OtConfigFormatFixer]) -> None:
self._fixer = fixer

def fix(self, content: dict) -> dict:
result = content
for fixer in self._fixer:
result = fixer.fix(result)
return result


class FixMissingAnalysis(OtConfigFormatFixer):
def __init__(self, run_config: OtConfigDefaultValueProvider) -> None:
self._run_config = run_config

def fix(self, content: dict) -> dict:
if ANALYSIS in content.keys():
return content
content[ANALYSIS] = self._create_analysis_block()
return content

def _create_analysis_block(self) -> dict:
return {
DO_EVENTS: self._run_config.do_events,
DO_COUNTING: self._run_config.do_counting,
TRACKS: self._run_config.track_files,
EXPORT: {
EVENT_FORMATS: self._run_config.event_formats,
SAVE_NAME: self._run_config.save_name,
SAVE_SUFFIX: self._run_config.save_suffix,
COUNT_INTERVALS: self._run_config.count_intervals,
},
NUM_PROCESSES: self._run_config.num_processes,
LOGFILE: self._run_config.log_file,
DEBUG: self._run_config.debug,
}


class OtConfigParser(ConfigParser):
def __init__(
self,
format_fixer: OtConfigFormatFixer,
video_parser: VideoParser,
flow_parser: FlowParser,
) -> None:
self._format_fixer = format_fixer
self._video_parser = video_parser
self._flow_parser = flow_parser

def parse(self, file: Path) -> OtConfig:
base_folder = file.parent
content = parse_json(file)
_project = self._parse_project(content[PROJECT])
analysis_config = self._parse_analysis(content[ANALYSIS], base_folder)
videos = self._video_parser.parse_list(content[video.VIDEOS], base_folder)
fixed_content = self._format_fixer.fix(content)
_project = self._parse_project(fixed_content[PROJECT])
analysis_config = self._parse_analysis(fixed_content[ANALYSIS], base_folder)
videos = self._video_parser.parse_list(fixed_content[video.VIDEOS], base_folder)
sections, flows = self._flow_parser.parse_content(
content[section.SECTIONS], content[flow.FLOWS]
fixed_content[section.SECTIONS], fixed_content[flow.FLOWS]
)
return OtConfig(
project=_project,
Expand Down
36 changes: 31 additions & 5 deletions OTAnalytics/plugin_ui/main_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from OTAnalytics.application.analysis.traffic_counting_specification import ExportCounts
from OTAnalytics.application.application import OTAnalyticsApplication
from OTAnalytics.application.config import DEFAULT_NUM_PROCESSES
from OTAnalytics.application.config_specification import OtConfigDefaultValueProvider
from OTAnalytics.application.datastore import (
Datastore,
EventListParser,
Expand All @@ -26,7 +27,11 @@
)
from OTAnalytics.application.eventlist import SceneActionDetector
from OTAnalytics.application.logger import logger, setup_logger
from OTAnalytics.application.parser.cli_parser import CliParseError, CliParser
from OTAnalytics.application.parser.cli_parser import (
CliParseError,
CliParser,
CliValueProvider,
)
from OTAnalytics.application.plotting import LayeredPlotter, PlottingLayer
from OTAnalytics.application.run_configuration import RunConfiguration
from OTAnalytics.application.state import (
Expand Down Expand Up @@ -128,7 +133,12 @@
FillZerosExporterFactory,
SimpleExporterFactory,
)
from OTAnalytics.plugin_parser.otconfig_parser import OtConfigParser
from OTAnalytics.plugin_parser.otconfig_parser import (
FixMissingAnalysis,
MultiFixer,
OtConfigFormatFixer,
OtConfigParser,
)
from OTAnalytics.plugin_parser.otvision_parser import (
DEFAULT_TRACK_LENGTH_LIMIT,
CachedVideoParser,
Expand Down Expand Up @@ -167,19 +177,31 @@ def start(self) -> None:
except CliParseError as e:
logger().exception(e, exc_info=True)
else:
self.start_gui()
self.start_gui(run_config)

def _parse_configuration(self) -> RunConfiguration:
cli_args_parser = self._build_cli_argument_parser()
cli_args = cli_args_parser.parse()
cli_value_provider: OtConfigDefaultValueProvider = CliValueProvider(cli_args)
format_fixer = self._create_format_fixer(cli_value_provider)
flow_parser = self._create_flow_parser()
config_parser = OtConfigParser(self._create_video_parser(), flow_parser)
config_parser = OtConfigParser(
format_fixer=format_fixer,
video_parser=self._create_video_parser(),
flow_parser=flow_parser,
)

if config_file := cli_args.config_file:
config = config_parser.parse(Path(config_file))
return RunConfiguration(flow_parser, cli_args, config)
return RunConfiguration(flow_parser, cli_args, None)

@staticmethod
def _create_format_fixer(
default_value_provider: OtConfigDefaultValueProvider,
) -> OtConfigFormatFixer:
return MultiFixer([FixMissingAnalysis(default_value_provider)])

def _build_cli_argument_parser(self) -> CliParser:
return ArgparseCliParser()

Expand All @@ -191,7 +213,7 @@ def _setup_logger(self, log_file: Path, overwrite: bool, debug: bool) -> None:
else:
setup_logger(log_file=log_file, overwrite=overwrite, log_level=logging.INFO)

def start_gui(self) -> None:
def start_gui(self, run_config: RunConfiguration) -> None:
from OTAnalytics.plugin_ui.customtkinter_gui.dummy_viewmodel import (
DummyViewModel,
)
Expand Down Expand Up @@ -228,6 +250,7 @@ def start_gui(self) -> None:
flow_repository,
event_repository,
pulling_progressbar_builder,
run_config,
)
track_state = self._create_track_state()
track_view_state = self._create_track_view_state()
Expand Down Expand Up @@ -521,6 +544,7 @@ def _create_datastore(
flow_repository: FlowRepository,
event_repository: EventRepository,
progressbar_builder: ProgressbarBuilder,
run_config: RunConfiguration,
) -> Datastore:
"""
Build all required objects and inject them where necessary
Expand All @@ -533,9 +557,11 @@ def _create_datastore(
flow_parser = self._create_flow_parser()
event_list_parser = self._create_event_list_parser()
track_video_parser = OttrkVideoParser(video_parser)
format_fixer = self._create_format_fixer(run_config)
config_parser = OtConfigParser(
video_parser=video_parser,
flow_parser=flow_parser,
format_fixer=format_fixer,
)
return Datastore(
track_repository,
Expand Down
Loading

0 comments on commit 837a9a4

Please sign in to comment.