first working prototype

This commit is contained in:
2022-05-19 10:32:02 +02:00
parent 6f33c8b16e
commit a6e4fdfdd3
9 changed files with 206 additions and 0 deletions

29
client.py Normal file
View File

@ -0,0 +1,29 @@
import requests
import json
class Client:
def __init__(self, host="127.0.0.1", port=8080):
self.host = host
self.port = port
self.session = requests.Session()
def add_row(self, **kwargs):
addr = f"http://{self.host}:{self.port}/"
resp = self.session.patch(addr, json=kwargs)
resp.raise_for_status()
return ResponseWrapper(resp)
class ResponseWrapper:
def __init__(self, resp):
self.resp = resp
def __repr__(self):
return self.resp.text

4
hacks/__init__.py Normal file
View File

@ -0,0 +1,4 @@
from . import patch_st_cp_stop

21
hacks/patch_st_cp_stop.py Normal file
View File

@ -0,0 +1,21 @@
"""
Monkey patch the streamlit server to stop cherrypy upon its own stop
"""
import cherrypy
from streamlit.server.server import Server
old_fn = Server._on_stopped
def new_fn(*args, **kwargs):
print("exit cherrypy")
cherrypy.engine.exit()
print("exit streamlit")
return old_fn(*args, **kwargs)
Server._on_stopped = new_fn

58
restapi.py Normal file
View File

@ -0,0 +1,58 @@
from threading import Thread
import json
import cherrypy as cp
from utils.df_utils import DateFrameHolder
from utils.st_utils import rerun
@cp.expose
class TableAPI:
def __init__(self):
self.dfh = DateFrameHolder()
self.sid = None
self.changed = True
@property
def data(self):
self.changed = False
return self.dfh.df
@data.setter
def data(self, df):
self.dfh.df = df
@cp.tools.json_in()
def PATCH(self, **kwargs):
kwargs = kwargs or cp.request.json
self.dfh.append(kwargs)
self.changed = True
rerun(self.sid)
return str(self.dfh.df)
# creating instances here allows to use TableAPI etc. as singletons
print(">>> start TableAPI")
api = TableAPI()
conf = {
"/": {
"request.dispatch": cp.dispatch.MethodDispatcher(),
"tools.sessions.on": True,
"tools.response_headers.on": True,
"tools.response_headers.headers": [("Content-Type", "text/plain")],
}
}
args = (api, "/", conf)
thread = Thread(target=cp.quickstart, args=args, daemon=True)
thread.start()

3
run.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
streamlit run stand.py

55
stand.py Normal file
View File

@ -0,0 +1,55 @@
import streamlit as st
from st_aggrid import AgGrid
import hacks
from restapi import api
from utils.st_utils import get_session_id, rerun
st.set_page_config(layout="wide")
api.sid = get_session_id() # rest api needs current session ID to trigger the next rerun
changed = api.changed
df = api.data
print(">>> start of streamlit run")
response = AgGrid(
df,
filter=True,
editable=True,
sortable=True,
resizable=True,
# defaultWidth=5,
fit_columns_on_grid_load=True,
reload_data=changed,
key="stand"
)
new_df = response.get("data")#, df)
if not new_df.equals(df) and not changed:
api.data = new_df
# print("old:")
# print(df)
# print("new:")
# print(new_df)
# print(">>> force rerun")
# rerun()
#st.dataframe(df.astype(str))
#st.dataframe(new_df.astype(str))
print(">>> end of streamlit run")

0
utils/__init__.py Normal file
View File

15
utils/df_utils.py Normal file
View File

@ -0,0 +1,15 @@
import pandas as pd
class DateFrameHolder:
def __init__(self, df=None):
self.df = df or pd.DataFrame()
# self.df = pd.DataFrame.from_records([dict(a=1, b=2, c=3)] * 4) #TODO: remove
def append(self, data):
data = pd.DataFrame.from_records([data])
self.df = pd.concat([self.df, data], ignore_index=True)

21
utils/st_utils.py Normal file
View File

@ -0,0 +1,21 @@
import streamlit as st
def rerun(session_id=None):
if session_id is None:
session_id = get_session_id()
server = st.server.server.Server.get_current()
session = server._get_session_info(session_id).session
client_state = None
session.request_rerun(client_state)
def get_session_id():
ctx = st.scriptrunner.script_run_context.get_script_run_ctx()
return ctx.session_id