first working prototype
This commit is contained in:
29
client.py
Normal file
29
client.py
Normal 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
4
hacks/__init__.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
from . import patch_st_cp_stop
|
||||||
|
|
||||||
|
|
21
hacks/patch_st_cp_stop.py
Normal file
21
hacks/patch_st_cp_stop.py
Normal 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
58
restapi.py
Normal 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()
|
||||||
|
|
||||||
|
|
||||||
|
|
55
stand.py
Normal file
55
stand.py
Normal 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
0
utils/__init__.py
Normal file
15
utils/df_utils.py
Normal file
15
utils/df_utils.py
Normal 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
21
utils/st_utils.py
Normal 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
|
||||||
|
|
||||||
|
|
||||||
|
|
Reference in New Issue
Block a user