Skip to content

Add bulk "Set status to Not Started" action to EvaluationAdmin#7

Merged
neomorphic merged 3 commits into
mainfrom
copilot/add-custom-action-set-status-not-started
Apr 24, 2026
Merged

Add bulk "Set status to Not Started" action to EvaluationAdmin#7
neomorphic merged 3 commits into
mainfrom
copilot/add-custom-action-set-status-not-started

Conversation

Copilot AI commented Apr 24, 2026

Copy link
Copy Markdown

Django admin EvaluationAdmin lacked any custom actions, making it impossible to bulk-reset Evaluation records without editing each one individually.

Changes

  • frx_challenges/web/admin.py: Added set_status_not_started action to EvaluationAdmin
    • Registers the action via actions = ["set_status_not_started"]
    • Uses @admin.action(description="Set status to Not Started") for the dropdown label
    • Calls queryset.update(status=Evaluation.Status.NOT_STARTED) for a single-query bulk update
    • Surfaces a success message via self.message_user
@admin.register(Evaluation)
class EvaluationAdmin(VersionAdmin):
    list_display = (...)
    actions = ["set_status_not_started"]

    @admin.action(description="Set status to Not Started")
    def set_status_not_started(self, request, queryset):
        updated = queryset.update(status=Evaluation.Status.NOT_STARTED)
        self.message_user(request, f"{updated} evaluation(s) set to Not Started.")
Original prompt

Problem

The Django admin EvaluationAdmin (at frx_challenges/web/admin.py) has no custom actions in its Actions dropdown. Users need to be able to select multiple Evaluation records and bulk-reset their status to NOT_STARTED.

Solution

Add a custom admin action set_status_not_started to EvaluationAdmin in frx_challenges/web/admin.py:

  1. Add actions = ["set_status_not_started"] to the EvaluationAdmin class.
  2. Define the action method using the @admin.action(description="Set status to Not Started") decorator.
  3. The method should call queryset.update(status=Evaluation.Status.NOT_STARTED) and then call self.message_user(request, ...) to display a success message.

Target file

frx_challenges/web/admin.py

Updated EvaluationAdmin class should look like:

@admin.register(Evaluation)
class EvaluationAdmin(VersionAdmin):
    list_display = ("id", "status", "version_link", "submission_name", "version__user__username", "created_at", "last_updated")
    actions = ["set_status_not_started"]

    @admin.action(description="Set status to Not Started")
    def set_status_not_started(self, request, queryset):
        updated = queryset.update(status=Evaluation.Status.NOT_STARTED)
        self.message_user(request, f"{updated} evaluation(s) set to Not Started.")

    def version_link(self, obj):
        url = reverse("admin:web_version_change", args=[obj.version.id])
        return format_html('<a href="{}">Version {} </a>', url, obj.version.id)

    def submission_name(self, obj):
        return obj.version.submission.name

    version_link.short_description = "Version"
    submission_name.short_description = "Submission"

No other classes or logic should be changed.

The following is the prior conversation context from the user's chat exploration (may be truncated):

User: How can I add "actions" do this Action drop down menu? I can select all records, and I want to be able to set the status for all the selected records to "Not Started" using the actions dropdown.

Assistant: To add a custom action to the Django admin Actions dropdown, you need to define an action function and register it on the EvaluationAdmin class in frx_challenges/web/admin.py.

Here's exactly what to add:

from allauth.account.decorators import secure_admin_login
from django.contrib import admin
from django.urls import reverse
from django.utils.html import format_html
from reversion.admin import VersionAdmin

from .models import Collaborator, ContentFile, Evaluation, Page, Submission, Version


@admin.register(Page)
class PageAdmin(VersionAdmin):
    prepopulated_fields = {"slug": ("title",)}


@admin.register(ContentFile)
class ContentFileAdmin(VersionAdmin):
    prepopulated_fields = {"slug": ("title",)}


