diff --git a/quest/events.py b/quest/events.py index 3d101e2..bc84ebd 100644 --- a/quest/events.py +++ b/quest/events.py @@ -3,6 +3,7 @@ Individual functions for handling WebSocket events. Gets called by the QuestConsumer object in consumers.py. """ +# TODO: quest owner only events import re import time import types @@ -11,7 +12,7 @@ import random import bleach from django.utils.timezone import localtime -from quest.models import Message, Quest, Post, DiceCall +from quest.models import Message, Quest, Post, DiceCall, DiceRoll from quest.forms import DiceCallForm def message(socket, data): @@ -62,16 +63,17 @@ def message(socket, data): assert -1000 < dice_mod < 1000 except (ValueError, AssertionError): return - dice = [random.randint(1, dice_sides) for _ in range(dice_num)] - total = sum(dice) + dice_mod - roll_msg = f"Rolled {', '.join(map(str, dice))}" + dice_results = [random.randint(1, dice_sides) for _ in range(dice_num)] + total = sum(dice_results) + dice_mod + roll_msg = f"Rolled {', '.join(map(str, dice_results))}" if dice_mod: if dice_mod > 0: roll_msg += " + " + str(dice_mod) else: roll_msg += " - " + str(dice_mod)[1:] roll_msg += " = " + str(total) - message += '
' + roll_msg + "" + message += '
' + roll_msg + "" + user = socket.scope['user'] @@ -89,6 +91,46 @@ def message(socket, data): data['name'] = user.username socket.send('message', data) + # append rolls to dicecall + if any(map(message.startswith, ["/dice", "/roll"])): + try: + dc = DiceCall.objects.get(post__quest__id=quest_id, open=True) + except DiceCall.DoesNotExist: + return + dice_roll = f"{dice_num}d{dice_sides}" + if dice_mod: + if dice_mod > 0: + dice_roll += "+" + dice_roll += str(dice_mod) + + if dc.strict and dc.dice_roll != dice_roll: + return + + dr = DiceRoll( + dicecall=dc, + message=m, + roll=dice_roll, + results=re.search(r"Rolled (.+) =", roll_msg).group(1), + total=total, + ) + dr.save() + + if DiceRoll.objects.filter(dicecall=dc).count() == dc.rolls_taken: + dc.open = False + dc.save() + socket.send('close_post', {'post_id': dc.post_id}) + + data = {} + data['post_text'] = roll_msg + " (" + dice_roll + ")" + if dc.dice_challenge: + if total >= dc.dice_challenge: + data["post_text"] += " - Pass" + else: + data["post_text"] += " - Fail" + data['post_type'] = 'dice' + data['post_id'] = dc.post_id + socket.send('update_post', data) + def text_post(socket, data): """ @@ -131,6 +173,13 @@ def dice_post(socket, data): return # error message? form = form.cleaned_data + posts = Post.objects.filter( + quest__id=quest_id, post_type='dice', dicecall__open=True) + for post in posts: + post.dicecall.open = False + post.dicecall.save() + socket.send('close_all_posts') + dice_roll = str(form['diceNum']) + "d" dice_roll += str(form['diceSides']) if form['diceMod']: @@ -149,7 +198,7 @@ def dice_post(socket, data): post_text=post_text ) p.save() - d = DiceCall( + dc = DiceCall( post=p, dice_roll=dice_roll, strict=form['diceStrict'], @@ -157,9 +206,7 @@ def dice_post(socket, data): rolls_taken=form['diceRollsTaken'], open=True, ) - d.save() - - socket.send('close_all_posts') + dc.save() data = {} data['post_text'] = post_text @@ -174,6 +221,7 @@ def close_post(socket, data): Called when the QM closes an open post. """ post_id = data.get('post_id') + quest_id = data.get('quest_id') p = Post.objects.get(id=post_id) if data.get('post_type') == 'dice': p.dicecall.open = False @@ -193,9 +241,11 @@ def open_post(socket, data): """ # TODO: only posts on last page can be opened post_id = data.get('post_id') + quest_id = data.get('quest_id') p = Post.objects.get(id=post_id) if data.get('post_type') == 'dice': - posts = Post.objects.filter(post_type='dice', dicecall__open=True) + posts = Post.objects.filter( + quest__id=quest_id, post_type='dice', dicecall__open=True) for post in posts: post.dicecall.open = False post.dicecall.save() diff --git a/quest/jinja2/quest/quest.html b/quest/jinja2/quest/quest.html index c5c4f71..4983e3a 100644 --- a/quest/jinja2/quest/quest.html +++ b/quest/jinja2/quest/quest.html @@ -60,11 +60,11 @@ {% endautoescape %} {% elif post.post_type == "dice" %}

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

