All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
DataLoader3.py
Go to the documentation of this file.
1 #----------------------------------------------------------
2 #
3 # This was developed by SCD of Fermilab.
4 # This file was copied from here
5 # https://cdcvs.fnal.gov/redmine/projects/hardwaredb/files
6 #
7 #-----------------------------------------------------------
8 
9 
10 import base64
11 import hashlib
12 import json
13 import os
14 import random
15 import ssl
16 import sys
17 import time
18 import urllib.error
19 import urllib.parse
20 import urllib.request
21 from io import IOBase
22 
23 
24 class DataLoader(object):
25  """Collects data intended for a hardware database table. On command, the data
26  is sent to a server for loading."""
27 
28  def __init__(self, password, url, group, table):
29  """ Class constructor.
30 
31  Args:
32  password - Agreed upon password - for the group.
33  url - Http URL to the server used for loading.
34  group - Group the table is part of.
35  table - Postgresql database table data will be loaded into.
36  """
37  self.password = password
38  self.group = group
39  self.url = url
40  self.args = "table=%s" % table
41  self.urlWithArgs = "%s" % self.url
42  self.data = {'table': table.lower(),
43  'rows': []
44  }
45 
46  def addRow(self, row, mode='insert'):
47  """ Adds a single row of data to the instance. This row will be
48  inserted or updated in the database.
49 
50  Args:
51  row - a dictionary containing a name/value pair
52  for each required table column.
53  mode - insert or update
54  """
55  if isinstance(row, dict) is False:
56  raise Exception("row must be a dictionary")
57  if mode not in ('insert', 'update'):
58  raise Exception("mode must be insert or update")
59  data = {k: (v.name, (lambda f: f.seek(0) or base64.b64encode(f.read()).decode())(v)) if isinstance(v, IOBase) else v for (k, v) in list(row.items())}
60  (self.data['rows']).append((mode, data))
61 
62  def send(self, echoUrl=False):
63  """Sends the data to the server for loading.
64 
65  Returns:
66  Boolean indicating success and failure of the call.
67  A code indicating Html return status.
68  Text describing any error which returned.
69  """
70  # Repeats if there is a collision on the salt.
71  while 1:
72  jdata = json.dumps(self.data)
73  random.seed(time.time())
74  salt = '%s' % (random.random(),)
75  sig = self.__signature(jdata, salt)
76  # The Request call is sending as a POST, not as a GET.
77  req = urllib.request.Request(self.urlWithArgs, jdata.encode(),
78  {'X-Salt': salt,
79  'X-Signature': sig,
80  'X-Group': self.group,
81  'X-Table': self.data['table']
82  })
83  if echoUrl:
84  print("URL: %s\n %s" % (req.get_full_url(), req.header_items()))
85  ssl_cert_file = os.environ.get("SSL_CERT_FILE")
86  try:
87  response = urllib.request.urlopen(req) if ssl_cert_file else urllib.request.urlopen(req, context=ssl.SSLContext())
88  except urllib.error.HTTPError as val:
89  if self.urlWithArgs.lower().startswith("https") and ssl_cert_file:
90  print("\n*** Please verify CA certificate provided with SSL_CERT_FILE environment variable!\n")
91  retValue = False
92  code = "%s %s" % (val.code, val.msg)
93  text = val.read()
94  else:
95  retValue = True
96  code = "%s %s" % (response.getcode(), response.msg)
97  text = response.read()
98  if text != "Signature Error":
99  break
100  return retValue, code, text
101 
102  def clearRows(self):
103  """ Deletes all rows from the instance, readying it for
104  the next set of data.
105  """
106  self.data['rows'] = []
107 
108  def __buildReq(self):
109  return req
110 
111  def __signature(self, data, salt):
112  m = hashlib.md5()
113  m.update(self.password.encode())
114  m.update(salt.encode())
115  m.update(data.encode())
116  return m.hexdigest()
117 
118  def __str__(self):
119  retVal = "URL: %s\nURL with Args: %s\nTable:%s\nPassword: XXXXX\nGroup:%s\n" % (
120  self.url, self.urlWithArgs, self.data['table'], self.group
121  )
122  rowCnt = 0
123  rows = self.data['rows']
124  if len(rows) == 0:
125  retVal += "Rows: None\n"
126  else:
127  for row in rows:
128  retVal += "Row %s:\n" % rowCnt
129  for column in list(row.keys()):
130  retVal += " %s: %s\n" % (column, str(row.get(column)))
131  rowCnt += 1
132  return retVal
133 
134 
135 class DataQuery:
136  """ Supports simple user queries through the use of QueryEngine.
137  (https://cdcvs.fnal.gov/redmine/projects/qengine/wiki)
138  """
139 
140  def __init__(self, url):
141  """ Class constructor.
142 
143  Args:
144  url - Http URL to QueryEngine.
145  """
146  self.url = url
147 
148  def query(self, database, table, columns, where=None, order=None, limit=None, echoUrl=False):
149  """ Executes a simple query and returns the results in a list. List data will
150  be in the same order as listed in the columns attribute.
151 
152  Args:
153  database - The name of the database to be queried. (This database must
154  be in QueryEngine's configuration file.)
155  table - The name of the table to query on.
156  columns - A comma separated string of the table columns to be returned.
157  where - (optional) <column>:<op>:<value> - can be repeated; seperated by ampersand (&)
158  op can be: lt, le, eq, ne, ge, gt
159  order - (optional) A comma separated string of columns designating row order in the returned list.
160  Start the string with a minus (-) for descending order.
161  limit - (optional) - A integer designating the maximum number of rows to be returned.
162  """
163 
164  parameters = [
165  ('dbname', database),
166  ('t', table),
167  ('c', columns),
168  ('x', 'no'),
169  ]
170  if where is not None:
171  parameters.append(('w', where))
172  if order is not None:
173  parameters.append(('o', order))
174  if limit is not None:
175  parameters.append(('l', limit))
176  fullUrl = self.url + '?' + urllib.parse.urlencode(parameters, doseq=True)
177  if echoUrl:
178  print(("Url: %s" % fullUrl))
179  req = urllib.request.Request(fullUrl)
180  ssl_cert_file = os.environ.get("SSL_CERT_FILE")
181  try:
182  resp = urllib.request.urlopen(req) if ssl_cert_file else urllib.request.urlopen(req, context=ssl.SSLContext())
183  except urllib.error.HTTPError as val:
184  retValue = False
185  code = "%s %s" % (val.code, val.msg)
186  text = val.read().decode()
187  print(f"\n*** ", code, text)
188  except urllib.error.URLError:
189  if fullUrl.lower().startswith("https") and ssl_cert_file:
190  print("\n*** Please verify CA certificate provided with SSL_CERT_FILE environment variable!\n")
191  pass
192  else:
193  text = resp.read().decode()
194  data = text.split('\n')
195  return data[1:]
196 
197  return []
do one_file $F done echo for F in find $TOP name CMakeLists txt print
void decode(std::any const &src, Interval< Args...> &iv)
Decodes an interval.
list
Definition: file_to_url.sh:28