|
| 1 | +"""odmlview |
| 2 | +
|
| 3 | +odmlview sets up a minimal webserver to view odml files saved in the |
| 4 | +XML format via the webbrowser. After it is started, the webserver will |
| 5 | +open a new tab in the default webbrowser and display the content of |
| 6 | +the directory the server was started from. odML files can then be |
| 7 | +viewed from there. |
| 8 | +To properly render XML, an odML file may contain the element |
| 9 | +'<?xml-stylesheet type="text/xsl" href="odmlTerms.xsl"?>' where the |
| 10 | +'odmlTerms.xsl' stylesheet should reside in the same directory as the |
| 11 | +odML file to be rendered. By using the '--fetch' flag the latest version |
| 12 | +of this stylesheet will be downloaded from 'templates.g-node.org' to |
| 13 | +the current directory when starting up the service. |
| 14 | +
|
| 15 | +Usage: odmlview [-p PORT] [--fetch] |
| 16 | +
|
| 17 | +Options: |
| 18 | + -p PORT Port the server will use. Default: 8000 |
| 19 | + --fetch Fetch latest stylesheet from templates.g-node.org |
| 20 | + to current directory |
| 21 | + -h --help Show this screen |
| 22 | + --version Show version |
| 23 | +""" |
| 24 | + |
| 25 | +import os |
| 26 | +try: |
| 27 | + import http.server as hs |
| 28 | +except ImportError: |
| 29 | + print("This script is only supported with Python 3") |
| 30 | + exit(-1) |
| 31 | + |
| 32 | +import socketserver |
| 33 | +import sys |
| 34 | +import urllib.request as urllib2 |
| 35 | +import webbrowser |
| 36 | + |
| 37 | +from docopt import docopt |
| 38 | + |
| 39 | +PORT = 8000 |
| 40 | +REPOSITORY = "https://templates.g-node.org/_resources/" |
| 41 | +STYLESHEET = "odmlTerms.xsl" |
| 42 | + |
| 43 | + |
| 44 | +def download_file(repo, filename): |
| 45 | + """ |
| 46 | + download_file fetches 'filename' from url 'repo' and |
| 47 | + saves it in the current directory as file 'filename'. |
| 48 | + """ |
| 49 | + try: |
| 50 | + data = urllib2.urlopen("%s%s" % (repo, filename)).read() |
| 51 | + data = data.decode("utf-8") |
| 52 | + except Exception as err: |
| 53 | + print("[Warning] Failed loading '%s%s': %s" % (repo, filename, err)) |
| 54 | + return |
| 55 | + |
| 56 | + with open(filename, "w") as local_file: |
| 57 | + local_file.write(str(data)) |
| 58 | + |
| 59 | + |
| 60 | +def run(port=PORT, extensions=None): |
| 61 | + """ |
| 62 | + run starts a simple webserver on localhost serving the current directory. |
| 63 | + Once started, it will open a tab on the default webbrowser and will continue |
| 64 | + to serve until manually stopped. |
| 65 | +
|
| 66 | + :param port: server port |
| 67 | + :param extensions: dictionary containing additional file extension - mime type |
| 68 | + mappings the server should be aware of. |
| 69 | + e.g. {'.xml': 'application/xml'} |
| 70 | + """ |
| 71 | + handler = hs.SimpleHTTPRequestHandler |
| 72 | + |
| 73 | + if extensions: |
| 74 | + handler.extensions_map.update(extensions) |
| 75 | + |
| 76 | + server_address = ('', port) |
| 77 | + |
| 78 | + socketserver.TCPServer.allow_reuse_address = True |
| 79 | + with socketserver.TCPServer(server_address, handler) as httpd: |
| 80 | + webbrowser.open_new_tab('http://localhost:%s' % port) |
| 81 | + try: |
| 82 | + print("[Info] The server can be stopped by pressing Ctrl+C") |
| 83 | + httpd.serve_forever() |
| 84 | + except KeyboardInterrupt: |
| 85 | + print("[Info] Received Keyboard interrupt, shutting down") |
| 86 | + httpd.shutdown() |
| 87 | + httpd.server_close() |
| 88 | + |
| 89 | + |
| 90 | +def main(args=None): |
| 91 | + parser = docopt(__doc__, argv=args, version="0.1.0") |
| 92 | + |
| 93 | + # Fetch stylesheet |
| 94 | + if parser['--fetch'] and not os.path.exists(STYLESHEET): |
| 95 | + print("[Info] Downloading stylesheet '%s'" % STYLESHEET) |
| 96 | + download_file(REPOSITORY, STYLESHEET) |
| 97 | + |
| 98 | + server_port = int(parser['-p']) if parser['-p'] else PORT |
| 99 | + |
| 100 | + # files with odML file extensions should be interpreted as XML |
| 101 | + extensions = {'.odml': 'application/xml'} |
| 102 | + |
| 103 | + run(server_port, extensions) |
| 104 | + |
| 105 | + |
| 106 | +if __name__ == "__main__": |
| 107 | + main(sys.argv[1:]) |
0 commit comments