Skip to content

Commit aec1ae5

Browse files
committed
Add support for additional data
1 parent 0da58f8 commit aec1ae5

2 files changed

Lines changed: 143 additions & 0 deletions

File tree

src/compas_cadwork/elements/element.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,72 @@ def setdefault(self, key: UserAttributeId, default: str) -> str:
8585
return self[key]
8686

8787

88+
class _ElementAdditionalData:
89+
"""Dictionary-like accessor for element additional data.
90+
91+
NOTE: Additional data is hidden from users and only accessible via the Cadwork API.
92+
"""
93+
94+
_id: Final[ElementId]
95+
96+
def __init__(self, element_id: ElementId) -> None:
97+
self._id = element_id
98+
99+
def __contains__(self, key: str) -> bool:
100+
raw_value = ac.get_additional_data(self._id, key)
101+
return raw_value != ""
102+
103+
def __getitem__(self, key: str) -> str:
104+
raw_value = ac.get_additional_data(self._id, key)
105+
if raw_value == "":
106+
raise KeyError(key)
107+
return raw_value
108+
109+
def __setitem__(self, key: str, value: str) -> None:
110+
ac.set_additional_data([self._id], key, value)
111+
112+
def __delitem__(self, key: str) -> None:
113+
if key not in self:
114+
raise KeyError(key)
115+
ac.delete_additional_data([self._id], key)
116+
117+
def get(self, key: str, default: str | None = None) -> str | None:
118+
"""Get additional data.
119+
120+
Parameters
121+
----------
122+
key : str
123+
Additional data ID.
124+
default : str | None, optional
125+
Value to return if additional data is not set.
126+
127+
Returns
128+
-------
129+
str | None
130+
Additional data value, or ``default`` if additional data is not set.
131+
"""
132+
return self[key] if key in self else default
133+
134+
def setdefault(self, key: str, default: str) -> str:
135+
"""Set additional data to ``default`` if not set, then return its value.
136+
137+
Parameters
138+
----------
139+
key : str
140+
Additional data ID.
141+
default : str
142+
Value to set if additional data is not set.
143+
144+
Returns
145+
-------
146+
str
147+
Existing additional data value, or ``default`` if data was not set.
148+
"""
149+
if key not in self:
150+
self[key] = default
151+
return self[key]
152+
153+
88154
class Element:
89155
"""Generic Cadwork element."""
90156

@@ -189,6 +255,11 @@ def attributes(self) -> _ElementUserAttributes:
189255
"""User attributes."""
190256
return _ElementUserAttributes(self.id)
191257

258+
@cached_property
259+
def data(self) -> _ElementAdditionalData:
260+
"""Additional data."""
261+
return _ElementAdditionalData(self.id)
262+
192263
def delete(self) -> None:
193264
"""Delete element.
194265

tests/compas_cadwork/elements/test_element.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,78 @@ def test_deletes_attributes(cadwork) -> None:
235235
cadwork.ac.set_user_attribute.assert_called_once_with([123], 700, "")
236236

237237

238+
def test_contains_data(cadwork) -> None:
239+
element = Element(123)
240+
241+
# Without value
242+
cadwork.ac.get_additional_data.return_value = ""
243+
assert "first" not in element.data
244+
cadwork.ac.get_additional_data.assert_called_once_with(123, "first")
245+
246+
# With value
247+
cadwork.ac.get_additional_data.return_value = "Test Value"
248+
assert "second" in element.data
249+
cadwork.ac.get_additional_data.assert_called_with(123, "second")
250+
251+
252+
def test_gets_data(cadwork) -> None:
253+
element = Element(123)
254+
255+
# Without value
256+
cadwork.ac.get_additional_data.return_value = ""
257+
with pytest.raises(KeyError):
258+
_ = element.data["third"]
259+
cadwork.ac.get_additional_data.assert_called_once_with(123, "third")
260+
assert element.data.get("third", None) is None
261+
assert element.data.get("third", "Default") == "Default"
262+
263+
# With value
264+
cadwork.ac.get_additional_data.return_value = "Test Value"
265+
assert element.data["fourth"] == "Test Value"
266+
cadwork.ac.get_additional_data.assert_called_with(123, "fourth")
267+
assert element.data.get("fourth", None) == "Test Value"
268+
assert element.data.get("fourth", "Default") == "Test Value"
269+
270+
271+
def test_sets_data(cadwork) -> None:
272+
element = Element(123)
273+
274+
# Base case
275+
element.data["fifth"] = "New Value"
276+
cadwork.ac.set_additional_data.assert_called_once_with([123], "fifth", "New Value")
277+
278+
# Without value
279+
cadwork.ac.get_additional_data.side_effect = ["", "Default Value"]
280+
assert element.data.setdefault("sixth", "Default Value") == "Default Value"
281+
cadwork.ac.get_additional_data.assert_called_with(123, "sixth")
282+
cadwork.ac.set_additional_data.assert_called_with([123], "sixth", "Default Value")
283+
284+
# With value
285+
cadwork.ac.set_additional_data.reset_mock()
286+
cadwork.ac.get_additional_data.reset_mock(side_effect=True)
287+
cadwork.ac.get_additional_data.return_value = "Existing Value"
288+
assert element.data.setdefault("seventh", "Default Value") == "Existing Value"
289+
cadwork.ac.get_additional_data.assert_called_with(123, "seventh")
290+
cadwork.ac.set_additional_data.assert_not_called()
291+
292+
293+
def test_deletes_data(cadwork) -> None:
294+
element = Element(123)
295+
296+
# Without value
297+
cadwork.ac.get_additional_data.return_value = ""
298+
with pytest.raises(KeyError):
299+
del element.data["first"]
300+
cadwork.ac.get_additional_data.assert_called_once_with(123, "first")
301+
302+
# With value
303+
cadwork.ac.get_additional_data.return_value = "Test Value"
304+
del element.data["second"]
305+
cadwork.ac.get_additional_data.assert_called_with(123, "second")
306+
cadwork.ac.delete_additional_data.assert_called_once_with([123], "second")
307+
cadwork.ac.set_additional_data.assert_not_called()
308+
309+
238310
def test_deletes_element(cadwork) -> None:
239311
element = Element(123)
240312
element.delete()

0 commit comments

Comments
 (0)