Skip to content

Merge pull request #6062 from specify/issue-5466#8157

Closed
CarolineDenis wants to merge 6 commits into
temp-pre-5466from
retro-review-5466-base
Closed

Merge pull request #6062 from specify/issue-5466#8157
CarolineDenis wants to merge 6 commits into
temp-pre-5466from
retro-review-5466-base

Conversation

@CarolineDenis
Copy link
Copy Markdown
Contributor

@CarolineDenis CarolineDenis commented Jun 3, 2026

Allow for re-running of creating default cots with django command

Fixes #5466

Checklist

  • Self-review the PR after opening it to make sure the changes look good and
    self-explanatory (or properly documented)
  • Add relevant issue to release milestone
  • Add pr to documentation list
  • Add automated tests
  • Add a reverse migration if a migration is present in the PR
  • Add migration function to
    def fix_schema_config(stdout: WriteToStdOut | None = None):

Testing instructions

Summary by CodeRabbit

  • New Features

    • Added a management command to create/ensure default collection object types.
  • Chores

    • Added migrations to run default collection-type initialization and adjust migration ordering.
    • Consolidated initialization into a shared utility and improved logging.
  • Bug Fixes

    • Ensure collections receive appropriate default types, handle duplicate-type and duplicate-code conflicts during initialization.

Allow for re-running of creating default cots with django command
@CarolineDenis
Copy link
Copy Markdown
Contributor Author

@CodeRabbit full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 3, 2026

✅ Action performed

Full review finished.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 3, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 88864c79-c2f3-4d8b-9fd6-d6af3ddafcfb

📥 Commits

Reviewing files that changed from the base of the PR and between f952b52 and 24fe95f.

📒 Files selected for processing (2)
  • specifyweb/specify/migrations/0022_update_hidden_geo_tables.py
  • specifyweb/specify/utils.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • specifyweb/specify/utils.py

📝 Walkthrough

Walkthrough

Consolidates default CollectionObjectType backfill into utils.create_default_collection_types, updates migration 0002 to delegate to that utility, adds an atomic migration 0021 to run it, adjusts migration 0022 dependency, and provides a management command to invoke the utility manually.

Changes

Default COTS Creation Refactoring

Layer / File(s) Summary
Utils module - logging setup and imports
specifyweb/specify/utils.py
Logging support added and imports adjusted to support the new COTS creation utility.
Utils module - COTS creation implementation and helpers
specifyweb/specify/utils.py
Added get_spmodel_class(), log_sqlalchemy_query(), and create_default_collection_types() with uniqueness-retry logic and SQL logging.
Migration 0002_geo refactored to use utility
specifyweb/specify/migrations/0002_geo.py
Removed inline COTS creation, import utility from specifyweb.specify.utils, added handle_default_collection_types() wrapper, and updated forward operation to call it.
New migration 0021 to ensure COTS creation
specifyweb/specify/migrations/0021_ensure_default_cots.py
New atomic RunPython migration that calls the utility via handle_default_collection_types, dependent on migration 0020.
Migration 0022 dependency update
specifyweb/specify/migrations/0022_update_hidden_geo_tables.py
Updated dependency to require 0021_ensure_default_cots before running.
Management command for manual COTS creation
specifyweb/specify/management/commands/create_default_cots.py
New Django management command that invokes the shared create_default_collection_types(apps) function.

Sequence Diagram(s)

sequenceDiagram
  participant ManagementCommand
  participant Migration0021
  participant Utils
  participant Database
  ManagementCommand->>Utils: create_default_collection_types(apps)
  Migration0021->>Utils: handle_default_collection_types(apps, schema_editor)
  Utils->>Database: read/update Collection, Collectionobjecttype, Collectionobject
  Database-->>Utils: results
  Utils-->>ManagementCommand: complete
  Utils-->>Migration0021: complete
Loading

Suggested reviewers:

  • grantfitzsimmons
