from datetime import datetime
from enum import Enum
from typing import Iterable
[docs]class Compilable:
"""
An abstract class for all metadata components.
"""
__compiler_name__ = None
"""Used to identify the appropriate compiler function for this structure"""
__required__ = None
__optional__ = None
def __repr__(self):
return "{}({})".format(
self.__class__.__name__,
",".join("{}={}".format(key, val) for key, val in self.__dict__.items()),
)
def __lt__(self, other):
for key in sorted(self.__dict__):
s = getattr(self, key)
o = getattr(other, key)
if s is None:
return True
elif s < o:
return True
elif s > o:
return False
return False
def get_missing_fields(self):
for key in sorted(self.__dict__):
if key in self.__required__:
if s is None:
yield key
v = getattr(self, key)
if isinstance(v, Compilable):
for x in v.get_missing_fields():
yield key + "." + x
class Language(Compilable):
__compiler_name__ = "language"
class Spatial(Compilable):
__compiler_name__ = "spatial"
def __init__(
self, location: str = None, extent: str = None, resolution: str = None
):
self.location = location
self.extent = extent
self.resolution = resolution
class TimestampOrientation(Compilable, Enum):
__compiler_name__ = "timestamp_orientation"
left = 0
middle = 1
right = 2
@staticmethod
def create(value: str) -> "TimestampOrientation":
if value == "left":
return TimestampOrientation.left
elif value == "middle":
return TimestampOrientation.middle
elif value == "right":
return TimestampOrientation.right
else:
raise Exception("Unknown timestamp orientation:", value)
class Temporal(Compilable):
__compiler_name__ = "temporal"
def __init__(
self,
reference_date: datetime = None,
start: datetime = None,
end: datetime = None,
resolution: str = None,
ts_orientation: TimestampOrientation = None,
): # TODO: This should not be a string... maybe
# we should use datetime instead?
self.reference_date = reference_date
self.ts_start = start
self.ts_end = end
self.ts_resolution = resolution
self.ts_orientation = ts_orientation
class License(Compilable):
__compiler_name__ = "license"
def __init__(
self,
name: str = None,
identifier: str = None,
text: str = None,
path: str = None,
other_references: Iterable[str] = None,
comment: str = None,
):
self.name = name
self.path = path
self.identifier = identifier
self.other_references = other_references
self.text = text
self.comment = comment
@staticmethod
def instance_name_from_id(identifier: str):
return "L_" + identifier.replace("-", "_").replace(".", "_").replace(
"+", "_plus"
)
class TermsOfUse(Compilable):
__compiler_name__ = "terms_of_use"
def __init__(
self, instruction: str = None, attribution: str = None, lic: License = None
):
self.instruction = instruction
self.attribution = attribution
self.license = lic
class Source(Compilable):
__compiler_name__ = "source"
def __init__(
self,
title: str = None,
description: str = None,
path: str = None,
source_license: License = None,
source_copyright: str = None,
):
self.title = title
self.description = description
self.path = path
self.license = source_license
self.copyright = source_copyright
class Person(Compilable):
__compiler_name__ = "person"
def __init__(self, name: str = None, email: str = None):
self.name = name
self.email = email
class Contribution(Compilable):
__compiler_name__ = "contribution"
def __init__(
self,
contributor: Person = None,
date: datetime = None,
obj: str = None,
comment: str = None,
):
self.contributor = contributor
self.date = date
self.object = obj
self.comment = comment
class Field(Compilable):
__compiler_name__ = "field"
def __init__(
self,
name: str = None,
description: str = None,
field_type: str = None,
unit: str = None,
resource: "Resource" = None,
):
self.name = name
self.description = description
self.type = field_type
self.unit = unit
self.resource = resource
def __repr__(self):
return "{}({})".format(
self.__class__.__name__,
",".join(
"{}={}".format(key, val)
for key, val in self.__dict__.items()
if key != "resource"
),
)
class Context(Compilable):
__compiler_name__ = "context"
def __init__(
self,
homepage: str = None,
documentation: str = None,
source_code: str = None,
contact: str = None,
grant_number: str = None,
):
self.homepage = homepage
self.documentation = documentation
self.source_code = source_code
self.contact = contact
self.grant_number = grant_number
class Reference(Compilable):
__compiler_name__ = "reference"
def __init__(self, source: Field = None, target: Field = None):
self.source = source
self.target = target
class ForeignKey(Compilable):
__compiler_name__ = "foreign_key"
def __init__(self, references: Iterable[Reference] = None):
self.references = references
class Schema(Compilable):
__compiler_name__ = "schema"
def __init__(
self,
fields: Iterable[Field] = None,
primary_key: Iterable[str] = None,
foreign_keys: Iterable[ForeignKey] = None,
):
self.fields = fields
self.primary_key = primary_key
self.foreign_keys = foreign_keys
class Dialect(Compilable):
__compiler_name__ = "dialect"
def __init__(self, delimiter: str = None, decimal_separator: str = None):
self.delimiter = delimiter
self.decimal_separator = decimal_separator
class Resource(Compilable):
__compiler_name__ = "resource"
def __init__(
self,
name: str = None,
path: str = None,
profile: str = None,
resource_format: str = None,
encoding: str = None,
schema: Schema = None,
dialect: Dialect = None,
):
self.name = name
self.path = path
self.profile = profile
self.format = resource_format
self.encoding = encoding
self.schema = schema
if schema is not None:
for field in schema.fields:
field.resource = self
self.dialect = dialect
class MetaComment(Compilable):
__compiler_name__ = "meta_comment"
def __init__(
self,
metadata_info: str = None,
dates: str = None,
units: str = None,
languages: str = None,
licenses: str = None,
review: str = None,
none: str = None,
):
self.metadata_info = metadata_info
self.dates = dates
self.units = units
self.languages = languages
self.licenses = licenses
self.review = review
self.none = none
class Review(Compilable):
__compiler_name__ = "review"
def __init__(self, path: str = None, badge: str = None):
self.path = path
self.badge = badge
class OEPMetadata(Compilable):
__compiler_name__ = "metadata"
__required__ = ["id"]
def __init__(
self,
name: str = None,
title: str = None,
identifier: str = None,
description: str = None,
languages: Iterable[Language] = None,
keywords: Iterable[str] = None,
publication_date: datetime = None,
context: Context = None,
spatial: Spatial = None,
temporal: Temporal = None,
sources: Iterable[Source] = None,
terms_of_use: Iterable[TermsOfUse] = None,
contributions: Iterable[Contribution] = None,
resources: Iterable[Resource] = None,
review: Review = None,
comment: MetaComment = None,
):
self.name = name
self.title = title
self.identifier = identifier
self.description = description
self.languages = languages
self.keywords = keywords
self.publication_date = publication_date
self.context = context
self.spatial = spatial
self.temporal = temporal
self.sources = sources
self.license = terms_of_use
self.contributions = contributions
self.resources = resources
self.review = review
self.comment = comment
def has_keywords(self):
return self.keywords is not None