Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Picnic shopping cart as Todo list #102855

Merged
merged 10 commits into from
Nov 22, 2023
2 changes: 1 addition & 1 deletion homeassistant/components/picnic/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from .coordinator import PicnicUpdateCoordinator
from .services import async_register_services

PLATFORMS = [Platform.SENSOR]
PLATFORMS = [Platform.SENSOR, Platform.TODO]


def create_picnic_client(entry: ConfigEntry):
Expand Down
5 changes: 5 additions & 0 deletions homeassistant/components/picnic/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
}
},
"entity": {
"todo": {
"shopping_cart": {
"name": "Shopping cart"
}
},
"sensor": {
"cart_items_count": {
"name": "Cart items count"
Expand Down
75 changes: 75 additions & 0 deletions homeassistant/components/picnic/todo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"""Definition of Picnic shopping cart."""
from __future__ import annotations

import logging
from typing import Any, cast

from homeassistant.components.todo import TodoItem, TodoItemStatus, TodoListEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
)

from .const import CONF_COORDINATOR, DOMAIN

_LOGGER = logging.getLogger(__name__)


async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the Picnic shopping cart todo platform config entry."""
picnic_coordinator = hass.data[DOMAIN][config_entry.entry_id][CONF_COORDINATOR]

# Add an entity shopping card
async_add_entities([PicnicCart(picnic_coordinator, config_entry)])


class PicnicCart(TodoListEntity, CoordinatorEntity):
DCSBL marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please invert the inheritance order.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also add a type annotation to the generic CoordinatorEntity what coordinator type it holds.

"""A Picnic Shopping Cart TodoListEntity."""

_attr_has_entity_name = True
_attr_translation_key = "shopping_cart"
_attr_icon = "mdi:cart"

def __init__(
self,
coordinator: DataUpdateCoordinator[Any],
config_entry: ConfigEntry,
) -> None:
"""Initialize PicnicCart."""
super().__init__(coordinator)
self._attr_device_info = DeviceInfo(
entry_type=DeviceEntryType.SERVICE,
identifiers={(DOMAIN, cast(str, config_entry.unique_id))},
manufacturer="Picnic",
model=config_entry.unique_id,
)
self._attr_unique_id = f"{config_entry.unique_id}-cart"

@property
def todo_items(self) -> list[TodoItem] | None:
"""Get the current set of items in cart items."""
if self.coordinator.data is None:
return None

Check warning on line 60 in homeassistant/components/picnic/todo.py

View check run for this annotation

Codecov / codecov/patch

homeassistant/components/picnic/todo.py#L60

Added line #L60 was not covered by tests
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please note that none of the To-do have this one covered.


_LOGGER.debug(self.coordinator.data["cart_data"]["items"])

items = []
for item in self.coordinator.data["cart_data"]["items"]:
for article in item["items"]:
items.append(
TodoItem(
summary=f"{article['name']} ({article['unit_quantity']})",
uid=f"{item['id']}-{article['id']}",
status=TodoItemStatus.NEEDS_ACTION, # We set 'NEEDS_ACTION' so they count as state
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move the comment above the line to decrease the line length.

)
)

return items
52 changes: 52 additions & 0 deletions tests/components/picnic/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""Conftest for Picnic tests."""
import json
from unittest.mock import MagicMock, patch

import pytest

from homeassistant.components.picnic import CONF_COUNTRY_CODE, DOMAIN
from homeassistant.const import CONF_ACCESS_TOKEN
from homeassistant.core import HomeAssistant

from tests.common import MockConfigEntry, load_fixture


@pytest.fixture
def mock_config_entry() -> MockConfigEntry:
"""Return the default mocked config entry."""
return MockConfigEntry(
domain=DOMAIN,
data={
CONF_ACCESS_TOKEN: "x-original-picnic-auth-token",
CONF_COUNTRY_CODE: "NL",
},
unique_id="295-6y3-1nf4",
)


@pytest.fixture
def mock_picnic_api():
"""Return a mocked PicnicAPI client."""
with patch("homeassistant.components.picnic.PicnicAPI") as mock:
client = mock.return_value
client.session.auth_token = "3q29fpwhulzes"
client.get_cart.return_value = json.loads(load_fixture("picnic/cart.json"))
client.get_user.return_value = json.loads(load_fixture("picnic/user.json"))
client.get_deliveries.return_value = json.loads(
load_fixture("picnic/delivery.json")
)
client.get_delivery_position.return_value = {}
yield client


@pytest.fixture
async def init_integration(
hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_picnic_api: MagicMock
) -> MockConfigEntry:
"""Set up the Picnic integration for testing."""
mock_config_entry.add_to_hass(hass)

await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()

return mock_config_entry
Loading