From 0169725f9c3318e94b23e34b6e9fbe95e900f813 Mon Sep 17 00:00:00 2001
From: David Luevano Alvarado <david@luevano.xyz>
Date: Mon, 18 Apr 2022 00:48:35 -0600
Subject: add logging to database, discovery and pyssg

---
 src/pyssg/database.py  | 71 +++++++++++++++++++++++++++++++++++++++++---------
 src/pyssg/discovery.py | 40 +++++++++++++++++++++-------
 src/pyssg/pyssg.py     | 10 +++----
 3 files changed, 94 insertions(+), 27 deletions(-)

diff --git a/src/pyssg/database.py b/src/pyssg/database.py
index 1b421c0..03eeb73 100644
--- a/src/pyssg/database.py
+++ b/src/pyssg/database.py
@@ -1,92 +1,139 @@
 import os
+import sys
+import logging
+from logging import Logger
+
+log: Logger = logging.getLogger(__name__)
 
 
 # db class that works for both html and md files
 class Database:
+    COLUMN_NUM: int = 4
+
     def __init__(self, db_path: str):
+        log.debug('initializing the page db on path "%s"', db_path)
         self.db_path: str = db_path
         self.e: dict[str, tuple[float, float, list[str]]] = dict()
 
 
     # updates the tags for a specific entry (file)
+    #   file_name only contains the entry name (without the absolute path)
     def update_tags(self, file_name: str,
                     tags: list[str]) -> None:
         if file_name in self.e:
-            cts, mts, _ = self.e[file_name]
+            log.debug('updating tags for entry "%s"', file_name)
+            cts, mts, old_tags = self.e[file_name]
+            log.debug('entry "%s" old content: (%s, %s, (%s))',
+                      file_name, cts, mts, ', '.join(old_tags))
             self.e[file_name] = (cts, mts, tags)
+            log.debug('entry "%s" new content: (%s, %s, (%s))',
+                      file_name, cts, mts, ', '.join(tags))
+        else:
+            log.error('can\'t update tags for entry "%s",'
+                      ' as it is not present in db', file_name)
+            sys.exit(1)
 
 
     # returns a bool that indicates if the entry
     # was (includes new entries) or wasn't updated
-    # 0.0 means no mod
     def update(self, file_name: str,
                remove: str=None) -> bool:
+        log.debug('updating entry for file "%s"', file_name)
         # initial default values
         f: str = file_name
         tags: list[str] = []
         if remove is not None:
             f = file_name.replace(remove, '')
+            log.debug('removed "%s" from "%s": "%s"', remove, file_name, f)
 
 
         # get current time, needs actual file name
         time: float = os.stat(file_name).st_mtime
+        log.debug('modified time for "%s": %f', file_name, time)
 
         # three cases, 1) entry didn't exist,
         # 2) entry hasn't been mod and,
         # 3) entry has been mod
         #1)
         if f not in self.e:
+            log.debug('entry "%s" didn\'t exist, adding with defaults', f)
             self.e[f] = (time, 0.0, tags)
             return True
 
         old_time, old_mod_time, tags = self.e[f]
+        log.debug('entry "%s" old content: (%s, %s, (%s))',
+                  f, old_time, old_mod_time, ', '.join(tags))
 
         # 2)
         if old_mod_time == 0.0:
             if time > old_time:
+                log.debug('entry "%s" has been modified for the first'
+                          ' time, updating', f)
                 self.e[f] = (old_time, time, tags)
+                log.debug('entry "%s" new content: (%s, %s, (%s))',
+                          f, old_time, time, ', '.join(tags))
                 return True
         # 3)
         else:
             if time > old_mod_time:
+                log.debug('entry "%s" has been modified, updating', f)
                 self.e[f] = (old_time, time, tags)
+                log.debug('entry "%s" new content: (%s, %s, (%s))',
+                          f, old_time, time, ', '.join(tags))
                 return True
 
+        log.warning('none of the case scenarios for updating'
+                    ' entry "%s" matched', f)
         return False
 
 
     def write(self) -> None:
-        with open(self.db_path, 'w') as file:
+        log.debug('writing db')
+        with open(self.db_path, 'w', newline='\n') as file:
             # write each k,v pair in dict to db file
             for k, v in self.e.items():
+                log.debug('parsing row for page "%s"', k)
                 t: str = None
+                row: str = None
                 if len(v[2]) == 0:
                     t = '-'
                 else:
                     t = ','.join(v[2])
-                file.write(f'{k} {v[0]} {v[1]} {t}\n')
+
+                row = f'{k} {v[0]} {v[1]} {t}'
+                log.debug('writing row: "%s"', row)
+                file.write(row)
 
 
     def read(self) -> None:
-        # only if the path exists and it is a file
         if os.path.exists(self.db_path) and os.path.isfile(self.db_path):
-            # get all db file lines
-            lines: list[str] = None
+            rows: list[str] = None
+            log.debug('reading db')
             with open(self.db_path, 'r') as file:
-                lines = file.readlines()
+                rows = file.readlines()
+            log.info('db contains %d rows', len(rows))
 
             # parse each entry and populate accordingly
             l: list[str] = None
             # l=list of values in entry
-            for line in lines:
-                l = tuple(line.strip().split())
-                if len(l) != 4:
-                    raise Exception('db entry doesn\'t contain 4 elements')
+            log.debug('parsing rows from db')
+            for i, row in enumerate(rows):
+                log.debug('row %d content: "%s"', i, row)
+                l = tuple(row.strip().split())
+                if len(l) != self.COLUMN_NUM:
+                    log.critical('row doesn\t contain %s columns,'
+                                 ' contains %d elements; row %d content: "%s"',
+                                 self.COLUMN_NUM, len(l), i, row)
+                    sys.exit(1)
 
                 t: list[str] = None
                 if l[3] == '-':
                     t = []
                 else:
                     t = l[3].split(',')
