CMS 3D CMS Logo

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