Remember the Milk - updating and completing tasks (#11069)
* Remember the Milk - updating and completing tasks Added new feature so that tasks can be updated and completed. For this feature a task id must be set when creating the task. * fixed hould complaints * fixed review comments by @MartinHjelmare * removed unnecessary check as proposed by @MartinHjelmarepull/11374/head
parent
3fd620198e
commit
7759ab6919
homeassistant/components/remember_the_milk
tests/components
|
@ -15,7 +15,8 @@ import json
|
|||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config import load_yaml_config_file
|
||||
from homeassistant.const import (CONF_API_KEY, STATE_OK, CONF_TOKEN, CONF_NAME)
|
||||
from homeassistant.const import (CONF_API_KEY, STATE_OK, CONF_TOKEN,
|
||||
CONF_NAME, CONF_ID)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
|
@ -31,6 +32,10 @@ DEFAULT_NAME = DOMAIN
|
|||
GROUP_NAME_RTM = 'remember the milk accounts'
|
||||
|
||||
CONF_SHARED_SECRET = 'shared_secret'
|
||||
CONF_ID_MAP = 'id_map'
|
||||
CONF_LIST_ID = 'list_id'
|
||||
CONF_TIMESERIES_ID = 'timeseries_id'
|
||||
CONF_TASK_ID = 'task_id'
|
||||
|
||||
RTM_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_NAME): cv.string,
|
||||
|
@ -44,9 +49,15 @@ CONFIG_SCHEMA = vol.Schema({
|
|||
|
||||
CONFIG_FILE_NAME = '.remember_the_milk.conf'
|
||||
SERVICE_CREATE_TASK = 'create_task'
|
||||
SERVICE_COMPLETE_TASK = 'complete_task'
|
||||
|
||||
SERVICE_SCHEMA_CREATE_TASK = vol.Schema({
|
||||
vol.Required(CONF_NAME): cv.string,
|
||||
vol.Optional(CONF_ID): cv.string,
|
||||
})
|
||||
|
||||
SERVICE_SCHEMA_COMPLETE_TASK = vol.Schema({
|
||||
vol.Required(CONF_ID): cv.string,
|
||||
})
|
||||
|
||||
|
||||
|
@ -84,10 +95,14 @@ def _create_instance(hass, account_name, api_key, shared_secret,
|
|||
entity = RememberTheMilk(account_name, api_key, shared_secret,
|
||||
token, stored_rtm_config)
|
||||
component.add_entity(entity)
|
||||
hass.services.async_register(
|
||||
hass.services.register(
|
||||
DOMAIN, '{}_create_task'.format(account_name), entity.create_task,
|
||||
description=descriptions.get(SERVICE_CREATE_TASK),
|
||||
schema=SERVICE_SCHEMA_CREATE_TASK)
|
||||
hass.services.register(
|
||||
DOMAIN, '{}_complete_task'.format(account_name), entity.complete_task,
|
||||
description=descriptions.get(SERVICE_COMPLETE_TASK),
|
||||
schema=SERVICE_SCHEMA_COMPLETE_TASK)
|
||||
|
||||
|
||||
def _register_new_account(hass, account_name, api_key, shared_secret,
|
||||
|
@ -168,8 +183,7 @@ class RememberTheMilkConfiguration(object):
|
|||
|
||||
def set_token(self, profile_name, token):
|
||||
"""Store a new server token for a profile."""
|
||||
if profile_name not in self._config:
|
||||
self._config[profile_name] = dict()
|
||||
self._initialize_profile(profile_name)
|
||||
self._config[profile_name][CONF_TOKEN] = token
|
||||
self.save_config()
|
||||
|
||||
|
@ -181,6 +195,44 @@ class RememberTheMilkConfiguration(object):
|
|||
self._config.pop(profile_name, None)
|
||||
self.save_config()
|
||||
|
||||
def _initialize_profile(self, profile_name):
|
||||
"""Initialize the data structures for a profile."""
|
||||
if profile_name not in self._config:
|
||||
self._config[profile_name] = dict()
|
||||
if CONF_ID_MAP not in self._config[profile_name]:
|
||||
self._config[profile_name][CONF_ID_MAP] = dict()
|
||||
|
||||
def get_rtm_id(self, profile_name, hass_id):
|
||||
"""Get the rtm ids for a home assistant task id.
|
||||
|
||||
The id of a rtm tasks consists of the tuple:
|
||||
list id, timeseries id and the task id.
|
||||
"""
|
||||
self._initialize_profile(profile_name)
|
||||
ids = self._config[profile_name][CONF_ID_MAP].get(hass_id)
|
||||
if ids is None:
|
||||
return None
|
||||
return ids[CONF_LIST_ID], ids[CONF_TIMESERIES_ID], ids[CONF_TASK_ID]
|
||||
|
||||
def set_rtm_id(self, profile_name, hass_id, list_id, time_series_id,
|
||||
rtm_task_id):
|
||||
"""Add/Update the rtm task id for a home assistant task id."""
|
||||
self._initialize_profile(profile_name)
|
||||
id_tuple = {
|
||||
CONF_LIST_ID: list_id,
|
||||
CONF_TIMESERIES_ID: time_series_id,
|
||||
CONF_TASK_ID: rtm_task_id,
|
||||
}
|
||||
self._config[profile_name][CONF_ID_MAP][hass_id] = id_tuple
|
||||
self.save_config()
|
||||
|
||||
def delete_rtm_id(self, profile_name, hass_id):
|
||||
"""Delete a key mapping."""
|
||||
self._initialize_profile(profile_name)
|
||||
if hass_id in self._config[profile_name][CONF_ID_MAP]:
|
||||
del self._config[profile_name][CONF_ID_MAP][hass_id]
|
||||
self.save_config()
|
||||
|
||||
|
||||
class RememberTheMilk(Entity):
|
||||
"""MVP implementation of an interface to Remember The Milk."""
|
||||
|
@ -225,19 +277,65 @@ class RememberTheMilk(Entity):
|
|||
import rtmapi
|
||||
|
||||
try:
|
||||
task_name = call.data.get('name')
|
||||
task_name = call.data.get(CONF_NAME)
|
||||
hass_id = call.data.get(CONF_ID)
|
||||
rtm_id = None
|
||||
if hass_id is not None:
|
||||
rtm_id = self._rtm_config.get_rtm_id(self._name, hass_id)
|
||||
result = self._rtm_api.rtm.timelines.create()
|
||||
timeline = result.timeline.value
|
||||
self._rtm_api.rtm.tasks.add(
|
||||
timeline=timeline, name=task_name, parse='1')
|
||||
_LOGGER.debug('created new task "%s" in account %s',
|
||||
task_name, self.name)
|
||||
|
||||
if hass_id is None or rtm_id is None:
|
||||
result = self._rtm_api.rtm.tasks.add(
|
||||
timeline=timeline, name=task_name, parse='1')
|
||||
_LOGGER.debug('created new task "%s" in account %s',
|
||||
task_name, self.name)
|
||||
self._rtm_config.set_rtm_id(self._name,
|
||||
hass_id,
|
||||
result.list.id,
|
||||
result.list.taskseries.id,
|
||||
result.list.taskseries.task.id)
|
||||
else:
|
||||
self._rtm_api.rtm.tasks.setName(name=task_name,
|
||||
list_id=rtm_id[0],
|
||||
taskseries_id=rtm_id[1],
|
||||
task_id=rtm_id[2],
|
||||
timeline=timeline)
|
||||
_LOGGER.debug('updated task with id "%s" in account '
|
||||
'%s to name %s',
|
||||
hass_id, self.name, task_name)
|
||||
except rtmapi.RtmRequestFailedException as rtm_exception:
|
||||
_LOGGER.error('Error creating new Remember The Milk task for '
|
||||
'account %s: %s', self._name, rtm_exception)
|
||||
return False
|
||||
return True
|
||||
|
||||
def complete_task(self, call):
|
||||
"""Complete a task that was previously created by this component."""
|
||||
import rtmapi
|
||||
|
||||
hass_id = call.data.get(CONF_ID)
|
||||
rtm_id = self._rtm_config.get_rtm_id(self._name, hass_id)
|
||||
if rtm_id is None:
|
||||
_LOGGER.error('Could not find task with id %s in account %s. '
|
||||
'So task could not be closed.',
|
||||
hass_id, self._name)
|
||||
return False
|
||||
try:
|
||||
result = self._rtm_api.rtm.timelines.create()
|
||||
timeline = result.timeline.value
|
||||
self._rtm_api.rtm.tasks.complete(list_id=rtm_id[0],
|
||||
taskseries_id=rtm_id[1],
|
||||
task_id=rtm_id[2],
|
||||
timeline=timeline)
|
||||
self._rtm_config.delete_rtm_id(self._name, hass_id)
|
||||
_LOGGER.debug('Completed task with id %s in account %s',
|
||||
hass_id, self._name)
|
||||
except rtmapi.RtmRequestFailedException as rtm_exception:
|
||||
_LOGGER.error('Error creating new Remember The Milk task for '
|
||||
'account %s: %s', self._name, rtm_exception)
|
||||
return True
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the device."""
|
||||
|
|
|
@ -1,9 +1,24 @@
|
|||
# Describes the format for available Remember The Milk services
|
||||
|
||||
create_task:
|
||||
description: Create a new task in your Remember The Milk account
|
||||
description: >
|
||||
Create (or update) a new task in your Remember The Milk account. If you want to update a task
|
||||
later on, you have to set an "id" when creating the task.
|
||||
Note: Updating a tasks does not support the smart syntax.
|
||||
|
||||
fields:
|
||||
name:
|
||||
description: name of the new task, you can use the smart syntax here
|
||||
example: 'do this ^today #from_hass'
|
||||
example: 'do this ^today #from_hass'
|
||||
|
||||
id:
|
||||
description: (optional) identifier for the task you're creating, can be used to update or complete the task later on
|
||||
example: myid
|
||||
|
||||
complete_task:
|
||||
description: Complete a tasks that was privously created.
|
||||
|
||||
fields:
|
||||
id:
|
||||
description: identifier that was defined when creating the task
|
||||
example: myid
|
|
@ -1,6 +1,7 @@
|
|||
"""Tests for the Remember The Milk component."""
|
||||
|
||||
import logging
|
||||
import json
|
||||
import unittest
|
||||
from unittest.mock import patch, mock_open, Mock
|
||||
|
||||
|
@ -19,7 +20,16 @@ class TestConfiguration(unittest.TestCase):
|
|||
self.hass = get_test_home_assistant()
|
||||
self.profile = "myprofile"
|
||||
self.token = "mytoken"
|
||||
self.json_string = '{"myprofile": {"token": "mytoken"}}'
|
||||
self.json_string = json.dumps(
|
||||
{"myprofile": {
|
||||
"token": "mytoken",
|
||||
"id_map": {"1234": {
|
||||
"list_id": "0",
|
||||
"timeseries_id": "1",
|
||||
"task_id": "2"
|
||||
}}
|
||||
}
|
||||
})
|
||||
|
||||
def tearDown(self):
|
||||
"""Exit home assistant."""
|
||||
|
@ -47,3 +57,29 @@ class TestConfiguration(unittest.TestCase):
|
|||
patch("os.path.isfile", Mock(return_value=True)):
|
||||
config = rtm.RememberTheMilkConfiguration(self.hass)
|
||||
self.assertIsNotNone(config)
|
||||
|
||||
def test_id_map(self):
|
||||
"""Test the hass to rtm task is mapping."""
|
||||
hass_id = "hass-id-1234"
|
||||
list_id = "mylist"
|
||||
timeseries_id = "my_timeseries"
|
||||
rtm_id = "rtm-id-4567"
|
||||
with patch("builtins.open", mock_open()), \
|
||||
patch("os.path.isfile", Mock(return_value=False)):
|
||||
config = rtm.RememberTheMilkConfiguration(self.hass)
|
||||
|
||||
self.assertEqual(None, config.get_rtm_id(self.profile, hass_id))
|
||||
config.set_rtm_id(self.profile, hass_id, list_id, timeseries_id,
|
||||
rtm_id)
|
||||
self.assertEqual((list_id, timeseries_id, rtm_id),
|
||||
config.get_rtm_id(self.profile, hass_id))
|
||||
config.delete_rtm_id(self.profile, hass_id)
|
||||
self.assertEqual(None, config.get_rtm_id(self.profile, hass_id))
|
||||
|
||||
def test_load_key_map(self):
|
||||
"""Test loading an existing key map from the file."""
|
||||
with patch("builtins.open", mock_open(read_data=self.json_string)), \
|
||||
patch("os.path.isfile", Mock(return_value=True)):
|
||||
config = rtm.RememberTheMilkConfiguration(self.hass)
|
||||
self.assertEqual(('0', '1', '2',),
|
||||
config.get_rtm_id(self.profile, "1234"))
|
||||
|
|
Loading…
Reference in New Issue