Skip to content

Commit

Permalink
View pivot table (#56)
Browse files Browse the repository at this point in the history
  • Loading branch information
SardorSharipov authored May 28, 2024
1 parent d76493c commit ea3ddb1
Show file tree
Hide file tree
Showing 18 changed files with 735 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
pip install -r requirements.txt
- name: Test with pytest
run: |
pip install pytest pytest pytest-asyncio
pip install pytest pytest-asyncio
pytest ./src
- name: Lint with Ruff
run: |
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ boards.txt
server/mongo_data
.vscode
venv
boards.txt
=======
.coverage
27 changes: 27 additions & 0 deletions src/internal/controller/impl/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,33 @@ def edit_linked_objects(
action.do()
self._undo_redo_manager.store_action(action)

def add_attribute(
self, attr_name: str, value: str
):
for obj in self._repo.get_all():
if isinstance(obj, internal.objects.interfaces.IBoardObjectCard):
attribute = obj.attribute
attribute[attr_name] = value
obj.attribute = attribute.copy()
logging.debug('added new attribute=%s to all cards with value=%s', attr_name, value)

def edit_attribute(
self, obj_id: internal.objects.interfaces.ObjectId, attr_name: str, value: str
):
obj: typing.Optional[internal.objects.interfaces.IBoardObjectCard] = self._repo.get(obj_id)
if obj:

attributes = obj.attribute
attributes[attr_name] = value
obj.attribute = attributes.copy()
logging.debug(
'editing attribute of an object old value=%s with new value=%s',
obj.attribute[attr_name], value
)
self._on_feature_finish()
return
logging.debug('no object id=%s found to edit with attribute=%s', obj_id, attr_name)

def undo_last_action(self):
logging.debug('controller was asked to undo last action')
self._undo_redo_manager.undo()
Expand Down
13 changes: 13 additions & 0 deletions src/internal/controller/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,27 @@ def edit_connector_type(
def edit_stroke_style(self, obj_id: internal.objects.interfaces.ObjectId, stroke_style: str):
pass

@abc.abstractmethod
def edit_table(self, obj_id: internal.objects.interfaces.ObjectId, list_col, list_row):
pass

@abc.abstractmethod
def edit_linked_objects(
self, obj_id: internal.objects.interfaces.ObjectId, linked_obj: typing.Dict[str, list[int]]
):
pass

@abc.abstractmethod
def add_attribute(self, attr_name: str, value: str):
pass

@abc.abstractmethod
def edit_attribute(
self, obj_id: internal.objects.interfaces.ObjectId, attr_name: str, value: str
):
pass

@abc.abstractmethod
def undo_last_action(self):
pass

Expand Down
14 changes: 14 additions & 0 deletions src/internal/objects/impl/card.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
_COLOR_FIELD = 'color'
_WIDTH_FIELD = 'width'
_HEIGHT_FIELD = 'height'
_ATTRIBUTED_FIELD = 'attribute'


class BoardObjectCard(interfaces.IBoardObjectCard, BoardObjectWithFont):

def __init__(
self,
id: interfaces.ObjectId,
Expand All @@ -29,11 +31,13 @@ def __init__(
color: str = 'light yellow',
width: int = 150,
height: int = 150,
attribute: dict[str, str] = dict(), # noqa
):
super().__init__(id, types.BoardObjectType.CARD, create_dttm, position, pub_sub_broker, text, font)
self.color = color
self.width = width
self.height = height
self.attribute = attribute

@property
def color(self) -> str:
Expand Down Expand Up @@ -62,11 +66,20 @@ def height(self, height: int) -> None:
self._height = height
self._publish(events.EventObjectChangedSize(self.id))

@property
def attribute(self) -> dict[str, str]:
return self._attribute

@attribute.setter
def attribute(self, attribute: dict[str, str]) -> None:
self._attribute = attribute

def serialize(self) -> dict:
serialized = super().serialize()
serialized[_COLOR_FIELD] = self.color
serialized[_WIDTH_FIELD] = self.width
serialized[_HEIGHT_FIELD] = self.height
serialized[_ATTRIBUTED_FIELD] = self.attribute
return serialized

@staticmethod
Expand All @@ -85,4 +98,5 @@ def from_serialized(
data[_COLOR_FIELD],
data[_WIDTH_FIELD],
data[_HEIGHT_FIELD],
data[_ATTRIBUTED_FIELD],
)
7 changes: 6 additions & 1 deletion src/internal/objects/impl/test_card.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ def test_board_object_card_serialization():
color = 'light blue'
width = 100
height = 150
attribute = {'age': 10}
broker = internal.pub_sub.mocks.MockPubSubBroker()

card = BoardObjectCard(id, create_dttm, position, broker, text, font, color, width, height)
card = BoardObjectCard(id, create_dttm, position, broker, text, font, color, width, height, attribute)
assert card.serialize() == {
'id': id,
'create_dttm': create_dttm.strftime('%Y-%m-%dT%H-%M-%SZ'),
Expand All @@ -30,6 +31,7 @@ def test_board_object_card_serialization():
'color': color,
'width': width,
'height': height,
'attribute': attribute,
}


Expand All @@ -43,6 +45,7 @@ def test_board_object_card_deserialization():
color = 'light blue'
width = 100
height = 150
attribute = {'age': '10'}
serialized = {
'id': id,
'create_dttm': create_dttm.strftime('%Y-%m-%dT%H-%M-%SZ'),
Expand All @@ -53,6 +56,7 @@ def test_board_object_card_deserialization():
'color': color,
'width': width,
'height': height,
'attribute': attribute,
}

broker = internal.pub_sub.mocks.MockPubSubBroker()
Expand All @@ -67,3 +71,4 @@ def test_board_object_card_deserialization():
assert card.color == color
assert card.width == width
assert card.height == height
assert card.attribute == attribute
10 changes: 10 additions & 0 deletions src/internal/objects/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,16 @@ def height(self) -> int:
def height(self, height: int) -> None:
pass

@property
@abc.abstractmethod
def attribute(self) -> dict[str, str]:
pass

@attribute.setter
@abc.abstractmethod
def attribute(self, attribute: dict[str, str]) -> None:
pass


class IBoardObjectPen(IBoardObject):
DEFAULT_WIDTH = 2
Expand Down
1 change: 1 addition & 0 deletions src/internal/objects/test_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def test_card_building():
'color': 'light yellow',
'width': 100,
'height': 150,
'attribute': {'age': '10'}
}
broker = internal.pub_sub.mocks.MockPubSubBroker()

Expand Down
1 change: 1 addition & 0 deletions src/internal/repositories/impl/test_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def _impl():
'color': 'light yellow',
'width': 100,
'height': 150,
'attribute': {'age': '10'},
}

return _impl
Expand Down
53 changes: 53 additions & 0 deletions src/internal/view/modules/card/card_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,19 @@ def widgets(
label, combobox = func(dependencies)
_widgets.append(label)
_widgets.append(combobox)
card: internal.objects.interfaces.IBoardObjectCard = dependencies.repo.get(self.id)

for attr, value in card.attribute.items():
label, entry = self._attribute_widget(
dependencies,
self.id,
attr,
self._get_attribute,
self._set_attribute
)
_widgets.append(label)
_widgets.append(entry)

return _widgets

def _widgets_func(self) -> List[Callable]:
Expand Down Expand Up @@ -221,6 +234,30 @@ def _base_widget(
string_var.trace('w', lambda *_: setter(dependencies, string_var.get()))
return label, combobox

def _attribute_widget(self,
dependencies: internal.view.dependencies.Dependencies,
obj_id: internal.objects.interfaces.ObjectId,
description: str,
getter: Callable,
setter: Callable
) -> List[tkinter.ttk.Widget]:
string_var = tkinter.StringVar(value=getter(dependencies, obj_id, description))
label = tkinter.ttk.Label(
dependencies.property_bar,
text=description,
justify='left',
anchor='w'
)
entry = tkinter.ttk.Entry(
dependencies.property_bar,
textvariable=string_var,
)
string_var.trace('w', lambda *_: setter(
dependencies, obj_id, description, string_var.get()
))

return label, entry

def move_to(self, dependencies: internal.view.dependencies.Dependencies, x: int, y: int):
self._update_coord_from_repo(dependencies)

Expand Down Expand Up @@ -324,6 +361,22 @@ def _set_card_size(self, dependencies: internal.view.dependencies.Dependencies,
else:
dependencies.controller.edit_size(self.id, _DEFAULT_LARGE_SIZE, _DEFAULT_LARGE_SIZE)

def _get_attribute(self,
dependencies: internal.view.dependencies.Dependencies,
obj_id: internal.objects.interfaces.ObjectId,
attr_name: str
):
card: internal.objects.interfaces.IBoardObjectCard = dependencies.repo.get(obj_id)
return card.attribute[attr_name]

def _set_attribute(self,
dependencies: internal.view.dependencies.Dependencies,
obj_id: internal.objects.interfaces.ObjectId,
attr_name: str,
value: str
):
dependencies.controller.edit_attribute(obj_id, attr_name, value)

def destroy(self, dependencies: internal.view.dependencies.Dependencies):
self._unsubscribe_from_repo_object_events(dependencies)
ViewObject.destroy(self, dependencies)
21 changes: 21 additions & 0 deletions src/internal/view/modules/pivot_table/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from __future__ import annotations

import internal.view.dependencies
from .states import add_attribute_state, show_axis_state
from .pivot_table_view import open_window as open_window


def create_states(dependencies: internal.view.dependencies):
dependencies.state_machine.add_state(add_attribute_state.create_state(dependencies.state_machine))
dependencies.state_machine.add_state(show_axis_state.create_state(dependencies.state_machine))


def register_module_menu(dependencies: internal.view.dependencies):
dependencies.menu.add_command_to_menu(add_attribute_state.ADD_ATTR_MENU_ENTRY_NAME)
dependencies.menu.add_command_to_menu(show_axis_state.SHOW_TABLE_MENU_ENTRY_NAME)


@internal.view.modules.modules.register_module('pivot_table')
def init_module(dependencies: internal.view.dependencies):
create_states(dependencies)
register_module_menu(dependencies)
Loading

0 comments on commit ea3ddb1

Please sign in to comment.