Commit 5a2899b9ddc175cef2e7d334d462f7d7f7e30ee6

Authored by Andrew Buss
1 parent 33f8a47a8b
Exists in master

add gobs of logging

Showing 4 changed files with 69 additions and 10 deletions Side-by-side Diff

flashcards/api.py View file @ 5a2899b
... ... @@ -46,7 +46,8 @@
46 46 """
47 47  
48 48 def has_permission(self, request, view):
49   - if not (request.user and request.user.is_authenticated()): return False
  49 + if not request.user: return False
  50 + if not request.user.is_authenticated(): return False
50 51 if request.user.confirmed_email: return True
51 52 if (now() - request.user.date_joined).days > 0:
52 53 raise PermissionDenied('Please verify your email before continuing')
flashcards/models.py View file @ 5a2899b
... ... @@ -10,6 +10,7 @@
10 10 from django.core.validators import MinLengthValidator
11 11 from django.db import IntegrityError
12 12 from django.db.models import *
  13 +from django.utils.log import getLogger
13 14 from django.utils.timezone import now, make_aware
14 15 from flashy.settings import QUARTER_START
15 16 from simple_email_confirmation import SimpleEmailConfirmationUserMixin
... ... @@ -18,8 +19,6 @@
18 19  
19 20  
20 21  
21   -
22   -
23 22 # Hack to fix AbstractUser before subclassing it
24 23  
25 24 AbstractUser._meta.get_field('email')._unique = True
26 25  
... ... @@ -154,7 +153,10 @@
154 153 # By default, order by most recently pulled
155 154 ordering = ['-pulled']
156 155  
  156 + def __unicode__(self):
  157 + return '%s has %s' % (str(self.user), str(self.flashcard))
157 158  
  159 +
158 160 class FlashcardHide(Model):
159 161 """
160 162 Represents the property of a flashcard being hidden by a user.
161 163  
... ... @@ -172,7 +174,10 @@
172 174 unique_together = (('user', 'flashcard'),)
173 175 index_together = ["user", "flashcard"]
174 176  
  177 + def __unicode__(self):
  178 + return '%s hid %s' % (str(self.user), str(self.flashcard))
175 179  
  180 +
176 181 class Flashcard(Model):
177 182 text = CharField(max_length=180, help_text='The text on the card', validators=[MinLengthValidator(5)])
178 183 section = ForeignKey('Section', help_text='The section with which the card is associated')
... ... @@ -190,6 +195,9 @@
190 195 # By default, order by most recently pushed
191 196 ordering = ['-pushed']
192 197  
  198 + def __unicode__(self):
  199 + return '<flashcard: %s>' % self.text
  200 +
193 201 @property
194 202 def material_week_num(self):
195 203 return (self.material_date - QUARTER_START).days / 7 + 1
... ... @@ -304,6 +312,9 @@
304 312 response = CharField(max_length=255, blank=True, null=True, default=None, help_text="The user's response")
305 313 correct = NullBooleanField(help_text="The user's self-evaluation of their response")
306 314  
  315 + def __unicode__(self):
  316 + return '%s reviewed %s' % (str(self.user_flashcard.user), str(self.user_flashcard.flashcard))
  317 +
307 318 def status(self):
308 319 """
309 320 There are three stages of a quiz object:
310 321  
311 322  
... ... @@ -438,11 +449,17 @@
438 449 unique_together = (('section', 'start_time', 'week_day'),)
439 450 ordering = ['section', 'week_day']
440 451  
  452 + def __unicode__(self):
  453 + return self.weekday_letter + ' ' + self.short_start_time
441 454  
  455 +
442 456 class WhitelistedAddress(Model):
443 457 """
444 458 An email address that has been whitelisted for a section at an instructor's request
445 459 """
446 460 email = EmailField()
447 461 section = ForeignKey(Section, related_name='whitelist')
  462 +
  463 + def __unicode__(self):
  464 + return '%s whitelisted for %s' % (str(self.email), str(self.section))
flashcards/views.py View file @ 5a2899b
... ... @@ -2,6 +2,7 @@
2 2 from django.contrib import auth
3 3 from django.db import IntegrityError
4 4 from django.shortcuts import get_object_or_404
  5 +from django.utils.log import getLogger
5 6 from flashcards.api import StandardResultsSetPagination, IsEnrolledInAssociatedSection, IsFlashcardReviewer, \
6 7 IsAuthenticatedAndConfirmed
7 8 from flashcards.models import Section, User, Flashcard, FlashcardHide, UserFlashcard, UserFlashcardQuiz
... ... @@ -24,6 +25,11 @@
24 25 from simple_email_confirmation import EmailAddress
25 26  
26 27  
  28 +def log_event(request, event=''):
  29 + getLogger('flashy.events').info(
  30 + '%s %s %s %s' % (request.META['REMOTE_ADDR'], str(request.user), request.META.get('PATH', ''), event))
  31 +
  32 +
27 33 class SectionViewSet(ReadOnlyModelViewSet):
28 34 queryset = Section.objects.all()
29 35 serializer_class = DeepSectionSerializer
30 36  
... ... @@ -43,10 +49,12 @@
43 49 else:
44 50 flashcards |= Flashcard.cards_hidden_by(request.user)
45 51 flashcards = flashcards.filter(section=self.get_object()).order_by('material_date').all()
  52 + log_event(request, str(self.get_object()))
46 53 return Response(FlashcardSerializer(flashcards, context={"user": request.user}, many=True).data)
47 54  
48 55 @detail_route(methods=['POST'])
49 56 def enroll(self, request, pk):
  57 +
50 58 """
51 59 Add the current user to a specified section
52 60 If the class has a whitelist, but the user is not on the whitelist, the request will fail.
... ... @@ -55,6 +63,7 @@
55 63 """
56 64 try:
57 65 self.get_object().enroll(request.user)
  66 + log_event(request, str(self.get_object()))
58 67 except django.core.exceptions.PermissionDenied as e:
59 68 raise PermissionDenied(e)
60 69 except django.core.exceptions.ValidationError as e:
... ... @@ -71,6 +80,7 @@
71 80 """
72 81 try:
73 82 self.get_object().drop(request.user)
  83 + log_event(request, str(self.get_object()))
