From 842dcf2d9ea4fd491eccad1720f9d0f483521534 Mon Sep 17 00:00:00 2001 From: Torben <59419684+entorb@users.noreply.github.com> Date: Sat, 14 Dec 2024 07:28:42 +0100 Subject: [PATCH] Added Streamlit --- .streamlit/config.toml | 5 +++++ README.md | 23 +++++++++++++++++++++- cspell-words.txt | 4 ++++ src/app.py | 10 ++++++++++ src/helper.py | 5 +++++ src/pages/Completed.py | 39 ++++++++++++++++++++++++++++++++++++++ src/pages/Overdue.py | 43 ++++++++++++++++++++++++++++++++++++++++++ src/tasks_completed.py | 43 ++++++++++++++++++++++++++++++------------ src/tasks_overdue.py | 33 +++++++++++++++++++++++++++----- 9 files changed, 187 insertions(+), 18 deletions(-) create mode 100644 .streamlit/config.toml create mode 100644 src/app.py create mode 100644 src/pages/Completed.py create mode 100644 src/pages/Overdue.py diff --git a/.streamlit/config.toml b/.streamlit/config.toml new file mode 100644 index 0000000..c57747c --- /dev/null +++ b/.streamlit/config.toml @@ -0,0 +1,5 @@ +[browser] +gatherUsageStats = false + +[server] +port = 8501 diff --git a/README.md b/README.md index 5125bc5..cb71c0d 100644 --- a/README.md +++ b/README.md @@ -23,12 +23,23 @@ store these info in [rememberthemilk.ini](src/rememberthemilk.ini) (see [remembe pip install -r requirements.txt ``` -optionally: +#### Dev Tools + +optionally: ruff and pre-commit ```sh pip install ruff pre-commit ``` +optionally: pytest coverage report + +```sh +pip install pytest-cov +pytest --cov +# or +pytest --cov --cov-report=html:coverage_report +``` + ### Obtain API token run [auth.py](src/auth.py) once and add the resulting `token` to [rememberthemilk.ini](src/rememberthemilk.ini) @@ -51,6 +62,16 @@ run [auth.py](src/auth.py) once and add the resulting `token` to [rememberthemil * ranked by product of overdue days x priority, to focus on most urgent ones * display time estimation in minutes to motivate you for solving the minor ones right away +## Streamlit for interactive data analysis + +```sh +pip install streamlit watchdog +``` + +```sh +streamlit run src/app.py +``` + ## My RTM lifehacks see original post at diff --git a/cspell-words.txt b/cspell-words.txt index 052b22c..6dd93d3 100644 --- a/cspell-words.txt +++ b/cspell-words.txt @@ -2,13 +2,17 @@ abcbazfegbaryxzfoo astype autouse +DataFrame frob lifehacks Menke noqa prio +pytest rememberthemilk rrule +selectbox +Streamlit Taschengeld taskseries todos diff --git a/src/app.py b/src/app.py new file mode 100644 index 0000000..ca89a03 --- /dev/null +++ b/src/app.py @@ -0,0 +1,10 @@ +"""Streamlit UI.""" + +import streamlit as st + +from helper import delete_cache + +st.set_page_config(page_title="RTM Report", page_icon=None, layout="wide") +st.title("RTM") + +delete_cache() diff --git a/src/helper.py b/src/helper.py index 0351e61..02a7d3a 100644 --- a/src/helper.py +++ b/src/helper.py @@ -51,6 +51,11 @@ # +def delete_cache() -> None: # noqa: D103 + for file_path in CACHE_DIR.glob("*.json"): # pragma: no cover + file_path.unlink() + + def dict_to_url_param(d: dict[str, str]) -> str: """ Convert a dictionary of parameter to url parameter string. diff --git a/src/pages/Completed.py b/src/pages/Completed.py new file mode 100644 index 0000000..cd534b4 --- /dev/null +++ b/src/pages/Completed.py @@ -0,0 +1,39 @@ +"""Completed Tasks.""" # noqa: INP001 + +import altair as alt +import streamlit as st + +from tasks_completed import completed_week, get_tasks_completed + +st.set_page_config(page_title="RTM Completed", page_icon=None, layout="wide") +st.title("Completed") + +df = get_tasks_completed() +st.dataframe( + df, + hide_index=True, + column_config={"url": st.column_config.LinkColumn("url", display_text="url")}, +) + +st.header("per Week") +df = completed_week(df) +df = df.reset_index() + +sel_param = st.selectbox( + label="Parameter", + options=("count", "sum_prio", "sum_overdue_prio", "sum_estimate"), +) + +df["completed_week"] = df["completed_week"].astype(str) +chart = ( + alt.Chart(df) + .mark_bar() + .encode( + x=alt.X("completed_week:N", title=None), + y=alt.Y(f"{sel_param}:Q", title=None), + color="list:N", + ) +) +st.altair_chart(chart, use_container_width=True) + +st.dataframe(df, hide_index=True) diff --git a/src/pages/Overdue.py b/src/pages/Overdue.py new file mode 100644 index 0000000..c12ad58 --- /dev/null +++ b/src/pages/Overdue.py @@ -0,0 +1,43 @@ +"""Completed Tasks.""" # noqa: INP001 + +import altair as alt +import streamlit as st + +from tasks_overdue import get_tasks_overdue, group_by_list + +st.set_page_config(page_title="RTM Overdue", page_icon=None, layout="wide") +st.title("Overdue") + +df = get_tasks_overdue().sort_values(by=["overdue"], ascending=[True]) +lists = sorted(set(df["list"].to_list())) + +sel_list = st.selectbox(label="List", index=None, options=lists) + +st.dataframe( + df.query(f"list == '{sel_list}'") if sel_list else df, + hide_index=True, + column_config={"url": st.column_config.LinkColumn("url", display_text="url")}, +) + +st.header("by List") +df = group_by_list(df) +df = df.reset_index() + + +sel_list = st.selectbox( + label="Parameter", + options=("count", "sum_prio", "sum_overdue_prio", "sum_estimate"), +) + +chart = ( + alt.Chart(df) + .mark_bar() + .encode( + x=alt.X("list:N", title=None), + y=alt.Y(f"{sel_list}:Q", title=None), + color="list:N", + ) +) +st.altair_chart(chart, use_container_width=True) + +st.dataframe(df, hide_index=True) diff --git a/src/tasks_completed.py b/src/tasks_completed.py index cb746eb..8a27a12 100644 --- a/src/tasks_completed.py +++ b/src/tasks_completed.py @@ -6,6 +6,8 @@ import datetime as dt +from pandas import DataFrame + from helper import ( DATE_TODAY, df_name_url_to_html, @@ -20,11 +22,8 @@ AND NOT list:Taschengeld""" -if __name__ == "__main__": +def get_tasks_completed() -> DataFrame: # noqa: D103 lists_dict = get_lists_dict() - - print("# RTM tasks completed this year") - df = get_tasks_as_df( my_filter=FILTER_COMPLETED, lists_dict=lists_dict, @@ -34,7 +33,6 @@ ) df = df.reset_index() - df = df_name_url_to_html(df) cols = [ "name", "list", @@ -46,20 +44,41 @@ "overdue_prio", "postponed", "estimate", + "url", ] + df = df[cols] + + return df + + +def completed_week(df: DataFrame) -> DataFrame: # noqa: D103 + df = ( + df.groupby(["completed_week", "list"]) + .agg( + count=("completed_week", "count"), + sum_prio=("prio", "sum"), + sum_overdue_prio=("overdue_prio", "sum"), + sum_estimate=("estimate", "sum"), + ) + .sort_values(by=["completed_week", "list"], ascending=[False, True]) + ) + return df + + +if __name__ == "__main__": + df = get_tasks_completed() + + print("# RTM tasks completed this year") + + df = df_name_url_to_html(df) df_to_html( - df[cols], + df, "out-completed.html", ) # df.to_excel(output_dir/"out-done-year.xlsx", index=False) - df2 = df.groupby(["completed_week", "list"]).agg( - count=("completed_week", "count"), - sum_prio=("prio", "sum"), - sum_overdue_prio=("overdue_prio", "sum"), - sum_estimate=("estimate", "sum"), - ) + df2 = completed_week(df) print(df2) df_to_html(df2, "out-completed-week.html", index=True) diff --git a/src/tasks_overdue.py b/src/tasks_overdue.py index e29ede6..d1b6d29 100644 --- a/src/tasks_overdue.py +++ b/src/tasks_overdue.py @@ -4,6 +4,8 @@ by Dr. Torben Menke https://entorb.net """ +from pandas import DataFrame + from helper import ( df_name_url_to_html, df_to_html, @@ -18,9 +20,8 @@ """ -if __name__ == "__main__": +def get_tasks_overdue() -> DataFrame: # noqa: D103 lists_dict = get_lists_dict() - print("# RTM tasks overdue") df = get_tasks_as_df( my_filter=FILTER_OVERDUE, @@ -29,12 +30,34 @@ df = df.sort_values(by=["overdue_prio"], ascending=False) df = df.reset_index() - cols = ["name", "list", "due", "overdue", "prio", "overdue_prio", "estimate"] + cols = ["name", "list", "due", "overdue", "prio", "overdue_prio", "estimate", "url"] + df = df[cols] + + return df + + +def group_by_list(df: DataFrame) -> DataFrame: # noqa: D103 + df = ( + df.groupby(["list"]) + .agg( + count=("list", "count"), + sum_prio=("prio", "sum"), + sum_overdue_prio=("overdue_prio", "sum"), + sum_estimate=("estimate", "sum"), + ) + .sort_values(by=["list"], ascending=[True]) + ) + return df + + +if __name__ == "__main__": + print("# RTM tasks overdue") + df = get_tasks_overdue() - print(df[cols]) + print(df) df = df_name_url_to_html(df) df_to_html( - df[cols], + df, "out-overdue.html", )