diff --git a/app.py b/app.py index ee5ac96..f916a09 100644 --- a/app.py +++ b/app.py @@ -1,9 +1,16 @@ -from flask import Flask, render_template +import json +from flask import Flask, render_template, request, jsonify app = Flask(__name__) @app.route('/') -def hello(): +def index(): return render_template('index.html') +@app.route('/download', methods=['POST']) +def downloadModel(): + modelNodeStates = request.get_json() + print(modelNodeStates) + return 'OK', 200 + if __name__ == '__main__': app.run() \ No newline at end of file diff --git a/static/__init__.py b/static/__init__.py deleted file mode 100644 index 5456384..0000000 --- a/static/__init__.py +++ /dev/null @@ -1,34 +0,0 @@ - -import argparse -import sys -import os - -from .server import start -from .server import stop -from .server import status -from .server import wait -from .server import serve -from .__version__ import __version__ - -def main(): - parser = argparse.ArgumentParser(description='Viewer for neural network, deep learning and machine learning models.') - parser.add_argument('file', metavar='MODEL_FILE', help='model file to serve', nargs='?', default=None) - parser.add_argument('-v', '--version', help="print version", action='store_true') - parser.add_argument('-b', '--browse', help='launch web browser', action='store_true') - parser.add_argument('-p', '--port', help='port to serve', type=int) - parser.add_argument('--host', help="host to serve") - parser.add_argument('--log', help='log details to console', action='store_true') - args = parser.parse_args() - if args.file and not os.path.exists(args.file): - print("Model file '" + args.file + "' does not exist.") - sys.exit(2) - if args.version: - print(__version__) - sys.exit(0) - address = (args.host, args.port) if args.host else args.port if args.port else None - start(args.file, address=address, browse=args.browse, log=args.log) - wait() - sys.exit(0) - -if __name__ == '__main__': - main() diff --git a/static/__version__.py b/static/__version__.py deleted file mode 100644 index 6853c36..0000000 --- a/static/__version__.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = '0.0.0' \ No newline at end of file diff --git a/static/server.py b/static/server.py deleted file mode 100644 index a1919d5..0000000 --- a/static/server.py +++ /dev/null @@ -1,298 +0,0 @@ - -import codecs -import errno -import os -import random -import re -import socket -import sys -import threading -import webbrowser -import time - -from .__version__ import __version__ - -if sys.version_info[0] > 2: - from urllib.parse import urlparse - from urllib.parse import unquote - from http.server import HTTPServer - from http.server import BaseHTTPRequestHandler - from socketserver import ThreadingMixIn -else: - from urlparse import urlparse - from urlparse import unquote - from BaseHTTPServer import HTTPServer - from BaseHTTPServer import BaseHTTPRequestHandler - from SocketServer import ThreadingMixIn - -class HTTPRequestHandler(BaseHTTPRequestHandler): - def handler(self): - if not hasattr(self, 'mime_types_map'): - self.mime_types_map = { - '.html': 'text/html', - '.js': 'text/javascript', - '.css': 'text/css', - '.png': 'image/png', - '.gif': 'image/gif', - '.jpg': 'image/jpeg', - '.ico': 'image/x-icon', - '.json': 'application/json', - '.pb': 'application/octet-stream', - '.ttf': 'font/truetype', - '.otf': 'font/opentype', - '.eot': 'application/vnd.ms-fontobject', - '.woff': 'font/woff', - '.woff2': 'application/font-woff2', - '.svg': 'image/svg+xml' - } - pathname = urlparse(self.path).path - folder = os.path.dirname(os.path.realpath(__file__)) - location = folder + pathname - status_code = 0 - headers = {} - buffer = None - data = '/data/' - if status_code == 0: - if pathname == '/': - meta = [] - meta.append('') - meta.append('') - if self.file: - meta.append('') - with codecs.open(location + 'index.html', mode="r", encoding="utf-8") as open_file: - buffer = open_file.read() - buffer = re.sub(r'', '\n'.join(meta), buffer) - buffer = buffer.encode('utf-8') - headers['Content-Type'] = 'text/html' - headers['Content-Length'] = len(buffer) - status_code = 200 - elif pathname.startswith(data): - file = pathname[len(data):] - if file == self.file and self.data: - buffer = self.data - else: - file = self.folder + '/' + unquote(file) - status_code = 404 - if os.path.exists(file): - with open(file, 'rb') as binary: - buffer = binary.read() - if buffer: - headers['Content-Type'] = 'application/octet-stream' - headers['Content-Length'] = len(buffer) - status_code = 200 - else: - if os.path.exists(location) and not os.path.isdir(location): - extension = os.path.splitext(location)[1] - content_type = self.mime_types_map[extension] - if content_type: - with open(location, 'rb') as binary: - buffer = binary.read() - headers['Content-Type'] = content_type - headers['Content-Length'] = len(buffer) - status_code = 200 - else: - status_code = 404 - if self.log: - sys.stdout.write(str(status_code) + ' ' + self.command + ' ' + self.path + '\n') - sys.stdout.flush() - self.send_response(status_code) - for key in headers: - self.send_header(key, headers[key]) - self.end_headers() - if self.command != 'HEAD': - if status_code == 404 and buffer is None: - self.wfile.write(bytes(status_code)) - elif (status_code in (200, 404)) and buffer is not None: - self.wfile.write(buffer) - def do_GET(self): - self.handler() - def do_HEAD(self): - self.handler() - def log_message(self, format, *args): - return - -class ThreadedHTTPServer(ThreadingMixIn, HTTPServer): - pass - -class HTTPServerThread(threading.Thread): - def __init__(self, data, file, address, log): - threading.Thread.__init__(self) - self.address = address - self.url = 'http://' + address[0] + ':' + str(address[1]) - self.file = file - self.server = ThreadedHTTPServer(address, HTTPRequestHandler) - self.server.timeout = 0.25 - if file: - self.server.RequestHandlerClass.folder = os.path.dirname(file) if os.path.dirname(file) else '.' - self.server.RequestHandlerClass.file = os.path.basename(file) - else: - self.server.RequestHandlerClass.folder = '' - self.server.RequestHandlerClass.file = '' - self.server.RequestHandlerClass.data = data - self.server.RequestHandlerClass.log = log - self.terminate_event = threading.Event() - self.terminate_event.set() - self.stop_event = threading.Event() - - def run(self): - self.stop_event.clear() - self.terminate_event.clear() - try: - while not self.stop_event.is_set(): - self.server.handle_request() - except Exception: - pass - self.terminate_event.set() - self.stop_event.clear() - - def stop(self): - if self.alive(): - sys.stdout.write("Stopping " + self.url + "\n") - self.stop_event.set() - self.server.server_close() - self.terminate_event.wait(1000) - - def alive(self): - return not self.terminate_event.is_set() - -_thread_list = [] - -def _add_thread(thread): - global _thread_list - _thread_list.append(thread) - -def _update_thread_list(address=None): - global _thread_list - _thread_list = [ thread for thread in _thread_list if thread.alive() ] - threads = _thread_list - if address is not None: - address = _make_address(address) - if address[1] is None: - threads = [ thread for thread in threads if address[0] == thread.address[0] ] - else: - threads = [ thread for thread in threads if address[0] == thread.address[0] and address[1] == thread.address[1] ] - return threads - -def _make_address(address): - if address is None or isinstance(address, int): - port = address - address = ('localhost', port) - if isinstance(address, tuple) and len(address) == 2: - host = address[0] - port = address[1] - if isinstance(host, str) and (port is None or isinstance(port, int)): - return address - raise ValueError('Invalid address.') - -def _make_port(address): - if address[1] is None or address[1] == 0: - ports = [] - if address[1] != 0: - ports.append(8080) - ports.append(8081) - rnd = random.Random() - for _ in range(4): - port = rnd.randrange(15000, 25000) - if port not in ports: - ports.append(port) - ports.append(0) - for port in ports: - temp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - temp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - temp_socket.settimeout(1) - try: - temp_socket.bind((address[0], port)) - sockname = temp_socket.getsockname() - address = (address[0], sockname[1]) - return address - except: - pass - finally: - temp_socket.close() - if isinstance(address[1], int): - return address - raise ValueError('Failed to allocate port.') - -def stop(address=None): - '''Stop serving model at address. - - Args: - address (tuple, optional): A (host, port) tuple, or a port number. - ''' - threads = _update_thread_list(address) - for thread in threads: - thread.stop() - _update_thread_list() - -def status(adrress=None): - '''Is model served at address. - - Args: - address (tuple, optional): A (host, port) tuple, or a port number. - ''' - threads = _update_thread_list(adrress) - return len(threads) > 0 - -def wait(): - '''Wait for console exit and stop all model servers.''' - try: - while len(_update_thread_list()) > 0: - time.sleep(1000) - except (KeyboardInterrupt, SystemExit): - sys.stdout.write('\n') - sys.stdout.flush() - stop() - -def serve(file, data, address=None, browse=False, log=False): - '''Start serving model from file or data buffer at address and open in web browser. - - Args: - file (string): Model file to serve. Required to detect format. - data (bytes): Model data to serve. None will load data from file. - log (bool, optional): Log details to console. Default: False - browse (bool, optional): Launch web browser. Default: True - address (tuple, optional): A (host, port) tuple, or a port number. - - Returns: - A (host, port) address tuple. - ''' - if not data and file and not os.path.exists(file): - raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), file) - - _update_thread_list() - address = _make_address(address) - if isinstance(address[1], int) and address[1] != 0: - stop(address) - else: - address = _make_port(address) - _update_thread_list() - - thread = HTTPServerThread(data, file, address, log) - thread.start() - while not thread.alive(): - time.sleep(10) - _add_thread(thread) - - if file: - sys.stdout.write("Serving '" + file + "' at " + thread.url + "\n") - else: - sys.stdout.write("Serving at " + thread.url + "\n") - sys.stdout.flush() - if browse: - webbrowser.open(thread.url) - - return address - -def start(file=None, address=None, browse=True, log=False): - '''Start serving model file at address and open in web browser. - - Args: - file (string): Model file to serve. - log (bool, optional): Log details to console. Default: False - browse (bool, optional): Launch web browser, Default: True - address (tuple, optional): A (host, port) tuple, or a port number. - - Returns: - A (host, port) address tuple. - ''' - return serve(file, None, browse=browse, address=address, log=log) diff --git a/static/view-sidebar.js b/static/view-sidebar.js index 26cd81f..bc7a28d 100644 --- a/static/view-sidebar.js +++ b/static/view-sidebar.js @@ -267,6 +267,19 @@ sidebar.NodeSidebar = class { } } + + _strMapToObj(strMap){ + let obj = Object.create(null); + for (let[k, v] of strMap) { + obj[k] = v; + } + return obj; + } + + _mapToJson(map) { + return JSON.stringify(this._strMapToObj(map)); + } + // My code _addButton(title) { const buttonElement = this._host.document.createElement('button'); @@ -293,6 +306,31 @@ sidebar.NodeSidebar = class { }); } + if (title === 'Download') { + buttonElement.addEventListener('click', () => { + // console.log(this._host._view._graph._modelNodeName2State) + // https://healeycodes.com/talking-between-languages + fetch('/download', { + // Declare what type of data we're sending + headers: { + 'Content-Type': 'application/json' + }, + // Specify the method + method: 'POST', + // https://blog.csdn.net/Crazy_SunShine/article/details/80624366 + body: this._mapToJson( + this._host._view._graph._modelNodeName2State + ) + }).then(function (response) { + return response.text(); + }).then(function (text) { + console.log('POST response: '); + // Should be 'OK' if everything was successful + console.log(text); + }); + }); + } + } diff --git a/static/weka.js b/static/weka.js deleted file mode 100644 index d121dc3..0000000 --- a/static/weka.js +++ /dev/null @@ -1,256 +0,0 @@ - -// Experimental - -var weka = weka || {}; -var json = json || require('./json'); -var java = {}; - -weka.ModelFactory = class { - - match(context) { - try { - const stream = context.stream; - if (stream.length >= 5) { - const signature = [ 0xac, 0xed ]; - if (stream.peek(2).every((value, index) => value === signature[index])) { - const reader = new java.io.InputObjectStream(stream); - const obj = reader.read(); - if (obj && obj.$class && obj.$class.name) { - return 'weka'; - } - } - } - } - catch (err) { - // continue regardless of error - } - return undefined; - } - - open(context) { - return Promise.resolve().then(() => { - const reader = new java.io.InputObjectStream(context.stream); - const obj = reader.read(); - throw new weka.Error("Unsupported type '" + obj.$class.name + "'."); - }); - } -}; - -weka.Error = class extends Error { - - constructor(message) { - super(message); - this.name = 'Error loading Weka model.'; - } -}; - -java.io = {}; - -java.io.InputObjectStream = class { - - constructor(stream) { - // Object Serialization Stream Protocol - // https://www.cis.upenn.edu/~bcpierce/courses/629/jdkdocs/guide/serialization/spec/protocol.doc.html - if (stream.length < 5) { - throw new java.io.Error('Invalid stream size'); - } - const signature = [ 0xac, 0xed ]; - if (!stream.peek(2).every((value, index) => value === signature[index])) { - throw new java.io.Error('Invalid stream signature'); - } - this._reader = new java.io.InputObjectStream.BinaryReader(stream.peek()); - this._references = []; - this._reader.skip(2); - const version = this._reader.uint16(); - if (version !== 0x0005) { - throw new java.io.Error("Unsupported version '" + version + "'."); - } - } - - read() { - return this._object(); - } - - _object() { - const code = this._reader.byte(); - switch (code) { - case 0x73: { // TC_OBJECT - const obj = {}; - obj.$class = this._classDesc(); - this._newHandle(obj); - this._classData(obj); - return obj; - } - case 0x74: { // TC_STRING - return this._newString(false); - } - } - throw new java.io.Error("Unsupported code '" + code + "'."); - } - - _classDesc() { - const code = this._reader.byte(); - switch (code) { - case 0x72: // TC_CLASSDESC - this._reader.skip(-1); - return this._newClassDesc(); - case 0x71: // TC_REFERENCE - return this._references[this._reader.uint32() - 0x7e0000]; - case 0x70: // TC_NULL - this._reader.byte(); - return null; - } - throw new java.io.Error("Unsupported code '" + code + "'."); - } - - _newClassDesc() { - const code = this._reader.byte(); - switch (code) { - case 0x72: { // TC_CLASSDESC - const classDesc = {}; - classDesc.name = this._reader.string(), - classDesc.id = this._reader.uint64().toString(); - this._newHandle(classDesc); - classDesc.flags = this._reader.byte(); - classDesc.fields = []; - const count = this._reader.uint16(); - for (let i = 0; i < count; i++) { - const field = {}; - field.type = String.fromCharCode(this._reader.byte()); - field.name = this._reader.string(); - if (field.type === '[' || field.type === 'L') { - field.classname = this._object(); - } - classDesc.fields.push(field); - } - if (this._reader.byte() !== 0x78) { - throw new java.io.Error('Expected TC_ENDBLOCKDATA.'); - } - classDesc.superClass = this._classDesc(); - return classDesc; - } - case 0x7D: // TC_PROXYCLASSDESC - break; - } - throw new java.io.Error("Unsupported code '" + code + "'."); - } - - _classData(/* obj */) { - /* - const classname = obj.$class.name; - let flags = obj.$class.flags; - let superClass = obj.$class.superClass; - while (superClass) { - flags |= superClass.flags; - superClass = superClass.superClass; - } - if (flags & 0x02) { // SC_SERIALIZABLE - debugger; - var customObject = objects[classname]; - var hasReadObjectMethod = customObject && customObject.readObject; - if (flags & 0x01) { // SC_WRITE_METHOD - if (!hasReadObjectMethod) { - throw new Error('Class "'+ classname + '" dose not implement readObject()'); - } - customObject.readObject(this, obj); - if (this._reader.byte() !== 0x78) { // TC_ENDBLOCKDATA - throw new java.io.Error('Expected TC_ENDBLOCKDATA.'); - } - } - else { - if (hasReadObjectMethod) { - customObject.readObject(this, obj); - if (this._reader.byte() !== 0x78) { // TC_ENDBLOCKDATA - throw new java.io.Error('Expected TC_ENDBLOCKDATA.'); - } - } - else { - this._nowrclass(obj); - } - } - } - else if (flags & 0x04) { // SC_EXTERNALIZABLE - if (flags & 0x08) { // SC_BLOCK_DATA - this._objectAnnotation(obj); - } - else { - this._externalContents(); - } - } - else { - throw new Error('Illegal flags: ' + flags); - } - */ - } - - _newString(long) { - const value = this._reader.string(long); - this._newHandle(value); - return value; - } - - _newHandle(obj) { - this._references.push(obj); - } -}; - -java.io.InputObjectStream.BinaryReader = class { - - constructor(buffer) { - this._buffer = buffer; - this._position = 0; - this._length = buffer.length; - this._view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength); - this._decoder = new TextDecoder('utf-8'); - } - - skip(offset) { - this._position += offset; - if (this._position > this._end) { - throw new java.io.Error('Expected ' + (this._position - this._end) + ' more bytes. The file might be corrupted. Unexpected end of file.'); - } - } - - byte() { - const position = this._position; - this.skip(1); - return this._buffer[position]; - } - - uint16() { - const position = this._position; - this.skip(2); - return this._view.getUint16(position, false); - } - - uint32() { - const position = this._position; - this.skip(4); - return this._view.getUint32(position, false); - } - - uint64() { - const position = this._position; - this.skip(8); - return this._view.getUint64(position, false); - } - - string(long) { - const size = long ? this.uint64().toNumber() : this.uint16(); - const position = this._position; - this.skip(size); - return this._decoder.decode(this._buffer.subarray(position, this._position)); - } -}; - -java.io.Error = class extends Error { - - constructor(message) { - super(message); - this.name = 'Error loading Object Serialization Stream Protocol.'; - } -}; - -if (typeof module !== 'undefined' && typeof module.exports === 'object') { - module.exports.ModelFactory = weka.ModelFactory; -} \ No newline at end of file