From 2210b909a47cedeff322f860db0169dc71f2723f Mon Sep 17 00:00:00 2001 From: Michael Booth Date: Mon, 15 May 2023 12:49:46 +1000 Subject: [PATCH] Cleanup, tweak main screen and added About page --- Dockerfile | 25 ++++ README_pdm.md | 3 + app_config.toml | 2 +- .../emmaus_walking.cache.feather | Bin docs/about.md | 8 ++ pdm.lock | 12 +- pyproject.toml | 1 + requirements.txt | 117 ++++++++++++++++++ src/{app.py => Emmaus_Walking.py} | 48 ++++--- src/helper_functions.py | 38 ++++++ src/pages/1_About.py | 3 + 11 files changed, 241 insertions(+), 16 deletions(-) create mode 100644 Dockerfile create mode 100644 README_pdm.md rename emmaus_walking.cache.feather => data/emmaus_walking.cache.feather (100%) create mode 100644 docs/about.md create mode 100644 requirements.txt rename src/{app.py => Emmaus_Walking.py} (84%) create mode 100644 src/helper_functions.py create mode 100644 src/pages/1_About.py diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e8ed8c2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,25 @@ +# See https://stackoverflow.com/questions/68673221/warning-running-pip-as-the-root-user +# for enhancements to Dockerfile e.g. not running as root & in venv + +FROM python:3.11-slim-bullseye +RUN apt-get update && apt-get install -y + +EXPOSE 8080 + +RUN pip install -U pip + +COPY requirements.txt requirements.txt +RUN pip install -r requirements.txt + +# Copy into a directory of its own (so it isn't in the toplevel dir) + +COPY .streamlit app/.streamlit +COPY data app/data +COPY docs app/docs +COPY src app/src + +WORKDIR /app + +# Run it! + +ENTRYPOINT ["streamlit", "run", "src/Emmaus_Walking.py", "--server.port=8080", "--server.address=0.0.0.0"] diff --git a/README_pdm.md b/README_pdm.md new file mode 100644 index 0000000..d6a8bca --- /dev/null +++ b/README_pdm.md @@ -0,0 +1,3 @@ +pdm update + +pdm export --o requirements.txt --without-hashes diff --git a/app_config.toml b/app_config.toml index d995af7..1afcccf 100644 --- a/app_config.toml +++ b/app_config.toml @@ -2,7 +2,7 @@ DATASOURCE = 'Apple Watch via Health Fit' AUTHOR = 'by [DataBooth.com.au](https://www.databooth.com.au)' NAME = 'Emmaus Walking Mapping App' -CACHED_DATAFILE = 'emmaus_walking.cache.feather' +CACHED_DATAFILE = 'data/emmaus_walking.cache.feather' LAYOUT = 'wide' [S3] # Cloud Object Storage: https://console.scaleway.com diff --git a/emmaus_walking.cache.feather b/data/emmaus_walking.cache.feather similarity index 100% rename from emmaus_walking.cache.feather rename to data/emmaus_walking.cache.feather diff --git a/docs/about.md b/docs/about.md new file mode 100644 index 0000000..601b121 --- /dev/null +++ b/docs/about.md @@ -0,0 +1,8 @@ +### Emmaus Walking App + +Simple [streamlit.io](https://www.streamlit.io) web app to display some of the major walking trails that I have done. + +Data has been captured on a number of Apple Watches. + + +Developed by [DataBooth](https://www.databooth.com.au). \ No newline at end of file diff --git a/pdm.lock b/pdm.lock index 10ad39b..7ec4973 100644 --- a/pdm.lock +++ b/pdm.lock @@ -919,6 +919,12 @@ version = "0.10.2" requires_python = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" summary = "Python Library for Tom's Obvious, Minimal Language" +[[package]] +name = "tomli" +version = "2.0.1" +requires_python = ">=3.7" +summary = "A lil' TOML parser" + [[package]] name = "toolz" version = "0.12.0" @@ -1017,7 +1023,7 @@ summary = "Backport of pathlib-compatible object wrapper for zip files" lock_version = "4.2" cross_platform = true groups = ["default", "dev"] -content_hash = "sha256:b3f69b1ecb9b30d9a84b0f47dc707517468401c47a15a97d3865d995b8f13611" +content_hash = "sha256:008c81a25d1df7a5113ffabfb68af9bf54fd6c4253cc5284ce6c01bbe9b0f9ef" [metadata.files] "altair 4.2.2" = [ @@ -1968,6 +1974,10 @@ content_hash = "sha256:b3f69b1ecb9b30d9a84b0f47dc707517468401c47a15a97d3865d995b {url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] +"tomli 2.0.1" = [ + {url = "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {url = "https://files.pythonhosted.org/packages/c0/3f/d7af728f075fb08564c5949a9c95e44352e23dee646869fa104a3b2060a3/tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] "toolz 0.12.0" = [ {url = "https://files.pythonhosted.org/packages/7f/5c/922a3508f5bda2892be3df86c74f9cf1e01217c2b1f8a0ac4841d903e3e9/toolz-0.12.0-py3-none-any.whl", hash = "sha256:2059bd4148deb1884bb0eb770a3cde70e7f954cfbbdc2285f1f2de01fd21eb6f"}, {url = "https://files.pythonhosted.org/packages/cf/05/2008534bbaa716b46a2d795d7b54b999d0f7638fbb9ed0b6e87bfa934f84/toolz-0.12.0.tar.gz", hash = "sha256:88c570861c440ee3f2f6037c4654613228ff40c93a6c25e0eba70d17282c6194"}, diff --git a/pyproject.toml b/pyproject.toml index 21c9af7..51706da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ dependencies = [ "streamlit-folium>=0.11.1", "folium>=0.14.0", "Pillow>=9.5.0", + "tomli>=2.0.1", ] requires-python = ">=3.10" license = {text = "DataBooth"} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f20cc6f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,117 @@ +# This file is @generated by PDM. +# Please do not edit it manually. + +altair==4.2.2 +anyio==3.6.2 +appnope==0.1.3 +argon2-cffi==21.3.0 +argon2-cffi-bindings==21.2.0 +arrow==1.2.3 +asttokens==2.2.1 +attrs==23.1.0 +backcall==0.2.0 +beautifulsoup4==4.12.2 +bleach==6.0.0 +blinker==1.6.2 +branca==0.6.0 +cachetools==5.3.0 +certifi==2023.5.7 +cffi==1.15.1 +charset-normalizer==3.1.0 +click==8.1.3 +comm==0.1.3 +debugpy==1.6.7 +decorator==5.1.1 +defusedxml==0.7.1 +entrypoints==0.4 +executing==1.2.0 +fastjsonschema==2.16.3 +folium==0.14.0 +fqdn==1.5.1 +gitdb==4.0.10 +gitpython==3.1.31 +idna==3.4 +importlib-metadata==6.6.0 +ipykernel==6.23.0 +ipython==8.13.2 +ipython-genutils==0.2.0 +isoduration==20.11.0 +jedi==0.18.2 +jinja2==3.1.2 +jsonpointer==2.3 +jsonschema==4.17.3 +jupyter-client==8.2.0 +jupyter-core==5.3.0 +jupyter-events==0.6.3 +jupyter-server==2.5.0 +jupyter-server-terminals==0.4.4 +jupyterlab-pygments==0.2.2 +markdown-it-py==2.2.0 +MarkupSafe==2.1.2 +matplotlib-inline==0.1.6 +mdurl==0.1.2 +mistune==2.0.5 +nbclassic==1.0.0 +nbclient==0.7.4 +nbconvert==7.4.0 +nbformat==5.8.0 +nest-asyncio==1.5.6 +notebook==6.5.4 +notebook-shim==0.2.3 +numpy==1.24.3 +packaging==23.1 +pandas==2.0.1 +pandocfilters==1.5.0 +parso==0.8.3 +pexpect==4.8.0 +pickleshare==0.7.5 +Pillow==9.5.0 +platformdirs==3.5.1 +prometheus-client==0.16.0 +prompt-toolkit==3.0.38 +protobuf==3.20.3 +psutil==5.9.5 +ptyprocess==0.7.0 +pure-eval==0.2.2 +pyarrow==12.0.0 +pycparser==2.21 +pydeck==0.8.1b0 +pygments==2.15.1 +pympler==1.0.1 +pyrsistent==0.19.3 +python-dateutil==2.8.2 +python-json-logger==2.0.7 +pytz==2023.3 +pyyaml==6.0 +pyzmq==25.0.2 +requests==2.30.0 +rfc3339-validator==0.1.4 +rfc3986-validator==0.1.1 +rich==13.3.5 +Send2Trash==1.8.2 +six==1.16.0 +smmap==5.0.0 +sniffio==1.3.0 +soupsieve==2.4.1 +stack-data==0.6.2 +streamlit==1.22.0 +streamlit-folium==0.11.1 +tenacity==8.2.2 +terminado==0.17.1 +tinycss2==1.2.1 +toml==0.10.2 +toolz==0.12.0 +tornado==6.3.2 +traitlets==5.9.0 +typing-extensions==4.5.0 +tzdata==2023.3 +tzlocal==5.0 +uri-template==1.2.0 +urllib3==2.0.2 +validators==0.20.0 +watchdog==3.0.0 +wcwidth==0.2.6 +webcolors==1.13 +webencodings==0.5.1 +websocket-client==1.5.1 +zipp==3.15.0 diff --git a/src/app.py b/src/Emmaus_Walking.py similarity index 84% rename from src/app.py rename to src/Emmaus_Walking.py index 7e34cf3..effc0c9 100644 --- a/src/app.py +++ b/src/Emmaus_Walking.py @@ -6,12 +6,12 @@ import folium from PIL import Image -# from datapipe import load_and_cache_raw_walk_data, calc_walk_stats +# from datapipe import load_and_cache_raw_walk_data, calc_walk_stats - no longer needed DATA_INFO = "Apple Watch via Health Fit" AUTHOR_INFO = "by [DataBooth.com.au](https://www.databooth.com.au)" -APP_NAME = "Emmaus Walking Mapping App" -CACHED_WALK_DATA = "emmaus_walking.cache.feather" +APP_TITLE = "Emmaus Walking Mapping App" +CACHED_WALK_DATA = "data/emmaus_walking.cache.feather" IMAGE_PATH = "src/resources" IMAGE_PATH = Path.cwd().resolve() / IMAGE_PATH @@ -30,14 +30,36 @@ WALK_NAME += ["ALL: All Walks"] -st.set_page_config(page_title=APP_NAME, layout="wide") +# Note this must be the first function call in the Streamlit app code + +st.set_page_config( + page_title=APP_TITLE, + page_icon="🚶", + layout="wide", + initial_sidebar_state="expanded", + menu_items={ + 'Get Help': 'https://www.databooth.com.au/help', + 'Report a bug': "https://www.databooth.com.au/bug", + "About": "Created with love & care at DataBooth - www.databooth.com.au" + }, +) + +# config = read_toml_file() +# SUB_TITLE = config["st-app"]["SUB_TITLE"] + +def create_app_header(app_title, subtitle=None): + st.header(app_title) + if subtitle is not None: + st.subheader(subtitle) + return None + + +# create_app_header(APP_TITLE) class SideBar: datasource = DATA_INFO datasize = 0 # look to calculate this (in MB?) - TEST: Comment change - author = AUTHOR_INFO - data_title = "Data details..." data_local = False start_date = dt.date.today() end_date = dt.date.today() @@ -66,9 +88,8 @@ def plot_walk_points(walk_points, map_handle, linecolour, linewidth): folium.PolyLine(walk_points, color=linecolour, weight=linewidth).add_to(map_handle) -def app_sidebar(APP_NAME): +def app_sidebar(): sb = SideBar() - st.sidebar.info(APP_NAME) col1, col2 = st.sidebar.columns(2) with col1: image1 = Image.open(IMAGE_PATH / "AppleWatchExercise.jpeg").resize( @@ -78,7 +99,7 @@ def app_sidebar(APP_NAME): with col2: image2 = Image.open(IMAGE_PATH / "HealthFitLogo.png") st.image(image=image2, use_column_width=True, output_format="PNG") - st.sidebar.markdown(sb.author) + # st.sidebar.markdown(sb.author) # st.sidebar.markdown(sb.datasource) # st.sidebar.info(sb.data_title) # st.sidebar.markdown('Datasize: ' + str(sb.datasize)) @@ -100,9 +121,8 @@ def load_cached_walking_data(): return pd.read_feather(CACHED_WALK_DATA) -def app_mainscreen(APP_NAME, sb): - # st.title(APP_NAME) - st.header(sb.walk_name) +def app_mainscreen(APP_TITLE, sb): + st.subheader(sb.walk_name) # Load walking data # OLD_WAY --------------------------------------------------------------------------------------------- @@ -167,5 +187,5 @@ def app_mainscreen(APP_NAME, sb): # return map_handle, walk_data, walk_date, walk_points -sb = app_sidebar(APP_NAME) -map_handle = app_mainscreen(APP_NAME, sb) +sb = app_sidebar() +map_handle = app_mainscreen(APP_TITLE, sb) diff --git a/src/helper_functions.py b/src/helper_functions.py new file mode 100644 index 0000000..b5a7d9e --- /dev/null +++ b/src/helper_functions.py @@ -0,0 +1,38 @@ +from pathlib import Path + +import tomli as tomllib +from IPython.display import Markdown, display +from streamlit import cache_data, markdown + + +@cache_data +def st_read_markdown_file(markdown_file): + return Path(markdown_file).read_text() + + +def read_markdown_file(markdown_file): + return Path(markdown_file).read_text() + + +def read_render_markdown_file(markdown_file, output="jupyter"): + if output == "jupyter": + try: + md_text = read_markdown_file(markdown_file) + display(Markdown(md_text)) + except Exception: + print(f"Error with markdown file: {markdown_file}") + return None + else: + try: + md_text = st_read_markdown_file(markdown_file) + markdown(md_text, unsafe_allow_html=True) + except Exception: + print(f"Error with markdown file: {markdown_file}") + return None + + +@cache_data +def read_toml_file(toml_file="./src/app_config.toml"): + with open(toml_file, "rb") as f: + toml = tomllib.load(f) + return toml diff --git a/src/pages/1_About.py b/src/pages/1_About.py new file mode 100644 index 0000000..5fa081f --- /dev/null +++ b/src/pages/1_About.py @@ -0,0 +1,3 @@ +from helper_functions import read_render_markdown_file + +read_render_markdown_file("docs/about.md", "streamlit")