Skip to content

Commit

Permalink
Merge pull request #486 from OpenTrafficCam/feature/4641-implement-sa…
Browse files Browse the repository at this point in the history
…ve-and-save-as

Feature/4641 implement save and save as
  • Loading branch information
briemla authored Mar 21, 2024
2 parents 5886758 + 6c0d17c commit 0e61fc3
Show file tree
Hide file tree
Showing 46 changed files with 1,345 additions and 283 deletions.
12 changes: 12 additions & 0 deletions OTAnalytics/adapter_ui/abstract_button_quick_save_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from abc import ABC, abstractmethod


class AbstractButtonQuickSaveConfig(ABC):

@abstractmethod
def set_state_changed_color(self) -> None:
raise NotImplementedError

@abstractmethod
def set_default_color(self) -> None:
raise NotImplementedError
14 changes: 14 additions & 0 deletions OTAnalytics/adapter_ui/view_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
from pathlib import Path
from typing import Callable, Iterable, Optional

from OTAnalytics.adapter_ui.abstract_button_quick_save_config import (
AbstractButtonQuickSaveConfig,
)
from OTAnalytics.adapter_ui.abstract_canvas import AbstractCanvas
from OTAnalytics.adapter_ui.abstract_frame import AbstractFrame
from OTAnalytics.adapter_ui.abstract_frame_canvas import AbstractFrameCanvas
Expand Down Expand Up @@ -73,6 +76,13 @@ def set_filter_frame(self, filter_frame: AbstractFrameFilter) -> None:
def set_frame_project(self, project_frame: AbstractFrameProject) -> None:
pass

@abstractmethod
def set_button_quick_save_config(
self, button_quick_save_config: AbstractButtonQuickSaveConfig
) -> None:

raise NotImplementedError

@abstractmethod
def load_otconfig(self) -> None:
pass
Expand Down Expand Up @@ -129,6 +139,10 @@ def load_configuration(self) -> None:
def save_configuration(self) -> None:
raise NotImplementedError

@abstractmethod
def quick_save_configuration(self) -> None:
raise NotImplementedError

