spectre-core 0.0.11__py3-none-any.whl → 0.0.13__py3-none-any.whl
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.
- spectre_core/_file_io/__init__.py +1 -3
- spectre_core/_file_io/file_handlers.py +170 -65
- spectre_core/batches/__init__.py +21 -0
- spectre_core/batches/_base.py +238 -0
- spectre_core/batches/_batches.py +247 -0
- spectre_core/batches/_factory.py +69 -0
- spectre_core/batches/_register.py +30 -0
- spectre_core/batches/plugins/_batch_keys.py +16 -0
- spectre_core/batches/plugins/_callisto.py +183 -0
- spectre_core/batches/plugins/_iq_stream.py +354 -0
- spectre_core/capture_configs/__init__.py +17 -13
- spectre_core/capture_configs/_capture_config.py +93 -34
- spectre_core/capture_configs/_capture_modes.py +22 -0
- spectre_core/capture_configs/_capture_templates.py +207 -122
- spectre_core/capture_configs/_parameters.py +116 -46
- spectre_core/capture_configs/_pconstraints.py +86 -35
- spectre_core/capture_configs/_pnames.py +49 -0
- spectre_core/capture_configs/_ptemplates.py +389 -346
- spectre_core/capture_configs/_pvalidators.py +121 -77
- spectre_core/config/__init__.py +7 -9
- spectre_core/config/_paths.py +66 -26
- spectre_core/config/_time_formats.py +15 -8
- spectre_core/exceptions.py +2 -4
- spectre_core/jobs/__init__.py +14 -0
- spectre_core/jobs/_jobs.py +111 -0
- spectre_core/jobs/_workers.py +171 -0
- spectre_core/logs/__init__.py +17 -0
- spectre_core/logs/_configure.py +67 -0
- spectre_core/logs/_decorators.py +33 -0
- spectre_core/logs/_logs.py +228 -0
- spectre_core/logs/_process_types.py +14 -0
- spectre_core/plotting/__init__.py +4 -2
- spectre_core/plotting/_base.py +204 -102
- spectre_core/plotting/_format.py +17 -4
- spectre_core/plotting/_panel_names.py +18 -0
- spectre_core/plotting/_panel_stack.py +167 -53
- spectre_core/plotting/_panels.py +341 -141
- spectre_core/post_processing/__init__.py +8 -6
- spectre_core/post_processing/_base.py +71 -45
- spectre_core/post_processing/_factory.py +42 -12
- spectre_core/post_processing/_post_processor.py +27 -29
- spectre_core/post_processing/_register.py +22 -6
- spectre_core/post_processing/plugins/_event_handler_keys.py +16 -0
- spectre_core/post_processing/plugins/_fixed_center_frequency.py +129 -0
- spectre_core/post_processing/plugins/_swept_center_frequency.py +439 -0
- spectre_core/py.typed +0 -0
- spectre_core/receivers/__init__.py +10 -7
- spectre_core/receivers/_base.py +220 -69
- spectre_core/receivers/_factory.py +53 -7
- spectre_core/receivers/_register.py +30 -9
- spectre_core/receivers/_spec_names.py +26 -15
- spectre_core/receivers/plugins/__init__.py +0 -0
- spectre_core/receivers/plugins/_receiver_names.py +16 -0
- spectre_core/receivers/plugins/_rsp1a.py +59 -0
- spectre_core/receivers/plugins/_rspduo.py +67 -0
- spectre_core/receivers/plugins/_sdrplay_receiver.py +190 -0
- spectre_core/receivers/plugins/_test.py +218 -0
- spectre_core/receivers/plugins/gr/_base.py +80 -0
- spectre_core/receivers/{gr → plugins/gr}/_rsp1a.py +45 -55
- spectre_core/receivers/{gr → plugins/gr}/_rspduo.py +65 -78
- spectre_core/receivers/{gr → plugins/gr}/_test.py +36 -34
- spectre_core/spectrograms/__init__.py +5 -3
- spectre_core/spectrograms/_analytical.py +121 -72
- spectre_core/spectrograms/_array_operations.py +103 -36
- spectre_core/spectrograms/_spectrogram.py +410 -203
- spectre_core/spectrograms/_transform.py +199 -188
- spectre_core/wgetting/__init__.py +4 -2
- spectre_core/wgetting/_callisto.py +178 -127
- {spectre_core-0.0.11.dist-info → spectre_core-0.0.13.dist-info}/METADATA +14 -7
- spectre_core-0.0.13.dist-info/RECORD +75 -0
- {spectre_core-0.0.11.dist-info → spectre_core-0.0.13.dist-info}/WHEEL +1 -1
- spectre_core/chunks/__init__.py +0 -22
- spectre_core/chunks/_base.py +0 -116
- spectre_core/chunks/_chunks.py +0 -200
- spectre_core/chunks/_factory.py +0 -25
- spectre_core/chunks/_register.py +0 -15
- spectre_core/chunks/library/_callisto.py +0 -98
- spectre_core/chunks/library/_fixed_center_frequency.py +0 -128
- spectre_core/chunks/library/_swept_center_frequency.py +0 -103
- spectre_core/logging/__init__.py +0 -11
- spectre_core/logging/_configure.py +0 -35
- spectre_core/logging/_decorators.py +0 -19
- spectre_core/logging/_log_handlers.py +0 -176
- spectre_core/post_processing/library/_fixed_center_frequency.py +0 -115
- spectre_core/post_processing/library/_swept_center_frequency.py +0 -382
- spectre_core/receivers/gr/_base.py +0 -33
- spectre_core/receivers/library/_rsp1a.py +0 -61
- spectre_core/receivers/library/_rspduo.py +0 -69
- spectre_core/receivers/library/_sdrplay_receiver.py +0 -185
- spectre_core/receivers/library/_test.py +0 -221
- spectre_core-0.0.11.dist-info/RECORD +0 -64
- /spectre_core/receivers/{gr → plugins/gr}/__init__.py +0 -0
- {spectre_core-0.0.11.dist-info → spectre_core-0.0.13.dist-info}/LICENSE +0 -0
- {spectre_core-0.0.11.dist-info → spectre_core-0.0.13.dist-info}/top_level.txt +0 -0
@@ -5,124 +5,229 @@
|
|
5
5
|
import os
|
6
6
|
import json
|
7
7
|
from abc import ABC, abstractmethod
|
8
|
-
from typing import Any, Optional
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
8
|
+
from typing import Any, Optional, TypeVar, Generic
|
9
|
+
|
10
|
+
T = TypeVar('T')
|
11
|
+
|
12
|
+
class BaseFileHandler(ABC, Generic[T]):
|
13
|
+
"""
|
14
|
+
Base class for handling file operations.
|
15
|
+
|
16
|
+
When subclassing, specify the return type of `_read`
|
17
|
+
using `Generic[T]`.
|
18
|
+
|
19
|
+
Example:
|
20
|
+
.. code-block:: python
|
21
|
+
|
22
|
+
from typing import Any, Generic
|
23
|
+
|
24
|
+
class JsonHandler(BaseFileHandler[dict[str, Any]]):
|
25
|
+
def _read(self) -> dict[str, Any]:
|
26
|
+
# Implementation here
|
27
|
+
...
|
28
|
+
"""
|
29
|
+
def __init__(
|
30
|
+
self,
|
31
|
+
parent_dir_path: str,
|
32
|
+
base_file_name: str,
|
33
|
+
extension: Optional[str] = None
|
34
|
+
) -> None:
|
35
|
+
"""Initialise a `BaseFileHandler` instance.
|
36
|
+
|
37
|
+
:param parent_dir_path: The directory path where the file is located (absolute or relative).
|
38
|
+
:param base_file_name: The name of the file without its extension.
|
39
|
+
:param extension: The file extension (without the dot), defaults to None
|
40
|
+
"""
|
41
|
+
self._data_cache: Optional[T] = None
|
42
|
+
|
43
|
+
self._parent_dir_path = parent_dir_path
|
44
|
+
self._base_file_name = base_file_name
|
45
|
+
|
46
|
+
if extension == "":
|
47
|
+
extension = None
|
18
48
|
self._extension = extension
|
19
49
|
|
20
|
-
|
50
|
+
|
21
51
|
@abstractmethod
|
22
|
-
def
|
23
|
-
|
24
|
-
|
25
|
-
|
52
|
+
def _read(
|
53
|
+
self
|
54
|
+
) -> T:
|
55
|
+
"""The grunt work to return the file contents.
|
56
|
+
|
57
|
+
:return: The file contents.
|
58
|
+
"""
|
59
|
+
|
60
|
+
|
26
61
|
@property
|
27
|
-
def
|
28
|
-
|
62
|
+
def parent_dir_path(
|
63
|
+
self
|
64
|
+
) -> str:
|
65
|
+
"""Return the parent directory path for the file."""
|
66
|
+
return self._parent_dir_path
|
29
67
|
|
30
68
|
|
31
69
|
@property
|
32
|
-
def base_file_name(
|
70
|
+
def base_file_name(
|
71
|
+
self
|
72
|
+
) -> str:
|
73
|
+
"""Return the file name, stripped of the file extension."""
|
33
74
|
return self._base_file_name
|
34
75
|
|
35
76
|
|
36
77
|
@property
|
37
|
-
def extension(
|
78
|
+
def extension(
|
79
|
+
self
|
80
|
+
) -> Optional[str]:
|
81
|
+
"""Return the file path suffix, excluding the dot."""
|
38
82
|
return self._extension
|
39
83
|
|
40
84
|
|
41
85
|
@property
|
42
|
-
def file_name(
|
86
|
+
def file_name(
|
87
|
+
self
|
88
|
+
) -> str:
|
89
|
+
"""Generate the file name based on the base name and extension.
|
90
|
+
|
91
|
+
:return: The file name with the extension (including the dot), or the base name if no extension is set.
|
92
|
+
"""
|
43
93
|
return self._base_file_name if (self._extension is None) else f"{self._base_file_name}.{self._extension}"
|
44
94
|
|
45
95
|
|
46
96
|
@property
|
47
|
-
def file_path(
|
48
|
-
|
97
|
+
def file_path(
|
98
|
+
self
|
99
|
+
) -> str:
|
100
|
+
"""The absolute or relative file path as defined by the parent directory path,
|
101
|
+
base file name and extension."""
|
102
|
+
return os.path.join(self._parent_dir_path, self.file_name)
|
49
103
|
|
50
104
|
|
51
105
|
@property
|
52
|
-
def exists(
|
106
|
+
def exists(
|
107
|
+
self
|
108
|
+
) -> bool:
|
109
|
+
"""Check if the file exists in the filesystem."""
|
53
110
|
return os.path.exists(self.file_path)
|
54
111
|
|
55
112
|
|
56
|
-
def
|
57
|
-
|
113
|
+
def read(
|
114
|
+
self,
|
115
|
+
cache: bool = True
|
116
|
+
) -> T:
|
117
|
+
"""Read the file contents.
|
118
|
+
|
119
|
+
:param cache: If False, bypasses the cache and reads the file directly on each `read` call, defaults to True
|
120
|
+
:return: The file contents.
|
121
|
+
"""
|
122
|
+
# if the user has specified to ignore the cache, simply read the file.
|
123
|
+
if not cache:
|
124
|
+
return self._read()
|
125
|
+
|
126
|
+
# otherwise make use of the cache
|
127
|
+
if self._data_cache is None:
|
128
|
+
self._data_cache = self._read()
|
129
|
+
return self._data_cache
|
130
|
+
|
58
131
|
|
132
|
+
def make_parent_dir_path(
|
133
|
+
self
|
134
|
+
) -> None:
|
135
|
+
"""Make the parent directory path of the file. No error is raised if the target
|
136
|
+
directory already exists.
|
137
|
+
"""
|
138
|
+
os.makedirs(self.parent_dir_path, exist_ok=True)
|
139
|
+
|
140
|
+
|
141
|
+
def delete(
|
142
|
+
self,
|
143
|
+
ignore_if_missing: bool = False
|
144
|
+
) -> None:
|
145
|
+
"""Delete the file from the filesystem.
|
59
146
|
|
60
|
-
|
61
|
-
|
147
|
+
:param ignore_if_missing: If True, skips deletion if the file does not exist, defaults to False
|
148
|
+
:raises FileNotFoundError: If the file is missing and `ignore_if_missing` is False.
|
149
|
+
"""
|
62
150
|
if not self.exists and not ignore_if_missing:
|
63
151
|
raise FileNotFoundError(f"{self.file_name} does not exist, and so cannot be deleted")
|
64
152
|
else:
|
65
153
|
os.remove(self.file_path)
|
66
154
|
|
67
155
|
|
68
|
-
def cat(
|
156
|
+
def cat(
|
157
|
+
self
|
158
|
+
) -> None:
|
159
|
+
"""Display the file contents on the standard output."""
|
69
160
|
print(self.read())
|
70
161
|
|
71
162
|
|
72
|
-
class JsonHandler(BaseFileHandler):
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
163
|
+
class JsonHandler(BaseFileHandler[dict[str, Any]]):
|
164
|
+
"""File handler for JSON formatted files.
|
165
|
+
|
166
|
+
We assume that the files are of the form
|
167
|
+
{
|
168
|
+
"foo": <JSON compatable structure>
|
169
|
+
... and so on.
|
170
|
+
}
|
171
|
+
|
172
|
+
"""
|
173
|
+
def __init__(
|
174
|
+
self,
|
175
|
+
parent_dir_path: str,
|
176
|
+
base_file_name: str,
|
177
|
+
extension: str = "json"
|
178
|
+
) -> None:
|
179
|
+
super().__init__(parent_dir_path,
|
81
180
|
base_file_name,
|
82
|
-
extension
|
83
|
-
**kwargs)
|
181
|
+
extension)
|
84
182
|
|
85
183
|
|
86
|
-
def
|
184
|
+
def _read(
|
185
|
+
self
|
186
|
+
) -> dict[str, Any]:
|
87
187
|
with open(self.file_path, 'r') as f:
|
88
188
|
return json.load(f)
|
89
189
|
|
90
|
-
|
91
|
-
def save(
|
92
|
-
|
93
|
-
|
94
|
-
|
190
|
+
|
191
|
+
def save(
|
192
|
+
self,
|
193
|
+
d: dict[str, Any],
|
194
|
+
force: bool = False
|
195
|
+
) -> None:
|
196
|
+
"""Save the input dictionary to file in the JSON file format.
|
197
|
+
|
198
|
+
:param d: The dictionary to save.
|
199
|
+
:param force: If True, overwrites the file if it already exists, defaults to False
|
200
|
+
:raises FileExistsError: If the file exists and `force` is False.
|
201
|
+
"""
|
202
|
+
self.make_parent_dir_path()
|
95
203
|
|
96
204
|
if self.exists:
|
97
205
|
if force:
|
98
206
|
pass
|
99
207
|
else:
|
100
|
-
raise
|
101
|
-
|
208
|
+
raise FileExistsError((f"{self.file_name} already exists, write has been abandoned. "
|
209
|
+
f"You can override this functionality with `force`"))
|
102
210
|
|
103
211
|
with open(self.file_path, 'w') as file:
|
104
212
|
json.dump(d, file, indent=4)
|
105
213
|
|
214
|
+
|
106
215
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
parent_path: str,
|
117
|
-
base_file_name: str,
|
118
|
-
extension: str = "txt",
|
119
|
-
**kwargs):
|
120
|
-
super().__init__(parent_path,
|
216
|
+
class TextHandler(BaseFileHandler[str]):
|
217
|
+
"""File handler for text formatted files."""
|
218
|
+
def __init__(
|
219
|
+
self,
|
220
|
+
parent_dir_path: str,
|
221
|
+
base_file_name: str,
|
222
|
+
extension: str = "txt"
|
223
|
+
) -> None:
|
224
|
+
super().__init__(parent_dir_path,
|
121
225
|
base_file_name,
|
122
|
-
extension
|
123
|
-
**kwargs)
|
226
|
+
extension)
|
124
227
|
|
125
|
-
|
126
|
-
def
|
228
|
+
|
229
|
+
def _read(
|
230
|
+
self
|
231
|
+
) -> str:
|
127
232
|
with open(self.file_path, 'r') as f:
|
128
233
|
return f.read()
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
|
2
|
+
# This file is part of SPECTRE
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
4
|
+
|
5
|
+
"""IO operations on batched data files."""
|
6
|
+
|
7
|
+
from .plugins._batch_keys import BatchKey
|
8
|
+
|
9
|
+
# register decorators take effect on import
|
10
|
+
from .plugins._iq_stream import IQStreamBatch, IQMetadata
|
11
|
+
from .plugins._callisto import CallistoBatch
|
12
|
+
|
13
|
+
from ._base import BaseBatch, BatchFile
|
14
|
+
from ._batches import Batches
|
15
|
+
from ._factory import get_batch_cls, get_batch_cls_from_tag
|
16
|
+
|
17
|
+
__all__ = [
|
18
|
+
"IQStreamBatch", "IQMetadata", "CallistoBatch", "BaseBatch", "BatchFile",
|
19
|
+
"Batches", "get_batch_cls", "BatchKey", "get_batch_cls_from_tag"
|
20
|
+
]
|
21
|
+
|
@@ -0,0 +1,238 @@
|
|
1
|
+
# SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
|
2
|
+
# This file is part of SPECTRE
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
4
|
+
|
5
|
+
from datetime import datetime
|
6
|
+
from typing import TypeVar
|
7
|
+
from functools import cached_property
|
8
|
+
from abc import ABC, abstractmethod
|
9
|
+
|
10
|
+
from spectre_core._file_io import BaseFileHandler
|
11
|
+
from spectre_core.config import get_batches_dir_path, TimeFormat
|
12
|
+
from spectre_core.spectrograms import Spectrogram
|
13
|
+
|
14
|
+
|
15
|
+
T = TypeVar('T')
|
16
|
+
|
17
|
+
class BatchFile(BaseFileHandler[T]):
|
18
|
+
"""Abstract base class for files belonging to a batch, identified by their file extension.
|
19
|
+
|
20
|
+
Batch file names must conform to the following structure:
|
21
|
+
|
22
|
+
`<start time>_<tag>.<extension>`
|
23
|
+
|
24
|
+
The substring `<start time>_<tag>` is referred to as the batch name. Files with the same batch name
|
25
|
+
belong to the same batch.
|
26
|
+
"""
|
27
|
+
def __init__(
|
28
|
+
self,
|
29
|
+
batch_parent_dir_path: str,
|
30
|
+
batch_name: str,
|
31
|
+
extension: str
|
32
|
+
) -> None:
|
33
|
+
"""Initialise a `BatchFile` instance.
|
34
|
+
|
35
|
+
:param batch_parent_dir_path: Parent directory of the batch.
|
36
|
+
:param batch_name: Base file name, composed of the batch start time and tag.
|
37
|
+
:param extension: File extension.
|
38
|
+
"""
|
39
|
+
super().__init__(batch_parent_dir_path,
|
40
|
+
batch_name,
|
41
|
+
extension)
|
42
|
+
self._start_time, self._tag = batch_name.split("_")
|
43
|
+
|
44
|
+
|
45
|
+
@property
|
46
|
+
def start_time(
|
47
|
+
self
|
48
|
+
) -> str:
|
49
|
+
"""The start time of the batch, formatted as a string up to seconds precision."""
|
50
|
+
return self._start_time
|
51
|
+
|
52
|
+
|
53
|
+
@cached_property
|
54
|
+
def start_datetime(
|
55
|
+
self
|
56
|
+
) -> datetime:
|
57
|
+
"""The start time of the batch, parsed as a datetime up to seconds precision."""
|
58
|
+
return datetime.strptime(self.start_time, TimeFormat.DATETIME)
|
59
|
+
|
60
|
+
|
61
|
+
@property
|
62
|
+
def tag(
|
63
|
+
self
|
64
|
+
) -> str:
|
65
|
+
"""The batch name tag."""
|
66
|
+
return self._tag
|
67
|
+
|
68
|
+
|
69
|
+
class BaseBatch(ABC):
|
70
|
+
"""
|
71
|
+
An abstract base class representing a group of data files over a common time interval.
|
72
|
+
|
73
|
+
All files in a batch share a base file name and differ only by their extension.
|
74
|
+
Subclasses of `BaseBatch` define the expected data for each file extension and
|
75
|
+
provide an API for accessing their contents using `BatchFile` subclasses.
|
76
|
+
|
77
|
+
Subclasses should expose `BatchFile` instances directly as attributes, which
|
78
|
+
simplifies static typing. Additionally, they should call `add_file` in the constructor
|
79
|
+
to formally register each `BatchFile`.
|
80
|
+
"""
|
81
|
+
def __init__(
|
82
|
+
self,
|
83
|
+
start_time: str,
|
84
|
+
tag: str
|
85
|
+
) -> None:
|
86
|
+
"""Initialise a `BaseBatch` instance.
|
87
|
+
|
88
|
+
:param start_time: Start time of the batch as a string with seconds precision.
|
89
|
+
:param tag: The batch name tag.
|
90
|
+
"""
|
91
|
+
self._start_time = start_time
|
92
|
+
self._tag: str = tag
|
93
|
+
self._start_datetime = datetime.strptime(self.start_time, TimeFormat.DATETIME)
|
94
|
+
self._parent_dir_path = get_batches_dir_path(year = self.start_datetime.year,
|
95
|
+
month = self.start_datetime.month,
|
96
|
+
day = self.start_datetime.day)
|
97
|
+
|
98
|
+
# internal register of batch files
|
99
|
+
self._batch_files: dict[str, BatchFile] = {}
|
100
|
+
|
101
|
+
|
102
|
+
@property
|
103
|
+
@abstractmethod
|
104
|
+
def spectrogram_file(
|
105
|
+
self
|
106
|
+
) -> BatchFile:
|
107
|
+
"""The batch file which contains spectrogram data."""
|
108
|
+
|
109
|
+
|
110
|
+
@property
|
111
|
+
def start_time(
|
112
|
+
self
|
113
|
+
) -> str:
|
114
|
+
"""The start time of the batch, formatted as a string up to seconds precision."""
|
115
|
+
return self._start_time
|
116
|
+
|
117
|
+
|
118
|
+
@property
|
119
|
+
def start_datetime(
|
120
|
+
self
|
121
|
+
) -> datetime:
|
122
|
+
"""The start time of the batch, parsed as a datetime up to seconds precision."""
|
123
|
+
return self._start_datetime
|
124
|
+
|
125
|
+
|
126
|
+
@property
|
127
|
+
def tag(
|
128
|
+
self
|
129
|
+
) -> str:
|
130
|
+
"""The batch name tag."""
|
131
|
+
return self._tag
|
132
|
+
|
133
|
+
|
134
|
+
@property
|
135
|
+
def parent_dir_path(
|
136
|
+
self
|
137
|
+
) -> str:
|
138
|
+
"""The parent directory for the batch."""
|
139
|
+
return self._parent_dir_path
|
140
|
+
|
141
|
+
|
142
|
+
@property
|
143
|
+
def name(
|
144
|
+
self
|
145
|
+
) -> str:
|
146
|
+
"""Return the base file name shared by all files in the batch,
|
147
|
+
composed of the start time and the batch tag."""
|
148
|
+
return f"{self._start_time}_{self._tag}"
|
149
|
+
|
150
|
+
|
151
|
+
@property
|
152
|
+
def extensions(
|
153
|
+
self
|
154
|
+
) -> list[str]:
|
155
|
+
"""All defined file extensions for the batch."""
|
156
|
+
return list(self._batch_files.keys())
|
157
|
+
|
158
|
+
|
159
|
+
@property
|
160
|
+
def batch_files(
|
161
|
+
self
|
162
|
+
) -> dict[str, BatchFile]:
|
163
|
+
"""Map each file extension in the batch to the corresponding batch file instance.
|
164
|
+
|
165
|
+
Use `add_file` to add a file to the batch.
|
166
|
+
"""
|
167
|
+
return self._batch_files
|
168
|
+
|
169
|
+
|
170
|
+
def add_file(
|
171
|
+
self,
|
172
|
+
batch_file: BatchFile
|
173
|
+
) -> None:
|
174
|
+
"""Add an instance of a batch file to the batch.
|
175
|
+
|
176
|
+
:param batch_file: The `BatchFile` instance to add to the batch.
|
177
|
+
:raises ValueError: If the `BatchFile` instance does not have a defined file extension.
|
178
|
+
"""
|
179
|
+
if batch_file.extension is None:
|
180
|
+
raise ValueError(f"The `BatchFile` must have a defined file extension. "
|
181
|
+
f"Received '{batch_file.extension}.")
|
182
|
+
self._batch_files[batch_file.extension] = batch_file
|
183
|
+
|
184
|
+
|
185
|
+
def get_file(
|
186
|
+
self,
|
187
|
+
extension: str
|
188
|
+
) -> BatchFile:
|
189
|
+
"""Get a batch file instance from the batch, according to the file extension.
|
190
|
+
|
191
|
+
:param extension: The file extension of the batch file.
|
192
|
+
:raises NotImplementedError: If the extension is undefined for the batch.
|
193
|
+
:return: The batch file instance registered under the input file extension.
|
194
|
+
"""
|
195
|
+
try:
|
196
|
+
return self._batch_files[extension]
|
197
|
+
except KeyError:
|
198
|
+
raise NotImplementedError(f"A batch file with extension '{extension}' is not implemented for this batch.")
|
199
|
+
|
200
|
+
|
201
|
+
def delete_file(
|
202
|
+
self,
|
203
|
+
extension: str
|
204
|
+
) -> None:
|
205
|
+
"""Delete a file from the batch, according to the file extension.
|
206
|
+
|
207
|
+
:param extension: The file extension of the batch file.
|
208
|
+
:raises FileNotFoundError: If the batch file does not exist in the file system.
|
209
|
+
"""
|
210
|
+
batch_file = self.get_file(extension)
|
211
|
+
batch_file.delete()
|
212
|
+
|
213
|
+
|
214
|
+
def has_file(
|
215
|
+
self,
|
216
|
+
extension: str
|
217
|
+
) -> bool:
|
218
|
+
"""Determine the existance of a batch file in the file system.
|
219
|
+
|
220
|
+
:param extension: The file extension of the batch file.
|
221
|
+
:return: True if the batch file exists in the file system, False otherwise.
|
222
|
+
"""
|
223
|
+
try:
|
224
|
+
batch_file = self.get_file(extension)
|
225
|
+
return batch_file.exists
|
226
|
+
except FileNotFoundError:
|
227
|
+
return False
|
228
|
+
|
229
|
+
|
230
|
+
def read_spectrogram(
|
231
|
+
self
|
232
|
+
) -> Spectrogram:
|
233
|
+
"""Read and return the spectrogram data stored in the batch.
|
234
|
+
|
235
|
+
:return: The spectrogram stored by the batch `spectrogram_file`.
|
236
|
+
"""
|
237
|
+
return self.spectrogram_file.read()
|
238
|
+
|