Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ exports[`autoMerge cautious 1`] = `
"agentAttachments": [],
"agentGeographies": [],
"agentSpecialties": [],
"agentType": 1,
"agentType": 0,
"catalogerOf": "/api/specify/CollectionObject?cataloger=2305",
"collContentContact": null,
"collTechContact": null,
Expand All @@ -121,7 +121,7 @@ exports[`autoMerge cautious 1`] = `
"date1": null,
"date1Precision": null,
"date2": null,
"date2Precision": null,
"date2Precision": 2,
"dateOfBirth": null,
"dateOfBirthPrecision": null,
"dateOfDeath": null,
Expand Down Expand Up @@ -277,7 +277,7 @@ exports[`autoMerge not cautious 1`] = `
"agentAttachments": [],
"agentGeographies": [],
"agentSpecialties": [],
"agentType": 1,
"agentType": 0,
"catalogerOf": "/api/specify/CollectionObject?cataloger=2305",
"collContentContact": null,
"collTechContact": null,
Expand All @@ -287,7 +287,7 @@ exports[`autoMerge not cautious 1`] = `
"date1": "2020-01-01",
"date1Precision": null,
"date2": null,
"date2Precision": null,
"date2Precision": 2,
"dateOfBirth": null,
"dateOfBirthPrecision": null,
"dateOfDeath": null,
Expand Down
25 changes: 20 additions & 5 deletions specifyweb/frontend/js_src/lib/components/Merging/autoMerge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,16 @@ function mergeField(
resource[field.name],
]);
const values = parentChildValues.map(([_, child]) => child);
const nonFalsyValues = f.unique(values.filter(Boolean));
const nonFalsyValues = f.unique(
values.filter(
(value) =>
value !== null &&
value !== undefined &&
value !== '' &&
// Preserve zeros and false, but drop NaN
value === value
)
);
const firstValue = nonFalsyValues[0] ?? values[0];
if (field.isRelationship)
if (field.isDependent())
Expand Down Expand Up @@ -150,8 +159,9 @@ function mergeField(
// Pick the longest value
return (
Array.from(nonFalsyValues).sort(
sortFunction((string) =>
typeof string === 'string' ? string.length : 0
sortFunction(
(string) => (typeof string === 'string' ? string.length : 0),
true
)
)[0] ?? firstValue
);
Expand Down Expand Up @@ -213,10 +223,15 @@ function mergeDependentField(
sourceValue: ReturnType<typeof mergeField>
): ReturnType<typeof mergeField> {
const sourceField = strictDependentFields()[fieldName];
const sourceResource = resources.find(
const matchingResources = resources.filter(
(resource) => resource[sourceField] === sourceValue
);
return sourceResource?.[fieldName] ?? null;
const preferredResource =
matchingResources.find((resource) => {
const value = resource[fieldName];
return value !== null && value !== undefined;
}) ?? matchingResources[0];
return preferredResource?.[fieldName] ?? null;
}

/**
Expand Down
56 changes: 31 additions & 25 deletions specifyweb/frontend/js_src/lib/components/Merging/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { icons } from '../Atoms/Icons';
import { Link } from '../Atoms/Link';
import { Submit } from '../Atoms/Submit';
import { LoadingContext } from '../Core/Contexts';
import { addMissingFields } from '../DataModel/addMissingFields';
import { runAllFieldChecks } from '../DataModel/businessRules';
import type { AnySchema, SerializedResource } from '../DataModel/helperTypes';
import type { SpecifyResource } from '../DataModel/legacyTypes';
Expand Down Expand Up @@ -210,31 +211,36 @@ function Merging({
const clones = sortedResources.slice(1);

const [merged, setMerged] = useAsyncState(
React.useCallback(
async () =>
records === undefined || initialRecords.current === undefined
? undefined
: postMergeResource(
initialRecords.current,
autoMerge(
table,
initialRecords.current,
userPreferences.get(
'recordMerging',
'behavior',
'autoPopulate'
),
target.id
)
).then(async (merged) => {
const mergedResource = deserializeResource(
merged as SerializedResource<AnySchema>
);
if (merged !== undefined) await runAllFieldChecks(mergedResource);
return mergedResource;
}),
[table, records]
),
React.useCallback(async () => {
if (
records === undefined ||
initialRecords.current === undefined ||
target === undefined
)
return undefined;

const shouldAutoPopulate = userPreferences.get(
'recordMerging',
'behavior',
'autoPopulate'
);

const mergedPayload = shouldAutoPopulate
? await postMergeResource(
initialRecords.current,
autoMerge(table, initialRecords.current, false, target.id)
)
: addMissingFields(
table.name,
{} as Partial<SerializedResource<AnySchema>>
);

const mergedResource = deserializeResource(
mergedPayload as SerializedResource<AnySchema>
);
if (mergedPayload !== undefined) await runAllFieldChecks(mergedResource);
return mergedResource;
}, [table, records, target]),
true
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ import { definePref } from './types';
const tableLabel = (tableName: keyof Tables): LocalizedString =>
genericTables[tableName]?.label ?? camelToHuman(tableName);

const fieldLabel = (tableName: keyof Tables, fieldName: keyof typeof genericTables[keyof Tables]['field']): LocalizedString =>
const fieldLabel = (
tableName: keyof Tables,
fieldName: keyof (typeof genericTables)[keyof Tables]['field']
): LocalizedString =>
genericTables[tableName]?.field[fieldName].label ?? camelToHuman(fieldName);

export const collectionPreferenceDefinitions = {
Expand Down Expand Up @@ -136,7 +139,8 @@ export const collectionPreferenceDefinitions = {
description: () =>
statsText.showPreparationsTotalDescription({
preparationTableName: tableLabel('Preparation'),
lowerPreparationTableName: tableLabel('Preparation').toLowerCase(),
lowerPreparationTableName:
tableLabel('Preparation').toLowerCase(),
prepTypeTableName: tableLabel('PrepType').toLowerCase(),
}),
requiresReload: false,
Expand Down Expand Up @@ -190,14 +194,24 @@ export const collectionPreferenceDefinitions = {
inheritance: definePref<boolean>({
title: () =>
preferencesText.inheritanceCatNumberPref({
catalogNumberFieldName: fieldLabel('CollectionObject','catalogNumber'),
collectionObjectGroupTableName: tableLabel('CollectionObjectGroup'),
catalogNumberFieldName: fieldLabel(
'CollectionObject',
'catalogNumber'
),
collectionObjectGroupTableName: tableLabel(
'CollectionObjectGroup'
),
}),
description: () =>
preferencesText.inheritanceCatNumberPrefDescription({
catalogNumberFieldName: fieldLabel('CollectionObject','catalogNumber'),
catalogNumberFieldName: fieldLabel(
'CollectionObject',
'catalogNumber'
),
collectionObjectTableName: tableLabel('CollectionObject'),
collectionObjectGroupTableName: tableLabel('CollectionObjectGroup'),
collectionObjectGroupTableName: tableLabel(
'CollectionObjectGroup'
),
}),
requiresReload: false,
visible: true,
Expand All @@ -211,7 +225,7 @@ export const collectionPreferenceDefinitions = {
catalogNumberParentInheritance: {
title: () =>
queryText.parentCatalogNumberInheritance({
componentTableName: tableLabel('Component')
componentTableName: tableLabel('Component'),
}),
subCategories: {
behavior: {
Expand All @@ -220,12 +234,18 @@ export const collectionPreferenceDefinitions = {
inheritance: definePref<boolean>({
title: () =>
preferencesText.inheritanceCatNumberParentCOPref({
catalogNumberFieldName: fieldLabel('CollectionObject','catalogNumber'),
catalogNumberFieldName: fieldLabel(
'CollectionObject',
'catalogNumber'
),
componentTableName: tableLabel('Component'),
}),
description: () =>
preferencesText.inheritanceCatNumberParentCOPrefDescription({
catalogNumberFieldName: fieldLabel('CollectionObject','catalogNumber'),
catalogNumberFieldName: fieldLabel(
'CollectionObject',
'catalogNumber'
),
componentTableName: tableLabel('Component'),
collectionObjectTableName: tableLabel('CollectionObject'),
}),
Expand All @@ -251,7 +271,10 @@ export const collectionPreferenceDefinitions = {
uniqueness: definePref<boolean>({
title: () =>
preferencesText.uniqueCatNumberAcrossCompAndCo({
catalogNumberFieldName: fieldLabel('CollectionObject','catalogNumber'),
catalogNumberFieldName: fieldLabel(
'CollectionObject',
'catalogNumber'
),
componentTableName: tableLabel('Component'),
collectionObjectTableName: tableLabel('CollectionObject'),
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1792,7 +1792,7 @@ export const userPreferenceDefinitions = {
description: preferencesText.autoPopulateDescription(),
requiresReload: false,
visible: 'protected',
defaultValue: false,
defaultValue: true,
type: 'java.lang.Boolean',
}),
},
Expand Down
Loading