Skip to content
This repository was archived by the owner on Apr 23, 2024. It is now read-only.

Commit 8fff1fa

Browse files
committed
Merge branch 'release/1.4.0'
2 parents 4c85a48 + 4c01ef6 commit 8fff1fa

File tree

44 files changed

+2074
-58
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+2074
-58
lines changed

.pre-commit-config.yaml

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
repos:
22
- repo: https://github.com/ambv/black
3-
rev: stable
3+
rev: 22.3.0
44
hooks:
55
- id: black
66
- repo: https://gitlab.com/pycqa/flake8
7-
rev: 3.7.9
7+
rev: 3.9.2
88
hooks:
99
- id: flake8
1010
- repo: https://github.com/pycqa/isort
11-
rev: 5.7.0
11+
rev: 5.10.1
1212
hooks:
1313
- id: isort
1414
args: ["--profile", "black", "--filter-files"]

CHANGELOG.md

+15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
11
# Changelog
22

3+
## [v1.4.0](https://github.com/SebRut/pygrocy/tree/v1.4.0) (2022-07-24)
4+
5+
[Full Changelog](https://github.com/SebRut/pygrocy/compare/v1.3.0...v1.4.0)
6+
7+
**Merged pull requests:**
8+
9+
- Add system endpoints [\#247](https://github.com/SebRut/pygrocy/pull/247) ([marcelvriend](https://github.com/marcelvriend))
10+
- Add aggregated amounts to stock info [\#246](https://github.com/SebRut/pygrocy/pull/246) ([marcelvriend](https://github.com/marcelvriend))
11+
- Return empty list instead of None [\#245](https://github.com/SebRut/pygrocy/pull/245) ([marcelvriend](https://github.com/marcelvriend))
12+
- Get details for all batteries [\#244](https://github.com/SebRut/pygrocy/pull/244) ([marcelvriend](https://github.com/marcelvriend))
13+
- Add open\_product API support [\#243](https://github.com/SebRut/pygrocy/pull/243) ([grablair](https://github.com/grablair))
14+
- Add support for filter conditions [\#242](https://github.com/SebRut/pygrocy/pull/242) ([marcelvriend](https://github.com/marcelvriend))
15+
- Add consume recipe endpoint [\#241](https://github.com/SebRut/pygrocy/pull/241) ([marcelvriend](https://github.com/marcelvriend))
16+
- Add support for skipping chores [\#240](https://github.com/SebRut/pygrocy/pull/240) ([marcelvriend](https://github.com/marcelvriend))
17+
318
## [v1.3.0](https://github.com/SebRut/pygrocy/tree/v1.3.0) (2022-06-05)
419

520
[Full Changelog](https://github.com/SebRut/pygrocy/compare/v1.2.1...v1.3.0)

pygrocy/data_models/battery.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
from datetime import datetime
22

33
from pygrocy.base import DataModel
4-
from pygrocy.grocy_api_client import BatteryDetailsResponse, CurrentBatteryResponse
4+
from pygrocy.grocy_api_client import (
5+
BatteryDetailsResponse,
6+
CurrentBatteryResponse,
7+
GrocyApiClient,
8+
)
59

610

711
class Battery(DataModel):
@@ -22,6 +26,7 @@ def _init_from_CurrentBatteryResponse(self, response: CurrentBatteryResponse):
2226
def _init_from_BatteryDetailsResponse(self, response: BatteryDetailsResponse):
2327
self._charge_cycles_count = response.charge_cycles_count
2428
self._last_charged = response.last_charged
29+
self._last_tracked_time = response.last_charged # For compatibility
2530
self._id = response.battery.id
2631
self._name = response.battery.name
2732
self._description = response.battery.description
@@ -41,6 +46,10 @@ def _init_empty(self):
4146
self._created_timestamp = None
4247
self._userfields = None
4348

49+
def get_details(self, api_client: GrocyApiClient):
50+
details = api_client.get_battery(self._id)
51+
self._init_from_BatteryDetailsResponse(details)
52+
4453
@property
4554
def id(self) -> int:
4655
return self._id

pygrocy/data_models/product.py

+24
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ def _init_empty(self):
7070
self._is_partly_in_stock = None
7171

7272
self._available_amount = None
73+
self._amount_aggregated = None
74+
self._amount_opened = None
75+
self._amount_opened_aggregated = None
76+
self._is_aggregated_amount = None
7377
self._best_before_date = None
7478

7579
self._default_quantity_unit_purchase = None
@@ -81,6 +85,10 @@ def _init_empty(self):
8185
def _init_from_CurrentStockResponse(self, response: CurrentStockResponse):
8286
self._id = response.product_id
8387
self._available_amount = response.amount
88+
self._amount_aggregated = response.amount_aggregated
89+
self._amount_opened = response.amount_opened
90+
self._amount_opened_aggregated = response.amount_opened_aggregated
91+
self._is_aggregated_amount = response.is_aggregated_amount
8492
self._best_before_date = response.best_before_date
8593

8694
if response.product:
@@ -136,6 +144,22 @@ def product_group_id(self) -> int:
136144
def available_amount(self) -> float:
137145
return self._available_amount
138146

147+
@property
148+
def amount_aggregated(self) -> float:
149+
return self._amount_aggregated
150+
151+
@property
152+
def amount_opened(self) -> float:
153+
return self._amount_opened
154+
155+
@property
156+
def amount_opened_aggregated(self) -> float:
157+
return self._amount_opened_aggregated
158+
159+
@property
160+
def is_aggregated_amount(self) -> bool:
161+
return self._is_aggregated_amount
162+
139163
@property
140164
def best_before_date(self) -> datetime.date:
141165
return self._best_before_date

pygrocy/data_models/system.py

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
from datetime import date, datetime
2+
from typing import List
3+
4+
from pygrocy.base import DataModel
5+
from pygrocy.grocy_api_client import SystemConfigDto, SystemInfoDto, SystemTimeDto
6+
7+
8+
class SystemInfo(DataModel):
9+
def __init__(self, system_info_dto: SystemInfoDto):
10+
self._grocy_version = system_info_dto.grocy_version_info.version
11+
self._grocy_release_date = system_info_dto.grocy_version_info.release_date
12+
self._php_version = system_info_dto.php_version
13+
self._sqlite_version = system_info_dto.sqlite_version
14+
self._os = system_info_dto.os
15+
self._client = system_info_dto.client
16+
17+
@property
18+
def grocy_version(self) -> str:
19+
return self._grocy_version
20+
21+
@property
22+
def grocy_release_date(self) -> date:
23+
return self._grocy_release_date
24+
25+
@property
26+
def php_version(self) -> str:
27+
return self._php_version
28+
29+
@property
30+
def sqlite_version(self) -> str:
31+
return self._sqlite_version
32+
33+
@property
34+
def os(self) -> str:
35+
return self._os
36+
37+
@property
38+
def client(self) -> str:
39+
return self._client
40+
41+
42+
class SystemTime(DataModel):
43+
def __init__(self, system_time_dto: SystemTimeDto):
44+
self._timezone = system_time_dto.timezone
45+
self._time_local = system_time_dto.time_local
46+
self._time_local_sqlite3 = system_time_dto.time_local_sqlite3
47+
self._time_utc = system_time_dto.time_utc
48+
self._timestamp = system_time_dto.timestamp
49+
50+
@property
51+
def timezone(self) -> str:
52+
return self._timezone
53+
54+
@property
55+
def time_local(self) -> datetime:
56+
return self._time_local
57+
58+
@property
59+
def time_local_sqlite3(self) -> datetime:
60+
return self._time_local_sqlite3
61+
62+
@property
63+
def time_utc(self) -> datetime:
64+
return self._time_utc
65+
66+
@property
67+
def timestamp(self) -> int:
68+
return self._timestamp
69+
70+
71+
class SystemConfig(DataModel):
72+
def __init__(self, system_config_dto: SystemConfigDto):
73+
self._username = system_config_dto.username
74+
self._base_path = system_config_dto.base_path
75+
self._base_url = system_config_dto.base_url
76+
self._mode = system_config_dto.mode
77+
self._default_locale = system_config_dto.default_locale
78+
self._locale = system_config_dto.locale
79+
self._currency = system_config_dto.currency
80+
81+
self._enabled_features = []
82+
for feature, value in system_config_dto.feature_flags.items():
83+
if bool(value):
84+
self._enabled_features.append(feature)
85+
86+
@property
87+
def username(self) -> str:
88+
return self._username
89+
90+
@property
91+
def base_path(self) -> str:
92+
return self._base_path
93+
94+
@property
95+
def base_url(self) -> str:
96+
return self._base_url
97+
98+
@property
99+
def mode(self) -> str:
100+
return self._mode
101+
102+
@property
103+
def default_locale(self) -> str:
104+
return self._default_locale
105+
106+
@property
107+
def locale(self) -> str:
108+
return self._locale
109+
110+
@property
111+
def currency(self) -> str:
112+
return self._currency
113+
114+
@property
115+
def enabled_features(self) -> List[str]:
116+
return self._enabled_features

pygrocy/grocy.py

+70-18
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from .data_models.generic import EntityType
1111
from .data_models.meal_items import MealPlanItem, MealPlanSection, RecipeItem
1212
from .data_models.product import Group, Product, ShoppingListProduct
13+
from .data_models.system import SystemConfig, SystemInfo, SystemTime
1314
from .data_models.task import Task
1415
from .data_models.user import User # noqa: F401
1516
from .errors import GrocyError # noqa: F401
@@ -109,8 +110,10 @@ def all_products(self) -> List[Product]:
109110
product_datas = [ProductData(**product) for product in raw_products]
110111
return [Product(product) for product in product_datas]
111112

112-
def chores(self, get_details: bool = False) -> List[Chore]:
113-
raw_chores = self._api_client.get_chores()
113+
def chores(
114+
self, get_details: bool = False, query_filters: List[str] = None
115+
) -> List[Chore]:
116+
raw_chores = self._api_client.get_chores(query_filters)
114117
chores = [Chore(chore) for chore in raw_chores]
115118

116119
if get_details:
@@ -123,8 +126,9 @@ def execute_chore(
123126
chore_id: int,
124127
done_by: int = None,
125128
tracked_time: datetime = datetime.now(),
129+
skipped: bool = False,
126130
):
127-
return self._api_client.execute_chore(chore_id, done_by, tracked_time)
131+
return self._api_client.execute_chore(chore_id, done_by, tracked_time, skipped)
128132

129133
def chore(self, chore_id: int) -> Chore:
130134
resp = self._api_client.get_chore(chore_id)
@@ -154,6 +158,22 @@ def consume_product(
154158
product_id, amount, spoiled, transaction_type, allow_subproduct_substitution
155159
)
156160

161+
def consume_recipe(
162+
self,
163+
recipe_id: int,
164+
):
165+
return self._api_client.consume_recipe(recipe_id)
166+
167+
def open_product(
168+
self,
169+
product_id: int,
170+
amount: float = 1,
171+
allow_subproduct_substitution: bool = False,
172+
):
173+
return self._api_client.open_product(
174+
product_id, amount, allow_subproduct_substitution
175+
)
176+
157177
def inventory_product(
158178
self,
159179
product_id: int,
@@ -231,8 +251,10 @@ def inventory_product_by_barcode(
231251
product.get_details(self._api_client)
232252
return product
233253

234-
def shopping_list(self, get_details: bool = False) -> List[ShoppingListProduct]:
235-
raw_shoppinglist = self._api_client.get_shopping_list()
254+
def shopping_list(
255+
self, get_details: bool = False, query_filters: List[str] = None
256+
) -> List[ShoppingListProduct]:
257+
raw_shoppinglist = self._api_client.get_shopping_list(query_filters)
236258
shopping_list = [ShoppingListProduct(resp) for resp in raw_shoppinglist]
237259

238260
if get_details:
@@ -264,8 +286,8 @@ def remove_product_in_shopping_list(
264286
product_id, shopping_list_id, amount
265287
)
266288

267-
def product_groups(self) -> List[Group]:
268-
raw_groups = self._api_client.get_product_groups()
289+
def product_groups(self, query_filters: List[str] = None) -> List[Group]:
290+
raw_groups = self._api_client.get_product_groups(query_filters)
269291
return [Group(resp) for resp in raw_groups]
270292

271293
def add_product_pic(self, product_id: int, pic_path: str):
@@ -281,8 +303,23 @@ def set_userfields(self, entity: str, object_id: int, key: str, value):
281303
def get_last_db_changed(self):
282304
return self._api_client.get_last_db_changed()
283305

284-
def tasks(self) -> List[Task]:
285-
raw_tasks = self._api_client.get_tasks()
306+
def get_system_info(self) -> SystemInfo:
307+
raw_system_info = self._api_client.get_system_info()
308+
if raw_system_info:
309+
return SystemInfo(raw_system_info)
310+
311+
def get_system_time(self) -> SystemTime:
312+
raw_system_time = self._api_client.get_system_time()
313+
if raw_system_time:
314+
return SystemTime(raw_system_time)
315+
316+
def get_system_config(self) -> SystemConfig:
317+
raw_system_config = self._api_client.get_system_config()
318+
if raw_system_config:
319+
return SystemConfig(raw_system_config)
320+
321+
def tasks(self, query_filters: List[str] = None) -> List[Task]:
322+
raw_tasks = self._api_client.get_tasks(query_filters)
286323
return [Task(task) for task in raw_tasks]
287324

288325
def task(self, task_id: int) -> Task:
@@ -292,8 +329,10 @@ def task(self, task_id: int) -> Task:
292329
def complete_task(self, task_id, done_time: datetime = datetime.now()):
293330
return self._api_client.complete_task(task_id, done_time)
294331

295-
def meal_plan(self, get_details: bool = False) -> List[MealPlanItem]:
296-
raw_meal_plan = self._api_client.get_meal_plan()
332+
def meal_plan(
333+
self, get_details: bool = False, query_filters: List[str] = None
334+
) -> List[MealPlanItem]:
335+
raw_meal_plan = self._api_client.get_meal_plan(query_filters)
297336
meal_plan = [MealPlanItem(data) for data in raw_meal_plan]
298337

299338
if get_details:
@@ -306,9 +345,16 @@ def recipe(self, recipe_id: int) -> RecipeItem:
306345
if recipe:
307346
return RecipeItem(recipe)
308347

309-
def batteries(self) -> List[Battery]:
310-
raw_batteries = self._api_client.get_batteries()
311-
return [Battery(bat) for bat in raw_batteries]
348+
def batteries(
349+
self, query_filters: List[str] = None, get_details: bool = False
350+
) -> List[Battery]:
351+
raw_batteries = self._api_client.get_batteries(query_filters)
352+
batteries = [Battery(bat) for bat in raw_batteries]
353+
354+
if get_details:
355+
for item in batteries:
356+
item.get_details(self._api_client)
357+
return batteries
312358

313359
def battery(self, battery_id: int) -> Battery:
314360
battery = self._api_client.get_battery(battery_id)
@@ -329,11 +375,17 @@ def update_generic(self, entity_type: EntityType, object_id: int, updated_data):
329375
def delete_generic(self, entity_type: EntityType, object_id: int):
330376
return self._api_client.delete_generic(entity_type, object_id)
331377

332-
def get_generic_objects_for_type(self, entity_type: EntityType):
333-
return self._api_client.get_generic_objects_for_type(entity_type.value)
378+
def get_generic_objects_for_type(
379+
self, entity_type: EntityType, query_filters: List[str] = None
380+
):
381+
return self._api_client.get_generic_objects_for_type(
382+
entity_type.value, query_filters
383+
)
334384

335-
def meal_plan_sections(self) -> List[MealPlanSection]:
336-
raw_sections = self._api_client.get_meal_plan_sections()
385+
def meal_plan_sections(
386+
self, query_filters: List[str] = None
387+
) -> List[MealPlanSection]:
388+
raw_sections = self._api_client.get_meal_plan_sections(query_filters)
337389
return [MealPlanSection(section) for section in raw_sections]
338390

339391
def meal_plan_section(self, meal_plan_section_id: int) -> MealPlanSection:

0 commit comments

Comments
 (0)