Coverage for pesummary/core/webpage/webpage.py: 82.5%
475 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-12-09 22:34 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-12-09 22:34 +0000
1# Licensed under an MIT style license -- see LICENSE.md
3import pesummary
4from pesummary.core.webpage import tables
5from pesummary.core.webpage.base import Base
7from pygments import highlight
8from pygments.lexers import get_lexer_by_name
9from pygments.formatters import HtmlFormatter
10import time
12__author__ = [
13 "Charlie Hoy <charlie.hoy@ligo.org>",
14 "Edward Fauchon-Jones <edward.fauchon-jones@ligo.org>"
15]
17BOOTSTRAP = """<!DOCTYPE html>
19<!--
20 Made by
21 ____ ___________
22 / __ \/ ____/ ___/__ ______ ___ ____ ___ ____ ________ __
23 / /_/ / __/ \__ \/ / / / __ `__ \/ __ `__ \/ __ `/ ___/ / / /
24 / ____/ /___ ___/ / /_/ / / / / / / / / / / / /_/ / / / /_/ /
25 /_/ /_____//____/\__,_/_/ /_/ /_/_/ /_/ /_/\__,_/_/ \__, /
26 /____/
28 MIT License
30 PESummary was developed by Hoy et al. and source code can be seen
31 here: git.ligo.org/lscsoft/pesummary. If you wish to use PESummary
32 for your own work, please cite PESummary. The following page gives details
33 https://lscsoft.docs.ligo.org/pesummary/stable/citing_pesummary.html.
34 Thanks!
35 -->
36<html lang='en'>
37 <title>title</title>
38 <meta charset='utf-8'>
39 <meta name='viewport' content='width=device-width, initial-scale=1'>
40 <link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css'>
41 stylesheet elements
42</head>
43<body style='background-color:#F8F8F8; margin-top:5em; min-height: 100%'>
44"""
46HOME_SCRIPTS = """ <script src='https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script>
47 <script src='https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js'></script>
48 <script src='https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js'></script>
49 <script src='./js/combine_corner.js'></script>
50 <script src='./js/grab.js'></script>
51 <script src='./js/modal.js'></script>
52 <script src='./js/multi_dropbar.js'></script>
53 <script src='./js/multiple_posteriors.js'></script>
54 <script src='./js/search.js'></script>
55 <script src='./js/side_bar.js'></script>
56 <script src='./js/html_to_csv.js'></script>
57 <script src='./js/html_to_json.js'></script>
58 <script src='./js/html_to_shell.js'></script>
59 <script src='./js/expert.js'></script>
60 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
61 <link rel="stylesheet" href="./css/navbar.css">
62 <link rel="stylesheet" href="./css/font.css">
63 <link rel="stylesheet" href="./css/table.css">
64 <link rel="stylesheet" href="./css/image_styles.css">
65 <link rel="stylesheet" href="./css/watermark.css">
66 <link rel="stylesheet" href="./css/toggle.css">
67"""
69OTHER_SCRIPTS = """ <script src='https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script>
70 <script src='https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js'></script>
71 <script src='https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js'></script>
72 <script src='../js/combine_corner.js'></script>
73 <script src='../js/grab.js'></script>
74 <script src='../js/modal.js'></script>
75 <script src='../js/multi_dropbar.js'></script>
76 <script src='../js/multiple_posteriors.js'></script>
77 <script src='../js/search.js'></script>
78 <script src='../js/side_bar.js'></script>
79 <script src='../js/html_to_csv.js'></script>
80 <script src='../js/html_to_json.js'></script>
81 <script src='../js/html_to_shell.js'></script>
82 <script src='../js/expert.js'></script>
83 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
84 <link rel="stylesheet" href="../css/navbar.css">
85 <link rel="stylesheet" href="../css/font.css">
86 <link rel="stylesheet" href="../css/table.css">
87 <link rel="stylesheet" href="../css/image_styles.css">
88 <link rel="stylesheet" href="../css/watermark.css">
89 <link rel="stylesheet" href="../css/toggle.css">
90"""
93def make_html(web_dir, title="Summary Pages", pages=None, stylesheets=[],
94 label=None):
95 """Make the initial html page.
97 Parameters
98 ----------
99 web_dir: str
100 path to the location where you would like the html file to be saved
101 title: str, optional
102 header title of html page
103 pages: list, optional
104 list of pages that you would like to be created
105 stylesheets: list, optional
106 list of stylesheets to including in the html page. It is assumed all
107 stylesheets are located in the `css` in the root of the summary pages.
108 This should be provided as basenames without extension that is assumed
109 to be `.css`.
110 """
111 for i in pages:
112 if i != "home":
113 f = open("{}/html/{}.html".format(web_dir, i), "w")
114 else:
115 f = open("{}/{}.html".format(web_dir, i), "w")
116 stylesheet_elements = ''.join([
117 " <link rel='stylesheet' href='../css/{0:s}.css'>\n".format(s)
118 for s in stylesheets])
119 bootstrap = BOOTSTRAP.split("\n")
120 bootstrap[1] = " <title>{}</title>".format(title)
121 bootstrap[-4] = stylesheet_elements
122 bootstrap = [j + "\n" for j in bootstrap]
123 f.writelines(bootstrap)
124 if i != "home":
125 scripts = OTHER_SCRIPTS.split("\n")
126 else:
127 scripts = HOME_SCRIPTS.split("\n")
128 scripts = [j + "\n" for j in scripts]
129 f.writelines(scripts)
132def open_html(web_dir, base_url, html_page, label=None):
133 """Open html page ready so you can manipulate the contents
135 Parameters
136 ----------
137 web_dir: str
138 path to the location where you would like the html file to be saved
139 base_url: str
140 url to the location where you would like the html file to be saved
141 page: str
142 name of the html page that you would like to edit
143 label: str
144 the label that prepends your page name
145 """
146 try:
147 if html_page[-5:] == ".html":
148 html_page = html_page[:-5]
149 except Exception:
150 pass
151 if html_page == "home.html" or html_page == "home":
152 f = open(web_dir + "/home.html", "a")
153 else:
154 if label is not None:
155 f = open(web_dir + "/html/{}_".format(label) + html_page
156 + ".html", "a")
157 else:
158 f = open(web_dir + "/html/" + html_page + ".html", "a")
159 return page(f, web_dir, base_url, label)
162class page(Base):
163 """Class to generate and manipulate an html page.
164 """
165 def __init__(self, html_file, web_dir, base_url, label):
166 self.html_file = html_file
167 self.web_dir = web_dir
168 self.base_url = base_url
169 self.label = label
170 self.content = []
172 def _header(self, approximant):
173 """
174 """
175 self.add_content("<h7 hidden>{}</h7>".format(self.label))
176 self.add_content("<h7 hidden>{}</h7>".format(approximant))
178 def _footer(self, user, rundir, fix_bottom=False):
179 """
180 """
181 self.add_content("<script>")
182 self.add_content("$(document).ready(function(){", indent=2)
183 self.add_content("$('[data-toggle=\"tooltip\"]').tooltip();", indent=4)
184 self.add_content("});", indent=2)
185 self.add_content("</script>")
186 if fix_bottom:
187 self.make_div(
188 _class='container', _style='bottom: 0px; '
189 + 'top: 0px; min-height: 100%; left: 0; right: 0;'
190 + 'min-width: 100%; padding-left: 0px; padding-right: 0px'
191 )
192 self.make_div(
193 _class='jumbotron', _style='margin-bottom:0; line-height: 0.5;'
194 + 'background-color:#989898; bottom:0; position:absolute;'
195 + 'width:100%')
196 else:
197 self.make_div(
198 _class='jumbotron', _style='margin-bottom:0; line-height: 0.5;'
199 + 'background-color:#989898; bottom:0; position:bottom;'
200 + 'width:100%')
201 self.add_content("<div class='container'>")
202 self.add_content("<div class='row'>", indent=2)
203 self.add_content("<div class='col-sm-4 icon-bar'>", indent=4)
204 self.add_content("<div class='icon'>", indent=6)
205 self.add_content(
206 "<a href='https://git.ligo.org/lscsoft/pesummary/tree/v{}' "
207 "data-toggle='tooltip' title='View PESummary-v{}'>"
208 "<i class='fa fa-code' style='font-size: 30px; color: #E8E8E8; "
209 "font-weight: 900; padding-right:10px'></i></a>".format(
210 pesummary.__short_version__, pesummary.__short_version__
211 )
212 )
213 self.add_content(
214 "<a href='https://git.ligo.org/lscsoft/pesummary/issues' "
215 "data-toggle='tooltip' title='Open an issue ticket'>"
216 "<i class='fa fa-ticket' style='font-size: 30px; color: #E8E8E8; "
217 "font-weight: 900; padding-right:10px'></i></a>"
218 )
219 self.add_content(
220 "<a href='https://lscsoft.docs.ligo.org/pesummary/' "
221 "data-toggle='tooltip' title='View the docs!'>"
222 "<i class='fa fa-book' style='font-size: 30px; color: #E8E8E8; "
223 "font-weight: 900; padding-right:10px'></i></a>"
224 )
225 link = (
226 "https://lscsoft.docs.ligo.org/pesummary/stable/tutorials/"
227 "make_your_own_page_from_metafile.html"
228 )
229 self.add_content(
230 "<a href='{}' data-toggle='tooltip' title='Make your own page'>"
231 "<i class='fa fa-window-restore' style='font-size: 30px; "
232 "color: #E8E8E8; font-weight: 900; padding-right:10px'></i>"
233 "</a>".format(link)
234 )
235 self.add_content("</div>", indent=6)
236 self.add_content("</div>", indent=4)
237 self.add_content("<div class='col-sm-7'>", indent=4)
238 self.add_content(
239 "<p style='color: #E8E8E8; font-weight: bold; "
240 "font-family: arial-body; margin-top:14px'>This page was produced "
241 "by {} at {} on {}</p>".format(
242 user, time.strftime("%H:%M"), time.strftime("%B %d %Y")
243 )
244 )
245 self.add_content("</div>", indent=4)
246 self.add_content("</div>", indent=2)
247 self.add_content("</div>")
248 if fix_bottom:
249 self.add_content("</div>")
250 self.end_div()
252 def _setup_navbar(self, background_colour):
253 if background_colour == "navbar-dark" or background_colour is None:
254 self.add_content("<nav class='navbar navbar-expand-sm navbar-dark "
255 "bg-dark fixed-top'>\n")
256 else:
257 self.add_content("<nav class='navbar navbar-expand-sm fixed-top "
258 "navbar-custom' style='background-color: %s'>" % (
259 background_colour))
260 self.add_content("<a class='navbar-brand' href='#' style='color: white'"
261 ">Navbar</a>\n", indent=2)
262 self.add_content("<button class='navbar-toggler' type='button' "
263 "data-toggle='collapse' data-target='#collapsibleNavbar'>\n", indent=2)
264 self.add_content("<span class='navbar-toggler-icon'></span>\n", indent=4)
265 self.add_content("</button>\n", indent=2)
267 def make_header(self, approximant="IMRPhenomPv2"):
268 """Make header for document in bootstrap format.
270 Parameters
271 ----------
272 title: str, optional
273 header title of html page
274 approximant: str, optional
275 the approximant that you are analysing
276 """
277 self._header(approximant)
279 def make_footer(self, user=None, rundir=None, fix_bottom=False):
280 """Make footer for document in bootstrap format.
281 """
282 self._footer(user, rundir, fix_bottom=fix_bottom)
284 def make_banner(
285 self, approximant=None, key="Summary", _style=None, link=None,
286 custom=""
287 ):
288 """Make a banner for the document.
289 """
290 self.make_div(indent=2, _class='banner', _style=_style)
291 self.add_content("%s" % (approximant))
292 self.end_div()
293 self.make_div(indent=2, _class='paragraph')
294 if key == "Summary":
295 self.add_content(
296 "The figures below show the summary plots for the run")
297 elif key == "config":
298 self.add_content(
299 "Below is the config file for %s" % (approximant))
300 elif key == "prior":
301 self.add_content(
302 "Below is the prior file for %s" % (approximant))
303 elif key == "corner":
304 self.add_content(
305 "Below is the custom corner plotter for %s" % (approximant))
306 elif key == "additional":
307 self.add_content(
308 "Below we show plots which have been generated previously "
309 "and passed to pesummary via the `--add_existing_plot` "
310 "command line argument"
311 )
312 elif key == "interactive_corner":
313 self.add_content(
314 "Below are interative corner plots for %s. Simply use the "
315 "Box Select to select the points you wish to look at" % (
316 approximant
317 )
318 )
319 elif key == "Comparison":
320 self.add_content(
321 "Below are the summary comparison plots")
322 elif key == "Version":
323 self.add_content(
324 "Below is the version information for all files passed and the "
325 "PESummary version used to generate these pages")
326 elif key == "Publication":
327 self.add_content(
328 "Below are publication quality plots for the passed result "
329 "files")
330 elif key == "Logging":
331 self.add_content("Below is the output from the PESummary code")
332 elif key == "Notes":
333 self.add_content("Below are your custom notes")
334 elif key == "classification":
335 self.add_content(
336 "Below we look at the source probabilities and plots for the "
337 "passed result file")
338 elif key == "sampler_kwargs":
339 self.add_content(
340 "Sampler information extracted from each result file")
341 elif key == "meta_data":
342 self.add_content(
343 "Meta data extracted from each result file")
344 elif key == "summary_table":
345 self.add_content(
346 "Table summarising the key data for each posterior "
347 "distribution."
348 )
349 elif key == "ks_test":
350 self.add_content(
351 "Table summarising the Kolmogorov-Smirnov p-values for each "
352 "posterior distribution. 0 means the samples from analysis A "
353 "are not drawn from analysis B, and 1 means the samples from "
354 "analysis A are drawn from analysis B"
355 )
356 elif key == "js_test":
357 self.add_content(
358 "Table summarising the Jensen-Shannon divergence for each "
359 "posterior distributions. 0 means the distributions are "
360 "identical and 1 means maximal divergence"
361 )
362 elif key == "command_line":
363 if link is not None:
364 self.add_content(
365 "This page was generated with the following command-line "
366 "call from the directory %s" % (link)
367 )
368 else:
369 self.add_content(
370 "This page was generated with the following command-line "
371 "call:"
372 )
373 elif key == "detchar":
374 base_string = "Below are summary plots for the detector %s.{}" % (
375 approximant
376 )
377 if link is not None:
378 base_string = base_string.format(
379 " For more details see the LIGO summary pages "
380 "<a href=%s>here</a>" % (link)
381 )
382 else:
383 base_string = base_string.format("")
384 self.add_content(base_string)
385 elif key == "Downloads":
386 self.add_content(
387 "Below are links to download all relevant information"
388 )
389 elif key == "About":
390 self.add_content(
391 "Below is information about how these pages were generated"
392 )
393 elif key == "custom":
394 self.add_content(custom)
395 else:
396 self.add_content(
397 "The figures below show the plots for %s" % (approximant))
398 self.end_div()
400 def make_navbar(self, links=None, samples_path="./samples", search=True,
401 histogram_download=None,
402 background_color="navbar-dark",
403 hdf5=False, about=True, toggle=False):
404 """Make a navigation bar in boostrap format.
406 Parameters
407 ----------
408 links: list, optional
409 list giving links that you want your navbar to include. If a
410 dropdown option is required, give a 2d list showing the main link
411 followed by dropdown links. For instance, if you wanted to have
412 links corner plots and a dropdown link named 1d_histograms with
413 options mass1, mass2, mchirp, then we would give,
415 links=[corner, [1d_histograms, [mass1, mass2, mchirp]]]
417 samples_path: str, optional
418 path to the location of the meta file
419 search: bool, optional
420 if True, search bar will be given in navbar
421 histogram_download: str, optional
422 path to the location of the data associated with the histogram
423 hdf5: Bool, optional
424 true if a hdf5 file format is chosen for the meta file
425 """
426 self._setup_navbar(background_color)
427 if links is None:
428 raise Exception("Please specify links for use with navbar\n")
429 self.add_content("<div class='collapse navbar-collapse' id='collapsibleNavbar'>\n", indent=4)
430 self.add_content("<ul class='navbar-nav'>\n", indent=6)
431 for i in links:
432 if type(i) == list:
433 self.add_content("<li class='nav-item'>\n", indent=8)
434 self.add_content("<li class='nav-item dropdown'>\n", indent=10)
435 self.add_content("<a class='nav-link dropdown-toggle' "
436 "href='#' id='navbarDropdown' role='button' "
437 "data-toggle='dropdown' aria-haspopup='true' "
438 "aria-expanded='false'>\n", indent=12)
439 self.add_content("{}\n".format(i[0]), indent=12)
440 self.add_content("</a>\n", indent=12)
441 self.add_content("<ul class='dropdown-menu' aria-labelledby='dropdown1'>\n", indent=12)
442 for j in i:
443 if type(j) == list:
444 if len(j) > 1:
445 if type(j[1]) == list:
446 self.add_content("<li class='dropdown-item dropdown'>\n", indent=14)
447 self.add_content("<a class='dropdown-toggle' id='{}' "
448 "data-toggle='dropdown' "
449 "aria-haspopup='true' "
450 "aria-expanded='false'>{}</a>\n".format(j[0], j[0]), indent=16)
451 self.add_content("<ul class='dropdown-menu' "
452 "aria-labelledby='{}'>\n".format(j[0]), indent=16)
453 for k in j[1]:
454 if type(k) == dict:
455 key = list(k.keys())[0]
456 if "external:" in k[key]:
457 self.add_content(
458 "<li class='dropdown-item' "
459 "href='#' onclick='window.location"
460 "=\"{}\"'><a>{}</a></li>\n".format(
461 k[key].split("external:")[1],
462 key
463 ), indent=18
464 )
465 else:
466 self.add_content(
467 "<li class='dropdown-item' "
468 "href='#' onclick='grab_html"
469 "(\"{}\", label=\"{}\")'>"
470 "<a>{}</a></li>\n".format(
471 key, k[key], key
472 ), indent=18
473 )
474 else:
475 self.add_content(
476 "<li class='dropdown-item' href='#' "
477 "onclick='grab_html(\"{}\")'>"
478 "<a>{}</a></li>\n".format(k, k), indent=18)
480 self.add_content("</ul>", indent=16)
481 self.add_content("</li>", indent=14)
482 else:
483 for k in j:
484 if type(k) == dict:
485 key = list(k.keys())[0]
486 if "external:" in k[key]:
487 self.add_content(
488 "<li class='dropdown-item' "
489 "href='#' onclick='window.location"
490 "=\"{}\"'><a>{}</a></li>\n".format(
491 k[key].split("external:")[1],
492 key
493 ), indent=18
494 )
495 else:
496 self.add_content(
497 "<li class='dropdown-item' "
498 "href='#' onclick='grab_html"
499 "(\"{}\", label=\"{}\")'>"
500 "<a>{}</a></li>\n".format(
501 key, k[key], key
502 ), indent=14
503 )
505 else:
506 self.add_content(
507 "<li class='dropdown-item' href='#' "
508 "onclick='grab_html(\"{}\")'>"
509 "<a>{}</a></li>\n".format(k, k), indent=14)
511 else:
512 if type(j[0]) == dict:
513 key = list(j[0].keys())[0]
514 if 'external:' in j[0][key]:
515 self.add_content(
516 "<li class='dropdown-item' href='#' "
517 "onclick='window.location=\"{}\"'>"
518 "<a>{}</a></li>\n".format(
519 j[0][key].split("external:")[1], key
520 ), indent=14
521 )
522 else:
523 self.add_content(
524 "<li class='dropdown-item' href='#' "
525 "onclick='grab_html(\"{}\", label=\"{}\")'>"
526 "<a>{}</a></li>\n".format(key, j[0][key], key),
527 indent=14
528 )
530 else:
531 if "external:" in j[0]:
532 self.add_content(
533 "<li class='dropdown-item' href='#' "
534 "onclick='window.location=\"{}\"'>"
535 "<a>{}</a></li>\n".format(
536 j[0].split("external:")[1], j[0]
537 ), indent=14
538 )
539 else:
540 self.add_content(
541 "<li class='dropdown-item' href='#' "
542 "onclick='grab_html(\"{}\")'>"
543 "<a>{}</a></li>\n".format(j[0], j[0]),
544 indent=14
545 )
547 self.add_content("</ul>\n", indent=12)
548 self.add_content("</li>\n", indent=10)
549 else:
550 self.add_content("<li class='nav-item'>\n", indent=8)
551 if i == "home":
552 if "external:" in i:
553 self.add_content(
554 "<a class='nav-link' href='#' onclick='window.location"
555 "=\"{}\"'>{}</a>\n".format(i.split("external:")[1], i),
556 indent=10)
557 else:
558 self.add_content("<a class='nav-link' "
559 "href='#' onclick='grab_html(\"{}\")'"
560 ">{}</a>\n".format(i, i), indent=10)
561 else:
562 if type(i) == dict:
563 key = list(i.keys())[0]
564 if "external:" in i[key]:
565 self.add_content(
566 "<a class='nav-link' "
567 "href='#' onclick='window.location=\"{}\"'"
568 ">{}</a>\n".format(
569 i[key].split("external:")[1], key
570 ), indent=10)
571 else:
572 self.add_content(
573 "<a class='nav-link' "
574 "href='#' onclick='grab_html(\"{}\", label=\"{}\")'"
575 ">{}</a>\n".format(key, i[key], key), indent=10)
577 else:
578 if "external:" in i:
579 self.add_content(
580 "<a class='nav-link' "
581 "href='#' onclick='window.location=\"{}\"'"
582 ">{}</a>\n".format(
583 i.split("external:")[1], i
584 ), indent=10)
585 else:
586 self.add_content(
587 "<a class='nav-link' "
588 "href='#' onclick='grab_html(\"{}\")'"
589 ">{}</a>\n".format(i, i), indent=10)
591 self.add_content("</li>\n", indent=8)
592 self.add_content("</ul>\n", indent=6)
593 self.add_content("</div>\n", indent=4)
594 if histogram_download:
595 self.add_content("<a href='%s' download>" % (histogram_download),
596 indent=4)
597 self.add_content(
598 "<button type='submit' style='margin-right: 15px; cursor:pointer'> "
599 "<i class='fa fa-download'></i> Histogram Data</button>", indent=6)
600 self.add_content("</a>", indent=4)
602 self.add_content("<div class='collapse navbar-collapse' id='collapsibleNavbar'>\n", indent=4)
603 self.add_content(
604 "<ul class='navbar-nav flex-row ml-md-auto d-none d-md-flex'"
605 "style='margin-right:1em;'>\n", indent=6)
606 if toggle:
607 self.add_content(
608 "<div style='margin-top:0.5em; margin-right: 1em;' "
609 "data-toggle='tooltip' title='Activate expert mode'>", indent=6
610 )
611 self.add_content("<label class='switch'>", indent=8)
612 self.add_content(
613 "<input type='checkbox' onchange='show_expert_div()'>", indent=10
614 )
615 self.add_content("<span class='slider round'></span>", indent=10)
616 self.add_content("</label>", indent=8)
617 self.add_content("</div>", indent=6)
618 self.add_content(
619 "<a class='nav-link' href='#', onclick='grab_html(\"{}\")'"
620 ">{}</a>\n".format("Downloads", "Downloads"), indent=2
621 )
622 if about:
623 self.add_content(
624 "<a class='nav-link' href='#', onclick='grab_html(\"{}\")'"
625 ">{}</a>\n".format("About", "About"), indent=2
626 )
627 self.add_content("</ul>\n", indent=6)
628 self.add_content("</div>\n", indent=4)
629 if search:
630 self.add_content("<input type='text' placeholder='search' id='search'>\n", indent=4)
631 self.add_content("<button type='submit' onclick='myFunction()'>Search</button>\n", indent=4)
632 self.add_content("</nav>\n")
634 def make_table(self, headings=None, contents=None, heading_span=1,
635 colors=None, accordian_header="Summary Table",
636 accordian=True, format="table-striped table-sm",
637 sticky_header=False, scroll_table=False, **kwargs):
638 """Generate a table in bootstrap format.
640 Parameters
641 ----------
642 headings: list, optional
643 list of headings
644 contents: list, optional
645 nd list giving the contents of the table.
646 heading_span: int, optional
647 width of the header cell. By default it will span a single column
648 colors: list, optional
649 list of colors for the table columns
650 """
651 if sticky_header:
652 self.add_content(
653 "<style>\n"
654 ".header-fixed > tbody > tr > td,\n"
655 ".header-fixed > thead > tr > th {\n"
656 )
657 self.add_content(" width: {width}%;\n".format(
658 width=100. / len(headings)
659 ))
660 self.add_content(
661 " float: left;\n}\n</style>"
662 )
664 if scroll_table:
665 self.add_content(
666 "<style>\n"
667 ".scroll-table tbody, tr, th {\n"
668 " width: 100%;\n"
669 " float: left;\n}\n"
670 ".scroll-table td {\n"
671 )
672 self.add_content(" width: {}%;\n".format(100 / len(headings)))
673 self.add_content(
674 " float: left;\n}\n</style>"
675 )
677 if accordian:
678 label = accordian_header.replace(" ", "_")
679 self.make_container(style=kwargs.get("style", None))
680 self.make_div(indent=2, _class='row justify-content-center')
681 self.make_div(
682 indent=4, _class='accordian', _style='width: 100%',
683 _id='accordian%s' % (label))
684 self.make_div(indent=6, _class='card')
685 self.make_div(
686 indent=8, _class='card-header', _style='background-color: #E0E0E0',
687 _id='table')
688 self.add_content("<h5 class='mb-0'>", indent=10)
689 self.make_div(indent=12, _class='row justify-content-center')
690 self.add_content(
691 "<button class='btn btn-link collapsed' type='button' "
692 "data-toggle='collapse' data-target='#collapsetable%s' "
693 "aria-expanded='false' aria-controls='collapsetable'>" % (label), indent=14)
694 self.add_content(accordian_header)
695 self.add_content("</button>")
696 self.end_div(indent=12)
697 self.end_div(indent=10)
698 self.add_content(
699 "<div id='collapsetable%s' class='collapse' "
700 "aria-labelledby='table' data-parent='#accordian%s'>" % (label, label),
701 indent=12)
702 self.make_div(_class='card-body', indent=14)
703 self.make_div(_class='row justify-content-center', indent=16)
704 self.make_div(_class='container', _style='max-width:1400px', indent=18)
705 if type(contents) == list:
706 self.make_div(indent=20, _class='table-responsive')
707 if heading_span > 1:
708 self.add_content(
709 "<table class='table table-sm' style='max-width:1400px'>\n",
710 indent=22
711 )
712 else:
713 self.add_content(
714 "<table class='table %s' style='max-width:1400px'>\n" % (format),
715 indent=24
716 )
717 self.add_content("<thead>\n", indent=26)
718 self.add_content("<tr>\n", indent=28)
719 for i in headings:
720 self.add_content("<th colspan='{}'>{}</th>\n".format(heading_span, i), indent=30)
721 self.add_content("</tr>\n", indent=28)
722 self.add_content("<tbody>\n", indent=26)
724 for num, i in enumerate(contents):
725 self.add_content("<tr>\n", indent=28)
726 if heading_span == 2:
727 for j, col in zip(i, ["#ffffff"] + colors * (len(i) - 1)):
728 self.add_content(
729 "<td style='background-color: {}'>{}</td>\n".format(col, j), indent=30)
730 self.add_content("</tr>", indent=28)
731 else:
732 for j in i:
733 self.add_content("<td>{}</td>\n".format(j), indent=30)
734 self.add_content("</tr>\n", indent=28)
735 self.add_content("</tbody>\n", indent=26)
736 self.add_content("</table>\n", indent=24)
737 self.end_div(indent=20)
738 elif type(contents) == dict:
739 self.make_div(indent=20, _class='table-responsive')
740 if heading_span > 1:
741 self.add_content("<table class='table table-sm'>\n", indent=22)
742 else:
743 self.add_content(
744 "<table class='table %s' style='max-width:1400px'>\n" % (format),
745 indent=24
746 )
747 self.add_content("<tbody>\n", indent=26)
748 for j in contents.keys():
749 self.add_content("<tr bgcolor='#F0F0F0'>\n", indent=28)
750 self.add_content(
751 "<th colspan='{}' style='width: 100%' class='text-center'>"
752 "{}</th>\n".format(len(contents[j][0]), j), indent=30)
753 self.add_content("</tr>\n", indent=28)
754 self.add_content("<tr>\n", indent=28)
755 for i in headings:
756 self.add_content(
757 "<td colspan='{}' class='text-center'>"
758 "{}</td>\n".format(heading_span, i), indent=30)
759 self.add_content("</tr>\n", indent=28)
761 for num, i in enumerate(contents[j]):
762 self.add_content("<tr>\n", indent=28)
763 for j in i:
764 self.add_content(
765 "<td>{}</td>\n".format(j), indent=30
766 )
767 self.add_content("</tr>\n", indent=28)
768 self.add_content("</tbody>\n", indent=26)
769 self.add_content("</table>\n", indent=24)
770 self.end_div(indent=20)
772 self.end_div(indent=18)
773 if accordian:
774 self.end_div(indent=16)
775 self.end_div(indent=14)
776 self.end_div(indent=12)
777 self.end_div(indent=10)
778 self.end_div(indent=8)
779 self.end_div(indent=6)
780 self.end_div(indent=4)
781 self.end_div(indent=2)
782 self.end_div()
784 def make_code_block(self, language=None, contents=None):
785 """Generate a code block hightlighted using pigments.
787 Parameters
788 ----------
789 language: str, optional
790 The lanaguge of the configuration file to use for syntax
791 highlighting.
792 contents: str, optional
793 String containing the contents of the config file.
795 Returns
796 -------
797 style: str
798 The styles used to highlight the rendered contents.
799 """
800 lexer = get_lexer_by_name(language)
801 formatter = HtmlFormatter(style='manni')
802 render = highlight(contents, lexer, formatter)
803 self.add_content(render)
804 styles = formatter.get_style_defs('.highlight')
805 styles += ".highlight {margin: 5px; padding: 10px; background: #FFFFFF}"
806 return styles
808 def make_table_of_images(self, contents=None, rows=None, columns=None,
809 code="modal", cli=None, autoscale=False,
810 unique_id=None, captions=None, extra_div=False,
811 mcmc_samples=False, margin_left=None, display=None,
812 container_id=None, **kwargs):
813 """Generate a table of images in bootstrap format.
815 Parameters
816 ----------
817 headings: list, optional
818 list of headings
819 contents: list, optional
820 nd list giving the contents of the table.
821 carousel: bool, optional
822 if True, the images will be configured to work operate as part of
823 a carousel
824 width: float, optional
825 width of the images in the table
826 container: bool, optional
827 if True, the table of images is placed inside a container
828 """
829 table = tables.table_of_images(contents, rows, columns, self.html_file,
830 code=code, cli=cli, autoscale=autoscale,
831 unique_id=unique_id, captions=captions,
832 extra_div=extra_div, display=display,
833 mcmc_samples=mcmc_samples,
834 margin_left=margin_left,
835 container_id=container_id, **kwargs)
836 table.make()
838 def insert_image(self, path, justify="center", code=None):
839 """Generate an image in bootstrap format.
841 Parameters
842 ----------
843 path: str, optional
844 path to the image that you would like inserted
845 justify: str, optional
846 justifies the image to either the left, right or center
847 """
848 self.make_container()
849 _id = path.split("/")[-1][:-4]
850 string = "<img src='{}' id='{}' alt='No image available' ".format(path, _id) + \
851 "style='align-items:center; width:850px; cursor: pointer'"
852 if justify == "center":
853 string += " class='mx-auto d-block'"
854 elif justify == "left":
855 string = string[:-1] + " float:left;'"
856 elif justify == "right":
857 string = string[:-1] + " float:right;'"
858 if code:
859 string += " onclick='{}(\"{}\")'".format(code, _id)
860 string += ">\n"
861 self.add_content(string, indent=2)
862 self.end_container()
864 def make_accordian(self, headings=None, content=None):
865 """Generate an accordian in bootstrap format with images as content.
867 Parameters
868 ----------
869 headings: list, optional
870 list of headings that you want your accordian to have
871 content: nd list, optional
872 n dimensional list where n is the number of rows. The content
873 of each list should be the path to the location of the image
874 """
875 self.make_div(_class='row justify-content-center')
876 self.add_content("<div class='accordian' id='accordian' style='width:70%'>\n", indent=2)
877 for num, i in enumerate(headings):
878 self.add_content("<div class='card' style='border: 0px solid black'>\n", indent=4)
879 self.add_content("<div class='card-header' id='{}'>\n".format(i), indent=6)
880 self.add_content("<h5 class='mb-0'>\n", indent=8)
881 self.add_content(
882 "<button class='btn btn-link collapsed' type='button' data-toggle='collapse' "
883 "data-target='#collapse{}' aria-expanded='false' ".format(i)
884 + "aria-controls='collapse{}'>\n".format(i), indent=10)
885 self.add_content("{}\n".format(i), indent=12)
886 self.add_content("</button>\n", indent=10)
887 self.add_content("</h5>\n", indent=8)
888 self.add_content("</div>\n", indent=6)
889 self.add_content(
890 "<div id='collapse{}' class='collapse' ".format(i)
891 + "aria-labelledby='{}' data-parent='#accordian'>\n".format(i), indent=6)
892 self.add_content("<div class='card-body'>\n", indent=8)
893 self.add_content("<img src='{}' ".format(content[num])
894 + "alt='No image available' style='width:700px;' "
895 + "class='mx-auto d-block'>\n", indent=10)
896 self.add_content("</div>\n", indent=8)
897 self.add_content("</div>\n", indent=6)
898 self.add_content("</div>\n", indent=4)
899 self.add_content("</div>\n", indent=2)
900 self.add_content("</div>\n")
902 def make_search_bar(self, sidebar=None, popular_options=None, label=None,
903 code="combine"):
904 """Generate a search bar to combine the corner plots
905 javascript.
907 Parameters
908 ----------
909 sidebar: list, optional
910 a list of parameters that you would like included in the side bar
911 popular_options: list, optional
912 a list of popular options for your search bar
913 """
914 import copy
915 ids = "canvas" if code == "combine" else code
916 self.add_content("<link rel='stylesheet' href='../css/side_bar.css'>\n")
917 self.add_content("<div class='w3-sidebar w3-bar-block w3-border-right sidenav' "
918 "style='display:none' id='mySidebar'>\n")
919 self.add_content("<button onclick='side_bar_close()' class='close'>×</button>\n", indent=2)
920 if sidebar is not None:
921 _sidebar = copy.deepcopy(sidebar)
922 _sidebar.sort()
923 for i in _sidebar:
924 self.add_content("<input type='checkbox' name='type' "
925 "value='{}' id='{}' style='text-align: center; margin: 0 5px 0;'"
926 ">{}<br>\n".format(i, i, i), indent=2)
927 self.add_content("</div>")
928 self.add_content("<div class='row justify-content-center'>")
929 self.add_content(
930 "<p style='margin-top:2.5em; font-family: Arial-body; font-size:14px'>"
931 " Input the parameter names that you would like to compare</p>", indent=2)
932 self.add_content("</div>")
933 self.add_content("<div class='row justify-content-center'>\n")
934 self.add_content("<input type='text' placeholder='search' id='corner_search'>\n", indent=2)
935 self.add_content(
936 "<button type='submit' onclick='{}(undefined, label=\"{}\")' "
937 "style='cursor: pointer'>Submit</button>\n".format(code, label), indent=2)
938 self.add_content(
939 "<button class='w3-button w3-teal w3-xlarge' "
940 "onclick='side_bar_open()' style='cursor: pointer'>≡</button>\n", indent=2)
941 self.add_content("</div>\n")
942 self.add_content("<div class='row justify-content-center'>\n")
943 if popular_options:
944 for i in popular_options:
945 if type(i) == dict and list(i.keys()) == ["all"]:
946 self.add_content("<button type='button' class='btn btn-info' "
947 "onclick='{}(\"{}\", label=\"{}\")' "
948 "style='margin-left:0.25em; margin-right:0.25em; "
949 "margin-top: 1.0em; cursor: pointer'>all</button>\n".format(
950 code, i["all"], label), indent=2)
951 else:
952 self.add_content("<button type='button' class='btn btn-info' "
953 "onclick='{}(\"{}\", label=\"{}\")' "
954 "style='margin-left:0.25em; margin-right:0.25em; "
955 "margin-top: 1.0em; cursor: pointer'>{}</button>\n".format(
956 code, i, label, i), indent=2)
957 self.add_content("</div>")
958 self.make_container()
959 self.add_content("<div class='row justify-content-center' id='corner_plot'>\n", indent=2)
960 self.add_content("<canvas id='{}' width='1000' height='1000'></canvas>\n".format(ids), indent=4)
961 if code == "combine":
962 self.add_content("<img src='' id='mirror'/>", indent=4)
963 self.add_content("</div>\n", indent=2)
964 self.end_container()
966 def make_modal_carousel(self, images=None, unique_id=None):
967 """Make a pop up window that appears on top of the home page showing
968 images in a carousel.
970 Parameters
971 ----------
972 images: list
973 list of image locations that you would like included in the
974 carousel
975 """
976 if unique_id is not None:
977 modal_id = "Modal_{}".format(unique_id)
978 demo_id = "demo_{}".format(unique_id)
979 else:
980 modal_id = "MyModal"
981 demo_id = "demo"
982 self.add_content("<div class='modal fade bs-example-modal-lg' tabindex='-1' "
983 "role='dialog' aria-labelledby='myLargeModalLabel' "
984 "aria-hidden='true' id='{}' style='margin-top: 200px;'>"
985 "\n".format(modal_id))
986 self.add_content("<div class='modal-dialog modal-lg' style='width:90%'>\n", indent=2)
987 self.add_content("<div class='modal-content'>\n", indent=4)
988 self.add_content("<div id='{}' class='carousel slide' data-ride='carousel'"
989 " data-interval='false'>\n".format(demo_id), indent=6)
990 self.add_content("<ul class='carousel-indicators'>\n", indent=8)
991 for num, i in enumerate(images):
992 if num == 0:
993 self.add_content("<li data-target='#%s' data-slide-to-'%s' "
994 "class='active'></li>\n" % (demo_id, num), indent=10)
995 self.add_content("<li data-target='#%s' data-slide-to-'%s'>"
996 "</li>\n" % (demo_id, num), indent=10)
997 self.add_content("<li data-target='#{}' data-slide-to='0' "
998 "class='active'></li>\n".format(demo_id), indent=10)
999 self.add_content(
1000 "<li data-target='#{}' data-slide-to='1'></li>\n".format(demo_id),
1001 indent=10
1002 )
1003 self.add_content(
1004 "<li data-target='#{}' data-slide-to='2'></li>\n".format(demo_id),
1005 indent=10
1006 )
1007 self.add_content("</ul>\n", indent=8)
1008 self.add_content("<div class='carousel-inner'>\n", indent=8)
1009 for num, i in enumerate(images):
1010 if num == 0:
1011 self.add_content("<div class='carousel-item active'>\n", indent=10)
1012 else:
1013 self.add_content("<div class='carousel-item'>\n", indent=10)
1014 self.add_content("<img src={} style='align-items:center;' "
1015 "class='mx-auto d-block'>\n".format(i), indent=12)
1016 self.add_content("</div>\n", indent=10)
1018 self.add_content("</div>\n", indent=8)
1019 self.add_content("<a class='carousel-control-prev' href='#{}' "
1020 "data-slide='prev'>\n".format(demo_id), indent=8)
1021 self.add_content("<span class='carousel-control-prev-icon'></span>\n", indent=10)
1022 self.add_content("</a>\n", indent=8)
1023 self.add_content("<a class='carousel-control-next' href='#{}' "
1024 "data-slide='next'>\n".format(demo_id), indent=8)
1025 self.add_content("<span class='carousel-control-next-icon'></span>\n", indent=10)
1026 self.add_content("</a>\n", indent=8)
1027 self.add_content("</div>\n", indent=6)
1028 self.add_content("</div>\n", indent=4)
1029 self.add_content("</div>\n", indent=2)
1030 self.add_content("</div>\n")
1032 def make_cli_button(self, cli):
1033 """Make a button showing the command line used
1035 Parameters
1036 ----------
1037 cli: str
1038 the command line that you wish to display in the modal
1039 """
1040 self.add_content("<style>")
1041 self.add_content(".popover {", indent=2)
1042 self.add_content("max-width: 550px;", indent=4)
1043 self.add_content("width: 550px;", indent=4)
1044 self.add_content("}", indent=2)
1045 self.add_content("</style>")
1046 self.make_div(2, _class="imgButton", _style=None)
1047 self.add_content("<button value='test' class='btn btn-info btn-xs' "
1048 "style='cursor: pointer' "
1049 "data-toggle='popover' data-placement='top' "
1050 "data-content='%s'>Command Line</button>" % (cli),
1051 indent=12)
1052 self.end_div(0)
1054 def make_watermark(self, text="Preliminary"):
1055 """Add a watermark to the html page
1057 Parameters
1058 ----------
1059 text: str
1060 work you wish to use as a watermark
1061 """
1062 self.add_content("<div id='background'>")
1063 for _ in range(3):
1064 self.add_content(
1065 "<p id='bg-text'>{} {}</p>".format(text, text)
1066 )
1067 self.end_div()
1069 def export(
1070 self, filename, csv=True, json=False, shell=False, histogram_dat=None,
1071 requirements=False, conda=False, margin_top="-4em", margin_bottom="5em",
1072 ):
1073 """Make a button which to export a html table to csv
1075 Parameters
1076 ----------
1077 filename: str
1078 the name of the file you wish to save the data too
1079 """
1080 if "." in filename:
1081 basename = ".".join(filename.split(".")[:-1]) + ".{}"
1082 else:
1083 basename = filename + ".{}"
1084 self.add_content(
1085 "<div class='container' style='margin-top:{}; margin-bottom:{}; "
1086 "max-width: 1400px'>".format(margin_top, margin_bottom)
1087 )
1088 json_margin = "0em"
1089 bash_margin = "0.5em"
1090 if csv and json:
1091 json_margin = "0.5em"
1092 bash_margin = "0em"
1093 self.add_content("<div class='row' style='margin-left: 0.2em'>")
1094 if csv:
1095 self.add_content(
1096 "<button type='button' onclick='export_table_to_csv(\"{}\")' "
1097 "class='btn btn-outline-secondary btn-table'>Export to CSV"
1098 "</button>".format(basename.format("csv"))
1099 )
1100 if requirements:
1101 self.add_content(
1102 "<button type='button' onclick='export_table_to_pip(\"{}\")' "
1103 "class='btn btn-outline-secondary btn-table'>Export to pip"
1104 "</button>".format(basename.format("txt"))
1105 )
1106 if conda:
1107 self.add_content(
1108 "<button type='button' onclick='export_table_to_conda(\"{}\")' "
1109 "class='btn btn-outline-secondary btn-table'>Export to conda"
1110 "</button>".format(basename.format("txt"))
1111 )
1112 if json:
1113 self.add_content(
1114 "<button type='button' onclick='export_table_to_json(\"{}\")' "
1115 "style='margin-left: {}' class='btn btn-outline-secondary "
1116 "btn-table'>Export to JSON</button>".format(
1117 basename.format("json"), json_margin
1118 )
1119 )
1120 if shell:
1121 self.add_content(
1122 "<button type='button' onclick='export_table_to_shell(\"{}\")' "
1123 "style='margin-left: {}; margin-bottom: {}' "
1124 "class='btn btn-outline-secondary btn-table'>Export to bash"
1125 "</button>".format(
1126 basename.format("sh"), json_margin, bash_margin
1127 )
1128 )
1129 if histogram_dat is not None:
1130 self.add_content("<a href='%s' download>" % (histogram_dat))
1131 self.add_content(
1132 "<button type='button' style='margin-left: {}; margin-bottom: "
1133 "{}' class='btn btn-outline-secondary btn-table'>Export to dat"
1134 "</button>".format(json_margin, margin_bottom)
1135 )
1136 self.add_content("</a>")
1137 if csv and json:
1138 self.end_div()
1139 self.end_div(0)