comparison environment.py @ 3:e7f33fa31883

port to starlette/asyncio
author drewp@bigasterisk.com
date Sun, 24 Apr 2022 14:46:32 -0700
parents 0f532eb91364
children b5bfd0dd69d6
comparison
equal deleted inserted replaced
2:a547c300bd6e 3:e7f33fa31883
2 """ 2 """
3 return some rdf about the environment, e.g. the current time, 3 return some rdf about the environment, e.g. the current time,
4 daytime/night, overall modes like 'maintenance mode', etc 4 daytime/night, overall modes like 'maintenance mode', etc
5 5
6 """ 6 """
7 import asyncio
7 import datetime 8 import datetime
8 9 import logging
9 import cyclone.web
10 from dateutil.relativedelta import FR, relativedelta 10 from dateutil.relativedelta import FR, relativedelta
11 from dateutil.tz import tzlocal 11 from dateutil.tz import tzlocal
12 from docopt import docopt 12 from prometheus_client import Gauge, Summary
13 from rdflib import Literal, Namespace 13 from rdflib import Literal, Namespace
14 from twisted.internet import defer, reactor, task 14 from starlette.applications import Starlette
15 from starlette.routing import Route
16 from starlette.staticfiles import StaticFiles
17 from starlette_exporter import PrometheusMiddleware, handle_metrics
15 18
16 from standardservice.logsetup import log, verboseLogging 19 import background_loop
17 from patchablegraph import (CycloneGraphEventsHandler, CycloneGraphHandler, PatchableGraph) 20 from patchablegraph import PatchableGraph
18 21 from patchablegraph.handler import StaticGraph, GraphEvents
19 from patch_cyclone import patch_sse 22 # from rdfdoc import Doc
20 from rdfdoc import Doc
21 from twilight import isWithinTwilight 23 from twilight import isWithinTwilight
22
23 patch_sse()
24 24
25 ROOM = Namespace("http://projects.bigasterisk.com/room/") 25 ROOM = Namespace("http://projects.bigasterisk.com/room/")
26 DEV = Namespace("http://projects.bigasterisk.com/device/") 26 DEV = Namespace("http://projects.bigasterisk.com/device/")
27 27
28 # STATS = scales.collection( 28 logging.basicConfig(level=logging.INFO)
29 # '/root', 29
30 # scales.PmfStat('update'), 30 STAT_UPDATE_UP = Gauge('background_loop_up', 'not erroring')
31 # ) 31 STAT_UPDATE_CALLS = Summary('background_loop_calls', 'calls')
32 32
33 33
34 # see pending fix in patchablegraph.py 34 def update(masterGraph):
35 class CycloneGraphEventsHandlerWithCors(CycloneGraphEventsHandler):
36 35
37 def __init__(self, application, request, masterGraph, allowOrigins=None): 36 def stmt(s, p, o):
38 super().__init__(application, request, masterGraph) 37 masterGraph.patchObject(ROOM.environment, s, p, o)
39 self.allowOrigins = allowOrigins or []
40
41 def flush(self):
42 origin = self.request.headers.get('origin', None)
43 if origin and origin in self.allowOrigins:
44 self.set_header(b"Access-Control-Allow-Origin", origin.encode('utf8'))
45 self.set_header(b"Access-Control-Allow-Credentials", b"true")
46 return CycloneGraphEventsHandler.flush(self)
47
48
49 # @STATS.update.time()
50 def update(masterGraph):
51 stmt = lambda s, p, o: masterGraph.patchObject(ROOM.environment, s, p, o)
52 38
53 now = datetime.datetime.now(tzlocal()) 39 now = datetime.datetime.now(tzlocal())
54 40
55 stmt(DEV.environment, ROOM.localHour, Literal(now.hour)) 41 stmt(DEV.environment, ROOM.localHour, Literal(now.hour))
56 stmt(DEV.environment, ROOM.localTimeToMinute, Literal(now.strftime("%H:%M"))) 42 stmt(DEV.environment, ROOM.localTimeToMinute, Literal(now.strftime("%H:%M")))
68 54
69 stmt(DEV.calendar, ROOM.twilight, ROOM['withinTwilight'] if isWithinTwilight(now) else ROOM['daytime']) 55 stmt(DEV.calendar, ROOM.twilight, ROOM['withinTwilight'] if isWithinTwilight(now) else ROOM['daytime'])
70 56
71 57
72 def main(): 58 def main():
73 arg = docopt("""
74 Usage: environment.py [options]
75
76 -v Verbose
77 """)
78 verboseLogging(arg['-v'])
79
80 masterGraph = PatchableGraph() 59 masterGraph = PatchableGraph()
81 60
82 class Application(cyclone.web.Application): 61 asyncio.create_task(background_loop.loop_forever(lambda first: update(masterGraph), 1, STAT_UPDATE_UP, STAT_UPDATE_CALLS))
83 62
84 def __init__(self): 63 app = Starlette(
85 handlers = [ 64 debug=True,
86 (r"/()", cyclone.web.StaticFileHandler, { 65 routes=[
87 "path": ".", 66 Route('/', StaticFiles(directory='.', html=True)),
88 "default_filename": "index.html" 67 Route('/graph/environment', StaticGraph(masterGraph)),
89 }), 68 Route('/graph/environment/events', GraphEvents(masterGraph)),
90 (r'/graph/environment', CycloneGraphHandler, { 69 # Route('/doc', Doc),
91 'masterGraph': masterGraph 70 ])
92 }),
93 (r'/graph/environment/events', CycloneGraphEventsHandlerWithCors, {
94 'masterGraph': masterGraph,
95 'allowOrigins': ['http://localhost:8001'],
96 }),
97 (r'/doc', Doc), # to be shared
98 # (r'/stats/(.*)', StatsHandler, {
99 # 'serverName': 'environment'
100 # }),
101 ]
102 cyclone.web.Application.__init__(self, handlers, masterGraph=masterGraph)
103 71
104 task.LoopingCall(update, masterGraph).start(1) 72 app.add_middleware(PrometheusMiddleware, app_name='environment')
105 reactor.listenTCP(8000, Application()) 73 app.add_route("/metrics", handle_metrics)
106 reactor.run() 74 return app
107 75
108 76
109 if __name__ == '__main__': 77 app = main()
110 main()