Commit 5d861cbfb8e4b0316de8ccec0c804809ed7090f3
1 parent
61c43267c4
Exists in
master
Wrote tests for FlashcardViewSet.edit
Showing 4 changed files with 77 additions and 20 deletions Side-by-side Diff
flashcards/models.py
View file @
5d861cb
1 | 1 | from datetime import datetime |
2 | 2 | |
3 | 3 | from django.contrib.auth.models import AbstractUser, UserManager |
4 | -from django.core.exceptions import PermissionDenied | |
4 | +from django.core.exceptions import PermissionDenied, SuspiciousOperation | |
5 | +from django.db import IntegrityError | |
5 | 6 | from django.db.models import * |
6 | 7 | from django.utils.timezone import now |
7 | 8 | from simple_email_confirmation import SimpleEmailConfirmationUserMixin |
8 | 9 | |
... | ... | @@ -109,11 +110,12 @@ |
109 | 110 | section = ForeignKey('Section', help_text='The section with which the card is associated') |
110 | 111 | pushed = DateTimeField(auto_now_add=True, help_text="When the card was first pushed") |
111 | 112 | material_date = DateTimeField(default=now, help_text="The date with which the card is associated") |
112 | - previous = ForeignKey('Flashcard', null=True, blank=True, | |
113 | + previous = ForeignKey('Flashcard', null=True, blank=True, default=None, | |
113 | 114 | help_text="The previous version of this card, if one exists") |
114 | 115 | author = ForeignKey(User) |
115 | 116 | is_hidden = BooleanField(default=False) |
116 | - hide_reason = CharField(blank=True, max_length=255, help_text="Reason for hiding this card") | |
117 | + hide_reason = CharField(blank=True, null=True, max_length=255, | |
118 | + default=None, help_text="Reason for hiding this card") | |
117 | 119 | mask = MaskField(max_length=255, null=True, blank=True, help_text="The mask on the card") |
118 | 120 | |
119 | 121 | class Meta: |
... | ... | @@ -131,6 +133,16 @@ |
131 | 133 | if not result.exists(): return self.is_hidden |
132 | 134 | return result[0].is_hidden() |
133 | 135 | |
136 | + def add_to_deck(self, user): | |
137 | + if not user.is_in_section(self.section): | |
138 | + raise PermissionDenied("You don't have the permission to add this card") | |
139 | + try: | |
140 | + user_flashcard = UserFlashcard.objects.create(user=user, flashcard=self, mask=self.mask) | |
141 | + except IntegrityError: | |
142 | + raise SuspiciousOperation("The flashcard is already in the user's deck") | |
143 | + user_flashcard.save() | |
144 | + return user_flashcard | |
145 | + | |
134 | 146 | def edit(self, user, new_flashcard): |
135 | 147 | """ |
136 | 148 | Creates a new flashcard if a new flashcard should be created when the given user edits this flashcard. |
137 | 149 | |
138 | 150 | |
... | ... | @@ -155,10 +167,15 @@ |
155 | 167 | content_changed |= True |
156 | 168 | self.text = new_flashcard['text'] |
157 | 169 | if create_new and content_changed: |
170 | + mask = self.mask | |
171 | + pk = self.pk | |
158 | 172 | self.pk = None |
159 | 173 | if 'mask' in new_flashcard: |
160 | - self.mask = new_flashcard['mask'] | |
174 | + mask = new_flashcard['mask'] | |
175 | + self.mask = mask | |
176 | + self.previous_id = pk | |
161 | 177 | self.save() |
178 | + return create_new and content_changed | |
162 | 179 | |
163 | 180 | @classmethod |
164 | 181 | def cards_visible_to(cls, user): |
flashcards/serializers.py
View file @
5d861cb
... | ... | @@ -119,7 +119,7 @@ |
119 | 119 | is_hidden = BooleanField(read_only=True) |
120 | 120 | hide_reason = CharField(read_only=True) |
121 | 121 | material_date = DateTimeField(default=now) |
122 | - mask = MaskFieldSerializer() | |
122 | + mask = MaskFieldSerializer(allow_null=True) | |
123 | 123 | |
124 | 124 | def validate_material_date(self, value): |
125 | 125 | utc = pytz.UTC |
... | ... | @@ -135,7 +135,7 @@ |
135 | 135 | def validate_previous(self, value): |
136 | 136 | if value is None: |
137 | 137 | return value |
138 | - if Flashcard.objects.filter(pk=value).count() > 0: | |
138 | + if Flashcard.objects.filter(pk=value.pk).count() > 0: | |
139 | 139 | return value |
140 | 140 | raise serializers.ValidationError("Invalid previous Flashcard object") |
141 | 141 | |
... | ... | @@ -155,6 +155,8 @@ |
155 | 155 | return value |
156 | 156 | |
157 | 157 | def validate_mask(self, value): |
158 | + if value is None: | |
159 | + return None | |
158 | 160 | if len(self.initial_data['text']) < value.max_offset(): |
159 | 161 | raise serializers.ValidationError("Mask out of bounds") |
160 | 162 | return value |
flashcards/tests/test_api.py
View file @
5d861cb
... | ... | @@ -4,6 +4,9 @@ |
4 | 4 | from rest_framework.test import APITestCase |
5 | 5 | from re import search |
6 | 6 | from django.utils.timezone import now |
7 | +from flashcards.validators import FlashcardMask | |
8 | +from flashcards.serializers import FlashcardSerializer | |
9 | +from pytz import UTC as utc | |
7 | 10 | |
8 | 11 | |
9 | 12 | class LoginTests(APITestCase): |
10 | 13 | |
11 | 14 | |
... | ... | @@ -181,17 +184,46 @@ |
181 | 184 | fixtures = ['testusers', 'testsections'] |
182 | 185 | |
183 | 186 | def setUp(self): |
184 | - section = Section.objects.get(pk=1) | |
185 | - user = User.objects.get(email='none@none.com') | |
186 | - | |
187 | - self.flashcard = Flashcard(text="jason", section=section, material_date=now(), author=user) | |
187 | + self.section = Section.objects.get(pk=1) | |
188 | + self.section.save() | |
189 | + self.user = User.objects.get(email='none@none.com') | |
190 | + self.user.sections.add(self.section) | |
191 | + self.user.save() | |
192 | + self.flashcard = Flashcard(text="jason", | |
193 | + section=self.section, | |
194 | + material_date=now(), | |
195 | + author=self.user) | |
188 | 196 | self.flashcard.save() |
197 | + self.flashcard.add_to_deck(self.user) | |
189 | 198 | |
190 | 199 | def test_edit_flashcard(self): |
191 | 200 | self.client.login(email='none@none.com', password='1234') |
192 | - user = User.objects.get(email='none@none.com') | |
193 | - user.sections.add(Section.objects.get(pk=1)) | |
194 | - user.save() | |
201 | + user = self.user | |
202 | + flashcard = self.flashcard | |
203 | + url = "/api/flashcards/{}/".format(flashcard.pk) | |
204 | + data = {'text': 'new wow for the flashcard', | |
205 | + 'mask': '[[0,4]]'} | |
206 | + self.assertNotEqual(flashcard.text, data['text']) | |
207 | + response = self.client.patch(url, data, format='json') | |
208 | + self.assertEqual(response.status_code, HTTP_200_OK) | |
209 | + self.assertEqual(response.data['text'], data['text']) | |
210 | + data = {'material_date': datetime(2015, 4, 12, 2, 2, 2), | |
211 | + 'mask': '[[1, 3]]'} | |
212 | + user2 = User.objects.create(email='wow@wow.wow', password='wow') | |
213 | + user2.sections.add(self.section) | |
214 | + user2.save() | |
215 | + UserFlashcard.objects.create(user=user2, flashcard=flashcard).save() | |
216 | + response = self.client.patch(url, data, format='json') | |
217 | + serializer = FlashcardSerializer(data=response.data) | |
218 | + serializer.is_valid(raise_exception=True) | |
219 | + self.assertEqual(response.status_code, HTTP_200_OK) | |
220 | + # self.assertEqual(serializer.validated_data['material_date'], utc.localize(data['material_date'])) | |
221 | + self.assertEqual(serializer.validated_data['mask'], FlashcardMask([[1, 3]])) | |
222 | + data = {'mask': '[[3,6]]'} | |
223 | + response = self.client.patch(url, data, format='json') | |
224 | + user_flashcard = UserFlashcard.objects.get(user=user, flashcard=flashcard) | |
225 | + self.assertEqual(response.status_code, HTTP_200_OK) | |
226 | + self.assertEqual(user_flashcard.mask, FlashcardMask([[3, 6]])) | |
195 | 227 | |
196 | 228 | def test_create_flashcard(self): |
197 | 229 | self.client.login(email='none@none.com', password='1234') |
... | ... | @@ -291,7 +323,10 @@ |
291 | 323 | self.assertEqual(response.status_code, HTTP_200_OK) |
292 | 324 | |
293 | 325 | def test_section_feed(self): |
294 | - response = self.client.get('/api/sections/1/feed/') | |
326 | + Flashcard.objects.create(author=self.user, material_date=datetime.now(), | |
327 | + text='wow', section=self.section, | |
328 | + mask=None).save() | |
329 | + response = self.client.get('/api/sections/{}/feed/'.format(self.section.pk)) | |
295 | 330 | self.assertEqual(response.status_code, HTTP_200_OK) |
296 | 331 | self.assertEqual(response.data[0]['id'], 1) |
297 | 332 |
flashcards/views.py
View file @
5d861cb
... | ... | @@ -13,7 +13,7 @@ |
13 | 13 | from django.core.mail import send_mail |
14 | 14 | from django.contrib.auth import authenticate |
15 | 15 | from django.contrib.auth.tokens import default_token_generator |
16 | -from rest_framework.status import HTTP_204_NO_CONTENT, HTTP_201_CREATED | |
16 | +from rest_framework.status import HTTP_204_NO_CONTENT, HTTP_201_CREATED, HTTP_200_OK | |
17 | 17 | from rest_framework.response import Response |
18 | 18 | from rest_framework.exceptions import AuthenticationFailed, NotAuthenticated, ValidationError, PermissionDenied |
19 | 19 | from simple_email_confirmation import EmailAddress |
20 | 20 | |
21 | 21 | |
... | ... | @@ -378,14 +378,17 @@ |
378 | 378 | data.is_valid(raise_exception=True) |
379 | 379 | new_flashcard = data.validated_data |
380 | 380 | |
381 | - flashcard.edit(user, new_flashcard) | |
382 | - user_card, created = UserFlashcard.objects.get_or_create(user=user, flashcard=flashcard) | |
383 | - user_card.mask = flashcard.mask | |
381 | + new_card = flashcard.edit(user, new_flashcard) | |
382 | + if new_card: | |
383 | + user_card = flashcard.add_to_deck(user) | |
384 | + else: | |
385 | + user_card, created = UserFlashcard.objects.get_or_create(user=user, flashcard=flashcard) | |
386 | + if created and ('mask' not in new_flashcard): | |
387 | + user_card.save() | |
384 | 388 | |
385 | 389 | if 'mask' in new_flashcard: |
386 | 390 | user_card.mask = new_flashcard['mask'] |
387 | - if 'mask' in new_flashcard or created: | |
388 | 391 | user_card.save() |
389 | 392 | |
390 | - return Response(status=HTTP_204_NO_CONTENT) | |
393 | + return Response(FlashcardSerializer(flashcard).data, status=HTTP_200_OK) |