diff options
-rw-r--r-- | src/pyssg/database.py | 71 | ||||
-rw-r--r-- | src/pyssg/discovery.py | 40 | ||||
-rw-r--r-- | 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) |