Package pylal :: Module htmlutils
[hide private]
[frames] | no frames]

Source Code for Module pylal.htmlutils

  1  #!/usr/bin/env python 
  2   
  3  # Copyright (C) 2012 Duncan M. Macleod 
  4  # 
  5  # This program is free software; you can redistribute it and/or modify it 
  6  # under the terms of the GNU General Public License as published by the 
  7  # Free Software Foundation; either version 3 of the License, or (at your 
  8  # option) any later version. 
  9  # 
 10  # This program is distributed in the hope that it will be useful, but 
 11  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General 
 13  # Public License for more details. 
 14  # 
 15  # You should have received a copy of the GNU General Public License along 
 16  # with this program; if not, write to the Free Software Foundation, Inc., 
 17  # 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. 
 18   
 19  """Utilities for HTML generation on the LIGO Data Grid. 
 20   
 21  This module provides some useful functions for buildling HTML content 
 22  on the LIGO Data Grid, and a few extensions to glue.markup to streamline 
 23  GEO/LIGO tools that use very similar web interfaces. 
 24  """ 
 25   
 26  # ============================================================================= 
 27  # Preamble 
 28  # ============================================================================= 
 29   
 30  from __future__ import division 
 31   
 32  import re 
 33  import os 
 34  import sys 
 35  import socket 
 36   
 37  from glue import markup 
 38   
 39  from pylal import git_version 
 40   
 41  __author__ = "Duncan M. Macleod <duncan.macleod@ligo.org>" 
 42  __version__ = "git id %s" % git_version.id 
 43  __date__ = git_version.date 
 44   
 45  __all__ = ["about_page", "build_page", "get_web_server", "simple_page",\ 
 46             "summary_page", "write_glossary", "write_menu", "write_table"] 
 47   
 48  # ============================================================================= 
 49  # Write table 
 50  # ============================================================================= 
 51   
