Skip to content

Commit

Permalink
Added 'entity_class_byname' field
Browse files Browse the repository at this point in the history
  • Loading branch information
soininen committed Jan 30, 2025
1 parent 9d796f2 commit 7c39cbe
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 14 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
parameter definition, paramater value and list value items.
``parsed_value`` replaces the ``value`` and ``type`` (``default_value`` and ``default_type`` for parameter definitions)
fields and accepts the value directly so manual conversion using ``to_database()`` is not needed anymore.
- Added a read-only field ``entity_class_byname`` to EntityClassItem (accessible from EntityItem as well)
which works analogously to ``entity_byname``.

### Changed

Expand Down
41 changes: 27 additions & 14 deletions spinedb_api/mapped_items.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@
# Public License for more details. You should have received a copy of the GNU Lesser General Public License along with
# this program. If not, see <http://www.gnu.org/licenses/>.
######################################################################################################################
from collections.abc import Iterator
import inspect
from operator import itemgetter
import re
from typing import ClassVar
from .db_mapping_base import MappedItemBase
from typing import ClassVar, Union
from .db_mapping_base import DatabaseMappingBase, MappedItemBase
from .exception import SpineDBAPIError
from .helpers import DisplayStatus, name_from_dimensions, name_from_elements
from .parameter_value import (
Expand All @@ -36,7 +37,12 @@ def item_factory(item_type):

_ENTITY_BYNAME_VALUE = (
"A tuple with the entity name as single element if the entity is 0-dimensional, "
"or the 0-dimensional element names if the entity is multi-dimensional."
"or the 0-dimensional element names if it is multi-dimensional."
)

_ENTITY_CLASS_BYNAME_VALUE = (
"A tuple with the class name as single element if the class is 0-dimensional, "
"or the 0-dimensional class names if it is multi-dimensional."
)


Expand Down Expand Up @@ -64,6 +70,7 @@ class EntityClassItem(MappedItemBase):
"value": "The dimension names for a multi-dimensional class.",
"optional": True,
},
"entity_class_byname": {"type": tuple, "value": _ENTITY_CLASS_BYNAME_VALUE},
"description": {"type": str, "value": "The class description.", "optional": True},
"display_icon": {
"type": int,
Expand Down Expand Up @@ -109,6 +116,8 @@ def __getitem__(self, key):
if key in ("superclass_id", "superclass_name"):
mapped_table = self._db_map.mapped_table("superclass_subclass")
return mapped_table.find_item({"subclass_id": self["id"]}, fetch=True).get(key)
if key == "entity_class_byname":
return tuple(_byname_iter(self, "dimension_id_list", self._db_map))
return super().__getitem__(key)

def merge(self, other):
Expand Down Expand Up @@ -144,6 +153,7 @@ class EntityItem(MappedItemBase):
_references = {"class_id": "entity_class", "element_id_list": "entity"}
_external_fields = {
"entity_class_name": ("class_id", "name"),
"entity_class_byname": ("class_id", "entity_class_byname"),
"dimension_id_list": ("class_id", "dimension_id_list"),
"dimension_name_list": ("class_id", "dimension_name_list"),
"superclass_id": ("class_id", "superclass_id"),
Expand Down Expand Up @@ -180,19 +190,9 @@ def unique_values_for_item(cls, item, skip_keys=()):
if None not in sc_value:
yield (key, sc_value)

def _byname_iter(self, entity):
element_id_list = entity["element_id_list"]
if not element_id_list:
yield entity["name"]
else:
find_by_id = self._db_map.mapped_table("entity").find_item_by_id
for el_id in element_id_list:
element = find_by_id(el_id)
yield from self._byname_iter(element)

def __getitem__(self, key):
if key == "entity_byname":
return tuple(self._byname_iter(self))
return tuple(_byname_iter(self, "element_id_list", self._db_map))
return super().__getitem__(key)

def resolve_internal_fields(self, skip_keys=()):
Expand Down Expand Up @@ -1149,3 +1149,16 @@ def commit(self, _commit_id):
x for x in tuple(locals().values()) if inspect.isclass(x) and issubclass(x, MappedItemBase) and x != MappedItemBase
)
ITEM_CLASS_BY_TYPE = {klass.item_type: klass for klass in ITEM_CLASSES}


def _byname_iter(
item: Union[EntityClassItem, EntityItem], id_list_name: str, db_map: DatabaseMappingBase
) -> Iterator[str]:
id_list = item[id_list_name]
if not id_list:
yield item["name"]
else:
find_by_id = db_map.mapped_table(item.item_type).find_item_by_id
for id_ in id_list:
element = find_by_id(id_)
yield from _byname_iter(element, id_list_name, db_map)
13 changes: 13 additions & 0 deletions tests/test_DatabaseMapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -2039,6 +2039,19 @@ def test_update_list_value_with_parsed_value(self):
value_item.update(parsed_value=2.3)
self.assertEqual(value_item["parsed_value"], 2.3)

def test_entity_class_bynames(self):
with DatabaseMapping("sqlite://", create=True) as db_map:
item = self._assert_success(db_map.add_entity_class_item(name="Object"))
self.assertEqual(item["entity_class_byname"], ("Object",))
self._assert_success(db_map.add_entity_class_item(name="Subject"))
item = self._assert_success(db_map.add_entity_class_item(dimension_name_list=("Subject", "Object")))
self.assertEqual(item["entity_class_byname"], ("Subject", "Object"))
self._assert_success(db_map.add_entity_class_item(dimension_name_list=("Object", "Subject")))
item = self._assert_success(
db_map.add_entity_class_item(dimension_name_list=("Subject__Object", "Object__Subject"))
)
self.assertEqual(item["entity_class_byname"], ("Subject", "Object", "Object", "Subject"))


class TestDatabaseMappingLegacy(unittest.TestCase):
"""'Backward compatibility' tests, i.e. pre-entity tests converted to work with the entity structure."""
Expand Down

0 comments on commit 7c39cbe

Please sign in to comment.