from typing import Optional, List, Union, Tuple
from time import time
from datetime import datetime
from nextcloud_tasks_api import ApiError
from .api import TasksApi, FetchMode
from .task import TaskList, Task, TaskTree, TaskError
from .prompts import print_err, print_value, print_text
from .prompts import prompt_tasklist, prompt_fetch_mode, prompt_task, prompt_task_op, prompt_task_content, prompt_text
def _do_tasklists(api: TasksApi) -> bool:
default_list: Optional[TaskList] = None
fetch_mode: FetchMode = FetchMode.auto
while True:
task_lists: List[TaskList] = api.get_lists()
task_list: Optional[Union[str, TaskList]] = prompt_tasklist(task_lists, default_list)
if task_list is None:
return True
elif isinstance(task_list, TaskList):
default_list = task_list
if not _do_tasklist(api, fetch_mode, task_list):
return False
elif isinstance(task_list, str) and task_list == 'mode':
fetch_mode = prompt_fetch_mode(fetch_mode)
elif isinstance(task_list, str) and task_list == 'new':
default_list = _create_tasklist(api) or default_list
def _do_tasklist(api: TasksApi, fetch_mode: FetchMode, tasklist: TaskList) -> bool:
last: Optional[Tuple[Task, Optional[int]]] = None
while True:
tasks: TaskTree = api.get_list(tasklist, fetch_mode)
while True:
default: Optional[int] = None
if last is not None:
default = tasks.index(last[0])
if default is None:
default = last[1]
task: Optional[Union[str, Task]] = prompt_task(tasks, default=default)
if task is None:
return True
elif isinstance(task, Task):
last = task, tasks.index(task)
_print_task(task)
if _do_task(api, tasklist, task):
break
elif isinstance(task, str) and task == 'new':
new_task: Optional[Task] = _create_task(api, tasklist, None)
if new_task is not None:
last = new_task, None
break
elif isinstance(task, str) and task == 'edit':
name: Optional[str] = prompt_text("Tasklist name", tasklist.name)
if name is not None and name != tasklist.name:
tasklist.name = name
api.update_list(tasklist)
elif isinstance(task, str) and task == 'delete':
api.delete_list(tasklist)
return True
def _do_task(api: TasksApi, tasklist: TaskList, task: Task) -> bool:
operation: Optional[str] = prompt_task_op()
if operation is None:
return False
elif operation == 'subtask':
return _create_task(api, tasklist, task) is not None
elif operation == 'complete':
return _complete_task(api, task) is not None
elif operation == 'edit':
return _edit_task(api, task) is not None
elif operation == 'delete':
api.delete(task)
return True
else:
return False
def _complete_task(api: TasksApi, task: Task) -> Task:
try:
task.completed = int(time()) if task.completed is None else None
task.apply()
except (ValueError, KeyError) as e:
raise TaskError(f"Cannot update task: {str(e)}") from None
else:
return api.update(task)
def _create_task(api: TasksApi, tasklist: TaskList, parent: Optional[Task]) -> Optional[Task]:
summary, description = prompt_task_content(None, None)
if summary is None:
return None
try:
parent_uid: Optional[str] = parent.uid if parent is not None else None
task: Task = Task(None)
task.summary = summary
if description is not None:
task.description = description
if parent_uid is not None:
task.related_to = parent_uid
except (ValueError, KeyError) as e:
raise TaskError(f"Cannot create task: {str(e)}") from None
else:
return api.create(tasklist, task) # no apply
def _create_tasklist(api: TasksApi) -> Optional[TaskList]:
name: Optional[str] = prompt_text("Tasklist name", validate="[a-zA-Z0-9 _-]*")
if name is None:
return None
try:
return api.create_list(name)
except ApiError as e:
print_err(str(e))
return None
def _print_task(task: Task) -> None:
def format_time(ts: Optional[int]) -> str:
return datetime.fromtimestamp(ts).isoformat(sep=" ") if ts is not None else "-"
try:
print_text(task.description)
print_value("Created ", format_time(task.created))
print_value("Modified ", format_time(task.last_modified))
print_value("Completed", format_time(task.completed))
except (ValueError, KeyError) as e:
print_err(f"Cannot parse task: {str(e)}")
return
def _edit_task(api: TasksApi, task: Task) -> Optional[Task]:
try:
summary, description = prompt_task_content(task.summary, task.description)
except (ValueError, KeyError) as e:
raise TaskError(f"Cannot parse task: {str(e)}") from None
else:
if summary is None:
return None
try:
task.summary = summary
task.description = description
task.last_modified = int(time())
task.apply()
except (ValueError, KeyError) as e:
raise TaskError(f"Cannot update task: {str(e)}") from None
else:
return api.update(task)
def cli_main(api: TasksApi) -> bool:
try:
return _do_tasklists(api)
except (ApiError, TaskError) as e:
print_err(f"{type(e)}: {str(e)}")
return False