Skip to content

Commit

Permalink
CAOM2.5 Observation successfully parsed.
Browse files Browse the repository at this point in the history
  • Loading branch information
Adrian Damian authored and Adrian Damian committed Jan 28, 2025
1 parent b5931f7 commit e7eb31e
Show file tree
Hide file tree
Showing 36 changed files with 7,666 additions and 1,485 deletions.
1 change: 1 addition & 0 deletions caom2/caom2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
from .checksum import * # noqa
from .chunk import * # noqa
from .common import * # noqa
from .dali import * # noqa
from .diff import * # noqa
from .obs_reader_writer import * # noqa
from .observation import * # noqa
Expand Down
71 changes: 52 additions & 19 deletions caom2/caom2/artifact.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# ****************** CANADIAN ASTRONOMY DATA CENTRE *******************
# ************* CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES **************
#
# (c) 2022. (c) 2022.
# (c) 2025. (c) 2025.
# Government of Canada Gouvernement du Canada
# National Research Council Conseil national de recherches
# Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6
Expand Down Expand Up @@ -74,10 +74,10 @@
from urllib.parse import urlparse

from . import caom_util
from .chunk import ProductType
from .common import AbstractCaomEntity
from .common import AbstractCaomEntity, CaomObject, compute_bucket
from .common import ChecksumURI, OrderedEnum
from .part import Part
from .chunk import DataLinkSemantics
from datetime import datetime


Expand All @@ -95,17 +95,39 @@ class ReleaseType(OrderedEnum):
META = "meta"


class ArtifactDescription(CaomObject):
""" Short description with a URI reference for details"""

def __init__(self, uri, description):
super().__init__()
try:
urlparse(uri)
except ValueError:
raise TypeError('Expected any IVOA URI for ArtifactDescription.uri, '
'received {}'.format(uri))
self._uri = uri
self._description = description

@property
def uri(self):
return self._uri

@property
def description(self):
return self._description


class Artifact(AbstractCaomEntity):
"""Contains the meta data assocaited with a file.
"""Contains the metadata associated with a file.
- location of the file (uri)
- the http content-type
- size of the file (content-lenght)
As well as a pointer (parts) to content of the file.
eg: Artifact('ad:CFHT/1234567o')
where 'ad:CFHT/1234567o' is a uri that refernce the file...
eg: Artifact('cadc:CFHT/1234567o')
where 'cadc:CFHT/1234567o' is an uri that reference the file...
"""

Expand All @@ -118,17 +140,23 @@ def __init__(self,
content_checksum=None,
content_release=None,
content_read_groups=None,
parts=None
parts=None,
description_id=None
):
"""
Initialize a Artifact instance.
Initialize an Artifact instance.
Arguments: uri of the artifact. eg:
vos://cadc.nrc.ca!vospace/APASS/apass_north/proc/100605/n100605.fz
ad:CFHT/123456p
"""
super(Artifact, self).__init__()
self.uri = uri
try:
urlparse(uri)
except ValueError:
raise TypeError('Expected URI for Artifact.uri, received {}'.format(uri))
self._uri = uri
self._uri_bucket = compute_bucket(uri)
self.product_type = product_type
self.release_type = release_type
self.content_type = content_type
Expand All @@ -139,6 +167,7 @@ def __init__(self,
if parts is None:
parts = caom_util.TypedOrderedDict(Part, )
self.parts = parts
self.description_id = description_id

def _key(self):
return self.uri
Expand All @@ -160,15 +189,9 @@ def uri(self):
"""
return self._uri

@uri.setter
def uri(self, value):
caom_util.type_check(value, str, 'uri')
uri = urlparse(value)
if not uri.scheme:
raise ValueError('URI without scheme: {}'.format(value))
uri_str = uri.geturl()
caom_util.value_check(value, None, None, 'uri', override=uri_str)
self._uri = uri_str
@property
def uri_bucket(self):
return self._uri_bucket

@property
def product_type(self):
Expand All @@ -183,7 +206,7 @@ def product_type(self):

@product_type.setter
def product_type(self, value):
caom_util.type_check(value, ProductType, "product_type", False)
caom_util.type_check(value, DataLinkSemantics, "product_type", False)
self._product_type = value

@property
Expand Down Expand Up @@ -306,3 +329,13 @@ def parts(self, value):
caom_util.type_check(value, caom_util.TypedOrderedDict, 'parts',
override=False)
self._parts = value

@property
def description_id(self):
return self._description_id

