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