Skip to content

Commit a23777f

Browse files
authored
Add sampling app (#22)
1 parent 8b93f85 commit a23777f

File tree

6 files changed

+587
-4
lines changed

6 files changed

+587
-4
lines changed

Makefile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,15 @@ test: ## Run tests with coverage
3838
# Dashboard commands
3939

4040
serve-dev: ## Serve Panel dashboard - Dev mode
41-
panel serve panel/app.py \
41+
panel serve panel/simdec.py panel/sampling.py \
42+
--index panel/index.html \
4243
--show --autoreload \
4344
--static-dirs _static=docs/_static \
4445
--reuse-sessions --warm
4546

4647
serve-oauth: ## Serve Panel dashboard - Prod mode with OAuth2. Needs: PANEL_OAUTH_REDIRECT_URI, PANEL_OAUTH_KEY, PANEL_OAUTH_SECRET, PANEL_OAUTH_ENCRYPTION
47-
PANEL_OAUTH_SCOPE=email panel serve panel/app.py \
48+
PANEL_OAUTH_SCOPE=email panel serve panel/simdec.py panel/sampling.py \
49+
--index panel/index.html \
4850
--show \
4951
--cookie-secret panel_cookie_secret_oauth \
5052
--basic-login-template panel/login.html \

panel/Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ USER app
6262
WORKDIR /app
6363

6464
# Run the web service on container startup.
65-
CMD ["panel", "serve", "panel/app.py", \
65+
CMD ["panel", "serve", "panel/simdec.py", "panel/sampling.py", \
6666
"--address", "0.0.0.0", "--port", "8080", \
6767
"--num-procs", "2", \
6868
"--allow-websocket-origin", "simdec.io", \
@@ -72,6 +72,7 @@ CMD ["panel", "serve", "panel/app.py", \
7272
"--basic-login-template", "panel/login.html", \
7373
"--logout-template", "panel/logout.html", \
7474
"--oauth-provider", "custom_google", \
75+
"--index", "panel/index.html", \
7576
"--static-dirs", "_static=_static", \
7677
"--reuse-sessions", "--warm", \
7778
"--global-loading-spinner"]

panel/index.html

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<!-- Basic Page Needs
5+
-------------------------------------------------- -->
6+
<meta charset="utf-8">
7+
<title>Sensitivity Analysis Applications</title>
8+
9+
<!-- Mobile Specific Metas
10+
-------------------------------------------------- -->
11+
<meta name="viewport" content="width=device-width, initial-scale=1">
12+
13+
<!-- FONT
14+
-------------------------------------------------- -->
15+
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,600&Lato|Work+Sans:400,700&display=swap" rel="stylesheet" type='text/css'>
16+
17+
<!-- CSS
18+
-------------------------------------------------- -->
19+
<style>*:not(:defined){visibility:hidden}</style>
20+
21+
<!-- Favicon
22+
-------------------------------------------------- -->
23+
<link rel="icon" type="image/x-icon" href="_static/icon.png">
24+
<link rel="manifest" href="{{ PANEL_CDN }}images/site.webmanifest">
25+
<meta name="msapplication-TileColor" content="#da532c">
26+
<meta name="theme-color" content="#ffffff">
27+
<script type="module" src="{{ PANEL_CDN }}bundled/@microsoft/fast-components@2.30.6/dist/fast-components.js"></script>
28+
<script type="module" src="{{ PANEL_CDN }}bundled/fast/js/fast_design.js"></script>
29+
<script type="text/javascript">
30+
function setParamsFromSearch(text){
31+
const params = new URLSearchParams(location.search);
32+
if (text===""){
33+
params.delete("search")
34+
} else {
35+
params.set('search', text);
36+
}
37+
window.history.replaceState({}, '', `${location.pathname}?${params}`);
38+
}
39+
function hideCards(text) {
40+
text=text.toLowerCase();
41+
const cards = document.getElementsByTagName("li")
42+
for (const card of cards){
43+
if (text==="" || card.innerHTML.toLowerCase().includes(text)){
44+
card.style.display=""
45+
} else {card.style.display="none"}
46+
}
47+
48+
setParamsFromSearch(text)
49+
}
50+
function toggleLightDarkTheme(update) {
51+
const switchEl = document.getElementById("theme-switch")
52+
const params = new URLSearchParams(location.search);
53+
if (switchEl.checked) {
54+
window.bodyDesign.setLuminance(1)
55+
window.bodyDesign.setBackgroundColor("#ffffff")
56+
params.set('theme', "default");
57+
} else {
58+
window.bodyDesign.setLuminance(0.1)
59+
window.bodyDesign.setBackgroundColor("#000000")
60+
params.set('theme', "dark");
61+
}
62+
if (update) {
63+
window.history.replaceState({}, '', `${location.pathname}?${params}`);
64+
}
65+
}
66+
function setSwitchFromParams(){
67+
const params = new URLSearchParams(window.location.search)
68+
if (params.has('theme')){
69+
const theme = params.get('theme')
70+
const switchEl = document.getElementById("theme-switch")
71+
if (theme==='dark'){
72+
switchEl.checked = false
73+
} else {
74+
switchEl.checked = true
75+
}
76+
toggleLightDarkTheme(true)
77+
} else {
78+
toggleLightDarkTheme(false)
79+
}
80+
}
81+
function setSearchFromParams(){
82+
const params = new URLSearchParams(window.location.search)
83+
if (params.has('search')){
84+
const search = params.get('search')
85+
const searchEl = document.getElementById("search-input")
86+
searchEl.value = search
87+
hideCards(search)
88+
}
89+
}
90+
</script>
91+
92+
<style>
93+
:root {
94+
--background-color: #ffffff;
95+
--header-background: #000000;
96+
}
97+
html {
98+
height:100%;
99+
}
100+
html, #body-design-provider {
101+
min-height: 100vh;
102+
}
103+
body {
104+
margin: 0px;
105+
padding: 0;
106+
font-style: normal;
107+
font-variant-ligatures: normal;
108+
font-variant-caps: normal;
109+
font-variant-numeric: normal;
110+
font-variant-east-asian: normal;
111+
font-weight: normal;
112+
font-stretch: normal;
113+
font-size: 16px;
114+
line-height: normal;
115+
font-family: aktiv-grotesk, "Segoe UI", Arial, Helvetica, sans-serif;
116+
overflow-y: auto;
117+
}
118+
.gallery-item:hover {
119+
box-shadow: 0 1px 5px var(--neutral-fill-strong-focus);
120+
}
121+
.gallery-item {
122+
cursor: pointer;
123+
text-align: center;
124+
}
125+
.header {
126+
/*background-image: linear-gradient(to top, rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0) ),url('{{ PANEL_CDN }}images/index_background.png');*/
127+
background-size: cover;
128+
background-repeat: space;
129+
background-position: center;
130+
}
131+
.header-content {
132+
display: flex;
133+
flex-direction: column;
134+
padding: 2rem;
135+
}
136+
#header-design-provider {
137+
background-color: var(--header-background);
138+
}
139+
#body-design-provider {
140+
color: var(--neutral-foreground-rest);
141+
}
142+
#title {
143+
background: transparent;
144+
}
145+
#subtitle {
146+
color: black;
147+
font-size: 2em;
148+
font-weight: bold;
149+
margin: 1em 0 0 1em;
150+
}
151+
#search-input {
152+
margin-top: 1em;
153+
margin-left:2em;
154+
margin-bottom:0em;
155+
width: calc(100% - 4em);
156+
}
157+
.theme-toggle-icon {
158+
height: 25px;
159+
width: 25px;
160+
margin-top: 5px;
161+
fill: #bababa;
162+
}
163+
/* The grid layout is inspired by
164+
https://css-tricks.com/look-ma-no-media-queries-responsive-layouts-using-css-grid/
165+
https://codepen.io/andybelldesign/pen/vMMYKJ */
166+
/*
167+
AUTO GRID
168+
Set the minimum item size with `--cards-grid-min-size` and you'll
169+
get a fully responsive grid with no media queries.
170+
*/
171+
.cards-grid {
172+
--cards-grid-min-size: 16rem;
173+
display: grid;
174+
grid-template-columns: repeat(auto-fill, minmax(var(--cards-grid-min-size), 1fr));
175+
grid-gap: 2rem;
176+
list-style: none;
177+
}
178+
/* Presentational styles */
179+
.card {
180+
padding: 0px;
181+
}
182+
.cards-grid {
183+
margin: 2rem;
184+
padding: 0px;
185+
}
186+
.avatar {
187+
vertical-align: middle;
188+
float: right;
189+
width: 30px;
190+
height: 30px;
191+
margin-top: 5px;
192+
border-radius: 50%;
193+
}
194+
.card-action svg {
195+
vertical-align: middle;
196+
float: right;
197+
height: 20px;
198+
color: white;
199+
margin-top: 10px;
200+
margin-right: 10px;
201+
fill: var(--neutral-foreground-rest);
202+
}
203+
.card-image {
204+
height: 175px;
205+
width: 100%;
206+
margin-top: 25px;
207+
}
208+
object {
209+
height: 175px;
210+
max-width: calc(100% - 50px);
211+
margin-top: 25px;
212+
border-radius: calc(var(--control-corner-radius) * 1px);
213+
}
214+
.card-content {
215+
padding: 10px 10px 10px;
216+
color: var(--neutral-foreground-rest);
217+
}
218+
.card-text {
219+
height: 100px;
220+
}
221+
.card-header {
222+
height: 2em;
223+
text-align: center;
224+
}
225+
footer {
226+
padding: .50rem;
227+
text-align: center;
228+
font-size: .75rem;
229+
}
230+
#panel-logo {
231+
width: 300px;
232+
}
233+
.card-link {
234+
text-decoration: none;
235+
color: var(--neutral-foreground-rest);
236+
}
237+
</style>
238+
</head>
239+
<body>
240+
<fast-design-system-provider id="body-design-provider" use-defaults>
241+
<fast-design-system-provider id="header-design-provider" use-defaults>
242+
<section class="header">
243+
<fast-switch id="theme-switch" style="float: right; padding-top: 1em; padding-right: 2em;" onChange="toggleLightDarkTheme()" checked>
244+
<span slot="checked-message">
245+
<svg class="theme-toggle-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 25"><path d="M0 0h24v24H0z" fill="none"/><path d="M6.76 4.84l-1.8-1.79-1.41 1.41 1.79 1.79 1.42-1.41zM4 10.5H1v2h3v-2zm9-9.95h-2V3.5h2V.55zm7.45 3.91l-1.41-1.41-1.79 1.79 1.41 1.41 1.79-1.79zm-3.21 13.7l1.79 1.8 1.41-1.41-1.8-1.79-1.4 1.4zM20 10.5v2h3v-2h-3zm-8-5c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6zm-1 16.95h2V19.5h-2v2.95zm-7.45-3.91l1.41 1.41 1.79-1.8-1.41-1.41-1.79 1.8z"/></svg>
246+
</span>
247+
<span slot="unchecked-message">
248+
<svg class="theme-toggle-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 25"><path d="M0 0h24v24H0z" fill="none"/><path d="M10 2c-1.82 0-3.53.5-5 1.35C7.99 5.08 10 8.3 10 12s-2.01 6.92-5 8.65C6.47 21.5 8.18 22 10 22c5.52 0 10-4.48 10-10S15.52 2 10 2z"/></svg>
249+
</span>
250+
</fast-switch>
251+
<fast-tooltip anchor="theme-switch">Click to toggle the Theme</fast-tooltip>
252+
<div class="header-content">
253+
<div>
254+
<fast-anchor id="title" href="https://simdec.fi" appearance="neutral" target="_self">
255+
<img id="logo" src="_static/logo.gif" width="150" height="150">
256+
</fast-anchor>
257+
<fast-tooltip anchor="title">Click to visit the SimDec web site</fast-tooltip>
258+
</div>
259+
</div>
260+
</section>
261+
</fast-design-system-provider>
262+
<section class="search">
263+
<fast-text-field id="search-input" placeholder="search" onInput="hideCards(event.target.value)"></fast-text-field>
264+
</section>
265+
<section id="cards">
266+
<ul class="cards-grid">
267+
{% for item in sorted(items, key=lambda item: item[1:].replace("_", " ").title()) %}
268+
<li class="card">
269+
<a class="card-link" href=".{{ item }}" id="{{ item }}">
270+
<fast-card class="gallery-item">
271+
<object data="thumbnails{{ item }}.png" type="image/png">
272+
<svg class="card-image" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-window" viewBox="0 0 16 16">
273+
<path d="M2.5 4a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1zm2-.5a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0zm1 .5a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1z"/>
274+
<path d="M2 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H2zm13 2v2H1V3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1zM2 14a1 1 0 0 1-1-1V6h14v7a1 1 0 0 1-1 1H2z"/>
275+
</svg>
276+
</object>
277+
<div class="card-content">
278+
<h2 class="card-header">{{ item[1:].replace("_", " ").title() }}</h2>
279+
</div>
280+
</fast-card>
281+
</a>
282+
</li>
283+
{% end %}
284+
</ul>
285+
</section>
286+
<section>
287+
<fast-divider></fast-divider>
288+
<footer></footer>
289+
</section>
290+
</fast-design-system-provider>
291+
<script type="text/javascript">
292+
document.addEventListener('DOMContentLoaded', (event) => {
293+
const header_design = new window.fastDesignProvider("#header-design-provider");
294+
header_design.setBackgroundColor('#ffffff')
295+
const body_design = window.bodyDesign = new window.fastDesignProvider("#body-design-provider");
296+
body_design.setAccentColor("#0072B5")
297+
setSwitchFromParams()
298+
setSearchFromParams()
299+
})
300+
</script>
301+
</body>
302+
</html>

0 commit comments

Comments
 (0)