Cosmographer commited on
Commit
2abe322
·
verified ·
1 Parent(s): c7e280d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +55 -128
app.py CHANGED
@@ -1,131 +1,58 @@
1
- # app.py (corrected for HF Spaces)
2
- import gradio as gr
3
- import plotly.graph_objects as go
4
- import plotly.io as pio
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- def add_card(fig, x, y, w, h, title, subtitle=None,
7
- fillcolor="#0f1724", border="#263145",
8
- title_color="#ffffff", subtitle_color="#bfcbdc"):
9
- fig.add_shape(type="rect",
10
- x0=x - w/2, y0=y - h/2, x1=x + w/2, y1=y + h/2,
11
- xref="x", yref="y",
12
- line=dict(color=border, width=1),
13
- fillcolor=fillcolor, layer="below")
14
- fig.add_trace(go.Scatter(
15
- x=[x], y=[y + h*0.18],
16
- mode="text",
17
- text=[f"<b>{title}</b>"],
18
- textfont=dict(color=title_color, size=12),
19
- hoverinfo="none", showlegend=False
20
- ))
21
- if subtitle:
22
- fig.add_trace(go.Scatter(
23
- x=[x], y=[y - h*0.18],
24
- mode="text",
25
- text=[subtitle],
26
- textfont=dict(color=subtitle_color, size=10),
27
- hoverinfo="none", showlegend=False
28
- ))
29
-
30
- def add_arrow(fig, x1, y1, x2, y2, color="#86b6ff"):
31
- fig.add_annotation(
32
- x=x2, y=y2,
33
- ax=x1, ay=y1,
34
- xref="x", yref="y",
35
- axref="x", ayref="y",
36
- showarrow=True, arrowhead=3, arrowsize=1, arrowwidth=1.6,
37
- arrowcolor=color, opacity=0.95
38
- )
39
-
40
- def build_qids_html():
41
- # sample nodes (tweak / replace with your data)
42
- nodes = [
43
- {"id":"Intake & Consent", "row":3, "col":1, "subtitle":"Collect identity, demographics"},
44
- {"id":"Prepare Assessment Suite", "row":3, "col":2, "subtitle":"Assemble IQ, psychometrics"},
45
- {"id":"Baseline Data Collection", "row":3, "col":3, "subtitle":"Administer tests"},
46
- {"id":"Standardize & Score", "row":3, "col":4, "subtitle":"Normalize results"},
47
- {"id":"Integrated Profile Construction", "row":3, "col":5, "subtitle":"Triangulate multi-method data"},
48
- {"id":"Scheduling & Resource Allocation", "row":2, "col":1, "subtitle":"Session calendar"},
49
- {"id":"Module Deployment", "row":2, "col":2, "subtitle":"Deploy modules"},
50
- {"id":"Session Data Capture", "row":2, "col":3, "subtitle":"Attendance, engagement"},
51
- {"id":"Progress Evaluation Engine", "row":2, "col":4, "subtitle":"Progress metrics"},
52
- {"id":"DEI Practice (EQ)", "row":2, "col":5, "subtitle":"Emotional integration"},
53
- {"id":"Full Reassessment", "row":1, "col":2, "subtitle":"Re-run assessments"},
54
- {"id":"Compute Outcomes", "row":1, "col":3, "subtitle":"Calculate KPIs"},
55
- {"id":"Intervention Effectiveness", "row":1, "col":4, "subtitle":"Aggregate effectiveness"},
56
- {"id":"Final IDP & Roadmap", "row":1, "col":5, "subtitle":"Produce final IDP"},
57
- ]
58
-
59
- x_spacing = 3.5
60
- y_spacing = 2.5
61
- for n in nodes:
62
- n["x"] = n["col"] * x_spacing
63
- n["y"] = n["row"] * y_spacing
64
-
65
- fig = go.Figure()
66
-
67
- # subtle grid lines (visual only)
68
- x_min, x_max = 0, 22
69
- y_min, y_max = 0, 9
70
- for gx in range(int(x_min), int(x_max)+1):
71
- fig.add_shape(type="line", x0=gx, y0=y_min, x1=gx, y1=y_max,
72
- line=dict(color="#0b1220", width=1), layer="below")
73
- for gy in [i*0.5 for i in range(int(y_min*2), int(y_max*2)+1)]:
74
- fig.add_shape(type="line", x0=x_min, y0=gy, x1=x_max, y1=gy,
75
- line=dict(color="#08121c", width=1), layer="below")
76
-
77
- card_w, card_h = 2.8, 1.0
78
- for n in nodes:
79
- add_card(fig, n["x"], n["y"], card_w, card_h, n["id"], n.get("subtitle"))
80
-
81
- rows = {}
82
- for n in nodes:
83
- rows.setdefault(n["row"], []).append(n)
84
- for r, row_nodes in rows.items():
85
- row_nodes = sorted(row_nodes, key=lambda x: x["col"])
86
- for a,b in zip(row_nodes, row_nodes[1:]):
87
- add_arrow(fig, a["x"]+card_w/2, a["y"], b["x"]-card_w/2, b["y"])
88
-
89
- # right panel
90
- panel_x0 = x_max - 2.5; panel_x1 = x_max + 2.5
91
- panel_y0 = y_min + 0.5; panel_y1 = y_max - 0.5
92
- fig.add_shape(type="rect", x0=panel_x0, y0=panel_y0, x1=panel_x1, y1=panel_y1,
93
- xref="x", yref="y", line=dict(color="#243445"), fillcolor="#0e1724", layer="below")
94
- fig.add_trace(go.Scatter(x=[panel_x0+0.2], y=[panel_y1-0.4], mode="text",
95
- text=["<b>Legal & Technical Highlights</b>"],
96
- textfont=dict(color="#8fb4ff", size=12), showlegend=False))
97
- panel_items = [
98
- ("Unique IQ Measurement Model", "Four-parameter model: Verbal, Quantitative, Psychometric"),
99
- ("Standardization Algorithm", "Unified 0-100% scale and conversion factors"),
100
- ("Dynamic Weightage Algorithm (DWA)", "Context-sensitive weights by role/culture/mission"),
101
- ]
102
- y = panel_y1 - 1.0
103
- for title, body in panel_items:
104
- fig.add_trace(go.Scatter(x=[panel_x0+0.2], y=[y], mode="text",
105
- text=[f"<b>{title}</b><br><span style='color:#bfcbdc;font-size:10px'>{body}</span>"],
106
- showlegend=False, textfont=dict(size=11)))
107
- y -= 0.9
108
-
109
- fig.update_layout(
110
- width=1200, height=700,
111
- paper_bgcolor="#071021", plot_bgcolor="#071021",
112
- margin=dict(l=20, r=20, t=20, b=20),
113
- xaxis=dict(range=[0, x_max+3], visible=False),
114
- yaxis=dict(range=[0, y_max+1], visible=False, scaleanchor="x"),
115
- hovermode="closest"
116
- )
117
-
118
- # NOTE: full_html=False returns only the chart DIV and JS that we can safely embed
119
- fragment = pio.to_html(fig, full_html=False, include_plotlyjs='cdn')
120
- wrapper = f"<div id='qids-plotly-container'>{fragment}</div>"
121
- return wrapper
122
-
123
- PLOTLY_FRAGMENT = build_qids_html()
124
-
125
- with gr.Blocks() as demo:
126
- gr.Markdown("## QIDS Framework — Interactive Visualizer")
127
- gr.HTML(PLOTLY_FRAGMENT)
128
- gr.Markdown("---\n*Interactive Plotly visualization served with Gradio*")
129
 