52 -def write_table(headers, data, classdict={}):
53 54 """ 55 Write table into glue.markup.page object. headers are written with <th>, 56 multiple columns of data are written with <td>. 57 58 Arguments: 59 60 page : glue.markup.page 61 page object into which to write table 62 headers : list 63 list of table header elements 64 data : list 65 list (or nested list) of table data elements, list of lists used for 66 multiple rows 67 68 Keyword arguments: 69 70 classdict : dict 71 dict containing tag:class pairs for table, td, th, and td HTML tags 72 "table":"list" prints table with headers and data side-by-side, 73 all others print all headers in one row, then all data in one row 74 (use list of lists for multiple rows). 75 """ 76 77 # extract classes 78 tclass = classdict.get("table","") 79 rclass = classdict.get("tr","") 80 hclass = classdict.get("th","") 81 dclass = classdict.get("td","") 82 83 page = markup.page() 84 85 # open table 86 page.table(class_=tclass) 87 88 # list: print two vertical columns of header:data pairs 89 if tclass == "list": 90 for i in range(len(headers)): 91 page.tr(class_=rclass) 92 page.th(str(headers[i]), class_=hclass) 93 page.td(str(data[i]), class_=classdict.get(str(data[i]),dclass)) 94 page.tr.close() 95 96 # otherwise print "standard" table with single header row and multiple data 97 # rows 98 else: 99 page.tr(class_=rclass) 100 if len(headers)==1: 101 page.th(str(headers[0]), colspan="100%", class_=hclass) 102 else: 103 for n in headers: 104 page.th(str(n), class_=hclass) 105 page.tr.close() 106 107 if data and not re.search("list",str(type(data[0]))): 108 data = [data] 109 110 for row in data: 111 page.tr(class_=rclass) 112 for item in map(str, row): 113 page.td(item, class_=classdict.get(item,dclass)) 114 115 page.table.close() 116 117 return page
118 119 # ============================================================================= 120 # Write <div id_="menubar"> 121 # ============================================================================= 122
123 -def write_menu(sections, pages, current=None, classdict={"a":"menulink"}):
124 125 """ 126 Returns glue.markup.page object for <div id_="menubar">, constructing menu 127 in HTML. 128 129 Arguments: 130 131 sections : list 132 ordered list of menu entry names 133 pages : dict 134 dict of section:href pairs holding link paths for each element of 135 sections list 136 137 Keyword arguments: 138 139 current : str 140 element of sections list to identify with class="open" 141 classdict : dict 142 dict of tag:class pairs for setting HTML tags 143 144 """ 145 146 page = markup.page() 147 page.div(id_="menubar", class_=classdict.get("div", "")) 148 149 for i,sec in enumerate(sections): 150 # set current class 151 if sec == current and not re.search("open", classdict.get("a", "")): 152 cl = classdict["a"] + " open" 153 else: 154 cl = classdict["a"] 155 156 # remove index.html to make url look nicer 157 if pages[sec].endswith("/index.html"): 158 href = pages[sec][:-11] 159 else: 160 href = pages[sec] 161 162 # make link 163 page.a(sec, id_="a_%d" % i, class_=cl, href=href) 164 165 page.div.close() 166 167 return page
168 169 # ============================================================================= 170 # Write glossary 171 # ============================================================================= 172
173 -def write_glossary(entries, htag="h1",\ 174 classdict={"p":"line", "h4":"glossary closed"}):
175 """ 176 Write a glossary of DQ terms into the glue.markup.page object page using the 177 list of (term,definition) tuples terms. 178 179 Arguments: 180 181 entries : dict 182 dict of term:definition pairs for inclusion in glossary 183 184 Keyword arguments: 185 186 htag : str 187 HTML tag to use for header, default <h1> 188 classdict : dict 189 dict of tag:class pairs for HTML class assignment 190 """ 191 192 page = markup.page() 193 194 # write heading and description 195 getattr(page, htag)("Glossary", id_="%s_glossary" % htag) 196 page.div(id_="div_glossary", style="display: block;",\ 197 class_=classdict.get("div", ""), onclick="toggleVisible();") 198 page.p("This section gives a glossary of terms relevant to this page.",\ 199 class_=classdict.get("p", "")) 200 lvwiki = "https://www.lsc-group.phys.uwm.edu/ligovirgo/cbcnote/Acronyms" 201 page.p("The LIGO-Virgo acronym wiki can be found on %s."\ 202 % markup.oneliner.a("this page", href=lvwiki),\ 203 class_=classdict.get("p", "")) 204 205 # write glossary table 206 terms = sorted(entries.keys()) 207 for i,term in enumerate(terms): 208 page.h4(term, id_="glossaryh4_%d" % i, class_=classdict.get("h4", "")) 209 page.div(entries[term], id_="div_%d" % i, style="display: none;",\ 210 class_="glossary") 211 212 page.div.close() 213 214 return page
215 216 # ============================================================================= 217 # Construct HTML base 218 # ============================================================================= 219
220 -def get_web_server():
221 """Find the web server for this host on the LIGO Data Grid. 222 223 @returns the fully-qualified domain name of the web server 224 associated with this host. 225 226 Example: 227 >>> socket.getfqdn() 228 ldas-pcdev1.ligo-la.caltech.edu 229 >>> htmlutils.get_web_server() 230 "https://ldas-jobs.ligo-la.caltech.edu" 231 """ 232 # get fully qualified domain name (FQDN) 233 fqdn = socket.getfqdn() 234 235 # work out web root 236 if re.search("ligo.caltech", fqdn): 237 url = "https://ldas-jobs.ligo.caltech.edu" 238 elif re.search("ligo-wa", fqdn): 239 url = "https://ldas-jobs.ligo-wa.caltech.edu" 240 elif re.search("ligo-la", fqdn): 241 url = "https://ldas-jobs.ligo-la.caltech.edu" 242 elif re.search("atlas", fqdn): 243 match = re.search("atlas[13]", fqdn) 244 if match: 245 host = match.group() 246 url = "https://%s.atlas.aei.uni-hannover.de" % host 247 else: 248 url = "https://atlas1.atlas.aei.uni-hannover.de" 249 elif re.search("phy.syr", fqdn): 250 url = "https://sugar-jobs.phy.syr.edu" 251 elif re.search("astro.cf", fqdn): 252 url = "https://gravity.astro.cf.ac.uk" 253 elif re.search("phys.uwm", fqdn): 254 url = "https://ldas-jobs.phys.uwm.edu" 255 else: 256 raise socket.herror("No web domain known for host %s." % fqdn) 257 258 return url
259 260 # ============================================================================= 261 # Write simple page with plots and run information 262 # ============================================================================= 263
264 -def simple_page(header, htag="h1", desc=None, plotlist=None, 265 args=None, version=None, classdict={}, init=False):
266 """ 267 Returns a glue.markup.page object with an h1 heading, descriptive text,\ 268 some plots, and the arguments used to generate it. 269 270 Designed for embedding/including as a frame in a larger page. 271 272 Arguments: 273 274 header : str 275 text to write as header (<h1>) 276 htag : str 277 HTML tag to use for header, default <h1> 278 desc : str 279 descriptive text to print below header(s) (<p>) 280 plotlist : list 281 list of filepath strings or (path, title) tuples to include 282 (<a>,<img>) 283 args : [ str | list ] 284 string with arguments used to generate plots, or list of arguments 285 to be space-separated 286 version : str 287 code version str/number to include 288 classdict : dict 289 dict containing HTML class strings for each tag used 290 init : [ True | False ] 291 initialise the markup.page object, adds HTML and BODY tags 292 """ 293 294 # generate object 295 page = markup.page() 296 297 # initialise 298 if init: page.init() 299 300 # print header 301 if header is not None: 302 getattr(page, htag)(header, class_=classdict.get(htag,""),\ 303 id_="%s_simple-page" % htag, 304 onclick="toggleVisible();") 305 306 # set div 307 hclass = classdict.get(htag, "open") 308 if hclass == "closed": display="none" 309 else: display="block" 310 page.div(class_=classdict.get("div",""), id_="div_simple-page",\ 311 style="display: %s;" % display) 312 313 if desc is not None: 314 page.p(desc, class_=classdict.get("p","")) 315 316 # add plots 317 if plotlist is not None: 318 for p in plotlist: 319 if isinstance(p, str): 320 alt = None 321 else: 322 p,alt = p 323 page.a(href=p, title=alt, class_=classdict.get("a","")) 324 page.img(src=p, alt=alt, class_=classdict.get("img","")) 325 page.a.close() 326 327 if args is not None: 328 page.p("Generated by running:", class_=classdict.get("p","")) 329 if not isinstance(args, str): args = " ".join(args) 330 page.pre(args, class_=classdict.get("pre","")) 331 if version is not None: 332 page.p("Code version: %s" % version, class_=classdict.get("p","")) 333 334 page.div.close() 335 336 return page
337 338 # ============================================================================= 339 # Construct page from various component 340 # ============================================================================= 341
342 -def build_page(icon=None, banner=None, homebutton=None, tabs=None,\ 343 menu=None, frame=None, **initargs):
344 """ 345 Build a complete HTML page from 6 components: icon banner homebutton tabs 346 menu frame. All other args passed to glue.markup.page.init function. 347 See docstring of that function for help. 348 349 Body is built to the following format: 350 351 <div id_="container"> 352 <div class="content" id_="header"> 353 <div> 354 <div class="nav"> 355 ICON 356 </div> 357 <div class="frame"> 358 BANNER 359 </div> 360 </div> 361 </div> 362 <div class="content" id_="tabs"> 363 <div> 364 <div class="nav"> 365 HOMEBUTTON 366 </div> 367 <div class="frame"> 368 TABS 369 </div> 370 </div> 371 </div> 372 <div class="content" id_="main"> 373 <div> 374 <div class="nav"> 375 MENUBAR 376 </div> 377 <div class="frame"> 378 FRAME 379 </div> 380 </div> 381 </div> 382 </div> 383 384 """ 385 386 # setup page object 387 page = markup.page() 388 initargs.setdefault("doctype", "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">") 389 page.init(**initargs) 390 391 page.div(id_="container") 392 393 # build top bar from icon and banner 394 if icon is not None or banner is not None: 395 page.div(class_="content", id_="header") 396 page.div() 397 page.div(str(icon), class_="nav", id_="headernav") 398 page.div(str(banner), class_="frame", id_="headerframe") 399 page.div.close() 400 page.div.close() 401 402 # build tab bar from home button and tab buttons 403 if homebutton is not None or tabs is not None: 404 page.div(class_="content", id_="tabs") 405 page.div() 406 if not homebutton: homebutton = "" 407 page.div(str(homebutton), class_="nav", id_="tabsnav") 408 page.div(str(tabs), class_="frame", id_="tabsframe") 409 page.div.close() 410 page.div.close() 411 412 # build main page from menu and frame 413 if menu is not None or frame is not None: 414 page.div(class_="content", id_="main") 415 page.div() 416 page.div(str(menu), class_="nav", id_="mainnav") 417 page.div(str(frame), class_="frame", id_="mainframe") 418 page.div.close() 419 page.div.close() 420 421 page.div.close() 422 423 return page
424 425 # ============================================================================= 426 # Write summary page style page with plots and information 427 # ============================================================================= 428
429 -def summary_page(header=None, htag="h1", desc=None, plotlist=None, 430 text=None, subplots=None, info=None, classdict={},\ 431 init=False):
432 """ 433 Returns a glue.markup.page object with an heading, descriptive text,\ 434 summary section with one plot and text, section with other plots, 435 section with subplots, then section with info. 436 437 Designed for embedding/including as a frame in a larger page. 438 439 Keyword arguments: 440 441 header : str 442 text to write as header (<h1>) 443 htag : str 444 HTML tag to use for header, default <h1> 445 desc : str 446 descriptive text to print below header(s) (<p>) 447 plotlist : list 448 list of filepath strings or (path, title) tuples to include 449 (<a>,<img>) 450 text : [ str | glue.markup.page ] 451 text to print below summary plot (<p>), or glue.markup.page to 452 drop in with no enclosing tags. 453 subplotlist : list 454 list of filepath strings or (path, title) tuples to include 455 (<a>,<img>) 456 info : str 457 information to print below summary plot (<p>), or 458 glue.markup.page to drop in with no enclosing tags. 459 classdict : dict 460 dict containing HTML class strings for each tag used 461 init : [ True | False ] 462 initialise the markup.page object, adds HTML and BODY tags 463 """ 464 465 # generate object 466 page = markup.page() 467 468 # initialise 469 if init: page.init() 470 471 # print header 472 if header is not None: 473 getattr(page, htag)(header, class_=classdict.get(htag,""),\ 474 id_="%s_simple-page" % htag, 475 onclick="toggleVisible();") 476 477 # set div 478 hclass = classdict.get(htag, "open") 479 if hclass == "closed": display="none" 480 else: display="block" 481 page.div(class_=classdict.get("div"), id_="div_simple-page",\ 482 style="display: %s;" % display) 483 484 # print description 485 if desc is not None: 486 page.p(desc, class_=classdict.get("p","")) 487 488 # print first plot 489 if plotlist is not None: 490 Np = len(plotlist) 491 if Np: 492 p = plotlist[0] 493 if isinstance(p, str): 494 alt = None 495 else: 496 p,alt = p 497 page.a(href=p, title=alt, class_=classdict.get("a","")) 498 page.img(src=p, alt=alt, class_=classdict.get("img","")) 499 page.a.close() 500 501 # print text 502 if text is not None: 503 if isinstance(markup.page): 504 page.add(text()) 505 else: 506 page.p(str(text), class_=classdict.get("p","")) 507 508 # add rest of plots 509 if plotlist is not None and Np > 1: 510 for p in plotlist[1:]: 511 if isinstance(p, str): 512 alt = None 513 else: 514 p,alt = p 515 page.a(href=p, title=alt, class_=classdict.get("a","")) 516 page.img(src=p, alt=alt, class_=classdict.get("img","")) 517 page.a.close() 518 519 if info is not None: 520 if isinstance(markup.page): 521 page.add(info()) 522 else: 523 page.p(str(info), class_=classdict.get("p","")) 524 525 page.div.close()
526 527 # ============================================================================= 528 # Write about page 529 # ============================================================================= 530
531 -def about_page(executable, cmdargs, version=False, filedict={},\ 532 classdict={"h2":"open", "p":"line", "div":"about"}, init=False):
533 """ 534 Returns a glue.markup.page object formatting the given executable,\ 535 commandline arguments, and any included files. 536 537 Arguments: 538 539 executable : string 540 path of executable file (sys.argv[0]) 541 cmdargs : iterable 542 set of command line arguments (sys.argv[1:]) 543 544 Keyword arguments: 545 546 filedict : [ dict | iterable ] 547 iterable of ("name", filepath) pairs to insert in full into the page 548 classdict : dict 549 dict containing HTML class strings for each tag used 550 init : [ True | False ] 551 initialise the markup.page object, adds HTML and BODY tags 552 """ 553 page = markup.page() 554 555 # initialise 556 if init: page.init() 557 558 page.h1("About", class_=classdict.get("h1",None)) 559 page.p("This page was generated with %s using the following tools."\ 560 % (executable), class_=classdict.get("p",None)) 561 562 def pre(h2, content, id_=0): 563 page.h2(h2, id_="h2_%s" % id_, onclick="toggleVisible();",\ 564 class_=classdict.get("h2",None)) 565 page.div(id_="div_%s" % id_, style="display: block;",\ 566 class_=classdict.get("div",None)) 567 page.pre(content, class_=classdict.get("pre",None)) 568 page.div.close()
569 570 i = 0 571 572 # write command line 573 pre("Command line arguments", " ".join([executable]+cmdargs), id_=i) 574 i += 1 575 576 # write version 577 if version: 578 pre("Version", version, id_=i) 579 i += 1 580 581 if isinstance(filedict, dict): 582 filedict = filedict.iteritems() 583 584 for name,path in filedict: 585 pre(name, open(path, "r").read(), id_=i) 586 i += 1 587 588 return page 589 590 # ============================================================================= 591 # Find user's public directory 592 # ============================================================================= 593
594 -def get_public_html():
595 """Find the web readable directory for the user on thie host. 596 597 @returns the absolute path of the "public_html" equivalent 598 directory on this host. 599 600 Example: 601 >>> socket.getfqdn() 602 ldas-pcdev1.ligo-la.caltech.edu 603 >>> htmlutils.get_html_directory() 604 "/home/duncan.macleod/public_html" 605 """ 606 # get fully qualified domain name (FQDN) 607 fqdn = socket.getfqdn() 608 609 if sys.platform == "darwin": 610 public_html = "Sites" 611 elif re.search("atlas", fqdn): 612 public_html = "WWW/LSC" 613 else: 614 public_html = "public_html" 615 616 # verify public_html 617 home = os.path.expanduser("~") 618 userdir = os.path.join(home, public_html) 619 if not os.path.isdir(userdir): 620 raise OSError("Web UserDir %s not found." % userdir) 621 622 return userdir
623