Require login to download a note, shift points around #302
[oweals/karmaworld.git] / karmaworld / apps / users / models.py
1 #!/usr/bin/env python
2 # -*- coding:utf8 -*-
3 # Copyright (C) 2013  FinalsClub Foundation
4 import logging
5 import datetime
6 from allauth.account.signals import email_confirmed
7 from django.contrib.auth.models import User
8 from django.db.models import Sum
9 from django.db.models.signals import post_save
10 from django.dispatch import receiver
11 from django.db import models, DatabaseError
12 from django.middleware.transaction import transaction
13 from karmaworld.apps.courses.models import School
14
15 logger = logging.getLogger(__name__)
16
17
18 class UserProfile(models.Model):
19     user      = models.OneToOneField(User)
20
21     school    = models.ForeignKey(School, blank=True, null=True)
22
23     def get_points(self):
24         sum = 0
25         for cls in ALL_KARMA_EVENT_CLASSES:
26             points = cls.objects.filter(user=self.user).aggregate(Sum('points'))['points__sum']
27             if points:
28                 sum += points
29
30         return sum
31
32     NO_BADGE = 0
33     PROSPECT = 1
34     BEGINNER = 2
35     TRAINEE = 3
36     APPRENTICE = 4
37     SCHOLAR = 5
38
39     BADGES = (
40         PROSPECT,
41         BEGINNER,
42         TRAINEE,
43         APPRENTICE,
44         SCHOLAR
45     )
46
47     BADGE_NAMES = {
48         PROSPECT: 'Prospect',
49         BEGINNER: 'Beginner',
50         TRAINEE: 'Trainee',
51         APPRENTICE: 'Apprentice',
52         SCHOLAR: 'Scholar'
53     }
54
55     BADGE_THRESHOLDS = {
56         PROSPECT: 10,
57         BEGINNER: 100,
58         TRAINEE: 200,
59         APPRENTICE: 500,
60         SCHOLAR: 1000
61     }
62
63     def get_badge(self):
64         points = self.get_points()
65         highest_badge = self.NO_BADGE
66         for badge in self.BADGES:
67             if points >= self.BADGE_THRESHOLDS[badge]:
68                 highest_badge = badge
69
70         if highest_badge is not self.NO_BADGE:
71             return self.BADGE_NAMES[highest_badge]
72         else:
73             return None
74
75     def __unicode__(self):
76         return self.user.__unicode__()
77
78
79 @receiver(email_confirmed, weak=True)
80 def give_email_confirm_karma(sender, **kwargs):
81     GenericKarmaEvent.create_event(kwargs['email_address'].user, kwargs['email_address'].email, GenericKarmaEvent.EMAIL_CONFIRMED)
82
83
84 class BaseKarmaEvent(models.Model):
85     points    = models.IntegerField()
86     user      = models.ForeignKey(User)
87     timestamp = models.DateTimeField(default=datetime.datetime.utcnow)
88
89     class Meta:
90         abstract = True
91
92     def get_message(self):
93         raise NotImplemented()
94
95
96 class GenericKarmaEvent(BaseKarmaEvent):
97     NONE = 'none'
98     NOTE_DELETED       = 'upload'
99     EMAIL_CONFIRMED    = 'thanks'
100
101     EVENT_TYPE_CHOICES = (
102         (NONE,               'This should not happen'),
103         (NOTE_DELETED,       'Your note "{m}" was deleted'),
104         (EMAIL_CONFIRMED,    'You confirmed your email address {m}'),
105     )
106
107     POINTS = {
108         NOTE_DELETED: -5,
109         EMAIL_CONFIRMED: 5,
110     }
111
112     event_type = models.CharField(max_length=15, choices=EVENT_TYPE_CHOICES, default=NONE)
113     message = models.CharField(max_length=255)
114
115     @staticmethod
116     def create_event(user, message, type):
117         event = GenericKarmaEvent.objects.create(user=user,
118                                                  points=GenericKarmaEvent.POINTS[type],
119                                                  event_type=type,
120                                                  message=message)
121         event.save()
122
123     def get_message(self):
124         if self.event_type == self.NONE:
125             return self.message
126         else:
127             return self.get_event_type_display().format(m=self.message)
128
129     def __unicode__(self):
130         return unicode(self.user) + ' -- ' + self.get_message()
131
132
133 class NoteKarmaEvent(BaseKarmaEvent):
134     UPLOAD       = 'upload'
135     THANKS       = 'thanks'
136     NOTE_DELETED = 'deleted'
137     GIVE_FLAG    = 'give_flag'
138     GET_FLAGGED  = 'get_flagged'
139     DOWNLOADED_NOTE = 'downloaded'
140     HAD_NOTE_DOWNLOADED = 'was_downloaded'
141
142     EVENT_TYPE_CHOICES = (
143         (UPLOAD,       "You uploaded a note"),
144         (THANKS,       "You received a thanks for your note"),
145         (NOTE_DELETED, "Your note was deleted"),
146         (GIVE_FLAG,    "You flagged a note"),
147         (GET_FLAGGED,  "Your note was flagged as spam"),
148         (DOWNLOADED_NOTE,  "You downloaded a note"),
149         (HAD_NOTE_DOWNLOADED,  "Your note was downloaded"),
150     )
151     note = models.ForeignKey('notes.Note')
152     event_type = models.CharField(max_length=15, choices=EVENT_TYPE_CHOICES)
153
154     POINTS = {
155         UPLOAD: 5,
156         THANKS: 1,
157         NOTE_DELETED: -5,
158         GIVE_FLAG: -1,
159         GET_FLAGGED: -100,
160         DOWNLOADED_NOTE: -2,
161         HAD_NOTE_DOWNLOADED: 2,
162     }
163
164     def get_message(self):
165         return self.get_event_type_display()
166
167     def __unicode__(self):
168         return unicode(self.user) + ' -- ' + self.get_event_type_display() + ' -- ' + unicode(self.note)
169
170     @staticmethod
171     def create_event(user, note, type):
172         event = NoteKarmaEvent.objects.create(user=user,
173                                       note=note,
174                                       points=NoteKarmaEvent.POINTS[type],
175                                       event_type=type)
176         event.save()
177
178
179 class CourseKarmaEvent(BaseKarmaEvent):
180     GIVE_FLAG    = 'give_flag'
181     EVENT_TYPE_CHOICES = (
182         (GIVE_FLAG,    "You flagged a course"),
183     )
184     course = models.ForeignKey('courses.Course')
185     event_type = models.CharField(max_length=15, choices=EVENT_TYPE_CHOICES)
186
187     POINTS = {
188         GIVE_FLAG: -1,
189     }
190
191     def get_message(self):
192         return self.get_event_type_display()
193
194     def __unicode__(self):
195         return unicode(self.user) + ' -- ' + self.get_event_type_display() + ' -- ' + unicode(self.course)
196
197     @staticmethod
198     def create_event(user, course, type):
199         event = CourseKarmaEvent.objects.create(user=user,
200                                       course=course,
201                                       points=CourseKarmaEvent.POINTS[type],
202                                       event_type=type)
203         event.save()
204
205
206 ALL_KARMA_EVENT_CLASSES = (GenericKarmaEvent, NoteKarmaEvent, CourseKarmaEvent)
207
208
209 def user_display_name(user):
210     """Return the best way to display a user's
211     name to them on the site."""
212     if hasattr(user, 'first_name') and user.first_name and \
213             hasattr(user, 'last_name') and user.last_name:
214         return user.first_name + ' ' + user.last_name
215     elif hasattr(user, 'email') and user.email:
216         return user.email
217     else:
218         return user.username
219
220
221 @receiver(post_save, sender=User, weak=True)
222 def create_user_profile(sender, instance, created, **kwargs):
223     if created:
224         with transaction.commit_on_success():
225             try:
226                 UserProfile.objects.create(user=instance)
227             except DatabaseError:
228                 logger.warn("Could not create UserProfile for user {u}. This is okay if running syncdb.".format(u=instance))
229