Skip to content
Snippets Groups Projects
Commit d1c50fb3 authored by Bavo Langerock's avatar Bavo Langerock
Browse files

added support for PDINST with an InstrumentMetadata class

parent 58cdd44e
No related branches found
No related tags found
No related merge requests found
......@@ -36,13 +36,17 @@ class WIKIDATA_mixin:
@dataclasses.dataclass
class Identifier:
value:str
itype:typing.Literal['DOI']='DOI' #only this value is allowed
itype:typing.Literal['DOI','Handle']='DOI' #only these values are allowed
@property
def xml(self): return jinja2.Template("""<identifier identifierType="{{item.itype}}">{{item.value}}</identifier>""").render(item=self)
@property
def evdc_html(self): return jinja2.Template("""<h3>{{item.itype}}</h3><p>{{item.value}}</p>""").render(item=self)
@property
def url(self): return os.path.join(DOIRelationFactory.base,self.value) #DOIRelationFactory.base is the https://doi.org/ prefix
def url(self):
if self.itype=='DOI': return os.path.join(DOIRelationFactory.base,self.value) #DOIRelationFactory.base is the https://doi.org/ prefix
elif self.itype=='Handle': return os.path.join('https://hdl.handle.net',self.value)
@property
def json(self): return {"identifierValue": self.url, "identifierType": self.itype}
########
# Dates https://datacite-metadata-schema.readthedocs.io/en/4.5/properties/date/
########
......@@ -58,6 +62,8 @@ class DateTime:
def html_paragraph(self): return jinja2.Template("""<p>{{item.role}}: {{item.value.strftime(item.ISO8601)}}</p>""").render(item=self)
@property
def djson(self): return {'date': self.value.strftime(self.ISO8601), 'dateType': self.role,'dateInformation':None}
@property
def pjson(self): out=self.djson;out.pop('dateInformation');return out
@classmethod
def from_djson(cls,js:dict): return cls(value=dt.datetime.fromisoformat(js['date']),role=js['dateType'])
......@@ -105,14 +111,23 @@ class Organisation:
if self.Scheme:
return f"<{xtype} {xtype}Identifier=\"{self.identifier}\" {xtype}IdentifierScheme=\"{self.Scheme}\" schemeURI=\"{self.schemeURI}\">{self.name}</{xtype}>"
else: return f"<{xtype}>{self.name}</{xtype}>"
#datacite
@property
def djson(self): return {'name': self.name,'affiliationIdentifier': self.identifier,'affiliationIdentifierScheme': self.Scheme,'schemeURI': self.schemeURI} if self.identifier else self.name
@property
def publisher_djson(self): return {"name":self.name, "publisherIdentifier":self.identifier, "publisherIdentifierScheme": self.Scheme, "schemeUri": self.schemeURI } if self.identifier else self.name
#pdinst
def _pdinst(self,t:str)->dict: return {f"{t}Name": self.name, f"{t}Identifier": {f"{t}IdentifierValue": self.identifier.removeprefix(self.schemeURI).strip('/'), f"{t}IdentifierType": self.Scheme}} if self.identifier else {f"{t}Name": self.name}
@property
def owner_pjson(self): return self._pdinst('owner')
@property
def manufacturer_pjson(self): return self._pdinst('manufacturer')
#datacite xml
@property
def affiliation_xml(self): return self._xml('affiliation')
@property
def publisher_xml(self): return self._xml('publisher')
#html
@property
def logo_html(self): return jinja2.Template("""{% if item.logo_url %}{% if item.home_url %}<a href="{{item.home_url}}">{% endif %}<img src="{{item.logo_url}}" alt="{{item.shortname}} Logo" height="200">{% if item.home_url %}</a>{% endif %}{% endif %}""").render(item=self)
@property
......@@ -267,6 +282,10 @@ class Subject: #abstract class ... GND, DDC and WIKIDATASubject can be used
return jinja2.Template("""<p>{% if item.valueURI|length %} <a href="{{item.valueURI}}">{{item.Scheme}} {{item.value}}</a>{% else %}{{item.Scheme}} {{item.value}}{% endif %}</p>""").render(item=self)
@property
def djson(self): return {'subject': self.value,'subjectScheme': self.Scheme,'schemeUri': self.schemeURI,'lang': self.lang}
def pjson(self,ptype='instrumentType'):
out={f"{ptype}Name": self.value}
if self.valueURI: out[f"{ptype}Identifier"]={f"{ptype}IdentifierValue": self.valueURI, f"{ptype}IdentifierType": "URL"}
return out
@property
def tag(self): return self.alias or self.value.split(None,1)[-1]
......@@ -284,6 +303,7 @@ class WIKIDATASubject(WIKIDATA_mixin,Subject):
"""here we can calculate the valueURI"""
super().__init__(*args,**kargs)
self.valueURI=os.path.join(self.schemeURI,self.value.split()[0])
def pjson(self,ptype): out=super().pjson(ptype);out[f"{ptype}Name"]=self.value.split(None,1)[-1];return out
class SubjectsFactory:
"""definitions is a dict eg REMOTE.SENSING:(Subject1,Subject2,...)
......@@ -509,23 +529,23 @@ class Metadata:
#sort contributors
ordering=typing.get_args({fi.name:fi.type for fi in dataclasses.fields(Person) if fi.name=='role'}.pop('role'))
self.contributors=tuple(sorted(self.contributors,key=lambda c: ordering.index(c.role)))
def is_completed(self): return bool(self.identifier is not None and self.publisher is not None and self.publicationyear>1900)
def is_completed(self): return bool(self.identifier is not None and self.publisher is not None and self.publicationyear>1900)
def merge_with_existing_metadata(self,old_metadata:dict={}):
"""old_metadata is a json output of api.datacite"""
if not old_metadata: old_metadata=DataCiteAPI.get_registered_metadata(self.identifier.value)
dates=tuple(Date.from_djson(djson) for djson in old_metadata['data']['attributes'].get('dates',[]))
specialdates={d.role:d for d in dates if d.role in ('Issued',)}
self.dates=tuple(specialdates.get(nd.role,nd) for nd in self.dates) #do not change the Issued date
@property
@check_completed
def prefix(self): return self.identifier.value.split('/',1)[0]
@property
@check_completed
def suffix(self): cs=self.identifier.value.split('/',1);return ('' if len(cs)==1 else cs[-1])
@property
def principal_investigator(self):
for p in self.contributors:
......@@ -652,7 +672,32 @@ class Metadata:
if self.suffix: payload['data']['attributes'].update(doi=self.identifier.value)
return payload
@dataclasses.dataclass
class InstrumentMetadata:
identifier:Identifier
name:str
owners:tuple[Organisation,...]
manufacturers:tuple[Organisation,...]
model:Subject
dates:tuple[Date,...]
serial:str
instrument_types:tuple[Subject,...]
landingpage:str=''
def to_pdinst_json(self):
return {"SchemaVersion": "1.0",
"LandingPage": self.landingpage,
"Name": self.name,
"Owners": [{'owner':o.owner_pjson} for o in self.owners],
"Manufacturers": [{'manufacturer': m.manufacturer_pjson} for m in self.manufacturers],
"Model": self.model.pjson('model'),
"Identifier": self.identifier.json,
#"Description": "Placed on the North side of the Remote sensing site at 1m elevation",
"InstrumentType": [{'instrumentType':i.pjson('instrumentType')} for i in self.instrument_types],
#"MeasuredVariables": [{"measuredVariable": {"variableMeasured": "precipitation intensity"}}, {"measuredVariable": {"variableMeasured": "drop size distribution"}}],
"Dates": [{"date": d.pjson} for d in self.dates],
"AlternateIdentifiers": [{"alternateIdentifier": {"alternateIdentifierValue": self.serial, "alternateIdentifierType": "SerialNumber"}}]}
@dataclasses.dataclass
class DataCiteAPI:
"""DataCite API wrapper: implements some methods from https://support.datacite.org/reference/post_dois
......@@ -672,7 +717,7 @@ class DataCiteAPI:
def authorization(self): return (self.username, self.password)
@property
def headers(self): return {"content-type": "application/json","accept": "application/vnd.api+json" }
@classmethod
def get_registered_metadata(cls,doi:str)->dict: return requests.get(cls.api_url+'/'+doi).json()
......@@ -715,3 +760,4 @@ class DataCiteAPI:
payload = { "data": {'type': "dois",'attributes':{'url':landingpage,'doi':ids}}}
return requests.put(url, json=payload, headers=self.headers,auth=self.authorization)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment