Commit 2958a1827ea8e98f73b9ba9edff502bdf62d9398
1 parent
ea351c696e
Exists in
master
cleaned up some docstrings; compacted views
Showing 7 changed files with 418 additions and 82 deletions Side-by-side Diff
flashcards/api.py
View file @
2958a18
flashcards/migrations/0001_squashed_0015_auto_20150518_0017.py
View file @
2958a18
1 | +# -*- coding: utf-8 -*- | |
2 | +from __future__ import unicode_literals | |
3 | + | |
4 | +from django.db import models, migrations | |
5 | +import django.contrib.auth.models | |
6 | +import django.utils.timezone | |
7 | +from django.conf import settings | |
8 | +import django.core.validators | |
9 | +import simple_email_confirmation.models | |
10 | +import flashcards.models | |
11 | +import flashcards.fields | |
12 | + | |
13 | + | |
14 | +class Migration(migrations.Migration): | |
15 | + | |
16 | + replaces = [(b'flashcards', '0001_initial'), (b'flashcards', '0002_auto_20150504_1327'), (b'flashcards', '0003_auto_20150504_1600'), (b'flashcards', '0004_auto_20150506_1443'), (b'flashcards', '0005_auto_20150510_1458'), (b'flashcards', '0006_auto_20150512_0042'), (b'flashcards', '0007_userflashcard_mask'), (b'flashcards', '0008_section_department_abbreviation'), (b'flashcards', '0009_auto_20150512_0318'), (b'flashcards', '0010_auto_20150513_1546'), (b'flashcards', '0011_auto_20150514_0207'), (b'flashcards', '0012_auto_20150516_0313'), (b'flashcards', '0013_auto_20150517_0402'), (b'flashcards', '0013_auto_20150516_2356'), (b'flashcards', '0014_merge'), (b'flashcards', '0015_auto_20150518_0017')] | |
17 | + | |
18 | + dependencies = [ | |
19 | + ('auth', '__latest__'), | |
20 | + ] | |
21 | + | |
22 | + operations = [ | |
23 | + migrations.CreateModel( | |
24 | + name='User', | |
25 | + fields=[ | |
26 | + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | |
27 | + ('password', models.CharField(max_length=128, verbose_name='password')), | |
28 | + ('last_login', models.DateTimeField(null=True, verbose_name='last login', blank=True)), | |
29 | + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), | |
30 | + ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, max_length=30, validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.', 'invalid')], help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', unique=True, verbose_name='username')), | |
31 | + ('first_name', models.CharField(max_length=30, verbose_name='first name', blank=True)), | |
32 | + ('last_name', models.CharField(max_length=30, verbose_name='last name', blank=True)), | |
33 | + ('email', models.EmailField(unique=True, max_length=254, verbose_name='email address', blank=True)), | |
34 | + ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), | |
35 | + ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), | |
36 | + ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), | |
37 | + ('groups', models.ManyToManyField(related_query_name='user', related_name='user_set', to=b'auth.Group', blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', verbose_name='groups')), | |
38 | + ], | |
39 | + options={ | |
40 | + 'abstract': False, | |
41 | + 'verbose_name': 'user', | |
42 | + 'verbose_name_plural': 'users', | |
43 | + }, | |
44 | + bases=(models.Model, simple_email_confirmation.models.SimpleEmailConfirmationUserMixin), | |
45 | + managers=[ | |
46 | + ('objects', django.contrib.auth.models.UserManager()), | |
47 | + ], | |
48 | + ), | |
49 | + migrations.CreateModel( | |
50 | + name='Flashcard', | |
51 | + fields=[ | |
52 | + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | |
53 | + ('text', models.CharField(help_text=b'The text on the card', max_length=255)), | |
54 | + ('pushed', models.DateTimeField(help_text=b'When the card was first pushed', auto_now_add=True)), | |
55 | + ('material_date', models.DateTimeField(help_text=b'The date with which the card is associated')), | |
56 | + ('is_hidden', models.BooleanField(default=False)), | |
57 | + ('hide_reason', models.CharField(help_text=b'Reason for hiding this card', max_length=255, blank=True)), | |
58 | + ('author', models.ForeignKey(to=settings.AUTH_USER_MODEL)), | |
59 | + ], | |
60 | + options={ | |
61 | + 'ordering': ['-pushed'], | |
62 | + }, | |
63 | + ), | |
64 | + migrations.CreateModel( | |
65 | + name='FlashcardMask', | |
66 | + fields=[ | |
67 | + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | |
68 | + ('ranges', models.CharField(max_length=255)), | |
69 | + ], | |
70 | + ), | |
71 | + migrations.CreateModel( | |
72 | + name='FlashcardReport', | |
73 | + fields=[ | |
74 | + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | |
75 | + ('reason', models.CharField(max_length=255)), | |
76 | + ('flashcard', models.ForeignKey(to='flashcards.Flashcard')), | |
77 | + ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), | |
78 | + ], | |
79 | + ), | |
80 | + migrations.CreateModel( | |
81 | + name='LecturePeriod', | |
82 | + fields=[ | |
83 | + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | |
84 | + ('week_day', models.IntegerField(help_text=b'0-indexed day of week, starting at Monday')), | |
85 | + ('start_time', models.TimeField()), | |
86 | + ('end_time', models.TimeField()), | |
87 | + ], | |
88 | + ), | |
89 | + migrations.CreateModel( | |
90 | + name='Section', | |
91 | + fields=[ | |
92 | + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | |
93 | + ('department', models.CharField(max_length=50)), | |
94 | + ('course_num', models.CharField(max_length=6)), | |
95 | + ('course_title', models.CharField(max_length=50)), | |
96 | + ('instructor', models.CharField(max_length=50)), | |
97 | + ('quarter', models.CharField(max_length=4)), | |
98 | + ('whitelist', models.ManyToManyField(related_name='whitelisted_sections', to=settings.AUTH_USER_MODEL)), | |
99 | + ], | |
100 | + options={ | |
101 | + 'ordering': ['-quarter'], | |
102 | + }, | |
103 | + ), | |
104 | + migrations.CreateModel( | |
105 | + name='UserFlashcard', | |
106 | + fields=[ | |
107 | + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | |
108 | + ('pulled', models.DateTimeField(help_text=b'When the user pulled the card', null=True, blank=True)), | |
109 | + ('unpulled', models.DateTimeField(help_text=b'When the user unpulled this card', null=True, blank=True)), | |
110 | + ('flashcard', models.ForeignKey(to='flashcards.Flashcard')), | |
111 | + ('mask', models.ForeignKey(help_text=b"A mask which overrides the card's mask", to='flashcards.FlashcardMask')), | |
112 | + ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), | |
113 | + ], | |
114 | + options={ | |
115 | + 'ordering': ['-pulled'], | |
116 | + }, | |
117 | + ), | |
118 | + migrations.CreateModel( | |
119 | + name='WhitelistedAddress', | |
120 | + fields=[ | |
121 | + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | |
122 | + ('email', models.EmailField(max_length=254)), | |
123 | + ('section', models.ForeignKey(related_name='whitelist', to='flashcards.Section')), | |
124 | + ], | |
125 | + ), | |
126 | + migrations.AddField( | |
127 | + model_name='lectureperiod', | |
128 | + name='section', | |
129 | + field=models.ForeignKey(to='flashcards.Section'), | |
130 | + ), | |
131 | + migrations.AddField( | |
132 | + model_name='flashcard', | |
133 | + name='previous', | |
134 | + field=models.ForeignKey(blank=True, to='flashcards.Flashcard', help_text=b'The previous version of this card, if one exists', null=True), | |
135 | + ), | |
136 | + migrations.AddField( | |
137 | + model_name='flashcard', | |
138 | + name='section', | |
139 | + field=models.ForeignKey(help_text=b'The section with which the card is associated', to='flashcards.Section'), | |
140 | + ), | |
141 | + migrations.AddField( | |
142 | + model_name='user', | |
143 | + name='sections', | |
144 | + field=models.ManyToManyField(to=b'flashcards.Section'), | |
145 | + ), | |
146 | + migrations.AddField( | |
147 | + model_name='user', | |
148 | + name='user_permissions', | |
149 | + field=models.ManyToManyField(related_query_name='user', related_name='user_set', to=b'auth.Permission', blank=True, help_text='Specific permissions for this user.', verbose_name='user permissions'), | |
150 | + ), | |
151 | + migrations.AlterUniqueTogether( | |
152 | + name='userflashcard', | |
153 | + unique_together=set([('user', 'flashcard')]), | |
154 | + ), | |
155 | + migrations.AlterIndexTogether( | |
156 | + name='userflashcard', | |
157 | + index_together=set([('user', 'flashcard')]), | |
158 | + ), | |
159 | + migrations.AlterUniqueTogether( | |
160 | + name='section', | |
161 | + unique_together=set([('department', 'course_num', 'quarter', 'instructor')]), | |
162 | + ), | |
163 | + migrations.AlterUniqueTogether( | |
164 | + name='lectureperiod', | |
165 | + unique_together=set([('section', 'start_time', 'week_day')]), | |
166 | + ), | |
167 | + migrations.AlterUniqueTogether( | |
168 | + name='flashcardreport', | |
169 | + unique_together=set([('user', 'flashcard')]), | |
170 | + ), | |
171 | + migrations.AlterModelOptions( | |
172 | + name='section', | |
173 | + options={}, | |
174 | + ), | |
175 | + migrations.AlterModelManagers( | |
176 | + name='user', | |
177 | + managers=[ | |
178 | + ('objects', flashcards.models.EmailOnlyUserManager()), | |
179 | + ], | |
180 | + ), | |
181 | + migrations.AlterField( | |
182 | + model_name='flashcardreport', | |
183 | + name='reason', | |
184 | + field=models.CharField(max_length=255, blank=True), | |
185 | + ), | |
186 | + migrations.AlterField( | |
187 | + model_name='lectureperiod', | |
188 | + name='week_day', | |
189 | + field=models.IntegerField(help_text=b'1-indexed day of week, starting at Sunday'), | |
190 | + ), | |
191 | + migrations.AlterField( | |
192 | + model_name='user', | |
193 | + name='sections', | |
194 | + field=models.ManyToManyField(help_text=b'The sections which the user is enrolled in', to=b'flashcards.Section'), | |
195 | + ), | |
196 | + migrations.AlterField( | |
197 | + model_name='user', | |
198 | + name='username', | |
199 | + field=models.CharField(help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=30, error_messages={'unique': 'A user with that username already exists.'}, verbose_name='username', validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.', 'invalid')]), | |
200 | + ), | |
201 | + migrations.AlterField( | |
202 | + model_name='section', | |
203 | + name='instructor', | |
204 | + field=models.CharField(max_length=100), | |
205 | + ), | |
206 | + migrations.AlterField( | |
207 | + model_name='userflashcard', | |
208 | + name='mask', | |
209 | + field=models.ForeignKey(blank=True, to='flashcards.FlashcardMask', help_text=b"A mask which overrides the card's mask", null=True), | |
210 | + ), | |
211 | + migrations.CreateModel( | |
212 | + name='UserFlashcardQuiz', | |
213 | + fields=[ | |
214 | + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | |
215 | + ('when', models.DateTimeField(auto_now=True)), | |
216 | + ('blanked_word', models.CharField(help_text=b'The character range which was blanked', max_length=8, blank=True)), | |
217 | + ('response', models.CharField(help_text=b"The user's response", max_length=255, null=True, blank=True)), | |
218 | + ('correct', models.NullBooleanField(help_text=b"The user's self-evaluation of their response")), | |
219 | + ('user_flashcard', models.ForeignKey(to='flashcards.UserFlashcard')), | |
220 | + ], | |
221 | + ), | |
222 | + migrations.AlterModelOptions( | |
223 | + name='section', | |
224 | + options={'ordering': ['-course_title']}, | |
225 | + ), | |
226 | + migrations.AlterUniqueTogether( | |
227 | + name='section', | |
228 | + unique_together=set([]), | |
229 | + ), | |
230 | + migrations.RemoveField( | |
231 | + model_name='section', | |
232 | + name='whitelist', | |
233 | + ), | |
234 | + migrations.RemoveField( | |
235 | + model_name='userflashcard', | |
236 | + name='mask', | |
237 | + ), | |
238 | + migrations.DeleteModel( | |
239 | + name='FlashcardMask', | |
240 | + ), | |
241 | + migrations.AddField( | |
242 | + model_name='flashcard', | |
243 | + name='mask', | |
244 | + field=flashcards.fields.MaskField(blank_sep=b',', range_sep=b'-', max_length=255, blank=True, help_text=b'The mask on the card', null=True), | |
245 | + ), | |
246 | + migrations.AddField( | |
247 | + model_name='userflashcard', | |
248 | + name='mask', | |
249 | + field=flashcards.fields.MaskField(default=None, blank_sep=b',', range_sep=b'-', max_length=255, blank=True, help_text=b'The user-specific mask on the card', null=True), | |
250 | + ), | |
251 | + migrations.AddField( | |
252 | + model_name='section', | |
253 | + name='department_abbreviation', | |
254 | + field=models.CharField(max_length=10, db_index=True), | |
255 | + ), | |
256 | + migrations.AlterField( | |
257 | + model_name='section', | |
258 | + name='course_num', | |
259 | + field=models.CharField(max_length=6, db_index=True), | |
260 | + ), | |
261 | + migrations.AlterField( | |
262 | + model_name='section', | |
263 | + name='course_title', | |
264 | + field=models.CharField(max_length=50, db_index=True), | |
265 | + ), | |
266 | + migrations.AlterField( | |
267 | + model_name='section', | |
268 | + name='department', | |
269 | + field=models.CharField(max_length=50, db_index=True), | |
270 | + ), | |
271 | + migrations.AlterField( | |
272 | + model_name='section', | |
273 | + name='instructor', | |
274 | + field=models.CharField(max_length=100, db_index=True), | |
275 | + ), | |
276 | + migrations.AlterField( | |
277 | + model_name='section', | |
278 | + name='quarter', | |
279 | + field=models.CharField(max_length=4, db_index=True), | |
280 | + ), | |
281 | + migrations.AlterField( | |
282 | + model_name='flashcard', | |
283 | + name='material_date', | |
284 | + field=models.DateTimeField(default=django.utils.timezone.now, help_text=b'The date with which the card is associated'), | |
285 | + ), | |
286 | + migrations.AlterModelOptions( | |
287 | + name='lectureperiod', | |
288 | + options={'ordering': ['section', 'week_day']}, | |
289 | + ), | |
290 | + migrations.AlterField( | |
291 | + model_name='userflashcard', | |
292 | + name='pulled', | |
293 | + field=models.DateTimeField(default=None, help_text=b'When the user pulled the card', null=True, blank=True), | |
294 | + ), | |
295 | + migrations.RemoveField( | |
296 | + model_name='userflashcard', | |
297 | + name='unpulled', | |
298 | + ), | |
299 | + migrations.CreateModel( | |
300 | + name='FlashcardHide', | |
301 | + fields=[ | |
302 | + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | |
303 | + ('reason', models.CharField(max_length=255, null=True, blank=True)), | |
304 | + ('hidden', models.DateTimeField(auto_now_add=True)), | |
305 | + ('flashcard', models.ForeignKey(to='flashcards.Flashcard')), | |
306 | + ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), | |
307 | + ], | |
308 | + ), | |
309 | + migrations.AlterUniqueTogether( | |
310 | + name='flashcardreport', | |
311 | + unique_together=set([]), | |
312 | + ), | |
313 | + migrations.RemoveField( | |
314 | + model_name='flashcardreport', | |
315 | + name='flashcard', | |
316 | + ), | |
317 | + migrations.RemoveField( | |
318 | + model_name='flashcardreport', | |
319 | + name='user', | |
320 | + ), | |
321 | + migrations.DeleteModel( | |
322 | + name='FlashcardReport', | |
323 | + ), | |
324 | + migrations.AlterUniqueTogether( | |
325 | + name='flashcardhide', | |
326 | + unique_together=set([('user', 'flashcard')]), | |
327 | + ), | |
328 | + migrations.AlterIndexTogether( | |
329 | + name='flashcardhide', | |
330 | + index_together=set([('user', 'flashcard')]), | |
331 | + ), | |
332 | + migrations.AlterField( | |
333 | + model_name='flashcard', | |
334 | + name='hide_reason', | |
335 | + field=models.CharField(default=None, help_text=b'Reason for hiding this card', max_length=255, blank=True), | |
336 | + ), | |
337 | + migrations.AlterField( | |
338 | + model_name='flashcard', | |
339 | + name='previous', | |
340 | + field=models.ForeignKey(default=None, blank=True, to='flashcards.Flashcard', help_text=b'The previous version of this card, if one exists', null=True), | |
341 | + ), | |
342 | + migrations.AlterField( | |
343 | + model_name='flashcard', | |
344 | + name='hide_reason', | |
345 | + field=models.CharField(default=None, max_length=255, null=True, help_text=b'Reason for hiding this card', blank=True), | |
346 | + ), | |
347 | + migrations.AlterField( | |
348 | + model_name='flashcard', | |
349 | + name='previous', | |
350 | + field=models.ForeignKey(default=None, blank=True, to='flashcards.Flashcard', help_text=b'The previous version of this card, if one exists', null=True), | |
351 | + ), | |
352 | + migrations.AlterField( | |
353 | + model_name='flashcard', | |
354 | + name='hide_reason', | |
355 | + field=models.CharField(default=b'', max_length=255, null=True, help_text=b'Reason for hiding this card', blank=True), | |
356 | + ), | |
357 | + ] |
flashcards/migrations/0015_auto_20150518_0017.py
View file @
2958a18
1 | +# -*- coding: utf-8 -*- | |
2 | +from __future__ import unicode_literals | |
3 | + | |
4 | +from django.db import models, migrations | |
5 | + | |
6 | + | |
7 | +class Migration(migrations.Migration): | |
8 | + | |
9 | + dependencies = [ | |
10 | + ('flashcards', '0014_merge'), | |
11 | + ] | |
12 | + | |
13 | + operations = [ | |
14 | + migrations.AlterField( | |
15 | + model_name='flashcard', | |
16 | + name='hide_reason', | |
17 | + field=models.CharField(default=b'', max_length=255, null=True, help_text=b'Reason for hiding this card', blank=True), | |
18 | + ), | |
19 | + ] |
flashcards/models.py
View file @
2958a18
... | ... | @@ -12,7 +12,6 @@ |
12 | 12 | AbstractUser._meta.get_field('email')._unique = True |
13 | 13 | AbstractUser._meta.get_field('username')._unique = False |
14 | 14 | |
15 | - | |
16 | 15 | class EmailOnlyUserManager(UserManager): |
17 | 16 | """ |
18 | 17 | A tiny extension of Django's UserManager which correctly creates users |
19 | 18 | |
... | ... | @@ -143,10 +142,9 @@ |
143 | 142 | if self.is_hidden or self.flashcardhide_set.filter(user=user).exists(): return True |
144 | 143 | return False |
145 | 144 | |
146 | - | |
147 | - def hide_from(self, user): | |
145 | + def hide_from(self, user, reason=None): | |
148 | 146 | if self.is_in_deck(user): user.unpull(self) |
149 | - obj, created = FlashcardHide.objects.get_or_create(user=user, flashcard=self) | |
147 | + obj, created = FlashcardHide.objects.get_or_create(user=user, flashcard=self, reason=None) | |
150 | 148 | if not created: |
151 | 149 | raise ValidationError("The card has already been hidden.") |
152 | 150 | obj.save() |
... | ... | @@ -308,6 +306,9 @@ |
308 | 306 | def get_feed_for_user(self, user): |
309 | 307 | qs = Flashcard.objects.filter(section=self).exclude(userflashcard__user=user).order_by('pushed') |
310 | 308 | return qs |
309 | + | |
310 | + def get_cards_for_user(self, user): | |
311 | + return Flashcard.cards_visible_to(user).filter(section=self) | |
311 | 312 | |
312 | 313 | def __unicode__(self): |
313 | 314 | return '%s %s: %s (%s %s)' % ( |
flashcards/serializers.py
View file @
2958a18
1 | +from json import dumps, loads | |
2 | + | |
1 | 3 | from django.utils.datetime_safe import datetime |
2 | 4 | from django.utils.timezone import now |
3 | 5 | import pytz |
... | ... | @@ -8,7 +10,6 @@ |
8 | 10 | from rest_framework.relations import HyperlinkedRelatedField |
9 | 11 | from rest_framework.serializers import ModelSerializer, Serializer |
10 | 12 | from rest_framework.validators import UniqueValidator |
11 | -from json import dumps, loads | |
12 | 13 | |
13 | 14 | |
14 | 15 | class EmailSerializer(Serializer): |
15 | 16 | |
... | ... | @@ -132,28 +133,11 @@ |
132 | 133 | else: |
133 | 134 | raise serializers.ValidationError("Material date is outside allowed range for this quarter") |
134 | 135 | |
135 | - def validate_previous(self, value): | |
136 | - if value is None: | |
137 | - return value | |
138 | - if Flashcard.objects.filter(pk=value.pk).count() > 0: | |
139 | - return value | |
140 | - raise serializers.ValidationError("Invalid previous Flashcard object") | |
141 | - | |
142 | 136 | def validate_pushed(self, value): |
143 | 137 | if value > datetime.now(): |
144 | 138 | raise serializers.ValidationError("Invalid creation date for the Flashcard") |
145 | 139 | return value |
146 | 140 | |
147 | - def validate_text(self, value): | |
148 | - if len(value) > 255: | |
149 | - raise serializers.ValidationError("Flashcard text limit exceeded") | |
150 | - return value | |
151 | - | |
152 | - def validate_hide_reason(self, value): | |
153 | - if len(value) > 255: | |
154 | - raise serializers.ValidationError("Hide reason limit exceeded") | |
155 | - return value | |
156 | - | |
157 | 141 | def validate_mask(self, value): |
158 | 142 | if value is None: |
159 | 143 | return None |
... | ... | @@ -163,7 +147,7 @@ |
163 | 147 | |
164 | 148 | class Meta: |
165 | 149 | model = Flashcard |
166 | - exclude = 'author', | |
150 | + exclude = 'author', 'previous' | |
167 | 151 | |
168 | 152 | |
169 | 153 | class FlashcardUpdateSerializer(serializers.Serializer): |
flashcards/tests/test_api.py
View file @
2958a18
... | ... | @@ -207,7 +207,7 @@ |
207 | 207 | self.inaccessible_flashcard.save() |
208 | 208 | self.flashcard = Flashcard(text="jason", section=self.section, author=self.user) |
209 | 209 | self.flashcard.save() |
210 | - self.flashcard.add_to_deck(self.user) | |
210 | + #self.flashcard.add_to_deck(self.user) | |
211 | 211 | self.client.login(email='none@none.com', password='1234') |
212 | 212 | |
213 | 213 | def test_edit_flashcard(self): |
... | ... | @@ -258,9 +258,6 @@ |
258 | 258 | response = self.client.post('/api/flashcards/%d/hide/' % self.flashcard.id, format='json') |
259 | 259 | self.assertEqual(response.status_code, HTTP_204_NO_CONTENT) |
260 | 260 | self.assertTrue(self.flashcard.is_hidden_from(self.user)) |
261 | - | |
262 | - response = self.client.post('/api/flashcards/%d/hide/' % self.flashcard.id, format='json') | |
263 | - self.assertContains(response, 'The card has already been hidden', status_code=400) | |
264 | 261 | |
265 | 262 | response = self.client.post('/api/flashcards/%d/hide/' % self.inaccessible_flashcard.pk, format='json') |
266 | 263 | # This should fail because the user is not enrolled in section id 2 |
flashcards/views.py
View file @
2958a18
... | ... | @@ -9,7 +9,7 @@ |
9 | 9 | FlashcardUpdateSerializer |
10 | 10 | from rest_framework.decorators import detail_route, permission_classes, api_view, list_route |
11 | 11 | from rest_framework.generics import ListAPIView, GenericAPIView |
12 | -from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin, UpdateModelMixin | |
12 | +from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin | |
13 | 13 | from rest_framework.permissions import IsAuthenticated |
14 | 14 | from rest_framework.viewsets import ReadOnlyModelViewSet, GenericViewSet |
15 | 15 | from django.core.mail import send_mail |
... | ... | @@ -42,11 +42,7 @@ |
42 | 42 | Add the current user to a specified section |
43 | 43 | If the class has a whitelist, but the user is not on the whitelist, the request will fail. |
44 | 44 | --- |
45 | - omit_serializer: true | |
46 | - parameters: | |
47 | - - fake: None | |
48 | - parameters_strategy: | |
49 | - form: replace | |
45 | + view_mocker: flashcards.api.mock_no_params | |
50 | 46 | """ |
51 | 47 | |
52 | 48 | self.get_object().enroll(request.user) |
53 | 49 | |
54 | 50 | |
... | ... | @@ -58,22 +54,26 @@ |
58 | 54 | Remove the current user from a specified section |
59 | 55 | If the user is not in the class, the request will fail. |
60 | 56 | --- |
61 | - omit_serializer: true | |
62 | - parameters: | |
63 | - - fake: None | |
64 | - parameters_strategy: | |
65 | - form: replace | |
57 | + view_mocker: flashcards.api.mock_no_params | |
66 | 58 | """ |
67 | 59 | try: |
68 | 60 | self.get_object().drop(request.user) |
69 | - except django.core.exceptions.PermissionDenied as e: raise PermissionDenied(e) | |
70 | - except django.core.exceptions.ValidationError as e: raise ValidationError(e) | |
61 | + except django.core.exceptions.PermissionDenied as e: | |
62 | + raise PermissionDenied(e) | |
63 | + except django.core.exceptions.ValidationError as e: | |
64 | + raise ValidationError(e) | |
71 | 65 | return Response(status=HTTP_204_NO_CONTENT) |
72 | 66 | |
73 | 67 | @list_route(methods=['GET']) |
74 | 68 | def search(self, request): |
75 | 69 | """ |
76 | 70 | Returns a list of sections which match a user's query |
71 | + --- | |
72 | + parameters: | |
73 | + - name: q | |
74 | + description: space-separated list of terms | |
75 | + required: true | |
76 | + type: form | |
77 | 77 | """ |
78 | 78 | query = request.GET.get('q', None) |
79 | 79 | if not query: return Response('[]') |
... | ... | @@ -277,7 +277,7 @@ |
277 | 277 | return Response(status=HTTP_204_NO_CONTENT) |
278 | 278 | |
279 | 279 | |
280 | -class FlashcardViewSet(GenericViewSet, UpdateModelMixin, CreateModelMixin, RetrieveModelMixin): | |
280 | +class FlashcardViewSet(GenericViewSet, CreateModelMixin, RetrieveModelMixin): | |
281 | 281 | queryset = Flashcard.objects.all() |
282 | 282 | serializer_class = FlashcardSerializer |
283 | 283 | permission_classes = [IsAuthenticated, IsEnrolledInAssociatedSection] |
... | ... | @@ -287,6 +287,8 @@ |
287 | 287 | serializer = FlashcardSerializer(data=request.data) |
288 | 288 | serializer.is_valid(raise_exception=True) |
289 | 289 | data = serializer.validated_data |
290 | + if not request.user.is_in_section(data['section']): | |
291 | + raise PermissionDenied('The user is not enrolled in that section') | |
290 | 292 | data['author'] = request.user |
291 | 293 | flashcard = Flashcard.objects.create(**data) |
292 | 294 | self.perform_create(flashcard) |
293 | 295 | |
294 | 296 | |
... | ... | @@ -294,33 +296,13 @@ |
294 | 296 | response_data = FlashcardSerializer(flashcard) |
295 | 297 | return Response(response_data.data, status=HTTP_201_CREATED, headers=headers) |
296 | 298 | |
297 | - @detail_route(methods=['post']) | |
298 | - def hide(self, request, pk): | |
299 | - """ | |
300 | - Hide a flashcard | |
301 | - --- | |
302 | - omit_serializer: true | |
303 | - parameters: | |
304 | - - fake: None | |
305 | - parameters_strategy: | |
306 | - form: replace | |
307 | - """ | |
308 | - try: | |
309 | - self.get_object().hide_from(request.user) | |
310 | - except django.core.exceptions.ValidationError: | |
311 | - raise ValidationError("The card has already been hidden.") | |
312 | - return Response(status=HTTP_204_NO_CONTENT) | |
313 | 299 | |
314 | 300 | @detail_route(methods=['post']) |
315 | 301 | def unhide(self, request, pk): |
316 | 302 | """ |
317 | - Report the given card | |
303 | + Unhide the given card | |
318 | 304 | --- |
319 | - omit_serializer: true | |
320 | - parameters: | |
321 | - - fake: None | |
322 | - parameters_strategy: | |
323 | - form: replace | |
305 | + view_mocker: flashcards.api.mock_no_params | |
324 | 306 | """ |
325 | 307 | hide = get_object_or_404(FlashcardHide, user=request.user, flashcard=self.get_object()) |
326 | 308 | hide.delete() |
327 | 309 | |
328 | 310 | |
329 | 311 | |
330 | 312 | |
... | ... | @@ -329,27 +311,23 @@ |
329 | 311 | @detail_route(methods=['post']) |
330 | 312 | def report(self, request, pk): |
331 | 313 | """ |
332 | - Report the given card | |
314 | + Hide the given card | |
333 | 315 | --- |
334 | - omit_serializer: true | |
335 | - parameters: | |
336 | - - fake: None | |
337 | - parameters_strategy: | |
338 | - form: replace | |
316 | + view_mocker: flashcards.api.mock_no_params | |
339 | 317 | """ |
340 | 318 | obj, created = FlashcardHide.objects.get_or_create(user=request.user, flashcard=self.get_object()) |
341 | - obj.reason = request.data['reason'] | |
342 | - if created: | |
343 | - obj.save() | |
319 | + obj.reason = request.data.get('reason', None) | |
320 | + obj.save() | |
344 | 321 | return Response(status=HTTP_204_NO_CONTENT) |
345 | 322 | |
323 | + hide = report | |
324 | + | |
346 | 325 | @detail_route(methods=['POST']) |
347 | 326 | def pull(self, request, pk): |
348 | 327 | """ |
349 | 328 | Pull a card from the live feed into the user's deck. |
350 | - :param request: The request object | |
351 | - :param pk: The primary key | |
352 | - :return: A 204 response upon success. | |
329 | + --- | |
330 | + view_mocker: flashcards.api.mock_no_params | |
353 | 331 | """ |
354 | 332 | user = request.user |
355 | 333 | flashcard = self.get_object() |
356 | 334 | |
357 | 335 | |
... | ... | @@ -360,21 +338,19 @@ |
360 | 338 | def unpull(self, request, pk): |
361 | 339 | """ |
362 | 340 | Unpull a card from the user's deck |
363 | - :param request: The request object | |
364 | - :param pk: The primary key | |
365 | - :return: A 204 response upon success. | |
341 | + --- | |
342 | + view_mocker: flashcards.api.mock_no_params | |
366 | 343 | """ |
367 | 344 | user = request.user |
368 | 345 | flashcard = self.get_object() |
369 | 346 | user.unpull(flashcard) |
370 | 347 | return Response(status=HTTP_204_NO_CONTENT) |
371 | 348 | |
372 | - def update(self, request, *args, **kwargs): | |
349 | + def partial_update(self, request, *args, **kwargs): | |
373 | 350 | """ |
374 | 351 | Edit settings related to a card for the user. |
375 | - :param request: The request object. | |
376 | - :param pk: The primary key of the flashcard. | |
377 | - :return: A 204 response upon success. | |
352 | + --- | |
353 | + request_serializer: FlashcardUpdateSerializer | |
378 | 354 | """ |
379 | 355 | user = request.user |
380 | 356 | flashcard = self.get_object() |