Edit on GitHub

pytact.visualisation_webserver

  1from dataclasses import asdict
  2from pathlib import Path
  3import argparse
  4import pkg_resources
  5import inflection
  6from functools import partial
  7
  8from pytact.data_reader import data_reader
  9from pytact.graph_visualize_browse import (
 10    GraphVisualizationData, GraphVisualizator, UrlMaker, Settings, GraphVisualizationOutput)
 11
 12import capnp
 13import pytact.graph_api_capnp as graph_api_capnp
 14
 15from sanic import Sanic
 16from sanic_ext import validate
 17from sanic.worker.loader import AppLoader
 18
 19def post_process(output: GraphVisualizationOutput, settings: Settings):
 20    result = asdict(output)
 21    result['settings'] = settings
 22    result['edge_labels'] = [(v, inflection.camelize(name)) for (name, v) in
 23                             graph_api_capnp.EdgeClassification.schema.enumerants.items()]
 24    result['node_labels'] = [(v, inflection.camelize(name)) for (v, name) in
 25                             enumerate(list(graph_api_capnp.Graph.Node.Label.schema.union_fields))]
 26    return result
 27
 28def create_app(dataset_path: Path) -> Sanic:
 29    app = Sanic("graph-visualizer")
 30    app.config.TEMPLATING_PATH_TO_TEMPLATES = pkg_resources.resource_filename('pytact', 'templates/')
 31
 32    @app.before_server_start
 33    async def setup_dataset(app):
 34        app.ctx.data_ctx = data_reader(dataset_path)
 35        app.ctx.gvd = GraphVisualizationData(app.ctx.data_ctx.__enter__())
 36    @app.after_server_stop
 37    async def teardown_dataset(app):
 38        del app.ctx.gvd
 39        app.ctx.data_ctx.__exit__(None, None, None)
 40        del app.ctx.data_ctx
 41
 42    class SanicUrlMaker(UrlMaker):
 43
 44        def __init__(self, settings: Settings):
 45            self.query = {k: v for k, v in asdict(settings).items() if type(v) != bool or v}
 46
 47        def definition(self, fname: Path, defid: int) -> str:
 48            return app.url_for('definition', path=fname.with_suffix(''), defid=defid, **self.query)
 49
 50        def proof(self, fname: Path, defid: int) -> str:
 51            return app.url_for('proof', path=fname.with_suffix(''), defid=defid, **self.query)
 52
 53        def outcome(self, fname: Path, defid: int, stepi: int, outcomei: int) -> str:
 54            return app.url_for('outcome', path=fname.with_suffix(''),
 55                            defid=defid, stepi=stepi, outcomei=outcomei, **self.query)
 56
 57        def global_context(self, fname: Path) -> str:
 58            return app.url_for('global_context', path=fname.with_suffix(''), **self.query)
 59
 60        def folder(self, path: Path) -> str:
 61            return app.url_for('folder', path=path, **self.query)
 62
 63        def root_folder(self) -> str:
 64            return app.url_for('root_folder', **self.query)
 65
 66    @app.get('/<path:path>/definition/<defid:int>/proof/step/<stepi:int>/outcome/<outcomei:int>')
 67    @validate(query=Settings)
 68    @app.ext.template("visualizer.html")
 69    async def outcome(request, path: str, defid: str, stepi: str, outcomei: str, query: Settings):
 70        gv = GraphVisualizator(app.ctx.gvd, SanicUrlMaker(query), query)
 71        return post_process(gv.outcome(Path(path).with_suffix(".bin"), int(defid), int(stepi), int(outcomei)), query)
 72
 73    @app.get('/<path:path>/definition/<defid:int>/proof')
 74    @validate(query=Settings)
 75    @app.ext.template("visualizer.html")
 76    async def proof(request, path: str, defid: str, query: Settings):
 77        gv = GraphVisualizator(app.ctx.gvd, SanicUrlMaker(query), query)
 78        return post_process(gv.proof(Path(path).with_suffix(".bin"), int(defid)), query)
 79
 80    @app.get('/<path:path>/definition/<defid:int>')
 81    @validate(query=Settings)
 82    @app.ext.template("visualizer.html")
 83    async def definition(request, path: str, defid: str, query: Settings):
 84        gv = GraphVisualizator(app.ctx.gvd, SanicUrlMaker(query), query)
 85        return post_process(gv.definition(Path(path).with_suffix(".bin"), int(defid)), query)
 86
 87    @app.get('/<path:path>/context')
 88    @validate(query=Settings)
 89    @app.ext.template("visualizer.html")
 90    async def global_context(request, path: str, query: Settings):
 91        gv = GraphVisualizator(app.ctx.gvd, SanicUrlMaker(query), query)
 92        return post_process(gv.global_context(Path(path).with_suffix(".bin")), query)
 93
 94    @app.get('/<path:path>')
 95    @validate(query=Settings)
 96    @app.ext.template("visualizer.html")
 97    async def folder(request, path: str, query: Settings):
 98        gv = GraphVisualizator(app.ctx.gvd, SanicUrlMaker(query), query)
 99        return post_process(gv.folder(Path(path)), query)
