Skip to content

Commit aff1387

Browse files
committed
refactor datetime generation
1 parent a03a308 commit aff1387

File tree

3 files changed

+91
-184
lines changed

3 files changed

+91
-184
lines changed

pygeometa/helpers.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,48 @@ def json_serial(obj) -> Any:
9393
msg = f'{obj} type {type(obj)} not serializable'
9494
LOGGER.error(msg)
9595
raise TypeError(msg)
96+
97+
98+
def generate_datetime(date_value: str) -> str:
99+
"""
100+
Helper function to derive RFC3339 date from MCF date type
101+
102+
:param date_value: `str` of date value
103+
104+
:returns: `str` of date-time value
105+
"""
106+
107+
value = None
108+
109+
if isinstance(date_value, str) and date_value != 'None':
110+
if len(date_value) == 10: # YYYY-MM-DD
111+
format_ = '%Y-%m-%d'
112+
elif len(date_value) == 7: # YYYY-MM
113+
format_ = '%Y-%m'
114+
elif len(date_value) == 4: # YYYY
115+
format_ = '%Y'
116+
elif len(date_value) == 19: # YYYY-MM-DDTHH:MM:SS
117+
msg = 'YYYY-MM-DDTHH:MM:SS with no timezone; converting to UTC'
118+
LOGGER.debug(msg)
119+
format_ = '%Y-%m-%dT%H:%M:%S'
120+
121+
LOGGER.debug('date type found; expanding to date-time')
122+
value = datetime.strptime(date_value, format_).strftime('%Y-%m-%dT%H:%M:%SZ') # noqa
123+
124+
elif isinstance(date_value, int) and len(str(date_value)) == 4:
125+
date_value2 = str(date_value)
126+
LOGGER.debug('date type found; expanding to date-time')
127+
format_ = '%Y'
128+
value = datetime.strptime(date_value2, format_).strftime('%Y-%m-%dT%H:%M:%SZ') # noqa
129+
130+
elif isinstance(date_value, (date, datetime)):
131+
value = date_value.strftime('%Y-%m-%dT%H:%M:%SZ')
132+
133+
elif date_value in [None, 'None']:
134+
value = datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ')
135+
136+
else:
137+
msg = f'Unknown date string: {date_value}'
138+
raise RuntimeError(msg)
139+
140+
return value

pygeometa/schemas/ogcapi_records/__init__.py

Lines changed: 6 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,12 @@
4343
#
4444
# =================================================================
4545

46-
from datetime import date, datetime
4746
import logging
4847
import os
4948
from typing import Union
5049

5150
from pygeometa.core import get_charstring
52-
from pygeometa.helpers import json_dumps
51+
from pygeometa.helpers import generate_datetime, json_dumps
5352
from pygeometa.schemas.base import BaseOutputSchema
5453

5554
THISDIR = os.path.dirname(os.path.realpath(__file__))
@@ -158,12 +157,11 @@ def write(self, mcf: dict, stringify: str = True) -> Union[dict, str]:
158157

159158
LOGGER.debug('Checking for dates')
160159

161-
if 'dates' in mcf['identification']:
162-
if 'creation' in mcf['identification']['dates']:
163-
record['properties']['created'] = self.generate_date(mcf['identification']['dates']['creation']) # noqa
164-
165-
if 'revision' in mcf['identification']['dates']:
166-
record['properties']['updated'] = self.generate_date(mcf['identification']['dates']['revision']) # noqa
160+
for key, value in mcf['identification']['dates'].items():
161+
if key == 'creation':
162+
record['properties']['created'] = generate_datetime(value)
163+
elif key == 'revision':
164+
record['properties']['updated'] = generate_datetime(value)
167165

168166
rights = get_charstring(mcf['identification'].get('rights'),
169167
self.lang1, self.lang2)
@@ -417,47 +415,3 @@ def generate_link(self, distribution: dict) -> dict:
417415
link['channel'] = distribution['channel']
418416

