Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Option to keep already existing counts when loading other otevent #69

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions OTGroundTruther/gui/frame_treeview_counts.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ def add_next_column_sort_direction(self):

def refresh_treeview(self, count_repository: CountRepository) -> None:
self.delete(*self.get_children())
if not count_repository.get_all_as_dict():
return
selected_classes = self._presenter.get_selected_classes_from_gui()
for count in list(count_repository.get_all_as_dict().values()):
if count.get_road_user_class().get_name() in selected_classes:
Expand Down
92 changes: 91 additions & 1 deletion OTGroundTruther/gui/gui.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Any

import customtkinter as ctk
from CTkMessagebox import CTkMessagebox

from OTGroundTruther.gui.constants import PADX, PADY, tk_events
from OTGroundTruther.gui.frame_canvas import FrameCanvas
Expand All @@ -12,6 +13,32 @@
TITLE: str = "OTGroundTruther"
DELETE_BUTTON_TXT: str = "Delete"

SUBW_KEEPEXISTINGCOUNTS_TITLE: str = "Keep existing Counts?"
SUBW_KEEPEXISTINGCOUNTS_QUESTION: str = (
"Do you want to keep the already existing counts and sections?"
)
SUBW_KEEPEXISTINGCOUNTS_ICON: str = "question"
SUBW_KEEPEXISTINGCOUNTS_KEEP: str = "Yes"
SUBW_KEEPEXISTINGCOUNTS_CLEAR: str = "No"

SUBW_ENTERSUFFIXCOUNTS_TITLE: str = "Suffix for the counts of the file"
SUBW_ENTERSUFFIXCOUNTS_INSTRUCTION: str = "Enter a suffix for the counts of the file."


SUBW_SECTIONS_NOT_COMPATIBLE_TITLE: str = "Sections are not compatible."
SUBW_SECTIONS_NOT_COMPATIBLE_INFO: str = (
"The sections from the file are not compatible with the existing "
+ "sections. Therefore the existing sections and counts got deleted."
)
SUBW_SECTIONS_NOT_COMPATIBLE_ICON: str = "info"

SUBW_COUNTS_NOT_COMPATIBLE_TITLE: str = "Counts are not compatible."
SUBW_COUNTS_NOT_COMPATIBLE_INFO: str = (
"Duplication of at least one counting ID. All already existing"
+ " counts have been removed."
)
SUBW_COUNTS_NOT_COMPATIBLE_ICON: str = "info"


class Gui(ctk.CTk):
def __init__(self, presenter: PresenterInterface, **kwargs: Any) -> None:
Expand Down Expand Up @@ -46,13 +73,51 @@ def _place_widgets(self) -> None:
side=ctk.RIGHT, fill=ctk.BOTH, expand=True, padx=PADX, pady=PADY
)

def build_key_assignment_window(self, key_assignment_text: dict[str, str]):
def build_key_assignment_window(self, key_assignment_text: dict[str, str]) -> None:
self.key_assigment_window = KeyAssignmentWindow(
master=self,
key_assignment_text=key_assignment_text,
presenter=self._presenter,
)

def ask_if_keep_existing_counts(self) -> bool:
msg = CTkMessagebox(
master=self,
title=SUBW_KEEPEXISTINGCOUNTS_TITLE,
message=SUBW_KEEPEXISTINGCOUNTS_QUESTION,
icon=SUBW_KEEPEXISTINGCOUNTS_ICON,
option_1=SUBW_KEEPEXISTINGCOUNTS_CLEAR,
option_2=SUBW_KEEPEXISTINGCOUNTS_KEEP,
)
keep_existing = msg.get()
if keep_existing == SUBW_KEEPEXISTINGCOUNTS_KEEP:
return True
else:
return False

def get_new_suffix_for_new_counts(self) -> None:
self.subwindow = EnteringStringSubwindow(
self,
title=SUBW_ENTERSUFFIXCOUNTS_TITLE,
label=SUBW_ENTERSUFFIXCOUNTS_INSTRUCTION,
)

