45
45
from pycsw .core .pygeofilter_evaluate import to_filter
46
46
from pycsw .core .util import bind_url , get_today_and_now , jsonify_links , load_custom_repo_mappings , str2bool , wkt2geom
47
47
from pycsw .ogc .api .oapi import gen_oapi
48
- from pycsw .ogc .api .util import match_env_var , render_j2_template , to_json
48
+ from pycsw .ogc .api .util import match_env_var , render_j2_template , to_json , to_rfc3339
49
49
50
50
LOGGER = logging .getLogger (__name__ )
51
51
@@ -109,7 +109,7 @@ def __init__(self, config: dict):
109
109
try :
110
110
self .limit = int (self .config ['server' ]['maxrecords' ])
111
111
except KeyError :
112
- self .limit = 10
112
+ self .limit = 10
113
113
LOGGER .debug (f'limit: { self .limit } ' )
114
114
115
115
repo_filter = self .config ['repository' ].get ('filter' )
@@ -466,7 +466,7 @@ def queryables(self, headers_, args, collection='metadata:main'):
466
466
headers_ ['Content-Type' ] = 'application/schema+json'
467
467
468
468
if collection not in self .get_all_collections ():
469
- msg = f 'Invalid collection'
469
+ msg = 'Invalid collection'
470
470
LOGGER .exception (msg )
471
471
return self .get_exception (400 , headers_ , 'InvalidParameterValue' , msg )
472
472
@@ -506,6 +506,11 @@ def items(self, headers_, json_post_data, args, collection='metadata:main'):
506
506
:returns: tuple of headers, status code, content
507
507
"""
508
508
509
+ LOGGER .debug (f'Request args: { args .keys ()} ' )
510
+ LOGGER .debug ('converting request argument names to lower case' )
511
+ args = {k .lower (): v for k , v in args .items ()}
512
+ LOGGER .debug (f'Request args (lower case): { args .keys ()} ' )
513
+
509
514
headers_ ['Content-Type' ] = self .get_content_type (headers_ , args )
510
515
511
516
reserved_query_params = [
@@ -539,7 +544,7 @@ def items(self, headers_, json_post_data, args, collection='metadata:main'):
539
544
collections = []
540
545
541
546
if collection not in self .get_all_collections ():
542
- msg = f 'Invalid collection'
547
+ msg = 'Invalid collection'
543
548
LOGGER .exception (msg )
544
549
return self .get_exception (400 , headers_ , 'InvalidParameterValue' , msg )
545
550
@@ -830,7 +835,7 @@ def item(self, headers_, args, collection, item):
830
835
headers_ ['Content-Type' ] = self .get_content_type (headers_ , args )
831
836
832
837
if collection not in self .get_all_collections ():
833
- msg = f 'Invalid collection'
838
+ msg = 'Invalid collection'
834
839
LOGGER .exception (msg )
835
840
return self .get_exception (400 , headers_ , 'InvalidParameterValue' , msg )
836
841
@@ -992,6 +997,7 @@ def get_collection_info(self, collection_name: str = 'metadata:main',
992
997
993
998
collection_info = {
994
999
'id' : id_ ,
1000
+ 'type' : 'catalog' ,
995
1001
'title' : title ,
996
1002
'description' : description ,
997
1003
'itemType' : 'record' ,
@@ -1127,22 +1133,29 @@ def record2json(record, url, collection, mode='ogcapi-records'):
1127
1133
'id' : record .identifier ,
1128
1134
'type' : 'Feature' ,
1129
1135
'geometry' : None ,
1130
- 'time' : record .date ,
1131
1136
'properties' : {},
1132
1137
'links' : []
1133
1138
}
1134
1139
1140
+ try :
1141
+ dt , dt_type = to_rfc3339 (record .date )
1142
+ record_dict ['time' ] = {
1143
+ dt_type : dt
1144
+ }
1145
+ except Exception :
1146
+ record_dict ['time' ] = None
1147
+
1135
1148
# todo; for keywords with a scheme use the theme property
1136
- themes = []
1137
1149
if record .topicategory :
1150
+ themes = []
1138
1151
themes .append ({'concepts' : [record .topicategory ],
1139
1152
'scheme' : 'https://standards.iso.org/iso/19139/resources/gmxCodelists.xml#MD_TopicCategoryCode' })
1140
- record_dict ['properties' ]['themes' ] = themes
1153
+ record_dict ['properties' ]['themes' ] = themes
1141
1154
1142
1155
if record .otherconstraints :
1143
- if isinstance (record .otherconstraints , str ):
1156
+ if isinstance (record .otherconstraints , str ) and record . otherconstraints not in [ None , 'None' ] :
1144
1157
record .otherconstraints = [record .otherconstraints ]
1145
- record_dict ['properties' ]['license' ] = ", " .join (record .otherconstraints )
1158
+ record_dict ['properties' ]['license' ] = ", " .join (record .otherconstraints )
1146
1159
1147
1160
record_dict ['properties' ]['updated' ] = record .insert_date
1148
1161
@@ -1168,7 +1181,7 @@ def record2json(record, url, collection, mode='ogcapi-records'):
1168
1181
record_dict ['properties' ]['description' ] = record .abstract
1169
1182
1170
1183
if record .format :
1171
- record_dict ['properties' ]['formats' ] = [record .format ]
1184
+ record_dict ['properties' ]['formats' ] = [{ 'name' : record .format } ]
1172
1185
1173
1186
if record .keywords :
1174
1187
record_dict ['properties' ]['keywords' ] = [x for x in record .keywords .split (',' )]
@@ -1181,58 +1194,67 @@ def record2json(record, url, collection, mode='ogcapi-records'):
1181
1194
rcnt .append ({
1182
1195
'name' : cnt ['name' ],
1183
1196
'organization' : cnt .get ('organization' , '' ),
1184
- 'positionName ' : cnt .get ('position' , '' ),
1185
- 'roles' : [
1186
- { 'name ' : cnt . get ( 'role' , '' )}
1187
- ],
1188
- 'contactInfo' : {
1189
- 'phone ' : { 'work' : cnt . get ( 'phone' , '' )},
1190
- 'email ' : { 'work' : cnt .get ('email' , '' )},
1191
- 'address' : {
1192
- 'work ' : {
1193
- 'deliveryPoint' : cnt .get ('address' , '' ),
1194
- 'city' : cnt .get ('city' , '' ),
1195
- 'administrativeArea' : cnt .get ('region' , '' ),
1196
- 'postalCode' : cnt .get ('postcode' , '' ),
1197
- 'country' : cnt .get ('country' , '' ),
1198
- }
1199
- },
1200
- 'url ' : cnt .get ('onlineresource' , ' ' )
1201
- }
1197
+ 'position ' : cnt .get ('position' , '' ),
1198
+ 'roles' : [cnt . get ( 'role' , '' )],
1199
+ 'phones ' : [{
1200
+ 'value' : cnt . get ( 'phone' , '' )
1201
+ }],
1202
+ 'emails ' : [{
1203
+ 'value ' : cnt .get ('email' , '' )
1204
+ }],
1205
+ 'addresses ' : [ {
1206
+ 'deliveryPoint' : [ cnt .get ('address' , '' )] ,
1207
+ 'city' : cnt .get ('city' , '' ),
1208
+ 'administrativeArea' : cnt .get ('region' , '' ),
1209
+ 'postalCode' : cnt .get ('postcode' , '' ),
1210
+ 'country' : cnt .get ('country' , '' )
1211
+ }],
1212
+ 'links' : [{
1213
+ 'href ' : cnt .get ('onlineresource' )
1214
+ }]
1202
1215
})
1203
1216
except Exception as err :
1204
1217
LOGGER .exception (f"failed to parse contact of { record .identifier } : { err } " )
1205
1218
except Exception as err :
1206
1219
LOGGER .exception (f"failed to parse contacts json of { record .identifier } : { err } " )
1207
- record_dict ['properties' ]['providers' ] = rcnt
1220
+
1221
+ record_dict ['properties' ]['contacts' ] = rcnt
1208
1222
1209
1223
if record .themes not in [None , '' , 'null' ]:
1210
- ogcapiThemes = []
1224
+ ogcapi_themes = []
1211
1225
# For a scheme, prefer uri over label
1212
1226
# OWSlib currently uses .keywords_object for keywords with url, see https://github.com/geopython/OWSLib/pull/765
1213
1227
try :
1214
1228
for theme in json .loads (record .themes ):
1215
1229
try :
1216
- ogcapiThemes .append ({
1230
+ ogcapi_themes .append ({
1217
1231
'scheme' : theme ['thesaurus' ].get ('url' , theme ['thesaurus' ].get ('title' , '' )),
1218
1232
'concepts' : [c for c in theme .get ('keywords_object' , []) if c not in [None , '' ]]
1219
1233
})
1220
1234
except Exception as err :
1221
1235
LOGGER .exception (f"failed to parse theme of { record .identifier } : { err } " )
1222
1236
except Exception as err :
1223
1237
LOGGER .exception (f"failed to parse themes json of { record .identifier } : { err } " )
1224
- record_dict ['properties' ]['themes' ] = ogcapiThemes
1238
+
1239
+ record_dict ['properties' ]['themes' ] = ogcapi_themes
1225
1240
1226
1241
if record .links :
1227
1242
rdl = record_dict ['links' ]
1228
1243
1229
1244
for link in jsonify_links (record .links ):
1245
+ if link ['url' ] in [None , 'None' ]:
1246
+ LOGGER .debug (f'Skipping null link: { link } ' )
1247
+ continue
1248
+
1230
1249
link2 = {
1231
- 'href' : link ['url' ],
1232
- 'name' : link .get ('name' ),
1233
- 'description' : link .get ('description' ),
1234
- 'type' : link .get ('protocol' )
1250
+ 'href' : link ['url' ]
1235
1251
}
1252
+ if link .get ('name' ) not in [None , 'None' ]:
1253
+ link2 ['name' ] = link ['name' ]
1254
+ if link .get ('description' ) not in [None , 'None' ]:
1255
+ link2 ['description' ] = link ['description' ]
1256
+ if link .get ('protocol' ) not in [None , 'None' ]:
1257
+ link2 ['procotol' ] = link ['protocol' ]
1236
1258
if 'rel' in link :
1237
1259
link2 ['rel' ] = link ['rel' ]
1238
1260
elif link ['protocol' ] == 'WWW:LINK-1.0-http--image-thumbnail' :
@@ -1289,18 +1311,28 @@ def record2json(record, url, collection, mode='ogcapi-records'):
1289
1311
if record .time_begin or record .time_end :
1290
1312
if record .time_end not in [None , '' ]:
1291
1313
if record .time_begin not in [None , '' ]:
1292
- record_dict ['time' ] = [record .time_begin , record .time_end ]
1314
+ begin , _ = to_rfc3339 (record .time_begin )
1315
+ end , _ = to_rfc3339 (record .time_end )
1316
+ record_dict ['time' ] = {
1317
+ 'interval' : [begin , end ]
1318
+ }
1293
1319
else :
1294
- record_dict ['time' ] = record .time_end
1320
+ end , end_type = to_rfc3339 (record .time_end )
1321
+ record_dict ['time' ] = {
1322
+ end_type : end
1323
+ }
1295
1324
else :
1296
- record_dict ['time' ] = record .time_begin
1325
+ begin , begin_type = to_rfc3339 (record .time_begin )
1326
+ record_dict ['time' ] = {
1327
+ begin_type : begin
1328
+ }
1297
1329
1298
1330
if mode == 'stac-api' :
1299
- record_dict ['properties' ]['datetime' ] = record .date
1331
+ record_dict ['properties' ]['datetime' ] = to_rfc3339 ( record .date )
1300
1332
1301
1333
if None not in [record .time_begin , record .time_end ]:
1302
- record_dict ['properties' ]['start_datetime' ] = record .time_begin
1303
- record_dict ['properties' ]['end_datetime' ] = record .time_end
1334
+ record_dict ['properties' ]['start_datetime' ] = to_rfc3339 ( record .time_begin )
1335
+ record_dict ['properties' ]['end_datetime' ] = to_rfc3339 ( record .time_end )
1304
1336
1305
1337
return record_dict
1306
1338
@@ -1322,7 +1354,7 @@ def build_anytext(name, value):
1322
1354
tokens = value .split (',' )
1323
1355
1324
1356
if len (tokens ) == 1 and ' ' not in value : # single term
1325
- LOGGER .debug (f 'Single term with no spaces' )
1357
+ LOGGER .debug ('Single term with no spaces' )
1326
1358
return f"{ name } ILIKE '%{ value } %'"
1327
1359
1328
1360
for token in tokens :
0 commit comments