@abstractmethod
def cancel_action(self) -> None:
raise NotImplementedError
Expand Down
32 changes: 27 additions & 5 deletions OTAnalytics/application/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from OTAnalytics.application.datastore import Datastore
from OTAnalytics.application.state import (
ActionState,
FileState,
FlowState,
SectionState,
TracksMetadata,
Expand All @@ -23,6 +24,7 @@
SwitchToPrevious,
)
from OTAnalytics.application.use_cases.config import SaveOtconfig
from OTAnalytics.application.use_cases.config_has_changed import ConfigHasChanged
from OTAnalytics.application.use_cases.create_events import (
CreateEvents,
CreateIntersectionEvents,
Expand All @@ -34,8 +36,13 @@
)
from OTAnalytics.application.use_cases.flow_repository import AddFlow
from OTAnalytics.application.use_cases.generate_flows import GenerateFlows
from OTAnalytics.application.use_cases.load_otconfig import LoadOtconfig
from OTAnalytics.application.use_cases.load_otflow import LoadOtflow
from OTAnalytics.application.use_cases.load_track_files import LoadTrackFiles
from OTAnalytics.application.use_cases.quick_save_configuration import (
QuickSaveConfiguration,
)
from OTAnalytics.application.use_cases.save_otflow import SaveOtflow
from OTAnalytics.application.use_cases.section_repository import (
AddSection,
GetSectionOffset,
Expand Down Expand Up @@ -90,6 +97,7 @@ def __init__(
track_view_state: TrackViewState,
section_state: SectionState,
flow_state: FlowState,
file_state: FileState,
tracks_metadata: TracksMetadata,
videos_metadata: VideosMetadata,
action_state: ActionState,
Expand All @@ -105,17 +113,23 @@ def __init__(
clear_all_events: ClearAllEvents,
start_new_project: StartNewProject,
project_updater: ProjectUpdater,
save_otconfig: SaveOtconfig,
load_track_files: LoadTrackFiles,
enable_filter_by_date: EnableFilterTrackByDate,
previous_frame: SwitchToPrevious,
next_frame: SwitchToNext,
switch_event: SwitchToEvent,
save_otflow: SaveOtflow,
quick_save_configuration: QuickSaveConfiguration,
load_otconfig: LoadOtconfig,
config_has_changed: ConfigHasChanged,
) -> None:
self._datastore: Datastore = datastore
self.track_state: TrackState = track_state
self.track_view_state: TrackViewState = track_view_state
self.section_state: SectionState = section_state
self.flow_state: FlowState = flow_state
self.file_state = file_state
self._tracks_metadata = tracks_metadata
self._videos_metadata = videos_metadata
self.action_state = action_state
Expand All @@ -128,9 +142,7 @@ def __init__(
self._clear_all_events = clear_all_events
self._export_counts = export_counts
self._project_updater = project_updater
self._save_otconfig = SaveOtconfig(
datastore, config_parser=datastore._config_parser
)
self._save_otconfig = save_otconfig
self._create_events = create_events
self._load_otflow = load_otflow
self._start_new_project = start_new_project
Expand All @@ -145,6 +157,10 @@ def __init__(
self._switch_previous = previous_frame
self._switch_next = next_frame
self._switch_event = switch_event
self._save_otflow = save_otflow
self._quick_save_configuration = quick_save_configuration
self._load_otconfig = load_otconfig
self._config_has_changed = config_has_changed

def connect_observers(self) -> None:
"""
Expand Down Expand Up @@ -247,7 +263,7 @@ def save_otconfig(self, file: Path) -> None:
self._save_otconfig(file)

def load_otconfig(self, file: Path) -> None:
self._datastore.load_otconfig(file)
self._load_otconfig.load(file)

def add_tracks_of_files(self, track_files: list[Path]) -> None:
"""
Expand Down Expand Up @@ -359,7 +375,7 @@ def save_otflow(self, file: Path) -> None:
Args:
file (Path): file to save the flows and sections to
"""
self._datastore.save_flow_file(file)
self._save_otflow.save(file)

def get_image_of_track(self, track_id: TrackId) -> Optional[TrackImage]:
"""
Expand Down Expand Up @@ -600,6 +616,12 @@ def update_project_start_date(self, start_date: datetime | None) -> None:
def get_track_repository_size(self) -> int:
return self._track_repository_size.get()

def quick_save_configuration(self) -> None:
self._quick_save_configuration.save()

def config_has_changed(self) -> bool:
return self._config_has_changed.has_changed()


class MissingTracksError(Exception):
pass
69 changes: 0 additions & 69 deletions OTAnalytics/application/datastore.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from pathlib import Path
from typing import Iterable, Optional, Sequence, Tuple

from OTAnalytics.application.parser.config_parser import ConfigParser
from OTAnalytics.application.project import Project
from OTAnalytics.application.use_cases.export_events import EventListExporter
from OTAnalytics.domain.event import Event, EventRepository
Expand Down Expand Up @@ -81,41 +80,6 @@ def parse(self, file: Path) -> TrackParseResult:
raise NotImplementedError


class FlowParser(ABC):
@abstractmethod
def parse(self, file: Path) -> tuple[Sequence[Section], Sequence[Flow]]:
pass

@abstractmethod
def parse_content(
self,
section_content: list[dict],
flow_content: list[dict],
) -> tuple[Sequence[Section], Sequence[Flow]]:
pass

@abstractmethod
def parse_section(self, entry: dict) -> Section:
pass

@abstractmethod
def serialize(
self,
sections: Iterable[Section],
flows: Iterable[Flow],
file: Path,
) -> None:
pass

@abstractmethod
def convert(
self,
sections: Iterable[Section],
flows: Iterable[Flow],
) -> dict[str, list[dict]]:
pass


class EventListParser(ABC):
@abstractmethod
def serialize(
Expand Down Expand Up @@ -216,10 +180,6 @@ def parse(
pass


class NoSectionsToSave(Exception):
pass


class Datastore:
"""
Central element to hold data in the application.
Expand All @@ -231,7 +191,6 @@ def __init__(
track_file_repository: TrackFileRepository,
track_parser: TrackParser,
section_repository: SectionRepository,
flow_parser: FlowParser,
flow_repository: FlowRepository,
event_repository: EventRepository,
event_list_parser: EventListParser,
Expand All @@ -240,10 +199,8 @@ def __init__(
video_parser: VideoParser,
track_video_parser: TrackVideoParser,
progressbar: ProgressbarBuilder,
config_parser: ConfigParser,
) -> None:
self._track_parser = track_parser
self._flow_parser = flow_parser
self._event_list_parser = event_list_parser
self._video_parser = video_parser
self._track_video_parser = track_video_parser
Expand All @@ -255,7 +212,6 @@ def __init__(
self._video_repository = video_repository
self._track_to_video_repository = track_to_video_repository
self._progressbar = progressbar
self._config_parser = config_parser
self.project = Project(name="", start_date=None)

def register_video_observer(self, observer: VideoListObserver) -> None:
Expand All @@ -279,14 +235,6 @@ def register_sections_observer(self, observer: SectionListObserver) -> None:
"""
self._section_repository.register_sections_observer(observer)

def load_otconfig(self, file: Path) -> None:
self.clear_repositories()
config = self._config_parser.parse(file)
self.project = config.project
self._video_repository.add_all(config.videos)
self._section_repository.add_all(config.sections)
self._flow_repository.add_all(config.flows)

def clear_repositories(self) -> None:
self._event_repository.clear()
self._section_repository.clear()
Expand Down Expand Up @@ -341,23 +289,6 @@ def delete_all_tracks(self) -> None:
"""Delete all tracks in repository."""
self._track_repository.clear()

def save_flow_file(self, file: Path) -> None:
"""
Save the flows and sections from the repositories into a file.
Args:
file (Path): file to save the flows and sections to
"""
if sections := self._section_repository.get_all():
flows = self._flow_repository.get_all()
self._flow_parser.serialize(
sections=sections,
flows=flows,
file=file,
)
else:
raise NoSectionsToSave()

def get_all_sections(self) -> list[Section]:
return self._section_repository.get_all()

Expand Down
19 changes: 17 additions & 2 deletions OTAnalytics/application/parser/config_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def parse(
self,
file: Path,
) -> OtConfig:
pass
raise NotImplementedError

@abstractmethod
def serialize(
Expand All @@ -57,4 +57,19 @@ def serialize(
flows: Iterable[Flow],
file: Path,
) -> None:
pass
raise NotImplementedError

@abstractmethod
def convert(
self,
project: Project,
video_files: Iterable[Video],
sections: Iterable[Section],
flows: Iterable[Flow],
file: Path,
) -> dict:
raise NotImplementedError


class StartDateMissing(Exception):
pass
4 changes: 4 additions & 0 deletions OTAnalytics/application/parser/deserializer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from pathlib import Path
from typing import Callable

Deserializer = Callable[[Path], dict]
41 changes: 41 additions & 0 deletions OTAnalytics/application/parser/flow_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from abc import ABC, abstractmethod
from pathlib import Path
from typing import Iterable, Sequence

from OTAnalytics.domain.flow import Flow
from OTAnalytics.domain.section import Section


class FlowParser(ABC):
@abstractmethod
def parse(self, file: Path) -> tuple[Sequence[Section], Sequence[Flow]]:
pass

@abstractmethod
def parse_content(
self,
section_content: list[dict],
flow_content: list[dict],
) -> tuple[Sequence[Section], Sequence[Flow]]:
pass

@abstractmethod
def parse_section(self, entry: dict) -> Section:
pass

@abstractmethod
def serialize(
self,
sections: Iterable[Section],
flows: Iterable[Flow],
file: Path,
) -> None:
pass

@abstractmethod
def convert(
self,
sections: Iterable[Section],
flows: Iterable[Flow],
) -> dict[str, list[dict]]:
pass
14 changes: 4 additions & 10 deletions OTAnalytics/application/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,13 @@
START_DATE: str = "start_date"


class StartDateMissing(Exception):
pass


@dataclass
class Project:
name: str
start_date: Optional[datetime]

def to_dict(self) -> dict:
if self.start_date:
return {
NAME: self.name,
START_DATE: self.start_date.timestamp(),
}
raise StartDateMissing()
return {
NAME: self.name,
START_DATE: self.start_date.timestamp() if self.start_date else None,
}
Loading

0 comments on commit 0e61fc3

Please sign in to comment.