Skip to content

Commit

Permalink
feat(question_ui): split allowed roles using regex
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinGauk committed Jun 28, 2024
1 parent 8d23146 commit 5d3e97d
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 20 deletions.
31 changes: 19 additions & 12 deletions questionpy_sdk/webserver/question_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from __future__ import annotations

import re
from enum import StrEnum
from functools import cached_property
from random import Random
from typing import Any
Expand Down Expand Up @@ -149,11 +150,23 @@ def __init__(self) -> None:
self.required_fields: list[str] = []


class QuestionDisplayRole(StrEnum):
DEVELOPER = "DEVELOPER"
PROCTOR = "PROCTOR"
SCORER = "SCORER"
TEACHER = "TEACHER"


class QuestionDisplayOptions(BaseModel):
general_feedback: bool = True
feedback: bool = True
right_answer: bool = True
context: dict = {}
roles: set[QuestionDisplayRole] = {
QuestionDisplayRole.DEVELOPER,
QuestionDisplayRole.PROCTOR,
QuestionDisplayRole.SCORER,
QuestionDisplayRole.TEACHER,
}
readonly: bool = False


Expand Down Expand Up @@ -262,20 +275,14 @@ def _hide_unwanted_feedback(self) -> None:
def _hide_if_role(self) -> None:
"""Hides elements based on user role.
Removes elements with `qpy:if-role` attributes if the user matches none of the given roles in this context.
Removes elements with `qpy:if-role` attributes if the user matches none of the roles.
"""
if self._options.context.get("role") == "admin":
return

for element in _assert_element_list(self._xpath("//*[@qpy:if-role]")):
attr = element.attrib.get(f"{{{self.QPY_NAMESPACE}}}if-role")
if attr is None:
continue
allowed_roles = attr.split()
if attr := element.get(f"{{{self.QPY_NAMESPACE}}}if-role"):
allowed_roles = [role.upper() for role in re.split(r"[\s|]+", attr)]
has_role = any(role in allowed_roles and role in self._options.roles for role in QuestionDisplayRole)

if self._options.context.get("role") not in allowed_roles:
parent = element.getparent()
if parent is not None:
if not has_role and (parent := element.getparent()) is not None:
parent.remove(element)

def _set_input_values_and_readonly(self) -> None:
Expand Down
3 changes: 2 additions & 1 deletion tests/questionpy_sdk/webserver/test_data/if-role.xhtml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
<div qpy:if-role="developer">You're a developer!</div>
<div qpy:if-role="scorer">You're a scorer!</div>
<div qpy:if-role="proctor">You're a proctor!</div>
<div qpy:if-role="teacher developer scorer proctor">You're any of the above!</div>
<div qpy:if-role="teacher|developer scorer
proctor">You're any of the above!</div>
</div>
21 changes: 14 additions & 7 deletions tests/questionpy_sdk/webserver/test_question_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from questionpy_sdk.webserver.question_ui import (
QuestionDisplayOptions,
QuestionDisplayRole,
QuestionFormulationUIRenderer,
QuestionMetadata,
QuestionUIRenderer,
Expand Down Expand Up @@ -122,14 +123,23 @@ def test_should_show_inline_feedback(result: str) -> None:


@pytest.mark.parametrize(
("user_context", "expected"),
("options", "expected"),
[
(
"guest",
QuestionDisplayOptions(roles=set()),
"<div></div>",
),
(
"admin",
QuestionDisplayOptions(roles={QuestionDisplayRole.SCORER}),
"""
<div>
<div>You're a scorer!</div>
<div>You're any of the above!</div>
</div>
""",
),
(
QuestionDisplayOptions(),
"""
<div>
<div>You're a teacher!</div>
Expand All @@ -143,10 +153,7 @@ def test_should_show_inline_feedback(result: str) -> None:
],
)
@pytest.mark.ui_file("if-role")
def test_element_visibility_based_on_role(user_context: str, expected: str, xml_content: str) -> None:
options = QuestionDisplayOptions()
options.context["role"] = user_context

def test_element_visibility_based_on_role(options: QuestionDisplayOptions, expected: str, xml_content: str) -> None:
renderer = QuestionUIRenderer(xml_content, {}, options)
result = renderer.html

Expand Down

0 comments on commit 5d3e97d

Please sign in to comment.