patkit 0.22.2__tar.gz → 0.22.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {patkit-0.22.2 → patkit-0.22.3}/PKG-INFO +3 -3
- {patkit-0.22.2 → patkit-0.22.3}/README.md +1 -1
- {patkit-0.22.2 → patkit-0.22.3}/patkit/constants.py +15 -1
- {patkit-0.22.2 → patkit-0.22.3}/patkit/data_loader.py +5 -1
- {patkit-0.22.2 → patkit-0.22.3}/patkit/data_structures/metadata_classes.py +3 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/gui/__init__.py +3 -1
- {patkit-0.22.2 → patkit-0.22.3}/patkit/gui/annotator_window.py +11 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/gui/exercise_dialogs.py +120 -2
- {patkit-0.22.2 → patkit-0.22.3}/patkit/gui/image_save_dialog.py +14 -10
- {patkit-0.22.2 → patkit-0.22.3}/patkit/gui/plot_controller.py +18 -4
- {patkit-0.22.2 → patkit-0.22.3}/patkit/qt_annotator.py +91 -8
- {patkit-0.22.2 → patkit-0.22.3}/patkit/save_and_load/__init__.py +3 -0
- patkit-0.22.3/patkit/save_and_load/exercise_packaging.py +141 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/save_and_load/save.py +6 -1
- {patkit-0.22.2 → patkit-0.22.3}/pyproject.toml +1 -1
- {patkit-0.22.2 → patkit-0.22.3}/.gitignore +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/LICENSE.markdown +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/__init__.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/annotations/__init__.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/annotations/peaks.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/audio_processing/__init__.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/audio_processing/audio_processing.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/cli.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/cli_commands.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/computational.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/configuration/__init__.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/configuration/configuration.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/configuration/configuration_models.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/configuration/configuration_parsers.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/configuration/exclusion_list_functions.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/data_import/AAA_raw_ultrasound.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/data_import/AAA_recordings.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/data_import/AAA_splines.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/data_import/RASL_3D_ultrasound_recordings.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/data_import/__init__.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/data_import/audio.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/data_import/session_import_config.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/data_import/spline_import_config.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/data_import/three_dim_ultrasound.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/data_import/video.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/data_import/wav_recordings.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/data_processor.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/data_structures/__init__.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/data_structures/base_classes.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/data_structures/data_structures.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/default_configuration/patkit-data.yaml +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/default_configuration/patkit-gui.yaml +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/default_configuration/patkit-publish.yaml +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/default_configuration/patkit_logging_configuration.json +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/errors.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/export/__init__.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/export/images.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/export/metadata.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/export/modality_data.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/external_class_extensions/__init__.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/external_class_extensions/base_model_extensions.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/external_class_extensions/enum_extensions.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/get_modality_types.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/gui/audio_player.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/gui/boundary_animation.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/gui/dialog_template.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/gui/list_save_dialog.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/gui/list_selection_dialog.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/gui/ofbrowse.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/gui/player_controls.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/gui/replace_dialog.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/gui/select_scenario_dialog.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/import_formats/__init__.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/import_formats/avi_read.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/import_formats/dicom_ultra_read.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/import_formats/rasl_dat_to_wav.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/import_formats/splines_read.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/import_formats/ult_read.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/import_formats/wav_read.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/initialise.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/interpolate_raw_uti.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/interpreter.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/metrics/__init__.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/metrics/aggregate_image.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/metrics/calculate_aggregate_images.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/metrics/calculate_distance_matrices.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/metrics/calculate_intensity.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/metrics/calculate_pd.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/metrics/calculate_spline_metric.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/metrics/distance_matrix.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/metrics/downsample_metric.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/metrics/intensity.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/metrics/metrics_helpers.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/metrics/ofreg.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/metrics/pd.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/metrics/spline_metric.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/metrics/tongue_shape_analysis.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/modalities/__init__.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/modalities/recorded_metadata_classes.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/modalities/recorded_modalities.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/modalities/splines.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/patgrid.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/path_resolution.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/plot_and_publish/__init__.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/plot_and_publish/legend_generation.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/plot_and_publish/plot.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/plot_and_publish/plot_utilities.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/plot_and_publish/publish.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/save_and_load/load.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/save_and_load/save_and_load_schemas.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/simulation/__init__.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/simulation/contour_tools.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/simulation/metric_calculations.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/simulation/perturbation_series_plots.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/simulation/rays_on_contours.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/simulation/simulate.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/simulation/simulation_datastructures.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/simulation/simulation_plots.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/ui_callbacks.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/utility_functions/__init__.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/utility_functions/logging_helpers.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/utility_functions/path.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/utility_functions/processing_helpers.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/utility_functions/string_formatters.py +0 -0
- {patkit-0.22.2 → patkit-0.22.3}/patkit/utility_functions/types.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: patkit
|
|
3
|
-
Version: 0.22.
|
|
3
|
+
Version: 0.22.3
|
|
4
4
|
Summary: Phonetic Analysis ToolKit: Tools for processing phonetic data
|
|
5
5
|
Project-URL: bug_tracker, https://github.com/giuthas/patkit/issues
|
|
6
6
|
Project-URL: changelog, https://github.com/giuthas/patkit/blob/main/docs/Release_notes.markdown
|
|
@@ -718,7 +718,7 @@ Description-Content-Type: text/markdown
|
|
|
718
718
|
[](https://github.com/giuthas/patkit/actions/workflows/test.yaml)
|
|
719
719
|
# PATKIT - Phonetic Analysis ToolKIT
|
|
720
720
|
|
|
721
|
-

|
|
722
722
|
|
|
723
723
|
PATKIT provides tools for phonetic analysis of speech data. It includes a GUI
|
|
724
724
|
for manual assessment/analysis/annotation (see picture above), command line
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[](https://github.com/giuthas/patkit/actions/workflows/test.yaml)
|
|
2
2
|
# PATKIT - Phonetic Analysis ToolKIT
|
|
3
3
|
|
|
4
|
-

|
|
5
5
|
|
|
6
6
|
PATKIT provides tools for phonetic analysis of speech data. It includes a GUI
|
|
7
7
|
for manual assessment/analysis/annotation (see picture above), command line
|
|
@@ -510,7 +510,21 @@ class DefaultCursorColors(str, Enum):
|
|
|
510
510
|
Default colors for GUI cursor and selection elements.
|
|
511
511
|
"""
|
|
512
512
|
SELECTION = "deepskyblue"
|
|
513
|
-
PLAYBACK = "
|
|
513
|
+
PLAYBACK = "#FD7400"
|
|
514
|
+
|
|
515
|
+
def __str__(self) -> str:
|
|
516
|
+
"""
|
|
517
|
+
Ensure f-strings and print() output the string value.
|
|
518
|
+
"""
|
|
519
|
+
return self.value
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
class ColorPalettes(str, Enum):
|
|
523
|
+
"""
|
|
524
|
+
Colors for plotting lines.
|
|
525
|
+
"""
|
|
526
|
+
COLORBLIND_10 = 'tableau-colorblind10'
|
|
527
|
+
# COLORBLIND_6 = 'viscid-colorblind'
|
|
514
528
|
|
|
515
529
|
def __str__(self) -> str:
|
|
516
530
|
"""
|
|
@@ -148,9 +148,13 @@ def read_recorded_session_from_dir(
|
|
|
148
148
|
)
|
|
149
149
|
return session
|
|
150
150
|
|
|
151
|
+
# TODO 1.0: if Sessions are no longer the highest possible container tier,
|
|
152
|
+
# this logic needs to change.
|
|
151
153
|
file_info = FileInformation(
|
|
152
154
|
recorded_path=recorded_data_path,
|
|
153
|
-
recorded_meta_file=session_config_path.name
|
|
155
|
+
recorded_meta_file=session_config_path.name,
|
|
156
|
+
is_top_level_object=True,
|
|
157
|
+
)
|
|
154
158
|
if session_config_path.is_file():
|
|
155
159
|
session_config = load_session_config(
|
|
156
160
|
recorded_data_path, session_config_path)
|
|
@@ -69,6 +69,8 @@ class FileInformation:
|
|
|
69
69
|
Name of the patkit meta file, if it exists. Defaults to None.
|
|
70
70
|
patkit_path : Path | None
|
|
71
71
|
Path to the saved patkit data, if it exists. Defaults to None.
|
|
72
|
+
is_top_level_object : bool
|
|
73
|
+
Is this the top level container, by default False.
|
|
72
74
|
"""
|
|
73
75
|
recorded_data_file: str | None = None
|
|
74
76
|
recorded_meta_file: str | None = None
|
|
@@ -76,6 +78,7 @@ class FileInformation:
|
|
|
76
78
|
patkit_data_file: str | None = None
|
|
77
79
|
patkit_meta_file: str | None = None
|
|
78
80
|
patkit_path: Path | None = None
|
|
81
|
+
is_top_level_object: bool = False
|
|
79
82
|
|
|
80
83
|
@property
|
|
81
84
|
def basename(self) -> str:
|
|
@@ -34,7 +34,9 @@
|
|
|
34
34
|
from .annotator_window import UiMainWindow
|
|
35
35
|
from .audio_player import AudioPlayer
|
|
36
36
|
from .boundary_animation import BoundaryAnimator
|
|
37
|
-
from .exercise_dialogs import
|
|
37
|
+
from .exercise_dialogs import (
|
|
38
|
+
PackageExerciseDialog, NewAnswerDialog, NewExerciseDialog
|
|
39
|
+
)
|
|
38
40
|
from .image_save_dialog import ImageSaveDialog
|
|
39
41
|
from .list_save_dialog import ListSaveDialog
|
|
40
42
|
from .list_selection_dialog import ListSelectionDialog
|
|
@@ -275,6 +275,11 @@ class UiMainWindow(object):
|
|
|
275
275
|
self.action_new_exercise.setObjectName("action_new_exercise")
|
|
276
276
|
self.action_save_exercise = QtGui.QAction(main_window)
|
|
277
277
|
self.action_save_exercise.setObjectName("action_save_exercise")
|
|
278
|
+
self.action_package_exercise = QtGui.QAction(main_window)
|
|
279
|
+
self.action_package_exercise.setObjectName("action_package_exercise")
|
|
280
|
+
self.action_unpackage_exercise = QtGui.QAction(main_window)
|
|
281
|
+
self.action_unpackage_exercise.setObjectName(
|
|
282
|
+
"action_unpackage_exercise")
|
|
278
283
|
self.action_new_answer = QtGui.QAction(main_window)
|
|
279
284
|
self.action_new_answer.setObjectName("action_new_answer")
|
|
280
285
|
self.action_save_answer = QtGui.QAction(main_window)
|
|
@@ -289,6 +294,8 @@ class UiMainWindow(object):
|
|
|
289
294
|
|
|
290
295
|
self.menu_exercise.addAction(self.action_new_exercise)
|
|
291
296
|
self.menu_exercise.addAction(self.action_save_exercise)
|
|
297
|
+
self.menu_exercise.addAction(self.action_package_exercise)
|
|
298
|
+
self.menu_exercise.addAction(self.action_unpackage_exercise)
|
|
292
299
|
self.menu_exercise.addSeparator()
|
|
293
300
|
self.menu_exercise.addAction(self.action_new_answer)
|
|
294
301
|
self.menu_exercise.addAction(self.action_save_answer)
|
|
@@ -450,6 +457,10 @@ class UiMainWindow(object):
|
|
|
450
457
|
_translate("MainWindow", "Ctrl+Shift+N"))
|
|
451
458
|
self.action_save_exercise.setText(
|
|
452
459
|
_translate("MainWindow", "Save exercise"))
|
|
460
|
+
self.action_package_exercise.setText(
|
|
461
|
+
_translate("MainWindow", "Package exercise..."))
|
|
462
|
+
self.action_unpackage_exercise.setText(
|
|
463
|
+
_translate("MainWindow", "Unpackage exercise..."))
|
|
453
464
|
self.action_new_answer.setText(
|
|
454
465
|
_translate("MainWindow", "New answer..."))
|
|
455
466
|
self.action_new_answer.setShortcut(
|
|
@@ -36,8 +36,8 @@ Dialogs for Exercises and Answers.
|
|
|
36
36
|
from pathlib import Path
|
|
37
37
|
|
|
38
38
|
from PyQt6.QtWidgets import (
|
|
39
|
-
QComboBox, QDialog, QDialogButtonBox, QFileDialog, QHBoxLayout,
|
|
40
|
-
QLabel, QLineEdit, QSizePolicy, QVBoxLayout, QWidget
|
|
39
|
+
QCheckBox, QComboBox, QDialog, QDialogButtonBox, QFileDialog, QHBoxLayout,
|
|
40
|
+
QLabel, QLineEdit, QPushButton, QSizePolicy, QVBoxLayout, QWidget
|
|
41
41
|
)
|
|
42
42
|
|
|
43
43
|
from patkit.constants import ExerciseScrambler
|
|
@@ -188,3 +188,121 @@ class NewAnswerDialog(QDialog):
|
|
|
188
188
|
if dialog.exec() == QDialog.DialogCode.Rejected:
|
|
189
189
|
return None, None
|
|
190
190
|
return dialog.answer_name, dialog.author_name
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class PackageExerciseDialog(QDialog):
|
|
194
|
+
"""
|
|
195
|
+
Dialog for configuring an Exercise packaging.
|
|
196
|
+
|
|
197
|
+
This dialog allows the user to specify a package path and choose whether
|
|
198
|
+
to include the unscrambled Session TextGrids in the resulting archive.
|
|
199
|
+
"""
|
|
200
|
+
|
|
201
|
+
def __init__(
|
|
202
|
+
self,
|
|
203
|
+
parent: QWidget | None = None,
|
|
204
|
+
default_path: Path | None = None
|
|
205
|
+
) -> None:
|
|
206
|
+
"""
|
|
207
|
+
Initialize the PackageExerciseDialog.
|
|
208
|
+
|
|
209
|
+
Parameters
|
|
210
|
+
----------
|
|
211
|
+
parent : QWidget | None, optional
|
|
212
|
+
The parent widget, by default None.
|
|
213
|
+
default_path : Path | None, optional
|
|
214
|
+
The default file path to populate the dialog, by default None.
|
|
215
|
+
"""
|
|
216
|
+
super().__init__(parent=parent)
|
|
217
|
+
self.setWindowTitle("Package Exercise")
|
|
218
|
+
self.resize(700, 100)
|
|
219
|
+
|
|
220
|
+
self.package_path: Path | None = None
|
|
221
|
+
self.include_textgrids: bool = False
|
|
222
|
+
|
|
223
|
+
self.path_label = QLabel(text="Package Path:", parent=self)
|
|
224
|
+
self.path_field = QLineEdit(parent=self)
|
|
225
|
+
if default_path is not None:
|
|
226
|
+
self.path_field.setText(str(default_path))
|
|
227
|
+
|
|
228
|
+
self.browse_button = QPushButton(text="Browse...", parent=self)
|
|
229
|
+
self.browse_button.clicked.connect(slot=self._browse)
|
|
230
|
+
|
|
231
|
+
path_layout = QHBoxLayout()
|
|
232
|
+
path_layout.addWidget(self.path_label)
|
|
233
|
+
path_layout.addWidget(self.path_field)
|
|
234
|
+
path_layout.addWidget(self.browse_button)
|
|
235
|
+
|
|
236
|
+
self.textgrid_checkbox = QCheckBox(
|
|
237
|
+
text=(
|
|
238
|
+
"Include original TextGrids (this will make "
|
|
239
|
+
"them viewable when running the exercise)."
|
|
240
|
+
),
|
|
241
|
+
parent=self
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
dialog_buttons = (
|
|
245
|
+
QDialogButtonBox.StandardButton.Ok |
|
|
246
|
+
QDialogButtonBox.StandardButton.Cancel
|
|
247
|
+
)
|
|
248
|
+
self.ok_cancel_buttons = QDialogButtonBox(dialog_buttons)
|
|
249
|
+
self.ok_cancel_buttons.accepted.connect(slot=self._on_accepted)
|
|
250
|
+
self.ok_cancel_buttons.rejected.connect(slot=self.reject)
|
|
251
|
+
|
|
252
|
+
main_layout = QVBoxLayout(self)
|
|
253
|
+
main_layout.addLayout(path_layout)
|
|
254
|
+
main_layout.addWidget(self.textgrid_checkbox)
|
|
255
|
+
main_layout.addWidget(self.ok_cancel_buttons)
|
|
256
|
+
|
|
257
|
+
self.setMinimumWidth(450)
|
|
258
|
+
|
|
259
|
+
def _browse(self) -> None:
|
|
260
|
+
"""Open a file dialog to select the destination zip path."""
|
|
261
|
+
file_path, _ = QFileDialog.getSaveFileName(
|
|
262
|
+
parent=self,
|
|
263
|
+
caption="Select Packaging Destination",
|
|
264
|
+
directory=self.path_field.text(),
|
|
265
|
+
filter="Zip Files (*.zip)"
|
|
266
|
+
)
|
|
267
|
+
if file_path:
|
|
268
|
+
self.path_field.setText(file_path)
|
|
269
|
+
|
|
270
|
+
def _on_accepted(self) -> None:
|
|
271
|
+
"""Validate input and accept the dialog."""
|
|
272
|
+
path_text = self.path_field.text()
|
|
273
|
+
if not path_text:
|
|
274
|
+
self.reject()
|
|
275
|
+
return
|
|
276
|
+
|
|
277
|
+
self.package_path = Path(path_text)
|
|
278
|
+
self.include_textgrids = self.textgrid_checkbox.isChecked()
|
|
279
|
+
self.accept()
|
|
280
|
+
|
|
281
|
+
@staticmethod
|
|
282
|
+
def get_packaging_params(
|
|
283
|
+
parent: QWidget | None = None,
|
|
284
|
+
default_path: Path | None = None
|
|
285
|
+
) -> tuple[Path | None, bool]:
|
|
286
|
+
"""
|
|
287
|
+
Open the package dialog and query the user for packaging parameters.
|
|
288
|
+
|
|
289
|
+
Parameters
|
|
290
|
+
----------
|
|
291
|
+
parent : QWidget | None, optional
|
|
292
|
+
Parent window.
|
|
293
|
+
default_path : Path | None, optional
|
|
294
|
+
The default file path to populate the line edit.
|
|
295
|
+
|
|
296
|
+
Returns
|
|
297
|
+
-------
|
|
298
|
+
tuple[Path | None, bool]
|
|
299
|
+
The selected file path and boolean indicating if TextGrids
|
|
300
|
+
should be included. Path is None if the user cancelled.
|
|
301
|
+
"""
|
|
302
|
+
dialog = PackageExerciseDialog(
|
|
303
|
+
parent=parent, default_path=default_path
|
|
304
|
+
)
|
|
305
|
+
if dialog.exec() == QDialog.DialogCode.Rejected:
|
|
306
|
+
return None, False
|
|
307
|
+
|
|
308
|
+
return dialog.package_path, dialog.include_textgrids
|
|
@@ -34,12 +34,14 @@ import os
|
|
|
34
34
|
from pathlib import Path
|
|
35
35
|
|
|
36
36
|
from PyQt6 import QtCore
|
|
37
|
-
from PyQt6.QtGui import
|
|
37
|
+
from PyQt6.QtGui import QIcon
|
|
38
38
|
from PyQt6.QtWidgets import (
|
|
39
39
|
QCheckBox, QDialog, QDialogButtonBox, QFileDialog, QHBoxLayout, QLabel,
|
|
40
40
|
QLineEdit, QPushButton, QVBoxLayout, QWidget
|
|
41
41
|
)
|
|
42
42
|
|
|
43
|
+
# TODO 1.0: docstrings
|
|
44
|
+
|
|
43
45
|
|
|
44
46
|
class ImageSaveDialog(QDialog):
|
|
45
47
|
|
|
@@ -68,14 +70,14 @@ class ImageSaveDialog(QDialog):
|
|
|
68
70
|
option_box = None
|
|
69
71
|
if options is not None:
|
|
70
72
|
option_box = QHBoxLayout()
|
|
71
|
-
self.
|
|
73
|
+
self.checkboxes = {}
|
|
72
74
|
for option, checked in options.items():
|
|
73
75
|
checkbox = QCheckBox(option)
|
|
74
76
|
check = (QtCore.Qt.CheckState.Checked
|
|
75
77
|
if checked else QtCore.Qt.CheckState.Unchecked)
|
|
76
78
|
checkbox.setCheckState(check)
|
|
77
|
-
self.
|
|
78
|
-
option_box.addWidget(self.
|
|
79
|
+
self.checkboxes[option] = checkbox
|
|
80
|
+
option_box.addWidget(self.checkboxes[option])
|
|
79
81
|
path_and_name_box = QHBoxLayout()
|
|
80
82
|
self.path_label = QLabel(self)
|
|
81
83
|
self.path_label.setText("Path:")
|
|
@@ -110,7 +112,7 @@ class ImageSaveDialog(QDialog):
|
|
|
110
112
|
vbox.addLayout(option_box)
|
|
111
113
|
vbox.addLayout(path_and_name_box)
|
|
112
114
|
vbox.addWidget(self.ok_cancel_buttons)
|
|
113
|
-
|
|
115
|
+
|
|
114
116
|
self.setWindowTitle(self.name)
|
|
115
117
|
if icon:
|
|
116
118
|
self.setWindowIcon(icon)
|
|
@@ -118,19 +120,21 @@ class ImageSaveDialog(QDialog):
|
|
|
118
120
|
self.adjustSize()
|
|
119
121
|
|
|
120
122
|
def _browse(self):
|
|
121
|
-
|
|
123
|
+
# TODO 0.23: getSaveFileName doesn't necessarily match the purpose
|
|
124
|
+
# here?
|
|
125
|
+
path, something = QFileDialog.getSaveFileName(
|
|
122
126
|
parent=self,
|
|
123
|
-
caption="Select
|
|
127
|
+
caption="Select Path to Export to",
|
|
124
128
|
directory=self.path_field.text(),
|
|
125
129
|
options=QFileDialog.Option.DontResolveSymlinks
|
|
126
130
|
)
|
|
127
|
-
if
|
|
128
|
-
self.path_field.setText(
|
|
131
|
+
if path:
|
|
132
|
+
self.path_field.setText(path)
|
|
129
133
|
|
|
130
134
|
def _on_accepted(self):
|
|
131
135
|
if self.options is not None:
|
|
132
136
|
for option in self.options:
|
|
133
|
-
self.options[option] = self.
|
|
137
|
+
self.options[option] = self.checkboxes[option].isChecked()
|
|
134
138
|
self.save_path = Path(self.path_field.text())
|
|
135
139
|
self.accept()
|
|
136
140
|
|
|
@@ -44,7 +44,7 @@ import numpy as np
|
|
|
44
44
|
from patkit.data_structures import Recording
|
|
45
45
|
from patkit.configuration import DataConfig, GuiConfig
|
|
46
46
|
from patkit.constants import (
|
|
47
|
-
AnnotatorMode, DefaultCanvasColors, DefaultCursorColors,
|
|
47
|
+
AnnotatorMode, ColorPalettes, DefaultCanvasColors, DefaultCursorColors,
|
|
48
48
|
ExerciseMode, GuiColorScheme, GuiImageType
|
|
49
49
|
)
|
|
50
50
|
from patkit.plot_and_publish import (
|
|
@@ -136,7 +136,7 @@ class PlotController:
|
|
|
136
136
|
matplotlib.rcParams.update(
|
|
137
137
|
{'font.size': self.gui_config.default_font_size}
|
|
138
138
|
)
|
|
139
|
-
plt.style.use(
|
|
139
|
+
plt.style.use(ColorPalettes.COLORBLIND_10)
|
|
140
140
|
|
|
141
141
|
def to_annotator_mode(
|
|
142
142
|
self,
|
|
@@ -156,6 +156,7 @@ class PlotController:
|
|
|
156
156
|
raise ValueError(
|
|
157
157
|
f"Unknown GUI color scheme: {gui_color_mode}"
|
|
158
158
|
)
|
|
159
|
+
plt.style.use(ColorPalettes.COLORBLIND_10)
|
|
159
160
|
|
|
160
161
|
def to_exercise_mode(
|
|
161
162
|
self,
|
|
@@ -175,6 +176,7 @@ class PlotController:
|
|
|
175
176
|
raise ValueError(
|
|
176
177
|
f"Unknown GUI color scheme: {gui_color_mode}"
|
|
177
178
|
)
|
|
179
|
+
plt.style.use(ColorPalettes.COLORBLIND_10)
|
|
178
180
|
|
|
179
181
|
def to_example_mode(
|
|
180
182
|
self,
|
|
@@ -194,6 +196,7 @@ class PlotController:
|
|
|
194
196
|
raise ValueError(
|
|
195
197
|
f"Unknown GUI color scheme: {gui_color_mode}"
|
|
196
198
|
)
|
|
199
|
+
plt.style.use(ColorPalettes.COLORBLIND_10)
|
|
197
200
|
|
|
198
201
|
def to_answer_mode(
|
|
199
202
|
self,
|
|
@@ -213,6 +216,7 @@ class PlotController:
|
|
|
213
216
|
raise ValueError(
|
|
214
217
|
f"Unknown GUI color scheme: {gui_color_mode}"
|
|
215
218
|
)
|
|
219
|
+
plt.style.use(ColorPalettes.COLORBLIND_10)
|
|
216
220
|
|
|
217
221
|
def setup_axes(self) -> None:
|
|
218
222
|
"""
|
|
@@ -416,8 +420,11 @@ class PlotController:
|
|
|
416
420
|
)
|
|
417
421
|
boundaries_by_axis.append(boundary_set)
|
|
418
422
|
axis.set_ylabel(
|
|
419
|
-
name,
|
|
420
|
-
|
|
423
|
+
name,
|
|
424
|
+
rotation=0,
|
|
425
|
+
horizontalalignment="center",
|
|
426
|
+
verticalalignment="center"
|
|
427
|
+
)
|
|
421
428
|
axis.set_xlim(xlim)
|
|
422
429
|
if name in self.gui_config.pervasive_tiers:
|
|
423
430
|
for data_axis in self.data_axes:
|
|
@@ -903,3 +910,10 @@ class PlotController:
|
|
|
903
910
|
self.canvas.draw()
|
|
904
911
|
|
|
905
912
|
self.update_multicursor()
|
|
913
|
+
|
|
914
|
+
def display_exclusion(self):
|
|
915
|
+
"""
|
|
916
|
+
Updates title and graphs to show this Recording is excluded.
|
|
917
|
+
"""
|
|
918
|
+
# TODO 0.23: do this correctly
|
|
919
|
+
pass
|
|
@@ -76,14 +76,14 @@ from patkit.export import (
|
|
|
76
76
|
from patkit.gui import (
|
|
77
77
|
AudioPlayer, ImageSaveDialog, ListSaveDialog,
|
|
78
78
|
NewAnswerDialog, NewExerciseDialog,
|
|
79
|
-
ListSelectionDialog, PlotController,
|
|
79
|
+
ListSelectionDialog, PackageExerciseDialog, PlotController,
|
|
80
80
|
ReplaceDialog, UiMainWindow,
|
|
81
81
|
)
|
|
82
82
|
from patkit.initialise import initialise_config, initialise_patkit
|
|
83
83
|
from patkit.path_resolution import get_manifest_scenarios, resolve_open_path
|
|
84
84
|
from patkit.save_and_load import (
|
|
85
|
-
load_answer,
|
|
86
|
-
save_recording_session,
|
|
85
|
+
load_answer, package_exercise_to_zip, unpackage_exercise_from_zip,
|
|
86
|
+
save_answer, save_exercise, save_recording_session,
|
|
87
87
|
)
|
|
88
88
|
from patkit.ui_callbacks import UiCallbacks
|
|
89
89
|
|
|
@@ -203,6 +203,9 @@ class PdQtAnnotator(QMainWindow, UiMainWindow):
|
|
|
203
203
|
self.action_new_exercise.triggered.connect(
|
|
204
204
|
self.new_exercise)
|
|
205
205
|
self.action_save_exercise.triggered.connect(self.save_exercise)
|
|
206
|
+
self.action_package_exercise.triggered.connect(self.package_exercise)
|
|
207
|
+
self.action_unpackage_exercise.triggered.connect(
|
|
208
|
+
self.unpackage_exercise)
|
|
206
209
|
self.action_new_answer.triggered.connect(self.new_answer)
|
|
207
210
|
self.action_save_answer.triggered.connect(self.save_answer)
|
|
208
211
|
self.action_open_answer.triggered.connect(self.open_answer)
|
|
@@ -709,7 +712,7 @@ class PdQtAnnotator(QMainWindow, UiMainWindow):
|
|
|
709
712
|
|
|
710
713
|
self.update()
|
|
711
714
|
# In case labels change as new plots are drawn.
|
|
712
|
-
self.figure.align_ylabels()
|
|
715
|
+
self.plot_controller.figure.align_ylabels()
|
|
713
716
|
self.update_ui()
|
|
714
717
|
|
|
715
718
|
def open_file(self):
|
|
@@ -727,7 +730,7 @@ class PdQtAnnotator(QMainWindow, UiMainWindow):
|
|
|
727
730
|
"""
|
|
728
731
|
Save derived modalities and annotations.
|
|
729
732
|
"""
|
|
730
|
-
# TODO 0.
|
|
733
|
+
# TODO 0.23.3: does this save textgrids too and how does it interact
|
|
731
734
|
# with saving answers and exercises.
|
|
732
735
|
save_recording_session(self.session)
|
|
733
736
|
|
|
@@ -755,7 +758,7 @@ class PdQtAnnotator(QMainWindow, UiMainWindow):
|
|
|
755
758
|
"""
|
|
756
759
|
Save the all TextGrids in this Session.
|
|
757
760
|
"""
|
|
758
|
-
# TODO 0.
|
|
761
|
+
# TODO 0.23.3: write a call back for asking for overwrite confirmation.
|
|
759
762
|
if self.annotator_mode is AnnotatorMode.EXERCISE:
|
|
760
763
|
return
|
|
761
764
|
|
|
@@ -809,6 +812,85 @@ class PdQtAnnotator(QMainWindow, UiMainWindow):
|
|
|
809
812
|
"""Save the active exercise to disk."""
|
|
810
813
|
save_exercise(exercise=self.session.exercise)
|
|
811
814
|
|
|
815
|
+
def package_exercise(self) -> None:
|
|
816
|
+
"""
|
|
817
|
+
Package the active exercise to a zip.
|
|
818
|
+
"""
|
|
819
|
+
default_name = f"{self.session.patkit_path.name}.zip"
|
|
820
|
+
default_path = self.session.patkit_path.parent / default_name
|
|
821
|
+
|
|
822
|
+
zip_path, include_grids = PackageExerciseDialog.get_packaging_params(
|
|
823
|
+
parent=self,
|
|
824
|
+
default_path=default_path
|
|
825
|
+
)
|
|
826
|
+
|
|
827
|
+
if zip_path is None:
|
|
828
|
+
return
|
|
829
|
+
|
|
830
|
+
current_answer_name = self.session.exercise.current_answer.name
|
|
831
|
+
|
|
832
|
+
package_exercise_to_zip(
|
|
833
|
+
file_info=self.session.file_info,
|
|
834
|
+
zip_path=zip_path,
|
|
835
|
+
active_answer_name=current_answer_name,
|
|
836
|
+
include_root_textgrids=include_grids
|
|
837
|
+
)
|
|
838
|
+
|
|
839
|
+
# TODO: status message instead
|
|
840
|
+
# QMessageBox.information(
|
|
841
|
+
# parent=self,
|
|
842
|
+
# title="Export Successful",
|
|
843
|
+
# text=f"Exercise successfully exported to:\n{zip_path}"
|
|
844
|
+
# )
|
|
845
|
+
|
|
846
|
+
def unpackage_exercise(self) -> None:
|
|
847
|
+
"""
|
|
848
|
+
Prompt the user to extract a zipped exercise and load it.
|
|
849
|
+
"""
|
|
850
|
+
zip_path_str, _ = QFileDialog.getOpenFileName(
|
|
851
|
+
parent=self,
|
|
852
|
+
caption="Open Zipped Exercise",
|
|
853
|
+
directory=str(Path.cwd()),
|
|
854
|
+
filter="Zip Files (*.zip)"
|
|
855
|
+
)
|
|
856
|
+
|
|
857
|
+
if not zip_path_str:
|
|
858
|
+
return
|
|
859
|
+
|
|
860
|
+
zip_path = Path(zip_path_str)
|
|
861
|
+
|
|
862
|
+
dest_dir_str = QFileDialog.getExistingDirectory(
|
|
863
|
+
parent=self,
|
|
864
|
+
caption="Select Destination for Extraction",
|
|
865
|
+
directory=str(zip_path.parent)
|
|
866
|
+
)
|
|
867
|
+
|
|
868
|
+
if not dest_dir_str:
|
|
869
|
+
return
|
|
870
|
+
|
|
871
|
+
dest_dir = Path(dest_dir_str) / zip_path.stem
|
|
872
|
+
|
|
873
|
+
# TODO 0.25: ask for confirmation before overwriting instead of just
|
|
874
|
+
# refusing. This should take the form of a callback handed to
|
|
875
|
+
# unpackage_exercise_from_zip, so that individual files can be
|
|
876
|
+
# confirmed as needed.
|
|
877
|
+
unpackage_exercise_from_zip(
|
|
878
|
+
zip_filepath=zip_path,
|
|
879
|
+
destination_directory=dest_dir
|
|
880
|
+
)
|
|
881
|
+
|
|
882
|
+
# TODO: status message instead
|
|
883
|
+
# msg_text = (
|
|
884
|
+
# f"Exercise extracted to:\n{dest_dir}\n\n"
|
|
885
|
+
# "Please open this directory as a new Session."
|
|
886
|
+
# )
|
|
887
|
+
|
|
888
|
+
# QMessageBox.information(
|
|
889
|
+
# parent=self,
|
|
890
|
+
# title="Import Successful",
|
|
891
|
+
# text=msg_text
|
|
892
|
+
# )
|
|
893
|
+
|
|
812
894
|
def new_answer(self) -> bool:
|
|
813
895
|
"""Create a new blank answer for the current exercise."""
|
|
814
896
|
answer_name, author_name = NewAnswerDialog.get_answer_params(self)
|
|
@@ -854,7 +936,7 @@ class PdQtAnnotator(QMainWindow, UiMainWindow):
|
|
|
854
936
|
if not ok or not answer_name:
|
|
855
937
|
return
|
|
856
938
|
|
|
857
|
-
# TODO 0.
|
|
939
|
+
# TODO 0.23.3: The loading here might be redundant, if Exercise already
|
|
858
940
|
# front loads every answer.
|
|
859
941
|
directory = answers_dir / answer_name
|
|
860
942
|
answer = load_answer(
|
|
@@ -990,7 +1072,8 @@ class PdQtAnnotator(QMainWindow, UiMainWindow):
|
|
|
990
1072
|
parent=self,
|
|
991
1073
|
)
|
|
992
1074
|
if filename is not None:
|
|
993
|
-
self.figure.savefig(
|
|
1075
|
+
self.plot_controller.figure.savefig(
|
|
1076
|
+
filename, bbox_inches='tight', pad_inches=0.05)
|
|
994
1077
|
export_session_and_recording_meta(
|
|
995
1078
|
filename=filename,
|
|
996
1079
|
session=self.session,
|
|
@@ -29,6 +29,9 @@
|
|
|
29
29
|
# articles listed in README.md. They can also be found in
|
|
30
30
|
# citations.bib in BibTeX format.
|
|
31
31
|
#
|
|
32
|
+
from .exercise_packaging import (
|
|
33
|
+
package_exercise_to_zip, unpackage_exercise_from_zip
|
|
34
|
+
)
|
|
32
35
|
from .load import (
|
|
33
36
|
load_answer, load_exercise, load_recordings, load_recording_session
|
|
34
37
|
)
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2019-2026
|
|
3
|
+
# Pertti Palo, Scott Moisik, Matthew Faytak, and Motoki Saito.
|
|
4
|
+
#
|
|
5
|
+
# This file is part of the Phonetic Analysis ToolKIT
|
|
6
|
+
# (see https://github.com/giuthas/patkit/).
|
|
7
|
+
#
|
|
8
|
+
# This program is free software: you can redistribute it and/or modify
|
|
9
|
+
# it under the terms of the GNU General Public License as published by
|
|
10
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
11
|
+
# (at your option) any later version.
|
|
12
|
+
#
|
|
13
|
+
# This program is distributed in the hope that it will be useful,
|
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
16
|
+
# GNU General Public License for more details.
|
|
17
|
+
#
|
|
18
|
+
# You should have received a copy of the GNU General Public License
|
|
19
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
20
|
+
#
|
|
21
|
+
# The example data packaged with this program is licensed under the
|
|
22
|
+
# Creative Commons Attribution-NonCommercial-ShareAlike 4.0
|
|
23
|
+
# International (CC BY-NC-SA 4.0) License. You should have received a
|
|
24
|
+
# copy of the Creative Commons Attribution-NonCommercial-ShareAlike 4.0
|
|
25
|
+
# International (CC BY-NC-SA 4.0) License along with the data. If not,
|
|
26
|
+
# see <https://creativecommons.org/licenses/by-nc-sa/4.0/> for details.
|
|
27
|
+
#
|
|
28
|
+
# When using the toolkit for scientific publications, please cite the
|
|
29
|
+
# articles listed in README.md. They can also be found in
|
|
30
|
+
# citations.bib in BibTeX format.
|
|
31
|
+
#
|
|
32
|
+
"""
|
|
33
|
+
Utility functions for packaging and importing Patkit Exercises.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
import logging
|
|
37
|
+
from zipfile import ZipFile, ZIP_DEFLATED
|
|
38
|
+
from pathlib import Path
|
|
39
|
+
|
|
40
|
+
from patkit.constants import SourceSuffix
|
|
41
|
+
from patkit.data_structures import FileInformation
|
|
42
|
+
|
|
43
|
+
_logger = logging.getLogger('patkit.exercise_packaging')
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def package_exercise_to_zip(
|
|
47
|
+
file_info: FileInformation,
|
|
48
|
+
zip_path: Path,
|
|
49
|
+
active_answer_name: str,
|
|
50
|
+
include_root_textgrids: bool = False,
|
|
51
|
+
include_wav_files: bool = True,
|
|
52
|
+
) -> None:
|
|
53
|
+
"""
|
|
54
|
+
Package a Patkit Session and Exercise into a zip file.
|
|
55
|
+
|
|
56
|
+
Please note that, if the wav files will be included in the zip, they will
|
|
57
|
+
be included in the *patkit directory* rather than as a separate recorded
|
|
58
|
+
data directory. This is regardless of where they are originally situated
|
|
59
|
+
and done to keep the whole exercise contained in one directory.
|
|
60
|
+
|
|
61
|
+
Parameters
|
|
62
|
+
----------
|
|
63
|
+
file_info : FileInformation
|
|
64
|
+
The FileInformation for the Session.
|
|
65
|
+
zip_filepath : Path
|
|
66
|
+
The destination path for the output `.zip` file.
|
|
67
|
+
active_answer_name : str
|
|
68
|
+
The name of the specific answer to include in the archive. It will be
|
|
69
|
+
renamed to 'answer' in the resulting zip structure.
|
|
70
|
+
include_root_textgrids : bool, optional
|
|
71
|
+
Whether to include root-level `.TextGrid` files, by default False.
|
|
72
|
+
include_wav_files : bool, optional
|
|
73
|
+
Whether to include wav files, by default True.
|
|
74
|
+
"""
|
|
75
|
+
_logger.info('Packing an exercise.')
|
|
76
|
+
with ZipFile(file=zip_path, mode='w', compression=ZIP_DEFLATED) as output:
|
|
77
|
+
patkit_path = file_info.patkit_path
|
|
78
|
+
for item in patkit_path.rglob('*'):
|
|
79
|
+
# Only include files.
|
|
80
|
+
if not item.is_file():
|
|
81
|
+
continue
|
|
82
|
+
|
|
83
|
+
# Skip root-level TextGrids if they are not being included.
|
|
84
|
+
if (
|
|
85
|
+
not include_root_textgrids and
|
|
86
|
+
item.parent == file_info.patkit_path and
|
|
87
|
+
(item.suffix.lower() == SourceSuffix.TEXTGRID.lower())
|
|
88
|
+
):
|
|
89
|
+
_logger.info('skipping: %s', str(item))
|
|
90
|
+
continue
|
|
91
|
+
else:
|
|
92
|
+
_logger.info('including: %s', str(item))
|
|
93
|
+
|
|
94
|
+
relative_path = item.relative_to(patkit_path)
|
|
95
|
+
|
|
96
|
+
# Process the exercise directory to filter answers
|
|
97
|
+
if relative_path.parts[0] == 'exercise':
|
|
98
|
+
if (
|
|
99
|
+
len(relative_path.parts) >= 3 and
|
|
100
|
+
relative_path.parts[1] == 'answers'
|
|
101
|
+
):
|
|
102
|
+
# Only include the targeted answer,
|
|
103
|
+
# and rename it to 'answer'
|
|
104
|
+
if relative_path.parts[2] == active_answer_name:
|
|
105
|
+
new_parts = list(relative_path.parts)
|
|
106
|
+
new_parts[2] = 'answer'
|
|
107
|
+
arcname = Path(*new_parts)
|
|
108
|
+
output.write(filename=item, arcname=arcname)
|
|
109
|
+
continue # Skip all other answer directories
|
|
110
|
+
|
|
111
|
+
# Write all other allowed files
|
|
112
|
+
output.write(filename=item, arcname=relative_path)
|
|
113
|
+
|
|
114
|
+
recorded_path = file_info.recorded_path
|
|
115
|
+
if recorded_path is not None:
|
|
116
|
+
for item in recorded_path.glob('*' + SourceSuffix.WAV):
|
|
117
|
+
if not item.is_file():
|
|
118
|
+
continue
|
|
119
|
+
|
|
120
|
+
output.write(filename=item, arcname=item.name)
|
|
121
|
+
|
|
122
|
+
# TODO 0.24: verify that wavs or in future other data files are
|
|
123
|
+
# included
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def unpackage_exercise_from_zip(
|
|
127
|
+
zip_filepath: Path,
|
|
128
|
+
destination_directory: Path
|
|
129
|
+
) -> None:
|
|
130
|
+
"""
|
|
131
|
+
Extract a Patkit Exercise zip archive.
|
|
132
|
+
|
|
133
|
+
Parameters
|
|
134
|
+
----------
|
|
135
|
+
zip_filepath : Path
|
|
136
|
+
The path to the `.zip` file to extract.
|
|
137
|
+
destination_directory : Path
|
|
138
|
+
The folder where the archive's contents should be unpacked.
|
|
139
|
+
"""
|
|
140
|
+
with ZipFile(file=zip_filepath, mode='r') as zip_file:
|
|
141
|
+
zip_file.extractall(path=destination_directory)
|
|
@@ -387,7 +387,12 @@ def save_session_meta(
|
|
|
387
387
|
meta['format_version'] = PATKIT_FILE_VERSION
|
|
388
388
|
|
|
389
389
|
parameters = OrderedDict()
|
|
390
|
-
|
|
390
|
+
if session.file_info.is_top_level_object:
|
|
391
|
+
parameters['patkit_path'] = '.'
|
|
392
|
+
else:
|
|
393
|
+
# TODO 0.23.3: The next two should probably not be resolved, but this
|
|
394
|
+
# may break something else, so test.
|
|
395
|
+
parameters['patkit_path'] = str(session.patkit_path.resolve())
|
|
391
396
|
parameters['recorded_path'] = str(session.recorded_path.resolve())
|
|
392
397
|
parameters['datasource_name'] = session.metadata.data_source_name.value
|
|
393
398
|
parameters['path_structure'] = session.config.path_structure.model_dump()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{patkit-0.22.2 → patkit-0.22.3}/patkit/default_configuration/patkit_logging_configuration.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|