+                log.debug('tag content: (%s)', ', '.join(t))
 
                 self.e[l[0]] = (float(l[1]), float(l[2]), t)
+        else:
+            log.error('database file "%s" doesn\'t exist or it\'s not a file')
+            sys.exit(1)
diff --git a/src/pyssg/discovery.py b/src/pyssg/discovery.py
index a99f8bb..041ad64 100644
--- a/src/pyssg/discovery.py
+++ b/src/pyssg/discovery.py
@@ -1,31 +1,53 @@
 import os
+import logging
+from logging import Logger
 
+log: Logger = logging.getLogger(__name__)
 
-def get_file_list(directory: str,
-                  extensions: list[str],
+
+def get_file_list(path: str,
+                  exts: list[str],
                   exclude: list[str]=None) -> list[str]:
+    log.debug('retrieving file list in path "%s" that contain file'
+              ' extensions (%s) except (%s)',
+              path, ', '.join(exts),
+              ', '.join(exclude if exclude is not None else []))
     out: list[str] = []
-    for root, dirs, files in os.walk(directory):
+    for root, dirs, files in os.walk(path):
         if exclude is not None:
+            log.debug('removing excludes from list')
             dirs[:] = [d for d in dirs if d not in exclude]
 
         for f in files:
-            if f.endswith(tuple(extensions)):
-                out.append(os.path.join(root, f).replace(directory, '')[1:])
+            if f.endswith(tuple(exts)):
+                stripped_f: str = os.path.join(root, f).replace(path, '')[1:]
+                out.append(stripped_f)
+                log.debug('added file "%s" without "%s" part: "%s"',
+                          f, path, stripped_f)
+            else:
+                log.debug('ignoring file "%s" as it doesn\'t contain'
+                          ' any of the extensions (%s)', f, ', '.join(exts))
 
     return out
 
 
-def get_dir_structure(directory: str,
+def get_dir_structure(path: str,
                       exclude: list[str]=None) -> list[str]:
+    log.debug('retrieving dir structure in path "%s" except (%s)',
+              path, ', '.join(exclude if exclude is not None else []))
     out: list[str] = []
-    for root, dirs, files in os.walk(directory):
+    for root, dirs, files in os.walk(path):
         if exclude is not None:
+            log.debug('removing excludes from list')
             dirs[:] = [d for d in dirs if d not in exclude]
 
         for d in dirs:
             if root in out:
                 out.remove(root)
-            out.append(os.path.join(root, d))
+                log.debug('removed dir "%s" as it already is in the list', root)
+            joined_dir: str = os.path.join(root, d)
+            out.append(joined_dir)
+            log.debug('added dir "%s" to the list', joined_dir)
 
-    return [o.replace(directory, '')[1:] for o in out]
+    log.debug('removing "%s" from all dirs in list', path)
+    return [o.replace(path, '')[1:] for o in out]
diff --git a/src/pyssg/pyssg.py b/src/pyssg/pyssg.py
index 2844fbf..4de80d5 100644
--- a/src/pyssg/pyssg.py
+++ b/src/pyssg/pyssg.py
@@ -1,7 +1,7 @@
 import os
 import sys
 import logging
-from logging import Logger, StreamHandler
+from logging import Logger
 from importlib.resources import path as rpath
 from typing import Union
 from configparser import ConfigParser
@@ -12,7 +12,6 @@ from yafg import YafgExtension
 from MarkdownHighlight.highlight import HighlightExtension
 from markdown_checklist.extension import ChecklistExtension
 
-from .per_level_formatter import PerLevelFormatter
 from .utils import create_dir, copy_file, sanity_check_path
 from .arg_parser import get_parsed_arguments
 from .configuration import get_parsed_config, DEFAULT_CONFIG_PATH, VERSION
@@ -63,9 +62,7 @@ def main() -> None:
                   ' first time if you haven\'t already', config_path)
         sys.exit(1)
 
-    log.debug('parsing config file')
     config: ConfigParser = get_parsed_config(config_path)
-    log.debug('parsed config file')
 
     if args['init']:
         log.info('initializing the directory structure and copying over templates')
@@ -84,8 +81,6 @@ def main() -> None:
                 copy_file(p, plt_file)
         sys.exit(0)
 
-    # TODO: add logging to all of the build part, that includes the builder,
-    #   database, discovery, page and parser
     if args['build']:
         log.debug('building the html files')
         db: Database = Database(os.path.join(config.get('path', 'src'), '.files'))
@@ -93,6 +88,7 @@ def main() -> None:
 
         # the autoescape option could be a security risk if used in a dynamic
         # website, as far as i can tell
+        log.debug('initializing the jinja environment')
         env: Environment = Environment(loader=FileSystemLoader(config.get('path', 'plt')),
                                        autoescape=False,
                                        trim_blocks=True,
@@ -115,6 +111,8 @@ def main() -> None:
                                     figureNumberText="Figure"),
                       HighlightExtension(),
                       ChecklistExtension()]
+        log.debug('list of md extensions: (%s)', ', '.join(exts))
+        log.debug('initializing markdown parser')
         md: Markdown = Markdown(extensions=exts,
                                 output_format='html5')
         builder: Builder = Builder(config, env, db, md)
-- 
cgit v1.2.3-70-g09d2