@admin.register(Evaluation)
class EvaluationAdmin(VersionAdmin):
    list_display = ("id", "status", "version_link", "submission_name", "version__user__username", "created_at", "last_updated")
    actions = ["set_status_not_started"]  # ← register the action

    @admin.action(description="Set status to Not Started")
    def set_status_not_started(self, request, queryset):
        updated = queryset.update(status=Evaluation.Status.NOT_STARTED)
        self.message_user(request, f"{updated} evaluation(s) set to Not Started.")

    def version_link(self, obj):
        url = reverse("admin:web_version_change", args=[obj.version.id])
        return format_html('<a href="{}">Version {} </a>', url, obj.version.id)

    def submission_name(self, obj):
        return obj.version.submission.name

    version_link.short_description = "Version"
    submission_name.short_description = "Submission"

# ... rest of the file unchanged

Key changes explained:

  1. actions = ["set_status_not_started"] — Tells Django admin to include this method in the Actions dropdown.
  2. @admin.action(description="...") — Sets the label shown in the dropdown ("Set status to Not Started").
  3. queryset.update(...) — Efficiently updates all selected records in a single DB query.
  4. self.message_user(...) — Shows a success banner at the top of the page after the action runs.

After saving the file, the dropdown will show "Set status to Not Started" as an option. Select your records, choose it, and click Go.

Would you like me to open a PR with this change?

User: Yes please.

Assistant: I've kicked off the coding agent to create the PR! It will add the set_status_not_started action to EvaluationAdmin in frx_challenges/web/admin.py. Once the PR is open, you'll be able to review the change before merging. I'll let you know the link as soon as it's available.

This pull request was created from Copilot chat.

Agent-Logs-Url: https://github.com/janelia-cellmap/frx-challenges/sessions/0f977f9a-a80a-4a83-add9-0ff6313f03df

Co-authored-by: rhoadesScholar <37990507+rhoadesScholar@users.noreply.github.com>
Copilot AI changed the title [WIP] Add custom action to bulk-reset evaluation status to Not Started Add bulk "Set status to Not Started" action to EvaluationAdmin Apr 24, 2026
Copilot AI requested a review from rhoadesScholar April 24, 2026 15:29
@rhoadesScholar rhoadesScholar marked this pull request as ready for review April 24, 2026 15:29
@rhoadesScholar rhoadesScholar requested a review from Copilot April 24, 2026 15:29

Copilot AI 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.

Pull request overview

This PR adds a Django admin bulk action to EvaluationAdmin so administrators can reset the status of multiple Evaluation records to NOT_STARTED in one operation.

Changes:

  • Added a set_status_not_started admin action to EvaluationAdmin.
  • Registered the action in the admin actions dropdown and displayed a success message after execution.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +26 to +28
def set_status_not_started(self, request, queryset):
updated = queryset.update(status=Evaluation.Status.NOT_STARTED)
self.message_user(request, f"{updated} evaluation(s) set to Not Started.")

Copilot AI Apr 24, 2026

Copy link

Choose a reason for hiding this comment

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

Using queryset.update(...) will bypass Model.save(), so Evaluation.last_updated (auto_now) won’t be bumped and any model signals won’t run. If you expect the admin bulk reset to reflect as a fresh update (and since last_updated is displayed and used for latest("last_updated")), consider also updating last_updated explicitly (e.g., to timezone.now()) or iterating and calling save() if signal behavior is required.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@copilot apply changes based on this feedback

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 70e18e4. The queryset.update() call now also sets last_updated=timezone.now() explicitly, ensuring the timestamp is bumped correctly since auto_now fields are bypassed by bulk queryset updates.

@rhoadesScholar

Copy link
Copy Markdown
Member

@copilot Currently there is a "Delete selected evaluations" option under the actions menu, but I'm not seeing that in the Evaluation admin. Are you sure you got everything correct?
image

Copilot AI commented Apr 24, 2026

Copy link
Copy Markdown
Author

@copilot Currently there is a "Delete selected evaluations" option under the actions menu, but I'm not seeing that in the Evaluation admin. Are you sure you got everything correct?
<img width="696" h...

The code is correct — the action is properly registered via actions = ["set_status_not_started"] and decorated with @admin.action. The screenshot appears to have been taken on the deployed environment before the PR commits (bdeb7cb / 70e18e4) were applied there. Once deployed, the dropdown will show both "Delete selected evaluations" and "Set status to Not Started".

@neomorphic neomorphic merged commit 9986e55 into main Apr 24, 2026
2 checks passed
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.

4 participants