Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Vote application added for Call for papers

  • Loading branch information...
commit 548dd4e9caa33278d96b9f52e2bedf486b6d8f7b 1 parent d837e65
@olasitarska olasitarska authored
Showing with 643 additions and 57 deletions.
  1. +1 −0  .gitignore
  2. 0  djangocon/__init__.py
  3. +49 −7 { → djangocon}/settings.py
  4. +8 −1 { → djangocon}/urls.py
  5. 0  { → djangocon}/wsgi.py
  6. +1 −1  manage.py
  7. +9 −1 requirements.txt
  8. +47 −1 static/css/style.css
  9. BIN  static/img/cfp_vote_baloon.png
  10. BIN  static/img/signin_github.png
  11. BIN  static/img/vote_button_1.png
  12. BIN  static/img/vote_button_2.png
  13. BIN  static/img/vote_button_3.png
  14. +0 −14 static/js/background_scroll.js
  15. +79 −0 static/js/custom.js
  16. +0 −30 static/js/networks.js
  17. +1 −0  templates/404.html
  18. +1 −0  templates/500.html
  19. +1 −2  templates/index.html
  20. +55 −0 templates/vote.html
  21. 0  vote/__init__.py
  22. +15 −0 vote/admin.py
  23. +95 −0 vote/migrations/0001_initial.py
  24. +75 −0 vote/migrations/0002_auto__add_field_vote_kind.py
  25. +76 −0 vote/migrations/0003_auto__add_field_entry_score.py
  26. 0  vote/migrations/__init__.py
  27. +30 −0 vote/models.py
  28. 0  vote/templatetags/__init__.py
  29. +12 −0 vote/templatetags/vote_tags.py
  30. +16 −0 vote/tests.py
  31. +7 −0 vote/urls.py
  32. +9 −0 vote/utils.py
  33. +56 −0 vote/views.py
