Commit 7dbe162dc57d7006af95ef97f8d08bfa014727fa

Authored by Andrew Buss
Exists in master

merge

Showing 6 changed files Side-by-side Diff

flashcards/models.py View file @ 7dbe162
... ... @@ -50,7 +50,7 @@
50 50 sections = ManyToManyField('Section', help_text="The sections which the user is enrolled in")
51 51  
52 52 def is_in_section(self, section):
53   - return section in self.sections.all()
  53 + return self.sections.filter(pk=section.pk).exists()
54 54  
55 55 def pull(self, flashcard):
56 56 if not self.is_in_section(flashcard.section):
57 57  
... ... @@ -59,7 +59,12 @@
59 59 user_card.pulled = datetime.now()
60 60 user_card.save()
61 61  
  62 + def get_deck(self, section):
  63 + if not self.is_in_section(section):
  64 + raise ObjectDoesNotExist("User not enrolled in section")
  65 + return Flashcard.objects.all().filter(userflashcard__user=self).filter(section=section)
62 66  
  67 +
63 68 class UserFlashcard(Model):
64 69 """
65 70 Represents the relationship between a user and a flashcard by:
... ... @@ -153,6 +158,7 @@
153 158 self.pk = None
154 159 if 'mask' in new_flashcard:
155 160 self.mask = new_flashcard['mask']
  161 + self.save()
156 162  
157 163 @classmethod
158 164 def cards_visible_to(cls, user):
flashcards/serializers.py View file @ 7dbe162
... ... @@ -155,7 +155,7 @@
155 155 return value
156 156  
157 157 def validate_mask(self, value):
158   - if value.max_offset() >= len(self.text):
  158 + if len(self.data['text']) < value.max_offset():
159 159 raise serializers.ValidationError("Mask out of bounds")
160 160 return value
161 161  
flashcards/tests/test_api.py View file @ 7dbe162
... ... @@ -200,7 +200,8 @@
200 200 def setUp(self):
201 201 self.client.login(email='none@none.com', password='1234')
202 202 self.user = User.objects.get(email='none@none.com')
203   - flashcard = Flashcard(text="jason", section=Section.objects.get(pk=1), material_date=now(), author=self.user)
  203 + self.section = Section.objects.get(pk=1)
  204 + flashcard = Flashcard(text="jason", section=self.section, material_date=now(), author=self.user)
204 205 flashcard.save()
205 206  
206 207 def test_list_sections(self):
... ... @@ -208,7 +209,7 @@
208 209 self.assertEqual(response.status_code, HTTP_200_OK)
209 210  
210 211 def test_section_enroll(self):
211   - section = Section.objects.get(pk=1)
  212 + section = self.section
212 213 self.assertFalse(self.user.sections.filter(pk=section.pk))
213 214  
214 215 # test enrolling in a section without a whitelist
... ... @@ -232,7 +233,7 @@
232 233 self.assertTrue(self.user.sections.filter(pk=section.pk).exists())
233 234  
234 235 def test_section_drop(self):
235   - section = Section.objects.get(pk=3)
  236 + section = self.section
236 237  
237 238 # test dropping a section that the user isn't in
238 239 response = self.client.post('/api/sections/%d/drop/' % section.pk)
... ... @@ -255,6 +256,8 @@
255 256 self.assertEqual(response.status_code, HTTP_200_OK)
256 257  
257 258 def test_section_deck(self):
  259 + self.user.sections.add(self.section)
  260 + self.user.save()
258 261 response = self.client.get('/api/sections/1/deck/')
259 262 self.assertEqual(response.status_code, HTTP_200_OK)
260 263  
... ... @@ -263,4 +266,8 @@
263 266 self.assertEqual(response.status_code, HTTP_200_OK)
264 267 print response.data
265 268 self.assertEqual(response.data, {})
  269 +
  270 + def test_section_ordered_deck(self):
  271 + response = self.client.get('/api/sections/1/ordered_deck/')
  272 + self.assertEqual(response.status_code, HTTP_200_OK)
flashcards/tests/test_models.py View file @ 7dbe162
1 1 from datetime import datetime
2 2  
3 3 from django.test import TestCase
4   -from flashcards.models import User, Section, Flashcard
5   -from flashcards.validators import OverlapIntervalException
  4 +from flashcards.models import User, Section, Flashcard, UserFlashcard
  5 +from flashcards.validators import FlashcardMask, OverlapIntervalException
6 6  
7 7  
8 8 class RegistrationTests(TestCase):
9 9  
10 10  
11 11  
12 12  
13 13  
... ... @@ -37,24 +37,87 @@
37 37 self.assertEqual(user.sections.count(), 0)
38 38  
39 39  
  40 +class FlashcardMaskTest(TestCase):
  41 + def test_iterable(self):
  42 + try:
  43 + FlashcardMask(1)
  44 + except TypeError as te:
  45 + self.assertEqual(te.message, "Interval not a valid iterable")
  46 + try:
  47 + FlashcardMask([1, 2, 4])
  48 + except TypeError as te:
  49 + self.assertEqual(te.message, "Interval not a valid iterable")
  50 +
  51 + def test_interval(self):
  52 + try:
  53 + FlashcardMask([[1, 2, 3], [1]])
  54 + except TypeError as te:
  55 + self.assertEqual(te.message, "Intervals must have exactly 2 elements, begin and end")
  56 + try:
  57 + FlashcardMask([[1, 2], [1, 2, 4]])
  58 + except TypeError as te:
  59 + self.assertEqual(te.message, "Intervals must have exactly 2 elements, begin and end")
  60 + try:
  61 + FlashcardMask(([1, 2], [1]))
  62 + except TypeError as te:
  63 + self.assertEqual(te.message, "Intervals must have exactly 2 elements, begin and end")
  64 + try:
  65 + FlashcardMask("[1,2,3]")
  66 + except TypeError as te:
  67 + self.assertEqual(te.message, "Intervals must have exactly 2 elements, begin and end")
  68 +
  69 + def test_overlap(self):
  70 + try:
  71 + FlashcardMask({(1, 2), (2, 5)})
  72 + except OverlapIntervalException as oie:
  73 + self.assertEqual(oie.message, "Invalid interval offsets in the mask")
  74 + try:
  75 + FlashcardMask({(1, 20), (12, 15)})
  76 + except OverlapIntervalException as oie:
  77 + self.assertEqual(oie.message, "Invalid interval offsets in the mask")
  78 + try:
  79 + FlashcardMask({(2, 1), (5, 2)})
  80 + except OverlapIntervalException as oie:
  81 + self.assertEqual(oie.message, "Invalid interval offsets in the mask")
  82 +
  83 +
  84 +
40 85 class FlashcardTests(TestCase):
41 86 def setUp(self):
42   - user = User.objects.create_user(email="none@none.com", password="1234")
43 87 section = Section.objects.create(department='dept',
44 88 course_num='101a',
45 89 course_title='how 2 test',
46 90 instructor='George Lucas',
47 91 quarter='SP15')
48   - Flashcard.objects.create(text="This is the text of the Flashcard",
49   - section=section,
50   - author=user,
51   - material_date=datetime.now(),
52   - previous=None,
53   - mask={(24,34), (0, 4)})
  92 + user = User.objects.create_user(email="none@none.com", password="1234")
  93 + user.sections.add(section)
  94 + flashcard = Flashcard.objects.create(text="This is the text of the Flashcard",
  95 + section=section,
  96 + author=user,
  97 + material_date=datetime.now(),
  98 + previous=None,
  99 + mask={(24,34), (0, 4)})
  100 + user.save()
  101 + section.save()
  102 + flashcard.save()
54 103  
55   - def test_mask_field(self):
  104 + def test_flashcard_edit(self):
56 105 user = User.objects.get(email="none@none.com")
  106 + user2 = User.objects.create_user(email="wow@wow.com", password="wow")
57 107 section = Section.objects.get(course_title='how 2 test')
  108 + user2.sections.add(section)
  109 + user2.save()
  110 + flashcard = Flashcard.objects.filter(author=user).get(text="This is the text of the Flashcard")
  111 + pk_backup = flashcard.pk
  112 + self.assertTrue(user.is_in_section(section))
  113 + flashcard.edit(user, {})
  114 + self.assertIsNotNone(flashcard.pk)
  115 + UserFlashcard.objects.create(user=user2, flashcard=flashcard).save()
  116 + flashcard.edit(user2, {'text': 'This is the new text'})
  117 + self.assertNotEqual(flashcard.pk, pk_backup)
  118 +
  119 + def test_mask_field(self):
  120 + user = User.objects.get(email="none@none.com")
58 121 flashcard = Flashcard.objects.filter(author=user).get(text="This is the text of the Flashcard")
59 122 self.assertTrue(isinstance(flashcard.mask, set))
60 123 self.assertTrue(all([isinstance(interval, tuple) for interval in flashcard.mask]))
flashcards/validators.py View file @ 7dbe162
... ... @@ -3,17 +3,17 @@
3 3  
4 4 class FlashcardMask(set):
5 5 def __init__(self, iterable, *args, **kwargs):
  6 + self._iterable_check(iterable)
6 7 iterable = map(tuple, iterable)
7 8 super(FlashcardMask, self).__init__(iterable, *args, **kwargs)
8   - self._iterable_check()
9 9 self._interval_check()
10 10 self._overlap_check()
11 11  
12 12 def max_offset(self):
13 13 return self._end
14 14  
15   - def _iterable_check(self):
16   - if not all([isinstance(i, Iterable) for i in self]):
  15 + def _iterable_check(self, iterable):
  16 + if not isinstance(iterable, Iterable) or not all([isinstance(i, Iterable) for i in iterable]):
17 17 raise TypeError("Interval not a valid iterable")
18 18  
19 19 def _interval_check(self):
20 20  
21 21  
22 22  
... ... @@ -27,14 +27,14 @@
27 27 if not (0 <= beg <= 255) or not (0 <= end <= 255) or not (beg <= end) or not (beg > p_end):
28 28 raise OverlapIntervalException((beg, end), "Invalid interval offsets in the mask")
29 29 p_beg, p_end = beg, end
30   - self._end = end
  30 + self._end = p_end
31 31  
32 32  
33 33 class OverlapIntervalException(Exception):
34   - def __init__(self, interval, reason):
  34 + def __init__(self, interval, message):
35 35 self.interval = interval
36   - self.reason = reason
  36 + self.message = message
37 37  
38 38 def __str__(self):
39   - return repr(self.reason) + ': ' + repr(self.interval)
  39 + return repr(self.message) + ': ' + repr(self.interval)
flashcards/views.py View file @ 7dbe162
... ... @@ -87,12 +87,20 @@
87 87 """
88 88 Gets the contents of a user's deck for a given section.
89 89 """
90   - qs = Flashcard.objects.all()
91   - qs = qs.filter(userflashcard__user=request.user)
92   - qs = qs.filter(section=self.get_object())
  90 + qs = request.user.get_deck(self.get_object())
93 91 serializer = FlashcardSerializer(qs, many=True)
94 92 return Response(serializer.data)
95 93  
  94 + @detail_route(methods=['get'], permission_classes=[IsAuthenticated])
  95 + def ordered_deck(self, request, pk):
  96 + """
  97 + Get a chronological order by material_date of flashcards for a section.
  98 + This excludes hidden card.
  99 + """
  100 + qs = request.user.get_deck(self.get_object()).order_by('-material_date')
  101 + serializer = FlashcardSerializer(qs, many=True)
  102 + return Response(serializer.data)
  103 +
96 104 @detail_route(methods=['GET'])
97 105 def feed(self, request, pk):
98 106 """
... ... @@ -331,7 +339,6 @@
331 339 new_flashcard = data.validated_data
332 340  
333 341 flashcard.edit(user, new_flashcard)
334   - flashcard.save()
335 342 user_card, created = UserFlashcard.objects.get_or_create(user=user, flashcard=flashcard)
336 343 user_card.mask = flashcard.mask
337 344