74 84 except django.core.exceptions.PermissionDenied as e:
75 85 raise PermissionDenied(e)
76 86 except django.core.exceptions.ValidationError as e:
... ... @@ -93,6 +103,7 @@
93 103 if not query: return Response('[]')
94 104 qs = Section.search(query.split(' '))[:20]
95 105 data = SectionSerializer(qs, many=True).data
  106 + log_event(request, query)
96 107 return Response(data)
97 108  
98 109 @detail_route(methods=['GET'])
... ... @@ -102,6 +113,7 @@
102 113 """
103 114 qs = request.user.get_deck(self.get_object())
104 115 serializer = FlashcardSerializer(qs, many=True)
  116 + log_event(request, str(self.get_object()))
105 117 return Response(serializer.data)
106 118  
107 119 @detail_route(methods=['GET'])
... ... @@ -112,6 +124,7 @@
112 124 """
113 125 serializer = FlashcardSerializer(self.get_object().get_feed_for_user(request.user), many=True,
114 126 context={'user': request.user})
  127 + log_event(request, str(self.get_object()))
115 128 return Response(serializer.data)
116 129  
117 130  
118 131  
... ... @@ -145,10 +158,12 @@
145 158 raise ValidationError('old_password is incorrect')
146 159 request.user.set_password(data['new_password'])
147 160 request.user.save()
  161 + log_event(request, 'change password')
148 162  
149 163 if 'confirmation_key' in data:
150 164 try:
151 165 request.user.confirm_email(data['confirmation_key'])
  166 + log_event(request, 'confirm email')
152 167 except EmailAddress.DoesNotExist:
153 168 raise ValidationError('confirmation_key is invalid')
154 169  
... ... @@ -170,6 +185,7 @@
170 185 Yes, really
171 186 """
172 187 request.user.delete()
  188 + log_event(request)
173 189 return Response(status=HTTP_204_NO_CONTENT)
174 190  
175 191  
... ... @@ -187,7 +203,7 @@
187 203 User.objects.create_user(**data.validated_data)
188 204 user = authenticate(**data.validated_data)
189 205 auth.login(request, user)
190   -
  206 + log_event(request)
191 207 return Response(UserSerializer(request.user).data, status=HTTP_201_CREATED)
192 208  
193 209  
... ... @@ -209,6 +225,7 @@
209 225 if not user.is_active:
210 226 raise NotAuthenticated('Account is disabled')
211 227 auth.login(request, user)
  228 + log_event(request)
212 229 return Response(UserSerializer(request.user).data)
213 230  
214 231  
... ... @@ -219,6 +236,7 @@
219 236 Logs the authenticated user out.
220 237 """
221 238 auth.logout(request)
  239 + log_event(request)
