├── .gitignore ├── LICENSE ├── client ├── index.html └── js │ ├── alignment.js │ ├── translate.js │ └── vcomponent.js └── server.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Lua sources 2 | luac.out 3 | 4 | # luarocks build files 5 | *.src.rock 6 | *.zip 7 | *.tar.gz 8 | 9 | # Object files 10 | *.o 11 | *.os 12 | *.ko 13 | *.obj 14 | *.elf 15 | 16 | # Precompiled Headers 17 | *.gch 18 | *.pch 19 | 20 | # Libraries 21 | *.lib 22 | *.a 23 | *.la 24 | *.lo 25 | *.def 26 | *.exp 27 | 28 | # Shared objects (inc. Windows DLLs) 29 | *.dll 30 | *.so 31 | *.so.* 32 | *.dylib 33 | 34 | # Executables 35 | *.exe 36 | *.out 37 | *.app 38 | *.i*86 39 | *.x86_64 40 | *.hex 41 | 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 OpenNMT 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 |
22 |
23 | 24 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /client/js/alignment.js: -------------------------------------------------------------------------------- 1 | class AlignmentComponent extends VComponent { 2 | 3 | get events() { 4 | return { 5 | } 6 | } 7 | 8 | get defaultOptions() { 9 | return { 10 | } 11 | } 12 | 13 | // Data Style 14 | get _dataStyle() { 15 | return { 16 | } 17 | } 18 | 19 | 20 | // INIT METHODS 21 | _init() { 22 | this.base = this.parent.append("g") 23 | } 24 | 25 | _wrangle(data) { 26 | let attn = []; 27 | _.map(data.attn, (d, i) => _.map(d, (v, j) => attn.push({src: j, tgt: i, val: v}))); 28 | data.attnGroup = attn; 29 | return data; 30 | } 31 | 32 | 33 | _render(renderData) { 34 | let xWord = d3.scaleLinear().domain([0,25]).range([0, 1500]); 35 | console.log(renderData.src); 36 | this.src = this.base.append("g"); 37 | 38 | 39 | const src_words = this.src.selectAll("g") 40 | .data(renderData.src) 41 | .enter(); 42 | src_words 43 | .append("g") 44 | .attr("transform", (d, i) => "translate(" + xWord(i) + ", 0)") 45 | .append("text") 46 | .text(d => d); 47 | 48 | this.tgt = this.base.append("g").attr("transform", "translate(0, 100)") 49 | 50 | const tgt_words = this.tgt.selectAll("g") 51 | .data(renderData.message) 52 | .enter(); 53 | tgt_words 54 | .append("g") 55 | .attr("transform", (d, i) => "translate(" + xWord(i) + ", 0)") 56 | .append("text") 57 | .text(d => d); 58 | 59 | console.log(renderData.attnGroup); 60 | this.base.selectAll(".attn") 61 | .data(renderData.attnGroup) 62 | .enter() 63 | .append("line") 64 | .classed("attn", true) 65 | .attr("x1", d => xWord(d.src) + (xWord(0.5) -xWord(0))) 66 | .attr("x2", d => xWord(d.tgt) + (xWord(0.5) -xWord(0))) 67 | .attr("y1", 10) 68 | .attr("y2", 90) 69 | .style("stroke", "black") 70 | .style("opacity", d => d.val); 71 | 72 | 73 | } 74 | } 75 | 76 | AlignmentComponent; 77 | -------------------------------------------------------------------------------- /client/js/translate.js: -------------------------------------------------------------------------------- 1 | class TranslateComponent extends VComponent { 2 | 3 | get events() { 4 | return { 5 | } 6 | } 7 | 8 | get defaultOptions() { 9 | return { 10 | } 11 | } 12 | 13 | // Data Style 14 | get _dataStyle() { 15 | return { 16 | } 17 | } 18 | 19 | 20 | // INIT METHODS 21 | _init() { 22 | let form = this.parent.append("form"); 23 | this.input = form 24 | .append("input") 25 | .attr("type","input"); 26 | this.buttom = form 27 | .append("input") 28 | .attr("value", "Translate") 29 | .attr("type", "button") 30 | .on("click",() => this.translate()); 31 | this.output = form 32 | .append("input") 33 | .attr("type","input"); 34 | this.svg = this.parent.append("svg").attr("width",2000).attr("height", 1000); 35 | let alignments = SVG.group(this.svg, "", {x: 0, y: 100}); 36 | this.alignment = [new AlignmentComponent({parent: alignments})]; 37 | } 38 | 39 | translate() { 40 | console.log(this.input.property("value")); 41 | $.ajax("/api/translate?src="+this.input.property("value"), { 42 | dataType : 'json', 43 | success: translation => { 44 | console.log(translation); 45 | // this.output.property("value", translation.message); 46 | this.alignment[0].update(translation[1]); 47 | } 48 | }) 49 | } 50 | 51 | // RENDER/WRANGLE METHODS 52 | _wrangle(data) { 53 | } 54 | 55 | _render(renderData) { 56 | 57 | } 58 | 59 | // ACTION METHODS 60 | actionHoverCell(x, y, select) { 61 | const hovered = this.hm.selectAll(".x" + x + ".y" + y); 62 | hovered.classed('hovered', select); 63 | if (select) { 64 | const datum = hovered.datum(); 65 | this.tooltip.attrs({ 66 | opacity: 1, 67 | "transform": SVG.translate({ 68 | x: this.scaleX(datum.col), 69 | y: this.scaleY(datum.row + 1) + 5 70 | }) 71 | }).select('text').text(datum.label); 72 | } else { 73 | this.tooltip.attrs({opacity: 0}); 74 | } 75 | } 76 | 77 | bindEvents(handler) { 78 | this.eventHandler = handler; 79 | handler.bind(this.events.cellHovered, (e, data) => 80 | this.actionHoverCell(data.col, data.row, data.active)); 81 | 82 | handler.bind(this.events.rectSelected, (e, hm_id) => 83 | this.hm.selectAll('.mapping-rect-button').classed('selected', this.id == hm_id)); 84 | 85 | handler.bind(this.events.circleSelected, (e, hm_id) => 86 | this.hm.selectAll('.mapping-circle-button').classed('selected', this.id == hm_id)); 87 | } 88 | } 89 | 90 | TranslateComponent; 91 | -------------------------------------------------------------------------------- /client/js/vcomponent.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Hendrik Strobelt (hendrik.strobelt.com) on 12/3/16. 3 | */ 4 | 5 | class VComponent { 6 | 7 | /** 8 | * The static property that contains all class related events. 9 | * Should be overwritten and event strings have to be unique!! 10 | * @returns {{}} an key-value object for object to string 11 | */ 12 | static get events() { 13 | console.error('static get events() -- not implemented'); 14 | 15 | return {noEvent: 'VComponent_noEvent'} 16 | } 17 | 18 | /** 19 | * Should be overwritten to define the set of ALL options and their defaults 20 | * @returns {{}} an key-value object for default options 21 | */ 22 | static get defaultOptions() { 23 | console.error('static get defaultOptions() not implemented'); 24 | 25 | return {}; 26 | } 27 | 28 | /** 29 | * Inits the class and creates static DOM elements 30 | * @param {Element} parent SVG DOM Element 31 | * @param {Object} options initial options 32 | */ 33 | constructor({parent, options}) { 34 | this.parent = parent; 35 | 36 | const defaults = this.defaultOptions; 37 | this.options = {}; 38 | this.id = Util.simpleUId({}); 39 | Object.keys(defaults).forEach(key => this.options[key] = options[key] || defaults[key]); 40 | 41 | this._init() 42 | } 43 | 44 | /** 45 | * Should be overwritten to create the static DOM elements 46 | * @private 47 | * @return {*} --- 48 | */ 49 | _init() { 50 | console.error(this.constructor.name + '._init() not implemented') 51 | } 52 | 53 | /** 54 | * Every time data has changed, update is called and 55 | * triggers wrangling and re-rendering 56 | * @param {Object} data data object 57 | * @return {*} --- 58 | */ 59 | update(data) { 60 | this.data = data; 61 | this.renderData = this._wrangle(data); 62 | this._render(this.renderData); 63 | } 64 | 65 | 66 | /** 67 | * Data wrangling method -- implement in subclass 68 | * @param {Object} data data 69 | * @returns {*} --- 70 | * @private 71 | */ 72 | _wrangle(data) { 73 | 74 | return data; 75 | } 76 | 77 | /** 78 | * Is responsible for mapping data to DOM elements 79 | * @param {Object} renderData pre-processed (wrangled) data 80 | * @private 81 | * @returns {*} --- 82 | */ 83 | _render(renderData) { 84 | console.error(this.constructor.name + '._render() not implemented', renderData) 85 | } 86 | 87 | 88 | /** 89 | * Updates instance options 90 | * @param {Object} options only the options that should be updated 91 | * @returns {*} --- 92 | */ 93 | updateOptions({options}) { 94 | Object.keys(options).forEach(k => this.options[k] = options[k]); 95 | } 96 | 97 | } 98 | 99 | VComponent; 100 | 101 | /** 102 | * Created by Hendrik Strobelt (hendrik.strobelt.com) on 11/30/16. 103 | */ 104 | 105 | class SVG { 106 | static translate({x, y}) {return "translate(" + x + "," + y + ")"} 107 | 108 | static group(parent, classes, pos) { 109 | return parent.append('g') 110 | .attr("class", classes) 111 | .attr("transform", SVG.translate(pos)); 112 | 113 | } 114 | 115 | } 116 | 117 | let the_unique_id_counter = 0; 118 | class Util { 119 | static simpleUId({prefix = ''}) { 120 | the_unique_id_counter += 1; 121 | 122 | return prefix + the_unique_id_counter; 123 | } 124 | } 125 | 126 | SVG, Util; 127 | -------------------------------------------------------------------------------- /server.py: -------------------------------------------------------------------------------- 1 | import zmq 2 | import argparse 3 | import json 4 | 5 | import os 6 | 7 | import numpy as np 8 | import yaml 9 | from flask import Flask, send_from_directory, jsonify, Response, redirect 10 | from flask import request 11 | from flask_cors import CORS 12 | 13 | app = Flask(__name__) 14 | CORS(app) 15 | 16 | data_handlers = {} 17 | index_map = {} 18 | 19 | 20 | @app.route('/api/translate/') 21 | def transate(): 22 | options = request.args 23 | src = options.get('src') 24 | print("src", src) 25 | sock.send(json.dumps({"src" : src})) 26 | result = sock.recv() 27 | return result 28 | 29 | # send everything from client as static content 30 | @app.route('/client/') 31 | def send_static(path): 32 | """ serves all files from ./client/ to ``/client/`` 33 | 34 | :param path: path from api call 35 | """ 36 | return send_from_directory('client/', path) 37 | 38 | 39 | 40 | @app.route('/') 41 | def hello_world(): 42 | """ 43 | :return: "hello world" 44 | """ 45 | return redirect('client/index.html') 46 | 47 | parser = argparse.ArgumentParser() 48 | parser.add_argument("--nodebug", default=False) 49 | parser.add_argument("--port", default="8888") 50 | parser.add_argument("--nocache", default=False) 51 | parser.add_argument("-dir", type=str, default=os.path.abspath('data')) 52 | 53 | if __name__ == '__main__': 54 | args = parser.parse_args() 55 | # create_data_handlers(args.dir) 56 | 57 | # print args 58 | 59 | # ZeroMQ Context 60 | context = zmq.Context() 61 | 62 | # Define the socket using the "Context" 63 | sock = context.socket(zmq.REQ) 64 | sock.connect("tcp://127.0.0.1:5556") 65 | 66 | app.run(port=int(args.port), debug=not args.nodebug, host="0.0.0.0") 67 | --------------------------------------------------------------------------------