CMS 3D CMS Logo

ValidationMatrix_v2.py
Go to the documentation of this file.
1 #! /usr/bin/env python
2 """
3 The script compares two releases, generates SQLite3 database file with release
4 comparison information.
5 
6 Author: Albertas Gimbutas, Vilnius University (LT)
7 e-mail: albertasgim@gmail.com
8 
9 Note: default Pool size for file comparison is 7.
10 Note: did NOT finish static HTML generation implementation.
11 """
12 import sqlite3
13 from datetime import datetime
14 from multiprocessing import Pool, Queue, Process
15 from subprocess import call
16 from optparse import OptionParser, OptionGroup
17 from os import makedirs, remove
18 from os.path import basename, join, exists
19 
20 from Utilities.RelMon.utils_v2 import *
21 from compare_using_files_v2 import RootFileComparison
22 
23 
24 ## Parse options
25 parser = OptionParser(usage='Usage: %prog --re1 RELEASE1 [--f1 FR,FR,..] ' +
26  '--re2 RELEASE2 [--f2 FR,FR,..] [--st ST_TESTS] [options]')
27 parser.add_option('--re1', action='store', dest='release1', default=None,
28  help='First CMSSW release for release comparison, e.g. CMSSW_5_3_2_pre7.')
29 parser.add_option('--re2', action='store', dest='release2', default=None,
30  help='Second CMSSW release for release comparison.')
31 parser.add_option('--f1', action='store', dest='fragments1', default='',
32  help='Comma separated filename fragments that have or have not to be ' +
33  'in RELEASE1 filenames. For "not include" use `!` before fragment, ' +
34  'e.g. `--f1 FullSim,!2012`.''')
35 parser.add_option('--f2', action='store', dest='fragments2', default='',
36  help='Comma separated filename fragments that have or have not to be ' +
37  'in RELEASE2 filenames. For "not include" use `!` before fragment.''')
38 
39 optional_group = OptionGroup(parser, 'Optional')
40 optional_group.add_option('--st', action='store', dest='st_tests', default='KS',
41  help='Comma separated statistical tests to use. \nAvailable: KS, Chi2. Default: %default.')
42 optional_group.add_option('--title', action='store', dest='title', default=None,
43  help='Release comparison title.')
44 optional_group.add_option('--dir', action='store', dest='dir', default=None,
45  help='Directory to download and compare files in.')
46 optional_group.add_option('--url', action='store', dest='url', default=None,
47  help='URL to fetch ROOT files from. File search is recursive ' +
48  'for links in given URL.')
49 optional_group.add_option('--no-url', action='store_true', dest='no_url', default=False,
50  help='Search for files in DIR (specified by --dir option), ' +
51  'do NOT browse for files online.')
52 optional_group.add_option('--db', action='store', dest='db_name', default=None,
53  help='SQLite3 .db filename to use for the comparison. Default: auto-generated SQLite3 .db file.')
54 optional_group.add_option('--cl', action='store_true', dest='clear_db', default=False,
55  help='Clean DB before comparison.')
56 optional_group.add_option('--dry', action='store_true', dest='dry', default=False,
57  help='Do not download or compare files, just show the progress.')
58 optional_group.add_option('--html', action='store_true', dest='html', default=False,
59  help='Generate static html. Default: %default.')
60 parser.add_option_group(optional_group)
61 
62 
64  file1, file2, work_path, db_name, clear_db = args
65  command = ['./compare_using_files_v2.py', join(work_path, file1), join(work_path, file2), '--db', db_name]
66  if clear_db:
67  command.append('--cl')
68  return call(command)
69 
70 def partial_db_name(db_name, i):
71  """Generates temporary database name."""
72  return '%s___%d.db' % (db_name.strip('.db'), i + 1)
73 
74 def merge_dbs(main_db, partial_db):
75  conn = sqlite3.connect(main_db)
76  c = conn.cursor()
77 
78  ## Test if database is empty
79  c.execute('''SELECT * FROM Directory limit 1;''')
80  directory_row = c.fetchall()
81 
82  ## Select offsets
83  rel_cmp_offset, file_cmp_offset, directory_offset, hist_cmp_offset = 0, 0, 0, 0
84  if directory_row:
85  c.execute('''SELECT count(*) FROM ReleaseComparison;''')
86  rel_cmp_offset = c.fetchone()[0]
87  c.execute('''SELECT count(*) FROM RootFileComparison;''')
88  file_cmp_offset = c.fetchone()[0]
89  c.execute('''SELECT max(id) FROM Directory;''')
90  directory_offset = c.fetchone()[0]
91  c.execute('''SELECT max(id) FROM HistogramComparison;''')
92  hist_cmp_offset = c.fetchone()[0]
93 
94  ## Merge DBs
95  c.executescript("""
96  ATTACH '{0}' AS partial;
97  BEGIN;
98 
99  INSERT INTO ReleaseComparison (title, release1, release2, statistical_test)
100  SELECT title, release1, release2, statistical_test FROM partial.ReleaseComparison;
101 
102  INSERT INTO RootFileComparison (filename1, filename2, release_comparison_id, directory_id)
103  SELECT filename1, filename2, release_comparison_id+{1}, directory_id+{3} FROM partial.RootFileComparison;
104 
105  INSERT INTO Directory (id, name, parent_id, from_histogram_id, till_histogram_id)
106  SELECT id+{3}, name, parent_id+{3}, from_histogram_id+{4}, till_histogram_id+{4} FROM partial.Directory;
107 
108  INSERT INTO HistogramComparison (name, p_value, directory_id)
109  SELECT name, p_value, directory_id+{3} FROM partial.HistogramComparison;
110 
111  COMMIT;""".format(partial_db, rel_cmp_offset, file_cmp_offset, directory_offset, hist_cmp_offset))
112 
113  ## Select Last RootFileComparison ID
114  c.execute('''SELECT max(id) FROM RootFileComparison;''')
115  max_file_cmp_id = c.fetchone()[0]
116  conn.close()
117  return max_file_cmp_id
118 
119 
121  """Generates release comparison information and stores it on SQLite3 .db file."""
122  def __init__(self, work_path=None, db_name=None, clear_db=False, dry=False, no_url=False, use_external=False):
123  self.work_path = work_path
124  self.db_name = db_name
125  self.clear_db = clear_db
126  self.dry = dry
127  self.no_url = no_url
129 
130  def was_compared(self, release1, release2, st_test_name):
131  conn = sqlite3.connect(self.db_name)
132  c = conn.cursor()
133  c.execute('''SELECT id FROM ReleaseComparison WHERE release1=? AND
134  release2=? AND statistical_test=?''', (release1, release2, st_test_name))
135  release_comparison_id = c.fetchone()
136  conn.close()
137  if release_comparison_id:
138  return release_comparison_id[0]
139  return False
140 
141  def compare(self, rel1, frags1, rel2, frags2, st_tests, url=None, title=None):
142  print '\n################# Searching for files ###################'
143  if self.no_url:
144  print 'Searching for files on disk at %s' % (self.work_path)
145  files1, files2 = search_on_disk(self.work_path, rel1, frags1, rel2, frags2)
146  file_pairs = make_file_pairs(files1, files2)
147  else:
148  print 'Searching for files online at:'
149  files_with_urls1, files_with_urls2 = recursive_search_online(url, rel1, frags1, rel2, frags2)
150  file_pairs = make_file_pairs(files_with_urls1, files_with_urls2)
151  files_with_urls1.update(files_with_urls2)
152  files1, files2 = list(zip(*file_pairs))
153  paired_files_with_urls = [(file, files_with_urls1[file]) for file in files1 + files2]
154 
155  if self.dry:
156  print 'DRY: nothing to do. Exiting.'
157  exit()
158 
159  ## Create working directory if not given.
160  if not self.work_path:
161  self.work_path = '%s___VS___%s' % (get_version(files1[0]), get_version(files2[0]))
162  if self.db_name:
163  self.db_name = join(self.work_path, self.db_name)
164 
165  if not exists(self.work_path):
166  print '\n################### Preparing directory ###################'
167  print 'Creating working directory: %s ...' % self.work_path,
168  makedirs(self.work_path)
169  print 'Done.'
170 
171  print '\n################# Downloading the files ###################'
172  total_size, files_to_download = get_size_to_download(self.work_path, paired_files_with_urls)
173  check_disk_for_space(self.work_path, total_size)
174 
175  ## Download needed files.
176  q = Queue()
177  show_status_bar.q = q
178  auth_download_file.q = q
179  auth_download_file.work_dir = self.work_path
180 
181  Process(target=show_status_bar, args=(total_size,)).start()
182  Pool(2).map(auth_download_file, files_to_download)
183  if total_size:
184  print "Done."
185 
186  ## Create database
187  print '\n################# Preparing Database ###################'
188  if not self.db_name:
189  self.db_name = '%s___VS___%s.db' % (get_version(file_pairs[0][0]), get_version(file_pairs[0][1]))
190 
191  if self.clear_db:
192  print 'Clearing DB: %s...' % self.db_name,
193  open(join(self.work_path, self.db_name), 'w').close()
194  print 'Done.'
195 
196  ## Compare file pairs.
197  self.db_name = init_database(join(self.work_path, self.db_name))
198 
199  # TODO: Use multiprocessing for this task.
200  for st_test_name in st_tests.split(','):
201  print '\n################# Comparing Releases (%s) ###################' % st_test_name
202  st_test = tests[st_test_name]()
203 
204  some_files_compared = False
205  file_comparison_ids = []
207  # Compare files using compare_using_files_v2.py
208  arg_list = [list(pair) + [self.work_path, partial_db_name(self.db_name, i),
209  self.clear_db] for i, pair in enumerate(file_pairs)]
210  pool = Pool(7)
211  pool.map(call_compare_using_files, arg_list)
212 
213  # Merge databases
214  print '\n################# Merging DBs (%s) ###################' % st_test_name
215  for i, pair in enumerate(file_pairs):
216  tmp_db = partial_db_name(self.db_name, i)
217  print 'Merging %s...' % (basename(tmp_db),),
218  file_comparison_ids.append(merge_dbs(self.db_name, tmp_db))
219  remove(tmp_db)
220  print 'Done.'
221  some_files_compared = True
222  else:
223  file_comparison = RootFileComparison(self.db_name)
224 
225  for file1, file2 in file_pairs:
226  # TODO: If files are not found desplay nice message.
227  # TODO: Maybe subprocces would control the unwanted reports of RootFileComparison.compare()
228  file1_path = join(self.work_path, file1)
229  file2_path = join(self.work_path, file2)
230 
231  if not file_comparison.was_compared(file1, file2, st_test_name):
232  print "Comparing:\n%s\n%s\n" % (file1, file2)
233  file_comparison_id = file_comparison.compare(file1_path, file2_path, st_test)
234  file_comparison_ids.append(file_comparison_id)
235  some_files_compared = True
236  else:
237  print "Already compared:\n%s\n%s\n" % (file1, file2)
238 
239  ## Calculate statistics for the release.
240  release1 = get_version(file_pairs[0][0])
241  release2 = get_version(file_pairs[0][1])
242  if some_files_compared:
243  release_comparison_id = self.was_compared(release1, release2, st_test_name)
244  conn = sqlite3.connect(self.db_name)
245  c = conn.cursor()
246  if not release_comparison_id:
247  print 'Inserting release "%s VS %s" description.\n' % (release1, release2)
248  if not title:
249  title = "%s__VS__%s" % (release1, release2)
250  c.execute('''INSERT INTO ReleaseComparison(title, release1, release2,
251  statistical_test) VALUES (?, ?, ?, ?)''', (title,
252  release1, release2, st_test_name))
253  release_comparison_id = c.lastrowid
254  c.executemany('''UPDATE RootFileComparison SET release_comparison_id = ?
255  WHERE id == ?''', [(release_comparison_id, fid) for fid in file_comparison_ids])
256  conn.commit()
257  conn.close()
258 
259 
260 if __name__ == '__main__':
261  start = datetime.now()
262  opts, args = parser.parse_args()
263  if not opts.release1 or not opts.release2:
264  parser.error('Not all releases specified. Please check --re1 and --re2 options.')
265 
266  rel_cmp = ReleaseComparison(opts.dir, opts.db_name, opts.clear_db, opts.dry, opts.no_url, use_external=True)
267  rel_cmp.compare(opts.release1, opts.fragments1, opts.release2,
268  opts.fragments2, opts.st_tests, opts.url, opts.title)
269  if opts.html:
270  print '\n################# Generating static HTML #################'
271  print '\n Warrning!!! Did NOT finished the implementation. \n'
272  from Utilities.RelMon.web.dbfile2html import dbfile2html
273  dbfile2html(rel_cmp.db_name, opts.dir)
274  print '################# Execution time: %s #################\n' % (datetime.now() - start,)
Definition: start.py:1
def get_version(filename)
Definition: utils_v2.py:432
def make_file_pairs(files1, files2)
Definition: utils_v2.py:105
def call_compare_using_files(args)
def partial_db_name(db_name, i)
def __init__(self, work_path=None, db_name=None, clear_db=False, dry=False, no_url=False, use_external=False)
def recursive_search_online(url, rel1, frags1, rel2, frags2)
Definition: utils_v2.py:213
OutputIterator zip(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, Compare comp)
def get_size_to_download(work_path, files_with_urls)
Definition: utils_v2.py:443
work_path
Create working directory if not given.
def search_on_disk(work_path, rel1, frags1, rel2, frags2)
Definition: utils_v2.py:253
static std::string join(char **cmd)
Definition: RemoteFile.cc:18
def was_compared(self, release1, release2, st_test_name)
def remove(d, key, TELL=False)
Definition: MatrixUtil.py:210
def check_disk_for_space(work_path, size_needed)
Definition: utils_v2.py:459
def compare(self, rel1, frags1, rel2, frags2, st_tests, url=None, title=None)
def init_database(db_path)
Utils.
Definition: utils_v2.py:388
def dbfile2html(db_name, work_path, threshold=1e-5)
Definition: dbfile2html.py:35
def merge_dbs(main_db, partial_db)
How EventSelector::AcceptEvent() decides whether to accept an event for output otherwise it is excluding the probing of A single or multiple positive and the trigger will pass if any such matching triggers are PASS or EXCEPTION[A criterion thatmatches no triggers at all is detected and causes a throw.] A single negative with an expectation of appropriate bit checking in the decision and the trigger will pass if any such matching triggers are FAIL or EXCEPTION A wildcarded negative criterion that matches more than one trigger in the trigger list("!*","!HLTx*"if it matches 2 triggers or more) will accept the event if all the matching triggers are FAIL.It will reject the event if any of the triggers are PASS or EXCEPTION(this matches the behavior of"!*"before the partial wildcard feature was incorporated).Triggers which are in the READY state are completely ignored.(READY should never be returned since the trigger paths have been run