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

Commit

Permalink
Merge branch 'release/0.26.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
SebRut committed Feb 13, 2021
2 parents 042063b + e145bcf commit 9710908
Show file tree
Hide file tree
Showing 45 changed files with 2,931 additions and 236 deletions.
11 changes: 0 additions & 11 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ env:
global:
# Doctr deploy key for SebRut/pygrocy
- secure: "YoHepp7ZFuwmdlghnhwT7aczFamnuwTrAuOyXuhxV9bX9eqziurL5K39R04OD5hdPcbNXoVos1YbE4hBGFQpAk6ReiBqqhh+3qDw/HeEpZxRX9dISw+74Nu9kAYXNR8Ab5Zcov85IV3xvTqpawMeBpiXBuJFTbdvZbWhN1jglpR4D9VG//LaNjR/FbCH/41xzpuK3L1mSCQM3jJSSxPU7ZfkWzNvsGj1X///m3la3gR7DL1M4QC9hnzKF6KRNl0kVUuNnK8GhT66ZBGaCVDTqQeDQGTrjsCGuCsQnoydVV2XFwljZ3B6qE27ZqhehlloK2s8/go18zAuj9HpdkTx0RzeUOkxIDp9hAnAys4iGz7Ltk+sx0jGbNsv2Z8oHBgDhv6mu/0UMwD1dL83ZL3L0qWDJfDY3NZSlV0oW9ZPz5ZG8ObnnKKh4vHfp7HUpp/ElYEio7JzQit4FI7gnZRDFEAeH/NvwX8+t0SGXITyyn2U2X5Hz7TUe3laNaDe6Bq59+jebnRdTttuhyoXcMHtjm9HerqRussV+2jyTUf+ktWOFZFjavqUSj6k3iV9yKrgYwfW2+tsaOBZqEHwkRkd5DbNFLvkd0+NEBRfsJV4v51z9QdES3FlPpY/nPqORVkPCLxriCWLf+lTtYi6rtjixvvc4poFFz6WTNILwuOLqrs="
- GROCY_MODE=demo

addons:
apt:
Expand All @@ -18,16 +17,7 @@ addons:
- python3-wheel
- tox

services:
- docker

before_install:
- git clone --branch v3.0.1-8 https://github.com/grocy/grocy-docker.git
- cd grocy-docker
- docker-compose pull
- docker-compose up -d
- curl http://localhost
- cd ..
- pip3 install virtualenv==20.0.23 --force-reinstall

install:
Expand All @@ -42,7 +32,6 @@ script:

after_success:
- coveralls
- cd grocy-docker && docker-compose down && cd ..

deploy:
- provider: pypi
Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# Changelog

## [v0.26.0](https://github.com/SebRut/pygrocy/tree/v0.26.0) (2021-02-13)

