caom2utils 1.7.2__tar.gz → 1.7.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.
- {caom2utils-1.7.2/caom2utils.egg-info → caom2utils-1.7.3}/PKG-INFO +4 -3
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/blueprints.py +5 -1
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/data_util.py +3 -8
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/parsers.py +104 -47
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/test_data_util.py +0 -1
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/test_fits2caom2.py +17 -0
- caom2utils-1.7.3/caom2utils/version.py +1 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/wcs_parsers.py +3 -2
- {caom2utils-1.7.2 → caom2utils-1.7.3/caom2utils.egg-info}/PKG-INFO +4 -3
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils.egg-info/requires.txt +1 -1
- {caom2utils-1.7.2 → caom2utils-1.7.3}/setup.cfg +2 -2
- caom2utils-1.7.2/caom2utils/version.py +0 -1
- {caom2utils-1.7.2 → caom2utils-1.7.3}/LICENSE +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/MANIFEST.in +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/README.rst +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/__init__.py +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/caom2blueprint.py +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/caomvalidator.py +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/legacy.py +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/polygonvalidator.py +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/__init__.py +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/conftest.py +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/data/4axes.fits +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/data/4axes.override +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/data/4axes_obs.fits +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/data/SampleComposite-CAOM-2.3.xml +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/data/bad_product_id.txt +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/data/bad_product_id.xml +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/data/edge_case.blueprint +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/data/fits2caom2.config +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/data/help.txt +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/data/java.config +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/data/missing_observation_help.txt +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/data/missing_positional_argument_help.txt +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/data/missing_product_id.txt +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/data/nonconformant.py +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/data/test.override +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/data/test_plugin.py +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/data/test_plugin_class.py +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/data/text.override +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/data/time_axes.fits +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/data/too_few_arguments_help.txt +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/test_caomvalidator.py +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/test_collections.py +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/test_convert_from_java.py +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/test_custom_axis_util.py +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/test_obs_blueprint.py +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/test_polygonvalidator.py +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/test_si_uris.py +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/test_wcsvalidator.py +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/wcs_util.py +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/wcsvalidator.py +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils.egg-info/SOURCES.txt +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils.egg-info/dependency_links.txt +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils.egg-info/entry_points.txt +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils.egg-info/not-zip-safe +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils.egg-info/top_level.txt +0 -0
- {caom2utils-1.7.2 → caom2utils-1.7.3}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: caom2utils
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.3
|
|
4
4
|
Summary: CAOM-2.4 utils
|
|
5
5
|
Home-page: https://www.cadc-ccda.hia-iha.nrc-cnrc.gc.ca/caom2
|
|
6
6
|
Author: Canadian Astronomy Data Centre
|
|
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python
|
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
13
13
|
Requires-Python: >=3.8, <4
|
|
14
14
|
License-File: LICENSE
|
|
15
|
-
Requires-Dist: cadcdata>=2.
|
|
15
|
+
Requires-Dist: cadcdata>=2.5.2
|
|
16
16
|
Requires-Dist: caom2>=2.6
|
|
17
17
|
Requires-Dist: astropy>=2.0
|
|
18
18
|
Requires-Dist: spherical-geometry>=1.2.11
|
|
@@ -30,6 +30,7 @@ Dynamic: classifier
|
|
|
30
30
|
Dynamic: description
|
|
31
31
|
Dynamic: home-page
|
|
32
32
|
Dynamic: license
|
|
33
|
+
Dynamic: license-file
|
|
33
34
|
Dynamic: requires-python
|
|
34
35
|
Dynamic: summary
|
|
35
36
|
|
|
@@ -1187,10 +1187,14 @@ class Hdf5ObsBlueprint(ObsBlueprint):
|
|
|
1187
1187
|
# lookup value starting with // means rooted at base of the hdf5 file
|
|
1188
1188
|
ob.add_attribute('Observation.target.name', '//header/object/obj_id')
|
|
1189
1189
|
|
|
1190
|
-
# lookup value starting with / means rooted at the base of the
|
|
1190
|
+
# lookup value starting with / means rooted at the base of one of the extension_names parameter for Hdf5Parser
|
|
1191
1191
|
# (integer) means return only the value with the index of "integer" from a list
|
|
1192
1192
|
ob.add_attribute('Chunk.position.axis.function.refCoord.coord1.pix', '/header/wcs/crpix(0)')
|
|
1193
1193
|
|
|
1194
|
+
# lookup values starting with / and with "{}" in the path will cause the blueprint application to attempt to
|
|
1195
|
+
# guess the extension names from the file content
|
|
1196
|
+
ob.add_attribute('Chunk.position.axis.function.refCoord.coord1.pix', '/sitedata/site{}/header/wcs/crpix(0)')
|
|
1197
|
+
|
|
1194
1198
|
# (integer:integer) means return only the value with the index of "integer" from a list, followed by "integer"
|
|
1195
1199
|
# from the list in the list
|
|
1196
1200
|
ob.add_attribute('Chunk.position.axis.function.cd11', '/header/wcs/cd(0:0)')
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# ****************** CANADIAN ASTRONOMY DATA CENTRE *******************
|
|
3
3
|
# ************* CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES **************
|
|
4
4
|
#
|
|
5
|
-
# (c)
|
|
5
|
+
# (c) 2025. (c) 2025.
|
|
6
6
|
# Government of Canada Gouvernement du Canada
|
|
7
7
|
# National Research Council Conseil national de recherches
|
|
8
8
|
# Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6
|
|
@@ -188,24 +188,19 @@ class StorageClientWrapper:
|
|
|
188
188
|
self._logger.debug(f'Begin put for {uri} in {working_directory}')
|
|
189
189
|
start = self._current()
|
|
190
190
|
cwd = getcwd()
|
|
191
|
-
|
|
191
|
+
_, f_name = StorageClientWrapper._decompose(uri)
|
|
192
192
|
fqn = path.join(working_directory, f_name)
|
|
193
193
|
chdir(working_directory)
|
|
194
194
|
try:
|
|
195
195
|
local_meta = get_local_file_info(f_name)
|
|
196
196
|
encoding = get_file_encoding(f_name)
|
|
197
|
-
replace = True
|
|
198
|
-
cadc_meta = self.info(uri)
|
|
199
|
-
if cadc_meta is None:
|
|
200
|
-
replace = False
|
|
201
197
|
self._logger.debug(
|
|
202
|
-
f'uri {uri} src {fqn}
|
|
198
|
+
f'uri {uri} src {fqn} file_type {local_meta.file_type} encoding {encoding} '
|
|
203
199
|
f'md5_checksum {local_meta.md5sum}'
|
|
204
200
|
)
|
|
205
201
|
self._cadc_client.cadcput(
|
|
206
202
|
uri,
|
|
207
203
|
src=f_name,
|
|
208
|
-
replace=replace,
|
|
209
204
|
file_type=local_meta.file_type,
|
|
210
205
|
file_encoding=encoding,
|
|
211
206
|
md5_checksum=local_meta.md5sum,
|
|
@@ -284,7 +284,7 @@ class BlueprintParser:
|
|
|
284
284
|
self.logger.debug(f'Could not find \'{lookup}\' in caom2blueprint ' f'configuration.')
|
|
285
285
|
|
|
286
286
|
# if there's something useful as a value in the keywords, extract it
|
|
287
|
-
if keywords:
|
|
287
|
+
if keywords is not None and any(keywords):
|
|
288
288
|
if ObsBlueprint.needs_lookup(keywords):
|
|
289
289
|
# if there's a default value use it
|
|
290
290
|
if keywords[1]:
|
|
@@ -351,17 +351,18 @@ class BlueprintParser:
|
|
|
351
351
|
self.logger.debug(tb)
|
|
352
352
|
self.logger.error(e)
|
|
353
353
|
return result
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
354
|
+
if execute:
|
|
355
|
+
try:
|
|
356
|
+
result = execute(parameter)
|
|
357
|
+
self.logger.debug(f'Key {key} calculated value of {result} using {value} type {type(result)}')
|
|
358
|
+
except Exception as e:
|
|
359
|
+
msg = f'Failed to execute {execute.__name__} for {key} in {self.uri}'
|
|
360
|
+
self.logger.error(msg)
|
|
361
|
+
self.logger.debug(f'Input parameter was {parameter}, value was {value}')
|
|
362
|
+
self._errors.append(msg)
|
|
363
|
+
tb = traceback.format_exc()
|
|
364
|
+
self.logger.debug(tb)
|
|
365
|
+
self.logger.error(e)
|
|
365
366
|
return result
|
|
366
367
|
|
|
367
368
|
def _execute_external_instance(self, value, key, extension):
|
|
@@ -423,6 +424,7 @@ class BlueprintParser:
|
|
|
423
424
|
# CFHT 2003/03/29,01:34:54
|
|
424
425
|
# CFHT 2003/03/29
|
|
425
426
|
# DDO 12/02/95
|
|
427
|
+
# TAOSII 2024-01-26T14:52:49Z
|
|
426
428
|
for dt_format in [
|
|
427
429
|
'%Y-%m-%dT%H:%M:%S',
|
|
428
430
|
'%Y-%m-%dT%H:%M:%S.%f',
|
|
@@ -435,6 +437,7 @@ class BlueprintParser:
|
|
|
435
437
|
'%d/%m/%y',
|
|
436
438
|
'%d/%m/%y %H:%M:%S',
|
|
437
439
|
'%d-%m-%Y',
|
|
440
|
+
'%Y-%m-%dT%H:%M:%SZ',
|
|
438
441
|
]:
|
|
439
442
|
try:
|
|
440
443
|
result = datetime.strptime(from_value, dt_format)
|
|
@@ -545,13 +548,17 @@ class BlueprintParser:
|
|
|
545
548
|
if name:
|
|
546
549
|
prov = caom2.Provenance(name, p_version, project, producer, run_id, reference, last_executed)
|
|
547
550
|
ContentParser._add_keywords(keywords, current, prov)
|
|
548
|
-
if inputs:
|
|
551
|
+
if inputs is not None and any(inputs):
|
|
549
552
|
if isinstance(inputs, caom2.TypedSet):
|
|
550
553
|
for i in inputs:
|
|
551
554
|
prov.inputs.add(i)
|
|
552
555
|
else:
|
|
553
|
-
|
|
554
|
-
|
|
556
|
+
if isinstance(inputs, str):
|
|
557
|
+
for i in inputs.split():
|
|
558
|
+
prov.inputs.add(caom2.PlaneURI(str(i)))
|
|
559
|
+
else:
|
|
560
|
+
for i in inputs:
|
|
561
|
+
prov.inputs.add(caom2.PlaneURI(str(i)))
|
|
555
562
|
else:
|
|
556
563
|
if current is not None and len(current.inputs) > 0:
|
|
557
564
|
# preserve the original value
|
|
@@ -572,10 +579,13 @@ class BlueprintParser:
|
|
|
572
579
|
|
|
573
580
|
|
|
574
581
|
class ContentParser(BlueprintParser):
|
|
575
|
-
def __init__(self, obs_blueprint=None, uri=None):
|
|
582
|
+
def __init__(self, obs_blueprint=None, uri=None, extension_start_index=0, extension_end_index=None):
|
|
576
583
|
super().__init__(obs_blueprint, uri)
|
|
584
|
+
# for those cases where the extensions of interest are not all the extensions in the original file
|
|
585
|
+
self._extension_start_index = extension_start_index
|
|
586
|
+
self._extension_end_index = extension_end_index if extension_end_index else self._get_num_parts()
|
|
577
587
|
self._wcs_parsers = {}
|
|
578
|
-
self.
|
|
588
|
+
self._set_wcs_parsers(obs_blueprint)
|
|
579
589
|
|
|
580
590
|
def _get_chunk_naxis(self, chunk, index):
|
|
581
591
|
chunk.naxis = self._get_from_list('Chunk.naxis', index, self.blueprint.get_configed_axes_count())
|
|
@@ -585,6 +595,9 @@ class ContentParser(BlueprintParser):
|
|
|
585
595
|
"""
|
|
586
596
|
return len(self._blueprint._extensions) + 1
|
|
587
597
|
|
|
598
|
+
def _set_wcs_parsers(self, obs_blueprint):
|
|
599
|
+
self._wcs_parsers[0] = WcsParser(obs_blueprint, extension=self._extension_start_index)
|
|
600
|
+
|
|
588
601
|
def augment_artifact(self, artifact):
|
|
589
602
|
"""
|
|
590
603
|
Augments a given CAOM2 artifact with available content information
|
|
@@ -592,23 +605,26 @@ class ContentParser(BlueprintParser):
|
|
|
592
605
|
:param index: int Part name
|
|
593
606
|
"""
|
|
594
607
|
super().augment_artifact(artifact)
|
|
595
|
-
|
|
596
608
|
self.logger.debug(f'Begin content artifact augmentation for {artifact.uri}')
|
|
597
609
|
|
|
598
610
|
if self.blueprint.get_configed_axes_count() == 0:
|
|
599
611
|
raise TypeError(f'No WCS Data. End content artifact augmentation for ' f'{artifact.uri}.')
|
|
600
612
|
|
|
601
|
-
for index in range(
|
|
613
|
+
for index in range(self._extension_start_index, self._extension_end_index):
|
|
602
614
|
if self.add_parts(artifact, index):
|
|
603
615
|
part = artifact.parts[str(index)]
|
|
604
616
|
part.product_type = self._get_from_list('Part.productType', index)
|
|
605
|
-
part.meta_producer = self._get_from_list(
|
|
617
|
+
part.meta_producer = self._get_from_list(
|
|
618
|
+
'Part.metaProducer', index=self._extension_start_index, current=part.meta_producer
|
|
619
|
+
)
|
|
606
620
|
|
|
607
621
|
# each Part has one Chunk, if it's not an empty part as determined just previously
|
|
608
622
|
if not part.chunks:
|
|
609
623
|
part.chunks.append(caom2.Chunk())
|
|
610
624
|
chunk = part.chunks[0]
|
|
611
|
-
chunk.meta_producer = self._get_from_list(
|
|
625
|
+
chunk.meta_producer = self._get_from_list(
|
|
626
|
+
'Chunk.metaProducer', index=self._extension_start_index, current=chunk.meta_producer
|
|
627
|
+
)
|
|
612
628
|
|
|
613
629
|
self._get_chunk_naxis(chunk, index)
|
|
614
630
|
|
|
@@ -847,7 +863,8 @@ class ContentParser(BlueprintParser):
|
|
|
847
863
|
return members
|
|
848
864
|
|
|
849
865
|
def _get_axis_wcs(self, label, wcs, index):
|
|
850
|
-
"""Helper function to construct a CoordAxis1D instance, with all
|
|
866
|
+
"""Helper function to construct a CoordAxis1D instance, with all
|
|
867
|
+
it's members, from the blueprint.
|
|
851
868
|
|
|
852
869
|
:param label: axis name - must be one of 'custom', 'energy', 'time', or 'polarization', as it's used for the
|
|
853
870
|
blueprint lookup.
|
|
@@ -1460,7 +1477,7 @@ class FitsParser(ContentParser):
|
|
|
1460
1477
|
|
|
1461
1478
|
"""
|
|
1462
1479
|
|
|
1463
|
-
def __init__(self, src, obs_blueprint=None, uri=None):
|
|
1480
|
+
def __init__(self, src, obs_blueprint=None, uri=None, extension_start_index=0, extension_end_index=None):
|
|
1464
1481
|
"""
|
|
1465
1482
|
Ctor
|
|
1466
1483
|
:param src: List of headers (dictionary of FITS keywords:value) with one header for each extension or a FITS
|
|
@@ -1487,6 +1504,8 @@ class FitsParser(ContentParser):
|
|
|
1487
1504
|
self._errors = []
|
|
1488
1505
|
# for command-line parameter to module execution
|
|
1489
1506
|
self.uri = uri
|
|
1507
|
+
self._extension_start_index = extension_start_index
|
|
1508
|
+
self._extension_end_index = extension_end_index if extension_end_index is not None else self._get_num_parts()
|
|
1490
1509
|
self.apply_blueprint()
|
|
1491
1510
|
|
|
1492
1511
|
def _get_num_parts(self):
|
|
@@ -1845,22 +1864,19 @@ class Hdf5Parser(ContentParser):
|
|
|
1845
1864
|
- use the astropy.wcs instance and other blueprint metadata to fill the CAOM2 record.
|
|
1846
1865
|
"""
|
|
1847
1866
|
|
|
1848
|
-
def __init__(self, obs_blueprint, uri, h5_file,
|
|
1867
|
+
def __init__(self, obs_blueprint, uri, h5_file, extension_names=None, extension_start_index=0,
|
|
1868
|
+
extension_end_index=None):
|
|
1849
1869
|
"""
|
|
1850
1870
|
:param obs_blueprint: Hdf5ObsBlueprint instance
|
|
1851
1871
|
:param uri: which artifact augmentation is based on
|
|
1852
1872
|
:param h5_file: h5py file handle
|
|
1853
|
-
:param
|
|
1873
|
+
:param extension_names: list of str where Chunk metadata starts. There is one Part/Chunk per list entry
|
|
1854
1874
|
"""
|
|
1855
1875
|
self._file = h5_file
|
|
1856
|
-
#
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
super().__init__(obs_blueprint, uri)
|
|
1861
|
-
# used to set the astropy wcs info, resulting in a validated wcs that can be used to construct a valid CAOM2
|
|
1862
|
-
# record
|
|
1863
|
-
self._wcs_parsers = {}
|
|
1876
|
+
# the length of the array is the number of Parts in an HDF5 file,
|
|
1877
|
+
# and the values are HDF5 lookup path names.
|
|
1878
|
+
self._extension_names = extension_names
|
|
1879
|
+
super().__init__(obs_blueprint, uri, extension_start_index, extension_end_index)
|
|
1864
1880
|
|
|
1865
1881
|
def _get_num_parts(self):
|
|
1866
1882
|
"""return the number of Parts to create for a CAOM record
|
|
@@ -1871,6 +1887,13 @@ class Hdf5Parser(ContentParser):
|
|
|
1871
1887
|
result = 1
|
|
1872
1888
|
return result
|
|
1873
1889
|
|
|
1890
|
+
def _set_wcs_parsers(self, obs_blueprint):
|
|
1891
|
+
# used to set the astropy wcs info, resulting in a validated wcs that can be used to construct a valid CAOM2
|
|
1892
|
+
# record
|
|
1893
|
+
# This method call is over-writing the default behaviour in the ContentParser class. The default behaviour
|
|
1894
|
+
# uses the obs_blueprint. This method is called in the ContentParser constructor.
|
|
1895
|
+
self._wcs_parsers = {}
|
|
1896
|
+
|
|
1874
1897
|
def apply_blueprint_from_file(self):
|
|
1875
1898
|
"""
|
|
1876
1899
|
Retrieve metadata from file, cache in the blueprint.
|
|
@@ -1879,7 +1902,13 @@ class Hdf5Parser(ContentParser):
|
|
|
1879
1902
|
# h5py is an extra in this package since most collections do not require it
|
|
1880
1903
|
import h5py
|
|
1881
1904
|
|
|
1882
|
-
individual, multi, attributes = self._extract_path_names_from_blueprint()
|
|
1905
|
+
individual, multi, attributes, candidate_extensions = self._extract_path_names_from_blueprint()
|
|
1906
|
+
if self._extension_names is None and len(candidate_extensions) > 0:
|
|
1907
|
+
self._find_extension_names(candidate_extensions)
|
|
1908
|
+
for index, _ in enumerate(self._extension_names):
|
|
1909
|
+
self._blueprint._extensions[index] = {}
|
|
1910
|
+
else:
|
|
1911
|
+
self._blueprint._extensions[0] = {}
|
|
1883
1912
|
filtered_individual = [ii for ii in individual.keys() if '(' in ii]
|
|
1884
1913
|
|
|
1885
1914
|
def _extract_from_item(name, object):
|
|
@@ -1890,16 +1919,8 @@ class Hdf5Parser(ContentParser):
|
|
|
1890
1919
|
:param name: fully-qualified HDF5 path name
|
|
1891
1920
|
:param object: what the HDF5 path name points to
|
|
1892
1921
|
"""
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
# store the names and locations of the Part/Chunk metadata
|
|
1896
|
-
temp = f'{name}/{path_name}'
|
|
1897
|
-
self.logger.debug(f'Adding extension {temp}')
|
|
1898
|
-
self._extension_names.append(temp)
|
|
1899
|
-
self._blueprint._extensions[ii] = {}
|
|
1900
|
-
|
|
1901
|
-
# If it's the Part/Chunk metadata, capture it to extensions. Syntax of the keys described in
|
|
1902
|
-
# Hdf5ObsBlueprint class.
|
|
1922
|
+
# If it's the Part/Chunk metadata, capture it to extensions.
|
|
1923
|
+
# Syntax of the keys described in Hdf5ObsBlueprint class.
|
|
1903
1924
|
for part_index, part_name in enumerate(self._extension_names):
|
|
1904
1925
|
if name.startswith(part_name) and isinstance(object, h5py.Dataset) and object.dtype.names is not None:
|
|
1905
1926
|
for d_name in object.dtype.names:
|
|
@@ -1973,20 +1994,54 @@ class Hdf5Parser(ContentParser):
|
|
|
1973
1994
|
are _CAOM2_ELEMENT strings.
|
|
1974
1995
|
attributes - a dictionary of lists, keys reference expected content from the h5py.File().attrs data
|
|
1975
1996
|
structure and its keys.
|
|
1997
|
+
extensions - a list of prefixes for identifying extensions
|
|
1976
1998
|
"""
|
|
1977
1999
|
individual = defaultdict(list)
|
|
1978
2000
|
multi = defaultdict(list)
|
|
1979
2001
|
attributes = defaultdict(list)
|
|
2002
|
+
extensions = []
|
|
1980
2003
|
for key, value in self._blueprint._plan.items():
|
|
1981
2004
|
if ObsBlueprint.needs_lookup(value):
|
|
1982
2005
|
for ii in value[0]:
|
|
1983
2006
|
if ii.startswith('//'):
|
|
1984
2007
|
individual[ii].append(key)
|
|
1985
2008
|
elif ii.startswith('/'):
|
|
1986
|
-
|
|
2009
|
+
if '{}' in ii:
|
|
2010
|
+
bits = ii.split('{}')
|
|
2011
|
+
extensions.append(bits[0])
|
|
2012
|
+
multi[bits[1]].append(key)
|
|
2013
|
+
else:
|
|
2014
|
+
multi[ii].append(key)
|
|
1987
2015
|
else:
|
|
1988
2016
|
attributes[ii].append(key)
|
|
1989
|
-
|
|
2017
|
+
|
|
2018
|
+
return individual, multi, attributes, list(set(extensions))
|
|
2019
|
+
|
|
2020
|
+
def _find_extension_names(self, candidates):
|
|
2021
|
+
""" if the HDF5 file has a structure where-by more than one Chunk (the equivalent of a FITS HDU extension)
|
|
2022
|
+
is defined, try to guess that structure
|
|
2023
|
+
"""
|
|
2024
|
+
candidate_extension_names = []
|
|
2025
|
+
|
|
2026
|
+
def _extract_extension_prefixes(name, object):
|
|
2027
|
+
"""
|
|
2028
|
+
Function signature dictated by h5py visititems implementation. Executed for each dataset/group in an
|
|
2029
|
+
HDF5 file.
|
|
2030
|
+
|
|
2031
|
+
:param name: fully-qualified HDF5 path name
|
|
2032
|
+
:param object: what the HDF5 path name points to
|
|
2033
|
+
"""
|
|
2034
|
+
for part_name in candidates:
|
|
2035
|
+
y = part_name.replace('/', '', 1)
|
|
2036
|
+
if name.startswith(y):
|
|
2037
|
+
x = name.split(y)[1].split('/')
|
|
2038
|
+
temp = f'{y}{x[0]}'
|
|
2039
|
+
candidate_extension_names.append(temp)
|
|
2040
|
+
self._extension_names = list(sorted(set(candidate_extension_names)))
|
|
2041
|
+
|
|
2042
|
+
self._file.visititems(_extract_extension_prefixes)
|
|
2043
|
+
msg = '\n'.join(ii for ii in self._extension_names)
|
|
2044
|
+
self.logger.info(f'Found extension_names:\n{msg}')
|
|
1990
2045
|
|
|
1991
2046
|
def apply_blueprint(self):
|
|
1992
2047
|
self.logger.debug('Begin apply_blueprint')
|
|
@@ -2015,6 +2070,7 @@ class Hdf5Parser(ContentParser):
|
|
|
2015
2070
|
else:
|
|
2016
2071
|
exts[extension][key] = self._execute_external_instance(value, key, extension)
|
|
2017
2072
|
|
|
2073
|
+
# apply overrides
|
|
2018
2074
|
# blueprint already contains all the overrides, only need to make sure the overrides get applied to all the
|
|
2019
2075
|
# extensions
|
|
2020
2076
|
for extension in exts:
|
|
@@ -2032,6 +2088,7 @@ class Hdf5Parser(ContentParser):
|
|
|
2032
2088
|
exts[extension][key] = value
|
|
2033
2089
|
self.logger.debug(f'{key}: set to {value} in extension {extension}')
|
|
2034
2090
|
|
|
2091
|
+
# apply defaults
|
|
2035
2092
|
# if no values have been set by file lookups, function execution, or applying overrides, apply defaults,
|
|
2036
2093
|
# including to all extensions
|
|
2037
2094
|
for key, value in plan.items():
|
|
@@ -2054,7 +2111,7 @@ class Hdf5Parser(ContentParser):
|
|
|
2054
2111
|
return
|
|
2055
2112
|
|
|
2056
2113
|
def augment_artifact(self, artifact):
|
|
2057
|
-
for ii in range(
|
|
2114
|
+
for ii in range(self._extension_start_index, self._extension_end_index):
|
|
2058
2115
|
# one WCS parser per Part/Chunk
|
|
2059
2116
|
self._wcs_parsers[ii] = Hdf5WcsParser(self._blueprint, ii)
|
|
2060
2117
|
super().augment_artifact(artifact)
|
|
@@ -257,7 +257,6 @@ def _check_put_result(client_mock):
|
|
|
257
257
|
client_mock.assert_called_with(
|
|
258
258
|
'cadc:TEST/test_file.fits',
|
|
259
259
|
src='test_file.fits',
|
|
260
|
-
replace=True,
|
|
261
260
|
file_type='application/fits',
|
|
262
261
|
file_encoding=None,
|
|
263
262
|
md5_checksum='3c66ee2cb6e0c2cfb5cd6824d353dc11',
|
|
@@ -1412,6 +1412,23 @@ def test_generic_parser1():
|
|
|
1412
1412
|
assert test_parser._blueprint._plan[test_key] == test_value, 'original value over-ridden'
|
|
1413
1413
|
|
|
1414
1414
|
|
|
1415
|
+
def test_generic_parser_imported_module_error_handling():
|
|
1416
|
+
# this test exercises the error handling code for executing functions defined by blueprints
|
|
1417
|
+
test_key = 'Plane.metaRelease'
|
|
1418
|
+
test_key_2 = 'Plane.dataRelease'
|
|
1419
|
+
test_value = '2013-10-10'
|
|
1420
|
+
test_blueprint = ObsBlueprint()
|
|
1421
|
+
test_blueprint.set(test_key, '2013-10-10')
|
|
1422
|
+
# pick __sizeof__ as an attribute that will fail to execute for any module
|
|
1423
|
+
test_blueprint.set(test_key_2, '__sizeof__()')
|
|
1424
|
+
test_parser = BlueprintParser()
|
|
1425
|
+
assert test_parser._blueprint._plan[test_key] == (['RELEASE', 'REL_DATE'], None), 'default value changed'
|
|
1426
|
+
test_parser.blueprint = test_blueprint
|
|
1427
|
+
assert test_parser._blueprint._plan[test_key] == test_value, 'original value over-ridden'
|
|
1428
|
+
test_result = test_parser._execute_external('__sizeof__(uri)', test_key_2, 0)
|
|
1429
|
+
assert test_result == '', 'wrong result'
|
|
1430
|
+
|
|
1431
|
+
|
|
1415
1432
|
def test_get_external_headers():
|
|
1416
1433
|
test_uri = 'http://localhost/obs23/collection/obsid-1'
|
|
1417
1434
|
with patch('requests.Session.get') as session_get_mock:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
version = '1.7.3'
|
|
@@ -644,8 +644,9 @@ class WcsParser:
|
|
|
644
644
|
else:
|
|
645
645
|
result = self._wcs.array_shape[for_axis - 1]
|
|
646
646
|
if isinstance(result, tuple):
|
|
647
|
-
|
|
648
|
-
|
|
647
|
+
raise ValueError(
|
|
648
|
+
f'Could not find axis length for axis {for_axis}. The blueprint is incompletely configured.'
|
|
649
|
+
)
|
|
649
650
|
return _to_int(result)
|
|
650
651
|
|
|
651
652
|
def _get_cd(self, x_index, y_index):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: caom2utils
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.3
|
|
4
4
|
Summary: CAOM-2.4 utils
|
|
5
5
|
Home-page: https://www.cadc-ccda.hia-iha.nrc-cnrc.gc.ca/caom2
|
|
6
6
|
Author: Canadian Astronomy Data Centre
|
|
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python
|
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
13
13
|
Requires-Python: >=3.8, <4
|
|
14
14
|
License-File: LICENSE
|
|
15
|
-
Requires-Dist: cadcdata>=2.
|
|
15
|
+
Requires-Dist: cadcdata>=2.5.2
|
|
16
16
|
Requires-Dist: caom2>=2.6
|
|
17
17
|
Requires-Dist: astropy>=2.0
|
|
18
18
|
Requires-Dist: spherical-geometry>=1.2.11
|
|
@@ -30,6 +30,7 @@ Dynamic: classifier
|
|
|
30
30
|
Dynamic: description
|
|
31
31
|
Dynamic: home-page
|
|
32
32
|
Dynamic: license
|
|
33
|
+
Dynamic: license-file
|
|
33
34
|
Dynamic: requires-python
|
|
34
35
|
Dynamic: summary
|
|
35
36
|
|
|
@@ -32,11 +32,11 @@ license = AGPLv3
|
|
|
32
32
|
url = https://www.cadc-ccda.hia-iha.nrc-cnrc.gc.ca/caom2
|
|
33
33
|
edit_on_github = False
|
|
34
34
|
github_project = opencadc/caom2tools
|
|
35
|
-
version = 1.7.
|
|
35
|
+
version = 1.7.3
|
|
36
36
|
|
|
37
37
|
[options]
|
|
38
38
|
install_requires =
|
|
39
|
-
cadcdata>=2.
|
|
39
|
+
cadcdata>=2.5.2
|
|
40
40
|
caom2>=2.6
|
|
41
41
|
astropy>=2.0
|
|
42
42
|
spherical-geometry>=1.2.11
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
version = '1.7.2'
|
|
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
|
{caom2utils-1.7.2 → caom2utils-1.7.3}/caom2utils/tests/data/missing_positional_argument_help.txt
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
|