added 3rd party packages, elog, bigtree
This commit is contained in:
0
python310/packages/bigtree/workflows/__init__.py
Normal file
0
python310/packages/bigtree/workflows/__init__.py
Normal file
200
python310/packages/bigtree/workflows/app_calendar.py
Normal file
200
python310/packages/bigtree/workflows/app_calendar.py
Normal file
@@ -0,0 +1,200 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime as dt
|
||||
from typing import Any, Optional, Union
|
||||
|
||||
from bigtree.node.node import Node
|
||||
from bigtree.tree.construct import add_path_to_tree
|
||||
from bigtree.tree.export import tree_to_dataframe
|
||||
from bigtree.tree.search import find_full_path, findall
|
||||
|
||||
try:
|
||||
import pandas as pd
|
||||
except ImportError: # pragma: no cover
|
||||
pd = None
|
||||
|
||||
|
||||
class Calendar:
|
||||
"""
|
||||
Calendar Implementation with Big Tree.
|
||||
- Calendar has four levels - year, month, day, and event name (with event attributes)
|
||||
|
||||
Examples:
|
||||
*Initializing and Adding Events*
|
||||
|
||||
>>> from bigtree import Calendar
|
||||
>>> calendar = Calendar("My Calendar")
|
||||
>>> calendar.add_event("Gym", "2023-01-01 18:00")
|
||||
>>> calendar.add_event("Dinner", "2023-01-01", date_format="%Y-%m-%d", budget=20)
|
||||
>>> calendar.add_event("Gym", "2023-01-02 18:00")
|
||||
>>> calendar.show()
|
||||
My Calendar
|
||||
2023-01-01 00:00:00 - Dinner (budget: 20)
|
||||
2023-01-01 18:00:00 - Gym
|
||||
2023-01-02 18:00:00 - Gym
|
||||
|
||||
*Search for Events*
|
||||
|
||||
>>> calendar.find_event("Gym")
|
||||
2023-01-01 18:00:00 - Gym
|
||||
2023-01-02 18:00:00 - Gym
|
||||
|
||||
*Removing Events*
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> calendar.delete_event("Gym", dt.date(2023, 1, 1))
|
||||
>>> calendar.show()
|
||||
My Calendar
|
||||
2023-01-01 00:00:00 - Dinner (budget: 20)
|
||||
2023-01-02 18:00:00 - Gym
|
||||
|
||||
*Export Calendar*
|
||||
|
||||
>>> calendar.to_dataframe()
|
||||
path name date time budget
|
||||
0 /My Calendar/2023/01/01/Dinner Dinner 2023-01-01 00:00:00 20.0
|
||||
1 /My Calendar/2023/01/02/Gym Gym 2023-01-02 18:00:00 NaN
|
||||
"""
|
||||
|
||||
def __init__(self, name: str):
|
||||
self.calendar = Node(name)
|
||||
self.__sorted = True
|
||||
|
||||
def add_event(
|
||||
self,
|
||||
event_name: str,
|
||||
event_datetime: Union[str, dt.datetime],
|
||||
date_format: str = "%Y-%m-%d %H:%M",
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
"""Add event to calendar
|
||||
|
||||
Args:
|
||||
event_name (str): event name to be added
|
||||
event_datetime (Union[str, dt.datetime]): event date and time
|
||||
date_format (str): specify datetime format if event_datetime is str
|
||||
"""
|
||||
if isinstance(event_datetime, str):
|
||||
event_datetime = dt.datetime.strptime(event_datetime, date_format)
|
||||
year, month, day, date, time = (
|
||||
event_datetime.year,
|
||||
str(event_datetime.month).zfill(2),
|
||||
str(event_datetime.day).zfill(2),
|
||||
event_datetime.date(),
|
||||
event_datetime.time(),
|
||||
)
|
||||
event_path = f"{self.calendar.node_name}/{year}/{month}/{day}/{event_name}"
|
||||
event_attr = {"date": date, "time": time, **kwargs}
|
||||
if find_full_path(self.calendar, event_path):
|
||||
print(
|
||||
f"Event {event_name} exists on {date}, overwriting information for {event_name}"
|
||||
)
|
||||
add_path_to_tree(
|
||||
tree=self.calendar,
|
||||
path=event_path,
|
||||
node_attrs=event_attr,
|
||||
)
|
||||
self.__sorted = False
|
||||
|
||||
def delete_event(
|
||||
self, event_name: str, event_date: Optional[dt.date] = None
|
||||
) -> None:
|
||||
"""Delete event from calendar
|
||||
|
||||
Args:
|
||||
event_name (str): event name to be deleted
|
||||
event_date (dt.date): event date to be deleted
|
||||
"""
|
||||
if event_date:
|
||||
year, month, day = (
|
||||
event_date.year,
|
||||
str(event_date.month).zfill(2),
|
||||
str(event_date.day).zfill(2),
|
||||
)
|
||||
event_path = f"{self.calendar.node_name}/{year}/{month}/{day}/{event_name}"
|
||||
event = find_full_path(self.calendar, event_path)
|
||||
if event:
|
||||
self._delete_event(event)
|
||||
else:
|
||||
print(f"Event {event_name} does not exist on {event_date}")
|
||||
else:
|
||||
for event in findall(
|
||||
self.calendar, lambda node: node.node_name == event_name
|
||||
):
|
||||
self._delete_event(event)
|
||||
|
||||
def find_event(self, event_name: str) -> None:
|
||||
"""Find event by name, prints result to console
|
||||
|
||||
Args:
|
||||
event_name (str): event name
|
||||
"""
|
||||
if not self.__sorted:
|
||||
self._sort()
|
||||
for event in findall(self.calendar, lambda node: node.node_name == event_name):
|
||||
self._show(event)
|
||||
|
||||
def show(self) -> None:
|
||||
"""Show calendar, prints result to console"""
|
||||
if not len(self.calendar.children):
|
||||
raise Exception("Calendar is empty!")
|
||||
if not self.__sorted:
|
||||
self._sort()
|
||||
print(self.calendar.node_name)
|
||||
for event in self.calendar.leaves:
|
||||
self._show(event)
|
||||
|
||||
def to_dataframe(self) -> pd.DataFrame:
|
||||
"""
|
||||
Export calendar to DataFrame
|
||||
|
||||
Returns:
|
||||
(pd.DataFrame)
|
||||
"""
|
||||
if not len(self.calendar.children):
|
||||
raise Exception("Calendar is empty!")
|
||||
data = tree_to_dataframe(self.calendar, all_attrs=True, leaf_only=True)
|
||||
compulsory_cols = ["path", "name", "date", "time"]
|
||||
other_cols = list(set(data.columns) - set(compulsory_cols))
|
||||
return data[compulsory_cols + other_cols]
|
||||
|
||||
def _delete_event(self, event: Node) -> None:
|
||||
"""Private method to delete event, delete parent node as well
|
||||
|
||||
Args:
|
||||
event (Node): event to be deleted
|
||||
"""
|
||||
if len(event.parent.children) == 1:
|
||||
if event.parent.parent:
|
||||
self._delete_event(event.parent)
|
||||
event.parent.parent = None
|
||||
else:
|
||||
event.parent = None
|
||||
|
||||
def _sort(self) -> None:
|
||||
"""Private method to sort calendar by event date, followed by event time"""
|
||||
for day_event in findall(self.calendar, lambda node: node.depth <= 4):
|
||||
if day_event.depth < 4:
|
||||
day_event.sort(key=lambda attr: attr.node_name)
|
||||
else:
|
||||
day_event.sort(key=lambda attr: attr.time)
|
||||
self.__sorted = True
|
||||
|
||||
@staticmethod
|
||||
def _show(event: Node) -> None:
|
||||
"""Private method to show event, handles the formatting of event
|
||||
Prints result to console
|
||||
|
||||
Args:
|
||||
event (Node): event
|
||||
"""
|
||||
event_datetime = dt.datetime.combine(
|
||||
event.get_attr("date"), event.get_attr("time")
|
||||
)
|
||||
event_attrs = event.describe(
|
||||
exclude_attributes=["date", "time", "name"], exclude_prefix="_"
|
||||
)
|
||||
event_attrs_str = ", ".join([f"{attr[0]}: {attr[1]}" for attr in event_attrs])
|
||||
if event_attrs_str:
|
||||
event_attrs_str = f" ({event_attrs_str})"
|
||||
print(f"{event_datetime} - {event.node_name}{event_attrs_str}")
|
||||
261
python310/packages/bigtree/workflows/app_todo.py
Normal file
261
python310/packages/bigtree/workflows/app_todo.py
Normal file
@@ -0,0 +1,261 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
from typing import Any, List, Union
|
||||
|
||||
from bigtree.node.node import Node
|
||||
from bigtree.tree.construct import nested_dict_to_tree
|
||||
from bigtree.tree.export import print_tree, tree_to_nested_dict
|
||||
from bigtree.tree.search import find_child_by_name, find_name
|
||||
|
||||
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
class AppToDo:
|
||||
"""
|
||||
To-Do List Implementation with Big Tree.
|
||||
- To-Do List has three levels - app name, list name, and item name.
|
||||
- If list name is not given, item will be assigned to a `General` list.
|
||||
|
||||
Examples:
|
||||
*Initializing and Adding Items*
|
||||
|
||||
>>> from bigtree import AppToDo
|
||||
>>> app = AppToDo("To Do App")
|
||||
>>> app.add_item(item_name="Homework 1", list_name="School")
|
||||
>>> app.add_item(item_name=["Milk", "Bread"], list_name="Groceries", description="Urgent")
|
||||
>>> app.add_item(item_name="Cook")
|
||||
>>> app.show()
|
||||
To Do App
|
||||
├── School
|
||||
│ └── Homework 1
|
||||
├── Groceries
|
||||
│ ├── Milk [description=Urgent]
|
||||
│ └── Bread [description=Urgent]
|
||||
└── General
|
||||
└── Cook
|
||||
|
||||
*Reorder List and Item*
|
||||
|
||||
>>> app.prioritize_list(list_name="General")
|
||||
>>> app.show()
|
||||
To Do App
|
||||
├── General
|
||||
│ └── Cook
|
||||
├── School
|
||||
│ └── Homework 1
|
||||
└── Groceries
|
||||
├── Milk [description=Urgent]
|
||||
└── Bread [description=Urgent]
|
||||
|
||||
>>> app.prioritize_item(item_name="Bread")
|
||||
>>> app.show()
|
||||
To Do App
|
||||
├── General
|
||||
│ └── Cook
|
||||
├── School
|
||||
│ └── Homework 1
|
||||
└── Groceries
|
||||
├── Bread [description=Urgent]
|
||||
└── Milk [description=Urgent]
|
||||
|
||||
*Removing Items*
|
||||
|
||||
>>> app.remove_item("Homework 1")
|
||||
>>> app.show()
|
||||
To Do App
|
||||
├── General
|
||||
│ └── Cook
|
||||
└── Groceries
|
||||
├── Bread [description=Urgent]
|
||||
└── Milk [description=Urgent]
|
||||
|
||||
*Exporting and Importing List*
|
||||
|
||||
>>> app.save("assets/docstr/list.json")
|
||||
>>> app2 = AppToDo.load("assets/docstr/list.json")
|
||||
>>> app2.show()
|
||||
To Do App
|
||||
├── General
|
||||
│ └── Cook
|
||||
└── Groceries
|
||||
├── Bread [description=Urgent]
|
||||
└── Milk [description=Urgent]
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
app_name: str = "",
|
||||
):
|
||||
"""Initialize To-Do app
|
||||
|
||||
Args:
|
||||
app_name (str): name of to-do app, optional
|
||||
"""
|
||||
self._root = Node(app_name)
|
||||
|
||||
def add_list(self, list_name: str, **kwargs: Any) -> Node:
|
||||
"""Add list to app
|
||||
|
||||
If list is present, return list node, else a new list will be created
|
||||
|
||||
Args:
|
||||
list_name (str): name of list
|
||||
|
||||
Returns:
|
||||
(Node)
|
||||
"""
|
||||
list_node = find_child_by_name(self._root, list_name)
|
||||
if not list_node:
|
||||
list_node = Node(list_name, parent=self._root, **kwargs)
|
||||
logging.info(f"Created list {list_name}")
|
||||
return list_node
|
||||
|
||||
def prioritize_list(self, list_name: str) -> None:
|
||||
"""Prioritize list in app, shift it to be the first list
|
||||
|
||||
Args:
|
||||
list_name (str): name of list
|
||||
"""
|
||||
list_node = find_child_by_name(self._root, list_name)
|
||||
if not list_node:
|
||||
raise ValueError(f"List {list_name} not found")
|
||||
current_children = list(self._root.children)
|
||||
current_children.remove(list_node)
|
||||
current_children.insert(0, list_node)
|
||||
self._root.children = current_children # type: ignore
|
||||
|
||||
def add_item(
|
||||
self, item_name: Union[str, List[str]], list_name: str = "", **kwargs: Any
|
||||
) -> None:
|
||||
"""Add items to list
|
||||
|
||||
Args:
|
||||
item_name (str/List[str]): items to be added
|
||||
list_name (str): list to add items to, optional
|
||||
"""
|
||||
if not isinstance(item_name, str) and not isinstance(item_name, list):
|
||||
raise TypeError("Invalid data type for item")
|
||||
if isinstance(item_name, str):
|
||||
item_name = [item_name]
|
||||
|
||||
# Get list to add to
|
||||
if list_name:
|
||||
list_node = self.add_list(list_name)
|
||||
else:
|
||||
list_node = self.add_list("General")
|
||||
|
||||
# Add items to list
|
||||
for _item in item_name:
|
||||
_ = Node(_item, parent=list_node, **kwargs)
|
||||
logging.info(f"Created item(s) {', '.join(item_name)}")
|
||||
|
||||
def remove_item(
|
||||
self, item_name: Union[str, List[str]], list_name: str = ""
|
||||
) -> None:
|
||||
"""Remove items from list
|
||||
|
||||
Args:
|
||||
item_name (str/List[str]): items to be added
|
||||
list_name (str): list to add items to, optional
|
||||
"""
|
||||
if not isinstance(item_name, str) and not isinstance(item_name, list):
|
||||
raise TypeError("Invalid data type for item")
|
||||
if isinstance(item_name, str):
|
||||
item_name = [item_name]
|
||||
|
||||
# Check if items can be found
|
||||
items_to_remove = []
|
||||
parent_to_check: set[Node] = set()
|
||||
if list_name:
|
||||
list_node = find_child_by_name(self._root, list_name)
|
||||
if not list_node:
|
||||
raise ValueError(f"List {list_name} does not exist!")
|
||||
for _item in item_name:
|
||||
item_node = find_child_by_name(list_node, _item)
|
||||
if not item_node:
|
||||
raise ValueError(f"Item {_item} does not exist!")
|
||||
assert isinstance(item_node.parent, Node) # for mypy type checking
|
||||
items_to_remove.append(item_node)
|
||||
parent_to_check.add(item_node.parent)
|
||||
else:
|
||||
for _item in item_name:
|
||||
item_node = find_name(self._root, _item)
|
||||
if not item_node:
|
||||
raise ValueError(f"Item {_item} does not exist!")
|
||||
assert isinstance(item_node.parent, Node) # for mypy type checking
|
||||
items_to_remove.append(item_node)
|
||||
parent_to_check.add(item_node.parent)
|
||||
|
||||
# Remove items
|
||||
for item_to_remove in items_to_remove:
|
||||
if item_to_remove.depth != 3:
|
||||
raise ValueError(
|
||||
f"Check item to remove {item_to_remove} is an item at item-level"
|
||||
)
|
||||
item_to_remove.parent = None
|
||||
logging.info(
|
||||
f"Removed item(s) {', '.join(item.node_name for item in items_to_remove)}"
|
||||
)
|
||||
|
||||
# Remove list if empty
|
||||
for list_node in parent_to_check:
|
||||
if not len(list(list_node.children)):
|
||||
list_node.parent = None
|
||||
logging.info(f"Removed list {list_node.node_name}")
|
||||
|
||||
def prioritize_item(self, item_name: str) -> None:
|
||||
"""Prioritize item in list, shift it to be the first item in list
|
||||
|
||||
Args:
|
||||
item_name (str): name of item
|
||||
"""
|
||||
item_node = find_name(self._root, item_name)
|
||||
if not item_node:
|
||||
raise ValueError(f"Item {item_name} not found")
|
||||
if item_node.depth != 3:
|
||||
raise ValueError(f"{item_name} is not an item")
|
||||
assert isinstance(item_node.parent, Node) # for mypy type checking
|
||||
current_parent = item_node.parent
|
||||
current_children = list(current_parent.children)
|
||||
current_children.remove(item_node)
|
||||
current_children.insert(0, item_node)
|
||||
current_parent.children = current_children # type: ignore
|
||||
|
||||
def show(self, **kwargs: Any) -> None:
|
||||
"""Print tree to console"""
|
||||
print_tree(self._root, all_attrs=True, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def load(json_path: str) -> AppToDo:
|
||||
"""Load To-Do app from json
|
||||
|
||||
Args:
|
||||
json_path (str): json load path
|
||||
|
||||
Returns:
|
||||
(Self)
|
||||
"""
|
||||
if not json_path.endswith(".json"):
|
||||
raise ValueError("Path should end with .json")
|
||||
|
||||
with open(json_path, "r") as fp:
|
||||
app_dict = json.load(fp)
|
||||
_app = AppToDo("dummy")
|
||||
AppToDo.__setattr__(_app, "_root", nested_dict_to_tree(app_dict["root"]))
|
||||
return _app
|
||||
|
||||
def save(self, json_path: str) -> None:
|
||||
"""Save To-Do app as json
|
||||
|
||||
Args:
|
||||
json_path (str): json save path
|
||||
"""
|
||||
if not json_path.endswith(".json"):
|
||||
raise ValueError("Path should end with .json")
|
||||
|
||||
node_dict = tree_to_nested_dict(self._root, all_attrs=True)
|
||||
app_dict = {"root": node_dict}
|
||||
with open(json_path, "w") as fp:
|
||||
json.dump(app_dict, fp)
|
||||
Reference in New Issue
Block a user