From a485a36897d0e5463aa7f1b0089633da95e20521 Mon Sep 17 00:00:00 2001 From: iou1name Date: Mon, 3 Sep 2018 17:59:41 -0400 Subject: [PATCH] poll voting works --- quest/consumers.py | 8 ++++ quest/events.py | 52 +++++++++++++++++++-- quest/jinja2/quest/quest.html | 12 ++--- quest/migrations/0011_auto_20180902_2007.py | 20 ++++++++ quest/models.py | 5 +- quest/static/quest.js | 44 +++++++++++++---- quest/static/questQM.js | 4 +- quest/views.py | 1 + 8 files changed, 122 insertions(+), 24 deletions(-) create mode 100644 quest/migrations/0011_auto_20180902_2007.py diff --git a/quest/consumers.py b/quest/consumers.py index cd8799c..926b8ad 100644 --- a/quest/consumers.py +++ b/quest/consumers.py @@ -65,6 +65,14 @@ class QuestConsumer(WebsocketConsumer): 'message': data } ) + + def self_send(self, event, data={}): + """ + Like `send`, except only sends events to the paired websocket + instead of the entire group. + """ + data = json.dumps({'event': event, 'data': data}) + self.dispatch_send({'message': data}) def dispatch_send(self, event): """ diff --git a/quest/events.py b/quest/events.py index c6212f6..d5ef839 100644 --- a/quest/events.py +++ b/quest/events.py @@ -4,6 +4,7 @@ Individual functions for handling WebSocket events. Gets called by the QuestConsumer object in consumers.py. """ # TODO: quest owner only events +# TODO: try/except on database calls import re import time import types @@ -43,9 +44,9 @@ def message(socket, data): for quote in quotes: msg_id = quote.replace(">>", "") msg = '' + msg += 'href="javascript:scroll_to_msg(\'' + msg_id + '\')" ' + msg += 'onmousemove="show_preview(event, \'' + msg_id + '\')" ' + msg += 'onmouseout="clear_preview()">' + quote + '' message = message.replace(quote, msg) # handle image @@ -255,7 +256,7 @@ def poll_post(socket, data): data['post_id'] = p.id data['post_text'] = "Poll" data['date'] = localtime(p.timestamp).strftime('%Y-%m-%d %H:%M') - data['options'] = [o.text for o in options] + data['options'] = [(o.id, o.text) for o in options] socket.send('new_post', data) @@ -305,6 +306,49 @@ def open_post(socket, data): socket.send('open_post', data) +def vote(socket, data): + """ + Called when a player votes in a poll. + """ + quest_id = data.get('quest_id') + post_id = data.get('post_id') + option_id = data.get('option_id') + polarity = data.get('polarity') + ip_address = socket.scope['client'][0] + user = socket.scope['user'] + + if polarity == False: + v = PollVote.objects.get(ip_address=ip_address, option__id=option_id) + v.delete() + else: + p = Poll.objects.get(post_id=post_id) + if p.multi_choice == False: + votes = PollVote.objects.filter( + ip_address=ip_address, + option__poll=p + ) + for vote in votes: + vote.delete() + + data = {} + data['option_id'] = vote.option.id + data['polarity'] = False + socket.send('vote', data) + socket.self_send('set_option_box', data) + v = PollVote( + option=PollOption.objects.get(id=option_id), + ip_address=ip_address + ) + if user.username: + v.user = user + v.save() + + data = {} + data['option_id'] = option_id + data['polarity'] = polarity + socket.send('vote', data) + + events = {} for obj in dir(): if type(locals()[obj]) == types.FunctionType: diff --git a/quest/jinja2/quest/quest.html b/quest/jinja2/quest/quest.html index 90d2df8..bd93db4 100644 --- a/quest/jinja2/quest/quest.html +++ b/quest/jinja2/quest/quest.html @@ -68,14 +68,14 @@ {% elif post.post_type == "poll" %}

{{ post.post_text }} - {% if post.poll.open %}Open{% else %}Closed{% endif %}

