Skip to content

feat: reorder reusable annotations within an annotation category - #8184

Merged
david-yz-liu merged 6 commits into
MarkUsProject:masterfrom
YheChen:feat/reorder-reusable-annotations
Oct 2, 2026
Merged

david-yz-liu merged 6 commits into
MarkUsProject:masterfrom
YheChen:feat/reorder-reusable-annotations

Conversation

@YheChen

@YheChen YheChen commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Proposed Changes

Closes #7778.

Instructors (and graders with the "manage assessments" permission) can now reorder the reusable annotations within an annotation category from the assignment's Annotations tab, by dragging the handle next to each annotation, the same way annotation categories are already reordered. Graders see a category's annotations in this order in its drop-down menu in the grading view.

Changes after review

  • Annotation texts now get a position when they're created, including through annotation uploads (in the order they appear in the file) and the API: a new annotation text is placed after the existing annotation texts in its category. For backwards compatibility, if any annotation texts in the category don't have a position yet, they're all given positions first, in their current order.
  • Moving an annotation text to a different category places it after the existing annotation texts in that category.
  • position is validated to be at least 1 and unique within an annotation category.
  • The reorder route uses PATCH instead of POST, saves the positions with a single upsert_all, and rejects requests that don't list each of the category's annotation texts exactly once (responding with 400 and an error message), so the saved positions are always consecutive and without duplicates.

Implementation notes

  • AnnotationText has a new nullable position column, which must be an integer that is at least 1 and unique within an annotation category. It is NULL for one-time only annotations, and for existing annotation texts until their category is reordered or gets a new annotation text. AnnotationCategory#annotation_texts is ordered by position, id, so annotation texts without a position are listed after positioned ones, in creation order (PostgreSQL sorts NULLs last).
  • New annotation texts, including those created by annotation uploads and by the API, are placed after the existing annotation texts in their category, so uploaded annotation texts keep the order they appear in the file. An annotation text that is moved to a different category is likewise placed at the end of its new category, and one that is made one-time only loses its position. For backwards compatibility, if the category has annotation texts without a position, they are first given positions in their current order. Positions are assigned while holding a lock on the annotation category, so that annotation texts added to a category at the same time don't get the same position.
  • The order is applied on the AnnotationCategory#annotation_texts association (used by the grading view and the annotation CSV/YAML downloads) and in the Annotations tab's query, which previously listed a category's annotations by their creator's user name.
  • The new PATCH update_annotation_text_positions member route on annotation categories uses the existing manage? rule. It expects the ids of all of the category's annotation texts, each exactly once, in their new order, and otherwise responds with 400 and an error message (e.g. if an annotation was added in another tab). The positions are saved with a single upsert_all, which skips the AnnotationText callbacks, so annotations that have been applied to released results can still be reordered (their order isn't visible to students).
  • The annotation category pane's sortable is now bound to #annotation_category_pane_list instead of .sortable ul, since the annotation list is now also .sortable and its annotations may contain Markdown lists.
  • jQuery UI doesn't size the sortable placeholder for these list items (the empty placeholder's content height rounds to a tiny negative number, which jQuery UI treats as already sized), so the placeholder is sized to the dragged annotation in the start callback.
Screenshots of your changes (if applicable) image

Type of Change

Type Applies?
🚨 Breaking change (fix or feature that would cause existing functionality to change)
✨ New feature (non-breaking change that adds functionality) X
🐛 Bug fix (non-breaking change that fixes an issue)
🎨 User interface change (change to user interface; provide screenshots) X
♻️ Refactoring (internal change to codebase, without changing functionality)
🚦 Test update (change that only adds or modifies tests)
📦 Dependency update (change that updates a dependency)
📖 Documentation update (change that updates documentation) X
🔧 Internal (change that only affects developers or continuous integration)

Checklist

