Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("annotations", "0009_alter_graph_allograph"),
]

operations = [
migrations.AddField(
model_name="graph",
name="deleted_at",
field=models.DateTimeField(blank=True, db_index=True, null=True),
),
migrations.AddField(
model_name="graph",
name="deleted_by",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="+",
to=settings.AUTH_USER_MODEL,
),
),
]
15 changes: 14 additions & 1 deletion apps/annotations/models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
from django.db import models

from apps.common.models import SoftDeleteModel

class Graph(models.Model):

class GraphQuerySet(models.QuerySet):
def live(self) -> GraphQuerySet:
"""Rows not in the trash — every public/read surface must use this."""
return self.filter(deleted_at__isnull=True) # type: ignore[no-any-return]

def trashed(self) -> GraphQuerySet:
return self.filter(deleted_at__isnull=False) # type: ignore[no-any-return]


class Graph(SoftDeleteModel):
class AnnotationType(models.TextChoices):
IMAGE = "image", "Image"
TEXT = "text", "Text"
Expand Down Expand Up @@ -38,6 +49,8 @@ class AnnotationType(models.TextChoices):
# sparkline, which silently ignores null-created rows.
created = models.DateTimeField(auto_now_add=True, null=True, blank=True, db_index=True)

objects = GraphQuerySet.as_manager()

class Meta:
ordering = ["id"]
constraints = [
Expand Down
101 changes: 101 additions & 0 deletions apps/annotations/schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,41 @@ paths:
summary: List graphs for management
security:
- api_key: []
parameters:
- in: query
name: deleted
schema:
type: boolean
required: false
description: >-
When true, list trashed graphs (soft-deleted, newest first) instead
of live ones. Default lists live graphs only.
- in: query
name: annotation_type
schema:
type: string
enum: [image, text, editorial, unknown]
required: false
- in: query
name: deleted_by__username
schema:
type: string
required: false
description: Username of the user who trashed the row.
- in: query
name: deleted_at__gte
schema:
type: string
format: date-time
required: false
description: Only rows trashed at or after this instant (ISO 8601).
- in: query
name: deleted_at__lte
schema:
type: string
format: date-time
required: false
description: Only rows trashed at or before this instant (ISO 8601).
responses:
200:
description: List graphs for management.
Expand Down Expand Up @@ -123,6 +158,72 @@ paths:
$ref: '#/components/schemas/Graph'
tags:
- annotations-management
/api/v1/management/annotations/graphs/trash-actors/:
get:
operationId: management-graphs-trash-actors
summary: Usernames that currently have graphs in the trash
description: >-
Distinct, sorted usernames of users with at least one trashed graph —
the option list for the trash "deleted by" filter. Rows whose deleter
no longer exists are omitted.
security:
- api_key: []
responses:
200:
description: Usernames with at least one trashed graph.
content:
application/json:
schema:
type: array
items:
type: string
tags:
- annotations-management
/api/v1/management/annotations/graphs/{id}/restore/:
post:
operationId: management-graphs-restore
summary: Restore a trashed graph
security:
- api_key: []
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
200:
description: The restored graph (deleted_at/deleted_by cleared).
content:
application/json:
schema:
$ref: '#/components/schemas/Graph'
404:
description: No trashed graph with this id.
tags:
- annotations-management
/api/v1/management/annotations/graphs/{id}/purge/:
delete:
operationId: management-graphs-purge
summary: Permanently delete a trashed graph
security:
- api_key: []
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
204:
description: >-
Trashed graph permanently deleted (cascades to its components;
for TEXT graphs the corresp reference is stripped from the
transcription). Cannot be undone.
404:
description: No trashed graph with this id.
tags:
- annotations-management
/api/v1/management/annotations/graph-components/:
get:
operationId: management-graph-components-list
Expand Down
5 changes: 5 additions & 0 deletions apps/annotations/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ class GraphManagementSerializer(GraphDescriptionMixin, serializers.ModelSerializ
position_details = serializers.SerializerMethodField(read_only=True)
num_features = serializers.SerializerMethodField()
is_described = serializers.SerializerMethodField()
deleted_by = serializers.SlugRelatedField(slug_field="username", read_only=True)

class Meta:
model = Graph
Expand All @@ -147,7 +148,11 @@ class Meta:
"graphcomponent_set",
"num_features",
"is_described",
"created",
"deleted_at",
"deleted_by",
]
read_only_fields = ["created", "deleted_at", "deleted_by"]


def _replace_graph_components(graph: Graph, components_data: list[dict]) -> None:
Expand Down
Loading
Loading