Skip to content
Snippets Groups Projects
Commit 230c52d9 authored by Tim Repke's avatar Tim Repke
Browse files

add several annotation related endpoints

parent 800192d4
No related branches found
No related tags found
No related merge requests found
......@@ -44,6 +44,10 @@ loggers:
handlers: [default]
level: DEBUG
propagate: no
nacsos_data:
handlers: [default]
level: DEBUG
propagate: no
root:
level: DEBUG
handlers: [default]
\ No newline at end of file
......@@ -12,7 +12,6 @@ router = APIRouter()
# route for testing / checking the service is reachable
router.include_router(ping.router, prefix='/ping')
# route to fetch, manage, submit item annotations
router.include_router(annotations.router, prefix='/annotations')
......
from fastapi import APIRouter, Depends
from fastapi import APIRouter, Depends, HTTPException, status as http_status
from nacsos_data.models.annotations import AnnotationTaskModel, AnnotationTaskLabel, AnnotationTaskLabelChoice, \
AssignmentScopeModel, AssignmentModel
AssignmentScopeModel, AssignmentModel, AssignmentStatus
from nacsos_data.models.items import ItemModel
from nacsos_data.models.items.twitter import TwitterItemModel
from nacsos_data.db.crud.items.twitter import read_tweet_by_item_id
......@@ -8,9 +8,15 @@ from nacsos_data.db.crud.annotations import \
read_assignments_for_scope_for_user, \
read_assignment_scopes_for_project_for_user, \
read_annotations_for_assignment, \
read_assignment_scope, \
read_next_assignment_for_scope_for_user, \
read_next_open_assignment_for_scope_for_user, \
read_annotation_task, \
read_annotation_tasks_for_project, \
read_assignment, \
upsert_annotations, \
UserProjectAssignmentScope
from nacsos_data.util.annotations.validation import merge_task_and_annotations, annotated_task_to_annotations
from pydantic import BaseModel
from server.util.security import UserPermissionChecker
......@@ -19,8 +25,13 @@ from server.data import db_engine
router = APIRouter()
class AnnotationItem(BaseModel):
class AnnotatedItem(BaseModel):
task: AnnotationTaskModel
assignment: AssignmentModel
class AnnotationItem(AnnotatedItem):
scope: AssignmentScopeModel
item: ItemModel | TwitterItemModel
......@@ -46,22 +57,67 @@ async def get_task_definitions_for_project(project_id: str) -> list[AnnotationTa
return await read_annotation_tasks_for_project(project_id=project_id, engine=db_engine)
@router.get('/annotate/next/{project_id}/{assignment_scope_id}', response_model=AnnotationItem)
@router.get('/annotate/next/{assignment_scope_id}/{current_assignment_id}', response_model=AnnotationItem)
async def get_next_assignment_for_scope_for_user(assignment_scope_id: str,
permissions=Depends(UserPermissionChecker('annotations_edit'))):
assignments = await read_assignments_for_scope_for_user(assignment_scope_id=assignment_scope_id,
user_id=permissions.user.user_id,
engine=db_engine)
# FIXME: magically find the next-to-annotate id
item = await read_tweet_by_item_id(assignments[0].item, engine=db_engine)
return AnnotationItem(
task=ATM('668529f5-1c54-44ef-b665-290ad3632f0d'),
item=tweet
)
current_assignment_id: str,
permissions=Depends(UserPermissionChecker('annotations_read'))):
# FIXME response for "last in list"
assignment = await read_next_assignment_for_scope_for_user(current_assignment_id=current_assignment_id,
assignment_scope_id=assignment_scope_id,
user_id=permissions.user.user_id,
engine=db_engine)
scope = await read_assignment_scope(assignment_scope_id=assignment_scope_id, engine=db_engine)
task = await read_annotation_task(annotation_task_id=assignment.task_id, engine=db_engine)
annotations = await read_annotations_for_assignment(assignment_id=assignment.assignment_id, engine=db_engine)
_, task = merge_task_and_annotations(annotation_task=task, annotations=annotations)
# FIXME: get any item type, not just tweets
item = await read_tweet_by_item_id(item_id=assignment.item_id, engine=db_engine)
return AnnotationItem(task=task, assignment=assignment, scope=scope, item=item)
@router.get('/annotate/next/{assignment_scope_id}', response_model=AnnotationItem)
async def get_next_open_assignment_for_scope_for_user(assignment_scope_id: str,
permissions=Depends(UserPermissionChecker('annotations_read'))):
# FIXME response for "all done"
assignment = await read_next_open_assignment_for_scope_for_user(assignment_scope_id=assignment_scope_id,
user_id=permissions.user.user_id,
engine=db_engine)
scope = await read_assignment_scope(assignment_scope_id=assignment_scope_id, engine=db_engine)
task = await read_annotation_task(annotation_task_id=assignment.task_id, engine=db_engine)
annotations = await read_annotations_for_assignment(assignment_id=assignment.assignment_id, engine=db_engine)
_, task = merge_task_and_annotations(annotation_task=task, annotations=annotations)
# FIXME: get any item type, not just tweets
item = await read_tweet_by_item_id(item_id=assignment.item_id, engine=db_engine)
return AnnotationItem(task=task, assignment=assignment, scope=scope, item=item)
@router.get('/annotate/assignment/{assignment_id}', response_model=AnnotationItem)
async def get_assignment(assignment_id: str,
permissions=Depends(UserPermissionChecker('annotations_read'))):
assignment = await read_assignment(assignment_id=assignment_id, engine=db_engine)
assert assignment.user_id == permissions.user.user_id
scope = await read_assignment_scope(assignment_scope_id=assignment.assignment_scope_id, engine=db_engine)
task = await read_annotation_task(annotation_task_id=assignment.task_id, engine=db_engine)
annotations = await read_annotations_for_assignment(assignment_id=assignment_id, engine=db_engine)
_, task = merge_task_and_annotations(annotation_task=task, annotations=annotations)
# FIXME: get any item type, not just tweets
item = await read_tweet_by_item_id(item_id=assignment.item_id, engine=db_engine)
return AnnotationItem(task=task, assignment=assignment, scope=scope, item=item)
@router.get('/annotate/scopes/{project_id}', response_model=list[UserProjectAssignmentScope])
async def get_assignment_scopes(project_id: str, permissions=Depends(UserPermissionChecker('annotations_read'))) \
async def get_assignment_scopes_for_user(project_id: str,
permissions=Depends(UserPermissionChecker('annotations_read'))) \
-> list[UserProjectAssignmentScope]:
scopes = await read_assignment_scopes_for_project_for_user(project_id=project_id,
user_id=permissions.user.user_id,
......@@ -69,23 +125,42 @@ async def get_assignment_scopes(project_id: str, permissions=Depends(UserPermiss
return scopes
@router.get('/annotate/assignments/{project_id}/{assignment_scope_id}', response_model=list[AssignmentModel])
@router.get('/annotate/assignments/{assignment_scope_id}', response_model=list[AssignmentModel])
async def get_assignments(assignment_scope_id: str, permissions=Depends(UserPermissionChecker('annotations_read'))) \
-> list[AssignmentModel]:
# FIXME: would be more elegant to get the project ID indirectly via the assignment_scope
# rather than passing it as a redundant parameter
assignments = await read_assignments_for_scope_for_user(assignment_scope_id=assignment_scope_id,
user_id=permissions.user.user_id,
engine=db_engine)
return assignments
@router.get('/annotate/annotations/{project_id}/{assignment_scope_id}', response_model=list[AssignmentModel])
@router.get('/annotate/annotations/{assignment_scope_id}', response_model=list[AssignmentModel])
async def get_annotations(assignment_scope_id: str, permissions=Depends(UserPermissionChecker('annotations_read'))) \
-> list[AssignmentModel]:
# FIXME: would be more elegant to get the project ID indirectly via the assignment_scope
# rather than passing it as a redundant parameter
assignments = await read_assignments_for_scope_for_user(assignment_scope_id=assignment_scope_id,
user_id=permissions.user.user_id,
engine=db_engine)
return assignments
@router.post('/annotate/save')
async def save_annotation(annotated_item: AnnotatedItem,
permissions=Depends(UserPermissionChecker('annotations_read'))) -> AssignmentStatus:
# double-check, that the supposed assignment actually exists
assignment_db = await read_assignment(assignment_id=annotated_item.assignment.assignment_id, engine=db_engine)
if permissions.user.user_id == assignment_db.user_id \
and str(assignment_db.assignment_scope_id) == annotated_item.assignment.assignment_scope_id \
and str(assignment_db.item_id) == annotated_item.assignment.item_id \
and str(assignment_db.task_id) == annotated_item.assignment.task_id:
print('permission yay')
annotations = annotated_task_to_annotations(annotated_item.task)
status = await upsert_annotations(annotations=annotations,
assignment_id=annotated_item.assignment.assignment_id,
engine=db_engine)
return status
else:
raise HTTPException(
status_code=http_status.HTTP_403_FORBIDDEN,
detail=f'The combination of project, assignment, user, task, and item is invalid.',
)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment