Skip to content

Allow multiple projects/categories/lab priorities per entry - #22

Merged
normanajn merged 1 commit into
mainfrom
feature/multiselect-taxonomy
Jun 25, 2026
Merged

normanajn merged 1 commit into
mainfrom
feature/multiselect-taxonomy

Conversation

@normanajn

Copy link
Copy Markdown
Collaborator

Lets an entry be classified under multiple projects, categories, and lab priorities, and lets the Reports page filter by multiple values. Addresses #15 (multi-select in both the entry form and the report page).

Schema

  • WorkItem.project / category / lab_priority (single FKs) → projects / categories / lab_priorities ManyToMany.
  • Migration 0008_multiselect_taxonomy adds the M2M fields, copies each existing single value across, then drops the old FKs — no data loss (verified on the existing 31 entries; reverse migration collapses back to the first value).

Entry form

  • Multi-select widgets; at least one project and one category still required, lab priorities optional.
  • Field order: Projects → Categories → Lab Priorities → Group.

Reports

  • ModelMultipleChoiceFilter (match ANY) for Project / Category / Lab Priority; multi-selects with "none = all" semantics; functional-lead project-scope locking preserved.

Displays

  • Detail, My Entries, manage, reports preview, dashboard, and all exporters (CSV/Excel/PDF/AI/JSON) join multiple names with , (export column names unchanged).

JSON API (backward-compatible)

  • Accepts a single slug or a list for projects/categories/lab_priorities (legacy singular keys still work).
  • Returns plural lists and the legacy singular project/category (first value). API docs page updated.

Audit

  • project/category/lab_priority changes now logged via m2m_changed handlers (like tags).

Tests

  • All fixtures updated to set the M2M relations; new coverage for multi-project create, required-field validation, reports match-ANY filtering, and a new test_api_entries.py for API back-compat. 205 passing.

Note

Closes #15

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings June 25, 2026 18:35

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 updates the WorkItem taxonomy model and surrounding UI/API surfaces to support multi-select classification for projects, categories, and lab priorities, and extends report filtering to accept multiple selections (match-any semantics).

Changes:

  • Convert WorkItem.project/category/lab_priority single FKs to ManyToMany relations with a data-migrating schema change.
  • Update entry form + list/detail/manage/report templates, report filters, and exporters to display multiple taxonomy values.
  • Update JSON entry-create API to accept plural lists (while still accepting legacy singular keys) and add/adjust test coverage.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/test_reports.py Adjust report tests for multi-select filters and add match-any coverage.
tests/test_entries.py Update entry CRUD tests for M2M taxonomy fields; add multi-project create + required validation coverage.
tests/test_audit.py Update audit tests to create entries with M2M taxonomy relations.
tests/test_api_entries.py Add new API tests covering backward-compatible multi-select payloads.
tests/test_accounts_permissions.py Update permission test setup to assign projects/categories via M2M.
apps/reports/views.py Switch report queryset loading/filtering to prefetch M2M taxonomy and apply project-scope filtering via M2M.
apps/reports/templates/reports/partials/_preview.html Render multiple projects/categories in the preview table.
apps/reports/templates/reports/index.html Replace single-select report filters with multi-select controls and preserve functional-lead scoping behavior.
apps/reports/filters.py Convert taxonomy filters to ModelMultipleChoiceFilter (match-any).
apps/reports/exporters.py Export projects/categories as comma-joined strings and prefetch M2M taxonomy.
apps/entries/views.py Update entry list/manage/detail querysets and filters for M2M taxonomy fields.
apps/entries/templates/entries/manage.html Render multiple projects in the manage table.
apps/entries/templates/entries/list.html Render multiple projects/categories in “My Entries”.
apps/entries/templates/entries/form.html Replace single-select widgets with multi-selects and adjust layout/header placement.
apps/entries/templates/entries/detail.html Render multiple projects/categories and optionally lab priorities on the detail page.
apps/entries/models.py Replace FK taxonomy fields with M2M fields; remove FK-based indexes.
apps/entries/migrations/0008_multiselect_taxonomy.py Add M2M fields, backfill from legacy FK columns, then drop the old fields/indexes.
apps/entries/forms.py Update ModelForm fields/querysets and apply consistent multi-select widget styling.
apps/entries/api_views.py Accept singular-or-list taxonomy payloads and persist them via M2M sets; return plural lists plus legacy singular keys for project/category.
apps/entries/admin.py Update admin list display/filters for M2M taxonomy and add filter_horizontal for new relations.
apps/core/views.py Update dashboard querysets to prefetch M2M taxonomy fields.
apps/core/templates/core/dashboard.html Render multiple projects/categories for recent entries on the dashboard.
apps/core/templates/core/api.html Update API docs to describe plural taxonomy keys (and legacy behavior).
apps/audit/signals.py Track taxonomy changes via m2m_changed handlers instead of FK tracked fields.

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