222 240 return Response(status=HTTP_204_NO_CONTENT)
223 241  
224 242  
... ... @@ -231,6 +249,7 @@
231 249 """
232 250 data = PasswordResetRequestSerializer(data=request.data)
233 251 data.is_valid(raise_exception=True)
  252 + log_event(request, 'email: ' + str(data['email']))
234 253 get_object_or_404(User, email=data['email'].value).request_password_reset()
235 254 return Response(status=HTTP_204_NO_CONTENT)
236 255  
... ... @@ -251,6 +270,7 @@
251 270 if default_token_generator.check_token(user, data['token'].value):
252 271 user.set_password(data['new_password'].value)
253 272 user.save()
  273 + log_event(request)
254 274 else:
255 275 raise ValidationError('Could not verify reset token')
256 276 return Response(status=HTTP_204_NO_CONTENT)
... ... @@ -274,7 +294,7 @@
274 294 headers = self.get_success_headers(data)
275 295 request.user.pull(flashcard)
276 296 response_data = FlashcardSerializer(flashcard).data
277   -
  297 + log_event(request, response_data)
278 298 return Response(response_data, status=HTTP_201_CREATED, headers=headers)
279 299  
280 300 @detail_route(methods=['POST'])
... ... @@ -286,6 +306,7 @@
286 306 """
287 307 hide = get_object_or_404(FlashcardHide, user=request.user, flashcard=self.get_object())
288 308 hide.delete()
  309 + log_event(request, str(self.get_object()))
289 310 return Response(status=HTTP_204_NO_CONTENT)
290 311  
291 312 @detail_route(methods=['POST'])
... ... @@ -296,6 +317,7 @@
296 317 view_mocker: flashcards.api.mock_no_params
297 318 """
298 319 self.get_object().report(request.user)
  320 + log_event(request, str(self.get_object()))
299 321 return Response(status=HTTP_204_NO_CONTENT)
300 322  
301 323 hide = report
... ... @@ -309,6 +331,7 @@
309 331 """
310 332 try:
311 333 request.user.pull(self.get_object())
  334 + log_event(request, str(self.get_object()))
312 335 return Response(status=HTTP_204_NO_CONTENT)
313 336 except IntegrityError, e:
314 337 raise ValidationError('Cannot pull a card already in deck')
... ... @@ -323,6 +346,7 @@
323 346 user = request.user
324 347 flashcard = self.get_object()
325 348 user.unpull(flashcard)
  349 + log_event(request, str(self.get_object()))
326 350 return Response(status=HTTP_204_NO_CONTENT)
327 351  
328 352 def partial_update(self, request, *args, **kwargs):
... ... @@ -337,6 +361,7 @@
337 361 data.is_valid(raise_exception=True)
338 362 new_flashcard = data.validated_data
339 363 new_flashcard = flashcard.edit(user, new_flashcard)
  364 + log_event(request, str(new_flashcard))
340 365 return Response(FlashcardSerializer(new_flashcard, context={'user': request.user}).data, status=HTTP_200_OK)
341 366  
342 367  
... ... @@ -386,6 +411,7 @@
386 411 blanked_word=user_flashcard.flashcard.text[slice(*mask)])
387 412 user_flashcard_quiz.save()
388 413 response = QuizResponseSerializer(instance=user_flashcard_quiz, mask=mask)
  414 + log_event(request, response)
389 415 return Response(response.data, status=HTTP_200_OK)
390 416  
391 417 def partial_update(self, request, *args, **kwargs):
... ... @@ -400,5 +426,6 @@
400 426 serializer = QuizAnswerRequestSerializer(instance=user_flashcard_quiz, data=request.data)
401 427 serializer.is_valid(raise_exception=True)
402 428 serializer.update(user_flashcard_quiz, serializer.validated_data)
  429 + log_event(request, serializer.data)
403 430 return Response(status=HTTP_204_NO_CONTENT)
flashy/settings.py View file @ 5a2899b
1   -# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
2   -import os
3 1 from datetime import datetime
  2 +
  3 +import os
4 4 from pytz import UTC
5 5  
6 6 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
... ... @@ -35,7 +35,6 @@
35 35  
36 36 WEBSOCKET_URL = '/ws/'
37 37  
38   -
39 38 MIDDLEWARE_CLASSES = (
40 39 'django.contrib.sessions.middleware.SessionMiddleware',
41 40 'django.middleware.common.CommonMiddleware',
42 41  
43 42  
44 43  
... ... @@ -132,13 +131,29 @@
132 131 'class': 'logging.FileHandler',
133 132 'filename': 'debug.log',
134 133 },
  134 + 'eventslog': {
  135 + 'level': 'INFO',
  136 + 'class': 'logging.FileHandler',
  137 + 'filename': 'events.log',
  138 + 'formatter': 'verbose'
  139 + },
135 140 },
  141 + 'formatters': {
  142 + 'verbose': {
  143 + 'format': '%(asctime)s %(module)s %(message)s'
  144 + },
  145 + },
136 146 'loggers': {
137   - 'django.request': {
  147 + 'django': {
138 148 'handlers': ['file'],
139 149 'level': 'DEBUG',
140 150 'propagate': True,
141 151 },
  152 + 'flashy.events': {
  153 + 'level': 'INFO',
  154 + 'propagate': True,
  155 + 'handlers': ['eventslog'],
  156 + }
142 157 },
143 158 }
144 159