def inform_user_sections_not_compatible(self) -> None:
self.subwindow = CTkMessagebox(
master=self,
title=SUBW_SECTIONS_NOT_COMPATIBLE_TITLE,
message=SUBW_SECTIONS_NOT_COMPATIBLE_INFO,
icon=SUBW_SECTIONS_NOT_COMPATIBLE_ICON,
)

def inform_user_counts_not_compatible(self) -> None:
self.subwindow = CTkMessagebox(
master=self,
title=SUBW_COUNTS_NOT_COMPATIBLE_TITLE,
message=SUBW_COUNTS_NOT_COMPATIBLE_INFO,
icon=SUBW_COUNTS_NOT_COMPATIBLE_ICON,
)


class GuiEventTranslator:
def __init__(self, gui: Gui, presenter: PresenterInterface):
Expand All @@ -66,3 +131,28 @@ def _bind_events(self) -> None:

def _on_delete_key(self, event: Any) -> None:
self._presenter.delete_selected_counts()


class EnteringStringSubwindow(ctk.CTkToplevel):
def __init__(self, gui: Gui, title: str, label: str) -> None:
super().__init__(gui)
self._gui = gui
self.title(title)
ctk.CTkLabel(self, text=label).pack(pady=0)
self.entry = ctk.CTkEntry(self)
self.entry.pack(pady=5)
ctk.CTkButton(self, text="OK", command=self._get_suffix_and_add_counts).pack(
pady=10
)
self.protocol("WM_DELETE_WINDOW", self.on_close)
self.transient(gui)
self.grab_set()
gui.wait_window(self)

def _get_suffix_and_add_counts(self) -> None:
user_input = self.entry.get()
self.destroy()
self._gui._presenter.add_new_counts(keep_existing_s_c=True, suffix=user_input)

def on_close(self):
self.destroy()
4 changes: 3 additions & 1 deletion OTGroundTruther/gui/menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ def __init__(self, presenter: PresenterInterface, **kwargs: Any):
def _get_and_place_widgets(self) -> None:
self.add_command(label="Load Videos", command=self._presenter.load_video_files)
self.add_command(label="Load otflow", command=self._presenter.load_otflow)
self.add_command(label="Load Events", command=self._presenter.load_events)
self.add_command(
label="Load Events", command=self._presenter.load_otevents_otgtevents
)
self.add_command(label="Save Events", command=self._presenter.save_events)


Expand Down
6 changes: 5 additions & 1 deletion OTGroundTruther/gui/presenter_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ def load_otflow(self) -> None:
raise NotImplementedError

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

@abstractmethod
def add_new_counts(self, keep_existing_s_c: bool, suffix: str) -> None:
raise NotImplementedError

@abstractmethod
Expand Down
27 changes: 22 additions & 5 deletions OTGroundTruther/model/count.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,11 @@ def to_event_list(self) -> list[EventForParsingSerializing]:
event_list.append(event_for_save)
return event_list

def from_event_list(self, event_list: list[EventForParsingSerializing]) -> None:
def event_list_to_count_dict(
self,
event_list: list[EventForParsingSerializing],
suffix: str,
) -> tuple[bool, dict[str, Count]]:
"""
create count list from event list and the suitable list of the object ids
set current id = 0 (only important for id naming of new events later) if the
Expand All @@ -297,20 +301,33 @@ def from_event_list(self, event_list: list[EventForParsingSerializing]) -> None:
event_list (list[Event_For_Saving]): List of events.
"""
events, classes = self._get_events_and_classes_by_id(event_list)

