Commit 3aac269b by cahrens

Add tags to model.

TNL-1923
parent d52887fd
Oleg Marshev <oleg@edx.org> Oleg Marshev <oleg@edx.org>
Tim Babych <tim.babych@gmail.com> Tim Babych <tim.babych@gmail.com>
Christina Roberts <christina@edx.org>
# -*- coding: utf-8 -*-
""" Initial migration file for creating Note model """
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
""" Initial migration file for creating Note model """
dependencies = [
]
operations = [
migrations.CreateModel(
name='Note',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('user_id', models.CharField(
help_text=b'Anonymized user id, not course specific', max_length=255, db_index=True
)),
('course_id', models.CharField(max_length=255, db_index=True)),
('usage_id', models.CharField(help_text=b'ID of XBlock where the text comes from', max_length=255)),
('quote', models.TextField(default=b'')),
('text', models.TextField(default=b'', help_text=b"Student's thoughts on the quote", blank=True)),
('ranges', models.TextField(help_text=b'JSON, describes position of quote in the source text')),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
],
options={
},
bases=(models.Model,),
),
]
# -*- coding: utf-8 -*-
""" Add tags field to Note model """
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
""" Add tags field to Note model """
dependencies = [
('v1', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='note',
name='tags',
field=models.TextField(default=b'[]', help_text=b'JSON, list of comma-separated tags'),
preserve_default=True,
),
]
...@@ -15,6 +15,7 @@ class Note(models.Model): ...@@ -15,6 +15,7 @@ class Note(models.Model):
ranges = models.TextField(help_text="JSON, describes position of quote in the source text") ranges = models.TextField(help_text="JSON, describes position of quote in the source text")
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True) updated = models.DateTimeField(auto_now=True)
tags = models.TextField(help_text="JSON, list of comma-separated tags", default="[]")
@classmethod @classmethod
def create(cls, note_dict): def create(cls, note_dict):
...@@ -34,6 +35,7 @@ class Note(models.Model): ...@@ -34,6 +35,7 @@ class Note(models.Model):
note_dict['ranges'] = json.dumps(ranges) note_dict['ranges'] = json.dumps(ranges)
note_dict['user_id'] = note_dict.pop('user', None) note_dict['user_id'] = note_dict.pop('user', None)
note_dict['tags'] = json.dumps(note_dict.get('tags', list()))
return cls(**note_dict) return cls(**note_dict)
...@@ -43,7 +45,6 @@ class Note(models.Model): ...@@ -43,7 +45,6 @@ class Note(models.Model):
""" """
created = self.created.isoformat() if self.created else None created = self.created.isoformat() if self.created else None
updated = self.updated.isoformat() if self.updated else None updated = self.updated.isoformat() if self.updated else None
return { return {
'id': str(self.pk), 'id': str(self.pk),
'user': self.user_id, 'user': self.user_id,
...@@ -54,4 +55,5 @@ class Note(models.Model): ...@@ -54,4 +55,5 @@ class Note(models.Model):
'ranges': json.loads(self.ranges), 'ranges': json.loads(self.ranges),
'created': created, 'created': created,
'updated': updated, 'updated': updated,
'tags': json.loads(self.tags)
} }
...@@ -20,6 +20,7 @@ class NoteTest(TestCase): ...@@ -20,6 +20,7 @@ class NoteTest(TestCase):
"endOffset": 10, "endOffset": 10,
} }
], ],
"tags": [u"apple", u"pear"],
} }
def test_create_valid_note(self): def test_create_valid_note(self):
...@@ -47,3 +48,13 @@ class NoteTest(TestCase): ...@@ -47,3 +48,13 @@ class NoteTest(TestCase):
with self.assertRaises(ValidationError): with self.assertRaises(ValidationError):
note = Note.create(payload) note = Note.create(payload)
note.full_clean() note.full_clean()
def test_default_tags_value(self):
""" Test that a note without explicit tags has an empty list for the tags value """
payload = self.note_dict.copy()
payload.pop("tags")
note = Note.create(payload)
note.full_clean()
note.save()
self.assertEqual("[]", note.tags)
self.assertEqual([], note.as_dict()["tags"])
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import jwt import jwt
import unittest import unittest
import ddt
from calendar import timegm from calendar import timegm
from datetime import datetime, timedelta from datetime import datetime, timedelta
from mock import patch from mock import patch
...@@ -49,6 +50,7 @@ class BaseAnnotationViewTests(APITestCase): ...@@ -49,6 +50,7 @@ class BaseAnnotationViewTests(APITestCase):
"endOffset": 10, "endOffset": 10,
} }
], ],
"tags": ["pink", "lady"]
} }
def _create_annotation(self, **kwargs): def _create_annotation(self, **kwargs):
...@@ -62,6 +64,20 @@ class BaseAnnotationViewTests(APITestCase): ...@@ -62,6 +64,20 @@ class BaseAnnotationViewTests(APITestCase):
self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(response.status_code, status.HTTP_201_CREATED)
return response.data.copy() return response.data.copy()
def _do_annotation_update(self, data, updated_fields):
"""
Helper method for updating an annotation.
Returns the response and the updated annotation.
"""
payload = self.payload.copy()
payload.update(updated_fields)
payload.update(self.headers)
url = reverse('api:v1:annotations_detail', kwargs={'annotation_id': data['id']})
response = self.client.put(url, payload, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
return response, self._get_annotation(data['id'])
def _get_annotation(self, annotation_id): def _get_annotation(self, annotation_id):
""" """
Fetch annotation Fetch annotation
...@@ -124,6 +140,16 @@ class AnnotationListViewTests(BaseAnnotationViewTests): ...@@ -124,6 +140,16 @@ class AnnotationListViewTests(BaseAnnotationViewTests):
self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data['text'], '') self.assertEqual(response.data['text'], '')
def test_create_no_tags(self):
"""
Ensure we can create a new note with empty list of tags.
"""
url = reverse('api:v1:annotations')
self.payload['tags'] = []
response = self.client.post(url, self.payload, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data['tags'], [])
def test_create_ignore_created(self): def test_create_ignore_created(self):
""" """
Test if annotation 'created' field is not used by API. Test if annotation 'created' field is not used by API.
...@@ -245,6 +271,7 @@ class AnnotationListViewTests(BaseAnnotationViewTests): ...@@ -245,6 +271,7 @@ class AnnotationListViewTests(BaseAnnotationViewTests):
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
@ddt.ddt
class AnnotationDetailViewTests(BaseAnnotationViewTests): class AnnotationDetailViewTests(BaseAnnotationViewTests):
""" """
Test one annotation updating, reading and deleting Test one annotation updating, reading and deleting
...@@ -279,17 +306,29 @@ class AnnotationDetailViewTests(BaseAnnotationViewTests): ...@@ -279,17 +306,29 @@ class AnnotationDetailViewTests(BaseAnnotationViewTests):
Ensure we can update an existing annotation. Ensure we can update an existing annotation.
""" """
data = self._create_annotation(text=u"Foo") data = self._create_annotation(text=u"Foo")
payload = self.payload.copy() response, annotation = self._do_annotation_update(data, {'id': data['id'], 'text': 'Bar'})
payload.update({'id': data['id'], 'text': 'Bar'})
payload.update(self.headers)
url = reverse('api:v1:annotations_detail', kwargs={'annotation_id': data['id']})
response = self.client.put(url, payload, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
annotation = self._get_annotation(data['id'])
self.assertEqual(annotation['text'], "Bar", "annotation wasn't updated in db") self.assertEqual(annotation['text'], "Bar", "annotation wasn't updated in db")
self.assertEqual(response.data['text'], "Bar", "update annotation should be returned in response") self.assertEqual(response.data['text'], "Bar", "update annotation should be returned in response")
@ddt.data(
(["new", "tags"], ["new", "tags"]),
([], []),
(None, [u"pink", u"lady"]),
)
@ddt.unpack
def test_update_tags(self, updated_tags, expected_tags):
"""
Test that we can update tags on an existing annotation.
"""
data = self._create_annotation()
# If no tags are sent in the update, previously present tags should remain.
if updated_tags is None:
response, annotation = self._do_annotation_update(data, {'id': data['id']})
else:
response, annotation = self._do_annotation_update(data, {'id': data['id'], 'tags': updated_tags})
self.assertEqual(annotation["tags"], expected_tags)
self.assertEqual(response.data["tags"], expected_tags)
def test_update_fail(self): def test_update_fail(self):
""" """
Ensure can not update an existing annotation with bad note. Ensure can not update an existing annotation with bad note.
......
...@@ -156,6 +156,7 @@ class AnnotationDetailView(APIView): ...@@ -156,6 +156,7 @@ class AnnotationDetailView(APIView):
try: try:
note.text = self.request.data['text'] note.text = self.request.data['text']
note.tags = json.dumps(self.request.data['tags'])
note.full_clean() note.full_clean()
except KeyError as error: except KeyError as error:
log.debug(error, exc_info=True) log.debug(error, exc_info=True)
......
...@@ -49,7 +49,6 @@ INSTALLED_APPS = [ ...@@ -49,7 +49,6 @@ INSTALLED_APPS = [
'rest_framework_swagger', 'rest_framework_swagger',
'corsheaders', 'corsheaders',
'haystack', 'haystack',
'notesapi',
'notesapi.v1', 'notesapi.v1',
] ]
......
...@@ -9,3 +9,4 @@ pep8==1.5.7 ...@@ -9,3 +9,4 @@ pep8==1.5.7
pylint==1.4.1 pylint==1.4.1
diff-cover==0.7.2 diff-cover==0.7.2
factory_boy==2.4.1 factory_boy==2.4.1
ddt==0.8.0
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment