Package pylal :: Package dq :: Module summary
[hide private]
[frames] | no frames]

Source Code for Module pylal.dq.summary

   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 2 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  """This module defines the classes used to generate interferometer 
  20  summary screens from data. 
  21   
  22  """ 
  23   
  24  # ============================================================================= 
  25  # Preamble 
  26  # ============================================================================= 
  27   
  28  from __future__ import division 
  29   
  30  import re 
  31  import calendar 
  32  import datetime 
  33  import os 
  34  import sys 
  35  import numpy 
  36  import copy 
  37  import StringIO 
  38  import lal 
  39  import warnings 
  40   
  41  from dateutil.relativedelta import relativedelta 
  42  from matplotlib import use 
  43   
  44  # set display 
  45  use("Agg") 
  46   
  47  from pylal import git_version 
  48  from pylal import htmlutils 
  49  from pylal import plotdata 
  50  from pylal import plotsegments 
  51  from pylal import plottriggers 
  52  from pylal import seriesutils 
  53  from pylal.dq import dqTriggerUtils 
  54  from pylal.plotutils import display_name as latex 
  55   
  56  from glue import markup 
  57  from glue import segments 
  58  from glue import segmentsUtils 
  59  from glue.ligolw import table, lsctables 
  60   
  61  LIGOTimeGPS = float 
  62   
  63  # set metadata 
  64  __author__  = "Duncan M. Macleod <duncan.macleod@ligo.org>" 
  65  __version__ = git_version.id 
  66  __date__    = git_version.date 
  67   
  68  # set regex 
  69  _r_cchar  = re.compile("[\W_]+") 
  70  rindex = re.compile("index.html\Z") 
  71   
  72  # set mode enum 
  73  SUMMARY_MODE_GPS   = 0 
  74  SUMMARY_MODE_DAY   = 1 
  75  SUMMARY_MODE_WEEK  = 2 
  76  SUMMARY_MODE_MONTH = 3 
  77  SUMMARY_MODE_YEAR  = 5 
  78  MODE_NAME = {SUMMARY_MODE_GPS: "GPS",\ 
  79               SUMMARY_MODE_DAY: "DAY",\ 
  80               SUMMARY_MODE_WEEK: "WEEK",\ 
  81               SUMMARY_MODE_MONTH: "MONTH",\ 
  82               SUMMARY_MODE_YEAR: "YEAR"} 
  83   
  84  # set HTML globals 
  85  TOGGLE = "toggleVisible();" 