419417
return link
420-
421-
def generate_date(self, date_value: str) -> str:
422-
"""
423-
Helper function to derive RFC3339 date from MCF date type
424-
425-
:param date_value: `str` of date value
426-
427-
:returns: `str` of date-time value
428-
"""
429-
430-
value = None
431-
432-
if isinstance(date_value, str) and date_value != 'None':
433-
if len(date_value) == 10: # YYYY-MM-DD
434-
format_ = '%Y-%m-%d'
435-
elif len(date_value) == 7: # YYYY-MM
436-
format_ = '%Y-%m'
437-
elif len(date_value) == 4: # YYYY
438-
format_ = '%Y'
439-
elif len(date_value) == 19: # YYYY-MM-DDTHH:MM:SS
440-
msg = 'YYYY-MM-DDTHH:MM:SS with no timezone; converting to UTC'
441-
LOGGER.debug(msg)
442-
format_ = '%Y-%m-%dT%H:%M:%S'
443-
444-
LOGGER.debug('date type found; expanding to date-time')
445-
value = datetime.strptime(date_value, format_).strftime('%Y-%m-%dT%H:%M:%SZ') # noqa
446-
447-
elif isinstance(date_value, int) and len(str(date_value)) == 4:
448-
date_value2 = str(date_value)
449-
LOGGER.debug('date type found; expanding to date-time')
450-
format_ = '%Y'
451-
value = datetime.strptime(date_value2, format_).strftime('%Y-%m-%dT%H:%M:%SZ') # noqa
452-
453-
elif isinstance(date_value, (date, datetime)):
454-
value = date_value.strftime('%Y-%m-%dT%H:%M:%SZ')
455-
456-
elif date_value in [None, 'None']:
457-
value = datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ')
458-
459-
else:
460-
msg = f'Unknown date string: {date_value}'
461-
raise RuntimeError(msg)
462-
463-
return value

pygeometa/schemas/schema_org/__init__.py

Lines changed: 40 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,13 @@
4343
#
4444
# =================================================================
4545

46-
from datetime import date, datetime
4746
import json
4847
import logging
4948
import os
5049
from typing import Union
5150

5251
from pygeometa.core import get_charstring
53-
from pygeometa.helpers import json_dumps
52+
from pygeometa.helpers import generate_datetime, json_dumps
5453
from pygeometa.schemas.base import BaseOutputSchema
5554

5655
THISDIR = os.path.dirname(os.path.realpath(__file__))
@@ -123,7 +122,8 @@ def import_(self, metadata: str) -> dict:
123122
elif geo['@type'] == 'GeoShape':
124123
mcf['spatial']['datatype'] = 'vector'
125124
mcf['spatial']['geomtype'] = 'polygon'
126-
bbox = geo['box'].split()
125+
bt = geo['box'].split()
126+
bbox = bt[1], bt[0], bt[3], bt[2]
127127

