Skip to content

Allow the data explorer to add to existing collections - #1578

Merged
danlamanna merged 1 commit into
masterfrom
data-explorer-add-to-collection
Aug 11, 2026
Merged

Allow the data explorer to add to existing collections#1578
danlamanna merged 1 commit into
masterfrom
data-explorer-add-to-collection

Conversation

@danlamanna

@danlamanna danlamanna commented Aug 11, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features

    • Added the ability to add data explorer results to an existing collection.
    • Collection workflows now support searching collections and displaying recent collections.
    • Added validation and access checks when populating collections from image IDs.
    • Collection updates are processed asynchronously with status feedback.
  • Bug Fixes

    • Improved messaging for authentication, eligibility, permissions, and collection errors.
    • Inaccessible images are skipped during collection population.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds an authenticated API endpoint for populating collections from ISIC IDs. The data explorer now supports creating collections and adding results to existing collections through one searchable modal. API and browser tests cover permissions, locking, focus, search, and population.

Changes

Collection population workflow

Layer / File(s) Summary
ISIC ID population API
isic/core/api/collection.py, isic/core/tests/test_api_collection.py
Adds reusable ISIC ID validation and an authenticated population endpoint. The endpoint enforces visibility, ownership, add permission, and unlocked-collection checks. Tests cover HTTP 401, 403, 404, 409, and 202 responses, including invisible images.
Recent collection context and modal structure
isic/core/views/data_explorer.py, isic/core/templates/core/data_explorer.html
Loads up to five recent unlocked collections for authenticated users. Replaces the create-only dialog with New Collection and Existing Collection tabs.
Collection search and submission flow
isic/core/templates/core/data_explorer.html, isic/core/tests/test_data_explorer_browser.py
Adds collection search, selection, focus handling, shared saving and error states, and mode-specific submission. Browser tests cover modal focus, adding query results to an existing collection, and searching beyond recent collections.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested reviewers: brianhelba

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: enabling the data explorer to add results to existing collections.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch data-explorer-add-to-collection

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
isic/core/api/collection.py (1)

79-79: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Consider bounding the ISIC ID list length.

IsicIds sets min_length=1 but no upper bound. The data explorer posts every isic_id in the query result set, which can be very large. The full list travels in the HTTP body and then in the Celery message body. Add a max_length that matches the largest supported result set.

♻️ Proposed change
-IsicIds = Annotated[list[Annotated[str, Field(pattern=ISIC_ID_REGEX)]], Field(min_length=1)]
+IsicIds = Annotated[
+    list[Annotated[str, Field(pattern=ISIC_ID_REGEX)]], Field(min_length=1, max_length=100_000)
+]
🤖 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 `@isic/core/api/collection.py` at line 79, Update the IsicIds type alias to add
a max_length matching the largest supported result set, while preserving its
existing non-empty constraint and ISIC_ID_REGEX validation.
isic/core/templates/core/data_explorer.html (1)

666-681: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Guard against out-of-order search responses.

searchCollections assigns collectionResults from whichever response resolves last. The 300 ms debounce reduces overlap but does not prevent it. A slow response for an older query can overwrite results for the current query. Track a request token and discard stale responses.

♻️ Proposed change
         async searchCollections() {
           if (!this.hasCollectionQuery) {
             this.collectionResults = [];
             return;
           }
 
+          const requestId = ++this.collectionSearchId;
           this.collectionSearchPending = true;
           try {
             const {data} = await axiosSession.get("{% url 'api:collection_autocomplete' %}", {
               params: {query: this.collectionQuery.trim()},
             });
+            if (requestId !== this.collectionSearchId) {
+              return;
+            }
             this.collectionResults = data.map(c => ({id: c.id, name: c.name}));
           } catch (e) {
+            if (requestId !== this.collectionSearchId) {
+              return;
+            }
             this.collectionError = this.errorMessage(e);
           } finally {
-            this.collectionSearchPending = false;
+            if (requestId === this.collectionSearchId) {
+              this.collectionSearchPending = false;
+            }

Add the counter to the state block near line 325:

collectionSearchId: 0,
🤖 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 `@isic/core/templates/core/data_explorer.html` around lines 666 - 681, Update
the component state with the proposed collectionSearchId counter, then modify
searchCollections to increment and capture a request token for each search.
Before assigning collectionResults or collectionError, ignore the response when
its token is no longer the latest, while preserving collectionSearchPending
cleanup for the active request.
isic/core/tests/test_data_explorer_browser.py (1)

237-240: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use exact collection-name matching.

get_by_role(..., name=...) uses case-insensitive substring matching by default. The factory does not guarantee unique collection names. Pass exact=True to both locators.

🤖 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 `@isic/core/tests/test_data_explorer_browser.py` around lines 237 - 240, Update
both get_by_role calls in the collection search test to pass exact=True,
ensuring button name matching uses the complete collection name rather than
case-insensitive substring matching.
🤖 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.

Nitpick comments:
In `@isic/core/api/collection.py`:
- Line 79: Update the IsicIds type alias to add a max_length matching the
largest supported result set, while preserving its existing non-empty constraint
and ISIC_ID_REGEX validation.

In `@isic/core/templates/core/data_explorer.html`:
- Around line 666-681: Update the component state with the proposed
collectionSearchId counter, then modify searchCollections to increment and
capture a request token for each search. Before assigning collectionResults or
collectionError, ignore the response when its token is no longer the latest,
while preserving collectionSearchPending cleanup for the active request.

In `@isic/core/tests/test_data_explorer_browser.py`:
- Around line 237-240: Update both get_by_role calls in the collection search
test to pass exact=True, ensuring button name matching uses the complete
collection name rather than case-insensitive substring matching.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f5f97a26-da73-4d0c-a83c-6fbc2e8ecca2

📥 Commits

Reviewing files that changed from the base of the PR and between 2f83b38 and b8f20bd.

📒 Files selected for processing (5)
  • isic/core/api/collection.py
  • isic/core/templates/core/data_explorer.html
  • isic/core/tests/test_api_collection.py
  • isic/core/tests/test_data_explorer_browser.py
  • isic/core/views/data_explorer.py

@danlamanna
danlamanna merged commit bc11677 into master Aug 11, 2026
2 checks passed
@danlamanna
danlamanna deleted the data-explorer-add-to-collection branch August 11, 2026 16:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant