Belle II Software  release-06-01-15
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):
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 
115 
117 
118  """
119  Wrapper for a file containing a set of plots, only
120  root files up to now
121  """
122 
123  def __init__(self, package, title, rootfile, plots, description=""):
124  """
125  Create a new PlotFile object and fill all members
126  """
127 
128 
129  self.packagepackage = package
130 
131  self.titletitle = title
132 
133  self.rootfilerootfile = rootfile
134 
135  self.plotsplots = plots
136 
137  self.descriptiondescription = description
138 
139  self.n_shifter_plotsn_shifter_plots = sum([not plot.is_expert for plot in self.plotsplots])
140 
141 
142 class Plot(JsonBase):
143 
144  """
145  Wrapper for one specfic plot.
146  """
147 
148  def __init__(
149  self,
150  is_expert=False,
151  description=None,
152  check=None,
153  contact=None,
154  width=None,
155  height=None,
156  ):
157  """
158  Create a new Plot object and fill all members
159  """
160 
161 
162  self.is_expertis_expert = is_expert
163 
164  self.descriptiondescription = description
165 
166  self.checkcheck = check
167 
168  self.contactcontact = contact
169 
170  self.widthwidth = width
171 
172  self.heightheight = height
173 
174 
176 
177  """
178  Wrapper for NTuple lists. This is not a graphical plot, but a list of
179  values
180  """
181 
182  def __init__(self, is_expert=False, description=None, check=None):
183  """
184  Create a new NTuple object and fill all members
185  """
186 
187 
188  self.is_expertis_expert = is_expert
189 
190  self.descriptiondescription = description
191 
192  self.checkcheck = check
193 
194 
196 
197  """
198  Wrapper for user HTML Content. This is not a graphical plot but HTML
199  code which will be directly output on the validation website.
200 
201  """
202 
203  def __init__(self, is_expert=False, description=None, check=None):
204  """
205  Create a new NTuple object and fill all members
206  """
207 
208 
209  self.is_expertis_expert = is_expert
210 
211  self.descriptiondescription = description
212 
213  self.checkcheck = check
214 
215 
217 
218  """
219  One high-level package of the validation suites which contains a set of
220  scripts and output plot files
221  """
222 
223  def __init__(self, name, plotfiles=None, scriptfiles=None, fail_count=0):
224  """
225  Create a new NTuple object and fill all members
226  """
227 
228  if not plotfiles:
229  plotfiles = []
230  if not scriptfiles:
231  scriptfiles = []
232 
233 
234  self.namename = name
235 
236  self.plotfilesplotfiles = plotfiles
237 
238  self.scriptfilesscriptfiles = scriptfiles
239 
240  self.visiblevisible = True
241 
242  self.fail_countfail_count = fail_count
243 
244 
245 class ComparisonState(enum.Enum):
246 
247  """
248  Enum to classify the comparison result of two validation plots
249  """
250 
251 
253  NotCompared = "not_compared"
254 
256  NotSupported = "not_supported"
257 
259  FailureTechnical = "technical_failure"
260 
262  FailureStastical = "statistical_failure"
263 
265  Equal = "equal"
266 
267 
269 
270  """
271  Contains the comparison result of two plots
272  """
273 
274  def __init__(self, state, chi2):
275  """
276  Create a new ComparisonResult object and fill all members
277  """
278 
279 
280  self.statestate = state
281 
282  self.chi2chi2 = chi2
283 
284 
286 
287  """
288  Contains information about a file containing plots and the comparison which
289  have been performed for the content of this file
290  """
291 
292  def __init__(
293  self,
294  package,
295  title,
296  rootfile,
297  compared_revisions=None,
298  plots=None,
299  has_reference=False,
300  ntuples=None,
301  html_content=None,
302  description=None,
303  ):
304  """
305  Create a new ComparisonPlotFile object and fill all members
306  """
307 
308  if not plots:
309  plots = []
310  if not ntuples:
311  ntuples = []
312  if not html_content:
313  html_content = []
314 
315  super().__init__(
316  package, title, rootfile, plots, description=description
317  )
318 
319  self.compared_revisioncompared_revision = compared_revisions
320 
321  self.ntuplesntuples = ntuples
322 
323  self.html_contenthtml_content = html_content
324 
325 
326  self.has_referencehas_reference = has_reference
327 
328 
329  self.comparison_errorcomparison_error = len(
330  [plt for plt in self.plotsplots if plt.comparison_result == "error"]
331  )
332 
333  self.comparison_error_shiftercomparison_error_shifter = len(
334  [
335  plt
336  for plt in self.plotsplots
337  if (not plt.is_expert) and plt.comparison_result == "error"
338  ]
339  )
340 
341  self.comparison_warningcomparison_warning = len(
342  [plt for plt in self.plotsplots if plt.comparison_result == "warning"]
343  )
344 
346  self.comparison_warning_shiftercomparison_warning_shifter = len(
347  [
348  plt
349  for plt in self.plotsplots
350  if (not plt.is_expert) and plt.comparison_result == "warning"
351  ]
352  )
353 
354 
355  self.n_shifter_ntuplesn_shifter_ntuples = sum(
356  [not tuple.is_expert for tuple in self.ntuplesntuples]
357  )
358 
359 
360  self.show_shiftershow_shifter = bool(
361  self.n_shifter_plotsn_shifter_plots or self.n_shifter_ntuplesn_shifter_ntuples or self.html_contenthtml_content
362  )
363 
364 
366 
367  """
368  One individual plot including its comparison outcome.
369  """
370 
371  def __init__(
372  self,
373  title,
374  comparison_result=None,
375  png_filename=None,
376  pdf_filename=None,
377  contact=None,
378  description=None,
379  check=None,
380  is_expert=None,
381  plot_path=None,
382  comparison_text=None,
383  height=None,
384  width=None,
385  warnings=None,
386  ):
387  """
388  Create a new ComparisonPlot object and fill all members
389  """
390 
391  # todo: move more into the base class
392  super().__init__(
393  is_expert=is_expert,
394  description=description,
395  check=check,
396  contact=contact,
397  height=height,
398  width=width,
399  )
400 
401  self.titletitle = title
402 
403 
404  self.comparison_resultcomparison_result = comparison_result
405 
406 
407  self.comparison_textcomparison_text = comparison_text
408 
409 
410  self.png_filenamepng_filename = png_filename
411 
412 
413  self.pdf_filenamepdf_filename = pdf_filename
414 
415 
417  self.plot_pathplot_path = plot_path
418 
419 
420  if warnings is None:
421  warnings = []
422  self.warningswarnings = warnings
423 
424 
426 
427  """
428  Comparison outcome for NTuples
429  """
430 
431  def __init__(
432  self,
433  title,
434  contact=None,
435  description=None,
436  check=None,
437  is_expert=None,
438  json_file_path=None,
439  ):
440  """
441  Create a new ComparisonNTuple object and fill all members
442  """
443 
444  # todo: move more into the base class
445  super().__init__(
446  is_expert=is_expert, description=description, check=check
447  )
448 
449  self.titletitle = title
450 
451  self.contactcontact = contact
452 
454  self.json_file_pathjson_file_path = json_file_path
455 
456 
458 
459  """
460  Compiled HTLM Content
461  """
462 
463  def __init__(
464  self,
465  title,
466  contact=None,
467  description=None,
468  check=None,
469  is_expert=None,
470  html_content=None,
471  ):
472  """
473  Create a new ComparisonNTuple object and fill all members
474  """
475 
476  # todo: move more into the base class
477  super().__init__(
478  is_expert=is_expert, description=description, check=check
479  )
480 
481  self.titletitle = title
482 
483  self.contactcontact = contact
484 
486  self.html_contenthtml_content = html_content
487 
488 
490 
491  """
492  Information about a Package which was used in a comparison operation
493  """
494 
495  def __init__(
496  self, name, plotfiles=None, scriptfiles=None, ntuplefiles=None
497  ):
498  """
499  Create a new ComparisonPackage object and fill all members
500  """
501 
502  if not plotfiles:
503  plotfiles = []
504  if not scriptfiles:
505  scriptfiles = []
506  if not ntuplefiles:
507  ntuplefiles = []
508 
509  super().__init__(name, plotfiles=plotfiles, scriptfiles=scriptfiles)
510 
511 
512  self.comparison_errorcomparison_error = sum([pf.comparison_error for pf in plotfiles])
513 
514  self.comparison_error_shiftercomparison_error_shifter = sum(
515  [pf.comparison_error_shifter for pf in plotfiles]
516  )
517 
518  self.comparison_warningcomparison_warning = sum(
519  [pf.comparison_warning for pf in plotfiles]
520  )
521 
523  self.comparison_warning_shiftercomparison_warning_shifter = sum(
524  [pf.comparison_warning_shifter for pf in plotfiles]
525  )
526 
527 
529 
530  """
531  Revision information enriched by the information gained during
532  a comparison.
533  """
534 
535  def __init__(self, label, git_hash=None, creation_date=None, color=None):
536  """
537  Create a new ComparisonRevision object and fill all members
538  """
539 
540  # todo: creation_date
541  super().__init__(label, git_hash=git_hash, creation_date=None)
542 
544  self.colorcolor = color
545 
546 
548 
549  """
550  Contains information and plots generated for comparisons
551  between revisions
552  """
553 
554  def __init__(self, revisions=None, packages=None):
555  """
556  Create a new ComparisonRevision object and fill all members
557  """
558 
559  if not revisions:
560  revisions = []
561  if not packages:
562  packages = []
563 
564 
565  self.revisionsrevisions = revisions
566 
567  self.packagespackages = packages
568  sorted_revs = sorted(revisions, key=lambda x: x.label)
569 
570  self.labellabel = functools.reduce(
571  lambda x, y: x + "_" + y.label, sorted_revs, ""
572  )[1:]
573 
574 
575 # ==============================================================================
576 # Functions
577 # ==============================================================================
578 
579 
580 def dump(file_name, obj):
581  """
582  Output a tree of objects into a json file
583  """
584 
585  with open(file_name, "w+") as f:
586  json.dump(dump_rec(obj), f, sort_keys=True, indent=4)
587 
588 
589 def dumps(obj):
590  """
591  Convert a tree of python objects into a json file
592  """
593 
594  kk = dump_rec(obj)
595  return json.dumps(kk, sort_keys=True, indent=4)
596 
597 
598 def dump_rec(top_object):
599  """
600  Recursive generating of dictionary from a tree
601  of JsonBase objects
602  """
603 
604  this_dict = {}
605  # iterate through the dictionary of the top object
606  for (k, v) in top_object.__dict__.items():
607 
608  # list which needs special handing ?
609  if isinstance(v, list):
610  obj_list = []
611  for it in v:
612  # one of our object's in the list, which needs
613  # special treatment?
614  if isinstance(it, JsonBase):
615  # yen, recurse in to the object
616  obj_list.append(dump_rec(it))
617  else:
618  # no, just add value to list
619  obj_list.append(it)
620 
621  # store compiled list with corresponding key
622  this_dict[k] = obj_list
623  # one of our objects, which might be nested and
624  # needs special treatment ?
625  elif isinstance(v, JsonBase):
626  this_dict[k] = dump_rec(v)
627  # treat enum classes
628  elif isinstance(v, enum.Enum):
629  this_dict[k] = v.value
630  # regular value, just store with correct key
631  else:
632  this_dict[k] = v
633  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
is_expert
true if this is marked as an expert-only plot
def __init__(self, is_expert=False, description=None, check=None, contact=None, width=None, height=None)
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)
Definition: json_objects.py:95
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