mirror of
https://github.com/Smaug123/PulumiConfig
synced 2025-10-06 09:08:39 +00:00
160 lines
6.4 KiB
Python
160 lines
6.4 KiB
Python
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
import subprocess
|
|
import os
|
|
from datetime import datetime, timedelta
|
|
from typing import AnyStr, Callable
|
|
from urllib.parse import urlparse, parse_qs
|
|
from collections import defaultdict
|
|
|
|
|
|
class MyHandler(BaseHTTPRequestHandler):
|
|
_cache_result_by_id = {}
|
|
_cache_result_by_name = {}
|
|
_last_accessed_by_id = defaultdict(lambda: datetime.min)
|
|
_last_accessed_by_name = defaultdict(lambda: datetime.min)
|
|
|
|
_all_gyms = {}
|
|
_last_refreshed_gyms = datetime.min
|
|
|
|
def _bad_request(self, text: str, code: int = 400) -> None:
|
|
self.send_response(code)
|
|
self.send_header('Content-type', 'text/plain; charset=utf-8')
|
|
self.end_headers()
|
|
self.wfile.write(text.encode('utf-8'))
|
|
|
|
def _get_fullness(self, gym_id: int) -> bytes:
|
|
if abs(datetime.now() - self._last_accessed_by_id[gym_id]) > timedelta(seconds=30):
|
|
token = subprocess.check_output(['cat', '/tmp/puregym_token']).strip()
|
|
output = subprocess.check_output(
|
|
[puregym, 'fullness', '--bearer-token', token, '--gym-id', str(gym_id)], text=True,
|
|
encoding='utf-8')
|
|
output = output.encode('utf-8')
|
|
self._cache_result_by_id[gym_id] = output
|
|
self._last_accessed_by_id[gym_id] = datetime.now()
|
|
else:
|
|
output = self._cache_result_by_id[gym_id]
|
|
return output
|
|
|
|
def _refresh_gyms(self) -> None:
|
|
if self._last_refreshed_gyms < datetime.now() - timedelta(days=1):
|
|
token = subprocess.check_output(['cat', '/tmp/puregym_token']).strip()
|
|
output = subprocess.check_output(
|
|
[puregym, 'all-gyms', '--bearer-token', token, '--terse', 'true'], text=True,
|
|
encoding='utf-8')
|
|
new_gyms = {}
|
|
for line in output.splitlines():
|
|
gym_id, gym_name = line.split(',')
|
|
new_gyms[int(gym_id)] = gym_name
|
|
self._all_gyms = new_gyms
|
|
self._last_refreshed_gyms = datetime.now()
|
|
|
|
def get_all_gyms(self, _query: dict[AnyStr, list[AnyStr]]) -> None:
|
|
self._refresh_gyms()
|
|
self.send_response(200)
|
|
self.send_header('Content-type', 'text/plain')
|
|
self.end_headers()
|
|
for gym_id, gym_name in self._all_gyms.items():
|
|
self.wfile.write(f'{gym_id}: {gym_name}\n'.encode())
|
|
|
|
def get_fullness(self, query: dict[AnyStr, list[AnyStr]]) -> None:
|
|
desired_gym_name = None
|
|
query_gym = query.get("gym_name", None)
|
|
if query_gym is not None:
|
|
if not len(query_gym) == 1:
|
|
self._bad_request('Send only one gym_name')
|
|
return
|
|
desired_gym_name = query_gym[0]
|
|
|
|
query_gym = query.get("gym_id", None)
|
|
if query_gym is not None:
|
|
if desired_gym_name is not None:
|
|
self._bad_request('Cannot supply both gym_id and gym_name')
|
|
return
|
|
if not len(query_gym) == 1:
|
|
self._bad_request('Send only one gym_id')
|
|
return
|
|
try:
|
|
desired_gym_id = int(query_gym[0])
|
|
except ValueError:
|
|
self._bad_request('gym_id did not parse as an int')
|
|
return
|
|
elif desired_gym_name is None:
|
|
# London Oval
|
|
desired_gym_id = 19
|
|
else:
|
|
desired_gym_id = None
|
|
|
|
if desired_gym_id is not None:
|
|
output = self._get_fullness(desired_gym_id)
|
|
elif desired_gym_name is not None:
|
|
if abs(datetime.now() - self._last_accessed_by_name[desired_gym_name]) > timedelta(seconds=30):
|
|
token = subprocess.check_output(['cat', '/tmp/puregym_token']).strip()
|
|
completed_process = subprocess.run(
|
|
[puregym, 'fullness', '--bearer-token', token, '--gym-name', desired_gym_name], text=True,
|
|
encoding='utf-8', stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
|
|
output = completed_process.stderr + '\n' + completed_process.stdout
|
|
output = output.encode('utf-8')
|
|
self._cache_result_by_name[desired_gym_name] = output
|
|
self._last_accessed_by_id[desired_gym_name] = datetime.now()
|
|
else:
|
|
output = self._cache_result_by_name[desired_gym_name]
|
|
else:
|
|
self._bad_request('Logic error: server reached impossible flow', 500)
|
|
return
|
|
|
|
self.send_response(200)
|
|
self.send_header('Content-type', 'text/plain; charset=utf-8')
|
|
self.end_headers()
|
|
self.wfile.write(output)
|
|
|
|
def get_prometheus(self, query: dict[AnyStr, list[AnyStr]]) -> None:
|
|
query_gym = query.get("gym_id", None)
|
|
if query_gym is None:
|
|
self._bad_request('Must supply gym_id')
|
|
return
|
|
try:
|
|
gym_id = [int(i) for i in query_gym]
|
|
except ValueError:
|
|
self._bad_request('at least one gym_id did not parse as an int')
|
|
return
|
|
|
|
if not gym_id:
|
|
self._bad_request('supply at least one gym_id')
|
|
return
|
|
|
|
try:
|
|
fullness = [(i, int(self._get_fullness(i).split(b' ')[0])) for i in gym_id]
|
|
except ValueError:
|
|
self._bad_request('at least one fullness did not yield an int', 500)
|
|
return
|
|
|
|
self.send_response(200)
|
|
self.send_header('Content-type', 'text/plain')
|
|
self.end_headers()
|
|
self._refresh_gyms()
|
|
for gym_id, fullness in fullness:
|
|
gym_name = ''.join(c for c in self._all_gyms[gym_id] if c == ' ' or str.isalnum(c))
|
|
self.wfile.write(f'fullness{{label="{gym_name}"}} {fullness}\n'.encode())
|
|
|
|
_handlers: dict[str, Callable[["MyHandler", dict[AnyStr, list[AnyStr]]], None]] = {
|
|
"/fullness": get_fullness,
|
|
"/fullness-prometheus": get_prometheus,
|
|
"/gym-mapping": get_all_gyms,
|
|
}
|
|
|
|
def do_GET(self):
|
|
parsed_path = urlparse(self.path)
|
|
handler = self._handlers.get(str(parsed_path.path), None)
|
|
if handler is None:
|
|
self._bad_request(f"Unrecognised endpoint. Available: {' '.join(self._handlers.keys())}")
|
|
else:
|
|
params = parse_qs(parsed_path.query)
|
|
handler(self, params)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
puregym = os.environ["PUREGYM_CLIENT"]
|
|
port = int(os.environ["PUREGYM_PORT"])
|
|
server = HTTPServer(('localhost', port), MyHandler)
|
|
server.serve_forever()
|