Skip to content

Commit 534ed83

Browse files
More cleanup
1 parent 53c7140 commit 534ed83

10 files changed

Lines changed: 255 additions & 254 deletions

File tree

backend/annotation/serializers.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,7 @@ def get_removable(self, labeling: Labeling) -> bool:
5353
if user is None or user.is_anonymous:
5454
return False
5555

56-
if user.is_superuser:
57-
return True
58-
59-
if user.has_perm("annotation.delete_any_labeling"):
56+
if user.is_superuser or user.has_perm("annotation.delete_any_labeling"):
6057
return True
6158

6259
if user.has_perm("annotation.delete_own_labeling"):

backend/annotation/urls.py

Lines changed: 0 additions & 1 deletion
This file was deleted.

backend/annotation/views.py

Lines changed: 53 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -72,55 +72,70 @@ def create(self, request: Request) -> Response:
7272

7373
selected_label_ids = {label["id"] for label in selected_labels}
7474

75+
try:
76+
self._update_labelings(problem, user, selected_label_ids, remarks)
77+
except PermissionError as e:
78+
return Response({"detail": str(e)}, status=403)
79+
80+
return Response({"ok": True}, status=HTTP_200_OK)
81+
82+
def _update_labelings(
83+
self,
84+
problem: Problem,
85+
user: User,
86+
selected_label_ids: set[int],
87+
remarks: str,
88+
) -> None:
89+
"""Update labelings for a problem based on selected labels."""
90+
7591
with transaction.atomic():
76-
# Get all active labelings for this problem
7792
active_labelings = Labeling.objects.filter(
7893
problem=problem, removed_at__isnull=True
79-
)
94+
).select_related("label", "attached_by")
8095

81-
# Determine which labels to remove and which to add
8296
current_label_ids = {labeling.label.pk for labeling in active_labelings}
8397
labels_to_remove = current_label_ids - selected_label_ids
8498
labels_to_add = selected_label_ids - current_label_ids
8599

86-
# Mark labels as removed
87100
for labeling in active_labelings:
88101
if labeling.label.pk in labels_to_remove:
89-
# Check permission for removal
90-
if not self._can_remove_labeling(user, labeling):
91-
logger.warning(
92-
f"User {user.username} attempted to remove label {labeling.label.pk} "
93-
f"attached by {labeling.attached_by.username}"
94-
)
95-
return Response(
96-
{
97-
"error": "You can only remove labels you attached yourself."
98-
},
99-
status=403,
100-
)
101-
labeling.removed_at = timezone.now()
102-
labeling.removed_by = user
103-
if remarks:
104-
labeling.notes = remarks
105-
labeling.save()
106-
107-
# Add new labels
102+
self._remove_labeling(
103+
labeling=labeling,
104+
user=user,
105+
remarks=remarks,
106+
)
107+
108108
for label_id in labels_to_add:
109-
Labeling.objects.create(
110-
problem=problem,
109+
self._create_labeling(
111110
label_id=label_id,
112-
attached_by=user,
113-
notes=remarks,
111+
problem=problem,
112+
user=user,
113+
remarks=remarks,
114114
)
115115

116-
return Response({"ok": True}, status=HTTP_200_OK)
117-
118-
def _can_remove_labeling(self, user: User, labeling: Labeling) -> bool:
119-
"""Check if user can remove a specific labeling."""
120-
if user.is_superuser or user.has_perm("annotation.delete_any_labeling"):
121-
return True
122-
123-
if user.has_perm("annotation.delete_own_labeling"):
124-
return labeling.attached_by.pk == user.pk
125-
126-
return False
116+
def _create_labeling(
117+
self, label_id: int, problem: Problem, user: User, remarks: str
118+
) -> None:
119+
"""Create a new labeling."""
120+
121+
Labeling.objects.create(
122+
problem=problem,
123+
label_id=label_id,
124+
attached_by=user,
125+
notes=remarks,
126+
)
127+
128+
def _remove_labeling(self, labeling: Labeling, user: User, remarks: str) -> None:
129+
"""Mark a labeling as removed."""
130+
131+
if not user.can_remove_labeling(labeling):
132+
logger.warning(
133+
f"User {user.username} attempted to remove label {labeling.label.pk} "
134+
f"attached by {labeling.attached_by.username}"
135+
)
136+
raise PermissionError("You can only remove labels you attached yourself.")
137+
labeling.removed_at = timezone.now()
138+
labeling.removed_by = user
139+
if remarks:
140+
labeling.notes = remarks
141+
labeling.save()

backend/annotation/views_test.py

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,8 @@ def test_master_annotator_can_remove_others_labeling(
219219
assert labeling_by_annotator.removed_by == master_annotator
220220

221221

222-
class TestLabelingAddAndRemoveOperations:
223-
"""Tests for add/remove labeling operations."""
222+
class TestLabelingAddAndRemove:
223+
"""Tests for adding / removing labelings."""
224224

225225
def test_adding_label_creates_labeling(
226226
self, api_client, annotator, sample_problem, sample_label
@@ -288,25 +288,3 @@ def test_keeping_existing_labels_unchanged(
288288
assert Labeling.objects.filter(
289289
problem=sample_problem, label=another_label, removed_at__isnull=True
290290
).exists()
291-
292-
293-
class TestUnsupportedHttpMethods:
294-
"""Tests for unsupported HTTP methods."""
295-
296-
def test_put_not_allowed(self, api_client, master_annotator, sample_label):
297-
"""PUT method should not be allowed."""
298-
api_client.force_authenticate(user=master_annotator)
299-
response = api_client.put(f"/api/label/{sample_label.id}/", {}, format="json")
300-
assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED
301-
302-
def test_patch_not_allowed(self, api_client, master_annotator, sample_label):
303-
"""PATCH method should not be allowed."""
304-
api_client.force_authenticate(user=master_annotator)
305-
response = api_client.patch(f"/api/label/{sample_label.id}/", {}, format="json")
306-
assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED
307-
308-
def test_delete_not_allowed(self, api_client, master_annotator, sample_label):
309-
"""DELETE method should not be allowed."""
310-
api_client.force_authenticate(user=master_annotator)
311-
response = api_client.delete(f"/api/label/{sample_label.id}/")
312-
assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED

0 commit comments

Comments
 (0)