CMS 3D CMS Logo

genericValidation.py
Go to the documentation of this file.
1 from __future__ import print_function
2 from abc import ABCMeta, abstractmethod, abstractproperty
3 import os
4 import re
5 import json
6 import globalDictionaries
7 import configTemplates
8 from dataset import Dataset
9 from helperFunctions import replaceByMap, addIndex, getCommandOutput2, boolfromstring, pythonboolstring
10 from TkAlExceptions import AllInOneError
11 
12 class ValidationMetaClass(ABCMeta):
13  sets = ["mandatories", "optionals", "needpackages"]
14  dicts = ["defaults"]
15  def __new__(cls, clsname, bases, dct):
16  for setname in cls.sets:
17  if setname not in dct: dct[setname] = set()
18  dct[setname] = set.union(dct[setname], *(getattr(base, setname) for base in bases if hasattr(base, setname)))
19 
20  for dictname in cls.dicts:
21  if dictname not in dct: dct[dictname] = {}
22  for base in bases:
23  if not hasattr(base, dictname): continue
24  newdict = getattr(base, dictname)
25  for key in set(newdict) & set(dct[dictname]):
26  if newdict[key] != dct[dictname][key]:
27  raise ValueError("Inconsistent values of defaults[{}]: {}, {}".format(key, newdict[key], dct[dictname][key]))
28  dct[dictname].update(newdict)
29 
30  for setname in cls.sets: #e.g. removemandatories, used in preexistingvalidation
31  #use with caution
32  if "remove"+setname not in dct: dct["remove"+setname] = set()
33  dct["remove"+setname] = set.union(dct["remove"+setname], *(getattr(base, "remove"+setname) for base in bases if hasattr(base, "remove"+setname)))
34 
35  dct[setname] -= dct["remove"+setname]
36 
37  return super(ValidationMetaClass, cls).__new__(cls, clsname, bases, dct)
38 
40  __metaclass__ = ValidationMetaClass
41  defaultReferenceName = "DEFAULT"
42  mandatories = set()
43  defaults = {
44  "cmssw": os.environ['CMSSW_BASE'],
45  "parallelJobs": "1",
46  "jobid": "",
47  "needsproxy": "false",
48  }
49  needpackages = {"Alignment/OfflineValidation"}
50  optionals = {"jobmode"}
51 
52  def __init__(self, valName, alignment, config):
53  import random
54  self.name = valName
55  self.alignmentToValidate = alignment
56  self.general = config.getGeneral()
57  self.randomWorkdirPart = "%0i"%random.randint(1,10e9)
58  self.configFiles = []
59  self.config = config
60  self.jobid = ""
61 
62  theUpdate = config.getResultingSection(self.valType+":"+self.name,
63  defaultDict = self.defaults,
64  demandPars = self.mandatories)
65  self.general.update(theUpdate)
66  self.jobmode = self.general["jobmode"]
67  self.NJobs = int(self.general["parallelJobs"])
68  self.needsproxy = boolfromstring(self.general["needsproxy"], "needsproxy")
69 
70  # limit maximum number of parallel jobs to 40
71  # (each output file is approximately 20MB)
72  maximumNumberJobs = 40
73  if self.NJobs > maximumNumberJobs:
74  msg = ("Maximum allowed number of parallel jobs "
75  +str(maximumNumberJobs)+" exceeded!!!")
76  raise AllInOneError(msg)
77  if self.NJobs > 1 and not isinstance(self, ParallelValidation):
78  raise AllInOneError("Parallel jobs not implemented for {}!\n"
79  "Please set parallelJobs = 1.".format(type(self).__name__))
80 
81  self.jobid = self.general["jobid"]
82  if self.jobid:
83  try: #make sure it's actually a valid jobid
84  output = getCommandOutput2("bjobs %(jobid)s 2>&1"%self.general)
85  if "is not found" in output: raise RuntimeError
86  except RuntimeError:
87  raise AllInOneError("%s is not a valid jobid.\nMaybe it finished already?"%self.jobid)
88 
89  self.cmssw = self.general["cmssw"]
90  badcharacters = r"\'"
91  for character in badcharacters:
92  if character in self.cmssw:
93  raise AllInOneError("The bad characters " + badcharacters + " are not allowed in the cmssw\n"
94  "path name. If you really have it in such a ridiculously named location,\n"
95  "try making a symbolic link somewhere with a decent name.")
96  try:
97  os.listdir(self.cmssw)
98  except OSError:
99  raise AllInOneError("Your cmssw release " + self.cmssw + ' does not exist')
100 
101  if self.cmssw == os.environ["CMSSW_BASE"]:
102  self.scramarch = os.environ["SCRAM_ARCH"]
103  self.cmsswreleasebase = os.environ["CMSSW_RELEASE_BASE"]
104  else:
105  command = ("cd '" + self.cmssw + "' && eval `scramv1 ru -sh 2> /dev/null`"
106  ' && echo "$CMSSW_BASE\n$SCRAM_ARCH\n$CMSSW_RELEASE_BASE"')
107  commandoutput = getCommandOutput2(command).split('\n')
108  self.cmssw = commandoutput[0]
109  self.scramarch = commandoutput[1]
110  self.cmsswreleasebase = commandoutput[2]
111 
112  self.packages = {}
113  for package in self.needpackages:
114  for placetolook in self.cmssw, self.cmsswreleasebase:
115  pkgpath = os.path.join(placetolook, "src", package)
116  if os.path.exists(pkgpath):
117  self.packages[package] = pkgpath
118  break
119  else:
120  raise AllInOneError("Package {} does not exist in {} or {}!".format(package, self.cmssw, self.cmsswreleasebase))
121 
122  self.AutoAlternates = True
123  if config.has_option("alternateTemplates","AutoAlternates"):
124  try:
125  self.AutoAlternates = json.loads(config.get("alternateTemplates","AutoAlternates").lower())
126  except ValueError:
127  raise AllInOneError("AutoAlternates needs to be true or false, not %s" % config.get("alternateTemplates","AutoAlternates"))
128 
129  knownOpts = set(self.defaults.keys())|self.mandatories|self.optionals
130  ignoreOpts = []
131  config.checkInput(self.valType+":"+self.name,
132  knownSimpleOptions = knownOpts,
133  ignoreOptions = ignoreOpts)
134 
135  def getRepMap(self, alignment = None):
136  from plottingOptions import PlottingOptions
137  if alignment == None:
138  alignment = self.alignmentToValidate
139  try:
140  result = PlottingOptions(self.config, self.valType)
141  except KeyError:
142  result = {}
143  result.update(alignment.getRepMap())
144  result.update(self.general)
145  result.update({
146  "workdir": os.path.join(self.general["workdir"],
147  self.randomWorkdirPart),
148  "datadir": self.general["datadir"],
149  "logdir": self.general["logdir"],
150  "CommandLineTemplate": ("#run configfile and post-proccess it\n"
151  "cmsRun %(cfgFile)s\n"
152  "%(postProcess)s "),
153  "CMSSW_BASE": self.cmssw,
154  "SCRAM_ARCH": self.scramarch,
155  "CMSSW_RELEASE_BASE": self.cmsswreleasebase,
156  "alignmentName": alignment.name,
157  "condLoad": alignment.getConditions(),
158  "LoadGlobalTagTemplate": configTemplates.loadGlobalTagTemplate,
159  })
160  result.update(self.packages)
161  return result
162 
163  @abstractproperty
164  def filesToCompare(self):
165  pass
166 
167  def getCompareStrings( self, requestId = None, plain = False ):
168  result = {}
169  repMap = self.getRepMap().copy()
170  for validationId in self.filesToCompare:
171  repMap["file"] = self.filesToCompare[ validationId ]
172  if repMap["file"].startswith( "/castor/" ):
173  repMap["file"] = "rfio:%(file)s"%repMap
174  elif repMap["file"].startswith( "/store/" ):
175  repMap["file"] = "root://eoscms.cern.ch//eos/cms%(file)s"%repMap
176  if plain:
177  result[validationId]=repMap["file"]
178  else:
179  result[validationId]= "%(file)s=%(title)s|%(color)s|%(style)s"%repMap
180  if requestId == None:
181  return result
182  else:
183  if not "." in requestId:
184  requestId += ".%s"%self.defaultReferenceName
185  if not requestId.split(".")[-1] in result:
186  msg = ("could not find %s in reference Objects!"
187  %requestId.split(".")[-1])
188  raise AllInOneError(msg)
189  return result[ requestId.split(".")[-1] ]
190 
191  def createFiles(self, fileContents, path, repMap = None, repMaps = None):
192  """repMap: single map for all files
193  repMaps: a dict, with the filenames as the keys"""
194  if repMap is not None and repMaps is not None:
195  raise AllInOneError("createFiles can only take repMap or repMaps (or neither), not both")
196  result = []
197  for fileName in fileContents:
198  filePath = os.path.join(path, fileName)
199  result.append(filePath)
200 
201  for (i, filePathi) in enumerate(addIndex(filePath, self.NJobs)):
202  theFile = open( filePathi, "w" )
203  fileContentsi = fileContents[ fileName ]
204  if repMaps is not None:
205  repMap = repMaps[fileName]
206  if repMap is not None:
207  repMap.update({"nIndex": str(i)})
208  fileContentsi = replaceByMap(fileContentsi, repMap)
209  theFile.write( fileContentsi )
210  theFile.close()
211 
212  return result
213 
214  def createConfiguration(self, fileContents, path, schedule = None, repMap = None, repMaps = None):
215  self.configFiles = self.createFiles(fileContents,
216  path, repMap = repMap, repMaps = repMaps)
217  if not schedule == None:
218  schedule = [os.path.join( path, cfgName) for cfgName in schedule]
219  for cfgName in schedule:
220  if not cfgName in self.configFiles:
221  msg = ("scheduled %s missing in generated configfiles: %s"
222  %(cfgName, self.configFiles))
223  raise AllInOneError(msg)
224  for cfgName in self.configFiles:
225  if not cfgName in schedule:
226  msg = ("generated configuration %s not scheduled: %s"
227  %(cfgName, schedule))
228  raise AllInOneError(msg)
229  self.configFiles = schedule
230  return self.configFiles
231 
232  def createScript(self, fileContents, path, downloadFiles=[], repMap = None, repMaps = None):
233  self.scriptFiles = self.createFiles(fileContents,
234  path, repMap = repMap, repMaps = repMaps)
235  for script in self.scriptFiles:
236  for scriptwithindex in addIndex(script, self.NJobs):
237  os.chmod(scriptwithindex,0o755)
238  return self.scriptFiles
239 
240  def createCrabCfg(self, fileContents, path ):
241  if self.NJobs > 1:
242  msg = ("jobmode 'crab' not supported for parallel validation."
243  " Please set parallelJobs = 1.")
244  raise AllInOneError(msg)
245  self.crabConfigFiles = self.createFiles(fileContents, path)
246  return self.crabConfigFiles
247 
248 
250  """
251  Subclass of `GenericValidation` which is the base for validations using
252  datasets.
253  """
254  needParentFiles = False
255  mandatories = {"dataset", "maxevents"}
256  defaults = {
257  "runRange": "",
258  "firstRun": "",
259  "lastRun": "",
260  "begin": "",
261  "end": "",
262  "JSON": "",
263  "dasinstance": "prod/global",
264  "ttrhbuilder":"WithAngleAndTemplate",
265  "usepixelqualityflag": "True",
266  }
267  optionals = {"magneticfield"}
268 
269  def __init__(self, valName, alignment, config):
270  """
271  This method adds additional items to the `self.general` dictionary
272  which are only needed for validations using datasets.
273 
274  Arguments:
275  - `valName`: String which identifies individual validation instances
276  - `alignment`: `Alignment` instance to validate
277  - `config`: `BetterConfigParser` instance which includes the
278  configuration of the validations
279  """
280 
281  super(GenericValidationData, self).__init__(valName, alignment, config)
282 
283  # if maxevents is not specified, cannot calculate number of events for
284  # each parallel job, and therefore running only a single job
285  if int( self.general["maxevents"] ) < 0 and self.NJobs > 1:
286  msg = ("Maximum number of events (maxevents) not specified: "
287  "cannot use parallel jobs.")
288  raise AllInOneError(msg)
289  if int( self.general["maxevents"] ) / self.NJobs != float( self.general["maxevents"] ) / self.NJobs:
290  msg = ("maxevents has to be divisible by parallelJobs")
291  raise AllInOneError(msg)
292 
293  tryPredefinedFirst = (not self.jobmode.split( ',' )[0] == "crab" and self.general["JSON"] == ""
294  and self.general["firstRun"] == "" and self.general["lastRun"] == ""
295  and self.general["begin"] == "" and self.general["end"] == "")
296 
297  if self.general["dataset"] not in globalDictionaries.usedDatasets:
298  globalDictionaries.usedDatasets[self.general["dataset"]] = {}
299 
300  if self.cmssw not in globalDictionaries.usedDatasets[self.general["dataset"]]:
301  if globalDictionaries.usedDatasets[self.general["dataset"]] != {}:
302  print(("Warning: you use the same dataset '%s' in multiple cmssw releases.\n"
303  "This is allowed, but make sure it's not a mistake") % self.general["dataset"])
304  globalDictionaries.usedDatasets[self.general["dataset"]][self.cmssw] = {False: None, True: None}
305 
306  Bfield = self.general.get("magneticfield", None)
307  if globalDictionaries.usedDatasets[self.general["dataset"]][self.cmssw][tryPredefinedFirst] is None:
308  dataset = Dataset(
309  self.general["dataset"], tryPredefinedFirst = tryPredefinedFirst,
310  cmssw = self.cmssw, cmsswrelease = self.cmsswreleasebase, magneticfield = Bfield,
311  dasinstance = self.general["dasinstance"])
312  globalDictionaries.usedDatasets[self.general["dataset"]][self.cmssw][tryPredefinedFirst] = dataset
313  if tryPredefinedFirst and not dataset.predefined(): #No point finding the data twice in that case
314  globalDictionaries.usedDatasets[self.general["dataset"]][self.cmssw][False] = dataset
315 
316  self.dataset = globalDictionaries.usedDatasets[self.general["dataset"]][self.cmssw][tryPredefinedFirst]
317  self.general["magneticField"] = self.dataset.magneticField()
318  self.general["defaultMagneticField"] = "MagneticField"
319  if self.general["magneticField"] == "unknown":
320  print("Could not get the magnetic field for this dataset.")
321  print("Using the default: ", self.general["defaultMagneticField"])
322  self.general["magneticField"] = '.oO[defaultMagneticField]Oo.'
323 
324  if not self.jobmode.split( ',' )[0] == "crab":
325  try:
326  self.general["datasetDefinition"] = self.dataset.datasetSnippet(
327  jsonPath = self.general["JSON"],
328  firstRun = self.general["firstRun"],
329  lastRun = self.general["lastRun"],
330  begin = self.general["begin"],
331  end = self.general["end"],
332  parent = self.needParentFiles )
333  except AllInOneError as e:
334  msg = "In section [%s:%s]: "%(self.valType, self.name)
335  msg += str(e)
336  raise AllInOneError(msg)
337  else:
338  if self.dataset.predefined():
339  msg = ("For jobmode 'crab' you cannot use predefined datasets "
340  "(in your case: '%s')."%( self.dataset.name() ))
341  raise AllInOneError( msg )
342  try:
343  theUpdate = config.getResultingSection(self.valType+":"+self.name,
344  demandPars = ["parallelJobs"])
345  except AllInOneError as e:
346  msg = str(e)[:-1]+" when using 'jobmode: crab'."
347  raise AllInOneError(msg)
348  self.general.update(theUpdate)
349  if self.general["begin"] or self.general["end"]:
350  ( self.general["begin"],
351  self.general["end"],
352  self.general["firstRun"],
353  self.general["lastRun"] ) = self.dataset.convertTimeToRun(
354  firstRun = self.general["firstRun"],
355  lastRun = self.general["lastRun"],
356  begin = self.general["begin"],
357  end = self.general["end"],
358  shortTuple = False)
359  if self.general["begin"] == None:
360  self.general["begin"] = ""
361  if self.general["end"] == None:
362  self.general["end"] = ""
363  self.general["firstRun"] = str( self.general["firstRun"] )
364  self.general["lastRun"] = str( self.general["lastRun"] )
365  if ( not self.general["firstRun"] ) and \
366  ( self.general["end"] or self.general["lastRun"] ):
367  self.general["firstRun"] = str(
368  self.dataset.runList()[0]["run_number"])
369  if ( not self.general["lastRun"] ) and \
370  ( self.general["begin"] or self.general["firstRun"] ):
371  self.general["lastRun"] = str(
372  self.dataset.runList()[-1]["run_number"])
373  if self.general["firstRun"] and self.general["lastRun"]:
374  if int(self.general["firstRun"]) > int(self.general["lastRun"]):
375  msg = ( "The lower time/runrange limit ('begin'/'firstRun') "
376  "chosen is greater than the upper time/runrange limit "
377  "('end'/'lastRun').")
378  raise AllInOneError( msg )
379  self.general["runRange"] = (self.general["firstRun"]
380  + '-' + self.general["lastRun"])
381  try:
382  self.general["datasetDefinition"] = self.dataset.datasetSnippet(
383  jsonPath = self.general["JSON"],
384  firstRun = self.general["firstRun"],
385  lastRun = self.general["lastRun"],
386  begin = self.general["begin"],
387  end = self.general["end"],
388  crab = True )
389  except AllInOneError as e:
390  msg = "In section [%s:%s]: "%(self.valType, self.name)
391  msg += str( e )
392  raise AllInOneError( msg )
393 
394  self.general["usepixelqualityflag"] = pythonboolstring(self.general["usepixelqualityflag"], "usepixelqualityflag")
395 
396  def getRepMap(self, alignment = None):
397  result = super(GenericValidationData, self).getRepMap(alignment)
398  outputfile = os.path.expandvars(replaceByMap(
399  "%s_%s_.oO[name]Oo..root" % (self.outputBaseName, self.name)
400  , result))
401  resultfile = os.path.expandvars(replaceByMap(("/store/group/alca_trackeralign/AlignmentValidation/.oO[eosdir]Oo./" +
402  "%s_%s_.oO[name]Oo..root" % (self.resultBaseName, self.name))
403  , result))
404  result.update({
405  "resultFile": ".oO[resultFiles[.oO[nIndex]Oo.]]Oo.",
406  "resultFiles": addIndex(resultfile, self.NJobs),
407  "finalResultFile": resultfile,
408  "outputFile": ".oO[outputFiles[.oO[nIndex]Oo.]]Oo.",
409  "outputFiles": addIndex(outputfile, self.NJobs),
410  "finalOutputFile": outputfile,
411  "ProcessName": self.ProcessName,
412  "Bookkeeping": self.Bookkeeping,
413  "LoadBasicModules": self.LoadBasicModules,
414  "TrackSelectionRefitting": self.TrackSelectionRefitting,
415  "ValidationConfig": self.ValidationTemplate,
416  "FileOutputTemplate": self.FileOutputTemplate,
417  "DefinePath": self.DefinePath,
418  })
419  return result
420 
421  @property
422  def cfgName(self):
423  return "%s.%s.%s_cfg.py"%( self.configBaseName, self.name,
424  self.alignmentToValidate.name )
425  @abstractproperty
426  def ProcessName(self):
427  pass
428 
429  @property
430  def cfgTemplate(self):
431  return configTemplates.cfgTemplate
432 
433  @abstractproperty
435  pass
436 
437  @property
438  def filesToCompare(self):
439  return {self.defaultReferenceName: self.getRepMap()["finalResultFile"]}
440 
441  def createConfiguration(self, path ):
442  repMap = self.getRepMap()
443  cfgs = {self.cfgName: self.cfgTemplate}
444  super(GenericValidationData, self).createConfiguration(cfgs, path, repMap=repMap)
445 
446  def createScript(self, path, template = configTemplates.scriptTemplate, downloadFiles=[], repMap = None, repMaps = None):
447  scriptName = "%s.%s.%s.sh"%(self.scriptBaseName, self.name,
448  self.alignmentToValidate.name )
449  if repMap is None and repMaps is None:
450  repMap = self.getRepMap()
451  repMap["CommandLine"]=""
452  for cfg in self.configFiles:
453  repMap["CommandLine"]+= repMap["CommandLineTemplate"]%{"cfgFile":addIndex(cfg, self.NJobs, ".oO[nIndex]Oo."),
454  "postProcess":""
455  }
456  scripts = {scriptName: template}
457  return super(GenericValidationData, self).createScript(scripts, path, downloadFiles = downloadFiles,
458  repMap = repMap, repMaps = repMaps)
459 
460  def createCrabCfg(self, path, crabCfgBaseName):
461  """
462  Method which creates a `crab.cfg` for a validation on datasets.
463 
464  Arguments:
465  - `path`: Path at which the file will be stored.
466  - `crabCfgBaseName`: String which depends on the actual type of
467  validation calling this method.
468  """
469  crabCfgName = "crab.%s.%s.%s.cfg"%( crabCfgBaseName, self.name,
470  self.alignmentToValidate.name )
471  repMap = self.getRepMap()
472  repMap["script"] = "dummy_script.sh"
473  # repMap["crabOutputDir"] = os.path.basename( path )
474  repMap["crabWorkingDir"] = crabCfgName.split( '.cfg' )[0]
475  self.crabWorkingDir = repMap["crabWorkingDir"]
476  repMap["numberOfJobs"] = self.general["parallelJobs"]
477  repMap["cfgFile"] = self.configFiles[0]
478  repMap["queue"] = self.jobmode.split( ',' )[1].split( '-q' )[1]
479  if self.dataset.dataType() == "mc":
480  repMap["McOrData"] = "events = .oO[nEvents]Oo."
481  elif self.dataset.dataType() == "data":
482  repMap["McOrData"] = "lumis = -1"
483  if self.jobmode.split( ',' )[0] == "crab":
484  print ("For jobmode 'crab' the parameter 'maxevents' will be "
485  "ignored and all events will be processed.")
486  else:
487  raise AllInOneError("Unknown data type! Can't run in crab mode")
488  crabCfg = {crabCfgName: replaceByMap( configTemplates.crabCfgTemplate,
489  repMap ) }
490  return super(GenericValidationData, self).createCrabCfg( crabCfg, path )
491 
492  @property
493  def Bookkeeping(self):
494  return configTemplates.Bookkeeping
495  @property
496  def LoadBasicModules(self):
497  return configTemplates.LoadBasicModules
498  @abstractproperty
500  pass
501  @property
503  return configTemplates.FileOutputTemplate
504  @abstractproperty
505  def DefinePath(self):
506  pass
507 
508 class GenericValidationData_CTSR(GenericValidationData):
509  #common track selection and refitting
510  defaults = {
511  "momentumconstraint": "None",
512  "openmasswindow": "False",
513  "cosmicsdecomode": "True",
514  "removetrackhitfiltercommands": "",
515  "appendtrackhitfiltercommands": "",
516  }
517  def getRepMap(self, alignment=None):
518  result = super(GenericValidationData_CTSR, self).getRepMap(alignment)
519 
520  from trackSplittingValidation import TrackSplittingValidation
521  result.update({
522  "ValidationSequence": self.ValidationSequence,
523  "istracksplitting": str(isinstance(self, TrackSplittingValidation)),
524  "cosmics0T": str(self.cosmics0T),
525  "use_d0cut": str(self.use_d0cut),
526  "ispvvalidation": str(self.isPVValidation)
527  })
528 
529  commands = []
530  for removeorappend in "remove", "append":
531  optionname = removeorappend + "trackhitfiltercommands"
532  if result[optionname]:
533  for command in result[optionname].split(","):
534  command = command.strip()
535  commands.append('process.TrackerTrackHitFilter.commands.{}("{}")'.format(removeorappend, command))
536  result["trackhitfiltercommands"] = "\n".join(commands)
537 
538  return result
539  @property
540  def use_d0cut(self):
541  return "Cosmics" not in self.general["trackcollection"] #use it for collisions only
542  @property
543  def isPVValidation(self):
544  return False # only for PV Validation sequence
545  @property
547  return configTemplates.CommonTrackSelectionRefitting
548  @property
549  def DefinePath(self):
550  return configTemplates.DefinePath_CommonSelectionRefitting
551  @abstractproperty
553  pass
554  @property
555  def cosmics0T(self):
556  if "Cosmics" not in self.general["trackcollection"]: return False
557  Bfield = self.dataset.magneticFieldForRun()
558  if Bfield < 0.5: return True
559  if isinstance(Bfield, str):
560  if "unknown " in Bfield:
561  msg = Bfield.replace("unknown ","",1)
562  elif Bfield == "unknown":
563  msg = "Can't get the B field for %s." % self.dataset.name()
564  else:
565  msg = "B field = {}???".format(Bfield)
566  raise AllInOneError(msg + "\n"
567  "To use this dataset, specify magneticfield = [value] in your .ini config file.")
568  return False
569 
571  @classmethod
572  def initMerge(cls):
573  return ""
574  @abstractmethod
575  def appendToMerge(self):
576  pass
577 
578  @classmethod
579  def doInitMerge(cls):
580  from plottingOptions import PlottingOptions
581  result = cls.initMerge()
582  result = replaceByMap(result, PlottingOptions(None, cls))
583  if result and result[-1] != "\n": result += "\n"
584  return result
585  def doMerge(self):
586  result = self.appendToMerge()
587  if result[-1] != "\n": result += "\n"
588  result += ("if [[ tmpMergeRetCode -eq 0 ]]; then\n"
589  " xrdcp -f .oO[finalOutputFile]Oo. root://eoscms//eos/cms.oO[finalResultFile]Oo.\n"
590  "fi\n"
591  "if [[ ${tmpMergeRetCode} -gt ${mergeRetCode} ]]; then\n"
592  " mergeRetCode=${tmpMergeRetCode}\n"
593  "fi\n")
594  result = replaceByMap(result, self.getRepMap())
595  return result
596 
598  @classmethod
599  def runPlots(cls, validations):
600  return ("rfcp .oO[plottingscriptpath]Oo. .\n"
601  "root -x -b -q .oO[plottingscriptname]Oo.++")
602  @abstractmethod
603  def appendToPlots(self):
604  pass
605  @abstractmethod
607  """override with a classmethod"""
608  @abstractmethod
610  """override with a classmethod"""
611  @abstractmethod
612  def plotsdirname(cls):
613  """override with a classmethod"""
614 
615  @classmethod
616  def doRunPlots(cls, validations):
617  from plottingOptions import PlottingOptions
618  cls.createPlottingScript(validations)
619  result = cls.runPlots(validations)
620  result = replaceByMap(result, PlottingOptions(None, cls))
621  if result and result[-1] != "\n": result += "\n"
622  return result
623  @classmethod
624  def createPlottingScript(cls, validations):
625  from plottingOptions import PlottingOptions
626  repmap = PlottingOptions(None, cls).copy()
627  filename = replaceByMap(".oO[plottingscriptpath]Oo.", repmap)
628  repmap["PlottingInstantiation"] = "\n".join(
629  replaceByMap(v.appendToPlots(), v.getRepMap()).rstrip("\n")
630  for v in validations
631  )
632  plottingscript = replaceByMap(cls.plottingscripttemplate(), repmap)
633  with open(filename, 'w') as f:
634  f.write(plottingscript)
635 
638  def __init__(self, name, values, format=None, latexname=None, latexformat=None):
639  """
640  name: name of the summary item, goes on top of the column
641  values: value for each alignment (in order of rows)
642  format: python format string (default: {:.3g}, meaning up to 3 significant digits)
643  latexname: name in latex form, e.g. if name=sigma you might want latexname=\sigma (default: name)
644  latexformat: format for latex (default: format)
645  """
646  if format is None: format = "{:.3g}"
647  if latexname is None: latexname = name
648  if latexformat is None: latexformat = format
649 
650  self.__name = name
651  self.__values = values
652  self.__format = format
653  self.__latexname = latexname
654  self.__latexformat = latexformat
655 
656  def name(self, latex=False):
657  if latex:
658  return self.__latexname
659  else:
660  return self.__name
661 
662  def format(self, value, latex=False):
663  if latex:
664  fmt = self.__latexformat
665  else:
666  fmt = self.__format
667  if re.match(".*[{][^}]*[fg][}].*", fmt):
668  value = float(value)
669  return fmt.format(value)
670 
671  def values(self, latex=False):
672  result = [self.format(v, latex=latex) for v in self.__values]
673  return result
674 
675  def value(self, i, latex):
676  return self.values(latex)[i]
677 
678  @abstractmethod
679  def getsummaryitems(cls, folder):
680  """override with a classmethod that returns a list of SummaryItems
681  based on the plots saved in folder"""
682 
683  __summaryitems = None
684  __lastfolder = None
685 
686  @classmethod
687  def summaryitemsstring(cls, folder=None, latex=False, transpose=True):
688  if folder is None: folder = cls.plotsdirname()
689  if folder.startswith( "/castor/" ):
690  folder = "rfio:%(file)s"%repMap
691  elif folder.startswith( "/store/" ):
692  folder = "root://eoscms.cern.ch//eos/cms%(file)s"%repMap
693 
694  if cls.__summaryitems is None or cls.__lastfolder != folder:
695  cls.__lastfolder = folder
696  cls.__summaryitems = cls.getsummaryitems(folder)
697 
698  summaryitems = cls.__summaryitems
699 
700  if not summaryitems:
701  raise AllInOneError("No summary items!")
702  size = {len(_.values(latex)) for _ in summaryitems}
703  if len(size) != 1:
704  raise AllInOneError("Some summary items have different numbers of values\n{}".format(size))
705  size = size.pop()
706 
707  if transpose:
708  columnwidths = ([max(len(_.name(latex)) for _ in summaryitems)]
709  + [max(len(_.value(i, latex)) for _ in summaryitems) for i in range(size)])
710  else:
711  columnwidths = [max(len(entry) for entry in [_.name(latex)] + _.values(latex)) for _ in summaryitems]
712 
713  if latex:
714  join = " & "
715  else:
716  join = " "
717  row = join.join("{{:{}}}".format(width) for width in columnwidths)
718 
719  if transpose:
720  rows = [row.format(*[_.name(latex)]+_.values(latex)) for _ in summaryitems]
721  else:
722  rows = []
723  rows.append(row.format(*(_.name for _ in summaryitems)))
724  for i in range(size):
725  rows.append(row.format(*(_.value(i, latex) for _ in summaryitems)))
726 
727  if latex:
728  join = " \\\\\n"
729  else:
730  join = "\n"
731  result = join.join(rows)
732  if latex:
733  result = (r"\begin{{tabular}}{{{}}}".format("|" + "|".join("c"*(len(columnwidths))) + "|") + "\n"
734  + result + "\n"
735  + r"\end{tabular}")
736  return result
737 
738  @classmethod
739  def printsummaryitems(cls, *args, **kwargs):
740  print(cls.summaryitemsstring(*args, **kwargs))
741  @classmethod
742  def writesummaryitems(cls, filename, *args, **kwargs):
743  with open(filename, "w") as f:
744  f.write(cls.summaryitemsstring(*args, **kwargs)+"\n")
745 
747  @classmethod
748  def getsummaryitems(cls, folder):
749  result = []
750  with open(os.path.join(folder, "{}Summary.txt".format(cls.__name__))) as f:
751  for line in f:
752  split = line.rstrip("\n").split("\t")
753  kwargs = {}
754  for thing in split[:]:
755  if thing.startswith("format="):
756  kwargs["format"] = thing.replace("format=", "", 1)
757  split.remove(thing)
758  if thing.startswith("latexname="):
759  kwargs["latexname"] = thing.replace("latexname=", "", 1)
760  split.remove(thing)
761  if thing.startswith("latexformat="):
762  kwargs["latexformat"] = thing.replace("latexformat=", "", 1)
763  split.remove(thing)
764 
765  name = split[0]
766  values = split[1:]
767  result.append(cls.SummaryItem(name, values, **kwargs))
768  return result
769 
771  @classmethod
772  def doComparison(cls, validations):
773  from plottingOptions import PlottingOptions
774  repmap = PlottingOptions(None, cls).copy()
775  repmap["compareStrings"] = " , ".join(v.getCompareStrings("OfflineValidation") for v in validations)
776  repmap["compareStringsPlain"] = " , ".join(v.getCompareStrings("OfflineValidation", True) for v in validations)
777  comparison = replaceByMap(cls.comparisontemplate(), repmap)
778  return comparison
779 
780  @classmethod
782  return configTemplates.compareAlignmentsExecution
783  @classmethod
785  return ".oO[Alignment/OfflineValidation]Oo./scripts/.oO[compareAlignmentsName]Oo."
786  @abstractmethod
788  """classmethod"""
789 
790 class ValidationForPresentation(ValidationWithPlots):
791  @abstractmethod
793  """classmethod"""
def __init__(self, valName, alignment, config)
def pythonboolstring(string, name)
def copy(args, dbName)
def createConfiguration(self, fileContents, path, schedule=None, repMap=None, repMaps=None)
S & print(S &os, JobReport::InputFile const &f)
Definition: JobReport.cc:65
def getCommandOutput2(command)
def createScript(self, fileContents, path, downloadFiles=[], repMap=None, repMaps=None)
def writesummaryitems(cls, filename, args, kwargs)
def __init__(self, valName, alignment, config)
def addIndex(filename, njobs, index=None)
def createCrabCfg(self, path, crabCfgBaseName)
def __new__(cls, clsname, bases, dct)
def summaryitemsstring(cls, folder=None, latex=False, transpose=True)
def PlottingOptions(config, valType)
def replaceByMap(target, the_map)
— Helpers —############################
def createFiles(self, fileContents, path, repMap=None, repMaps=None)
static std::string join(char **cmd)
Definition: RemoteFile.cc:18
def __init__(self, name, values, format=None, latexname=None, latexformat=None)
def boolfromstring(string, name)
def createScript(self, path, template=configTemplates.scriptTemplate, downloadFiles=[], repMap=None, repMaps=None)
def createCrabCfg(self, fileContents, path)
#define update(a, b)
def getRepMap(self, alignment=None)
def getCompareStrings(self, requestId=None, plain=False)
#define str(s)
double split
Definition: MVATrainer.cc:139