├── .gitignore ├── README.md ├── gamification ├── __init__.py ├── badges │ ├── __init__.py │ ├── admin.py │ ├── badges_notes.txt │ ├── fixtures │ │ └── initial_data.json │ ├── managers.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto__add_field_projectbadge_awardLevel__add_field_projectbadge_multip.py │ │ ├── 0003_auto__chg_field_badge_icon.py │ │ ├── 0004_auto__add_field_projectbadge_tags.py │ │ └── __init__.py │ ├── models.py │ ├── signals.py │ ├── templates │ │ └── badges │ │ │ ├── badge.html │ │ │ ├── badges_display.html │ │ │ ├── detail.html │ │ │ └── overview.html │ ├── templatetags │ │ ├── __init__.py │ │ └── badges_tags.py │ ├── tests.py │ ├── urls.py │ ├── utils.py │ └── views.py ├── core │ ├── __init__.py │ ├── admin.py │ ├── fixtures │ │ └── initial_data.json │ ├── forms.py │ ├── meta_badges.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_initdata.py │ │ ├── 0003_auto__add_field_project_viewing_pass_phrase__add_field_project_query_t.py │ │ ├── 0004_auto__add_team__add_field_project_visual_theme__add_field_project_allo.py │ │ ├── 0005_auto__add_field_project_background_image.py │ │ ├── 0006_auto__add_field_project_properties.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── templates │ │ └── core │ │ │ ├── 404.json │ │ │ ├── award.html │ │ │ ├── badge_list.html │ │ │ ├── base.html │ │ │ ├── index.html │ │ │ ├── master_badge_list.html │ │ │ ├── masterprojects_list.html │ │ │ ├── points_list.html │ │ │ ├── project_admin.html │ │ │ ├── projects.html │ │ │ ├── projects_list.html │ │ │ ├── user_project_points_list.html │ │ │ └── users.html │ ├── templatetags │ │ ├── __init__.py │ │ └── version.py │ ├── tests.py │ ├── urls.py │ ├── utils.py │ └── views.py ├── events │ ├── __init__.py │ ├── admin.py │ ├── experimental │ │ ├── mandatory_training_1.policy │ │ ├── mandatory_training_2.policy │ │ ├── rules_engine.py │ │ └── state.py │ ├── fixtures │ │ ├── camp_data.json │ │ └── initial_data.json │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── state.py │ └── views.py ├── png.py ├── receivers.py ├── requirements.txt ├── settings.py ├── site_media │ └── media │ │ └── badge_images │ │ ├── bronze.png │ │ ├── gold.png │ │ └── silver.png ├── startup.py ├── static │ ├── README │ ├── admin │ │ └── css │ │ │ └── base.css │ ├── bootstrap │ │ ├── css │ │ │ ├── bootstrap-responsive.css │ │ │ ├── bootstrap.css │ │ │ └── bootstrap.min.css │ │ └── js │ │ │ ├── bootstrap-dialog.js │ │ │ └── bootstrap.min.js │ ├── css │ │ ├── bootstrap.min.css │ │ └── project_list.css │ ├── img │ │ ├── bronze.png │ │ ├── gold.png │ │ ├── silver.png │ │ └── title.png │ ├── js │ │ ├── TimeCircles │ │ │ ├── TimeCircles.css │ │ │ └── TimeCircles.js │ │ ├── jquery.min.js │ │ ├── maths.js │ │ ├── moment.min.js │ │ ├── underscore-min.js │ │ └── underscore.string.min.js │ └── themes │ │ ├── camping │ │ ├── badges.css │ │ ├── badges.js │ │ ├── blue_jersey.png │ │ ├── canoe_green_sideways.png │ │ ├── card_wood.png │ │ ├── card_wood_100.png │ │ ├── card_wood_150.png │ │ ├── card_wood_200.png │ │ ├── card_wood_300.png │ │ ├── corner_border_brown.png │ │ ├── corner_border_cyber_blue.png │ │ ├── oar.png │ │ ├── paddles.js │ │ ├── page_background.jpg │ │ ├── style.css │ │ ├── test_canvas.html │ │ └── tinycolor.js │ │ ├── camping2 │ │ ├── page_background.jpg │ │ └── style.css │ │ └── map │ │ ├── page_background.jpg │ │ └── style.css ├── templates │ ├── _account_bar.html │ ├── _footer.html │ ├── homepage.html │ └── site_base.html ├── urls.py └── wsgi.py ├── manage.py ├── pavement.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | ### 6 | # Ignore Chef key files and secrets 7 | ### 8 | .chef/*.pem 9 | .chef/encrypted_data_bag_secret 10 | .idea 11 | .idea/* 12 | .cache 13 | .cache/* 14 | .vagrant 15 | .vagrant/* 16 | *.swp 17 | 18 | .DS_Store 19 | CACHE/* 20 | *.log 21 | *.pot 22 | 23 | Berksfile.lock 24 | vagrant_dev_settings.yml 25 | 26 | # C extensions 27 | *.so 28 | 29 | # Distribution / packaging 30 | .Python 31 | env/ 32 | bin/ 33 | build/ 34 | develop-eggs/ 35 | dist/ 36 | eggs/ 37 | lib/ 38 | lib64/ 39 | parts/ 40 | sdist/ 41 | var/ 42 | *.egg-info/ 43 | .installed.cfg 44 | *.egg 45 | 46 | # Installer logs 47 | pip-log.txt 48 | pip-delete-this-directory.txt 49 | 50 | # Unit test / coverage reports 51 | htmlcov/ 52 | .tox/ 53 | .coverage 54 | .cache 55 | nosetests.xml 56 | coverage.xml 57 | 58 | # Translations 59 | *.mo 60 | 61 | # Mr Developer 62 | .mr.developer.cfg 63 | .project 64 | .pydevproject 65 | 66 | # Rope 67 | .ropeproject 68 | 69 | 70 | # Sphinx documentation 71 | docs/_build/ 72 | 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # django-gamification 2 | 3 | #### Django Gamification API #### 4 | 5 | The aim of this project is to create a prototype API for implementing game incentives into an application 6 | 7 | Anyone who would like to change development priorities is welcome to Fork the library. Please submit any proposed fixes or improvements through a Github Pull Request. 8 | 9 | ### django-gamificaiton Configuration ### 10 | 11 | The ``django-gamification/settings.py`` file contains installation-specific settings. The Database name/pw and server URLs will need to be configured here. 12 | 13 | 14 | ### django-gamification Installation ### 15 | 16 | 17 | 1. Make sure Python, Virtualenv, and Git are installed 18 | 19 | 2. Install and setup geoq-django: 20 | 21 | % mkdir -p ~/pyenv 22 | % virtualenv --no-site-packages ~/pyenv/gamification 23 | % source ~/pyenv/gamification/bin/activate 24 | % git clone https://github.com/stephenrjones/django-gamification 25 | 26 | 3. Create the database and sync dependencies and data 27 | 28 | % cd django-gamification 29 | % pip install paver 30 | % paver install_dependencies 31 | % paver createdb 32 | % paver create_db_user 33 | % paver sync 34 | 35 | 36 | 4. Build user accounts: 37 | 38 | % python manage.py createsuperuser 39 | 40 | 41 | 9. Start it up! 42 | 43 | % python manage.py runserver 44 | 45 | 46 | ### License ### 47 | MIT license 48 | 49 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, as long as any reuse or further development of the software attributes the authorship as follows: 'This software (django-gamification) is provided to the public as a courtesy of the National Geospatial-Intelligence Agency. 50 | 51 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 52 | 53 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 54 | 55 | 56 | ### TODOs ### 57 | Current next development goals are tracked as Issues within GitHub, and high-level goals are in ```django-gamification/TODO.rst```. 58 | -------------------------------------------------------------------------------- /gamification/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from gamification.core.models import Points 4 | from gamification.badges.utils import MetaBadge 5 | from gamification.badges.models import ProjectBadge 6 | 7 | def check_points(self,instance): 8 | return False 9 | 10 | def create_badge_classes(): 11 | pbadges = ProjectBadge.objects.all() 12 | 13 | for pbadge in pbadges: 14 | cls = type(pbadge.name.encode('ascii','ignore'),(MetaBadge,),{'id':badge.id,'name':badge.name,'model':Points,'level':badge.level,'check_points':check_points}) 15 | 16 | # create_badge_classes() 17 | 18 | 19 | -------------------------------------------------------------------------------- /gamification/badges/__init__.py: -------------------------------------------------------------------------------- 1 | from utils import registered_badges, MetaBadge 2 | -------------------------------------------------------------------------------- /gamification/badges/admin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.contrib import admin 4 | from django import forms 5 | from django.contrib.auth.models import User 6 | 7 | from models import Badge,BadgeSettings, ProjectBadge, ProjectBadgeToUser 8 | from singleton_models.admin import SingletonModelAdmin 9 | 10 | class BadgeAdmin(admin.ModelAdmin): 11 | fields = ('name','level','icon',) 12 | list_display = ('name','level') 13 | 14 | 15 | class ProjectBadgeAdmin(admin.ModelAdmin): 16 | list_display = ('name','description','awardLevel') 17 | fields = ('name','description','project','badge','awardLevel','multipleAwards','tags') 18 | 19 | class BadgeSettingsAdmin(admin.ModelAdmin): 20 | fields = ('awardLevel','multipleAwards') 21 | list_display = ('awardLevel','multipleAwards') 22 | 23 | class ProjectBadgeToUserAdminForm(forms.ModelForm): 24 | class Meta: 25 | model = ProjectBadgeToUser 26 | 27 | def __init__(self, *args, **kwargs): 28 | super(ProjectBadgeToUserAdminForm, self).__init__(*args, **kwargs) 29 | self.fields['user'].queryset = User.objects.order_by('username') 30 | 31 | class ProjectBadgeToUserAdmin(admin.ModelAdmin): 32 | list_display = ('projectbadge','user','created') 33 | form = ProjectBadgeToUserAdminForm 34 | 35 | admin.site.register(Badge, BadgeAdmin) 36 | #admin.site.register(BadgeSettings, BadgeSettingsAdmin) 37 | admin.site.register(ProjectBadge, ProjectBadgeAdmin) 38 | admin.site.register(ProjectBadgeToUser, ProjectBadgeToUserAdmin) 39 | -------------------------------------------------------------------------------- /gamification/badges/badges_notes.txt: -------------------------------------------------------------------------------- 1 | Pip install django-badges (then cloned due to having to make changes for Django 1.4+) 2 | pip install PIL (un-identified dependency) 3 | Modified models to add metabadge and also add user profile 4 | 5 | 6 | How does this work: 7 | 8 | Django-badges hooks into the save callback for one or more models (in our case, currently the AOI model). Any time an AOI is saved, we currently check: 9 | Is this AOI marked completed + with an assigned analyst. 10 | If so, then that analyst's user profile's score is incremented by BadgeSettings.points 11 | The analyst is marked as eligible to receive a badge for completing an AOI (will check if analyst already has this badge) 12 | It then also checks if the analyst has multiple AOIs in multiple projects (NOTE: may want to first check if analyst has the badge since I think checking for the badge is cheaper than how I'm checking for multiple jobs) 13 | 14 | Menu.html was modified to include the user's current score and badges; clicking on the badges item in the menu will go to the badge page. 15 | 16 | Possible extensions: 17 | 1) Currently, django-badges hooks into the post-save function; as far as I know, this means I cannot determine (from inside this function) what changed in the model, so I don't currently handle if an analyst was awarded points but would have just lost them because an admin "fixed" an AOI to be owned by a different analyst (but I do sort of handle it in the sense that when the analyst does get points, it currently recalculates the score from scratch, because I also can't tell if an AOI was being marked as completed (which should score points) or if a completed AOI was being updated for other reasons (which shouldn't). I considered modifying django-badges to hook into pre-save function which lets me see the fields to be changed, which gives me some ability to potentially identify stuff 18 | 19 | 2) Adding additional badges 20 | 21 | 3) Points for supervisors 22 | 23 | 4) Django-badges has the ability to map images to different badges but they aren't being shown at the moment (this looks like I just need to find / write the css for metal and/or medal classes) 24 | 25 | 26 | 11/19/2013: Installation Notes 27 | 28 | Here's a couple of things I needed to do when I installed the badging stuff into the baseline: 29 | 30 | - A new table (UserProfile) was added to track badge points. This should be created when new users are added to the system, 31 | but records may need to be added for existing users 32 | 33 | - In the settings.py file, I updated MEDIA_ROOT and MEDIA_URL variables so that the badge images could be served out if 34 | running geoq-django from the command line (paver start_django). You'll need to update MEDIA_ROOT for your installation. 35 | A 'static' url was also added to the top-level urls.py file. If you're running geoq-django on a wsgi server, 36 | you won't need these as you'll likely have an image directory setup there. This means you'll also make a change to 37 | the entries in the badges_badge db table for the icon path -------------------------------------------------------------------------------- /gamification/badges/fixtures/initial_data.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "pk": 1, 4 | "model": "badges.badge", 5 | "fields": { 6 | "icon": "img/bronze.png", 7 | "name": "Bronze", 8 | "level": "1" 9 | } 10 | }, 11 | { 12 | "pk": 2, 13 | "model": "badges.badge", 14 | "fields": { 15 | "icon": "img/silver.png", 16 | "name": "Silver", 17 | "level": "2" 18 | } 19 | }, 20 | { 21 | "pk": 3, 22 | "model": "badges.badge", 23 | "fields": { 24 | "icon": "img/gold.png", 25 | "name": "Gold", 26 | "level": "3" 27 | } 28 | }, 29 | { 30 | "pk": 4, 31 | "model": "badges.projectbadge", 32 | "fields": { 33 | "multipleAwards": true, 34 | "name": "Gold", 35 | "created": "2013-12-12T17:58:15.381Z", 36 | "project": 3, 37 | "awardLevel": 10, 38 | "badge": 3, 39 | "description": "Gold badge" 40 | } 41 | }, 42 | { 43 | "pk": 5, 44 | "model": "badges.projectbadge", 45 | "fields": { 46 | "multipleAwards": true, 47 | "name": "Silver", 48 | "created": "2013-12-12T17:58:35.170Z", 49 | "project": 3, 50 | "awardLevel": 5, 51 | "badge": 2, 52 | "description": "Silver award" 53 | } 54 | }, 55 | { 56 | "pk": 6, 57 | "model": "badges.projectbadge", 58 | "fields": { 59 | "multipleAwards": true, 60 | "name": "Bronze", 61 | "created": "2013-12-12T19:24:48.592Z", 62 | "project": 3, 63 | "awardLevel": 1, 64 | "badge": 1, 65 | "description": "Bronze award" 66 | } 67 | }, 68 | { 69 | "pk": 7, 70 | "model": "badges.projectbadge", 71 | "fields": { 72 | "multipleAwards": true, 73 | "name": "contributer", 74 | "created": "2014-05-05T17:33:06.034Z", 75 | "project": 4, 76 | "awardLevel": 1, 77 | "badge": 1, 78 | "description": "Contributer to NGA Connect" 79 | } 80 | } 81 | ] 82 | -------------------------------------------------------------------------------- /gamification/badges/managers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.db import models 4 | 5 | class BadgeManager(models.Manager): 6 | def active(self): 7 | import badges 8 | return self.get_query_set().filter(id__in=badges.registered_badges.keys()) 9 | -------------------------------------------------------------------------------- /gamification/badges/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import datetime 3 | from south.db import db 4 | from south.v2 import SchemaMigration 5 | from django.db import models 6 | 7 | 8 | class Migration(SchemaMigration): 9 | 10 | def forwards(self, orm): 11 | # Adding model 'Badge' 12 | db.create_table(u'badges_badge', ( 13 | (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), 14 | ('name', self.gf('django.db.models.fields.CharField')(max_length=255)), 15 | ('level', self.gf('django.db.models.fields.CharField')(max_length=1)), 16 | ('icon', self.gf('django.db.models.fields.files.ImageField')(max_length=100)), 17 | )) 18 | db.send_create_signal(u'badges', ['Badge']) 19 | 20 | # Adding model 'ProjectBadge' 21 | db.create_table(u'badges_projectbadge', ( 22 | (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), 23 | ('project', self.gf('django.db.models.fields.IntegerField')()), 24 | ('badge', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['badges.Badge'])), 25 | ('name', self.gf('django.db.models.fields.CharField')(max_length=255)), 26 | ('description', self.gf('django.db.models.fields.TextField')()), 27 | ('created', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), 28 | )) 29 | db.send_create_signal(u'badges', ['ProjectBadge']) 30 | 31 | # Adding model 'ProjectBadgeToUser' 32 | db.create_table(u'badges_projectbadgetouser', ( 33 | (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), 34 | ('projectbadge', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['badges.ProjectBadge'])), 35 | ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])), 36 | ('created', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), 37 | )) 38 | db.send_create_signal(u'badges', ['ProjectBadgeToUser']) 39 | 40 | # Adding model 'BadgeSettings' 41 | db.create_table(u'badges_badgesettings', ( 42 | (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), 43 | ('awardLevel', self.gf('django.db.models.fields.IntegerField')(default=1000)), 44 | ('multipleAwards', self.gf('django.db.models.fields.BooleanField')(default=True)), 45 | )) 46 | db.send_create_signal(u'badges', ['BadgeSettings']) 47 | 48 | 49 | def backwards(self, orm): 50 | # Deleting model 'Badge' 51 | db.delete_table(u'badges_badge') 52 | 53 | # Deleting model 'ProjectBadge' 54 | db.delete_table(u'badges_projectbadge') 55 | 56 | # Deleting model 'ProjectBadgeToUser' 57 | db.delete_table(u'badges_projectbadgetouser') 58 | 59 | # Deleting model 'BadgeSettings' 60 | db.delete_table(u'badges_badgesettings') 61 | 62 | 63 | models = { 64 | u'auth.group': { 65 | 'Meta': {'object_name': 'Group'}, 66 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 67 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), 68 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) 69 | }, 70 | u'auth.permission': { 71 | 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, 72 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 73 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), 74 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 75 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) 76 | }, 77 | u'auth.user': { 78 | 'Meta': {'object_name': 'User'}, 79 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 80 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), 81 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 82 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), 83 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 84 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 85 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 86 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 87 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 88 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 89 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), 90 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), 91 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) 92 | }, 93 | u'badges.badge': { 94 | 'Meta': {'object_name': 'Badge'}, 95 | 'icon': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}), 96 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 97 | 'level': ('django.db.models.fields.CharField', [], {'max_length': '1'}), 98 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}) 99 | }, 100 | u'badges.badgesettings': { 101 | 'Meta': {'object_name': 'BadgeSettings'}, 102 | 'awardLevel': ('django.db.models.fields.IntegerField', [], {'default': '1000'}), 103 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 104 | 'multipleAwards': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) 105 | }, 106 | u'badges.projectbadge': { 107 | 'Meta': {'object_name': 'ProjectBadge'}, 108 | 'badge': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['badges.Badge']"}), 109 | 'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 110 | 'description': ('django.db.models.fields.TextField', [], {}), 111 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 112 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), 113 | 'project': ('django.db.models.fields.IntegerField', [], {}), 114 | 'user': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': u"orm['badges.ProjectBadgeToUser']", 'to': u"orm['auth.User']"}) 115 | }, 116 | u'badges.projectbadgetouser': { 117 | 'Meta': {'object_name': 'ProjectBadgeToUser'}, 118 | 'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 119 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 120 | 'projectbadge': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['badges.ProjectBadge']"}), 121 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) 122 | }, 123 | u'contenttypes.contenttype': { 124 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, 125 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 126 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 127 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 128 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 129 | }, 130 | } 131 | 132 | complete_apps = ['badges'] -------------------------------------------------------------------------------- /gamification/badges/migrations/0002_auto__add_field_projectbadge_awardLevel__add_field_projectbadge_multip.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import datetime 3 | from south.db import db 4 | from south.v2 import SchemaMigration 5 | from django.db import models 6 | 7 | 8 | class Migration(SchemaMigration): 9 | 10 | def forwards(self, orm): 11 | # Adding field 'ProjectBadge.awardLevel' 12 | db.add_column(u'badges_projectbadge', 'awardLevel', 13 | self.gf('django.db.models.fields.IntegerField')(default=1000), 14 | keep_default=False) 15 | 16 | # Adding field 'ProjectBadge.multipleAwards' 17 | db.add_column(u'badges_projectbadge', 'multipleAwards', 18 | self.gf('django.db.models.fields.BooleanField')(default=True), 19 | keep_default=False) 20 | 21 | 22 | # Renaming column for 'ProjectBadge.project' to match new field type. 23 | db.rename_column(u'badges_projectbadge', 'project', 'project_id') 24 | # Changing field 'ProjectBadge.project' 25 | db.alter_column(u'badges_projectbadge', 'project_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['core.Project'])) 26 | # Adding index on 'ProjectBadge', fields ['project'] 27 | db.create_index(u'badges_projectbadge', ['project_id']) 28 | 29 | 30 | def backwards(self, orm): 31 | # Removing index on 'ProjectBadge', fields ['project'] 32 | db.delete_index(u'badges_projectbadge', ['project_id']) 33 | 34 | # Deleting field 'ProjectBadge.awardLevel' 35 | db.delete_column(u'badges_projectbadge', 'awardLevel') 36 | 37 | # Deleting field 'ProjectBadge.multipleAwards' 38 | db.delete_column(u'badges_projectbadge', 'multipleAwards') 39 | 40 | 41 | # Renaming column for 'ProjectBadge.project' to match new field type. 42 | db.rename_column(u'badges_projectbadge', 'project_id', 'project') 43 | # Changing field 'ProjectBadge.project' 44 | db.alter_column(u'badges_projectbadge', 'project', self.gf('django.db.models.fields.IntegerField')()) 45 | 46 | models = { 47 | u'auth.group': { 48 | 'Meta': {'object_name': 'Group'}, 49 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 50 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), 51 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) 52 | }, 53 | u'auth.permission': { 54 | 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, 55 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 56 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), 57 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 58 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) 59 | }, 60 | u'auth.user': { 61 | 'Meta': {'object_name': 'User'}, 62 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 63 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), 64 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 65 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), 66 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 67 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 68 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 69 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 70 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 71 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 72 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), 73 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), 74 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) 75 | }, 76 | u'badges.badge': { 77 | 'Meta': {'object_name': 'Badge'}, 78 | 'icon': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}), 79 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 80 | 'level': ('django.db.models.fields.CharField', [], {'max_length': '1'}), 81 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}) 82 | }, 83 | u'badges.badgesettings': { 84 | 'Meta': {'object_name': 'BadgeSettings'}, 85 | 'awardLevel': ('django.db.models.fields.IntegerField', [], {'default': '1000'}), 86 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 87 | 'multipleAwards': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) 88 | }, 89 | u'badges.projectbadge': { 90 | 'Meta': {'object_name': 'ProjectBadge'}, 91 | 'awardLevel': ('django.db.models.fields.IntegerField', [], {'default': '1000'}), 92 | 'badge': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['badges.Badge']"}), 93 | 'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 94 | 'description': ('django.db.models.fields.TextField', [], {}), 95 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 96 | 'multipleAwards': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 97 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), 98 | 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Project']"}), 99 | 'user': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': u"orm['badges.ProjectBadgeToUser']", 'to': u"orm['auth.User']"}) 100 | }, 101 | u'badges.projectbadgetouser': { 102 | 'Meta': {'object_name': 'ProjectBadgeToUser'}, 103 | 'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 104 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 105 | 'projectbadge': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['badges.ProjectBadge']"}), 106 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) 107 | }, 108 | u'contenttypes.contenttype': { 109 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, 110 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 111 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 112 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 113 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 114 | }, 115 | u'core.project': { 116 | 'Meta': {'ordering': "('-created_at',)", 'object_name': 'Project'}, 117 | 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 118 | 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), 119 | 'description': ('django.db.models.fields.TextField', [], {}), 120 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 121 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), 122 | 'private': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 123 | 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}) 124 | } 125 | } 126 | 127 | complete_apps = ['badges'] -------------------------------------------------------------------------------- /gamification/badges/migrations/0003_auto__chg_field_badge_icon.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import datetime 3 | from south.db import db 4 | from south.v2 import SchemaMigration 5 | from django.db import models 6 | 7 | 8 | class Migration(SchemaMigration): 9 | 10 | def forwards(self, orm): 11 | 12 | # Changing field 'Badge.icon' 13 | db.alter_column(u'badges_badge', 'icon', self.gf('django.db.models.fields.files.ImageField')(max_length=100, null=True)) 14 | 15 | def backwards(self, orm): 16 | 17 | # Changing field 'Badge.icon' 18 | db.alter_column(u'badges_badge', 'icon', self.gf('django.db.models.fields.files.ImageField')(default='', max_length=100)) 19 | 20 | models = { 21 | u'auth.group': { 22 | 'Meta': {'object_name': 'Group'}, 23 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 24 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), 25 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) 26 | }, 27 | u'auth.permission': { 28 | 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, 29 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 30 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), 31 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 32 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) 33 | }, 34 | u'auth.user': { 35 | 'Meta': {'object_name': 'User'}, 36 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 37 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), 38 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 39 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), 40 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 41 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 42 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 43 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 44 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 45 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 46 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), 47 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), 48 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) 49 | }, 50 | u'badges.badge': { 51 | 'Meta': {'object_name': 'Badge'}, 52 | 'icon': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), 53 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 54 | 'level': ('django.db.models.fields.CharField', [], {'max_length': '1'}), 55 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}) 56 | }, 57 | u'badges.badgesettings': { 58 | 'Meta': {'object_name': 'BadgeSettings'}, 59 | 'awardLevel': ('django.db.models.fields.IntegerField', [], {'default': '1000'}), 60 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 61 | 'multipleAwards': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) 62 | }, 63 | u'badges.projectbadge': { 64 | 'Meta': {'object_name': 'ProjectBadge'}, 65 | 'awardLevel': ('django.db.models.fields.IntegerField', [], {'default': '1000'}), 66 | 'badge': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['badges.Badge']"}), 67 | 'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 68 | 'description': ('django.db.models.fields.TextField', [], {}), 69 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 70 | 'multipleAwards': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 71 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), 72 | 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Project']"}), 73 | 'user': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': u"orm['badges.ProjectBadgeToUser']", 'to': u"orm['auth.User']"}) 74 | }, 75 | u'badges.projectbadgetouser': { 76 | 'Meta': {'object_name': 'ProjectBadgeToUser'}, 77 | 'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 78 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 79 | 'projectbadge': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['badges.ProjectBadge']"}), 80 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) 81 | }, 82 | u'contenttypes.contenttype': { 83 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, 84 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 85 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 86 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 87 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 88 | }, 89 | u'core.project': { 90 | 'Meta': {'ordering': "('-created_at',)", 'object_name': 'Project'}, 91 | 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 92 | 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), 93 | 'description': ('django.db.models.fields.TextField', [], {}), 94 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 95 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), 96 | 'private': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 97 | 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}) 98 | } 99 | } 100 | 101 | complete_apps = ['badges'] -------------------------------------------------------------------------------- /gamification/badges/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenrjones/django-gamification/d22882f148375102ec351cb2bc75275083468d73/gamification/badges/migrations/__init__.py -------------------------------------------------------------------------------- /gamification/badges/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from datetime import datetime 4 | 5 | from django.contrib.auth.models import User 6 | from django.core.urlresolvers import reverse 7 | from django.db import models 8 | from django.conf import settings 9 | 10 | from singleton_models.models import SingletonModel 11 | #from gamification.core.models import Project 12 | 13 | from signals import badge_awarded 14 | from managers import BadgeManager 15 | 16 | if hasattr(settings, 'BADGE_LEVEL_CHOICES'): 17 | LEVEL_CHOICES = settings.BADGE_LEVEL_CHOICES 18 | else: 19 | LEVEL_CHOICES = ( 20 | ("1", "Bronze"), 21 | ("2", "Silver"), 22 | ("3", "Gold"), 23 | ("4", "Diamond"), 24 | ) 25 | 26 | class Badge(models.Model): 27 | name = models.CharField(max_length=255) 28 | level = models.CharField(max_length=1, choices=LEVEL_CHOICES) 29 | icon = models.ImageField(upload_to='badge_images', default='', null=True, blank=True ) 30 | value = 1 31 | 32 | objects = BadgeManager() 33 | 34 | @property 35 | def meta_badge(self): 36 | from utils import registered_badges 37 | return registered_badges[self.id] 38 | 39 | @property 40 | def title(self): 41 | return self.name 42 | 43 | @property 44 | def description(self): 45 | return self.meta_badge.description 46 | 47 | def __unicode__(self): 48 | return u"%s" % self.name 49 | 50 | def get_absolute_url(self): 51 | return reverse('badge_detail', kwargs={'slug': self.id}) 52 | 53 | class Meta: 54 | verbose_name_plural = "Badge Templates" 55 | 56 | 57 | class ProjectBadge(models.Model): 58 | project = models.ForeignKey('core.Project') 59 | badge = models.ForeignKey('Badge') 60 | user = models.ManyToManyField(User, related_name="badges", through='ProjectBadgeToUser') 61 | name = models.CharField(max_length=255) 62 | description = models.TextField() 63 | created = models.DateTimeField(default=datetime.now) 64 | awardLevel = models.IntegerField(default=1) 65 | multipleAwards = models.BooleanField(default=True) 66 | tags = models.CharField(max_length=400, default='', null=True, blank=True, help_text='Tags associated with this badge. Use a few small words separated by commas.') 67 | 68 | @property 69 | def meta_badge(self): 70 | from utils import registered_badges 71 | return registered_badges[self.id] 72 | 73 | def award_to(self, user): 74 | #has_badge = self in user.badges.all() 75 | #if self.meta_badge.one_time_only and has_badge: 76 | # return False 77 | 78 | ProjectBadgeToUser.objects.create(projectbadge=self, user=user) 79 | 80 | #badge_awarded.send(sender=self.meta_badge, user=user, badge=self) 81 | 82 | #Grr... deprecated for Django 1.4+ 83 | #message_template = "You just got the %s Badge!" 84 | #user.message.success(message = message_template % self.title) 85 | 86 | return ProjectBadgeToUser.objects.filter(projectbadge=self, user=user).count() 87 | 88 | def number_awarded(self, user_or_qs=None): 89 | """ 90 | Gives the number awarded total. Pass in an argument to 91 | get the number per user, or per queryset. 92 | """ 93 | kwargs = {'badge':self} 94 | if user_or_qs is None: 95 | pass 96 | elif isinstance(user_or_qs, User): 97 | kwargs.update(dict(user=user_or_qs)) 98 | else: 99 | kwargs.update(dict(user__in=user_or_qs)) 100 | return ProjectBadgeToUser.objects.filter(**kwargs).count() 101 | 102 | def __str__(self): 103 | return self.name 104 | 105 | class ProjectBadgeToUser(models.Model): 106 | projectbadge = models.ForeignKey(ProjectBadge) 107 | user = models.ForeignKey(User) 108 | created = models.DateTimeField(default=datetime.now) 109 | 110 | class Meta: 111 | verbose_name_plural = "Awarded Badges" 112 | 113 | class BadgeSettings(models.Model): 114 | awardLevel = models.IntegerField(default=1) 115 | multipleAwards = models.BooleanField(default=True) 116 | 117 | class Meta: 118 | verbose_name_plural = "Badge Settings" 119 | 120 | -------------------------------------------------------------------------------- /gamification/badges/signals.py: -------------------------------------------------------------------------------- 1 | import django.dispatch 2 | 3 | badge_awarded = django.dispatch.Signal(providing_args=['user', 'badge']) -------------------------------------------------------------------------------- /gamification/badges/templates/badges/badge.html: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | This is the html for displaying an individual badge on the site. It expects that 3 | you will pass it a badge object called "badge". 4 | 5 | Here is an example: 6 |
7 | {% for badge in user_profile.user.badges.all %} 8 | {% include "badges/badge.html" %} 9 | {% endfor %} 10 |
11 | 12 | {% endcomment %} 13 | 14 | 15 | {{ badge.title }} 16 | 17 | -------------------------------------------------------------------------------- /gamification/badges/templates/badges/badges_display.html: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | This template is for displaying the badges a user has earned next to their name. It 3 | expects that you will pass it a list of badge objects called badges. 4 | 5 | Here is an easy way to do that: 6 | 7 | {% with badges as post.user.badges %} 8 | {% include "badges/badges_display.html %} 9 | {% endwith %} 10 | 11 | {% endcomment %} 12 | 13 | 14 | 15 | {% load badges_tags %} 16 | 17 | 18 | {% if request.user %} 19 | {% for level in request.user|badge_count %} 20 | {% if level.count %} 21 | {{level.count}}   22 | {% endif %} 23 | {% endfor %} 24 | {% endif %} 25 | 26 | -------------------------------------------------------------------------------- /gamification/badges/templates/badges/detail.html: -------------------------------------------------------------------------------- 1 | {% extends "badges/overview.html" %} 2 | {% load badges_tags %} 3 | {% load humanize %} 4 | 5 | {% block body %} 6 |
7 |

{% block head_title %}Badge: {{ badge.title }}{% endblock %}

8 |

{{ badge.description }}

9 | 16 |
17 | {% endblock %} -------------------------------------------------------------------------------- /gamification/badges/templates/badges/overview.html: -------------------------------------------------------------------------------- 1 | {% extends "site_base.html" %} 2 | {% load badges_tags %} 3 | {% load humanize %} 4 | 5 | {% block container %} 6 |
7 |

{% block head_title %}Badges{% endblock %}

8 |

Contribute to one or more analyst tasks to earn points and badges. Here is a list of the badges, and how many people have earned each one.

9 |

Badges are fun! They're just little reminders that you are awesome

10 | 11 | {% for badge in badges %} 12 | 13 | 16 | 20 | 21 | 22 | {% endfor %} 23 |
14 | {% if badge|is_in:request.user.badges.all %}{% endif %}  15 | 17 | {% include "badges/badge.html" %} 18 | × {{ badge.user.count|intcomma }} 19 | {{ badge.description }}
24 |
25 | {% endblock %} 26 | 27 | -------------------------------------------------------------------------------- /gamification/badges/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenrjones/django-gamification/d22882f148375102ec351cb2bc75275083468d73/gamification/badges/templatetags/__init__.py -------------------------------------------------------------------------------- /gamification/badges/templatetags/badges_tags.py: -------------------------------------------------------------------------------- 1 | from django.template import Library 2 | from badges.utils import badge_count 3 | from badges.models import LEVEL_CHOICES, Badge 4 | level_choices = dict(LEVEL_CHOICES) 5 | 6 | register = Library() 7 | 8 | @register.filter 9 | def is_in(value,arg): 10 | return value in arg 11 | 12 | @register.filter 13 | def level_count(badges, level): 14 | return badges.filter(level=level).count() 15 | 16 | @register.filter 17 | def level_title(level): 18 | return level_choices[level] 19 | 20 | @register.filter('badge_count') 21 | def _badge_count(user_or_qs): 22 | bc = badge_count(user_or_qs) 23 | return badge_count(user_or_qs) 24 | 25 | @register.filter 26 | def number_awarded(badge, user_or_qs=None): 27 | return badge.number_awarded(user_or_qs) 28 | 29 | @register.filter 30 | def progress_start(badge): 31 | return badge.meta_badge.progress_start 32 | 33 | @register.filter 34 | def progress_finish(badge): 35 | return badge.meta_badge.progress_finish 36 | 37 | @register.filter 38 | def progress(badge, user): 39 | return badge.meta_badge.get_progress(user) 40 | 41 | @register.filter 42 | def is_in_progress(badge, user): 43 | return 0 < badge.meta_badge.get_progress(user) < progress_finish(badge) 44 | 45 | @register.filter 46 | def progress_percentage(badge, user): 47 | prog = badge.meta_badge.get_progress_percentage(user=user) 48 | return max(min(prog, 100), 0) 49 | 50 | @register.filter 51 | def level_icon(level): 52 | badge = Badge.objects.get(level=level) 53 | return badge.icon.url -------------------------------------------------------------------------------- /gamification/badges/tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | This file demonstrates writing tests using the unittest module. These will pass 5 | when you run "manage.py test". 6 | 7 | Replace this with more appropriate tests for your application. 8 | """ 9 | 10 | from django.test import TestCase 11 | 12 | 13 | class SimpleTest(TestCase): 14 | def test_basic_addition(self): 15 | """ 16 | Tests that 1 + 1 always equals 2. 17 | """ 18 | self.assertEqual(1 + 1, 2) 19 | -------------------------------------------------------------------------------- /gamification/badges/urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.conf.urls.defaults import * 4 | 5 | from badges import views 6 | 7 | urlpatterns = patterns('', 8 | url(r'^$', views.overview, name="badges_overview"), 9 | url(r'^(?P[A-Za-z0-9_-]+)/$', views.detail, name="badge_detail"), 10 | ) -------------------------------------------------------------------------------- /gamification/badges/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.core.urlresolvers import reverse 4 | from django.db import models 5 | from django.db.models.signals import post_save 6 | from django.contrib.auth.models import User 7 | 8 | from models import Badge as BadgeModel 9 | from models import ProjectBadgeToUser, LEVEL_CHOICES 10 | 11 | 12 | class RequiresUserOrProgress(Exception): pass 13 | 14 | registered_badges = {} 15 | 16 | def register(badge): 17 | if badge.id not in registered_badges: 18 | registered_badges[badge.id] = badge() 19 | return badge 20 | 21 | def badge_count(user_or_qs=None): 22 | """ 23 | Given a user or queryset of users, this returns the badge 24 | count at each badge level that the user(s) have earned. 25 | 26 | Example: 27 | 28 | >>> badge_count(User.objects.filter(username='admin')) 29 | [{'count': 0, 'badge__level': '1'}, {'count': 0, 'badge__level': '2'}, {'count': 0, 'badge__level': '3'}, {'count': 0, 'badge__level': '4'}] 30 | 31 | Uses a single database query. 32 | """ 33 | 34 | badge_counts = BadgeToUser.objects.all() 35 | if isinstance(user_or_qs, User): 36 | badge_counts = badge_counts.filter(user=user_or_qs) 37 | elif isinstance(user_or_qs, models.query.QuerySet): 38 | badge_counts = badge_counts.filter(user__in=user_or_qs) 39 | 40 | badge_counts = badge_counts.values('badge__level') 41 | badge_counts = badge_counts.annotate(count=models.Count('badge__level')) 42 | 43 | def get_badge_count(level): 44 | bc = [bc for bc in badge_counts if bc['badge__level'] == level] 45 | if bc: 46 | return bc[0] 47 | else: 48 | # if the user has no badges at this level, return the appropriate response 49 | return {'count': 0, 'badge__level': level} 50 | 51 | 52 | return [get_badge_count(level_choice[0]) for level_choice in LEVEL_CHOICES] 53 | 54 | def project_badge_count(user, project, badge_choices, url): 55 | """ 56 | Given a user or queryset of users, this returns the badge 57 | count at each badge level that the user(s) have earned. 58 | 59 | Example: 60 | 61 | >>> badge_count(User.objects.filter(username='admin')) 62 | [{'count': 0, 'badge__level': '1'}, {'count': 0, 'badge__level': '2'}, {'count': 0, 'badge__level': '3'}, {'count': 0, 'badge__level': '4'}] 63 | 64 | Uses a single database query. 65 | """ 66 | 67 | badges = ProjectBadgeToUser.objects.all() 68 | badge_counts = badges.filter(user=user) 69 | 70 | badge_counts = badge_counts.values('projectbadge__name','projectbadge__badge__icon', 'projectbadge__badge__level', 'projectbadge__description','projectbadge__project__name').order_by('projectbadge__badge__level') 71 | badge_counts = badge_counts.annotate(count=models.Count('projectbadge__name')) 72 | 73 | def get_badge_count(badge): 74 | bc = [bc for bc in badge_counts if bc['projectbadge__name'] == badge.name] 75 | if bc: 76 | # append url path for icon 77 | bc[0]['projectbadge__badge__icon'] = url + bc[0]['projectbadge__badge__icon'] 78 | return bc[0] 79 | else: 80 | # if the user has no badges at this level, return the appropriate response 81 | return {'count': 0, 'projectbadge__name': badge.name, 'projectbadge__badge__icon': url + badge.badge.icon.url, 82 | 'level':badge.badge.level, 'projectbadge__description':badge.description, 'projectbadge__project__name':project.name} 83 | 84 | return [get_badge_count(badge) for badge in badge_choices] 85 | 86 | class MetaBadgeMeta(type): 87 | 88 | def __new__(cls, name, bases, attrs): 89 | new_badge = super(MetaBadgeMeta, cls).__new__(cls, name, bases, attrs) 90 | parents = [b for b in bases if isinstance(b, MetaBadgeMeta)] 91 | if not parents: 92 | # If this isn't a subclass of MetaBadge, don't do anything special. 93 | return new_badge 94 | return register(new_badge) 95 | 96 | 97 | class MetaBadge(object): 98 | __metaclass__ = MetaBadgeMeta 99 | 100 | one_time_only = False 101 | model = models.Model 102 | 103 | progress_start = 0 104 | progress_finish = 1 105 | 106 | def __init__(self): 107 | # whenever the server is reloaded, the badge will be initialized and 108 | # added to the database 109 | self._keep_badge_updated() 110 | post_save.connect(self._signal_callback, sender=self.model) 111 | 112 | def _signal_callback(self, **kwargs): 113 | i = kwargs['instance'] 114 | self.award_ceremony(i) 115 | 116 | def _test_conditions(self, instance): 117 | condition_callbacks = [getattr(self, c) for c in dir(self) if c.startswith('check')] 118 | 119 | # will return False on the first False condition 120 | return all( fn(instance) for fn in condition_callbacks ) 121 | 122 | def get_user(self, instance): 123 | return instance.user 124 | 125 | def get_progress(self, user): 126 | if BadgeToUser.objects.filter(user=user, badge=self.badge).count(): 127 | return 1 128 | return 0 129 | 130 | def get_progress_percentage(self, progress=None, user=None): 131 | if user is None and progress is None: 132 | raise RequiresUserOrProgress("This method requires either a user or progress keyword argument") 133 | 134 | if not progress: 135 | progress = self.get_progress(user) 136 | 137 | progress = min(progress, self.progress_finish) 138 | 139 | # multiply by a float to get floating point precision 140 | return (100.0 * progress) / (self.progress_finish - self.progress_start) 141 | 142 | def _keep_badge_updated(self): 143 | if getattr(self, 'badge', False): 144 | return False 145 | badge, created = BadgeModel.objects.get_or_create(id=self.id) 146 | if badge.level != self.level: 147 | badge.level = self.level 148 | badge.save() 149 | self.badge = badge 150 | 151 | def award_ceremony(self, instance): 152 | if self._test_conditions(instance): 153 | user = self.get_user(instance) 154 | self.badge.award_to(user) 155 | -------------------------------------------------------------------------------- /gamification/badges/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.shortcuts import render_to_response, get_object_or_404 4 | from django.template import RequestContext 5 | 6 | from models import Badge 7 | 8 | def overview(request, extra_context={}): 9 | badges = Badge.objects.active().order_by("level") 10 | 11 | context = locals() 12 | context.update(extra_context) 13 | return render_to_response("badges/overview.html", context, context_instance=RequestContext(request)) 14 | 15 | def detail(request, slug, extra_context={}): 16 | badge = get_object_or_404(Badge, id=slug) 17 | users = badge.user.all() 18 | 19 | context = locals() 20 | context.update(extra_context) 21 | return render_to_response("badges/detail.html", context, context_instance=RequestContext(request)) -------------------------------------------------------------------------------- /gamification/core/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Permission is hereby granted, free of charge, to any person obtaining 4 | # a copy of this software and associated documentation files (the 5 | # "Software"), to deal in the Software without restriction, including 6 | # without limitation the rights to use, copy, modify, merge, publish, 7 | # distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so, as long as 9 | # any reuse or further development of the software attributes the 10 | # National Geospatial-Intelligence Agency (NGA) authorship as follows: 11 | # 'This software (django-gamification) 12 | # is provided to the public as a courtesy of the National 13 | # Geospatial-Intelligence Agency. 14 | # 15 | # The above copyright notice and this permission notice shall be 16 | # included in all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /gamification/core/admin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Permission is hereby granted, free of charge, to any person obtaining 4 | # a copy of this software and associated documentation files (the 5 | # "Software"), to deal in the Software without restriction, including 6 | # without limitation the rights to use, copy, modify, merge, publish, 7 | # distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so, as long as 9 | # any reuse or further development of the software attributes the 10 | # National Geospatial-Intelligence Agency (NGA) authorship as follows: 11 | # 'This software (django-gamification) 12 | # is provided to the public as a courtesy of the National 13 | # Geospatial-Intelligence Agency. 14 | # 15 | # The above copyright notice and this permission notice shall be 16 | # included in all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | import reversion 27 | from django.shortcuts import render 28 | from django.http import HttpResponseRedirect 29 | from django import forms 30 | from models import Project, UserProfile, Points, Team 31 | from django.contrib import admin 32 | 33 | class ObjectAdmin(admin.ModelAdmin): 34 | list_display = ('name', 'created_at', 'updated_at') 35 | 36 | class UserProfileAdmin(ObjectAdmin): 37 | list_display = ('user','score') 38 | 39 | class PointAdmin(admin.ModelAdmin): 40 | list_display = ('user', 'date_awarded', 'projectbadge', 'value') 41 | 42 | class TeamAdmin(admin.ModelAdmin): 43 | list_display = ('name', 'description', 'date_created') 44 | filter_horizontal = ('members',) 45 | 46 | class ProjectAdmin(admin.ModelAdmin): 47 | model = Project 48 | list_filter = ("private", "active", "visual_theme") 49 | filter_horizontal = ('supervisors', 'teams',) 50 | 51 | normal_fields = ('name', 'private', 'active', 'description', 'visual_theme', 'project_closing_date') 52 | readonly_fields = ('created_at', 'updated_at') 53 | 54 | save_on_top = True 55 | save_as = True 56 | 57 | advanced_fields = ( 'viewing_pass_phrase', 'supervisors', 'teams', 'background_image', 'properties' ) 58 | desc = 'The settings below are advanced. Please contact and admin if you have questions.' 59 | 60 | fieldsets = ( 61 | (None, {'fields': normal_fields}), 62 | ('Advanced Settings', {'classes': ('collapse',), 63 | 'description': desc, 64 | 'fields': advanced_fields, 65 | })) 66 | 67 | 68 | 69 | admin.site.register(Project, ProjectAdmin) 70 | #admin.site.register(UserProfile, UserProfileAdmin) 71 | admin.site.register(Points, PointAdmin) 72 | admin.site.register(Team, TeamAdmin) -------------------------------------------------------------------------------- /gamification/core/fixtures/initial_data.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "pk": 3, 4 | "model": "core.project", 5 | "fields": { 6 | "name": "geoq", 7 | "created_at": "2013-12-12T17:57:23.228Z", 8 | "updated_at": "2013-12-12T17:57:23.228Z", 9 | "private": false, 10 | "active": true, 11 | "description": "GeoQ on Django" 12 | } 13 | }, 14 | { 15 | "pk": 4, 16 | "model": "core.project", 17 | "fields": { 18 | "name": "ngaconnect", 19 | "created_at": "2014-04-01T18:48:38.278Z", 20 | "updated_at": "2014-04-01T18:48:38.278Z", 21 | "private": false, 22 | "active": true, 23 | "description": "NGA Connect project" 24 | } 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /gamification/core/forms.py: -------------------------------------------------------------------------------- 1 | # This technical data was produced for the U. S. Government under Contract No. W15P7T-13-C-F600, and 2 | # is subject to the Rights in Technical Data-Noncommercial Items clause at DFARS 252.227-7013 (FEB 2012) 3 | 4 | from django import forms 5 | from django.contrib.auth.models import User 6 | from gamification.badges.models import ProjectBadge 7 | 8 | class AwardForm(forms.Form): 9 | 10 | award_id = forms.ModelChoiceField(label=(u'Award'), 11 | queryset=ProjectBadge.objects.all(), 12 | required=True) 13 | points = forms.ChoiceField(label=(u'Points to Award'), 14 | widget=forms.Select(), 15 | choices=([('1','1'),('5','5'),('10','10'),('20','20'),('50','50'),]), 16 | required=True) 17 | comment = forms.CharField(label=(u'Comment'), 18 | max_length=255) -------------------------------------------------------------------------------- /gamification/core/meta_badges.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Permission is hereby granted, free of charge, to any person obtaining 4 | # a copy of this software and associated documentation files (the 5 | # "Software"), to deal in the Software without restriction, including 6 | # without limitation the rights to use, copy, modify, merge, publish, 7 | # distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so, as long as 9 | # any reuse or further development of the software attributes the 10 | # National Geospatial-Intelligence Agency (NGA) authorship as follows: 11 | # 'This software (django-gamification) 12 | # is provided to the public as a courtesy of the National 13 | # Geospatial-Intelligence Agency. 14 | # 15 | # The above copyright notice and this permission notice shall be 16 | # included in all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | 27 | from gamification.core.models import Points 28 | from gamification.badges import MetaBadge 29 | 30 | class Gold(MetaBadge): 31 | id = 1 32 | name = "Gold" 33 | model = Points 34 | one_time_only = False 35 | title = "Gold Award" 36 | level = "1" 37 | def check_project(self,instance): 38 | return False 39 | 40 | 41 | class Silver(MetaBadge): 42 | id = 2 43 | name = "Silver" 44 | model = Points 45 | one_time_only = False 46 | title = "Silver Award" 47 | level = "2" 48 | 49 | def check_project(self, instance): 50 | return False 51 | 52 | 53 | class Bronze(MetaBadge): 54 | id = 3 55 | name = "Bronze" 56 | model = Points 57 | one_time_only = False 58 | title = "Bronze Award" 59 | level = "3" 60 | 61 | def check_project(self, instance): 62 | return False 63 | -------------------------------------------------------------------------------- /gamification/core/migrations/0002_initdata.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import datetime 3 | from south.db import db 4 | from south.v2 import DataMigration 5 | from django.db import models 6 | 7 | class Migration(DataMigration): 8 | 9 | def forwards(self, orm): 10 | "Write your forwards methods here." 11 | # Note: Don't use "from appname.models import ModelName". 12 | # Use orm.ModelName to refer to models in this application, 13 | # and orm['appname.ModelName'] for models in other applications. 14 | 15 | def backwards(self, orm): 16 | "Write your backwards methods here." 17 | 18 | models = { 19 | u'auth.group': { 20 | 'Meta': {'object_name': 'Group'}, 21 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 22 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), 23 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) 24 | }, 25 | u'auth.permission': { 26 | 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, 27 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 28 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), 29 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 30 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) 31 | }, 32 | u'auth.user': { 33 | 'Meta': {'object_name': 'User'}, 34 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 35 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), 36 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 37 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), 38 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 39 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 40 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 41 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 42 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 43 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 44 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), 45 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), 46 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) 47 | }, 48 | u'badges.badge': { 49 | 'Meta': {'object_name': 'Badge'}, 50 | 'icon': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}), 51 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 52 | 'level': ('django.db.models.fields.CharField', [], {'max_length': '1'}), 53 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}) 54 | }, 55 | u'badges.projectbadge': { 56 | 'Meta': {'object_name': 'ProjectBadge'}, 57 | 'awardLevel': ('django.db.models.fields.IntegerField', [], {'default': '1000'}), 58 | 'badge': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['badges.Badge']"}), 59 | 'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 60 | 'description': ('django.db.models.fields.TextField', [], {}), 61 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 62 | 'multipleAwards': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 63 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), 64 | 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Project']"}), 65 | 'user': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': u"orm['badges.ProjectBadgeToUser']", 'to': u"orm['auth.User']"}) 66 | }, 67 | u'badges.projectbadgetouser': { 68 | 'Meta': {'object_name': 'ProjectBadgeToUser'}, 69 | 'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 70 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 71 | 'projectbadge': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['badges.ProjectBadge']"}), 72 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) 73 | }, 74 | u'contenttypes.contenttype': { 75 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, 76 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 77 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 78 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 79 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 80 | }, 81 | u'core.points': { 82 | 'Meta': {'object_name': 'Points'}, 83 | 'date_awarded': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), 84 | 'description': ('django.db.models.fields.CharField', [], {'max_length': '200'}), 85 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 86 | 'projectbadge': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['badges.ProjectBadge']"}), 87 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}), 88 | 'value': ('django.db.models.fields.IntegerField', [], {'default': '0'}) 89 | }, 90 | u'core.project': { 91 | 'Meta': {'ordering': "('-created_at',)", 'object_name': 'Project'}, 92 | 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 93 | 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), 94 | 'description': ('django.db.models.fields.TextField', [], {}), 95 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 96 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), 97 | 'private': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 98 | 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}) 99 | }, 100 | u'core.userprofile': { 101 | 'Meta': {'object_name': 'UserProfile'}, 102 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 103 | 'score': ('django.db.models.fields.IntegerField', [], {'default': '1'}), 104 | 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'}) 105 | } 106 | } 107 | 108 | complete_apps = ['core'] 109 | symmetrical = True 110 | -------------------------------------------------------------------------------- /gamification/core/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenrjones/django-gamification/d22882f148375102ec351cb2bc75275083468d73/gamification/core/migrations/__init__.py -------------------------------------------------------------------------------- /gamification/core/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Permission is hereby granted, free of charge, to any person obtaining 4 | # a copy of this software and associated documentation files (the 5 | # "Software"), to deal in the Software without restriction, including 6 | # without limitation the rights to use, copy, modify, merge, publish, 7 | # distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so, as long as 9 | # any reuse or further development of the software attributes the 10 | # National Geospatial-Intelligence Agency (NGA) authorship as follows: 11 | # 'This software (django-gamification) 12 | # is provided to the public as a courtesy of the National 13 | # Geospatial-Intelligence Agency. 14 | # 15 | # The above copyright notice and this permission notice shall be 16 | # included in all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | import json 27 | from django.contrib.auth.models import User 28 | from django.core.urlresolvers import reverse 29 | from django.utils.datastructures import SortedDict 30 | from django.db.models.signals import post_save 31 | from django.db import models 32 | from gamification.badges.models import ProjectBadge, ProjectBadgeToUser 33 | from jsonfield import JSONField 34 | 35 | 36 | TRUE_FALSE = [(0, 'False'), (1, 'True')] 37 | 38 | 39 | class ProjectBase(models.Model): 40 | """ 41 | A generic model for GeoQ objects. 42 | """ 43 | 44 | active = models.BooleanField(default=True, help_text='If checked, this project will be listed in the active list.') 45 | created_at = models.DateTimeField(auto_now_add=True) 46 | name = models.CharField(max_length=200, help_text='Name of the project.') 47 | description = models.TextField(help_text='Details of this project that will be listed on the viewing page.') 48 | updated_at = models.DateTimeField(auto_now=True) 49 | url = models.TextField(help_text='Project Information URL', null=True) 50 | 51 | def __unicode__(self): 52 | return self.name 53 | 54 | class Meta: 55 | abstract = True 56 | ordering = ('-created_at',) 57 | 58 | 59 | class Team(models.Model): 60 | name = models.CharField(max_length=50) 61 | description = models.TextField(null=True, blank=True) 62 | members = models.ManyToManyField(User, null=True, blank=True) 63 | order = models.IntegerField(default=0, null=True, blank=True, help_text='Optionally specify the order teams should appear. Lower numbers appear sooner. By default, teams appear in the order they were created.') 64 | date_created = models.DateTimeField(auto_now_add=True) 65 | 66 | background_color = models.CharField(max_length=50, null=True, blank=True, help_text='Optional - Color to use for background of all team badges') 67 | icon = models.ImageField(upload_to='badge_images', null=True, blank=True, help_text='Optional - Image to show next to team names') 68 | 69 | def __str__(self): 70 | return "%s (%s)" % (self.name, str(len(self.members.all()))) 71 | 72 | class Meta: 73 | ordering = ['-order', '-date_created', 'id'] 74 | 75 | 76 | class Project(ProjectBase): 77 | """ 78 | Top-level organizational object. 79 | """ 80 | 81 | THEMES = ( 82 | ("", "None"), 83 | ("camping", "Camping"), 84 | ("camping2", "Camping Theme 2"), 85 | ("map", "Geospatial"), 86 | ) 87 | 88 | private = models.BooleanField(default=False, help_text='If checked, hide this project from the list of projects and public badge APIs.') 89 | supervisors = models.ManyToManyField(User, blank=True, null=True, related_name="supervisors", help_text='Anyone other than site administrators that can add badges and update the site') 90 | teams = models.ManyToManyField(Team, blank=True, null=True) 91 | viewing_pass_phrase = models.CharField(max_length=200, null=True, blank=True, help_text='Phrase that must be entered to view this page.') 92 | project_closing_date = models.DateTimeField(null=True, blank=True, help_text='Date that project "closes" with countdown shown on project page. Badges can still be added after this.') 93 | visual_theme = models.CharField(max_length=20, default="none", choices=THEMES, help_text='Visual Theme used to style the project page') 94 | background_image = models.ImageField(upload_to='badge_images', null=True, blank=True, help_text='Optional - Override theme background with this image') 95 | 96 | properties = JSONField(null=True, blank=True, help_text='JSON key/value pairs associated with this object, e.g. {"badges_mode":"blue"}') 97 | 98 | query_token = models.CharField(max_length=200, null=True, blank=True, help_text='Token that must be entered by any server requesting data - not implemented yet.') 99 | allowed_api_hosts = models.TextField(null=True, blank=True, help_text='Comma-separated list of hosts (IPs or Hostnames) that can access this project via data requests - not implemented yet') 100 | 101 | @property 102 | def user_count(self): 103 | return User.objects.filter(projectbadgetouser__projectbadge__project=self).distinct().count() 104 | 105 | @property 106 | def badge_count(self): 107 | return ProjectBadgeToUser.objects.filter(projectbadge__project=self).count() 108 | 109 | def get_absolute_url(self): 110 | return reverse('project-list', args=[self.name]) 111 | 112 | class Points(models.Model): 113 | user = models.ForeignKey(User) 114 | projectbadge = models.ForeignKey(ProjectBadge) 115 | value = models.IntegerField(default=0) 116 | date_awarded = models.DateTimeField('date awarded',auto_now=True) 117 | description = models.CharField(max_length=200) 118 | 119 | def get_absolute_url(self): 120 | return reverse('points-list', args=[self.id]) 121 | 122 | class Meta: 123 | verbose_name_plural = "Points" 124 | 125 | 126 | class UserProfile(models.Model): 127 | """ from http://stackoverflow.com/questions/44109/extending-the-user-model-with-custom-fields-in-django; this is one mechanism for adding extra details (currently score for badges) to the User model """ 128 | defaultScore = 1 129 | user = models.OneToOneField(User) 130 | score = models.IntegerField(default=defaultScore) 131 | 132 | def __str__(self): 133 | return "%s's profile" % self.user 134 | 135 | def create_user_profile(sender, instance, created, **kwargs): 136 | if created: 137 | profile, created = UserProfile.objects.get_or_create(user=instance) 138 | 139 | post_save.connect(create_user_profile, sender=User) 140 | 141 | import sys 142 | if not 'syncdb' in sys.argv[1:2] and not 'migrate' in sys.argv[1:2]: 143 | from meta_badges import * -------------------------------------------------------------------------------- /gamification/core/serializers.py: -------------------------------------------------------------------------------- 1 | from django.forms import widgets 2 | from rest_framework import serializers 3 | from models import Project, Points 4 | from django.contrib.auth.models import User 5 | 6 | 7 | class ProjectSerializer(serializers.ModelSerializer): 8 | class Meta: 9 | model = Project 10 | fields = ('id', 'name', 'description', 'created_at', 'active') 11 | 12 | class PointsSerializer(serializers.ModelSerializer): 13 | username = serializers.RelatedField(source='user') 14 | projectbadge_name = serializers.RelatedField(source='projectbadge') 15 | 16 | class Meta: 17 | model = Points 18 | fields = ('username', 'projectbadge_name', 'value', 'date_awarded', 'description') 19 | -------------------------------------------------------------------------------- /gamification/core/templates/core/404.json: -------------------------------------------------------------------------------- 1 | { "message" : "{{ object.message }}" } 2 | -------------------------------------------------------------------------------- /gamification/core/templates/core/award.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | 3 | {% block content %} 4 |

Submit Award

5 |
6 |
7 |
8 |
9 |
10 | {% csrf_token %} 11 | {% for field in form %} 12 |
13 |
14 |
15 | {{ field.label_tag }} 16 |
17 |
18 | 19 |
20 | {{ field }} 21 |
22 |
23 | {% endfor %} 24 |
25 |
26 | 27 |
28 |
29 | 30 |
31 |
32 |
33 |
34 | {% endblock %} -------------------------------------------------------------------------------- /gamification/core/templates/core/badge_list.html: -------------------------------------------------------------------------------- 1 | {% extends "core/base.html" %} 2 | {% load staticfiles %} 3 | {% block content %} 4 | {% block heading %} 5 | 6 |
7 |
8 | 11 |
12 |
13 | 14 | 15 | {% endblock %} 16 |
17 |
18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | {% for badge in object_list %} 31 | 32 | 35 | 41 | 44 | 47 | 50 | 51 | {% endfor %} 52 | 53 |
NameIconDescriptionPoints WorthCan win Multiple?
33 | {{ badge.name }} 34 | 36 | {% with badge_icon=badge.badge__icon %} 37 | {% static badge_icon as badge_url %} 38 | 39 | {% endwith %} 40 | 42 | {{ badge.description }} 43 | 45 | {{ badge.awardLevel }} 46 | 48 | {{ badge.multipleAwards }} 49 |
54 |
55 |
56 |
57 | 58 | {% endblock %} 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /gamification/core/templates/core/base.html: -------------------------------------------------------------------------------- 1 | 2 | {% load bootstrap3 %} 3 | {% load staticfiles %} 4 | {% load version %} 5 | 6 | 7 | 8 | 9 | 10 | 11 | {% block title %}Gamification{% endblock %} 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 36 | 53 | 54 | 55 | {% block container %} 56 | 57 | 105 | 106 | {% block header_title %} 107 | {##} 115 | {% endblock %} 116 | 117 | {% block content %}Empty page{% endblock %} 118 | {% endblock %} 119 | 120 | 121 | -------------------------------------------------------------------------------- /gamification/core/templates/core/index.html: -------------------------------------------------------------------------------- 1 | {% extends "core/base.html" %} 2 | {% block title %}Gamification Server{% endblock %} 3 | {% load staticfiles %} 4 | {% block content %} 5 | 6 |
7 |
8 |
9 |

Gamification Server

10 |

The Gamification Server is a management tool that allows other web pages, apps, or tools to 11 | utilize "Gamification" concepts. These ideas 12 | have been found to help engage with some users and encourage teamwork. This is a prototype concept used to integrate 13 | tools and test out concepts. Interfaces are available either through this web interface or through a REST-based API 14 | calls.

15 |

The source code for this server is Open Source and currently available on GitHub 16 | and available for anyone to use and improve upon. We encourage your participation and want to hear your ideas! 17 |

18 |
19 | 20 |
21 |

Some of the Badges in various projects

22 | {% for badge in object_list %} 23 | {% with badge_icon=badge.badge__icon %} 24 | {% static badge_icon as badge_url %} 25 | 26 | {% endwith %} 27 | {% endfor %} 28 |
29 |
30 |
31 | 32 | {% endblock %} -------------------------------------------------------------------------------- /gamification/core/templates/core/master_badge_list.html: -------------------------------------------------------------------------------- 1 | {% extends "core/base.html" %} 2 | {% load staticfiles %} 3 | {% block title %}Badges by project{% endblock %} 4 | {% block content %} 5 | {% block heading %} 6 | 7 | {% endblock %} 8 |
9 |
10 |
11 | {% regroup object_list by project__name as projects %} 12 | 13 | {% for project in projects %} 14 | 15 |

Project: {{ project.grouper|capfirst }}

16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | {% for badge in project.list %} 29 | 30 | 33 | 39 | 42 | 45 | 48 | 49 | {% endfor %} 50 | 51 |
NameIconDescriptionTagsPoints
31 | {{ badge.name }} 32 | 34 | {% with badge_icon=badge.badge__icon %} 35 | {% static badge_icon as badge_url %} 36 | 37 | {% endwith %} 38 | 40 | {{ badge.description }} 41 | 43 | {{ badge.tags }} 44 | 46 | {{ badge.awardLevel }} 47 |
52 | {% endfor %} 53 |
54 |
55 |
56 | 57 | {% endblock %} 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /gamification/core/templates/core/masterprojects_list.html: -------------------------------------------------------------------------------- 1 | {% extends "core/base.html" %} 2 | 3 | {% block heading %} 4 | 5 |

Project Leaders

6 | 7 | {% endblock %} 8 | 9 | 10 | {% block content %} 11 | {% for project in profile %} 12 |

{{ project.description }}

13 | {% for badge in project.badges %} 14 |
{{ badge.description }}
15 | 16 |
17 |
18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | {% for winner in badge.winners %} 29 | 30 | 33 | 36 | 39 | 40 | {% endfor %} 41 | 42 |
NamePoints ReceivedBadges Awarded
31 | {{ winner.user__username }} 32 | 34 | {{ winner.points_count }} 35 | 37 | TODO 38 |
43 |
44 |
45 |
46 | 47 | {% endfor %} 48 |
49 | {% endfor %} 50 | {% endblock %} 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /gamification/core/templates/core/points_list.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | {% block title %}Points for {{ username }}{% endblock %} 3 | 4 | {% block header_title %} 5 |
6 |

Points for {{ username|capfirst }}

7 |
8 | {% endblock %} 9 | 10 | 11 | 12 | {% block content %} 13 | {% for project in profile %} 14 |
15 |
16 |

{{ project.description }}

17 |
18 |
19 |

20 |
21 |
22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | {% for badge in project.badges %} 35 | {% if badge.total %} 36 | {% for award in badge.awarded %} 37 | 38 | 41 | 44 | 47 | 50 | 51 | {% endfor %} 52 | {#
#} 53 | {#
#} 54 | {#

{{ badge.total }} {{ badge.name }} Points Accumulated

#} 55 | {#
#} 56 | {#
#} 57 | {#
#} 58 | {% endif %} 59 | {% endfor %} 60 | 61 |
Award NamePoints ReceivedDateComment
39 | {{ badge.name }} 40 | 42 | {{ award.value }} 43 | 45 | {{ award.date_awarded|date:"SHORT_DATE_FORMAT" }} at {{ award.date_awarded|time:"H:i" }} 46 | 48 | {{ award.description }} 49 |
62 |
63 |
64 | 65 |
66 |
67 | 68 |
69 |
70 |
71 | 72 |

73 | {% endfor %} 74 | {% endblock %} 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /gamification/core/templates/core/project_admin.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | 3 | {% block header_title %} 4 |
5 |

Administer badges within: {{ project.name|capfirst }}

6 |
7 | {% endblock %} 8 | 9 | {% block content %} 10 | 11 | {% if project.visual_theme %} 12 | 13 | {% endif %} 14 | 15 | 33 | 34 | {% if admin %} 35 | 36 | 65 | 66 | {#

Edit Project Info

#} 67 | {# TODO: Add Form and edit all project details #} 68 | {# #} 69 | {# #} 70 | {# Ending Date#} 71 | 72 |
73 | 74 |

Assign a badge to a user:

75 | {# Add Form #} 76 | 81 | 82 | 87 | 88 |
89 | 90 | 91 | 92 |
93 | 94 |

Create a new user

95 | Username (one word): 96 | (then push 'POST' on bottom to create) 97 | 98 | {% if project.project_closing_date %} 99 | 100 | 101 | 102 |
Project closes in:
103 | 104 | {% endif %} 105 | 106 | {% else %} 107 |

Access Denied - You need admin access to this project

108 |

Return to {{ project.name|capfirst }}

109 | {% endif %} 110 | 111 | {% endblock %} -------------------------------------------------------------------------------- /gamification/core/templates/core/projects.html: -------------------------------------------------------------------------------- 1 | {% extends "core/base.html" %} 2 | {% block title %}Gamification Projects{% endblock %} 3 | {% block content %} 4 |
5 |
6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | {% for project in active_projects %} 20 | 21 | 24 | 27 | 30 | 33 | 36 | 39 | 40 | {% endfor %} 41 | 42 |
Project NameProject DescriptionCreatedLeaderboardBadges GivenBadge Recipients
22 | {{ project.name|title }} 23 | 25 | {{ project.description }} 26 | 28 | {{ project.created_at|date:"SHORT_DATE_FORMAT" }} 29 | 31 | 32 | 34 | {{ project.badge_count }} 35 | 37 | {{ project.user_count }} 38 |
43 | 44 |
45 | 46 | {% if non_active_projects %} 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | {% for project in non_active_projects %} 60 | 61 | 64 | 67 | 70 | 73 | 76 | 77 | {% endfor %} 78 | 79 |
Inactive ProjectProject DescriptionCreatedBadges GivenBadge Recipients
62 | {{ project.name|title }} 63 | 65 | {{ project.description }} 66 | 68 | {{ project.created_at|date:"SHORT_DATE_FORMAT" }} 69 | 71 | {{ project.badge_count }} 72 | 74 | {{ project.user_count }} 75 |
80 | 81 | {% endif %} 82 | 83 |
84 |
85 |
86 | 87 | {% endblock %} 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /gamification/core/templates/core/user_project_points_list.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | 3 | {% block heading %} 4 | 5 | {% endblock %} 6 | 7 | 8 | {% block content %} 9 |
10 |
11 | 14 |
15 |
16 | {% for badge in projectbadges %} 17 | 18 |
19 |
20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | {% for award in badge.awarded %} 32 | 33 | 36 | 39 | 42 | 45 | 46 | {% endfor %} 47 | 48 |
Award NamePoints ReceivedDateComment
34 | {{ badge.name }} 35 | 37 | {{ award.value }} 38 | 40 | {{ award.date_awarded|date:"SHORT_DATE_FORMAT" }} at {{ award.date_awarded|time:"H:i" }} 41 | 43 | {{ award.description }} 44 |
49 |
50 |
51 |
52 |
53 |

Total Points: {{ badge.total }}

54 |
55 |
56 |
57 |
58 | 59 | {% endfor %} 60 | 61 | {% endblock %} 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /gamification/core/templates/core/users.html: -------------------------------------------------------------------------------- 1 | {% extends "core/base.html" %} 2 | {% block content %} 3 | {% block heading %} 4 | 5 |
6 |
7 | 10 |
11 |
12 | 13 | 14 | {% endblock %} 15 |
16 |
17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | {% for user in object_list %} 30 | 31 | 34 | 37 | 40 | 43 | 46 | 47 | {% endfor %} 48 | 49 |
Last NameFirst NameLoginEmailPoints
32 | {{ user.last_name }} 33 | 35 | {{ user.first_name }} 36 | 38 | {{ user.username }} 39 | 41 | {{ user.email }} 42 | 44 | 45 |
50 |
51 |
52 |
53 | 54 | {% endblock %} 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /gamification/core/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- -------------------------------------------------------------------------------- /gamification/core/templatetags/version.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | import time 3 | import os 4 | 5 | register = template.Library() 6 | 7 | @register.simple_tag 8 | def version_date(): 9 | try: 10 | timestamp = "Updated: " + time.strftime('%m/%d/%Y', time.gmtime(os.path.getmtime('.git'))) 11 | except: 12 | timestamp = "" 13 | return timestamp 14 | -------------------------------------------------------------------------------- /gamification/core/tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Permission is hereby granted, free of charge, to any person obtaining 4 | # a copy of this software and associated documentation files (the 5 | # "Software"), to deal in the Software without restriction, including 6 | # without limitation the rights to use, copy, modify, merge, publish, 7 | # distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so, as long as 9 | # any reuse or further development of the software attributes the 10 | # National Geospatial-Intelligence Agency (NGA) authorship as follows: 11 | # 'This software (django-gamification) 12 | # is provided to the public as a courtesy of the National 13 | # Geospatial-Intelligence Agency. 14 | # 15 | # The above copyright notice and this permission notice shall be 16 | # included in all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | 27 | 28 | """ 29 | This file demonstrates writing tests using the unittest module. These will pass 30 | when you run "manage.py test". 31 | 32 | Replace this with more appropriate tests for your application. 33 | """ 34 | 35 | from django.test import TestCase 36 | 37 | 38 | class SimpleTest(TestCase): 39 | def test_basic_addition(self): 40 | """ 41 | Tests that 1 + 1 always equals 2. 42 | """ 43 | self.assertEqual(1 + 1, 2) 44 | -------------------------------------------------------------------------------- /gamification/core/urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Permission is hereby granted, free of charge, to any person obtaining 4 | # a copy of this software and associated documentation files (the 5 | # "Software"), to deal in the Software without restriction, including 6 | # without limitation the rights to use, copy, modify, merge, publish, 7 | # distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so, as long as 9 | # any reuse or further development of the software attributes the 10 | # National Geospatial-Intelligence Agency (NGA) authorship as follows: 11 | # 'This software (django-gamification) 12 | # is provided to the public as a courtesy of the National 13 | # Geospatial-Intelligence Agency. 14 | # 15 | # The above copyright notice and this permission notice shall be 16 | # included in all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | from django.conf.urls import patterns, url, include 27 | from views import * 28 | 29 | 30 | urlpatterns = patterns('', 31 | 32 | # PROJECTS 33 | 34 | url(r'^projects/(?P\w+)/award/?$', 'gamification.core.views.award', name='award'), 35 | url(r'^projects/(?P\w+)/points/?$', user_project_points_list), 36 | url(r'^projects/(?P\w+)/points/?format=(?P\w+)?$', user_project_points_list), 37 | url(r'^projects/(?P\w+)/total/?$', user_points), 38 | url(r'^projects/(?P[\w,]+)/badges/?$', user_project_badges_list), 39 | url(r'^projects/(?P[\w,]+)/badges/?format=(?P\w+)?$', user_project_badges_list), 40 | 41 | # POINTS 42 | url(r'^points/?$', user_points_list), 43 | 44 | ) 45 | -------------------------------------------------------------------------------- /gamification/core/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.db import models 4 | from django.contrib.auth.models import User 5 | 6 | from models import Points, Project, ProjectBadge 7 | from gamification.badges.models import ProjectBadgeToUser 8 | 9 | def badge_count(user): 10 | """ 11 | Given a user or queryset of users, this returns the badge 12 | count at each badge level that the user(s) have earned. 13 | 14 | Example: 15 | 16 | >>> badge_count(User.objects.filter(username='admin')) 17 | [{'count': 0, 'badge__level': '1'}, {'count': 0, 'badge__level': '2'}, {'count': 0, 'badge__level': '3'}, {'count': 0, 'badge__level': '4'}] 18 | 19 | Uses a single database query. 20 | """ 21 | 22 | projects = Project.objects.all().values('id','name','description') 23 | projects = list(projects) 24 | 25 | projectbadges = ProjectBadge.objects.all() 26 | points = Points.objects.filter(user=user) 27 | 28 | for project in projects: 29 | badges = projectbadges.filter(project_id=project['id']).values('id','name') 30 | badges = list(badges) 31 | 32 | for badge in badges: 33 | badge_points = points.filter(projectbadge_id=badge['id']) 34 | total = badge_points.aggregate(models.Sum('value')) 35 | badge_points = badge_points.values('value','date_awarded','description') 36 | badge_points = list(badge_points) 37 | badge['awarded'] = badge_points 38 | badge['total'] = total['value__sum'] 39 | 40 | project['badges'] = badges 41 | 42 | return projects 43 | 44 | 45 | def top_n_points_winners(projects, n): 46 | """ 47 | Given a particular project, this returns the top n points 48 | winners at each badge level. 49 | 50 | Example: 51 | 52 | >>> top_five_badge_winners(Project.objects.filter(projectname='geoq')) 53 | [{'count': 0, 'badge__level': '1'}, {'count': 0, 'badge__level': '2'}, {'count': 0, 'badge__level': '3'}, {'count': 0, 'badge__level': '4'}] 54 | 55 | """ 56 | points = Points.objects.all() 57 | projects = projects.values('id','active','description','private') 58 | projects = list(projects) 59 | 60 | for project in projects: 61 | projectbadges = ProjectBadge.objects.filter(project_id=project['id']).values('id','description') 62 | projectbadges = list(projectbadges) 63 | 64 | for badge in projectbadges: 65 | badge_points_winners = points.filter(projectbadge_id=badge['id']).select_related('user__username').values('user_id','user__username').annotate(points_count=models.Sum('value')).order_by('-points_count')[:n] 66 | badge['winners'] = badge_points_winners 67 | 68 | project['badges'] = projectbadges 69 | 70 | return projects 71 | 72 | def project_badge_awards(project): 73 | """ 74 | Given a particular project, this returns all badge winners. 75 | """ 76 | ids = ProjectBadge.objects.filter(project=project).values('id') 77 | 78 | user_badges = ProjectBadgeToUser.objects.filter(projectbadge__in=ids)\ 79 | .values('user__username', 'projectbadge__name', 'created', 'projectbadge__badge__icon', 'projectbadge__awardLevel', 'projectbadge__tags') 80 | 81 | project_teams = project[0].teams.all() 82 | 83 | from collections import defaultdict 84 | groups = defaultdict(list) 85 | scores = defaultdict(int) 86 | teams = defaultdict(str) 87 | for obj in user_badges: 88 | username = obj['user__username'] 89 | groups[username].append({ 90 | 'badge':str(obj['projectbadge__name']), 91 | 'date':str(obj['created']), 92 | 'icon':str(obj['projectbadge__badge__icon']), 93 | 'tags':str(obj['projectbadge__tags']), 94 | 'points':str(obj['projectbadge__awardLevel'])}) 95 | 96 | #Find any teams the user is on that are on this project 97 | user_team = '' 98 | for project_team in project_teams: 99 | if username in [(u.username) for u in project_team.members.all()]: 100 | user_team = str(project_team.name) 101 | teams[username] = user_team 102 | 103 | scores[username] += obj['projectbadge__awardLevel'] 104 | 105 | #add scores 106 | items = groups.items() 107 | for i in range(len(items)): 108 | items[i] = items[i] + (scores[items[i][0]],) + (teams[items[i][0]],) 109 | 110 | return sorted(items, key=lambda rec: rec[2],reverse=True) 111 | 112 | def get_files_in_dir(mypath): 113 | from os import listdir 114 | from os.path import isfile, join 115 | return [ f for f in listdir(mypath) if isfile(join(mypath,f)) ] 116 | 117 | 118 | def top_n_badge_winners(project, num=3): 119 | """ 120 | Given a particular project, this returns the top n badge 121 | winners at each badge level. 122 | 123 | Example: 124 | 125 | >>> top_n_badge_winners(Project.objects.filter(name='geoq'),5) 126 | """ 127 | projectbadges = ProjectBadge.objects.filter(project=project) 128 | ids = projectbadges.values('id') 129 | badges = projectbadges.values('id','name','description','badge__icon','awardLevel') 130 | 131 | pbtu = ProjectBadgeToUser.objects.filter(projectbadge__in=ids) 132 | badges = list(badges) 133 | 134 | for badge in badges: 135 | badge['leaders'] = top_n_badge_project_winners(pbtu,badge['id'],num) 136 | del badge['id'] 137 | 138 | return badges 139 | 140 | def top_n_project_badge_winners(project,badge,num): 141 | """ 142 | Given a particular project and badge, determine top n leaders 143 | """ 144 | 145 | pbtu = ProjectBadgeToUser.objects.filter(projectbadge=badge) 146 | badge.leaders = top_n_badge_project_winners(pbtu, badge.id, num) 147 | 148 | return badge 149 | 150 | def top_n_badge_project_winners(pbtu_qs,pb_id,num): 151 | topn = pbtu_qs.filter(projectbadge__id=pb_id).values('user__username').annotate(awarded=models.Count('user__username')).order_by('-awarded')[:num] 152 | return list(topn) 153 | 154 | def users_project_points(user,project): 155 | """ 156 | Find out a user's total points won on project, factoring in weighted values of badges 157 | """ 158 | total = ProjectBadge.objects.filter(user=user,project=project).aggregate(models.Sum('awardLevel')) 159 | return total['awardLevel__sum'] 160 | 161 | def users_total_points(user): 162 | """ 163 | Find out a user's points from all projects 164 | """ 165 | total = ProjectBadge.objects.filter(user=user).aggregate(models.Sum('awardLevel')) 166 | return total['awardLevel__sum'] 167 | 168 | def user_project_badge_count(user,project): 169 | """ 170 | Given a user or queryset of users, this returns the badge 171 | count at each badge level that the user(s) have earned. 172 | 173 | Example: 174 | 175 | >>> badge_count(User.objects.filter(username='admin')) 176 | [{'count': 0, 'badge__level': '1'}, {'count': 0, 'badge__level': '2'}, {'count': 0, 'badge__level': '3'}, {'count': 0, 'badge__level': '4'}] 177 | 178 | Uses a single database query. 179 | """ 180 | 181 | projectbadges = ProjectBadge.objects.filter(project_id=project.id).values('id','name','description') 182 | projectbadgeids = projectbadges.values('id') 183 | points = Points.objects.filter(user=user,projectbadge__id__in=projectbadgeids) 184 | badges = list(projectbadges) 185 | 186 | for badge in badges: 187 | badge_points = points.filter(projectbadge_id=badge['id']) 188 | total = badge_points.aggregate(models.Sum('value')) 189 | badge_points = badge_points.values('value','date_awarded','description') 190 | badge_points = list(badge_points) 191 | badge['awarded'] = badge_points 192 | badge['total'] = total['value__sum'] 193 | 194 | return badges -------------------------------------------------------------------------------- /gamification/events/__init__.py: -------------------------------------------------------------------------------- 1 | # Permission is hereby granted, free of charge, to any person obtaining 2 | # a copy of this software and associated documentation files (the 3 | # "Software"), to deal in the Software without restriction, including 4 | # without limitation the rights to use, copy, modify, merge, publish, 5 | # distribute, sublicense, and/or sell copies of the Software, and to 6 | # permit persons to whom the Software is furnished to do so, as long as 7 | # any reuse or further development of the software attributes the 8 | # National Geospatial-Intelligence Agency (NGA) authorship as follows: 9 | # 'This software (django-gamification) 10 | # is provided to the public as a courtesy of the National 11 | # Geospatial-Intelligence Agency. 12 | # 13 | # The above copyright notice and this permission notice shall be 14 | # included in all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /gamification/events/admin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Permission is hereby granted, free of charge, to any person obtaining 4 | # a copy of this software and associated documentation files (the 5 | # "Software"), to deal in the Software without restriction, including 6 | # without limitation the rights to use, copy, modify, merge, publish, 7 | # distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so, as long as 9 | # any reuse or further development of the software attributes the 10 | # National Geospatial-Intelligence Agency (NGA) authorship as follows: 11 | # 'This software (django-gamification) 12 | # is provided to the public as a courtesy of the National 13 | # Geospatial-Intelligence Agency. 14 | # 15 | # The above copyright notice and this permission notice shall be 16 | # included in all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | from models import Event, Policy 27 | from django.contrib import admin 28 | 29 | class EventAdmin(admin.ModelAdmin): 30 | list_display = ('id', 'user', 'project', 'event_dtg', 'details') 31 | 32 | class PolicyAdmin(admin.ModelAdmin): 33 | list_display = ('id', 'project','projectbadge', 'type') 34 | 35 | 36 | admin.site.register(Event, EventAdmin) 37 | admin.site.register(Policy, PolicyAdmin) -------------------------------------------------------------------------------- /gamification/events/experimental/mandatory_training_1.policy: -------------------------------------------------------------------------------- 1 | from state import DemoState 2 | 3 | rule "Rule 1": 4 | when: 5 | $state := DemoState((project.name == 'training') and ('course_complete' in event_data) and ('008031' in event_data['course_complete']) and ('008189' in event_data['course_complete']) and ('008582' in event_data['course_complete']) and ('009446' in event_data['course_complete']) and ('013413' in event_data['course_complete']) and ('013567' in event_data['course_complete']) and ('016003' in event_data['course_complete']) and ('016094' in event_data['course_complete']) and ('017724' in event_data['course_complete']) and ('020146' in event_data['course_complete']) and ('023416' in event_data['course_complete'])) 6 | then: 7 | $state.award($state.user, $state.project, 'Gold') 8 | -------------------------------------------------------------------------------- /gamification/events/experimental/mandatory_training_2.policy: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from state import DemoState 3 | 4 | rule "Rule 1": 5 | when: 6 | $state := DemoState((project.name == 'training') and ('course_complete' in event_data) and ('008031' in event_data['course_complete']) and (event_data['course_complete']['008031'] < datetime(2015, 1, 1)) and ('008189' in event_data['course_complete']) and (event_data['course_complete']['008189'] < datetime(2015, 1, 1)) and ('008582' in event_data['course_complete']) and (event_data['course_complete']['008582'] < datetime(2015, 1, 1)) and ('009446' in event_data['course_complete']) and (event_data['course_complete']['009446'] < datetime(2015, 1, 1)) and ('013413' in event_data['course_complete']) and (event_data['course_complete']['013413'] < datetime(2015, 1, 1)) and ('013567' in event_data['course_complete']) and (event_data['course_complete']['013567'] < datetime(2015, 1, 1)) and ('016003' in event_data['course_complete']) and (event_data['course_complete']['016003'] < datetime(2015, 1, 1)) and ('016094' in event_data['course_complete']) and (event_data['course_complete']['016094'] < datetime(2015, 1, 1)) and ('017724' in event_data['course_complete']) and (event_data['course_complete']['017724'] < datetime(2015, 1, 1)) and ('020146' in event_data['course_complete']) and (event_data['course_complete']['020146'] < datetime(2015, 1, 1)) and ('023416' in event_data['course_complete']) and (event_data['course_complete']['023416'] < datetime(2015, 1, 1))) 7 | then: 8 | $state.award($state.user, $state.project, 'Gold') 9 | halt 10 | 11 | rule "Rule 2": 12 | when: 13 | $state := DemoState((project.name == 'training') and ('course_complete' in event_data) and ('008031' in event_data['course_complete']) and (event_data['course_complete']['008031'] <= datetime(2015, 1, 1)) and ('008189' in event_data['course_complete']) and (event_data['course_complete']['008189'] <= datetime(2015, 1, 1)) and ('008582' in event_data['course_complete']) and (event_data['course_complete']['008582'] <= datetime(2015, 1, 1)) and ('009446' in event_data['course_complete']) and (event_data['course_complete']['009446'] <= datetime(2015, 1, 1)) and ('013413' in event_data['course_complete']) and (event_data['course_complete']['013413'] <= datetime(2015, 1, 1)) and ('013567' in event_data['course_complete']) and (event_data['course_complete']['013567'] <= datetime(2015, 1, 1)) and ('016003' in event_data['course_complete']) and (event_data['course_complete']['016003'] <= datetime(2015, 1, 1)) and ('016094' in event_data['course_complete']) and (event_data['course_complete']['016094'] <= datetime(2015, 1, 1)) and ('017724' in event_data['course_complete']) and (event_data['course_complete']['017724'] <= datetime(2015, 1, 1)) and ('020146' in event_data['course_complete']) and (event_data['course_complete']['020146'] <= datetime(2015, 1, 1)) and ('023416' in event_data['course_complete']) and (event_data['course_complete']['023416'] <= datetime(2015, 1, 1))) 14 | then: 15 | $state.award($state.user, $state.project, 'Silver') 16 | halt 17 | 18 | rule "Rule 3": 19 | when: 20 | $state := DemoState((project.name == 'training') and ('course_complete' in event_data) and ('008031' in event_data['course_complete']) and (event_data['course_complete']['008031'] >= datetime(2015, 1, 1)) and ('008189' in event_data['course_complete']) and (event_data['course_complete']['008189'] >= datetime(2015, 1, 1)) and ('008582' in event_data['course_complete']) and (event_data['course_complete']['008582'] >= datetime(2015, 1, 1)) and ('009446' in event_data['course_complete']) and (event_data['course_complete']['009446'] >= datetime(2015, 1, 1)) and ('013413' in event_data['course_complete']) and (event_data['course_complete']['013413'] >= datetime(2015, 1, 1)) and ('013567' in event_data['course_complete']) and (event_data['course_complete']['013567'] >= datetime(2015, 1, 1)) and ('016003' in event_data['course_complete']) and (event_data['course_complete']['016003'] >= datetime(2015, 1, 1)) and ('016094' in event_data['course_complete']) and (event_data['course_complete']['016094'] >= datetime(2015, 1, 1)) and ('017724' in event_data['course_complete']) and (event_data['course_complete']['017724'] >= datetime(2015, 1, 1)) and ('020146' in event_data['course_complete']) and (event_data['course_complete']['020146'] >= datetime(2015, 1, 1)) and ('023416' in event_data['course_complete']) and (event_data['course_complete']['023416'] >= datetime(2015, 1, 1))) 21 | then: 22 | $state.award($state.user, $state.project, 'Bronze') 23 | -------------------------------------------------------------------------------- /gamification/events/experimental/rules_engine.py: -------------------------------------------------------------------------------- 1 | # Permission is hereby granted, free of charge, to any person obtaining 2 | # a copy of this software and associated documentation files (the 3 | # "Software"), to deal in the Software without restriction, including 4 | # without limitation the rights to use, copy, modify, merge, publish, 5 | # distribute, sublicense, and/or sell copies of the Software, and to 6 | # permit persons to whom the Software is furnished to do so, as long as 7 | # any reuse or further development of the software attributes the 8 | # National Geospatial-Intelligence Agency (NGA) authorship as follows: 9 | # 'This software (django-gamification) 10 | # is provided to the public as a courtesy of the National 11 | # Geospatial-Intelligence Agency. 12 | # 13 | # The above copyright notice and this permission notice shall be 14 | # included in all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | from datetime import datetime 25 | from intellect.Intellect import Intellect 26 | from state import DemoState 27 | 28 | # Experimental class that was used for early rules engine testing purposes 29 | class RulesEngine(object): 30 | 31 | def reason(self, policy, state): 32 | intellect = Intellect() 33 | intellect.learn(policy) 34 | intellect.learn(state) 35 | intellect.reason() 36 | 37 | def run_test(self, policy, event_dtg): 38 | username = 'John Doe' 39 | projectname = 'training' 40 | event_type = 'course_complete' 41 | event_data = {} 42 | event_data[event_type] = training_event_data = {} 43 | 44 | course_ids = ['008031', '008189', '008582', '009446', '013413', '013567', '016003', '016094', '017724', '020146', '023416'] 45 | 46 | for cid in course_ids: 47 | print ('Adding course {0}'.format(cid)) 48 | training_event_data[cid] = event_dtg 49 | 50 | class Object(object): 51 | pass 52 | user = Object() 53 | user.username = username 54 | project = Object() 55 | project.name = projectname 56 | 57 | self.reason(policy, DemoState(user, project, event_data)) 58 | 59 | if __name__ == '__main__': 60 | policy1 = "from state import DemoState\nrule 'Rule 1':\n\twhen:\n\t\t$state := DemoState((project.name == 'training') and ('course_complete' in event_data) and ('008031' in event_data['course_complete']) and ('008189' in event_data['course_complete']) and ('008582' in event_data['course_complete']) and ('009446' in event_data['course_complete']) and ('013413' in event_data['course_complete']) and ('013567' in event_data['course_complete']) and ('016003' in event_data['course_complete']) and ('016094' in event_data['course_complete']) and ('017724' in event_data['course_complete']) and ('020146' in event_data['course_complete']) and ('023416' in event_data['course_complete']))\n\tthen:\n\t\t$state.award($state.user, $state.project, 'Gold')\n" 61 | utilIntellect = Intellect() 62 | policy2 = utilIntellect.local_file_uri('./mandatory_training_2.policy') 63 | engine = RulesEngine() 64 | engine.run_test(policy1, '') # User should get gold 65 | engine.run_test(policy2, datetime.now()) # User should get gold 66 | engine.run_test(policy2, datetime(2015, 1, 1)) # User should get silver 67 | engine.run_test(policy2, datetime(2015, 2, 1)) # User should get bronze 68 | -------------------------------------------------------------------------------- /gamification/events/experimental/state.py: -------------------------------------------------------------------------------- 1 | # Permission is hereby granted, free of charge, to any person obtaining 2 | # a copy of this software and associated documentation files (the 3 | # "Software"), to deal in the Software without restriction, including 4 | # without limitation the rights to use, copy, modify, merge, publish, 5 | # distribute, sublicense, and/or sell copies of the Software, and to 6 | # permit persons to whom the Software is furnished to do so, as long as 7 | # any reuse or further development of the software attributes the 8 | # National Geospatial-Intelligence Agency (NGA) authorship as follows: 9 | # 'This software (django-gamification) 10 | # is provided to the public as a courtesy of the National 11 | # Geospatial-Intelligence Agency. 12 | # 13 | # The above copyright notice and this permission notice shall be 14 | # included in all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | class DemoState(object): 25 | 26 | def __init__(self, user, project, event_data): 27 | self._user = user 28 | self._project = project 29 | self._event_data = event_data # A dictionary of dictionaries 30 | 31 | @property 32 | def user(self): 33 | return self._user 34 | 35 | @property 36 | def project(self): 37 | return self._project 38 | 39 | @property 40 | def event_data(self): 41 | return self._event_data 42 | 43 | @event_data.setter 44 | def event_data(self, event_data): 45 | self._event_data = event_data 46 | 47 | def award(self, user, project, award_id): 48 | print('User "{0}" got award "{1}" for project "{2}"').format(user.username, award_id, project.name) 49 | -------------------------------------------------------------------------------- /gamification/events/fixtures/camp_data.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "pk": 11, 4 | "model": "events.policy", 5 | "fields": { 6 | "project": 5, 7 | "type": 0, 8 | "projectbadge": 23, 9 | "rule": "from gamification.events.models import Event\nimport datetime\nrule 'Rule 1':\n\twhen:\n\t\t$event := Event(('event_type' in details_map) and ('attendance' == details_map['event_type']) and (dtg.date() >= datetime.date(2014,7,21)) and (dtg.date() <= datetime.date(2014,7,25)))\n\tthen:\n\t\t$event.update_state('attendance', $event.event_dtg.date(), \"\")\n" 10 | } 11 | }, 12 | { 13 | "pk": 12, 14 | "model": "events.policy", 15 | "fields": { 16 | "project": 5, 17 | "type": 1, 18 | "projectbadge": 23, 19 | "rule": "from gamification.events.state import State\nimport datetime\nrule 'Rule 1':\n\twhen:\n\t\t$state := State(('attendance' in event_data) and (datetime.date(2014,7,21) in event_data['attendance']) and (datetime.date(2014,7,22) in event_data['attendance']) and (datetime.date(2014,7,23) in event_data['attendance']) and (datetime.date(2014,7,24) in event_data['attendance']) and (datetime.date(2014,7,25) in event_data['attendance']))\n\tthen:\n\t\t$state.award($state.user, $state.project)\n" 20 | } 21 | }, 22 | { 23 | "pk": 13, 24 | "model": "events.policy", 25 | "fields": { 26 | "project": 5, 27 | "type": 0, 28 | "projectbadge": 24, 29 | "rule": "from gamification.events.models import Event\nimport datetime\nrule 'Rule 1':\n\twhen:\n\t\t$event := Event(('event_type' in details_map) and ('attendance' == details_map['event_type']) and (dtg.date() >= datetime.date(2014,7,29)) and (dtg.date() <= datetime.date(2014,8,1)))\n\tthen:\n\t\t$event.update_state('attendance', $event.event_dtg.date(), \"\")\n" 30 | } 31 | }, 32 | { 33 | "pk": 14, 34 | "model": "events.policy", 35 | "fields": { 36 | "project": 5, 37 | "type": 1, 38 | "projectbadge": 24, 39 | "rule": "from gamification.events.state import State\nimport datetime\nrule 'Rule 1':\n\twhen:\n\t\t$state := State(('attendance' in event_data) and (datetime.date(2014,7,28) in event_data['attendance']) and (datetime.date(2014,7,29) in event_data['attendance']) and (datetime.date(2014,7,30) in event_data['attendance']) and (datetime.date(2014,7,31) in event_data['attendance']) and (datetime.date(2014,8,1) in event_data['attendance']))\n\tthen:\n\t\t$state.award($state.user, $state.project)\n" 40 | } 41 | }, 42 | { 43 | "pk": 15, 44 | "model": "events.policy", 45 | "fields": { 46 | "project": 5, 47 | "type": 0, 48 | "projectbadge": 22, 49 | "rule": "from gamification.events.models import Event\nimport datetime\nrule 'Rule 1':\n\twhen:\n\t\t$event := Event(('event_type' in details_map) and ('attendance' == details_map['event_type']) and (dtg.date() >= datetime.date(2014,7,14)) and (dtg.date() <= datetime.date(2014,7,18)))\n\tthen:\n\t\t$event.update_state('attendance', $event.event_dtg.date(), \"\")\n" 50 | } 51 | }, 52 | { 53 | "pk": 16, 54 | "model": "events.policy", 55 | "fields": { 56 | "project": 5, 57 | "type": 1, 58 | "projectbadge": 22, 59 | "rule": "from gamification.events.state import State\nimport datetime\nrule 'Rule 1':\n\twhen:\n\t\t$state := State(('attendance' in event_data) and (datetime.date(2014,7,14) in event_data['attendance']) and (datetime.date(2014,7,15) in event_data['attendance']) and (datetime.date(2014,7,16) in event_data['attendance']) and (datetime.date(2014,7,17) in event_data['attendance']) and (datetime.date(2014,7,18) in event_data['attendance']))\n\tthen:\n\t\t$state.award($state.user, $state.project)\n" 60 | } 61 | }, 62 | { 63 | "pk": 17, 64 | "model": "events.policy", 65 | "fields": { 66 | "project": 5, 67 | "type": 0, 68 | "projectbadge": 14, 69 | "rule": "from gamification.events.models import Event\nimport datetime\nrule 'Rule 1':\n\twhen:\n\t\t$event := Event(('event_type' in details_map) and ('attendance' == details_map['event_type']) and (is_today()) and (dtg.date() >= datetime.date(2014,7,14)) and (dtg.date() <= datetime.date(2014,8,1)))\n\tthen:\n\t\t$event.update_state('participation', $event.event_dtg.date(), \"\")\n" 70 | } 71 | }, 72 | { 73 | "pk": 18, 74 | "model": "events.policy", 75 | "fields": { 76 | "project": 5, 77 | "type": 1, 78 | "projectbadge": 14, 79 | "rule": "from gamification.events.state import State\nimport datetime\nrule 'Rule 1':\n\twhen:\n\t\t$state := State(('participation' in event_data) and (len(event_data['participation']) == 1))\n\tthen:\n\t\t$state.award($state.user, $state.project)\n" 80 | } 81 | }, 82 | { 83 | "pk": 18, 84 | "model": "events.policy", 85 | "fields": { 86 | "project": 5, 87 | "type": 0, 88 | "projectbadge": 13, 89 | "rule": "from gamification.events.models import Event\nimport datetime\nrule 'Rule 1':\n\twhen:\n\t\t$event := Event(('event_type' in details_map) and ('awardee' in details_map) and ('nga' == details_map['event_type']) and (is_today()) and (dtg.date() >= datetime.date(2014,7,14)) and (dtg.date() <= datetime.date(2014,8,1)))\n\tthen:\n\t\t$event.update_state('nga', $event.user, details_map['awardee'])\n" 90 | } 91 | }, 92 | { 93 | "pk": 19, 94 | "model": "events.policy", 95 | "fields": { 96 | "project": 5, 97 | "type": 1, 98 | "projectbadge": 13, 99 | "rule": "from gamification.events.state import State\nimport datetime\nrule 'Rule 1':\n\twhen:\n\t\t$state := State(('nga' in event_data) and (len(event_data['nga']) == 1))\n\tthen:\n\t\t$state.award($state.user, $state.project)\n" 100 | } 101 | } 102 | ] 103 | -------------------------------------------------------------------------------- /gamification/events/fixtures/initial_data.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "pk": 1, 4 | "model": "events.policy", 5 | "fields": { 6 | "project": 3, 7 | "type": 0, 8 | "projectbadge": 6, 9 | "rule": "from gamification.events.models import Event\nrule 'Rule 1':\n\twhen:\n\t\t$event := Event(('event_type' in details_map) and ('aoi_complete' == details_map['event_type']) and ('aoi_id' in details_map) and (is_current_event) and (details_map['feature_count'] > 0))\n\tthen:\n\t\t$event.update_state('aoi_complete', $event.details_map['aoi_id'], $event.event_dtg)\n" 10 | } 11 | }, 12 | { 13 | "pk": 2, 14 | "model": "events.policy", 15 | "fields": { 16 | "project": 3, 17 | "type": 1, 18 | "projectbadge": 6, 19 | "rule": "from gamification.events.state import State\nrule 'Rule 1':\n\twhen:\n\t\t$state := State(('aoi_complete' in event_data) and (len(event_data['aoi_complete']) > 0))\n\tthen:\n\t\t$state.award($state.user, $state.project)\n" 20 | } 21 | }, 22 | { 23 | "pk": 3, 24 | "model": "events.policy", 25 | "fields": { 26 | "project": 3, 27 | "type": 0, 28 | "projectbadge": 5, 29 | "rule": "from gamification.events.models import Event\nrule 'Rule 1':\n\twhen:\n\t\t$event := Event(('event_type' in details_map) and ('aoi_complete' in details_map['event_type']) and ('aoi_id' in details_map) and (details_map['feature_count'] > 0))\n\tthen:\n\t\t$event.update_state('aoi_complete', $event.details_map['aoi_id'], $event.event_dtg)\n" 30 | } 31 | }, 32 | { 33 | "pk": 4, 34 | "model": "events.policy", 35 | "fields": { 36 | "project": 3, 37 | "type": 1, 38 | "projectbadge": 5, 39 | "rule": "from gamification.events.state import State\nrule 'Rule 1':\n\twhen:\n\t\t$state := State(('aoi_complete' in event_data) and (len(event_data['aoi_complete']) % 3 == 0))\n\tthen:\n\t\t$state.award($state.user, $state.project)\n" 40 | } 41 | }, 42 | { 43 | "pk": 9, 44 | "model": "events.policy", 45 | "fields": { 46 | "project": 4, 47 | "type": 0, 48 | "projectbadge": 7, 49 | "rule": "from gamification.events.models import Event\nrule 'Rule 1':\n\twhen:\n\t\t$event := Event(('event_type' in details_map) and (is_current_event))\n\tthen:\n\t\t$event.update_state('action_complete',$event.details_map['event_type'],$event.event_dtg)\n" 50 | } 51 | }, 52 | { 53 | "pk": 10, 54 | "model": "events.policy", 55 | "fields": { 56 | "project": 4, 57 | "type": 1, 58 | "projectbadge": 7, 59 | "rule": "from gamification.events.state import State\nrule 'Rule 1':\n\twhen:\n\t\t$state := State(('action_complete' in event_data))\n\tthen:\n\t\t$state.award($state.user,$state.project)\n" 60 | } 61 | } 62 | ] 63 | -------------------------------------------------------------------------------- /gamification/events/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import datetime 3 | from south.db import db 4 | from south.v2 import SchemaMigration 5 | from django.db import models 6 | 7 | 8 | class Migration(SchemaMigration): 9 | 10 | def forwards(self, orm): 11 | # Adding model 'Event' 12 | db.create_table(u'events_event', ( 13 | (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), 14 | ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])), 15 | ('project', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['core.Project'])), 16 | ('event_dtg', self.gf('django.db.models.fields.DateTimeField')()), 17 | ('details', self.gf('django.db.models.fields.TextField')()), 18 | )) 19 | db.send_create_signal(u'events', ['Event']) 20 | 21 | # Adding model 'Policy' 22 | db.create_table(u'events_policy', ( 23 | (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), 24 | ('project', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['core.Project'])), 25 | ('projectbadge', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['badges.ProjectBadge'])), 26 | ('type', self.gf('django.db.models.fields.IntegerField')()), 27 | ('rule', self.gf('django.db.models.fields.TextField')()), 28 | )) 29 | db.send_create_signal(u'events', ['Policy']) 30 | 31 | 32 | def backwards(self, orm): 33 | # Deleting model 'Event' 34 | db.delete_table(u'events_event') 35 | 36 | # Deleting model 'Policy' 37 | db.delete_table(u'events_policy') 38 | 39 | 40 | models = { 41 | u'auth.group': { 42 | 'Meta': {'object_name': 'Group'}, 43 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 44 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), 45 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) 46 | }, 47 | u'auth.permission': { 48 | 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, 49 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 50 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), 51 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 52 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) 53 | }, 54 | u'auth.user': { 55 | 'Meta': {'object_name': 'User'}, 56 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 57 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), 58 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 59 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), 60 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 61 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 62 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 63 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 64 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 65 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 66 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), 67 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), 68 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) 69 | }, 70 | u'badges.badge': { 71 | 'Meta': {'object_name': 'Badge'}, 72 | 'icon': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}), 73 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 74 | 'level': ('django.db.models.fields.CharField', [], {'max_length': '1'}), 75 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}) 76 | }, 77 | u'badges.projectbadge': { 78 | 'Meta': {'object_name': 'ProjectBadge'}, 79 | 'awardLevel': ('django.db.models.fields.IntegerField', [], {'default': '1000'}), 80 | 'badge': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['badges.Badge']"}), 81 | 'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 82 | 'description': ('django.db.models.fields.TextField', [], {}), 83 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 84 | 'multipleAwards': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 85 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), 86 | 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Project']"}), 87 | 'user': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': u"orm['badges.ProjectBadgeToUser']", 'to': u"orm['auth.User']"}) 88 | }, 89 | u'badges.projectbadgetouser': { 90 | 'Meta': {'object_name': 'ProjectBadgeToUser'}, 91 | 'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 92 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 93 | 'projectbadge': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['badges.ProjectBadge']"}), 94 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) 95 | }, 96 | u'contenttypes.contenttype': { 97 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, 98 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 99 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 100 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 101 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 102 | }, 103 | u'core.project': { 104 | 'Meta': {'ordering': "('-created_at',)", 'object_name': 'Project'}, 105 | 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 106 | 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), 107 | 'description': ('django.db.models.fields.TextField', [], {}), 108 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 109 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), 110 | 'private': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 111 | 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}) 112 | }, 113 | u'events.event': { 114 | 'Meta': {'object_name': 'Event'}, 115 | 'details': ('django.db.models.fields.TextField', [], {}), 116 | 'event_dtg': ('django.db.models.fields.DateTimeField', [], {}), 117 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 118 | 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Project']"}), 119 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) 120 | }, 121 | u'events.policy': { 122 | 'Meta': {'object_name': 'Policy'}, 123 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 124 | 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Project']"}), 125 | 'projectbadge': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['badges.ProjectBadge']"}), 126 | 'rule': ('django.db.models.fields.TextField', [], {}), 127 | 'type': ('django.db.models.fields.IntegerField', [], {}) 128 | } 129 | } 130 | 131 | complete_apps = ['events'] -------------------------------------------------------------------------------- /gamification/events/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenrjones/django-gamification/d22882f148375102ec351cb2bc75275083468d73/gamification/events/migrations/__init__.py -------------------------------------------------------------------------------- /gamification/events/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Permission is hereby granted, free of charge, to any person obtaining 4 | # a copy of this software and associated documentation files (the 5 | # "Software"), to deal in the Software without restriction, including 6 | # without limitation the rights to use, copy, modify, merge, publish, 7 | # distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so, as long as 9 | # any reuse or further development of the software attributes the 10 | # National Geospatial-Intelligence Agency (NGA) authorship as follows: 11 | # 'This software (django-gamification) 12 | # is provided to the public as a courtesy of the National 13 | # Geospatial-Intelligence Agency. 14 | # 15 | # The above copyright notice and this permission notice shall be 16 | # included in all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | import json 27 | import datetime 28 | from django.contrib.auth.models import User 29 | from gamification.core.models import Project, ProjectBadge 30 | from django.db import models 31 | 32 | class Event(models.Model): 33 | """ 34 | An Event is an action reported by an external system 35 | """ 36 | 37 | user = models.ForeignKey(User) 38 | project = models.ForeignKey(Project) 39 | event_dtg = models.DateTimeField('event date') 40 | details = models.TextField(editable=False) 41 | 42 | def __init__(self, *args, **kw): 43 | # dictionary for the details of the event 44 | self.details_map = {} 45 | super(Event, self).__init__(*args, **kw) 46 | if self.details: 47 | try: 48 | self.details_map = json.loads(self.details) 49 | except TypeError: 50 | self.details_map = self.details 51 | 52 | def save(self, *args, **kw): 53 | self.details = json.dumps(self.details_map) 54 | super(Event, self).save(*args, **kw) 55 | 56 | @property 57 | def dtg(self): 58 | return self.event_dtg 59 | 60 | @property 61 | def details_map(self): 62 | return self._details_map 63 | 64 | @details_map.setter 65 | def details_map(self, details_map): 66 | self._details_map = details_map 67 | 68 | @property 69 | def state(self): 70 | return self._state 71 | 72 | @state.setter 73 | def state(self, state): 74 | self._state = state 75 | 76 | @property 77 | def is_current_event(self): 78 | return self._current_event 79 | 80 | @property 81 | def is_today(self): 82 | return datetime.datetime.date() == event_dtg.date() 83 | 84 | @state.setter 85 | def current_event(self, current_event_id): 86 | self._current_event = (self.id == current_event_id) 87 | 88 | # Adds specified event data to the state 89 | def update_state(self, outer_key, inner_key, inner_value): 90 | try: 91 | if not outer_key in self._state.event_data: 92 | self._state._event_data[outer_key] = {} 93 | self._state._event_data[outer_key][inner_key] = inner_value 94 | except AttributeError: 95 | print 'AttributeError' 96 | pass 97 | 98 | class Policy(models.Model): 99 | """ 100 | A Policy is a condition - action specifier for the rules engine. Will include 1 or more Rules 101 | """ 102 | 103 | STATE_POLICY = 0 104 | AWARD_POLICY = 1 105 | POLICY_CHOICES = ( (STATE_POLICY, 'State Policy'), (AWARD_POLICY, 'Award Policy'), ) 106 | 107 | project = models.ForeignKey(Project) 108 | projectbadge = models.ForeignKey(ProjectBadge) 109 | type = models.IntegerField(choices=POLICY_CHOICES) 110 | rule = models.TextField() 111 | 112 | def __unicode__(self): 113 | try: 114 | kind = self.POLICY_CHOICES[self.projectbadge.type][1] 115 | except: 116 | kind = 'Policy' 117 | return u"%s for %s on %s" % (kind, self.projectbadge.name, self.project.name) 118 | 119 | 120 | #class Rule(models.Model): 121 | # """ 122 | # A Rule is a decision specifier that will be the basis for a Policy 123 | # """ 124 | # 125 | # name = models.CharField(max_length=100) 126 | # policy = models.ForeignKey(Policy) 127 | # badge = models.ForeignKey(ProjectBadge) 128 | # conditions = models.TextField(editable=False) 129 | # 130 | # def __init__(self, *args, **kw): 131 | # # dictionary for the details of the event 132 | # self.conditions_list = [] 133 | # super(Event, self).__init__(*args, **kw) 134 | # if self.conditions: 135 | # self.conditions_list = json.loads(self.conditions) 136 | # 137 | # def save(self, *args, **kw): 138 | # self.conditions = json.dumps(self.conditions_list) 139 | # super(Event, self).save(*args, **kw) -------------------------------------------------------------------------------- /gamification/events/state.py: -------------------------------------------------------------------------------- 1 | # Permission is hereby granted, free of charge, to any person obtaining 2 | # a copy of this software and associated documentation files (the 3 | # "Software"), to deal in the Software without restriction, including 4 | # without limitation the rights to use, copy, modify, merge, publish, 5 | # distribute, sublicense, and/or sell copies of the Software, and to 6 | # permit persons to whom the Software is furnished to do so, as long as 7 | # any reuse or further development of the software attributes the 8 | # National Geospatial-Intelligence Agency (NGA) authorship as follows: 9 | # 'This software (django-gamification) 10 | # is provided to the public as a courtesy of the National 11 | # Geospatial-Intelligence Agency. 12 | # 13 | # The above copyright notice and this permission notice shall be 14 | # included in all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | from django.core.exceptions import ObjectDoesNotExist 25 | from gamification.badges.models import ProjectBadge, ProjectBadgeToUser 26 | 27 | class State(object): 28 | """ 29 | A State is a collection of event data (associated with a user and project) 30 | that will be submitted to the rules engine for reasoning. 31 | """ 32 | 33 | def __init__(self, user, project, projectbadge, event_data): 34 | self._user = user 35 | self._project = project 36 | self._projectbadge = projectbadge 37 | self._event_data = event_data # A dictionary of dictionaries 38 | 39 | @property 40 | def user(self): 41 | return self._user 42 | 43 | @property 44 | def project(self): 45 | return self._project 46 | 47 | @property 48 | def event_data(self): 49 | return self._event_data 50 | 51 | @event_data.setter 52 | def event_data(self, event_data): 53 | self._event_data = event_data 54 | 55 | # Awards a project badge to a user (if the user does not yet have the badge) 56 | def award(self, user, project): 57 | project_badge = self._projectbadge 58 | project_badge.award_to(user) -------------------------------------------------------------------------------- /gamification/png.py: -------------------------------------------------------------------------------- 1 | # http://www.codeproject.com/KB/web-image/pnggammastrip.aspx 2 | 3 | import struct 4 | import sys 5 | import urllib 6 | import urllib2 7 | import zlib 8 | 9 | def read_signature(stream): 10 | return stream.read(8) 11 | 12 | class Chunk: 13 | def __init__(self, size, chunk_type, data, crc): 14 | self.size =size 15 | self.chunk_type = chunk_type 16 | self.data = data 17 | self.crc = crc 18 | 19 | def __repr__(self): 20 | return repr(self.chunk_type) + ' ' + repr(self.size) 21 | 22 | def write(self, stream): 23 | stream.write(struct.pack('!I', self.size)) 24 | stream.write(self.chunk_type) 25 | stream.write(self.data) 26 | stream.write(self.crc) 27 | 28 | @staticmethod 29 | def read(stream): 30 | len_string = stream.read(4) 31 | if not len_string: 32 | return None 33 | 34 | length = struct.unpack('!I', len_string)[0] 35 | chunk_type = stream.read(4) 36 | data = stream.read(length) 37 | crc = stream.read(4) 38 | return Chunk(length, chunk_type, data, crc) 39 | 40 | @staticmethod 41 | def create(chunk_type, data): 42 | data_length = len(data) 43 | payload = chunk_type + data 44 | chunk_crc = zlib.crc32(payload) & 0xFFFFFFFF 45 | return Chunk(data_length, chunk_type, data, struct.pack('!I', chunk_crc)) 46 | 47 | class iTXtChunk(Chunk): 48 | def __init__(self, chunk): 49 | Chunk.__init__(self, chunk.size, chunk.chunk_type, chunk.data, chunk.crc) 50 | keyword_length = self.data.find('\0') 51 | self.keyword = self.data[:keyword_length] 52 | self.text = unicode(self.data[keyword_length+5:], 'utf-8') 53 | 54 | @staticmethod 55 | def create(keyword, text): 56 | '''Create an uncompressed, untranslated iTXt chunk 57 | ''' 58 | null = '\0' 59 | uncompressed = '\0\0' 60 | 61 | chunk_data = keyword 62 | chunk_data += null 63 | chunk_data += uncompressed 64 | # no language specified 65 | chunk_data += null 66 | # no translated keyword 67 | chunk_data += null 68 | chunk_data += unicode(text).encode('utf-8') 69 | 70 | return Chunk.create('iTXt', chunk_data) 71 | 72 | def all_chunks(stream): 73 | while True: 74 | chunk = Chunk.read(stream) 75 | 76 | if chunk: 77 | yield chunk 78 | else: 79 | return 80 | 81 | def add_itXT_to_png(text, in_stream, out_stream): 82 | signature = png.read_signature(in_stream) 83 | out_stream.write(signature) 84 | 85 | for chunk in png.all_chunks(in_stream): 86 | if chunk.chunk_type == 'IEND': 87 | break; 88 | chunk.write(out_stream) 89 | 90 | itxt_chunk = png.iTXtChunk.create('openbadges',text) 91 | itxt_chunk.write(out_stream) 92 | 93 | # write IEND 94 | chunk.write(out_stream) 95 | 96 | 97 | 98 | if __name__ == '__main__': 99 | inputFilename = sys.argv[1] 100 | inputFile = file(inputFilename, 'rb') 101 | read_signature(inputFile) 102 | 103 | for chunk in all_chunks(inputFile): 104 | print chunk -------------------------------------------------------------------------------- /gamification/receivers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Permission is hereby granted, free of charge, to any person obtaining 4 | # a copy of this software and associated documentation files (the 5 | # "Software"), to deal in the Software without restriction, including 6 | # without limitation the rights to use, copy, modify, merge, publish, 7 | # distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so, as long as 9 | # any reuse or further development of the software attributes the 10 | # National Geospatial-Intelligence Agency (NGA) authorship as follows: 11 | # 'This software (django-gamification) 12 | # is provided to the public as a courtesy of the National 13 | # Geospatial-Intelligence Agency. 14 | # 15 | # The above copyright notice and this permission notice shall be 16 | # included in all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | 27 | from django.dispatch import receiver 28 | 29 | from account.signals import password_changed 30 | from account.signals import user_sign_up_attempt, user_signed_up 31 | from account.signals import user_login_attempt, user_logged_in 32 | 33 | from eventlog.models import log 34 | 35 | 36 | @receiver(user_logged_in) 37 | def handle_user_logged_in(sender, **kwargs): 38 | log( 39 | user=kwargs.get("user"), 40 | action="USER_LOGGED_IN", 41 | extra={} 42 | ) 43 | 44 | 45 | @receiver(password_changed) 46 | def handle_password_changed(sender, **kwargs): 47 | log( 48 | user=kwargs.get("user"), 49 | action="PASSWORD_CHANGED", 50 | extra={} 51 | ) 52 | 53 | 54 | @receiver(user_login_attempt) 55 | def handle_user_login_attempt(sender, **kwargs): 56 | log( 57 | user=None, 58 | action="LOGIN_ATTEMPTED", 59 | extra={ 60 | "username": kwargs.get("username"), 61 | "result": kwargs.get("result") 62 | } 63 | ) 64 | 65 | 66 | @receiver(user_sign_up_attempt) 67 | def handle_user_sign_up_attempt(sender, **kwargs): 68 | log( 69 | user=None, 70 | action="SIGNUP_ATTEMPTED", 71 | extra={ 72 | "username": kwargs.get("username"), 73 | "email": kwargs.get("email"), 74 | "result": kwargs.get("result") 75 | } 76 | ) 77 | 78 | 79 | @receiver(user_signed_up) 80 | def handle_user_signed_up(sender, **kwargs): 81 | log( 82 | user=kwargs.get("user"), 83 | action="USER_SIGNED_UP", 84 | extra={} 85 | ) 86 | -------------------------------------------------------------------------------- /gamification/requirements.txt: -------------------------------------------------------------------------------- 1 | Django==1.5.4 2 | Django-Select2==4.2.1 3 | PIL==1.1.7 4 | Paver==1.2.1 5 | South==0.8.2 6 | django-appconf==0.6 7 | #django-badges==0.1.6 8 | django-bootstrap-toolkit==2.15.0 9 | django-compressor==1.3 10 | django-reversion==1.7.1 11 | django-singleton==0.1.7 12 | django-stronghold==0.2.2 13 | jsonfield==0.9.17 14 | psycopg2==2.5.1 15 | requests==1.2.3 16 | six==1.4.1 17 | wsgiref==0.1.2 18 | -------------------------------------------------------------------------------- /gamification/site_media/media/badge_images/bronze.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenrjones/django-gamification/d22882f148375102ec351cb2bc75275083468d73/gamification/site_media/media/badge_images/bronze.png -------------------------------------------------------------------------------- /gamification/site_media/media/badge_images/gold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenrjones/django-gamification/d22882f148375102ec351cb2bc75275083468d73/gamification/site_media/media/badge_images/gold.png -------------------------------------------------------------------------------- /gamification/site_media/media/badge_images/silver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenrjones/django-gamification/d22882f148375102ec351cb2bc75275083468d73/gamification/site_media/media/badge_images/silver.png -------------------------------------------------------------------------------- /gamification/startup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Permission is hereby granted, free of charge, to any person obtaining 4 | # a copy of this software and associated documentation files (the 5 | # "Software"), to deal in the Software without restriction, including 6 | # without limitation the rights to use, copy, modify, merge, publish, 7 | # distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so, as long as 9 | # any reuse or further development of the software attributes the 10 | # National Geospatial-Intelligence Agency (NGA) authorship as follows: 11 | # 'This software (django-gamification) 12 | # is provided to the public as a courtesy of the National 13 | # Geospatial-Intelligence Agency. 14 | # 15 | # The above copyright notice and this permission notice shall be 16 | # included in all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | 27 | import copy 28 | 29 | from django.conf import settings 30 | from django.utils.importlib import import_module 31 | from django.utils.module_loading import module_has_submodule 32 | 33 | 34 | def autoload(submodules): 35 | for app in settings.INSTALLED_APPS: 36 | mod = import_module(app) 37 | for submodule in submodules: 38 | try: 39 | import_module("{0}.{1}".format(app, submodule)) 40 | except: 41 | if module_has_submodule(mod, submodule): 42 | raise 43 | 44 | 45 | def run(): 46 | autoload(["receivers"]) 47 | -------------------------------------------------------------------------------- /gamification/static/README: -------------------------------------------------------------------------------- 1 | This directory is used to store static assets for your project. User media files 2 | (FileFields/ImageFields) are not stored here. 3 | 4 | The convention for this directory is: 5 | 6 | * css/ — stores CSS files 7 | * js/ — stores Javascript files 8 | * img/ — stores image files 9 | -------------------------------------------------------------------------------- /gamification/static/css/project_list.css: -------------------------------------------------------------------------------- 1 | div.row { 2 | margin-left: 0px; 3 | } 4 | .badge-row.leader { 5 | padding: 0px; line-height: 1em; 6 | } 7 | .badge-row.leader img{ 8 | padding: 0px;width: 24px;vertical-align: baseline; 9 | } 10 | 11 | .badge-row { 12 | padding:5px; border:1px solid gray 13 | } 14 | .badge-row h4 { 15 | margin: 0px 0px 5px 0px; 16 | } 17 | .badge-row:nth-child(even) { 18 | background-color: lightcyan; 19 | } 20 | .badge-row:nth-child(odd) { 21 | background-color: lightyellow; 22 | } 23 | .badge-row span.dtg { 24 | display: inline; padding: 0px; 25 | } 26 | .badge-row span.awardee { 27 | display: inline; padding: 0px; width: 150px; 28 | } 29 | .badge-row span { 30 | display: inline-block; vertical-align: top; padding:10px; 31 | } 32 | #header_title h1{ 33 | margin:0px; 34 | } 35 | #header_title { 36 | margin-bottom: 20px; 37 | } 38 | .tab-content{ 39 | max-height: 650px; 40 | overflow: scroll; 41 | } -------------------------------------------------------------------------------- /gamification/static/img/bronze.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenrjones/django-gamification/d22882f148375102ec351cb2bc75275083468d73/gamification/static/img/bronze.png -------------------------------------------------------------------------------- /gamification/static/img/gold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenrjones/django-gamification/d22882f148375102ec351cb2bc75275083468d73/gamification/static/img/gold.png -------------------------------------------------------------------------------- /gamification/static/img/silver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenrjones/django-gamification/d22882f148375102ec351cb2bc75275083468d73/gamification/static/img/silver.png -------------------------------------------------------------------------------- /gamification/static/img/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenrjones/django-gamification/d22882f148375102ec351cb2bc75275083468d73/gamification/static/img/title.png -------------------------------------------------------------------------------- /gamification/static/js/TimeCircles/TimeCircles.css: -------------------------------------------------------------------------------- 1 | /** 2 | * This element is created inside your target element 3 | * It is used so that your own element will not need to be altered 4 | **/ 5 | .time_circles { 6 | position: relative; 7 | width: 100%; 8 | height: 100%; 9 | } 10 | 11 | /** 12 | * This is all the elements used to house all text used 13 | * in time circles 14 | **/ 15 | .time_circles > div { 16 | position: absolute; 17 | text-align: center; 18 | } 19 | 20 | /** 21 | * Titles (Days, Hours, etc) 22 | **/ 23 | .time_circles > div > h4 { 24 | margin: 0px; 25 | padding: 0px; 26 | text-align: center; 27 | text-transform: uppercase; 28 | font-family: 'Century Gothic', Arial; 29 | } 30 | 31 | /** 32 | * Time numbers, ie: 12 33 | **/ 34 | .time_circles > div > span { 35 | display: block; 36 | width: 100%; 37 | text-align: center; 38 | font-family: 'Century Gothic', Arial; 39 | font-size: 300%; 40 | margin-top: 0.4em; 41 | font-weight: bold; 42 | } 43 | -------------------------------------------------------------------------------- /gamification/static/js/maths.js: -------------------------------------------------------------------------------- 1 | //Jay's math helpers 2 | _.mixin({ deepClone: function (p_object) { return JSON.parse(JSON.stringify(p_object)); } }); 3 | 4 | var maths = {}; 5 | maths.heightOnSin=function(theta_min,theta_max,step,steps,amplitude,func){ 6 | func = func || Math.sin; //Find the Sin value by default, you can also pass in Math.cos 7 | 8 | var percent = step/steps; 9 | var theta = theta_min + ((theta_max-theta_min)*percent); 10 | return Math.sin(theta * Math.PI) * amplitude; 11 | }; 12 | maths.sizeFromAmountRange=function(size_min,size_max,amount,amount_min,amount_max){ 13 | var percent = (amount-amount_min)/(amount_max-amount_min); 14 | return size_min+ (percent * (size_max-size_min)); 15 | }; 16 | maths.colorBlendFromAmountRange=function(color_start,color_end,amount,amount_min,amount_max){ 17 | var percent = (amount-amount_min)/(amount_max-amount_min); 18 | 19 | if (color_start.substring(0,1) =="#") color_start = color_start.substring(1,7); 20 | if (color_end.substring(0,1) =="#") color_end = color_end.substring(1,7); 21 | 22 | var s_r = color_start.substring(0,2); 23 | var s_g = color_start.substring(2,4); 24 | var s_b = color_start.substring(4,6); 25 | var e_r = color_end.substring(0,2); 26 | var e_g = color_end.substring(2,4); 27 | var e_b = color_end.substring(4,6); 28 | 29 | var n_r = Math.abs(parseInt((parseInt(s_r, 16) * percent) + (parseInt(e_r, 16) * (1-percent)))); 30 | var n_g = Math.abs(parseInt((parseInt(s_g, 16) * percent) + (parseInt(e_g, 16) * (1-percent)))); 31 | var n_b = Math.abs(parseInt((parseInt(s_b, 16) * percent) + (parseInt(e_b, 16) * (1-percent)))); 32 | var rgb = maths.decimalToHex(n_r) + maths.decimalToHex(n_g) + maths.decimalToHex(n_b); 33 | 34 | return "#"+rgb; 35 | }; 36 | maths.decimalToHex = function(d, padding) { 37 | var hex = Number(d).toString(16); 38 | padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding; 39 | 40 | while (hex.length < padding) { 41 | hex = "0" + hex; 42 | } 43 | 44 | return hex; 45 | }; 46 | maths.idealTextColor=function(bgColor) { 47 | 48 | var nThreshold = 150; 49 | var components = maths.getRGBComponents(bgColor); 50 | var bgDelta = (components.R * 0.299) + (components.G * 0.587) + (components.B * 0.114); 51 | 52 | return ((255 - bgDelta) < nThreshold) ? "#000000" : "#ffffff"; 53 | }; 54 | maths.getRGBComponents=function(color) { 55 | 56 | var r = color.substring(1, 3); 57 | var g = color.substring(3, 5); 58 | var b = color.substring(5, 7); 59 | 60 | return { 61 | R: parseInt(r, 16), 62 | G: parseInt(g, 16), 63 | B: parseInt(b, 16) 64 | }; 65 | }; -------------------------------------------------------------------------------- /gamification/static/js/underscore.string.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"use strict";var n=t.prototype.trim,r=t.prototype.trimRight,i=t.prototype.trimLeft,s=function(e){return e*1||0},o=function(e,t){if(t<1)return"";var n="";while(t>0)t&1&&(n+=e),t>>=1,e+=e;return n},u=[].slice,a=function(e){return e==null?"\\s":e.source?e.source:"["+p.escapeRegExp(e)+"]"},f={lt:"<",gt:">",quot:'"',apos:"'",amp:"&"},l={};for(var c in f)l[f[c]]=c;var h=function(){function e(e){return Object.prototype.toString.call(e).slice(8,-1).toLowerCase()}var n=o,r=function(){return r.cache.hasOwnProperty(arguments[0])||(r.cache[arguments[0]]=r.parse(arguments[0])),r.format.call(null,r.cache[arguments[0]],arguments)};return r.format=function(r,i){var s=1,o=r.length,u="",a,f=[],l,c,p,d,v,m;for(l=0;l=0?"+"+a:a,v=p[4]?p[4]=="0"?"0":p[4].charAt(1):" ",m=p[6]-t(a).length,d=p[6]?n(v,m):"",f.push(p[5]?a+d:d+a)}}return f.join("")},r.cache={},r.parse=function(e){var t=e,n=[],r=[],i=0;while(t){if((n=/^[^\x25]+/.exec(t))!==null)r.push(n[0]);else if((n=/^\x25{2}/.exec(t))!==null)r.push("%");else{if((n=/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(t))===null)throw new Error("[_.sprintf] huh?");if(n[2]){i|=1;var s=[],o=n[2],u=[];if((u=/^([a-z_][a-z_\d]*)/i.exec(o))===null)throw new Error("[_.sprintf] huh?");s.push(u[1]);while((o=o.substring(u[0].length))!=="")if((u=/^\.([a-z_][a-z_\d]*)/i.exec(o))!==null)s.push(u[1]);else{if((u=/^\[(\d+)\]/.exec(o))===null)throw new Error("[_.sprintf] huh?");s.push(u[1])}n[2]=s}else i|=2;if(i===3)throw new Error("[_.sprintf] mixing positional and named placeholders is not (yet) supported");r.push(n)}t=t.substring(n[0].length)}return r},r}(),p={VERSION:"2.3.0",isBlank:function(e){return e==null&&(e=""),/^\s*$/.test(e)},stripTags:function(e){return e==null?"":t(e).replace(/<\/?[^>]+>/g,"")},capitalize:function(e){return e=e==null?"":t(e),e.charAt(0).toUpperCase()+e.slice(1)},chop:function(e,n){return e==null?[]:(e=t(e),n=~~n,n>0?e.match(new RegExp(".{1,"+n+"}","g")):[e])},clean:function(e){return p.strip(e).replace(/\s+/g," ")},count:function(e,n){return e==null||n==null?0:t(e).split(n).length-1},chars:function(e){return e==null?[]:t(e).split("")},swapCase:function(e){return e==null?"":t(e).replace(/\S/g,function(e){return e===e.toUpperCase()?e.toLowerCase():e.toUpperCase()})},escapeHTML:function(e){return e==null?"":t(e).replace(/[&<>"']/g,function(e){return"&"+l[e]+";"})},unescapeHTML:function(e){return e==null?"":t(e).replace(/\&([^;]+);/g,function(e,n){var r;return n in f?f[n]:(r=n.match(/^#x([\da-fA-F]+)$/))?t.fromCharCode(parseInt(r[1],16)):(r=n.match(/^#(\d+)$/))?t.fromCharCode(~~r[1]):e})},escapeRegExp:function(e){return e==null?"":t(e).replace(/([.*+?^=!:${}()|[\]\/\\])/g,"\\$1")},splice:function(e,t,n,r){var i=p.chars(e);return i.splice(~~t,~~n,r),i.join("")},insert:function(e,t,n){return p.splice(e,t,0,n)},include:function(e,n){return n===""?!0:e==null?!1:t(e).indexOf(n)!==-1},join:function(){var e=u.call(arguments),t=e.shift();return t==null&&(t=""),e.join(t)},lines:function(e){return e==null?[]:t(e).split("\n")},reverse:function(e){return p.chars(e).reverse().join("")},startsWith:function(e,n){return n===""?!0:e==null||n==null?!1:(e=t(e),n=t(n),e.length>=n.length&&e.slice(0,n.length)===n)},endsWith:function(e,n){return n===""?!0:e==null||n==null?!1:(e=t(e),n=t(n),e.length>=n.length&&e.slice(e.length-n.length)===n)},succ:function(e){return e==null?"":(e=t(e),e.slice(0,-1)+t.fromCharCode(e.charCodeAt(e.length-1)+1))},titleize:function(e){return e==null?"":t(e).replace(/(?:^|\s)\S/g,function(e){return e.toUpperCase()})},camelize:function(e){return p.trim(e).replace(/[-_\s]+(.)?/g,function(e,t){return t.toUpperCase()})},underscored:function(e){return p.trim(e).replace(/([a-z\d])([A-Z]+)/g,"$1_$2").replace(/[-\s]+/g,"_").toLowerCase()},dasherize:function(e){return p.trim(e).replace(/([A-Z])/g,"-$1").replace(/[-_\s]+/g,"-").toLowerCase()},classify:function(e){return p.titleize(t(e).replace(/_/g," ")).replace(/\s/g,"")},humanize:function(e){return p.capitalize(p.underscored(e).replace(/_id$/,"").replace(/_/g," "))},trim:function(e,r){return e==null?"":!r&&n?n.call(e):(r=a(r),t(e).replace(new RegExp("^"+r+"+|"+r+"+$","g"),""))},ltrim:function(e,n){return e==null?"":!n&&i?i.call(e):(n=a(n),t(e).replace(new RegExp("^"+n+"+"),""))},rtrim:function(e,n){return e==null?"":!n&&r?r.call(e):(n=a(n),t(e).replace(new RegExp(n+"+$"),""))},truncate:function(e,n,r){return e==null?"":(e=t(e),r=r||"...",n=~~n,e.length>n?e.slice(0,n)+r:e)},prune:function(e,n,r){if(e==null)return"";e=t(e),n=~~n,r=r!=null?t(r):"...";if(e.length<=n)return e;var i=function(e){return e.toUpperCase()!==e.toLowerCase()?"A":" "},s=e.slice(0,n+1).replace(/.(?=\W*\w*$)/g,i);return s.slice(s.length-2).match(/\w\w/)?s=s.replace(/\s*\S+$/,""):s=p.rtrim(s.slice(0,s.length-1)),(s+r).length>e.length?e:e.slice(0,s.length)+r},words:function(e,t){return p.isBlank(e)?[]:p.trim(e,t).split(t||/\s+/)},pad:function(e,n,r,i){e=e==null?"":t(e),n=~~n;var s=0;r?r.length>1&&(r=r.charAt(0)):r=" ";switch(i){case"right":return s=n-e.length,e+o(r,s);case"both":return s=n-e.length,o(r,Math.ceil(s/2))+e+o(r,Math.floor(s/2));default:return s=n-e.length,o(r,s)+e}},lpad:function(e,t,n){return p.pad(e,t,n)},rpad:function(e,t,n){return p.pad(e,t,n,"right")},lrpad:function(e,t,n){return p.pad(e,t,n,"both")},sprintf:h,vsprintf:function(e,t){return t.unshift(e),h.apply(null,t)},toNumber:function(e,n){if(e==null||e=="")return 0;e=t(e);var r=s(s(e).toFixed(~~n));return r===0&&!e.match(/^0+$/)?Number.NaN:r},numberFormat:function(e,t,n,r){if(isNaN(e)||e==null)return"";e=e.toFixed(~~t),r=r||",";var i=e.split("."),s=i[0],o=i[1]?(n||".")+i[1]:"";return s.replace(/(\d)(?=(?:\d{3})+$)/g,"$1"+r)+o},strRight:function(e,n){if(e==null)return"";e=t(e),n=n!=null?t(n):n;var r=n?e.indexOf(n):-1;return~r?e.slice(r+n.length,e.length):e},strRightBack:function(e,n){if(e==null)return"";e=t(e),n=n!=null?t(n):n;var r=n?e.lastIndexOf(n):-1;return~r?e.slice(r+n.length,e.length):e},strLeft:function(e,n){if(e==null)return"";e=t(e),n=n!=null?t(n):n;var r=n?e.indexOf(n):-1;return~r?e.slice(0,r):e},strLeftBack:function(e,t){if(e==null)return"";e+="",t=t!=null?""+t:t;var n=e.lastIndexOf(t);return~n?e.slice(0,n):e},toSentence:function(e,t,n,r){t=t||", ",n=n||" and ";var i=e.slice(),s=i.pop();return e.length>2&&r&&(n=p.rtrim(t)+n),i.length?i.join(t)+n+s:s},toSentenceSerial:function(){var e=u.call(arguments);return e[3]=!0,p.toSentence.apply(p,e)},slugify:function(e){if(e==null)return"";var n="ąàáäâãåæćęèéëêìíïîłńòóöôõøùúüûñçżź",r="aaaaaaaaceeeeeiiiilnoooooouuuunczz",i=new RegExp(a(n),"g");return e=t(e).toLowerCase().replace(i,function(e){var t=n.indexOf(e);return r.charAt(t)||"-"}),p.dasherize(e.replace(/[^\w\s-]/g,""))},surround:function(e,t){return[t,e,t].join("")},quote:function(e){return p.surround(e,'"')},exports:function(){var e={};for(var t in this){if(!this.hasOwnProperty(t)||t.match(/^(?:include|contains|reverse)$/))continue;e[t]=this[t]}return e},repeat:function(e,n,r){if(e==null)return"";n=~~n;if(r==null)return o(t(e),n);for(var i=[];n>0;i[--n]=e);return i.join(r)},levenshtein:function(e,n){if(e==null&&n==null)return 0;if(e==null)return t(n).length;if(n==null)return t(e).length;e=t(e),n=t(n);var r=[],i,s;for(var o=0;o<=n.length;o++)for(var u=0;u<=e.length;u++)o&&u?e.charAt(u-1)===n.charAt(o-1)?s=i:s=Math.min(r[u],r[u-1],i)+1:s=o+u,i=r[u],r[u]=s;return r.pop()}};p.strip=p.trim,p.lstrip=p.ltrim,p.rstrip=p.rtrim,p.center=p.lrpad,p.rjust=p.lpad,p.ljust=p.rpad,p.contains=p.include,p.q=p.quote,typeof exports!="undefined"?(typeof module!="undefined"&&module.exports&&(module.exports=p),exports._s=p):typeof define=="function"&&define.amd?define("underscore.string",[],function(){return p}):(e._=e._||{},e._.string=e._.str=p)}(this,String); -------------------------------------------------------------------------------- /gamification/static/themes/camping/badges.css: -------------------------------------------------------------------------------- 1 | .personJersey { 2 | display:inline-block; 3 | margin:4px; 4 | vertical-align: top; 5 | 6 | width: 210px; 7 | height: 250px; 8 | position: relative; 9 | } 10 | 11 | .personHeaderCard { 12 | display:inline-block; 13 | margin:2px; 14 | vertical-align: top; 15 | 16 | width: 200px; 17 | background-repeat: no-repeat; 18 | position: relative; 19 | } 20 | 21 | .cardHeight100 { 22 | background: url(card_wood_100.png); 23 | background-size: 200px 100px; 24 | height: 100px; 25 | } 26 | 27 | .cardHeight150 { 28 | background: url(card_wood_150.png); 29 | background-size: 200px 150px; 30 | height: 150px; 31 | } 32 | 33 | .cardHeight200 { 34 | background: url(card_wood_200.png); 35 | background-size: 200px 200px; 36 | height: 200px; 37 | } 38 | .cardHeight300 { 39 | background: url(card_wood_300.png); 40 | background-size: 300px 300px; 41 | height: 300px; 42 | } 43 | .personHeaderCardText { 44 | font-family:Arial; 45 | cursor: cell; 46 | padding: 2px; 47 | font-size:17px; 48 | color:white; 49 | font-weight:bold; 50 | text-shadow: 2px 2px #000000; 51 | text-align:center; 52 | top:17px; 53 | left:0; 54 | right:0; 55 | margin-left:auto; 56 | margin-right:auto; 57 | /*left:50px;*/ 58 | position: absolute; 59 | } 60 | .personHeaderTextLeft { 61 | left: 10px; 62 | right: 155px; 63 | color: yellow; 64 | } 65 | .personHeaderTextRight { 66 | left: 155px; 67 | right: 10px; 68 | color: rgb(230, 254, 253); 69 | } 70 | 71 | .personCardBadgeHolder { 72 | position: absolute; 73 | top:52px; 74 | left:18px; 75 | } 76 | .personBadgeHolderCard { 77 | display:inline-block; 78 | background-repeat: no-repeat; 79 | padding:4px; 80 | margin:3px; 81 | /*margin-bottom: 0px;*/ 82 | margin-top: 1px; 83 | position:relative; 84 | /*border:1px solid black;*/ 85 | } 86 | 87 | .personHeader { 88 | border:2px solid black; 89 | display:inline-block; 90 | margin:2px; 91 | background-color: #eff; 92 | border-image: url(corner_border_cyber_blue.png) 12% 8% 8% 8% round; 93 | border-width: 14px; 94 | vertical-align: top; 95 | max-width: 470px; 96 | max-height: 74px; 97 | overflow-y: scroll; 98 | } 99 | .personHeaderText { 100 | font-family:Arial; 101 | padding: 2px; 102 | font-size:16px; 103 | color:black; 104 | font-weight:bold; 105 | text-align:center 106 | } 107 | 108 | .personBadgeHolder { 109 | display:inline-block; 110 | background-repeat: no-repeat; 111 | padding:4px; 112 | margin:4px; 113 | position:relative; 114 | /*border:1px solid black;*/ 115 | } 116 | 117 | .personBadgeHolderMeatball { 118 | color:white; 119 | opacity:0.85; 120 | font-weight:bold; 121 | text-align:center; 122 | position:absolute; 123 | background-color: #ffedc1; 124 | border:1px solid orange; 125 | } 126 | .personBadgeHolderText { 127 | font-family:Arial; 128 | font-weight:bold; 129 | text-align:center; 130 | color:black; 131 | position:absolute; 132 | } -------------------------------------------------------------------------------- /gamification/static/themes/camping/blue_jersey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenrjones/django-gamification/d22882f148375102ec351cb2bc75275083468d73/gamification/static/themes/camping/blue_jersey.png -------------------------------------------------------------------------------- /gamification/static/themes/camping/canoe_green_sideways.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenrjones/django-gamification/d22882f148375102ec351cb2bc75275083468d73/gamification/static/themes/camping/canoe_green_sideways.png -------------------------------------------------------------------------------- /gamification/static/themes/camping/card_wood.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenrjones/django-gamification/d22882f148375102ec351cb2bc75275083468d73/gamification/static/themes/camping/card_wood.png -------------------------------------------------------------------------------- /gamification/static/themes/camping/card_wood_100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenrjones/django-gamification/d22882f148375102ec351cb2bc75275083468d73/gamification/static/themes/camping/card_wood_100.png -------------------------------------------------------------------------------- /gamification/static/themes/camping/card_wood_150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenrjones/django-gamification/d22882f148375102ec351cb2bc75275083468d73/gamification/static/themes/camping/card_wood_150.png -------------------------------------------------------------------------------- /gamification/static/themes/camping/card_wood_200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenrjones/django-gamification/d22882f148375102ec351cb2bc75275083468d73/gamification/static/themes/camping/card_wood_200.png -------------------------------------------------------------------------------- /gamification/static/themes/camping/card_wood_300.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenrjones/django-gamification/d22882f148375102ec351cb2bc75275083468d73/gamification/static/themes/camping/card_wood_300.png -------------------------------------------------------------------------------- /gamification/static/themes/camping/corner_border_brown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenrjones/django-gamification/d22882f148375102ec351cb2bc75275083468d73/gamification/static/themes/camping/corner_border_brown.png -------------------------------------------------------------------------------- /gamification/static/themes/camping/corner_border_cyber_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenrjones/django-gamification/d22882f148375102ec351cb2bc75275083468d73/gamification/static/themes/camping/corner_border_cyber_blue.png -------------------------------------------------------------------------------- /gamification/static/themes/camping/oar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenrjones/django-gamification/d22882f148375102ec351cb2bc75275083468d73/gamification/static/themes/camping/oar.png -------------------------------------------------------------------------------- /gamification/static/themes/camping/paddles.js: -------------------------------------------------------------------------------- 1 | var paddles = {canvas:null, $canvas:null, ctx:null, loadedImages:0, loadedArray:[]}; 2 | paddles.imageArray = "canoe_green_sideways.png oar.png".split(" "); 3 | paddles.imageUrlPrefix = "/static/themes/camping/"; 4 | 5 | paddles.init = function(){ 6 | //Initialization 7 | 8 | paddles.$canvas = $('') 9 | .attr({width:1000,height:220,id:'badge_data_canvas'}) 10 | .text("This graph doesn't show on your browser") 11 | .prependTo($('#leader_board')); 12 | 13 | paddles.canvas = document.getElementById('badge_data_canvas'); 14 | if (paddles.canvas && paddles.canvas.getContext){ 15 | paddles.ctx = paddles.canvas.getContext('2d'); 16 | paddles.preloadImages(); 17 | } else { 18 | console.log("No Canvas Support"); 19 | if (paddles.$canvas) paddles.$canvas.hide(); 20 | } 21 | }; 22 | 23 | paddles.preloadImages = function() { 24 | for (var i = 0; i < paddles.imageArray.length; i++) { 25 | paddles.loadedArray[i] = new Image(); 26 | paddles.loadedArray[i].addEventListener("load", paddles.trackProgress, true); 27 | var url = paddles.imageArray[i]; 28 | if (document.location.host != "") url = paddles.imageUrlPrefix + url; 29 | paddles.loadedArray[i].src = url; 30 | } 31 | }; 32 | 33 | paddles.trackProgress = function() { 34 | paddles.loadedImages++; 35 | if (paddles.loadedImages == paddles.imageArray.length) { 36 | paddles.imagesLoaded(); 37 | } 38 | }; 39 | 40 | paddles.imagesLoaded = function() { 41 | //Run after all images are ready 42 | //TODO: Pass a list of visualization types and call appropriately with proper data 43 | paddles.draw_canoe_and_oars(project_info.badge_json || []); 44 | }; 45 | 46 | paddles.draw_canoe_and_oars = function(badge_info){ 47 | //Shortcuts 48 | var ctx = paddles.ctx; 49 | var canoe = paddles.loadedArray[0]; 50 | var oar = paddles.loadedArray[1]; 51 | 52 | var canvas_width = paddles.canvas.width; 53 | var canvas_height = paddles.canvas.height; 54 | 55 | //Variables 56 | var canvas_spacing = 50; 57 | var canoe_spacing = 15; 58 | var canoe_spacing_right = 25; 59 | var canoe_width = canvas_width-(canvas_spacing*2); 60 | var canoe_height = 200 / (1460/canoe_width); 61 | var oar_width = 70; 62 | var num_oars = 20; 63 | if (badge_info && badge_info.length < num_oars) num_oars = badge_info.length; 64 | if (num_oars < 2) num_oars = 2; 65 | 66 | var oar_spacing = parseInt((canoe_width-canvas_spacing-canoe_spacing_right-(canoe_spacing/2)) / (num_oars-1)); 67 | ctx.font="bold 12px Verdana"; 68 | 69 | var badges_max=0; 70 | var badges_min=100000000; 71 | var points_max=0; 72 | var points_min=100000000; 73 | 74 | for (var i=0;ibadges_max) badges_max=awards; 79 | if (awardspoints_max) points_max=points; 81 | if (points 6 | 31 | 32 | 33 | 36 | -------------------------------------------------------------------------------- /gamification/templates/_footer.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | 3 | {% trans "© 2013 <your company here>" %} 4 | -------------------------------------------------------------------------------- /gamification/templates/homepage.html: -------------------------------------------------------------------------------- 1 | {% extends "banner_base.html" %} 2 | 3 | {% load i18n %} 4 | {% load url from future %} 5 | 6 | {% block head_title %}{% trans "Welcome" %}{% endblock %} 7 | 8 | {% block body_class %}home{% endblock %} 9 | 10 | {% block banner %} 11 |

{% trans "Welcome to Pinax" %}

12 |

13 | {% blocktrans %} 14 | Pinax is a Django 15 | project intended to provide a starting point for websites. By 16 | integrating numerous reusable Django apps to take care of the 17 | things that many sites have in common, it lets you focus on what 18 | makes your site different. 19 | {% endblocktrans %} 20 |

21 | 22 |

About Account Project

23 |

24 | {% blocktrans %} 25 | In addition to what is provided by the "zero" project, this project 26 | provides thorough integration with django-user-accounts, adding 27 | comprehensive account management functionality. It is a foundation 28 | suitable for most sites that have user accounts. 29 | {% endblocktrans %} 30 |

31 | 32 | {% if user.is_authenticated %} 33 | {% url "what_next" as what_next_url %} 34 |

{% blocktrans %}Wondering What Next?{% endblocktrans %}

35 | {% else %} 36 | {% url "account_login" as login_url %} 37 | {% url "account_signup" as signup_url %} 38 |

{% blocktrans %}You can Log In or Sign Up to try out the site.{% endblocktrans %}

39 | {% endif %} 40 | {% endblock %} 41 | -------------------------------------------------------------------------------- /gamification/templates/site_base.html: -------------------------------------------------------------------------------- 1 | {% extends "theme_base.html" %} 2 | 3 | {% load metron_tags %} 4 | {% load i18n %} 5 | 6 | {% block extra_head_base %} 7 | {% block extra_head %}{% endblock %} 8 | {% endblock %} 9 | 10 | {% block footer %} 11 | {% include "_footer.html" %} 12 | {% endblock %} 13 | 14 | {% block extra_body_base %} 15 | {% analytics %} 16 | {% block extra_body %}{% endblock %} 17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /gamification/urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Permission is hereby granted, free of charge, to any person obtaining 4 | # a copy of this software and associated documentation files (the 5 | # "Software"), to deal in the Software without restriction, including 6 | # without limitation the rights to use, copy, modify, merge, publish, 7 | # distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so, as long as 9 | # any reuse or further development of the software attributes the 10 | # National Geospatial-Intelligence Agency (NGA) authorship as follows: 11 | # 'This software (django-gamification) 12 | # is provided to the public as a courtesy of the National 13 | # Geospatial-Intelligence Agency. 14 | # 15 | # The above copyright notice and this permission notice shall be 16 | # included in all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | 27 | from django.conf import settings 28 | from django.conf.urls import patterns, include, url 29 | from django.conf.urls.static import static 30 | from django.views.generic import TemplateView, ListView 31 | 32 | from django.contrib import admin 33 | admin.autodiscover() 34 | from gamification.core.models import Project 35 | from gamification.core.views import * 36 | from gamification.events.views import handle_event, assign_badge 37 | 38 | urlpatterns = patterns("", 39 | url(r"^gamification/$", TemplateView.as_view(template_name="core/index.html"), name="home"), 40 | url(r"^admin/", include(admin.site.urls)), 41 | url(r"^account/", include("account.urls")), 42 | url(r"^users/?$", UserView.as_view(template_name='core/users.html'), name='user_list'), 43 | url(r"^users/(?P[\w .-@]+)/", include("gamification.core.urls")), 44 | url(r"^projects/$", master_project_list), 45 | 46 | url(r"^projects/all/", MasterProjectListView.as_view(template_name='core/masterprojects_list.html'), name='master-project-list'), 47 | url(r"^projects/(?P[\w ]+)/?$", ProjectListView.as_view(template_name='core/projects_list.html'), name='project-list'), 48 | url(r"^projects/(?P[\w ]+)/admin/?$", ProjectAdminListView.as_view(template_name='core/project_admin.html'), name='project-admin'), 49 | 50 | url(r"^projects/(?P[\w ]+)/(?P\w+)/?$", ProjectListView.as_view(template_name='core/projects_list.html'), name='project-list'), 51 | 52 | url(r"^projects/(?P[\w ]+)/leaders/?$", project_all_badgeleaders_view), 53 | # url(r"^projects/(?P\w+)/badges/?$", BadgeListView.as_view(template_name='core/badge_list.html'), name='badge-list'), 54 | url(r"^projects/(?P[\w ]+)/badges/(?P\w+)/leaders/?$", project_badgeleaders_view), 55 | url(r'^badges/?$', MasterBadgeListView.as_view(template_name='core/master_badge_list.html'), name='master-badge-list'), 56 | url(r'^users/(?P[\w .-@]+)/projects/(?P[\w ]+)/event/?$', handle_event), 57 | url(r'^users/(?P[\w .-@]+)/assign_badge/(?P[\w ]+)?$', assign_badge), 58 | url(r'^users/(?P[\w .-@]+)/create/?$', create_new_user), 59 | url(r'^$', MasterBadgeListView.as_view(template_name="core/index.html"), name="home"), 60 | url(r'^users/(?P[\w .-@])/projects/(?P\w+)/event/?$', handle_event), 61 | url(r'^users/(?P[\w .-@])/assign_badge/(?P\w+)?$', assign_badge), 62 | url(r'^users/(?P[\w .-@])/create/?$', create_new_user), 63 | url(r'^$', TemplateView.as_view(template_name="core/index.html"), name="home"), 64 | 65 | # Open Badges interoperability. Define an Issuer 66 | url(r'^projects/(?P\w+)/badges/(?P\w+)/issuer/?$', \ 67 | get_issuer, name="openbadges_issuer"), 68 | 69 | # and a way to retrieve a badge's image 70 | url(r'^projects/(?P\w+)/badges/(?P\w+)/image/?$', \ 71 | get_image, name="openbadges_image"), 72 | 73 | # a badge's metadata 74 | url(r'^projects/(?P\w+)/badges/(?P\w+)/metadata/?$', \ 75 | get_metadata, name="openbadges_metadata"), 76 | 77 | # a badge's criteria 78 | url(r'projects/(?P\w+)/badges/(?P\w+)/criteria/?$', \ 79 | get_criteria, name='openbadges_criteria'), 80 | 81 | # badge instance metadata 82 | url(r'projects/(?P\w+)/badges/(?P\w+)/users/(?P[\w .-@])/award/?$', \ 83 | get_badge_award, name='openbadges_award'), 84 | ) 85 | 86 | # urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 87 | -------------------------------------------------------------------------------- /gamification/wsgi.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ## Permission is hereby granted, free of charge, to any person obtaining 4 | # a copy of this software and associated documentation files (the 5 | # "Software"), to deal in the Software without restriction, including 6 | # without limitation the rights to use, copy, modify, merge, publish, 7 | # distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so, as long as 9 | # any reuse or further development of the software attributes the 10 | # National Geospatial-Intelligence Agency (NGA) authorship as follows: 11 | # 'This software (django-gamification) 12 | # is provided to the public as a courtesy of the National 13 | # Geospatial-Intelligence Agency. 14 | # 15 | # The above copyright notice and this permission notice shall be 16 | # included in all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | 27 | """ 28 | WSGI config for django_gamification project. 29 | 30 | This module contains the WSGI application used by Django's development server 31 | and any production WSGI deployments. It should expose a module-level variable 32 | named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover 33 | this application via the ``WSGI_APPLICATION`` setting. 34 | 35 | Usually you will have the standard Django WSGI application here, but it also 36 | might make sense to replace the whole Django WSGI application with a custom one 37 | that later delegates to the Django one. For example, you could introduce WSGI 38 | middleware here, or combine a Django application with an application of another 39 | framework. 40 | 41 | """ 42 | import os 43 | 44 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gamification.settings") 45 | 46 | import gamification.startup as startup 47 | startup.run() 48 | 49 | # This application object is used by any WSGI server configured to use this 50 | # file. This includes Django's development server, if the WSGI_APPLICATION 51 | # setting points here. 52 | from django.core.wsgi import get_wsgi_application 53 | application = get_wsgi_application() 54 | 55 | # Apply WSGI middleware here. 56 | # from helloworld.wsgi import HelloWorldApplication 57 | # application = HelloWorldApplication(application) -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Permission is hereby granted, free of charge, to any person obtaining 4 | # a copy of this software and associated documentation files (the 5 | # "Software"), to deal in the Software without restriction, including 6 | # without limitation the rights to use, copy, modify, merge, publish, 7 | # distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so, as long as 9 | # any reuse or further development of the software attributes the 10 | # National Geospatial-Intelligence Agency (NGA) authorship as follows: 11 | # 'This software (django-gamification) 12 | # is provided to the public as a courtesy of the National 13 | # Geospatial-Intelligence Agency. 14 | # 15 | # The above copyright notice and this permission notice shall be 16 | # included in all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | 27 | import os, sys 28 | 29 | if __name__ == "__main__": 30 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gamification.settings") 31 | os.environ['PGCONNECT_TIMEOUT'] = '30' 32 | 33 | manage_dir = os.path.dirname(os.path.realpath(__file__)) 34 | sys.path.append(os.path.join(manage_dir, 'gamification')) 35 | 36 | from django.core.management import execute_from_command_line 37 | import gamification.startup as startup 38 | startup.run() 39 | execute_from_command_line(sys.argv) 40 | -------------------------------------------------------------------------------- /pavement.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Permission is hereby granted, free of charge, to any person obtaining 4 | # a copy of this software and associated documentation files (the 5 | # "Software"), to deal in the Software without restriction, including 6 | # without limitation the rights to use, copy, modify, merge, publish, 7 | # distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so, as long as 9 | # any reuse or further development of the software attributes the 10 | # National Geospatial-Intelligence Agency (NGA) authorship as follows: 11 | # 'This software (django-gamification) 12 | # is provided to the public as a courtesy of the National 13 | # Geospatial-Intelligence Agency. 14 | # 15 | # The above copyright notice and this permission notice shall be 16 | # included in all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | import os 27 | import sys 28 | 29 | from paver.easy import * 30 | from paver.setuputils import setup 31 | 32 | sys.path.append(os.path.dirname(os.path.realpath(__file__))) 33 | 34 | setup( 35 | name="gamification", 36 | packages=['gamification'], 37 | version='0.0.0.1', 38 | url="", 39 | author="Site Admin", 40 | author_email="admin@localhost" 41 | ) 42 | 43 | 44 | @task 45 | def install_dependencies(): 46 | """ Installs dependencies.""" 47 | sh('pip install --upgrade -r gamification/requirements.txt') 48 | 49 | 50 | @cmdopts([ 51 | ('fixture=', 'f', 'Fixture to install"'), 52 | ]) 53 | @task 54 | def install_fixture(options): 55 | """ Loads the supplied fixture """ 56 | fixture = options.get('fixture') 57 | sh("python manage.py loaddata {fixture}".format(fixture=fixture)) 58 | 59 | 60 | @task 61 | def install_dev_fixtures(): 62 | """ Installs development fixtures in the correct order """ 63 | fixtures = [ 64 | 'gamification/core/fixtures/initial_data.json', # Core 65 | 'gamification/badges/fixtures/initial_data.json', # Badges 66 | 'gamification/events/fixtures/initial_data.json', # Events 67 | ] 68 | 69 | for fixture in fixtures: 70 | sh("python manage.py loaddata {fixture}".format(fixture=fixture)) 71 | 72 | @task 73 | def sync_initial(): 74 | sh("python manage.py syncdb; python manage.py migrate --all 2> /dev/null") 75 | sh("python manage.py syncdb; python manage.py migrate --all 2> /dev/null") 76 | sh("python manage.py syncdb; python manage.py migrate core") 77 | sh("python manage.py syncdb; python manage.py migrate --all") 78 | 79 | @task 80 | def sync(): 81 | """ Runs the syncdb process with migrations """ 82 | sh("python manage.py migrate core") 83 | sh("python manage.py syncdb --noinput") 84 | sh("python manage.py migrate --all") 85 | 86 | 87 | @cmdopts([ 88 | ('bind=', 'b', 'Bind server to provided IP address and port number.'), 89 | ]) 90 | @task 91 | def start_django(options): 92 | """ Starts the Django application. """ 93 | bind = options.get('bind', '') 94 | sh('python manage.py runserver %s &' % bind) 95 | 96 | 97 | @needs(['sync', 98 | 'start_django']) 99 | def start(): 100 | """ Syncs the database and then starts the development server. """ 101 | info("Gamification is now available.") 102 | 103 | 104 | @cmdopts([ 105 | ('template=', 'T', 'Database template to use when creating new database, defaults to "template_postgis"'), 106 | ]) 107 | @task 108 | def createdb(options): 109 | """ Creates the database in postgres. """ 110 | from gamification import settings 111 | template = options.get('template', 'template_postgis') 112 | database = settings.DATABASES.get('default').get('NAME') 113 | sh('createdb {database}'.format(database=database, template=template)) 114 | 115 | 116 | @task 117 | def create_db_user(): 118 | """ Creates the database in postgres. """ 119 | from gamification import settings 120 | database = settings.DATABASES.get('default').get('NAME') 121 | user = settings.DATABASES.get('default').get('USER') 122 | password = settings.DATABASES.get('default').get('PASSWORD') 123 | 124 | sh('psql -d {database} -c {sql}'.format(database=database, 125 | sql='"CREATE USER {user} WITH PASSWORD \'{password}\';"'.format(user=user, 126 | password=password))) 127 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | --allow-external antlr-python-runtime 2 | --allow-unverified antlr-python-runtime 3 | --allow-external PIL 4 | --allow-unverified PIL 5 | 6 | Django==1.5.4 7 | Django-Select2==4.2.1 8 | PIL==1.1.7 9 | Paver==1.2.1 10 | South==0.8.2 11 | django-appconf==0.6 12 | django-bootstrap3==4.7.1 13 | django-bootstrap-toolkit==2.15.0 14 | django-compressor==1.3 15 | django-forms-bootstrap==2.0.3.post1 16 | django-geoexplorer==3.0.4 17 | django-jsonfield==0.8.12 18 | django-reversion==1.7.1 19 | django-singleton==0.1.7 20 | django-stronghold==0.2.2 21 | django-tastypie==0.11.0 22 | django-user-accounts==1.0b13 23 | eventlog==0.6.2 24 | #gamification==0.0.0.1 25 | jsonfield==0.9.17 26 | metron==1.1 27 | pinax-theme-bootstrap==3.0a9 28 | psycopg2==2.5.1 29 | python-dateutil==2.2 30 | python-mimeparse==0.1.4 31 | pytz==2012j 32 | requests==1.2.3 33 | six==1.4.1 34 | wsgiref==0.1.2 35 | Intellect==1.4.9 36 | djangorestframework==2.3.14 37 | django-cors-headers==0.12 --------------------------------------------------------------------------------