Skip to content

Commit

Permalink
messages: WIP poll widget.
Browse files Browse the repository at this point in the history
  • Loading branch information
rsashank committed Jul 15, 2024
1 parent 440a3fa commit 4ab806c
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 0 deletions.
67 changes: 67 additions & 0 deletions tests/ui_tools/test_messages.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from collections import OrderedDict, defaultdict
from datetime import date
from typing import Any, Dict, List

import pytest
import pytz
Expand Down Expand Up @@ -1429,6 +1430,72 @@ def test_keypress_EDIT_MESSAGE(
else:
report_error.assert_called_once_with([expect_footer_text[message_type]])

@pytest.mark.parametrize(
"submessages, expected_question, expected_voters_for_options",
[
(
[
{
"id": 10981,
"message_id": 1813721,
"sender_id": 27294,
"msg_type": "widget",
"content": '{"widget_type": "poll", "extra_data": {"question": "Can you view polls on Zulip Terminal?", "options": ["Yes", "No"]}}', # noqa: E501
},
{
"id": 10982,
"message_id": 1813721,
"sender_id": 27294,
"msg_type": "widget",
"content": '{"type":"vote","key":"canned,0","vote":1}',
},
],
"Can you view polls on Zulip Terminal?",
{"Yes": [27294], "No": []},
),
(
[
{
"id": 10983,
"message_id": 1813729,
"sender_id": 27294,
"msg_type": "widget",
"content": '{"widget_type": "poll", "extra_data": {"question": "", "options": ["Option 1", "Option 2"]}}', # noqa: E501
}
],
"",
{"Option 1": [27294], "Option 2": [27294]},
),
(
[
{
"id": 10996,
"message_id": 1813730,
"sender_id": 27294,
"msg_type": "widget",
"content": '{"widget_type": "poll", "extra_data": {"question": "How is the weather today?", "options": []}}', # noqa: E501
}
],
"How is the weather today?",
{},
),
],
ids=[
"poll_with_votes",
"poll_without_question",
"poll_without_options",
],
)
def test_process_poll_data(
self,
submessages: Any,
expected_question: str,
expected_voters_for_options: Dict[str, List[int]],
):
question, voters_for_options = MessageBox.process_poll_data(submessages)
assert question == expected_question
assert voters_for_options == expected_voters_for_options

@pytest.mark.parametrize(
"raw_html, expected_content",
[
Expand Down
70 changes: 70 additions & 0 deletions zulipterminal/ui_tools/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
UI to render a Zulip message for display, and respond contextually to actions
"""

import json
import typing
from collections import defaultdict
from datetime import date, datetime
Expand Down Expand Up @@ -729,6 +730,35 @@ def main_view(self) -> List[Any]:
"/me", f"<strong>{self.message['sender_full_name']}</strong>", 1
)

# Submessages handling (like polls, todo)
if self.message.get("submessages"):
try:
first_submessage_content = json.loads(
self.message["submessages"][0]["content"]
)
except (json.JSONDecodeError, TypeError):
first_submessage_content = {}

if (
"widget_type" in first_submessage_content
and first_submessage_content["widget_type"] == "poll"
):
question, votes_for_option = self.process_poll_data(
self.message["submessages"]
)

if question:
self.message[
"content"
] = f"<strong>Poll Question: {question}</strong>\n"
else:
self.message["content"] = "<strong>Add Poll Question</strong>\n"
for option, voters in votes_for_option.items():
voter_count = len(voters)
count_text = f"<strong>[{voter_count:^3}]</strong>"
option_text = f"{option}"
self.message["content"] += f"{count_text} {option_text}\n"

# Transform raw message content into markup (As needed by urwid.Text)
content, self.message_links, self.time_mentions = self.transform_content(
self.message["content"], self.model.server_url
Expand Down Expand Up @@ -812,6 +842,46 @@ def update_message_author_status(self) -> bool:

return author_is_present

@classmethod
def process_poll_data(cls, poll_data: Any) -> Tuple[str, Dict[str, List[int]]]:
question = ""
votes_for_option: Dict[str, List[int]] = {}

for submessage in poll_data:
content = submessage.get("content", {})
if isinstance(content, str):
try:
content = json.loads(content)
except json.JSONDecodeError:
continue

if "widget_type" in content and content["widget_type"] == "poll":
question = content["extra_data"]["question"]
votes_for_option = {
option: [] for option in content["extra_data"]["options"]
}

elif "type" in content and content["type"] == "new_option":
option = content["option"]
votes_for_option[option] = []

elif "type" in content and content["type"] == "vote":
key = content["key"]
index_str = key.split(",")[1]
if index_str.isdigit():
index = int(index_str)
if index < len(votes_for_option):
option = list(votes_for_option.keys())[index]
voter_id = submessage["sender_id"]
if content["vote"] == -1:
if voter_id in votes_for_option[option]:
votes_for_option[option].remove(voter_id)
else:
if voter_id not in votes_for_option[option]:
votes_for_option[option].append(voter_id)

return question, votes_for_option

@classmethod
def transform_content(
cls, content: Any, server_url: str
Expand Down

0 comments on commit 4ab806c

Please sign in to comment.