diff --git a/specifyweb/backend/stored_queries/query_construct.py b/specifyweb/backend/stored_queries/query_construct.py
index 848795bf7e2..230e7b788a4 100644
--- a/specifyweb/backend/stored_queries/query_construct.py
+++ b/specifyweb/backend/stored_queries/query_construct.py
@@ -2,6 +2,9 @@
from collections import namedtuple, deque
from sqlalchemy import orm, sql, or_
+from sqlalchemy import inspect
+from sqlalchemy.orm.util import AliasedClass
+from sqlalchemy.inspection import inspect as sa_inspect
import specifyweb.specify.models as spmodels
from specifyweb.backend.trees.utils import get_treedefs
@@ -29,47 +32,55 @@ def __new__(cls, *args, **kwargs):
def handle_tree_field(self, node, table, tree_rank: TreeRankQuery, next_join_path, current_field_spec: QueryFieldSpec):
query = self
- if query.collection is None: raise AssertionError( # Not sure it makes sense to query across collections
- f"No Collection found in Query for {table}",
- {"table" : table,
- "localizationKey" : "noCollectionInQuery"})
- logger.info('handling treefield %s rank: %s field: %s', table, tree_rank.name, next_join_path)
+ if query.collection is None:
+ raise AssertionError(
+ f"No Collection found in Query for {table}",
+ {"table": table, "localizationKey": "noCollectionInQuery"},
+ )
+ logger.info("handling treefield %s rank: %s field: %s", table, tree_rank.name, next_join_path)
treedefitem_column = table.name + 'TreeDefItemID'
treedef_column = table.name + 'TreeDefID'
- if (table, 'TreeRanks') in query.join_cache:
- logger.debug("using join cache for %r tree ranks.", table)
- ancestors, treedefs = query.join_cache[(table, 'TreeRanks')]
+ # Determine starting anchor correctly:
+ # If node is already an alias (from a relationship path), use that alias.
+ # If node is the mapped class (base table), don't alias it, start from the base table.
+ is_alias = isinstance(node, AliasedClass)
+ start_alias = node # keep as-is
+ mapped_cls = sa_inspect(node).mapper.class_ if is_alias else node
+
+ # Use the specific start anchor in the cache key, so each branch has its own chain
+ cache_key = (start_alias, "TreeRanks")
+
+ if cache_key in query.join_cache:
+ logger.debug("using join cache for %r tree ranks.", start_alias)
+ ancestors, treedefs = query.join_cache[cache_key]
else:
-
treedefs = get_treedefs(query.collection, table.name)
-
- # We need to take the max here. Otherwise, it is possible that the same rank
- # name may not occur at the same level across tree defs.
max_depth = max(depth for _, depth in treedefs)
-
- ancestors = [node]
- for _ in range(max_depth-1):
- ancestor = orm.aliased(node)
+
+ # Start ancestry from the provided alias (e.g., HostTaxon alias)
+ ancestors = [start_alias]
+
+ # Build parent chain using aliases of the mapped class
+ for _ in range(max_depth - 1):
+ ancestor = orm.aliased(mapped_cls)
query = query.outerjoin(ancestor, ancestors[-1].ParentID == ancestor._id)
ancestors.append(ancestor)
-
- logger.debug("adding to join cache for %r tree ranks.", table)
+ logger.debug("adding to join cache for %r tree ranks.", start_alias)
query = query._replace(join_cache=query.join_cache.copy())
- query.join_cache[(table, 'TreeRanks')] = (ancestors, treedefs)
+ query.join_cache[cache_key] = (ancestors, treedefs)
item_model = getattr(spmodels, table.django_name + "treedefitem")
# TODO: optimize out the ranks that appear? cache them
- treedefs_with_ranks: list[tuple[int, int]] = [tup for tup in [
- (treedef_id, _safe_filter(item_model.objects.filter(treedef_id=treedef_id, name=tree_rank.name).values_list('id', flat=True)))
+ treedefs_with_ranks: list[tuple[int, int]] = [
+ (treedef_id, _safe_filter(item_model.objects.filter(treedef_id=treedef_id, name=tree_rank.name).values_list("id", flat=True)))
for treedef_id, _ in treedefs
# For constructing tree queries for batch edit
if (tree_rank.treedef_id is None or tree_rank.treedef_id == treedef_id)
- ] if tup[1] is not None]
-
+ ]
assert len(treedefs_with_ranks) >= 1, "Didn't find the tree rank across any tree"
treedefitem_params = [treedefitem_id for (_, treedefitem_id) in treedefs_with_ranks]
@@ -96,7 +107,8 @@ def make_tree_field_spec(tree_node):
# We don't want to include treedef if the rank is not present.
new_filters = [
*query.internal_filters,
- or_(getattr(node, treedef_column).in_(defs_to_filter_on), getattr(node, treedef_column) == None)]
+ or_(getattr(start_alias, treedef_column).in_(defs_to_filter_on), getattr(start_alias, treedef_column) == None),
+ ]
query = query._replace(internal_filters=new_filters)
return query, column, field, table
diff --git a/specifyweb/backend/stored_queries/queryfieldspec.py b/specifyweb/backend/stored_queries/queryfieldspec.py
index 01469e73b8c..55ecb7b5576 100644
--- a/specifyweb/backend/stored_queries/queryfieldspec.py
+++ b/specifyweb/backend/stored_queries/queryfieldspec.py
@@ -480,17 +480,26 @@ def add_spec_to_query(
cycle_detector,
)
else:
- query, orm_model, table, field = self.build_join(query, self.join_path)
- if isinstance(field, TreeRankQuery):
- tree_rank_idx = self.join_path.index(field)
+ tree_rank_idxs = [i for i, n in enumerate(self.join_path) if isinstance(n, TreeRankQuery)]
+ if tree_rank_idxs:
+ tree_rank_idx = tree_rank_idxs[0]
+ prefix = self.join_path[:tree_rank_idx] # up to (but not including) the tree-rank node
+ tree_rank_node = self.join_path[tree_rank_idx]
+ suffix = self.join_path[tree_rank_idx + 1 :] # field after the rank, e.g., "Name"
+
+ # Join only the prefix to obtain the correct starting alias (e.g., HostTaxon)
+ query, orm_model, table, _ = self.build_join(query, prefix)
+
+ # Build the CASE/joins for the tree rank starting at that alias
query, orm_field, field, table = query.handle_tree_field(
orm_model,
table,
- field,
- self.join_path[tree_rank_idx + 1 :],
+ tree_rank_node,
+ suffix,
self,
)
else:
+ query, orm_model, table, field = self.build_join(query, self.join_path)
try:
field_name = self.get_field().name
orm_field = getattr(orm_model, field_name)
diff --git a/specifyweb/frontend/js_src/lib/components/AppResources/Filters.tsx b/specifyweb/frontend/js_src/lib/components/AppResources/Filters.tsx
index 02811602936..44e458072e4 100644
--- a/specifyweb/frontend/js_src/lib/components/AppResources/Filters.tsx
+++ b/specifyweb/frontend/js_src/lib/components/AppResources/Filters.tsx
@@ -15,6 +15,7 @@ import { Input, Label } from '../Atoms/Form';
import { icons } from '../Atoms/Icons';
import { Link } from '../Atoms/Link';
import { Dialog } from '../Molecules/Dialog';
+import { hasPermission } from '../Permissions/helpers';
import {
allAppResources,
countAppResources,
@@ -23,7 +24,6 @@ import {
} from './filtersHelpers';
import type { AppResources } from './hooks';
import { appResourceSubTypes, appResourceTypes } from './types';
-import { hasPermission } from '../Permissions/helpers';
export function AppResourcesFilters({
initialResources,
diff --git a/specifyweb/frontend/js_src/lib/components/Attachments/Plugin.tsx b/specifyweb/frontend/js_src/lib/components/Attachments/Plugin.tsx
index 57084c9fd9c..3924016f65f 100644
--- a/specifyweb/frontend/js_src/lib/components/Attachments/Plugin.tsx
+++ b/specifyweb/frontend/js_src/lib/components/Attachments/Plugin.tsx
@@ -23,11 +23,11 @@ import { loadingBar } from '../Molecules';
import { Dialog } from '../Molecules/Dialog';
import { FilePicker } from '../Molecules/FilePicker';
import { ProtectedTable } from '../Permissions/PermissionDenied';
+import { collectionPreferences } from '../Preferences/collectionPreferences';
import { userPreferences } from '../Preferences/userPreferences';
import { AttachmentPluginSkeleton } from '../SkeletonLoaders/AttachmentPlugin';
import { attachmentSettingsPromise, uploadFile } from './attachments';
import { AttachmentViewer } from './Viewer';
-import { collectionPreferences } from '../Preferences/collectionPreferences';
export function AttachmentsPlugin(
props: Parameters[0]
diff --git a/specifyweb/frontend/js_src/lib/components/AttachmentsBulkImport/Upload.tsx b/specifyweb/frontend/js_src/lib/components/AttachmentsBulkImport/Upload.tsx
index fa85db57fd3..e9b9bd05253 100644
--- a/specifyweb/frontend/js_src/lib/components/AttachmentsBulkImport/Upload.tsx
+++ b/specifyweb/frontend/js_src/lib/components/AttachmentsBulkImport/Upload.tsx
@@ -23,6 +23,7 @@ import { strictGetTable } from '../DataModel/tables';
import type { Attachment, Tables } from '../DataModel/types';
import { Dialog } from '../Molecules/Dialog';
import { hasPermission } from '../Permissions/helpers';
+import { collectionPreferences } from '../Preferences/collectionPreferences';
import { ActionState } from './ActionState';
import type { AttachmentUploadSpec, EagerDataSet } from './Import';
import { PerformAttachmentTask } from './PerformAttachmentTask';
@@ -39,7 +40,6 @@ import {
saveForAttachmentUpload,
validateAttachmentFiles,
} from './utils';
-import { collectionPreferences } from '../Preferences/collectionPreferences';
async function prepareForUpload(
dataSet: EagerDataSet,
diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/businessRules.ts b/specifyweb/frontend/js_src/lib/components/DataModel/businessRules.ts
index 87a88209da6..fbbcd168f57 100644
--- a/specifyweb/frontend/js_src/lib/components/DataModel/businessRules.ts
+++ b/specifyweb/frontend/js_src/lib/components/DataModel/businessRules.ts
@@ -71,8 +71,10 @@ export class BusinessRuleManager {
fieldName: string &
(keyof SCHEMA['fields'] | keyof SCHEMA['toOneIndependent'])
): Promise>> {
- // REFACTOR: When checkField is called directly, the promises are not
- // added to the public pendingPromise
+ /*
+ * REFACTOR: When checkField is called directly, the promises are not
+ * added to the public pendingPromise
+ */
const field = this.resource.specifyTable.getField(fieldName);
if (field === undefined) return [];
diff --git a/specifyweb/frontend/js_src/lib/components/FormPlugins/__tests__/dateUtils.test.ts b/specifyweb/frontend/js_src/lib/components/FormPlugins/__tests__/dateUtils.test.ts
index b2b20c07d2f..f38a72f6a8e 100644
--- a/specifyweb/frontend/js_src/lib/components/FormPlugins/__tests__/dateUtils.test.ts
+++ b/specifyweb/frontend/js_src/lib/components/FormPlugins/__tests__/dateUtils.test.ts
@@ -17,24 +17,24 @@ describe('getDateParser', () => {
new Date()
)
).toMatchInlineSnapshot(`
- {
- "formatters": [
- [Function],
- [Function],
- ],
- "max": "9999-12-31",
- "minLength": 10,
- "parser": [Function],
- "required": false,
- "title": "Required Format: MM/DD/YYYY.",
- "type": "date",
- "validators": [
- [Function],
- ],
- "value": "2022-08-31",
- "whiteSpaceSensitive": false,
- }
-`));
+ {
+ "formatters": [
+ [Function],
+ [Function],
+ ],
+ "max": "9999-12-31",
+ "minLength": 10,
+ "parser": [Function],
+ "required": false,
+ "title": "Required Format: MM/DD/YYYY.",
+ "type": "date",
+ "validators": [
+ [Function],
+ ],
+ "value": "2022-08-31",
+ "whiteSpaceSensitive": false,
+ }
+ `));
test('month-year', () =>
expect(getDateParser(undefined, 'month-year', undefined))
diff --git a/specifyweb/frontend/js_src/lib/components/Preferences/Aside.tsx b/specifyweb/frontend/js_src/lib/components/Preferences/Aside.tsx
index 6aaa1115190..2e4e6cd813e 100644
--- a/specifyweb/frontend/js_src/lib/components/Preferences/Aside.tsx
+++ b/specifyweb/frontend/js_src/lib/components/Preferences/Aside.tsx
@@ -7,7 +7,8 @@ import type { GetSet, WritableArray } from '../../utils/types';
import { Link } from '../Atoms/Link';
import { pathIsOverlay } from '../Router/UnloadProtect';
import { scrollIntoView } from '../TreeView/helpers';
-import { PreferenceType, usePrefDefinitions } from './index';
+import type { PreferenceType } from './index';
+import { usePrefDefinitions } from './index';
export function PreferencesAside({
activeCategory,
diff --git a/specifyweb/frontend/js_src/lib/components/Preferences/CollectionDefinitions.tsx b/specifyweb/frontend/js_src/lib/components/Preferences/CollectionDefinitions.tsx
index 7af14f3f74c..e487b6876cc 100644
--- a/specifyweb/frontend/js_src/lib/components/Preferences/CollectionDefinitions.tsx
+++ b/specifyweb/frontend/js_src/lib/components/Preferences/CollectionDefinitions.tsx
@@ -1,4 +1,5 @@
-import { LocalizedString } from 'typesafe-i18n';
+import type { LocalizedString } from 'typesafe-i18n';
+
import { attachmentsText } from '../../localization/attachments';
import { preferencesText } from '../../localization/preferences';
import { queryText } from '../../localization/query';
@@ -8,13 +9,13 @@ import { treeText } from '../../localization/tree';
import { f } from '../../utils/functools';
import type { RA } from '../../utils/types';
import { ensure } from '../../utils/types';
+import { camelToHuman } from '../../utils/utils';
import { genericTables } from '../DataModel/tables';
-import { Tables } from '../DataModel/types';
+import type { Tables } from '../DataModel/types';
+import type { QueryView } from '../QueryBuilder/Header';
import type { StatLayout } from '../Statistics/types';
import type { GenericPreferences } from './types';
import { definePref } from './types';
-import { camelToHuman } from '../../utils/utils';
-import { QueryView } from '../QueryBuilder/Header';
const tableLabel = (tableName: keyof Tables): LocalizedString =>
genericTables[tableName]?.label ?? camelToHuman(tableName);
diff --git a/specifyweb/frontend/js_src/lib/components/Preferences/index.tsx b/specifyweb/frontend/js_src/lib/components/Preferences/index.tsx
index 246bc9ffb34..2c8b96df308 100644
--- a/specifyweb/frontend/js_src/lib/components/Preferences/index.tsx
+++ b/specifyweb/frontend/js_src/lib/components/Preferences/index.tsx
@@ -9,9 +9,11 @@ import type { LocalizedString } from 'typesafe-i18n';
import { usePromise } from '../../hooks/useAsyncState';
import { useBooleanState } from '../../hooks/useBooleanState';
import { commonText } from '../../localization/common';
+import { headerText } from '../../localization/header';
import { preferencesText } from '../../localization/preferences';
import { StringToJsx } from '../../localization/utils';
import { f } from '../../utils/functools';
+import type { IR } from '../../utils/types';
import { Container, H2, Key } from '../Atoms';
import { Button } from '../Atoms/Button';
import { className } from '../Atoms/className';
@@ -21,7 +23,13 @@ import { Submit } from '../Atoms/Submit';
import { LoadingContext, ReadOnlyContext } from '../Core/Contexts';
import { ErrorBoundary } from '../Errors/ErrorBoundary';
import { hasPermission } from '../Permissions/helpers';
+import {
+ ProtectedAction,
+ ProtectedTool,
+} from '../Permissions/PermissionDenied';
import { PreferencesAside } from './Aside';
+import type { BasePreferences } from './BasePreferences';
+import { collectionPreferenceDefinitions } from './CollectionDefinitions';
import { collectionPreferences } from './collectionPreferences';
import { useDarkMode } from './Hooks';
import { DefaultPreferenceItemRender } from './Renderers';
@@ -29,14 +37,6 @@ import type { GenericPreferences, PreferenceItem } from './types';
import { userPreferenceDefinitions } from './UserDefinitions';
import { userPreferences } from './userPreferences';
import { useTopChild } from './useTopChild';
-import { IR } from '../../utils/types';
-import { headerText } from '../../localization/header';
-import { BasePreferences } from './BasePreferences';
-import {
- ProtectedAction,
- ProtectedTool,
-} from '../Permissions/PermissionDenied';
-import { collectionPreferenceDefinitions } from './CollectionDefinitions';
export type PreferenceType = keyof typeof preferenceInstances;
@@ -142,9 +142,9 @@ function Preferences({
>
@@ -323,9 +323,7 @@ export function PreferencesContent({
/>
{item.description !== undefined && (
-
+
{item.description !== undefined && (
{
- return (
-
-
-
- {typeof title === 'function' ? title() : title}
-
- {description !== undefined && (
-
- {typeof description === 'function'
- ? description()
- : description}
-
- )}
- {subCategories.map(([subcategory, data]) =>
- renderSubCategory(category, subcategory, data)
- )}
-
-
- );
- }
+ ) => (
+
+
+
+ {typeof title === 'function' ? title() : title}
+
+ {description !== undefined && (
+
+ {typeof description === 'function'
+ ? description()
+ : description}
+
+ )}
+ {subCategories.map(([subcategory, data]) =>
+ renderSubCategory(category, subcategory, data)
+ )}
+
+
+ )
)}
);
@@ -484,7 +480,7 @@ function UserPrefItem(props: PreferenceItemProps) {
props.subcategory as any,
props.name as any
);
- return ;
+ return ;
}
function CollectionPrefItem(props: PreferenceItemProps) {
@@ -493,7 +489,7 @@ function CollectionPrefItem(props: PreferenceItemProps) {
props.subcategory as any,
props.name as any
);
- return ;
+ return ;
}
function CollectionPreferences(): JSX.Element {
diff --git a/specifyweb/frontend/js_src/lib/components/TreeView/Actions.tsx b/specifyweb/frontend/js_src/lib/components/TreeView/Actions.tsx
index 7155fc0a9d7..28c3c8c7faf 100644
--- a/specifyweb/frontend/js_src/lib/components/TreeView/Actions.tsx
+++ b/specifyweb/frontend/js_src/lib/components/TreeView/Actions.tsx
@@ -20,9 +20,9 @@ import { DeleteButton } from '../Forms/DeleteButton';
import { Dialog } from '../Molecules/Dialog';
import { ResourceLink } from '../Molecules/ResourceLink';
import { hasPermission, hasTablePermission } from '../Permissions/helpers';
+import { collectionPreferences } from '../Preferences/collectionPreferences';
import type { Row } from './helpers';
import { checkMoveViolatesEnforced } from './helpers';
-import { collectionPreferences } from '../Preferences/collectionPreferences';
const treeActions = [
'add',
diff --git a/specifyweb/frontend/js_src/lib/components/WbImportAttachments/index.tsx b/specifyweb/frontend/js_src/lib/components/WbImportAttachments/index.tsx
index 0f46b44f669..54abf29e444 100644
--- a/specifyweb/frontend/js_src/lib/components/WbImportAttachments/index.tsx
+++ b/specifyweb/frontend/js_src/lib/components/WbImportAttachments/index.tsx
@@ -36,6 +36,7 @@ import { loadingBar } from '../Molecules';
import { Dialog } from '../Molecules/Dialog';
import { FilePicker } from '../Molecules/FilePicker';
import { Preview } from '../Molecules/FilePicker';
+import { collectionPreferences } from '../Preferences/collectionPreferences';
import { uniquifyDataSetName } from '../WbImport/helpers';
import { ChooseName } from '../WbImport/index';
import {
@@ -43,7 +44,6 @@ import {
attachmentsToCell,
BASE_TABLE_NAME,
} from '../WorkBench/attachmentHelpers';
-import { collectionPreferences } from '../Preferences/collectionPreferences';
export function WbImportAttachmentsView(): JSX.Element {
useMenuItem('workBench');
diff --git a/specifyweb/frontend/js_src/lib/components/WbPlanView/__tests__/automapper.test.ts b/specifyweb/frontend/js_src/lib/components/WbPlanView/__tests__/automapper.test.ts
index f1838e3e7cf..801eca21018 100644
--- a/specifyweb/frontend/js_src/lib/components/WbPlanView/__tests__/automapper.test.ts
+++ b/specifyweb/frontend/js_src/lib/components/WbPlanView/__tests__/automapper.test.ts
@@ -27,16 +27,19 @@ theories(
* TODO: The tests are mapping these Taxon headers to Component
* over Determination. The issue is not happening within the
* application
- * */
- // 'Class',
- // 'Superfamily',
- // 'Family',
- // 'Genus',
- // 'Subgenus',
- // 'Species',
- // 'Subspecies',
- // 'Species Author',
- // 'Subspecies Author',
+ *
+ */
+ /*
+ * 'Class',
+ * 'Superfamily',
+ * 'Family',
+ * 'Genus',
+ * 'Subgenus',
+ * 'Species',
+ * 'Subspecies',
+ * 'Species Author',
+ * 'Subspecies Author',
+ */
'Who ID First Name',
'Determiner 1 Title',
'Determiner 1 First Name',
@@ -184,18 +187,20 @@ theories(
Latitude2: [['collectingEvent', 'locality', 'latitude2']],
Longitude1: [['collectingEvent', 'locality', 'longitude1']],
Longitude2: [['collectingEvent', 'locality', 'longitude2']],
- // Class: [['determinations', '#1', 'taxon', '$Class', 'name']],
- // Family: [['determinations', '#1', 'taxon', '$Family', 'name']],
- // Genus: [['determinations', '#1', 'taxon', '$Genus', 'name']],
- // Subgenus: [['determinations', '#1', 'taxon', '$Subgenus', 'name']],
- // 'Species Author': [
- // ['determinations', '#1', 'taxon', '$Species', 'author'],
- // ],
- // Species: [['determinations', '#1', 'taxon', '$Species', 'name']],
- // 'Subspecies Author': [
- // ['determinations', '#1', 'taxon', '$Subspecies', 'author'],
- // ],
- // Subspecies: [['determinations', '#1', 'taxon', '$Subspecies', 'name']],
+ /*
+ * Class: [['determinations', '#1', 'taxon', '$Class', 'name']],
+ * Family: [['determinations', '#1', 'taxon', '$Family', 'name']],
+ * Genus: [['determinations', '#1', 'taxon', '$Genus', 'name']],
+ * Subgenus: [['determinations', '#1', 'taxon', '$Subgenus', 'name']],
+ * 'Species Author': [
+ * ['determinations', '#1', 'taxon', '$Species', 'author'],
+ * ],
+ * Species: [['determinations', '#1', 'taxon', '$Species', 'name']],
+ * 'Subspecies Author': [
+ * ['determinations', '#1', 'taxon', '$Subspecies', 'author'],
+ * ],
+ * Subspecies: [['determinations', '#1', 'taxon', '$Subspecies', 'name']],
+ */
'Prep Type 1': [['preparations', '#1', 'prepType', 'name']],
Country: [
['collectingEvent', 'locality', 'geography', '$Country', 'name'],
diff --git a/specifyweb/frontend/js_src/lib/components/WbPlanView/__tests__/linesGetter.test.ts b/specifyweb/frontend/js_src/lib/components/WbPlanView/__tests__/linesGetter.test.ts
index d3703ff070f..8d804e02644 100644
--- a/specifyweb/frontend/js_src/lib/components/WbPlanView/__tests__/linesGetter.test.ts
+++ b/specifyweb/frontend/js_src/lib/components/WbPlanView/__tests__/linesGetter.test.ts
@@ -21,16 +21,19 @@ theories(getLinesFromHeaders, [
* TODO: The tests are mapping these Taxon headers to Component
* over Determination. The issue is not happening within the
* application
- * */
- // 'Class',
- // 'Superfamily',
- // 'Family',
- // 'Genus',
- // 'Subgenus',
- // 'Species',
- // 'Subspecies',
- // 'Species Author',
- // 'Subspecies Author',
+ *
+ */
+ /*
+ * 'Class',
+ * 'Superfamily',
+ * 'Family',
+ * 'Genus',
+ * 'Subgenus',
+ * 'Species',
+ * 'Subspecies',
+ * 'Species Author',
+ * 'Subspecies Author',
+ */
],
runAutoMapper: true,
baseTableName: 'CollectionObject',
@@ -46,87 +49,89 @@ theories(getLinesFromHeaders, [
default: null,
},
},
- // {
- // mappingPath: ['determinations', '#1', 'taxon', '$Class', 'name'],
- // headerName: 'Class',
- // columnOptions: {
- // matchBehavior: 'ignoreNever',
- // nullAllowed: true,
- // default: null,
- // },
- // },
- // {
- // mappingPath: [emptyMapping],
- // headerName: 'Superfamily',
- // columnOptions: {
- // matchBehavior: 'ignoreNever',
- // nullAllowed: true,
- // default: null,
- // },
- // },
- // {
- // mappingPath: ['determinations', '#1', 'taxon', '$Family', 'name'],
- // headerName: 'Family',
- // columnOptions: {
- // matchBehavior: 'ignoreNever',
- // nullAllowed: true,
- // default: null,
- // },
- // },
- // {
- // mappingPath: ['determinations', '#1', 'taxon', '$Genus', 'name'],
- // headerName: 'Genus',
- // columnOptions: {
- // matchBehavior: 'ignoreNever',
- // nullAllowed: true,
- // default: null,
- // },
- // },
- // {
- // mappingPath: ['determinations', '#1', 'taxon', '$Subgenus', 'name'],
- // headerName: 'Subgenus',
- // columnOptions: {
- // matchBehavior: 'ignoreNever',
- // nullAllowed: true,
- // default: null,
- // },
- // },
- // {
- // mappingPath: ['determinations', '#1', 'taxon', '$Species', 'name'],
- // headerName: 'Species',
- // columnOptions: {
- // matchBehavior: 'ignoreNever',
- // nullAllowed: true,
- // default: null,
- // },
- // },
- // {
- // mappingPath: ['determinations', '#1', 'taxon', '$Subspecies', 'name'],
- // headerName: 'Subspecies',
- // columnOptions: {
- // matchBehavior: 'ignoreNever',
- // nullAllowed: true,
- // default: null,
- // },
- // },
- // {
- // mappingPath: ['determinations', '#1', 'taxon', '$Species', 'author'],
- // headerName: 'Species Author',
- // columnOptions: {
- // matchBehavior: 'ignoreNever',
- // nullAllowed: true,
- // default: null,
- // },
- // },
- // {
- // mappingPath: ['determinations', '#1', 'taxon', '$Subspecies', 'author'],
- // headerName: 'Subspecies Author',
- // columnOptions: {
- // matchBehavior: 'ignoreNever',
- // nullAllowed: true,
- // default: null,
- // },
- // },
+ /*
+ * {
+ * mappingPath: ['determinations', '#1', 'taxon', '$Class', 'name'],
+ * headerName: 'Class',
+ * columnOptions: {
+ * matchBehavior: 'ignoreNever',
+ * nullAllowed: true,
+ * default: null,
+ * },
+ * },
+ * {
+ * mappingPath: [emptyMapping],
+ * headerName: 'Superfamily',
+ * columnOptions: {
+ * matchBehavior: 'ignoreNever',
+ * nullAllowed: true,
+ * default: null,
+ * },
+ * },
+ * {
+ * mappingPath: ['determinations', '#1', 'taxon', '$Family', 'name'],
+ * headerName: 'Family',
+ * columnOptions: {
+ * matchBehavior: 'ignoreNever',
+ * nullAllowed: true,
+ * default: null,
+ * },
+ * },
+ * {
+ * mappingPath: ['determinations', '#1', 'taxon', '$Genus', 'name'],
+ * headerName: 'Genus',
+ * columnOptions: {
+ * matchBehavior: 'ignoreNever',
+ * nullAllowed: true,
+ * default: null,
+ * },
+ * },
+ * {
+ * mappingPath: ['determinations', '#1', 'taxon', '$Subgenus', 'name'],
+ * headerName: 'Subgenus',
+ * columnOptions: {
+ * matchBehavior: 'ignoreNever',
+ * nullAllowed: true,
+ * default: null,
+ * },
+ * },
+ * {
+ * mappingPath: ['determinations', '#1', 'taxon', '$Species', 'name'],
+ * headerName: 'Species',
+ * columnOptions: {
+ * matchBehavior: 'ignoreNever',
+ * nullAllowed: true,
+ * default: null,
+ * },
+ * },
+ * {
+ * mappingPath: ['determinations', '#1', 'taxon', '$Subspecies', 'name'],
+ * headerName: 'Subspecies',
+ * columnOptions: {
+ * matchBehavior: 'ignoreNever',
+ * nullAllowed: true,
+ * default: null,
+ * },
+ * },
+ * {
+ * mappingPath: ['determinations', '#1', 'taxon', '$Species', 'author'],
+ * headerName: 'Species Author',
+ * columnOptions: {
+ * matchBehavior: 'ignoreNever',
+ * nullAllowed: true,
+ * default: null,
+ * },
+ * },
+ * {
+ * mappingPath: ['determinations', '#1', 'taxon', '$Subspecies', 'author'],
+ * headerName: 'Subspecies Author',
+ * columnOptions: {
+ * matchBehavior: 'ignoreNever',
+ * nullAllowed: true,
+ * default: null,
+ * },
+ * },
+ */
],
},
{
diff --git a/specifyweb/frontend/js_src/lib/components/WbPlanView/mappingPreview.ts b/specifyweb/frontend/js_src/lib/components/WbPlanView/mappingPreview.ts
index fec15a14a18..7f3b6b1aa2e 100644
--- a/specifyweb/frontend/js_src/lib/components/WbPlanView/mappingPreview.ts
+++ b/specifyweb/frontend/js_src/lib/components/WbPlanView/mappingPreview.ts
@@ -171,7 +171,13 @@ export function generateMappingPathPreview(
? [parentTableOrTreeName, tableNameNonEmpty]
: [tableNameNonEmpty];
+ // Special case for disambiguation: Host taxon under CollectionObject
+ const isHostTaxonCase =
+ baseTableName === 'CollectionObject' &&
+ (parentTableOrTreeName ?? '').trim().toLowerCase() === 'host taxon';
+
return filterArray([
+ ...(isHostTaxonCase ? ['Host'] : []),
...(valueIsTreeRank(databaseTableOrRankName)
? [isAnyRank ? parentTableOrTreeName : tableOrRankName]
: tableNameFormatted),