Skip to content

Commit 889d48c

Browse files
KIRA009abrichr
andauthored
feat: Delete/transfer video (#696)
* feat: Delete recording files when deleting recording * fix: Fix share recording feature * feat: Share related files when transferring recording * fix: Fix tests and lint code --------- Co-authored-by: Richard Abrich <[email protected]>
1 parent 6492120 commit 889d48c

File tree

6 files changed

+94
-21
lines changed

6 files changed

+94
-21
lines changed

openadapt/db/crud.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,9 +268,16 @@ def delete_recording(session: SaSession, recording: Recording) -> None:
268268
session (sa.orm.Session): The database session.
269269
recording (Recording): The recording object.
270270
"""
271+
recording_timestamp = recording.timestamp
271272
session.query(Recording).filter(Recording.id == recording.id).delete()
272273
session.commit()
273274

275+
utils.delete_performance_plot(recording_timestamp)
276+
277+
from openadapt.video import delete_video_file
278+
279+
delete_video_file(recording_timestamp)
280+
274281

275282
def get_all_recordings(session: SaSession) -> list[Recording]:
276283
"""Get all recordings.

openadapt/db/db.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -159,18 +159,15 @@ def genericize_datatypes(
159159
# Insert the recording into the target recording table
160160
tgt_conn.execute(tgt_recording_table.insert().values(src_recording))
161161

162-
# Get the timestamp from the source recording
163-
src_timestamp = src_recording["timestamp"]
164-
165-
# Copy data from tables with the same timestamp
162+
# Copy data from tables with the same recording_id
166163
for table in src_metadata.sorted_tables:
167164
if (
168165
table.name not in exclude_tables
169166
and "recording_timestamp" in table.columns.keys()
170167
):
171-
# Select data from source table with the same timestamp
168+
# Select data from source table with the same recording_id
172169
src_select = table.select().where(
173-
table.c.recording_timestamp == src_timestamp
170+
table.c.recording_id == recording_id
174171
)
175172
src_rows = src_conn.execute(src_select).fetchall()
176173

openadapt/share.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
from openadapt import db, utils
2121
from openadapt.config import RECORDING_DIR_PATH
22+
from openadapt.db import crud
23+
from openadapt.video import get_video_file_path
2224

2325
LOG_LEVEL = "INFO"
2426
utils.configure_logging(logger, LOG_LEVEL)
@@ -33,11 +35,12 @@ def export_recording_to_folder(recording_id: int) -> None:
3335
Returns:
3436
str: The path of the created zip file.
3537
"""
38+
# Create the directory if it doesn't exist
39+
os.makedirs(RECORDING_DIR_PATH, exist_ok=True)
40+
3641
recording_db_path = db.export_recording(recording_id)
3742

3843
assert recording_db_path, recording_db_path
39-
# Create the directory if it doesn't exist
40-
os.makedirs(RECORDING_DIR_PATH, exist_ok=True)
4144

4245
# Get the timestamp from the recording_db_path
4346
timestamp = extract_timestamp_from_filename(os.path.basename(recording_db_path))
@@ -49,9 +52,26 @@ def export_recording_to_folder(recording_id: int) -> None:
4952
zip_filename = f"recording_{recording_id}_{timestamp}.zip"
5053
zip_path = os.path.join(RECORDING_DIR_PATH, zip_filename)
5154

52-
# Create an in-memory zip file and add the db file
53-
with ZipFile(zip_path, "w", ZIP_DEFLATED, compresslevel=9) as zip_file:
54-
zip_file.write(recording_db_path, arcname=db_filename)
55+
zipfile = ZipFile(zip_path, "w", ZIP_DEFLATED, compresslevel=9)
56+
zipfile.write(recording_db_path, arcname=db_filename)
57+
58+
with crud.get_new_session(read_only=True) as session:
59+
recording = crud.get_recording_by_id(session, recording_id)
60+
recording_timestamp = recording.timestamp
61+
62+
performance_plot_path = utils.get_performance_plot_file_path(recording_timestamp)
63+
if os.path.exists(performance_plot_path):
64+
zipfile.write(
65+
performance_plot_path, arcname=os.path.basename(performance_plot_path)
66+
)
67+
logger.info(f"added {performance_plot_path=}")
68+
69+
video_file_path = get_video_file_path(recording_timestamp)
70+
if os.path.exists(video_file_path):
71+
zipfile.write(video_file_path, arcname=os.path.basename(video_file_path))
72+
logger.info(f"added {video_file_path=}")
73+
74+
zipfile.close()
5575

5676
logger.info(f"created {zip_path=}")
5777

openadapt/utils.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,22 @@ def get_strategy_class_by_name() -> dict:
709709
return class_by_name
710710

711711

712+
def get_performance_plot_file_path(recording_timestamp: float) -> str:
713+
"""Get the filename for the performance plot.
714+
715+
Args:
716+
recording_timestamp (float): The timestamp of the recording.
717+
718+
Returns:
719+
str: The filename.
720+
"""
721+
os.makedirs(PERFORMANCE_PLOTS_DIR_PATH, exist_ok=True)
722+
723+
fname_parts = ["performance", str(recording_timestamp)]
724+
fname = "-".join(fname_parts) + ".png"
725+
return os.path.join(PERFORMANCE_PLOTS_DIR_PATH, fname)
726+
727+
712728
def plot_performance(
713729
recording: Recording = None,
714730
view_file: bool = False,
@@ -788,10 +804,7 @@ def plot_performance(
788804

789805
# TODO: add PROC_WRITE_BY_EVENT_TYPE
790806
if save_file:
791-
fname_parts = ["performance", str(recording.timestamp)]
792-
fname = "-".join(fname_parts) + ".png"
793-
os.makedirs(PERFORMANCE_PLOTS_DIR_PATH, exist_ok=True)
794-
fpath = os.path.join(PERFORMANCE_PLOTS_DIR_PATH, fname)
807+
fpath = get_performance_plot_file_path(recording.timestamp)
795808
logger.info(f"{fpath=}")
796809
plt.savefig(fpath)
797810
if view_file:
@@ -812,6 +825,19 @@ def plot_performance(
812825
)
813826

814827

828+
def delete_performance_plot(recording_timestamp: float) -> None:
829+
"""Delete the performance plot for the given recording timestamp.
830+
831+
Args:
832+
recording_timestamp (float): The timestamp of the recording.
833+
"""
834+
fpath = get_performance_plot_file_path(recording_timestamp)
835+
try:
836+
os.remove(fpath)
837+
except FileNotFoundError as exc:
838+
logger.warning(f"{exc=}")
839+
840+
815841
def strip_element_state(action_event: ActionEvent) -> ActionEvent:
816842
"""Strip the element state from the action event and its children.
817843

openadapt/video.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,20 @@ def get_video_file_path(recording_timestamp: float) -> str:
2828
)
2929

3030

31+
def delete_video_file(recording_timestamp: float) -> None:
32+
"""Deletes the video file corresponding to the given recording timestamp.
33+
34+
Args:
35+
recording_timestamp (float): The timestamp of the recording to delete.
36+
"""
37+
video_file_path = get_video_file_path(recording_timestamp)
38+
if os.path.exists(video_file_path):
39+
os.remove(video_file_path)
40+
logger.info(f"Deleted video file: {video_file_path}")
41+
else:
42+
logger.error(f"Video file not found: {video_file_path}")
43+
44+
3145
def initialize_video_writer(
3246
output_path: str,
3347
width: int,

tests/openadapt/test_share.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Module to test share.py."""
22

3+
from collections import namedtuple
34
from unittest.mock import patch
45
from zipfile import ZIP_DEFLATED, ZipFile
56
import os
@@ -26,12 +27,20 @@ def test_export_recording_to_folder() -> None:
2627
with open(recording_db_path, "w") as f:
2728
f.write("Recording data")
2829

29-
# Mock the db.export_recording() function to return the temporary file path
30-
with patch("openadapt.share.db.export_recording", return_value=recording_db_path):
31-
zip_file_path = share.export_recording_to_folder(recording_id)
32-
33-
assert zip_file_path is not None
34-
assert os.path.exists(zip_file_path)
30+
# Mock the crud.get_recording_by_id() function to return a recording object
31+
Recording = namedtuple("Recording", ["timestamp"])
32+
with patch(
33+
"openadapt.db.crud.get_recording_by_id",
34+
return_value=Recording(timestamp=193994394),
35+
):
36+
# Mock the db.export_recording() function to return the temporary file path
37+
with patch(
38+
"openadapt.share.db.export_recording", return_value=recording_db_path
39+
):
40+
zip_file_path = share.export_recording_to_folder(recording_id)
41+
42+
assert zip_file_path is not None
43+
assert os.path.exists(zip_file_path)
3544

3645
# Assert that the file is removed after calling export_recording_to_folder
3746
assert not os.path.exists(recording_db_path), "Temporary file was not removed."

0 commit comments

Comments
 (0)