Comment thread apps/reports/views.py
Comment on lines +44 to +49
qs = WorkItem.objects.select_related('author').prefetch_related('projects', 'categories', 'tags')
if group_scope is not None:
# Prefer explicit WorkItem.group; fall back to author.group only when entry has no group set.
qs = qs.filter(Q(group__in=group_scope) | Q(group__isnull=True, author__group__in=group_scope))
if project_scope is not None:
qs = qs.filter(project__in=project_scope)
qs = qs.filter(projects__in=project_scope).distinct()
Comment thread apps/entries/admin.py
Comment on lines 15 to +16
readonly_fields = ('created_at', 'updated_at')

Comment thread tests/test_api_entries.py
Comment on lines +80 to +84
def test_missing_project_is_an_error(self, client, token, category):
resp = _post(client, token, {'categories': ['scientific']})
assert resp.status_code == 400
assert 'projects' in resp.json()['errors']

Comment thread tests/test_entries.py
Comment on lines +12 to +21
def _new_entry(project=None, category=None, lab_priority=None, **kwargs):
"""Create a WorkItem and set its project/category/lab_priority M2M relations."""
item = WorkItem.objects.create(**kwargs)
if project is not None:
item.projects.set(project if isinstance(project, (list, tuple)) else [project])
if category is not None:
item.categories.set(category if isinstance(category, (list, tuple)) else [category])
if lab_priority is not None:
item.lab_priorities.set(lab_priority if isinstance(lab_priority, (list, tuple)) else [lab_priority])
return item
Convert WorkItem.project / category / lab_priority from single foreign
keys to many-to-many relations (projects / categories / lab_priorities)
so an entry can be classified under several at once, and let the Reports
page filter by multiple values (match ANY). A data migration copies each
existing single value into the new M2M before dropping the old columns,
so no data is lost.

- model + migration 0008: FK -> M2M with forward/backward data copy.
- entry form: multi-select widgets; >=1 project and >=1 category still
  required; lab priorities optional. Field order is now Projects,
  Categories, Lab Priorities, Group.
- reports: ModelMultipleChoiceFilter (OR) for the three menus.
- displays (detail/list/manage/reports/dashboard/exporters/PDF/AI) join
  multiple names with ", ".
- JSON API: backward-compatible — accepts a single slug or a list, and
  returns plural lists plus the legacy singular keys (first value).
- audit: project/category/lab_priority tracked via m2m_changed handlers.
- tests: fixtures updated for M2M; new multi-select form, reports filter,
  and API back-compat coverage (205 passing).

Also nudges the markdown cheat-sheet panel on the entry form to align
with the top of the form card (moves the page header above the columns).

Closes #15

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@normanajn
normanajn force-pushed the feature/multiselect-taxonomy branch from 84bd162 to 0a73a6e Compare June 25, 2026 18:44
@normanajn
normanajn merged commit 4780fe1 into main Jun 25, 2026
@normanajn
normanajn deleted the feature/multiselect-taxonomy branch June 25, 2026 19:06
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.

Add option to select multiple items in pull down menus

2 participants