Theflame47 commited on
Commit
68316d3
·
verified ·
1 Parent(s): c363249

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +132 -160
app.py CHANGED
@@ -1,162 +1,134 @@
1
- import faicons as fa
2
- import plotly.express as px
3
-
4
- # Load data and compute static values
5
- from shared import app_dir, tips
6
- from shinywidgets import render_plotly
7
-
8
- from shiny import reactive, render
9
- from shiny.express import input, ui
10
-
11
- bill_rng = (min(tips.total_bill), max(tips.total_bill))
12
-
13
- # Add page title and sidebar
14
- ui.page_opts(title="Restaurant tipping", fillable=True)
15
-
16
- with ui.sidebar(open="desktop"):
17
- ui.input_slider(
18
- "total_bill",
19
- "Bill amount",
20
- min=bill_rng[0],
21
- max=bill_rng[1],
22
- value=bill_rng,
23
- pre="$",
24
- )
25
- ui.input_checkbox_group(
26
- "time",
27
- "Food service",
28
- ["Lunch", "Dinner"],
29
- selected=["Lunch", "Dinner"],
30
- inline=True,
31
  )
32
- ui.input_action_button("reset", "Reset filter")
33
-
34
- # Add main content
35
- ICONS = {
36
- "user": fa.icon_svg("user", "regular"),
37
- "wallet": fa.icon_svg("wallet"),
38
- "currency-dollar": fa.icon_svg("dollar-sign"),
39
- "ellipsis": fa.icon_svg("ellipsis"),
40
- }
41
-
42
- with ui.layout_columns(fill=False):
43
- with ui.value_box(showcase=ICONS["user"]):
44
- "Total tippers"
45
-
46
- @render.express
47
- def total_tippers():
48
- tips_data().shape[0]
49
-
50
- with ui.value_box(showcase=ICONS["wallet"]):
51
- "Average tip"
52
-
53
- @render.express
54
- def average_tip():
55
- d = tips_data()
56
- if d.shape[0] > 0:
57
- perc = d.tip / d.total_bill
58
- f"{perc.mean():.1%}"
59
-
60
- with ui.value_box(showcase=ICONS["currency-dollar"]):
61
- "Average bill"
62
-
63
- @render.express
64
- def average_bill():
65
- d = tips_data()
66
- if d.shape[0] > 0:
67
- bill = d.total_bill.mean()
68
- f"${bill:.2f}"
69
-
70
-
71
- with ui.layout_columns(col_widths=[6, 6, 12]):
72
- with ui.card(full_screen=True):
73
- ui.card_header("Tips data")
74
-
75
- @render.data_frame
76
- def table():
77
- return render.DataGrid(tips_data())
78
-
79
- with ui.card(full_screen=True):
80
- with ui.card_header(class_="d-flex justify-content-between align-items-center"):
81
- "Total bill vs tip"
82
- with ui.popover(title="Add a color variable", placement="top"):
83
- ICONS["ellipsis"]
84
- ui.input_radio_buttons(
85
- "scatter_color",
86
- None,
87
- ["none", "sex", "smoker", "day", "time"],
88
- inline=True,
89
- )
90
-
91
- @render_plotly
92
- def scatterplot():
93
- color = input.scatter_color()
94
- return px.scatter(
95
- tips_data(),
96
- x="total_bill",
97
- y="tip",
98
- color=None if color == "none" else color,
99
- trendline="lowess",
100
- )
101
-
102
- with ui.card(full_screen=True):
103
- with ui.card_header(class_="d-flex justify-content-between align-items-center"):
104
- "Tip percentages"
105
- with ui.popover(title="Add a color variable"):
106
- ICONS["ellipsis"]
107
- ui.input_radio_buttons(
108
- "tip_perc_y",
109
- "Split by:",
110
- ["sex", "smoker", "day", "time"],
111
- selected="day",
112
- inline=True,
113
- )
114
-
115
- @render_plotly
116
- def tip_perc():
117
- from ridgeplot import ridgeplot
118
-
119
- dat = tips_data()
120
- dat["percent"] = dat.tip / dat.total_bill
121
- yvar = input.tip_perc_y()
122
- uvals = dat[yvar].unique()
123
-
124
- samples = [[dat.percent[dat[yvar] == val]] for val in uvals]
125
-
126
- plt = ridgeplot(
127
- samples=samples,
128
- labels=uvals,
129
- bandwidth=0.01,
130
- colorscale="viridis",
131
- colormode="row-index",
132
- )
133
-
134
- plt.update_layout(
135
- legend=dict(
136
- orientation="h", yanchor="bottom", y=1.02, xanchor="center", x=0.5
137
- )
138
- )
139
-
140
- return plt
141
-
142
-
143
- ui.include_css(app_dir / "styles.css")
144
-
145
- # --------------------------------------------------------
146
- # Reactive calculations and effects
147
- # --------------------------------------------------------
148
-
149
-
150
- @reactive.calc
151
- def tips_data():
152
- bill = input.total_bill()
153
- idx1 = tips.total_bill.between(bill[0], bill[1])
154
- idx2 = tips.time.isin(input.time())
155
- return tips[idx1 & idx2]
156
-
157
 
