Adds shuffle/scramble to list_utils.
[python_utils.git] / site_config.py
1 #!/usr/bin/env python3
2
3 """Location/site dependent data."""
4
5 import logging
6 import platform
7 from dataclasses import dataclass
8 from typing import Callable, Optional
9
10 # Note: this module is fairly early loaded.  Be aware of dependencies.
11 import config
12 from type.locations import Location
13
14 logger = logging.getLogger(__name__)
15
16 args = config.add_commandline_args(
17     f'Global Site Config ({__file__})',
18     'Args related to global site-specific configuration',
19 )
20 args.add_argument(
21     '--site_config_override_location',
22     default='NONE',
23     const='NONE',
24     nargs='?',
25     choices=['HOUSE', 'CABIN', 'NONE'],
26     help='Where are we, HOUSE, CABIN?  Overrides standard detection code.',
27 )
28
29
30 @dataclass
31 class SiteConfig(object):
32     """The set of information specific to where the program is running."""
33
34     location_name: str
35     location: Location
36     network: str
37     network_netmask: str
38     network_router_ip: str
39     presence_location: Location
40     is_anyone_present: Callable
41     arper_minimum_device_count: int
42     arper_cache_file: str
43
44
45 def get_location_name():
46     """
47     Where are we?
48
49     >>> location = get_location_name()
50     >>> location == 'HOUSE' or location == 'CABIN'
51     True
52
53     """
54     return get_config().location_name
55
56
57 def get_location():
58     """
59     Returns location as an enum instead of a string.
60
61     >>> from type.locations import Location
62     >>> location = get_location()
63     >>> location == Location.HOUSE or location == Location.CABIN
64     True
65
66     """
67     return get_config().location
68
69
70 def is_anyone_present_wrapper(location: Location):
71     import base_presence
72
73     p = base_presence.PresenceDetection()
74     return p.is_anyone_in_location_now(location)
75
76
77 def other_location() -> str:
78     """
79     Returns the location where this program is _NOT_ running.
80
81     >>> x = other_location()
82     >>> x in set(['HOUSE', 'CABIN'])
83     True
84
85     >>> y = this_location()
86     >>> x == y
87     False
88
89     """
90     this = this_location()
91     if this == 'HOUSE':
92         return 'CABIN'
93     elif this == 'CABIN':
94         return 'HOUSE'
95     else:
96         raise Exception(f"{this} doesn't tell me where I'm running?!")
97
98
99 def this_location() -> str:
100     """
101     Returns the location where this program _IS_ running.
102
103     >>> x = this_location()
104     >>> x in set(['HOUSE', 'CABIN'])
105     True
106
107     """
108     hostname = platform.node()
109     if '.house' in hostname:
110         location = 'HOUSE'
111     elif '.cabin' in hostname:
112         location = 'CABIN'
113     elif '.local' in hostname:
114         location = 'HOUSE'
115     else:
116         raise Exception(f"{hostname} doesn't help me know where I'm running?!")
117     return location
118
119
120 def effective_location(location_override: Optional[str] = None) -> str:
121     """Detects and returns a location taking into account two override
122     mechanisms.
123
124     >>> x = effective_location()
125     >>> x in set(['HOUSE', 'CABIN'])
126     True
127
128     >>> effective_location('HOUSE')
129     'HOUSE'
130
131     """
132     if location_override is None:
133         try:
134             location_override = config.config['site_config_override_location']
135         except KeyError:
136             location_override = None
137
138     if location_override is None or location_override == 'NONE':
139         location = this_location()
140     else:
141         logger.debug('site_config\'s location_override was set to: %s', location_override)
142         location = location_override
143     return location
144
145
146 def get_config(location_override: Optional[str] = None):
147     """
148     Get a configuration dataclass with information that is
149     site-specific including the current running location.
150
151     >>> cfg = get_config()
152     >>> cfg.location_name == 'HOUSE' or cfg.location_name == 'CABIN'
153     True
154
155     """
156     location = effective_location(location_override)
157     if location == 'HOUSE':
158         return SiteConfig(
159             location_name='HOUSE',
160             location=Location.HOUSE,
161             network='10.0.0.0/24',
162             network_netmask='255.255.255.0',
163             network_router_ip='10.0.0.1',
164             presence_location=Location.HOUSE,
165             is_anyone_present=lambda x=Location.HOUSE: is_anyone_present_wrapper(x),
166             arper_minimum_device_count=50,
167             arper_cache_file='/home/scott/cache/.arp_table_cache_house',
168         )
169     elif location == 'CABIN':
170         return SiteConfig(
171             location_name='CABIN',
172             location=Location.CABIN,
173             network='192.168.0.0/24',
174             network_netmask='255.255.255.0',
175             network_router_ip='192.168.0.1',
176             presence_location=Location.CABIN,
177             is_anyone_present=lambda x=Location.CABIN: is_anyone_present_wrapper(x),
178             arper_minimum_device_count=15,
179             arper_cache_file='/home/scott/cache/.arp_table_cache_cabin',
180         )
181     else:
182         raise Exception(f'Unknown site location: {location}')
183
184
185 if __name__ == '__main__':
186     import doctest
187
188     doctest.testmod()