Odyssey
ody_migr_utils.py
1 #!/usr/bin/env python
2 """This module includes utility functions."""
3 
4 
5 import base64
6 import json
7 import hashlib
8 import logging
9 import sys
10 
11 from functools import wraps
12 from errno import EACCES, EPERM, ENOENT
13 
14 LOGGER = logging.getLogger(__name__)
15 
16 
17 def get_ith_batch(_list, batch_index, batch_size):
18  """Utility function to obtain a ith batch of data from list
19 
20  Args:
21  _list: list to choose batch from
22  batch_index: batch index
23  batch_size: size of batch to select
24 
25  Returns:
26  batch (sub list of _list) of size batch_size
27  """
28  start = batch_index * batch_size
29  end = min(batch_index * batch_size + batch_size, len(_list))
30  return _list[start:end]
31 
32 
33 def encode_base64(_data):
34  """Encode content into using base64 encoding
35 
36  Args:
37  _data (str): input data to encode
38 
39  Returns:
40  base64 encoded bytes
41  """
42  return base64.b64encode(_data.encode(encoding="utf-8"))
43 
44 
45 def decode_base64(_encode):
46  """Decode base64 encoded content to string
47 
48  Args:
49  _encode : bytes-like object
50 
51  Returns:
52  decoded string
53  """
54  return base64.b64decode(_encode).decode(encoding="utf-8")
55 
56 
57 def get_valid_json(_obj, **kwargs):
58  """Utility function to print pretty json string
59 
60  Args:
61  _obj: python dictionary
62  **kwargs: optional keywords arguments
63  possible values:
64  pretty: for pretty printing
65  Returns:
66  prints formatted json string
67 
68  """
69  pretty = kwargs.get("pretty", False)
70  if pretty:
71  # formatted print layout
72  return json.dumps(_obj, indent=4, sort_keys=True,
73  separators=(',', ':'))
74  # compact encoding
75  return json.dumps(_obj)
76 
77 
79  """Cleanup list values
80 
81  Args:
82  _values: list of values
83 
84  Returns:
85  list of normalized list
86  """
87  assert type(_values) == list
88  sanitized_values = []
89  for val in _values:
90  if hasattr(val, 'strip'):
91  val = val.strip()
92  sanitized_values.append(val)
93  return sanitized_values
94 
95 
96 def sanitize_dict(records_dict):
97  """Sanitize dictionary values
98 
99  Args:
100  records_dict: dictionary values
101 
102  Returns:
103  list of stripped, sanitized dictionary
104  """
105  for k in records_dict.keys():
106  v = records_dict[k]
107  if hasattr(v, 'strip'):
108  v = v.strip()
109  records_dict[k] = v
110  return records_dict
111 
112 
113 def sanitize_insert_collection(_collection):
114  """Cleanup list values
115 
116  Args:
117  _collection: list of dictionary values
118 
119  Returns:
120  list of stripped, sanitized list of dictionary values
121  """
122  assert type(_collection) == list
123  if len(_collection) > 0:
124  assert type(_collection[0]) == dict
125 
126  for records_dict in _collection:
127  records_dict = sanitize_dict(records_dict)
128  return _collection
129 
130 
131 def generate_mdx_hash(_cu, _key):
132  """Generate and return MDX new_id hash
133 
134  Args:
135  _cu: Current CU
136  _key: key to append with _cu
137 
138  Returns:
139  SHA1 encoded hexdigest code
140  """
141  assert _key is not None
142  assert type(_key) == str
143  message = "{}{}".format(_cu, _key)
144  return hashlib.sha1(bytes(message, encoding='utf-8')).hexdigest()
145 
146 
147 def generate_hash(_action, _cu, _secret_key):
148  """Generate and return authentication hash code
149 
150  Args:
151  _action: Data target
152  _cu: Current CU
153 
154  Returns:
155  SHA1 encoded hexdigest code
156  """
157  assert _secret_key is not None
158  assert type(_secret_key) == str
159  message = "".join([_action, _cu, _secret_key])
160  return hashlib.sha1(bytes(message, encoding='utf-8')).hexdigest()
161 
162 
163 def format_error_permission(_e, _file__or_script):
164  """Display error messages for FileNotFound/Permission Denied errors.
165 
166  Args:
167  _e: error message object
168  _file_or_script: script/file that caused error
169 
170  Returns:
171  Formatted PermissionError/FileNotFoundError error message
172  """
173  error_msg = ""
174  # PermissionError
175  if _e.errno == EPERM or _e.errno == EACCES:
176  error_msg = "PermissionError - error({}): {} for: {}\t".format(
177  _e.errno, _e.strerror, _file__or_script)
178  # FileNotFoundError
179  elif _e.errno == ENOENT:
180  error_msg = "FileNotFoundError - error({}): {} as:{}\t".format(
181  _e.errno, _e.strerror, _file__or_script)
182  # returning for logging purpose whenever needed
183  return error_msg
184 
185 
186 def file_exc_decorator(original_file_handler):
187  """decorator to handle all file operation exceptions"""
188  @wraps(original_file_handler)
189  def wrapper(*args, **kwargs):
190  try:
191  return original_file_handler(*args, **kwargs)
192  except (IOError, NameError) as e:
193  error_msg = format_error_permission(e, "")
194  LOGGER.error(error_msg)
195  raise SystemExit(error_msg)
196  except Exception as e:
197  LOGGER.error(e)
198  raise SystemExit(e)
199  except:
200  # sys.exc_info() returns a tuple (type, value, traceback)
201  error_msg = sys.exc_info()[1].args[0]
202  LOGGER.error(error_msg)
203  raise SystemExit(error_msg)
204  return wrapper
205 
206 
207 def get_strip_value_dict(_k, _dict, **kwargs):
208  """Get or Pop an element from a dictionary
209 
210  Args:
211  _k: key to get or pop
212  _dict: dictionary to get or pop an element from
213  **kwargs: Options:
214  default: default value to return
215  pop: do we `get` or `pop` from the dict
216 
217  Returns:
218  value associated with key in a dictionary
219 
220  Raises:
221  ValueError, TypeError if the invalid castto is encountered
222  """
223  default = kwargs.get("default", None)
224  pop = kwargs.get("pop", False)
225 
226  if pop:
227  val = _dict.pop(_k, default)
228  else:
229  val = _dict.get(_k, default)
230 
231  if val in [None, ''] and default not in [None, '']:
232  val = default
233 
234  if hasattr(val, 'strip'):
235  val = val.strip()
236 
237  return val
238 
239 
240 def log_progress(_progress_dict):
241  """Log progress information to be used in progressbar in Monitor
242 
243  Args:
244  _progress_dict: dictionary that contains completed and total stat
245  """
246  LOGGER.info("[PROGRESS] Tables imported: {}/{}".format(
247  _progress_dict["completed"], _progress_dict["total"]))
248 
249 
250 def custom_css_parser(_content):
251  """Simple css parser to convert css attributes to python dict.
252 
253  Args:
254  _content (str): main css content
255 
256  Returns:
257  cssdict (dict): parsed python dictionary
258 
259  Alternative advanced css parsing using tinycss
260  **implementation starts**
261  import tinycss
262  parser = tinycss.make_parser()
263  stylesheet = parser.parse_stylesheet(_content)
264  cssdict = {}
265  for rule in stylesheet.rules:
266  this_rule = rule.selector.as_css()
267  cssdict[this_rule] = {}
268  print (this_rule)
269  for css_attr in rule.declarations:
270  # print (css_attr)
271  cssdict[this_rule][css_attr.name] = css_attr.value.as_css()
272  print (css_attr.name, css_attr.value.as_css())
273  print(stylesheet.errors)
274  return cssdict
275  **implementation ends**
276  """
277 
278  # are we inside /* and */ block?
279  inside_comment_block = False
280 
281  # are we inside { and } block?
282  reading_values = False
283 
284  # main dict to store parsed css file as a dict
285  cssdict = {}
286  # running css rule value eg. body
287  running_css_rule = ""
288  # running css attribute values of current css rule
289  # eg. attributes inside body
290  running_css_attrs_dict = {}
291 
292  # iterate through each line in the css content ("\n" delimited)
293  for line in _content.splitlines():
294  # only after "{" is read
295  if reading_values:
296  # re-initiate running variabels and stop parsing attributes
297  # for the current rule
298  if "}" in line:
299  assert running_css_rule != ""
300 
301  cssdict[running_css_rule] = running_css_attrs_dict
302 
303  running_css_rule = ""
304  running_css_attrs_dict = {}
305  reading_values = False
306  continue
307 
308  # otherwise, keep adding attribtues to the dict
309  # corresponding to the running css rule
310  k_v = line.split(":", 2)
311 
312  k = k_v[0].strip()
313  # handle special case url(https://..)
314  v = ":".join(k_v[1:]).strip()[:-1] # do not include ;
315 
316  running_css_attrs_dict[k] = v
317 
318  # very naive assumption about comment blocks
319  if "/* **" in line and "** */" in line:
320  continue
321 
322  if "/*" in line:
323  inside_comment_block = True
324  continue
325 
326  if "*/" in line:
327  inside_comment_block = False
328  continue
329 
330  # assumption is made that comment blocks are not found inside
331  # { and } blocks
332  if inside_comment_block is False:
333  # we start parsing attribtues from next line
334  if "{" in line:
335  running_css_rule = line.split("{")[0].strip()
336  assert running_css_attrs_dict == {}
337 
338  reading_values = True
339 
340  return cssdict
def log_progress(_progress_dict)
def format_error_permission(_e, _file__or_script)
def get_valid_json(_obj, **kwargs)
def decode_base64(_encode)
def custom_css_parser(_content)
def sanitize_insert_collection(_collection)
def generate_hash(_action, _cu, _secret_key)
def get_ith_batch(_list, batch_index, batch_size)
def encode_base64(_data)
def get_strip_value_dict(_k, _dict, **kwargs)
def sanitize_dict(records_dict)
def file_exc_decorator(original_file_handler)
def sanitize_insert_values(_values)
def generate_mdx_hash(_cu, _key)