implemented UI part of Condorcet voting
This commit is contained in:
parent
1a48b80734
commit
20fcc6c9a2
2 changed files with 96 additions and 17 deletions
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in a new issue