astro-metadata-translator 29.2025.4300__py3-none-any.whl → 29.2025.4800__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.
- astro_metadata_translator/bin/translate.py +18 -13
- astro_metadata_translator/bin/writeindex.py +14 -9
- astro_metadata_translator/bin/writesidecar.py +14 -38
- astro_metadata_translator/file_helpers.py +106 -64
- astro_metadata_translator/indexing.py +28 -15
- astro_metadata_translator/tests.py +20 -12
- astro_metadata_translator/translator.py +3 -2
- astro_metadata_translator/translators/decam.py +7 -3
- astro_metadata_translator/translators/helpers.py +14 -0
- astro_metadata_translator/translators/megaprime.py +7 -3
- astro_metadata_translator/version.py +1 -1
- {astro_metadata_translator-29.2025.4300.dist-info → astro_metadata_translator-29.2025.4800.dist-info}/METADATA +6 -5
- {astro_metadata_translator-29.2025.4300.dist-info → astro_metadata_translator-29.2025.4800.dist-info}/RECORD +18 -18
- {astro_metadata_translator-29.2025.4300.dist-info → astro_metadata_translator-29.2025.4800.dist-info}/WHEEL +0 -0
- {astro_metadata_translator-29.2025.4300.dist-info → astro_metadata_translator-29.2025.4800.dist-info}/entry_points.txt +0 -0
- {astro_metadata_translator-29.2025.4300.dist-info → astro_metadata_translator-29.2025.4800.dist-info}/licenses/LICENSE +0 -0
- {astro_metadata_translator-29.2025.4300.dist-info → astro_metadata_translator-29.2025.4800.dist-info}/top_level.txt +0 -0
- {astro_metadata_translator-29.2025.4300.dist-info → astro_metadata_translator-29.2025.4800.dist-info}/zip-safe +0 -0
|
@@ -23,18 +23,22 @@ import math
|
|
|
23
23
|
import traceback
|
|
24
24
|
from collections import defaultdict
|
|
25
25
|
from collections.abc import Sequence
|
|
26
|
-
from typing import IO, Any
|
|
26
|
+
from typing import IO, TYPE_CHECKING, Any
|
|
27
27
|
|
|
28
28
|
import astropy.time
|
|
29
29
|
import astropy.units as u
|
|
30
30
|
import yaml
|
|
31
31
|
from astropy.table import Column, MaskedColumn, QTable
|
|
32
|
+
from lsst.resources import ResourcePath
|
|
32
33
|
|
|
33
34
|
from astro_metadata_translator import MetadataTranslator, ObservationInfo, fix_header
|
|
34
35
|
|
|
35
36
|
from ..file_helpers import find_files, read_basic_metadata_from_file
|
|
36
37
|
from ..properties import PROPERTIES
|
|
37
38
|
|
|
39
|
+
if TYPE_CHECKING:
|
|
40
|
+
from lsst.resources import ResourcePathExpression
|
|
41
|
+
|
|
38
42
|
log = logging.getLogger(__name__)
|
|
39
43
|
|
|
40
44
|
# Output mode choices
|
|
@@ -74,7 +78,7 @@ TABLE_COLUMNS = (
|
|
|
74
78
|
|
|
75
79
|
|
|
76
80
|
def read_file(
|
|
77
|
-
file:
|
|
81
|
+
file: ResourcePathExpression,
|
|
78
82
|
hdrnum: int,
|
|
79
83
|
print_trace: bool,
|
|
80
84
|
output_columns: defaultdict[str, list],
|
|
@@ -86,7 +90,7 @@ def read_file(
|
|
|
86
90
|
|
|
87
91
|
Parameters
|
|
88
92
|
----------
|
|
89
|
-
file : `str`
|
|
93
|
+
file : `str` or `lsst.resources.ResourcePathExpression`
|
|
90
94
|
The file from which the header is to be read.
|
|
91
95
|
hdrnum : `int`
|
|
92
96
|
The HDU number to read. The primary header is always read and
|
|
@@ -127,10 +131,11 @@ def read_file(
|
|
|
127
131
|
if output_mode != "table":
|
|
128
132
|
log.info("Analyzing %s...", file)
|
|
129
133
|
|
|
134
|
+
uri = ResourcePath(file, forceDirectory=False)
|
|
130
135
|
try:
|
|
131
|
-
md = read_basic_metadata_from_file(
|
|
136
|
+
md = read_basic_metadata_from_file(uri, hdrnum, can_raise=True)
|
|
132
137
|
if md is None:
|
|
133
|
-
raise RuntimeError(f"Failed to read file {
|
|
138
|
+
raise RuntimeError(f"Failed to read file {uri} HDU={hdrnum}")
|
|
134
139
|
|
|
135
140
|
if output_mode.endswith("native"):
|
|
136
141
|
# Strip native and don't change type of md
|
|
@@ -141,21 +146,21 @@ def read_file(
|
|
|
141
146
|
|
|
142
147
|
if output_mode in ("yaml", "fixed"):
|
|
143
148
|
if output_mode == "fixed":
|
|
144
|
-
fix_header(md, filename=
|
|
149
|
+
fix_header(md, filename=str(uri))
|
|
145
150
|
|
|
146
151
|
# The header should be written out in the insertion order
|
|
147
152
|
print(yaml.dump(md, sort_keys=False), file=outstream)
|
|
148
153
|
return True
|
|
149
154
|
|
|
150
155
|
# Try to work out a translator class.
|
|
151
|
-
translator_class = MetadataTranslator.determine_translator(md, filename=
|
|
156
|
+
translator_class = MetadataTranslator.determine_translator(md, filename=str(uri))
|
|
152
157
|
|
|
153
158
|
# Work out which headers to translate, assuming the default if
|
|
154
159
|
# we have a YAML test file.
|
|
155
|
-
if
|
|
160
|
+
if uri.getExtension() == ".yaml":
|
|
156
161
|
headers = [md]
|
|
157
162
|
else:
|
|
158
|
-
headers = list(translator_class.determine_translatable_headers(
|
|
163
|
+
headers = list(translator_class.determine_translatable_headers(uri, md))
|
|
159
164
|
if output_mode == "auto":
|
|
160
165
|
output_mode = "table" if len(headers) > 1 else "verbose"
|
|
161
166
|
|
|
@@ -163,7 +168,7 @@ def read_file(
|
|
|
163
168
|
raise ValueError("Table mode requires output columns")
|
|
164
169
|
|
|
165
170
|
for md in headers:
|
|
166
|
-
obs_info = ObservationInfo(md, pedantic=False, filename=
|
|
171
|
+
obs_info = ObservationInfo(md, pedantic=False, filename=str(uri))
|
|
167
172
|
if output_mode == "table":
|
|
168
173
|
for c in TABLE_COLUMNS:
|
|
169
174
|
output_columns[c["label"]].append(getattr(obs_info, c["attr"]))
|
|
@@ -178,7 +183,7 @@ def read_file(
|
|
|
178
183
|
if print_trace:
|
|
179
184
|
traceback.print_exc(file=outstream)
|
|
180
185
|
else:
|
|
181
|
-
print(f"Failure processing {
|
|
186
|
+
print(f"Failure processing {uri}: {e}", file=outstream)
|
|
182
187
|
return False
|
|
183
188
|
return True
|
|
184
189
|
|
|
@@ -308,9 +313,9 @@ def translate_or_dump_headers(
|
|
|
308
313
|
isok = read_file(path, hdrnum, print_trace, output_columns, outstream, output_mode, heading)
|
|
309
314
|
heading = False
|
|
310
315
|
if isok:
|
|
311
|
-
okay.append(path)
|
|
316
|
+
okay.append(str(path))
|
|
312
317
|
else:
|
|
313
|
-
failed.append(path)
|
|
318
|
+
failed.append(str(path))
|
|
314
319
|
|
|
315
320
|
# Check if we have exceeded the page size and should dump the table.
|
|
316
321
|
if output_mode == "table" and len(output_columns[TABLE_COLUMNS[0]["label"]]) >= MAX_TABLE_PAGE_SIZE:
|
|
@@ -19,6 +19,8 @@ import os
|
|
|
19
19
|
from collections.abc import MutableMapping, Sequence
|
|
20
20
|
from typing import IO
|
|
21
21
|
|
|
22
|
+
from lsst.resources import ResourcePath
|
|
23
|
+
|
|
22
24
|
from ..file_helpers import find_files
|
|
23
25
|
from ..indexing import index_files
|
|
24
26
|
|
|
@@ -87,15 +89,19 @@ def write_index_files(
|
|
|
87
89
|
|
|
88
90
|
failed = []
|
|
89
91
|
okay = []
|
|
90
|
-
files_per_directory: MutableMapping[
|
|
92
|
+
files_per_directory: MutableMapping[ResourcePath, list[ResourcePath]] = {}
|
|
91
93
|
|
|
92
94
|
# Group each file by directory if no explicit output path
|
|
93
95
|
if outpath is None:
|
|
94
96
|
for path in found_files:
|
|
95
|
-
head, tail =
|
|
96
|
-
files_per_directory.setdefault(head, []).append(tail)
|
|
97
|
+
head, tail = path.split()
|
|
98
|
+
files_per_directory.setdefault(head, []).append(ResourcePath(tail, forceAbsolute=False))
|
|
97
99
|
else:
|
|
98
|
-
|
|
100
|
+
# We want the requested files to be paths relative to the current
|
|
101
|
+
# directory. For now this assumes that all the input files are
|
|
102
|
+
# local -- we are not trying to discover a shared root directory.
|
|
103
|
+
cwd = ResourcePath(".", forceAbsolute=True, forceDirectory=True)
|
|
104
|
+
files_per_directory[cwd] = list(found_files)
|
|
99
105
|
|
|
100
106
|
# Extract translated metadata for each file in each directory
|
|
101
107
|
for directory, files_in_dir in files_per_directory.items():
|
|
@@ -113,11 +119,10 @@ def write_index_files(
|
|
|
113
119
|
|
|
114
120
|
# Write the index file
|
|
115
121
|
if outpath is None:
|
|
116
|
-
index_file =
|
|
122
|
+
index_file = directory.join("_index.json")
|
|
117
123
|
else:
|
|
118
|
-
index_file = outpath
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
log.info("Wrote index file to %s", index_file)
|
|
124
|
+
index_file = ResourcePath(outpath, forceAbsolute=False)
|
|
125
|
+
index_file.write(json.dumps(output).encode())
|
|
126
|
+
log.info("Wrote index file to %s", index_file)
|
|
122
127
|
|
|
123
128
|
return okay, failed
|
|
@@ -14,45 +14,22 @@ from __future__ import annotations
|
|
|
14
14
|
__all__ = ("write_sidecar_file", "write_sidecar_files")
|
|
15
15
|
|
|
16
16
|
import logging
|
|
17
|
-
import os
|
|
18
17
|
import traceback
|
|
19
18
|
from collections.abc import Sequence
|
|
20
|
-
from typing import IO
|
|
19
|
+
from typing import IO, TYPE_CHECKING
|
|
21
20
|
|
|
22
|
-
from
|
|
23
|
-
|
|
24
|
-
log = logging.getLogger(__name__)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def _split_ext(file: str) -> tuple[str, str]:
|
|
28
|
-
"""Split the extension from the file name and return it and the root.
|
|
29
|
-
|
|
30
|
-
Parameters
|
|
31
|
-
----------
|
|
32
|
-
file : `str`
|
|
33
|
-
The file name to examine.
|
|
21
|
+
from lsst.resources import ResourcePath
|
|
34
22
|
|
|
35
|
-
|
|
36
|
-
-------
|
|
37
|
-
root : `str`
|
|
38
|
-
The root of the file name.
|
|
39
|
-
ext : `str`
|
|
40
|
-
The file extension. There is special case handling of .gz and other
|
|
41
|
-
compression extensions.
|
|
42
|
-
"""
|
|
43
|
-
special = {".gz", ".bz2", ".xz", ".fz"}
|
|
44
|
-
|
|
45
|
-
root, ext = os.path.splitext(file)
|
|
23
|
+
from ..file_helpers import find_files, read_file_info
|
|
46
24
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
ext = second_ext + ext
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from lsst.resources import ResourcePathExpression
|
|
50
27
|
|
|
51
|
-
|
|
28
|
+
log = logging.getLogger(__name__)
|
|
52
29
|
|
|
53
30
|
|
|
54
31
|
def write_sidecar_file(
|
|
55
|
-
file:
|
|
32
|
+
file: ResourcePathExpression,
|
|
56
33
|
hdrnum: int,
|
|
57
34
|
content_mode: str,
|
|
58
35
|
print_trace: bool,
|
|
@@ -62,7 +39,7 @@ def write_sidecar_file(
|
|
|
62
39
|
|
|
63
40
|
Parameters
|
|
64
41
|
----------
|
|
65
|
-
file : `str`
|
|
42
|
+
file : `str` or `lsst.resources.ResourcePathExpression`
|
|
66
43
|
The file from which the header is to be read.
|
|
67
44
|
hdrnum : `int`
|
|
68
45
|
The HDU number to read. The primary header is always read and
|
|
@@ -103,12 +80,11 @@ def write_sidecar_file(
|
|
|
103
80
|
return False
|
|
104
81
|
|
|
105
82
|
# Calculate sidecar file name derived from this file.
|
|
106
|
-
#
|
|
83
|
+
# .fits.gz should be replaced
|
|
107
84
|
# with .json, and not resulting in .fits.json.
|
|
108
|
-
|
|
109
|
-
newfile =
|
|
110
|
-
|
|
111
|
-
print(json_str, file=fd)
|
|
85
|
+
uri = ResourcePath(file)
|
|
86
|
+
newfile = uri.updatedExtension(".json")
|
|
87
|
+
newfile.write(str(json_str).encode())
|
|
112
88
|
log.debug("Writing sidecar file %s", newfile)
|
|
113
89
|
|
|
114
90
|
except Exception as e:
|
|
@@ -169,8 +145,8 @@ def write_sidecar_files(
|
|
|
169
145
|
for path in sorted(found_files):
|
|
170
146
|
isok = write_sidecar_file(path, hdrnum, content_mode, print_trace, outstream)
|
|
171
147
|
if isok:
|
|
172
|
-
okay.append(path)
|
|
148
|
+
okay.append(str(path))
|
|
173
149
|
else:
|
|
174
|
-
failed.append(path)
|
|
150
|
+
failed.append(str(path))
|
|
175
151
|
|
|
176
152
|
return okay, failed
|
|
@@ -20,16 +20,21 @@ __all__ = ("find_files", "read_basic_metadata_from_file", "read_file_info")
|
|
|
20
20
|
|
|
21
21
|
import json
|
|
22
22
|
import logging
|
|
23
|
-
import os
|
|
24
23
|
import re
|
|
25
24
|
import traceback
|
|
26
25
|
from collections.abc import Iterable, MutableMapping
|
|
27
|
-
from typing import IO, Any
|
|
26
|
+
from typing import IO, TYPE_CHECKING, Any
|
|
27
|
+
|
|
28
|
+
from astropy.io import fits
|
|
29
|
+
from lsst.resources import ResourcePath
|
|
28
30
|
|
|
29
31
|
from .headers import merge_headers
|
|
30
32
|
from .observationInfo import ObservationInfo
|
|
31
33
|
from .tests import read_test_file
|
|
32
34
|
|
|
35
|
+
if TYPE_CHECKING:
|
|
36
|
+
from lsst.resources import ResourcePathExpression
|
|
37
|
+
|
|
33
38
|
log = logging.getLogger(__name__)
|
|
34
39
|
|
|
35
40
|
# Prefer afw over Astropy
|
|
@@ -37,30 +42,15 @@ try:
|
|
|
37
42
|
import lsst.daf.base # noqa: F401 need PropertyBase for readMetadata
|
|
38
43
|
from lsst.afw.fits import FitsError, readMetadata
|
|
39
44
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
can_raise : `bool`, optional
|
|
50
|
-
Indicate whether the function can raise and exception (default)
|
|
51
|
-
or should return `None` on error. Can still raise if an unexpected
|
|
52
|
-
error is encountered.
|
|
53
|
-
|
|
54
|
-
Returns
|
|
55
|
-
-------
|
|
56
|
-
md : `dict`
|
|
57
|
-
The requested header. `None` if it could not be read and
|
|
58
|
-
``can_raise`` is `False`.
|
|
59
|
-
|
|
60
|
-
Notes
|
|
61
|
-
-----
|
|
62
|
-
Tries to catch a FitsError 104 and convert to `FileNotFoundError`.
|
|
63
|
-
"""
|
|
45
|
+
have_afw = True
|
|
46
|
+
|
|
47
|
+
def _read_fits_metadata_afw(
|
|
48
|
+
file: str, hdu: int, can_raise: bool = False
|
|
49
|
+
) -> MutableMapping[str, Any] | None:
|
|
50
|
+
# Only works with local files.
|
|
51
|
+
# Tries to catch a FitsError 104 and convert to `FileNotFoundError`.
|
|
52
|
+
# For detailed docstrings see the _read_fits_metadata implementation
|
|
53
|
+
# below.
|
|
64
54
|
try:
|
|
65
55
|
return readMetadata(file, hdu=hdu)
|
|
66
56
|
except FitsError as e:
|
|
@@ -72,31 +62,84 @@ try:
|
|
|
72
62
|
return None
|
|
73
63
|
|
|
74
64
|
except ImportError:
|
|
75
|
-
|
|
65
|
+
have_afw = False
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _read_fits_metadata_astropy(
|
|
69
|
+
file: ResourcePathExpression, hdu: int, can_raise: bool = False
|
|
70
|
+
) -> MutableMapping[str, Any] | None:
|
|
71
|
+
"""Read a FITS header using astropy.
|
|
72
|
+
|
|
73
|
+
Parameters
|
|
74
|
+
----------
|
|
75
|
+
file : `str` or `lsst.resources.ResourcePathExpression`
|
|
76
|
+
The file to read.
|
|
77
|
+
hdu : `int`
|
|
78
|
+
The header number to read.
|
|
79
|
+
can_raise : `bool`, optional
|
|
80
|
+
Indicate whether the function can raise and exception (default)
|
|
81
|
+
or should return `None` on error. Can still raise if an unexpected
|
|
82
|
+
error is encountered.
|
|
83
|
+
|
|
84
|
+
Returns
|
|
85
|
+
-------
|
|
86
|
+
md : `dict`
|
|
87
|
+
The requested header. `None` if it could not be read and
|
|
88
|
+
``can_raise`` is `False`.
|
|
89
|
+
"""
|
|
90
|
+
header = None
|
|
91
|
+
uri = ResourcePath(file, forceDirectory=False)
|
|
92
|
+
try:
|
|
93
|
+
fs, fspath = uri.to_fsspec()
|
|
94
|
+
with fs.open(fspath) as f, fits.open(f) as fits_file:
|
|
95
|
+
try:
|
|
96
|
+
# Copy forces a download of the remote resource.
|
|
97
|
+
header = fits_file[hdu].header.copy()
|
|
98
|
+
except IndexError as e:
|
|
99
|
+
if can_raise:
|
|
100
|
+
raise e
|
|
101
|
+
except Exception as e:
|
|
102
|
+
if can_raise:
|
|
103
|
+
raise e
|
|
104
|
+
return header
|
|
76
105
|
|
|
77
|
-
def _read_fits_metadata(file: str, hdu: int, can_raise: bool = False) -> MutableMapping[str, Any] | None:
|
|
78
|
-
"""Read a FITS header using astropy."""
|
|
79
|
-
# For detailed docstrings see the afw implementation above
|
|
80
|
-
header = None
|
|
81
|
-
try:
|
|
82
|
-
with fits.open(file) as fits_file:
|
|
83
|
-
try:
|
|
84
|
-
header = fits_file[hdu].header
|
|
85
|
-
except IndexError as e:
|
|
86
|
-
if can_raise:
|
|
87
|
-
raise e
|
|
88
|
-
except Exception as e:
|
|
89
|
-
if can_raise:
|
|
90
|
-
raise e
|
|
91
|
-
return header
|
|
92
106
|
|
|
107
|
+
def _read_fits_metadata(
|
|
108
|
+
file: ResourcePathExpression, hdu: int, can_raise: bool = False
|
|
109
|
+
) -> MutableMapping[str, Any] | None:
|
|
110
|
+
"""Read a FITS header using afw or astropy.
|
|
111
|
+
|
|
112
|
+
Prefer afw for local reads if available.
|
|
93
113
|
|
|
94
|
-
|
|
114
|
+
Parameters
|
|
115
|
+
----------
|
|
116
|
+
file : `str` or `lsst.resources.ResourcePathExpression`
|
|
117
|
+
The file to read.
|
|
118
|
+
hdu : `int`
|
|
119
|
+
The header number to read.
|
|
120
|
+
can_raise : `bool`, optional
|
|
121
|
+
Indicate whether the function can raise and exception (default)
|
|
122
|
+
or should return `None` on error. Can still raise if an unexpected
|
|
123
|
+
error is encountered.
|
|
124
|
+
|
|
125
|
+
Returns
|
|
126
|
+
-------
|
|
127
|
+
md : `dict`
|
|
128
|
+
The requested header. `None` if it could not be read and
|
|
129
|
+
``can_raise`` is `False`.
|
|
130
|
+
"""
|
|
131
|
+
uri = ResourcePath(file, forceAbsolute=False)
|
|
132
|
+
if have_afw and uri.isLocal:
|
|
133
|
+
return _read_fits_metadata_afw(uri.ospath, hdu, can_raise=can_raise)
|
|
134
|
+
return _read_fits_metadata_astropy(uri, hdu, can_raise=can_raise)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def find_files(files: Iterable[ResourcePathExpression], regex: str) -> list[ResourcePath]:
|
|
95
138
|
"""Find files for processing.
|
|
96
139
|
|
|
97
140
|
Parameters
|
|
98
141
|
----------
|
|
99
|
-
files : iterable of `
|
|
142
|
+
files : iterable of `lsst.resources.ResourcePathExpression`
|
|
100
143
|
The files or directories from which the headers are to be read.
|
|
101
144
|
regex : `str`
|
|
102
145
|
Regular expression string used to filter files when a directory is
|
|
@@ -104,34 +147,31 @@ def find_files(files: Iterable[str], regex: str) -> list[str]:
|
|
|
104
147
|
|
|
105
148
|
Returns
|
|
106
149
|
-------
|
|
107
|
-
found_files : `list` of `
|
|
150
|
+
found_files : `list` of `lsst.resources.ResourcePath`
|
|
108
151
|
The files that were found.
|
|
109
152
|
"""
|
|
110
153
|
file_regex = re.compile(regex)
|
|
111
|
-
found_files = []
|
|
154
|
+
found_files: list[ResourcePath] = []
|
|
112
155
|
|
|
113
156
|
# Find all the files of interest
|
|
114
|
-
for
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
path = os.path.join(root, name)
|
|
119
|
-
if os.path.isfile(path) and file_regex.search(name):
|
|
120
|
-
found_files.append(path)
|
|
157
|
+
for candidate in files:
|
|
158
|
+
uri = ResourcePath(candidate, forceAbsolute=False)
|
|
159
|
+
if uri.isdir():
|
|
160
|
+
found_files.extend(ResourcePath.findFileResources([uri], file_filter=file_regex, grouped=False))
|
|
121
161
|
else:
|
|
122
|
-
found_files.append(
|
|
162
|
+
found_files.append(uri)
|
|
123
163
|
|
|
124
164
|
return found_files
|
|
125
165
|
|
|
126
166
|
|
|
127
167
|
def read_basic_metadata_from_file(
|
|
128
|
-
file:
|
|
168
|
+
file: ResourcePathExpression, hdrnum: int, can_raise: bool = True
|
|
129
169
|
) -> MutableMapping[str, Any] | None:
|
|
130
170
|
"""Read a raw header from a file, merging if necessary.
|
|
131
171
|
|
|
132
172
|
Parameters
|
|
133
173
|
----------
|
|
134
|
-
file : `str`
|
|
174
|
+
file : `str` or `lsst.resources.ResourcePathExpression`
|
|
135
175
|
Name of file to read. Can be FITS, YAML or JSON. YAML or JSON must be
|
|
136
176
|
a simple top-level dict.
|
|
137
177
|
hdrnum : `int`
|
|
@@ -151,10 +191,11 @@ def read_basic_metadata_from_file(
|
|
|
151
191
|
The header as a dict. Can be `None` if there was a problem reading
|
|
152
192
|
the file.
|
|
153
193
|
"""
|
|
154
|
-
|
|
194
|
+
uri = ResourcePath(file, forceAbsolute=False)
|
|
195
|
+
if uri.getExtension() in (".yaml", ".json"):
|
|
155
196
|
try:
|
|
156
197
|
md = read_test_file(
|
|
157
|
-
|
|
198
|
+
uri,
|
|
158
199
|
)
|
|
159
200
|
except Exception as e:
|
|
160
201
|
if not can_raise:
|
|
@@ -165,7 +206,7 @@ def read_basic_metadata_from_file(
|
|
|
165
206
|
# YAML can't have HDUs so skip merging below
|
|
166
207
|
hdrnum = 0
|
|
167
208
|
else:
|
|
168
|
-
md = _read_fits_metadata(
|
|
209
|
+
md = _read_fits_metadata(uri, 0, can_raise=can_raise)
|
|
169
210
|
if md is None:
|
|
170
211
|
log.warning("Unable to open file %s", file)
|
|
171
212
|
return None
|
|
@@ -174,19 +215,19 @@ def read_basic_metadata_from_file(
|
|
|
174
215
|
hdrnum = 1
|
|
175
216
|
if hdrnum > 0:
|
|
176
217
|
# Allow this to fail
|
|
177
|
-
mdn = _read_fits_metadata(
|
|
218
|
+
mdn = _read_fits_metadata(uri, int(hdrnum), can_raise=False)
|
|
178
219
|
# Astropy does not allow append mode since it does not
|
|
179
220
|
# convert lists to multiple cards. Overwrite for now
|
|
180
221
|
if mdn is not None:
|
|
181
222
|
md = merge_headers([md, mdn], mode="overwrite")
|
|
182
223
|
else:
|
|
183
|
-
log.warning("HDU %d was not found in file %s. Ignoring request.", hdrnum,
|
|
224
|
+
log.warning("HDU %d was not found in file %s. Ignoring request.", hdrnum, uri)
|
|
184
225
|
|
|
185
226
|
return md
|
|
186
227
|
|
|
187
228
|
|
|
188
229
|
def read_file_info(
|
|
189
|
-
file:
|
|
230
|
+
file: ResourcePathExpression,
|
|
190
231
|
hdrnum: int,
|
|
191
232
|
print_trace: bool | None = None,
|
|
192
233
|
content_mode: str = "translated",
|
|
@@ -232,9 +273,10 @@ def read_file_info(
|
|
|
232
273
|
if content_type not in ("native", "simple", "json"):
|
|
233
274
|
raise ValueError(f"Unrecognized content type request {content_type}")
|
|
234
275
|
|
|
276
|
+
uri = ResourcePath(file, forceAbsolute=False)
|
|
235
277
|
try:
|
|
236
278
|
# Calculate the JSON from the file
|
|
237
|
-
md = read_basic_metadata_from_file(
|
|
279
|
+
md = read_basic_metadata_from_file(uri, hdrnum, can_raise=True if print_trace is None else False)
|
|
238
280
|
if md is None:
|
|
239
281
|
return None
|
|
240
282
|
if content_mode == "metadata":
|
|
@@ -249,7 +291,7 @@ def read_file_info(
|
|
|
249
291
|
json_str = json.dumps(dict(md))
|
|
250
292
|
return json_str
|
|
251
293
|
return md
|
|
252
|
-
obs_info = ObservationInfo(md, pedantic=True, filename=
|
|
294
|
+
obs_info = ObservationInfo(md, pedantic=True, filename=str(uri))
|
|
253
295
|
if content_type == "native":
|
|
254
296
|
return obs_info
|
|
255
297
|
simple = obs_info.to_simple()
|
|
@@ -22,19 +22,22 @@ __all__ = (
|
|
|
22
22
|
"read_sidecar",
|
|
23
23
|
)
|
|
24
24
|
|
|
25
|
-
import collections.abc
|
|
26
25
|
import json
|
|
27
26
|
import logging
|
|
28
|
-
import os
|
|
29
27
|
from collections.abc import MutableMapping, Sequence
|
|
30
28
|
from copy import deepcopy
|
|
31
|
-
from typing import IO, Any, Literal, overload
|
|
29
|
+
from typing import IO, TYPE_CHECKING, Any, Literal, overload
|
|
30
|
+
|
|
31
|
+
from lsst.resources import ResourcePath
|
|
32
32
|
|
|
33
33
|
from .file_helpers import read_file_info
|
|
34
34
|
from .headers import merge_headers
|
|
35
35
|
from .observationGroup import ObservationGroup
|
|
36
36
|
from .observationInfo import ObservationInfo
|
|
37
37
|
|
|
38
|
+
if TYPE_CHECKING:
|
|
39
|
+
from lsst.resources import ResourcePathExpression
|
|
40
|
+
|
|
38
41
|
log = logging.getLogger(__name__)
|
|
39
42
|
|
|
40
43
|
COMMON_KEY = "__COMMON__"
|
|
@@ -42,8 +45,8 @@ CONTENT_KEY = "__CONTENT__"
|
|
|
42
45
|
|
|
43
46
|
|
|
44
47
|
def index_files(
|
|
45
|
-
files: Sequence[
|
|
46
|
-
root:
|
|
48
|
+
files: Sequence[ResourcePathExpression],
|
|
49
|
+
root: ResourcePathExpression | None,
|
|
47
50
|
hdrnum: int,
|
|
48
51
|
print_trace: bool,
|
|
49
52
|
content: str,
|
|
@@ -56,7 +59,7 @@ def index_files(
|
|
|
56
59
|
|
|
57
60
|
Parameters
|
|
58
61
|
----------
|
|
59
|
-
files : iterable of `
|
|
62
|
+
files : iterable of `lsst.resources.ResourcePathExpression`
|
|
60
63
|
Paths to the files to be indexed. They do not have to all be
|
|
61
64
|
in a single directory but all content will be indexed into a single
|
|
62
65
|
index.
|
|
@@ -99,19 +102,22 @@ def index_files(
|
|
|
99
102
|
|
|
100
103
|
failed: list[str] = []
|
|
101
104
|
okay: list[str] = []
|
|
105
|
+
root_uri = ResourcePath(root, forceDirectory=True) if root else None
|
|
102
106
|
|
|
103
107
|
content_by_file: MutableMapping[str, MutableMapping[str, Any]] = {} # Mapping of path to file content
|
|
104
108
|
for file in sorted(files):
|
|
105
|
-
|
|
106
|
-
|
|
109
|
+
uri = ResourcePath(file, forceAbsolute=False, forceDirectory=False)
|
|
110
|
+
if root_uri is not None:
|
|
111
|
+
path = root_uri.join(uri)
|
|
107
112
|
else:
|
|
108
|
-
path =
|
|
113
|
+
path = uri
|
|
109
114
|
simple = read_file_info(path, hdrnum, print_trace, content, "simple", outstream)
|
|
115
|
+
path_key = path.ospath if path.isLocal else str(path)
|
|
110
116
|
if simple is None:
|
|
111
|
-
failed.append(
|
|
117
|
+
failed.append(path_key)
|
|
112
118
|
continue
|
|
113
119
|
else:
|
|
114
|
-
okay.append(
|
|
120
|
+
okay.append(path_key)
|
|
115
121
|
|
|
116
122
|
# Store the information indexed by the filename within dir
|
|
117
123
|
# We may get a PropertyList here and can therefore not just
|
|
@@ -119,7 +125,13 @@ def index_files(
|
|
|
119
125
|
# other 2 options, which we were enforcing with the "simple" parameter
|
|
120
126
|
# in the call to read_file_info.
|
|
121
127
|
assert not isinstance(simple, str | ObservationInfo)
|
|
122
|
-
|
|
128
|
+
# Force string as key since this is required to be a relative path.
|
|
129
|
+
# Make it relative to the given directory, else it might be absolute.
|
|
130
|
+
if root_uri is not None:
|
|
131
|
+
relative = path.relative_to(root_uri)
|
|
132
|
+
if relative is not None:
|
|
133
|
+
path_key = relative
|
|
134
|
+
content_by_file[path_key] = simple
|
|
123
135
|
|
|
124
136
|
output = calculate_index(content_by_file, content)
|
|
125
137
|
|
|
@@ -154,9 +166,10 @@ def calculate_index(
|
|
|
154
166
|
# For a single file it is possible that the merged contents
|
|
155
167
|
# are not a dict but are an LSST-style PropertyList. JSON needs
|
|
156
168
|
# dict though. mypy can't know about PropertyList so we must ignore
|
|
157
|
-
# the type error.
|
|
158
|
-
if not isinstance(merged,
|
|
159
|
-
|
|
169
|
+
# the type error. We also need to force Astropy Header to a dict.
|
|
170
|
+
if not isinstance(merged, dict):
|
|
171
|
+
# dict(Header) brings along additional keys that can't be serialized.
|
|
172
|
+
merged = {k: v for k, v in merged.items()}
|
|
160
173
|
|
|
161
174
|
# The structure to write to file is intended to look like (in YAML):
|
|
162
175
|
# __COMMON__:
|
|
@@ -14,7 +14,6 @@ from __future__ import annotations
|
|
|
14
14
|
__all__ = ("MetadataAssertHelper", "read_test_file")
|
|
15
15
|
|
|
16
16
|
import json
|
|
17
|
-
import os
|
|
18
17
|
import pickle
|
|
19
18
|
import warnings
|
|
20
19
|
from collections.abc import MutableMapping
|
|
@@ -26,6 +25,7 @@ import yaml
|
|
|
26
25
|
from astropy.io.fits import Header
|
|
27
26
|
from astropy.io.fits.verify import VerifyWarning
|
|
28
27
|
from astropy.time import Time
|
|
28
|
+
from lsst.resources import ResourcePath
|
|
29
29
|
|
|
30
30
|
from astro_metadata_translator import ObservationInfo
|
|
31
31
|
|
|
@@ -43,6 +43,9 @@ try:
|
|
|
43
43
|
except AttributeError:
|
|
44
44
|
Loader = yaml.Loader
|
|
45
45
|
|
|
46
|
+
if TYPE_CHECKING:
|
|
47
|
+
from lsst.resources import ResourcePathExpression
|
|
48
|
+
|
|
46
49
|
|
|
47
50
|
# Define a YAML loader for lsst.daf.base.PropertySet serializations that
|
|
48
51
|
# we can use if daf_base is not available.
|
|
@@ -79,12 +82,14 @@ if daf_base is None:
|
|
|
79
82
|
yaml.add_constructor("lsst.daf.base.PropertyList", pl_constructor, Loader=Loader) # type: ignore
|
|
80
83
|
|
|
81
84
|
|
|
82
|
-
def read_test_file(
|
|
85
|
+
def read_test_file(
|
|
86
|
+
filename: ResourcePathExpression, dir: ResourcePathExpression | None = None
|
|
87
|
+
) -> MutableMapping[str, Any]:
|
|
83
88
|
"""Read the named test file relative to the location of this helper.
|
|
84
89
|
|
|
85
90
|
Parameters
|
|
86
91
|
----------
|
|
87
|
-
filename : `str`
|
|
92
|
+
filename : `str` or `lsst.resources.ResourcePathExpression`
|
|
88
93
|
Name of file in the data directory.
|
|
89
94
|
dir : `str`, optional
|
|
90
95
|
Directory from which to read file. Current directory used if none
|
|
@@ -95,15 +100,18 @@ def read_test_file(filename: str, dir: str | None = None) -> MutableMapping[str,
|
|
|
95
100
|
header : `dict`-like
|
|
96
101
|
Header read from file.
|
|
97
102
|
"""
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
103
|
+
uri = ResourcePath(filename, forceAbsolute=False, forceDirectory=False)
|
|
104
|
+
if dir is not None and not uri.isabs():
|
|
105
|
+
test_dir = ResourcePath(dir, forceDirectory=True)
|
|
106
|
+
uri = test_dir.join(uri, forceDirectory=False)
|
|
107
|
+
content = uri.read()
|
|
108
|
+
ext = uri.getExtension()
|
|
109
|
+
if ext == ".yaml":
|
|
110
|
+
header = yaml.load(content, Loader=Loader)
|
|
111
|
+
elif ext == ".json":
|
|
112
|
+
header = json.loads(content)
|
|
113
|
+
else:
|
|
114
|
+
raise RuntimeError(f"Unrecognized file format: {uri}")
|
|
107
115
|
|
|
108
116
|
# Cannot directly check for Mapping because PropertyList is not one
|
|
109
117
|
if not hasattr(header, "items"):
|
|
@@ -35,6 +35,7 @@ from .properties import PROPERTIES, PropertyDefinition
|
|
|
35
35
|
|
|
36
36
|
if TYPE_CHECKING:
|
|
37
37
|
import astropy.coordinates
|
|
38
|
+
from lsst.resources import ResourcePathExpression
|
|
38
39
|
|
|
39
40
|
log = logging.getLogger(__name__)
|
|
40
41
|
|
|
@@ -1298,7 +1299,7 @@ class MetadataTranslator:
|
|
|
1298
1299
|
|
|
1299
1300
|
@classmethod
|
|
1300
1301
|
def determine_translatable_headers(
|
|
1301
|
-
cls, filename:
|
|
1302
|
+
cls, filename: ResourcePathExpression, primary: MutableMapping[str, Any] | None = None
|
|
1302
1303
|
) -> Iterator[MutableMapping[str, Any]]:
|
|
1303
1304
|
"""Given a file return all the headers usable for metadata translation.
|
|
1304
1305
|
|
|
@@ -1325,7 +1326,7 @@ class MetadataTranslator:
|
|
|
1325
1326
|
|
|
1326
1327
|
Parameters
|
|
1327
1328
|
----------
|
|
1328
|
-
filename : `str`
|
|
1329
|
+
filename : `str` or `lsst.resources.ResourcePathExpression`
|
|
1329
1330
|
Path to a file in a format understood by this translator.
|
|
1330
1331
|
primary : `dict`-like, optional
|
|
1331
1332
|
The primary header obtained by the caller. This is sometimes
|
|
@@ -25,6 +25,7 @@ import astropy.time
|
|
|
25
25
|
import astropy.units as u
|
|
26
26
|
from astropy.coordinates import Angle, EarthLocation, UnknownSiteException
|
|
27
27
|
from astropy.io import fits
|
|
28
|
+
from lsst.resources import ResourcePath
|
|
28
29
|
|
|
29
30
|
from ..translator import CORRECTIONS_RESOURCE_ROOT, cache_translation
|
|
30
31
|
from .fits import FitsTranslator
|
|
@@ -32,6 +33,7 @@ from .helpers import altaz_from_degree_headers, is_non_science, tracking_from_de
|
|
|
32
33
|
|
|
33
34
|
if TYPE_CHECKING:
|
|
34
35
|
import astropy.coordinates
|
|
36
|
+
from lsst.resources import ResourcePathExpression
|
|
35
37
|
|
|
36
38
|
log = logging.getLogger(__name__)
|
|
37
39
|
|
|
@@ -425,7 +427,7 @@ class DecamTranslator(FitsTranslator):
|
|
|
425
427
|
|
|
426
428
|
@classmethod
|
|
427
429
|
def determine_translatable_headers(
|
|
428
|
-
cls, filename:
|
|
430
|
+
cls, filename: ResourcePathExpression, primary: MutableMapping[str, Any] | None = None
|
|
429
431
|
) -> Iterator[MutableMapping[str, Any]]:
|
|
430
432
|
"""Given a file return all the headers usable for metadata translation.
|
|
431
433
|
|
|
@@ -438,7 +440,7 @@ class DecamTranslator(FitsTranslator):
|
|
|
438
440
|
|
|
439
441
|
Parameters
|
|
440
442
|
----------
|
|
441
|
-
filename : `str`
|
|
443
|
+
filename : `str` or `lsst.resources.ResourcePathExpression`
|
|
442
444
|
Path to a file in a format understood by this translator.
|
|
443
445
|
primary : `dict`-like, optional
|
|
444
446
|
The primary header obtained by the caller. This is sometimes
|
|
@@ -472,7 +474,9 @@ class DecamTranslator(FitsTranslator):
|
|
|
472
474
|
# Since we want to scan many HDUs we use astropy directly to keep
|
|
473
475
|
# the file open rather than continually opening and closing it
|
|
474
476
|
# as we go to each HDU.
|
|
475
|
-
|
|
477
|
+
uri = ResourcePath(filename, forceDirectory=False)
|
|
478
|
+
fs, fspath = uri.to_fsspec()
|
|
479
|
+
with fs.open(fspath) as f, fits.open(f) as fits_file:
|
|
476
480
|
# Astropy does not automatically handle the INHERIT=T in
|
|
477
481
|
# DECam headers so the primary header must be merged.
|
|
478
482
|
first_pass = True
|
|
@@ -48,6 +48,11 @@ log = logging.getLogger(__name__)
|
|
|
48
48
|
def to_location_via_telescope_name(self: MetadataTranslator) -> EarthLocation:
|
|
49
49
|
"""Calculate the observatory location via the telescope name.
|
|
50
50
|
|
|
51
|
+
Parameters
|
|
52
|
+
----------
|
|
53
|
+
self : `MetadataTranslator`
|
|
54
|
+
The translator being used.
|
|
55
|
+
|
|
51
56
|
Returns
|
|
52
57
|
-------
|
|
53
58
|
loc : `astropy.coordinates.EarthLocation`
|
|
@@ -59,6 +64,11 @@ def to_location_via_telescope_name(self: MetadataTranslator) -> EarthLocation:
|
|
|
59
64
|
def is_non_science(self: MetadataTranslator) -> None:
|
|
60
65
|
"""Raise an exception if this is a science observation.
|
|
61
66
|
|
|
67
|
+
Parameters
|
|
68
|
+
----------
|
|
69
|
+
self : `MetadataTranslator`
|
|
70
|
+
The translator being used.
|
|
71
|
+
|
|
62
72
|
Raises
|
|
63
73
|
------
|
|
64
74
|
KeyError
|
|
@@ -94,6 +104,8 @@ def tracking_from_degree_headers(
|
|
|
94
104
|
|
|
95
105
|
Parameters
|
|
96
106
|
----------
|
|
107
|
+
self : `MetadataTranslator`
|
|
108
|
+
The translator being used.
|
|
97
109
|
radecsys : `list` or `tuple`
|
|
98
110
|
Header keywords to try corresponding to the tracking system. If none
|
|
99
111
|
match ICRS will be assumed.
|
|
@@ -163,6 +175,8 @@ def altaz_from_degree_headers(
|
|
|
163
175
|
|
|
164
176
|
Parameters
|
|
165
177
|
----------
|
|
178
|
+
self : `MetadataTranslator`
|
|
179
|
+
The translator being used.
|
|
166
180
|
altazpairs : `tuple` of `str`
|
|
167
181
|
Pairs of keywords specifying Alt/Az in degrees. Each pair is tried
|
|
168
182
|
in turn.
|
|
@@ -24,6 +24,7 @@ import astropy.time
|
|
|
24
24
|
import astropy.units as u
|
|
25
25
|
from astropy.coordinates import Angle, EarthLocation, UnknownSiteException
|
|
26
26
|
from astropy.io import fits
|
|
27
|
+
from lsst.resources import ResourcePath
|
|
27
28
|
|
|
28
29
|
from ..translator import CORRECTIONS_RESOURCE_ROOT, cache_translation
|
|
29
30
|
from .fits import FitsTranslator
|
|
@@ -32,6 +33,7 @@ from .helpers import altaz_from_degree_headers, tracking_from_degree_headers
|
|
|
32
33
|
if TYPE_CHECKING:
|
|
33
34
|
import astropy.coordinates
|
|
34
35
|
import astropy.units
|
|
36
|
+
from lsst.resources import ResourcePathExpression
|
|
35
37
|
|
|
36
38
|
_MK_FALLBACK_LOCATION = EarthLocation.from_geocentric(
|
|
37
39
|
-5464301.77135369, -2493489.8730419, 2151085.16950589, unit=u.m
|
|
@@ -213,7 +215,7 @@ class MegaPrimeTranslator(FitsTranslator):
|
|
|
213
215
|
|
|
214
216
|
@classmethod
|
|
215
217
|
def determine_translatable_headers(
|
|
216
|
-
cls, filename:
|
|
218
|
+
cls, filename: ResourcePathExpression, primary: MutableMapping[str, Any] | None = None
|
|
217
219
|
) -> Iterator[MutableMapping[str, Any]]:
|
|
218
220
|
"""Given a file return all the headers usable for metadata translation.
|
|
219
221
|
|
|
@@ -224,7 +226,7 @@ class MegaPrimeTranslator(FitsTranslator):
|
|
|
224
226
|
|
|
225
227
|
Parameters
|
|
226
228
|
----------
|
|
227
|
-
filename : `str`
|
|
229
|
+
filename : `str` or `lsst.resources.ResourcePathExpression`
|
|
228
230
|
Path to a file in a format understood by this translator.
|
|
229
231
|
primary : `dict`-like, optional
|
|
230
232
|
The primary header obtained by the caller. This is sometimes
|
|
@@ -250,7 +252,9 @@ class MegaPrimeTranslator(FitsTranslator):
|
|
|
250
252
|
# Since we want to scan many HDUs we use astropy directly to keep
|
|
251
253
|
# the file open rather than continually opening and closing it
|
|
252
254
|
# as we go to each HDU.
|
|
253
|
-
|
|
255
|
+
uri = ResourcePath(filename, forceDirectory=False)
|
|
256
|
+
fs, fspath = uri.to_fsspec()
|
|
257
|
+
with fs.open(fspath) as f, fits.open(f) as fits_file:
|
|
254
258
|
for hdu in fits_file:
|
|
255
259
|
# Astropy <=4.2 strips the EXTNAME header but some CFHT data
|
|
256
260
|
# have two EXTNAME headers and the CCD number is in the
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
__all__ = ["__version__"]
|
|
2
|
-
__version__ = "29.2025.
|
|
2
|
+
__version__ = "29.2025.4800"
|
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: astro-metadata-translator
|
|
3
|
-
Version: 29.2025.
|
|
3
|
+
Version: 29.2025.4800
|
|
4
4
|
Summary: A translator for astronomical metadata.
|
|
5
5
|
Author-email: Rubin Observatory Data Management <dm-admin@lists.lsst.org>
|
|
6
|
-
License: BSD
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
7
|
Project-URL: Homepage, https://github.com/lsst/astro_metadata_translator
|
|
8
8
|
Keywords: lsst
|
|
9
9
|
Classifier: Intended Audience :: Science/Research
|
|
10
10
|
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
11
|
-
Classifier: License :: OSI Approved :: BSD License
|
|
12
11
|
Classifier: Operating System :: OS Independent
|
|
13
12
|
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.13
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
-
|
|
18
|
-
Requires-Python: >=3.10.0
|
|
17
|
+
Requires-Python: >=3.11.0
|
|
19
18
|
Description-Content-Type: text/markdown
|
|
20
19
|
License-File: LICENSE
|
|
21
20
|
Requires-Dist: astropy>=3.0.5
|
|
22
21
|
Requires-Dist: pyyaml>=3.13
|
|
22
|
+
Requires-Dist: lsst-resources
|
|
23
|
+
Requires-Dist: fsspec
|
|
23
24
|
Requires-Dist: click>=8
|
|
24
25
|
Provides-Extra: test
|
|
25
26
|
Requires-Dist: pytest; extra == "test"
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
astro_metadata_translator/__init__.py,sha256=TgZ7RPdgoCQwsGlvIOMcrDTLBPefDVhvkb-S6N5XTWY,595
|
|
2
|
-
astro_metadata_translator/file_helpers.py,sha256=
|
|
2
|
+
astro_metadata_translator/file_helpers.py,sha256=jAqiwCr3HzdA-ruLfMZz6pOFxaUIojmjn-dk4bXVT7A,10913
|
|
3
3
|
astro_metadata_translator/headers.py,sha256=21KWaPnBPDkonh5coZ0NolMdtM6KVozqi1Fd2d1z80M,18944
|
|
4
|
-
astro_metadata_translator/indexing.py,sha256=
|
|
4
|
+
astro_metadata_translator/indexing.py,sha256=WcltI-8Jiu7JzKcn_xtT1YRAJ7XD_m-WNKY7J2XPVx8,15340
|
|
5
5
|
astro_metadata_translator/observationGroup.py,sha256=XRP8xpHQXx4hSl_SrBBan_q1gmxpu2VuepjLZEWthsQ,8689
|
|
6
6
|
astro_metadata_translator/observationInfo.py,sha256=mlxtrOlJB7qjMZzxVo2-8CjC-KFCexZsBkI_9VtXakI,27543
|
|
7
7
|
astro_metadata_translator/properties.py,sha256=oiatCGP6g7O0sm5dku50dUyMJtu8IORFLXRu4Sx1hCo,18787
|
|
8
8
|
astro_metadata_translator/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
astro_metadata_translator/tests.py,sha256=
|
|
10
|
-
astro_metadata_translator/translator.py,sha256=
|
|
11
|
-
astro_metadata_translator/version.py,sha256=
|
|
9
|
+
astro_metadata_translator/tests.py,sha256=QCIniK0S0MBnDXudTvH_Z4vSn7ItlIJzOXSRrFafB-4,13552
|
|
10
|
+
astro_metadata_translator/translator.py,sha256=o-MYrRNWwQwidcXcmtQB69ucGHEBhITv33hHBEloTgw,56718
|
|
11
|
+
astro_metadata_translator/version.py,sha256=MHZJr_Yoevn1SeqerO6dmfTP3xtJLGxPmEi-9Jpl380,55
|
|
12
12
|
astro_metadata_translator/bin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
-
astro_metadata_translator/bin/translate.py,sha256=
|
|
14
|
-
astro_metadata_translator/bin/writeindex.py,sha256=
|
|
15
|
-
astro_metadata_translator/bin/writesidecar.py,sha256=
|
|
13
|
+
astro_metadata_translator/bin/translate.py,sha256=RzUBoq4ZfpDx7Xg2O43DmguKELUUY0xMLMUdAe12ctE,11284
|
|
14
|
+
astro_metadata_translator/bin/writeindex.py,sha256=JYNgbrTjy-ExeWmg2EdXNe6zdwaICfQSvMcSFiRPRKM,4586
|
|
15
|
+
astro_metadata_translator/bin/writesidecar.py,sha256=85v_oF4sp3XmA8Rb3mYPhtY-tFLfa1cyd0fvfCINlRE,4938
|
|
16
16
|
astro_metadata_translator/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
17
|
astro_metadata_translator/cli/astrometadata.py,sha256=F8MHvWCgykijmOKKpy7YfQv3E5MuJFTl3SNeLHJrosw,9609
|
|
18
18
|
astro_metadata_translator/corrections/CFHT/README.md,sha256=qgf5zPK9NUtlufSPESZgKqizWHH4fIL58IKWbmmjjWQ,190
|
|
@@ -50,18 +50,18 @@ astro_metadata_translator/corrections/SuprimeCam/README.md,sha256=UaaDRv8T5ik-iY
|
|
|
50
50
|
astro_metadata_translator/serialize/__init__.py,sha256=pthEgasrT3Qw4e7nZk_X1rPBjDju5AsdHDnxqoOoAsU,427
|
|
51
51
|
astro_metadata_translator/serialize/fits.py,sha256=mjBYhL_wy_-aGgmswxMn1bIgq0U764uv02ge_1_5iRg,3811
|
|
52
52
|
astro_metadata_translator/translators/__init__.py,sha256=RHzTw7pnp5o5wyZPWg__udthT6qFQMZAJBsicxkRVZY,560
|
|
53
|
-
astro_metadata_translator/translators/decam.py,sha256=
|
|
53
|
+
astro_metadata_translator/translators/decam.py,sha256=03nXEPol97ZTRDitxVMZek8alo7arockSYfTJRgPGXY,18553
|
|
54
54
|
astro_metadata_translator/translators/fits.py,sha256=h4tIB2XIg_oV-m5kVe6ny1gJIKMcDsEBO-SvC_NWinM,7030
|
|
55
|
-
astro_metadata_translator/translators/helpers.py,sha256=
|
|
55
|
+
astro_metadata_translator/translators/helpers.py,sha256=uK4q3r2dLzzJVJMjlEyT14lQCDogASp0h_lCGmYeMOc,7808
|
|
56
56
|
astro_metadata_translator/translators/hsc.py,sha256=Ca7zKICl04HrKQG51_bWyxAio7Oiibuuw39fcvtG6wI,8529
|
|
57
|
-
astro_metadata_translator/translators/megaprime.py,sha256=
|
|
57
|
+
astro_metadata_translator/translators/megaprime.py,sha256=fWbXVh_klB6t1dLJFMqfzX0W8XVoQOgo-M5Yxfy3vUw,11149
|
|
58
58
|
astro_metadata_translator/translators/sdss.py,sha256=prUTawPqAXzJ5i2p2yXH3ekLuj3rWGy-YjJMquJPMq0,9445
|
|
59
59
|
astro_metadata_translator/translators/subaru.py,sha256=-CTzxc2MpgEEpma-toD6B8RcvV-t5MNZq_fb4UGbHQU,2143
|
|
60
60
|
astro_metadata_translator/translators/suprimecam.py,sha256=b3S54aHvZ2SprvXRdOLCIJToj6KAiqjK-JB5My8xLWM,8409
|
|
61
|
-
astro_metadata_translator-29.2025.
|
|
62
|
-
astro_metadata_translator-29.2025.
|
|
63
|
-
astro_metadata_translator-29.2025.
|
|
64
|
-
astro_metadata_translator-29.2025.
|
|
65
|
-
astro_metadata_translator-29.2025.
|
|
66
|
-
astro_metadata_translator-29.2025.
|
|
67
|
-
astro_metadata_translator-29.2025.
|
|
61
|
+
astro_metadata_translator-29.2025.4800.dist-info/licenses/LICENSE,sha256=6DRIob1hPfPI0HmYxqGJXcJr7Vc81mXQJHtjH29gygw,1518
|
|
62
|
+
astro_metadata_translator-29.2025.4800.dist-info/METADATA,sha256=9P0M6-k39IXEx848IlaTKFAGaov1FRe6HYuvy6rdi9o,1905
|
|
63
|
+
astro_metadata_translator-29.2025.4800.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
64
|
+
astro_metadata_translator-29.2025.4800.dist-info/entry_points.txt,sha256=9dwf9VZWuNtomGwNBEc0S9HJ740wnPSVEO8yNEPto6Y,83
|
|
65
|
+
astro_metadata_translator-29.2025.4800.dist-info/top_level.txt,sha256=ll138q-If3_TxKr2nTm5F3MEhWqvHbF3ptEDOhvgfRU,26
|
|
66
|
+
astro_metadata_translator-29.2025.4800.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
67
|
+
astro_metadata_translator-29.2025.4800.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|