View
1  .gitignore
@@ -6,3 +6,4 @@ collected_static/*
.idea/*
.project
.pydevproject
+djangocon/local_settings.py
View
0  djangocon/__init__.py
No changes.
View
56 settings.py → djangocon/settings.py
@@ -2,6 +2,9 @@
import os
import dj_database_url
+def project_path(path):
+ return os.path.abspath(os.path.join(os.path.dirname(__file__), path))
+
DEBUG = False
TEMPLATE_DEBUG = DEBUG
@@ -11,7 +14,7 @@
MANAGERS = ADMINS
-DATABASES = {'default': dj_database_url.config(default='postgres://localhost')}
+DATABASES = {'default': dj_database_url.config(default=os.environ.get('DATABASE_URL'))}
# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
@@ -54,7 +57,7 @@
# Don't put anything in this directory yourself; store your static files
# in apps' "static/" subdirectories and in STATICFILES_DIRS.
# Example: "/home/media/media.lawrence.com/static/"
-STATIC_ROOT = os.path.join(PROJECT_PATH, 'collected_static')
+STATIC_ROOT = project_path('../collected_static')
# URL prefix for static files.
# Example: "http://media.lawrence.com/static/"
@@ -62,7 +65,7 @@
# Additional locations of static files
STATICFILES_DIRS = (
- os.path.join(PROJECT_PATH, 'static'),
+ project_path('../static'),
)
# List of finder classes that know how to find static files in
@@ -90,23 +93,43 @@
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
-ROOT_URLCONF = 'urls'
+ROOT_URLCONF = 'djangocon.urls'
# Python dotted path to the WSGI application used by Django's runserver.
-WSGI_APPLICATION = 'wsgi.application'
+WSGI_APPLICATION = 'djangocon.wsgi.application'
-TEMPLATE_DIRS = ()
+TEMPLATE_DIRS = (
+ project_path('../templates'),
+)
for root, dirs, files in os.walk(PROJECT_PATH):
if 'templates' in dirs: TEMPLATE_DIRS += (os.path.join(root, 'templates'),)
+TEMPLATE_CONTEXT_PROCESSORS = (
+ 'django.core.context_processors.debug',
+ 'django.core.context_processors.i18n',
+ 'django.core.context_processors.media',
+ 'django.core.context_processors.request',
+ 'django.core.context_processors.static',
+ 'django.contrib.auth.context_processors.auth',
+ 'django.contrib.messages.context_processors.messages',
+ 'social_auth.context_processors.social_auth_backends',
+ 'social_auth.context_processors.social_auth_login_redirect',
+)
+
INSTALLED_APPS = (
+ 'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
- "gunicorn",
+ 'gunicorn',
+ 'south',
+ 'social_auth',
+ 'raven.contrib.django',
+
+ 'vote',
)
# A sample logging configuration. The only tangible logging
@@ -137,3 +160,22 @@
},
}
}
+
+AUTHENTICATION_BACKENDS = (
+ 'social_auth.backends.contrib.github.GithubBackend',
+ 'django.contrib.auth.backends.ModelBackend',
+)
+
+SOCIAL_AUTH_RAISE_EXCEPTIONS = False
+LOGIN_REDIRECT_URL = LOGIN_ERROR_URL = LOGIN_URL = SOCIAL_AUTH_BACKEND_ERROR_URL = SOCIAL_AUTH_DISCONNECT_REDIRECT_URL = '/vote/'
+
+try:
+ GITHUB_APP_ID = os.environ['GITHUB_APP_ID']
+ GITHUB_API_SECRET = os.environ['GITHUB_API_SECRET']
+except KeyError:
+ pass
+
+try:
+ from local_settings import *
+except ImportError:
+ pass
View
9 urls.py → djangocon/urls.py
@@ -1,7 +1,10 @@
+import settings
+
from django.conf.urls import patterns, include, url
+from django.contrib import admin
from django.views.generic import TemplateView
-import settings
+admin.autodiscover()
urlpatterns = patterns('',
@@ -11,6 +14,10 @@
url(r'^codeofconduct/$', TemplateView.as_view(template_name='codeofconduct.html')),
url(r'^speakers/$', TemplateView.as_view(template_name='speakers.html')),
+ url(r'^vote/', include('vote.urls', namespace='vote', app_name='vote')),
+
+ url(r'^admin/', include(admin.site.urls)),
+ url(r'', include('social_auth.urls')),
)
if not settings.DEBUG:
View
0  wsgi.py → djangocon/wsgi.py
File renamed without changes
View
2  manage.py
@@ -3,7 +3,7 @@
import sys
if __name__ == "__main__":
- os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangocon.settings")
from django.core.management import execute_from_command_line
View
10 requirements.txt
@@ -1,4 +1,12 @@
Django==1.4.3
+South==0.7.6
dj-database-url==0.2.1
+django-import-export==0.1.1
+django-social-auth==0.7.17
+gunicorn==0.17.0
+httplib2==0.7.6
+oauth2==1.5.211
psycopg2==2.4.5
-gunicorn==0.17.0
+python-openid==2.2.5
+raven
+simplejson==2.6.1
View
48 static/css/style.css
@@ -371,8 +371,54 @@ p.circusrole{
margin:0 0 10px 0;
padding: 0 !important;
}
-#sponsors {
+#cfp p .github_login {
+ margin: 20px 0 0 0;
+ display: block;
+}
+
+#cfp .entry {
+ clear: both;
+ padding: 10px 0 50px 0;
+}
+#cfp .entry p {
+ float: right;
+ width: 640px;
+}
+#cfp .entry .baloon_counter {
+ width: 90px;
+ height: 100%;
+ min-height: 160px;
+ background: url(/static/img/cfp_vote_baloon.png) no-repeat;
+ font-family: "RiotSquad", Impact, fantasy;
+ font-size: 40px;
+ color: #6b1806;
+ padding: 40px 0 0 2px;
+ text-align: center;
+ float: left;
+ margin: 0 15px 15px 0;
+}
+#cfp .entry h4 {
+ font-family: "RiotSquad", Impact, fantasy;
+ font-size: 24px;
+ color: #d01e32;
+ line-height: 120%;
+ margin: 10px 0 0px 0;
+}
+#cfp .entry:first-child {
+ padding-top:0;
+}
+#cfp img {
+ margin-right: 15px;
+}
+#cfp .vote_buttons {
+ height: 59px;
+ font-size: 18px;
+ font-style: italic;
+ color: #D9DC9E;
+}
+#cfp .cancel {
+ font-size: 14px;
}
@media screen and (max-width: 800px) {
View
BIN  static/img/cfp_vote_baloon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  static/img/signin_github.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  static/img/vote_button_1.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  static/img/vote_button_2.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  static/img/vote_button_3.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
14 static/js/background_scroll.js
@@ -1,14 +0,0 @@
-function start_backgroundscroll(){
- $(window).scroll(function () {
- if ($(window).scrollTop() > 650){
- $('body').css('background-position', 'center -650px');
- $('body').css('background-attachment', 'fixed');
- } else {
- $('body').css('background-position', 'center 0px');
- $('body').css('background-attachment', 'scroll');
- }
- });
-}
-$(document).ready(function(){
- start_backgroundscroll();
-});
View
79 static/js/custom.js
@@ -0,0 +1,79 @@
+function start_backgroundscroll(){
+ $(window).scroll(function () {
+ if ($(window).scrollTop() > 650){
+ $('body').css('background-position', 'center -650px');
+ $('body').css('background-attachment', 'fixed');
+ } else {
+ $('body').css('background-position', 'center 0px');
+ $('body').css('background-attachment', 'scroll');
+ }
+ });
+}
+
+var fb_url = 'https://graph.facebook.com/469934899704539';
+var tw_url = 'https://api.twitter.com/1/users/show.json?screen_name=djangocon&callback=?';
+var tw_tl_url = 'https://api.twitter.com/1/statuses/user_timeline.json?screen_name=djangocon&count=20&include_rts=1&callback=?';
+
+function set_facebook() {
+ $.getJSON(fb_url, function(data){
+ $('.counter.facebook').html(data.likes);
+ });
+}
+
+function set_twitter() {
+ $.getJSON(tw_url, function(data){
+ $('.counter.twitter').html(data.followers_count);
+ });
+
+ $.getJSON(tw_tl_url, function(data){
+ var i, tweet;
+ for(i=0; i < 3; i++) {
+ tweet = '<a target="_blank" href="https://twitter.com/djangocon/status/' +
+ data[i].id_str + '"><div class="tweet">' +
+ data[i].text + '</div></a>';
+ $('.tweets').append(tweet);
+ }
+ });
+}
+
+$(document).ready(function(){
+ start_backgroundscroll();
+ set_facebook();
+ set_twitter();
+
+ $('.vote_buttons .vote').click(function(){
+
+ var button = $(this)
+
+ $.getJSON('/vote/add_vote/'+button.attr('data-id')+'/'+button.attr('data-kind')+'/', function(data){
+ if (data.result == 'success'){
+ var baloon = button.parent().parent().find('.baloon_counter');
+ var score = parseInt(baloon.text())+parseInt(button.attr('data-kind'));
+ baloon.html(score);
+ button.parent().html(data.message);
+ } else {
+ button.parent().html(data.message);
+ }
+ });
+ });
+
+ $('.vote_buttons .cancel').click(function(){
+
+ var button = $(this)
+
+ $.getJSON('/vote/cancel_vote/'+button.attr('data-id')+'/', function(data){
+ if (data.result == 'success'){
+ var baloon = button.parent().parent().find('.baloon_counter');
+ var score = parseInt(baloon.text())-parseInt(button.attr('data-kind'));
+ baloon.html(score);
+ button.parent().html(data.message);
+
+ } else {
+ button.parent().html(data.message);
+ }
+ });
+
+ })
+
+});
+
View
30 static/js/networks.js
@@ -1,30 +0,0 @@
-var fb_url = 'https://graph.facebook.com/469934899704539';
-var tw_url = 'https://api.twitter.com/1/users/show.json?screen_name=djangocon&callback=?';
-var tw_tl_url = 'https://api.twitter.com/1/statuses/user_timeline.json?screen_name=djangocon&count=20&include_rts=1&callback=?';
-
-function set_facebook() {
- $.getJSON(fb_url, function(data){
- $('.counter.facebook').html(data.likes);
- });
-}
-
-function set_twitter() {
- $.getJSON(tw_url, function(data){
- $('.counter.twitter').html(data.followers_count);
- });
-
- $.getJSON(tw_tl_url, function(data){
- var i, tweet;
- for(i=0; i < 3; i++) {
- tweet = '<a target="_blank" href="https://twitter.com/djangocon/status/' +
- data[i].id_str + '"><div class="tweet">' +
- data[i].text + '</div></a>';
- $('.tweets').append(tweet);
- }
- });
-}
-
-$(document).ready(function(){
- set_facebook();
- set_twitter();
-});
View
1  templates/404.html
@@ -0,0 +1 @@
+404 Page Not Found. Some day we'll come up with something better here :)
View
1  templates/500.html
@@ -0,0 +1 @@
+Internal Server Error
View
3  templates/index.html
@@ -9,8 +9,7 @@
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/font-awesome.css" />
<script type="text/javascript" src="{{ STATIC_URL }}js/jquery.js"></script>
- <script type="text/javascript" src="{{ STATIC_URL }}js/background_scroll.js" ></script>
- <script type="text/javascript" src="{{ STATIC_URL }}js/networks.js" ></script>
+ <script type="text/javascript" src="{{ STATIC_URL }}js/custom.js" ></script>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<meta name="google-site-verification" content="t7HLmwO2QDEbBFL7MKpOqoViw0eED7tolEtKRQtlMI8" />
View
55 templates/vote.html
@@ -0,0 +1,55 @@
+{% extends 'index.html' %}
+{% load url from future %}
+{% load vote_tags %}
+
+{% block content %}
+
+<div class="popup" id="cfp">
+ <div class="header">
+ Call for Papers - voting
+ </div>
+ <div class="content">
+ {% if not request.user.is_authenticated %}
+ <p id="signin">
+ Welcome to the DjangoCon Call for Papers voting! We're more than happy to present you 75 excellent submission to vote on. Every Django community member is welcome to vote and choose the best talks to be presented at DjangoCon on 15-17th May. We're looking for 15 great, diverse and inspiring speeches. In order to vote, please indentify yourself by signing in with your Github account.
+
+ {% for backend in social_auth.backends %}
+ <a href="{% url 'socialauth_begin' backend %}?{{ redirect_querystring }}" class="github_login">
+ <center><img src="{{ STATIC_URL }}img/signin_github.png" /></center>
+ </a>
+ {% endfor %}
+ </p>
+
+ <h2>Proposals:</h2>
+ {% endif %}
+
+ {% for entry in entries %}
+
+ <div class="entry" data-id="{{ entry.id }}">
+ <div class="baloon_counter">{{ entry.score }}</div>
+
+ <h4>{{ entry.topic }}</h4>
+
+ <p>{{ entry.description|linebreaks|urlize }}
+
+ <p class="vote_buttons">
+ {% if entry|voted_by_user:request.user %}
+ {% with vote=entry|voted_by_user:request.user %}
+ You already voted for {{ vote.get_kind_display }}. <a href="javascript:void(0);" class="cancel" data-kind="{{ vote.kind }}" data-id="{{ vote.id }}">Cancel your vote?</a>
+ {% endwith %}
+ {% else %}
+ <a href="javascript:void(0);" class="vote" data-id="{{ entry.id }}" data-kind="2"><img src="{{ STATIC_URL }}img/vote_button_1.png" /></a>
+ <a href="javascript:void(0);" class="vote" data-id="{{ entry.id }}" data-kind="1"><img src="{{ STATIC_URL }}img/vote_button_2.png" /></a>
+ <a href="javascript:void(0);" class="vote" data-id="{{ entry.id }}" data-kind="0"><img src="{{ STATIC_URL }}img/vote_button_3.png" /></a>
+ {% endif %}
+ </p>
+
+ </div>
+
+
+ {% endfor %}
+
+ <div style="clear:both;"></div>
+ </div>
+</div>
+{% endblock %}
View
0  vote/__init__.py
No changes.
View
15 vote/admin.py
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+from django.contrib import admin
+from vote.models import *
+
+class EntryAdmin(admin.ModelAdmin):
+ list_display = ['topic', 'description','score']
+ search_fields = ['topic', 'description']
+
+class VoteAdmin(admin.ModelAdmin):
+ list_display = ['user', 'entry', 'created']
+ search_fields = ['=user__username',]
+ list_filter = ['entry',]
+
+admin.site.register(Entry, EntryAdmin)
+admin.site.register(Vote, VoteAdmin)
View
95 vote/migrations/0001_initial.py
@@ -0,0 +1,95 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding model 'Entry'
+ db.create_table('vote_entry', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('topic', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('description', self.gf('django.db.models.fields.TextField')()),
+ ))
+ db.send_create_signal('vote', ['Entry'])
+
+ # Adding model 'Vote'
+ db.create_table('vote_vote', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('entry', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['vote.Entry'])),
+ ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
+ ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
+ ))
+ db.send_create_signal('vote', ['Vote'])
+
+ # Adding unique constraint on 'Vote', fields ['entry', 'user']
+ db.create_unique('vote_vote', ['entry_id', 'user_id'])
+
+
+ def backwards(self, orm):
+ # Removing unique constraint on 'Vote', fields ['entry', 'user']
+ db.delete_unique('vote_vote', ['entry_id', 'user_id'])
+
+ # Deleting model 'Entry'
+ db.delete_table('vote_entry')
+
+ # Deleting model 'Vote'
+ db.delete_table('vote_vote')
+
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'vote.entry': {
+ 'Meta': {'object_name': 'Entry'},
+ 'description': ('django.db.models.fields.TextField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'topic': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'vote.vote': {
+ 'Meta': {'unique_together': "(('entry', 'user'),)", 'object_name': 'Vote'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'entry': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['vote.Entry']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ }
+ }
+
+ complete_apps = ['vote']
View
75 vote/migrations/0002_auto__add_field_vote_kind.py
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding field 'Vote.kind'
+ db.add_column('vote_vote', 'kind',
+ self.gf('django.db.models.fields.IntegerField')(default=0),
+ keep_default=False)
+
+
+ def backwards(self, orm):
+ # Deleting field 'Vote.kind'
+ db.delete_column('vote_vote', 'kind')
+
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'vote.entry': {
+ 'Meta': {'object_name': 'Entry'},
+ 'description': ('django.db.models.fields.TextField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'topic': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'vote.vote': {
+ 'Meta': {'unique_together': "(('entry', 'user'),)", 'object_name': 'Vote'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'entry': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['vote.Entry']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'kind': ('django.db.models.fields.IntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ }
+ }
+
+ complete_apps = ['vote']
View
76 vote/migrations/0003_auto__add_field_entry_score.py
@@ -0,0 +1,76 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding field 'Entry.score'
+ db.add_column('vote_entry', 'score',
+ self.gf('django.db.models.fields.IntegerField')(default=0),
+ keep_default=False)
+
+
+ def backwards(self, orm):
+ # Deleting field 'Entry.score'
+ db.delete_column('vote_entry', 'score')
+
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'vote.entry': {
+ 'Meta': {'ordering': "('-score',)", 'object_name': 'Entry'},
+ 'description': ('django.db.models.fields.TextField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'topic': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'vote.vote': {
+ 'Meta': {'unique_together': "(('entry', 'user'),)", 'object_name': 'Vote'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'entry': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['vote.Entry']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'kind': ('django.db.models.fields.IntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ }
+ }
+
+ complete_apps = ['vote']
View
0  vote/migrations/__init__.py
No changes.
View
30 vote/models.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+from django.contrib.auth.models import User
+from django.db import models
+
+class Entry(models.Model):
+ topic = models.CharField(max_length=255, null=False, blank=False)
+ description = models.TextField(null=False, blank=False)
+ score = models.IntegerField(default=0, null=False, blank=False)
+
+ class Meta:
+ verbose_name = "Call for papers: Entry"
+ verbose_name_plural = "Call for papers: Entries"
+ ordering = ('-score',)
+
+ def __unicode__(self):
+ return self.topic
+
+class Vote(models.Model):
+ entry = models.ForeignKey(Entry, null=False, blank=False)
+ user = models.ForeignKey(User, null=False, blank=False)
+ kind = models.IntegerField(null=False, blank=False, choices=((2,'MUST have!'),(1, 'Yaaay'),(0,'Meh')))
+ created = models.DateTimeField(auto_now_add=True, null=False, blank=False)
+
+ class Meta:
+ verbose_name = "Call for papers: Vote"
+ verbose_name_plural = "Call for papers: Votes"
+ unique_together = ('entry', 'user')
+
+ def __unicode__(self):
+ return "%s's vote on %s" % (self.user.username, self.entry.topic)
View
0  vote/templatetags/__init__.py
No changes.
View
12 vote/templatetags/vote_tags.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+from django import template
+from vote.models import Vote
+
+register = template.Library()
+
+@register.filter
+def voted_by_user(entry, user):
+ try:
+ return Vote.objects.get(user=user, entry=entry)
+ except:
+ return None
View
16 vote/tests.py
@@ -0,0 +1,16 @@
+"""
+This file demonstrates writing tests using the unittest module. These will pass
+when you run "manage.py test".
+
+Replace this with more appropriate tests for your application.
+"""
+
+from django.test import TestCase
+
+
+class SimpleTest(TestCase):
+ def test_basic_addition(self):
+ """
+ Tests that 1 + 1 always equals 2.
+ """
+ self.assertEqual(1 + 1, 2)
View
7 vote/urls.py
@@ -0,0 +1,7 @@
+from django.conf.urls.defaults import patterns, url
+
+urlpatterns = patterns('vote.views',
+ url(r'^$', 'index', name='index'),
+ url(r'^add_vote/(?P<id>\d+)/(?P<kind>\d+)/$', 'add_vote', name='add_vote'),
+ url(r'^cancel_vote/(?P<id>\d+)/$', 'cancel_vote', name='cancel_vote'),
+)
View
9 vote/utils.py
@@ -0,0 +1,9 @@
+import simplejson
+
+from django.http import HttpResponse
+
+def json_response(data):
+ return HttpResponse(simplejson.dumps(data), mimetype='application/json')
+
+def json_error(message):
+ return json_response({'result': 'error', 'message': message})
View
56 vote/views.py
@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+
+from django.contrib.auth.decorators import login_required
+from django.db import IntegrityError
+from django.http import HttpResponse
+from django.shortcuts import redirect, render
+from django.utils import simplejson as json
+
+from vote.models import Entry, Vote
+from vote.utils import json_response, json_error
+
+def index(request):
+
+ return render(request, "vote.html", {
+ 'entries': Entry.objects.all(),
+ })
+
+def add_vote(request, id=0, kind=2):
+ if request.user.is_anonymous():
+ return json_error('<a href="#signin">Sign in</a> to vote for submissions.')
+
+ try:
+ entry = Entry.objects.get(id=id)
+ except Entry.DoesNotExist:
+ return json_error('Entry does not exist.')
+
+ try:
+ kind = int(kind)
+ except ValueError:
+ return json_error('Wrong vote kind.')
+
+ if kind < 0 or kind > 2:
+ return json_error('Wrong vote kind.')
+
+ if Vote.objects.filter(entry=entry, user=request.user).count() == 0:
+ Vote.objects.create(entry=entry, user=request.user, kind=kind)
+ entry.score += kind
+ entry.save()
+ else:
+ return json_error('You can only add one vote for each entry.')
+
+ return json_response({'result':'success', 'message':'Your vote has been counted.'})
+
+@login_required
+def cancel_vote(request, id=0):
+ try:
+ vote = Vote.objects.get(id=id, user=request.user)
+ except Vote.DoesNotExist:
+ return json_error('Vote does not exist.')
+
+ entry = vote.entry
+ entry.score -= vote.kind
+ entry.save()
+ vote.delete()
+
+ return json_response({'result':'success', 'message':'Your vote has been cancelled.'})
Please sign in to comment.
Something went wrong with that request. Please try again.