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 quotes.models import Quote
|
||||||
from models import Version
|
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
|
# models and forms needed for core objects
|
||||||
from models import Organisation
|
from models import Organisation
|
||||||
from forms import OrganisationForm
|
from forms import OrganisationForm
|
||||||
@ -32,6 +35,16 @@ def home(request):
|
|||||||
'versions': versions,
|
'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
|
# Generic fallback view for static pages
|
||||||
def fallback(request, url):
|
def fallback(request, url):
|
||||||
|
@ -102,6 +102,7 @@ INSTALLED_APPS = [
|
|||||||
'pgweb.profserv',
|
'pgweb.profserv',
|
||||||
'pgweb.lists',
|
'pgweb.lists',
|
||||||
'pgweb.sponsors',
|
'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'^download/products/(\d+)(-.*)?/$', 'downloads.views.productlist'),
|
||||||
|
|
||||||
(r'^docs/(current|\d\.\d)/(static|interactive)/(.*).html$', 'docs.views.docpage'),
|
(r'^docs/(current|\d\.\d)/(static|interactive)/(.*).html$', 'docs.views.docpage'),
|
||||||
|
|
||||||
|
(r'^community/$', 'core.views.community'),
|
||||||
(r'^community/contributors/$', 'contributors.views.completelist'),
|
(r'^community/contributors/$', 'contributors.views.completelist'),
|
||||||
(r'^community/lists/$', 'lists.views.root'),
|
(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.root'),
|
||||||
(r'^support/professional_(support|hosting)[/_](.*)/$', 'profserv.views.region'),
|
(r'^support/professional_(support|hosting)[/_](.*)/$', 'profserv.views.region'),
|
||||||
|
@ -11,30 +11,29 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- BEGIN survey_block -->
|
{%if survey%}
|
||||||
<div id="pgSurveyWrap">
|
<div id="pgSurveyWrap">
|
||||||
<div id="pgSurvey">
|
<div id="pgSurvey">
|
||||||
<dl>
|
<dl>
|
||||||
<dt>User Survey</dt>
|
<dt>User Survey</dt>
|
||||||
<dd>{survey_question}</dd>
|
<dd>{{survey.question}}</dd>
|
||||||
<dd>
|
<dd>
|
||||||
<form method="post" action="{master_server}/vote">
|
<form method="post" action="/community/survey/vote/{{survey.id}}/">
|
||||||
<p>
|
<p>
|
||||||
<input type="hidden" name="action" value="vote"/>
|
{%for q in survey.questions%}
|
||||||
<input type="hidden" name="surveyid" value="{survey_id}"/>
|
<input type="radio" name="answer" value="{{q.value}}" id="surv_opt_{{q.value}}" /><label for="surv_opt_{{q.value}}">{{q.text}}</label><br />
|
||||||
<!-- BEGIN survey_option_loop -->
|
{%endfor%}
|
||||||
<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 -->
|
|
||||||
</p>
|
</p>
|
||||||
<p><input id="surveySubmit" type="submit" name="submit" value="Vote" /></p>
|
<p><input id="surveySubmit" type="submit" name="submit" value="Vote" /></p>
|
||||||
</form>
|
</form>
|
||||||
</dd>
|
</dd>
|
||||||
<dd><a href="/community/survey.{survey_id}">Results</a></dd>
|
<dd><a href="/community/survey/{{survey.id}}-{{survey|slugify}}/">Results</a></dd>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- END survey_block -->
|
{%endif%}
|
||||||
|
|
||||||
|
<p>FIXME</p>
|
||||||
<!-- BEGIN planet_block -->
|
<!-- BEGIN planet_block -->
|
||||||
<div id="pgPlanetWrap">
|
<div id="pgPlanetWrap">
|
||||||
<div id="pgPlanet">
|
<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