86 87 # ============================================================================= 88 # Classes for tabs 89 # ============================================================================= 90 91 -class SummaryTab(object):
92 """ 93 The default meta-class for the summary tabs. It provides the basics so 94 developers should subclass SummaryTab to generate something useful. 95 """ 96 mode = SUMMARY_MODE_GPS 97 ifo = None
98 - def __init__(self, name, **kwargs):
99 """ 100 Initialise a SummaryTab object. At least a name must be given. 101 102 @param name: a name string for the tab 103 @type name: C{str} 104 """ 105 # set name 106 self.name = name 107 108 # set defaults 109 kwargs.setdefault("plots", list()) 110 kwargs.setdefault("subplots", list()) 111 kwargs.setdefault("children", list()) 112 kwargs.setdefault("parent", None) 113 kwargs.setdefault("state", None) 114 kwargs.setdefault("information", None) 115 kwargs.setdefault("skip_summary", False) 116 117 # set all other values 118 for key,val in kwargs.iteritems(): 119 setattr(self, key, val)
120 121 # ================ 122 # basic properties 123 124 @property
125 - def name(self):
126 """Descriptive string for this Tab.""" 127 if not hasattr(self, "_name"): 128 self.name = "" 129 return self._name
130 @name.setter
131 - def name(self, value):
132 self._name = str(value)
133 @name.deleter
134 - def name(self):
135 del self._name
136 137 @property
138 - def directory(self):
139 """path to the directory for this Tab.""" 140 if not hasattr(self, "_directory"): 141 n = _r_cchar.sub("_", self.name.lower()) 142 if hasattr(self, "parent") and self.parent is not None: 143 self.directory = os.path.join(self.parent.directory, n) 144 else: 145 self.directory = os.path.join(os.path.curdir, n) 146 return self._directory
147 @directory.setter
148 - def directory(self, d):
149 self._directory = os.path.normpath(d)
150 @directory.deleter
151 - def directory(self):
152 del self._directory
153 154 @property
155 - def start_time(self):
156 """GPS start time for this Tab.""" 157 return self._start_time
158 @start_time.setter
159 - def start_time(self, gps):
160 self._start_time = LIGOTimeGPS(gps)
161 @start_time.deleter
162 - def start_time(self):
163 del self._start_time
164 165 @property
166 - def end_time(self):
167 """GPS end time for this Tab.""" 168 return self._end_time
169 @end_time.setter
170 - def end_time(self, gps):
171 self._end_time = LIGOTimeGPS(gps)
172 @end_time.deleter
173 - def end_time(self):
174 del self._end_time
175 176 @property
177 - def span(self):
178 """GPS [start_time, stop_time) segment for this Tab.""" 179 return segments.segment(self.start_time, self.end_time)
180 @span.setter
181 - def span(self, seg):
182 self.start_time = LIGOTimeGPS(seg[0]) 183 self.end_time = LIGOTimeGPS(seg[1])
184 @span.deleter
185 - def span(self):
186 del self._span
187 188 @property
189 - def segments(self):
190 """glue.segments.segmentlist describing the analysis segments for 191 this Tab. 192 """ 193 return self._segments
194 @segments.setter
195 - def segments(self, seglist):
196 self._segments =\ 197 segments.segmentlist([segments.segment(map(float, s))\ 198 for s in seglist])
199 @segments.deleter
200 - def segments(self):
201 del self._segments
202 203 # =============== 204 # plot properties 205 206 @property
207 - def plots(self):
208 """list of plots for this Tab.""" 209 return self._plotlist
210 @plots.setter
211 - def plots(self, plotlist):
212 self._plotlist = list(map(os.path.normpath, plotlist))
213 @plots.deleter
214 - def plots(self):
215 del self._plotlist
216 217 @property
218 - def subplots(self):
219 """list of subplots for this Tab.""" 220 return self._subplotlist
221 @subplots.setter
222 - def subplots(self, subplotlist):
223 self._subplotlist = list(map(os.path.normpath, subplotlist)) 224 return self._subplotlist
225 @subplots.deleter
226 - def subplots(self):
227 del self._subplotlist
228 229 # =============== 230 # HTML properties 231 232 @property
233 - def href(self):
234 """path str to the frame for this Tab.""" 235 if not hasattr(self, "_href"): 236 basename = "%s.html" % _r_cchar.sub("_", self.state.name).lower() 237 self.href = os.path.join(self.directory, basename) 238 return self._href
239 @href.setter
240 - def href(self, url):
241 self._href = os.path.normpath(url)
242 @href.deleter
243 - def href(self):
244 del self._href
245 246 @property
247 - def index(self):
248 """URL to the index for this Tab.""" 249 return os.path.join(os.path.normpath(self.directory), "")
250 251 # 252 # class methods 253 # 254
255 - def add_child(self, tab):
256 self.children.append(tab) 257 self.plots.append(tab.plots[0])
258
259 - def get_child(self, name):
260 names = [c.name for c in self.children] 261 try: 262 idx = names.index(name) 263 except ValueError,e: 264 raise RuntimeError("Parent tab has no child named \"%s\". Valid " 265 "children are: %s." % (name, ", ".join(names))) 266 else: 267 return self.children[idx]
268
269 - def write_menu(self, jobdir, startdate=None, weekday=calendar.MONDAY):
270 """ 271 Write the HTML menu for this tab. 272 273 @param jobdir: output directory of this job 274 @type jobdir: C{str} 275 @param startdate: date at which to start the menubar calendar 276 @type startdate: C{datetime.datetime} 277 @param weekday: day on which to start week in calendar (0=monday) 278 @type weekday: C{int} 279 """ 280 self.menu = markup.page() 281 self.menu.div(id_="menubar") 282 283 # work out which sections we want 284 if self.mode == SUMMARY_MODE_DAY: 285 sections = ["Today", "Previous day", "Next day", "Calendar"] 286 marker = "today" 287 strf = "%Y%m%d" 288 delta = datetime.timedelta(days=1) 289 elif self.mode == SUMMARY_MODE_WEEK: 290 sections = ["This week", "Previous week", "Next week",\ 291 "Calendar"] 292 marker = "thisweek" 293 strf = "%Y%m%d" 294 delta = datetime.timedelta(days=7) 295 elif self.mode == SUMMARY_MODE_MONTH: 296 sections = ["This month", "Previous month", "Next month",\ 297 "Calendar"] 298 marker = "thismonth" 299 strf = "%Y%m" 300 delta = relativedelta(months=1) 301 elif self.mode == SUMMARY_MODE_YEAR: 302 sections = ["This year", "Previous year", "Next year",\ 303 "Calendar"] 304 marker = "thisyear" 305 strf = "%Y" 306 delta = relativedelta(years=1) 307 else: 308 sections = ["Full data"] 309 sections.extend(["About", "Glossary"]) 310 311 # link different run states 312 if self.name == "Online" and len(self.states): 313 self.menu.div(class_="statetoggle") 314 self.menu.ul(id_="id_statetoggle") 315 for i,s in enumerate(self.states): 316 s2 = _r_cchar.sub("-", s.name.lower()) 317 setcls = """$("#li_%s").attr("class","%s");$("#div_%s").%s()""" 318 onclick = [] 319 for j,tag in enumerate(self.states): 320 tag = _r_cchar.sub("-", tag.name.lower()) 321 class_ = j==i and "open" or "closed" 322 show = j==i and "show" or "hide" 323 onclick.append(setcls % (tag, class_, tag, show)) 324 class_ = i==0 and "open" or "closed" 325 id_ = "li_%s" % s2 326 href = "#%s" % s2 327 self.menu.li(s.name, class_=class_, id_=id_, href=href,\ 328 onclick=";".join(onclick)) 329 self.menu.ul.close() 330 self.menu.div.close() 331 else: 332 run_states = [child.state for child in self.children\ 333 if child.state is not None] 334 if len(run_states): 335 self.menu.div(class_="statetoggle") 336 self.menu.ul(id_="statetoggle") 337 for i,state in enumerate(run_states): 338 title = "%s time" % state.name.title() 339 class_ = i==0 and "open" or "closed" 340 id_ = "li_%s" % _r_cchar.sub("-", state.name.lower()) 341 onclick = "$(this).loadrunstate(\"%s\");"\ 342 % self.children[i].href 343 self.menu.li(title, class_=class_, id_=id_,\ 344 onclick=onclick) 345 self.menu.ul.close() 346 self.menu.div.close() 347 348 # write section links 349 if self.mode != SUMMARY_MODE_GPS: 350 cday = datetime.datetime(*lal.GPSToUTC(int(self.start_time))[:6]) 351 cstrf = cday.strftime(strf) 352 for i,sec in enumerate(sections): 353 if re.match("(Today|This )", sec): 354 if re.search(cstrf, self.index): 355 link =\ 356 re.sub("%s%s%s" % (os.path.sep, cstrf, os.path.sep),\ 357 "%s%s%s" % (os.path.sep, marker, os.path.sep),\ 358 self.index) 359 else: 360 link = os.path.join(jobdir, os.pardir, marker, "") 361 if link.endswith("about%s" % os.path.sep): 362 link = link[:-6] 363 elif sec.startswith("Previous "): 364 previous = (cday-delta).strftime(strf) 365 if re.search(cstrf, self.index): 366 link = self.index 367 else: 368 link = jobdir 369 link = link.replace(cstrf, previous) 370 elif sec.startswith("Next "): 371 next_ = (cday+delta).strftime(strf) 372 if re.search(cstrf, self.index): 373 link = self.index 374 else: 375 link = jobdir 376 link = link.replace(cstrf, next_) 377 elif sec == "About" and self.mode != SUMMARY_MODE_GPS: 378 link = os.path.join(jobdir, sec.lower()) 379 else: 380 link = sec.lower() 381 link = "%s%s"\ 382 % (os.path.normpath(rindex.sub("", link)), os.path.sep) 383 self.menu.a(sec, id_="link_%d" % i, class_="menulink", href=link) 384 385 # write menucalendar 386 if self.mode == SUMMARY_MODE_GPS or self.name == "Calendar": 387 self.menu.div.close() 388 else: 389 now = int(lal.GPSTimeNow()) 390 enddate = datetime.datetime(*lal.GPSToUTC(now)[:6]) 391 if self.name in ["Summary", "Glossary", "Calendar"]: 392 menucal = calendar_page(startdate, enddate, weekday=weekday,\ 393 path=None, ncol=1, reverse=True) 394 menupath = os.path.join("html", "menucal.html") 395 else: 396 menucal = calendar_page(startdate, enddate, weekday=weekday,\ 397 path=self.directory, ncol=1,\ 398 reverse=True) 399 menupath = os.path.join("html", "%s_menucal.html"\ 400 % _r_cchar.sub("_", re.split("\d\d\d\d%s"\ 401 % os.path.sep,\ 402 self.directory)[-1])) 403 with open(menupath, "w") as menuf: 404 menuf.write(re.sub(" class=\"\"", "", str(menucal))) 405 self.menu.div("",id_="menucalendar") 406 self.menu.script("$(\"div#menucalendar\").load(\"%s\");"\ 407 % menupath, type="text/javascript") 408 self.menu.div.close()
409 435
436 - def frametohtml(self):
437 """ 438 Write this Tab's frame to file as HTML. This allows it to be jquery 439 loaded into a parent page. 440 """ 441 with open(self.href, "w") as framef: 442 framef.write(re.sub(" class=\"\"", "", str(self.frame)))
443
444 - def build(self, **initargs):
445 """ 446 Write this Tab's full HTML to the index file. 447 448 @keyword **initargs: keyword arguments to pass to markup.page.init. 449 """ 450 # default banner 451 if isinstance(self, OnlineSummaryTab): 452 banner = markup.oneliner.h1("Online data") 453 else: 454 date = datetime.date(*lal.GPSToUTC(int(self.start_time))[:3]) 455 if self.mode == SUMMARY_MODE_DAY: 456 banner = "%s: %d-%d" % (date.strftime("%B %d %Y"),\ 457 self.start_time, self.end_time) 458 elif self.mode == SUMMARY_MODE_WEEK: 459 banner = "Week of %s: %d-%d" % (date.strftime("%B %d %Y"),\ 460 self.start_time, self.end_time) 461 elif self.mode == SUMMARY_MODE_MONTH: 462 banner = "%s: %d-%d" % (date.strftime("%B %Y"),\ 463 self.start_time, self.end_time) 464 elif self.mode == SUMMARY_MODE_YEAR: 465 banner = "%s: %d-%d" % (date.strftime("%Y"),\ 466 self.start_time, self.end_time) 467 else: 468 banner = "%d-%d" % (self.start_time, self.end_time) 469 banner = markup.oneliner.h1(banner) 470 471 # default online button 472 if self.mode != SUMMARY_MODE_GPS: 473 homebutton = markup.page() 474 homebutton.ul(class_="buttons") 475 homebutton.li(id_="online") 476 homebutton.a("Online", href="") 477 homebutton.li.close() 478 homebutton.ul.close() 479 else: 480 homebutton = False 481 482 # set init defaults 483 initargs.setdefault("title", "Summary: %s" % self.name) 484 485 page = htmlutils.build_page(icon=self.ifobar, banner=banner,\ 486 homebutton=homebutton, tabs=self.tabs,\ 487 menu=self.menu, frame=self.frame,\ 488 **initargs) 489 with open(os.path.join(self.index, "index.html"), "w") as indexf: 490 indexf.write(re.sub(" class=\"\"", "", str(page)))
491
492 - def add_information(self, cp, cpsec):
493 """ 494 Add an "Information" section to this SummaryTab. 495 496 @param cp: INI configuration for this job. 497 @type cp: C{configparser.configparser} 498 @param cpsec: INI-file section name from which to read the 499 'html-information' option. 500 @type cpsec: C{str} 501 """ 502 if cp.has_option(cpsec, "html-information"): 503 self.information = cp.get(cpsec, "html-information") 504 else: 505 warnings.warn("No HTML information found for %s. " 506 "Please add this." % self.name)
507
508 509 -class SectionSummaryTab(SummaryTab):
510 """ 511 SummaryTab representing an overview Tab for a set of child Tabs. 512 """
513 - def write_tabs(self, sectiontabs):
514 """ 515 Write the tabbar for this Tab. 516 517 @param sectiontabs: list of SummaryTab objects to include in tabbar 518 @type sectiontabs: C{list} 519 """ 520 self.tabs = markup.page() 521 522 # sort with "Summary" at the top, and "Misc." at the bottom 523 sectiontabs.sort(key=lambda t: t.name == "Summary" and 1 or\ 524 "Misc." and 3 or 2) 525 526 # build ordered list of all tabs 527 alltabs = [] 528 for tab in sectiontabs: 529 tab.children.sort(key=lambda t: 530 re.search('odc(.*)(overview|summary)', 531 t.name, re.I) and 3 or 532 re.search('odc', t.name, re.I) and 4 or 533 re.search('(overview|summary)', t.name, re.I) 534 and 2 or 1) 535 alltabs.append(tab) 536 if tab.name != "Summary": 537 alltabs.extend(tab.children) 538 allnames = [t.name for t in alltabs] 539 if self.name in allnames: 540 current = allnames.index(self.name) 541 else: 542 current = None 543 544 # draw left, right arrows 545 self.tabs.div(class_="right") 546 self.tabs.ul(class_="tabs", id_="arrows") 547 if current is not None: 548 self.tabs.li(title="Previous", class_="arrow") 549 href = rindex.sub("", alltabs[(current-1)%len(alltabs)].index) 550 self.tabs.a("&#x2190",href=href) 551 self.tabs.li.close() 552 self.tabs.li(title="Next", class_="arrow") 553 href = rindex.sub("", alltabs[(current+1)%len(alltabs)].index) 554 self.tabs.a("&#x2192",href=href) 555 self.tabs.li.close() 556 self.tabs.ul.close() 557 self.tabs.div.close() 558 559 # open tablist 560 self.tabs.ul(class_="tabs") 561 562 # write each SectionSummaryTab 563 subtabs = None 564 for i,tab in enumerate(sectiontabs): 565 if self.name == tab.name\ 566 or (self.parent and self.parent.name == tab.name): 567 class_ = "open" 568 subtabs = tab.children 569 parent = tab 570 else: 571 class_ = "closed" 572 self.tabs.li(id_=_r_cchar.sub("_", tab.name.lower()),\ 573 class_=class_, title=self.name) 574 self.tabs.a(tab.name, href=rindex.sub("", tab.index)) 575 if class_=="closed" and tab.name == "Summary": 576 self.tabs.ul("",class_="dropdown") 577 elif class_=="closed": 578 self.tabs.ul(class_="dropdown") 579 for subtab in tab.children: 580 self.tabs.li(id_=_r_cchar.sub("_", subtab.name.lower()),\ 581 class_="closed", title="Open %s" % subtab.name) 582 self.tabs.a(subtab.name, href=rindex.sub("", subtab.index)) 583 self.tabs.li.close() 584 self.tabs.ul.close() 585 self.tabs.li.close() 586 self.tabs.ul.close() 587 588 self.tabs.div(class_="clear") 589 self.tabs.div.close() 590 591 if self.name != "Summary" and subtabs: 592 self.tabs.ul(class_="subtabs",\ 593 id_=_r_cchar.sub("_", parent.name.lower())) 594 for tab in subtabs: 595 if self.name == tab.name: 596 class_="open" 597 else: 598 class_="closed" 599 self.tabs.li(id_=_r_cchar.sub("_", tab.name.lower()),\ 600 class_=class_, title="Open %s" % tab.name) 601 self.tabs.a(tab.name, href=rindex.sub("", tab.index)) 602 self.tabs.li.close() 603 else: 604 self.tabs.ul(class_="subtabs") 605 self.tabs.ul.close() 606 607 # clear divs 608 self.tabs.div("", class_="clear")
609
610 - def finalize(self):
611 """ 612 Write the HTML frame for this SectionSummary. 613 """ 614 self.frame = markup.page() 615 div(self.frame, 0, self.name) 616 if self.name == "Summary": 617 order = ["Sensitivity", "Triggers", "Segments"] 618 self.children.sort(key=lambda x: x.parent.name in order\ 619 and order.index(x.parent.name)+1 or 1000) 620 children = [] 621 for tab in self.children: 622 children.append(tab) 623 else: 624 children = self.children 625 n = len(children) > 1 and 2 or 1 626 627 self.frame.table(style="table-layout: fixed; width: 100%;") 628 for i,tab in enumerate(children): 629 if self.name == "Summary" and tab.skip_summary: 630 continue 631 if i % n == 0: 632 self.frame.tr() 633 self.frame.td() 634 if (self.name == "Summary"): 635 parent = tab.parent 636 self.frame.a(markup.oneliner.h2(parent.name, class_='summary'), 637 href=parent.index, title=parent.name) 638 self.frame.a(href=parent.index, title=parent.name) 639 else: 640 self.frame.a(href=tab.index, title=tab.name) 641 self.frame.h2(tab.name, class_="summary") 642 self.frame.img(src=tab.plots[0][0], alt=tab.name, class_="full") 643 self.frame.a.close() 644 self.frame.td.close() 645 if i % n == (n-1): 646 self.frame.tr.close() 647 self.frame.table.close() 648 self.frame.div.close()
649
650 - def frametohtml(self):
651 raise AttributeError("The frametohtml function should not be used"+\ 652 " for SegmentSummaryTabs.")
653
654 655 -class MetaStateTab(SectionSummaryTab):
656 """ 657 This meta-class should stand between a SectionSummaryTab and each of it's 658 children, allowing to hold the information for running the same tab in 659 a variety of different run states. 660 """
661 - def finalize(self):
662 """ 663 Write the HTML page for this MetaStateTab. 664 """ 665 self.frame = markup.page() 666 state = self.children[0].state 667 html = self.children[0].href 668 self.frame.div.close() 669 self.frame.script("$(\"#li_%s\").loadrunstate(\"%s\");"\ 670 % (_r_cchar.sub("-", state.name).lower(), html),\ 671 type_="text/javascript") 672 self.frame.div()
673
674 675 -class SegmentSummaryTab(SummaryTab):
676 """ 677 Object representing a summary of segments. 678 """
679 - def __init__(self, *args, **kwargs):
680 SummaryTab.__init__(self, *args, **kwargs) 681 self.flags = list() 682 self.segdict = segments.segmentlistdict() 683 self.segment_files = dict()
684 685 # ========== 686 # properties 687 688 @property
689 - def segdict(self):
690 """glue.segments.segmentlistdict containing glue.segments.segmentlists 691 for each flag used in this SegmentSummaryTab.""" 692 return self._segdict
693 @segdict.setter
694 - def segdict(self, segdict):
695 # set type 696 for key in segdict.keys(): 697 segdict[key] = type(segdict[key])([map(float) for s in\ 698 segdict[key]]) 699 self._segdict = segments.segmentlistdict(segdict)
700 @segdict.deleter
701 - def segdict(self):
702 del self._segdict
703 704 # 705 # class methods 706 # 707
708 - def add_segments(self, flag, seglist):
709 """ 710 Add a glue.segments.segmentlist for a given flag to this object 711 712 @param flag: name of flag to record 713 @type flag: C{str} 714 @param seglist: list of segments to record 715 @type seglist: C{glue.segments.segmentlist} 716 717 """ 718 if self.segdict.has_key(flag): 719 self.segdict[flag] += segments.segmentlist(seglist) 720 else: 721 self.flags.append(flag) 722 self.segdict[flag] = segments.segmentlist(seglist)
723
724 - def tosegwizard(self, flag, filename=None):
725 """ 726 Write the segments know for the given flag to a segwizard format file. 727 If an output filename is not given, one is generated. 728 729 @param flag: name of flag whose segments are to be written 730 @type flag: C{str} 731 @param filename: path string of file to be written 732 @type filename: C{str} 733 """ 734 if not filename: 735 name = _r_cchar.sub("-", _r_cchar.sub("_", flag.upper()), 1) 736 filename = os.path.join(self.directory, "%s-%d-%d.txt"\ 737 % (name, self.start_time, abs(self.span))) 738 with open(filename, "w") as segf: 739 segmentsUtils.tosegwizard(segf, self.segdict[flag]) 740 self.segment_files[flag] = filename
741
742 - def plotsegments(self, outfile, subplot=False, **kwargs):
743 """ 744 Plot the segments associated with this SegmentSummaryTab. 745 746 @param outfile: filename for plot 747 @type outfile: C{str} 748 @param subplot: record plot as a subplot, default: False 749 @type subplot: C{bool} 750 @keyword **kwargs: other arguments to pass to 751 plotsegments.plotsegmentlistdict 752 """ 753 if not subplot: 754 kwargs.setdefault("xlim", [self.start_time, self.end_time]) 755 kwargs.setdefault("keys", list(self.flags)) 756 if re.search("segments\Z", self.name, re.I): 757 kwargs.setdefault("title", self.name) 758 else: 759 kwargs.setdefault("title", "%s segments" % self.name) 760 kwargs.setdefault("subtitle", "%d-%d" % (self.start_time,self.end_time)) 761 desc = kwargs.pop("description", None) 762 plotsegments.plotsegmentlistdict(self.segdict, outfile, **kwargs) 763 if subplot: 764 self.subplots.append((outfile, desc)) 765 else: 766 self.plots.append((outfile, desc))
767
768 - def plotduration(self, outfile, flag, subplot=False, **kwargs):
769 """ 770 Plot the segment durations for the given flag. 771 772 @param outfile: filename for plot 773 @type outfile: C{str} 774 @param flag: name of flag whose segments are to be plotted 775 @type flag: C{str} 776 @param subplot: record plot as a subplot, default: False 777 @type subplot: C{bool} 778 @keyword **kwargs: other arguments to pass to 779 plotsegments.plotsegmentlistdict 780 """ 781 desc = kwargs.pop("description", None) 782 if not subplot: 783 kwargs.setdefault("xlim", [self.start_time, self.end_time]) 784 kwargs.setdefault("flag", flag) 785 if re.search("segments\Z", self.name, re.I): 786 kwargs.setdefault("title", self.name) 787 else: 788 kwargs.setdefault("title", "%s segments" % self.name) 789 plotsegments.plotduration(self.segdict[flag], outfile, **kwargs) 790 if subplot: 791 self.subplots.append((outfile, desc)) 792 else: 793 self.plots.append((outfile, desc))
794
795 - def plothistogram(self, outfile, subplot=False, **kwargs):
796 """ 797 Plot a histogram of the segments associated with this SegmentSummaryTab. 798 799 @param outfile: filename for plot 800 @type outfile: C{str} 801 @param subplot: record plot as a subplot, default: False 802 @type subplot: C{bool} 803 @keyword **kwargs: other arguments to pass to 804 plotsegments.plotsegmentlistdict 805 """ 806 desc = kwargs.pop("description", None) 807 kwargs.setdefault("keys", list(self.flags)) 808 if re.search("segments\Z", self.name, re.I): 809 kwargs.setdefault("title", self.name) 810 else: 811 kwargs.setdefault("title", "%s segments" % self.name) 812 plotsegments.plothistogram(self.segdict, outfile, **kwargs) 813 if subplot: 814 self.subplots.append((outfile, desc)) 815 else: 816 self.plots.append((outfile, desc))
817
818 - def plotdutycycle(self, outfile, subplot=False, **kwargs):
819 """ 820 Plot the duty cycle of segments associated with this SegmentSummaryTab. 821 822 @param outfile: filename for plot 823 @type outfile: C{str} 824 @param subplot: record plot as a subplot, default: False 825 @type subplot: C{bool} 826 @keyword **kwargs: other arguments to pass to 827 plotsegments.plotsegmentlistdict 828 """ 829 desc = kwargs.pop("description", None) 830 kwargs.setdefault("keys", list(self.flags)) 831 if not subplot: 832 kwargs.setdefault("xlim", [self.start_time, self.end_time]) 833 if re.search("segments\Z", self.name, re.I): 834 kwargs.setdefault("title", self.name) 835 else: 836 kwargs.setdefault("title", "%s segments" % self.name) 837 if self.mode == SUMMARY_MODE_DAY: 838 kwargs.setdefault("binlength", 3600) 839 elif self.mode == SUMMARY_MODE_WEEK or self.mode == SUMMARY_MODE_MONTH: 840 kwargs.setdefault("binlength", 86400) 841 elif self.mode == SUMMARY_MODE_YEAR: 842 kwargs.setdefault("binlength", 365/12*86400) 843 plotsegments.plotdutycycle(self.segdict, outfile, **kwargs) 844 if subplot: 845 self.subplots.append((outfile, desc)) 846 else: 847 self.plots.append((outfile, desc))
848
849 - def finalize(self):
850 """ 851 Generate a markup.page object representing the HTML summary of the 852 segments associated with this SegmentSummaryTab. 853 """ 854 # opening 855 self.frame = markup.page() 856 div(self.frame, 0, self.name) 857 self.frame.p("This page summarises %s segments." % self.name,\ 858 class_="line") 859 860 # summary 861 div(self.frame, (0, 1), "Summary") 862 self.frame.a(href=self.plots[0][0], title=self.plots[0][1],\ 863 rel="full", class_="fancybox-button") 864 self.frame.img(src=self.plots[0][0], alt=self.plots[0][1],class_="full") 865 self.frame.a.close() 866 867 # construct livetime table, KLUDGE, the "now" time should be 868 # provided by when the calling script started 869 uptime = abs(self.span & segments.segment(self.span[0],float(lal.GPSTimeNow()))) 870 headers = ["Flags", "Livetime (s)", "Duty cycle (\%)", "Relative duty cycle (\%)"] 871 data = [] 872 for i,flag in enumerate(self.flags): 873 if i>0: 874 previousuptime = float(abs(self.segdict[self.flags[i-1]])) 875 if previousuptime == 0: 876 previousuptime = numpy.inf 877 data.append([flag, float(abs(self.segdict[flag])),\ 878 100*float(abs(self.segdict[flag]))/float(uptime),\ 879 100*float(abs(self.segdict[flag]))/previousuptime]) 880 else: 881 data.append([flag, float(abs(self.segdict[flag])),\ 882 100*float(abs(self.segdict[flag]))/float(uptime),\ 883 100*float(abs(self.segdict[flag]))/float(uptime)]) 884 self.frame.add(htmlutils.write_table(headers, data, {"table":"full"})()) 885 self.frame.div.close() 886 887 # other plots 888 div(self.frame, (0, 2), "Plots") 889 pclass = len(self.plots[1:]) == 1 and "full" or "half" 890 for plot,desc in self.plots[1:]: 891 self.frame.a(href=plot, title=desc, class_="fancybox-button",\ 892 rel="full") 893 self.frame.img(src=plot, alt=desc, class_=pclass) 894 self.frame.a.close() 895 self.frame.div.close() 896 897 # segment lists 898 div(self.frame, (0, 3), "Segment lists") 899 for i,flag in enumerate(self.flags): 900 div(self.frame, (0, 3, i), flag, display=False) 901 segfile = self.segment_files.get(flag, None) 902 if segfile is not None: 903 self.frame.p("The full segment list can be downloaded from %s."\ 904 % markup.oneliner.a("this file", href=segfile),\ 905 class_="line") 906 segwizard = StringIO.StringIO() 907 segmentsUtils.tosegwizard(segwizard, self.segdict[flag]) 908 self.frame.pre(segwizard.getvalue()) 909 segwizard.close() 910 self.frame.div.close() 911 self.frame.div.close() 912 913 # subplots 914 if len(self.subplots): 915 div(self.frame, (0, 4), "Subplots", display=True) 916 for plot,desc in self.subplots: 917 self.frame.a(href=plot, title=desc, class_="fancybox-button",\ 918 rel="subplots") 919 self.frame.img(src=plot, alt=desc, class_="quarter") 920 self.frame.a.close() 921 self.frame.div.close() 922 923 # information 924 if self.information: 925 div(self.frame, (0, 5), "Information", display=True) 926 self.frame.add(self.information) 927 self.frame.div.close() 928 929 self.frame.div.close() 930 931 # dereference 932 for attr in ["segments", "segdict"]: 933 if hasattr(self, attr): 934 try: 935 delattr(self, attr) 936 except AttributeError: 937 raise
938
939 940 -class DataSummaryTab(SummaryTab):
941 """ 942 SummaryTab representing the summary of data extracted from frames. 943 """
944 - def __init__(self, *args, **kwargs):
945 SummaryTab.__init__(self, *args, **kwargs) 946 self.timeseries = dict() 947 self.sampling = dict() 948 self.spectrum = dict() 949 self.minspectrum = dict() 950 self.maxspectrum = dict() 951 self.designspectrum = dict() 952 self.referencespectrum = dict() 953 self.spectrogram = dict() 954 self.channels = [] 955 self.framefiles = dict()
956 957 # 958 # class methods 959 # 960
961 - def add_timeseries(self, channel, series):
962 """ 963 Add a glue.ligolw.table for a given channel to this object 964 965 @param channel: name of channel to store 966 @type channel: C{str} 967 @param series: TimeSeries data object to store 968 @type series: C{lal.XXXXTimeSeries} 969 """ 970 self.timeseries[channel] = series 971 self.sampling[channel] = 1/series.deltaT 972 if channel not in self.channels: 973 self.channels.append(channel)
974
975 - def add_spectrogram(self, channel, *sequencelist):
976 """ 977 Add a list of REAL8VectorSequence objects as the spectrogram for this 978 channel. 979 980 @param channel: name of channel to store 981 @type channel: C{str} 982 @param sequencelist: spectrogram data object to store, or list of 983 spectrograms 984 """ 985 if not self.spectrogram.has_key(channel): 986 self.spectrogram[channel] = [] 987 if hasattr(sequencelist, "__contains__"): 988 self.spectrogram[channel].extend(sequencelist) 989 else: 990 self.spectrogram[channel].append(sequencelist) 991 if channel not in self.channels: 992 self.channels.append(channel)
993
994 - def add_median_spectrum(self, channel, series):
995 """ 996 Add a FrequencySeries object as the median spectrum for this 997 channel. 998 999 @param channel: name of channel to store 1000 @type channel: C{str} 1001 @param series: FrequencySeries data object to store 1002 @type series: C{lal.XXXXFrequencySeries} 1003 """ 1004 self.spectrum[channel] = series 1005 if channel not in self.channels: 1006 self.channels.append(channel)
1007
1008 - def add_min_spectrum(self, channel, series):
1009 """ 1010 Add a FrequencySeries object as the minimum spectrum for this 1011 channel. 1012 1013 @param channel: name of channel to store 1014 @type channel: C{str} 1015 @param series: FrequencySeries data object to store 1016 @type series: C{lal.XXXXFrequencySeries} 1017 """ 1018 if not series.name.endswith("min"): 1019 series.name = "%s_min" % series.name 1020 self.minspectrum[channel] = series 1021 if channel not in self.channels: 1022 self.channels.append(channel)
1023
1024 - def add_max_spectrum(self, channel, series):
1025 """ 1026 Add a REAL8FrequencySeries object as the maximum spectrum for this 1027 channel. 1028 1029 @param channel: name of channel to store 1030 @type channel: C{str} 1031 @param series: FrequencySeries data object to store 1032 @type series: C{lal.XXXXFrequencySeries} 1033 """ 1034 if not series.name.endswith("max"): 1035 series.name = "%s_max" % series.name 1036 self.maxspectrum[channel] = series 1037 if channel not in self.channels: 1038 self.channels.append(channel)
1039
1040 - def add_design_spectrum(self, channel, series):
1041 """ 1042 Add a FrequencySeries object as the maximum spectrum for this 1043 channel. 1044 1045 @param channel: name of channel to store 1046 @type channel: C{str} 1047 @param series: FrequencySeries data object to store 1048 @type series: C{lal.XXXXFrequencySeries} 1049 """ 1050 self.designspectrum[channel] = series
1051
1052 - def add_reference_spectrum(self, channel, series):
1053 """ 1054 Add a FrequencySeries object as the maximum spectrum for this 1055 channel. 1056 1057 @param channel: name of channel to store 1058 @type channel: C{str} 1059 @param series: FrequencySeries data object to store 1060 @type series: C{lal.XXXXFrequencySeries} 1061 """ 1062 self.referencespectrum[channel] = series
1063
1064 - def plottimeseries(self, outfile, channels=None, subplot=False, **kwargs):
1065 """ 1066 Plot the timeseries for this TriggerSummary, one column against another. 1067 1068 @param outfile: filename for plot 1069 @type outfile: C{str} 1070 @param channels: list of channels to plot, default: all of them 1071 @type channels: C{list} 1072 @param subplot: record plot as a subplot, default: False 1073 @type subplot: C{bool} 1074 @keyword **kwargs: other arguments to pass to 1075 plotdata.plottimeseries 1076 """ 1077 if not channels: 1078 channels = self.channels 1079 desc = kwargs.pop("description", None) 1080 if not subplot: 1081 kwargs.setdefault("xlim", [self.start_time, self.end_time]) 1082 data = [seriesutils.duplicate(self.timeseries[channel])\ 1083 for channel in channels] 1084 for i,channel in enumerate(channels): 1085 data[i].name = channel 1086 try: 1087 plotdata.plottimeseries(data, outfile, **kwargs) 1088 except ValueError as err: 1089 warnings.warn("ValueError: %s" % err.message) 1090 if subplot: 1091 self.subplots.append((outfile, desc)) 1092 else: 1093 self.plots.append((outfile, desc))
1094
1095 - def plothistogram(self, outfile, channels=None, subplot=False, **kwargs):
1096 """ 1097 Plot the histogram of data contained with the timeseries' for this 1098 DataSummary. 1099 1100 @param outfile: filename for plot 1101 @type outfile: C{str} 1102 @param channels: list of channels to plot, default: all of them 1103 @type channels: C{list} 1104 @param subplot: record plot as a subplot, default: False 1105 @type subplot: C{bool} 1106 @keyword **kwargs: other arguments to pass to 1107 plotdata.plothistogram 1108 """ 1109 if not channels: 1110 channels = self.channels 1111 desc = kwargs.pop("description", None) 1112 data = [self.timeseries[channel] for channel in channels] 1113 for i,channel in enumerate(channels): 1114 data[i].name = channel 1115 if len(channels) == 1: 1116 data[0].name = "_" 1117 plotdata.plothistogram(data, outfile, **kwargs) 1118 if subplot: 1119 self.subplots.append((outfile, desc)) 1120 else: 1121 self.plots.append((outfile, desc))
1122
1123 - def plotspectrogram(self, outfile, channel=None, ratio=None, subplot=False,\ 1124 **kwargs):
1125 """ 1126 Plot the spectrogram of the given channel 1127 1128 @param outfile: filename for plot 1129 @type outfile: C{str} 1130 @param channels: list of channels to plot, default: all of them 1131 @type channels: C{list} 1132 @param ratio: description of ratio to calculate on-the-fly, 1133 e.g. "median", or "design" 1134 @type ratio: C{list} 1135 @param subplot: record plot as a subplot, default: False 1136 @type subplot: C{bool} 1137 @keyword **kwargs: other arguments to pass to 1138 plotdata.plotspectrogram 1139 """ 1140 if not channel: 1141 channel = self.channels[0] 1142 desc = kwargs.pop("description", None) 1143 if not subplot: 1144 kwargs.setdefault("xlim", [self.start_time, self.end_time]) 1145 kwargs.setdefault("title", latex(channel)) 1146 1147 # get data 1148 if len(self.spectrogram[channel]): 1149 data = [] 1150 epoch = [] 1151 deltaT = [] 1152 f0 = [] 1153 deltaF = [] 1154 f_array = self.spectrogram[channel][0]['f_array'] 1155 for i in range(len(self.spectrogram[channel])): 1156 data.append(self.spectrogram[channel][i]['data']) 1157 epoch.append(self.spectrogram[channel][i]['epoch']) 1158 deltaT.append(self.spectrogram[channel][i]['deltaT']) 1159 f0.append(self.spectrogram[channel][i]['f0']) 1160 deltaF.append(self.spectrogram[channel][i]['deltaF']) 1161 1162 else: 1163 data = [] 1164 epoch = LIGOTimeGPS(self.start_time) 1165 deltaT = 1 1166 f0 = 0 1167 deltaF = 1 1168 f_array = None 1169 1170 # get ratio 1171 if ratio: 1172 if ratio == "median": 1173 spectrum = self.spectrum[channel].data.data 1174 elif ratio == "max": 1175 spectrum = self.maxspectrum[channel].data.data 1176 elif ratio == "min": 1177 spectrum = self.minspectrum[channel].data.data 1178 elif ratio == "design": 1179 spectrum = self.designspectrum[channel].data.data 1180 elif ratio == "reference": 1181 spectrum = self.referencespectrum[channel].data.data 1182 else: 1183 raise ValueError("Unknown ratio mode \"%s\"" % ratio) 1184 for i,sequence in enumerate(data): 1185 data[i] =\ 1186 lal.CreateREAL8VectorSequence(sequence.length,\ 1187 sequence.vectorLength) 1188 data[i].data = sequence.data/spectrum 1189 numpy.putmask(data[i].data, numpy.isnan(data[i].data), 1e100) 1190 1191 plotdata.plotspectrogram(data, outfile, epoch=epoch, deltaT=deltaT,\ 1192 f0=f0, deltaF=deltaF, ydata=f_array, **kwargs) 1193 if subplot: 1194 self.subplots.append((outfile, desc)) 1195 else: 1196 self.plots.append((outfile, desc))
1197
1198 - def plotspectrum(self, outfile, channel=None, psd=False, subplot=False,\ 1199 **kwargs):
1200 """ 1201 Plot the spectrum of the given channel 1202 1203 @param outfile: filename for plot 1204 @type outfile: C{str} 1205 @param channels: list of channels to plot, default: all of them 1206 @type channels: C{list} 1207 @param psd: plot spectrum as a PSD, default False 1208 @type psd: C{bool} 1209 @param subplot: record plot as a subplot, default: False 1210 @type subplot: C{bool} 1211 @keyword **kwargs: other arguments to pass to 1212 plotdata.plotfrequencyseries 1213 """ 1214 if channel: 1215 channels = [channel] 1216 else: 1217 channels = self.channels 1218 desc = kwargs.pop("description", None) 1219 serieslist = [] 1220 for channel in channels: 1221 serieslist.extend([self.spectrum[channel], 1222 self.minspectrum[channel],\ 1223 self.maxspectrum[channel]]) 1224 if self.designspectrum.has_key(channel): 1225 serieslist.append(self.designspectrum[channel]) 1226 if self.referencespectrum.has_key(channel): 1227 serieslist.append(self.referencespectrum[channel]) 1228 if psd: 1229 for i,series in serieslist: 1230 serieslist[i] = seriesutils.fromarray(series.data.data**2,\ 1231 name=series.name, epoch=series.epoch,\ 1232 deltaT=series.deltaF, f0=series.f0,\ 1233 sampleUnits=series.sampleUnits,\ 1234 frequencyseries=True) 1235 plotdata.plotfrequencyseries(serieslist, outfile, **kwargs) 1236 if subplot: 1237 self.subplots.append((outfile, desc)) 1238 else: 1239 self.plots.append((outfile, desc))
1240
1241 - def finalize(self):
1242 """ 1243 Generate a markup.page object representing the HTML summary of the 1244 data associated with this DataSummaryTab. 1245 """ 1246 # opening 1247 self.frame = markup.page() 1248 div(self.frame, 0, self.name) 1249 self.frame.p("This page summarises %s data." % self.name,\ 1250 class_="line") 1251 1252 # summary 1253 div(self.frame, (0, 1), "Summary") 1254 if len(self.plots): 1255 self.frame.a(href=self.plots[0][0], title=self.plots[0][1],\ 1256 rel="full", class_="fancybox-button") 1257 self.frame.img(src=self.plots[0][0], alt=self.plots[0][1],\ 1258 class_="full") 1259 self.frame.a.close() 1260 1261 # construct info table 1262 headers = ["Channel", "Sampling rate"] 1263 data = [[channel, channel in self.sampling and self.sampling[channel] or 'N/A']\ 1264 for channel in self.channels\ 1265 if not re.search("[-._](min|max)\Z", channel)] 1266 self.frame.add(htmlutils.write_table(headers, data, {"table":"full"})()) 1267 self.frame.div.close() 1268 1269 # other plots 1270 div(self.frame, (0, 2), "Plots") 1271 pclass = len(self.plots[1:]) == 1 and "full" or "half" 1272 for plot,desc in self.plots[1:]: 1273 self.frame.a(href=plot, title=desc, class_="fancybox-button",\ 1274 rel="full") 1275 self.frame.img(src=plot, alt=desc, class_=pclass) 1276 self.frame.a.close() 1277 self.frame.div.close() 1278 1279 # subplots 1280 if len(self.subplots): 1281 div(self.frame, (0, 4), "Subplots", display=True) 1282 for plot,desc in self.subplots: 1283 self.frame.a(href=plot, title=desc, class_="fancybox-button",\ 1284 rel="subplots") 1285 self.frame.img(src=plot, alt=desc, class_="quarter") 1286 self.frame.a.close() 1287 self.frame.div.close() 1288 1289 # information 1290 if self.information: 1291 div(self.frame, (0, 5), "Information", display=True) 1292 self.frame.add(self.information) 1293 self.frame.div.close() 1294 1295 self.frame.div.close() 1296 1297 # dereference 1298 for attr in ["timeseries", "spectrum", "minspectrum", "maxspectrum",\ 1299 "spectrogram"]: 1300 if hasattr(self, attr): 1301 delattr(self, attr)
1302
1303 1304 -class RangeSummaryTab(DataSummaryTab):
1305 """ 1306 SummaryTab representing a summary of detection range extracted from 1307 frames directly or calculated from h(t). 1308 """
1309 - def __init__(self, *args, **kwargs):
1310 DataSummaryTab.__init__(self, *args, **kwargs) 1311 self.sources = list()
1312
1313 - def add_source(self, sourcedict):
1314 self.sources.append(sourcedict) 1315 self.sources.sort(key=lambda s: (s["type"],s["name"]))
1316 1317 @property
1318 - def trigsegments(self):
1319 """glue.segments.segmentlist describing the veto segments for 1320 this Tab. 1321 """ 1322 return self._trigsegments
1323 @trigsegments.setter
1324 - def trigsegments(self, vetolist):
1325 self._trigsegments =\ 1326 segments.segmentlist([segments.segment(map(float, s))\ 1327 for s in vetolist])
1328 @trigsegments.deleter
1329 - def trigsegments(self):
1330 del self._trigsegments
1331
1332 - def finalize(self):
1333 """ 1334 Generate a markup.page object representing the HTML summary of the 1335 data associated with this RangeSummaryTAb. 1336 """ 1337 # opening 1338 self.frame = markup.page() 1339 div(self.frame, 0, self.name) 1340 self.frame.p("This page summarises %s range data." % self.name,\ 1341 class_="line") 1342 1343 # summary 1344 div(self.frame, (0, 1), "Summary") 1345 if len(self.plots): 1346 self.frame.a(href=self.plots[0][0], title=self.plots[0][1],\ 1347 rel="full", class_="fancybox-button") 1348 self.frame.img(src=self.plots[0][0], alt=self.plots[0][1],\ 1349 class_="full") 1350 self.frame.a.close() 1351 1352 # construct info table 1353 headers = ["Source", "Type", "Mean range (Mpc)", "Min range (Mpc)",\ 1354 "Max range (Mpc)"] 1355 td = [] 1356 for s in self.sources: 1357 data = self.timeseries[s["name"]].data.data 1358 data = data[data!=0] 1359 if data.size: 1360 td.append([s["name"], s["type"], data.mean(), data.min(),\ 1361 data.max()]) 1362 else: 1363 td.append([s["name"], s["type"], "-", "-", "-"]) 1364 self.frame.add(htmlutils.write_table(headers, td, {"table":"full"})()) 1365 self.frame.div.close() 1366 1367 # other plots 1368 div(self.frame, (0, 2), "Plots") 1369 pclass = len(self.plots[1:]) == 1 and "full" or "half" 1370 for plot,desc in self.plots[1:]: 1371 self.frame.a(href=plot, title=desc, class_="fancybox-button",\ 1372 rel="full") 1373 self.frame.img(src=plot, alt=desc, class_=pclass) 1374 self.frame.a.close() 1375 self.frame.div.close() 1376 1377 # subplots 1378 if len(self.subplots): 1379 div(self.frame, (0, 4), "Subplots", display=True) 1380 for plot,desc in self.subplots: 1381 self.frame.a(href=plot, title=desc, class_="fancybox-button",\ 1382 rel="subplots") 1383 self.frame.img(src=plot, alt=desc, class_="quarter") 1384 self.frame.a.close() 1385 self.frame.div.close() 1386 1387 # information 1388 if self.information: 1389 div(self.frame, (0, 5), "Information", display=True) 1390 self.frame.add(self.information) 1391 self.frame.div.close() 1392 1393 self.frame.div.close() 1394 1395 # dereference 1396 for attr in ["timeseries"]: 1397 if hasattr(self, attr): 1398 delattr(self, attr)
1399
1400 1401 -class TriggerSummaryTab(SummaryTab):
1402 """ 1403 Object representing a summary of triggers. 1404 """
1405 - def __init__(self, *args, **kwargs):
1406 SummaryTab.__init__(self, *args, **kwargs) 1407 self.triggers = dict() 1408 self.channels = [] 1409 self.trigger_files = dict()
1410
1411 - def add_triggers(self, channel, trigtable):
1412 """ 1413 Add a glue.ligolw.table for a given channel to this object 1414 1415 @param channel: name of channel to record 1416 @type channel: C{str} 1417 @param trigtable: LIGOLw table of triggers to record 1418 @type trigtable: C{glue.ligolw.table.Table} 1419 """ 1420 if self.triggers.has_key(channel): 1421 self.triggers[channel].extend(trigtable) 1422 else: 1423 self.triggers[channel] = trigtable 1424 if channel not in self.channels: 1425 self.channels.append(channel)
1426
1427 - def toxml(self, channel, filename=None):
1428 """ 1429 Write the trigtable for the given channel to an xml file. 1430 1431 @param channel: name of channel to record 1432 @type channel: C{str} 1433 @param filename: path of file to write, preferrably xml.gz extension 1434 @type filename: C{str} 1435 """ 1436 if not filename: 1437 name = _r_cchar.sub("-", _r_cchar.sub("_", channel.upper()), 1) 1438 filename = os.path.join(self.directory, "%s-%d-%d.txt"\ 1439 % (name, self.start_time, abs(self.span))) 1440 with open(filename, "w") as trigf: 1441 dqTriggerUtils.totrigxml(trigf, self.triggers[channel],\ 1442 program=sys.argv[0])
1443
1444 - def plottable(self, outfile, channels=None, subplot=False,\ 1445 **kwargs):
1446 """ 1447 Plot the triggers for this TriggerSummary, one column against another. 1448 1449 @param outfile: filename for plot 1450 @type outfile: C{str} 1451 @param channels: list of channels to plot 1452 @type channels: C{list} 1453 @param subplot: record plot as a subplot, default: False 1454 @type subplot: C{bool} 1455 @keyword **kwargs: other arguments to pass to 1456 plottriggers.plottable 1457 """ 1458 desc = kwargs.pop("description", None) 1459 if kwargs.get("xcolumn", None) == "time": 1460 kwargs.setdefault("xlim", [self.start_time, self.end_time]) 1461 if channels and len(channels) == 1: 1462 kwargs.setdefault("title", "%s (%s)" % (latex(channels[0]),\ 1463 latex(self.etg))) 1464 if channels is not None: 1465 trigs = dict((key,val) for (key,val) in self.triggers.iteritems()\ 1466 if key in channels) 1467 plottriggers.plottable(trigs, outfile, **kwargs) 1468 if len(self.triggers.keys()) == 1: 1469 kwargs.setdefault("title","%s (%s)" % (latex(self.channels[0]),\ 1470 latex(self.etg))) 1471 plottriggers.plottable({"_":self.triggers.values()[0]}, outfile,\ 1472 **kwargs) 1473 else: 1474 plottriggers.plottable(self.triggers, outfile, **kwargs) 1475 if subplot: 1476 self.subplots.append((outfile, desc)) 1477 else: 1478 self.plots.append((outfile, desc))
1479
1480 - def plothistogram(self, outfile, channels=None, subplot=False, **kwargs):
1481 """ 1482 Plot a histogram of the triggers for this TriggerSummary. 1483 1484 @param outfile: filename for plot 1485 @type outfile: C{str} 1486 @param channels: list of channels to plot 1487 @type channels: C{list} 1488 @param subplot: record plot as a subplot, default: False 1489 @type subplot: C{bool} 1490 @keyword **kwargs: other arguments to pass to 1491 plottriggers.plothistogram 1492 """ 1493 desc = kwargs.pop("description", None) 1494 if kwargs.get("cumulative", True) and not kwargs.has_key("normalize"): 1495 kwargs["normalize"] = float(abs(self.span)) 1496 if channels and len(channels) == 1: 1497 kwargs.setdefault("title", "%s (%s)" % (latex(channels[0]),\ 1498 latex(self.etg))) 1499 elif not channels: 1500 kwargs.setdefault("title", "%s (%s)" 1501 % (latex(self.triggers.keys()[0]),\ 1502 latex(self.etg))) 1503 kwargs.setdefault("subtitle", 1504 "%d-%d" % (int(self.start_time), int(self.end_time))) 1505 if channels is not None: 1506 trigs = dict((key,val) for key in self.triggers.keys() if key\ 1507 in channels) 1508 plottriggers.plothistogram(trigs, outfile, **kwargs) 1509 elif len(self.triggers.keys()) == 1: 1510 plottriggers.plothistogram({"_":self.triggers.values()[0]},\ 1511 outfile, **kwargs) 1512 else: 1513 plottriggers.plothistogram(self.triggers, outfile, **kwargs) 1514 if subplot: 1515 self.subplots.append((outfile, desc)) 1516 else: 1517 self.plots.append((outfile, desc))
1518
1519 - def plotrate(self, outfile, channels=None, subplot=False, **kwargs):
1520 """ 1521 Plot the rate of triggers for this TriggerSummary. 1522 1523 @param outfile: filename for plot 1524 @type outfile: C{str} 1525 @param channels: list of channels to plot 1526 @type channels: C{list} 1527 @param subplot: record plot as a subplot, default: False 1528 @type subplot: C{bool} 1529 @keyword **kwargs: other arguments to pass to 1530 plottriggers.plotrate 1531 """ 1532 desc = kwargs.pop("description", None) 1533 kwargs.setdefault("xlim", [self.start_time, self.end_time]) 1534 kwargs.setdefault("title", "%s (%s) binned by %s" 1535 % (latex(self.triggers.keys()[0]), 1536 latex(self.etg), latex(kwargs["column"]))) 1537 plottriggers.plotrate(self.triggers.values()[0], outfile, **kwargs) 1538 if subplot: 1539 self.subplots.append((outfile, desc)) 1540 else: 1541 self.plots.append((outfile, desc))
1542
1543 - def plotautocorrelation(self, outfile, channels=None, subplot=False,\ 1544 **kwargs):
1545 """ 1546 Plot the auto correlation function of the trigers for this 1547 TriggerSummary. 1548 1549 @param outfile: filename for plot 1550 @type outfile: C{str} 1551 @param channels: list of channels to plot 1552 @type channels: C{list} 1553 @param subplot: record plot as a subplot, default: False 1554 @type subplot: C{bool} 1555 @keyword **kwargs: other arguments to pass to 1556 plottriggers.plotautocorrelation 1557 """ 1558 desc = kwargs.pop("description", None) 1559 if kwargs.get("xcolumn", None) == "time": 1560 kwargs.setdefault("xlim", [self.start_time, self.end_time]) 1561 if channels: 1562 trigs = dict((key,val) for key in self.triggers.keys() if key\ 1563 in channels) 1564 plottriggers.plotautocorrelation(trigs, outfile, **kwargs) 1565 elif len(self.triggers.keys()) == 1: 1566 plottriggers.plotautocorrleation({"_":self.triggers.values()[0]},\ 1567 outfile, **kwargs) 1568 else: 1569 plottriggers.plotautocorrelation(self.triggers, outfile, **kwargs) 1570 if subplot: 1571 self.subplots.append((outfile, desc)) 1572 else: 1573 self.plots.append((outfile, desc))
1574
1575 - def finalize(self, snrs=[5,8,10,20,50,100,500,1000]):
1576 """ 1577 Generate a markup.page object summarising the triggers in this 1578 TriggerSummaryTab. 1579 """ 1580 # opening 1581 self.frame = markup.page() 1582 div(self.frame, 0, self.name) 1583 self.frame.p("This page summarises %s triggers." % self.name,\ 1584 class_="line") 1585 1586 # summary 1587 div(self.frame, (0, 1), "Summary") 1588 self.frame.a(href=self.plots[0][0], title=self.plots[0][1],\ 1589 rel="full", class_="fancybox-button") 1590 self.frame.img(src=self.plots[0][0], alt=self.plots[0][1],class_="full") 1591 self.frame.a.close() 1592 1593 # trig stats 1594 th = ["Channel"]+["SNR >= %d" % s for s in snrs] 1595 td = [] 1596 for chan in self.channels: 1597 snr = numpy.asarray(self.triggers[chan].getColumnByName("snr")) 1598 td.append([chan]+[(snr>s).sum() for s in snrs]) 1599 self.frame.add(htmlutils.write_table(th, td, {"table":"full"})()) 1600 self.frame.div.close() 1601 1602 # other plots 1603 div(self.frame, (0, 2), "Plots") 1604 pclass = len(self.plots[1:]) == 1 and "full" or "half" 1605 for plot,desc in self.plots[1:]: 1606 self.frame.a(href=plot, title=desc, class_="fancybox-button",\ 1607 rel="full") 1608 self.frame.img(src=plot, alt=desc, class_=pclass) 1609 self.frame.a.close() 1610 self.frame.div.close() 1611 1612 # loudest triggers 1613 div(self.frame, (0, 3), "Loudest events") 1614 for i,chan in enumerate(self.channels): 1615 div(self.frame, (0, 3, i), chan, display=False) 1616 trigfile = self.trigger_files.get(chan, None) 1617 if trigfile is not None: 1618 self.frame.p("The full segment list can be downloaded from %s."\ 1619 % markup.oneliner.a("this file", href=trigfile),\ 1620 class_="line") 1621 trigs = table.new_from_template(self.triggers[chan]) 1622 # FIXME write list of loudest triggers 1623 if re.search("sngl_inspiral", trigs.tableName, re.I): 1624 self.triggers[chan].sort(key=lambda t: t.get_new_snr(), 1625 reverse=True) 1626 else: 1627 self.triggers[chan].sort(key=lambda t: t.snr, reverse=True) 1628 trigs.extend(self.triggers[chan][:20]) 1629 data = [] 1630 data.append(plottriggers.get_column(trigs, "time")) 1631 head = ["Time"] 1632 if "central_freq" in trigs.validcolumns.keys(): 1633 data.append(plottriggers.get_column(trigs, "duration")) 1634 data.append(plottriggers.get_column(trigs, "central_freq")) 1635 data.append(plottriggers.get_column(trigs, "bandwidth")) 1636 head.extend(["Duration", "Frequency", "Bandwidth"]) 1637 else: 1638 data.append(plottriggers.get_column(trigs, "template_duration")) 1639 data.append(plottriggers.get_column(trigs, "mchirp")) 1640 head.extend(["Template duration", "Chirp mass"]) 1641 data.append(plottriggers.get_column(trigs, "snr")) 1642 head.append("SNR") 1643 if re.search("sngl_inspiral", trigs.tableName, re.I): 1644 data.append(trigs.get_column("new_snr")) 1645 head.append("newSNR") 1646 data = map(list, zip(*data)) 1647 self.frame.add(htmlutils.write_table(head,data,{"table":"full"})()) 1648 self.frame.div.close() 1649 self.frame.div.close() 1650 1651 # subplots 1652 if len(self.subplots): 1653 div(self.frame, (0, 4), "Subplots", display=True) 1654 for plot,desc in self.subplots: 1655 self.frame.a(href=plot, title=desc, class_="fancybox-button",\ 1656 rel="subplots") 1657 self.frame.img(src=plot, alt=desc, class_="quarter") 1658 self.frame.a.close() 1659 self.frame.div.close() 1660 1661 # information 1662 if self.information: 1663 div(self.frame, (0, 5), "Information", display=True) 1664 self.frame.add(self.information) 1665 self.frame.div.close() 1666 1667 self.frame.div.close() 1668 1669 # dereference 1670 for attr in ["triggers"]: 1671 if hasattr(self, attr): 1672 delattr(self, attr)
1673
1674 1675 -class AuxTriggerSummaryTab(TriggerSummaryTab):
1676 """ 1677 Object representing a summary of auxiliary channel triggers. 1678 """
1679 - def __init__(self, *args, **kwargs):
1680 SummaryTab.__init__(self, *args, **kwargs) 1681 self.triggers = dict() 1682 self.channels = [] 1683 self.mainchannel = [] 1684 self.trigger_files = dict() 1685 self.coincs = dict() 1686 self.numcoincs = dict() 1687 self.sigma = dict() 1688 self.auxplots = dict()
1689
1690 - def get_coincs(self, channel1, channel2, dt=0.03):
1691 """ 1692 Find triggers for channel1 within dt of a trigger for channel2. 1693 """ 1694 self.coincs[(channel1, channel2)] =\ 1695 dqTriggerUtils.get_coincs(self.triggers[channel1],\ 1696 self.triggers[channel2], dt=dt) 1697 self.numcoincs[(channel1, channel2)] =\ 1698 len(self.coincs[(channel1, channel2)])
1699
1700 - def compute_coinc_significance(self, channel, dt=0.03, shifts=[0]):
1701 """ 1702 Calculate the statistical significance of the number of triggers in 1703 channel coincident with a trigger in the mainchannel. 1704 Give a list of numerical time shifts to calculate for multiple events. 1705 """ 1706 trigs1 = self.triggers[channel] 1707 trigs2 = self.triggers[self.mainchannel] 1708 t = plottriggers.get_column(trigs1, "time") 1709 1710 _reburst = re.compile("burst", re.I) 1711 _rering = re.compile("ring", re.I) 1712 _recbc = re.compile("inspiral", re.I) 1713 shifts = numpy.asarray(shifts) 1714 ncoinc = numpy.zeros(len(shifts)) 1715 for i,shift in enumerate(shifts): 1716 if shift==0 and self.numcoincs.has_key((self.mainchannel, channel)): 1717 ncoinc[i] = self.numcoincs[(self.mainchannel, channel)] 1718 else: 1719 ncoinc[i] = dqTriggerUtils.get_number_coincs(trigs2, trigs1,\ 1720 dt=dt,\ 1721 timeshift=shift) 1722 1723 # calculate numsigma for each slide 1724 mean = ncoinc[shifts!=0].mean() 1725 std = ncoinc[shifts!=0].std() 1726 self.sigma[channel] = dict(zip(shifts, numpy.fabs(ncoinc-mean)/std))
1727
1728 - def plottable(self, outfile, channel, **kwargs):
1729 """ 1730 Plot the triggers for the given channel. 1731 """ 1732 desc = kwargs.pop("description", None) 1733 if kwargs.get("xcolumn", None) == "time": 1734 kwargs.setdefault("xlim", [self.start_time, self.end_time]) 1735 kwargs.setdefault("title", "%s (%s)" % (latex(channel),\ 1736 latex(self.etg))) 1737 plottriggers.plottable({"_":self.triggers[channel]}, outfile,\ 1738 **kwargs) 1739 self.auxplots[channel].append((outfile, desc))
1740
1741 - def plothistogram(self, outfile, channel, **kwargs):
1742 desc = kwargs.pop("description", None) 1743 plottriggers.plothistogram({"_":self.triggers[channel]}, outfile,\ 1744 **kwargs) 1745 self.auxplots[channel].append((outfile, desc))
1746
1747 - def plotrate(self, outfile, channel, **kwargs):
1748 desc = kwargs.pop("description", None) 1749 kwargs.setdefault("xlim", [self.start_time, self.end_time]) 1750 plottriggers.plotrate({"_":self.triggers[channel]}, outfile,\ 1751 **kwargs) 1752 self.auxplots[chan].append((outfile, desc))
1753
1754 - def plotautocorrelation(self, outfile, channel, **kwargs):
1755 desc = kwargs.pop("description", None) 1756 if kwargs.get("xcolumn", None) == "time": 1757 kwargs.setdefault("xlim", [self.start_time, self.end_time]) 1758 plottriggers.plotautocorrleation({"_":self.triggers[channel]},\ 1759 outfile, **kwargs) 1760 self.auxplots[chan].append((outfile, desc))
1761
1762 - def plotcoincs(self, outfile, channel, **kwargs):
1763 """ 1764 Plot the coincident triggers between the given channel and the 1765 mainchannel. 1766 """ 1767 desc = kwargs.pop("description", None) 1768 if kwargs.get("xcolumn", None) == "time": 1769 kwargs.setdefault("xlim", [self.start_time, self.end_time]) 1770 kwargs.setdefault("title", "Coincident %s and %s (%s)"\ 1771 % (latex(channel), latex(self.mainchannel),\ 1772 latex(self.etg))) 1773 trigs = {channel:self.coincs[(channel, self.mainchannel)],\ 1774 self.mainchannel:self.coincs[(self.mainchannel, channel)]} 1775 plottriggers.plottable(trigs, outfile, **kwargs) 1776 self.auxplots[channel].append((outfile, desc))
1777
1778 - def plotsigma(self, outfile, channel=None, **kwargs):
1779 """ 1780 Plot the statistical significance of the number of coincidences between 1781 the mainchannel and all auxiliary channels. 1782 """ 1783 desc = kwargs.pop("description", None) 1784 xdata = [] 1785 ydata = [] 1786 1787 # get data for one channel 1788 if channel: 1789 xdata,ydata = zip(*sorted(self.sigma[channel].items(),\ 1790 key=lambda (x,y): x)) 1791 # or data for all channels 1792 else: 1793 for chan in self.channels: 1794 if chan == self.mainchannel: 1795 continue 1796 if self.sigma[chan].has_key(0.0): 1797 ydata.append(self.sigma[chan][0.0]) 1798 xdata.append(chan) 1799 1800 # get axis params 1801 kwargs.pop("xlim", None) 1802 ylim = kwargs.pop("ylim", None) 1803 kwargs.pop("logx", False) 1804 logy = kwargs.pop("logy", False) 1805 1806 # get labels 1807 if channel: 1808 xlabel = kwargs.pop("xlabel", "Time shift (s)") 1809 else: 1810 xlabel = kwargs.pop("xlabel", "") 1811 ylabel = kwargs.pop("ylabel", r"Coincidence significance "\ 1812 "($\mathrm{\sigma}$)") 1813 if channel: 1814 title = kwargs.pop("title", "Coincident %s and %s (%s)"\ 1815 % (latex(channel), latex(self.mainchannel),\ 1816 latex(self.etg))) 1817 else: 1818 title = kwargs.pop("title",\ 1819 "Significance of coincidences with %s (%s)"\ 1820 % (latex(self.mainchannel), latex(self.etg))) 1821 subtitle = kwargs.pop("subtitle", "") 1822 1823 # get misc params 1824 bbox = kwargs.pop("bbox_inches", None) 1825 cbar = kwargs.pop("hidden_colorbar", None) 1826 1827 # make plot 1828 if channel: 1829 plot = plotdata.plotutils.BarPlot(xlabel, ylabel, title, subtitle) 1830 else: 1831 plot = plotdata.plotutils.BarPlot(xlabel, ylabel, title, subtitle,\ 1832 figsize=[24,6]) 1833 plot.add_content(numpy.arange(len(xdata)), ydata, **kwargs) 1834 plot.finalize() 1835 if cbar: 1836 plotdata.plotutils.add_colorbar(plot.ax, visible=False) 1837 1838 # set xticks to channel names and rotate 1839 plot.ax.set_xlim(-1, len(xdata)) 1840 plot.ax.set_xticks(numpy.arange(0,len(xdata))) 1841 if channel: 1842 plot.ax.set_xticklabels(map(lambda x: "$%s$"\ 1843 % plotdata.plotutils.float_to_latex(x),\ 1844 xdata)) 1845 else: 1846 plot.ax.set_xticklabels(map(latex, xdata)) 1847 for i,t in enumerate(plot.ax.get_xticklabels()): 1848 t.set_rotation(315) 1849 t.set_verticalalignment('top') 1850 t.set_horizontalalignment('left') 1851 t.set_fontsize("smaller") 1852 plot.fig.subplots_adjust(bottom=0.3) 1853 1854 if ylim: 1855 plot.ax.set_ylim(ylim) 1856 1857 plot.savefig(outfile, bbox_inches=bbox) 1858 plot.close() 1859 if channel: 1860 self.auxplots[channel].append((outfile, desc)) 1861 else: 1862 self.plots.append((outfile, desc))
1863
1864 - def plotnumslides(self, outfile, channel, **kwargs):
1865 """ 1866 Plot the number of coincidences between this channel and the 1867 mainchannel for all slides and zerolag. 1868 """ 1869 desc = kwargs.pop("description", None) 1870 data = sorted(self.sigma[channel].items(), key=lambda x: x[0]) 1871 shifts,sigma = map(numpy.asarray, zip(*data)) 1872 1873 # get axis params 1874 kwargs.pop("xlim") 1875 xlim = [shifts.min() - abs(shifts.min())*0.01,\ 1876 shifts.max() + abs(shifts.max())+0.01] 1877 ylim = kwargs.pop("ylim", None) 1878 kwargs.pop("logx") 1879 logy = kwargs.pop("logy") 1880 1881 # get labels 1882 xlabel = kwargs.pop("xlabel", "Time shift (s)") 1883 ylabel = kwargs.pop("ylabel", "Number of coincidences") 1884 title = kwargs.pop("title", "") 1885 subtitle = kwargs.pop("subtitle", "") 1886 1887 # get misc params 1888 bbox = kwargs.pop("bbox_inches", None) 1889 cbar = kwargs.pop("hidden_colorbar", None) 1890 1891 # make plot 1892 plot = plotdata.plotutils.SimplePlot(xlabel, ylabel, title, subtitle) 1893 plot.add_content(shift, sigma, **kwargs) 1894 plot.finalize() 1895 if logy: 1896 plot.ax.sey_yscale("log") 1897 plot.ax.set_xlim(xlim) 1898 if logy: 1899 plot.ax.set_ylim(ylim) 1900 if cbar: 1901 plotdata.plotutils.add_colorbar(plot.ax, visible=False) 1902 1903 plotdata.plotutils.set_ticks(plot.ax, x=False, y=True) 1904 plot.savefig(outfile, bbox_inches=bbox) 1905 plot.close() 1906 self.auxplots[chan].append((outfile, desc))
1907
1908 - def finalize(self):
1909 """ 1910 Generate a glue.markup.page summarising the auxiliary channel triggers 1911 for this AuxTriggerSummaryTab. 1912 """ 1913 # opening 1914 self.frame = markup.page() 1915 div(self.frame, 0, self.name) 1916 self.frame.p("This page summarises auxiliary channel %s triggers."\ 1917 % self.name, class_="line") 1918 1919 # summary 1920 if self.mainchannel: 1921 div(self.frame, (0, 1), "Summary") 1922 if len(self.plots): 1923 self.frame.a(href=self.plots[0][0], title=self.plots[0][1],\ 1924 class_="fancybox-button", rel="full") 1925 self.frame.img(src=self.plots[0][0], alt=self.plots[0][1],\ 1926 class_="full") 1927 self.frame.a.close() 1928 1929 # trig stats 1930 if len(self.numcoincs.keys()) != 0: 1931 th = ["Channel", "Num. coinc. with<br>%s" % self.mainchannel,\ 1932 "Num. %s<br>coinc. with aux." % self.mainchannel,\ 1933 "Zero shift coinc. &sigma;"] 1934 td = [] 1935 cellclasses = {"table":"full"} 1936 for chan in self.channels: 1937 if chan == self.mainchannel: 1938 continue 1939 td.append([chan]) 1940 if (chan, self.mainchannel) in self.numcoincs.keys(): 1941 td[-1].extend([self.numcoincs[(chan, self.mainchannel)], 1942 self.numcoincs[(self.mainchannel,chan)]]) 1943 else: 1944 td[-1].extend(["-", "-"]) 1945 if (self.sigma.has_key(chan) and 1946 self.sigma[chan].has_key(0.0)): 1947 sigmaStr = "%.2f" % self.sigma[chan][0.0] 1948 td[-1].append(sigmaStr) 1949 if self.sigma[chan][0.0] > 5: 1950 cellclasses[sigmaStr] = "red" 1951 cellclasses[chan] = "red" 1952 else: 1953 td[-1].append("-") 1954 self.frame.add(htmlutils.write_table(th, td, 1955 cellclasses)()) 1956 1957 self.frame.div.close() 1958 1959 # channel information 1960 div(self.frame, (0, 2), "Auxiliary channels", display=True) 1961 1962 for i,chan in enumerate(self.channels): 1963 if chan == self.mainchannel: 1964 continue 1965 div(self.frame, (0, 2, i), chan, display=True) 1966 if (chan, self.mainchannel) in self.numcoincs: 1967 th = ["Num. coinc with<br>%s" % (self.mainchannel),\ 1968 "Num %s<br>coinc with aux." % (self.mainchannel),\ 1969 "Zero time-shift coincidence &sigma;"] 1970 td = list() 1971 if (chan, self.mainchannel) in self.numcoincs.keys(): 1972 td.extend([self.numcoincs[(chan, self.mainchannel)],\ 1973 self.numcoincs[(self.mainchannel, chan)]]) 1974 else: 1975 td.extend(["-", "-"]) 1976 if self.sigma.has_key(chan) and self.sigma[chan].has_key(0.0): 1977 td.append("%.2f" % self.sigma[chan][0.0]) 1978 else: 1979 td.append("-") 1980 self.frame.add(htmlutils.write_table(th,td,{"table":"full"})()) 1981 class_ = plotclass(len(self.auxplots[chan])) 1982 for p,d in self.auxplots[chan]: 1983 self.frame.a(href=p, title=d, class_="fancybox-button", rel=class_) 1984 self.frame.img(src=p, alt=d, class_=class_) 1985 self.frame.a.close() 1986 self.frame.div.close() 1987 self.frame.div.close() 1988 1989 # information 1990 if self.information: 1991 div(self.frame, (0, 3), "Information", display=True) 1992 self.frame.add(self.information) 1993 self.frame.div.close() 1994 1995 self.frame.div.close() 1996 1997 # deference 1998 for attr in ["triggers", "coincs"]: 1999 if hasattr(self, attr): 2000 delattr(self, attr)
2001
2002 -class HvetoSummaryTab(TriggerSummaryTab):
2003 """ 2004 Object representing a summary of auxiliary channel triggers. 2005 """
2006 - def __init__(self, *args, **kwargs):
2007 SummaryTab.__init__(self, *args, **kwargs) 2008 self.triggers = dict() 2009 self.channels = [] 2010 self.winnerchannels = [] 2011 self.mainchannel = [] 2012 self.trigger_files = dict() 2013 self.coincs = dict() 2014 self.numcoincs = dict() 2015 self.sigma = dict() 2016 self.auxplots = dict() 2017 self.rounds = {}
2018
2019 - def plothistogram(self, outfile, rnd, **kwargs):
2020 """ 2021 Kludge, rnd=0 is all the rounds combined 2022 """ 2023 2024 desc = kwargs.pop("description", None) 2025 if kwargs.get("cumulative", True) and not kwargs.has_key("normalize"): 2026 kwargs["normalize"] = float(abs(self.span)) 2027 trigs = {} 2028 segments_before = segments.segmentlist([]) 2029 for i in range(1,rnd): 2030 segments_before |= self.rounds[i].veto_segments 2031 segments_after = segments.segmentlist([]) 2032 if rnd: 2033 segments_after |= segments_before 2034 segments_after |= self.rounds[rnd].veto_segments 2035 else: 2036 for i in range(1,1+len(self.rounds)): 2037 segments_after |= self.rounds[i].veto_segments 2038 # KLUDGE: putting space in front of "before" so that it is sorted in legends above the "after" line 2039 trigs[" before"] = lsctables.New(lsctables.SnglBurstTable) 2040 trigs[" before"].extend([t for t in self.triggers[self.mainchannel] if t.peak_time not in segments_before]) 2041 trigs["after"] = lsctables.New(lsctables.SnglBurstTable) 2042 trigs["after"].extend([t for t in self.triggers[self.mainchannel] if t.peak_time not in segments_after]) 2043 plottriggers.plothistogram(trigs, outfile,\ 2044 **kwargs) 2045 self.auxplots[rnd].append((outfile, desc))
2046
2047 - def plotcoincs(self, outfile, rnd, **kwargs):
2048 """ 2049 Plot the coincident triggers between the given round 2050 """ 2051 desc = kwargs.pop("description", None) 2052 if kwargs.get("xcolumn", None) == "time": 2053 kwargs.setdefault("xlim", [self.start_time, self.end_time]) 2054 if rnd: 2055 auxchannelname = latex(self.rounds[rnd].channel) 2056 else : 2057 auxchannelname = "auxiliary" 2058 kwargs.setdefault("title", "Coincident %s and %s (%s)"\ 2059 % (auxchannelname, latex(self.mainchannel),\ 2060 latex(self.etg))) 2061 trigs = {} 2062 if rnd: 2063 trigs[auxchannelname] = self.rounds[rnd].vetoing_aux_trigs 2064 trigs[self.mainchannel] = self.rounds[rnd].vetoed_h_trigs 2065 else: 2066 trigs[auxchannelname] = lsctables.New(lsctables.SnglBurstTable) 2067 trigs[self.mainchannel] = lsctables.New(lsctables.SnglBurstTable) 2068 for i in range(1,1+len(self.rounds)): 2069 trigs[auxchannelname].extend(self.rounds[i].vetoing_aux_trigs) 2070 trigs[self.mainchannel].extend(self.rounds[i].vetoed_h_trigs) 2071 plottriggers.plottable(trigs, outfile, **kwargs) 2072 self.auxplots[rnd].append((outfile, desc))
2073
2074 - def finalize(self):
2075 """ 2076 Generate a glue.markup.page summarising the auxiliary channel triggers 2077 for this AuxTriggerSummaryTab. 2078 """ 2079 # opening 2080 self.frame = markup.page() 2081 div(self.frame, 0, self.name) 2082 self.frame.p("This page summarise hveto with %s triggers."\ 2083 % self.name, class_="line") 2084 2085 # summary 2086 if self.mainchannel: 2087 div(self.frame, (0, 1), "Summary") 2088 if len(self.plots): 2089 self.frame.a(href=self.plots[0][0], title=self.plots[0][1],\ 2090 class_="fancybox-button", rel="full") 2091 self.frame.img(src=self.plots[0][0], alt=self.plots[0][1],\ 2092 class_="full") 2093 self.frame.a.close() 2094 # trig stats 2095 th = ['Channel', 'Significance', 'T win.', 'SNR', 'Use %', 'Eff.', 'Deadtime',\ 2096 'Eff./Deadtime', 'Cum. Eff.', 'Cum. Deadtime', 'Cum. Eff./Deadtime',\ 2097 'Safety', 'Segments' ] 2098 td = [] 2099 cellclasses = {"table":"full"} 2100 2101 # produce a row for each round 2102 cumeff = 0 2103 cumdt = 0 2104 for i in sorted(self.rounds.keys()): 2105 # work the numbers 2106 use = self.rounds[i].use_percentage 2107 eff = self.rounds[i].efficiency[0] 2108 dt = self.rounds[i].deadtime[0] 2109 edr = self.rounds[i].deadtime!=0\ 2110 and round(eff/dt, 2) or 'N/A' 2111 cumeff += eff 2112 cumdt += dt 2113 cumedr = cumeff/cumdt 2114 # generate strings 2115 use = '%s%%' % round(use, 2) 2116 eff = '%s%%' % round(eff, 2) 2117 dt = '%s%%' % round(dt, 3) 2118 cumeffstr = '%s%%' % round(cumeff, 2) 2119 cumdtstr = '%s%%' % round(cumdt, 2) 2120 cumedrstr = '%s%%' % round(cumedr, 2) 2121 2122 # work safety 2123 safe = 'N/A' 2124 2125 # add to table 2126 td.append([self.rounds[i].channel, round(self.rounds[i].significance, 2), self.rounds[i].dt, self.rounds[i].snr,\ 2127 use, eff, dt, edr, cumeffstr, cumdtstr, cumedrstr, safe,\ 2128 '<a href="%s" rel="external">link</a>' % self.rounds[i].veto_file]) 2129 2130 self.frame.add(htmlutils.write_table(th, td, 2131 cellclasses)()) 2132 2133 self.frame.div.close() 2134 2135 # channel information 2136 div(self.frame, (0, 2), "Auxiliary channels", display=True) 2137 2138 for i,chan in enumerate(self.winnerchannels): 2139 div(self.frame, (0, 2, i), chan, display=True) 2140 class_ = plotclass(len(self.auxplots[i+1])) 2141 for p,d in self.auxplots[i+1]: 2142 self.frame.a(href=p, title=d, class_="fancybox-button", rel=class_) 2143 self.frame.img(src=p, alt=d, class_=class_) 2144 self.frame.a.close() 2145 self.frame.div.close() 2146 self.frame.div.close() 2147 2148 # information 2149 if self.information: 2150 div(self.frame, (0, 3), "Information", display=True) 2151 self.frame.add(self.information) 2152 self.frame.div.close() 2153 2154 self.frame.div.close() 2155 2156 # deference 2157 for attr in ["triggers", "coincs"]: 2158 if hasattr(self, attr): 2159 delattr(self, attr)
2160
2161 2162 -class StateVectorSummaryTab(SegmentSummaryTab):
2163 """ 2164 Object representing the summary of a bitmasked channel. 2165 """
2166 - def __init__(self, *args, **kwargs):
2167 SegmentSummaryTab.__init__(self, *args, **kwargs) 2168 self.bitmask = dict() 2169 self.derived_bitmask = dict()
2170
2171 - def add_bitmask(self, bitmask):
2172 self.bitmask = dict(bitmask) 2173 self.bits = sorted(self.bitmask.keys())
2174
2175 - def add_derived_bits(self, derived_bitmask):
2176 self.derived_bitmask = dict(derived_bitmask) 2177 self.derived_bits = sorted(self.derived_bitmask.keys())
2178
2179 - def finalize(self):
2180 """ 2181 Generate a markup.page object representing the HTML summary of the 2182 segments associated with this StateVectorSummaryTab. 2183 """ 2184 # opening 2185 self.frame = markup.page() 2186 div(self.frame, 0, self.name) 2187 self.frame.p("This page summarises %s state vector." % self.name,\ 2188 class_="line") 2189 2190 # summary 2191 div(self.frame, (0, 1), "Summary") 2192 self.frame.a(href=self.plots[0][0], title=self.plots[0][1],\ 2193 class_="fancybox-button", rel="full") 2194 self.frame.img(src=self.plots[0][0], alt=self.plots[0][1],\ 2195 class_="full") 2196 self.frame.a.close() 2197 2198 # construct livetime table 2199 uptime = float(abs(self.span)) 2200 headers = ["Bit", "Flag", "Livetime (s)", "Duty cycle (%%)"] 2201 data = [[bit, self.bitmask[bit],\ 2202 abs(self.segdict[self.bitmask[bit]]),\ 2203 "%.2f" %\ 2204 (100*(abs(self.segdict[self.bitmask[bit]])/uptime))]\ 2205 for bit in self.bits] 2206 self.frame.add(htmlutils.write_table(headers, data, {"table":"full"})()) 2207 self.frame.div.close() 2208 2209 # other plots 2210 if len(self.plots) > 1: 2211 div(self.frame, (0, 2), "Plots") 2212 for plot,desc in self.plots[1:]: 2213 self.frame.a(href=plot, title=desc, class_="fancybox-button",\ 2214 rel="half") 2215 self.frame.img(src=plot, alt=desc, class_="half") 2216 self.frame.a.close() 2217 self.frame.div.close() 2218 2219 # segment lists 2220 div(self.frame, (0, 3), "Segment lists") 2221 for i,bit in enumerate(self.bits): 2222 flag = self.bitmask[bit] 2223 div(self.frame, (0, 3, i), flag, display=False) 2224 segfile = self.segment_files.get(flag, None) 2225 if segfile is not None: 2226 self.frame.p("The full segment list can be downloaded from %s."\ 2227 % markup.oneliner.a("this file", href=segfile),\ 2228 class_="line") 2229 segwizard = StringIO.StringIO() 2230 segmentsUtils.tosegwizard(segwizard, self.segdict[flag]) 2231 self.frame.pre(segwizard.getvalue()) 2232 segwizard.close() 2233 self.frame.div.close() 2234 self.frame.div.close() 2235 2236 # subplots 2237 if len(self.subplots): 2238 div(self.frame, (0, 4), "Subplots", display=True) 2239 for plot,desc in self.subplots: 2240 self.frame.a(href=plot, title=desc, class_="fancybox-button",\ 2241 rel="subplots") 2242 self.frame.img(src=plot, alt=desc, class_="quarter") 2243 self.frame.a.close() 2244 self.frame.div.close() 2245 2246 # information 2247 if self.information: 2248 div(self.frame, (0, 5), "Information", display=True) 2249 self.frame.add(self.information) 2250 self.frame.div.close() 2251 2252 self.frame.div.close() 2253 2254 # deference 2255 for attr in ["segdict"]: 2256 if hasattr(self, attr): 2257 delattr(self, attr)
2258
2259 2260 -class GlossaryTab(SummaryTab):
2261
2262 - def __init__(self, *args, **kwargs):
2263 SummaryTab.__init__(self, *args, **kwargs) 2264 self.tabs = "" 2265 self.definitions = dict()
2266
2267 - def add_entry(self, key, val):
2268 self.definitions[key] = val
2269
2270 - def add_entries(self, *args):
2271 for key,val in args: 2272 self.add_entry(key, val)
2273
2274 - def finalize(self):
2275 """ 2276 Write the GlossaryTab HTML frame. 2277 """ 2278 self.frame = htmlutils.write_glossary(self.definitions)
2279
2280 2281 -class AboutTab(SummaryTab):
2282 2283 version = None 2284 files = list() 2285
2286 - def __init__(self, *args, **kwargs):
2287 SummaryTab.__init__(self, *args, **kwargs) 2288 self.tabs = ""
2289
2290 - def add_file(self, name, path):
2291 self.files.append((name, path))
2292
2293 - def finalize(self):
2294 """ 2295 Write the AboutTab HTML frame. 2296 """ 2297 self.frame = htmlutils.about_page(sys.argv[0], sys.argv[1:],\ 2298 self.version, self.files)
2299
2300 2301 -class CalendarTab(SummaryTab):
2302
2303 - def __init__(self, *args, **kwargs):
2304 SummaryTab.__init__(self, *args, **kwargs) 2305 self.weekday = 0 2306 self.tabs = ""
2307 2308 @property
2309 - def start_date(self):
2310 """datetime.datetime start date for this CalendarTab.""" 2311 return self._start_date
2312 @start_date.setter
2313 - def start_date(self, date):
2314 if isinstance(date, str): 2315 if re.search("/", startdate): 2316 self._start_date = datetime.datetime.strptime(date, "%Y/%m/%d") 2317 else: 2318 self._start_date = datetime.datetime.strptime(date, "%Y%m%d") 2319 elif isinstance(date, int): 2320 self._start_date = datetime.date(*lal.GPSToUTC(int(date))[:3]) 2321 elif isinstance(date, datetime.datetime)\ 2322 or isinstance(date, datetime.date): 2323 self._start_date = date
2324 2325 @property
2326 - def end_date(self):
2327 """datetime.datetime end date for this CalendarTab.""" 2328 return self._end_date
2329 @end_date.setter
2330 - def end_date(self, date):
2331 if isinstance(date, str): 2332 if re.search("/", enddate): 2333 self._end_date = datetime.datetime.strptime(date, "%Y/%m/%d") 2334 else: 2335 self._end_date = datetime.datetime.strptime(date, "%Y%m%d") 2336 elif isinstance(date, int): 2337 self._end_date = datetime.date(*lal.GPSToUTC(int(date))[:3]) 2338 elif isinstance(date, datetime.datetime)\ 2339 or isinstance(date, datetime.date): 2340 self._end_date = date
2341 2342 # 2343 # class methods 2344 # 2345
2346 - def finalize(self):
2347 """ 2348 Returns a glue.markup.page containing a nested HTML table of links 2349 for the given SummaryTab. 2350 """ 2351 # get startday from configuration 2352 years = numpy.arange(self.start_date.year, self.end_date.year+1) 2353 2354 # make page 2355 self.frame = markup.page() 2356 self.frame.h1('Calendar') 2357 for i,y in enumerate(years[::-1]): 2358 startdate = datetime.date(y, 1, 1) 2359 enddate = min(self.end_date, datetime.date(y, 12, 31)) 2360 self.frame.h2(y, id="h2_%d" % i, onclick="toggleVisible();",\ 2361 class_="calendar %s" % (i==0 and 'open' or 'closed')) 2362 self.frame.div(id="div_%d" % i, class_="calendar",\ 2363 style="display: %s;" % (i==0 and 'block' or 'none')) 2364 self.frame.add(calendar_page(startdate, enddate,\ 2365 weekday=self.weekday)()) 2366 self.frame.div.close()
2367
2368 2369 -class OnlineSummaryTab(SummaryTab):
2370 """ 2371 SummaryTab representing the online data page. 2372 """
2373 - def __init__(self, *args, **kwargs):
2374 SummaryTab.__init__(self, *args, **kwargs) 2375 self.tabs = "" 2376 self.states = list() 2377 self.sections = list() 2378 self.plots = dict() 2379 self.refresh = None
2380 2381 @property
2382 - def plots(self):
2383 """dict of plots for this OnlineSummaryTab.""" 2384 return self._plotdict
2385 @plots.setter
2386 - def plots(self, plotdict):
2387 self._plotdict = plotdict 2388 if isinstance(plotdict, dict): 2389 for key,val in plotdict.iteritems(): 2390 for key2,val2 in val.iteritems(): 2391 self._plotdict[key][key2] =\ 2392 map(os.path.normpath(plotdict[key][key2]))
2393 @plots.deleter
2394 - def plots(self):
2395 del self._plotdict
2396
2397 - def add_states(self, statelist):
2398 self.states.append(statelist) 2399 for state in statelist: 2400 if not state in self.plots.keys(): 2401 self.plots[state] = dict()
2402
2403 - def add_plots(self, state, plotlist):
2404 state = SummaryState(str(state)) 2405 if not state in self.states: 2406 self.states.append(state) 2407 self.plots[state] = dict() 2408 for key,plot in plotlist: 2409 if key not in self.sections: 2410 self.sections.append(key) 2411 if key not in self.plots[state].keys(): 2412 self.plots[state][key] = [] 2413 self.plots[state][key].extend(plot.split(','))
2414
2415 - def write_tabs(self):
2416 """ 2417 Write the tabbar used for this OnlineSummaryTab. 2418 """ 2419 self.tabs = markup.page() 2420 self.tabs.ul(class_="buttons") 2421 for i,section in enumerate(sorted(self.sections, key=str.lower)): 2422 self.tabs.li(section, class_="open", onclick="toggleVisible();",\ 2423 id_="button_%s" % _r_cchar.sub("-", section), \ 2424 title="Hide/show %s" % section) 2425 self.tabs.ul.close()
2426
2427 - def finalize(self):
2428 """ 2429 Write the HTML frame for this OnlineSummaryTab. 2430 """ 2431 if not self.tabs: 2432 self.write_tabs() 2433 self.frame = markup.page() 2434 if len(self.states) == 0: 2435 self.frame.p("No online monitors configured.", class_="line") 2436 else: 2437 self.frame.p("This frame shows the current status of this "+\ 2438 "instrument. Select which sections to view using the "+\ 2439 "buttons above.", class_="line", id_="online") 2440 if self.refresh: 2441 self.frame.p("Plots will auto refresh every %d seconds"\ 2442 % self.refresh, class_="line") 2443 for i,state in enumerate(self.states): 2444 style = i==0 and "block" or "none" 2445 self.frame.div(id_="div_%s"\ 2446 % _r_cchar.sub("-", state.name.lower()),\ 2447 style="display: %s;" % style) 2448 for j,section in enumerate(sorted(self.plots[state].keys(), 2449 key=str.lower)): 2450 self.frame.div(class_="toggle_%s"% _r_cchar.sub("-", section),\ 2451 style="display: block;") 2452 self.frame.h2(section, id_="h2_%d" % i, class_="open",\ 2453 onclick="toggleVisible();") 2454 self.frame.div(id_="div_%d" % i, style="display: block;") 2455 c = plotclass(len(self.plots[state][section])) 2456 for k,plot in enumerate(self.plots[state][section]): 2457 self.frame.a(id_="img_%d-%d" % (i,k), href=plot, rel=c) 2458 self.frame.img(src=plot, id_="imd_%d-%d" % (i,k),\ 2459 class_="%s online" % c) 2460 self.frame.a.close() 2461 self.frame.div.close() 2462 self.frame.div.close() 2463 self.frame.div.close() 2464 if self.refresh: 2465 self.frame.script("refreshImages(%s);" % self.refresh, 2466 type="text/javascript")
2467
2468 # ============================================================================= 2469 # Define run state object 2470 # ============================================================================= 2471 2472 -class SummaryState(object):
2473 """ 2474 Object representing a choice of IFO state. 2475 """
2476 - def __init__(self, name):
2477 """ 2478 Define a new SummaryState with the given name 2479 2480 @param name: descriptive name for this SummaryState 2481 @type name: C{str} 2482 @return: a new SummaryState object 2483 @rtype: C{summary.SummaryState} 2484 """ 2485 # set default variables 2486 self.name = name 2487 self.definition = None 2488 self.segments = segments.segmentlist() 2489 self.set = False 2490 self.segment_buffer = 0 2491 self.event_buffer = 0 2492 self.minimum_segment_length = None 2493 2494 # define the tag and match 2495 self.tag = _r_cchar.sub("_", name.lower()).upper() 2496 self.match = re.compile("%s\Z" % self.tag, re.I).match
2497 2498 @property
2499 - def start_time(self):
2500 """GPS start time for this Tab.""" 2501 return self._start_time
2502 @start_time.setter
2503 - def start_time(self, gps):
2504 self._start_time = LIGOTimeGPS(gps)
2505 @start_time.deleter
2506 - def start_time(self):
2507 del self._start_time
2508 2509 @property
2510 - def end_time(self):
2511 """GPS end time for this Tab.""" 2512 return self._end_time
2513 @end_time.setter
2514 - def end_time(self, gps):
2515 self._end_time = LIGOTimeGPS(gps)
2516 @end_time.deleter
2517 - def end_time(self):
2518 del self._end_time
2519 2520 @property
2521 - def span(self):
2522 """GPS [start_time, stop_time) segment for this Tab.""" 2523 return segments.segment(self.start_time, self.end_time)
2524 @span.setter
2525 - def span(self, seg):
2526 self.start_time = LIGOTimeGPS(seg[0]) 2527 self.end_time = LIGOTimeGPS(seg[1])
2528 @span.deleter
2529 - def span(self):
2530 del self._span
2531 2532 @property
2533 - def segments(self):
2534 """glue.segments.segmentlist describing the valid segments for 2535 this SummaryState. 2536 """ 2537 return self._segments
2538 @segments.setter
2539 - def segments(self, seglist):
2540 self._segments =\ 2541 segments.segmentlist([segments.segment(map(float, s))\ 2542 for s in seglist])
2543 @segments.deleter
2544 - def segments(self):
2545 del self._segments
2546
2547 # ============================================================================= 2548 # Help functions 2549 # ============================================================================= 2550 2551 -def calendar_page(startdate, enddate, path=None, jobdir=".",\ 2552 weekday=calendar.MONDAY, ncol=4, reverse=False):
2553 """ 2554 Write an HTML calendar for the given [gpsstart, gpsend) interval. 2555 One table per month, one link per day. 2556 """ 2557 d = datetime.date(startdate.year, 1, 1) 2558 calendar.setfirstweekday(weekday) 2559 2560 if reverse: 2561 m = enddate 2562 else: 2563 m = datetime.date(startdate.year, 1, 1) 2564 2565 page = markup.page() 2566 2567 if not path: 2568 path = os.path.sep 2569 else: 2570 path = os.path.normpath(path) 2571 path = os.path.join(*re.split(os.path.sep, path)[-2:]) 2572 if re.match("\d+/", path): 2573 path = os.path.join(*os.path.split(path)[1:]) 2574 if path == "/": 2575 path = "" 2576 2577 # loop over months 2578 i = 0 2579 if ncol==1: 2580 page.table(class_="calendar") 2581 else: 2582 page.table(class_="calendar year") 2583 while startdate.year <= m.year < enddate.year+1: 2584 # link year 2585 if i==0 or (i % ncol == 0 and ((reverse and m.month==12)\ 2586 or (not reverse and m.month==1))): 2587 page.tr() 2588 Y = m.year 2589 idx = os.path.join(jobdir, "archive_yearly", str(m.year), 2590 path, "index.html") 2591 if os.path.isfile(os.path.expanduser(idx)): 2592 href = "%s%s" % (os.path.split(idx)[0], os.path.sep) 2593 page.th(markup.oneliner.a(Y, href=href), 2594 class_="year", colspan="100%") 2595 else: 2596 page.th(Y, class_="year", colspan="100%") 2597 page.tr.close() 2598 2599 2600 # open new table 2601 if i % ncol == 0: 2602 page.tr() 2603 # get delta 2604 if reverse and m.month == 1: 2605 delta = datetime.timedelta(days=-calendar.monthrange(m.year-1,\ 2606 12)[1]) 2607 elif reverse: 2608 delta = datetime.timedelta(days=-calendar.monthrange(m.year,\ 2609 m.month-1)[1]) 2610 else: 2611 delta = datetime.timedelta(days=calendar.monthrange(m.year,\ 2612 m.month)[1]) 2613 2614 # if month is outside range, make a blank entry for it 2615 if (m.year == startdate.year and m.month < startdate.month)\ 2616 or (m.year == enddate.year and m.month > enddate.month): 2617 if ncol==1: 2618 page.td(str()) 2619 else: 2620 page.td(str(), class_="calendar month") 2621 2622 # other wise make a month table for it 2623 else: 2624 if ncol==1: 2625 page.td() 2626 page.table(class_="calendar") 2627 else: 2628 page.td(class_="calendar month") 2629 page.table(class_="calendar month") 2630 # get month as calendar 2631 month = calendar.monthcalendar(m.year, m.month) 2632 2633 # get month link as table header 2634 page.tr() 2635 H = "%s %s" % (calendar.month_name[m.month], m.year) 2636 href = None 2637 idx = os.path.join(jobdir, "archive_monthly", m.strftime("%Y%m"), 2638 path, "index.html") 2639 if os.path.isfile(os.path.expanduser(idx)): 2640 href = "%s%s" % (os.path.split(idx)[0], os.path.sep) 2641 page.th(markup.oneliner.a(H, class_="day", href=href), 2642 colspan="100%") 2643 else: 2644 page.th(H, colspan="100%") 2645 page.tr.close() 2646 2647 # loop over weeks 2648 for week in month: 2649 page.tr() 2650 for idx,day in enumerate(week): 2651 if day != 0: 2652 break 2653 w = (datetime.date(m.year, m.month, day)\ 2654 - datetime.timedelta(days=idx)).strftime("%Y%m%d") 2655 href = None 2656 idx = os.path.join(jobdir, "archive_weekly", w, 2657 path, "index.html") 2658 if os.path.isfile(os.path.expanduser(idx)): 2659 href = "%s%s" % (os.path.split(idx)[0], os.path.sep) 2660 page.td(markup.oneliner.a("w", class_="day", href=href)) 2661 else: 2662 page.td("w") 2663 2664 for day in week: 2665 # format gaps in tabls 2666 if day == 0: 2667 page.td("") 2668 continue 2669 # find day page and link 2670 d = datetime.date(m.year, m.month, day).strftime("%Y%m%d") 2671 href = None 2672 idx = os.path.join(jobdir, "archive_daily", d, 2673 path, "index.html") 2674 if os.path.isfile(os.path.expanduser(idx)): 2675 href = "%s%s" % (os.path.split(idx)[0], os.path.sep) 2676 page.td(markup.oneliner.a(str(day), class_="day",\ 2677 href=href)) 2678 else: 2679 page.td(str(day)) 2680 page.tr.close() 2681 page.td.close() 2682 page.table.close() 2683 2684 # close row 2685 if i % ncol == ncol -1: 2686 page.tr.close() 2687 2688 # increment 2689 m += delta 2690 i += 1 2691 2692 page.table.close() 2693 return page
2694
2695 -def plotclass(n):
2696 """ 2697 Guess the plot class to use from the number of plots 2698 """ 2699 if n % 5 == 0: 2700 return "fifth" 2701 elif n % 4 == 0: 2702 return "quarter" 2703 elif n % 3 == 0: 2704 return "third" 2705 elif n % 2 == 0: 2706 return "half" 2707 else: 2708 return "full"
2709
2710 2711 -def div(page, id_, header, display=True):
2712 """Write a new <div> tag to the give markup.page object 2713 """ 2714 if isinstance(id_, int): 2715 id_ = str(id_) 2716 elif not isinstance(id_, str): 2717 id_ = ".".join(list(map(str, id_))) 2718 N = len(re.split("\.", id_)) 2719 2720 if N == 1: 2721 title = header 2722 else: 2723 title = "%s %s" % (id_.split(".", 1)[1], header) 2724 2725 if not display: 2726 display = "display: none;" 2727 class_ = "closed" 2728 else: 2729 display = "display: block;" 2730 class_ = "open" 2731 2732 getattr(page, "h%d" % N)(title, onclick=TOGGLE,\ 2733 id_="h%d_%s" % (N, id_.replace(".","-")),\ 2734 class_=class_) 2735 page.div(id_="div_%s" % id_.replace(".","-"), style=display)
2736