Updates on error logging and handling

- iterative error log storage
- config like value storage
- updates logging format for improved readablility

Change-Id: I171a1b44452c1225340a7d7b1f7593ab9b8ce7c2
Related-PROD: PROD-28199
diff --git a/cfg_checker/helpers/errors.py b/cfg_checker/helpers/errors.py
index b124315..ca8a8da 100644
--- a/cfg_checker/helpers/errors.py
+++ b/cfg_checker/helpers/errors.py
@@ -1,8 +1,21 @@
-from cfg_checker.common import logger
+import os
+
+from cfg_checker.common import file_utils as fu
+from cfg_checker.common import logger, logger_cli
+from cfg_checker.common.config_file import ConfigFile
 from cfg_checker.common.exception import ErrorMappingException
+from cfg_checker.common.settings import pkg_dir
 
 
 class ErrorIndex(object):
+    # logs folder filenames
+    _error_logs_folder_name = ".cfgerrors"
+    _conf_filename = "conf"
+    # config file object
+    conf = None
+    # iteration counter
+    _iteration = 0
+    # local vars for codes
     _area_code = ""
     _delimiter = ""
     _index = 0
@@ -16,6 +29,62 @@
         self._delimiter = delimiter
         self._index += 1
 
+        # init the error log storage folder
+        _folder = os.path.join(pkg_dir, self._error_logs_folder_name)
+        self._conf_filename = os.path.join(
+            _folder,
+            self._conf_filename
+        )
+
+        if not os.path.exists(_folder):
+            # it is not exists, create it
+            os.mkdir(_folder)
+            logger_cli.debug(
+                "... error logs folder '{}' created".format(_folder)
+            )
+        else:
+            logger_cli.debug(
+                "... error logs folder is at '{}'".format(_folder)
+            )
+        if not os.path.exists(self._conf_filename):
+            # put file with init values
+            self.conf = ConfigFile(self._area_code.lower())
+            self.conf.set_value('iteration', self._iteration)
+            self.conf.save_config(filepath=self._conf_filename)
+            logger_cli.debug(
+                "... create new config file '{}'".format(
+                    self._conf_filename
+                )
+            )
+        else:
+            # it exists, try to load latest run
+            self.conf = ConfigFile(
+                self._area_code.lower(),
+                filepath=self._conf_filename
+            )
+            # it is loaded, update iteration from file
+            self._iteration = self.conf.get_value('iteration', value_type=int)
+            self._iteration += 1
+        logger_cli.debug(" ... starting iteration {}".format(self._iteration))
+
+    def save_iteration_data(self):
+        # save error log
+        _filename = "-".join([self._area_code.lower(), "errors"])
+        _filename += "." + str(self._iteration)
+        _log_filename = os.path.join(
+            pkg_dir,
+            self._error_logs_folder_name,
+            _filename
+        )
+        fu.write_lines_to_file(_log_filename, self.get_errors(as_list=True))
+        fu.append_line_to_file(_log_filename, "")
+        fu.append_lines_to_file(_log_filename, self.get_summary(as_list=True))
+        logger_cli.debug("... saved errors to '{}'".format(_log_filename))
+
+        # save last iteration number
+        self.conf.set_value('iteration', self._iteration)
+        self.conf.save_config()
+
     def _format_error_code(self, index):
         _t = "{:02d}".format(self._errors[index]['type'])
         _i = "{:04d}".format(index)
@@ -27,9 +96,9 @@
         _code = self._format_error_code(index)
         # prepare data as string list
         _d = self._errors[index]['data']
-        _data = ["{}: {}".format(_k, _v) for _k, _v in _d.iteritems()]
+        _data = ["    {}: {}".format(_k, _v) for _k, _v in _d.iteritems()]
         # format message
-        _msg = "### {}: {}\n{}".format(
+        _msg = "### {}:\n    Description: {}\n{}".format(
             _code,
             self._get_error_type_text(self._errors[index]['type']),
             "\n".join(_data)
@@ -89,9 +158,14 @@
         else:
             return "Unknown error index of {}".format(index)
 
-    def get_summary(self, print_zeros=True):
+    def get_summary(self, print_zeros=True, as_list=False):
         # create summary with counts per error type
-        _list = []
+        _list = "\n{:=^8s}\n{:^8s}\n{:=^8s}".format(
+            "=",
+            "Totals",
+            "="
+        ).splitlines()
+
         for _type in self._types.keys():
             _len = len(
                 filter(
@@ -112,12 +186,27 @@
                 )
             )
 
-        return "\n".join(_list)
+        _total_errors = self.get_errors_total()
 
-    def get_errors_as_list(self):
-        # create list of strings with error messages
-        _list = []
-        for _idx in range(0, self._index - 1):
-            _list.append("{}".format(self.get_error(_idx)))
+        _list.append('-'*20)
+        _list.append("{:5d} total errors found\n".format(_total_errors))
+        if as_list:
+            return _list
+        else:
+            return "\n".join(_list)
 
-        return _list
+    def get_errors(self, as_list=False):
+        _list = ["# Errors"]
+        # Detailed errors
+        if self.get_errors_total() > 0:
+            # create list of strings with error messages
+            for _idx in range(1, self._index - 1):
+                _list.append(self._format_error(_idx))
+                _list.append("\n")
+        else:
+            _list.append("-> No errors")
+
+        if as_list:
+            return _list
+        else:
+            return "\n".join(_list)