2021-09-26 03:25:30 +00:00
|
|
|
from __future__ import annotations
|
2021-09-25 19:47:26 +00:00
|
|
|
import logging
|
2021-09-28 04:20:20 +00:00
|
|
|
import re
|
2021-09-25 19:47:26 +00:00
|
|
|
|
2021-09-28 04:20:20 +00:00
|
|
|
from typing import Optional, Type, TypeVar
|
2021-09-25 19:47:26 +00:00
|
|
|
|
|
|
|
logger = logging.getLogger("TodoObject")
|
|
|
|
logger.setLevel(logging.INFO)
|
|
|
|
logger.addHandler(logging.StreamHandler())
|
|
|
|
|
|
|
|
class TodoObject:
|
2021-09-28 04:20:20 +00:00
|
|
|
"""Base TodoObject for all classes in the todo library."""
|
2021-09-25 19:47:26 +00:00
|
|
|
def __init__(self, level, text, parent = None):
|
|
|
|
self.text: str = text
|
|
|
|
self.level: int = level
|
|
|
|
self.parents: list[TodoObject] = []
|
|
|
|
self.children: list[TodoObject] = []
|
|
|
|
self.parent: Optional[TodoObject] = parent
|
|
|
|
if parent is not None:
|
2021-09-28 04:20:20 +00:00
|
|
|
self._set_parents(parent)
|
2021-09-25 19:47:26 +00:00
|
|
|
|
2021-09-28 04:20:20 +00:00
|
|
|
def get_children(self, immediate = False, obj_type: Optional[Type[T]] = None) -> list[T]:
|
|
|
|
"""Get all children of an object. Optionally specify a class of TodoObject to return.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
immediate (bool, optional): Whether it should return immediate children only. Defaults to False.
|
2021-09-28 05:36:12 +00:00
|
|
|
obj_type (Type[Note | Category | Task], optional): Specify the types of children to return. Defaults to None.
|
2021-09-28 04:20:20 +00:00
|
|
|
|
|
|
|
Returns:
|
2021-09-28 05:36:12 +00:00
|
|
|
list[Note | Category | Task]: Returns all children that match the criteria.
|
2021-09-28 04:20:20 +00:00
|
|
|
"""
|
2021-09-25 19:47:26 +00:00
|
|
|
output = []
|
|
|
|
for child in self.children:
|
2021-09-26 03:25:30 +00:00
|
|
|
if obj_type is not None and isinstance(child, obj_type) and immediate and child.parent is self:
|
2021-09-25 19:47:26 +00:00
|
|
|
output.append(child)
|
2021-09-26 03:25:30 +00:00
|
|
|
elif obj_type is None and immediate and child.parent is self:
|
|
|
|
output.append(child)
|
|
|
|
elif obj_type is not None and isinstance(child, obj_type) and not immediate:
|
|
|
|
output.append(child)
|
|
|
|
elif obj_type is None and not immediate:
|
2021-09-25 19:47:26 +00:00
|
|
|
output.append(child)
|
2021-09-25 23:47:55 +00:00
|
|
|
return output
|
2021-09-25 19:47:26 +00:00
|
|
|
|
2021-09-28 04:20:20 +00:00
|
|
|
def get_parents(self, obj_type: Optional[Type[T]] = None) -> list[T]:
|
|
|
|
"""Get all parents of an object. Optionally specify a class of TodoObject to return.
|
|
|
|
|
|
|
|
Args:
|
2021-09-28 05:36:12 +00:00
|
|
|
obj_type (Type[Note | Category | Task], optional): Specify the types of parents to return. Defaults to None.
|
2021-09-28 04:20:20 +00:00
|
|
|
|
|
|
|
Returns:
|
2021-09-28 05:36:12 +00:00
|
|
|
list[Note | Category | Task]: List of parents found.
|
2021-09-28 04:20:20 +00:00
|
|
|
"""
|
|
|
|
output = []
|
|
|
|
for parent in self.parents:
|
|
|
|
if obj_type is not None and isinstance(parent, obj_type):
|
|
|
|
output.append(parent)
|
|
|
|
if obj_type is None:
|
|
|
|
output.append(parent)
|
|
|
|
return output
|
|
|
|
|
|
|
|
def get_md(self, category_spacing: int = 1) -> str:
|
|
|
|
"""Gets the markdown text of the current data loaded into the object.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
category_spacing (int, optional): Amount of newlines between categories. Defaults to 1.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
str: the markdown text generated
|
|
|
|
"""
|
|
|
|
output = ""
|
|
|
|
for i in self.get_children():
|
|
|
|
if len(output) > 0:
|
|
|
|
output += "\n"
|
|
|
|
if isinstance(i, Category):
|
|
|
|
if i.level > 0:
|
|
|
|
output += "\n"*category_spacing
|
|
|
|
output += f"{i}"
|
|
|
|
elif isinstance(i, Task):
|
|
|
|
output += f"{i.level*2*' '}{i}"
|
|
|
|
elif isinstance(i, Note):
|
|
|
|
output += f"{i.level*2*' '}{i}"
|
|
|
|
return output
|
|
|
|
|
|
|
|
def _set_parents(self, parent: TodoObject):
|
|
|
|
"""Set the parents of an object by supplying the first immediate parent.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
parent (TodoObject): The immediate parent of the object.
|
|
|
|
"""
|
2021-09-25 19:47:26 +00:00
|
|
|
for p in parent.parents:
|
|
|
|
logger.debug(f"adding parent '{p.text}' to '{self.text}'")
|
|
|
|
self.parents.append(p)
|
|
|
|
logger.debug(f"adding child '{self.text}' to '{p.text}'")
|
|
|
|
p.children.append(self)
|
|
|
|
logger.debug(f"adding parent '{parent.text}' to '{self.text}'")
|
|
|
|
self.parents.append(parent)
|
|
|
|
logger.debug(f"adding child '{self.text}' to '{parent.text}'")
|
|
|
|
parent.children.append(self)
|
|
|
|
self.parent = parent
|
2021-09-28 04:20:20 +00:00
|
|
|
|
2021-09-28 04:54:49 +00:00
|
|
|
def get_tasks(self, immediate: bool = False, complete: Optional[bool] = None) -> list[Task]:
|
2021-09-28 04:20:20 +00:00
|
|
|
"""Get all tasks from the children of the TodoObject.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
immediate (bool, optional): Whether it should return immediate children only. Defaults to False.
|
|
|
|
complete (Optional[bool], optional): If true, return completed tasks only, inverse if False. Defaults to None.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
list[Task]: List of tasks found.
|
|
|
|
"""
|
|
|
|
if complete is None:
|
|
|
|
return self.get_children(immediate, Task)
|
|
|
|
else:
|
|
|
|
output = []
|
|
|
|
for child in self.get_children(immediate, Task):
|
|
|
|
if child.complete is complete:
|
|
|
|
output.append(child)
|
|
|
|
return output
|
|
|
|
|
2021-09-28 04:54:49 +00:00
|
|
|
def get_task(self, regex: str, immediate: bool = False, complete: Optional[bool] = None) -> Task:
|
2021-09-28 04:20:20 +00:00
|
|
|
"""Get a specific task from a supplied regular expression.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
regex (str): The regular expression to match for the text of the task.
|
|
|
|
immediate (bool, optional): Whether or not it should return immediate children only. Defaults to False.
|
|
|
|
complete (Optional[bool], optional): If true, return completed tasks only, inverse if False. Defaults to None.
|
|
|
|
|
|
|
|
Returns:
|
2021-09-28 05:36:12 +00:00
|
|
|
Task: Returns the task found
|
2021-09-28 04:20:20 +00:00
|
|
|
"""
|
2021-09-28 04:54:49 +00:00
|
|
|
for task in self.get_tasks(immediate, complete):
|
2021-09-28 04:20:20 +00:00
|
|
|
if re.match(regex, task.text):
|
2021-09-28 04:54:49 +00:00
|
|
|
return task
|
2021-09-28 04:20:20 +00:00
|
|
|
|
2021-09-28 04:54:49 +00:00
|
|
|
def get_categories(self, immediate: bool = False) -> list[Category]:
|
2021-09-28 04:20:20 +00:00
|
|
|
"""Gets all categories from children of the TodoObject.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
list[Category]: Returns all categories found.
|
|
|
|
"""
|
|
|
|
return self.get_children(immediate, obj_type=Category)
|
|
|
|
|
|
|
|
def get_category(self, regex: str, immediate: bool = False) -> Category:
|
|
|
|
"""Get a specific task from a supplied regular expression.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
regex (str): The regular expression to match for the text of the category.
|
|
|
|
immediate (bool, optional): Whether or not it should return immediate children only. Defaults to False.
|
|
|
|
|
|
|
|
Returns:
|
2021-09-28 05:36:12 +00:00
|
|
|
Category: Returns the category found.
|
2021-09-28 04:20:20 +00:00
|
|
|
"""
|
|
|
|
for category in self.get_categories(immediate):
|
|
|
|
if re.match(regex, category.text):
|
|
|
|
return category
|
|
|
|
|
|
|
|
def get_notes(self, immediate: bool = False) -> list[Note]:
|
|
|
|
"""Get all notes from children of the TodoObject.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
immediate (bool, optional): Whether or not it should return immeduate children only. Defaults to False.
|
2021-09-28 05:36:12 +00:00
|
|
|
|
2021-09-28 04:20:20 +00:00
|
|
|
Returns:
|
|
|
|
Note: Returns all notes found.
|
|
|
|
"""
|
|
|
|
return self.get_children(immediate, obj_type=Note)
|
|
|
|
|
|
|
|
def get_note(self, regex: str, immediate: bool = False) -> Note:
|
|
|
|
"""Get a specific note from a supplied regular expression.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
regex (str): The regular expression to match for the text of the note.
|
|
|
|
immediate (bool, optional): Whether or not it should return immediate children only. Defaults to False.
|
|
|
|
|
|
|
|
Returns:
|
2021-09-28 05:36:12 +00:00
|
|
|
Note: Returns the note found.
|
2021-09-28 04:20:20 +00:00
|
|
|
"""
|
2021-09-28 04:54:49 +00:00
|
|
|
for note in self.get_notes(immediate):
|
2021-09-28 04:20:20 +00:00
|
|
|
if re.match(regex, note.text):
|
|
|
|
return note
|
|
|
|
|
|
|
|
@property
|
|
|
|
def has_children(self) -> bool:
|
|
|
|
if len(self.children) > 0:
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
|
|
|
from .Note import Note
|
|
|
|
from .Category import Category
|
|
|
|
from .Task import Task
|
|
|
|
T = TypeVar("T", Note, Category, Task)
|