Commit 7dbe162dc57d7006af95ef97f8d08bfa014727fa
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
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 |