🚥 Pre-merge checks | ✅ 3 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Automatic Tests ⚠️ Warning PR adds complex new functionality (management command, utility function with business logic) but includes no automated tests. PR checklist lists "add automated tests" as a required follow-up. Add test cases for the new management command and create_default_collection_types() function to specifyweb/specify/tests/.
Testing Instructions ⚠️ Warning No test cases added for management command or utility function; PR objectives explicitly state no explicit testing steps provided; testing listed as follow-up work. Add automated tests for management command, utility function, and migrations; document manual testing steps for verifying default COTs are created for collections.
Title check ❓ Inconclusive The PR title is generic and vague; it references only a merge action without describing the actual changes or feature being implemented. Consider retitling to something more descriptive of the actual change, such as 'Add management command to re-run default COT creation' or 'Fix issue #5466: Ensure default collection object types are set'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed The PR successfully addresses issue #5466 by implementing functionality to create and ensure default Collection Object Types are set for all collections.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing issue #5466: adding a management command, migrating COT creation logic to utils, creating new migrations, and updating migration dependencies.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch retro-review-5466-base
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch retro-review-5466-base

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (3)
specifyweb/specify/utils.py (2)

56-56: 💤 Low value

Prefix unused variable with underscore.

The created variable from get_or_create is never used.

♻️ Apply linter suggestion
-        cot, created = Collectionobjecttype.objects.get_or_create(
+        cot, _created = Collectionobjecttype.objects.get_or_create(
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@specifyweb/specify/utils.py` at line 56, The tuple unpacking from
Collectionobjecttype.objects.get_or_create assigns an unused variable named
created; update the unpacking in the call to get_or_create (the line with cot,
created = Collectionobjecttype.objects.get_or_create) to prefix the unused
variable with an underscore (cot, _created =
Collectionobjecttype.objects.get_or_create) so the linter recognizes it as
intentionally unused.

43-44: 💤 Low value

Remove the stale implementation reference comment.

The comment references a specific file and line number that will become outdated as the codebase evolves.

♻️ Suggested cleanup
     logger.debug(raw_sql)
-    # Run in the storred_queries.execute file, in the execute function, right before the return statement, line 546
-    # from specifyweb.specify.utils import log_sqlalchemy_query; log_sqlalchemy_query(query)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@specifyweb/specify/utils.py` around lines 43 - 44, Remove the stale
implementation-reference comment lines in specifyweb.specify.utils that mention
"Run in the storred_queries.execute file, in the execute function, right before
the return statement, line 546" and the example call; locate the comment by
searching for the string "log_sqlalchemy_query(query)" or the exact phrase "Run
in the storred_queries.execute file" and delete those lines (or replace them
with a short, stable note such as a module-level docstring that explains how to
use log_sqlalchemy_query without file/line references).
specifyweb/specify/management/commands/create_default_cots.py (1)

12-13: ⚡ Quick win

Wrap the manual repair path in a transaction.

0021_ensure_default_cots.py runs the same helper atomically, but this command does not. If one collection fails midway, operators can be left with a partially repaired database. Using transaction.atomic() here keeps the manual path consistent with the migration behavior.

Suggested change
 import logging
 from django.core.management.base import BaseCommand
 from django.apps import apps
+from django.db import transaction
 from specifyweb.businessrules.exceptions import BusinessRuleException
 from specifyweb.specify.utils import create_default_collection_types
@@
     help = "Create default COTS for the Specify database if they haven't been previously set."
 
     def handle(self, *args, **options):
-        create_default_collection_types(apps)
+        with transaction.atomic():
+            create_default_collection_types(apps)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@specifyweb/specify/management/commands/create_default_cots.py` around lines
12 - 13, The handle method currently calls create_default_collection_types(apps)
without transactional protection; wrap that call in a Django atomic transaction
(use transaction.atomic()) so the manual repair path is all-or-nothing, and
ensure transaction is imported from django.db if not already; specifically
modify the handle function to execute create_default_collection_types(apps)
inside a transaction.atomic() context to match the migration's atomic behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@specifyweb/specify/migrations/0002_geo.py`:
- Around line 15-17: The migration 0002_geo.py currently imports and calls
create_default_collection_types (via handle_default_collection_types) from
runtime code, which can change and break this shipped migration; remove that
import and instead inline the migration-safe logic (or move it into a new frozen
migration-only module) so the migration does not depend on
specifyweb.specify.utils at runtime. Specifically, replace the import of
create_default_collection_types and the call to handle_default_collection_types
with a copy of the minimal deterministic code required to create the default
collection types inside 0002_geo.py (or create a new module alongside migrations
that contains a frozen implementation) and ensure you do not reference runtime
symbols like BusinessRuleException or logger from utils—use migration-local
error handling and a migration-local logger if needed. Ensure the migration
remains initial=True and that the new code operates only on the provided
apps/models from the migration framework.

In `@specifyweb/specify/migrations/0021_ensure_default_cots.py`:
- Around line 1-2: The migration currently imports
create_default_collection_types from application code which risks future
divergence; instead inline a frozen copy of that helper directly into the
migration file (e.g., define a local function create_default_collection_types
within the migration module) and update the RunPython call to invoke this local
function (or reference it from the Migration class) so the migration is
self-contained and deterministic; ensure you copy any constants or behaviors the
helper depends on into the migration and remove the external import.

In `@specifyweb/specify/utils.py`:
- Around line 68-76: The loop mutates collection.code each iteration causing
cumulative suffixes; save the original value (e.g., original_code =
collection.code) before entering the while loop and then set collection.code =
f'{original_code}-{i}' on each attempt (incrementing i) and check against
code_set; once unique, add to code_set and break. Ensure you reference
collection.code, code_set and the loop counter i when applying this fix.
- Around line 67-81: The except BusinessRuleException handler currently only
handles the 'Collection must have unique code in discipline' case and silently
ignores any other BusinessRuleException; update the handler (the except
BusinessRuleException as e block that manipulates collection.code, code_set and
calls collection.save()) to log unexpected BusinessRuleException instances
before continuing — use logger.error or logger.warning to record the exception
message and context (e.g., include repr(collection) and str(e) or traceback) for
the branch where the error message does not match the uniqueness check so
unexpected errors are visible.

---

Nitpick comments:
In `@specifyweb/specify/management/commands/create_default_cots.py`:
- Around line 12-13: The handle method currently calls
create_default_collection_types(apps) without transactional protection; wrap
that call in a Django atomic transaction (use transaction.atomic()) so the
manual repair path is all-or-nothing, and ensure transaction is imported from
django.db if not already; specifically modify the handle function to execute
create_default_collection_types(apps) inside a transaction.atomic() context to
match the migration's atomic behavior.

In `@specifyweb/specify/utils.py`:
- Line 56: The tuple unpacking from Collectionobjecttype.objects.get_or_create
assigns an unused variable named created; update the unpacking in the call to
get_or_create (the line with cot, created =
Collectionobjecttype.objects.get_or_create) to prefix the unused variable with
an underscore (cot, _created = Collectionobjecttype.objects.get_or_create) so
the linter recognizes it as intentionally unused.
- Around line 43-44: Remove the stale implementation-reference comment lines in
specifyweb.specify.utils that mention "Run in the storred_queries.execute file,
in the execute function, right before the return statement, line 546" and the
example call; locate the comment by searching for the string
"log_sqlalchemy_query(query)" or the exact phrase "Run in the
storred_queries.execute file" and delete those lines (or replace them with a
short, stable note such as a module-level docstring that explains how to use
log_sqlalchemy_query without file/line references).
🪄 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: d50373e5-ded0-48bc-869d-5481b91a3985

