Belle II Software  release-05-02-19
validationplotuple.py
1 #!/usr/bin/env python3
2 
3 
4 # std
5 import os.path
6 import math
7 import json
8 from typing import List, Optional
9 
10 # 3rd
11 import ROOT
12 
13 # ours
14 import metaoptions
15 import validationcomparison
16 import validationpath
17 from validationfunctions import strip_ext, index_from_revision, get_style
18 import json_objects
19 from validationrootobject import RootObject
20 
21 
22 # todo: [Ref, low prio, medium work] Refactor into class with uniform interface
23 # and subclasses implementing actual functionality for Plot/Tuple etc.
24 # /klieret
25 class Plotuple:
26 
27  """!
28  A Plotuple is either a Plot or an N-Tuple
29 
30  @var work_folder: the work folder containing the results and plots
31  @var root_objects: A list of Root-objects which belong
32  together (i.e. should be drawn into one histogram or one table)
33  @var revisions: The list of revisions
34  @var warnings: A list of warnings that occured while creating the
35  plots/tables for this Plotuple object
36  @var reference: The reference RootObject for this Plotuple
37  @var elements: The elements (RootObject of different revisions) for this
38  Plotuple
39  @var newest: The newest element in elements
40  @var key: The key of the object within the corresponding ROOT file
41  @var type: The type of the elements (TH1, TH2, TNtuple)
42  @var description: The description of this Plotuple object
43  @var check: Hint how the Plotuple object should look like
44  @var contact: The contact person for this Plotuple object
45  @var package: The package to which this Plotuple object belongs to
46  @var rootfile: The rootfile to which the Plotuple object belongs to
47  @var chi2test_result: The result of the Chi^2-Test. By default, there is no
48  such result. If the Chi^2-Test has been performed, this variable holds
49  the information between which objects it has been performed.
50  @var pvalue: The p-value that the Chi^2-Test returned
51  @var file: The file, in which the histogram or the HMTL-table (for
52  n-tuples) are stored (without the file extension!)
53  """
54 
55  def __init__(
56  self,
57  root_objects: List[RootObject],
58  revisions: List[str],
59  work_folder: str
60  ):
61  """!
62  The default constructor for a Plotuple-object.
63  @param root_objects: A list of Root-objects which belong
64  together (i.e. should be drawn into one histogram or one table)
65  @param revisions: The list of revisions (Duh!)
66  """
67 
68  # the work folder containing the results and plots
69  self._work_folder = work_folder
70 
71  # The list of Root objects in this Plotuple-object
72  self._root_objects = root_objects
73 
74  # The list of revisions
75  self._revisions = revisions
76 
77  # A list of all problems that occured with this Plotuple,
78  # e.g. missing reference object, missing meta-information...
79  self.warnings = [] # type: List[str]
80 
81  # Find the reference element. If we can't find one, set it to 'None'
82  # The reference-object for this Plotuple object
83  self._reference = None # type: Optional[RootObject]
84  for root_object in self._root_objects:
85  if root_object.is_reference:
86  self._reference = root_object
87  break
88 
89  # If we couldn't find a reference element, add that to warnings
90  if self._reference is None:
91  self.warnings = ['No reference object']
92 
93  # All elements of the Plotuple that are not the reference-object
94  # Get the elements, i.e. all RootObjects except for the
95  # reference object. May be either histograms or n-tuples.
96  # Note that the reference doesn't have a date set (and if we only plot
97  # the reference, then is_reference is probably not set), so we have
98  # to be careful of how to sort
99  self._elements = [ro for ro in root_objects if not ro.is_reference]
100  self._elements.sort(
101  key=lambda ro: ro.date if ro.date else 0,
102  reverse=True
103  )
104 
105  # The newest element, i.e. the element belonging the revision
106  # whose data were created most recently.
107  # Should always be self.element[0], except if there is only a
108  # reference object
109  if self._elements:
110  self._newest = self._elements[0]
111  else:
112  self._newest = self._reference
113 
114  # All available meta-information about the plotuple object:
115 
116  # The key (more precisely: the name of the key) that all elements of
117  # this Plotuple object share
118  self.key = self._newest.key
119 
120  # The type of the elements in this Plotuple object
121  self.type = self._newest.type
122 
123  if self.type == "TNamed":
124  # Sometimes, we use TNamed to encode extra information about the
125  # ROOT file. In order to avoid that this will be plotted, we
126  # catch it here and assign it the type 'Meta'
127  meta_fields = ["description"]
128  if self._newest.object.GetName().lower().strip() in meta_fields:
129  self.type = "meta"
130 
131  # The description of the histogram/n-tuple which this Plotuple object
132  # will yield
133  self._description = self._newest.description
134 
135  # The 'Check for ...'-guideline for the histogram/n-tuple which this
136  # Plotuple object will yield
137  self._check = self._newest.check
138 
139  # A contact person for the histogram/n-tuple which this Plotuple object
140  # will yield
141  self._contact = self._newest.contact
142 
143  # MetaOptionParser for the meta-options for this Plotuple object
144  self._mop = metaoptions.MetaOptionParser(self._newest.metaoptions)
145 
146  # The package to which the elements in this Plotuple object belong
147  self.package = self._newest.package
148 
149  # The root file to which the elements in this Plotuple object belong
150  self.rootfile = self._newest.rootfile
151 
152  # The result of the Chi^2-Test. By default, there is no such result.
153  # If the Chi^2-Test has been performed, this variable holds between
154  # which objects it has been performed.
155  self._comparison_result_long = 'n/a'
156 
157  self.comparison_result = "not_compared"
158 
159  # The json file, in which the ntuple information is stored
160  self._file = None # type: Optional[str]
161 
162  self._html_content = None # type: Optional[str]
163 
164 
166  self._width = None # type: Optional[int]
167 
168 
170  self._height = None # type: Optional[int]
171 
172  # Deal with incomplete information
173  if self._description == '' or self._description is None:
174  self._description = 'n/a'
175  self.warnings.append('No description')
176  if self._check == '' or self._check is None:
177  self._check = 'n/a'
178  self.warnings.append('No Check')
179  if self._contact == '' or self._contact is None:
180  self._contact = 'n/a'
181  self.warnings.append('No Contact Person')
182 
183 
186  self._plot_folder = os.path.join(
188  work_folder, tags=revisions
189  ),
190  self.package
191  )
192  os.makedirs(self._plot_folder, exist_ok=True)
193 
194  def has_reference(self):
195  """!
196  @return True if a reference file is found for this plotuple
197  """
198  return self._reference is not None
199 
200  def create_plotuple(self):
201  """!
202  Creates the histogram/table/image that belongs to this Plotuble-object.
203  """
204 
205  if self.type == 'TH1' or self.type == 'TEfficiency':
206  self.create_histogram_plot('1D')
207  elif self.type == 'TGraph':
208  self.create_graph_plot()
209  elif self.type == 'TH2':
210  self.create_histogram_plot('2D')
211  # used to store HTML user content
212  elif self.type == 'TNamed':
213  self.create_html_content()
214  elif self.type == 'TASImage':
215  self.create_image_plot()
216  elif self.type == 'TNtuple':
217  self.create_ntuple_table_json()
218  elif self.type == "meta":
219  pass
220  else:
221  raise ValueError('Tried to create histogram/n-tuple, but received'
222  'invalid type')
223 
224  def is_expert(self):
225  """!
226  @return Returns true if this plotuple has the expert option
227  """
228  return not self._mop.has_option("shifter")
229 
230  def perform_comparison(self):
231  """!
232  Takes the reference (self.reference.object) and the newest revision
233  (self.newest.object) and a canvas. Performs a comparison of the
234  two objects.
235  @return: None
236  """
237 
238  tester = validationcomparison.get_comparison(
239  self._reference.object,
240  self._newest.object,
241  self._mop
242  )
243 
244  self._comparison_result_long = tester.comparison_result_long.format(
245  revision1=self._reference.revision,
246  revision2=self._newest.revision
247  )
248  self.comparison_result = tester.comparison_result
249 
250  def _set_background(self, canvas):
251 
252  # kRed #FF0000 Red
253  # kRed - 9 #FF9999 Sweet pink
254 
255  # kOrange + 1 #FF9832 Sun
256  # kOrange - 9 #FFCC9A Manhattan
257 
258  # kGreen - 3 #33CC33 Lime green
259  # kGreen - 10 #CCFFCC Chinook
260 
261  # kAzure #0032FE Medium blue
262  # kAzure - 2 #3265FE Medium slate blue
263  # kAzure - 9 #98CBFF Jordy blue
264 
265  colors = {
266  "error": ROOT.kRed,
267  "warning": ROOT.kOrange + 1,
268  "equal": ROOT.kGreen - 3,
269  "not_compared": ROOT.kAzure - 2
270  }
271  colors_expert = {
272  "error": ROOT.kRed - 9,
273  "warning": ROOT.kOrange - 9,
274  "equal": ROOT.kGreen - 10,
275  "not_compared": ROOT.kAzure - 9
276  }
277 
278  if self.is_expert():
279  color = colors_expert[self.comparison_result]
280  else:
281  color = colors[self.comparison_result]
282 
283  canvas.SetFillColor(color)
284  canvas.GetFrame().SetFillColor(ROOT.kWhite)
285 
286  def _draw_ref(self, canvas):
287  """!
288  Takes the reference RootObject (self.reference.object)
289  and a (sub)canvas and plots it with the correct line-style etc.
290  @param canvas: Reference to the canvas on which we will draw the
291  reference object.
292  @return. None
293  """
294  self._remove_stats_tf1(self._reference.object)
295 
296  # Line is thick and black
297  self._reference.object.SetLineColor(ROOT.kBlack)
298  self._reference.object.SetLineWidth(2)
299  self._reference.object.SetLineStyle(1)
300 
301  # Area under the curve is solid gray
302  self._reference.object.SetFillColor(ROOT.kGray)
303  self._reference.object.SetFillStyle(1001)
304 
305  # Draw the reference on the canvas
306  self._draw_root_object(
307  self.type,
308  self._reference.object,
309  self._reference.object.GetOption()
310  )
311  canvas.Update()
312  canvas.GetFrame().SetFillColor(ROOT.kWhite)
313 
314  @staticmethod
315  def _remove_stats_tf1(obj):
316  # removed TF1s which might have been added by validation scripts
317  # in tracking/scripts/tracking/validation/plot.py:1597
318  tf1 = obj.FindObject("FitAndStats")
319  if tf1:
320  function_list = obj.GetListOfFunctions()
321  function_list.Remove(tf1)
322 
323  # TODO: is this actually used or can it be removed ?
324  def create_image_plot(self):
325  """!
326  Creates image plot for TASImage-objects.
327  @return: None
328  """
329 
330  # Create a ROOT canvas on which we will draw our histograms
331  self._width = 700
332  if len(self._elements) > 4:
333  canvas = ROOT.TCanvas('', '', 700, 1050)
334  self._height = 1050
335  else:
336  canvas = ROOT.TCanvas('', '', 700, 525)
337  self._height = 525
338 
339  # Split the canvas into enough parts to fit all image_objects
340  # Find numbers x and y so that x*y = N (number of histograms to be
341  # plotted), and x,y close to sqrt(N)
342 
343  if len(self._root_objects) == 1:
344  x = y = 1
345  elif len(self._root_objects) == 2:
346  x = 2
347  y = 1
348  else:
349  x = 2
350  y = int(math.floor((len(self._root_objects) + 1) / 2))
351 
352  # Actually split the canvas and go to the first pad ('sub-canvas')
353  canvas.Divide(x, y)
354  pad = canvas.cd(1)
355  pad.SetFillColor(ROOT.kWhite)
356 
357  # If there is a reference object, plot it first
358  if self._reference is not None:
359  self._draw_ref(pad)
360 
361  # Now draw the normal plots
362  items_to_plot_count = len(self._elements)
363  for plot in reversed(self._elements):
364 
365  # Get the index of the current plot
366  index = index_from_revision(plot.revision, self._work_folder)
367  style = get_style(index, items_to_plot_count)
368 
369  self._remove_stats_tf1(plot.object)
370 
371  # Set line properties accordingly
372  plot.object.SetLineColor(style.GetLineColor())
373  plot.object.SetLineWidth(style.GetLineWidth())
374  plot.object.SetLineStyle(style.GetLineStyle())
375 
376  # Switch to the correct sub-panel of the canvas. If a ref-plot
377  # exists, we have to go one panel further compared to the
378  # no-ref-case
379  if self._reference is not None:
380  i = 2
381  else:
382  i = 1
383 
384  pad = canvas.cd(self._elements.index(plot) + i)
385  pad.SetFillColor(ROOT.kWhite)
386 
387  # Draw the reference on the canvas
388  self._draw_root_object(
389  self.type, plot.object,
390  plot.object.GetOption()
391  )
392  pad.Update()
393  pad.GetFrame().SetFillColor(ROOT.kWhite)
394 
395  # Write the title in the correct color
396  title = pad.GetListOfPrimitives().FindObject('title')
397  if title:
398  title.SetTextColor(style.GetLineColor())
399 
400  # Save the plot as PNG and PDF
401  canvas.Print(os.path.join(self._plot_folder, self.get_png_filename()))
402  canvas.Print(os.path.join(self._plot_folder, self.get_pdf_filename()))
403 
404  self._file = os.path.join(
405  self._plot_folder,
406  "{}_{}".format(
407  strip_ext(self.rootfile),
408  self.key
409  )
410  )
411 
412  def get_png_filename(self):
413  return '{}_{}.png'.format(strip_ext(self.rootfile), self.key)
414 
415  def get_pdf_filename(self):
416  return '{}_{}.pdf'.format(strip_ext(self.rootfile), self.key)
417 
418  @staticmethod
419  def _draw_root_object(typ, obj, options):
420  """
421  Special handling of the ROOT Draw calls, as some
422  ROOT objects have a slightly differen flavour.
423  """
424 
425  if typ == 'TEfficiency' or typ == "TGraph":
426  # TEff does not provide DrawCopy
427  obj.Draw(options)
428  else:
429  obj.DrawCopy(options)
430 
431  def create_histogram_plot(self, mode):
432  """!
433  Plots all histogram-objects in this Plotuple together in one histogram,
434  which is then given the name of the key.
435  @param mode: Determines whether it is a one- or
436  two-dimensional histogram.
437  Accepted values are '1D' and '2D'
438  @return: None
439  """
440 
441  # If we don't get a valid 'mode', we can stop right here
442  if mode not in ['1D', '2D']:
443  return
444 
445  # Create a ROOT canvas on which we will draw our histograms
446  self._width = 700
447  if mode == '2D' and len(self._elements) > 4:
448  self._height = 1050
449  else:
450  self._height = 525
451  canvas = ROOT.TCanvas('', '', self._width, self._height)
452 
453  # Allow possibility to turn off the stats box
454  if self._mop.has_option('nostats'):
455  ROOT.gStyle.SetOptStat("")
456  else:
457  ROOT.gStyle.SetOptStat("nemr")
458 
459  # If there is a reference object, and the list of plots is not empty,
460  # perform a Chi^2-Test on the reference object and the first object in
461  # the plot list:
462  if self._reference is not None and self._newest \
463  and not self._reference == self._newest:
464  self.perform_comparison()
465 
466  # A variable which holds whether we
467  # have drawn on the canvas already or not
468  # (only used for the 1D case)
469  drawn = False
470 
471  # Now we distinguish between 1D and 2D histograms
472  # If we have a 1D histogram
473  if mode == '1D':
474 
475  if not self._mop.has_option('nogrid'):
476  canvas.SetGrid()
477  if self._mop.has_option('logx'):
478  canvas.SetLogx()
479  if self._mop.has_option('logy'):
480  canvas.SetLogy()
481 
482  # If there is a reference object, plot it first
483  if self._reference is not None:
484  self._draw_ref(canvas)
485  drawn = True
486 
487  # If we have a 2D histogram
488  elif mode == '2D':
489 
490  # Split the canvas into enough parts to fit all histogram_objects
491  # Find numbers x and y so that x*y = N (number of histograms to be
492  # plotted), and x,y close to sqrt(N)
493 
494  if len(self._root_objects) == 1:
495  x = y = 1
496  elif len(self._root_objects) == 2:
497  x = 2
498  y = 1
499  else:
500  x = 2
501  y = int(math.floor((len(self._root_objects) + 1) / 2))
502 
503  # Actually split the canvas and go to the first pad ('sub-canvas')
504  canvas.Divide(x, y)
505  pad = canvas.cd(1)
506  pad.SetFillColor(ROOT.kWhite)
507 
508  # If there is a reference object, plot it first
509  if self._reference is not None:
510  self._draw_ref(pad)
511 
512  items_to_plot_count = len(self._elements)
513  # Now draw the normal plots
514  for plot in reversed(self._elements):
515 
516  # Get the index of the current plot
517  index = index_from_revision(plot.revision, self._work_folder)
518  style = get_style(index, items_to_plot_count)
519 
520  self._remove_stats_tf1(plot.object)
521 
522  # Set line properties accordingly
523  plot.object.SetLineColor(style.GetLineColor())
524  plot.object.SetLineWidth(style.GetLineWidth())
525  plot.object.SetLineStyle(style.GetLineStyle())
526 
527  # If we have a one-dimensional histogram
528  if mode == '1D':
529  if not drawn:
530  # Get additional options for 1D histograms
531  # (Intersection with self.metaoptions)
532  additional_options = ['C']
533  additional_options = [
534  option for option in additional_options
535  if self._mop.has_option(option)
536  ]
537  options_str = plot.object.GetOption() + \
538  ' '.join(additional_options)
539  drawn = True
540  else:
541  options_str = "SAME"
542 
543  self._draw_root_object(self.type, plot.object, options_str)
544 
545  # redraw grid ontop of histogram, if selected
546  if not self._mop.has_option('nogrid'):
547  canvas.RedrawAxis("g")
548 
549  canvas.Update()
550  canvas.GetFrame().SetFillColor(ROOT.kWhite)
551 
552  # If we have a two-dimensional histogram
553  elif mode == '2D':
554  # Switch to the correct sub-panel of the canvas. If a ref-plot
555  # exists, we have to go one panel further compared to the
556  # no-ref-case
557  if self._reference is not None:
558  i = 2
559  else:
560  i = 1
561 
562  pad = canvas.cd(self._elements.index(plot) + i)
563  pad.SetFillColor(ROOT.kWhite)
564 
565  # Get additional options for 2D histograms
566  additional_options = ''
567  for _ in ['col', 'colz', 'cont', 'contz', 'box']:
568  if self._mop.has_option(_):
569  additional_options += ' ' + _
570 
571  # Draw the reference on the canvas
572  self._draw_root_object(
573  self.type,
574  plot.object,
575  plot.object.GetOption() + additional_options
576  )
577  pad.Update()
578  pad.GetFrame().SetFillColor(ROOT.kWhite)
579 
580  # Write the title in the correct color
581  title = pad.GetListOfPrimitives().FindObject('title')
582  if title:
583  title.SetTextColor(style.GetLineColor())
584 
585  if self._newest:
586  # if there is at least one revision
587  self._set_background(canvas)
588 
589  canvas.GetFrame().SetFillColor(ROOT.kWhite)
590 
591  # Save the plot as PNG and PDF
592  canvas.Print(os.path.join(self._plot_folder, self.get_png_filename()))
593  canvas.Print(os.path.join(self._plot_folder, self.get_pdf_filename()))
594 
595  self._file = os.path.join(
596  self._plot_folder,
597  "{}_{}".format(
598  strip_ext(self.rootfile),
599  self.key
600  )
601  )
602 
603  def create_graph_plot(self):
604  """!
605  Plots as TGraph/TGraphErrors
606  @return: None
607  """
608 
609  # Create a ROOT canvas on which we will draw our plots
610  self._width = 700
611  self._height = 525
612  canvas = ROOT.TCanvas('', '', self._width, self._height)
613 
614  # Allow possibility to turn off the stats box
615  if self._mop.has_option('nostats'):
616  ROOT.gStyle.SetOptStat("")
617  else:
618  ROOT.gStyle.SetOptStat("nemr")
619 
620  # If there is a reference object, and the list of plots is not empty,
621  # perform a Chi^2-Test on the reference object and the first object in
622  # the plot list:
623  if self._reference is not None and self._newest \
624  and not self._reference == self._newest:
625  self.perform_comparison()
626 
627  if not self._mop.has_option('nogrid'):
628  canvas.SetGrid()
629  if self._mop.has_option('logx'):
630  canvas.SetLogx()
631  if self._mop.has_option('logy'):
632  canvas.SetLogy()
633 
634  # A variable which holds whether we
635  # have drawn on the canvas already or not
636  drawn = False
637 
638  # If there is a reference object, plot it first
639  if self._reference is not None:
640  self._draw_ref(canvas)
641  drawn = True
642 
643  items_to_plot_count = len(self._elements)
644  # Now draw the normal plots
645  for plot in reversed(self._elements):
646 
647  # Get the index of the current plot
648  index = index_from_revision(plot.revision, self._work_folder)
649  style = get_style(index, items_to_plot_count)
650 
651  # self.remove_stats_tf1(plot.object)
652 
653  # Set line properties accordingly
654  plot.object.SetLineColor(style.GetLineColor())
655  plot.object.SetLineWidth(style.GetLineWidth())
656  plot.object.SetLineStyle(style.GetLineStyle())
657 
658  # If we have a one-dimensional histogram
659  if not drawn:
660 
661  # todo: refactor like in plot hist
662  # Get additional options for 1D histograms
663  additional_options = ''
664  for _ in ['C']:
665  if self._mop.has_option(_):
666  additional_options += ' ' + _
667 
668  # Draw the reference on the canvas
669  self._draw_root_object(
670  self.type,
671  plot.object,
672  plot.object.GetOption() + additional_options
673  )
674  drawn = True
675  else:
676  self._draw_root_object(self.type, plot.object, "SAME")
677 
678  # redraw grid ontop of histogram, if selected
679  if not self._mop.has_option('nogrid'):
680  canvas.RedrawAxis("g")
681 
682  canvas.Update()
683  canvas.GetFrame().SetFillColor(ROOT.kWhite)
684 
685  if self._newest:
686  # if there is at least one revision
687  self._set_background(canvas)
688 
689  # Save the plot as PNG and PDF
690  canvas.Print(os.path.join(self._plot_folder, self.get_png_filename()))
691  canvas.Print(os.path.join(self._plot_folder, self.get_pdf_filename()))
692 
693  self._file = os.path.join(
694  self._plot_folder,
695  "{}_{}".format(
696  strip_ext(self.rootfile),
697  self.key
698  )
699  )
700 
701  def create_html_content(self):
702 
703  # self.elements
704  self._html_content = ""
705 
706  for elem in self._elements:
707  self._html_content += "<p>" + \
708  elem.revision + \
709  "</p>" + \
710  elem.object.GetTitle()
711 
712  # there is no file storing this, because it is directly in the json
713  # file
714  self._file = None
715 
716  def get_meta_information(self):
717  assert self.type == "meta"
718  key = self._newest.object.GetName().strip().lower()
719  value = self._newest.object.GetTitle()
720  return key, value
721 
722  def create_ntuple_table_json(self):
723  """!
724  If the Plotuple-object contains n-tuples, this will create the
725  a JSON file, which is later converted to HTML by the javascript
726  function fillNtupleTable.
727  """
728 
729  json_nutple = {}
730 
731  # The dictionary will have the following form
732  # {
733  # "reference (if exist)": [
734  # ('variable 1', 'reference value for variable 1'),
735  # ('variable 2', 'reference value for variable 2'),
736  # ...
737  # ],
738  # "revision": [
739  # ...
740  # ]
741  # }
742 
743  precision = self._mop.int_value("float-precision", default=4)
744  format_str = "{{0:.{}f}}".format(precision)
745 
746  def value2str(obj):
747  # assuming that I have a float
748  return format_str.format(obj)
749 
750  colum_names = []
751  for key in list(self._newest.object.keys()):
752  colum_names.append(key)
753 
754  # If there is a reference object, print the reference values as the
755  # first row of the table
756  if self._reference and 'reference' in self._revisions:
757  json_nutple['reference'] = []
758 
759  key_list = list(self._reference.object.keys())
760  for column in colum_names:
761  if column in key_list:
762  value_str = value2str(self._reference.object[column])
763  json_nutple['reference'].append(
764  (column, value_str)
765  )
766  else:
767  json_nutple['reference'].append((column, None))
768 
769  # Now print the values for all other revisions
770  for ntuple in self._elements:
771  if ntuple.revision not in json_nutple:
772  json_nutple[ntuple.revision] = []
773 
774  for column in colum_names:
775  if column in ntuple.object:
776  value_str = value2str(ntuple.object[column])
777  json_nutple[ntuple.revision].append(
778  (column, value_str)
779  )
780  else:
781  json_nutple[ntuple.revision].append((column, None))
782 
783  json_ntuple_file = os.path.join(
784  self._plot_folder,
785  "{}_{}.json".format(
786  strip_ext(self.rootfile),
787  self.key
788  )
789  )
790 
791  with open(json_ntuple_file, 'w+') as json_file:
792  json.dump(json_nutple, json_file)
793 
794  self._file = json_ntuple_file
795 
796  def get_plot_title(self):
797  if self._file:
798  return os.path.basename(self._file).replace(".", "_").strip()
799  else:
800  # this is for html content which is not stored in any file
801  return self.key
802 
803  def create_json_object(self):
804  if self.type == 'TNtuple':
806  title=self.get_plot_title(),
807  description=self._description,
808  contact=self._contact,
809  check=self._check,
810  is_expert=self.is_expert(),
811  json_file_path=os.path.relpath(
812  self._file,
813  validationpath.get_html_folder(self._work_folder)
814  ),
815  )
816  elif self.type == 'TNamed':
818  title=self.get_plot_title(),
819  description=self._description,
820  contact=self._contact,
821  check=self._check,
822  is_expert=self.is_expert(),
823  html_content=self._html_content
824  )
825  elif self.type == "meta":
826  return None
827  else:
829  title=self.get_plot_title(),
830  comparison_result=self.comparison_result,
831  comparison_text=self._comparison_result_long,
832  description=self._description,
833  contact=self._contact,
834  check=self._check,
835  height=self._height,
836  width=self._width,
837  is_expert=self.is_expert(),
838  plot_path=os.path.relpath(
839  self._plot_folder,
840  validationpath.get_html_folder(self._work_folder)
841  ) + "/",
842  png_filename=self.get_png_filename(),
843  pdf_filename=self.get_pdf_filename(),
844  warnings=self.warnings
845  )
json_objects.ComparisonNTuple
Definition: json_objects.py:400
json_objects.ComparisonPlot
Definition: json_objects.py:341
validationpath.get_html_folder
def get_html_folder(output_base_dir)
Return the absolute path to the results folder.
Definition: validationpath.py:77
validationpath.get_html_plots_tag_comparison_folder
def get_html_plots_tag_comparison_folder(output_base_dir, tags)
Return the absolute path to the results folder.
Definition: validationpath.py:91
json_objects.ComparisonHtmlContent
Definition: json_objects.py:433
metaoptions.MetaOptionParser
Definition: metaoptions.py:8