From ec4edd4699cf174dd82c3db3a0e58f6a5e4edb8e Mon Sep 17 00:00:00 2001 From: karilint Date: Tue, 28 Apr 2026 13:42:35 +0300 Subject: [PATCH] Adjust locality DwC export fields --- .../services/dwcArchiveExportLocalities.ts | 148 ++++-------------- .../dwcArchiveExportLocalities.test.ts | 33 +++- .../functionality/dwc_export_localities.md | 2 + 3 files changed, 61 insertions(+), 122 deletions(-) diff --git a/backend/src/services/dwcArchiveExportLocalities.ts b/backend/src/services/dwcArchiveExportLocalities.ts index d2c5b310..5c257a4c 100644 --- a/backend/src/services/dwcArchiveExportLocalities.ts +++ b/backend/src/services/dwcArchiveExportLocalities.ts @@ -233,19 +233,6 @@ type LocalityForExport = Pick< country: string | null } }> - now_plr: ReadonlyArray<{ - now_proj: { - proj_code: string | null - proj_name: string | null - proj_status: string | null - } - }> - now_lau: ReadonlyArray<{ - lau_date: Date | null - lau_comment: string | null - com_people_now_lau_lau_coordinatorTocom_people: { full_name: string } - com_people_now_lau_lau_authorizerTocom_people: { full_name: string } - }> now_ls: ReadonlyArray<{ com_species: { order_name: string @@ -550,6 +537,20 @@ const LOCALITY_MEASUREMENT_MAPPINGS: Array<{ // TODO(#1150): Add controlled vocabulary / description. measurementMethod: '', }, + { + field: 'basin', + measurementType: 'basin', + measurementUnit: '', + // TODO(#1150): Add field description / controlled vocabulary. + measurementMethod: '', + }, + { + field: 'subbasin', + measurementType: 'subbasin', + measurementUnit: '', + // TODO(#1150): Add field description / controlled vocabulary. + measurementMethod: '', + }, { field: 'rock_type', measurementType: 'rock type', @@ -711,6 +712,20 @@ const LOCALITY_MEASUREMENT_MAPPINGS: Array<{ // TODO(#1150): Add controlled vocabulary. measurementMethod: '', }, + { + field: 'plant_pres', + measurementType: 'Plant Preservation', + measurementUnit: '', + // TODO(#1150): Add controlled vocabulary. + measurementMethod: '', + }, + { + field: 'invert_pres', + measurementType: 'Invertebrate Preservation', + measurementUnit: '', + // TODO(#1150): Add controlled vocabulary. + measurementMethod: '', + }, { field: 'taph_comm', measurementType: 'Taphonomy comment', @@ -951,8 +966,6 @@ export const mapLocalityToMeasurementRows = (locality: LocalityForExport): Local const bosId = buildLocalityMeasurementId(lid, 'bos') const datumPlaneId = buildLocalityMeasurementId(lid, 'datum_plane') - const lastUpdateParentId = buildLocalityMeasurementId(lid, 'last_update') - const hasMaxAgeGroup = isMeaningfulMeasurementValue(locality.max_age) || isMeaningfulMeasurementValue(locality.bfa_max) || @@ -1036,68 +1049,6 @@ export const mapLocalityToMeasurementRows = (locality: LocalityForExport): Local } } - const lastUpdate = locality.now_lau[0] - if (lastUpdate) { - rows.push({ - taxonID, - measurementID: lastUpdateParentId, - parentMeasurementID: '', - measurementType: 'last update', - verbatimMeasurementType: 'now_lau', - measurementValue: '', - measurementUnit: '', - measurementMethod: '', - }) - - if (lastUpdate.lau_date) { - rows.push({ - taxonID, - measurementID: buildLocalityMeasurementId(lid, 'last_update_date'), - parentMeasurementID: lastUpdateParentId, - measurementType: 'last update date', - verbatimMeasurementType: 'now_lau.lau_date', - measurementValue: lastUpdate.lau_date.toISOString().slice(0, 10), - measurementUnit: '', - measurementMethod: '', - }) - } - - rows.push({ - taxonID, - measurementID: buildLocalityMeasurementId(lid, 'last_update_coordinator'), - parentMeasurementID: lastUpdateParentId, - measurementType: 'last update coordinator', - verbatimMeasurementType: 'now_lau.lau_coordinator', - measurementValue: toMaybeMeaningful(lastUpdate.com_people_now_lau_lau_coordinatorTocom_people.full_name), - measurementUnit: '', - measurementMethod: '', - }) - - rows.push({ - taxonID, - measurementID: buildLocalityMeasurementId(lid, 'last_update_authorizer'), - parentMeasurementID: lastUpdateParentId, - measurementType: 'last update authorizer', - verbatimMeasurementType: 'now_lau.lau_authorizer', - measurementValue: toMaybeMeaningful(lastUpdate.com_people_now_lau_lau_authorizerTocom_people.full_name), - measurementUnit: '', - measurementMethod: '', - }) - - if (isMeaningfulString(lastUpdate.lau_comment)) { - rows.push({ - taxonID, - measurementID: buildLocalityMeasurementId(lid, 'last_update_comment'), - parentMeasurementID: lastUpdateParentId, - measurementType: 'last update comment', - verbatimMeasurementType: 'now_lau.lau_comment', - measurementValue: lastUpdate.lau_comment.trim(), - measurementUnit: '', - measurementMethod: '', - }) - } - } - if (hasAnyAgeBasis) { rows.push({ taxonID, @@ -1214,34 +1165,6 @@ export const mapLocalityToMeasurementRows = (locality: LocalityForExport): Local }) } - const projects = locality.now_plr - .map(row => row.now_proj) - .map(project => - [ - toMaybeMeaningful(project.proj_code), - toMaybeMeaningful(project.proj_name), - toMaybeMeaningful(project.proj_status), - ] - .filter(Boolean) - .join(' - ') - ) - .filter(isMeaningfulString) - .map(value => value.trim()) - - if (projects.length) { - rows.push({ - taxonID, - measurementID: buildLocalityMeasurementId(lid, 'projects'), - parentMeasurementID: '', - measurementType: 'Projects', - verbatimMeasurementType: 'now_plr.pid', - measurementValue: projects.join('|'), - measurementUnit: '', - // TODO(#1150): Add field description. - measurementMethod: '', - }) - } - const collectingMethods = locality.now_coll_meth .map(method => method.coll_meth) .filter(value => isMeaningfulString(value)) @@ -1599,21 +1522,6 @@ export const buildDwcLocalityArchiveZipBuffer = async (): Promise => { com_mlist: { select: { institution: true, alt_int_name: true, city: true, state: true, country: true } }, }, }, - now_plr: { - select: { - now_proj: { select: { proj_code: true, proj_name: true, proj_status: true } }, - }, - }, - now_lau: { - take: 1, - orderBy: { lau_date: 'desc' }, - select: { - lau_date: true, - lau_comment: true, - com_people_now_lau_lau_coordinatorTocom_people: { select: { full_name: true } }, - com_people_now_lau_lau_authorizerTocom_people: { select: { full_name: true } }, - }, - }, now_ls: { select: { com_species: { diff --git a/backend/src/unit-tests/dwcArchiveExportLocalities.test.ts b/backend/src/unit-tests/dwcArchiveExportLocalities.test.ts index 1144876e..5489229d 100644 --- a/backend/src/unit-tests/dwcArchiveExportLocalities.test.ts +++ b/backend/src/unit-tests/dwcArchiveExportLocalities.test.ts @@ -112,8 +112,6 @@ describe('DwC-A locality export mapping', () => { now_ss: [], now_coll_meth: [], now_mus: [], - now_plr: [], - now_lau: [], now_ls: [], now_time_unit_now_loc_bfa_maxTonow_time_unit: null, now_time_unit_now_loc_bfa_minTonow_time_unit: null, @@ -158,6 +156,37 @@ describe('DwC-A locality export mapping', () => { expect(rows.some(r => r.verbatimMeasurementType === 'bfa_min')).toEqual(false) }) + it('exports requested locality fields and omits project and last-update rows', () => { + const rows = mapLocalityToMeasurementRows({ + ...baseLocality, + basin: 'Basin value', + subbasin: 'Subbasin value', + bipedal_footprints: true, + invert_pres: 'Invert preservation', + nutrients: 'High', + pers_pollen_ap: 10, + pers_pollen_nap: 20, + pers_pollen_other: 0, + plant_pres: 'Plant preservation', + }) + + expect(rows).toEqual( + expect.arrayContaining([ + expect.objectContaining({ verbatimMeasurementType: 'basin', measurementValue: 'Basin value' }), + expect.objectContaining({ verbatimMeasurementType: 'subbasin', measurementValue: 'Subbasin value' }), + expect.objectContaining({ verbatimMeasurementType: 'bipedal_footprints', measurementValue: 'true' }), + expect.objectContaining({ verbatimMeasurementType: 'invert_pres', measurementValue: 'Invert preservation' }), + expect.objectContaining({ verbatimMeasurementType: 'nutrients', measurementValue: 'High' }), + expect.objectContaining({ verbatimMeasurementType: 'pers_pollen_ap', measurementValue: '10' }), + expect.objectContaining({ verbatimMeasurementType: 'pers_pollen_nap', measurementValue: '20' }), + expect.objectContaining({ verbatimMeasurementType: 'pers_pollen_other', measurementValue: '0' }), + expect.objectContaining({ verbatimMeasurementType: 'plant_pres', measurementValue: 'Plant preservation' }), + ]) + ) + expect(rows.some(r => r.verbatimMeasurementType.startsWith('now_plr'))).toEqual(false) + expect(rows.some(r => r.verbatimMeasurementType.startsWith('now_lau'))).toEqual(false) + }) + it('concatenates collecting methods with |', () => { const rows = mapLocalityToMeasurementRows({ ...baseLocality, diff --git a/documentation/functionality/dwc_export_localities.md b/documentation/functionality/dwc_export_localities.md index 15a795ef..4606eadb 100644 --- a/documentation/functionality/dwc_export_localities.md +++ b/documentation/functionality/dwc_export_localities.md @@ -67,6 +67,8 @@ Concatenation rules (v1): Field coverage (v1): - Fossil Assemblage, Taphonomy, Climate, Ecometrics, and Archaeology tab fields are exported as `MeasurementOrFact` rows when populated (plus selected calculated values such as mean hypsodonty). +- `basin` and `subbasin` are exported both as part of `higherGeography` and as explicit `MeasurementOrFact` rows. +- `now_plr` project links and `now_lau` last-update rows are intentionally excluded. ## Admin-only