roc-film 1.13.5__py3-none-any.whl → 1.14.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- roc/__init__.py +2 -1
- roc/film/__init__.py +2 -2
- roc/film/commands.py +372 -323
- roc/film/config/__init__.py +0 -1
- roc/film/constants.py +101 -65
- roc/film/descriptor.json +127 -96
- roc/film/exceptions.py +28 -27
- roc/film/tasks/__init__.py +16 -16
- roc/film/tasks/cat_solo_hk.py +86 -74
- roc/film/tasks/cdf_postpro.py +438 -309
- roc/film/tasks/check_dds.py +39 -45
- roc/film/tasks/db_to_anc_bia_sweep_table.py +381 -0
- roc/film/tasks/dds_to_l0.py +232 -180
- roc/film/tasks/export_solo_coord.py +147 -0
- roc/film/tasks/file_handler.py +91 -75
- roc/film/tasks/l0_to_hk.py +117 -103
- roc/film/tasks/l0_to_l1_bia_current.py +38 -30
- roc/film/tasks/l0_to_l1_bia_sweep.py +417 -329
- roc/film/tasks/l0_to_l1_sbm.py +250 -208
- roc/film/tasks/l0_to_l1_surv.py +185 -130
- roc/film/tasks/make_daily_tm.py +40 -37
- roc/film/tasks/merge_tcreport.py +77 -71
- roc/film/tasks/merge_tmraw.py +101 -88
- roc/film/tasks/parse_dds_xml.py +21 -20
- roc/film/tasks/set_l0_utc.py +51 -49
- roc/film/tests/cdf_compare.py +565 -0
- roc/film/tests/hdf5_compare.py +84 -62
- roc/film/tests/test_dds_to_l0.py +93 -51
- roc/film/tests/test_dds_to_tc.py +8 -11
- roc/film/tests/test_dds_to_tm.py +8 -10
- roc/film/tests/test_film.py +161 -116
- roc/film/tests/test_l0_to_hk.py +64 -36
- roc/film/tests/test_l0_to_l1_bia.py +10 -14
- roc/film/tests/test_l0_to_l1_sbm.py +14 -19
- roc/film/tests/test_l0_to_l1_surv.py +68 -41
- roc/film/tests/test_metadata.py +21 -20
- roc/film/tests/tests.py +743 -396
- roc/film/tools/__init__.py +5 -5
- roc/film/tools/dataset_tasks.py +34 -2
- roc/film/tools/file_helpers.py +390 -269
- roc/film/tools/l0.py +402 -324
- roc/film/tools/metadata.py +147 -127
- roc/film/tools/skeleton.py +12 -17
- roc/film/tools/tools.py +109 -92
- roc/film/tools/xlsx2skt.py +161 -139
- {roc_film-1.13.5.dist-info → roc_film-1.14.0.dist-info}/LICENSE +127 -125
- roc_film-1.14.0.dist-info/METADATA +60 -0
- roc_film-1.14.0.dist-info/RECORD +50 -0
- {roc_film-1.13.5.dist-info → roc_film-1.14.0.dist-info}/WHEEL +1 -1
- roc/film/tasks/l0_to_anc_bia_sweep_table.py +0 -348
- roc_film-1.13.5.dist-info/METADATA +0 -120
- roc_film-1.13.5.dist-info/RECORD +0 -48
roc/film/tools/tools.py
CHANGED
@@ -17,32 +17,38 @@ from poppy.core.generic.metaclasses import Singleton
|
|
17
17
|
from poppy.core.configuration import Configuration
|
18
18
|
from poppy.core.generic.paths import Paths
|
19
19
|
from poppy.core.logger import logger
|
20
|
-
from roc.film.constants import
|
21
|
-
|
20
|
+
from roc.film.constants import (
|
21
|
+
_ROOT_DIRECTORY,
|
22
|
+
INPUT_DATETIME_STRFTIME,
|
23
|
+
DATA_VERSION,
|
24
|
+
TIME_DAILY_STRFORMAT,
|
25
|
+
)
|
22
26
|
|
23
27
|
from roc.film.exceptions import FilmException, HandlingFileError
|
24
28
|
|
25
|
-
__all__ = [
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
29
|
+
__all__ = [
|
30
|
+
"paths",
|
31
|
+
"DESCRIPTOR",
|
32
|
+
"raise_error",
|
33
|
+
"valid_time",
|
34
|
+
"valid_date",
|
35
|
+
"valid_data_version",
|
36
|
+
"valid_single_file",
|
37
|
+
"valid_dir",
|
38
|
+
"extract_datetime",
|
39
|
+
"extract_file_fields",
|
40
|
+
"get_datasets",
|
41
|
+
"sort_indices",
|
42
|
+
"unique_dict_list",
|
43
|
+
"sort_dict_list",
|
44
|
+
"safe_move",
|
45
|
+
"setup_lock",
|
46
|
+
"get_latest_file",
|
47
|
+
"Map",
|
48
|
+
"glob_list",
|
49
|
+
"move_to_trash",
|
50
|
+
"decode",
|
51
|
+
]
|
46
52
|
|
47
53
|
# ________________ Global Variables _____________
|
48
54
|
# (define here the global variables)
|
@@ -65,20 +71,18 @@ def get_descriptor():
|
|
65
71
|
"""
|
66
72
|
|
67
73
|
class Descriptor(object, metaclass=Singleton):
|
68
|
-
|
69
74
|
def __init__(self):
|
70
|
-
|
71
|
-
descriptor = paths.from_root('descriptor.json')
|
75
|
+
descriptor = paths.from_root("descriptor.json")
|
72
76
|
|
73
77
|
# Get descriptor content
|
74
|
-
with open(descriptor,
|
78
|
+
with open(descriptor, "r") as file_buffer:
|
75
79
|
for key, val in json.load(file_buffer).items():
|
76
80
|
setattr(self, key, val)
|
77
81
|
|
78
82
|
# Re-organize task section
|
79
83
|
tasks = dict()
|
80
84
|
for task in self.tasks:
|
81
|
-
tasks[task[
|
85
|
+
tasks[task["name"]] = task
|
82
86
|
|
83
87
|
self.tasks = tasks
|
84
88
|
|
@@ -107,8 +111,9 @@ def valid_time(t, format=INPUT_DATETIME_STRFTIME):
|
|
107
111
|
try:
|
108
112
|
return datetime.strptime(t, format)
|
109
113
|
except ValueError:
|
110
|
-
raise_error(
|
111
|
-
|
114
|
+
raise_error(
|
115
|
+
f"Not a valid datetime: '{t}'.", exception=argparse.ArgumentTypeError
|
116
|
+
)
|
112
117
|
|
113
118
|
|
114
119
|
def valid_date(t, format=TIME_DAILY_STRFORMAT):
|
@@ -123,8 +128,9 @@ def valid_date(t, format=TIME_DAILY_STRFORMAT):
|
|
123
128
|
try:
|
124
129
|
return datetime.strptime(t, format)
|
125
130
|
except ValueError:
|
126
|
-
raise_error(
|
127
|
-
|
131
|
+
raise_error(
|
132
|
+
f"Not a valid date: '{t}'.", exception=argparse.ArgumentTypeError
|
133
|
+
)
|
128
134
|
|
129
135
|
|
130
136
|
def valid_data_version(data_version):
|
@@ -138,9 +144,9 @@ def valid_data_version(data_version):
|
|
138
144
|
if isinstance(data_version, list):
|
139
145
|
data_version = data_version[0]
|
140
146
|
data_version = int(data_version)
|
141
|
-
return f
|
147
|
+
return f"{data_version:02d}"
|
142
148
|
except ValueError:
|
143
|
-
raise_error(f
|
149
|
+
raise_error(f"Input value for --data-version is not valid! ({data_version})")
|
144
150
|
|
145
151
|
|
146
152
|
def valid_single_file(file):
|
@@ -158,11 +164,11 @@ def valid_single_file(file):
|
|
158
164
|
else:
|
159
165
|
raise FileNotFoundError
|
160
166
|
except FileNotFoundError:
|
161
|
-
raise_error(f
|
167
|
+
raise_error(f"Input file not found! ({file})", exception=FileNotFoundError)
|
162
168
|
except ValueError:
|
163
|
-
raise_error(f
|
169
|
+
raise_error(f"Input file is not valid! ({file})", exception=ValueError)
|
164
170
|
except Exception as e:
|
165
|
-
raise_error(f
|
171
|
+
raise_error(f"Problem with input file! ({file})", exception=e)
|
166
172
|
|
167
173
|
|
168
174
|
def valid_dir(dir):
|
@@ -180,11 +186,11 @@ def valid_dir(dir):
|
|
180
186
|
else:
|
181
187
|
raise IsADirectoryError
|
182
188
|
except IsADirectoryError:
|
183
|
-
raise_error(f
|
189
|
+
raise_error(f"Input directory not found! ({dir})", exception=IsADirectoryError)
|
184
190
|
except ValueError:
|
185
|
-
raise_error(f
|
191
|
+
raise_error(f"Input directory is not valid! ({dir})", exception=ValueError)
|
186
192
|
except Exception as e:
|
187
|
-
raise_error(f
|
193
|
+
raise_error(f"Problem with input directory! ({dir})", exception=e)
|
188
194
|
|
189
195
|
|
190
196
|
def unique_dates(utc_times):
|
@@ -212,14 +218,15 @@ def extract_datetime(str_datetime):
|
|
212
218
|
:return: 2-elements list containing Datetime start/end time (if input Datetime has a daily format, return the day twice).
|
213
219
|
"""
|
214
220
|
|
215
|
-
str_datetime_list = str_datetime.split(
|
221
|
+
str_datetime_list = str_datetime.split("-")
|
216
222
|
if len(str_datetime_list) == 1:
|
217
|
-
out_datetime = [datetime.strptime(str_datetime,
|
223
|
+
out_datetime = [datetime.strptime(str_datetime, "%Y%m%d")] * 2
|
218
224
|
elif len(str_datetime_list) == 2:
|
219
|
-
out_datetime = [
|
220
|
-
dt,
|
225
|
+
out_datetime = [
|
226
|
+
datetime.strptime(dt, "%Y%m%dT%H%M%S") for dt in str_datetime_list
|
227
|
+
]
|
221
228
|
else:
|
222
|
-
logger.error(f
|
229
|
+
logger.error(f"Wrong input datetime format: {str_datetime}")
|
223
230
|
return None
|
224
231
|
|
225
232
|
return out_datetime
|
@@ -251,30 +258,29 @@ def get_datasets(task, task_name):
|
|
251
258
|
"""
|
252
259
|
|
253
260
|
# Get dataset JSON file provided as an input argument (if any)
|
254
|
-
dataset_file = task.pipeline.get(
|
255
|
-
'dataset_file', default=[None], args=True)[0]
|
261
|
+
dataset_file = task.pipeline.get("dataset_file", default=[None], args=True)[0]
|
256
262
|
# Get --dataset input keyword value (if any)
|
257
|
-
dataset_names = task.pipeline.get(
|
263
|
+
dataset_names = task.pipeline.get("dataset", default=[None], args=True)
|
258
264
|
# Get --data-version input keyword value (if any)
|
259
|
-
data_version = task.pipeline.get(
|
260
|
-
|
265
|
+
data_version = task.pipeline.get("data_version", default=[DATA_VERSION], args=True)[
|
266
|
+
0
|
267
|
+
]
|
261
268
|
|
262
269
|
# Get task output dataset description list from the descriptor.json file
|
263
|
-
task_output_list = DESCRIPTOR.tasks[task_name][
|
270
|
+
task_output_list = DESCRIPTOR.tasks[task_name]["outputs"]
|
264
271
|
|
265
272
|
# If dataset JSON file passed as an value of the --dataset_file input
|
266
273
|
# keyword, load list of datasets to create and related data_version
|
267
274
|
# (optional)
|
268
275
|
if dataset_file and os.path.isfile(dataset_file):
|
269
|
-
with open(dataset_file,
|
276
|
+
with open(dataset_file, "r") as file_buff:
|
270
277
|
# Loop over dataset array in the JSON file to get the name and
|
271
278
|
# optionally the version of the output file
|
272
279
|
dataset_to_create = []
|
273
280
|
data_versions = []
|
274
|
-
for current_dataset_obj in json.load(file_buff)[
|
275
|
-
dataset_to_create.append(current_dataset_obj[
|
276
|
-
data_versions.append(
|
277
|
-
current_dataset_obj.get('version', data_version))
|
281
|
+
for current_dataset_obj in json.load(file_buff)["dataset"]:
|
282
|
+
dataset_to_create.append(current_dataset_obj["name"])
|
283
|
+
data_versions.append(current_dataset_obj.get("version", data_version))
|
278
284
|
elif dataset_names[0]:
|
279
285
|
# Else if dataset list passed as values of the --dataset input keyword
|
280
286
|
dataset_to_create = dataset_names
|
@@ -282,22 +288,25 @@ def get_datasets(task, task_name):
|
|
282
288
|
else:
|
283
289
|
# Else load all the output datasets listed in descriptor for the given
|
284
290
|
# task by default
|
285
|
-
dataset_to_create = list(DESCRIPTOR.tasks[task_name][
|
291
|
+
dataset_to_create = list(DESCRIPTOR.tasks[task_name]["outputs"].keys())
|
286
292
|
data_versions = [data_version] * len(dataset_to_create)
|
287
293
|
|
288
294
|
# Retrieve dataset info from descriptor.json
|
289
295
|
dataset_list = []
|
290
296
|
for i, dataset_name in enumerate(dataset_to_create):
|
291
|
-
|
292
297
|
# Check if current dataset is indeed a output dataset of the task (as
|
293
298
|
# described in the descriptor.json file)
|
294
299
|
if dataset_name not in task_output_list:
|
295
|
-
logger.warning(
|
300
|
+
logger.warning(
|
301
|
+
f"{dataset_name} is not a valid dataset of the task {task_name}!"
|
302
|
+
)
|
296
303
|
else:
|
297
304
|
# if yes, get description and store info in the dataset_list list
|
298
|
-
current_dataset = {
|
299
|
-
|
300
|
-
|
305
|
+
current_dataset = {
|
306
|
+
"name": dataset_name,
|
307
|
+
"version": data_versions[i],
|
308
|
+
"descr": task_output_list[dataset_name],
|
309
|
+
}
|
301
310
|
dataset_list.append(current_dataset)
|
302
311
|
|
303
312
|
return dataset_list
|
@@ -310,7 +319,7 @@ def unique_dict_list(list_of_dict):
|
|
310
319
|
:param list_of_dict: List of dict to unify
|
311
320
|
:return: return list inside which each dict is unique
|
312
321
|
"""
|
313
|
-
return [i for n, i in enumerate(list_of_dict) if i not in list_of_dict[n + 1:]]
|
322
|
+
return [i for n, i in enumerate(list_of_dict) if i not in list_of_dict[n + 1 :]]
|
314
323
|
|
315
324
|
|
316
325
|
def sort_dict_list(list_of_dict, key):
|
@@ -333,8 +342,7 @@ def sort_indices(list_to_sort):
|
|
333
342
|
:return: list of sorted indices
|
334
343
|
"""
|
335
344
|
|
336
|
-
return sorted(range(len(list_to_sort)),
|
337
|
-
key=lambda k: list_to_sort[k])
|
345
|
+
return sorted(range(len(list_to_sort)), key=lambda k: list_to_sort[k])
|
338
346
|
|
339
347
|
|
340
348
|
def safe_move(src, dst, ignore_patterns=[]):
|
@@ -355,11 +363,14 @@ def safe_move(src, dst, ignore_patterns=[]):
|
|
355
363
|
if os.path.isfile(src):
|
356
364
|
shutil.copy(src, dst, follow_symlinks=True)
|
357
365
|
elif os.path.isdir(src):
|
358
|
-
shutil.copytree(
|
359
|
-
|
360
|
-
|
366
|
+
shutil.copytree(
|
367
|
+
src,
|
368
|
+
dst,
|
369
|
+
ignore=shutil.ignore_patterns(ignore_patterns),
|
370
|
+
dirs_exist_ok=True,
|
371
|
+
)
|
361
372
|
except Exception:
|
362
|
-
raise HandlingFileError(f
|
373
|
+
raise HandlingFileError(f"Cannot move {src} into {dst}!")
|
363
374
|
else:
|
364
375
|
# then delete if the file has well copied
|
365
376
|
if os.path.exists(dst):
|
@@ -382,10 +393,11 @@ def setup_lock(pipeline):
|
|
382
393
|
"""
|
383
394
|
|
384
395
|
# Retrieve lock_file input argument value
|
385
|
-
lock_file = pipeline.get(
|
396
|
+
lock_file = pipeline.get("lock_file", default=[None], args=True)[0]
|
386
397
|
if lock_file is not None:
|
387
398
|
# Retrieve output directory path
|
388
399
|
from roc.film.tools.file_helpers import get_output_dir
|
400
|
+
|
389
401
|
output_dir = get_output_dir(pipeline)
|
390
402
|
|
391
403
|
# Set the value of Pipeline.lock attribute filename
|
@@ -403,9 +415,9 @@ def sort_cdf_by_epoch(cdf, descending=False, zvar_list=[]):
|
|
403
415
|
"""
|
404
416
|
|
405
417
|
try:
|
406
|
-
epoch = cdf[
|
418
|
+
epoch = cdf["Epoch"]
|
407
419
|
except Exception:
|
408
|
-
logger.error(
|
420
|
+
logger.error("Cannot get Epoch zVariable from input CDF!")
|
409
421
|
return cdf
|
410
422
|
|
411
423
|
sorted_idx = np.argsort(epoch[...])
|
@@ -417,18 +429,20 @@ def sort_cdf_by_epoch(cdf, descending=False, zvar_list=[]):
|
|
417
429
|
|
418
430
|
for zvar in zvar_list:
|
419
431
|
current_zvar = cdf[zvar][...]
|
420
|
-
current_zvar = current_zvar[sorted_idx,
|
432
|
+
current_zvar = current_zvar[sorted_idx,]
|
421
433
|
cdf[zvar] = current_zvar
|
422
434
|
|
423
435
|
return cdf
|
424
436
|
|
425
437
|
|
426
|
-
def extract_file_fields(
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
438
|
+
def extract_file_fields(
|
439
|
+
rpw_file,
|
440
|
+
get_source=False,
|
441
|
+
get_level=False,
|
442
|
+
get_descriptor=False,
|
443
|
+
get_datetime=False,
|
444
|
+
get_version=False,
|
445
|
+
):
|
432
446
|
"""
|
433
447
|
Extract RPW file fields (assuming SolO file naming standards)
|
434
448
|
|
@@ -440,7 +454,7 @@ def extract_file_fields(rpw_file,
|
|
440
454
|
:param get_version: return only data version field
|
441
455
|
:return: list of file fields (or a scalar with expected field)
|
442
456
|
"""
|
443
|
-
fields = os.path.splitext(os.path.basename(rpw_file))[0].split(
|
457
|
+
fields = os.path.splitext(os.path.basename(rpw_file))[0].split("_")
|
444
458
|
|
445
459
|
if len(fields) < 5:
|
446
460
|
logger.warning(f'Cannot extract file fields: invalid input file "{rpw_file}"!')
|
@@ -497,12 +511,14 @@ class Map(dict):
|
|
497
511
|
del self.__dict__[key]
|
498
512
|
|
499
513
|
|
500
|
-
def glob_list(list_of_files):
|
514
|
+
def glob_list(list_of_files: list) -> list:
|
501
515
|
"""
|
502
516
|
Perform glob.glob on a list of input files.
|
503
517
|
|
504
|
-
:param list_of_files: List of input files (
|
518
|
+
:param list_of_files: List of input files (strings)
|
519
|
+
:type:list
|
505
520
|
:return: list of files globbed
|
521
|
+
:rtype: list
|
506
522
|
"""
|
507
523
|
output_list = []
|
508
524
|
for current_file in list_of_files:
|
@@ -526,7 +542,7 @@ def move_to_trash(file_or_dir):
|
|
526
542
|
is_file = False
|
527
543
|
|
528
544
|
try:
|
529
|
-
logger.debug(f
|
545
|
+
logger.debug(f"Moving {file_or_dir} into {trash_dir}")
|
530
546
|
target_path = os.path.join(trash_dir, os.path.basename(file_or_dir))
|
531
547
|
shutil.copyfile(file_or_dir, target_path)
|
532
548
|
if os.path.exists(target_path):
|
@@ -536,7 +552,7 @@ def move_to_trash(file_or_dir):
|
|
536
552
|
else:
|
537
553
|
shutil.rmtree(file_or_dir)
|
538
554
|
except Exception:
|
539
|
-
logger.exception(f
|
555
|
+
logger.exception(f"Moving {file_or_dir} into {trash_dir} has failed!")
|
540
556
|
target_path = None
|
541
557
|
|
542
558
|
return target_path
|
@@ -551,18 +567,17 @@ def get_trash_dir():
|
|
551
567
|
"""
|
552
568
|
|
553
569
|
# Get trash folder path
|
554
|
-
if
|
555
|
-
trash_dir = Configuration.manager[
|
556
|
-
|
557
|
-
|
558
|
-
trash_dir = os.environ['ROC_PIP_TRASH_DIR']
|
570
|
+
if "ROC_PIP_TRASH_DIR" in Configuration.manager["pipeline"]["environment"]:
|
571
|
+
trash_dir = Configuration.manager["pipeline"]["environment.ROC_PIP_TRASH_DIR"]
|
572
|
+
elif "ROC_PIP_TRASH_DIR" in os.environ:
|
573
|
+
trash_dir = os.environ["ROC_PIP_TRASH_DIR"]
|
559
574
|
else:
|
560
|
-
raise MissingProperty(
|
575
|
+
raise MissingProperty("ROC_PIP_TRASH_DIR variable is not defined!")
|
561
576
|
|
562
577
|
return trash_dir
|
563
578
|
|
564
579
|
|
565
|
-
def decode(binary, encoding=
|
580
|
+
def decode(binary, encoding="UTF-8"):
|
566
581
|
"""
|
567
582
|
Decode input binary into string.
|
568
583
|
|
@@ -575,10 +590,12 @@ def decode(binary, encoding='UTF-8'):
|
|
575
590
|
elif isinstance(binary, list):
|
576
591
|
return [element.decode(encoding) for element in binary]
|
577
592
|
elif isinstance(binary, np.ndarray):
|
593
|
+
|
578
594
|
def f(x):
|
579
595
|
return x.decode(encoding)
|
596
|
+
|
580
597
|
return np.vectorize(f)(binary)
|
581
598
|
elif isinstance(binary, bytes):
|
582
599
|
return binary.decode(encoding)
|
583
600
|
else:
|
584
|
-
raise ValueError(f
|
601
|
+
raise ValueError(f"Input binary type ({type(binary)}) is not valid!")
|