From 523c3a1581c433909fbd42e841b704070eed1f47 Mon Sep 17 00:00:00 2001 From: Tim Repke <repke@mcc-berlin.net> Date: Fri, 23 Aug 2024 21:01:14 +0200 Subject: [PATCH] add option to edit and clear assignments --- server/api/errors.py | 4 ++ server/api/routes/annotations.py | 90 ++++++++++++++++++++++++++++++-- 2 files changed, 91 insertions(+), 3 deletions(-) diff --git a/server/api/errors.py b/server/api/errors.py index 2cbda63..60fbc9e 100644 --- a/server/api/errors.py +++ b/server/api/errors.py @@ -33,6 +33,10 @@ class NoNextAssignmentWarning(Warning): status = http_status.HTTP_204_NO_CONTENT +class RemainingDependencyWarning(Warning): + status = http_status.HTTP_412_PRECONDITION_FAILED + + class AssignmentScopeNotFoundError(Exception): pass diff --git a/server/api/routes/annotations.py b/server/api/routes/annotations.py index 06da3d0..181a181 100644 --- a/server/api/routes/annotations.py +++ b/server/api/routes/annotations.py @@ -1,7 +1,8 @@ +import uuid from typing import TYPE_CHECKING from pydantic import BaseModel -from sqlalchemy import select, func as F, distinct +from sqlalchemy import select, func as F, distinct, text from sqlalchemy.orm import load_only from fastapi import APIRouter, Depends, HTTPException, status as http_status, Query @@ -10,7 +11,7 @@ from nacsos_data.db.schemas import ( AssignmentScope, User, Annotation, - BotAnnotation + BotAnnotation, Assignment ) from nacsos_data.models.annotations import ( AnnotationSchemeModel, @@ -81,7 +82,8 @@ from server.api.errors import ( NoNextAssignmentWarning, ProjectNotFoundError, AnnotationSchemeNotFoundError, - MissingInformationError + MissingInformationError, + RemainingDependencyWarning ) from server.util.security import UserPermissionChecker from server.data import db_engine @@ -412,6 +414,88 @@ async def make_assignments(payload: MakeAssignmentsRequestModel, return assignments +@router.post('/config/scopes/clear/{scheme_id}') +async def clear_empty_assignments(scope_id: str, + permissions=Depends(UserPermissionChecker('annotations_edit'))) -> None: + """ + Drop all assignments in a scope that are still incomplete... + + :param scope_id: + :param permissions: + :return: + """ + async with db_engine.session() as session: # type: AsyncSession + stmt = text(''' + DELETE + FROM assignment + WHERE assignment_id IN ( + WITH counts AS ( + SELECT ass.assignment_id, count(ann.assignment_id) as cnt + FROM assignment ass + LEFT OUTER JOIN annotation ann ON ass.assignment_id = ann.assignment_id + WHERE ass.assignment_scope_id = :scope_id + GROUP BY ass.assignment_id + ) + SELECT assignment_id + FROM counts + WHERE cnt = 0 + );''') + await session.execute(stmt, {'scope_id': scope_id}) + + +class AssignmentEditInfo(BaseModel): + scope_id: str + scheme_id: str + item_id: str + user_id: str + order: int + + +@router.put('/config/assignments/edit/', response_model=AssignmentModel) +async def edit_assignment(info: AssignmentEditInfo, + permissions=Depends(UserPermissionChecker('annotations_edit'))) -> AssignmentModel: + async with db_engine.session() as session: # type: AsyncSession + + # Check, if we already have an assignment for this... + assignment = (await session.execute( + select(Assignment) + .where( + Assignment.item_id == info.item_id, + Assignment.user_id == info.user_id, + Assignment.assignment_scope_id == info.scope_id + ))).scalars().one_or_none() + n_annotations: int = (await session.execute( + select(F.count(Annotation.annotation_id).label('n_annotations')) + .join(Assignment) + .where(Assignment.item_id == info.item_id, + Assignment.user_id == info.user_id, + Assignment.assignment_scope_id == info.scope_id))).scalar() + + # yes we do, drop this assignment! + if assignment is not None: + model = AssignmentModel.model_validate(assignment.__dict__) + if n_annotations == 0: + await session.delete(assignment) + return model + + raise RemainingDependencyWarning('Assignment has annotations, won\'t delete!') + + # seems to be a new one, create it! + assignment = Assignment( + assignment_id=uuid.uuid4(), + item_id=info.item_id, + user_id=info.user_id, + assignment_scope_id=info.scope_id, + annotation_scheme_id=info.scheme_id, + order=info.order, + status=AssignmentStatus.OPEN + ) + session.add(assignment) + model = AssignmentModel.model_validate(assignment.__dict__) + await session.commit() + return model + + @router.get('/config/scopes/{scheme_id}', response_model=list[AssignmentScopeModel]) async def get_assignment_scopes_for_scheme(scheme_id: str, permissions=Depends(UserPermissionChecker('annotations_read'))) \ -- GitLab