Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 464 lines (394 sloc) 16.795 kb
aea9cda @jledbetter Added ability to submit for a badge via the browser. Nicer UI coming.
jledbetter authored
1 import datetime
2
7f294ab @jledbetter Badges creation begun via admin interface for superusers to create them
jledbetter authored
3 from django.db import models
8cc8259 @zuzelvp More work around challenges completion.
zuzelvp authored
4 from django.conf import settings
1df36eb @zuzelvp New email notifications to adopters of challenges related to a badge sub...
zuzelvp authored
5 from django.db.models import Avg, Q
7f294ab @jledbetter Badges creation begun via admin interface for superusers to create them
jledbetter authored
6 from django.utils.translation import ugettext_lazy as _
cd21814 @jledbetter Started form for creation of badges. Also started rubrics in Django admi...
jledbetter authored
7 from django.template.defaultfilters import slugify
5a12c29 @jledbetter Award and assessment view updates
jledbetter authored
8 from django.db.models.signals import post_save
1df36eb @zuzelvp New email notifications to adopters of challenges related to a badge sub...
zuzelvp authored
9 from django.contrib.sites.models import Site
7f294ab @jledbetter Badges creation begun via admin interface for superusers to create them
jledbetter authored
10
11 from drumbeat import storage
968efbe @zuzelvp Filtering skill and peer badges and loadding badges dinamically to the o...
zuzelvp authored
12 from drumbeat.utils import get_partition_id, safe_filename, MultiQuerySet
aea9cda @jledbetter Added ability to submit for a badge via the browser. Nicer UI coming.
jledbetter authored
13 from drumbeat.models import ModelBase
14 from richtext.models import RichTextField
320fdd3 @dirkcuys Refactor notifications to separate translation and sending of notificati...
dirkcuys authored
15 from notifications.models import send_notifications_i18n
9f32728 @zuzelvp Updating lernanta to work with the new version of the django-obi app.
zuzelvp authored
16 from l10n.urlresolvers import reverse
7f294ab @jledbetter Badges creation begun via admin interface for superusers to create them
jledbetter authored
17
18
19 def determine_upload_path(instance, filename):
20 chunk_size = 1000 # max files per directory
21 return "images/badges/%(partition)d/%(filename)s" % {
22 'partition': get_partition_id(instance.pk, chunk_size),
23 'filename': safe_filename(filename),
24 }
25
37a1da9 @jledbetter Can add rubrics to badges via /admin
jledbetter authored
26
aea9cda @jledbetter Added ability to submit for a badge via the browser. Nicer UI coming.
jledbetter authored
27 def get_awarded_badges(user):
28 from pilot import get_awarded_badges as get_pilot_badges
9f32728 @zuzelvp Updating lernanta to work with the new version of the django-obi app.
zuzelvp authored
29 badges = get_pilot_badges(user)
30 profile = user.get_profile()
31 badges_ids = Award.objects.filter(user=profile).values(
32 'badge_id').distinct()
33 for badge in Badge.objects.filter(id__in=badges_ids):
34 evidence = reverse('user_awards_show',
35 kwargs= dict(slug=badge.slug, username=profile.username))
b4bf917 @zuzelvp Make share badges functionality (throught OBI integration) more visible ...
zuzelvp authored
36 awards_count = Award.objects.filter(user=profile,
37 badge=badge).count()
9f32728 @zuzelvp Updating lernanta to work with the new version of the django-obi app.
zuzelvp authored
38 data = {
39 'name': badge.name,
40 'description': badge.description,
41 'image': badge.get_image_url(),
42 'evidence': evidence,
43 'criteria': badge.get_absolute_url(),
b4bf917 @zuzelvp Make share badges functionality (throught OBI integration) more visible ...
zuzelvp authored
44 'count': awards_count,
9f32728 @zuzelvp Updating lernanta to work with the new version of the django-obi app.
zuzelvp authored
45 }
46 badges[badge.slug] = data
47 return badges
7f294ab @jledbetter Badges creation begun via admin interface for superusers to create them
jledbetter authored
48
aea9cda @jledbetter Added ability to submit for a badge via the browser. Nicer UI coming.
jledbetter authored
49
5a1acd0 @zuzelvp [splitting db model changes into smaller commits] Using ModelBase.
zuzelvp authored
50 class Badge(ModelBase):
660d8ce @dirkcuys code format
dirkcuys authored
51 """ Representation of a Badge """
7f294ab @jledbetter Badges creation begun via admin interface for superusers to create them
jledbetter authored
52 name = models.CharField(max_length=225, blank=False)
53 slug = models.SlugField(unique=True, max_length=110)
54 description = models.CharField(max_length=225, blank=False)
050ec12 @zuzelvp [splitting db model changes into smaller commits] including requirements...
zuzelvp authored
55 requirements = RichTextField(blank=True, null=True)
7f294ab @jledbetter Badges creation begun via admin interface for superusers to create them
jledbetter authored
56 image = models.ImageField(
57 upload_to=determine_upload_path, default='', blank=True, null=True,
58 storage=storage.ImageStorage())
92e24c6 @jledbetter Added prerequisite badges for badges
jledbetter authored
59 prerequisites = models.ManyToManyField('self', symmetrical=False,
3600909 @zuzelvp Fixing pep8 and pyflake errors.
zuzelvp authored
60 blank=True, null=True)
a71eedc @jledbetter Moved logic to its own model.
jledbetter authored
61 rubrics = models.ManyToManyField('badges.Rubric', related_name='badges',
3600909 @zuzelvp Fixing pep8 and pyflake errors.
zuzelvp authored
62 null=True, blank=True)
a71eedc @jledbetter Moved logic to its own model.
jledbetter authored
63 logic = models.ForeignKey('badges.Logic', related_name='badges',
8e4e4a4 @zuzelvp [splitting db model changes into smaller commits] Making badges' logic f...
zuzelvp authored
64 help_text=_('Regulates how the badge is awarded to users.'))
b17a89a @zuzelvp Adding global community badges.
zuzelvp authored
65 all_groups = models.BooleanField(default=False)
a71eedc @jledbetter Moved logic to its own model.
jledbetter authored
66 groups = models.ManyToManyField('projects.Project', related_name='badges',
3600909 @zuzelvp Fixing pep8 and pyflake errors.
zuzelvp authored
67 null=True, blank=True)
8cc8259 @zuzelvp More work around challenges completion.
zuzelvp authored
68 creator = models.ForeignKey('users.UserProfile', related_name='badges',
69 blank=True, null=True)
824796d @jledbetter Added creator and updated to the badge
jledbetter authored
70 created_on = models.DateTimeField(auto_now_add=True, blank=False)
697984a @zuzelvp Integrating UI functionality with the new version of django-obi send_bad...
zuzelvp authored
71
cd21814 @jledbetter Started form for creation of badges. Also started rubrics in Django admi...
jledbetter authored
72 def __unicode__(self):
6acbd35 @zuzelvp Multiple badges related changes.
zuzelvp authored
73 return "%s %s" % (self.name, _('Badge'))
cd21814 @jledbetter Started form for creation of badges. Also started rubrics in Django admi...
jledbetter authored
74
7bce587 @jledbetter Awarding continues: check eligibility and see what badges one
jledbetter authored
75 @models.permalink
7948c89 @jledbetter Removed criteria. Added minimum organizer and peer votes needed to badge...
jledbetter authored
76 def get_absolute_url(self):
7bce587 @jledbetter Awarding continues: check eligibility and see what badges one
jledbetter authored
77 return ('badges_show', (), {
78 'slug': self.slug,
79 })
7948c89 @jledbetter Removed criteria. Added minimum organizer and peer votes needed to badge...
jledbetter authored
80
8cc8259 @zuzelvp More work around challenges completion.
zuzelvp authored
81 def get_image_url(self):
82 # TODO: using project's default image until a default badge
83 # image is added.
0f1706f @dirkcuys moved static files out of media directory
dirkcuys authored
84 missing = settings.STATIC_URL + 'images/missing-badge.png'
8cc8259 @zuzelvp More work around challenges completion.
zuzelvp authored
85 image_path = self.image.url if self.image else missing
86 return image_path
87
cd21814 @jledbetter Started form for creation of badges. Also started rubrics in Django admi...
jledbetter authored
88 def save(self):
89 """Make sure each badge has a unique slug."""
90 count = 1
91 if not self.slug:
92 slug = slugify(self.name)
93 self.slug = slug
94 while True:
95 existing = Badge.objects.filter(slug=self.slug)
96 if len(existing) == 0:
97 break
98 self.slug = "%s-%s" % (slug, count + 1)
99 count += 1
100 super(Badge, self).save()
101
7bce587 @jledbetter Awarding continues: check eligibility and see what badges one
jledbetter authored
102 def is_eligible(self, user):
6acbd35 @zuzelvp Multiple badges related changes.
zuzelvp authored
103 """Check if the user eligible for the badge.
104
6552095 @jledbetter Tweaks to Apply for Badge page and View Badge page
jledbetter authored
105 If some prerequisite badges have not been
6acbd35 @zuzelvp Multiple badges related changes.
zuzelvp authored
106 awarded returns False."""
7c550b3 @zuzelvp [splitting db model changes into smaller commits] adding completion_badg...
zuzelvp authored
107 awarded_badges = Award.objects.filter(
108 user=user).values('badge_id')
109 return not self.prerequisites.exclude(
110 id__in=awarded_badges).exists()
7bce587 @jledbetter Awarding continues: check eligibility and see what badges one
jledbetter authored
111
112 def is_awarded_to(self, user):
113 """Does the user have the badge?"""
114 return Award.objects.filter(user=user, badge=self).count() > 0
697984a @zuzelvp Integrating UI functionality with the new version of django-obi send_bad...
zuzelvp authored
115
70ed7ac @zuzelvp [splitting db model changes into smaller commits] Removing progress mode...
zuzelvp authored
116 def award_to(self, user, submission=None):
6acbd35 @zuzelvp Multiple badges related changes.
zuzelvp authored
117 """Award the badge to the user.
a71eedc @jledbetter Moved logic to its own model.
jledbetter authored
118
6acbd35 @zuzelvp Multiple badges related changes.
zuzelvp authored
119 Returns None if no badge is awarded."""
8351471 @zuzelvp [splitting db model changes into smaller commits] moving unique field fr...
zuzelvp authored
120
7c550b3 @zuzelvp [splitting db model changes into smaller commits] adding completion_badg...
zuzelvp authored
121 if not self.is_eligible(user):
12d2932 @jledbetter Removed weighting functionality from badging.
jledbetter authored
122 # If the user is not eligible the badge
6acbd35 @zuzelvp Multiple badges related changes.
zuzelvp authored
123 # is not awarded.
124 return None
aea9cda @jledbetter Added ability to submit for a badge via the browser. Nicer UI coming.
jledbetter authored
125
70ed7ac @zuzelvp [splitting db model changes into smaller commits] Removing progress mode...
zuzelvp authored
126 if self.pending_peer_reviews(user, submission):
127 # The user has not received the necessary satisfactory reviews
128 # for the badge to be awarded.
6acbd35 @zuzelvp Multiple badges related changes.
zuzelvp authored
129 return None
aea9cda @jledbetter Added ability to submit for a badge via the browser. Nicer UI coming.
jledbetter authored
130
d4f9fe0 @zuzelvp [splitting db model changes into smaller commits] Adding pending field t...
zuzelvp authored
131 if submission:
132 submission.pending = False
133 submission.save()
134
8351471 @zuzelvp [splitting db model changes into smaller commits] moving unique field fr...
zuzelvp authored
135 if self.logic.unique:
6acbd35 @zuzelvp Multiple badges related changes.
zuzelvp authored
136 # Do not award the badge if the user can not have the badge
137 # more than once and it was already awarded.
138 award, created = Award.objects.get_or_create(user=user,
139 badge=self)
140 return award if created else None
cd21814 @jledbetter Started form for creation of badges. Also started rubrics in Django admi...
jledbetter authored
141
6acbd35 @zuzelvp Multiple badges related changes.
zuzelvp authored
142 return Award.objects.create(user=user, badge=self)
5a12c29 @jledbetter Award and assessment view updates
jledbetter authored
143
70ed7ac @zuzelvp [splitting db model changes into smaller commits] Removing progress mode...
zuzelvp authored
144 def pending_peer_reviews(self, user, submission):
145 if not self.logic.min_votes:
146 return False
147 assessments = Assessment.objects.filter(badge=self,
148 assessed=user, ready=True)
149 if submission:
150 assessments = assessments.filter(submission=submission)
151 else:
152 assessments = assessments.filter(submission__isnull=True)
153 if assessments.count() < self.logic.min_votes:
154 # More votes needed.
155 return True
156 if not self.logic.min_avg_rating:
157 return False
158 avg_rating = Assessment.compute_average_rating(assessments)
159 if avg_rating < self.logic.min_avg_rating:
160 # Rating too low.
161 return True
162
5a12c29 @jledbetter Award and assessment view updates
jledbetter authored
163 def get_pending_submissions(self):
164 """Submissions of users who haven't received the award yet"""
d4f9fe0 @zuzelvp [splitting db model changes into smaller commits] Adding pending field t...
zuzelvp authored
165 return Submission.objects.filter(badge=self, pending=True)
5a12c29 @jledbetter Award and assessment view updates
jledbetter authored
166
645b547 @zuzelvp Allowing users to give peer community badges to their peers.
zuzelvp authored
167 def get_peers(self, profile):
168 from projects.models import Participation
169 from users.models import UserProfile
170 user_projects = Participation.objects.filter(
171 user=profile).values('project__id')
172 peers = Participation.objects.filter(
b17a89a @zuzelvp Adding global community badges.
zuzelvp authored
173 project__in=user_projects)
174 if not self.all_groups:
175 badge_projects = self.groups.values('id')
176 peers = peers.filter(project__in=badge_projects)
645b547 @zuzelvp Allowing users to give peer community badges to their peers.
zuzelvp authored
177 return UserProfile.objects.filter(deleted=False,
b17a89a @zuzelvp Adding global community badges.
zuzelvp authored
178 id__in=peers.values('user__id')).exclude(
179 id=profile.id)
645b547 @zuzelvp Allowing users to give peer community badges to their peers.
zuzelvp authored
180
968efbe @zuzelvp Filtering skill and peer badges and loadding badges dinamically to the o...
zuzelvp authored
181 def other_badges_can_apply_for(self):
182 badges = Badge.objects.exclude(
2b1f127 @zuzelvp Removing badge_type field.
zuzelvp authored
183 id=self.id).exclude(
184 logic__submission_style=Logic.NO_SUBMISSIONS)
968efbe @zuzelvp Filtering skill and peer badges and loadding badges dinamically to the o...
zuzelvp authored
185 badge_groups = self.groups.values('id')
186 related_badges = badges.filter(
187 groups__in=badge_groups).distinct()
188 non_related_badges = badges.exclude(
189 groups__in=badge_groups).distinct()
190 return MultiQuerySet(related_badges, non_related_badges)
5a12c29 @jledbetter Award and assessment view updates
jledbetter authored
191
2b1f127 @zuzelvp Removing badge_type field.
zuzelvp authored
192 def can_post_submission(self, user):
193 if self.logic.submission_style == Logic.NO_SUBMISSIONS:
194 return False
195 if user.is_authenticated():
196 profile = user.get_profile()
39080e3 @dirkcuys Don't display or accept submissions from deleted profiles
dirkcuys authored
197 if not profile.can_post():
198 return False
2b1f127 @zuzelvp Removing badge_type field.
zuzelvp authored
199 if not self.is_eligible(profile):
200 return False
201 awards = Award.objects.filter(user=profile, badge=self)
202 if self.logic.unique and awards.exists():
203 return False
204 return True
205 else:
206 return False
207
208 def can_give_to_peer(self, user):
209 if not user.is_authenticated():
210 return False
211 if self.logic.submission_style == Logic.SUBMISSION_REQUIRED:
212 return False
213 if self.logic.min_votes != 1 or self.logic.min_avg_rating > 0:
214 return False
215 return True
216
cdadd98 @zuzelvp Merging/finishing submissions list tabs.
zuzelvp authored
217 def can_review_submission(self, submission, user):
15b76bc @dirkcuys Allow multiple reviews of a submission if the badge is not unique
dirkcuys authored
218 # only authenticated users can review a submission
219 if not user.is_authenticated():
cdadd98 @zuzelvp Merging/finishing submissions list tabs.
zuzelvp authored
220 return False
15b76bc @dirkcuys Allow multiple reviews of a submission if the badge is not unique
dirkcuys authored
221 profile = user.get_profile()
e66b196 @dirkcuys small changes to make pyflakes complain less
dirkcuys authored
222
15b76bc @dirkcuys Allow multiple reviews of a submission if the badge is not unique
dirkcuys authored
223 # user cannot review his/her own submission
224 if profile == submission.author:
225 return False
e66b196 @dirkcuys small changes to make pyflakes complain less
dirkcuys authored
226
15b76bc @dirkcuys Allow multiple reviews of a submission if the badge is not unique
dirkcuys authored
227 # if this is a unique badge, only allow one review of submission
e66b196 @dirkcuys small changes to make pyflakes complain less
dirkcuys authored
228 if self.logic.unique and not submission.pending:
15b76bc @dirkcuys Allow multiple reviews of a submission if the badge is not unique
dirkcuys authored
229 return False
e66b196 @dirkcuys small changes to make pyflakes complain less
dirkcuys authored
230
231 # user can only submit one review
15b76bc @dirkcuys Allow multiple reviews of a submission if the badge is not unique
dirkcuys authored
232 assessments = submission.assessments.filter(
233 assessor=profile)
234 if assessments.exists():
235 return False
e66b196 @dirkcuys small changes to make pyflakes complain less
dirkcuys authored
236
15b76bc @dirkcuys Allow multiple reviews of a submission if the badge is not unique
dirkcuys authored
237 return True
cdadd98 @zuzelvp Merging/finishing submissions list tabs.
zuzelvp authored
238
1df36eb @zuzelvp New email notifications to adopters of challenges related to a badge sub...
zuzelvp authored
239 def get_adopters(self):
240 from projects.models import Participation
ce42145 @zuzelvp In between releases fixes.
zuzelvp authored
241 from users.models import UserProfile
242 adopters = Participation.objects.filter(
1df36eb @zuzelvp New email notifications to adopters of challenges related to a badge sub...
zuzelvp authored
243 project__in=self.groups.values('id'),
244 left_on__isnull=True).filter(
ce42145 @zuzelvp In between releases fixes.
zuzelvp authored
245 Q(adopter=True) | Q(organizing=True)).values(
246 'user_id').distinct()
247 return UserProfile.objects.filter(id__in=adopters)
1df36eb @zuzelvp New email notifications to adopters of challenges related to a badge sub...
zuzelvp authored
248
2b1f127 @zuzelvp Removing badge_type field.
zuzelvp authored
249
5a1acd0 @zuzelvp [splitting db model changes into smaller commits] Using ModelBase.
zuzelvp authored
250 class Rubric(ModelBase):
aea9cda @jledbetter Added ability to submit for a badge via the browser. Nicer UI coming.
jledbetter authored
251 """Criteria for which a badge application is judged"""
cd21814 @jledbetter Started form for creation of badges. Also started rubrics in Django admi...
jledbetter authored
252 question = models.CharField(max_length=200)
37a1da9 @jledbetter Can add rubrics to badges via /admin
jledbetter authored
253
254 def __unicode__(self):
7948c89 @jledbetter Removed criteria. Added minimum organizer and peer votes needed to badge...
jledbetter authored
255 return self.question
256
a71eedc @jledbetter Moved logic to its own model.
jledbetter authored
257
5a1acd0 @zuzelvp [splitting db model changes into smaller commits] Using ModelBase.
zuzelvp authored
258 class Logic(ModelBase):
aea9cda @jledbetter Added ability to submit for a badge via the browser. Nicer UI coming.
jledbetter authored
259 """Representation of the logic behind awarding a badge"""
cf29c02 @zuzelvp Bug fix.
zuzelvp authored
260 name = models.CharField(max_length=40)
92f0d86 @zuzelvp More badges related changes.
zuzelvp authored
261 min_votes = models.PositiveIntegerField(
262 help_text=_('Minimum number of votes.'),
9ce3429 @zuzelvp [splitting db model changes into smaller commits] changind badge logic d...
zuzelvp authored
263 default=0)
12d2932 @jledbetter Removed weighting functionality from badging.
jledbetter authored
264 min_avg_rating = models.PositiveIntegerField(
265 help_text=_('Minimum average rating.'),
9ce3429 @zuzelvp [splitting db model changes into smaller commits] changind badge logic d...
zuzelvp authored
266 default=0)
8351471 @zuzelvp [splitting db model changes into smaller commits] moving unique field fr...
zuzelvp authored
267 unique = models.BooleanField(
268 help_text=_('If the badge can only be awarded to the user once.'),
269 default=False)
fc88319 @zuzelvp [splitting db model changes into smaller commits] Adding submission_styl...
zuzelvp authored
270 SUBMISSION_REQUIRED = 'submission_required'
271 SUBMISSION_OPTIONAL = 'submission_optional'
272 NO_SUBMISSIONS = 'no_submissions'
273 SUBMISSION_STYLE_CHOICES = (
274 (SUBMISSION_REQUIRED, _('Submission Required')),
275 (SUBMISSION_OPTIONAL, _('Submission Optional')),
276 (NO_SUBMISSIONS, _('No submissions'))
277 )
278 submission_style = models.CharField(max_length=30,
279 choices=SUBMISSION_STYLE_CHOICES,
280 default=NO_SUBMISSIONS)
a71eedc @jledbetter Moved logic to its own model.
jledbetter authored
281
282 def __unicode__(self):
2b1f127 @zuzelvp Removing badge_type field.
zuzelvp authored
283 return self.name
92f0d86 @zuzelvp More badges related changes.
zuzelvp authored
284
a71eedc @jledbetter Moved logic to its own model.
jledbetter authored
285
aea9cda @jledbetter Added ability to submit for a badge via the browser. Nicer UI coming.
jledbetter authored
286 class Submission(ModelBase):
287 """Application for a badge"""
3600909 @zuzelvp Fixing pep8 and pyflake errors.
zuzelvp authored
288 # TODO Refactor this and PageComment and Assessment?
289 # to extend off same base
3bf2e7c @jledbetter Continuing on submission UI and added url to work to the submission form
jledbetter authored
290 url = models.URLField(max_length=1023)
aea9cda @jledbetter Added ability to submit for a badge via the browser. Nicer UI coming.
jledbetter authored
291 content = RichTextField(config_name='rich', blank=False)
292 author = models.ForeignKey('users.UserProfile', related_name='submissions')
293 badge = models.ForeignKey('badges.Badge', related_name="submissions")
3600909 @zuzelvp Fixing pep8 and pyflake errors.
zuzelvp authored
294 created_on = models.DateTimeField(auto_now_add=True,
295 default=datetime.datetime.now)
d4f9fe0 @zuzelvp [splitting db model changes into smaller commits] Adding pending field t...
zuzelvp authored
296 pending = models.BooleanField(default=True)
aea9cda @jledbetter Added ability to submit for a badge via the browser. Nicer UI coming.
jledbetter authored
297
298 def __unicode__(self):
d3b0ace @zuzelvp Updating spanish translation.
zuzelvp authored
299 return _('%(author)s\'s application for %(badge)s') % {
300 'author': self.author, 'badge': self.badge}
aea9cda @jledbetter Added ability to submit for a badge via the browser. Nicer UI coming.
jledbetter authored
301
302 @models.permalink
303 def get_absolute_url(self):
c357c8b @jledbetter Tweaked a few pages: create a submission for a badge award, and view
jledbetter authored
304 return ('submission_show', (), {
6acbd35 @zuzelvp Multiple badges related changes.
zuzelvp authored
305 'slug': self.badge.slug,
aea9cda @jledbetter Added ability to submit for a badge via the browser. Nicer UI coming.
jledbetter authored
306 'submission_id': self.id,
307 })
308
1df36eb @zuzelvp New email notifications to adopters of challenges related to a badge sub...
zuzelvp authored
309 def send_notification(self):
310 """Send notification when a new submission is posted."""
c1297d6 @zuzelvp Refactoring notification emails.
zuzelvp authored
311 subject_template = 'badges/emails/new_submission_subject.txt'
312 body_template = 'badges/emails/new_submission.txt'
1df36eb @zuzelvp New email notifications to adopters of challenges related to a badge sub...
zuzelvp authored
313 context = {
314 'submission': self,
315 'domain': Site.objects.get_current().domain,
316 }
ce42145 @zuzelvp In between releases fixes.
zuzelvp authored
317 profiles = self.badge.get_adopters()
bfa2bbd @dirkcuys Revised method for unsubscribing from notifications
dirkcuys authored
318 send_notifications_i18n(
319 profiles, subject_template, body_template, context,
320 notification_category=u'badge-submission.badge-{0}'.format(self.badge.slug)
321 )
1df36eb @zuzelvp New email notifications to adopters of challenges related to a badge sub...
zuzelvp authored
322
aea9cda @jledbetter Added ability to submit for a badge via the browser. Nicer UI coming.
jledbetter authored
323
324 class Assessment(ModelBase):
325 """Assessment for a badge"""
c90dc55 @zuzelvp [splitting db model changes into smaller commits] adding weight field to...
zuzelvp authored
326 final_rating = models.FloatField(default=0)
327 weight = models.FloatField(default=1,
328 help_text=_("Allows to give more or less weight to the assessor's vote."))
3600909 @zuzelvp Fixing pep8 and pyflake errors.
zuzelvp authored
329 assessor = models.ForeignKey('users.UserProfile',
330 related_name='assessments')
331 assessed = models.ForeignKey('users.UserProfile',
332 related_name='badge_assessments')
aea9cda @jledbetter Added ability to submit for a badge via the browser. Nicer UI coming.
jledbetter authored
333 comment = RichTextField(config_name='rich', blank=False)
334 badge = models.ForeignKey('badges.Badge', related_name="assessments")
3600909 @zuzelvp Fixing pep8 and pyflake errors.
zuzelvp authored
335 created_on = models.DateTimeField(auto_now_add=True,
336 default=datetime.datetime.now)
337 submission = models.ForeignKey('badges.Submission',
338 related_name="assessments", null=True, blank=True,
339 help_text=_('If submission is blank, this is a '\
340 'peer awarded assessment or superuser granted'))
46ac588 @zuzelvp [splitting db model changes into smaller commits] adding ready field to ...
zuzelvp authored
341 ready = models.BooleanField(default=False,
342 help_text=_("If all rubric ratings were provided."))
3600909 @zuzelvp Fixing pep8 and pyflake errors.
zuzelvp authored
343
c90dc55 @zuzelvp [splitting db model changes into smaller commits] adding weight field to...
zuzelvp authored
344 def __unicode__(self):
345 return _('%(assessor)s for %(assessed)s for %(badge)s') % {
346 'assessor': self.assessor, 'assessed': self.assessed,
347 'badge': self.badge}
348
349 @models.permalink
350 def get_absolute_url(self):
351 return ('assessment_show', (), {
352 'slug': self.badge.slug,
353 'assessment_id': self.id,
354 })
355
81ebeea @jledbetter Added rated rubrics to view of assessment
jledbetter authored
356 def final_rating_as_percentage(self):
3600909 @zuzelvp Fixing pep8 and pyflake errors.
zuzelvp authored
357 """Return the final rating as a percentage for
81ebeea @jledbetter Added rated rubrics to view of assessment
jledbetter authored
358 styling of assessment view. Max number of ratings
359 is 4"""
3600909 @zuzelvp Fixing pep8 and pyflake errors.
zuzelvp authored
360 return (self.final_rating / 4.0) * 100
aea9cda @jledbetter Added ability to submit for a badge via the browser. Nicer UI coming.
jledbetter authored
361
6acbd35 @zuzelvp Multiple badges related changes.
zuzelvp authored
362 def get_final_rating_display(self):
363 rating_position = int(round(self.final_rating)) - 1
12d2932 @jledbetter Removed weighting functionality from badging.
jledbetter authored
364 # Guarantee rating_position does not go above or below
6acbd35 @zuzelvp Multiple badges related changes.
zuzelvp authored
365 # the boundaries.
366 if rating_position < 0:
367 rating_position = 0
368 max_index = len(Rating.RATING_CHOICES) - 1
369 if rating_position > max_index:
370 rating_position = max_index
371 return Rating.RATING_CHOICES[rating_position][1]
372
5a12c29 @jledbetter Award and assessment view updates
jledbetter authored
373 def update_final_rating(self):
81ebeea @jledbetter Added rated rubrics to view of assessment
jledbetter authored
374 """Used on Rating save signal to update the final
375 rating for the assessment"""
70ed7ac @zuzelvp [splitting db model changes into smaller commits] Removing progress mode...
zuzelvp authored
376 if self.ready:
377 return
5a12c29 @jledbetter Award and assessment view updates
jledbetter authored
378 ratings = Rating.objects.filter(assessment=self)
46ac588 @zuzelvp [splitting db model changes into smaller commits] adding ready field to ...
zuzelvp authored
379 self.final_rating = ratings.aggregate(
8351471 @zuzelvp [splitting db model changes into smaller commits] moving unique field fr...
zuzelvp authored
380 final_rating=Avg('score'))['final_rating'] or 0
46ac588 @zuzelvp [splitting db model changes into smaller commits] adding ready field to ...
zuzelvp authored
381 if ratings.count() == self.badge.rubrics.count():
382 self.ready = True
5a12c29 @jledbetter Award and assessment view updates
jledbetter authored
383 self.save()
bb68872 @zuzelvp Do not call badge.award_to is submission is not pending.
zuzelvp authored
384 if self.submission and not self.submission.pending:
385 return
70ed7ac @zuzelvp [splitting db model changes into smaller commits] Removing progress mode...
zuzelvp authored
386 if self.ready:
387 self.badge.award_to(self.assessed, self.submission)
5a12c29 @jledbetter Award and assessment view updates
jledbetter authored
388
c90dc55 @zuzelvp [splitting db model changes into smaller commits] adding weight field to...
zuzelvp authored
389 @classmethod
390 def compute_average_rating(cls, assessments):
391 ratings_sum = 0
392 weights_sum = 0
393 for assessment in assessments:
394 ratings_sum += assessment.final_rating
395 weights_sum += assessment.weight
e66b196 @dirkcuys small changes to make pyflakes complain less
dirkcuys authored
396 return ratings_sum / weights_sum if weights_sum > 0 else 0
8567932 @zuzelvp Adding more badges, fixing bugs and including more of the missing badges...
zuzelvp authored
397
aea9cda @jledbetter Added ability to submit for a badge via the browser. Nicer UI coming.
jledbetter authored
398
399 class Rating(ModelBase):
400 """Assessor's rating for the assessment's rubric(s)"""
84a8417 @jledbetter Beginning of rubrics for the assessment view
jledbetter authored
401
402 NEVER = 1
403 SOMETIMES = 2
404 MOST_OF_THE_TIME = 3
405 ALWAYS = 4
406
3600909 @zuzelvp Fixing pep8 and pyflake errors.
zuzelvp authored
407 RATING_CHOICES = (
6acbd35 @zuzelvp Multiple badges related changes.
zuzelvp authored
408 (NEVER, _('Never')),
409 (SOMETIMES, _('Sometimes')),
410 (MOST_OF_THE_TIME, _('Most of the time')),
411 (ALWAYS, _('Always'))
3600909 @zuzelvp Fixing pep8 and pyflake errors.
zuzelvp authored
412 )
84a8417 @jledbetter Beginning of rubrics for the assessment view
jledbetter authored
413
5a12c29 @jledbetter Award and assessment view updates
jledbetter authored
414 assessment = models.ForeignKey('badges.Assessment', related_name='ratings')
81ebeea @jledbetter Added rated rubrics to view of assessment
jledbetter authored
415 score = models.PositiveIntegerField(default=1, choices=RATING_CHOICES)
aea9cda @jledbetter Added ability to submit for a badge via the browser. Nicer UI coming.
jledbetter authored
416 rubric = models.ForeignKey('badges.Rubric', related_name='ratings')
417
418 def __unicode__(self):
d3b0ace @zuzelvp Updating spanish translation.
zuzelvp authored
419 return _('%(score)s for %(rubric)s') % {
420 'score': self.score,
421 'rubric': self.rubric
422 }
aea9cda @jledbetter Added ability to submit for a badge via the browser. Nicer UI coming.
jledbetter authored
423
81ebeea @jledbetter Added rated rubrics to view of assessment
jledbetter authored
424 def score_as_percentage(self):
3600909 @zuzelvp Fixing pep8 and pyflake errors.
zuzelvp authored
425 """Return the score as a percentage for
81ebeea @jledbetter Added rated rubrics to view of assessment
jledbetter authored
426 styling of assessment view. Max number of ratings
427 is 4"""
3600909 @zuzelvp Fixing pep8 and pyflake errors.
zuzelvp authored
428 return (self.score / 4.0) * 100
429
aea9cda @jledbetter Added ability to submit for a badge via the browser. Nicer UI coming.
jledbetter authored
430
5a1acd0 @zuzelvp [splitting db model changes into smaller commits] Using ModelBase.
zuzelvp authored
431 class Award(ModelBase):
aea9cda @jledbetter Added ability to submit for a badge via the browser. Nicer UI coming.
jledbetter authored
432 """Representation of a badge a user has received"""
d3711bf @jledbetter Added the start of the awarding
jledbetter authored
433 user = models.ForeignKey('users.UserProfile')
434 badge = models.ForeignKey('badges.Badge', related_name="awards")
435 awarded_on = models.DateTimeField(auto_now_add=True, blank=False)
aea9cda @jledbetter Added ability to submit for a badge via the browser. Nicer UI coming.
jledbetter authored
436
437 def __unicode__(self):
d3b0ace @zuzelvp Updating spanish translation.
zuzelvp authored
438 return _('%(user)s - %(badge)s') % {'user': self.user,
439 'badge': self.badge}
5a12c29 @jledbetter Award and assessment view updates
jledbetter authored
440
92f0d86 @zuzelvp More badges related changes.
zuzelvp authored
441 @models.permalink
442 def get_absolute_url(self):
443 # All the awards of the same badge to the same user
444 # share one awards page.
445 return ('user_awards_show', (), {
446 'slug': self.badge.slug,
447 'username': self.user.username,
448 })
449
5a12c29 @jledbetter Award and assessment view updates
jledbetter authored
450
451 ###########
452 # Signals #
453 ###########
454
1df36eb @zuzelvp New email notifications to adopters of challenges related to a badge sub...
zuzelvp authored
455
456 def post_submission_save(sender, **kwargs):
457 instance = kwargs.get('instance', None)
458 created = kwargs.get('created', False)
459 if created and isinstance(instance, Submission):
460 instance.send_notification()
461
462 post_save.connect(post_submission_save, sender=Submission,
463 dispatch_uid='badges_post_submission_save')
Something went wrong with that request. Please try again.