100
101    @app.get('/')
102    @validate(query=Settings)
103    @app.ext.template("visualizer.html")
104    async def root_folder(request, query: Settings):
105        gv = GraphVisualizator(app.ctx.gvd, SanicUrlMaker(query), query)
106        return post_process(gv.folder(Path()), query)
107
108    return app
109
110def main():
111
112    parser = argparse.ArgumentParser(
113        description = 'Start an interactive server that visualizes a dataset',
114        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
115
116    parser.add_argument('dataset',
117                        type=str,
118                        help=('The location of the dataset to visualize. ' +
119                              'Either a dataset directory, or a SquashFS image, ' +
120                              'which will be automatically mounted.'))
121    parser.add_argument('--port',
122                        type=int,
123                        default=8080,
124                        help='the port where the webserver should listen')
125    parser.add_argument('--hostname',
126                       type=str,
127                       default='0.0.0.0',
128                       help='the ip or domain of the hosting machine')
129    parser.add_argument('--dev',
130                        action='store_true',
131                        help='run the server in development mode')
132    group = parser.add_mutually_exclusive_group()
133    group.add_argument('--fast', action='store_true', default=False,
134                       help='Run the server with an optimal number of worker')
135    group.add_argument('--workers', type=int, default=1,
136                       help='The number of workers to use')
137
138    args = parser.parse_args()
139
140    dataset_path = Path(args.dataset).resolve()
141
142    loader = AppLoader(factory=partial(create_app, dataset_path))
143    app = loader.load()
144    app.prepare(host=args.hostname, port=args.port, dev=args.dev, fast=args.fast, workers=args.workers)
145    Sanic.serve(primary=app, app_loader=loader)
146
147if __name__ == '__main__':
148    main()
20def post_process(output: GraphVisualizationOutput, settings: Settings):
21    result = asdict(output)
22    result['settings'] = settings
23    result['edge_labels'] = [(v, inflection.camelize(name)) for (name, v) in
24                             graph_api_capnp.EdgeClassification.schema.enumerants.items()]
25    result['node_labels'] = [(v, inflection.camelize(name)) for (v, name) in
26                             enumerate(list(graph_api_capnp.Graph.Node.Label.schema.union_fields))]
27    return result
def create_app(dataset_path: pathlib.Path) -> sanic.app.Sanic:
 29def create_app(dataset_path: Path) -> Sanic:
 30    app = Sanic("graph-visualizer")
 31    app.config.TEMPLATING_PATH_TO_TEMPLATES = pkg_resources.resource_filename('pytact', 'templates/')
 32
 33    @app.before_server_start
 34    async def setup_dataset(app):
 35        app.ctx.data_ctx = data_reader(dataset_path)
 36        app.ctx.gvd = GraphVisualizationData(app.ctx.data_ctx.__enter__())
 37    @app.after_server_stop
 38    async def teardown_dataset(app):
 39        del app.ctx.gvd
 40        app.ctx.data_ctx.__exit__(None, None, None)
 41        del app.ctx.data_ctx
 42
 43    class SanicUrlMaker(UrlMaker):
 44
 45        def __init__(self, settings: Settings):
 46            self.query = {k: v for k, v in asdict(settings).items() if type(v) != bool or v}
 47
 48        def definition(self, fname: Path, defid: int) -> str:
 49            return app.url_for('definition', path=fname.with_suffix(''), defid=defid, **self.query)
 50
 51        def proof(self, fname: Path, defid: int) -> str:
 52            return app.url_for('proof', path=fname.with_suffix(''), defid=defid, **self.query)
 53
 54        def outcome(self, fname: Path, defid: int, stepi: int, outcomei: int) -> str:
 55            return app.url_for('outcome', path=fname.with_suffix(''),
 56                            defid=defid, stepi=stepi, outcomei=outcomei, **self.query)
 57
 58        def global_context(self, fname: Path) -> str:
 59            return app.url_for('global_context', path=fname.with_suffix(''), **self.query)
 60
 61        def folder(self, path: Path) -> str:
 62            return app.url_for('folder', path=path, **self.query)
 63
 64        def root_folder(self) -> str:
 65            return app.url_for('root_folder', **self.query)
 66
 67    @app.get('/<path:path>/definition/<defid:int>/proof/step/<stepi:int>/outcome/<outcomei:int>')
 68    @validate(query=Settings)
 69    @app.ext.template("visualizer.html")
 70    async def outcome(request, path: str, defid: str, stepi: str, outcomei: str, query: Settings):
 71        gv = GraphVisualizator(app.ctx.gvd, SanicUrlMaker(query), query)
 72        return post_process(gv.outcome(Path(path).with_suffix(".bin"), int(defid), int(stepi), int(outcomei)), query)
 73
 74    @app.get('/<path:path>/definition/<defid:int>/proof')
 75    @validate(query=Settings)
 76    @app.ext.template("visualizer.html")
 77    async def proof(request, path: str, defid: str, query: Settings):
 78        gv = GraphVisualizator(app.ctx.gvd, SanicUrlMaker(query), query)
 79        return post_process(gv.proof(Path(path).with_suffix(".bin"), int(defid)), query)
 80
 81    @app.get('/<path:path>/definition/<defid:int>')
 82    @validate(query=Settings)
 83    @app.ext.template("visualizer.html")
 84    async def definition(request, path: str, defid: str, query: Settings):
 85        gv = GraphVisualizator(app.ctx.gvd, SanicUrlMaker(query), query)
 86        return post_process(gv.definition(Path(path).with_suffix(".bin"), int(defid)), query)
 87
 88    @app.get('/<path:path>/context')
 89    @validate(query=Settings)
 90    @app.ext.template("visualizer.html")
 91    async def global_context(request, path: str, query: Settings):
 92        gv = GraphVisualizator(app.ctx.gvd, SanicUrlMaker(query), query)
 93        return post_process(gv.global_context(Path(path).with_suffix(".bin")), query)
 94
 95    @app.get('/<path:path>')
 96    @validate(query=Settings)
 97    @app.ext.template("visualizer.html")
 98    async def folder(request, path: str, query: Settings):
 99        gv = GraphVisualizator(app.ctx.gvd, SanicUrlMaker(query), query)
100        return post_process(gv.folder(Path(path)), query)
101
102    @app.get('/')
103    @validate(query=Settings)
104    @app.ext.template("visualizer.html")
105    async def root_folder(request, query: Settings):
106        gv = GraphVisualizator(app.ctx.gvd, SanicUrlMaker(query), query)
107        return post_process(gv.folder(Path()), query)
108
109    return app
def main():
111def main():
112
113    parser = argparse.ArgumentParser(
114        description = 'Start an interactive server that visualizes a dataset',
115        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
116
117    parser.add_argument('dataset',
118                        type=str,
119                        help=('The location of the dataset to visualize. ' +
120                              'Either a dataset directory, or a SquashFS image, ' +
121                              'which will be automatically mounted.'))
122    parser.add_argument('--port',
123                        type=int,
124                        default=8080,
125                        help='the port where the webserver should listen')
126    parser.add_argument('--hostname',
127                       type=str,
128                       default='0.0.0.0',
129                       help='the ip or domain of the hosting machine')
130    parser.add_argument('--dev',
131                        action='store_true',
132                        help='run the server in development mode')
133    group = parser.add_mutually_exclusive_group()
134    group.add_argument('--fast', action='store_true', default=False,
135                       help='Run the server with an optimal number of worker')
136    group.add_argument('--workers', type=int, default=1,
137                       help='The number of workers to use')
138
139    args = parser.parse_args()
140
141    dataset_path = Path(args.dataset).resolve()
142
143    loader = AppLoader(factory=partial(create_app, dataset_path))
144    app = loader.load()
145    app.prepare(host=args.hostname, port=args.port, dev=args.dev, fast=args.fast, workers=args.workers)
146    Sanic.serve(primary=app, app_loader=loader)