Before opening your pull request:

  • I have performed a self-review of my changes.
  • I have added tests for my changes, if applicable.
  • I have updated the project documentation, if applicable.
  • If this is my first contribution, I have added myself to the list of contributors.

After opening your pull request:

  • I have updated the project Changelog (this is required for all changes).
  • I have verified that the pre-commit.ci checks have passed.
  • I have verified that the CI tests have passed.
  • I have reviewed the test coverage changes reported by Coveralls.
  • I have requested a review from a project maintainer.

Questions and Comments

  • As discussed, the UI for creating a new annotation in the Annotations tab is unchanged: a new annotation is shown at the top of the list right after it's created, and at the end of the list (its position) after a reload.

YheChen and others added 2 commits September 26, 2026 20:03
…tegory

Adds a nullable `position` column to `annotation_texts` (left null for
one-time annotations and existing annotation texts). Instructors and graders
with the manage assessments permission can drag and drop the annotations in an
annotation category on the assignment Annotations tab, and graders see them in
that order in the grading view. Annotation texts without a position are listed
last, in creation order.

Closes MarkUsProject#7778

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coveralls

coveralls commented Sep 27, 2026 •

Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 36943506191

Coverage increased (+0.02%) to 90.843%

Details

  • Coverage increased (+0.02%) from the base build.
  • Patch coverage: 125 of 125 lines across 9 files are fully covered (100%).
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 53293
Covered Lines: 49454
Line Coverage: 92.8%
Relevant Branches: 2545
Covered Branches: 1271
Branch Coverage: 49.94%
Branches in Coverage %: Yes
Coverage Strength: 127.07 hits per line

💛 - Coveralls

@coveralls

Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 36289784112

Coverage decreased (-0.001%) to 90.808%

Details

  • Coverage decreased (-0.001%) from the base build.
  • Patch coverage: 8 uncovered changes across 1 file (70 of 78 lines covered, 89.74%).
  • No coverage regressions found.

Uncovered Changes

File Changed Covered %
spec/controllers/results_controller_spec.rb 8 0 0.0%
Total (8 files) 78 70 89.74%

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 53208
Covered Lines: 49357
Line Coverage: 92.76%
Relevant Branches: 2545
Covered Branches: 1271
Branch Coverage: 49.94%
Branches in Coverage %: Yes
Coverage Strength: 128.07 hits per line

💛 - Coveralls

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@david-yz-liu david-yz-liu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nice work, @YheChen! In addition to my inline comments, here are a few general comments (largely in response to your questions):

  1. In general let's update the codebase to assign position values when new annotation texts are created. I think for backwards compatibility we want to assign position values to all annotation texts within a category when a new annotation text for that category is created.
  2. In a similar vein, please assign positions when creating/updating categories by annotation upload. We can go with the order in which the annotation texts appear in the upload (overriding any old positions as needed).
  3. When an annotation text is moved between categories, let's compute a new position for it (after all existing annotation texts)
  4. The UI for creating a new annotation is a bit awkward as you've described, but I think it's okay for now. We might refresh the UI later.

Comment thread app/controllers/annotation_categories_controller.rb Outdated
Comment thread app/models/annotation_text.rb
Comment thread config/routes.rb Outdated
- New annotation texts (including uploaded ones and those added through the
  API) and annotation texts moved to another category are placed after the
  existing annotation texts in their category; existing annotation texts
  without a position are given one first
- Validated that positions are at least 1 and unique within a category
- Changed the reorder route to PATCH, saved positions with upsert_all, and
  rejected requests that don't list each of the category's annotation texts
  exactly once

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@david-yz-liu david-yz-liu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nice work, @YheChen!

@david-yz-liu
david-yz-liu merged commit 6454e53 into MarkUsProject:master Oct 2, 2026
8 checks passed
@YheChen
YheChen deleted the feat/reorder-reusable-annotations branch October 2, 2026 02:41
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.

Allow reordering of reusable annotations within a category

3 participants