Refactor GenericKarmaEvent
[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     EVENT_TYPE_CHOICES = (
140         (UPLOAD,       "You uploaded a note"),
141         (THANKS,       "You received a thanks for your note"),
142         (NOTE_DELETED, "Your note was deleted"),
143         (GIVE_FLAG,    "You flagged a note"),
144         (GET_FLAGGED,  "Your note was flagged as spam"),
145     )
146     note = models.ForeignKey('notes.Note')
147     event_type = models.CharField(max_length=15, choices=EVENT_TYPE_CHOICES)
148
149     POINTS = {
150         UPLOAD: 5,
151         THANKS: 1,
152         NOTE_DELETED: -5,
153         GIVE_FLAG: -1,
154         GET_FLAGGED: -100
155     }
156
157     def get_message(self):
158         return self.get_event_type_display()
159
160     def __unicode__(self):
161         return unicode(self.user) + ' -- ' + self.get_event_type_display() + ' -- ' + unicode(self.note)
162
163     @staticmethod
164     def create_event(user, note, type):
165         event = NoteKarmaEvent.objects.create(user=user,
166                                       note=note,
167                                       points=NoteKarmaEvent.POINTS[type],
168                                       event_type=type)
169         event.save()
170
171
172 class CourseKarmaEvent(BaseKarmaEvent):
173     GIVE_FLAG    = 'give_flag'
174     EVENT_TYPE_CHOICES = (
175         (GIVE_FLAG,    "You flagged a course"),
176     )
177     course = models.ForeignKey('courses.Course')
178     event_type = models.CharField(max_length=15, choices=EVENT_TYPE_CHOICES)
179
180     POINTS = {
181         GIVE_FLAG: -1,
182     }
183
184     def get_message(self):
185         return self.get_event_type_display()
186
187     def __unicode__(self):
188         return unicode(self.user) + ' -- ' + self.get_event_type_display() + ' -- ' + unicode(self.course)
189
190     @staticmethod
191     def create_event(user, course, type):
192         event = CourseKarmaEvent.objects.create(user=user,
193                                       course=course,
194                                       points=CourseKarmaEvent.POINTS[type],
195                                       event_type=type)
196         event.save()
197
198
199 ALL_KARMA_EVENT_CLASSES = (GenericKarmaEvent, NoteKarmaEvent, CourseKarmaEvent)
200
201
202 def user_display_name(user):
203     """Return the best way to display a user's
204     name to them on the site."""
205     if hasattr(user, 'first_name') and user.first_name and \
206             hasattr(user, 'last_name') and user.last_name:
207         return user.first_name + ' ' + user.last_name
208     elif hasattr(user, 'email') and user.email:
209         return user.email
210     else:
211         return user.username
212
213
214 @receiver(post_save, sender=User, weak=True)
215 def create_user_profile(sender, instance, created, **kwargs):
216     if created:
217         with transaction.commit_on_success():
218             try:
219                 UserProfile.objects.create(user=instance)
220             except DatabaseError:
221                 logger.warn("Could not create UserProfile for user {u}. This is okay if running syncdb.".format(u=instance))
222