Skip to content

Commit 086985e

Browse files
rsashankneiljp
authored andcommitted
widget: Add process_todo_widget function.
Added process_todo_widget function to process submessages containing a todo widget. Returns the title of the widget along with tasks and their state (completed/uncompleted) as a dict. Tests added.
1 parent 9d3afe6 commit 086985e

File tree

2 files changed

+281
-3
lines changed

2 files changed

+281
-3
lines changed

tests/widget/test_widget.py

Lines changed: 232 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
from typing import List
1+
from typing import Dict, List, Union
22

33
import pytest
44
from pytest import param as case
55

6-
from zulipterminal.widget import Submessage, find_widget_type
6+
from zulipterminal.widget import Submessage, find_widget_type, process_todo_widget
77

88

99
@pytest.mark.parametrize(
@@ -66,3 +66,233 @@ def test_find_widget_type(
6666
widget_type = find_widget_type(submessages)
6767

6868
assert widget_type == expected_widget_type
69+
70+
71+
@pytest.mark.parametrize(
72+
"submessages, expected_title, expected_tasks",
73+
[
74+
case(
75+
[
76+
{
77+
"id": 11899,
78+
"message_id": 1954463,
79+
"sender_id": 27294,
80+
"msg_type": "widget",
81+
"content": (
82+
'{"widget_type": "todo", "extra_data": '
83+
'{"task_list_title": "Today\'s Tasks", "tasks": [{"task": '
84+
'"Write code", "desc": ""}, {"task": "Sleep", "desc": ""}]}}'
85+
),
86+
},
87+
{
88+
"id": 11900,
89+
"message_id": 1954463,
90+
"sender_id": 27294,
91+
"msg_type": "widget",
92+
"content": (
93+
'{"type":"new_task","key":2,"task":"Eat","desc":"",'
94+
'"completed":false}'
95+
),
96+
},
97+
],
98+
"Today's Tasks",
99+
{
100+
"0,canned": {"task": "Write code", "desc": "", "completed": False},
101+
"1,canned": {"task": "Sleep", "desc": "", "completed": False},
102+
"2,27294": {"task": "Eat", "desc": "", "completed": False},
103+
},
104+
id="title_and_unfinished_tasks",
105+
),
106+
case(
107+
[
108+
{
109+
"id": 11912,
110+
"message_id": 1954626,
111+
"sender_id": 27294,
112+
"msg_type": "widget",
113+
"content": (
114+
'{"widget_type": "todo", "extra_data": '
115+
'{"task_list_title": "", "tasks": [{"task": "Hey", "desc": ""},'
116+
' {"task": "Hi", "desc": ""}]}}'
117+
),
118+
}
119+
],
120+
"Task list",
121+
{
122+
"0,canned": {"task": "Hey", "desc": "", "completed": False},
123+
"1,canned": {"task": "Hi", "desc": "", "completed": False},
124+
},
125+
id="no_title_and_unfinished_tasks",
126+
),
127+
case(
128+
[
129+
{
130+
"id": 11919,
131+
"message_id": 1954843,
132+
"sender_id": 27294,
133+
"msg_type": "widget",
134+
"content": (
135+
'{"widget_type": "todo", "extra_data": '
136+
'{"task_list_title": "", "tasks": []}}'
137+
),
138+
}
139+
],
140+
"Task list",
141+
{},
142+
id="no_title_or_tasks",
143+
),
144+
case(
145+
[
146+
{
147+
"id": 11932,
148+
"message_id": 1954847,
149+
"sender_id": 27294,
150+
"msg_type": "widget",
151+
"content": (
152+
'{"widget_type": "todo", "extra_data": '
153+
'{"task_list_title": "", "tasks": []}}'
154+
),
155+
},
156+
{
157+
"id": 11933,
158+
"message_id": 1954847,
159+
"sender_id": 27294,
160+
"msg_type": "widget",
161+
"content": (
162+
'{"type":"new_task","key":2,"task":"Write code",'
163+
'"desc":"Make the todo ZT PR!","completed":false}'
164+
),
165+
},
166+
{
167+
"id": 11934,
168+
"message_id": 1954847,
169+
"sender_id": 27294,
170+
"msg_type": "widget",
171+
"content": (
172+
'{"type":"new_task","key":4,"task":"Sleep",'
173+
'"desc":"at least 8 hours a day","completed":false}'
174+
),
175+
},
176+
{
177+
"id": 11935,
178+
"message_id": 1954847,
179+
"sender_id": 27294,
180+
"msg_type": "widget",
181+
"content": (
182+
'{"type":"new_task","key":6,"task":"Eat",'
183+
'"desc":"3 meals a day","completed":false}'
184+
),
185+
},
186+
{
187+
"id": 11936,
188+
"message_id": 1954847,
189+
"sender_id": 27294,
190+
"msg_type": "widget",
191+
"content": (
192+
'{"type":"new_task","key":8,"task":"Exercise",'
193+
'"desc":"an hour a day","completed":false}'
194+
),
195+
},
196+
{
197+
"id": 11937,
198+
"message_id": 1954847,
199+
"sender_id": 27294,
200+
"msg_type": "widget",
201+
"content": '{"type":"strike","key":"2,27294"}',
202+
},
203+
{
204+
"id": 11938,
205+
"message_id": 1954847,
206+
"sender_id": 27294,
207+
"msg_type": "widget",
208+
"content": '{"type":"strike","key":"2,27294"}',
209+
},
210+
{
211+
"id": 11939,
212+
"message_id": 1954847,
213+
"sender_id": 27294,
214+
"msg_type": "widget",
215+
"content": '{"type":"strike","key":"4,27294"}',
216+
},
217+
{
218+
"id": 11940,
219+
"message_id": 1954847,
220+
"sender_id": 27294,
221+
"msg_type": "widget",
222+
"content": '{"type":"strike","key":"6,27294"}',
223+
},
224+
{
225+
"id": 11941,
226+
"message_id": 1954847,
227+
"sender_id": 27294,
228+
"msg_type": "widget",
229+
"content": '{"type":"strike","key":"8,27294"}',
230+
},
231+
{
232+
"id": 11942,
233+
"message_id": 1954847,
234+
"sender_id": 27294,
235+
"msg_type": "widget",
236+
"content": '{"type":"strike","key":"2,27294"}',
237+
},
238+
{
239+
"id": 11943,
240+
"message_id": 1954847,
241+
"sender_id": 27294,
242+
"msg_type": "widget",
243+
"content": '{"type":"strike","key":"4,27294"}',
244+
},
245+
{
246+
"id": 11944,
247+
"message_id": 1954847,
248+
"sender_id": 27294,
249+
"msg_type": "widget",
250+
"content": '{"type":"strike","key":"6,27294"}',
251+
},
252+
{
253+
"id": 11945,
254+
"message_id": 1954847,
255+
"sender_id": 27294,
256+
"msg_type": "widget",
257+
"content": '{"type":"strike","key":"4,27294"}',
258+
},
259+
{
260+
"id": 11946,
261+
"message_id": 1954847,
262+
"sender_id": 27294,
263+
"msg_type": "widget",
264+
"content": '{"type":"strike","key":"8,27294"}',
265+
},
266+
],
267+
"Task list",
268+
{
269+
"2,27294": {
270+
"task": "Write code",
271+
"desc": "Make the todo ZT PR!",
272+
"completed": True,
273+
},
274+
"4,27294": {
275+
"task": "Sleep",
276+
"desc": "at least 8 hours a day",
277+
"completed": True,
278+
},
279+
"6,27294": {"task": "Eat", "desc": "3 meals a day", "completed": False},
280+
"8,27294": {
281+
"task": "Exercise",
282+
"desc": "an hour a day",
283+
"completed": False,
284+
},
285+
},
286+
id="title_and_description_and_finished_tasks",
287+
),
288+
],
289+
)
290+
def test_process_todo_widget(
291+
submessages: List[Submessage],
292+
expected_title: str,
293+
expected_tasks: Dict[str, Dict[str, Union[str, bool]]],
294+
) -> None:
295+
title, tasks = process_todo_widget(submessages)
296+
297+
assert title == expected_title
298+
assert tasks == expected_tasks

zulipterminal/widget.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"""
44

55
import json
6-
from typing import Dict, List, Union
6+
from typing import Dict, List, Tuple, Union
77

88

99
Submessage = Dict[str, Union[int, str]]
@@ -23,3 +23,51 @@ def find_widget_type(submessages: List[Submessage]) -> str:
2323
return "unknown"
2424
else:
2525
return "unknown"
26+
27+
28+
def process_todo_widget(
29+
todo_list: List[Submessage],
30+
) -> Tuple[str, Dict[str, Dict[str, Union[str, bool]]]]:
31+
title = ""
32+
tasks = {}
33+
34+
for entry in todo_list:
35+
content = entry.get("content")
36+
sender_id = entry.get("sender_id")
37+
msg_type = entry.get("msg_type")
38+
39+
if msg_type == "widget" and isinstance(content, str):
40+
widget = json.loads(content)
41+
42+
if widget.get("widget_type") == "todo":
43+
if "extra_data" in widget and widget["extra_data"] is not None:
44+
title = widget["extra_data"].get("task_list_title", "")
45+
if title == "":
46+
# Webapp uses "Task list" as default title
47+
title = "Task list"
48+
# Process initial tasks
49+
for i, task in enumerate(widget["extra_data"].get("tasks", [])):
50+
# Initial tasks get ID as "index,canned"
51+
task_id = f"{i},canned"
52+
tasks[task_id] = {
53+
"task": task["task"],
54+
"desc": task.get("desc", ""),
55+
"completed": False,
56+
}
57+
58+
elif widget.get("type") == "new_task":
59+
# New tasks get ID as "key,sender_id"
60+
task_id = f"{widget['key']},{sender_id}"
61+
tasks[task_id] = {
62+
"task": widget["task"],
63+
"desc": widget.get("desc", ""),
64+
"completed": False,
65+
}
66+
67+
elif widget.get("type") == "strike":
68+
# Strike event - toggle task completion state
69+
task_id = widget["key"]
70+
if task_id in tasks:
71+
tasks[task_id]["completed"] = not tasks[task_id]["completed"]
72+
73+
return title, tasks

0 commit comments

Comments
 (0)