128128
mcf['identification']['extents']['spatial'].append({
129129
'bbox': bbox,
@@ -198,8 +198,7 @@ def write(self, mcf: dict, stringify: str = True) -> Union[dict, str]:
198198
:param stringify: whether to return a string representation (default)
199199
else native (dict, etree)
200200
201-
202-
:returns: `dict` or `str` of MCF as an OARec record representation
201+
:returns: `dict` or `str` of MCF as Schema.org
203202
"""
204203

205204
self.lang1 = mcf['metadata'].get('language')
@@ -216,34 +215,22 @@ def write(self, mcf: dict, stringify: str = True) -> Union[dict, str]:
216215

217216
LOGGER.debug('Generating baseline record')
218217
record = {
219-
'id': mcf['metadata']['identifier'],
220-
'conformsTo': [
221-
'http://www.opengis.net/spec/ogcapi-records-1/1.0/conf/record-core', # noqa
222-
],
223-
'type': 'Feature',
224-
'geometry': {
225-
'type': 'Polygon',
226-
'coordinates': [[
227-
[minx, miny],
228-
[minx, maxy],
229-
[maxx, maxy],
230-
[maxx, miny],
231-
[minx, miny]
232-
]]
233-
},
234-
'properties': {
235-
'title': title[0],
236-
'description': description[0],
237-
'themes': [],
238-
'type': mcf['metadata']['hierarchylevel'],
239-
},
240-
'links': []
218+
'identifier': mcf['metadata']['identifier'],
219+
'@type': dict(zip(TYPES.values(), TYPES.keys()))[mcf['metadata']['hierarchylevel']], # noqa
220+
'spatialCoverage': [{
221+
'@type': 'Place',
222+
'geo': {
223+
'@type': 'GeoShape',
224+
'box': f'{miny},{minx} {maxy},{maxx}'
225+
}
226+
}],
227+
'title': title[0],
228+
'description': description[0],
229+
'distribution': []
241230
}
242231

243232
if self.lang1 is not None:
244-
record['properties']['language'] = {
245-
'code': self.lang1
246-
}
233+
record['inLanguage'] = self.lang1
247234

248235
LOGGER.debug('Checking for temporal')
249236
try:
@@ -265,44 +252,22 @@ def write(self, mcf: dict, stringify: str = True) -> Union[dict, str]:
265252
elif [begin, end] == ['..', '..']:
266253
pass
267254
else:
268-
record['time'] = {
269-
'interval': [begin, end]
270-
}
271-
272-
if 'resolution' in mcf['identification']['extents']['temporal'][0]: # noqa
273-
record['time']['resolution'] = mcf['identification']['extents']['temporal'][0]['resolution'] # noqa
274-
255+
record['temporalCoverage'] = [f'{begin}/{end}']
275256
except (IndexError, KeyError):
276-
record['time'] = None
257+
pass
277258

278259
LOGGER.debug('Checking for dates')
279260

280-
if 'dates' in mcf['identification']:
281-
if 'creation' in mcf['identification']['dates']:
282-
record['properties']['created'] = self.generate_date(mcf['identification']['dates']['creation']) # noqa
283-
284-
if 'revision' in mcf['identification']['dates']:
285-
record['properties']['updated'] = self.generate_date(mcf['identification']['dates']['revision']) # noqa
286-
287-
rights = get_charstring(mcf['identification'].get('rights'),
288-
self.lang1, self.lang2)
289-
290-
if rights != [None, None]:
291-
record['properties']['rights'] = rights[0]
292-
293-
formats = []
294-
for v in mcf['distribution'].values():
295-
format_ = get_charstring(v.get('format'), self.lang1, self.lang2)
296-
if format_[0] is not None:
297-
formats.append(format_[0])
298-
299-
LOGGER.debug('Checking for formats')
300-
if formats:
301-
formats2 = set(formats)
302-
record['properties']['formats'] = [{'name': f} for f in formats2]
261+
for key, value in mcf['identification']['dates'].items():
262+
if key == 'creation':
263+
record['dateCreated'] = generate_datetime(value)
264+
elif key == 'revision':
265+
record['dateModified'] = generate_datetime(value)
266+
elif key == 'publication':
267+
record['datePublished'] = generate_datetime(value)
303268

304269
LOGGER.debug('Checking for contacts')
305-
record['properties']['contacts'] = self.generate_contacts(
270+
record['contacts'] = self.generate_contacts(
306271
mcf['contact'])
307272

308273
all_keywords = []
@@ -332,13 +297,8 @@ def write(self, mcf: dict, stringify: str = True) -> Union[dict, str]:
332297

333298
theme['scheme'] = scheme
334299

335-
record['properties']['themes'].append(theme)
336-
337300
if all_keywords:
338-
record['properties']['keywords'] = all_keywords
339-
340-
if not record['properties']['themes']:
341-
_ = record['properties'].pop('themes', None)
301+
record['keywords'] = all_keywords
342302

343303
LOGGER.debug('Checking for licensing')
344304
if mcf['identification'].get('license') is not None:
@@ -352,14 +312,14 @@ def write(self, mcf: dict, stringify: str = True) -> Union[dict, str]:
352312
'title': license.get('name', 'license for this resource'),
353313
'url': license['url']
354314
}
355-
record['links'].append(self.generate_link(license_link))
315+
record['distribution'].append(self.generate_link(license_link))
356316
else:
357317
LOGGER.debug('Encoding license as property')
358-
record['properties']['license'] = license['name']
318+
record['license'] = license['name']
359319

360320
LOGGER.debug('Checking for distribution')
361321
for value in mcf['distribution'].values():
362-
record['links'].append(self.generate_link(value))
322+
record['distribution'].append(self.generate_link(value))
363323

364324
if stringify:
365325
return json_dumps(record)
@@ -453,7 +413,7 @@ def generate_party(self, contact: dict,
453413
rp['roles'].append(r)
454414

455415
if 'url' in contact:
456-
rp['links'] = [{
416+
rp['distribution'] = [{
457417
'rel': 'canonical',
458418
'type': 'text/html',
459419
'href': contact['url']
@@ -501,20 +461,18 @@ def generate_contacts(self, contact: dict) -> list:
501461

502462
def generate_link(self, distribution: dict) -> dict:
503463
"""
504-
Generates OARec link object from MCF distribution object
464+
Generates Schema.org link object from MCF distribution object
505465
506466
:param distribution: `dict` of MCF distribution
507467
508-
:returns: OARec link object
468+
:returns: Schema.org link object
509469
"""
510470

511-
title = get_charstring(distribution.get('title'),
512-
self.lang1, self.lang2)
513-
514-
name = get_charstring(distribution.get('name'), self.lang1, self.lang2)
471+
name = get_charstring(distribution.get('name'),
472+
self.lang1, self.lang2)
515473

516474
link = {
517-
'href': distribution['url']
475+
'contentUrl': distribution['url']
518476
}
519477

520478
if distribution.get('type') is not None:
@@ -524,59 +482,9 @@ def generate_link(self, distribution: dict) -> dict:
524482
if reltype is not None:
525483
link['rel'] = reltype
526484

527-
if title != [None, None]:
528-
link['title'] = title[0]
485+
if name != [None, None]:
486+
link['name'] = name[0]
529487
elif name != [None, None]:
530-
link['title'] = name[0]
531-
532-
if all(x in distribution['url'] for x in ['{', '}']):
533-
link['templated'] = True
534-
535-
if 'channel' in distribution:
536-
link['channel'] = distribution['channel']
488+
link['name'] = name[0]
537489

538490
return link
539-
540-
def generate_date(self, date_value: str) -> str:
541-
"""
542-
Helper function to derive RFC3339 date from MCF date type
543-
544-
:param date_value: `str` of date value
545-
546-
:returns: `str` of date-time value
547-
"""
548-
549-
value = None
550-
551-
if isinstance(date_value, str) and date_value != 'None':
552-
if len(date_value) == 10: # YYYY-MM-DD
553-
format_ = '%Y-%m-%d'
554-
elif len(date_value) == 7: # YYYY-MM
555-
format_ = '%Y-%m'
556-
elif len(date_value) == 4: # YYYY
557-
format_ = '%Y'
558-
elif len(date_value) == 19: # YYYY-MM-DDTHH:MM:SS
559-
msg = 'YYYY-MM-DDTHH:MM:SS with no timezone; converting to UTC'
560-
LOGGER.debug(msg)
561-
format_ = '%Y-%m-%dT%H:%M:%S'
562-
563-
LOGGER.debug('date type found; expanding to date-time')
564-
value = datetime.strptime(date_value, format_).strftime('%Y-%m-%dT%H:%M:%SZ') # noqa
565-
566-
elif isinstance(date_value, int) and len(str(date_value)) == 4:
567-
date_value2 = str(date_value)
568-
LOGGER.debug('date type found; expanding to date-time')
569-
format_ = '%Y'
570-
value = datetime.strptime(date_value2, format_).strftime('%Y-%m-%dT%H:%M:%SZ') # noqa
571-
572-
elif isinstance(date_value, (date, datetime)):
573-
value = date_value.strftime('%Y-%m-%dT%H:%M:%SZ')
574-
575-
elif date_value in [None, 'None']:
576-
value = datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ')
577-
578-
else:
579-
msg = f'Unknown date string: {date_value}'
580-
raise RuntimeError(msg)
581-
582-
return value

0 commit comments

Comments
 (0)