Belle II Software  release-08-01-10
json_objects.py
1 
8 import json
9 import enum
10 import functools
11 
12 # todo: shouldn't I call super().__init__() or similar to make sure that I
13 # execute code from mother classes?? This seems to only have been done for
14 # some of the subclasses here...
15 
16 """
17 Define datatypes for later serialization by json
18 """
19 
20 # todo: write a short overview over the many classes and their relationships here
21 
22 # ==============================================================================
23 # Data classes
24 # ==============================================================================
25 
26 
27 class JsonBase:
28 
29  """
30  Base object for all json-serializable objects of the validation suite
31  """
32 
33 
35 
36  """
37  Contains information about a specific revision
38  """
39 
40  def __init__(
41  self,
42  label,
43  git_hash=None,
44  creation_date=None,
45  packages=None,
46  creation_timezone=None,
47  ):
48  """
49  Create a new Revision object and fill all members
50  """
51 
52 
53  self.labellabel = label
54 
55 
57  self.creation_datecreation_date = creation_date
58 
59 
60  self.creation_timezonecreation_timezone = creation_timezone
61 
62 
64  self.git_hashgit_hash = git_hash
65 
66 
68  self.most_recentmost_recent = False
69 
70 
71  self.packagespackages = [] if (packages is None) else packages
72 
73 
75 
76  """
77  Container for a list of revisions
78  """
79 
80  def __init__(self, revisions):
81  """
82  Create a new Revisions object and fill all members
83  """
84 
85 
86  self.revisionsrevisions = revisions
87 
88 
90 
91  """
92  Contains information about a script and its execution output
93  """
94 
95  def __init__(self, name, path, status, log_url=None, return_code=None, input=None, output=None):
96  """
97  Create a new Script object and fill all members
98  """
99 
100 
101  self.namename = name
102 
103  self.pathpath = path
104 
107  self.statusstatus = status
108 
110  self.log_urllog_url = log_url
111 
113  self.return_codereturn_code = return_code
114 
116  self.inputinput = input
117 
119  self.outputoutput = output
120 
121 
123 
124  """
125  Wrapper for a file containing a set of plots, only
126  root files up to now
127  """
128 
129  def __init__(self, package, title, rootfile, plots, description=""):
130  """
131  Create a new PlotFile object and fill all members
132  """
133 
134 
135  self.packagepackage = package
136 
137  self.titletitle = title
138 
139  self.rootfilerootfile = rootfile
140 
141  self.plotsplots = plots
142 
143  self.descriptiondescription = description
144 
145  self.n_shifter_plotsn_shifter_plots = sum([not plot.is_expert for plot in self.plotsplots])
146 
147 
148 class Plot(JsonBase):
149 
150  """
151  Wrapper for one specfic plot.
152  """
153 
154  def __init__(
155  self,
156  is_expert=False,
157  description=None,
158  check=None,
159  contact=None,
160  width=None,
161  height=None,
162  issue=None,
163  ):
164  """
165  Create a new Plot object and fill all members
166  """
167 
168 
169  self.is_expertis_expert = is_expert
170 
171  self.descriptiondescription = description
172 
173  self.checkcheck = check
174 
175  self.contactcontact = contact
176 
177  self.widthwidth = width
178 
179  self.heightheight = height
180 
181  if not issue:
182  issue = []
183  self.issueissue = issue
184 
185 
187 
188  """
189  Wrapper for NTuple lists. This is not a graphical plot, but a list of
190  values
191  """
192 
193  def __init__(self, is_expert=False, description=None, check=None):
194  """
195  Create a new NTuple object and fill all members
196  """
197 
198 
199  self.is_expertis_expert = is_expert
200 
201  self.descriptiondescription = description
202 
203  self.checkcheck = check
204 
205 
207 
208  """
209  Wrapper for user HTML Content. This is not a graphical plot but HTML
210  code which will be directly output on the validation website.
211 
212  """
213 
214  def __init__(self, is_expert=False, description=None, check=None):
215  """
216  Create a new NTuple object and fill all members
217  """
218 
219 
220  self.is_expertis_expert = is_expert
221 
222  self.descriptiondescription = description
223 
224  self.checkcheck = check
225 
226 
228 
229  """
230  One high-level package of the validation suites which contains a set of
231  scripts and output plot files
232  """
233 
234  def __init__(self, name, plotfiles=None, scriptfiles=None, fail_count=0):
235  """
236  Create a new NTuple object and fill all members
237  """
238 
239  if not plotfiles:
240  plotfiles = []
241  if not scriptfiles:
242  scriptfiles = []
243 
244 
245  self.namename = name
246 
247  self.plotfilesplotfiles = plotfiles
248 
249  self.scriptfilesscriptfiles = scriptfiles
250 
251  self.visiblevisible = True
252 
253  self.fail_countfail_count = fail_count
254 
255 
256 class ComparisonState(enum.Enum):
257 
258  """
259  Enum to classify the comparison result of two validation plots
260  """
261 
262 
264  NotCompared = "not_compared"
265 
267  NotSupported = "not_supported"
268 
270  FailureTechnical = "technical_failure"
271 
273  FailureStastical = "statistical_failure"
274 
276  Equal = "equal"
277 
278 
280 
281  """
282  Contains the comparison result of two plots
283  """
284 
285  def __init__(self, state, chi2):
286  """
287  Create a new ComparisonResult object and fill all members
288  """
289 
290 
291  self.statestate = state
292 
293  self.chi2chi2 = chi2
294 
295 
297 
298  """
299  Contains information about a file containing plots and the comparison which
300  have been performed for the content of this file
301  """
302 
303  def __init__(
304  self,
305  package,
306  title,
307  rootfile,
308  compared_revisions=None,
309  plots=None,
310  has_reference=False,
311  ntuples=None,
312  html_content=None,
313  description=None,
314  ):
315  """
316  Create a new ComparisonPlotFile object and fill all members
317  """
318 
319  if not plots:
320  plots = []
321  if not ntuples:
322  ntuples = []
323  if not html_content:
324  html_content = []
325 
326  super().__init__(
327  package, title, rootfile, plots, description=description
328  )
329 
330  self.compared_revisioncompared_revision = compared_revisions
331 
332  self.ntuplesntuples = ntuples
333 
334  self.html_contenthtml_content = html_content
335 
336 
337  self.has_referencehas_reference = has_reference
338 
339 
340  self.comparison_errorcomparison_error = len(
341  [plt for plt in self.plotsplots if plt.comparison_result == "error"]
342  )
343 
344  self.comparison_error_shiftercomparison_error_shifter = len(
345  [
346  plt
347  for plt in self.plotsplots
348  if (not plt.is_expert) and plt.comparison_result == "error"
349  ]
350  )
351 
352  self.comparison_warningcomparison_warning = len(
353  [plt for plt in self.plotsplots if plt.comparison_result == "warning"]
354  )
355 
357  self.comparison_warning_shiftercomparison_warning_shifter = len(
358  [
359  plt
360  for plt in self.plotsplots
361  if (not plt.is_expert) and plt.comparison_result == "warning"
362  ]
363  )
364 
365 
366  self.n_shifter_ntuplesn_shifter_ntuples = sum(
367  [not tuple.is_expert for tuple in self.ntuplesntuples]
368  )
369 
370 
371  self.show_shiftershow_shifter = bool(
372  self.n_shifter_plotsn_shifter_plots or self.n_shifter_ntuplesn_shifter_ntuples or self.html_contenthtml_content
373  )
374 
375 
377 
378  """
379  One individual plot including its comparison outcome.
380  """
381 
382  def __init__(
383  self,
384  title,
385  comparison_result=None,
386  png_filename=None,
387  pdf_filename=None,
388  contact=None,
389  description=None,
390  check=None,
391  is_expert=None,
392  plot_path=None,
393  comparison_text=None,
394  height=None,
395  width=None,
396  warnings=None,
397  ):
398  """
399  Create a new ComparisonPlot object and fill all members
400  """
401 
402  # todo: move more into the base class
403  super().__init__(
404  is_expert=is_expert,
405  description=description,
406  check=check,
407  contact=contact,
408  height=height,
409  width=width,
410  )
411 
412  self.titletitle = title
413 
414 
415  self.comparison_resultcomparison_result = comparison_result
416 
417 
418  self.comparison_textcomparison_text = comparison_text
419 
420 
421  self.png_filenamepng_filename = png_filename
422 
423 
424  self.pdf_filenamepdf_filename = pdf_filename
425 
426 
428  self.plot_pathplot_path = plot_path
429 
430 
431  if warnings is None:
432  warnings = []
433  self.warningswarnings = warnings
434 
435 
437 
438  """
439  Comparison outcome for NTuples
440  """
441 
442  def __init__(
443  self,
444  title,
445  contact=None,
446  description=None,
447  check=None,
448  is_expert=None,
449  json_file_path=None,
450  ):
451  """
452  Create a new ComparisonNTuple object and fill all members
453  """
454 
455  # todo: move more into the base class
456  super().__init__(
457  is_expert=is_expert, description=description, check=check
458  )
459 
460  self.titletitle = title
461 
462  self.contactcontact = contact
463 
465  self.json_file_pathjson_file_path = json_file_path
466 
467 
469 
470  """
471  Compiled HTLM Content
472  """
473 
474  def __init__(
475  self,
476  title,
477  contact=None,
478  description=None,
479  check=None,
480  is_expert=None,
481  html_content=None,
482  ):
483  """
484  Create a new ComparisonNTuple object and fill all members
485  """
486 
487  # todo: move more into the base class
488  super().__init__(
489  is_expert=is_expert, description=description, check=check
490  )
491 
492  self.titletitle = title
493 
494  self.contactcontact = contact
495 
497  self.html_contenthtml_content = html_content
498 
499 
501 
502  """
503  Information about a Package which was used in a comparison operation
504  """
505 
506  def __init__(
507  self, name, plotfiles=None, scriptfiles=None, ntuplefiles=None
508  ):
509  """
510  Create a new ComparisonPackage object and fill all members
511  """
512 
513  if not plotfiles:
514  plotfiles = []
515  if not scriptfiles:
516  scriptfiles = []
517  if not ntuplefiles:
518  ntuplefiles = []
519 
520  super().__init__(name, plotfiles=plotfiles, scriptfiles=scriptfiles)
521 
522 
523  self.comparison_errorcomparison_error = sum([pf.comparison_error for pf in plotfiles])
524 
525  self.comparison_error_shiftercomparison_error_shifter = sum(
526  [pf.comparison_error_shifter for pf in plotfiles]
527  )
528 
529  self.comparison_warningcomparison_warning = sum(
530  [pf.comparison_warning for pf in plotfiles]
531  )
532 
534  self.comparison_warning_shiftercomparison_warning_shifter = sum(
535  [pf.comparison_warning_shifter for pf in plotfiles]
536  )
537 
538 
540 
541  """
542  Revision information enriched by the information gained during
543  a comparison.
544  """
545 
546  def __init__(self, label, git_hash=None, creation_date=None, color=None):
547  """
548  Create a new ComparisonRevision object and fill all members
549  """
550 
551  # todo: creation_date
552  super().__init__(label, git_hash=git_hash, creation_date=None)
553 
555  self.colorcolor = color
556 
557 
559 
560  """
561  Contains information and plots generated for comparisons
562  between revisions
563  """
564 
565  def __init__(self, revisions=None, packages=None):
566  """
567  Create a new ComparisonRevision object and fill all members
568  """
569 
570  if not revisions:
571  revisions = []
572  if not packages:
573  packages = []
574 
575 
576  self.revisionsrevisions = revisions
577 
578  self.packagespackages = packages
579  sorted_revs = sorted(revisions, key=lambda x: x.label)
580 
581  self.labellabel = functools.reduce(
582  lambda x, y: x + "_" + y.label, sorted_revs, ""
583  )[1:]
584 
585 
586 # ==============================================================================
587 # Functions
588 # ==============================================================================
589 
590 
591 def dump(file_name, obj):
592  """
593  Output a tree of objects into a json file
594  """
595 
596  with open(file_name, "w+") as f:
597  json.dump(dump_rec(obj), f, sort_keys=True, indent=4)
598 
599 
600 def dumps(obj):
601  """
602  Convert a tree of python objects into a json file
603  """
604 
605  kk = dump_rec(obj)
606  return json.dumps(kk, sort_keys=True, indent=4)
607 
608 
609 def dump_rec(top_object):
610  """
611  Recursive generating of dictionary from a tree
612  of JsonBase objects
613  """
614 
615  this_dict = {}
616  # iterate through the dictionary of the top object
617  for (k, v) in top_object.__dict__.items():
618 
619  # list which needs special handing ?
620  if isinstance(v, list):
621  obj_list = []
622  for it in v:
623  # one of our object's in the list, which needs
624  # special treatment?
625  if isinstance(it, JsonBase):
626  # yen, recurse in to the object
627  obj_list.append(dump_rec(it))
628  else:
629  # no, just add value to list
630  obj_list.append(it)
631 
632  # store compiled list with corresponding key
633  this_dict[k] = obj_list
634  # one of our objects, which might be nested and
635  # needs special treatment ?
636  elif isinstance(v, JsonBase):
637  this_dict[k] = dump_rec(v)
638  # treat enum classes
639  elif isinstance(v, enum.Enum):
640  this_dict[k] = v.value
641  # regular value, just store with correct key
642  else:
643  this_dict[k] = v
644  return this_dict
title
Text used as title for the ntuple item.
def __init__(self, title, contact=None, description=None, check=None, is_expert=None, html_content=None)
contact
name of contact person
html_content
path to the json file which contains the individual numbers of the ntuple
title
Text used as title for the ntuple item.
contact
name of contact person
json_file_path
path to the json file which contains the individual numbers of the ntuple (must be relative to html d...
def __init__(self, title, contact=None, description=None, check=None, is_expert=None, json_file_path=None)
comparison_warning_shifter
the number of comparisons of shifter plots which resulted in a warning
comparison_error
the number of failed comparisons in this package
def __init__(self, name, plotfiles=None, scriptfiles=None, ntuplefiles=None)
comparison_error_shifter
the number of failed comparisons of shifter plots in this package
comparison_warning
the number of comparisons which resulted in a warning
has_reference
true if a reference file is available for this plot file
def __init__(self, package, title, rootfile, compared_revisions=None, plots=None, has_reference=False, ntuples=None, html_content=None, description=None)
comparison_warning_shifter
the number of comparisons of shifter plots in this file which resulted in a warning
comparison_error
the number of failed comparisons in this file
show_shifter
Show to shifter, i.e.
comparison_error_shifter
the number of failed comparisons of shifter plots in this file
ntuples
the ntuples which were compared
comparison_warning
the number of comparisons which resulted in a warning
compared_revision
label of the revision which were used in this comparison
html_content
user's html content
n_shifter_ntuples
Number of shifter ntuples.
title
tile used to display this plot
pdf_filename
the filename of the pdf file plotted with the comparison graphs
warnings
Warnings ("no contact" person etc.)
comparison_result
text string for the comparison outcome
plot_path
path were the png and pdf files are located (relative to the html directory; has to end with trailing...
def __init__(self, title, comparison_result=None, png_filename=None, pdf_filename=None, contact=None, description=None, check=None, is_expert=None, plot_path=None, comparison_text=None, height=None, width=None, warnings=None)
png_filename
the filename of the png file plotted with the comparison graphs
comparison_text
verbose text describing the outcome of the comparison
chi2
the chi2 value computed during the comparison
state
a string containing a description of the comparison's outcome
def __init__(self, state, chi2)
color
the color which was used for this revision in the comparison plots
def __init__(self, label, git_hash=None, creation_date=None, color=None)
label
the unique label of this comparison
packages
the list of packages looked at in this comparison
revisions
the list of revisions used in this comparison
def __init__(self, revisions=None, packages=None)
description
telling description for this HTML code
def __init__(self, is_expert=False, description=None, check=None)
is_expert
true if this is marked as an expert-only HTML code
check
what should be checked for in this HTML code
description
telling description for this ntuple
def __init__(self, is_expert=False, description=None, check=None)
is_expert
true if this is marked as an expert-only ntuple list
check
what should be checked for in this ntuple ?
visible
true if this package is displayed on the default validation website
name
name of the package
def __init__(self, name, plotfiles=None, scriptfiles=None, fail_count=0)
plotfiles
list of plotfiles which were produced by the scripts in this package
scriptfiles
scripts which were run or skipped as this package was executed
fail_count
contains the number how many scripts failed to execute with error
title
Display name of this file.
description
Description of plot file.
plots
list of plots which are contained inside this plot file
n_shifter_plots
Number of shifter plots.
package
name of the package which created this file
rootfile
filename of the root file
def __init__(self, package, title, rootfile, plots, description="")
description
telling description for this plot
contact
Who is the contact person for this plot ?
height
height of the plot in pixels
width
width of the plot in pixels
def __init__(self, is_expert=False, description=None, check=None, contact=None, width=None, height=None, issue=None)
issue
linked issues
is_expert
true if this is marked as an expert-only plot
check
What should be checked for in this plot ?
label
label (or tag) used to display this revision
Definition: json_objects.py:53
packages
list of packages contained in this revision
Definition: json_objects.py:71
def __init__(self, label, git_hash=None, creation_date=None, packages=None, creation_timezone=None)
Definition: json_objects.py:47
most_recent
is this the most recent revision in the list this revision is contained ?
Definition: json_objects.py:68
git_hash
The git commit hash which has the HEAD while the validation scripts were executed.
Definition: json_objects.py:64
creation_date
date when the validation output of this revision was created, as datetime object
Definition: json_objects.py:57
creation_timezone
timezone used by the creation date
Definition: json_objects.py:60
revisions
the actual list
Definition: json_objects.py:86
def __init__(self, revisions)
Definition: json_objects.py:80
status
Output status of the script execution, can be one of the strings "failed", "finished",...
def __init__(self, name, path, status, log_url=None, return_code=None, input=None, output=None)
Definition: json_objects.py:95
input
input files for the script as declared in the header
path
path the script file is located
return_code
integer which is the return code of the script execution
name
the name of the script file
log_url
location where the log output of the script execution can be found
output
output files produced by the script as declared in the header