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

1"""Magnetism (Finnish: magnetismi) - another opinionated World Magnetic Model calculator.""" 

2 

3import calendar 

4import datetime as dti 

5import logging 

6import os 

7import pathlib 

8from typing import List, no_type_check 

9 

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] = [] 

17 

18ABS_LAT_DD_ANY_ARCITC = 55.0 

19 

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' 

30 

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 

39 

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

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

42 

43 

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 

48 

49 

50@no_type_check 

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

52 """Initialize module level logger""" 

53 global log # pylint: disable=global-statement 

54 

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 

65 

66 

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 

84 

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) 

89 

90 

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) 

95 

96 

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