Skip to content

Commit 0087d03

Browse files
MoonCubeshjanott
andauthored
Allow more constellations for live voting (#3425)
--------- Co-authored-by: Hannes Janott <hannes.janott@openslides.com>
1 parent 4d83c56 commit 0087d03

6 files changed

Lines changed: 236 additions & 58 deletions

File tree

docs/actions/poll.create.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,10 @@ The `entitled_group_ids` may not contain the meetings `anonymous_group_id`.
7070

7171
The `max_votes_per_option` and `min_votes_amount` must be smaller or equal to `max_votes_amount`.
7272

73-
The `live_voting_enabled` could be set for named motion and certain named assignment polls. Assignment polls also need pollmethod `Y` and not `global_yes` and `max_votes_amount` of 1 to set the option.
73+
The `live_voting_enabled` could be set for named motion and following named assignment polls:
74+
- polls with pollmethod `Y` and not `global_yes` and `max_votes_amount` of 1
75+
- polls with pollmethod `YNA` or `YN` and only one option
76+
- list polls
7477

7578
## Permissions
7679
The request user needs:

docs/actions/poll.update.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@ The `entitled_group_ids` may not contain the meetings `anonymous_group_id`.
4545

4646
The `max_votes_per_option` and `min_votes_amount` must be smaller or equal to `max_votes_amount` after the model has been updated.
4747

48-
The `live_voting_enabled` could be set for named motion and certain named assignment polls. Assignment polls also need pollmethod `Y` and not `global_yes` and `max_votes_amount` of 1 to set the option.
48+
The `live_voting_enabled` could be set for named motion and following named assignment polls:
49+
- polls with pollmethod `Y` and not `global_yes` and `max_votes_amount` of 1
50+
- polls with pollmethod `YNA` or `YN` and only one option
51+
- list polls
4952

5053
## Permissions
5154
The request user needs:

openslides_backend/action/actions/poll/create.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ def update_instance(self, instance: dict[str, Any]) -> dict[str, Any]:
8686
is_assignment_poll = (
8787
collection_from_fqid(instance["content_object_id"]) == "assignment"
8888
)
89+
is_list_poll = "poll_candidate_user_ids" in instance.get("options", [{}])[0]
8990

9091
# check enabled_electronic_voting
9192
if instance["type"] in (Poll.TYPE_NAMED, Poll.TYPE_PSEUDOANONYMOUS):
@@ -103,14 +104,26 @@ def update_instance(self, instance: dict[str, Any]) -> dict[str, Any]:
103104
is_motion_poll
104105
or (
105106
is_assignment_poll
106-
and not instance.get("global_yes")
107-
and instance["pollmethod"] == "Y"
108-
and instance.get("max_votes_amount") == 1
107+
and (
108+
(
109+
not instance.get("global_yes")
110+
and instance["pollmethod"] == "Y"
111+
and instance.get("max_votes_amount") == 1
112+
)
113+
or is_list_poll
114+
or (
115+
(
116+
instance["pollmethod"] == "YNA"
117+
or instance["pollmethod"] == "YN"
118+
)
119+
and len(instance.get("options", [])) == 1
120+
)
121+
)
109122
)
110123
)
111124
):
112125
raise ActionException(
113-
"live_voting_enabled only allowed for named motion polls and named Yes assignment polls."
126+
"live_voting_enabled only allowed for named motion polls and some named assignment polls."
114127
)
115128

116129
# check entitled_group_ids and analog

openslides_backend/action/actions/poll/update.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ def update_instance(self, instance: dict[str, Any]) -> dict[str, Any]:
8080
"pollmethod",
8181
"max_votes_amount",
8282
"global_yes",
83+
"option_ids",
8384
],
8485
)
8586

@@ -133,20 +134,42 @@ def update_instance(self, instance: dict[str, Any]) -> dict[str, Any]:
133134
max_votes_amount_for_check = instance.get("max_votes_amount") or poll.get(
134135
"max_votes_amount"
135136
)
137+
option_ids: list[int] = poll.get("option_ids", [])
138+
length_for_check = len(option_ids)
139+
content_object_id = (
140+
self.datastore.get(
141+
fqid_from_collection_and_id("option", option_ids[0]),
142+
["content_object_id"],
143+
).get("content_object_id")
144+
if length_for_check
145+
else None
146+
)
147+
is_list_poll = (
148+
collection_from_fqid(content_object_id) == "poll_candidate_list"
149+
if content_object_id
150+
else False
151+
)
136152
if instance.get("live_voting_enabled") and not (
137153
poll["type"] == Poll.TYPE_NAMED
138154
and (
139155
collection_from_fqid(poll["content_object_id"]) == "motion"
140156
or (
141157
collection_from_fqid(poll["content_object_id"]) == "assignment"
142-
and not global_yes_for_check
143-
and pollmethod_for_check == "Y"
144-
and max_votes_amount_for_check == 1
158+
and (
159+
not global_yes_for_check
160+
and pollmethod_for_check == "Y"
161+
and max_votes_amount_for_check == 1
162+
)
163+
or is_list_poll
164+
or (
165+
(pollmethod_for_check == "YNA" or pollmethod_for_check == "YN")
166+
and length_for_check == 1
167+
)
145168
)
146169
)
147170
):
148171
raise ActionException(
149-
"live_voting_enabled only allowed for named motion polls and named Yes assignment polls."
172+
"live_voting_enabled only allowed for named motion polls and some named assignment polls."
150173
)
151174

