Reputation system
authorCharles Connell <charles@connells.org>
Thu, 23 Jan 2014 00:17:39 +0000 (19:17 -0500)
committerCharles Connell <charles@connells.org>
Thu, 23 Jan 2014 00:17:52 +0000 (19:17 -0500)
karmaworld/apps/courses/views.py
karmaworld/apps/notes/gdrive.py
karmaworld/apps/notes/models.py
karmaworld/apps/notes/views.py
karmaworld/apps/users/admin.py
karmaworld/apps/users/migrations/0003_auto__add_coursekarmaevent__add_notekarmaevent__add_generickarmaevent_.py [new file with mode: 0644]
karmaworld/apps/users/models.py
karmaworld/apps/users/views.py
karmaworld/assets/css/global.css
karmaworld/templates/user_profile.html

index f23a68974211efbd2e90693ad55e4b54e3eba494..d6957fde63e2c3d169f7889c4baf832ab89e112e 100644 (file)
@@ -19,6 +19,7 @@ from karmaworld.apps.courses.forms import CourseForm
 from karmaworld.apps.courses.models import Course
 from karmaworld.apps.courses.models import School
 from karmaworld.apps.notes.models import Note
+from karmaworld.apps.users.models import CourseKarmaEvent
 
 
 class CourseListView(ListView, ModelFormMixin, ProcessFormView):
@@ -179,24 +180,46 @@ def school_course_instructor_list(request):
     return HttpResponse(json.dumps({'status':'success', 'instructors': instructors}),
                         mimetype="application/json")
 
-def ajaxIncrementBase(request, pk, field):
-    """Increment a note's field by one."""
+
+def format_session_increment_field(id, field):
+    return field + '-' + str(id)
+
+
+def ajaxIncrementBase(request, pk, field, event_processor=None):
+    """Increment a course's field by one."""
     if not (request.method == 'POST' and request.is_ajax()):
         # return that the api call failed
         return HttpResponseBadRequest(json.dumps({'status': 'fail', 'message': 'must be a POST ajax request'}),
-                                    mimetype="application/json")
+                                      mimetype="application/json")
 
     try:
-        note = Course.objects.get(pk=pk)
-        note.__dict__[field] += 1
-        note.save()
+        course = Course.objects.get(pk=pk)
+        count = getattr(course, field)
+        setattr(course, field,  count+1)
+        course.save()
+
+        event_processor(request.user, course)
+
+        # Record that user has performed this, to prevent
+        # them from doing it again
+        request.session[format_session_increment_field(pk, field)] = True
     except ObjectDoesNotExist:
