Implement HTTP table export
authorJakob Cornell <jakob@jcornell.net>
Sun, 5 Jan 2020 06:34:14 +0000 (00:34 -0600)
committerJakob Cornell <jakob@jcornell.net>
Sun, 5 Jan 2020 06:34:14 +0000 (00:34 -0600)
main.py
table_bookmarklet [new file with mode: 0644]

diff --git a/main.py b/main.py
index f091475fdf97f6d80b4461d6ff0971f9bfedd6f3..8c2c7d7a718f1687fb9cbbeaee26afda6c3a878c 100644 (file)
--- a/main.py
+++ b/main.py
@@ -1,9 +1,11 @@
 from collections import defaultdict, namedtuple, OrderedDict
 from contextlib import contextmanager
 from enum import Enum
+import http.server
 import itertools
 import json
 from matplotlib import pyplot
+from threading import Thread
 
 import tkinter as tk
 from tkinter import ttk
@@ -141,34 +143,25 @@ class MainModel(Model):
                        self.view.set_student(name)
 
 
-def get_table_bookmarklet(records):
-       import html
-
-       rows = [
-               ['' if v is None else str(v) for v in r] for r in records
-       ]
-       table_html = (
-               '<table style="border:1px solid black;">'
-               + '<thead>'
-               + ''.join('<th>' + html.escape(h) + '</th>' for h in DataEditModel.Record.DISPLAY_NAMES)
-               + '</thead>'
-               + '<tbody>'
-               + ''.join(
-                       '<tr>'
-                       + ''.join('<td>' + v + '</td>' for v in row)
-                       + '</tr>'
-                       for row in rows
-               )
-               + '</tbody>'
-               + '</table>'
-       )
-       assert "'" not in table_html
-       js_table_literal = "'{}'".format(table_html)
-       return (
-               'javascript:(function(){document.querySelector(\'[contenteditable=\\\'true\\\']\')'
-               + '.innerHTML+=\'<br/>\'+' + js_table_literal + '+\'<br/>\';})()'
-       )
+class AsyncHttpServer:
+       def __init__(self, port, data):
+               class Handler(http.server.BaseHTTPRequestHandler):
+                       def __init__(self): pass
+                       def __call__(self, *args): super().__init__(*args)
+
+                       def do_GET(self):
+                               self.send_response(200)
+                               self.send_header('Access-Control-Allow-Origin', '*')
+                               self.end_headers()
+                               self.wfile.write(data)
+
+               self.server = http.server.HTTPServer(('localhost', port), Handler())
+
+       def start(self):
+               Thread(target = lambda: self.server.serve_forever()).start()
 
+       def stop(self):
+               self.server.shutdown()
 
 class MainView(View):
        @contextmanager
@@ -274,17 +267,16 @@ class MainView(View):
 
                def on_gen_table():
                        name = self.controller.state['student']
-                       command = get_table_bookmarklet(self.controller.data[name])
-                       self.root.clipboard_clear()
-                       self.root.clipboard_append(command)
+                       rows = DataEditModel.Record.DISPLAY_NAMES + self.controller.data[name]
+                       data = json.dumps(rows).encode('utf-8')
+                       server = AsyncHttpServer(57853, data)
+                       server.start()
                        messagebox.showinfo(
-                               "Table Command Copied",
-                               "In Gmail, make sure you have a new message box open,"
-                                       + " then paste the command into the address bar and press Enter."
-                                       + "\nMake sure \"javascript:\" shows up at the beginning of the address bar."
-                               ,
+                               "Serving Table Data",
+                               "Table data is ready to be transferred to your browser. You may close this dialog when the transfer is complete.",
                        )
-               self.gen_table_btn = ttk.Button(actions_frame, text = "Generate Gmail table", command = on_gen_table)
+                       server.stop()
+               self.gen_table_btn = ttk.Button(actions_frame, text = "Export table", command = on_gen_table)
 
                self.student_sel.pack(**TK_PAD, side = tk.LEFT)
                add_student_btn.pack(**TK_PAD, side = tk.LEFT)
diff --git a/table_bookmarklet b/table_bookmarklet
new file mode 100644 (file)
index 0000000..fadfd81
--- /dev/null
@@ -0,0 +1 @@
+javascript:(function(){var r=new XMLHttpRequest();r.onReadyStateChange=d;function d(){if(r.readyState===XMLHttpRequest.DONE){var rows=JSON.parse(r.responseText);var t=document.createElement('table');var h=t.createTHead();rows.shift().forEach(v=>{var c=document.createElement('th');c.innerText=v;h.appendChild(c);});rows.forEach(r=>{var tr=t.insertRow(-1);r.forEach(v=>{var c=document.createElement('td');c.innerText=v;tr.appendChild(c);});});var box=document.querySelector('[contenteditable="true"]');box.appendChild(document.createElement('br'));box.appendChild(t);box.appendChild(document.createElement('br'));}}})()