Coverage for visailu/__init__.py: 100.00%

52 statements  

« prev     ^ index     » next       coverage.py v7.4.1, created at 2024-02-05 19:49:28 +00:00

1"""Quiz (Finnish: visailu) data operations.""" 

2 

3import datetime as dti 

4import logging 

5import os 

6import pathlib 

7from typing import List, no_type_check 

8 

9# [[[fill git_describe()]]] 

10__version__ = '2023.8.27+parent.gf8ac9e03' 

11# [[[end]]] (checksum: 55c93108a923ec560e6be75c4304454e) 

12__version_info__ = tuple( 

13 e if '-' not in e else e.split('-')[0] for part in __version__.split('+') for e in part.split('.') if e != 'parent' 

14) 

15 

16APP_ALIAS = str(pathlib.Path(__file__).parent.name) 

17APP_ENV = APP_ALIAS.upper() 

18APP_NAME = locals()['__doc__'] 

19DEBUG = bool(os.getenv(f'{APP_ENV}_DEBUG', '')) 

20VERBOSE = bool(os.getenv(f'{APP_ENV}_VERBOSE', '')) 

21QUIET = False 

22STRICT = bool(os.getenv(f'{APP_ENV}_STRICT', '')) 

23ENCODING = 'utf-8' 

24ENCODING_ERRORS_POLICY = 'ignore' 

25DEFAULT_CONFIG_NAME = f'.{APP_ALIAS}.json' 

26log = logging.getLogger() # Module level logger is sufficient 

27LOG_FOLDER = pathlib.Path('logs') 

28LOG_FILE = f'{APP_ALIAS}.log' 

29LOG_PATH = pathlib.Path(LOG_FOLDER, LOG_FILE) if LOG_FOLDER.is_dir() else pathlib.Path(LOG_FILE) 

30LOG_LEVEL = logging.INFO 

31 

32TS_FORMAT_LOG = '%Y-%m-%dT%H:%M:%S' 

33TS_FORMAT_PAYLOADS = '%Y-%m-%d %H:%M:%S.%f UTC' 

34 

35VERSION = __version__ 

36VERSION_INFO = __version_info__ 

37 

38# simplistic initial output expectations: 

39OUT_QUESTION_COUNT = 10 

40OUT_ANSWERS_COUNT = 4 

41 

42# messages registry: 

43INVALID_YAML_RESOURCE = 'is invalid yaml or the resource is inaccessible' 

44MODEL_META_INVALID_DEFAULTS = 'contains invalid defaults for scale in meta' 

45MODEL_META_INVALID_RANGE = 'contains an invalid range of scale in meta' 

46MODEL_META_INVALID_RANGE_VALUE = 'contains an invalid default value for the scale' 

47MODEL_QUESTION_ANSWER_MISSING = 'misses an answer' 

48MODEL_QUESTION_ANSWER_MISSING_RATING = 'misses a rating for an answer' 

49MODEL_QUESTION_INCOMPLETE = 'has incomplete questions' 

50MODEL_QUESTION_INVALID_RANGE = 'contains an invalid range value for the scale' 

51MODEL_QUESTION_INVALID_RANGE_VALUE = 'contains an invalid answer rating for the scale' 

52MODEL_STRUCTURE_UNEXPECTED = 'has unexpected model structure' 

53MODEL_VALUES_MISSING = 'misses model values' 

54 

55 

56__all__: List[str] = [ 

57 'APP_ALIAS', 

58 'APP_ENV', 

59 'APP_NAME', 

60 'DEBUG', 

61 'DEFAULT_CONFIG_NAME', 

62 'DEFAULT_STRUCTURE_NAME', 

63 'ENCODING', 

64 'INVALID_YAML_RESOURCE', 

65 'MODEL_META_INVALID_DEFAULTS', 

66 'MODEL_META_INVALID_RANGE', 

67 'MODEL_META_INVALID_RANGE_VALUE', 

68 'MODEL_QUESTION_ANSWER_MISSING', 

69 'MODEL_QUESTION_ANSWER_MISSING_RATING', 

70 'MODEL_QUESTION_INCOMPLETE', 

71 'MODEL_QUESTION_INVALID_RANGE', 

72 'MODEL_QUESTION_INVALID_RANGE_VALUE', 

73 'MODEL_STRUCTURE_UNEXPECTED', 

74 'MODEL_VALUES_MISSING', 

75 'OUT_QUESTION_COUNT', 

76 'OUT_ANSWERS_COUNT', 

77 'log', 

78] 

79 

80 

81def slugify(text: str) -> str: 

82 """Remove newlines and reduce multiple spaces to single spaces.""" 

83 return ' '.join(text.replace('\n', ' ').split()) 

84 

85 

86@no_type_check 

87def formatTime_RFC3339(self, record, datefmt=None): # noqa 

88 """HACK A DID ACK we could inject .astimezone() to localize ...""" 

89 return dti.datetime.fromtimestamp(record.created, dti.timezone.utc).isoformat() # pragma: no cover 

90 

91 

92@no_type_check 

93def init_logger(name=None, level=None): 

94 """Initialize module level logger""" 

95 global log # pylint: disable=global-statement 

96 

97 log_format = { 

98 'format': '%(asctime)s %(levelname)s [%(name)s]: %(message)s', 

99 'datefmt': TS_FORMAT_LOG, 

100 # 'filename': LOG_PATH, 

101 'level': LOG_LEVEL if level is None else level, 

102 } 

103 logging.Formatter.formatTime = formatTime_RFC3339 

104 logging.basicConfig(**log_format) 

105 log = logging.getLogger(APP_ENV if name is None else name) 

106 log.propagate = True 

107 

108 

109init_logger(name=APP_ENV, level=logging.DEBUG if DEBUG else None)