Coverage for pesummary/core/webpage/webpage.py: 93.7%

475 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-05-02 08:42 +0000

1# Licensed under an MIT style license -- see LICENSE.md 

2 

3import pesummary 

4from pesummary.core.webpage import tables 

5from pesummary.core.webpage.base import Base 

6 

7from pygments import highlight 

8from pygments.lexers import get_lexer_by_name 

9from pygments.formatters import HtmlFormatter 

10import time 

11 

12__author__ = [ 

13 "Charlie Hoy <charlie.hoy@ligo.org>", 

14 "Edward Fauchon-Jones <edward.fauchon-jones@ligo.org>" 

15] 

16 

17BOOTSTRAP = """<!DOCTYPE html> 

18 

19<!-- 

20 Made by 

21 ____ ___________ 

22 / __ \/ ____/ ___/__ ______ ___ ____ ___ ____ ________ __ 

23 / /_/ / __/ \__ \/ / / / __ `__ \/ __ `__ \/ __ `/ ___/ / / / 

24 / ____/ /___ ___/ / /_/ / / / / / / / / / / / /_/ / / / /_/ / 

25 /_/ /_____//____/\__,_/_/ /_/ /_/_/ /_/ /_/\__,_/_/ \__, / 

26 /____/ 

27 

28 MIT License 

29 

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""" 

45 

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""" 

68 

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""" 

91 

92 

93def make_html(web_dir, title="Summary Pages", pages=None, stylesheets=[], 

94 label=None): 

95 """Make the initial html page. 

96 

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) 

130 

131 

132def open_html(web_dir, base_url, html_page, label=None): 

133 """Open html page ready so you can manipulate the contents 

134 

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) 

160 

161 

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 = [] 

171 

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)) 

177 

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() 

251 

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) 

266 

267 def make_header(self, approximant="IMRPhenomPv2"): 

268 """Make header for document in bootstrap format. 

269 

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) 

278 

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) 

283 

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() 

399 

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. 

405 

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, 

414 

415 links=[corner, [1d_histograms, [mass1, mass2, mchirp]]] 

416 

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) 

479 

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 ) 

504 

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) 

510 

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 ) 

529 

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 ) 

546 

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) 

576 

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) 

590 

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) 

601 

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") 

633 

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. 

639 

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 ) 

663 

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 ) 

676 

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) 

723 

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) 

760 

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) 

771 

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() 

783 

784 def make_code_block(self, language=None, contents=None): 

785 """Generate a code block hightlighted using pigments. 

786 

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. 

794 

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 

807 

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. 

814 

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() 

837 

838 def insert_image(self, path, justify="center", code=None): 

839 """Generate an image in bootstrap format. 

840 

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() 

863 

864 def make_accordian(self, headings=None, content=None): 

865 """Generate an accordian in bootstrap format with images as content. 

866 

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") 

901 

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. 

906 

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'>&times;</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'>&#8801</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() 

965 

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. 

969 

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) 

1017 

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") 

1031 

1032 def make_cli_button(self, cli): 

1033 """Make a button showing the command line used 

1034 

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) 

1053 

1054 def make_watermark(self, text="Preliminary"): 

1055 """Add a watermark to the html page 

1056 

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() 

1068 

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 

1074 

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)