Merge v7.12.0.4 into main#8009
Conversation
|
Do we want to create another PR merging the issue in v7.12.0.5, or add it to this one? |
Make schema config updates deterministic and skip internal id fields
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a migrate_sp6_users flag to permission initialization; changes app-resource discipline mapping to use discipline.name and relaxes selection filters; reads Celery task id defensively in background tasks; tightens stored-query temporal and tree-rank join logic; refactors schema-config creation and adds deduplication utilities; updates migration/command flow to use task-style invocation and skip SP6 user migration; extends frontend QueryComboBox with an onSavingNewRecord hook to set Agent.division; and adds related tests. Changes
🚥 Pre-merge checks | ✅ 4 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
@acwhite211 Please go ahead and create another PR merging that into main. |
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
specifyweb/specify/migration_utils/update_schema_config.py (1)
762-807:⚠️ Potential issue | 🟠 MajorRepoint
SpLocaleItemStrrows to the keeper instead of deleting them.Here the cleanup deletes all strings attached to duplicate items before deleting the items themselves. If the surviving item is the one without strings, the schema field keeps existing but loses its label/description. The safer pattern already exists lower in this file in
update_hidden_prop(): moveitemname_id/itemdesc_idto the keeper first, then delete the duplicate items.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@specifyweb/specify/migration_utils/update_schema_config.py` around lines 762 - 807, The current deduplicate_containeritems_and_strings deletes all SpLocaleItemStr rows for duplicate ContainerItems which can drop labels/descriptions; instead, locate the keeper ContainerItem for each duplicate group (the row with rn == 1), build a mapping from each duplicate id -> keeper id, then update ItemStr rows to set itemname_id and itemdesc_id to the keeper id where they reference any duplicate id, and only after that delete the duplicate ContainerItem rows; use the existing qs annotated with rn on ContainerItem and the ItemStr model fields itemname_id/itemdesc_id to perform the updates within the same transaction.atomic block before calling ContainerItem.objects.filter(id__in=ids_to_delete).delete().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@specifyweb/backend/stored_queries/query_construct.py`:
- Around line 32-35: The method currently has two lint issues: move the inline
raise so the raise AssertionError is on its own line (replace the one-line `if
...: raise ...` pattern used with `query.collection` in query_construct.py so
the conditional and the raise are separate statements), and replace the
SQLAlchemy NULL comparison that uses `== None` (around the expression on/near
line 99) with the SQLAlchemy-aware `.is_(None)` call to correctly produce IS
NULL in SQLAlchemy queries; update the code references where `query.collection`
is checked and where the `== None` SQLAlchemy comparison occurs (use the same
function or method containing those checks) accordingly.
- Around line 41-61: The join-cache is keyed only by (table, 'TreeRanks') but
the cached ancestors are built from the incoming alias parameter node, so
different alias chains collide; change the cache key to include the starting
alias (e.g. use (table, node, 'TreeRanks') or another stable identifier derived
from node) both when checking query.join_cache and when storing it, so lookups
use the same composite key; update references around ancestors, node,
query.join_cache and the cache-setting block (where query =
query._replace(join_cache=...); query.join_cache[...] = ...) to use the new key
so cached ancestor lists are bound to the correct starting alias.
In
`@specifyweb/frontend/js_src/lib/components/AppResources/__tests__/AppResourcesAside.test.tsx`:
- Around line 43-56: Move the jest.mock call for '../../Permissions/helpers' to
the top-level of the test file (before any imports) and create the mock shape
with hasPermission and hasToolPermission as jest.fn(); then in the
beforeAll/afterAll hooks mutate the mocked functions using mockReturnValue()
(e.g., set hasPermission.mockReturnValue(false) in beforeAll and
hasPermission.mockReturnValue(true) in afterAll) so the module is rewired
reliably; refer to the functions hasPermission and hasToolPermission and the
hooks beforeAll/afterAll to locate where to change behavior.
In `@specifyweb/frontend/js_src/lib/components/QueryComboBox/index.tsx`:
- Around line 658-666: The onSaving callback should accept the
unsetUnloadProtect parameter and call it when we return false in the
dependent-create branch to clear unload protection; update the onSaving
signature to (unsetUnloadProtect?: () => void): false | void and, inside the if
(field.isDependent()) block after setState({ type: 'MainState' }), call
unsetUnloadProtect?.() before returning false — keeping existing calls to
handleSavingNewRecord, resource?.set(field.name, state.resource as never) and
setState intact.
In `@specifyweb/specify/management/commands/run_key_migration_functions.py`:
- Around line 118-125: The Exists predicate (earlier_exists) only checks
timestampcreated__lt so rows with equal timestamps are both kept; update the
filter on SpAppResourceDir to treat ties deterministically by selecting rows
with either timestampcreated < OuterRef('timestampcreated') OR (timestampcreated
== OuterRef('timestampcreated') AND id < OuterRef('id')) so one row is kept as
the tie-breaker; use Q(...) to combine these conditions and keep the rest of the
common_filters intact (reference SpAppResourceDir, earlier_exists,
OuterRef('timestampcreated'), OuterRef('id'), and common_filters).
In `@specifyweb/specify/migration_utils/update_schema_config.py`:
- Around line 734-759: Currently duplicate_containers is computed using
timestampcreated__lt and later containers are deleted which loses unique
ContainerItem/ItemStr rows; instead, for each group (discipline_id, name,
schematype=0) determine the keeper as the row with the lowest id (use id as
deterministic tie-breaker), find all other container ids in that group, reparent
unique child rows by updating ContainerItem.container_id and ItemStr.*_container
references to the keeper for rows that don't already exist on the keeper (avoid
creating duplicates), then run deduplicate_containeritems_and_strings() and
finally delete the non-keeper container rows; update the logic around
duplicate_containers, ContainerItem, ItemStr, and use id-based selection rather
than timestampcreated__lt so same-timestamp records are handled
deterministically.
- Around line 314-324: Replace the non-atomic read-then-create for
Splocalecontainer with an atomic DB-backed operation: either add a DB uniqueness
constraint on the identifying columns used in container_attrs and use
Splocalecontainer.objects.get_or_create(defaults={...}, **container_attrs) to
create-or-fetch in one call, or wrap the logic in transaction.atomic() and
acquire a row lock on the owner (via select_for_update() on the
discipline/container model) before re-checking and creating; update the block
that references Splocalecontainer.objects.filter(...).order_by("id").first() and
the create(...) call to use get_or_create or the locked transaction path, and
apply the same change to the analogous code at the later block (lines noted
467-481) so both codepaths consistently avoid race conditions.
---
Outside diff comments:
In `@specifyweb/specify/migration_utils/update_schema_config.py`:
- Around line 762-807: The current deduplicate_containeritems_and_strings
deletes all SpLocaleItemStr rows for duplicate ContainerItems which can drop
labels/descriptions; instead, locate the keeper ContainerItem for each duplicate
group (the row with rn == 1), build a mapping from each duplicate id -> keeper
id, then update ItemStr rows to set itemname_id and itemdesc_id to the keeper id
where they reference any duplicate id, and only after that delete the duplicate
ContainerItem rows; use the existing qs annotated with rn on ContainerItem and
the ItemStr model fields itemname_id/itemdesc_id to perform the updates within
the same transaction.atomic block before calling
ContainerItem.objects.filter(id__in=ids_to_delete).delete().
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 5964b48f-2634-466a-80a3-86d339ab6f69
⛔ Files ignored due to path filters (1)
specifyweb/frontend/js_src/lib/components/AppResources/__tests__/__snapshots__/AppResourcesAside.test.tsx.snapis excluded by!**/*.snap
📒 Files selected for processing (15)
specifyweb/backend/permissions/initialize.pyspecifyweb/backend/setup_tool/app_resource_defaults.pyspecifyweb/backend/setup_tool/schema_defaults.pyspecifyweb/backend/stored_queries/format.pyspecifyweb/backend/stored_queries/query_construct.pyspecifyweb/backend/stored_queries/queryfieldspec.pyspecifyweb/backend/stored_queries/tests/test_execution/test_execute.pyspecifyweb/backend/stored_queries/tests/test_execution/test_run_ephemeral_query.pyspecifyweb/frontend/js_src/lib/components/AppResources/__tests__/AppResourcesAside.test.tsxspecifyweb/frontend/js_src/lib/components/AppResources/filtersHelpers.tsspecifyweb/frontend/js_src/lib/components/QueryComboBox/index.tsxspecifyweb/frontend/js_src/lib/components/Security/MissingAgentsDialog.tsxspecifyweb/frontend/js_src/lib/components/Security/UserCollections.tsxspecifyweb/specify/management/commands/run_key_migration_functions.pyspecifyweb/specify/migration_utils/update_schema_config.py
grantfitzsimmons
left a comment
There was a problem hiding this comment.
Introduced in this branch: #8040
There was a problem hiding this comment.
🧹 Nitpick comments (1)
specifyweb/backend/stored_queries/query_construct.py (1)
37-37: Consider lowering this to debug-level logging.This path can execute frequently during query construction; keeping it at
infomay add avoidable log volume.Suggested tweak
- logger.info('handling treefield %s rank: %s field: %s', table, tree_rank.name, next_join_path) + logger.debug('handling treefield %s rank: %s field: %s', table, tree_rank.name, next_join_path)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@specifyweb/backend/stored_queries/query_construct.py` at line 37, Lower the verbosity of the log emitted in query_construct: replace the logger.info call that logs "handling treefield %s rank: %s field: %s" (the logger.info(...) that uses variables table, tree_rank.name, next_join_path) with logger.debug(...) so the same formatted message and arguments are preserved but recorded at debug level instead of info.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@specifyweb/backend/stored_queries/query_construct.py`:
- Line 37: Lower the verbosity of the log emitted in query_construct: replace
the logger.info call that logs "handling treefield %s rank: %s field: %s" (the
logger.info(...) that uses variables table, tree_rank.name, next_join_path) with
logger.debug(...) so the same formatted message and arguments are preserved but
recorded at debug level instead of info.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 90c7fb04-5cf0-444a-b136-6660dcd3ae92
📒 Files selected for processing (1)
specifyweb/backend/stored_queries/query_construct.py
Summary by CodeRabbit
New Features
Bug Fixes / Improvements
Tests