# -*- coding: utf-8 -*-
""" Implements a Habitica synchronisation task.
"""
# Ensure backwards compatibility with Python 2
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals)
from builtins import *
from datetime import datetime
from tzlocal import get_localzone
from .dates import parse_date_utc
from .task import CharacterAttribute, ChecklistItem, Difficulty, Task
[docs]class HabiticaTask(Task):
""" Defines a Habitica synchronisation task.
"""
[docs] def __init__(self, task_dict=None):
""" Initialise the task.
Args:
task_dict (dict): The Habitica task dictionary, as returned by
HabiticaService.
"""
super().__init__()
if not task_dict:
task_dict = {'text': 'scriptabit todo'}
if not isinstance(task_dict, dict):
raise TypeError(type(task_dict))
self.__task_dict = task_dict
# ensure that some required values are defined
task_dict['type'] = 'todo'
if 'priority' not in task_dict:
task_dict['priority'] = Difficulty.default.value
if 'attribute' not in task_dict:
task_dict['attribute'] = CharacterAttribute.default.value
# The Habitica API chokes if you attempt to update a task with a
# checklist in the request data. To work around this we move the
# checklist (if any) out of task_dict so it can be handled separately.
# We also need separate API calls for deleted, added, and updated
# checklist items items
self.new_checklist_items = []
if 'checklist' in task_dict.keys():
self.existing_checklist_items = task_dict['checklist']
del task_dict['checklist']
else:
self.existing_checklist_items = []
# April 2018: the API now complains if you update a task with the
# collapsechecklist field present. Rather than sort out the reason,
# I remove the field as it serves no relevant purpose.
try:
del task_dict['collapseChecklist']
except KeyError:
pass
@property
def is_challenge(self):
""" Returns True is this is a challenge task. """
return 'challenge' in self.__task_dict and \
'id' in self.__task_dict['challenge']
@property
def task_dict(self):
""" Gets the internal task dictionary. """
return self.__task_dict
@property
def id(self):
""" Task id """
return self.__task_dict['_id']
@property
def name(self):
""" Task name """
return self.__task_dict['text']
@name.setter
def name(self, name):
""" Task name """
self.__task_dict['text'] = name
@property
def description(self):
""" Task description """
return self.__task_dict['notes']
@description.setter
def description(self, description):
""" Task description """
self.__task_dict['notes'] = description
@property
def completed(self):
""" Task completed """
return self.__task_dict['completed']
@completed.setter
def completed(self, completed):
""" Task completed """
self.__task_dict['completed'] = completed
@property
def difficulty(self):
""" Task difficulty """
return Difficulty.from_value(self.__task_dict['priority'])
@difficulty.setter
def difficulty(self, difficulty):
""" Task difficulty """
if not isinstance(difficulty, Difficulty):
raise TypeError
self.__task_dict['priority'] = difficulty.value
@property
def attribute(self):
""" Task character attribute """
return CharacterAttribute.from_value(self.__task_dict['attribute'])
@attribute.setter
def attribute(self, attribute):
""" Task character attribute """
if not isinstance(attribute, CharacterAttribute):
raise TypeError
self.__task_dict['attribute'] = attribute.value
@property
def due_date(self):
""" The due date if there is one, or None. """
datestr = self.__task_dict.get('date', None)
if datestr:
return parse_date_utc(datestr, milliseconds=True)
return None
@due_date.setter
def due_date(self, due_date):
""" Sets or clears the due date. """
if due_date and not isinstance(due_date, datetime):
raise TypeError
if due_date:
self.__task_dict['date'] = \
due_date.astimezone(get_localzone()).date()
elif 'date' in self.__task_dict:
del self.__task_dict['date']
@property
def last_modified(self):
""" The last modified timestamp in UTC. """
timestamp = self.__task_dict['updatedAt']
if timestamp:
return parse_date_utc(timestamp)
@property
def checklist(self):
""" The checklist.
Returns:
list: The checklist, or an empty list if there are no
checklist items.
"""
checklist = []
for i in self.new_checklist_items:
checklist.append(ChecklistItem(
name=i['text'],
checked=i['completed']))
for i in self.existing_checklist_items:
checklist.append(ChecklistItem(
name=i['text'],
checked=i['completed']))
return checklist
@checklist.setter
def checklist(self, checklist):
""" Sets, or clears the checklist. """
for i in checklist:
# create new item
self.new_checklist_items.append({
'text': i.name,
'completed': i.checked})