152175
if poll["type"] == Poll.TYPE_ANALOG and (

tests/system/action/poll/test_create.py

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,19 +1132,57 @@ def test_live_voting_named_motion_poll(self) -> None:
11321132
)
11331133

11341134
def test_live_voting_named_assignment_poll(self) -> None:
1135-
self.base_live_voting_assigment()
1135+
self.base_test_live_voting_assignment()
11361136

1137-
def test_live_votng_named_assignment_poll_wrong_pollmethod(self) -> None:
1138-
self.base_live_voting_assigment({"pollmethod": "YN"})
1137+
def test_live_voting_list_poll(self) -> None:
1138+
self.set_models({"user/3": {"username": "User3"}})
1139+
self.base_test_live_voting_assignment(
1140+
{
1141+
"pollmethod": "YNA",
1142+
"options": [
1143+
{"poll_candidate_user_ids": [1, 3]},
1144+
],
1145+
}
1146+
)
1147+
1148+
def test_live_voting_named_assignment_poll_pollmethod_yna(self) -> None:
1149+
self.base_test_live_voting_assignment({"pollmethod": "YNA"})
1150+
1151+
def test_live_voting_named_assignment_poll_pollmethod_yn(self) -> None:
1152+
self.base_test_live_voting_assignment({"pollmethod": "YN"})
1153+
1154+
def test_live_voting_named_assignment_poll_wrong_globalyes(self) -> None:
1155+
self.base_test_live_voting_assignment(error_dict={"global_yes": True})
1156+
1157+
def test_live_voting_named_assignment_poll_wrong_max_votes(self) -> None:
1158+
self.base_test_live_voting_assignment(error_dict={"max_votes_amount": 2})
11391159

1140-
def test_live_votng_named_assignment_poll_wrong_globalyes(self) -> None:
1141-
self.base_live_voting_assigment({"global_yes": True})
1160+
def test_live_voting_named_assignment_poll_wrong_option_amount_yna(self) -> None:
1161+
self.base_test_live_voting_assignment(
1162+
error_dict={
1163+
"pollmethod": "YNA",
1164+
"options": [
1165+
{"text": "option1"},
1166+
{"text": "option2"},
1167+
],
1168+
}
1169+
)
11421170

1143-
def test_live_votng_named_assignment_poll_wrong_max_votes(self) -> None:
1144-
self.base_live_voting_assigment({"max_votes_amount": 2})
1171+
def test_live_voting_named_assignment_poll_wrong_option_amount_yn(self) -> None:
1172+
self.base_test_live_voting_assignment(
1173+
error_dict={
1174+
"pollmethod": "YN",
1175+
"options": [
1176+
{"text": "option1"},
1177+
{"text": "option2"},
1178+
],
1179+
}
1180+
)
11451181

1146-
def base_live_voting_assigment(
1147-
self, error_dict: dict[str, typing.Any] | None = None
1182+
def base_test_live_voting_assignment(
1183+
self,
1184+
override_dict: dict[str, typing.Any] | None = None,
1185+
error_dict: dict[str, typing.Any] | None = None,
11481186
) -> None:
11491187
self.create_assignment(3, 1)
11501188

@@ -1159,6 +1197,7 @@ def base_live_voting_assigment(
11591197
"meeting_id": 1,
11601198
"options": [{"text": "test"}],
11611199
"live_voting_enabled": True,
1200+
**(override_dict if override_dict else {}),
11621201
**(error_dict if error_dict else {}),
11631202
},
11641203
)
@@ -1171,7 +1210,7 @@ def base_live_voting_assigment(
11711210
self.assert_status_code(response, 400)
11721211
self.assert_model_not_exists("poll/1")
11731212
assert (
1174-
"live_voting_enabled only allowed for named motion polls and named Yes assignment polls."
1213+
"live_voting_enabled only allowed for named motion polls and some named assignment polls."
11751214
) in response.json["message"]
11761215

11771216
def test_live_voting_not_allowed_type_analog(self) -> None:
@@ -1180,9 +1219,6 @@ def test_live_voting_not_allowed_type_analog(self) -> None:
11801219
def test_live_voting_not_allowed_type_pseudoanonymous(self) -> None:
11811220
self.base_live_voting_not_allowed(Poll.TYPE_PSEUDOANONYMOUS, True)
11821221

1183-
def test_live_voting_not_allowed_is_motion_poll_false(self) -> None:
1184-
self.base_live_voting_not_allowed(Poll.TYPE_NAMED, False)
1185-
11861222
def base_live_voting_not_allowed(
11871223
self, poll_type: str, is_motion_poll: bool
11881224
) -> None:
@@ -1205,6 +1241,6 @@ def base_live_voting_not_allowed(
12051241
self.assert_status_code(response, 400)
12061242
self.assert_model_not_exists("poll/1")
12071243
self.assertEqual(
1208-
"live_voting_enabled only allowed for named motion polls and named Yes assignment polls.",
1244+
"live_voting_enabled only allowed for named motion polls and some named assignment polls.",
12091245
response.json["message"],
12101246
)

0 commit comments

Comments
 (0)