Skip to content

Commit

Permalink
Manual merge of main & type_annotations from base bdbe551
Browse files Browse the repository at this point in the history
  • Loading branch information
schwabix-1311 committed Dec 20, 2024
1 parent cd4db23 commit 41e6190
Show file tree
Hide file tree
Showing 32 changed files with 26,477 additions and 1,342 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pylint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.7", "3.10", "3.11"]
python-version: ["3.11"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
Expand Down
5 changes: 3 additions & 2 deletions ToDo
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,10 @@ THOUGHTS
DONE
====

- manually, global: pip pip install pylint, vim-jedi, jedi-language-server, vim-ale
- manually, global: pip pip install pylint, vim-jedi, mypy, jedi-language-server, vim-ale
- manually modify .vimrc:
let g:ale_linters = {
\ 'python': ['flake8', 'pylint', 'jedils'],
\ 'python': ['flake8', 'mypy', 'pylint', 'jedils'],
\ 'javascript': ['eslint'],
\}
let g:ale_fixers = {
Expand All @@ -135,6 +135,7 @@ DONE
python -m venv venv
. ./venv/bin/activate
pip install Flask sseclient ...
mypy --install-types
- run
export FLASK_APP=aquaPi
export FLASK_ENV=development
Expand Down
63 changes: 33 additions & 30 deletions aquaPi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,38 +54,38 @@
"loggers": {
"root": {
"level": "WARNING",
"handlers": ["stdout","file"]
"handlers": ["stdout", "file"]
},

"aquaPi": {"level": "NOTSET"},
#"aquaPi.api": {"level": "NOTSET"},
# "aquaPi.api": {"level": "NOTSET"},

"machineroom": {"level": "NOTSET"},
#"machineroom.alert_nodes": {"level": "NOTSET"},
#"machineroom.aux_nodes": {"level": "NOTSET"},
#"machineroom.ctrl_nodes": {"level": "NOTSET"},
#"machineroom.hist_nodes": {"level": "NOTSET"},
#"machineroom.in_nodes": {"level": "NOTSET"},
#"machineroom.msg_bus": {"level": "NOTSET"},
#"machineroom.msg_types": {"level": "NOTSET"},
#"machineroom.out_nodes": {"level": "NOTSET"},
# "machineroom.alert_nodes": {"level": "NOTSET"},
# "machineroom.aux_nodes": {"level": "NOTSET"},
# "machineroom.ctrl_nodes": {"level": "NOTSET"},
# "machineroom.hist_nodes": {"level": "NOTSET"},
# "machineroom.in_nodes": {"level": "NOTSET"},
# "machineroom.msg_bus": {"level": "NOTSET"},
# "machineroom.msg_types": {"level": "NOTSET"},
# "machineroom.out_nodes": {"level": "NOTSET"},

"driver": {"level": "NOTSET"},
#"driver.base": {"level": "NOTSET"},
#"driver.DriverADC": {"level": "NOTSET"},
#"driver.DriverAlert": {"level": "NOTSET"},
#"driver.DriverGPIO": {"level": "NOTSET"},
#"driver.DriverOneWire": {"level": "NOTSET"},
#"driver.DriverPWM": {"level": "NOTSET"},
#"driver.DriverTC420": {"level": "NOTSET"},
# "driver.base": {"level": "NOTSET"},
# "driver.DriverADC": {"level": "NOTSET"},
# "driver.DriverAlert": {"level": "NOTSET"},
# "driver.DriverGPIO": {"level": "NOTSET"},
# "driver.DriverOneWire": {"level": "NOTSET"},
# "driver.DriverPWM": {"level": "NOTSET"},
# "driver.DriverTC420": {"level": "NOTSET"},

"pages": {"level": "NOTSET"},
#"pages.about": {"level": "NOTSET"},
#"pages.config": {"level": "NOTSET"},
#"pages.home": {"level": "NOTSET"},
#"pages.settings": {"level": "NOTSET"},
#"pages.spa": {"level": "NOTSET"},
#"pages.sse_util": {"level": "NOTSET"},
# "pages.about": {"level": "NOTSET"},
# "pages.config": {"level": "NOTSET"},
# "pages.home": {"level": "NOTSET"},
# "pages.settings": {"level": "NOTSET"},
# "pages.spa": {"level": "NOTSET"},
# "pages.sse_util": {"level": "NOTSET"},

"werkzeug": {
"comment": "werkzeug is noisy, reduce to >=WARNING, INFO shows all https requests",
Expand All @@ -96,14 +96,13 @@
}



def create_app():
def create_app() -> Flask:
# TODO wrap in try/catch, but how should exceptions be handled?
app = Flask(__name__, instance_relative_config=True)

config_file = path.join(app.instance_path, "log_config.json")
if path.exists(config_file):
with open(config_file) as f_in:
with open(config_file, encoding='ascii') as f_in:
log_config = json.load(f_in)
else:
log_config = log_default
Expand Down Expand Up @@ -176,12 +175,16 @@ def create_app():
if 'routes' in sys.argv:
return app

from . import machineroom
app.machineroom = machineroom.init(app.config['CONFIG'])
app.bus = app.machineroom.bus
from .machineroom import MachineRoom
try:
app.extensions['machineroom'] = MachineRoom(app.config['CONFIG'])
except Exception:
log.fatal("Fatal error in App.__init__. Subsequent errors are a side effect.")
return None

#FIXME bus is used by Python code in jinja template 'settings'
@app.context_processor
def inject_globals():
return dict(bus=app.bus)
return dict(bus=app.extensions['machineroom'].bus)

return app
140 changes: 73 additions & 67 deletions aquaPi/api.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
#!/usr/bin/env python3

import logging
# import time
# import os
# from resource import *
# import sys
import jsonpickle

from flask import (
Blueprint, current_app, json, Response, request
)
import jsonpickle # type: ignore[import-untyped]
from flask import (Blueprint, current_app, json, Response, request)
from http import HTTPStatus

from .machineroom import (MachineRoom, MsgBus)
from .machineroom.msg_bus import BusRole
from .pages.sse_util import send_sse_events

Expand All @@ -23,83 +17,95 @@
bp = Blueprint('api', __name__)


@bp.route('/api/nodes/')
def api_nodes():
bus = current_app.bus
node_ids = [node.id for node in bus.get_nodes()]

if node_ids:
return json.dumps(node_ids)
else:
return Response(status=HTTPStatus.INTERNAL_SERVER_ERROR)


@bp.route('/api/nodes/<node_id>')
def api_node(node_id: str):
bus = current_app.bus
node_id = str(node_id.encode('ascii', 'xmlcharrefreplace'), errors='strict')
node = bus.get_node(node_id)
def the_bus() -> MsgBus | None:
mr: MachineRoom = current_app.extensions['machineroom']
return mr.bus

log.debug(str(node))

if node:
item = node.__getstate__()
item['type'] = type(node).__name__
item['role'] = str(node.ROLE).rsplit('.', 1)[1]

if hasattr(node, 'alert') and node.alert:
item['alert'] = node.alert
@bp.route('/api/nodes/')
def api_nodes() -> Response:
bus = the_bus()
if bus:
node_ids = [node.id for node in bus.get_nodes()]
if node_ids:
body = json.dumps(node_ids)
log.debug('API nodes: %s', body)
return Response(status=HTTPStatus.OK, response=body, mimetype='application/json')
return Response(status=HTTPStatus.INTERNAL_SERVER_ERROR)

log.debug(item)
body = jsonpickle.encode({'result': 'SUCCESS', 'data': item}, unpicklable=False, keys=True)

return Response(status=HTTPStatus.OK, response=body, mimetype='application/json')
else:
return Response(status=HTTPStatus.NOT_FOUND)
@bp.route('/api/nodes/<node_id>')
def api_node(node_id: str) -> Response:
bus = the_bus()
if bus:
node_id = str(node_id.encode('ascii', 'xmlcharrefreplace'), errors='strict')
node = bus.get_node(node_id)

if node:
item = node.__getstate__()
item['type'] = type(node).__name__
item['role'] = str(node.ROLE).rsplit('.', 1)[1]

if hasattr(node, 'alert') and node.alert:
item['alert'] = node.alert

body = jsonpickle.encode({'result': 'SUCCESS', 'data': item},
unpicklable=False, keys=True)
log.debug('API nodes/%s: %s', node_id, body)
return Response(status=HTTPStatus.OK, response=body, mimetype='application/json')
else:
return Response(status=HTTPStatus.NOT_FOUND)
return Response(status=HTTPStatus.INTERNAL_SERVER_ERROR)


@bp.route('/api/history/')
def api_history_nodes():
bus = current_app.bus
node_ids = [node.id for node in bus.get_nodes(BusRole.HISTORY)]

if node_ids:
return json.dumps(node_ids)
else:
return Response(status=HTTPStatus.NOT_FOUND)
def api_history_nodes() -> Response:
bus = the_bus()
if bus:
node_ids = [node.id for node in bus.get_nodes(BusRole.HISTORY)]
if node_ids:
body = json.dumps(node_ids)
log.debug('API history: %s', body)
return Response(status=HTTPStatus.OK, response=body, mimetype='application/json')
else:
return Response(status=HTTPStatus.NOT_FOUND)
return Response(status=HTTPStatus.INTERNAL_SERVER_ERROR)


@bp.route('/api/history/<node_id>')
def api_history(node_id: str):
bus = current_app.bus
node_id = str(node_id.encode('ascii', 'xmlcharrefreplace'), errors='strict')
node = bus.get_node(node_id)

start = int(request.args.get('start', 0))
step = int(request.args.get('step', 0))

log.debug('API %s start %d step %d', request.path, start, step)
if node:
if hasattr(node, 'get_history'):
hist = node.get_history(start, step)

body = json.dumps({'result': 'SUCCESS', 'data': hist}, sort_keys=False)
return Response(status=HTTPStatus.OK, response=body, mimetype='application/json')
def api_history(node_id: str) -> Response:
bus = the_bus()
if bus:
node_id = str(node_id.encode('ascii', 'xmlcharrefreplace'), errors='strict')
node = bus.get_node(node_id)

start = int(request.args.get('start', 0))
step = int(request.args.get('step', 0))

if node:
if hasattr(node, 'get_history'):
hist = node.get_history(start, step)

body = json.dumps({'result': 'SUCCESS', 'data': hist}, sort_keys=False)
log.debug('API history/%s (%d/%d): %s', node_id, start, step, body)
return Response(status=HTTPStatus.OK, response=body, mimetype='application/json')
else:
return Response(status=HTTPStatus.BAD_REQUEST)
else:
return Response(status=HTTPStatus.BAD_REQUEST)
else:
return Response(status=HTTPStatus.NOT_FOUND)
return Response(status=HTTPStatus.NOT_FOUND)
return Response(status=HTTPStatus.INTERNAL_SERVER_ERROR)


@bp.route('/api/sse', methods=['GET'])
def api_sse():
def api_sse() -> Response:
if request.headers.get('accept') != 'text/event-stream':
return Response('MUST ACCEPT content type text/event-stream', status=HTTPStatus.BAD_REQUEST)

bus = current_app.bus
bus = the_bus()

def sse_update():
changed_ids = bus.wait_for_changes()
return json.dumps(changed_ids)
log.debug('API sse reply: %r', changed_ids)
return json.dumps([id for id in changed_ids])

return send_sse_events(sse_update)
Loading

0 comments on commit 41e6190

Please sign in to comment.