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