@description_id.setter
def description_id(self, value):
if value is not None:
caom_util.type_check(value, str, 'description_id')
self._description_id = value
35 changes: 23 additions & 12 deletions caom2/caom2/caom_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# ****************** CANADIAN ASTRONOMY DATA CENTRE *******************
# ************* CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES **************
#
# (c) 2016. (c) 2016.
# (c) 2025. (c) 2025.
# Government of Canada Gouvernement du Canada
# National Research Council Conseil national de recherches
# Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6
Expand Down Expand Up @@ -74,17 +74,18 @@
the first point of use could be implemented. This helps the data
engineer get the correct meta data more quickly.
"""

import sys
import collections
from datetime import datetime

from urllib.parse import urlsplit
from builtins import int, str as newstr

from . import dali


__all__ = ['TypedList', 'TypedSet', 'TypedOrderedDict', 'ClassProperty',
'URISet']
'URISet', 'validate_uri']

# TODO both these are very bad, implement more sensibly
IVOA_DATE_FORMAT = "%Y-%m-%dT%H:%M:%S.%f"
Expand Down Expand Up @@ -235,8 +236,8 @@ def __repr__(self):

def check(self, v):
if not isinstance(v, self._oktypes):
raise TypeError("Wrong type in list. OK Types: {0}".
format(self._oktypes))
raise TypeError("Wrong type ({0}) in list. OK Types: {1}".
format(type(v), self._oktypes))

def __len__(self):
return len(self.list)
Expand Down Expand Up @@ -328,6 +329,22 @@ def __contains__(self, item):
return False


def validate_uri(uri, scheme=None):
"""
Validates a URI. If a scheme is provided, the URI must have that scheme.
:param uri:
:param scheme:
:raise TypeError: when uri not valid
"""
if uri:
tmp = urlsplit(uri)
if scheme and scheme != tmp.scheme:
raise TypeError("Invalid URI scheme: {}".format(uri))
if tmp.geturl() == uri:
return
raise TypeError("Invalid URI: " + uri)


class URISet(TypedSet):
"""
Class that customizes a TypedSet to check for URIs
Expand All @@ -350,13 +367,7 @@ def check(self, v):
:param v: value to check
:return:
"""
if v:
tmp = urlsplit(v)
if self.scheme and tmp.scheme != self.scheme:
raise TypeError("Invalid URI scheme: {}".format(v))
if tmp.geturl() == v:
return
raise TypeError("Invalid URI: " + v)
validate_uri(v, self.scheme)


class TypedOrderedDict(collections.OrderedDict):
Expand Down
10 changes: 9 additions & 1 deletion caom2/caom2/checksum.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@
from caom2.common import CaomObject, AbstractCaomEntity, ObservationURI
from caom2.common import ChecksumURI
from caom2.observation import Observation
from .obs_reader_writer import CAOM25_NAMESPACE, CAOM24_NAMESPACE, \
CAOM23_NAMESPACE

with warnings.catch_warnings():
warnings.simplefilter('ignore')
from aenum import Enum
Expand Down Expand Up @@ -399,8 +402,13 @@ def checksum_diff():
mistmatches += _print_diff(plane[0], plane[1])
mistmatches += _print_diff(orig, actual)

ns = CAOM25_NAMESPACE
if reader.version == 24:
ns = CAOM24_NAMESPACE
if reader.version == 23:
ns = CAOM23_NAMESPACE
if args.output:
writer = obs_reader_writer.ObservationWriter(validate=True)
writer = obs_reader_writer.ObservationWriter(validate=True, namespace=ns)
writer.write(actual, args.output)

print("Total: {} mistmatches".format(mistmatches))
Expand Down
63 changes: 32 additions & 31 deletions caom2/caom2/chunk.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# ****************** CANADIAN ASTRONOMY DATA CENTRE *******************
# ************* CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES **************
#
# (c) 2024. (c) 2024.
# (c) 2025. (c) 2025.
# Government of Canada Gouvernement du Canada
# National Research Council Conseil national de recherches
# Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6
Expand Down Expand Up @@ -76,14 +76,19 @@
from caom2.caom_util import int_32
from . import caom_util
from . import wcs
from .common import AbstractCaomEntity
from .common import CaomObject, OrderedEnum
from .common import AbstractCaomEntity, OrderedEnum, VocabularyTerm, \
_DATA_LINK_VOCAB_NS, _CAOM_PRODUCT_TYPE_NS
from .common import CaomObject


class ProductType(OrderedEnum):
__all__ = ['Chunk', 'ObservableAxis', 'SpatialWCS', 'DataLinkSemantics',
'SpectralWCS', 'TemporalWCS', 'PolarizationWCS', 'CustomWCS']


class DataLinkSemantics(OrderedEnum):
"""
Subset of IVOA DataLink terms at:
https://www.ivoa.net/rdf/datalink/core/2022-01-27/datalink.html
https://www.ivoa.net/rdf/datalink/core/
THIS = "this"
AUXILIARY = "auxiliary"
Expand All @@ -102,23 +107,22 @@ class ProductType(OrderedEnum):
WEIGHT = 'weight'
"""

THIS = "this"

AUXILIARY = "auxiliary"
BIAS = 'bias'
CALIBRATION = 'calibration'
CODERIVED = 'coderived'
DARK = 'dark'
DOCUMENTATION = 'documentation'
ERROR = 'error'
FLAT = 'flat'
NOISE = 'noise'
PREVIEW = 'preview'
PREVIEW_IMAGE = 'preview-image'
PREVIEW_PLOT = 'preview-plot'
THUMBNAIL = 'thumbnail'
WEIGHT = 'weight'
THIS = VocabularyTerm(_DATA_LINK_VOCAB_NS, "this", True).get_value()

AUXILIARY = VocabularyTerm(_DATA_LINK_VOCAB_NS, 'auxiliary', True).get_value()
BIAS = VocabularyTerm(_DATA_LINK_VOCAB_NS, 'bias', True).get_value()
CALIBRATION = VocabularyTerm(_DATA_LINK_VOCAB_NS, 'calibration', True).get_value()
CODERIVED = VocabularyTerm(_DATA_LINK_VOCAB_NS, 'coderived', True).get_value()
DARK = VocabularyTerm(_DATA_LINK_VOCAB_NS, 'dark', True).get_value()
DOCUMENTATION = VocabularyTerm(_DATA_LINK_VOCAB_NS, 'documentation', True).get_value()
ERROR = VocabularyTerm(_DATA_LINK_VOCAB_NS, 'error', True).get_value()
FLAT = VocabularyTerm(_DATA_LINK_VOCAB_NS, 'flat', True).get_value()
NOISE = VocabularyTerm(_DATA_LINK_VOCAB_NS, 'noise', True).get_value()
PREVIEW = VocabularyTerm(_DATA_LINK_VOCAB_NS, 'preview', True).get_value()
PREVIEW_IMAGE = VocabularyTerm(_DATA_LINK_VOCAB_NS, 'preview-image', True).get_value()
PREVIEW_PLOT = VocabularyTerm(_DATA_LINK_VOCAB_NS, 'preview-plot', True).get_value()
THUMBNAIL = VocabularyTerm(_DATA_LINK_VOCAB_NS, 'thumbnail', True).get_value()
WEIGHT = VocabularyTerm(_DATA_LINK_VOCAB_NS, 'weight', True).get_value()

# DataLink terms explicitly not included
# counterpart
Expand All @@ -130,16 +134,13 @@ class ProductType(OrderedEnum):
# progenitor

# CAOM specific terms public
SCIENCE = 'science' # this
SCIENCE = VocabularyTerm(_CAOM_PRODUCT_TYPE_NS, 'science', True).get_value() # this

# deprecated
# INFO = 'info'
# CATALOG = 'catalog'


__all__ = ['ProductType', 'Chunk', 'ObservableAxis', 'SpatialWCS',
'SpectralWCS', 'TemporalWCS', 'PolarizationWCS', 'CustomWCS']


class Chunk(AbstractCaomEntity):
"""A caom2.Chunk object. A chunk is a peice of file part.
Expand Down Expand Up @@ -199,21 +200,21 @@ def __init__(self, product_type=None,
def product_type(self):
"""A word that describes the content of the chunk.
eg. Chunk.product_type = ProductType.SCIENCE
eg. Chunk.product_type = DataLinkSemantics.SCIENCE
Allowed values:
""" + str(list(ProductType)) + """
""" + str(list(DataLinkSemantics)) + """
"""

return self._product_type

@product_type.setter
def product_type(self, value):
if isinstance(value, str) and value in ProductType.names():
if isinstance(value, str) and value in DataLinkSemantics.names():
# be helpful
value = ProductType('value')
caom_util.type_check(value, ProductType, 'product_type')
value = DataLinkSemantics('value')
caom_util.type_check(value, DataLinkSemantics, 'product_type')
self._product_type = value

@property
Expand Down
Loading

0 comments on commit e7eb31e

Please sign in to comment.