From 5a2899b9ddc175cef2e7d334d462f7d7f7e30ee6 Mon Sep 17 00:00:00 2001 From: Andrew Buss Date: Fri, 29 May 2015 02:56:39 -0700 Subject: [PATCH] add gobs of logging --- events.log | 0 flashcards/api.py | 3 ++- flashcards/models.py | 21 +++++++++++++++++++-- flashcards/views.py | 31 +++++++++++++++++++++++++++++-- flashy/settings.py | 24 +++++++++++++++++++----- 5 files changed, 69 insertions(+), 10 deletions(-) create mode 100644 events.log diff --git a/events.log b/events.log new file mode 100644 index 0000000..e69de29 diff --git a/flashcards/api.py b/flashcards/api.py index 0ff5fa9..5dc58cc 100644 --- a/flashcards/api.py +++ b/flashcards/api.py @@ -46,7 +46,8 @@ class IsAuthenticatedAndConfirmed(BasePermission): """ def has_permission(self, request, view): - if not (request.user and request.user.is_authenticated()): return False + if not request.user: return False + if not request.user.is_authenticated(): return False if request.user.confirmed_email: return True if (now() - request.user.date_joined).days > 0: raise PermissionDenied('Please verify your email before continuing') diff --git a/flashcards/models.py b/flashcards/models.py index 3a52c83..940ee41 100644 --- a/flashcards/models.py +++ b/flashcards/models.py @@ -10,6 +10,7 @@ from django.core.mail import send_mail from django.core.validators import MinLengthValidator from django.db import IntegrityError from django.db.models import * +from django.utils.log import getLogger from django.utils.timezone import now, make_aware from flashy.settings import QUARTER_START from simple_email_confirmation import SimpleEmailConfirmationUserMixin @@ -18,8 +19,6 @@ from cached_property import cached_property - - # Hack to fix AbstractUser before subclassing it AbstractUser._meta.get_field('email')._unique = True @@ -154,6 +153,9 @@ class UserFlashcard(Model): # By default, order by most recently pulled ordering = ['-pulled'] + def __unicode__(self): + return '%s has %s' % (str(self.user), str(self.flashcard)) + class FlashcardHide(Model): """ @@ -172,6 +174,9 @@ class FlashcardHide(Model): unique_together = (('user', 'flashcard'),) index_together = ["user", "flashcard"] + def __unicode__(self): + return '%s hid %s' % (str(self.user), str(self.flashcard)) + class Flashcard(Model): text = CharField(max_length=180, help_text='The text on the card', validators=[MinLengthValidator(5)]) @@ -190,6 +195,9 @@ class Flashcard(Model): # By default, order by most recently pushed ordering = ['-pushed'] + def __unicode__(self): + return '' % self.text + @property def material_week_num(self): return (self.material_date - QUARTER_START).days / 7 + 1 @@ -304,6 +312,9 @@ class UserFlashcardQuiz(Model): response = CharField(max_length=255, blank=True, null=True, default=None, help_text="The user's response") correct = NullBooleanField(help_text="The user's self-evaluation of their response") + def __unicode__(self): + return '%s reviewed %s' % (str(self.user_flashcard.user), str(self.user_flashcard.flashcard)) + def status(self): """ There are three stages of a quiz object: @@ -438,6 +449,9 @@ class LecturePeriod(Model): unique_together = (('section', 'start_time', 'week_day'),) ordering = ['section', 'week_day'] + def __unicode__(self): + return self.weekday_letter + ' ' + self.short_start_time + class WhitelistedAddress(Model): """ @@ -445,3 +459,6 @@ class WhitelistedAddress(Model): """ email = EmailField() section = ForeignKey(Section, related_name='whitelist') + + def __unicode__(self): + return '%s whitelisted for %s' % (str(self.email), str(self.section)) diff --git a/flashcards/views.py b/flashcards/views.py index 1eaa83e..1dc17ce 100644 --- a/flashcards/views.py +++ b/flashcards/views.py @@ -2,6 +2,7 @@ import django from django.contrib import auth from django.db import IntegrityError from django.shortcuts import get_object_or_404 +from django.utils.log import getLogger from flashcards.api import StandardResultsSetPagination, IsEnrolledInAssociatedSection, IsFlashcardReviewer, \ IsAuthenticatedAndConfirmed from flashcards.models import Section, User, Flashcard, FlashcardHide, UserFlashcard, UserFlashcardQuiz @@ -24,6 +25,11 @@ from rest_framework.exceptions import AuthenticationFailed, NotAuthenticated, Va from simple_email_confirmation import EmailAddress +def log_event(request, event=''): + getLogger('flashy.events').info( + '%s %s %s %s' % (request.META['REMOTE_ADDR'], str(request.user), request.META.get('PATH', ''), event)) + + class SectionViewSet(ReadOnlyModelViewSet): queryset = Section.objects.all() serializer_class = DeepSectionSerializer @@ -43,10 +49,12 @@ class SectionViewSet(ReadOnlyModelViewSet): else: flashcards |= Flashcard.cards_hidden_by(request.user) flashcards = flashcards.filter(section=self.get_object()).order_by('material_date').all() + log_event(request, str(self.get_object())) return Response(FlashcardSerializer(flashcards, context={"user": request.user}, many=True).data) @detail_route(methods=['POST']) def enroll(self, request, pk): + """ Add the current user to a specified section If the class has a whitelist, but the user is not on the whitelist, the request will fail. @@ -55,6 +63,7 @@ class SectionViewSet(ReadOnlyModelViewSet): """ try: self.get_object().enroll(request.user) + log_event(request, str(self.get_object())) except django.core.exceptions.PermissionDenied as e: raise PermissionDenied(e) except django.core.exceptions.ValidationError as e: @@ -71,6 +80,7 @@ class SectionViewSet(ReadOnlyModelViewSet): """ try: self.get_object().drop(request.user) + log_event(request, str(self.get_object())) except django.core.exceptions.PermissionDenied as e: raise PermissionDenied(e) except django.core.exceptions.ValidationError as e: @@ -93,6 +103,7 @@ class SectionViewSet(ReadOnlyModelViewSet): if not query: return Response('[]') qs = Section.search(query.split(' '))[:20] data = SectionSerializer(qs, many=True).data + log_event(request, query) return Response(data) @detail_route(methods=['GET']) @@ -102,6 +113,7 @@ class SectionViewSet(ReadOnlyModelViewSet): """ qs = request.user.get_deck(self.get_object()) serializer = FlashcardSerializer(qs, many=True) + log_event(request, str(self.get_object())) return Response(serializer.data) @detail_route(methods=['GET']) @@ -112,6 +124,7 @@ class SectionViewSet(ReadOnlyModelViewSet): """ serializer = FlashcardSerializer(self.get_object().get_feed_for_user(request.user), many=True, context={'user': request.user}) + log_event(request, str(self.get_object())) return Response(serializer.data) @@ -145,10 +158,12 @@ class UserDetail(GenericAPIView): raise ValidationError('old_password is incorrect') request.user.set_password(data['new_password']) request.user.save() + log_event(request, 'change password') if 'confirmation_key' in data: try: request.user.confirm_email(data['confirmation_key']) + log_event(request, 'confirm email') except EmailAddress.DoesNotExist: raise ValidationError('confirmation_key is invalid') @@ -170,6 +185,7 @@ class UserDetail(GenericAPIView): Yes, really """ request.user.delete() + log_event(request) return Response(status=HTTP_204_NO_CONTENT) @@ -187,7 +203,7 @@ def register(request, format=None): User.objects.create_user(**data.validated_data) user = authenticate(**data.validated_data) auth.login(request, user) - + log_event(request) return Response(UserSerializer(request.user).data, status=HTTP_201_CREATED) @@ -209,6 +225,7 @@ def login(request): if not user.is_active: raise NotAuthenticated('Account is disabled') auth.login(request, user) + log_event(request) return Response(UserSerializer(request.user).data) @@ -219,6 +236,7 @@ def logout(request, format=None): Logs the authenticated user out. """ auth.logout(request) + log_event(request) return Response(status=HTTP_204_NO_CONTENT) @@ -231,6 +249,7 @@ def request_password_reset(request, format=None): """ data = PasswordResetRequestSerializer(data=request.data) data.is_valid(raise_exception=True) + log_event(request, 'email: ' + str(data['email'])) get_object_or_404(User, email=data['email'].value).request_password_reset() return Response(status=HTTP_204_NO_CONTENT) @@ -251,6 +270,7 @@ def reset_password(request, format=None): if default_token_generator.check_token(user, data['token'].value): user.set_password(data['new_password'].value) user.save() + log_event(request) else: raise ValidationError('Could not verify reset token') return Response(status=HTTP_204_NO_CONTENT) @@ -274,7 +294,7 @@ class FlashcardViewSet(GenericViewSet, CreateModelMixin, RetrieveModelMixin): headers = self.get_success_headers(data) request.user.pull(flashcard) response_data = FlashcardSerializer(flashcard).data - + log_event(request, response_data) return Response(response_data, status=HTTP_201_CREATED, headers=headers) @detail_route(methods=['POST']) @@ -286,6 +306,7 @@ class FlashcardViewSet(GenericViewSet, CreateModelMixin, RetrieveModelMixin): """ hide = get_object_or_404(FlashcardHide, user=request.user, flashcard=self.get_object()) hide.delete() + log_event(request, str(self.get_object())) return Response(status=HTTP_204_NO_CONTENT) @detail_route(methods=['POST']) @@ -296,6 +317,7 @@ class FlashcardViewSet(GenericViewSet, CreateModelMixin, RetrieveModelMixin): view_mocker: flashcards.api.mock_no_params """ self.get_object().report(request.user) + log_event(request, str(self.get_object())) return Response(status=HTTP_204_NO_CONTENT) hide = report @@ -309,6 +331,7 @@ class FlashcardViewSet(GenericViewSet, CreateModelMixin, RetrieveModelMixin): """ try: request.user.pull(self.get_object()) + log_event(request, str(self.get_object())) return Response(status=HTTP_204_NO_CONTENT) except IntegrityError, e: raise ValidationError('Cannot pull a card already in deck') @@ -323,6 +346,7 @@ class FlashcardViewSet(GenericViewSet, CreateModelMixin, RetrieveModelMixin): user = request.user flashcard = self.get_object() user.unpull(flashcard) + log_event(request, str(self.get_object())) return Response(status=HTTP_204_NO_CONTENT) def partial_update(self, request, *args, **kwargs): @@ -337,6 +361,7 @@ class FlashcardViewSet(GenericViewSet, CreateModelMixin, RetrieveModelMixin): data.is_valid(raise_exception=True) new_flashcard = data.validated_data new_flashcard = flashcard.edit(user, new_flashcard) + log_event(request, str(new_flashcard)) return Response(FlashcardSerializer(new_flashcard, context={'user': request.user}).data, status=HTTP_200_OK) @@ -386,6 +411,7 @@ class UserFlashcardQuizViewSet(GenericViewSet, CreateModelMixin, UpdateModelMixi blanked_word=user_flashcard.flashcard.text[slice(*mask)]) user_flashcard_quiz.save() response = QuizResponseSerializer(instance=user_flashcard_quiz, mask=mask) + log_event(request, response) return Response(response.data, status=HTTP_200_OK) def partial_update(self, request, *args, **kwargs): @@ -400,4 +426,5 @@ class UserFlashcardQuizViewSet(GenericViewSet, CreateModelMixin, UpdateModelMixi serializer = QuizAnswerRequestSerializer(instance=user_flashcard_quiz, data=request.data) serializer.is_valid(raise_exception=True) serializer.update(user_flashcard_quiz, serializer.validated_data) + log_event(request, serializer.data) return Response(status=HTTP_204_NO_CONTENT) diff --git a/flashy/settings.py b/flashy/settings.py index 92d23ca..b437286 100644 --- a/flashy/settings.py +++ b/flashy/settings.py @@ -1,6 +1,6 @@ -# Build paths inside the project like this: os.path.join(BASE_DIR, ...) -import os from datetime import datetime + +import os from pytz import UTC BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -35,7 +35,6 @@ INSTALLED_APPS = [ WEBSOCKET_URL = '/ws/' - MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', @@ -132,13 +131,29 @@ if IN_PRODUCTION: 'class': 'logging.FileHandler', 'filename': 'debug.log', }, + 'eventslog': { + 'level': 'INFO', + 'class': 'logging.FileHandler', + 'filename': 'events.log', + 'formatter': 'verbose' + }, + }, + 'formatters': { + 'verbose': { + 'format': '%(asctime)s %(module)s %(message)s' + }, }, 'loggers': { - 'django.request': { + 'django': { 'handlers': ['file'], 'level': 'DEBUG', 'propagate': True, }, + 'flashy.events': { + 'level': 'INFO', + 'propagate': True, + 'handlers': ['eventslog'], + } }, } @@ -153,4 +168,3 @@ CACHES = { SWAGGER_SETTINGS = { 'doc_expansion': 'list' } - -- 1.9.1