Coverage for magnetismi/__init__.py: 98.72%
62 statements
« prev ^ index » next coverage.py v7.4.1, created at 2024-02-04 20:21:25 +00:00
« prev ^ index » next coverage.py v7.4.1, created at 2024-02-04 20:21:25 +00:00
1"""Magnetism (Finnish: magnetismi) - another opinionated World Magnetic Model calculator."""
3import calendar
4import datetime as dti
5import logging
6import os
7import pathlib
8from typing import List, no_type_check
10# [[[fill git_describe()]]]
11__version__ = '2022.10.9+parent.8ba013b1'
12# [[[end]]] (checksum: d66c693c48c69dde4fe7f21697cf846d)
13__version_info__ = tuple(
14 e if '-' not in e else e.split('-')[0] for part in __version__.split('+') for e in part.split('.') if e != 'parent'
15)
16__all__: List[str] = []
18ABS_LAT_DD_ANY_ARCITC = 55.0
20APP_ALIAS = str(pathlib.Path(__file__).parent.name)
21APP_ENV = APP_ALIAS.upper()
22APP_NAME = locals()['__doc__']
23DEBUG = bool(os.getenv(f'{APP_ENV}_DEBUG', ''))
24VERBOSE = bool(os.getenv(f'{APP_ENV}_VERBOSE', ''))
25QUIET = False
26STRICT = bool(os.getenv(f'{APP_ENV}_STRICT', ''))
27ENCODING = 'utf-8'
28ENCODING_ERRORS_POLICY = 'ignore'
29DEFAULT_CONFIG_NAME = f'.{APP_ALIAS}.json'
31DEFAULT_MAG_VAR = -999.0
32FEET_TO_KILOMETER = 1.0 / 3280.8399
33DEFAULT_LF_ONLY = 'YES'
34log = logging.getLogger() # Module level logger is sufficient
35LOG_FOLDER = pathlib.Path('logs')
36LOG_FILE = f'{APP_ALIAS}.log'
37LOG_PATH = pathlib.Path(LOG_FOLDER, LOG_FILE) if LOG_FOLDER.is_dir() else pathlib.Path(LOG_FILE)
38LOG_LEVEL = logging.INFO
40TS_FORMAT_LOG = '%Y-%m-%dT%H:%M:%S'
41TS_FORMAT_PAYLOADS = '%Y-%m-%d %H:%M:%S.%f UTC'
44@no_type_check
45def formatTime_RFC3339(self, record, datefmt=None): # noqa
46 """HACK A DID ACK we could inject .astimezone() to localize ..."""
47 return dti.datetime.fromtimestamp(record.created, dti.timezone.utc).isoformat() # pragma: no cover
50@no_type_check
51def init_logger(name=None, level=None):
52 """Initialize module level logger"""
53 global log # pylint: disable=global-statement
55 log_format = {
56 'format': '%(asctime)s %(levelname)s [%(name)s]: %(message)s',
57 'datefmt': TS_FORMAT_LOG,
58 # 'filename': LOG_PATH,
59 'level': LOG_LEVEL if level is None else level,
60 }
61 logging.Formatter.formatTime = formatTime_RFC3339
62 logging.basicConfig(**log_format)
63 log = logging.getLogger(APP_ENV if name is None else name)
64 log.propagate = True
67def date_from_fractional_year(date: float) -> dti.date:
68 """Going back ..."""
69 y_int = int(date)
70 rest_y_float = date - y_int
71 days_in_year = 365 + calendar.isleap(y_int)
72 rest_d_float = round(rest_y_float * days_in_year, 1)
73 if rest_d_float:
74 day_counts = [c for _, c in (calendar.monthrange(y_int, m) for m in range(1, 12 + 1))]
75 day_cum = [day_counts[0]] + [0] * 11
76 for m, c in enumerate(day_counts[1:], start=2):
77 day_cum[m - 1] = day_cum[m - 2] + c
78 m_int = 1 # Well, not really, but, ... happy linter
79 for m, c in enumerate(day_cum, start=1): 79 ↛ 85line 79 didn't jump to line 85, because the loop on line 79 didn't complete
80 if c < rest_d_float:
81 continue
82 m_int = m
83 break
85 d_int = int(rest_d_float - day_cum[m_int - 2])
86 else:
87 m_int, d_int = 1, 1
88 return dti.date(y_int, m_int, d_int)
91def fractional_year_from_date(date: dti.date) -> float:
92 """... and forth."""
93 days_in_year = float(365 + calendar.isleap(date.year))
94 return date.year + ((date - dti.date(date.year, 1, 1)).days / days_in_year)
97init_logger(name=APP_ENV, level=logging.DEBUG if DEBUG else None)