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