self.clear()
counts = {}
for id_ in events.keys():
if len(events[id_]) >= 2:
self._counts[id_] = Count(
road_user_id=id_,
counts[id_ + suffix] = Count(
road_user_id=id_ + suffix,
events=events[id_],
road_user_class=classes[id_],
)
else:
continue # TODO: Store in "SingleEventRepository"
compatible = not self.ids_already_exist_in_counts(counts=counts)
return compatible, counts

def add_new_counts(
self,
new_counts: dict[str, Count],
keep_existing_counts: bool,
) -> None:
if not keep_existing_counts:
self.clear()
self._counts = self._counts | new_counts
if len(self._counts.keys()) > 0:
self.set_current_id(list(self._counts.keys())[-1])

def ids_already_exist_in_counts(self, counts: dict[str, Count]):
return bool(set(counts.keys()) & set(self._counts.keys()))

def _get_events_and_classes_by_id(
self, event_list: list[EventForParsingSerializing]
) -> tuple[dict[str, list[Event]], dict[str, RoadUserClass]]:
Expand Down
36 changes: 28 additions & 8 deletions OTGroundTruther/model/model.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import datetime as dt
from pathlib import Path
from typing import Iterable, Sequence

from OTGroundTruther.gui.constants import tk_events
from OTGroundTruther.gui.key_assignment import (
Expand Down Expand Up @@ -65,21 +66,40 @@ def load_videos_from_files(self, files: list[Path]):
self._video_repository.add_all(videos)
print(f"Videos loaded: {files}")

def read_sections_from_file(self, file: Path) -> None:
self._section_repository.clear()
def read_sections_from_file(self, file: Path) -> tuple[bool, Sequence[LineSection]]:
self.last_file_path = file
sections, otanalytics_file_content = self._section_parser.parse(file=file)
self._section_repository.add_all(sections)
self._section_repository.set_otanalytics_file_content(otanalytics_file_content)
print(f"Sections read from {file}")
if self._count_repository.get_all_as_dict():
print(f"Check compatibility sections {file}")
compatible, gates_already_existed = (
self._section_repository.check_if_compatible(sections)
)
else:
compatible = True
return compatible, sections

def read_events_from_file(self, file: Path) -> None:
def add_sections(
self, sections: Iterable[LineSection], keep_existing_sections: bool
):
if not keep_existing_sections:
self._section_repository.clear()
self._section_repository.add_all(sections)
print("Sections added")

def read_events_from_file(self, suffix: str) -> tuple[bool, dict[str, Count]]:
event_list = self._eventlistparser.parse(
events_file=file,
events_file=self.last_file_path,
sections=self._section_repository.to_dict(),
valid_road_user_classes=self._valid_road_user_classes,
)
self._count_repository.from_event_list(event_list)
print(f"Events read from {file}")

(compatible, counts) = self._count_repository.event_list_to_count_dict(
event_list=event_list,
suffix=suffix,
)
print(f"Events read from {self.last_file_path}")
return (compatible, counts)

def write_events_and_sections_to_file(
self,
Expand Down
20 changes: 19 additions & 1 deletion OTGroundTruther/model/section.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,23 @@ def __init__(self) -> None:
self._sections: dict[str, LineSection] = {}
self._otanalytics_file_content: dict = {}

def check_if_compatible(
self, sections: Iterable[LineSection]
) -> tuple[bool, dict[str, dict[str, bool]]]:
gates_already_existed = {}
compatible = True
for section in sections:

id_already_exist = section.id in list(self._sections.keys())
is_equal = id_already_exist and self._sections[section.id] == section
if id_already_exist and not is_equal:
compatible = False
gates_already_existed[section.id] = {
"id_already_exist": id_already_exist,
"is_equal": is_equal,
}
return (compatible, gates_already_existed)

def add_all(self, sections: Iterable[LineSection]) -> None:
"""Add several sections at once to the repository.

Expand All @@ -130,9 +147,10 @@ def add_all(self, sections: Iterable[LineSection]) -> None:
"""
for section in sections:
self._add(section)

# TODO: Check if ellipses around different sections touch each other

def _add(self, section: LineSection) -> None:
def _add(self, section: LineSection):
"""Internal method to add sections without notifying observers.

Args:
Expand Down
6 changes: 5 additions & 1 deletion OTGroundTruther/presenter/model_initializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@ def _prefill_count_repository(self, file: Path) -> None:
sections=self._section_repository.to_dict(),
valid_road_user_classes=self._valid_road_user_classes,
)
self._count_repository.from_event_list(event_list)
(compatible, counts) = self._count_repository.event_list_to_count_dict(
event_list=event_list,
suffix="",
)
self._count_repository.add_new_counts(counts, keep_existing_counts=False)

def get(self) -> Model:
return self._model
Loading