# ------------------------------------------
# Name: task_functions
# Purpose: Functions creating/managing tasks/displaying.
#
# Author: Robin Siebler
# ------------------------------------------
__author__ = 'Robin Siebler'
import arrow
import platform
import util
from tasklist import Task, TaskList
from collections import OrderedDict
from colorama import init, Fore, Back, Style
if platform.system() == 'Windows':
init()
# TODO: Colors that work on the Mac don't work very well on Windows and vice versa
# TODO: Add an ini file so the user can specify the colors to use. Point to the colorma
# TODO: page for instructions
class Functions:
def __init__(self):
"""Initialize the task list."""
self.tasklist = TaskList()
self.legend = '\nLegend: Not Due ' + Fore.CYAN + Style.BRIGHT + 'Upcoming ' + Fore.BLUE + \
Style.BRIGHT + 'Due ' + Fore.RED + Style.BRIGHT + 'Overdue ' + Fore.WHITE + Style.BRIGHT + \
Back.WHITE + 'Completed' + Fore.RESET + Style.NORMAL + Back.RESET
def show_tasks(self, tasks=None, date_format=None):
"""Display the tasks (in ID order)
:param tasks: tasks object
"""
if not tasks:
tasks = self.tasklist.tasks
if len(tasks) > 0:
template = '{0:^3} {1:20} {2:^3} {3:20} {4:15} {5:20}'
print template.format('\nID', 'Description', ' Pri', 'Due', 'Created', 'Tags')
print template.format('---', '--------------------', '---', '--------------------', '---------------',
'--------------------')
for task in tasks:
if task.priority == 'L':
priority = Fore.YELLOW + Style.BRIGHT + task.priority.center(3) + Fore.RESET + Style.NORMAL
elif task.priority == 'M':
priority = Fore.BLUE + Style.BRIGHT + task.priority.center(3) + Fore.RESET + Style.NORMAL
elif task.priority == 'H':
priority = Fore.RED + Style.BRIGHT + task.priority.center(3) + Fore.RESET + Style.NORMAL
else:
priority = ''
if task.due_date is None:
due_date = ''
else:
if date_format:
due_date = task.due_date.rsplit(' ', 1)[0].ljust(20)
else:
due_date = (arrow.get(task.due_date, task.due_date_format).humanize()).ljust(20)
if not task.completed:
today = arrow.now()
diff = arrow.get(task.due_date, task.due_date_format) - today
if diff.days >= 1 and diff.seconds > 0:
due_date = Fore.CYAN + Style.BRIGHT + due_date + Fore.RESET + Style.NORMAL
elif diff.days >= 0:
due_date = Fore.BLUE + Style.BRIGHT + due_date + Fore.RESET + Style.NORMAL
elif diff.days <= 0:
due_date = Fore.RED + Style.BRIGHT + due_date + Fore.RESET + Style.NORMAL
if date_format:
age = (str(task.creation_date).split()[0]).ljust(15) # drop the time zone
else:
age = (arrow.get(task.creation_date, 'MM/DD/YYYY h:mm:ss A ZZ').humanize()).ljust(15)
if task.note:
desc = task.task + ' *'
else:
desc = task.task
if task.completed:
if task.priority:
priority = task.priority
else:
priority = ''
task_id = Fore.WHITE + Style.BRIGHT + Back.WHITE + str(task.id).center(3)
tags = str(task.tags) + Fore.RESET + Style.NORMAL + Back.RESET
print template.format(task_id, desc, priority, due_date, age, tags)
else:
print template.format(task.id, desc, priority, due_date, age, task.tags)
print self.legend
else:
print('\nThere are no tasks to display!\n')
def show_tasks_by_priority(self, tasks=None, date_format=None):
"""Display the tasks (in Priority order)
:param tasks: tasks object
"""
low_dict_o = OrderedDict()
med_dict_o = OrderedDict()
high_dict_o = OrderedDict()
no_dict_o = OrderedDict()
completed_dict_o = OrderedDict()
low_dict = {}
med_dict = {}
high_dict = {}
no_dict = {}
completed_dict = {}
temp_dict = {}
if not tasks:
tasks = self.tasklist.tasks
if len(tasks) > 0:
for task in tasks:
if task.due_date is None:
due_date = ''
else:
if date_format:
due_date = task.due_date.rsplit(' ', 1)[0].ljust(20)
else:
due_date = (arrow.get(task.due_date, task.due_date_format).humanize()).ljust(20)
age = (str(task.creation_date).split()[0]).ljust(15) # drop the time zone
if task.note:
desc = task.task + ' *'
else:
desc = task.task
if task.completed:
completed_dict[task.id] = task.priority, due_date, age, desc, task.tags
elif task.priority == 'L':
low_dict[task.id] = [task.priority, due_date, age, desc, task.tags]
elif task.priority == 'M':
med_dict[task.id] = [task.priority, due_date, age, desc, task.tags]
elif task.priority == 'H':
high_dict[task.id] = [task.priority, due_date, age, desc, task.tags]
else:
no_dict[task.id] = [task.priority, due_date, age, desc, task.tags]
else:
print('\nThere are no tasks to display!\n')
return
for key, value in sorted(no_dict.items(), key=lambda e: e[1][1]):
if value[1] is not '':
no_dict_o[key] = value
else:
temp_dict[key] = value
for key in temp_dict:
no_dict_o[key] = temp_dict[key]
temp_dict.clear()
for key, value in sorted(low_dict.items(), key=lambda e: e[1][1]):
if value[1] is not '':
low_dict_o[key] = value
else:
temp_dict[key] = value
for key, value in temp_dict.items():
low_dict_o[key] = value
temp_dict.clear()
for key, value in sorted(med_dict.items(), key=lambda e: e[1][1]):
if value[1] is not '':
med_dict_o[key] = value
else:
temp_dict[key] = value
for key, value in temp_dict.items():
med_dict_o[key] = value
temp_dict.clear()
for key, value in sorted(high_dict.items(), key=lambda e: e[1][1]):
if value[1] is not '':
high_dict_o[key] = value
else:
temp_dict[key] = value
for key, value in sorted(temp_dict.items(), key=lambda e: e[1][1]):
high_dict_o[key] = value
temp_dict.clear()
for key, value in sorted(completed_dict.items(), key=lambda e: e[1][1]):
if value[1] is not '':
completed_dict_o[key] = value
else:
temp_dict[key] = value
for key, value in temp_dict.items():
completed_dict_o[key] = value
temp_dict.clear()
del low_dict
del med_dict
del high_dict
del no_dict
del completed_dict
today = arrow.now()
# TODO: Figure out why the key is a tuple instead of a list
for dict in [low_dict_o, med_dict_o, high_dict_o, no_dict_o]:
for key, value in dict.items():
dict[key] = list(dict[key]) # hack - how is this key a tuple!?!
if value[0] == 'L':
dict[key][0] = Fore.YELLOW + Style.BRIGHT + value[0].center(3) + Fore.RESET + Style.NORMAL
elif value[0] == 'M':
dict[key][0] = Fore.BLUE + Style.BRIGHT + value[0].center(3) + Fore.RESET + Style.NORMAL
elif value[0] == 'H':
dict[key][0] = Fore.RED + Style.BRIGHT + value[0].center(3) + Fore.RESET + Style.NORMAL
else:
dict[key][0] = ''
task = self.tasklist.find_task(key)
if task.due_date:
diff = arrow.get(task.due_date, task.due_date_format) - today
if diff.days >= 1 and diff.seconds > 0:
dict[key][1] = Fore.CYAN + Style.BRIGHT + value[1] + Fore.RESET + Style.NORMAL
elif diff.days >= 0:
dict[key][1] = Fore.BLUE + Style.BRIGHT + value[1] + Fore.RESET + Style.NORMAL
elif diff.days <= 0:
dict[key][1] = Fore.RED + Style.BRIGHT + value[1] + Fore.RESET + Style.NORMAL
template = '{0:^3} {1:20} {2:^3} {3:20} {4:15} {5:20}'
print template.format('\nPri', 'Description', 'ID', 'Due', 'Created', 'Tags')
print template.format('---', '--------------------', '---', '--------------------', '---------------',
'--------------------')
if len(high_dict_o) > 0:
for key in high_dict_o:
print template.format(high_dict_o[key][0], high_dict_o[key][3], key, high_dict_o[key][1],
high_dict_o[key][2], high_dict_o[key][4])
if len(med_dict_o) > 0:
for key in med_dict_o:
print template.format(med_dict_o[key][0], med_dict_o[key][3], key, med_dict_o[key][1],
med_dict_o[key][2], med_dict_o[key][4])
if len(low_dict_o) > 0:
for key in low_dict_o:
print template.format(low_dict_o[key][0], low_dict_o[key][3], key, low_dict_o[key][1],
low_dict_o[key][2], low_dict_o[key][4])
if len(no_dict_o) > 0:
for key in no_dict_o:
print template.format(no_dict_o[key][0], no_dict_o[key][3], key, no_dict_o[key][1],
no_dict_o[key][2], no_dict_o[key][4])
completed_template = Fore.WHITE + Style.BRIGHT + Back.WHITE + '{0:^3} {1:20} {2:^3} {3:20} {4:15} {5:20}' + \
Fore.RESET + Style.NORMAL + Back.RESET
if len(completed_dict_o) > 0:
for key in completed_dict_o:
if completed_dict_o[key][0]:
priority = completed_dict_o[key][0]
else:
priority = ''
print completed_template.format(priority, completed_dict_o[key][3], key, completed_dict_o[key][1],
completed_dict_o[key][2], completed_dict_o[key][4])
print self.legend
def show_task(self, task_id):
"""Display the specified task, including its notes, if any.
:param str task_id: the task_id of the task.
"""
task_id = self._validate_task_id(task_id)
if task_id:
task = self.tasklist.find_task(task_id)
if task:
if task.priority == 'L':
priority = Fore.YELLOW + Style.BRIGHT + ' ' + task.priority + ' ' + Fore.RESET + Style.NORMAL
elif task.priority == 'M':
priority = Fore.BLUE + Style.BRIGHT + ' ' + task.priority + ' ' + Fore.RESET + Style.NORMAL
elif task.priority == 'H':
priority = Fore.RED + Style.BRIGHT + ' ' + task.priority + ' ' + Fore.RESET + Style.NORMAL
else:
priority = ''
template = '{0:^3} {1:^3} {2:20} {3:40}'
print template.format('\nID', ' Pri', 'Description', 'Note')
print template.format('---', '---', '--------------------',
'----------------------------------------')
print template.format(task.id, priority, task.task, task.note)
def search_tasks(self, search_string):
"""Search the task list for a task whose contents contains the user provided search string.
:param str search_string: the string to search for.
"""
tasks = self.tasklist.search(search_string.lower())
if tasks:
self.show_tasks(tasks)
else:
print('\nThere were no tasks containing "{}".\n'.format(search_string))
def add_task(self, task, priority=None, due_date=None, tags=None, note=None):
"""Add a new task."""
self.tasklist.add_task(task, priority, due_date, tags, note)
def delete_task(self, task_id):
"""Delete a task."""
task_id = self._validate_task_id(task_id)
if task_id:
self.tasklist.delete_task(task_id)
self.tasklist.renumber_tasks()
print('Task ' + task_id + ' was deleted.')
def modify_task(self, task_id, task_=None, completed=False, priority=None, due_date=None, note=None, tags=None, time=None):
"""Modify a task."""
task_id = self._validate_task_id(task_id)
if task_id:
task = self.tasklist.find_task(task_id)
if task:
print 'Modifying task ' + str(task_id) + ': ' + task.task
if task_:
task.task = task_
elif priority:
task.priority = priority
elif due_date:
if isinstance(due_date, list):
task.due_date = due_date[0]
task.due_date_format = due_date[1]
else:
task.due_date = due_date
elif note:
task.note = note
elif tags:
task.tags = tags
elif time:
time_str = time.split(' ')[0]
time_hour, time_minute = time_str.split(':')
if 'PM' in time:
time_hour = int(time_hour) + 12
due_date = arrow.get(task.due_date, task.due_date_format)
due_date = due_date.replace(hour=time_hour, minute=int(time_minute))
task.due_date = due_date.format(task.due_date_format)
elif completed:
task.completed = True
print 'Modified task ' + str(task_id)
def load_tasks(self, task_file):
"""Load the task file and retrieve the tasks."""
self.tasklist.tasks = util.load(task_file)
Task.last_id = len(self.tasklist.tasks)
def save_tasks(self, task_file):
"""Save the task file."""
util.save(self.tasklist.tasks, task_file)
def _validate_task_id(self, task_id):
"""Validate a task id.
:return: None if an invalid ID was provided, otherwise a string containing the valid task id.
"""
if task_id.isdigit() and int(task_id) <= len(self.tasklist.tasks):
return task_id
else:
print('{} is not an existing task!'.format(task_id))
return None