implemented UI part of Condorcet voting

This commit is contained in:
lars 2012-11-22 12:04:39 +00:00
parent 1a48b80734
commit 20fcc6c9a2
2 changed files with 96 additions and 17 deletions

View file

@ -17,6 +17,10 @@ class SubmitForm(formencode.Schema):
submitter = formencode.validators.UnicodeString(strip=True, not_empty=True) submitter = formencode.validators.UnicodeString(strip=True, not_empty=True)
content = formencode.validators.UnicodeString(strip=True, not_empty=True) content = formencode.validators.UnicodeString(strip=True, not_empty=True)
class VoteSubmissionOrderForm(formencode.Schema):
submitter = formencode.validators.UnicodeString(strip=True, not_empty=True)
vote_order = formencode.validators.UnicodeString(strip=True)
class ProfileForm(formencode.Schema): class ProfileForm(formencode.Schema):
email = formencode.validators.Email(resolve_domain=True, strip=True, not_empty=True) email = formencode.validators.Email(resolve_domain=True, strip=True, not_empty=True)

View file

@ -104,6 +104,12 @@ class ContentSubmission(sqlobject.SQLObject):
return hashlib.md5(str(self.id)).hexdigest() return hashlib.md5(str(self.id)).hexdigest()
class VoteOrder(sqlobject.SQLObject):
submitter = sqlobject.UnicodeCol()
content_submission_id = sqlobject.ForeignKey("ContentSubmission")
priority = sqlobject.IntCol()
class Profile(sqlobject.SQLObject): class Profile(sqlobject.SQLObject):
email = sqlobject.UnicodeCol(unique=True) email = sqlobject.UnicodeCol(unique=True)
hash_key = sqlobject.StringCol(unique=True) hash_key = sqlobject.StringCol(unique=True)
@ -198,6 +204,14 @@ class Poll(sqlobject.SQLObject):
if (key == 'public') and (value > 0): if (key == 'public') and (value > 0):
self.announce_via_twitter() self.announce_via_twitter()
def get_ordered_submissions(self, submitter):
votes = []
for submission in ContentSubmission.selectBy(poll_id=self.id):
votes.extend(list(VoteOrder.selectBy(content_submission_id=submission,
submitter=submitter)))
votes.sort(key=lambda item: item.priority)
return [order.content_submission_id for order in votes]
def announce_via_twitter(self): def announce_via_twitter(self):
complete_url = self.get_url(absolute=True) complete_url = self.get_url(absolute=True)
try: try:
@ -215,6 +229,14 @@ class Poll(sqlobject.SQLObject):
return return
publish_twitter_alert(title, twitter_key, twitter_secret, twitter_access_key, twitter_access_secret) publish_twitter_alert(title, twitter_key, twitter_secret, twitter_access_key, twitter_access_secret)
def get_num_of_votes(self):
submitters = []
for submission in ContentSubmission.selectBy(poll_id=self.id):
for vote in VoteOrder.selectBy(content_submission_id=submission):
if not vote.submitter in submitters:
submitters.append(vote.submitter)
return len(submitters)
def get_num_of_submitters(self): def get_num_of_submitters(self):
all_submitters = [submission.submitter for submission in ContentSubmission.selectBy(poll_id=self.id)] all_submitters = [submission.submitter for submission in ContentSubmission.selectBy(poll_id=self.id)]
unique_submitters = [] unique_submitters = []
@ -245,8 +267,7 @@ class Poll(sqlobject.SQLObject):
return get_url_string("%s%s/submit" % (BASE_DICT["base_url"], self.hash_key), absolute) return get_url_string("%s%s/submit" % (BASE_DICT["base_url"], self.hash_key), absolute)
def get_vote_url(self, absolute=False): def get_vote_url(self, absolute=False):
#TODO vote: Diese Funktion ist noch falsch, aber erstmal als Platzhalter noetig return get_url_string("%s%s/vote" % (BASE_DICT["base_url"], self.hash_key), absolute)
return ""
def get_admin_url(self, absolute=False, suffix=""): def get_admin_url(self, absolute=False, suffix=""):
return get_url_string("%s%s%s" % (BASE_DICT["base_url"], self.admin_hash_key, suffix), absolute) return get_url_string("%s%s%s" % (BASE_DICT["base_url"], self.admin_hash_key, suffix), absolute)
@ -266,6 +287,9 @@ class Poll(sqlobject.SQLObject):
def is_vote_enabled(self): def is_vote_enabled(self):
return self.get_settings()["vote_enabled"] return self.get_settings()["vote_enabled"]
def is_vote_finished(self, submitter):
return len(self.get_ordered_submissions(submitter)) > 0
def get_submissions_visibility(self): def get_submissions_visibility(self):
settings = self.get_settings() settings = self.get_settings()
return bool(settings["show_all_submissions"] or (settings["expose_date"] and \ return bool(settings["show_all_submissions"] or (settings["expose_date"] and \
@ -553,15 +577,24 @@ def publish_twitter_alert(text, key, secret,access_key,access_secret):
except urllib2.URLError, e: except urllib2.URLError, e:
print e.reason print e.reason
def check_spam_submitter_name(name): def is_spam_submitter_name(name, errors_dict):
lower_text = re.sub("[^a-z]", "", name) lower_text = re.sub("[^a-z]", "", name)
upper_text = re.sub("[^A-Z]", "", name) upper_text = re.sub("[^A-Z]", "", name)
return (len(lower_text) + len(upper_text) == len(name)) and \ if (len(lower_text) + len(upper_text) == len(name)) and \
(len(lower_text) > 3) and (len(upper_text) > 3) and \ (len(lower_text) > 3) and (len(upper_text) > 3) and \
(len(name) >= 8) and (not name.startswith(upper_text)) (len(name) >= 8) and (not name.startswith(upper_text)):
errors_dict["submitter"] = "Spam-Verdacht: bitte den Namen korrigieren"
return False
else:
return True
def check_spam_content(text): def is_spam_content(text, errors_dict):
return bool(re.search(r"(<a\s|\shref=|</a>)", text.lower())) if re.search(r"(<a\s|\shref=|</a>)", text.lower()):
errors_dict["content"] = \
"Spam-Verdacht: Inhalt darf keine HTML-Tags enthalten"
return True
else:
return False
@bobo.query('/profile/logout') @bobo.query('/profile/logout')
def user_logout(bobo_request): def user_logout(bobo_request):
@ -738,20 +771,61 @@ def new_poll(bobo_request, submit=None, cancel=None, author=None, title=None,
new_poll.change_setting(key, value) new_poll.change_setting(key, value)
return bobo.redirect(new_poll.get_admin_url()) return bobo.redirect(new_poll.get_admin_url())
@bobo.query('/:hash_key/vote')
def vote_submission_order(bobo_request, hash_key=None, submitter=None,
vote_order=None):
value_dict = get_default_values(bobo_request)
value_dict["errors"] = {}
data = {}
if submitter and is_spam_submitter_name(submitter, value_dict["errors"]):
data["submitter"] = submitter.strip()
if vote_order:
data["vote_order"] = vote_order
else:
data["vote_order"] = ""
poll_id = get_poll_id(hash_key)
if not poll_id is None:
poll = Poll.get(poll_id)
value_dict["poll"] = poll
try:
data = forms.VoteSubmissionOrderForm.to_python(data)
except formencode.Invalid, errors_packed:
# merge errors with previous ones - but never overwrite existing ones
errors = errors_packed.unpack_errors()
errors.update(value_dict["errors"])
value_dict["errors"] = errors
if value_dict["errors"] or poll.is_closed() or \
not poll.is_vote_enabled():
# ignore silently
pass
elif poll.is_vote_finished(submitter):
# there is already a vote stored for that submitter name
value_dict["errors"]["submit"] = \
"Fehler: deine Wahl wurde bereits gespeichert."
else:
# store this order of items
digest_dict = {}
for submission in ContentSubmission.selectBy(poll_id=poll.id):
digest_dict[submission.get_obfuscated_digest()] = submission
vote_order = vote_order.split()
for index, digest in enumerate(vote_order):
if digest in digest_dict:
VoteOrder(submitter=submitter, priority=index,
content_submission_id=digest_dict[digest])
#value_dict["errors"]["submit"] = str(poll.get_ordered_submissions(submitter))
value_dict["submitter"] = submitter
return render("poll_details.html", input_data=data, **value_dict)
else:
return bobo.redirect(BASE_DICT["base_url"])
@bobo.query('/:hash_key/submit') @bobo.query('/:hash_key/submit')
def submit_content(bobo_request, hash_key=None, submitter=None, content=None): def submit_content(bobo_request, hash_key=None, submitter=None, content=None):
value_dict = get_default_values(bobo_request) value_dict = get_default_values(bobo_request)
value_dict["errors"] = {} value_dict["errors"] = {}
data = {} data = {}
if content and check_spam_content(content): if content and is_spam_content(content, value_dict["errors"]):
value_dict["errors"]["content"] = \
"Spam-Verdacht: Inhalt darf keine HTML-Tags enthalten"
else:
data["content"] = content data["content"] = content
if submitter and check_spam_submitter_name(submitter): if submitter and is_spam_submitter_name(submitter, value_dict["errors"]):
value_dict["errors"]["submitter"] = \
"Spam-Verdacht: bitte den Namen korrigieren"
else:
data["submitter"] = submitter data["submitter"] = submitter
poll_id = get_poll_id(hash_key) poll_id = get_poll_id(hash_key)
if not poll_id is None: if not poll_id is None:
@ -774,7 +848,8 @@ def submit_content(bobo_request, hash_key=None, submitter=None, content=None):
# remove "content" for the next input # remove "content" for the next input
del data["content"] del data["content"]
return render("poll_details.html", input_data=data, **value_dict) return render("poll_details.html", input_data=data, **value_dict)
return bobo.redirect(BASE_DICT["base_url"]) else:
return bobo.redirect(BASE_DICT["base_url"])
@bobo.query('/:admin_hash_key/delete') @bobo.query('/:admin_hash_key/delete')
def delete_poll(bobo_request, admin_hash_key=None): def delete_poll(bobo_request, admin_hash_key=None):
@ -1149,7 +1224,7 @@ def static_files(p1=None, p2=None, p3=None):
return get_static_file(pathname) return get_static_file(pathname)
for table in (Poll, ContentSubmission, PollSetting, PollRelation, Profile, ProfilePolls): for table in (Poll, ContentSubmission, VoteOrder, PollSetting, PollRelation, Profile, ProfilePolls):
#table.dropTable() #table.dropTable()
if not table.tableExists(): if not table.tableExists():
table.createTable() table.createTable()