51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398 | class SpecimenView(DefaultView):
"""
Controller for displaying a specimen record.
"""
resource_id = toolkit.config.get('ckanext.nhm.specimen_resource_id')
grid_default_columns = DarwinCoreView.grid_default_columns
grid_column_widths = DarwinCoreView.grid_column_widths
field_facets = [
'collectionCode',
'typeStatus',
'family',
'genus',
'associatedMedia.category',
'gbifIssue',
]
# Additional search filter options
filter_options = [has_image, has_lat_long, exclude_mineralogy]
field_groups = OrderedDict(
[
(
'Classification',
OrderedDict(
[
('scientificName', 'Scientific name'),
('scientificNameAuthorship', 'Author'),
('kingdom', 'Kingdom'),
('phylum', 'Phylum'),
('class', 'Class'),
('order', 'Order'),
('family', 'Family'),
('genus', 'Genus'),
('subgenus', 'Subgenus'),
('specificEpithet', 'Species'),
('infraspecificEpithet', 'Subspecies'),
('higherClassification', 'Higher classification'),
('taxonRank', 'Taxon rank'),
]
),
),
(
'Location',
OrderedDict(
[
('labelLocality', 'Label locality'),
('locality', 'Locality'),
('stateProvince', 'State province'),
('mine', 'Mine'),
('miningDistrict', 'Mining district'),
('viceCounty', 'Vice County'),
('country', 'Country'),
('continent', 'Continent'),
('island', 'Island'),
('islandGroup', 'Island group'),
('waterBody', 'Water body'),
('higherGeography', 'Higher geography'),
('decimalLatitude', 'Decimal latitude'),
('decimalLongitude', 'Decimal longitude'),
('verbatimLatitude', 'Verbatim latitude'),
('verbatimLongitude', 'Verbatim longitude'),
('coordinateUncertaintyInMeters', 'Coordinate uncertainty'),
('centroid', 'Centroid'),
('maxError', 'Max error'),
('geodeticDatum', 'Geodetic datum'),
('georeferenceProtocol', 'Georeference protocol'),
('minimumElevationInMeters', 'Minimum elevation(m)'),
('maximumElevationInMeters', 'Maximum elevation(m)'),
('minimumDepthInMeters', 'Minimum depth(m)'),
('maximumDepthInMeters', 'Maximum depth(m)'),
]
),
),
(
'Collection event',
OrderedDict(
[
('recordedBy', 'Recorded by'),
('recordNumber', 'Record number'),
('year', 'Year'),
('month', 'Month'),
('day', 'Day'),
('eventTime', 'Event time'),
('expedition', 'Expedition'),
('habitat', 'Habitat'),
('vessel', 'Vessel'),
('samplingProtocol', 'Sampling protocol'),
]
),
),
(
'Identification',
OrderedDict(
[
('identifiedBy', 'Identified by'),
('dateIdentified', 'Date identified'),
('identificationQualifier', 'Identification qualifier'),
('typeStatus', 'Type status'),
('determinations', 'Determinations'),
]
),
),
(
'Specimen',
OrderedDict(
[
('catalogNumber', 'Catalogue number'),
('collectionCode', 'Collection code'),
('subDepartment', 'Sub department'),
('otherCatalogNumbers', 'Other catalog numbers'),
('registrationCode', 'Registration code'),
('kindOfObject', 'Kind of object'),
('preparations', 'Preparations'),
('preparationType', 'Preparation type'),
('preservative', 'Preservative'),
('collectionKind', 'Collection kind'),
('collectionName', 'Collection name'),
('donorName', 'Donor name'),
('kindOfCollection', 'Kind of collection'),
('observedWeight', 'Observed weight'),
('individualCount', 'Individual count'),
('sex', 'Sex'),
('lifeStage', 'Life stage'),
('catalogueDescription', 'Catalogue description'),
]
),
),
(
'Mineralogy',
OrderedDict(
[
('dateRegistered', 'Date registered'),
('occurrence', 'Occurrence'),
('commodity', 'Commodity'),
('depositType', 'Deposit type'),
('texture', 'Texture'),
('identificationAsRegistered', 'Identification as registered'),
('identificationDescription', 'Identification description'),
('identificationVariety', 'Identification variety'),
('identificationOther', 'Identification other'),
('hostRock', 'Host rock'),
('age', 'Age'),
('ageType', 'Age type'),
('geologyRegion', 'Geology region'),
('mineralComplex', 'Mineral complex'),
('tectonicProvince', 'Tectonic province'),
('registeredWeight', 'Registered weight'),
]
),
),
(
'Stratigraphy',
OrderedDict(
[
('earliestEonOrLowestEonothem', 'Earliest eon/lowest eonothem'),
('latestEonOrHighestEonothem', 'Latest eon/highest eonothem'),
('earliestEraOrLowestErathem', 'Earliest era/lowest erathem'),
('latestEraOrHighestErathem', 'Latest era/highest erathem'),
(
'earliestPeriodOrLowestSystem',
'Earliest period/lowest system',
),
('latestPeriodOrHighestSystem', 'Latest period/highest system'),
('earliestEpochOrLowestSeries', 'Earliest epoch/lowest series'),
('latestEpochOrHighestSeries', 'Latest epoch/highest series'),
('earliestAgeOrLowestStage', 'Earliest age/lowest stage'),
('latestAgeOrHighestStage', 'Latest age/highest stage'),
('lowestBiostratigraphicZone', 'Lowest biostratigraphic zone'),
(
'highestBiostratigraphicZone',
'Highest biostratigraphic zone',
),
('group', 'Group'),
('formation', 'Formation'),
('member', 'Member'),
('bed', 'Bed'),
('chronostratigraphy', 'Chronostratigraphy'),
('lithostratigraphy', 'Lithostratigraphy'),
]
),
),
(
'Meteorites',
OrderedDict(
[
('meteoriteType', 'Meteorite type'),
('meteoriteGroup', 'Meteorite group'),
('chondriteAchondrite', 'Chondrite Achondrite'),
('meteoriteClass', 'Meteorite class'),
('petrologyType', 'Petrology type'),
('petrologySubtype', 'Petrology subtype'),
('recovery', 'Recovery'),
('recoveryDate', 'Recovery date'),
('recoveryWeight', 'Recovery weight'),
# "Registered weight unit", # Merged into Registered weight
]
),
),
(
'Botany',
OrderedDict(
[
('exsiccata', 'Exsiccata'),
('exsiccataNumber', 'Exsiccata number'),
('plantDescription', 'Plant description'),
('cultivated', 'Cultivated'),
]
),
),
(
'Zoology',
OrderedDict(
[
('populationCode', 'Population code'),
('nestShape', 'Nest shape'),
('nestSite', 'Nest site'),
('clutchSize', 'Clutch size'),
('setMark', 'Set mark'),
('barcode', 'Barcode'),
('extractionMethod', 'Extraction method'),
('resuspendedIn', 'Resuspended in'),
('totalVolume', 'Total volume'),
('partType', 'Part type'),
]
),
),
(
'Record',
OrderedDict(
[
('occurrenceID', 'Occurrence ID'),
('modified', 'Modified'),
('created', 'Created'),
('recordType', 'Record type'),
]
),
),
]
)
def render_record(self, c):
"""
Render a record Called from record controller, when viewing a record page.
:param c:
:returns: html
"""
occurrence_id = c.record_dict.get('occurrenceID')
log.info(f'Viewing record {occurrence_id}')
c.record_title = c.record_dict.get('catalogNumber', None) or occurrence_id
# Act on a deep copy of field groups, so deleting element will not have
# any impact
c.field_groups = deepcopy(self.field_groups)
# Some fields are being merged together - in which case we'll need custom filters
# This can be set to bool false to not display a filter
c.custom_filters = {}
if c.record_dict.get('registeredWeight', None) and c.record_dict.get(
'registeredWeightUnit', None
):
# Create custom filter which acts on both weight and units
c.custom_filters['registeredWeight'] = (
f'registeredWeight:{c.record_dict["registeredWeight"]}|registeredWeightUnit:{c.record_dict["registeredWeightUnit"]}'
)
# Merge unit into the field
c.record_dict['registeredWeight'] += (
f' {c.record_dict["registeredWeightUnit"]}'
)
# add a meters unit to the coordinateUncertaintyInMeters value
coordinate_uncertainty_in_meters = c.record_dict.get(
'coordinateUncertaintyInMeters'
)
if coordinate_uncertainty_in_meters is not None:
c.record_dict['coordinateUncertaintyInMeters'] = (
f'{coordinate_uncertainty_in_meters}m'
)
c.custom_filters['coordinateUncertaintyInMeters'] = (
f'coordinateUncertaintyInMeters:{coordinate_uncertainty_in_meters}'
)
collection_date = []
collection_date_filter = []
# Merge day, month, year into one collection date field
for k in ('day', 'month', 'year'):
# Delete the exists field
try:
del c.field_groups['Collection event'][k]
except KeyError:
pass
# Add to collection date field
if c.record_dict.get(k, None):
collection_date.append(c.record_dict.get(k))
collection_date_filter.append(f'{k}:{c.record_dict.get(k)}')
# Join the date for the record view
c.record_dict['collectionDate'] = ' / '.join(collection_date)
# Create a custom filter, so collection date filters on day, month and year
c.custom_filters['collectionDate'] = '|'.join(collection_date_filter)
c.field_groups['Collection event']['collectionDate'] = 'Collection date'
# Parse determination names
c.record_dict['determinations'] = {}
c.record_dict['determination_labels'] = []
for field in [
'determinationNames',
'determinationTypes',
'determinationFiledAs',
]:
label = field.replace('determination', '')
# Add a space before capital letters
label = re.sub('([A-Z])"', ' \1"', label)
c.record_dict['determination_labels'].append(label)
value = c.record_dict.get(field, None)
if not value:
value = []
elif not isinstance(value, list):
value = [value]
c.record_dict['determinations'][label] = value
c.record_dict['determinations']['_len'] = max(
[len(l) for l in c.record_dict['determinations'].values()]
)
# Set determinations to None if we don't have any values - required by the
# specimen template
# to hide the Identification block
if not c.record_dict['determinations']['_len']:
c.record_dict['determinations'] = None
# No filters for determinations
c.custom_filters['determinations'] = None
return toolkit.render('record/specimen.html')
|