[Full Changelog](https://github.com/SebRut/pygrocy/compare/v0.25.0...v0.26.0)

**Implemented enhancements:**

- add support for batteries [\#127](https://github.com/SebRut/pygrocy/issues/127)
- Test data model changes [\#77](https://github.com/SebRut/pygrocy/issues/77)

**Closed issues:**

- make data used in docker grocy instance testing consistent [\#94](https://github.com/SebRut/pygrocy/issues/94)

**Merged pull requests:**

- add batteries support [\#148](https://github.com/SebRut/pygrocy/pull/148) ([SebRut](https://github.com/SebRut))

## [v0.25.0](https://github.com/SebRut/pygrocy/tree/v0.25.0) (2021-02-10)

[Full Changelog](https://github.com/SebRut/pygrocy/compare/v0.24.1...v0.25.0)
Expand Down
7 changes: 0 additions & 7 deletions RELEASE_GUIDE.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
# How to release

## Update used grocy version
1. Create a new branch using `git flow bugfix start update-used-grocy-release`
2. Update the used grocy-docker repository tag in `.travis.yml` to the latest available version
3. Run the tests against these new releases either locally or using Travis CI
4. (Create issues for all upcoming issues)
5. push branch and merge on when checks pass

## Prepare the new release
1. Create a new branch using `git flow release start {NEW_VERSION}` (insert new version number)
2. change the packages version to new version in `setup.py`
Expand Down
86 changes: 86 additions & 0 deletions pygrocy/data_models/battery.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from datetime import datetime

from pygrocy.base import DataModel
from pygrocy.grocy_api_client import BatteryDetailsResponse, CurrentBatteryResponse


class Battery(DataModel):
def __init__(self, response):
self._init_empty()

self._next_estimated_charge_time = response.next_estimated_charge_time

if isinstance(response, CurrentBatteryResponse):
self._init_from_CurrentBatteryResponse(response)
elif isinstance(response, BatteryDetailsResponse):
self._init_from_BatteryDetailsResponse(response)

def _init_from_CurrentBatteryResponse(self, response: CurrentBatteryResponse):
self._id = response.id
self._last_tracked_time = response.last_tracked_time

def _init_from_BatteryDetailsResponse(self, response: BatteryDetailsResponse):
self._charge_cycles_count = response.charge_cycles_count
self._last_charged = response.last_charged
self._id = response.battery.id
self._name = response.battery.name
self._description = response.battery.description
self._used_in = response.battery.used_in
self._charge_interval_days = response.battery.charge_interval_days
self._created_timestamp = response.battery.created_timestamp
self._userfields = response.battery.userfields

def _init_empty(self):
self._last_tracked_time = None
self._charge_cycles_count = None
self._last_charged = None
self._name = None
self._description = None
self._used_in = None
self._charge_interval_days = None
self._created_timestamp = None
self._userfields = None

@property
def id(self) -> int:
return self._id

@property
def name(self) -> str:
return self._name

@property
def description(self) -> str:
return self._description

@property
def used_in(self) -> str:
return self._used_in

@property
def charge_interval_days(self) -> int:
return self._charge_interval_days

@property
def created_timestamp(self) -> datetime:
return self._created_timestamp

@property
def charge_cycles_count(self) -> int:
return self._charge_cycles_count

@property
def userfields(self):
return self._userfields

@property
def last_charged(self) -> datetime:
return self._last_charged

@property
def last_tracked_time(self) -> datetime:
return self._last_tracked_time

@property
def next_estimated_charge_time(self) -> datetime:
return self._next_estimated_charge_time
1 change: 1 addition & 0 deletions pygrocy/data_models/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def _init_from_MissingProductResponse(self, response: MissingProductResponse):

def _init_from_ProductDetailsResponse(self, response: ProductDetailsResponse):
self._id = response.product.id
self._product_group_id = response.product.product_group_id
self._available_amount = response.stock_amount
self._best_before_date = response.next_best_before_date
self._name = response.product.name
Expand Down
13 changes: 13 additions & 0 deletions pygrocy/grocy.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import deprecation

from .base import DataModel # noqa: F401
from .data_models.battery import Battery
from .data_models.chore import Chore
from .data_models.meal_items import MealPlanItem, RecipeItem
from .data_models.product import Group, Product, ShoppingListProduct
Expand Down Expand Up @@ -192,3 +193,15 @@ def recipe(self, recipe_id: int) -> RecipeItem:

def add_generic(self, entity_type, data):
return self._api_client.add_generic(entity_type, data)

def batteries(self) -> List[Battery]:
raw_batteries = self._api_client.get_batteries()
return [Battery(bat) for bat in raw_batteries]

def battery(self, battery_id: int) -> Battery:
battery = self._api_client.get_battery(battery_id)
if battery:
return Battery(battery)

def charge_battery(self, battery_id: int, tracked_time: datetime = datetime.now()):
return self._api_client.charge_battery(battery_id, tracked_time)
60 changes: 56 additions & 4 deletions pygrocy/grocy_api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@

import requests

from pygrocy.utils import localize_datetime, parse_date, parse_float, parse_int
from pygrocy.utils import (
localize_datetime,
parse_bool_int,
parse_date,
parse_float,
parse_int,
)

DEFAULT_PORT_NUMBER = 9192

Expand Down Expand Up @@ -194,12 +200,12 @@ def __init__(self, parsed_json):
self.period_type = parsed_json.get("period_type")
self.period_config = parsed_json.get("period_config")
self.period_days = parse_int(parsed_json.get("period_days"))
self.track_date_only = parsed_json.get("track_date_only")
self.rollover = parsed_json.get("rollover")
self.track_date_only = parse_bool_int(parsed_json.get("track_date_only"))
self.rollover = parse_bool_int(parsed_json.get("rollover"))
self.assignment_type = parsed_json.get("assignment_type")
self.assignment_config = parsed_json.get("assignment_config")
self.next_execution_assigned_to_user_id = parse_int(
"next_execution_assigned_to_user_id"
parsed_json.get("next_execution_assigned_to_user_id")
)
self.userfields = parsed_json.get("userfields")

Expand Down Expand Up @@ -499,6 +505,36 @@ def __init__(self, parsed_json):
self.userfields = parsed_json.get("userfields")


class CurrentBatteryResponse(object):
def __init__(self, parsed_json):
self.id = parse_int(parsed_json.get("battery_id"))
self.last_tracked_time = parse_date(parsed_json.get("last_tracked_time"))
self.next_estimated_charge_time = parse_date(
parsed_json.get("'next_estimated_charge_time")
)


class BatteryData(object):
def __init__(self, parsed_json):
self.id = parse_int(parsed_json.get("id"))
self.name = parsed_json.get("name")
self.description = parsed_json.get("description")
self.used_in = parsed_json.get("used_in")
self.charge_interval_days = parse_int(parsed_json.get("charge_interval_days"))
self.created_timestamp = parse_date(parsed_json.get("row_created_timestamp"))
self.userfields = parsed_json.get("userfields")


class BatteryDetailsResponse(object):
def __init__(self, parsed_json):
self.battery = BatteryData(parsed_json.get("battery"))
self.charge_cycles_count = parse_int(parsed_json.get("charge_cycles_count"))
self.last_charged = parse_date(parsed_json.get("last_charged"))
self.next_estimated_charge_time = parse_date(
parsed_json.get("'next_estimated_charge_time")
)


class GrocyApiClient(object):
def __init__(
self, base_url, api_key, port: int = DEFAULT_PORT_NUMBER, verify_ssl=True
Expand Down Expand Up @@ -703,3 +739,19 @@ def get_recipe(self, object_id: int) -> RecipeDetailsResponse:

def add_generic(self, entity_type: str, data: object):
self._do_post_request(f"objects/{entity_type}", data)

def get_batteries(self) -> List[CurrentBatteryResponse]:
parsed_json = self._do_get_request(f"batteries")
if parsed_json:
return [CurrentBatteryResponse(data) for data in parsed_json]

def get_battery(self, battery_id: int) -> BatteryDetailsResponse:
parsed_json = self._do_get_request(f"batteries/{battery_id}")
if parsed_json:
return BatteryDetailsResponse(parsed_json)

def charge_battery(self, battery_id: int, tracked_time: datetime = datetime.now()):
localized_tracked_time = localize_datetime(tracked_time)
data = {"tracked_time": localized_tracked_time.isoformat()}

return self._do_post_request(f"batteries/{battery_id}/charge", data)
15 changes: 13 additions & 2 deletions pygrocy/utils.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from datetime import datetime

import iso8601
import pytz
from tzlocal import get_localzone
from datetime import datetime


def parse_date(input_value):
if input_value is "" or input_value is None:
if input_value == "" or input_value is None:
return None
return iso8601.parse_date(input_value)

Expand All @@ -28,6 +29,16 @@ def parse_float(input_value, default_value=None):
return default_value


def parse_bool_int(input_value):
if input_value is None:
return False
try:
num = int(input_value)
return bool(num)
except ValueError:
return False


def localize_datetime(timestamp: datetime) -> datetime:
if timestamp.tzinfo is not None:
return timestamp
Expand Down
5 changes: 4 additions & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@ iso8601~=0.1.14
pre-commit
isort
pytest
pytest-cov
pytest-cov
vcrpy
pytest-recording
pytest-mock
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setuptools.setup(
name="pygrocy",
version="0.25.0",
version="0.26.0",
author="Sebastian Rutofski",
author_email="[email protected]",
description="",
Expand Down
49 changes: 49 additions & 0 deletions test/cassettes/test_battery/TestBattery.test_charge_battery.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
interactions:
- request:
body: '{"tracked_time": "2021-02-13T21:43:24.942943+00:00"}'
headers:
Accept-Encoding:
- gzip, deflate
Connection:
- keep-alive
Content-Length:
- '52'
Content-Type:
- application/json
User-Agent:
- python-requests/2.25.1
accept:
- application/json
method: POST
uri: https://localhost/api/batteries/1/charge
response:
body:
string: !!binary |
H4sIAAAAAAAEA3WMQQqAIBBFrxKzTnCm2ngZsXQhlYWNRER3z7Jty/9475/gLShAhBp6w+zioQvJ
gKMZRmc1+9lliSShkCSwqQhV2yjqshSXXQ/RGf7Ejc28/top2CU8ZzKnZbz3XxXSNF03xQBpFpQA
AAA=
headers:
Access-Control-Allow-Headers:
- '*'
Access-Control-Allow-Methods:
- GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Origin:
- '*'
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Sat, 13 Feb 2021 21:43:25 GMT
Server:
- nginx/1.18.0
Transfer-Encoding:
- chunked
X-Powered-By:
- PHP/7.4.14
status:
code: 200
message: OK
version: 1
Loading

0 comments on commit 9710908

Please sign in to comment.