5 from __future__
import with_statement
7 from logging
import getLogger; log =
getLogger(
"DQUtils.db")
11 from collections
import namedtuple
12 from io
import StringIO
13 from datetime
import datetime
14 from keyword
import iskeyword
15 from os.path
import dirname
18 from CoolConvUtilities.AtlCoolLib
import indirectOpen
20 from .channel_mapping
import make_channelselection, get_channel_ids_names
21 from .selection
import make_browse_objects_selection
22 from .sugar
import IOVSet, RunLumi, RunLumiType, TimestampType, make_iov_type
24 DEFAULT_DBNAME =
"CONDBR2"
27 return [(field +
"_")
if iskeyword(field)
else field
for field
in fields]
31 Take `since`, `until` and `runs` and turn them into parameters.
33 If nothing is specified, an infinite range is used.
34 If `runs` is an integer, just that run is used
35 If `runs` is a two-tuple, then it is used as a (from_run, to_run)
37 if runs
and (since
is None and until
is None):
38 from builtins
import int
39 if isinstance(runs, tuple):
40 since, until = (runs[0], 0), (runs[1], 0)
42 elif isinstance(runs, int):
43 since, until = (runs, 0), (runs+1, 0)
45 raise RuntimeError(
"Invalid type for `runs`, should be int or tuple")
48 raise RuntimeError(
"Specify (since and/or until), OR runs, not both")
51 if since
is None: since = 0
52 if until
is None: until = 2**63-1
54 if isinstance(since, tuple): since =
RunLumi(*since)
55 if isinstance(until, tuple): until =
RunLumi(*until)
57 if isinstance(since, str): since = TimestampType.from_string(since)
58 if isinstance(until, str): until = TimestampType.from_string(until)
60 if isinstance(since, datetime): since = TimestampType.from_date(since)
61 if isinstance(until, datetime): until = TimestampType.from_date(until)
63 assert since <= until,
"Bad query range (since > until?)"
67 def fetch_iovs(folder_name, since=None, until=None, channels=None, tag="",
68 what="all", max_records=-1, with_channel=True, loud=False,
69 database=None, convert_time=False, named_channels=False,
70 selection=None, runs=None, with_time=False, unicode_strings=False):
72 Helper to fetch objects in a pythonic manner
73 `folder_name` may be an abbreviated name (DQMFONL) or a fully-qualified name
74 (e.g. /GLOBAL/DETSTATUS/DQMFONL)
75 `since`, `until` can be (run, lumi) tuples, or standard iov keys
76 `channels` can be a cool ChannelSelection object or a list of ids/names
78 `what` is a list of strings specifying which records should be fetched
79 if it is the string "all" (not a list), then all records are fetched,
80 and naming is turned on.
81 `max_records` specifies the maximum number of records to fetch. -1 means all
82 `with_channel` specifies whether the channel number should be in the result
84 `loud` specifies whether quick_retrieve (C++ function) should print its
85 status every 1000 objects
86 `database` can be used to specify an abbreviated database, or a connection
88 `convert_time` performs a conversion of `since` and `until` from runlumi
89 to nanoseconds since the epoch.
90 `named_channels` causes the iovs returned to contain strings in the channel
92 `selection` [NOT IMPLEMENTED YET] create a cool selection object
93 `runs` if it is an integer, it is a run number. If it is a tuple, it is a
95 `with_time` retrieve insertiontime for iovs
96 `unicode_strings` return unicode string objects, assuming database content
99 from .quick_retrieve
import quick_retrieve, browse_coracool, get_coracool_payload_spec
101 if channels == []:
return IOVSet()
105 channel_mapping =
None
106 if isinstance(folder_name, str):
107 folder = Databases.get_folder(folder_name, database)
111 folder_name = folder.fullPath()
113 log.error(
"Exception when interpreting folder: {0}".
format(folder_name))
116 log.info(
"Querying %s", folder_name)
117 log.debug(
"Query range: [%s, %s]", since, until)
119 short_folder = folder.fullPath().
split(
"/")[-1]
121 time_based_folder =
"<timeStamp>time</timeStamp>" in folder.description()
122 coracool_folder =
"<coracool>" in folder.description()
123 iov_key_type = TimestampType
if time_based_folder
else RunLumiType
125 if time_based_folder
and (convert_time
or runs):
135 since, until = runrange.first.StartTime, runrange.last.EndTime
136 return fetch_iovs(folder_name, since, until, channels, tag, what,
137 max_records, with_channel, loud,
138 database, convert_time=
False,
139 named_channels=named_channels, selection=selection,
141 unicode_strings=unicode_strings)
145 detstatus_names =
"DQMFOFL",
"DCSOFL",
"DQMFONL",
"SHIFTOFL",
"SHIFTONL",
"LBSUMM"
146 if any(short_folder.endswith(x)
for x
in detstatus_names):
147 channel_mapping =
None
150 cm_reversed = dict((value, key)
for key, value
in six.iteritems(channelmap))
151 channelmap.update(cm_reversed)
152 channel_mapping = channelmap
156 field_name =
"%s_VAL" % short_folder
158 if not coracool_folder:
160 what = folder.folderSpecification().payloadSpecification().
keys()
166 folder.setPrefetchAll(
False)
170 iterator = folder.browseObjects(since, until, channels, tag, sel)
172 iterator = folder.browseObjects(since, until, channels, tag)
176 fields.append(
"insertion_time")
178 fields.append(
"channel")
183 result =
quick_retrieve(iterator, record, what, max_records, with_channel,
184 loud, iov_key_type, channelmap, with_time,
188 args = folder_name, database
189 database, folder_path = Databases.resolve_folder_string(*args)
192 assert database,
"Coracool folder - you must specify a database"
194 db = Databases.get_instance(database)
199 assert isinstance(what, list), (
"Coracool folder - you must specify "
200 "`what` to fetch (it cannot be inferred, as with non-coracool.)")
207 what, record, element, iov_key_type)
209 result = IOVSet(result, iov_type=record, origin=short_folder)
213 def write_iovs(folder_name, iovs, record, multiversion=True, tag="",
214 create=False, storage_buffer=False):
215 args = folder_name, multiversion, record, create
216 db, folder, payload = Databases.fetch_for_writing(*args)
219 folder.setupStorageBuffer()
221 total_iovs = len(iovs)
222 for i, iov
in enumerate(iovs):
223 for field_name, field_value
in zip(iov._fields[3:], iov[3:]):
224 payload[field_name] = field_value
226 folder.storeObject(iov.since, iov.until, payload, iov.channel, tag)
228 log.debug(
"Wrote %5i / %5i records", i, total_iovs)
231 log.debug(
"Flushing records to database...")
232 folder.flushStorageBuffer()
233 log.debug(
"... done")
237 Databases helper class. Used as a singleton. (don't instantiate)
238 Specifies abbreviations for database connection strings and folders
242 "DQMFONL" :
"COOLONL_GLOBAL::/GLOBAL/DETSTATUS/DQMFONL",
243 "DQMFONLLB" :
"COOLONL_GLOBAL::/GLOBAL/DETSTATUS/DQMFONLLB",
244 "SHIFTONL" :
"COOLONL_GLOBAL::/GLOBAL/DETSTATUS/SHIFTONL",
246 "DQMFOFL" :
"COOLOFL_GLOBAL::/GLOBAL/DETSTATUS/DQMFOFL",
247 "DCSOFL" :
"COOLOFL_GLOBAL::/GLOBAL/DETSTATUS/DCSOFL",
248 "DQCALCOFL" :
"COOLOFL_GLOBAL::/GLOBAL/DETSTATUS/DQCALCOFL",
249 "SHIFTOFL" :
"COOLOFL_GLOBAL::/GLOBAL/DETSTATUS/SHIFTOFL",
250 "LBSUMM" :
"COOLOFL_GLOBAL::/GLOBAL/DETSTATUS/LBSUMM",
251 "VIRTUALFLAGS" :
"COOLOFL_GLOBAL::/GLOBAL/DETSTATUS/VIRTUALFLAGS",
253 "DEFECTS" :
"COOLOFL_GLOBAL::/GLOBAL/DETSTATUS/DEFECTS",
255 "SOR_Params" :
"COOLONL_TDAQ::/TDAQ/RunCtrl/SOR_Params",
256 "EOR_Params" :
"COOLONL_TDAQ::/TDAQ/RunCtrl/EOR_Params",
257 "SOR" :
"COOLONL_TDAQ::/TDAQ/RunCtrl/SOR",
258 "EOR" :
"COOLONL_TDAQ::/TDAQ/RunCtrl/EOR",
260 "LBLB" :
"COOLONL_TRIGGER::/TRIGGER/LUMI/LBLB",
261 "LBTIME" :
"COOLONL_TRIGGER::/TRIGGER/LUMI/LBTIME",
262 "LBLESTONL" :
"COOLONL_TRIGGER::/TRIGGER/LUMI/LBLESTONL",
263 "LVL1COUNTERS" :
"COOLONL_TRIGGER::/TRIGGER/LUMI/LVL1COUNTERS",
264 "HLTCOUNTERS" :
"COOLOFL_TRIGGER::/TRIGGER/LUMI/HLTCOUNTERS",
265 "HLT/Menu" :
"COOLONL_TRIGGER::/TRIGGER/HLT/Menu",
266 "LVL1/Menu" :
"COOLONL_TRIGGER::/TRIGGER/LVL1/Menu",
267 "Prescales" :
"COOLONL_TRIGGER::/TRIGGER/LVL1/Prescales",
273 Resolves a simplified folder URI.
276 folder_name == "SHIFTOFL"
277 will give a connection to COOLOFL_GLOBAL/COMP200
278 folder /GLOBAL/DETSTATUS/SHIFTOFL
279 folder_name == "test.db::SHIFTOFL"
280 will give a connection to an sqlite database test.db
283 * Database specified in db_override
284 * Database specified in `folder_name`
285 * Database specified in cls.FOLDERS[folder_name]
287 res_database = db_override
290 if "::" in folder_name:
291 assert folder_name.count(
"::") == 1,
"Bad folder format"
292 database, folder_name = folder_name.split(
"::")
295 res_database = database
if not res_database
else res_database
298 database, res_folder = cls.
FOLDERS[folder_name].
split(
"::")
299 res_database = database
if not res_database
else res_database
301 res_folder = folder_name
303 return res_database, res_folder
306 def get_folder(cls, folder_name, db_override=None, read_only=True,
307 create_function=None, also_db=False):
309 Retrieve a folder. The `db_override` parameter allows over-riding the
310 database which comes from the folder string.
313 `folder_name` : Folder name or alias to load
314 `db_override` : If specified, causes an alternate database string
316 `read_only` : Specifies if a read-only database connection should
318 `create_function` : If specified, function to be called in case the
319 folder doesn't exist. It is passed the database
321 `also_db` : Make the return value (db, folder)
325 assert not create_function,
"`read_only` specified with `create`"
328 assert database, (
"Unable to resolve database for (%s, %s)"
329 % (folder_name, db_override))
331 create =
bool(create_function)
335 cool_folder = db.getFolder(folder)
336 except Exception
as error:
337 log.debug(
'HELP! %s', error.args)
339 log.debug(
'THIS IS %s',
type(args))
340 log.debug(
'Value of boolean: %s', (
"not found" in args))
341 if not (
"cannot be established" in args
or
344 log.exception(
"Unknown error encountered whilst opening "
345 "database connection to '%s'",
349 if not create_function:
350 log.exception(
"Database does not exist, `create_function` not "
354 cool_folder = create_function(db)
356 return db, cool_folder
362 Take a database string - if it looks like a filename ending in ".db"
363 then assume it is a sqlite database with that name.
365 If the `db_string` is prepended with "WRITE|" then a writable connection
368 If the db_string doesn't contain a "/", then "/" + DEFAULT_DBNAME is
371 if "://" in db_string:
373 return db_string, read_only
375 if db_string.startswith(
"WRITE|"):
376 assert db_string.count(
"|") == 1,
"Bad db_string format"
377 db_string = db_string.split(
"|")[1]
380 sqlite_regex = re.compile(
r"(?P<filename>.*?\.db)(?:/?(?P<dbname>[^/]+))?$")
381 matched = sqlite_regex.match(db_string)
383 filename, dbname = matched.groups()
384 dbname = DEFAULT_DBNAME
if not dbname
else dbname
385 db_string =
"sqlite://schema=%s;dbname=%s" % (filename, dbname)
387 if "/" not in db_string:
388 return db_string +
"/" + DEFAULT_DBNAME, read_only
390 return db_string, read_only
395 Open a database connection
400 prev_stdout = sys.stdout
401 sys.stdout = StringIO()
403 connection =
indirectOpen(res_db_string, readOnly=read_only)
405 sys.stdout = prev_stdout
406 except Exception
as e:
407 if ((e.args
and "The database does not exist" in e.args[0])
or
408 str(e).find (
'The database does not exist') >= 0)
and not create:
409 log.info(
"Failed trying to connect to '%s'", res_db_string)
411 from PyCool
import cool
412 dbService = cool.DatabaseSvcFactory.databaseService()
413 connection = dbService.createDatabase(res_db_string)
419 Create `folder_name` on database instance `db`, with recordspecification
422 Also creates folderset to which folder_name belongs if necessary.
424 from PyCool
import cool
426 folderset_path =
dirname(folder_name)
428 db.getFolderSet(folderset_path)
429 except Exception
as error:
430 caught_error =
"Folder set %s not found" % folderset_path
431 if caught_error
not in error.args[0]:
434 log.debug(
"Folderset doesn't exist - creating it.")
435 db.createFolderSet(folderset_path,
"",
True)
437 if not isinstance(record, cool.RecordSpecification):
438 record_spec = cool.RecordSpecification()
440 record_spec.extend(*field)
444 FV = cool.FolderVersioning
445 versioning = FV.MULTI_VERSION
if multiversion
else FV.SINGLE_VERSION
446 folder_spec = cool.FolderSpecification(versioning, record_spec)
447 folder = db.createFolder(folder_name, folder_spec)
448 payload = cool.Record(record_spec)
450 return folder, payload
454 create=False, db_override=None):
456 Retrieve a folder for writing. Creates it if it doesn't exist.
458 `folder_name` specifies folder to be queried
459 `multiversion` specifies COOL versioning mode
460 `record` is a list of fields to be created in the form:
461 [("<field name>", cool.StorageType.<field type>), ...]
462 or if None, defaults to one Code record,
463 or if isinstance(record, cool.RecordSpecification), uses this.
464 `create` should the database be created if it doesn't
465 `db_override` overrides automatic detection of database string
467 from PyCool
import cool
469 record = [(
"Code", cool.StorageType.Int32)]
473 database = db_override
478 except Exception
as error:
479 if not create
or "The database does not exist" not in error.args[0]:
482 from PyCool
import cool
483 dbService = cool.DatabaseSvcFactory.databaseService()
487 log.info(
"Database doesn't exist - creating it.")
488 db = dbService.createDatabase(resolved_database)
491 folder = db.getFolder(folder_name)
492 payload = cool.Record(folder.payloadSpecification())
493 except Exception
as error:
494 if not create
or "Folder %s not found" % folder_name
not in error.args[0]:
497 log.debug(
"Folder doesn't exist - creating it.")
498 args = db, folder_name, multiversion, record
501 return db, folder, payload