- {# for dice_roll in dice_rolls.get(post.id, []) %} -
- Rolled {{ dice_roll[4] }} = {{ dice_roll[5] }} ({{ dice_roll[3] }}){% if post.id|dice_chal != 0 %} - {% if dice_roll[5] >= post.id|dice_chal %}Pass{% else %}Fail{% endif %}{% endif %} + {% for dice_roll in dice_rolls.filter(dicecall=post.dicecall) %} +
+ Rolled {{ dice_roll.results }} = {{ dice_roll.total }} ({{ dice_roll.roll }}){% if post.dicecall.dice_challenge %} - {% if dice_roll.total >= post.dicecall.dice_challenge %}Pass{% else %}Fail{% endif %}{% endif %}
- {% endfor #} + {% endfor %} {% elif post.post_type == "poll" %}

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

diff --git a/quest/migrations/0009_diceroll.py b/quest/migrations/0009_diceroll.py new file mode 100644 index 0000000..56e44af --- /dev/null +++ b/quest/migrations/0009_diceroll.py @@ -0,0 +1,24 @@ +# Generated by Django 2.1 on 2018-08-25 01:06 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('quest', '0008_auto_20180824_0855'), + ] + + operations = [ + migrations.CreateModel( + name='DiceRoll', + fields=[ + ('message', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='quest.Message')), + ('roll', models.CharField(max_length=9)), + ('results', models.TextField()), + ('total', models.IntegerField()), + ('dicecall', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='quest.DiceCall')), + ], + ), + ] diff --git a/quest/models.py b/quest/models.py index 79d7629..610cd16 100644 --- a/quest/models.py +++ b/quest/models.py @@ -16,6 +16,20 @@ class Quest(models.Model): on_delete=models.CASCADE) +class Message(models.Model): + """ + Represents a chat message. + """ + quest = models.ForeignKey(Quest, on_delete=models.CASCADE) + user = models.ForeignKey( + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + blank=True, + null=True) + timestamp = models.DateTimeField(auto_now=True) + message = models.TextField(max_length=512) + + class Post(models.Model): """ An object for arbituary posts. Contains all post data, type, etc. @@ -62,6 +76,21 @@ class DiceCall(models.Model): open = models.BooleanField(default=False) +class DiceRoll(models.Model): + """ + Represents a dice roll made on a valid dicecall. + """ + dicecall = models.ForeignKey(DiceCall, on_delete=models.CASCADE) + message = models.OneToOneField( + Message, + on_delete=models.CASCADE, + primary_key=True, + ) + roll = models.CharField(max_length=9) + results = models.TextField() + total = models.IntegerField() + + class PageTitle(models.Model): """ Represents the title of a quest page. @@ -72,17 +101,3 @@ class PageTitle(models.Model): def __str__(self): return self.title - - -class Message(models.Model): - """ - Represents a chat message. - """ - quest = models.ForeignKey(Quest, on_delete=models.CASCADE) - user = models.ForeignKey( - settings.AUTH_USER_MODEL, - on_delete=models.CASCADE, - blank=True, - null=True) - timestamp = models.DateTimeField(auto_now=True) - message = models.TextField(max_length=512) diff --git a/quest/static/quest.js b/quest/static/quest.js index b7393f6..b1f9d6e 100644 --- a/quest/static/quest.js +++ b/quest/static/quest.js @@ -17,10 +17,10 @@ function load() { document.getElementById('chatWindow').scrollTop = document.getElementById('chatWindow').scrollHeight; let mtarea = document.getElementById('messageTextArea'); mtarea.addEventListener('keypress', function(event) { - if (event.key == 'Enter' && !event.shiftKey) { + if (event.key === 'Enter' && !event.shiftKey) { let text = mtarea.value.trim(); mtarea.value = ''; - if (text == '') { return; } + if (text === '') { return; } document.getElementById('chatWindow').scrollTop = document.getElementById('chatWindow').scrollHeight; socket.send('message', {message: text, quest_id: quest_id}); } @@ -45,20 +45,20 @@ socket.events['new_post'] = function(data) { //deactivate_post(); let qposts = document.getElementById('questPosts'); let post_str = '
'; - } else if (data.post_type == 'dice') { + } else if (data.post_type === 'dice') { post_str += 'dicePost active_post">'; - } else if (data.post_type == 'poll') { + } else if (data.post_type === 'poll') { post_str += 'pollPost active_post">'; } post_str += '
' + data.date; post_str += '
'; - if (data.post_type == 'text') { + if (data.post_type === 'text') { post_str += data.post_text; - } else if (data.post_type == 'dice') { + } else if (data.post_type === 'dice') { post_str += '

' + data.post_text + ' - Open

'; - } else if (data.post_type == 'poll') { + } else if (data.post_type === 'poll') { post_str += '

' + data.post_text + ' - Open

'; post_str += '
'; post_str += ''; @@ -87,14 +87,36 @@ socket.events['open_post'] = function(data) { } socket.events['close_all_posts'] = function(data) { let class_set = ''; - if (data.post_type == 'dice') { class_set = 'dicePost activePost'; } - else if (data.post_type == 'poll') { class_set = 'pollPost activePost'; } + if (data.post_type === 'dice') { class_set = 'dicePost activePost'; } + else if (data.post_type === 'poll') { class_set = 'pollPost activePost'; } else { class_set = 'activePost'; } let posts = document.getElementsByClassName(class_set); for (let i = 0; i < posts.length; i++) { close_post(posts[i].children[1].id.slice(14)); // retreive the id number at the end } } +socket.events['update_post'] = function(data) { + let post = document.getElementById('questPostData-' + data.post_id); + if (data.post_type === 'text') { + post.innerHTML = data.post_text; + } else if (data.post_type === 'dice') { + post.innerHTML += '' + data.post_text + '
'; + } else if (data.post_type === 'poll') { + let row = post.children[1].insertRow(-1); + let cell = row.insertCell(0); + cell.className = 'pollCheckBox'; + cell.innerHTML = ''; + cell.innerHTML += ''; + + cell = row.insertCell(1); + cell.className = 'option_text'; + cell.innerHTML = data.option_text; + + cell = row.insertCell(2); + cell.className = "optionVotes"; + cell.innerHTML = "0"; + } +} /* Helpers */ function padToTwo(number) { @@ -155,7 +177,6 @@ function open_post(post_id) { let post = document.getElementById('questPostData-' + post_id); post.firstElementChild.textContent = post.firstElementChild.textContent.replace('Closed', 'Open'); post.parentElement.className += ' activePost'; - console.log(post.parentElement.className); if (post.parentElement.classList.contains('pollPost')) { let table = document.getElementById('poll-' + post_id); let boxes = table.getElementsByTagName('input'); diff --git a/quest/static/questQM.js b/quest/static/questQM.js index dc2bd3d..3f0d7fc 100644 --- a/quest/static/questQM.js +++ b/quest/static/questQM.js @@ -3,29 +3,29 @@ socket.events['new_post'] = function(data) { //deactivate_post(); let qposts = document.getElementById('questPosts'); let post_str = '
'; - } else if (data.post_type == 'dice') { + } else if (data.post_type === 'dice') { post_str += 'dicePost active_post">'; - } else if (data.post_type == 'poll') { + } else if (data.post_type === 'poll') { post_str += 'pollPost active_post">'; } post_str += '
' + data.date; /* QM only */ - if (data.post_type == 'text') { + if (data.post_type === 'text') { post_str += '
Edit'; post_str += ''; - } else if (data.post_type == 'dice' || data.post_type == 'poll') { + } else if (data.post_type === 'dice' || data.post_type === 'poll') { post_str += '
Close'; post_str += '' } /* end QM only */ post_str += '
'; - if (data.post_type == 'text') { + if (data.post_type === 'text') { post_str += data.post_text; - } else if (data.post_type == 'dice') { + } else if (data.post_type === 'dice') { post_str += '

' + data.post_text + ' - Open

'; - } else if (data.post_type == 'poll') { + } else if (data.post_type === 'poll') { post_str += '

' + data.post_text + ' - Open

'; post_str += '
'; post_str += ''; @@ -52,7 +52,7 @@ function makePost() { let qparea = document.getElementById('postTextArea'); let text = qparea.value.trim(); qparea.value = ''; - if (text == '') { return; } + if (text === '') { return; } socket.send('text_post', {text: text, page_num: page_num, quest_id: quest_id}); } function form_post(form_id, event) { @@ -67,20 +67,22 @@ function form_post(form_id, event) { document.getElementById(form_id).reset(); } function close_post_send(post_id) { + data = {post_id: post_id, quest_id: quest_id} let post = document.getElementById('questPostData-' + post_id); if (post.parentElement.classList.contains('dicePost')) { - data = {post_type: 'dice', post_id: post_id}; + data.post_type = 'dice'; } else if (post.parentElement.classList.contains('pollPost')) { - data = {post_type: 'poll', post_id: post_id}; + data.post_type = 'poll'; } socket.send('close_post', data); } function open_post_send(post_id) { + data = {post_id: post_id, quest_id: quest_id} let post = document.getElementById('questPostData-' + post_id); if (post.parentElement.classList.contains('dicePost')) { - data = {post_type: 'dice', post_id: post_id}; + data.post_type = 'dice'; } else if (post.parentElement.classList.contains('pollPost')) { - data = {post_type: 'poll', post_id: post_id}; + data.post_type = 'poll'; } socket.send('open_post', data); } diff --git a/quest/views.py b/quest/views.py index ff26a65..2e825c6 100644 --- a/quest/views.py +++ b/quest/views.py @@ -5,7 +5,7 @@ Quest and quest accessory views. from django.shortcuts import render from django.http import HttpResponse -from .models import Quest +from .models import Quest, DiceRoll def index(request): """ @@ -22,5 +22,6 @@ def quest(request, quest_id, page_num=1): quest = Quest.objects.get(id=quest_id) messages = quest.message_set.all() posts = quest.post_set.all() - context = {'quest': quest, 'posts': posts, 'messages': messages, 'page_num': page_num} + dice_rolls = DiceRoll.objects.filter(dicecall__post__quest__id=quest_id) + context = locals() return render(request, 'quest/quest.html', context)