mirror of
https://github.com/postgres/pgweb.git
synced 2025-08-13 13:12:42 +00:00
Add support for surveys
This commit is contained in:
@ -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):
|
||||
|
@ -102,6 +102,7 @@ INSTALLED_APPS = [
|
||||
'pgweb.profserv',
|
||||
'pgweb.lists',
|
||||
'pgweb.sponsors',
|
||||
'pgweb.survey',
|
||||
]
|
||||
|
||||
|
||||
|
0
pgweb/survey/__init__.py
Normal file
0
pgweb/survey/__init__.py
Normal file
15
pgweb/survey/admin.py
Normal file
15
pgweb/survey/admin.py
Normal file
@ -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)
|
||||
|
95
pgweb/survey/models.py
Normal file
95
pgweb/survey/models.py
Normal file
@ -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)
|
||||
|
56
pgweb/survey/views.py
Normal file
56
pgweb/survey/views.py
Normal file
@ -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)
|
||||
|
@ -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'),
|
||||
|
@ -11,30 +11,29 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- BEGIN survey_block -->
|
||||
{%if survey%}
|
||||
<div id="pgSurveyWrap">
|
||||
<div id="pgSurvey">
|
||||
<dl>
|
||||
<dt>User Survey</dt>
|
||||
<dd>{survey_question}</dd>
|
||||
<dd>{{survey.question}}</dd>
|
||||
<dd>
|
||||
<form method="post" action="{master_server}/vote">
|
||||
<form method="post" action="/community/survey/vote/{{survey.id}}/">
|
||||
<p>
|
||||
<input type="hidden" name="action" value="vote"/>
|
||||
<input type="hidden" name="surveyid" value="{survey_id}"/>
|
||||
<!-- BEGIN survey_option_loop -->
|
||||
<input type="radio" name="answer" value="{option_num}" id="surv_opt_{option_num}" /><label for="surv_opt_{option_num}">{option_text}</label><br />
|
||||
<!-- END survey_option_loop -->
|
||||
{%for q in survey.questions%}
|
||||
<input type="radio" name="answer" value="{{q.value}}" id="surv_opt_{{q.value}}" /><label for="surv_opt_{{q.value}}">{{q.text}}</label><br />
|
||||
{%endfor%}
|
||||
</p>
|
||||
<p><input id="surveySubmit" type="submit" name="submit" value="Vote" /></p>
|
||||
</form>
|
||||
</dd>
|
||||
<dd><a href="/community/survey.{survey_id}">Results</a></dd>
|
||||
<dd><a href="/community/survey/{{survey.id}}-{{survey|slugify}}/">Results</a></dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END survey_block -->
|
||||
{%endif%}
|
||||
|
||||
<p>FIXME</p>
|
||||
<!-- BEGIN planet_block -->
|
||||
<div id="pgPlanetWrap">
|
||||
<div id="pgPlanet">
|
34
templates/survey/results.html
Normal file
34
templates/survey/results.html
Normal file
@ -0,0 +1,34 @@
|
||||
{%extends "base/page.html"%}
|
||||
{%block title%}Survey Results: {{survey}}{%endblock%}
|
||||
{%block contents%}
|
||||
<h1>Survey Results</h1>
|
||||
<p>The current results of our <b>{{survey}}</b> survey are:</p>
|
||||
|
||||
<div class="tblBasic">
|
||||
<table border="0" cellpadding="0" cellspacing="0" class="tblBasicGrey">
|
||||
<tr>
|
||||
<th class="colFirst">Answer</th>
|
||||
<th class="colMid">Responses</th>
|
||||
<th class="colLast">Percentage</th>
|
||||
</tr>
|
||||
{%for a in survey.completeanswers%}
|
||||
<tr>
|
||||
<td class="colFirst">{{a.option}}</td>
|
||||
<td class="colMid">{{a.votes}}</td>
|
||||
<td class="colLast">{{a.votespercent}}%</td>
|
||||
</tr>
|
||||
{%endfor%}
|
||||
<tr class="lastrow">
|
||||
<td class="colFirst"><b>Total</b></td>
|
||||
<td class="colMid">{{survey.totalvotes}}</td>
|
||||
<td class="colLast"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h2>Other Surveys</h2>
|
||||
<ul>
|
||||
{%for s in surveylist%}
|
||||
<li><a href="/community/survey/{{s.id}}-{{s.question|slugify}}/">{{s.question}}</a></li>
|
||||
{%endfor%}
|
||||
</ul>
|
||||
{%endblock%}
|
Reference in New Issue
Block a user