Skip to content

Commit

Permalink
Merge pull request #636 from bounswe/annotations-post-api
Browse files Browse the repository at this point in the history
Annotations post api
  • Loading branch information
Bera0422 authored Dec 19, 2023
2 parents 1ee771b + 193b32a commit 05e3cda
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 12 deletions.
3 changes: 1 addition & 2 deletions project/annotation_project/annotation_project/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,5 @@

urlpatterns = [
path('admin/', admin.site.urls),
path('', include("annotations.urls"))

path('annotations/', include("annotations.urls")) # url must start with 'annotations/' according to the standard: https://www.w3.org/TR/annotation-protocol/#:~:text=5.2%20Suggesting%20an%20IRI%20for%20an%20Annotation
]
97 changes: 96 additions & 1 deletion project/annotation_project/annotations/tests.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django.test import TestCase, Client
from django.urls import reverse
from datetime import datetime
import json

from .models import *

Expand All @@ -16,6 +17,14 @@ def setUp(self):
creator=creator,
created=datetime.now(),
)

def tearDown(self):
Annotation.objects.all().delete()
Selector.objects.all().delete()
Creator.objects.all().delete()
Body.objects.all().delete()
Source.objects.all().delete()
print("All Annotation Get API Tests Completed")

def test_get_annotation_by_id(self):
#Test the annotation response format
Expand All @@ -28,7 +37,7 @@ def test_get_annotation_by_id(self):

