Coverage for /home/runner/work/viur-core/viur-core/viur/src/viur/core/bones/boolean.py: 23%
42 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 typing as t
2from viur.core import conf, db, utils
3from viur.core.bones.base import BaseBone
6DEFAULT_VALUE_T: t.TypeAlias = bool | None | list[bool] | dict[str, list[bool] | bool]
9class BooleanBone(BaseBone):
10 """
11 Represents a boolean data type, which can have two possible values: `True` or `False`.
12 It also allows for `None` to specify the "not yet set"-state.
13 BooleanBones cannot be defined as `multiple=True`.
15 :param defaultValue: The default value of the `BooleanBone` instance. Defaults to `None` (unset).
16 :raises ValueError: If the `defaultValue` is not either a boolean value (`True` or `False`) or `None`.
17 """
18 type = "bool"
20 def __init__(
21 self,
22 *,
23 defaultValue: DEFAULT_VALUE_T | t.Callable[[t.Self, "SkeletonInstance"], DEFAULT_VALUE_T] = None,
24 **kwargs
25 ):
26 if defaultValue is not None:
27 # We have given an explicit defaultValue and maybe a complex structure
28 if not kwargs.get("languages") and not (isinstance(defaultValue, bool) or callable(defaultValue)):
29 raise TypeError("Only True, False, None or callable can be provided as BooleanBone defaultValue")
30 # TODO: missing validation for complex types, but in other bones too
32 super().__init__(defaultValue=defaultValue, **kwargs)
34 # Disallow creation of BooleanBone(multiple=True)
35 if self.multiple:
36 raise ValueError("BooleanBone cannot be multiple")
38 def singleValueFromClient(self, value, skel, bone_name, client_data):
39 return utils.parse.bool(value, conf.bone_boolean_str2true), None
41 def getEmptyValue(self):
42 """
43 Returns the empty value of the `BooleanBone` class, which is `False`.
45 :return: The empty value of the `BooleanBone` class (`False`).
46 :rtype: bool
47 """
48 return False
50 def isEmpty(self, value: t.Any):
51 """
52 Checks if the given boolean value is empty.
54 :param value: The boolean value to be checked.
55 :return: `True` if the boolean value is empty (i.e., equal to the empty value of the `BooleanBone` class), \
56 `False` otherwise.
57 :rtype: bool
58 """
59 if value is self.getEmptyValue():
60 return True
61 return not bool(value)
63 def refresh(self, skel: 'viur.core.skeleton.SkeletonInstance', name: str) -> None:
64 """
65 Inverse of serialize. Evaluates whats
66 read from the datastore and populates
67 this bone accordingly.
69 :param name: The property-name this bone has in its Skeleton (not the description!)
70 """
71 if self.languages:
72 for lang in self.languages:
73 skel[name][lang] = utils.parse.bool(skel[name][lang], conf.bone_boolean_str2true) \
74 if lang in skel[name] else self.getDefaultValue(skel)
75 else:
76 skel[name] = utils.parse.bool(skel[name], conf.bone_boolean_str2true)
78 def setBoneValue(
79 self,
80 skel: 'SkeletonInstance',
81 boneName: str,
82 value: t.Any,
83 append: bool,
84 language: None | str = None
85 ) -> bool:
86 """
87 Sets the value of the bone to the provided 'value'.
88 Sanity checks are performed; if the value is invalid, the bone value will revert to its original
89 (default) value and the function will return False.
91 :param skel: Dictionary with the current values from the skeleton the bone belongs to
92 :param boneName: The name of the bone that should be modified
93 :param value: The value that should be assigned. Its type depends on the type of the bone
94 :param append: If True, the given value will be appended to the existing bone values instead of
95 replacing them. Only supported on bones with multiple=True
96 :param language: Optional, the language of the value if the bone is language-aware
97 :return: A boolean indicating whether the operation succeeded or not
98 :rtype: bool
99 """
100 if append:
101 raise ValueError(f"append is not possible on {self.type} bones")
103 if language:
104 if not self.languages or language not in self.languages:
105 return False
107 skel[boneName][language] = utils.parse.bool(value)
108 else:
109 skel[boneName] = utils.parse.bool(value)
111 return True
113 def singleValueSerialize(self, value, skel: 'SkeletonInstance', name: str, parentIndexed: bool):
114 """
115 Serializes a single value of the bone for storage in the database.
117 Derived bone classes should overwrite this method to implement their own logic for serializing single
118 values.
119 The serialized value should be suitable for storage in the database.
120 """
121 return utils.parse.bool(value, conf.bone_boolean_str2true)
123 def buildDBFilter(
124 self,
125 name: str,
126 skel: 'viur.core.skeleton.SkeletonInstance',
127 dbFilter: db.Query,
128 rawFilter: dict,
129 prefix: t.Optional[str] = None
130 ) -> db.Query:
131 """
132 Builds a database filter based on the boolean value.
134 :param name: The name of the `BooleanBone` instance.
135 :param skel: The `SkeletonInstance` object representing the data of the current entity.
136 :param dbFilter: The `Query` object representing the current database filter.
137 :param rawFilter: The dictionary representing the raw filter data received from the client.
138 :param prefix: A prefix to be added to the property name in the database filter.
139 :return: The updated `Query` object representing the updated database filter.
140 :rtype: google.cloud.ndb.query.Query
141 """
142 if name in rawFilter:
143 val = utils.parse.bool(rawFilter[name], conf.bone_boolean_str2true)
144 return super().buildDBFilter(name, skel, dbFilter, {name: val}, prefix=prefix)
146 return dbFilter