-        return HttpResponseNotFound(json.dumps({'status': 'fail', 'message': 'note id does not match a note'}),
+        return HttpResponseNotFound(json.dumps({'status': 'fail', 'message': 'course id does not match a course'}),
                                     mimetype="application/json")
 
     return HttpResponse(status=204)
 
+def process_course_flag_events(request_user, course):
+    # Take a point away from person flagging this course
+    if request_user.is_authenticated():
+        CourseKarmaEvent.create_event(request_user, course, CourseKarmaEvent.GIVE_FLAG)
+    # If this is the 6th time this course has been flagged,
+    # punish the uploader
+    if course.flags == 6:
+        CourseKarmaEvent.create_event(course.user, course, CourseKarmaEvent.GET_FLAGGED)
+
+
 def flag_course(request, pk):
     """Record that somebody has flagged a note."""
-    return ajaxIncrementBase(request, pk, 'flags')
+    return ajaxIncrementBase(request, pk, 'flags', process_course_flag_events)
 
index 128a5885933db6604f950b8379b836949a24fc9c..ade782bad36d26ba359a285a3fa8d37fabbc1841 100644 (file)
@@ -6,6 +6,7 @@ import datetime
 from django.contrib.auth.models import User
 from django.contrib.sessions.backends.db import SessionStore
 from django.core.exceptions import ObjectDoesNotExist
+from karmaworld.apps.users.models import NoteKarmaEvent
 import os
 import subprocess
 import tempfile
@@ -243,6 +244,7 @@ def convert_raw_document(raw_document, user=None, session=None):
     # associate them with the note
     if user:
         note.user = user
+        NoteKarmaEvent.create_event(user, note, NoteKarmaEvent.UPLOAD)
 
     # Finally, save whatever data we got back from google
     note.save()
@@ -255,6 +257,7 @@ def convert_raw_document(raw_document, user=None, session=None):
             uid = session[SESSION_KEY]
             user = User.objects.get(pk=uid)
             note.user = user
+            NoteKarmaEvent.create_event(user, note, NoteKarmaEvent.UPLOAD)
             note.save()
         # If we don't know the user who uploaded
         # this, then we should have a session key
index 93ab3f31f91d92aceab812fc9f353717ec873fcc..4a58f752ff712a89b8db7b5f2c0a1d08a84e7939 100644 (file)
@@ -17,6 +17,7 @@ from django.db.models import SET_NULL
 from django.db.models.signals import post_save, post_delete, pre_save
 from django.dispatch import receiver
 from karmaworld.apps.notes.gdrive import UPLOADED_NOTES_SESSION_KEY
+from karmaworld.apps.users.models import NoteKarmaEvent, GenericKarmaEvent
 import os
 import urllib
 
@@ -386,6 +387,9 @@ def note_delete_receiver(sender, **kwargs):
     index = SearchIndex()
     index.remove_note(note)
 
+    delete_message = 'Your note "{n}" was deleted'.format(n=note.name)
+    GenericKarmaEvent.create_event(note.user, delete_message, -5)
+
 @receiver(user_logged_in, weak=True)
 def find_orphan_notes(sender, **kwargs):
     user = kwargs['user']
index 98d476b13267e9c8297e3660fe26c889a4ca14a0..bc66cb1762266835a2585f211d9ff7c7be6b7bb8 100644 (file)
@@ -8,6 +8,7 @@ import logging
 from django.core.exceptions import ObjectDoesNotExist
 from karmaworld.apps.courses.models import Course
 from karmaworld.apps.notes.search import SearchIndex
+from karmaworld.apps.users.models import NoteKarmaEvent
 
 import os
 
@@ -229,7 +230,7 @@ class NoteSearchView(ListView):
         return super(NoteSearchView, self).get_context_data(**kwargs)
 
 
-def ajaxIncrementBase(request, pk, field):
+def ajaxIncrementBase(request, pk, field, event_processor=None):
     """Increment a note's field by one."""
     if not (request.method == 'POST' and request.is_ajax()):
         # return that the api call failed
@@ -243,6 +244,8 @@ def ajaxIncrementBase(request, pk, field):
         setattr(note, field,  count+1)
         note.save()
 
+        event_processor(request.user, note)
+
         # Record that user has performed this, to prevent
         # them from doing it again
         request.session[format_session_increment_field(pk, field)] = True
@@ -252,12 +255,30 @@ def ajaxIncrementBase(request, pk, field):
 
     return HttpResponse(status=204)
 
+
+def process_note_thank_events(request_user, note):
+    # Give points to the person who uploaded this note
+    if note.user != request_user:
+        NoteKarmaEvent.create_event(note.user, note, NoteKarmaEvent.THANKS)
+
+
 def thank_note(request, pk):
     """Record that somebody has thanked a note."""
-    return ajaxIncrementBase(request, pk, THANKS_FIELD)
+    return ajaxIncrementBase(request, pk, THANKS_FIELD, process_note_thank_events)
+
+
+def process_note_flag_events(request_user, note):
+    # Take a point away from person flagging this note
+    if request_user.is_authenticated():
+        NoteKarmaEvent.create_event(request_user, note, NoteKarmaEvent.GIVE_FLAG)
+    # If this is the 6th time this note has been flagged,
+    # punish the uploader
+    if note.flags == 6:
+        NoteKarmaEvent.create_event(note.user, note, NoteKarmaEvent.GET_FLAGGED)
+
 
 def flag_note(request, pk):
     """Record that somebody has flagged a note."""
-    return ajaxIncrementBase(request, pk, FLAG_FIELD)
+    return ajaxIncrementBase(request, pk, FLAG_FIELD, process_note_flag_events)
 
 
index db328157be5fee2051edea7aaebaa1d1cecd70f3..2e29b1668863200b863360f2acf063e1726bf1be 100644 (file)
@@ -2,6 +2,10 @@
 # -*- coding:utf8 -*-
 # Copyright (C) 2013  FinalsClub Foundation
 from django.contrib import admin
-from karmaworld.apps.users.models import UserProfile
+from karmaworld.apps.users.models import UserProfile, NoteKarmaEvent, CourseKarmaEvent, GenericKarmaEvent
 
-admin.site.register(UserProfile)
\ No newline at end of file
+
+admin.site.register(UserProfile)
+admin.site.register(GenericKarmaEvent)
+admin.site.register(NoteKarmaEvent)
+admin.site.register(CourseKarmaEvent)
diff --git a/karmaworld/apps/users/migrations/0003_auto__add_coursekarmaevent__add_notekarmaevent__add_generickarmaevent_.py b/karmaworld/apps/users/migrations/0003_auto__add_coursekarmaevent__add_notekarmaevent__add_generickarmaevent_.py
new file mode 100644 (file)
index 0000000..3188b6a
--- /dev/null
@@ -0,0 +1,217 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding model 'CourseKarmaEvent'
+        db.create_table(u'users_coursekarmaevent', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('points', self.gf('django.db.models.fields.IntegerField')()),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
+            ('timestamp', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.utcnow)),
+            ('course', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['courses.Course'])),
+            ('event_type', self.gf('django.db.models.fields.CharField')(max_length=15)),
+        ))
+        db.send_create_signal(u'users', ['CourseKarmaEvent'])
+
+        # Adding model 'NoteKarmaEvent'
+        db.create_table(u'users_notekarmaevent', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('points', self.gf('django.db.models.fields.IntegerField')()),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
+            ('timestamp', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.utcnow)),
+            ('note', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['notes.Note'])),
+            ('event_type', self.gf('django.db.models.fields.CharField')(max_length=15)),
+        ))
+        db.send_create_signal(u'users', ['NoteKarmaEvent'])
+
+        # Adding model 'GenericKarmaEvent'
+        db.create_table(u'users_generickarmaevent', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('points', self.gf('django.db.models.fields.IntegerField')()),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
+            ('timestamp', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.utcnow)),
+            ('message', self.gf('django.db.models.fields.CharField')(max_length=255)),
+        ))
+        db.send_create_signal(u'users', ['GenericKarmaEvent'])
+
+        # Deleting field 'UserProfile.karma'
+        db.delete_column(u'users_userprofile', 'karma')
+
+
+    def backwards(self, orm):
+        # Deleting model 'CourseKarmaEvent'
+        db.delete_table(u'users_coursekarmaevent')
+
+        # Deleting model 'NoteKarmaEvent'
+        db.delete_table(u'users_notekarmaevent')
+
+        # Deleting model 'GenericKarmaEvent'
+        db.delete_table(u'users_generickarmaevent')
+
+        # Adding field 'UserProfile.karma'
+        db.add_column(u'users_userprofile', 'karma',
+                      self.gf('django.db.models.fields.IntegerField')(default=0),
+                      keep_default=False)
+
+
+    models = {
+        u'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        u'auth.permission': {
+            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        u'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        u'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'courses.course': {
+            'Meta': {'ordering': "['-file_count', 'school', 'name']", 'unique_together': "(('name', 'department'),)", 'object_name': 'Course'},
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'department': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['courses.Department']", 'null': 'True', 'blank': 'True'}),
+            'desc': ('django.db.models.fields.TextField', [], {'max_length': '511', 'null': 'True', 'blank': 'True'}),
+            'file_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'flags': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'instructor_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+            'instructor_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'school': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['courses.School']", 'null': 'True', 'blank': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '150', 'null': 'True'}),
+            'updated_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '511', 'null': 'True', 'blank': 'True'})
+        },
+        u'courses.department': {
+            'Meta': {'unique_together': "(('name', 'school'),)", 'object_name': 'Department'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'school': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['courses.School']"}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '150', 'null': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '511', 'null': 'True', 'blank': 'True'})
+        },
+        u'courses.school': {
+            'Meta': {'ordering': "['-file_count', '-priority', 'name']", 'object_name': 'School'},
+            'alias': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'facebook_id': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'file_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'hashtag': ('django.db.models.fields.CharField', [], {'max_length': '16', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'location': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'priority': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '150', 'null': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '511', 'blank': 'True'}),
+            'usde_id': ('django.db.models.fields.BigIntegerField', [], {'unique': 'True', 'null': 'True', 'blank': 'True'})
+        },
+        u'licenses.license': {
+            'Meta': {'object_name': 'License'},
+            'html': ('django.db.models.fields.TextField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'})
+        },
+        u'notes.note': {
+            'Meta': {'ordering': "['-uploaded_at']", 'unique_together': "(('fp_file', 'upstream_link'),)", 'object_name': 'Note'},
+            'course': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['courses.Course']"}),
+            'file_type': ('django.db.models.fields.CharField', [], {'default': "'???'", 'max_length': '15', 'null': 'True', 'blank': 'True'}),
+            'flags': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'fp_file': ('django_filepicker.models.FPFileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+            'gdrive_url': ('django.db.models.fields.URLField', [], {'max_length': '1024', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+            'html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
+            'is_hidden': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'license': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['licenses.License']", 'null': 'True', 'blank': 'True'}),
+            'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'pdf_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
+            'static_html': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'text': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'thanks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'tweeted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow', 'null': 'True'}),
+            'upstream_link': ('django.db.models.fields.URLField', [], {'max_length': '1024', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'year': ('django.db.models.fields.IntegerField', [], {'default': '2014', 'null': 'True', 'blank': 'True'})
+        },
+        u'taggit.tag': {
+            'Meta': {'ordering': "['namespace', 'name']", 'object_name': 'Tag'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
+            'namespace': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100'})
+        },
+        u'taggit.taggeditem': {
+            'Meta': {'object_name': 'TaggedItem'},
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'taggit_taggeditem_tagged_items'", 'to': u"orm['contenttypes.ContentType']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+            'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'taggit_taggeditem_items'", 'to': u"orm['taggit.Tag']"})
+        },
+        u'users.coursekarmaevent': {
+            'Meta': {'object_name': 'CourseKarmaEvent'},
+            'course': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['courses.Course']"}),
+            'event_type': ('django.db.models.fields.CharField', [], {'max_length': '15'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'points': ('django.db.models.fields.IntegerField', [], {}),
+            'timestamp': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
+        },
+        u'users.generickarmaevent': {
+            'Meta': {'object_name': 'GenericKarmaEvent'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'message': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'points': ('django.db.models.fields.IntegerField', [], {}),
+            'timestamp': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
+        },
+        u'users.notekarmaevent': {
+            'Meta': {'object_name': 'NoteKarmaEvent'},
+            'event_type': ('django.db.models.fields.CharField', [], {'max_length': '15'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'note': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['notes.Note']"}),
+            'points': ('django.db.models.fields.IntegerField', [], {}),
+            'timestamp': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
+        },
+        u'users.userprofile': {
+            'Meta': {'object_name': 'UserProfile'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'school': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['courses.School']", 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'})
+        }
+    }
+
+    complete_apps = ['users']
\ No newline at end of file
index fc5030820322b7efadccf5a13ea8b1073b2f2d15..cc34cc6ec6ad79b428619737a21c465fb204c0a7 100644 (file)
@@ -1,13 +1,11 @@
 #!/usr/bin/env python
 # -*- coding:utf8 -*-
 # Copyright (C) 2013  FinalsClub Foundation
-import random
 import logging
-from allauth.account.signals import user_logged_in, user_signed_up, email_confirmed, email_changed, email_added
-from allauth.socialaccount.signals import pre_social_login
+import datetime
 from django.contrib.auth.models import User
-from django.core.exceptions import ObjectDoesNotExist
-from django.db.models.signals import post_save, pre_save
+from django.db.models import Sum
+from django.db.models.signals import post_save
 from django.dispatch import receiver
 from django.db import models, DatabaseError
 from django.middleware.transaction import transaction
@@ -15,16 +13,162 @@ from karmaworld.apps.courses.models import School
 
 logger = logging.getLogger(__name__)
 
+
 class UserProfile(models.Model):
     user      = models.OneToOneField(User)
 
     school    = models.ForeignKey(School, blank=True, null=True)
 
-    karma     = models.IntegerField(default=0)
+    def get_points(self):
+        sum = 0
+        for cls in ALL_KARMA_EVENT_CLASSES:
+            points = cls.objects.filter(user=self.user).aggregate(Sum('points'))['points__sum']
+            if points:
+                sum += points
+
+        return sum
+
+    NO_BADGE = 0
+    PROSPECT = 1
+    BEGINNER = 2
+    TRAINEE = 3
+    APPRENTICE = 4
+    SCHOLAR = 5
+
+    BADGES = (
+        PROSPECT,
+        BEGINNER,
+        TRAINEE,
+        APPRENTICE,
+        SCHOLAR
+    )
+
+    BADGE_NAMES = {
+        PROSPECT: 'Prospect',
+        BEGINNER: 'Beginner',
+        TRAINEE: 'Trainee',
+        APPRENTICE: 'Apprentice',
+        SCHOLAR: 'Scholar'
+    }
+
+    BADGE_THRESHOLDS = {
+        PROSPECT: 10,
+        BEGINNER: 100,
+        TRAINEE: 200,
+        APPRENTICE: 500,
+        SCHOLAR: 1000
+    }
+
+    def get_badge(self):
+        points = self.get_points()
+        highest_badge = self.NO_BADGE
+        for badge in self.BADGES:
+            if points >= self.BADGE_THRESHOLDS[badge]:
+                highest_badge = badge
+        return highest_badge
 
     def __unicode__(self):
         return self.user.__unicode__()
 
+
+class BaseKarmaEvent(models.Model):
+    points    = models.IntegerField()
+    user      = models.ForeignKey(User)
+    timestamp = models.DateTimeField(default=datetime.datetime.utcnow)
+
+    class Meta:
+        abstract = True
+
+    def get_message(self):
+        raise NotImplemented()
+
+
+class GenericKarmaEvent(BaseKarmaEvent):
+    message = models.CharField(max_length=255)
+
+    @staticmethod
+    def create_event(user, message, points):
+        event = GenericKarmaEvent.objects.create(user=user,
+                                                 points=points,
+                                                 message=message)
+        event.save()
+
+    def get_message(self):
+        return self.message
+
+
+class NoteKarmaEvent(BaseKarmaEvent):
+    UPLOAD       = 'upload'
+    THANKS       = 'thanks'
+    NOTE_DELETED = 'deleted'
+    GIVE_FLAG    = 'give_flag'
+    GET_FLAGGED  = 'get_flagged'
+    EVENT_TYPE_CHOICES = (
+        (UPLOAD,       "You uploaded a note"),
+        (THANKS,       "You received a thanks for your note"),
+        (NOTE_DELETED, "Your note was deleted"),
+        (GIVE_FLAG,    "You flagged a note"),
+        (GET_FLAGGED,  "Your note was flagged as spam"),
+    )
+    note = models.ForeignKey('notes.Note')
+    event_type = models.CharField(max_length=15, choices=EVENT_TYPE_CHOICES)
+
+    POINTS = {
+        UPLOAD: 5,
+        THANKS: 1,
+        NOTE_DELETED: -5,
+        GIVE_FLAG: -1,
+        GET_FLAGGED: -100
+    }
+
+    def get_message(self):
+        return self.get_event_type_display()
+
+    def __unicode__(self):
+        return unicode(self.user) + ' -- ' + self.get_event_type_display() + ' -- ' + unicode(self.note)
+
+    @staticmethod
+    def create_event(user, note, type):
+        event = NoteKarmaEvent.objects.create(user=user,
+                                      note=note,
+                                      points=NoteKarmaEvent.POINTS[type],
+                                      event_type=type)
+        event.save()
+
+
+class CourseKarmaEvent(BaseKarmaEvent):
+    GIVE_FLAG    = 'give_flag'
+    GET_FLAGGED  = 'get_flagged'
+    EVENT_TYPE_CHOICES = (
+        (GIVE_FLAG,    "You flagged a course"),
+        (GET_FLAGGED,  "Your course was flagged as spam"),
+    )
+    course = models.ForeignKey('courses.Course')
+    event_type = models.CharField(max_length=15, choices=EVENT_TYPE_CHOICES)
+
+    POINTS = {
+        GIVE_FLAG: -1,
+        GET_FLAGGED: -100
+    }
+
+    def get_message(self):
+        return self.get_event_type_display()
+
+    def __unicode__(self):
+        return unicode(self.user) + ' -- ' + self.get_event_type_display() + ' -- ' + unicode(self.course)
+
+    @staticmethod
+    def create_event(user, course, type):
+        event = CourseKarmaEvent.objects.create(user=user,
+                                      course=course,
+                                      points=CourseKarmaEvent.POINTS[type],
+                                      event_type=type)
+        event.save()
+
+
+ALL_KARMA_EVENT_CLASSES = (GenericKarmaEvent, NoteKarmaEvent, CourseKarmaEvent)
+
+
 def user_display_name(user):
     """Return the best way to display a user's
     name to them on the site."""
@@ -36,6 +180,7 @@ def user_display_name(user):
     else:
         return user.username
 
+
 @receiver(post_save, sender=User, weak=True)
 def create_user_profile(sender, instance, created, **kwargs):
     if created:
index ce51d841fc4693f43a6649e52afbf8d227a8c7a9..e2511e528b129aa51016575bca9f341ad1f7f670 100644 (file)
@@ -1,18 +1,51 @@
 #!/usr/bin/env python
 # -*- coding:utf8 -*-
 # Copyright (C) 2013  FinalsClub Foundation
+from itertools import chain
 
 from django.contrib.auth.models import User
 from django.views.generic import TemplateView
-from django.views.generic.detail import SingleObjectMixin
+from django.views.generic.list import MultipleObjectMixin
+from karmaworld.apps.notes.models import Note
+from karmaworld.apps.users.models import NoteKarmaEvent, CourseKarmaEvent, GenericKarmaEvent
 
 
-class ProfileView(TemplateView, SingleObjectMixin):
+class ProfileView(TemplateView, MultipleObjectMixin):
     model = User
     context_object_name = 'user' # name passed to template
     template_name = 'user_profile.html'
-    object = None
 
-    def get_object(self, queryset=None):
-        return self.request.user
+    @staticmethod
+    def compareProfileItems(a, b):
+        if a.__class__ == Note:
+            timestampA = a.uploaded_at
+        else:
+            timestampA = a.timestamp
+
+        if b.__class__ == Note:
+            timestampB = b.uploaded_at
+        else:
+            timestampB = b.timestamp
+
+        if timestampA < timestampB:
+            return -1
+        elif timestampA > timestampB:
+            return 1
+        else:
+            return 0
+
+    def get_context_data(self, **kwargs):
+        notes = [('note', o) for o in Note.objects.filter(user=self.request.user)]
+        generic_karma_events = [('generic_karma_events', o) for o in GenericKarmaEvent.objects.filter(user=self.request.user)]
+        note_karma_events = [('note_karma_event', o) for o in NoteKarmaEvent.objects.filter(user=self.request.user)]
+        course_karma_events = [('course_karma_event', o) for o in CourseKarmaEvent.objects.filter(user=self.request.user)]
+
+        result_list = sorted(chain(notes, generic_karma_events, note_karma_events, course_karma_events),
+                             cmp=ProfileView.compareProfileItems,
+                             key=lambda o: o[1],
+                             reverse=True)
+
+        kwargs['object_list'] = result_list
+
+        return super(ProfileView, self).get_context_data(**kwargs)
 
index 29caf9b3b4817b3d992df13b40371154d821c768..e4d2d9747fbe4c717ee9a087c35a4ae6f575b511 100644 (file)
@@ -463,6 +463,24 @@ a.course_link, a.course_link:hover, a.school_link, a.school_link:hover, a.prof_l
   padding: 5px 0;
 }
 
+.activity_details_points, .activity_details_points a
+{
+  font-family: "MuseoSlab-300";
+  font-size: 25px;
+  line-height: 1.4em;
+  padding: 5px 0;
+}
+
+.activity_details_points_positive
+{
+  color: green;
+}
+
+.activity_details_points_negative
+{
+  color: red;
+}
+
 .note_name, .note_name a
 {
   font-family: "MuseoSlab-300";
index acc53bdf58445dcff56ea5d11e25f328fe61ac59..dab9388016106bd368d45c2cd3b2ef2c40b5165f 100644 (file)
         </div>
       </div>
       <div class="row">
-        <div class="small-10 columns small-centered center stat_lead_in">
-          you've uploaded
-        </div>
-      </div>
-      <div class="row">
-        <div class="small-10 columns small-centered center stat_number stat_uploaded">
-          {{ user.note_set.count }}
-        </div>
-      </div>
-      <div class="row">
-        <div class="small-10 columns small-centered center stat_object stat_uploaded">
-          notes
+        <div class="small-12 medium-8 large-4 columns small-centered">
+          <div class="row">
+            <div class="small-6 columns center  stat_lead_in">
+              you've earned
+            </div>
+            <div class="small-6 columns center  stat_lead_in">
+              you've uploaded
+            </div>
+          </div>
+          <div class="row">
+            <div class="small-6 columns center  stat_number stat_earned">
+             {{ user.get_profile.get_points }}
+            </div>
+            <div class="small-6 columns  center stat_number stat_uploaded">
+              {{ user.note_set.count }}
+            </div>
+          </div>
+          <div class="row">
+            <div class="small-6 columns center  stat_object stat_earned">
+              karma points
+            </div>
+            <div class="small-6 columns center  stat_object stat_uploaded">
+              notes
+            </div>
+          </div>
         </div>
       </div>
+
     </div>
 
     <div id="activity_container">
         </div>
       {% endif %}
 
-      {% for note in user.note_set.all %}
+      {% for item in object_list %}
 
         <div class="row">
           <div class="small-10 columns small-centered {% cycle '' 'row_alt_on'%}">
 
             <div class="activity_item">
+              {% if item.0 == 'note' %}
+                {% with note=item.1 %}
+                  <div class="row">
+                    <div class="small-12 columns activity_timestamp">
+                      <span class="activity_date">{{ note.uploaded_at|date:"D b j Y" }}</span>
+                    </div>
+                    <div class="small-12 columns activity_details_action">
+                      You uploaded <a href="{{ note.get_absolute_url }}" class="activity_target">{{ note.name }}</a>
+                    </div>
+                    <div class="small-12 columns activity_details_context">
+                      <a href="{{ note.course.get_absolute_url }}" class="course_link">{{ note.course.name }} // {{ note.course.school.name }}</a>
+                    </div>
+                  </div>
+                {% endwith %}
+              {% endif %}
+
+              {% if 'karma_event' in item.0 %}
+                {% with event=item.1 %}
+                  <div class="row">
+                    <div class="small-12 columns activity_timestamp">
+                      <span class="activity_date">{{ event.timestamp|date:"D b j Y" }}</span>
+                    </div>
+                    <div class="small-12 columns activity_details_action">
+                      {% if event.points > 0 %}
+                      <span class="activity_details_points_positive">+{{ event.points }}</span>
+                      {% else %}
+                      <span class="activity_details_points_negative">{{ event.points }}</span>
+                      {% endif %}
+                      {{ event.get_message }}
+                    </div>
+                    {% if item.0 == 'note_karma_event' %}
+                      <div class="small-12 columns activity_details_action">
+                        <a href="{{ event.note.get_absolute_url }}" class="activity_target">{{ event.note.name }}</a>
+                      </div>
+                      <div class="small-12 columns activity_details_context">
+                        <a href="{{ event.note.course.get_absolute_url }}" class="course_link">{{ event.note.course.name }} // {{ event.note.course.school.name }}</a>
+                      </div>
+                    {% endif %}
+                    {% if item.0 == 'course_karma_event' %}
+                      <div class="small-12 columns activity_details_action">
+                        <a href="{{ event.course.get_absolute_url }}" class="activity_target">{{ event.course.name }}</a>
+                      </div>
+                      <div class="small-12 columns activity_details_context">
+                        {{ event.course.school.name }}
+                      </div>
+                    {% endif %}
+
+                  </div>
+                {% endwith %}
+
+              {% endif %}
 
-              <div class="row">
-                <div class="small-12 columns activity_timestamp">
-                  <span class="activity_date">{{ note.uploaded_at|date:"D b j Y" }}</span>
-                </div>
-                <div class="small-12 columns activity_details_action">
-                  You uploaded <a href="{{ note.get_absolute_url }}" class="activity_target">{{ note.name }}</a>
-                </div>
-                <div class="small-12 columns activity_details_context">
-                  <a href="{{ note.course.get_absolute_url }}" class="course_link">{{ note.course.name }} // {{ note.course.school.name }}</a>
-                </div>
-              </div>
 
             </div>