158
- @reactive.effect
159
- @reactive.event(input.reset)
160
- def _():
161
- ui.update_slider("total_bill", value=bill_rng)
162
- ui.update_checkbox_group("time", selected=["Lunch", "Dinner"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, Request
2
+ from fastapi.responses import HTMLResponse, JSONResponse
3
+ import os, pathlib, json
4
+
5
+ from Deployment_UI import router as deployment_ui_router
6
+ from Deployment_UI_BE import router as deployment_be_router
7
+ from modelblob import router as modelblob_router # NEW (blob page + /modelblob.json)
8
+
9
+ app = FastAPI()
10
+ app.include_router(deployment_ui_router)
11
+ app.include_router(deployment_be_router)
12
+ app.include_router(modelblob_router) # NEW
13
+
14
+ LOCAL_BLOB_PATH = os.getenv("MODEL_BLOB_PATH", "/tmp/model_blob.json")
15
+
16
+ def _load_blob_text() -> str:
17
+ try:
18
+ if os.path.exists(LOCAL_BLOB_PATH):
19
+ return pathlib.Path(LOCAL_BLOB_PATH).read_text(encoding="utf-8")
20
+ except Exception:
21
+ pass
22
+ return "{\n \"note\": \"paste or programmatically write the model blob to /tmp/model_blob.json\"\n}"
23
+
24
+ def html_escape(s: str) -> str:
25
+ return (
26
+ s.replace("&", "&")
27
+ .replace("<", "&lt;")
28
+ .replace(">", "&gt;")
29
+ .replace('"', "&quot;")
30
+ .replace("'", "&#39;")
31
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
+ @app.get("/", response_class=HTMLResponse)
34
+ def landing_page() -> HTMLResponse:
35
+ blob_text = _load_blob_text()
36
+ html = f"""
37
+ <!doctype html>
38
+ <html>
39
+ <head>
40
+ <meta charset="utf-8"/>
41
+ <title>RunPod Model Tester — Blob</title>
42
+ <style>
43
+ body {{ font-family: system-ui, sans-serif; margin: 24px; }}
44
+ pre {{ border:1px solid #ddd; border-radius:8px; padding:12px; background:#fafafa; max-height:50vh; overflow:auto; }}
45
+ .btn {{ display:inline-block; width:100%; max-width:680px; padding:12px 16px; margin:10px 0;
46
+ border:none; border-radius:8px; color:#fff; background:#0078d7; cursor:pointer; }}
47
+ .btn.secondary {{ background:#444; }}
48
+ .row {{ max-width: 980px; }}
49
+ #msg {{ margin-top:10px; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size:12px; }}
50
+ /* overlay */
51
+ #blobOverlay {{ display:none; position:fixed; inset:0; background:rgba(0,0,0,.6); z-index:9999; }}
52
+ #blobCard {{ position:absolute; inset:4%; background:#0d1117; border-radius:12px; overflow:hidden;
53
+ box-shadow:0 10px 40px rgba(0,0,0,.5); }}
54
+ #blobHeader {{ padding:8px 12px; background:#111; color:#c9d1d9; display:flex; justify-content:space-between; align-items:center; }}
55
+ #closeBlob {{ width:auto; padding:6px 10px; }}
56
+ </style>
57
+ </head>
58
+ <body>
59
+ <div class="row">
60
+ <h1>RunPod Model Tester — Blob</h1>
61
+
62
+ <div id="count">{len(blob_text)}</div>
63
+ <div>
64
+ <div style="font-size:12px; color:#555; margin-bottom:6px;">Model blob (read-only)</div>
65
+ <pre id="blob" aria-label="model blob">{html_escape(blob_text)}</pre>
66
+ </div>
67
+
68
+ <!-- CHANGED: pass blob URL to the UI; no separate ingest button -->
69
+ <a href="/Deployment_UI?blob_url=/modelblob.json"
70
+ class="btn secondary" style="text-decoration:none; text-align:center;">
71
+ Open Deployment UI
72
+ </a>
73
+ <button id="openBlob" class="btn secondary">View Model Blob</button>
74
+
75
+ <div id="msg"></div>
76
+ </div>
77
+
78
+ <!-- Overlay -->
79
+ <div id="blobOverlay" aria-hidden="true">
80
+ <div id="blobCard">
81
+ <div id="blobHeader">
82
+ <div>Model Blob</div>
83
+ <button id="closeBlob" class="btn secondary">Close</button>
84
+ </div>
85
+ <iframe src="/modelblob" title="Model Blob" style="width:100%; height:calc(100% - 44px); border:0;"></iframe>
86
+ </div>
87
+ </div>
88
+
89
+ <script>
90
+ function setMsg(s) {{
91
+ document.getElementById("msg").textContent = s;
92
+ }}
93
+
94
+ // Overlay controls
95
+ const overlay = document.getElementById("blobOverlay");
96
+ document.getElementById("openBlob").addEventListener("click", () => {{
97
+ overlay.style.display = "block";
98
+ overlay.setAttribute("aria-hidden", "false");
99
+ }});
100
+ document.getElementById("closeBlob").addEventListener("click", () => {{
101
+ overlay.style.display = "none";
102
+ overlay.setAttribute("aria-hidden", "true");
103
+ }});
104
+ overlay.addEventListener("click", (e) => {{
105
+ if (e.target === overlay) {{
106
+ overlay.style.display = "none";
107
+ overlay.setAttribute("aria-hidden", "true");
108
+ }}
109
+ }});
110
+ </script>
111
+ </body>
112
+ </html>
113
+ """
114
+ return HTMLResponse(html)
115
+
116
+ # kept for compatibility but no longer used by the UI flow
117
+ @app.post("/set_blob")
118
+ async def set_blob(req: Request):
119
+ try:
120
+ ctype = req.headers.get("content-type","")
121
+ if "application/json" in ctype:
122
+ data = await req.json()
123
+ txt = data.get("blob") if isinstance(data, dict) else None
124
+ else:
125
+ form = await req.form()
126
+ txt = form.get("blob")
127
+
128
+ if not isinstance(txt, str) or not txt.strip():
129
+ return JSONResponse({"error": "Missing 'blob' string"}, 400)
130
+
131
+ pathlib.Path(LOCAL_BLOB_PATH).write_text(txt, encoding="utf-8")
132
+ return JSONResponse({"ok": True, "path": LOCAL_BLOB_PATH})
133
+ except Exception as e:
134
+ return JSONResponse({"error": str(e)}, 500)