Skip to content

Commit

Permalink
Convenience functions for adding and removing children
Browse files Browse the repository at this point in the history
- introduce the StoreItem.add_child and StoreItem.remove_child
  methods (automatically trigger signals and potentially other
  functionality)
- add unit tests for the methods above
- leverage these methods in the BaseStore, TaskStore and TagStore
  • Loading branch information
gycsaba96 authored and diegogangl committed Feb 26, 2025
1 parent ec6af6c commit 913d2aa
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 18 deletions.
28 changes: 24 additions & 4 deletions GTG/core/base_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,26 @@ def has_children(self) -> bool:
return len(self.children) > 0


def add_child(self,child:Self):
"""Add an item as child if not yet present."""
if child in self.children:
return
self.children.append(child)
self.notify('children_count')
if len(self.children) == 1:
self.notify('has_children')


def remove_child(self,child:Self):
"""Remove a child item if present."""
if child not in self.children:
return
self.children.remove(child)
self.notify('children_count')
if len(self.children) == 0:
self.notify('has_children')


def get_ancestors(self) -> list[Self]:
"""Return all ancestors of this tag"""
ancestors: list[Self] = []
Expand Down Expand Up @@ -111,7 +131,7 @@ def add(self, item: S, parent_id: Optional[UUID] = None) -> None:

if parent_id:
try:
self.lookup[parent_id].children.append(item)
self.lookup[parent_id].add_child(item)
item.parent = self.lookup[parent_id]

except KeyError:
Expand Down Expand Up @@ -157,7 +177,7 @@ def remove(self, item_id: UUID) -> None:
self.remove(cid)

if parent:
parent.children.remove(item)
parent.remove_child(item)
else:
self.data.remove(item)
del self.lookup[item_id]
Expand Down Expand Up @@ -188,7 +208,7 @@ def parent(self, item_id: UUID, parent_id: UUID) -> None:
item.parent = self.lookup[parent_id]

self.data.remove(item)
self.lookup[parent_id].children.append(item)
self.lookup[parent_id].add_child(item)
self.emit('parent-change', item, self.lookup[parent_id])


Expand All @@ -203,7 +223,7 @@ def unparent(self, item_id: UUID) -> None:
parent = child.parent
assert parent is not None

parent.children.remove(child)
parent.remove_child(child)
self.data.append(child)
child.parent = None

Expand Down
7 changes: 0 additions & 7 deletions GTG/core/tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,8 +396,6 @@ def add(self, item: Tag, parent_id: Optional[UUID] = None) -> None:
self._append_to_parent_model(item.id)

self.emit('added', item)
if parent_id:
self.lookup[parent_id].notify('children_count')


def remove(self, item_id: UUID) -> None:
Expand All @@ -414,7 +412,6 @@ def remove(self, item_id: UUID) -> None:
self.model.remove(pos[1])

super().remove(item_id)
if parent: self.lookup[parent.id].notify('children_count')


def parent(self, item_id: UUID, parent_id: UUID) -> None:
Expand All @@ -425,7 +422,6 @@ def parent(self, item_id: UUID, parent_id: UUID) -> None:
if item.parent is not None:
old_parent = item.parent
self._remove_from_parent_model(item_id)
self.lookup[old_parent.id].notify('children_count')
else:
pos = self.model.find(item)
self.model.remove(pos[1])
Expand All @@ -434,7 +430,6 @@ def parent(self, item_id: UUID, parent_id: UUID) -> None:

# Add back to UI
self._append_to_parent_model(item_id)
self.lookup[parent_id].notify('children_count')


def unparent(self, item_id: UUID) -> None:
Expand All @@ -451,5 +446,3 @@ def unparent(self, item_id: UUID) -> None:

# Add back to UI
self.model.append(item)

parent.notify('children_count')
7 changes: 0 additions & 7 deletions GTG/core/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -962,7 +962,6 @@ def add(self, item: Any, parent_id: Optional[UUID] = None) -> None:
self.model.append(item)
else:
self._append_to_parent_model(item.id)
self.lookup[parent_id].notify('has_children')

item.duplicate_cb = self.duplicate_for_recurrent
self.notify('task_count_all')
Expand All @@ -978,7 +977,6 @@ def remove(self, item_id: UUID) -> None:
item = self.lookup[item_id]
if item.parent is not None:
self._remove_from_parent_model(item.id)
item.parent.notify('has_children')
else:
pos = self.model.find(item)
self.model.remove(pos[1])
Expand All @@ -996,7 +994,6 @@ def parent(self, item_id: UUID, parent_id: UUID) -> None:
# Remove from UI
if item.parent is not None:
self._remove_from_parent_model(item_id)
item.parent.notify('has_children')
else:
pos = self.model.find(item)
self.model.remove(pos[1])
Expand All @@ -1005,8 +1002,6 @@ def parent(self, item_id: UUID, parent_id: UUID) -> None:

