diff --git a/db.sqlite3 b/db.sqlite3 new file mode 100644 index 0000000..4b863e7 Binary files /dev/null and b/db.sqlite3 differ diff --git a/headshotbig.jpg b/headshotbig.jpg new file mode 100644 index 0000000..605b025 Binary files /dev/null and b/headshotbig.jpg differ diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..76406fd --- /dev/null +++ b/manage.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "vaa.settings") + + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..981b975 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,20 @@ +Django==1.9.7 +Pillow==3.3.0 +argparse==1.2.1 +backports.shutil-get-terminal-size==1.0.0 +crc16==0.1.1 +decorator==4.0.10 +django-crispy-forms==1.6.0 +ipython==4.2.1 +ipython-genutils==0.1.0 +pathlib2==2.1.0 +pexpect==4.2.0 +pickleshare==0.7.2 +psycopg2==2.6.1 +ptyprocess==0.5.1 +python-redis==0.0.7 +simplegeneric==0.8.1 +six==1.10.0 +traitlets==4.2.2 +wsgiref==0.1.2 +git+https://github.com/timmyomahony/django-charsleft-widget.git diff --git a/static/charsleft-widget/css/charsleft.css b/static/charsleft-widget/css/charsleft.css new file mode 100644 index 0000000..1a6b930 --- /dev/null +++ b/static/charsleft-widget/css/charsleft.css @@ -0,0 +1,17 @@ +.charsleft{ + height: 23px; +} +.charsleft > span{ + vertical-align: middle; + margin-left: 0.5em; + color: #ccc; + line-height: 23px; +} +.charsleft span .count{ + color: #333; +} +.charsleft .maxlength{ + display: none; +} +.charsleft span .orange{color:#EA1300 !important;} +.charsleft span .red{color: #D56F24 !important;} \ No newline at end of file diff --git a/static/charsleft-widget/js/charsleft.js b/static/charsleft-widget/js/charsleft.js new file mode 100644 index 0000000..a8f2987 --- /dev/null +++ b/static/charsleft-widget/js/charsleft.js @@ -0,0 +1,45 @@ +(function($){ + + $.fn.charsLeft = function(options){ + + var defaults = { + 'source':'input', + 'dest':'.count', + } + var options = $.extend(defaults, options); + + var calculate = function(source, dest, maxlength){ + var remaining = maxlength - source.val().length; + dest.html(remaining); + /* Over 50%, change colour to orange */ + p=(100*remaining)/maxlength; + console.log(p) + if(p<25){ + dest.addClass('orange'); + }else if(p<50){ + dest.addClass('red'); + }else{ + dest.removeClass('orange red'); + } + }; + + this.each(function(i, el) { + var maxlength = $(this).find('.maxlength').html(); + var dest = $(this).find(options.dest); + var source = $(this).find(options.source); + source.keyup(function(){ + calculate(source, dest, maxlength) + }); + source.change(function(){ + calculate(source, dest, maxlength) + }); + }); + }; + $(function() { // Added page ready wrapper + $(".charsleft-input").charsLeft({ + 'source':'input', + 'dest':".count", + }); + }); +})(jQuery); + diff --git a/static/charsleft-widget/js/charsleft.js~ b/static/charsleft-widget/js/charsleft.js~ new file mode 100644 index 0000000..8483556 --- /dev/null +++ b/static/charsleft-widget/js/charsleft.js~ @@ -0,0 +1,45 @@ +(function($){ + + $.fn.charsLeft = function(options){ + + var defaults = { + 'source':'input', + 'dest':'.count', + } + var options = $.extend(defaults, options); + + var calculate = function(source, dest, maxlength){ + var remaining = maxlength - source.val().length; + dest.html(remaining); + /* Over 50%, change colour to orange */ + p=(100*remaining)/maxlength; + console.log(p) + if(p<25){ + dest.addClass('orange'); + }else if(p<50){ + dest.addClass('red'); + }else{ + dest.removeClass('orange red'); + } + }; + + this.each(function(i, el) { + var maxlength = $(this).find('.maxlength').html(); + var dest = $(this).find(options.dest); + var source = $(this).find(options.source); + source.keyup(function(){ + calculate(source, dest, maxlength) + }); + source.change(function(){ + calculate(source, dest, maxlength) + }); + }); + }; + $(function() { // Added page ready wrapper + $(".charsleft-input").charsLeft({ + 'source':'input', + 'dest':".count", + }); + }); +})(django.jQuery); + diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..edd1dc3 --- /dev/null +++ b/templates/base.html @@ -0,0 +1,12 @@ + + +{% block title %}{% endblock title %} + + +{% block extrahead %} +{% endblock extrahead %} + + +{% block content %} +{% endblock content %} + diff --git a/templates/base.html~ b/templates/base.html~ new file mode 100644 index 0000000..17be314 --- /dev/null +++ b/templates/base.html~ @@ -0,0 +1,8 @@ + + +{% block title %}{% endblock title %} + + +{% block content %} +{% endblock content %} + diff --git a/templates/candanswers.html b/templates/candanswers.html new file mode 100644 index 0000000..89575ea --- /dev/null +++ b/templates/candanswers.html @@ -0,0 +1,7 @@ +{% extends "base.html" %} +{% load crispy_forms_tags %} +{% block content %} +{% crispy answerform %} +{{ answerform.media }} +{% endblock content %} + diff --git a/templates/candanswers.html~ b/templates/candanswers.html~ new file mode 100644 index 0000000..b145f55 --- /dev/null +++ b/templates/candanswers.html~ @@ -0,0 +1,2 @@ +{% extends "base.html" %} +{% crispy answerform %} diff --git a/templates/comparison.html b/templates/comparison.html new file mode 100644 index 0000000..9579280 --- /dev/null +++ b/templates/comparison.html @@ -0,0 +1,13 @@ +{% extends "base.html" %} +{% block content %} + + + + + +{% for cand, percent in data %} + +{% endfor %} + +
FrambjóðandiHversu sammála
{{ cand.name }}{{ percent }}
+{% endblock content %} diff --git a/templates/comparison.html~ b/templates/comparison.html~ new file mode 100644 index 0000000..7699a7f --- /dev/null +++ b/templates/comparison.html~ @@ -0,0 +1,4 @@ +{% extends "base.html" %} +{% block content %} + +{% endblock content %} diff --git a/templates/userpage.html b/templates/userpage.html new file mode 100644 index 0000000..85d233c --- /dev/null +++ b/templates/userpage.html @@ -0,0 +1,7 @@ +{% extends "base.html" %} +{% load crispy_forms_tags %} +{% block content %} +
+{% crispy userpageform %} +
+{% endblock content %} diff --git a/templates/userpage.html~ b/templates/userpage.html~ new file mode 100644 index 0000000..9800e1d --- /dev/null +++ b/templates/userpage.html~ @@ -0,0 +1,4 @@ +{% extends "base.html" %} +{% block content %} +{{ userform.as_p }} +{% endblock content %} diff --git a/templates/voter_form.html b/templates/voter_form.html new file mode 100644 index 0000000..1bf58ed --- /dev/null +++ b/templates/voter_form.html @@ -0,0 +1,5 @@ +{% extends "base.html" %} +{% load crispy_forms_tags %} +{% block content %} +{% crispy voterform %} +{% endblock content %} diff --git a/vaa/__init__.py b/vaa/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vaa/__init__.pyc b/vaa/__init__.pyc new file mode 100644 index 0000000..91e7bd6 Binary files /dev/null and b/vaa/__init__.pyc differ diff --git a/vaa/questions/__init__.py b/vaa/questions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vaa/questions/__init__.pyc b/vaa/questions/__init__.pyc new file mode 100644 index 0000000..629aff7 Binary files /dev/null and b/vaa/questions/__init__.pyc differ diff --git a/vaa/questions/__init__.py~ b/vaa/questions/__init__.py~ new file mode 100644 index 0000000..e69de29 diff --git a/vaa/questions/admin.py b/vaa/questions/admin.py new file mode 100644 index 0000000..8de4a31 --- /dev/null +++ b/vaa/questions/admin.py @@ -0,0 +1,32 @@ +from django.contrib import admin + +from .models import Candidate, Question, QuestionText, AnswerSheet, AnswerText + + +class QuestionTextInline(admin.TabularInline): + model = QuestionText + + +class AnswerSheetInline(admin.TabularInline): + model = AnswerSheet + + +class CandidateAdmin(admin.ModelAdmin): + inlines = [AnswerSheetInline,] + + +class QuestionAdmin(admin.ModelAdmin): + inlines = [QuestionTextInline,] + + +class AnswerSheetAdmin(admin.ModelAdmin): + pass + + +class AnswerTextAdmin(admin.ModelAdmin): + pass + +admin.site.register(Candidate, CandidateAdmin) +admin.site.register(Question, QuestionAdmin) +admin.site.register(AnswerSheet, AnswerSheetAdmin) +admin.site.register(AnswerText, AnswerTextAdmin) diff --git a/vaa/questions/admin.pyc b/vaa/questions/admin.pyc new file mode 100644 index 0000000..5e8ab6c Binary files /dev/null and b/vaa/questions/admin.pyc differ diff --git a/vaa/questions/admin.py~ b/vaa/questions/admin.py~ new file mode 100644 index 0000000..03c52a8 --- /dev/null +++ b/vaa/questions/admin.py~ @@ -0,0 +1,28 @@ +from django.contrib import admin + +from .models import Candidate, Question, QuestionText, AnswerSheet + + +class QuestionTextInline(admin.TabularInline): + model = QuestionText + + +class AnswerSheetInline(admin.TabularInline): + model = AnswerSheet + + +class CandidateAdmin(admin.ModelAdmin): + inlines = [AnswerSheetInline,] + + +class QuestionAdmin(admin.ModelAdmin): + inlines = [QuestionTextInline,] + + +class AnswerSheetAdmin(admin.ModelAdmin): + pass + + +admin.site.register(Candidate, CandidateAdmin) +admin.site.register(Question, QuestionAdmin) +admin.site.register(AnswerSheet, AnswerSheetAdmin) diff --git a/vaa/questions/forms.py b/vaa/questions/forms.py new file mode 100644 index 0000000..8864cee --- /dev/null +++ b/vaa/questions/forms.py @@ -0,0 +1,129 @@ +# encoding=utf8 + +from django import forms +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Submit, Layout, HTML, Fieldset, Field +from crispy_forms.bootstrap import InlineRadios +from charsleft_widget.widgets import CharsLeftInput + +from .models import Question, AnswerText + + +class UserForm(forms.Form): + first_name = forms.CharField( + label=u"Fornafn", + max_length=40, + widget=forms.TextInput(attrs={'size': '40'}) + ) + last_name = forms.CharField( + label=u"Föðurnafn", + max_length=40, + widget=forms.TextInput(attrs={'size': '40'}) + ) + ssn = forms.CharField( + label=u"Kennitala (dddddd-dddd)", + max_length=11, + widget=forms.TextInput(attrs={'size': '12'}) + ) + last_answer = forms.DateTimeField(label=u"Spurningum svarað", required=False) + picture = forms.ImageField(label=u"Mynd (til kynningar)", required=False) + blurb = forms.CharField( + label=u"Kynningartexti frambjóðanda", + max_length=2000, + widget=CharsLeftInput(), + required=False, + + ) + + def __init__(self, *args, **kwargs): + super(UserForm, self).__init__(*args, **kwargs) + self.helper = FormHelper() + self.helper.form_id = 'id-userform' + self.helper.form_class = 'form-horizontal' + self.helper.label_class = 'col-lg-3' + self.helper.field_class = 'col-lg-9' + self.helper.form_method = 'post' + self.helper.form_action = '/userupdate/' + self.helper.layout = Layout( + 'first_name', + 'last_name', + 'ssn', + 'picture', + 'blurb', + 'last_answer', + HTML('
Svara spurningum
') + ) + self.helper.add_input(Submit('submit', 'Vista')) + + +class AnswerForm(forms.Form): + def __init__(self, *args, **kwargs): + initial = kwargs.pop('initial', None) + lang = kwargs.pop('language', "IS") + answertexts = AnswerText.objects.filter(lang=lang).order_by('mod') + choices = [(str(at.mod), at.text) for at in answertexts] + super(AnswerForm, self).__init__(*args, **kwargs) + qs = [q.pk for q in Question.objects.filter(active=True)] + for question in Question.objects.filter(active=True): + self.fields['q_%s' % question.pk] = forms.ChoiceField( + label=question.questiontext_set.filter(lang=lang)[0].text, + widget=forms.RadioSelect, + choices=choices, + ) + self.fields['t_%s' % question.pk] = forms.CharField( + label=u"Nánari skýring á afstöðu", + max_length=500, + widget=CharsLeftInput(), + required=False, + ) + if initial: + self.fields['t_%s' % question.pk].initial = initial.pop('t_%s' % question.pk) + self.fields['q_%s' % question.pk].initial = initial.pop('q_%s' % question.pk) + + self.helper = FormHelper(self) + self.helper.form_id = 'id-answerform' + self.helper.form_class = 'form-horizontal' + self.helper.label_class = 'col-lg-3' + self.helper.field_class = 'col-lg-8' + self.helper.form_method = 'post' + self.helper.form_action = '/candans/' + layout = zip([Field("q_%s" % q) for q in qs], [Field("t_%s"%q) for q in qs]) + self.helper.layout = Layout(*layout) + self.helper.add_input(Submit('submit', 'Vista')) + + def get_data(self): + return self.cleaned_data.items() + + +class VoterForm(forms.Form): + def __init__(self, *args, **kwargs): + lang = kwargs.pop('language', "IS") + answertexts = AnswerText.objects.filter(lang=lang).order_by('mod') + choices = [(str(at.mod), at.text) for at in answertexts] + super(VoterForm, self).__init__(*args, **kwargs) + qs = [q.pk for q in Question.objects.filter(active=True)] + for question in Question.objects.filter(active=True): + self.fields['q_%s' % question.pk] = forms.ChoiceField( + label=question.questiontext_set.filter(lang=lang)[0].text, + widget=forms.RadioSelect, + choices=choices, + ) + self.fields['i_%s' % question.pk] = forms.ChoiceField( + label=u"Mikilvægi spurningar", + widget=forms.RadioSelect, + choices=[(1,1),(2,2),(3,3),(4,4),(5,5)], + initial=3 + ) + self.helper = FormHelper(self) + self.helper.form_id = 'id-answerform' + self.helper.form_class = 'form-horizontal' + self.helper.label_class = 'col-lg-3' + self.helper.field_class = 'col-lg-8' + self.helper.form_method = 'post' + self.helper.form_action = '/compare/' + layout = zip([Field("q_%s" % q) for q in qs], [InlineRadios("i_%s"%q) for q in qs]) + self.helper.layout = Layout(*layout) + self.helper.add_input(Submit('submit', u'Fá samanburð')) + + def get_data(self): + return self.cleaned_data.items() diff --git a/vaa/questions/forms.pyc b/vaa/questions/forms.pyc new file mode 100644 index 0000000..0db6187 Binary files /dev/null and b/vaa/questions/forms.pyc differ diff --git a/vaa/questions/forms.py~ b/vaa/questions/forms.py~ new file mode 100644 index 0000000..86e0894 --- /dev/null +++ b/vaa/questions/forms.py~ @@ -0,0 +1,89 @@ +# encoding=utf8 + +from django import forms +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Submit, Layout, HTML, Fieldset, Field +from crispy_forms.bootstrap import InlineRadios +from charsleft_widget.widgets import CharsLeftInput + +from .models import Question, AnswerText + + +class UserForm(forms.Form): + first_name = forms.CharField( + label=u"Fornafn", + max_length=40, + widget=forms.TextInput(attrs={'size': '40'}) + ) + last_name = forms.CharField( + label=u"Föðurnafn", + max_length=40, + widget=forms.TextInput(attrs={'size': '40'}) + ) + ssn = forms.CharField( + label=u"Kennitala (dddddd-dddd)", + max_length=11, + widget=forms.TextInput(attrs={'size': '12'}) + ) + last_answer = forms.DateTimeField(label=u"Spurningum svarað", disabled=True, required=False) + picture = forms.ImageField(label=u"Mynd (til kynningar)", required=False) + blurb = forms.CharField( + label=u"Kynningartexti frambjóðanda", + widget=forms.TextInput(attrs={'size': '80'}), + required=False, + + ) + + def __init__(self, *args, **kwargs): + super(UserForm, self).__init__(*args, **kwargs) + self.helper = FormHelper() + self.helper.form_id = 'id-userform' + self.helper.form_class = 'form-horizontal' + self.helper.label_class = 'col-lg-3' + self.helper.field_class = 'col-lg-8' + self.helper.form_method = 'post' + self.helper.form_action = '/userupdate/' + self.helper.layout = Layout( + 'first_name', + 'last_name', + 'ssn', + 'picture', + 'blurb', + HTML('
Svara spurningum
') + ) + self.helper.add_input(Submit('submit', 'Vista')) + + +class AnswerForm(forms.Form): + def __init__(self, *args, **kwargs): + lang = kwargs.pop('language', "IS") + answertexts = AnswerText.objects.filter(lang=lang).order_by('mod') + choices = [(str(at.mod), at.text) for at in answertexts] + super(AnswerForm, self).__init__(*args, **kwargs) + qs = [q.pk for q in Question.objects.filter(active=True)] + for question in Question.objects.filter(active=True): + self.fields['q_%s' % question.pk] = forms.ChoiceField( + label=question.questiontext_set.filter(lang=lang)[0].text, + widget=forms.RadioSelect, + choices=choices, + ) + self.fields['t_%s' % question.pk] = forms.CharField( + label=u"Nánari skýring á afstöðu", + max_length=500, + widget=CharsLeftInput(), + required=False, + ) + self.helper = FormHelper(self) + self.helper.form_id = 'id-answerform' + self.helper.form_class = 'form-horizontal' + self.helper.label_class = 'col-lg-3' + self.helper.field_class = 'col-lg-8' + self.helper.form_method = 'post' + self.helper.form_action = '/candans/' + layout = zip([Field("q_%s" % q) for q in qs], [Field("t_%s"%q) for q in qs]) + #layout = [InlineRadios(self.fields["q_%s" % q]) for q in qs]#, [self.fields['t_%s' % q] for q in qs]) + self.helper.layout = Layout(*layout) + self.helper.add_input(Submit('submit', 'Vista')) + + def get_data(self): + return self.cleaned_data.items() diff --git a/vaa/questions/migrations/0001_initial.py b/vaa/questions/migrations/0001_initial.py new file mode 100644 index 0000000..9dedd23 --- /dev/null +++ b/vaa/questions/migrations/0001_initial.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-07-05 17:55 +from __future__ import unicode_literals + +from django.conf import settings +import django.contrib.postgres.fields.jsonb +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='AnswerSheet', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('timestamp', models.DateTimeField()), + ('answers', django.contrib.postgres.fields.jsonb.JSONField()), + ], + ), + migrations.CreateModel( + name='Candidate', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Question', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('order', models.IntegerField()), + ], + ), + migrations.CreateModel( + name='QuestionText', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('lang', models.CharField(max_length=2)), + ('text', models.TextField()), + ], + ), + migrations.AddField( + model_name='question', + name='text', + field=models.ManyToManyField(to='questions.QuestionText'), + ), + migrations.AddField( + model_name='answersheet', + name='candidate', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='questions.Candidate'), + ), + ] diff --git a/vaa/questions/migrations/0001_initial.pyc b/vaa/questions/migrations/0001_initial.pyc new file mode 100644 index 0000000..894dc9b Binary files /dev/null and b/vaa/questions/migrations/0001_initial.pyc differ diff --git a/vaa/questions/migrations/0002_candidate_ssn.py b/vaa/questions/migrations/0002_candidate_ssn.py new file mode 100644 index 0000000..3e01bf7 --- /dev/null +++ b/vaa/questions/migrations/0002_candidate_ssn.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-07-05 17:58 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('questions', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='candidate', + name='ssn', + field=models.CharField(default='000000-0000', max_length=11), + preserve_default=False, + ), + ] diff --git a/vaa/questions/migrations/0002_candidate_ssn.pyc b/vaa/questions/migrations/0002_candidate_ssn.pyc new file mode 100644 index 0000000..ca63710 Binary files /dev/null and b/vaa/questions/migrations/0002_candidate_ssn.pyc differ diff --git a/vaa/questions/migrations/0003_auto_20160706_0949.py b/vaa/questions/migrations/0003_auto_20160706_0949.py new file mode 100644 index 0000000..dafc64e --- /dev/null +++ b/vaa/questions/migrations/0003_auto_20160706_0949.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-07-06 09:49 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('questions', '0002_candidate_ssn'), + ] + + operations = [ + migrations.RemoveField( + model_name='question', + name='text', + ), + migrations.AddField( + model_name='questiontext', + name='question', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='questions.Question'), + preserve_default=False, + ), + ] diff --git a/vaa/questions/migrations/0003_auto_20160706_0949.pyc b/vaa/questions/migrations/0003_auto_20160706_0949.pyc new file mode 100644 index 0000000..aaeab61 Binary files /dev/null and b/vaa/questions/migrations/0003_auto_20160706_0949.pyc differ diff --git a/vaa/questions/migrations/0004_auto_20160706_1326.py b/vaa/questions/migrations/0004_auto_20160706_1326.py new file mode 100644 index 0000000..0011fe8 --- /dev/null +++ b/vaa/questions/migrations/0004_auto_20160706_1326.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-07-06 13:26 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('questions', '0003_auto_20160706_0949'), + ] + + operations = [ + migrations.AddField( + model_name='candidate', + name='blurb', + field=models.TextField(default='Empty'), + preserve_default=False, + ), + migrations.AddField( + model_name='candidate', + name='picture', + field=models.ImageField(blank=True, null=True, upload_to=b''), + ), + ] diff --git a/vaa/questions/migrations/0004_auto_20160706_1326.pyc b/vaa/questions/migrations/0004_auto_20160706_1326.pyc new file mode 100644 index 0000000..2e51cf6 Binary files /dev/null and b/vaa/questions/migrations/0004_auto_20160706_1326.pyc differ diff --git a/vaa/questions/migrations/0005_auto_20160707_1343.py b/vaa/questions/migrations/0005_auto_20160707_1343.py new file mode 100644 index 0000000..dd0b74a --- /dev/null +++ b/vaa/questions/migrations/0005_auto_20160707_1343.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-07-07 13:43 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('questions', '0004_auto_20160706_1326'), + ] + + operations = [ + migrations.CreateModel( + name='AnswerText', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('lang', models.CharField(max_length=2)), + ('text', models.TextField()), + ('mod', models.IntegerField()), + ], + ), + migrations.AddField( + model_name='question', + name='active', + field=models.BooleanField(default=False), + preserve_default=False, + ), + ] diff --git a/vaa/questions/migrations/0005_auto_20160707_1343.pyc b/vaa/questions/migrations/0005_auto_20160707_1343.pyc new file mode 100644 index 0000000..026afd0 Binary files /dev/null and b/vaa/questions/migrations/0005_auto_20160707_1343.pyc differ diff --git a/vaa/questions/migrations/__init__.py b/vaa/questions/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vaa/questions/migrations/__init__.pyc b/vaa/questions/migrations/__init__.pyc new file mode 100644 index 0000000..f4a989c Binary files /dev/null and b/vaa/questions/migrations/__init__.pyc differ diff --git a/vaa/questions/models.py b/vaa/questions/models.py new file mode 100644 index 0000000..8e794e8 --- /dev/null +++ b/vaa/questions/models.py @@ -0,0 +1,69 @@ +from django.db import models +from django.contrib.auth.models import User +from django.contrib.postgres.fields import JSONField + +from vaa.utils import trunc, zipvalues + +def simple_distance(this, other): + other_mul = other[1] + other = other[0] + this = int(this) + if this == 6 or other == 6: + return 1 * other_mul + else: + return (max(this, other) - min(this, other)) * other_mul + +class Candidate(models.Model): + user = models.ForeignKey(User) + ssn = models.CharField(max_length=11) + picture = models.ImageField(null=True, blank=True) + blurb = models.TextField() + + def __unicode__(self): + return self.user.get_full_name() + + @property + def last_answers(self): + return self.answersheet_set.order_by('-timestamp').first().answers + + def compare(self, other_data, method=simple_distance): + d = zipvalues(other_data, d=True) + return sum([method(value, d[key]) for key, value in self.last_answers if 'q_' in key]) + + def name(self): + return self.user.get_full_name() + + +class Question(models.Model): + order = models.IntegerField() + active = models.BooleanField() + + def __unicode__(self): + istext = self.questiontext_set.get(lang="IS") + return trunc(istext.text, 30) + + +class QuestionText(models.Model): + lang = models.CharField(max_length=2) + text = models.TextField() + question = models.ForeignKey(Question) + + def __unicode__(self): + return trunc(self.text, 30) + +class AnswerText(models.Model): + lang = models.CharField(max_length=2) + text = models.TextField() + mod = models.IntegerField() + + def __unicode__(self): + return self.text + ":" + unicode(self.mod) + + +class AnswerSheet(models.Model): + timestamp = models.DateTimeField() + candidate = models.ForeignKey(Candidate, null=True, blank=True) + answers = JSONField() + + def __unicode__(self): + return self.candidate.user.get_full_name() diff --git a/vaa/questions/models.pyc b/vaa/questions/models.pyc new file mode 100644 index 0000000..6a619f0 Binary files /dev/null and b/vaa/questions/models.pyc differ diff --git a/vaa/questions/models.py~ b/vaa/questions/models.py~ new file mode 100644 index 0000000..75115a3 --- /dev/null +++ b/vaa/questions/models.py~ @@ -0,0 +1,49 @@ +from django.db import models +from django.contrib.auth.models import User +from django.contrib.postgres.fields import JSONField + +from vaa.utils import trunc + +class Candidate(models.Model): + user = models.ForeignKey(User) + ssn = models.CharField(max_length=11) + picture = models.ImageField(null=True, blank=True) + blurb = models.TextField() + + def __unicode__(self): + return self.user.get_full_name() + + +class Question(models.Model): + order = models.IntegerField() + active = models.BooleanField() + + def __unicode__(self): + istext = self.questiontext_set.get(lang="IS") + return trunc(istext.text, 30) + + +class QuestionText(models.Model): + lang = models.CharField(max_length=2) + text = models.TextField() + question = models.ForeignKey(Question) + + def __unicode__(self): + return trunc(self.text, 30) + +class AnswerText(models.Model): + lang = models.CharField(max_length=2) + text = models.TextField() + mod = models.IntegerField() + + def __unicode__(self): + return self.text + ":" + unicode(self.mod) + + +class AnswerSheet(models.Model): + timestamp = models.DateTimeField() + candidate = models.ForeignKey(Candidate, null=True, blank=True) + answers = JSONField() + + def __unicode__(self): + return self.candidate.user.get_full_name() diff --git a/vaa/questions/views.py b/vaa/questions/views.py new file mode 100644 index 0000000..2ca025b --- /dev/null +++ b/vaa/questions/views.py @@ -0,0 +1,90 @@ +import datetime + +from django.http import HttpResponseRedirect +from django.shortcuts import render + + +from .forms import UserForm, AnswerForm, VoterForm +from .models import AnswerSheet, Candidate +from vaa.utils import render_with, max_d + +@render_with("home.html") +def home(request): + return {} + +@render_with("userpage.html") +def userpage(request): + candidate = request.user.candidate_set.all()[0] + last_answers = candidate.answersheet_set.first() + return { + 'userpageform': UserForm( + { + 'first_name':request.user.first_name, + 'last_name':request.user.last_name, + 'ssn':candidate.ssn, + 'last_answer':last_answers.timestamp, + 'picture':candidate.picture, + 'blurb':candidate.blurb, + } + ) + } + +def userupdate(request): + userform = UserForm(request.POST) + candidate = request.user.candidate_set.all()[0] + if userform.is_valid(): + data = userform.cleaned_data + request.user.first_name = data['first_name'] + request.user.last_name = data['last_name'] + candidate.ssn = data['ssn'] + candidate.picture = data['picture'] + candidate.blurb = data['blurb'] + request.user.save() + candidate.save() + return HttpResponseRedirect("/userpage/") + + +@render_with("candanswers.html") +def candanswer(request): + last_answers = request.user.candidate_set.first().last_answers + if last_answers: + form = AnswerForm(initial=dict(last_answers)) + else: + form = AnswerForm() + return { + 'answerform':form + } + +def candreply(request): + form = AnswerForm(request.POST) + if form.is_valid() == False: + return render(request, "candanswers.html", {'answerform':form}) + data = form.get_data() + print data + AnswerSheet( + timestamp=datetime.datetime.utcnow(), + candidate=request.user.candidate_set.all()[0], + answers=[[k,v] for k,v in data] + ).save() + return HttpResponseRedirect("/userpage/") + + +@render_with("voter_form.html") +def voterform(request): + form = VoterForm() + return {'voterform':form} + + + +@render_with("comparison.html") +def compare(request): + form = VoterForm(request.POST) + if form.is_valid() == False: + return render(request, "voter_form.html", {'voterform':form}) + voterdata = form.get_data() + print voterdata + max_distance = max_d(voterdata) + context = {'data': sorted( + [(cand, 1.0 - (float(cand.compare(voterdata))/float(max_distance))) for cand in Candidate.objects.all() if cand.last_answers], + key=lambda i:i[1], reverse=True)} + return context diff --git a/vaa/questions/views.pyc b/vaa/questions/views.pyc new file mode 100644 index 0000000..103ec96 Binary files /dev/null and b/vaa/questions/views.pyc differ diff --git a/vaa/questions/views.py~ b/vaa/questions/views.py~ new file mode 100644 index 0000000..fb134d7 --- /dev/null +++ b/vaa/questions/views.py~ @@ -0,0 +1,63 @@ +import datetime + +from django.http import HttpResponseRedirect +from django.shortcuts import render + + +from .forms import UserForm, AnswerForm +from .models import AnswerSheet +from vaa.utils import render_with + +@render_with("home.html") +def home(request): + return {} + +@render_with("userpage.html") +def userpage(request): + candidate = request.user.candidate_set.all()[0] + return { + 'userpageform': UserForm( + { + 'first_name':request.user.first_name, + 'last_name':request.user.last_name, + 'ssn':candidate.ssn, + 'last_answer':candidate.answersheet_set.order_by('-timestamp').first() or None, + 'picture':candidate.picture, + 'blurb':candidate.blurb, + } + ) + } + +def userupdate(request): + userform = UserForm(request.POST) + candidate = request.user.candidate_set.all()[0] + if userform.is_valid(): + data = userform.cleaned_data + request.user.first_name = data['first_name'] + request.user.last_name = data['last_name'] + candidate.ssn = data['ssn'] + candidate.picture = data['picture'] + candidate.blurb = data['blurb'] + request.user.save() + candidate.save() + return HttpResponseRedirect("/userpage/") + + +@render_with("candanswers.html") +def candanswer(request): + return { + 'answerform':AnswerForm() + } + +def candreply(request): + form = AnswerForm(request.POST) + if form.is_valid() == False: + return render(request, "candanswers.html", {'answerform':form}) + data = form.get_data() + print data + AnswerSheet( + timestamp=datetime.datetime.utcnow(), + candidate=request.user.candidate_set.all()[0], + answers=[[k,v] for k,v in data] + ).save() + return HttpResponseRedirect("/userpage/") diff --git a/vaa/settings.py b/vaa/settings.py new file mode 100644 index 0000000..48af8c6 --- /dev/null +++ b/vaa/settings.py @@ -0,0 +1,134 @@ +""" +Django settings for vaa project. + +Generated by 'django-admin startproject' using Django 1.9.7. + +For more information on this file, see +https://docs.djangoproject.com/en/1.9/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.9/ref/settings/ +""" + +import os + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'z9c5o(5rfxzs0y_801pdre#epak)5qoz1y5pt&_&afxg)crsxi' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'vaa.questions', + 'crispy_forms', + 'charsleft_widget' +] + +MIDDLEWARE_CLASSES = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'vaa.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [BASE_DIR + "/templates/",], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'vaa.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/1.9/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. + 'NAME': 'vaa', # Or path to database file if using sqlite3. + # The following settings are not used with sqlite3: + 'USER': 'vaa', + 'PASSWORD': 'vaa1234', + 'HOST': 'localhost', # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP. + 'PORT': '', # Set to empty string for default. + } +} + + +# Password validation +# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/1.9/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.9/howto/static-files/ + +STATIC_URL = '/static/' +STATIC_ROOT = '/var/www/vaa-static/' +CRISPY_TEMPLATE_PACK = 'bootstrap3' +STATICFILES_DIRS = [ + '/var/www/vaa/static', + ] diff --git a/vaa/settings.pyc b/vaa/settings.pyc new file mode 100644 index 0000000..5f97a4b Binary files /dev/null and b/vaa/settings.pyc differ diff --git a/vaa/settings.py~ b/vaa/settings.py~ new file mode 100644 index 0000000..cbfb3b4 --- /dev/null +++ b/vaa/settings.py~ @@ -0,0 +1,129 @@ +""" +Django settings for vaa project. + +Generated by 'django-admin startproject' using Django 1.9.7. + +For more information on this file, see +https://docs.djangoproject.com/en/1.9/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.9/ref/settings/ +""" + +import os + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'z9c5o(5rfxzs0y_801pdre#epak)5qoz1y5pt&_&afxg)crsxi' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'vaa.questions', + 'crispy_forms', + 'charsleft_widget' +] + +MIDDLEWARE_CLASSES = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'vaa.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [BASE_DIR + "/templates/",], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'vaa.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/1.9/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + + +# Password validation +# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/1.9/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.9/howto/static-files/ + +STATIC_URL = '/static/' +STATIC_ROOT = '/var/www/vaa-static/' +CRISPY_TEMPLATE_PACK = 'bootstrap3' +STATICFILES_DIRS = [ + '/var/www/vaa/static', + ] diff --git a/vaa/urls.py b/vaa/urls.py new file mode 100644 index 0000000..8882e7f --- /dev/null +++ b/vaa/urls.py @@ -0,0 +1,32 @@ +"""vaa URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/1.9/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.conf.urls import url, include + 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) +""" +from django.conf import settings +from django.conf.urls import url +from django.contrib import admin +from django.conf.urls.static import static + +from vaa.questions import views as vaav + +urlpatterns = [ + url(r'^userpage/$', vaav.userpage), + url(r'^$', vaav.home), + url(r'^userupdate/', vaav.userupdate), + url(r'^candans/', vaav.candreply), + url(r'^candanswer/', vaav.candanswer), + url(r'^voterform/', vaav.voterform), + url(r'^compare/', vaav.compare), + url(r'^admin/', admin.site.urls), +] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/vaa/urls.pyc b/vaa/urls.pyc new file mode 100644 index 0000000..4f9ca31 Binary files /dev/null and b/vaa/urls.pyc differ diff --git a/vaa/urls.py~ b/vaa/urls.py~ new file mode 100644 index 0000000..deafaa4 --- /dev/null +++ b/vaa/urls.py~ @@ -0,0 +1,30 @@ +"""vaa URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/1.9/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.conf.urls import url, include + 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) +""" +from django.conf import settings +from django.conf.urls import url +from django.contrib import admin +from django.conf.urls.static import static + +from vaa.questions import views as vaav + +urlpatterns = [ + url(r'^userpage/$', vaav.userpage), + url(r'^$', vaav.home), + url(r'^userupdate/', vaav.userupdate), + url(r'^candans/', vaav.candreply), + url(r'^candanswer/', vaav.candanswer), + url(r'^admin/', admin.site.urls), +] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/vaa/utils.py b/vaa/utils.py new file mode 100644 index 0000000..5570f45 --- /dev/null +++ b/vaa/utils.py @@ -0,0 +1,26 @@ +from django.shortcuts import render + + +def trunc(text, size): + return text[:size] + (" ..." if len(text) > size else "") + + +def max_d(dataset): + return sum([[4,3,2,3,4,1][q]*i for q, i in zipvalues(dataset)]) + + +def zipvalues(dataset, d=False): + dataset = dict(dataset) + if not d: + return [(int(dataset['q_'+str(i)]), int(dataset['i_'+str(i)])) for i in xrange(1,len(dataset)/2+1)] + else: + return dict([('q_%s' % i, (int(dataset['q_'+str(i)]), int(dataset['i_'+str(i)]))) for i in xrange(1,len(dataset)/2+1)]) + +class render_with: + def __init__(self, template): + self.template = template + + def __call__(self, f): + def wrapped(*args, **kwargs): + return render(args[0], self.template, f(*args, **kwargs)) + return wrapped diff --git a/vaa/utils.pyc b/vaa/utils.pyc new file mode 100644 index 0000000..2e13ca0 Binary files /dev/null and b/vaa/utils.pyc differ diff --git a/vaa/utils.py~ b/vaa/utils.py~ new file mode 100644 index 0000000..fd1a8a1 --- /dev/null +++ b/vaa/utils.py~ @@ -0,0 +1,15 @@ +from django.shortcuts import render + + +def trunc(text, size): + return text[:size] + (" ..." if len(text) > size else "") + + +class render_with: + def __init__(self, template): + self.template = template + + def __call__(self, f): + def wrapped(*args, **kwargs): + return render(args[0], self.template, f(*args, **kwargs)) + return wrapped diff --git a/vaa/wsgi.py b/vaa/wsgi.py new file mode 100644 index 0000000..12a5016 --- /dev/null +++ b/vaa/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for vaa project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "vaa.settings") + +application = get_wsgi_application() diff --git a/vaa/wsgi.pyc b/vaa/wsgi.pyc new file mode 100644 index 0000000..aa40a46 Binary files /dev/null and b/vaa/wsgi.pyc differ