- {% for option in poll_options.filter(poll=post.poll) %} - + {% for option in poll_options.filter(poll=post.poll).order_by("id") %} + - + {% endfor %}
- + {{ option.text }}{# poll_votes.get(option[0], [])|length #}{{ poll_votes.filter(option=option).count() }}
@@ -130,8 +130,8 @@

- Allow multiple choices
- Allow user-created options
+ Allow multiple choices
+ Allow user-created options
diff --git a/quest/migrations/0011_auto_20180902_2007.py b/quest/migrations/0011_auto_20180902_2007.py new file mode 100644 index 0000000..f7de7ce --- /dev/null +++ b/quest/migrations/0011_auto_20180902_2007.py @@ -0,0 +1,20 @@ +# Generated by Django 2.1 on 2018-09-03 00:07 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('quest', '0010_auto_20180829_1308'), + ] + + operations = [ + migrations.AlterField( + model_name='pollvote', + name='user', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/quest/models.py b/quest/models.py index bc0e15b..e8196ac 100644 --- a/quest/models.py +++ b/quest/models.py @@ -121,8 +121,9 @@ class PollVote(models.Model): option = models.ForeignKey(PollOption, on_delete=models.CASCADE) user = models.ForeignKey( settings.AUTH_USER_MODEL, - on_delete=models.CASCADE - ) + on_delete=models.CASCADE, + blank=True, + null=True) ip_address = models.GenericIPAddressField() diff --git a/quest/static/quest.js b/quest/static/quest.js index b1f9d6e..d17bea4 100644 --- a/quest/static/quest.js +++ b/quest/static/quest.js @@ -42,7 +42,6 @@ socket.events['message'] = function(data) { } } socket.events['new_post'] = function(data) { - //deactivate_post(); let qposts = document.getElementById('questPosts'); let post_str = '
'; - post_str += ''; - for (i = 0; i < data.options.length; i++) { - post_str += ''; - post_str += ''; + for (let i = 0; i < data.options.length; i++) { + post_str += ''; + post_str += ''; post_str += ''; post_str += '' + data.options[i][1] + ''; post_str += '0'; @@ -105,7 +103,7 @@ socket.events['update_post'] = function(data) { let row = post.children[1].insertRow(-1); let cell = row.insertCell(0); cell.className = 'pollCheckBox'; - cell.innerHTML = ''; + cell.innerHTML = ''; cell.innerHTML += ''; cell = row.insertCell(1); @@ -117,6 +115,31 @@ socket.events['update_post'] = function(data) { cell.innerHTML = "0"; } } +socket.events['vote'] = function(data) { + let row = document.getElementById('optionRow-' + data.option_id); + if (data.polarity) { + row.cells[2].textContent = Number(row.cells[2].textContent) + 1; + } else { + row.cells[2].textContent = Number(row.cells[2].textContent) - 1; + } + let table = row.parentElement.parentElement; + arr = Array.prototype.slice.call(table.rows); + arr.sort(sort_by_votes); + let new_tbody = document.createElement('tbody'); + for (let i = 0; i < arr.length; i++) { + new_tbody.appendChild(arr[i]); + } + table.replaceChild(new_tbody, table.children[0]); +} +socket.events['set_option_box'] = function(data) { + document.getElementById('pollInput-' + data.option_id).checked = data.polarity; +} + +/* Websocket send */ +function vote(post_id, option_id) { + let polarity = document.getElementById('pollInput-' + option_id).checked; + socket.send('vote', {post_id: post_id, option_id: option_id, polarity: polarity, quest_id: quest_id}); +} /* Helpers */ function padToTwo(number) { @@ -129,6 +152,9 @@ function strftime(date) { date_str += padToTwo(date.getHours()) + ':' + padToTwo(date.getMinutes()) + ':' + padToTwo(date.getSeconds()); return date_str; } +function sort_by_votes(a, b) { + return b.cells[2].textContent.localeCompare(a.cells[2].textContent); +} /* DOM editing */ function quote(message_id) { @@ -136,7 +162,7 @@ function quote(message_id) { textbox.value += '>>' + message_id + '\n'; textbox.focus(); } -function showPreview(event, message_id) { +function show_preview(event, message_id) { let elem = document.getElementById('msg-' + message_id); if (!elem) { return; } let preview = document.getElementById('preview'); @@ -149,11 +175,11 @@ function showPreview(event, message_id) { preview.style.left = x; preview.style.maxWidth = maxWidth; } -function clearPreview() { +function clear_preview() { document.getElementById('preview').innerHTML = ''; document.getElementById('preview').style.display = 'none'; } -function scrollToMsg(message_id) { +function scroll_to_msg(message_id) { let elem = document.getElementById('msg-' + message_id); if (!elem) { return; } elem.scrollIntoView(); diff --git a/quest/static/questQM.js b/quest/static/questQM.js index b2a4698..c65d1a7 100644 --- a/quest/static/questQM.js +++ b/quest/static/questQM.js @@ -1,6 +1,5 @@ /* Websocket receive */ socket.events['new_post'] = function(data) { - //deactivate_post(); let qposts = document.getElementById('questPosts'); let post_str = '
'; - post_str += ''; for (i = 0; i < data.options.length; i++) { post_str += ''; - post_str += ''; + post_str += ''; post_str += ''; post_str += '' + data.options[i][1] + ''; post_str += '0'; diff --git a/quest/views.py b/quest/views.py index fb58656..509fb01 100644 --- a/quest/views.py +++ b/quest/views.py @@ -25,5 +25,6 @@ def quest(request, quest_id, page_num=1): dice_rolls = DiceRoll.objects.filter(dicecall__post__quest__id=quest_id) poll_options = PollOption.objects.filter(poll__post__quest__id=quest_id) poll_votes = PollVote.objects.filter(option__poll__post__quest__id=quest_id) + ip_address = request.META['REMOTE_ADDR'] context = locals() return render(request, 'quest/quest.html', context)