From 8fd4948ee4a4403ac5e7922afeae0d78eac5a487 Mon Sep 17 00:00:00 2001 From: leo Date: Sun, 31 Dec 2023 16:11:21 +0800 Subject: [PATCH] init --- .dockerignore | 2 + .gitignore | 144 +++++++++++++++++++++++++++++++++++++++++++ Dockerfile | 11 ++++ README.md | 5 ++ app/__init__.py | 2 + app/core/__init__.py | 2 + app/core/config.py | 36 +++++++++++ app/core/redis_.py | 15 +++++ main.py | 43 +++++++++++++ pages/form.py | 47 ++++++++++++++ requirements.txt | 4 ++ start.bat | 1 + start.sh | 1 + utils/__init__.py | 2 + 14 files changed, 315 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 app/__init__.py create mode 100644 app/core/__init__.py create mode 100644 app/core/config.py create mode 100644 app/core/redis_.py create mode 100644 main.py create mode 100644 pages/form.py create mode 100644 requirements.txt create mode 100644 start.bat create mode 100644 start.sh create mode 100644 utils/__init__.py diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..389959e --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +.git +.idea \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eab0608 --- /dev/null +++ b/.gitignore @@ -0,0 +1,144 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +*.db + +.idea +.vscode +log diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..cc6869d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM python:3.10 + +COPY requirements.txt /tmp/requirements.txt + +RUN pip install -r /tmp/requirements.txt + +COPY . /app + +WORKDIR /app + +CMD ["python", "/app/main.py"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..1973525 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# 使用 `Streamlit` 实现demo自举 + +## 1. 项目介绍 + +通过 streamlit的多页面应用, 实现多demo的动态展示 diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..c53f601 --- /dev/null +++ b/app/__init__.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python3 +# -*- coding:utf-8 -*- diff --git a/app/core/__init__.py b/app/core/__init__.py new file mode 100644 index 0000000..0f76362 --- /dev/null +++ b/app/core/__init__.py @@ -0,0 +1,2 @@ +from .config import settings +from .redis_ import kv_redis_client, redis_client diff --git a/app/core/config.py b/app/core/config.py new file mode 100644 index 0000000..5c921f5 --- /dev/null +++ b/app/core/config.py @@ -0,0 +1,36 @@ +import os + +from loguru import logger +from pydantic_settings import BaseSettings + + +class Settings(BaseSettings): + BASE_DIR: str = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + + VERSION: str | None = None + + redis_dsn: str = 'redis://:@localhost:6379/0' + kv_redis_dsn: str = 'redis://:password@localhost:6379/1' + mysql_dsn: str = 'mysql+pymysql://username:password@localhost:3306/data?charset=utf8mb4' + + @property + def version(self): + if self.VERSION: + return self.VERSION + if not os.path.exists(os.path.join(self.BASE_DIR, ".version")): + self.VERSION = "0.0.0" + return self.VERSION + with open(os.path.join(self.BASE_DIR, ".version"), "r") as f: + self.VERSION = f.read() + return self.VERSION + + PROJECT_NAME: str = 'project_name' + + class Config: + case_sensitive = True + + +settings = Settings() + +if __name__ == '__main__': + logger.debug(settings.BASE_DIR) diff --git a/app/core/redis_.py b/app/core/redis_.py new file mode 100644 index 0000000..ba6b548 --- /dev/null +++ b/app/core/redis_.py @@ -0,0 +1,15 @@ +import redis +from loguru import logger + +from app.core.config import settings + +redis_client = redis.Redis.from_url(settings.redis_dsn) +kv_redis_client = redis.Redis.from_url(settings.kv_redis_dsn) + +if __name__ == '__main__': + kv_redis_client.set("name", "leo") + keys = kv_redis_client.keys("*") + logger.debug(keys) + kv_redis_client.delete("name") + keys = kv_redis_client.keys("*") + logger.debug(keys) diff --git a/main.py b/main.py new file mode 100644 index 0000000..65fc50d --- /dev/null +++ b/main.py @@ -0,0 +1,43 @@ +import os + +import streamlit as st + +from app.core import settings + + +def select_file(): + ... + + +def new_file(): + new_file_name = st.session_state.new_file + if not new_file_name.endswith('.py'): + new_file_name = f'{new_file_name}.py' + with open(os.path.join(settings.BASE_DIR, 'pages', new_file_name), 'w') as f: + f.write(''' +#!/usr/bin/env python3 +# -*- coding:utf-8 -*- +import streamlit as st +st.write('Hello World!') +''') + + +with st.sidebar: + pages = os.listdir(os.path.join(settings.BASE_DIR, 'pages')) + pages = [{ + 'filename': page, + 'filepath': os.path.join(settings.BASE_DIR, 'pages', page) + } for page in pages if page.endswith('.py') and page != '__init__.py'] + + option = st.selectbox( + 'How would you like to be contacted?', + pages, + index=0, + format_func=lambda x: x['filename'], + on_change=select_file, + key='page_file' + ) + + st.write('You selected:', option) + +st.text_input('新建文件', on_change=new_file, key='new_file') diff --git a/pages/form.py b/pages/form.py new file mode 100644 index 0000000..81a340f --- /dev/null +++ b/pages/form.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# -*- coding:utf-8 -*- +import streamlit as st +import pandas as pd +import numpy as np + + +def get_data(): + df = pd.DataFrame({ + "lat": np.random.randn(200) / 50 + 37.76, + "lon": np.random.randn(200) / 50 + -122.4, + "team": ['A', 'B'] * 100 + }) + return df + + +if st.button('Generate new points'): + st.session_state.df = get_data() +if 'df' not in st.session_state: + st.session_state.df = get_data() +df = st.session_state.df + +with st.form("my_form"): + header = st.columns([1, 2, 2]) + header[0].subheader('Color') + header[1].subheader('Opacity') + header[2].subheader('Size') + + row1 = st.columns([1, 2, 2]) + colorA = row1[0].color_picker('Team A', '#0000FF') + opacityA = row1[1].slider('A opacity', 20, 100, 50, label_visibility='hidden') + sizeA = row1[2].slider('A size', 50, 200, 100, step=10, label_visibility='hidden') + + row2 = st.columns([1, 2, 2]) + colorB = row2[0].color_picker('Team B', '#FF0000') + opacityB = row2[1].slider('B opacity', 20, 100, 50, label_visibility='hidden') + sizeB = row2[2].slider('B size', 50, 200, 100, step=10, label_visibility='hidden') + + st.form_submit_button('Update map') + +alphaA = int(opacityA * 255 / 100) +alphaB = int(opacityB * 255 / 100) + +df['color'] = np.where(df.team == 'A', colorA + f'{alphaA:02x}', colorB + f'{alphaB:02x}') +df['size'] = np.where(df.team == 'A', sizeA, sizeB) + +st.map(df, size='size', color='color') diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1163376 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +streamlit~=1.29.0 +loguru +pydantic +redis \ No newline at end of file diff --git a/start.bat b/start.bat new file mode 100644 index 0000000..b828833 --- /dev/null +++ b/start.bat @@ -0,0 +1 @@ +streamlit run main.py \ No newline at end of file diff --git a/start.sh b/start.sh new file mode 100644 index 0000000..b828833 --- /dev/null +++ b/start.sh @@ -0,0 +1 @@ +streamlit run main.py \ No newline at end of file diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..c53f601 --- /dev/null +++ b/utils/__init__.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python3 +# -*- coding:utf-8 -*-