📥 Commits

Reviewing files that changed from the base of the PR and between 7fc918f and 79cbe2a.

📒 Files selected for processing (4)
  • specifyweb/specify/management/commands/create_default_cots.py
  • specifyweb/specify/migrations/0002_geo.py
  • specifyweb/specify/migrations/0021_ensure_default_cots.py
  • specifyweb/specify/utils.py

Comment thread specifyweb/specify/migrations/0002_geo.py
Comment thread specifyweb/specify/migrations/0021_ensure_default_cots.py
Comment thread specifyweb/specify/utils.py
Comment thread specifyweb/specify/utils.py
@github-project-automation github-project-automation Bot moved this from 📋Back Log to Dev Attention Needed in General Tester Board Jun 3, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (4)
specifyweb/specify/utils.py (2)

67-81: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Unexpected BusinessRuleException is still silently swallowed.

When the message doesn't match the uniqueness check, the exception is dropped without logging. Add an else branch to log it, as previously suggested on this hunk.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@specifyweb/specify/utils.py` around lines 67 - 81, The except block for
BusinessRuleException (around the uniqueness handling for collection.code)
currently only handles the 'Collection must have unique code in discipline' case
and silently ignores all other BusinessRuleException instances; modify the
except BusinessRuleException as e: block to add an else branch that logs
unexpected exceptions (include the exception details and the collection
identifier) using logger.warning or logger.exception so they aren't swallowed,
keeping the existing uniqueness-renaming logic that calls collection.save() and
manipulates code_set intact.