response = response.json()
self.assertEqual(response['@context'], 'http://www.w3.org/ns/anno.jsonld')
self.assertTrue(response['id'].endswith(f'annotation{self.annotation.id}'))
self.assertEqual(response['id'],f'http://13.51.55.11:8001/annotations/annotation/{self.annotation.id}')
self.assertEqual(response['type'], self.annotation.type)
self.assertEqual(response['body'], {
'type': self.annotation.body.type,
Expand All @@ -48,3 +57,89 @@ def test_get_annotation_by_id(self):
self.assertTrue(response['creator']['id'], self.annotation.creator.name)
self.assertIn('created', response)

class AnnotationPostTest(TestCase):
def setUp(self):
self.client = Client()
self.url = reverse('create_annotation')
self.headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
self.json = {
"@context": "http://www.w3.org/ns/anno.jsonld",
"type": "Annotation",
"body": {
"type": "TextualBody",
"format": "text/html",
"language": "en",
"value": "Turing machine is great!!!"
},
"target": {
"id": "http://13.51.205.39/node/55#theorem",
"type": "text",
"selector": {
"type": "TextPositionSelector",
"start": 30,
"end": 45
}
},
"creator": {
"id": "http://13.51.205.39/profile/cemsay@gmail.com",
"type": "Person"
}
}
self.body = {
"@context": "http://www.w3.org/ns/anno.jsonld",
"type": "Annotation",
"body": str(self.json['body']),
"target": str(self.json['target']),
"creator": str(self.json['creator'])
}
self.body_missing_body = {
"@context": "http://www.w3.org/ns/anno.jsonld",
"type": "Annotation",
"target": str(self.json['target']),
"creator": str(self.json['creator'])
}
self.body_missing_target = {
"@context": "http://www.w3.org/ns/anno.jsonld",
"type": "Annotation",
"body": str(self.json['body']),
"creator": str(self.json['creator'])
}
self.body_missing_creator = {
"@context": "http://www.w3.org/ns/anno.jsonld",
"type": "Annotation",
"body": str(self.json['body']),
"target": str(self.json['target']),
}

def tearDown(self):
Annotation.objects.all().delete()
Selector.objects.all().delete()
Creator.objects.all().delete()
Body.objects.all().delete()
Source.objects.all().delete()
print("All Annotation Post API Tests Completed")

def test_create_annotation(self):
response = self.client.post(self.url, data=self.body_missing_body, headers=self.headers)
self.assertEqual(response.status_code, 400, "Missing body field test failed. expected 400, got " + str(response.status_code))
response = self.client.post(self.url, data=self.body_missing_target, headers=self.headers)
self.assertEqual(response.status_code, 400, "Missing target field test failed. expected 400, got " + str(response.status_code))
response = self.client.post(self.url, data=self.body_missing_creator, headers=self.headers)
self.assertEqual(response.status_code, 400, "Missing creator field test failed. expected 400, got " + str(response.status_code))

response = self.client.post(self.url, data=self.body, headers=self.headers, content_type='application/json')
self.assertEqual(response.status_code, 200, "Successful create annotation test failed. expected 200, got " + str(response.status_code))

records = Annotation.objects.filter(id=response.json()['id'])
self.assertEqual(len(records), 1, "Successful create annotation test (database insertion) failed. expected 1 row, got " + str(len(records)))

if records:
record = records[0]
data = response.json()
self.assertEqual(data['body'], self.json['body'], "Successful create annotation test (body) failed. expected " + str(self.json['body']) + ", got " + str(data['body']))
self.assertEqual(data['target'], self.json['target'], "Successful create annotation test (target) failed. expected " + str(self.json['target']) + ", got " + str(data['target']))
self.assertEqual(data['creator'], self.json['creator'], "Successful create annotation test (creator) failed. expected " + str(self.json['creator']) + ", got " + str(data['creator']))
self.assertEqual(data['id'], "http://13.51.55.11:8001/annotations/annotation/" + str(record.pk), "Successful create annotation test (id) failed. expected " + "http://13.51.55.11:8001/annotations/annotation/" + str(record.pk))
4 changes: 2 additions & 2 deletions project/annotation_project/annotations/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

urlpatterns = [
path('get_annotation/', matched_annotations_get_view, name='get_annotation'),
path('annotation<annotation_id>/', get_annotation_by_id, name='get_annotation_by_id'),

path('annotation/<annotation_id>/', get_annotation_by_id, name='get_annotation_by_id'),
path('create_annotation/', create_annotation, name='create_annotation'),
]


120 changes: 113 additions & 7 deletions project/annotation_project/annotations/views.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from django.shortcuts import render
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
import json

from .models import *


def serialize_annotation(annotation, scheme, host):
def serialize_annotation(annotation):
return {
'@context': 'http://www.w3.org/ns/anno.jsonld',
'id': f'{scheme}://{host}/annotation{annotation.id}',
'id': f'http://13.51.55.11:8001/annotations/annotation/{annotation.id}',
'type': annotation.type,
'body': {
'type': annotation.body.type,
Expand All @@ -25,7 +25,7 @@ def serialize_annotation(annotation, scheme, host):
},
},
'creator': {
'id': f'http://13.51.205.39/profile/{annotation.creator.name}',
'id': annotation.creator.name,
'type': annotation.creator.type,
},
'created': annotation.created,
Expand All @@ -49,7 +49,7 @@ def matched_annotations_get_view(request):
return JsonResponse({'message': 'No annotation found!'}, status=404)

matched_data = [
serialize_annotation(annotation, request.scheme, request.get_host()) for annotation in matched_annotations
serialize_annotation(annotation) for annotation in matched_annotations
]

return JsonResponse(matched_data, status=200, safe=False)
Expand All @@ -63,6 +63,112 @@ def get_annotation_by_id(request, annotation_id):

if not annotation:
return JsonResponse({'message': 'No annotation found!'}, status=404)
return JsonResponse(data = serialize_annotation(annotation, request.scheme, request.get_host()), status=200)
return JsonResponse(data = serialize_annotation(annotation), status=200)
except Exception as e:
return JsonResponse({'message': str(e)}, status=500)

@csrf_exempt
def create_annotation(request):
try:
body = request.POST.get('body')
if not body:
return JsonResponse({'message': 'body must be given!'}, status=400)
body = json.loads(body)

value = body.get('value')
if not value:
return JsonResponse({'message': 'body value must be given!'}, status=400)

body_type = body.get('type')
body_format = body.get('format')
body_language = body.get('language')

target = request.POST.get('target')
if not target:
return JsonResponse({'message': 'target must be given!'}, status=400)
target = json.loads(target)

target_id = target.get('id')
if not target_id:
return JsonResponse({'message': 'target id must be given!'}, status=400)

selector = target.get('selector')
if not selector:
return JsonResponse({'message': 'target selector must be given!'}, status=400)

selector_type = selector.get('type')
selector_start = selector.get('start')
if not selector_start:
return JsonResponse({'message': 'selector start must be given!'}, status=400)

selector_end = selector.get('end')
if not selector_end:
return JsonResponse({'message': 'selector end must be given!'}, status=400)

creator = request.POST.get('creator')
if not creator:
return JsonResponse({'message': 'creator must be given!'}, status=400)
creator = json.loads(creator)

creator_id = creator.get('id')
if not creator_id:
return JsonResponse({'message': 'creator id must be given!'}, status=400)

creator_type = creator.get('type')

body_object = Body.objects.create(
value=value
)

if body_type:
body_object.type = body_type

if body_format:
body_object.format = body_format

if body_language:
body_object.language = body_language

body_object.save()

source_object, created = Source.objects.get_or_create(
uri=target_id
)

selector_object = Selector.objects.create(
start=selector_start,
end=selector_end,
source=source_object
)

if selector_type:
selector_object.type = selector_type

selector_object.save()

creator_object = Creator.objects.create(
name=creator_id
)

if creator_type:
creator_object.type = creator_type

creator_object.save()

annotation_type = request.POST.get('type')
annotation_object = Annotation.objects.create(
body=body_object,
target=selector_object,
creator=creator_object
)

if annotation_type:
annotation_object.type = annotation_type

annotation_object.save()

return JsonResponse(data = serialize_annotation(annotation_object), status=200)
except Exception as e:
if "duplicate key value violates unique constraint" in str(e):
return JsonResponse({'message': 'Annotation already exists!'}, status=400)
return JsonResponse({'message': "Internal server error"}, status=500)

0 comments on commit 05e3cda

Please sign in to comment.