mirror of
https://github.com/Garmelon/PFERD.git
synced 2023-12-21 10:23:01 +01:00
Move logging into its own file
This commit is contained in:
parent
80247400a4
commit
8a42a2a396
@ -5,33 +5,4 @@ This module exports only what you need for a basic configuration. If you want a
|
|||||||
more complex configuration, you need to import the other submodules manually.
|
more complex configuration, you need to import the other submodules manually.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from .pferd import Pferd
|
from .pferd import Pferd
|
||||||
|
|
||||||
STYLE = "{"
|
|
||||||
FORMAT = "[{levelname:<7}] {message}"
|
|
||||||
DATE_FORMAT = "%F %T"
|
|
||||||
|
|
||||||
FORMATTER = logging.Formatter(
|
|
||||||
fmt=FORMAT,
|
|
||||||
datefmt=DATE_FORMAT,
|
|
||||||
style=STYLE,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def enable_logging(name: str = "PFERD", level: int = logging.INFO) -> None:
|
|
||||||
"""
|
|
||||||
Enable and configure logging via the logging module.
|
|
||||||
"""
|
|
||||||
|
|
||||||
handler = logging.StreamHandler()
|
|
||||||
handler.setFormatter(FORMATTER)
|
|
||||||
|
|
||||||
logger = logging.getLogger(name)
|
|
||||||
logger.setLevel(level)
|
|
||||||
logger.addHandler(handler)
|
|
||||||
|
|
||||||
# This should be logged by our own handler, and not the root logger's
|
|
||||||
# default handler, so we don't pass it on to the root logger.
|
|
||||||
logger.propagate = False
|
|
||||||
|
@ -14,7 +14,8 @@ from urllib.parse import (parse_qs, urlencode, urljoin, urlparse, urlsplit,
|
|||||||
import bs4
|
import bs4
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from ..utils import PrettyLogger, soupify
|
from ..logging import PrettyLogger
|
||||||
|
from ..utils import soupify
|
||||||
from .authenticators import IliasAuthenticator
|
from .authenticators import IliasAuthenticator
|
||||||
from .date_demangler import demangle_date
|
from .date_demangler import demangle_date
|
||||||
from .downloader import IliasDownloadInfo
|
from .downloader import IliasDownloadInfo
|
||||||
|
@ -9,10 +9,11 @@ from typing import Callable, List, Optional
|
|||||||
import bs4
|
import bs4
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
from ..logging import PrettyLogger
|
||||||
from ..organizer import Organizer
|
from ..organizer import Organizer
|
||||||
from ..tmp_dir import TmpDir
|
from ..tmp_dir import TmpDir
|
||||||
from ..transform import Transformable
|
from ..transform import Transformable
|
||||||
from ..utils import PrettyLogger, soupify, stream_to_path
|
from ..utils import soupify, stream_to_path
|
||||||
from .authenticators import IliasAuthenticator
|
from .authenticators import IliasAuthenticator
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
133
PFERD/logging.py
Normal file
133
PFERD/logging.py
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
"""
|
||||||
|
Contains a few logger utility functions and implementations.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import colorama
|
||||||
|
from colorama import Fore, Style
|
||||||
|
|
||||||
|
from .utils import PathLike, to_path
|
||||||
|
|
||||||
|
STYLE = "{"
|
||||||
|
FORMAT = "[{levelname:<7}] {message}"
|
||||||
|
DATE_FORMAT = "%F %T"
|
||||||
|
|
||||||
|
FORMATTER = logging.Formatter(
|
||||||
|
fmt=FORMAT,
|
||||||
|
datefmt=DATE_FORMAT,
|
||||||
|
style=STYLE,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def enable_logging(name: str = "PFERD", level: int = logging.INFO) -> None:
|
||||||
|
"""
|
||||||
|
Enable and configure logging via the logging module.
|
||||||
|
"""
|
||||||
|
|
||||||
|
handler = logging.StreamHandler()
|
||||||
|
handler.setFormatter(FORMATTER)
|
||||||
|
|
||||||
|
logger = logging.getLogger(name)
|
||||||
|
logger.setLevel(level)
|
||||||
|
logger.addHandler(handler)
|
||||||
|
|
||||||
|
# This should be logged by our own handler, and not the root logger's
|
||||||
|
# default handler, so we don't pass it on to the root logger.
|
||||||
|
logger.propagate = False
|
||||||
|
|
||||||
|
colorama.init()
|
||||||
|
|
||||||
|
|
||||||
|
class PrettyLogger:
|
||||||
|
"""
|
||||||
|
A logger that prints some specially formatted log messages in color.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, logger: logging.Logger) -> None:
|
||||||
|
self.logger = logger
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _format_path(path: PathLike) -> str:
|
||||||
|
return repr(str(to_path(path)))
|
||||||
|
|
||||||
|
def error(self, message: str) -> None:
|
||||||
|
"""
|
||||||
|
Print an error message indicating some operation fatally failed.
|
||||||
|
"""
|
||||||
|
self.logger.error(
|
||||||
|
f"{Fore.RED}{Style.BRIGHT}{message}{Style.RESET_ALL}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def warn(self, message: str) -> None:
|
||||||
|
"""
|
||||||
|
Print a warning message indicating some operation failed, but the error can be recovered
|
||||||
|
or ignored.
|
||||||
|
"""
|
||||||
|
self.logger.warning(
|
||||||
|
f"{Fore.YELLOW}{Style.BRIGHT}{message}{Style.RESET_ALL}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def modified_file(self, path: PathLike) -> None:
|
||||||
|
"""
|
||||||
|
An existing file has changed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.logger.info(
|
||||||
|
f"{Fore.MAGENTA}{Style.BRIGHT}Modified {self._format_path(path)}.{Style.RESET_ALL}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def new_file(self, path: PathLike) -> None:
|
||||||
|
"""
|
||||||
|
A new file has been downloaded.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.logger.info(
|
||||||
|
f"{Fore.GREEN}{Style.BRIGHT}Created {self._format_path(path)}.{Style.RESET_ALL}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def ignored_file(self, path: PathLike, reason: str) -> None:
|
||||||
|
"""
|
||||||
|
File was not downloaded or modified.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.logger.info(
|
||||||
|
f"{Style.DIM}Ignored {self._format_path(path)} "
|
||||||
|
f"({Style.NORMAL}{reason}{Style.DIM}).{Style.RESET_ALL}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def searching(self, path: PathLike) -> None:
|
||||||
|
"""
|
||||||
|
A crawler searches a particular object.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.logger.info(f"Searching {self._format_path(path)}")
|
||||||
|
|
||||||
|
def not_searching(self, path: PathLike, reason: str) -> None:
|
||||||
|
"""
|
||||||
|
A crawler does not search a particular object.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.logger.info(
|
||||||
|
f"{Style.DIM}Not searching {self._format_path(path)} "
|
||||||
|
f"({Style.NORMAL}{reason}{Style.DIM}).{Style.RESET_ALL}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def starting_synchronizer(
|
||||||
|
self,
|
||||||
|
target_directory: PathLike,
|
||||||
|
synchronizer_name: str,
|
||||||
|
subject: Optional[str] = None,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
A special message marking that a synchronizer has been started.
|
||||||
|
"""
|
||||||
|
|
||||||
|
subject_str = f"{subject} " if subject else ""
|
||||||
|
self.logger.info("")
|
||||||
|
self.logger.info((
|
||||||
|
f"{Fore.CYAN}{Style.BRIGHT}Synchronizing "
|
||||||
|
f"{subject_str}to {self._format_path(target_directory)} "
|
||||||
|
f"using the {synchronizer_name} synchronizer.{Style.RESET_ALL}"
|
||||||
|
))
|
@ -10,7 +10,8 @@ from pathlib import Path, PurePath
|
|||||||
from typing import List, Set
|
from typing import List, Set
|
||||||
|
|
||||||
from .location import Location
|
from .location import Location
|
||||||
from .utils import PrettyLogger, prompt_yes_no
|
from .logging import PrettyLogger
|
||||||
|
from .utils import prompt_yes_no
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
LOGGER = logging.getLogger(__name__)
|
||||||
PRETTY = PrettyLogger(LOGGER)
|
PRETTY = PrettyLogger(LOGGER)
|
||||||
|
@ -11,10 +11,11 @@ from .ilias import (IliasAuthenticator, IliasCrawler, IliasDirectoryFilter,
|
|||||||
IliasDownloader, IliasDownloadStrategy,
|
IliasDownloader, IliasDownloadStrategy,
|
||||||
KitShibbolethAuthenticator, download_modified_or_new)
|
KitShibbolethAuthenticator, download_modified_or_new)
|
||||||
from .location import Location
|
from .location import Location
|
||||||
|
from .logging import PrettyLogger
|
||||||
from .organizer import Organizer
|
from .organizer import Organizer
|
||||||
from .tmp_dir import TmpDir
|
from .tmp_dir import TmpDir
|
||||||
from .transform import TF, Transform, apply_transform
|
from .transform import TF, Transform, apply_transform
|
||||||
from .utils import PrettyLogger, PathLike, to_path
|
from .utils import PathLike, to_path
|
||||||
|
|
||||||
# TODO save known-good cookies as soon as possible
|
# TODO save known-good cookies as soon as possible
|
||||||
|
|
||||||
|
@ -2,19 +2,20 @@
|
|||||||
A few utility bobs and bits.
|
A few utility bobs and bits.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
|
||||||
import re
|
import re
|
||||||
from pathlib import Path, PurePath
|
from pathlib import Path, PurePath
|
||||||
from typing import Optional, Tuple, Union
|
from typing import Optional, Tuple, Union
|
||||||
|
|
||||||
import bs4
|
import bs4
|
||||||
import requests
|
import requests
|
||||||
from colorama import Fore, Style
|
|
||||||
|
|
||||||
PathLike = Union[PurePath, str, Tuple[str, ...]]
|
PathLike = Union[PurePath, str, Tuple[str, ...]]
|
||||||
|
|
||||||
|
|
||||||
def to_path(pathlike: PathLike) -> Path:
|
def to_path(pathlike: PathLike) -> Path:
|
||||||
|
"""
|
||||||
|
Convert a given PathLike into a Path.
|
||||||
|
"""
|
||||||
if isinstance(pathlike, tuple):
|
if isinstance(pathlike, tuple):
|
||||||
return Path(*pathlike)
|
return Path(*pathlike)
|
||||||
return Path(pathlike)
|
return Path(pathlike)
|
||||||
@ -24,6 +25,9 @@ Regex = Union[str, re.Pattern]
|
|||||||
|
|
||||||
|
|
||||||
def to_pattern(regex: Regex) -> re.Pattern:
|
def to_pattern(regex: Regex) -> re.Pattern:
|
||||||
|
"""
|
||||||
|
Convert a regex to a re.Pattern.
|
||||||
|
"""
|
||||||
if isinstance(regex, re.Pattern):
|
if isinstance(regex, re.Pattern):
|
||||||
return regex
|
return regex
|
||||||
return re.compile(regex)
|
return re.compile(regex)
|
||||||
@ -74,79 +78,3 @@ def prompt_yes_no(question: str, default: Optional[bool] = None) -> bool:
|
|||||||
if response == "" and default is not None:
|
if response == "" and default is not None:
|
||||||
return default
|
return default
|
||||||
print(wrong_reply)
|
print(wrong_reply)
|
||||||
|
|
||||||
|
|
||||||
class PrettyLogger:
|
|
||||||
"""
|
|
||||||
A logger that prints some specially formatted log messages in color.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, logger: logging.Logger) -> None:
|
|
||||||
self.logger = logger
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _format_path(path: PathLike) -> str:
|
|
||||||
return repr(str(to_path(path)))
|
|
||||||
|
|
||||||
def modified_file(self, path: PathLike) -> None:
|
|
||||||
"""
|
|
||||||
An existing file has changed.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.logger.info(
|
|
||||||
f"{Fore.MAGENTA}{Style.BRIGHT}Modified {self._format_path(path)}.{Style.RESET_ALL}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def new_file(self, path: PathLike) -> None:
|
|
||||||
"""
|
|
||||||
A new file has been downloaded.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.logger.info(
|
|
||||||
f"{Fore.GREEN}{Style.BRIGHT}Created {self._format_path(path)}.{Style.RESET_ALL}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def ignored_file(self, path: PathLike, reason: str) -> None:
|
|
||||||
"""
|
|
||||||
File was not downloaded or modified.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.logger.info(
|
|
||||||
f"{Style.DIM}Ignored {self._format_path(path)} "
|
|
||||||
f"({Style.NORMAL}{reason}{Style.DIM}).{Style.RESET_ALL}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def searching(self, path: PathLike) -> None:
|
|
||||||
"""
|
|
||||||
A crawler searches a particular object.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.logger.info(f"Searching {self._format_path(path)}")
|
|
||||||
|
|
||||||
def not_searching(self, path: PathLike, reason: str) -> None:
|
|
||||||
"""
|
|
||||||
A crawler does not search a particular object.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.logger.info(
|
|
||||||
f"{Style.DIM}Not searching {self._format_path(path)} "
|
|
||||||
f"({Style.NORMAL}{reason}{Style.DIM}).{Style.RESET_ALL}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def starting_synchronizer(
|
|
||||||
self,
|
|
||||||
target_directory: PathLike,
|
|
||||||
synchronizer_name: str,
|
|
||||||
subject: Optional[str] = None,
|
|
||||||
) -> None:
|
|
||||||
"""
|
|
||||||
A special message marking that a synchronizer has been started.
|
|
||||||
"""
|
|
||||||
|
|
||||||
subject_str = f"{subject} " if subject else ""
|
|
||||||
self.logger.info("")
|
|
||||||
self.logger.info((
|
|
||||||
f"{Fore.CYAN}{Style.BRIGHT}Synchronizing "
|
|
||||||
f"{subject_str}to {self._format_path(target_directory)} "
|
|
||||||
f"using the {synchronizer_name} synchronizer.{Style.RESET_ALL}"
|
|
||||||
))
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import argparse
|
import argparse
|
||||||
from pathlib import Path, PurePath
|
from pathlib import Path, PurePath
|
||||||
|
|
||||||
import PFERD
|
from PFERD import Pferd
|
||||||
|
from PFERD.logging import enable_logging
|
||||||
from PFERD.transform import (attempt, glob, keep, move, move_dir, optionally,
|
from PFERD.transform import (attempt, glob, keep, move, move_dir, optionally,
|
||||||
re_move)
|
re_move)
|
||||||
|
|
||||||
@ -45,9 +46,12 @@ tf_ss_2020_pg = attempt(
|
|||||||
|
|
||||||
|
|
||||||
def df_ss_2020_or1(path: PurePath) -> bool:
|
def df_ss_2020_or1(path: PurePath) -> bool:
|
||||||
if glob("Tutorien/")(path): return True
|
if glob("Tutorien/")(path):
|
||||||
if glob("Tutorien/Tutorium 10, dienstags 15:45 Uhr/")(path): return True
|
return True
|
||||||
if glob("Tutorien/*")(path): return False
|
if glob("Tutorien/Tutorium 10, dienstags 15:45 Uhr/")(path):
|
||||||
|
return True
|
||||||
|
if glob("Tutorien/*")(path):
|
||||||
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@ -64,8 +68,8 @@ def main() -> None:
|
|||||||
parser.add_argument("synchronizers", nargs="*")
|
parser.add_argument("synchronizers", nargs="*")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
PFERD.enable_logging()
|
enable_logging()
|
||||||
pferd = PFERD.Pferd(Path(__file__).parent, test_run=args.test_run)
|
pferd = Pferd(Path(__file__).parent, test_run=args.test_run)
|
||||||
|
|
||||||
if not args.synchronizers or "numerik" in args.synchronizers:
|
if not args.synchronizers or "numerik" in args.synchronizers:
|
||||||
pferd.ilias_kit(
|
pferd.ilias_kit(
|
||||||
|
Loading…
Reference in New Issue
Block a user