Reputation system
[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         return highest_badge
69
70     def __unicode__(self):
71         return self.user.__unicode__()
72
73
74 class BaseKarmaEvent(models.Model):
75     points    = models.IntegerField()
76     user      = models.ForeignKey(User)
77     timestamp = models.DateTimeField(default=datetime.datetime.utcnow)
78
79     class Meta:
80         abstract = True
81
82     def get_message(self):
83         raise NotImplemented()
84
85
86 class GenericKarmaEvent(BaseKarmaEvent):
87     message = models.CharField(max_length=255)
88
89     @staticmethod
90     def create_event(user, message, points):
91         event = GenericKarmaEvent.objects.create(user=user,
92                                                  points=points,
93                                                  message=message)
94         event.save()
95
96     def get_message(self):
97         return self.message
98
99
100 class NoteKarmaEvent(BaseKarmaEvent):
101     UPLOAD       = 'upload'
102     THANKS       = 'thanks'
103     NOTE_DELETED = 'deleted'
104     GIVE_FLAG    = 'give_flag'
105     GET_FLAGGED  = 'get_flagged'
106     EVENT_TYPE_CHOICES = (
107         (UPLOAD,       "You uploaded a note"),
108         (THANKS,       "You received a thanks for your note"),
109         (NOTE_DELETED, "Your note was deleted"),
110         (GIVE_FLAG,    "You flagged a note"),
111         (GET_FLAGGED,  "Your note was flagged as spam"),
112     )
113     note = models.ForeignKey('notes.Note')
114     event_type = models.CharField(max_length=15, choices=EVENT_TYPE_CHOICES)
115
116     POINTS = {
117         UPLOAD: 5,
118         THANKS: 1,
119         NOTE_DELETED: -5,
120         GIVE_FLAG: -1,
121         GET_FLAGGED: -100
122     }
123
124     def get_message(self):
125         return self.get_event_type_display()
126
127     def __unicode__(self):
128         return unicode(self.user) + ' -- ' + self.get_event_type_display() + ' -- ' + unicode(self.note)
129
130     @staticmethod
131     def create_event(user, note, type):
132         event = NoteKarmaEvent.objects.create(user=user,
133                                       note=note,
134                                       points=NoteKarmaEvent.POINTS[type],
135                                       event_type=type)
136         event.save()
137
138
139 class CourseKarmaEvent(BaseKarmaEvent):
140     GIVE_FLAG    = 'give_flag'
141     GET_FLAGGED  = 'get_flagged'
142     EVENT_TYPE_CHOICES = (
143         (GIVE_FLAG,    "You flagged a course"),
144         (GET_FLAGGED,  "Your course was flagged as spam"),
145     )
146     course = models.ForeignKey('courses.Course')
147     event_type = models.CharField(max_length=15, choices=EVENT_TYPE_CHOICES)
148
149     POINTS = {
150         GIVE_FLAG: -1,
151         GET_FLAGGED: -100
152     }
153
154     def get_message(self):
155         return self.get_event_type_display()
156
157     def __unicode__(self):
158         return unicode(self.user) + ' -- ' + self.get_event_type_display() + ' -- ' + unicode(self.course)
159
160     @staticmethod
161     def create_event(user, course, type):
162         event = CourseKarmaEvent.objects.create(user=user,
163                                       course=course,
164                                       points=CourseKarmaEvent.POINTS[type],
165                                       event_type=type)
166         event.save()
167
168
169 ALL_KARMA_EVENT_CLASSES = (GenericKarmaEvent, NoteKarmaEvent, CourseKarmaEvent)
170
171
172 def user_display_name(user):
173     """Return the best way to display a user's
174     name to them on the site."""
175     if hasattr(user, 'first_name') and user.first_name and \
176             hasattr(user, 'last_name') and user.last_name:
177         return user.first_name + ' ' + user.last_name
178     elif hasattr(user, 'email') and user.email:
179         return user.email
180     else:
181         return user.username
182
183
184 @receiver(post_save, sender=User, weak=True)
185 def create_user_profile(sender, instance, created, **kwargs):
186     if created:
187         with transaction.commit_on_success():
188             try:
189                 UserProfile.objects.create(user=instance)
190             except DatabaseError:
191                 logger.warn("Could not create UserProfile for user {u}. This is okay if running syncdb.".format(u=instance))
192