diff --git a/pgweb/core/views.py b/pgweb/core/views.py index 1b5a3321..b6cf0d6e 100644 --- a/pgweb/core/views.py +++ b/pgweb/core/views.py @@ -13,6 +13,9 @@ from events.models import Event from quotes.models import Quote from models import Version +# models needed for the pieces on the community page +from survey.models import Survey + # models and forms needed for core objects from models import Organisation from forms import OrganisationForm @@ -32,6 +35,16 @@ def home(request): 'versions': versions, }) +# Community main page (contains surveys and potentially more) +def community(request): + s = Survey.objects.filter(current=True) + try: + s = s[0] + except: + s = None + return render_to_response('core/community.html', { + 'survey': s, + }, NavContext(request, 'community')) # Generic fallback view for static pages def fallback(request, url): diff --git a/pgweb/settings.py b/pgweb/settings.py index 0bf62a98..0802b195 100644 --- a/pgweb/settings.py +++ b/pgweb/settings.py @@ -102,6 +102,7 @@ INSTALLED_APPS = [ 'pgweb.profserv', 'pgweb.lists', 'pgweb.sponsors', + 'pgweb.survey', ] diff --git a/pgweb/survey/__init__.py b/pgweb/survey/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pgweb/survey/admin.py b/pgweb/survey/admin.py new file mode 100644 index 00000000..6d93b846 --- /dev/null +++ b/pgweb/survey/admin.py @@ -0,0 +1,15 @@ +from django.contrib import admin +from models import * + +class SurveyAdmin(admin.ModelAdmin): + list_display = ('question','posted','current',) + ordering = ('-posted',) + +class SurveyAnswerAdmin(admin.ModelAdmin): + list_display = ('survey','tot1','tot2','tot3','tot4','tot5','tot6','tot7','tot8') + ordering = ('-survey__posted',) + +admin.site.register(Survey, SurveyAdmin) +admin.site.register(SurveyLock) +admin.site.register(SurveyAnswer, SurveyAnswerAdmin) + diff --git a/pgweb/survey/models.py b/pgweb/survey/models.py new file mode 100644 index 00000000..f0cd1608 --- /dev/null +++ b/pgweb/survey/models.py @@ -0,0 +1,95 @@ +from django.db import models +from django.contrib.auth.models import User + +from datetime import datetime + +# internal text/value object +class SurveyQuestion(object): + def __init__(self, value, text): + self.value = value + self.text = text +class SurveyAnswerValues(object): + def __init__(self, option, votes, votespercent): + self.option = option + self.votes = votes + self.votespercent = votespercent + +class Survey(models.Model): + question = models.CharField(max_length=100, null=False, blank=False) + opt1 = models.CharField(max_length=100, null=False, blank=False) + opt2 = models.CharField(max_length=100, null=False, blank=False) + opt3 = models.CharField(max_length=100, null=False, blank=True) + opt4 = models.CharField(max_length=100, null=False, blank=True) + opt5 = models.CharField(max_length=100, null=False, blank=True) + opt6 = models.CharField(max_length=100, null=False, blank=True) + opt7 = models.CharField(max_length=100, null=False, blank=True) + opt8 = models.CharField(max_length=100, null=False, blank=True) + posted = models.DateTimeField(null=False, default=datetime.now) + current = models.BooleanField(null=False, default=False) + + def __unicode__(self): + return self.question + + @property + def questions(self): + for i in range (1,9): + v = getattr(self, "opt%s" % i) + if not v: break + yield SurveyQuestion(i, v) + + @property + def answers(self): + if not hasattr(self, "_answers"): + self._answers = SurveyAnswer.objects.get_or_create(survey=self)[0] + return self._answers + + @property + def completeanswers(self): + for a in self._get_complete_answers(): + yield SurveyAnswerValues(a[0], a[1], self.totalvotes>0 and (100*a[1]/self.totalvotes) or 0) + + @property + def totalvotes(self): + if not hasattr(self,"_totalvotes"): + self._totalvotes = 0 + for a in self._get_complete_answers(): + self._totalvotes = self._totalvotes + a[1] + return self._totalvotes + + def _get_complete_answers(self): + for i in range(1,9): + q = getattr(self, "opt%s" % i) + if not q: break + n = getattr(self.answers, "tot%s" % i) + yield (q,n) + + def save(self): + # Make sure only one survey at a time can be the current one + # (there may be some small race conditions here, but the likelyhood + # that two admins are editing the surveys at the same time...) + if self.current: + previous = Survey.objects.filter(current=True) + for p in previous: + if not p == self: + p.current = False + p.save() # primary key check avoids recursion + + # Now that we've made any previously current ones non-current, we are + # free to save this one. + super(Survey, self).save() + +class SurveyAnswer(models.Model): + survey = models.ForeignKey(Survey, null=False, blank=False, primary_key=True) + tot1 = models.IntegerField(null=False, default=0) + tot2 = models.IntegerField(null=False, default=0) + tot3 = models.IntegerField(null=False, default=0) + tot4 = models.IntegerField(null=False, default=0) + tot5 = models.IntegerField(null=False, default=0) + tot6 = models.IntegerField(null=False, default=0) + tot7 = models.IntegerField(null=False, default=0) + tot8 = models.IntegerField(null=False, default=0) + +class SurveyLock(models.Model): + ipaddr = models.IPAddressField(null=False, blank=False) + time = models.DateTimeField(null=False, default=datetime.now) + diff --git a/pgweb/survey/views.py b/pgweb/survey/views.py new file mode 100644 index 00000000..a59b6c85 --- /dev/null +++ b/pgweb/survey/views.py @@ -0,0 +1,56 @@ +from django.shortcuts import render_to_response, get_object_or_404 +from django.http import HttpResponse, Http404, HttpResponseServerError, HttpResponseRedirect +from django.db import connection + +from pgweb.util.contexts import NavContext + +from models import Survey, SurveyAnswer, SurveyLock + +def results(request, surveyid, junk=None): + survey = get_object_or_404(Survey, pk=surveyid) + surveylist = Survey.objects.all() + + return render_to_response('survey/results.html', { + 'survey': survey, + 'surveylist': surveylist, + }, NavContext(request, 'community')) + +def vote(request, surveyid): + # Check that we have a valid answer number + try: + ansnum = int(request.POST['answer']) + if ansnum < 1 or ansnum > 8: + return HttpResponseServerError("Invalid answer") + except: + return HttpResponseServerError("Unable to determine answer") + attrname = "tot%s" % ansnum + + # Do IP based locking... + try: + addr = request.META.get('REMOTE_ADDR') + if addr == None or addr == "": + raise Exception() + except: + return HttpResponseServerError("Unable to determine client IP address") + + # Clean out any old junk + curs = connection.cursor() + curs.execute("DELETE FROM survey_surveylock WHERE (\"time\" + '15 minutes') < now()") + + # Check if we are locked + lock = SurveyLock.objects.filter(ipaddr=addr) + if len(lock) > 0: + return HttpResponseServerError("Too many requests from your IP in the past 15 minutes") + + # Generate a new lock item, and store it + lock = SurveyLock(ipaddr=addr) + lock.save() + + # Only now do we bother actually finding out if the survey exists... + surv = get_object_or_404(Survey, pk=surveyid) + answers = SurveyAnswer.objects.get_or_create(survey=surv)[0] + setattr(answers, attrname, getattr(answers, attrname)+1) + answers.save() + + return HttpResponseRedirect("/community/survey/%s/" % ansnum) + diff --git a/pgweb/urls.py b/pgweb/urls.py index 4c4d7653..e0b630cd 100644 --- a/pgweb/urls.py +++ b/pgweb/urls.py @@ -33,9 +33,12 @@ urlpatterns = patterns('', (r'^download/products/(\d+)(-.*)?/$', 'downloads.views.productlist'), (r'^docs/(current|\d\.\d)/(static|interactive)/(.*).html$', 'docs.views.docpage'), - + + (r'^community/$', 'core.views.community'), (r'^community/contributors/$', 'contributors.views.completelist'), (r'^community/lists/$', 'lists.views.root'), + (r'^community/survey/vote/(\d+)/$', 'survey.views.vote'), + (r'^community/survey[/\.](\d+)(-.*)?/$', 'survey.views.results'), (r'^support/professional_(support|hosting)/$', 'profserv.views.root'), (r'^support/professional_(support|hosting)[/_](.*)/$', 'profserv.views.region'), diff --git a/templates/pages/community.html b/templates/core/community.html similarity index 73% rename from templates/pages/community.html rename to templates/core/community.html index ed9aa946..39963ad5 100644 --- a/templates/pages/community.html +++ b/templates/core/community.html @@ -11,30 +11,29 @@ - +{%if survey%}
- +{%endif%} +FIXME
The current results of our {{survey}} survey are:
+ +Answer | +Responses | +Percentage | +
---|---|---|
{{a.option}} | +{{a.votes}} | +{{a.votespercent}}% | +
Total | +{{survey.totalvotes}} | ++ |