Coverage for /home/runner/work/viur-core/viur-core/viur/src/viur/core/bones/spam.py: 25%
47 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-02-07 19:28 +0000
« prev ^ index » next coverage.py v7.6.10, created at 2025-02-07 19:28 +0000
1import logging
2import random
3import typing as t
4from viur.core import i18n, current
5from viur.core.bones import NumericBone
6from viur.core.bones.base import getSystemInitialized
9class SpamBone(NumericBone):
10 type = "numeric.spam"
12 def __init__(
13 self,
14 descr: str = i18n.translate(
15 "core.bones.spam.question",
16 "What is the result of the addition of {{a}} and {{b}}?"
17 ),
18 values: t.Iterable[str] = tuple(
19 i18n.translate(f"core.bones.spam.value.{digit}", digit)
20 for digit in ("one", "two", "three", "four", "five", "six", "seven", "eight", "nine")
21 ),
22 required: bool = True,
23 msg_invalid: str = i18n.translate(
24 "core.bones.spam.invalid",
25 "Your answer was wrong! Please try again."
26 ),
27 **kwargs
28 ):
29 if "precision" in kwargs:
30 raise ValueError(f"Cannot use {self.__class__.__name__!r} with a precision")
32 super().__init__(
33 required=required,
34 precision=0,
35 **kwargs
36 )
38 self.values = tuple(values)
39 if not self.values or len(self.values) == 1:
40 raise ValueError("Requires for at least 2 values!")
42 self.descr_template = descr
43 self.msg_invalid = msg_invalid
45 def _dice(self) -> int:
46 return random.randint(1, len(self.values))
48 @property
49 def descr(self):
50 """
51 The descr-property is generated and uses session variables to construct the question.
52 """
53 # This descr can only be evaluated on initialized systems.
54 if not getSystemInitialized():
55 return None
57 session = current.session.get()
59 a = session.get("spambone.value.a")
60 b = session.get("spambone.value.b")
62 if a is None or b is None:
63 a = session["spambone.value.a"] = self._dice()
64 b = session["spambone.value.b"] = self._dice()
66 return i18n.translate(self.descr_template).translate(a=self.values[a - 1], b=self.values[b - 1])
68 @descr.setter
69 def descr(self, value: t.Any) -> None:
70 """
71 This setter is a null operation. It makes it possible to make a
72 super call in init, although the `descr` attribute is set there.
73 """
74 pass
76 def isInvalid(self, value):
77 session = current.session.get()
79 a = session.get("spambone.value.a") or 0
80 b = session.get("spambone.value.b") or 0
82 if a and b:
83 del session["spambone.value.a"]
84 del session["spambone.value.b"]
86 try:
87 value = int(value)
88 except ValueError:
89 value = 0
91 logging.debug(f"{a=}, {b=}, {value=}, expecting {a + b=}")
92 if value != a + b:
93 return str(self.msg_invalid)