Odyssey
ody_migr_settings.py
1 #!/usr/bin/env python
2 """Main module to migrate settings data."""
3 
4 
5 import os
6 import logging
7 import json
8 import re
9 import csv
10 
11 # import only necessary variables for settings
12 from ody_migr_config import (FEATURE_NOT_FOUND,
13  EXPECTED_EMPTY_FEATURE_CODE,
14  NO_FEATURE_CODE_HREF,
15  NO_MENU_LINK_MODE,
16  HCUCONNECT_MODE_CODE_MAP,
17  FEATURE_CODE_HREF_MAP,
18  DEF_MENU_ITEM_PLATFORM,
19  FA_ICON_MAP,
20  BRANDING_DEFAULTS,
21  BRANDING_TARGET_FILE,
22  BRANDING_BCK_FILE,
23  BRANDING_MMTH_FILE,
24  BRANDING_TMPL_FILE,
25  INSERT,
26  INSERT_ONE,
27  SELECT,
28  DELETE,
29  SUMMARY,
30  TABLE_EXISTS,
31  SERVER_URL,
32  ACTION_OPT_CLEAN,
33  ACTION_OPT_MIGRATE,
34  ACTION_OPT_SUMMARY,
35  DATA_OPT_SETTINGS)
36 
37 from ody_migr_transaction import PgTransaction
38 from ody_migr_transaction import pg_crsr_hndlr_decrtr
39 import ody_migr_db_handler as pg_handler
40 from ody_migr_mmth_endpoint import MammothMigration
41 from ody_migr_utils import (decode_base64,
42  custom_css_parser,
43  get_valid_json,
44  format_error_permission,
45  file_exc_decorator,
46  log_progress)
47 
48 LOGGER = logging.getLogger(__name__)
49 
50 
51 # MENU MIGRATION OPERATIONS
52 
53 def _menu_get_feature_code(_menu_dict, _menu_type):
54  """ Rule to generate feature code given the href and hrefExtraParams
55 
56  Also replace the script url by adding .prg in the relevant string
57  location of the href attribute.
58  1. handle single hcu* script
59  2. handle hcuForms/some-html.html patter
60  3. handle "lnapp/AppMain" pattern
61  4. handle hcuConnect with mode attribute as extra parameter
62 
63  Args:
64  _menu_dict: menu record object for this menu/sub-menu item
65  _menu_type: type of menu (D, H or S)
66 
67  Returns:
68  _menu_dict: _menu_dict with updated href path
69  code: feature code
70  """
71  mode = None
72  href = EXPECTED_EMPTY_FEATURE_CODE
73  code = FEATURE_NOT_FOUND
74 
75  # extract hcu* link from the dictionary eg. hcuAccounts, hcuConnect
76  if "href" in _menu_dict and _menu_type != "H":
77  href = _menu_dict["href"].strip()
78 
79  # href is a script name only (no url pattern) other than hcuConnect
80  if "/" not in href and href != "hcuConnect":
81  if href in NO_FEATURE_CODE_HREF:
82  _menu_dict["href"] += ".prg"
83  code = EXPECTED_EMPTY_FEATURE_CODE
84 
85  elif href in FEATURE_CODE_HREF_MAP:
86  code = FEATURE_CODE_HREF_MAP[href]
87  _menu_dict["href"] += ".prg"
88 
89  # href contains hcuForms format like hcuForms/display-eftfrm.html
90  elif "hcuForms" in href:
91  code = FEATURE_CODE_HREF_MAP["hcuForms"]
92  _menu_dict["href"] = _menu_dict["href"].replace(
93  "hcuForms", "hcuForms.prg")
94 
95  # href contains "lnapp/AppMain" pattern
96  elif "lnapp/AppMain" in href:
97  code = "ONLINEAPP"
98 
99  _menu_dict["href"] = "{}/eforms/LoanApp.prg".format(SERVER_URL)
100 
101  # handle hcuConnect and mode parameter value
102  elif href == "hcuConnect" and "hrefExtraParam" in _menu_dict:
103  _menu_dict["href"] += ".prg"
104  # obtain mode
105  if "mode=" in _menu_dict["hrefExtraParam"]:
106  ext_params = _menu_dict["hrefExtraParam"]
107  for ext_param in ext_params.strip().split("&"):
108  k_v_list = ext_param.strip().split("=")
109  if k_v_list[0].strip() == "mode":
110  mode = k_v_list[1].strip()
111  break
112 
113  # if no mode with hcuConnect, ERROR
114  if mode is None:
115  code = FEATURE_NOT_FOUND
116 
117  # mode corresponding to no menu link
118  elif mode is not None and mode in NO_MENU_LINK_MODE:
119  code = EXPECTED_EMPTY_FEATURE_CODE
120 
121  # valid feature code for this specific mode
122  elif mode is not None and mode in HCUCONNECT_MODE_CODE_MAP:
123  code = HCUCONNECT_MODE_CODE_MAP[mode]
124  else:
125  pass
126 
127  return _menu_dict, code
128 
129  else:
130  return _menu_dict, href
131 
132 
133 def _menu_prepare_record(_menu_dict, _type, _order, parent_id, _cu):
134  """Prepare a menu record for cu_featuremenu table (all S, H and D types)
135 
136  Args:
137  _menu_dict: mammoth key-valued pair of data for this menu record
138  _type: type of menu -S, D or H
139  _order: order of display of this menu/sub-menu
140  parent_id: id of the parent menu - only applicable for _type=D
141  _cu: current credit union
142 
143  Returns:
144  record_values:
145  single record: list of values to be inserted in cu_featuremenu
146  """
147  _menu_dict, feature_code = _menu_get_feature_code(_menu_dict, _type)
148 
149  if _type == "S":
150  assert parent_id is None
151  if "hrefExtraParam" not in _menu_dict:
152  _menu_dict["hrefExtraParam"] = ""
153  if _menu_dict["target"] == '_blank':
154  _menu_dict["target"] = 1
155  else:
156  _menu_dict["target"] = 0
157  _menu_dict["fa-icon"] = FA_ICON_MAP.get(
158  _menu_dict["display"]["en_US"], "")
159 
160  menu_item_attr = _menu_dict
161 
162  elif _type == "H":
163  assert parent_id is None
164  menu_item_display = _menu_dict["display"]
165 
166  assert "en_US" in menu_item_display
167  if (menu_item_display["en_US"] in
168  ["Communications", "Profile", "Disclosures"]):
169  collapseGroup = 1
170  else:
171  collapseGroup = 0
172 
173  memAccFilter = ""
174  menu_item_attr = {}
175  menu_item_attr["display"] = menu_item_display
176  menu_item_attr["collapseGroup"] = collapseGroup
177  menu_item_attr["memAccFilter"] = memAccFilter
178  menu_item_attr["fa-icon"] = FA_ICON_MAP.get(
179  menu_item_display["en_US"], "")
180 
181  elif _type == "D":
182  if (_menu_dict["display"]["en_US"].lower() == "my moneydesktop"):
183  _menu_dict["target"] = 1
184  else:
185  _menu_dict["target"] = 0
186 
187  assert parent_id is not None
188  _menu_dict["collapseGroup"] = 0
189  _menu_dict["memAccFilter"] = ""
190  _menu_dict["fa-icon"] = ""
191  if "hrefExtraParam" not in _menu_dict:
192  _menu_dict["hrefExtraParam"] = ""
193  menu_item_attr = _menu_dict
194 
195  else:
196  pass
197 
198  record_values = [
199  _cu,
200  _order,
201  feature_code,
202  parent_id,
203  _type,
204  get_valid_json(DEF_MENU_ITEM_PLATFORM),
205  get_valid_json(menu_item_attr)
206  ]
207 
208  return record_values
209 
210 
211 @pg_crsr_hndlr_decrtr
212 def menu_insert_cu_featuremenu(settings_migr_trns, _menu_main, _cu):
213  """Prepare and insert menu records in the database
214 
215  Each root-level menu type are identified as type S or H. For H, type,
216  sub-menus are sub-identified as type D.
217 
218  For all menu/sub-menu record, it first obtains the mapping of Feature Code
219  for each record using the href and hrefExtraParam attributes. Then it
220  prepares the list of values to be inserted in the cu_featuremenu table.
221 
222  The records must be inserted one by one as there are dependencies among the
223  records (menu_item_id column of H type should be associated with D types).
224 
225  Args:
226  settings_migr_trns: PgTransaction instance handling the
227  current transaction
228  _menu_main: menu dictionary collection
229  _cu: current credit union code
230 
231  Raises:
232  psycopg2.Error, psycopg2.Warning on db operation errors
233  """
234 
235  tbl_name = "cu_featuremenu"
236 
237  # cu_featuremenu column names
238  columns = [
239  "cu",
240  "display_order",
241  "feature_code",
242  "parent_item_id",
243  "menu_item_type",
244  "menu_item_platform",
245  "menu_item_attr"
246  ]
247 
248  # counter for menu_order (increments for all levels: S, H and D)
249  menu_order = 0
250  for ind, menu_item in enumerate(_menu_main):
251  # suppressdetail = menu_item["suppressdetail"]
252  menu_order += 1
253 
254  # if type(menu_item["detail"]) is not list:
255  # menu_item["detail"] = [menu_item["detail"]]
256 
257  # standalone menu ("S types")
258  if len(menu_item["detail"]) == 1:
259 
260  # assert suppressdetail == 1
261  standalone_menu = menu_item["detail"][0]
262  values = _menu_prepare_record(
263  standalone_menu, "S", menu_order, None, _cu)
264  assert len(columns) == len(values)
265 
266  settings_migr_trns(
267  INSERT_ONE,
268  tbl_name,
269  columns,
270  values
271  )
272 
273  # group of sub-menus ("H types")
274  else:
275  # head of the group
276  values = _menu_prepare_record(
277  menu_item, "H", menu_order, None, _cu)
278  assert len(columns) == len(values)
279 
280  settings_migr_trns(
281  INSERT_ONE,
282  tbl_name,
283  columns,
284  values,
285  returning_col="menu_item_id"
286  )
287  # get the curerntly inserted PK id
288  parent_id = settings_migr_trns.cur.fetchone()[0]
289 
290  # children of this group ("D types")
291  for ind_sub, sub_menu in enumerate(menu_item["detail"]):
292  menu_order += 1
293  values = _menu_prepare_record(
294  sub_menu, "D", menu_order, parent_id, _cu)
295  assert len(columns) == len(values)
296 
297  settings_migr_trns(
298  INSERT_ONE,
299  tbl_name,
300  columns,
301  values
302  )
303 
304 
305 # BRANDING MIGRATION OPERATIONS
306 
307 def _brnd_update_attr(_m_rule,
308  _m_key,
309  update_with,
310  _ody_attr,
311  _report_list,
312  _updated_branding_attrs):
313  """Update a single css attribute
314 
315  Also write the udpate report to the report list (_report_list)
316 
317  Args:
318  _m_rule: mammoth css rule eg.body
319  _m_key: mammoth css rule's one attribute: eg. color
320  update_wth: css attribute mapped value for Odyssey
321  _ody_attr: one of the keys from default
322  `$styleDefaults` associative
323  array in /app/web/monitor/
324  scriptssiteSetColor.prg
325  _report_list: list to add attribtue changes for update
326  report
327  _updated_branding_attrs: dictionary to update attribute changes to
328  write to a branding file
329 
330  """
331  default = BRANDING_DEFAULTS[_ody_attr]
332  # update if the attribute value is different from default
333  if default != update_with:
334  _updated_branding_attrs[_ody_attr] = update_with
335  _report_list.append("[Mammoth] {} -> {} ===> [Odyssey] '{}' "
336  "(default '{}') -> {}"
337  .format(_m_rule, _m_key,
338  _ody_attr, default, update_with))
339 
340 
341 def _brnd_update_styles(_brand_mammoth):
342  """Map mammoth branding css content to Odyssey
343 
344  Useful reources:
345  /app/web/monitor/scriptssiteSetColor{.prg, .data}
346  /homecu/public_html/bankingIncludes/css/brand.css
347 
348  Args:
349  _brand_mammoth: dictionary of css attributes from Mammoth brand content
350 
351  Results:
352  Formatted update result of what values changed from Mammoth to Odyssey
353 
354  Example report: (it would be a compressed, single line json string to
355  make it easier to deal with it in log monitoring services.)
356  [Mammoth] .cu-primary-color -> background-color ===>
357  [Odyssey] 'headerBackground' (default '#24417A') -> '#01707B'
358  [Mammoth] .cu-primary-color -> background-color ===>
359  [Odyssey] 'footerBackground' (default '#E7E8EA') -> '#01707B'
360  [Mammoth] .cu-primary-color -> color ===>
361  [Odyssey] 'headerForeground' (default '#FFFFFF') -> '#df1313'
362  [Mammoth] .cu-primary-color -> color ===>
363  [Odyssey] 'footerForeground' (default '#777777') -> '#df1313'
364  [Mammoth] body -> font-family ===>
365  [Odyssey] 'fontFamily' (default 'Helvetica, "Helvetica Neue",
366  Arial, sans-serif') -> 'helvetica, verdana, sans-serif'
367  [Mammoth] body -> background-color ===>
368  [Odyssey] 'menuBackground' (default '#F8F8F8') -> '#eaeaea'
369  [Mammoth] body -> color ===>
370  [Odyssey] 'menuForeground' (default '#337AB7') -> '#303030'
371  [Mammoth] #content -> background ===>
372  [Odyssey] 'contentBackground' (default '#FFFFFF') -> '#fff'
373  [Mammoth] #content -> color ===>
374  [Odyssey] 'contentForeground' (default '#333333') -> '#000000'
375  [Mammoth] #content -> border ===>
376  [Odyssey] 'contentBorderWidth' (default '0px') -> '1px'
377  [Mammoth] #content -> border ===>
378  [Odyssey] 'contentBorderStyle' (default 'solid') -> 'solid'
379  [Mammoth] #content -> border ===>
380  [Odyssey] 'contentBorderColor' (default '#000000') -> '#afafaf'
381  [Mammoth] #homecuBannerArea -> background-color ===>
382  [Odyssey] 'bannerBackground' (default '#161620') -> '#333333'
383  [Mammoth] #homecuBannerArea -> height ===>
384  [Odyssey] 'bannerHeight' (default '60px') -> '60px'
385  [Mammoth] #logo_wrapper .logo_content -> background ===>
386  [Odyssey] 'logoBackground' (default 'transparent') -> '#FFFFFF'
387  [Mammoth] #logo_wrapper .logo_content -> background-size ===>
388  [Odyssey] 'logoHeightDesktop' (default '75px') -> '80px'
389  [Mammoth] .cu-secondary-color -> background-color ===>
390  [Odyssey] 'menuGroupBackground' (default '#1B8DD6') -> '#76B84B'
391  [Mammoth] .cu-secondary-color -> background-color ===>
392  [Odyssey] 'menuGroupBackgroundLighter'
393  (default '#2198E3') -> '#76B84B'
394  [Mammoth] .cu-secondary-color -> color ===>
395  [Odyssey] 'menuGroupForeground' (default '#FFFFFF') -> '#FFFFFF'
396  """
397  update_report = []
398  updated_branding_attrs = BRANDING_DEFAULTS.copy()
399 
400  for _mammoth_rule, _mammoth_attrs in _brand_mammoth.items():
401 
402  if _mammoth_rule == "body":
403 
404  m_attr_key = "font-family"
406  _mammoth_rule,
407  m_attr_key,
408  _mammoth_attrs[m_attr_key],
409  "fontFamily",
410  update_report,
411  updated_branding_attrs
412  )
413 
414  m_attr_key = "background-color"
416  _mammoth_rule,
417  m_attr_key,
418  _mammoth_attrs[m_attr_key],
419  "menuBackground",
420  update_report,
421  updated_branding_attrs
422  )
423 
424  m_attr_key = "color"
426  _mammoth_rule,
427  m_attr_key,
428  _mammoth_attrs[m_attr_key],
429  "menuForeground",
430  update_report,
431  updated_branding_attrs
432  )
433 
434  elif _mammoth_rule == ".cu-primary-color":
435 
436  m_attr_key = "background-color"
438  _mammoth_rule,
439  m_attr_key,
440  _mammoth_attrs[m_attr_key],
441  "headerBackground",
442  update_report,
443  updated_branding_attrs
444  )
446  _mammoth_rule,
447  m_attr_key,
448  _mammoth_attrs[m_attr_key],
449  "footerBackground",
450  update_report,
451  updated_branding_attrs
452  )
453 
454  m_attr_key = "color"
456  _mammoth_rule,
457  m_attr_key,
458  _mammoth_attrs[m_attr_key],
459  "headerForeground",
460  update_report,
461  updated_branding_attrs
462  )
464  _mammoth_rule,
465  m_attr_key,
466  _mammoth_attrs[m_attr_key],
467  "footerForeground",
468  update_report,
469  updated_branding_attrs
470  )
471 
472  elif _mammoth_rule == "#logo_wrapper .logo_content":
473 
474  # exclude url part
475  m_attr_key = "background"
476  m_attr_val = _mammoth_attrs[m_attr_key].split()[0].strip()
478  _mammoth_rule,
479  m_attr_key,
480  m_attr_val,
481  "logoBackground",
482  update_report,
483  updated_branding_attrs
484  )
485 
486  # take only height part, discard width
487  m_attr_key = "background-size"
488  m_attr_val = _mammoth_attrs[m_attr_key].split()[1].strip()
490  _mammoth_rule,
491  m_attr_key,
492  m_attr_val,
493  "logoHeightDesktop",
494  update_report,
495  updated_branding_attrs
496  )
497 
498  elif _mammoth_rule == "#content":
499 
500  m_attr_key = "background"
502  _mammoth_rule,
503  m_attr_key,
504  _mammoth_attrs[m_attr_key],
505  "contentBackground",
506  update_report,
507  updated_branding_attrs
508  )
509 
510  m_attr_key = "color"
512  _mammoth_rule,
513  m_attr_key,
514  _mammoth_attrs[m_attr_key],
515  "contentForeground",
516  update_report,
517  updated_branding_attrs
518  )
519 
520  m_attr_key = "border"
521  m_attr_val = _mammoth_attrs[m_attr_key].split()
523  _mammoth_rule,
524  m_attr_key,
525  m_attr_val[0].strip(),
526  "contentBorderWidth",
527  update_report,
528  updated_branding_attrs
529  )
531  _mammoth_rule,
532  m_attr_key,
533  m_attr_val[1].strip(),
534  "contentBorderStyle",
535  update_report,
536  updated_branding_attrs
537  )
539  _mammoth_rule,
540  m_attr_key,
541  m_attr_val[2].strip(),
542  "contentBorderColor",
543  update_report,
544  updated_branding_attrs
545  )
546 
547  elif _mammoth_rule == ".cu-secondary-color":
548 
549  m_attr_key = "background-color"
551  _mammoth_rule,
552  m_attr_key,
553  _mammoth_attrs[m_attr_key],
554  "menuGroupBackground",
555  update_report,
556  updated_branding_attrs
557  )
559  _mammoth_rule,
560  m_attr_key,
561  _mammoth_attrs[m_attr_key],
562  "menuGroupBackgroundLighter",
563  update_report,
564  updated_branding_attrs
565  )
566 
567  m_attr_key = "color"
569  _mammoth_rule,
570  m_attr_key,
571  _mammoth_attrs[m_attr_key],
572  "menuGroupForeground",
573  update_report,
574  updated_branding_attrs
575  )
576 
577  elif _mammoth_rule == "#homecuBannerArea":
578 
579  m_attr_key = "background-color"
581  _mammoth_rule,
582  m_attr_key,
583  _mammoth_attrs[m_attr_key],
584  "bannerBackground",
585  update_report,
586  updated_branding_attrs
587  )
588 
589  m_attr_key = "height"
591  _mammoth_rule,
592  m_attr_key,
593  _mammoth_attrs[m_attr_key],
594  "bannerHeight",
595  update_report,
596  updated_branding_attrs
597  )
598 
599  return get_valid_json(update_report), updated_branding_attrs
600 
601 
603  """Utility function to return relevant file paths for branding content"""
604  cu_lower = _cu.lower()
605  return (BRANDING_TARGET_FILE.format(cu_lower, cu_lower),
606  BRANDING_BCK_FILE.format(cu_lower, cu_lower, cu_lower),
607  BRANDING_MMTH_FILE.format(cu_lower),
608  BRANDING_TMPL_FILE)
609 
610 
611 def _brnd_migrate_content(_b64_encoded_content, _cu):
612  """Decode base64 encoded content and dump to a target file
613 
614  - Original mammoth file is saved as BRANDING_MMTH_FILE
615  - Migrated odyssey file is saved as BRANDING_TARGET_FILE
616  - Backup odyssey file is saved as BRANDING_BCK_FILE
617 
618  Args:
619  _b64_encoded_content: base64 encoded content
620  _cu: current cu code
621 
622  Raises:
623  SystemExit: if IOError, NameError or PermissionError is caught
624  """
625  target_file, bck_file, mmth_file, template_file = _get_branding_fnames(_cu)
626 
627  # keep a temporary copy of a previous target fiel (if exist)
628  target_file_exist = False
629 
630  try:
631  # if the cu specific branding css file exist, copy it to backup
632  if os.path.exists(target_file):
633  target_file_exist = True
634  with open(target_file, "r") as f_init:
635  with open(bck_file, "w") as f_bck:
636  f_bck.write(f_init.read())
637 
638  # write content to target file
639  with open(target_file, 'w') as f_main:
640  # decode base64 encoded content to string
641  str_content = decode_base64(_b64_encoded_content)
642 
643  # save the imported css file as brand.css as mammoth version
644  with open(mmth_file, "w") as f_mmth:
645  f_mmth.write(str_content)
646 
647  # obtain main customizable css content
648  css_content_list = str_content.split("/* ** CUSTOM ** */")
649 
650  # only the css content excluding contents after CUSTOM part
651  main_branding_css = css_content_list[0].strip()
652 
653  # there is another commented out function that uses 'tinycss' - a
654  # python library for advanced css content parsing
655  brand_mammoth = custom_css_parser(main_branding_css)
656 
657  # update default branding css content received from mammoth
658  # replaces defaults
659  update_report, updated_branding_attrs = _brnd_update_styles(
660  brand_mammoth)
661 
662  LOGGER.debug("[BRANDING] Mammoth to Ody changes:\t{}".format(
663  update_report))
664 
665  # read template and replace template placeholdes with actual values
666  rendered_template_css = ""
667  with open(template_file, "r") as f_template:
668  rendered_template_css = f_template.read()
669  for k, v in updated_branding_attrs.items():
670  rendered_template_css = rendered_template_css.replace(
671  "{{" + k + "}}", v)
672 
673  # write the updated css content to a target css file for CU
674  f_main.write(rendered_template_css)
675 
676 
677  except (IOError, NameError) as err:
678  # if target file existed earlier, restore if anything goes wrong
679  if target_file_exist and os.path.exists(bck_file):
680  os.rename(bck_file, target_file)
681 
682  formatted_err = format_error_permission(err, "")
683  LOGGER.error(formatted_err)
684  raise SystemExit(formatted_err)
685 
686 
687 # MIGRATE SETTINGS CONTENT FROM MAMMOTH TO ODYSSEY
688 
689 def move_settings(settings_migr_trns, _response, _verbose, _cu):
690  """Migrate settings data obtained from Mammoth to Odyssey.
691 
692  Args:
693  settings_migr_trns: settings migration transaction instance
694  _response (dict): python dictionary represetntation of the
695  response json dictionary from mammoth
696  _verbose: verbosity of log
697  _cu: current credit union code
698  """
699  # transfer cuadmin data
700 
701  progress_dict = {"completed": 0, "total": 4}
702 
703  cuadmin_coll = _response["data"]["cuadmin"]
704  where_cu_condition = {"cu": _cu}
705  assert type(cuadmin_coll) == list
706  for cuadmin_record in cuadmin_coll:
707  cuadmin_record["settings"] = get_valid_json({"profile" : "DEF"})
708  if _cu.lower().strip() == "cruisecu" and os.getenv('DEVMODE') == '1':
709  cuadmin_record["liveserver"] = \
710  "https://int-apl0.homecu.net/hculive/xenia-homecu-faux.mp"
711  else:
712  cuadmin_record["liveserver"] = "#"
713 
714  settings_migr_trns(
715  INSERT,
716  "cuadmin",
717  where_conditions={"cu": _cu},
718  collection=cuadmin_coll
719  )
720 
721  if _verbose:
722  settings_migr_trns(
723  SELECT,
724  "cuadmin",
725  where_conditions=where_cu_condition
726  )
727 
728  progress_dict["completed"] += 1
729  log_progress(progress_dict)
730 
731  # transfer custom-content data
732  custom_content_coll = _response["data"]["custom-content"]
733  assert type(custom_content_coll) == list
734 
735  settings_migr_trns(
736  INSERT,
737  "cucmsfrags",
738  where_conditions=where_cu_condition,
739  collection=custom_content_coll
740  )
741 
742  if _verbose:
743  settings_migr_trns(
744  SELECT,
745  "cucmsfrags",
746  where_conditions=where_cu_condition
747  )
748 
749  progress_dict["completed"] += 1
750  log_progress(progress_dict)
751 
752  # transfer menu content
753  menu_content_coll = _response["data"]["menu"]
754  assert type(menu_content_coll) == list
755 
756  # prepare row records and populate cu_featuremenu table
757  menu_insert_cu_featuremenu(settings_migr_trns, menu_content_coll, _cu)
758 
759  if _verbose:
760  settings_migr_trns(
761  SELECT,
762  "cu_featuremenu",
763  where_conditions=where_cu_condition
764  )
765 
766  progress_dict["completed"] += 1
767  log_progress(progress_dict)
768 
769  # populate lnappconfig table for loanapps
770  mmth_lnappconfig_coll = _response["data"]["lnappconfig"]
771  for lnappconfig_record in mmth_lnappconfig_coll:
772  dict_lnapp_config= json.loads(lnappconfig_record['appconfig'])
773 
774  # replace wwwX.homecu.net with TICKET_DOMAIN value, if set
775  # otherwise, localhost:8000
776  subdomain = os.getenv("TICKET_DOMAIN", "localhost:8000")
777  subdomain = subdomain if subdomain.strip() != "" else "localhost:8000"
778  dict_lnapp_config["configLoanCSS"] = re.sub("(.*)(www[356]{1}\.homecu.net)(.*)",
779  "\g<1>{}\g<3>".format(subdomain),
780  dict_lnapp_config["configLoanCSS"])
781 
782  # convert back the value to json string
783  lnappconfig_record['appconfig'] = get_valid_json(dict_lnapp_config)
784 
785  settings_migr_trns(
786  INSERT,
787  "lnappconfig",
788  collection=mmth_lnappconfig_coll
789  )
790 
791  progress_dict["completed"] += 1
792  log_progress(progress_dict)
793 
794  # Migrate file based contents after the db tables are migrated first
795  # transfer branding content
796  branding_content = _response["data"]["branding"]
797 
798  # move brand.css file to CU specific target location
799  _brnd_migrate_content(branding_content, _cu)
800 
801  LOGGER.info("Branding content migrated!")
802 
803  # We wait until the branding content migration completion for db commit.
804  # Looks like we did it, good to commit the transaction!
805  settings_migr_trns.commit()
806 
807 
808 @file_exc_decorator
809 @pg_crsr_hndlr_decrtr
810 def cleanup_settings(cleanup_transaction, _cu, **kwargs):
811  """Cleanup settings content
812 
813  If kwargs.get('commit_later') flag is True, we do not commit the
814  transaction here (and only cleanup database tables, not the files),
815  meaning that the the cleanup may have been executed before actual
816  migration. If commit_later flag is False, we commit the transaction
817  here and proceed with the file cleanup.
818 
819  Args:
820  cleanup_transaction: instance of PgTransaction to which this cleanup
821  process is part of
822  _cu: current credit union code
823  **kwargs: Options:
824  commit_later: flag to commit transaction later or now
825 
826  Raises:
827  SystemExit: if IOError, NameError or PermissionError is caught
828  psycopg2.Error, psycopg2.Warning on db operation errors
829  """
830  commit_later = kwargs.get("commit_later", False)
831 
832  # all cleanup tables conditioned on cu
833  clean_up_tables = ["cucmsfrags", "cuadmin", "cu_featuremenu", "lnappconfig"]
834  for tbl in clean_up_tables:
835  cleanup_transaction(
836  DELETE,
837  tbl,
838  where_conditions={"cu": _cu}
839  )
840 
841  if not commit_later:
842  (target_file,
843  bck_file,
844  mmth_file,
845  template_file) = _get_branding_fnames(_cu)
846 
847  # cleanup file based contents, first
848  if os.path.exists(target_file):
849  os.remove(target_file)
850  if os.path.exists(bck_file):
851  os.remove(bck_file)
852  if os.path.exists(mmth_file):
853  os.remove(mmth_file)
854 
855  LOGGER.info("Branding content deleted.")
856 
857  # then, commit all the table deletions here
858  cleanup_transaction.commit()
859  # display summary after cleanup
860  cleanup_transaction(
861  SUMMARY,
862  DATA_OPT_SETTINGS,
863  _cu.lower()
864  )
865 
866 
867 @pg_crsr_hndlr_decrtr
868 def migrate_settings(cu, server, action, user, passwd, verbose):
869  """Entry point to the settings migration.
870 
871  Args:
872  cu: current credit union code
873  server: Mammoth endpoint server
874  action: one of the migration options to be
875  performed on settings data category
876  user: Mammoth monitor username
877  passwd: Mammoth monitor password
878  verbose: level of verbosity
879 
880  Raises:
881  psycopg2.Error, psycopg2.Warning on db operation errors
882  """
883  # check if the required tables exist
884  with pg_handler.PGSession() as conn:
885  with conn.cursor() as cur:
886  tables_exist_transaction = PgTransaction(conn, cur)
887 
888  (tables_exist_flag,
889  do_not_exist_list) = tables_exist_transaction(
890  TABLE_EXISTS,
891  DATA_OPT_SETTINGS,
892  cu.lower()
893  )
894 
895  if not tables_exist_flag:
896  error_msg = ("Stopping migration. Following table"
897  " schemas do not exist: {}").format(
898  get_valid_json(", ".join(do_not_exist_list)))
899  LOGGER.error(error_msg)
900  raise SystemExit(error_msg)
901  else:
902  LOGGER.info(
903  "All required target tables exist! Continuing migration..")
904 
905  # migrate settings data
906  if action == ACTION_OPT_MIGRATE:
907  # get settings data from Mammoth
908  settings_migration = MammothMigration(
909  cu,
910  DATA_OPT_SETTINGS,
911  server,
912  user,
913  passwd
914  )
915  settings_migration.run()
916 
917  LOGGER.info("Initiating migrating response data to odyssey")
918  with pg_handler.PGSession() as conn:
919  with conn.cursor() as cur:
920  # main transaction for settigns migration
921  settings_migr_trns = PgTransaction(conn, cur)
922 
923  # display summary before migration
924  settings_migr_trns(
925  SUMMARY,
926  DATA_OPT_SETTINGS,
927  cu,
928  msg_prefix="BEFORE"
929  )
930 
931  # we will only commit this cleanup later together with
932  # all the migrations for settings
933  cleanup_settings(settings_migr_trns, cu, commit_later=True)
934  LOGGER.info(
935  "Running initial db cleanup before migration...")
936 
937  # migrate obtained data to Odyssey
939  settings_migr_trns,
940  settings_migration.response,
941  verbose,
942  cu
943  )
944  # display summary after migration
945  settings_migr_trns(
946  SUMMARY,
947  DATA_OPT_SETTINGS,
948  cu,
949  msg_prefix="AFTER"
950  )
951  LOGGER.info("Settings Migration Completed!")
952 
953  # cleanup settings data
954  elif action == ACTION_OPT_CLEAN:
955  with pg_handler.PGSession() as conn:
956  with conn.cursor() as cur:
957  # main transaction for settings cleanup
958  settings_delete_trns = PgTransaction(conn, cur)
959  cleanup_settings(settings_delete_trns, cu)
960  LOGGER.info("Settings cleanup completed!")
961 
962  # log display settings tables' records count summary
963  elif action == ACTION_OPT_SUMMARY:
964  with pg_handler.PGSession() as conn:
965  with conn.cursor() as cur:
966  settings_summary_transaction = PgTransaction(conn, cur)
967  # display_summary(settings_summary_transaction, cu)
968 
969  settings_summary_transaction(
970  SUMMARY,
971  DATA_OPT_SETTINGS,
972  cu
973  )
def _brnd_update_styles(_brand_mammoth)
def _menu_prepare_record(_menu_dict, _type, _order, parent_id, _cu)
def _menu_get_feature_code(_menu_dict, _menu_type)
def migrate_settings(cu, server, action, user, passwd, verbose)
def cleanup_settings(cleanup_transaction, _cu, **kwargs)
def menu_insert_cu_featuremenu(settings_migr_trns, _menu_main, _cu)
def _brnd_update_attr(_m_rule, _m_key, update_with, _ody_attr, _report_list, _updated_branding_attrs)
def _brnd_migrate_content(_b64_encoded_content, _cu)
def move_settings(settings_migr_trns, _response, _verbose, _cu)