68-76: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Cumulative suffix bug in the unique-code retry.

collection.code = f'{collection.code}-{i}' mutates code in place each iteration, producing ABC-1-2-3 rather than ABC-1, ABC-2, … Save the original code before the loop and rebuild from it. This matches a prior review comment on this hunk.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@specifyweb/specify/utils.py` around lines 68 - 76, The retry loop that
handles duplicate collection codes mutates collection.code each iteration
causing cumulative suffixes; before the while loop in the block that checks
"Collection must have unique code in discipline" (around where collection.code
is reassigned) capture the original code into a variable (e.g., orig_code) and
inside the loop set collection.code = f'{orig_code}-{i}' (incrementing i) until
a unique value is found, then add to code_set and break; update the logic in the
surrounding function/method in specify.utils to use the preserved orig_code
instead of repeatedly appending to collection.code.
specifyweb/specify/migrations/0021_ensure_default_cots.py (1)

1-2: 🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Migration imports mutable application code.

Same concern as 0002_geo: 0021 imports create_default_collection_types from runtime utils, coupling a historical migration to code that can change. Inline/freeze it. This restates a prior review comment on these lines.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@specifyweb/specify/migrations/0021_ensure_default_cots.py` around lines 1 -
2, The migration currently imports create_default_collection_types from mutable
application code; instead copy (inline) the exact implementation of
create_default_collection_types into this migration and use that local function
in the RunPython operation (or implement the creation logic directly inside the
migration's forward function), then remove the import from
specifyweb.specify.utils so the migration is self-contained and not dependent on
runtime code changes; reference the existing symbol
create_default_collection_types and ensure any helper names you introduce are
declared inside the migration file.
specifyweb/specify/migrations/0002_geo.py (1)

15-17: 🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Initial migration now depends on mutable runtime code.

0002_geo (initial = True) imports create_default_collection_types from specifyweb.specify.utils; future edits to that helper can alter this already-shipped migration's behavior. Freeze the logic in a migration-owned module. This restates a prior review comment on these lines.

🧹 Nitpick comments (2)
specifyweb/specify/management/commands/create_default_cots.py (1)

12-13: ⚡ Quick win

Wrap the run in a transaction and surface the result.

Unlike migration 0021 (atomic=True), the command runs without a surrounding transaction, so a mid-run failure leaves partial commits. Wrapping in transaction.atomic() plus a short success message improves the manual-run UX.

♻️ Proposed fix
+from django.db import transaction
 ...
     def handle(self, *args, **options):
-        create_default_collection_types(apps)
+        with transaction.atomic():
+            create_default_collection_types(apps)
+        self.stdout.write(self.style.SUCCESS("Default COTs ensured."))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@specifyweb/specify/management/commands/create_default_cots.py` around lines
12 - 13, The handle method currently calls create_default_collection_types(apps)
without transactional protection; wrap that call in a
django.db.transaction.atomic() block inside the Command.handle to ensure
all-or-nothing commits, and after successful completion write a concise success
message (e.g., using self.stdout.write or self.stdout.success) so the user sees
the result; update imports to include transaction and keep the reference to
create_default_collection_types(apps) and Command.handle as the change points.
specifyweb/specify/migrations/0021_ensure_default_cots.py (1)

4-15: ⚡ Quick win

Move the migration callable to module scope and add a reverse.

handle_default_collection_types is defined as a method inside the Migration class — it works only because the name resolves in the class body, which is non-idiomatic and fragile. Also, the RunPython operation has no reverse_code, so the migration is irreversible (the PR checklist flags adding a reverse). Define it at module level and pass migrations.RunPython.noop for the reverse.

♻️ Proposed fix
 from django.db import migrations
 from specifyweb.specify.utils import create_default_collection_types
 
-class Migration(migrations.Migration):
-
-    def handle_default_collection_types(apps, schema_editor):
-        create_default_collection_types(apps)
+def handle_default_collection_types(apps, schema_editor):
+    create_default_collection_types(apps)
 
+class Migration(migrations.Migration):
+
     dependencies = [
         ('specify', '0020_add_tectonicunit_to_pc_in_schema_config'),
     ]
 
     operations = [
-        migrations.RunPython(handle_default_collection_types, atomic=True),
+        migrations.RunPython(handle_default_collection_types, migrations.RunPython.noop, atomic=True),
     ]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@specifyweb/specify/migrations/0021_ensure_default_cots.py` around lines 4 -
15, The migration defines handle_default_collection_types as a nested function
inside the Migration class and omits a reversible operation; move
handle_default_collection_types out to module scope (so it's a top-level
function that calls create_default_collection_types(apps)), update the
Migration.operations entry to use
migrations.RunPython(handle_default_collection_types,
reverse_code=migrations.RunPython.noop) (or pass migrations.RunPython.noop as
the second arg) and keep the Migration class and dependencies as-is so the
migration becomes non-fragile and reversible.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@specifyweb/specify/utils.py`:
- Around line 62-66: The current update unconditionally repoints every
Collectionobject and overwrites the collection's default COT; change it to only
affect unset values: update Collectionobject rows using
Collectionobject.objects.filter(collection=collection,
collectionobjecttype__isnull=True).update(collectionobjecttype=cot) and only set
collection.collectionobjecttype and call collection.save() when
collection.collectionobjecttype is None (or not already set) so existing
user-assigned COTs and collection defaults are preserved.
- Around line 56-60: The get_or_create call in create_default_collection_types
can raise MultipleObjectsReturned on dirty DBs; replace it with a safe
fetch-first-then-create pattern: use
Collectionobjecttype.objects.filter(name=discipline_name, collection=collection,
taxontreedef_id=discipline.taxontreedef_id).order_by("id").first() and if that
returns None call Collectionobjecttype.objects.create(...) to insert the record;
optionally wrap the create in a transaction.atomic block to avoid race
conditions. Ensure you update any variable names (cot, created) accordingly so
downstream code continues to work with the retrieved or newly created
Collectionobjecttype instance.

---

Duplicate comments:
In `@specifyweb/specify/migrations/0021_ensure_default_cots.py`:
- Around line 1-2: The migration currently imports
create_default_collection_types from mutable application code; instead copy
(inline) the exact implementation of create_default_collection_types into this
migration and use that local function in the RunPython operation (or implement
the creation logic directly inside the migration's forward function), then
remove the import from specifyweb.specify.utils so the migration is
self-contained and not dependent on runtime code changes; reference the existing
symbol create_default_collection_types and ensure any helper names you introduce
are declared inside the migration file.

In `@specifyweb/specify/utils.py`:
- Around line 67-81: The except block for BusinessRuleException (around the
uniqueness handling for collection.code) currently only handles the 'Collection
must have unique code in discipline' case and silently ignores all other
BusinessRuleException instances; modify the except BusinessRuleException as e:
block to add an else branch that logs unexpected exceptions (include the
exception details and the collection identifier) using logger.warning or
logger.exception so they aren't swallowed, keeping the existing
uniqueness-renaming logic that calls collection.save() and manipulates code_set
intact.
- Around line 68-76: The retry loop that handles duplicate collection codes
mutates collection.code each iteration causing cumulative suffixes; before the
while loop in the block that checks "Collection must have unique code in
discipline" (around where collection.code is reassigned) capture the original
code into a variable (e.g., orig_code) and inside the loop set collection.code =
f'{orig_code}-{i}' (incrementing i) until a unique value is found, then add to
code_set and break; update the logic in the surrounding function/method in
specify.utils to use the preserved orig_code instead of repeatedly appending to
collection.code.

---

Nitpick comments:
In `@specifyweb/specify/management/commands/create_default_cots.py`:
- Around line 12-13: The handle method currently calls
create_default_collection_types(apps) without transactional protection; wrap
that call in a django.db.transaction.atomic() block inside the Command.handle to
ensure all-or-nothing commits, and after successful completion write a concise
success message (e.g., using self.stdout.write or self.stdout.success) so the
user sees the result; update imports to include transaction and keep the
reference to create_default_collection_types(apps) and Command.handle as the
change points.

In `@specifyweb/specify/migrations/0021_ensure_default_cots.py`:
- Around line 4-15: The migration defines handle_default_collection_types as a
nested function inside the Migration class and omits a reversible operation;
move handle_default_collection_types out to module scope (so it's a top-level
function that calls create_default_collection_types(apps)), update the
Migration.operations entry to use
migrations.RunPython(handle_default_collection_types,
reverse_code=migrations.RunPython.noop) (or pass migrations.RunPython.noop as
the second arg) and keep the Migration class and dependencies as-is so the
migration becomes non-fragile and reversible.
🪄 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: aaf3e2c7-b381-451b-a62c-e3f9b5dcc1f9

📥 Commits

Reviewing files that changed from the base of the PR and between 7fc918f and 79cbe2a.

📒 Files selected for processing (4)
  • specifyweb/specify/management/commands/create_default_cots.py
  • specifyweb/specify/migrations/0002_geo.py
  • specifyweb/specify/migrations/0021_ensure_default_cots.py
  • specifyweb/specify/utils.py

Comment thread specifyweb/specify/utils.py Outdated
Comment thread specifyweb/specify/utils.py Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
specifyweb/specify/utils.py (1)

84-99: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Log unexpected BusinessRuleException errors.

When a BusinessRuleException is raised that doesn't match the uniqueness message, the code falls through to continue without logging. This silently swallows unexpected exceptions, making debugging difficult. (This appears to have been addressed previously but the fix is not present in the current code.)

🛡️ Proposed fix
         except BusinessRuleException as e:
             if 'Collection must have unique code in discipline' in str(e):
                 # May want to do something besides numbering, but users can edit if after the migrqation if they want.
                 original_code = collection.code
                 i = 1
                 while True:
                     collection.code = f'{original_code}-{i}'
                     i += 1
                     if collection.code not in code_set:
                         code_set.add(collection.code)
                         break
                 try:
                     collection.save()
                 except BusinessRuleException as e:
                     logger.warning(f'Problem saving collection {collection}: {e}')
+            else:
+                logger.warning(f'Unexpected BusinessRuleException saving collection {collection}: {e}')
             continue
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@specifyweb/specify/utils.py` around lines 84 - 99, The except
BusinessRuleException block currently only handles the specific uniqueness
message and otherwise silently continues; update the handler for
BusinessRuleException in the function where collection is processed so that when
the exception message does NOT contain 'Collection must have unique code in
discipline' you log the unexpected error (use logger.error or logger.exception)
including the collection identifier (e.g., collection.code or repr(collection))
and the exception details before the continue; keep the existing
uniqueness-handling branch that renumbers and calls collection.save(), and
ensure you still catch and warn on BusinessRuleException from collection.save().
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@specifyweb/specify/utils.py`:
- Around line 84-99: The except BusinessRuleException block currently only
handles the specific uniqueness message and otherwise silently continues; update
the handler for BusinessRuleException in the function where collection is
processed so that when the exception message does NOT contain 'Collection must
have unique code in discipline' you log the unexpected error (use logger.error
or logger.exception) including the collection identifier (e.g., collection.code
or repr(collection)) and the exception details before the continue; keep the
existing uniqueness-handling branch that renumbers and calls collection.save(),
and ensure you still catch and warn on BusinessRuleException from
collection.save().

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4b2de207-69d3-48b5-aa81-34d3df906a87

📥 Commits

Reviewing files that changed from the base of the PR and between 50ab9f0 and f952b52.

📒 Files selected for processing (1)
  • specifyweb/specify/utils.py

@CarolineDenis CarolineDenis changed the base branch from main to temp-pre-5466 June 4, 2026 09:56
@CarolineDenis
Copy link
Copy Markdown
Contributor Author

Changes are not relevant anymore with code changes present in newer versions

@github-project-automation github-project-automation Bot moved this from Dev Attention Needed to ✅Done in General Tester Board Jun 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: ✅Done

Development

Successfully merging this pull request may close these issues.

Default COTypes are not always being set

2 participants