Belle II Software  release-05-02-19
database.py
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3 
4 import sqlite3
5 import pathlib
6 import pandas
7 from basf2 import B2DEBUG
8 
9 
10 class SQLiteDB():
11  """
12  Parameters:
13  database_path (pathlib.Path): The path to the database file we want to create/connect to.
14 
15  Keyword Arguments:
16  schema (dict): Database table schema for the DB of the form:
17  {"tablename": ["columnname1 text primary key",
18  "columnname2 int"]
19  }
20  read_only (bool): Should the connection be treated as a read-only connection (no update/insert calls)
21  timeout (float): What timeout value should the connection have. How long to wait for other changes to commit.
22  isolation_level (str): How should the connection behave when making transactions?
23  Choices are [None, "DEFERRED", "IMMEDIATE", "EXCLUSIVE"] where None is autocommit behaviour.
24  """
25 
26  def __init__(self, database_path, schema=None, read_only=False, timeout=5.0, isolation_level=None):
27  self.database_path = database_path
28  self.schema = schema
29  self.conn = None
30  self.read_only = read_only
31  self.timeout = timeout
32  self.isolation_level = isolation_level
33  try:
34  if not self.database_path.exists() and not self.read_only:
35  if not self.schema:
36  raise ValueError("The requested database did not exist, "
37  "but you didn't provide a schema to create the tables.")
38  else:
39  self.open()
40  self.create_schema()
41  elif not self.database_path.exists() and read_only:
42  raise ValueError("The requested database did not exist, "
43  "but you specified that this was a read_only connection.")
44  else:
45  self.open()
46  except AttributeError as err:
47  if not isinstance(self.database_path, pathlib.Path):
48  raise TypeError("You did not use a pathlib.Path object as the database_path.")
49  else:
50  raise err
51 
52  def __enter__(self):
53  return self
54 
55  def __exit__(self, type, value, traceback):
56  self.close()
57 
58  def close(self):
59  if self.conn:
60  if not self.read_only:
61  B2DEBUG(29, f"Committing changes and closing Connection for database {self.database_path}.")
62  self.conn.commit()
63  self.conn.close()
64 
65  def open(self):
66  B2DEBUG(29, f"Opening Connection for database {self.database_path}. Readonly = {self.read_only}")
67  connection_uri = self.get_uri()
68  self.conn = sqlite3.connect(connection_uri, uri=True, timeout=self.timeout, isolation_level=self.isolation_level)
69 
70  def commit(self):
71  self.conn.commit()
72 
73  def query(self, sql, parameters=tuple()):
74  cursor = self.conn.cursor()
75  B2DEBUG(29, f"execute({sql}, {parameters}).")
76  return cursor.execute(sql, parameters)
77 
78  def create_schema(self):
79  for table_name, fields in self.schema.items():
80  columns = ",".join(fields)
81  sql = f"CREATE TABLE {table_name} ({columns})"
82  self.query(sql)
83  self.conn.commit()
84 
85  def get_uri(self):
86  uri = f"file:{self.database_path.as_posix()}"
87  if self.read_only:
88  uri += "?mode=ro"
89  return uri
90 
91 
92 class CAFDB(SQLiteDB):
93  """
94  Parameters:
95  database_path (pathlib.Path): The path to the database file we want to create/connect to.
96 
97  Keyword Arguments:
98  read_only (bool): Should the connection be treated as a read-only connection (no update/insert calls)
99  timeout (float): What timeout value should the connection have. How long to wait for other changes to commit.
100  """
101 
102  default_schema = {"calibrations": ["name text primary key",
103  "state text",
104  "checkpoint text",
105  "iteration int"]}
106 
107  def __init__(self, database_path, read_only=False, timeout=30.0, isolation_level=None):
108  super().__init__(database_path, self.default_schema, read_only, timeout, isolation_level)
109 
110  def insert_calibration(self, calibration_name, state="init", checkpoint="init", iteration=0):
111  self.query("INSERT INTO calibrations VALUES (?,?,?,?)", (calibration_name, state, checkpoint, iteration))
112 
113  def update_calibration_value(self, calibration_name, column_name, new_value, attempts=3):
114  attempt = 1
115  finished = False
116  while not finished:
117  try:
118  self.query("UPDATE calibrations SET {}=? WHERE name=?".format(column_name), (new_value, calibration_name))
119  finished = True
120  except sqlite3.OperationalError as e:
121  if attempt < attempts:
122  attempt += 1
123  else:
124  raise e
125 
126  def get_calibration_value(self, calibration_name, column_name):
127  return self.query("SELECT {} FROM calibrations WHERE name=?".format(column_name), (calibration_name,)).fetchone()[0]
128 
129  def output_calibration_table(self):
130  data = {"name": [], "state": [], "checkpoint": [], "iteration": []}
131  for row in self.query("SELECT * FROM calibrations"):
132  data["name"].append(row[0])
133  data["state"].append(row[1])
134  data["checkpoint"].append(row[2])
135  data["iteration"].append(row[3])
136 
137  table = pandas.DataFrame(data)
138  table_string = table.to_string()
139 
140  line_len = len(table_string.split("\n")[1])
141  title = " Calibrations Table ".center(line_len, " ")
142  border = line_len * "="
143  header = "\n".join((border, title, border))
144  return "\n".join((header, table_string, border))
database.SQLiteDB.timeout
timeout
Definition: database.py:31
database.SQLiteDB.get_uri
def get_uri(self)
Definition: database.py:85
database.SQLiteDB.schema
schema
Definition: database.py:28
database.SQLiteDB.database_path
database_path
Definition: database.py:27
database.SQLiteDB.query
def query(self, sql, parameters=tuple())
Definition: database.py:73
database.SQLiteDB.isolation_level
isolation_level
Definition: database.py:32
database.CAFDB
Definition: database.py:92
database.SQLiteDB
Definition: database.py:10
database.SQLiteDB.read_only
read_only
Definition: database.py:30
database.SQLiteDB.open
def open(self)
Definition: database.py:65
database.SQLiteDB.close
def close(self)
Definition: database.py:58
database.CAFDB.default_schema
dictionary default_schema
Definition: database.py:102
database.SQLiteDB.create_schema
def create_schema(self)
Definition: database.py:78
database.SQLiteDB.conn
conn
Definition: database.py:29