# Add back to UI
self._append_to_parent_model(item_id)
assert item.parent is not None
item.parent.notify('has_children')


def unparent(self, item_id: UUID) -> None:
Expand All @@ -1018,7 +1013,6 @@ def unparent(self, item_id: UUID) -> None:

# Remove from UI
self._remove_from_parent_model(item_id)
parent.notify('has_children')

super().unparent(item_id)

Expand All @@ -1027,7 +1021,6 @@ def unparent(self, item_id: UUID) -> None:

# Add back to UI
self.model.append(item)
parent.notify('has_children')


def filter(self, filter_type: Filter, arg: Union[Tag,List[Tag],None] = None) -> List[Task]:
Expand Down
117 changes: 117 additions & 0 deletions tests/core/test_store_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
from unittest import TestCase
from uuid import uuid4

from gi.repository import GObject

from GTG.core.base_store import StoreItem


Expand Down Expand Up @@ -52,6 +54,121 @@ def test_children_count_multiple_children(self):



class Dummy(GObject.Object):
"""A dummy GObject that helps test bindings."""

int_value = 0
num_of_updates = 0

@GObject.Property(type=int)
def an_int(self):
return self.int_value

@an_int.setter
def an_int(self,value):
self.int_value = value
self.num_of_updates += 1



class TestStoreItemAddChild(TestCase):


def setUp(self):
self.root = StoreItem(uuid4())
self.child = StoreItem(uuid4())
self.root.children = [ self.child ]
self.dummy = Dummy()


def test_add_new_child(self):
new_item = StoreItem(uuid4())
self.root.add_child(new_item)
self.assertIn(new_item,self.root.children)


def test_adding_existing_child_does_nothing(self):
self.root.add_child(self.child)
self.assertEqual(self.root.children,[self.child])


def test_has_child_notification_for_first_child(self):
self.child.bind_property('has_children',self.dummy,'an_int',GObject.BindingFlags.DEFAULT,lambda _,b: 5)
new_item = StoreItem(uuid4())
self.child.add_child(new_item)
self.assertEqual(self.dummy.num_of_updates,1)


def test_children_count_notification(self):
self.root.bind_property('children_count',self.dummy,'an_int',GObject.BindingFlags.DEFAULT)
new_item = StoreItem(uuid4())
self.root.add_child(new_item)
self.assertEqual(self.dummy.num_of_updates,1)


def test_no_has_children_notification_for_non_first_child(self):
self.root.bind_property('has_children',self.dummy,'an_int',GObject.BindingFlags.DEFAULT,lambda _,b: 5)
new_item = StoreItem(uuid4())
self.root.add_child(new_item)
self.assertEqual(self.dummy.num_of_updates,0)


def test_no_children_count_notification_for_existing_child(self):
self.root.bind_property('children_count',self.dummy,'an_int',GObject.BindingFlags.DEFAULT)
self.root.add_child(self.child)
self.assertEqual(self.dummy.num_of_updates,0)



class TestStoreItemRemoveChild(TestCase):


def setUp(self):
self.root = StoreItem(uuid4())
self.child1 = StoreItem(uuid4())
self.child2 = StoreItem(uuid4())
self.root.children = [ self.child1, self.child2 ]
self.dummy = Dummy()


def test_remove_child(self):
self.root.remove_child(self.child1)
self.assertNotIn(self.child1,self.root.children)


def test_removing_non_existing_child_does_nothing(self):
new_item = StoreItem(uuid4())
self.root.remove_child(new_item)
self.assertEqual(self.root.children,[self.child1,self.child2])


def test_has_child_notification_for_last_child(self):
self.root.bind_property('has_children',self.dummy,'an_int',GObject.BindingFlags.DEFAULT,lambda _,b: 5)
self.root.remove_child(self.child1)
self.root.remove_child(self.child2)
self.assertEqual(self.dummy.num_of_updates,1)


def test_children_count_notification(self):
self.root.bind_property('children_count',self.dummy,'an_int',GObject.BindingFlags.DEFAULT)
self.root.remove_child(self.child1)
self.assertEqual(self.dummy.num_of_updates,1)


def test_no_has_children_notification_for_non_last_child(self):
self.root.bind_property('has_children',self.dummy,'an_int',GObject.BindingFlags.DEFAULT,lambda _,b: 5)
self.root.remove_child(self.child2)
self.assertEqual(self.dummy.num_of_updates,0)


def test_no_children_count_notification_for_non_existing_child(self):
self.root.bind_property('children_count',self.dummy,'an_int',GObject.BindingFlags.DEFAULT)
new_item = StoreItem(uuid4())
self.root.remove_child(new_item)
self.assertEqual(self.dummy.num_of_updates,0)



class TestStoreItemGetAncestors(TestCase):


Expand Down

0 comments on commit 913d2aa

Please sign in to comment.