130
  if __name__ == "__main__":
131
- demo.launch()
 
 
 
 
1
+ # app.py
2
+ import os
3
+ from pathlib import Path
4
+ from flask import Flask, send_from_directory, render_template_string, abort
5
+
6
+ # Configure where built frontend lives
7
+ DIST_DIR = Path(__file__).parent.joinpath("dist")
8
+
9
+ app = Flask(
10
+ __name__,
11
+ static_folder=str(DIST_DIR), # serve static files from dist/
12
+ static_url_path="" # serve them from root (so /assets/... works)
13
+ )
14
+
15
+ @app.route("/", defaults={"path": ""})
16
+ @app.route("/<path:path>")
17
+ def serve_spa(path: str):
18
+ """
19
+ Serve files from dist/ when they exist.
20
+ If a file doesn't exist, serve index.html (SPA fallback).
21
+ If dist/index.html is missing, show a helpful message.
22
+ """
23
+ # If dist doesn't exist or index.html missing, show message.
24
+ index_file = DIST_DIR / "index.html"
25
+ if not index_file.exists():
26
+ msg = """
27
+ <h2>Static frontend not found (dist/index.html)</h2>
28
+ <p>The Space is running but the built frontend (dist/) is missing.</p>
29
+ <p>Possible fixes:</p>
30
+ <ol>
31
+ <li>Ensure your GitHub Actions builds the Vite app and copies <code>dist/</code> into the Space repo root.</li>
32
+ <li>Or manually build locally and copy the <code>dist/</code> contents into this repo.</li>
33
+ </ol>
34
+ <p>Once <code>dist/index.html</code> is present, the app will be served automatically.</p>
35
+ """
36
+ return render_template_string(msg), 200
37
+
38
+ # If path points to an existing file in dist => serve it
39
+ target = DIST_DIR.joinpath(path)
40
+ if path != "" and target.exists() and target.is_file():
41
+ # send direct file, preserves mime types
42
+ return send_from_directory(str(DIST_DIR), path)
43
+
44
+ # Otherwise return index.html (SPA fallback)
45
+ return send_from_directory(str(DIST_DIR), "index.html")
46
+
47
+
48
+ # Health endpoint (optional)
49
+ @app.route("/_health")
50
+ def health():
51
+ return {"status": "ok"}, 200
52
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
  if __name__ == "__main__":
55
+ # When launched by HF Spaces, this script will be executed directly.
56
+ # Use environment PORT if provided, else default 7860.
57
+ port = int(os.environ.get("PORT", 7860))
58
+ app.run(host="0.0.0.0", port=port)