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

Add events tab #72

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
2 changes: 2 additions & 0 deletions wazp/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from dash import Dash, dcc, html

import wazp.callbacks.dashboard as dashboard
import wazp.callbacks.events as events
import wazp.callbacks.home as home
import wazp.callbacks.metadata as metadata
import wazp.callbacks.roi as roi
Expand Down Expand Up @@ -106,6 +107,7 @@
home.get_callbacks(app)
metadata.get_callbacks(app)
roi.get_callbacks(app)
events.get_callbacks(app)
dashboard.get_callbacks(app)


Expand Down
197 changes: 197 additions & 0 deletions wazp/callbacks/events.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import dash
from dash import Input, Output, State


def get_callbacks(app: dash.Dash) -> None:
"""
Return all callback functions for the events tab.

Parameters
----------
app : dash.Dash
Dash app object for which these callbacks are defined
"""

@app.callback(
[
Output("events-video-select", "options"),
Output("events-video-select", "value"),
],
Input("session-storage", "data"),
)
def update_events_video_select(
app_storage: dict,
) -> tuple[list, str]:
"""Update the video selection dropdown with the videos defined in
the project configuration file.

Parameters
----------
app_storage : dict
data held in temporary memory storage,
accessible to all tabs in the app

Returns
-------
options : list
list of dictionaries with the following keys and values:
- 'label': video name
- 'value': video name
value : str
Currently selected video name
"""

if "video_paths" in app_storage:
options = [
{"label": v, "value": v} for v in app_storage["video_paths"]
]
value = options[0]["value"]
return options, value
else:
return dash.no_update, dash.no_update

@app.callback(
[
Output("event-select", "options"),
Output("event-select", "value"),
],
Input("session-storage", "data"),
)
def update_event_select_options(
app_storage: dict,
) -> tuple[list, str]:
"""Update the options of the event select dropdown with
the event tags defined in the project configuration file.

Parameters
----------
app_storage : dict
data held in temporary memory storage,
accessible to all tabs in the app

Returns
-------
options : list
list of dictionaries with the following keys and values:
- 'label': event tag
- 'value': event tag
value : str
Currently selected event tag
"""
if "config" in app_storage.keys():
# Get ROI names from stored config
config = app_storage["config"]
event_tags = config["event_tags"]
options = [{"label": t, "value": t} for t in event_tags]
value = event_tags[0]
return options, value
else:
return dash.no_update, dash.no_update

@app.callback(
Output("tag-event-button", "disabled"),
Input("event-select", "value"),
)
def disable_tag_event_button(
event_tag: str,
) -> bool:
"""Disable the tag event button if the event tag is empty

Parameters
----------
event_tag : str
Currently selected event tag

Returns
-------
disabled : bool
True if the tag event button should be disabled, False otherwise
"""
if event_tag == "" or event_tag is None:
return True
else:
return False

@app.callback(
Output("events-storage", "data"),
Input("tag-event-button", "n_clicks"),
[
State("frame-index-input", "value"),
State("events-video-select", "value"),
State("event-select", "value"),
State("events-storage", "data"),
],
)
def update_events_storage(
n_clicks: int,
frame_index: int,
video_name: str,
event_tag: str,
events_storage: dict,
) -> dict:
"""Update the events storage with the currently selected event tag
and frame index, when the tag event button is clicked.

Parameters
----------
n_clicks : int
Number of times the tag event button has been clicked
frame_index : int
Currently selected frame index
video_name : str
Currently selected video name
event_tag : str
Currently selected event tag
events_storage : dict
Dictionary storing event tags for each video.

Returns
-------
events_storage : dict
data held in temporary memory storage,
accessible to all tabs in the app
"""
if n_clicks > 0:
if video_name not in events_storage.keys():
events_storage[video_name] = dict()
events_storage[video_name][event_tag] = frame_index
return events_storage

@app.callback(
Output("events-table", "data"),
[
Input("events-video-select", "value"),
Input("events-storage", "data"),
],
)
def update_events_table(
video_name: str,
events_storage: dict,
) -> list[dict]:
"""Update the events table based on the stored events.

Parameters
----------
video_name : str
Currently selected video name
events_storage : dict
Dictionary storing event tags for each video.

Returns
-------
data : list[dict]
list of dictionaries with the following keys and values:
- 'tag': event tag
- 'frame index': frame index
- 'seconds': frame index converted to seconds, based on the
video FPS
"""
fps = 40
rows = []
if video_name in events_storage.keys():
for event, frame_idx in events_storage[video_name].items():
sec = frame_idx / fps
rows.append(
{"tag": event, "frame index": frame_idx, "seconds": sec}
)
return rows
17 changes: 17 additions & 0 deletions wazp/callbacks/home.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import base64
import pathlib as pl
from typing import Any

import dash
import yaml
from dash import Input, Output, State

from wazp import utils


def get_callbacks(app: dash.Dash) -> None:
"""Return all callback functions for the home tab.
Expand Down Expand Up @@ -47,6 +50,8 @@ def save_input_config_to_storage(
- 'config': a dict with the project configuration parameters
- 'metadata_fields': a dict with a set of attributes (description, type...)
for each metadata field
- 'video_paths': a dict with the path to the video file for each video
(key: video name, value: video path)
up_message_state : bool
visibility of the upload message
output_message : str
Expand All @@ -71,10 +76,22 @@ def save_input_config_to_storage(
with open(config["metadata_fields_file_path"]) as mdf:
metadata_fields_dict = yaml.safe_load(mdf)

# get video paths from the videos directory
video_paths = utils.get_video_paths_from_folder(
pl.Path(config["videos_dir_path"]),
video_extensions=[".mp4", ".avi"],
)
video_paths.sort()
# store video paths as a dict with video names as keys
video_paths_dict = {
v.name: v.absolute().as_posix() for v in video_paths
}

# bundle data
data_to_store = {
"config": config,
"metadata_fields": metadata_fields_dict,
"video_paths": video_paths_dict,
}

# output message
Expand Down
18 changes: 3 additions & 15 deletions wazp/callbacks/roi.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,26 +54,14 @@ def update_video_select_options(
str
value of the first video in the list
"""
if "config" in app_storage.keys():
# Get videos directory from stored config
config = app_storage["config"]
videos_dir = config["videos_dir_path"]
# get all videos in the videos directory
video_paths = []
for video_type in VIDEO_TYPES:
video_paths += [
p for p in pl.Path(videos_dir).glob(f"*{video_type}")
]
video_paths.sort()
video_names = [p.name for p in video_paths]
video_paths_str = [p.absolute().as_posix() for p in video_paths]
if "video_paths" in app_storage.keys():
# Video names become the labels and video paths the values
# of the video select dropdown
options = [
{"label": v, "value": p}
for v, p in zip(video_names, video_paths_str)
for v, p in app_storage["video_paths"].items()
]
value = video_paths_str[0]
value = options[0]["value"]
return options, value
else:
return dash.no_update, dash.no_update
Expand Down
Loading