Skip to content

Commit

Permalink
Draft implementation of the first D3I sprint
Browse files Browse the repository at this point in the history
  • Loading branch information
mellelieuwes committed Oct 2, 2022
1 parent 2b3b03f commit ea02f26
Show file tree
Hide file tree
Showing 114 changed files with 3,894 additions and 535 deletions.
2 changes: 1 addition & 1 deletion .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npm run ts-standard
npm run prod
npm run test-ci
3 changes: 3 additions & 0 deletions dist/assets/assets/images/radio.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions dist/assets/assets/images/radio_active.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions dist/assets/assets/images/spinner.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions dist/assets/images/radio.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions dist/assets/images/radio_active.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions dist/assets/images/spinner.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions dist/framework/abstractions/processing_engine.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default interface ProcessingEngine {
start: () => void;
loadScript: (script: any) => void;
firstRunCycle: () => void;
nextRunCycle: (response: any) => void;
terminate: () => void;
}
1 change: 1 addition & 0 deletions dist/framework/abstractions/processing_engine.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {};
4 changes: 4 additions & 0 deletions dist/framework/abstractions/visualisation_engine.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default interface VisualisationEngine {
start: (script: string, rootElement: HTMLElement, locale: string) => Promise<any>;
terminate: () => void;
}
1 change: 1 addition & 0 deletions dist/framework/abstractions/visualisation_engine.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {};
2 changes: 2 additions & 0 deletions dist/framework/assembly.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import VisualisationEngine from './abstractions/visualisation_engine';
export declare const Assembly: (worker: Worker) => VisualisationEngine;
9 changes: 9 additions & 0 deletions dist/framework/assembly.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import ReactEngine from './visualisation/react/engine';
import ReactFactory from './visualisation/react/factory';
import WorkerProcessingEngine from './processing/worker_engine';
export const Assembly = (worker) => {
const processingEngine = new WorkerProcessingEngine(worker);
const visualisationEngine = new ReactEngine(new ReactFactory(), processingEngine);
processingEngine.eventListener = visualisationEngine.onEvent;
return visualisationEngine;
};
8 changes: 8 additions & 0 deletions dist/framework/processing/python/worker.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
declare function runCycle(userInput: any): void;
declare function unwrap(response: any): Promise<any>;
declare function copyFileToPyFS(file: any, resolve: any): void;
declare function initialise(): any;
declare function loadScript(script: any): void;
declare function pyWorker(): string;
declare let pyScript: any;
declare const pyPortApi: "\nclass Event:\n def toDict(self):\n return setType({}, \"Event\")\n\n\nclass EndOfFlow(Event):\n __slots__ = \"result\"\n def __init__(self, result):\n self.result = result\n def translate_result(self):\n print(\"translate\")\n data_output = []\n for data in self.result:\n df = data[\"data_frame\"]\n data_output.append({\"id\": data[\"id\"], \"data_frame\": df.to_json()})\n return {\n \"title\": data[\"title\"],\n \"data\": data_output,\n }\n def toDict(self):\n print(\"toDict2\")\n dict = toDict(super(), \"EndOfFlow\") \n dict = dict | self.translate_result()\n return dict\n \n\nclass Command(Event):\n def toDict(self):\n return toDict(super(), \"Command\")\n\n\nclass Prompt(Command):\n __slots__ = \"title\", \"description\"\n def __init__(self, title, description):\n self.title = title\n self.description = description\n def toDict(self):\n dict = toDict(super(), \"Prompt\")\n dict[\"title\"] = self.title.toDict()\n dict[\"description\"] = self.description.toDict()\n return dict\n\n\nclass FileInput(Prompt):\n __slots__ = \"extensions\"\n def __init__(self, title, description, extensions):\n super().__init__(title, description)\n self.extensions = extensions\n def toDict(self):\n dict = toDict(super(), \"FileInput\")\n dict[\"extensions\"] = self.extensions\n return dict\n\n\nclass RadioInput(Prompt):\n def __init__(self, title, description, items):\n super().__init__(title, description)\n self.items = items\n def toDict(self):\n dict = toDict(super(), \"RadioInput\")\n dict[\"items\"] = self.items\n return dict\n\n\nclass Translatable:\n __slots__ = \"translations\"\n def __init__(self):\n self.translations = {}\n def add(self, locale, text):\n self.translations[locale] = text\n return self\n def toDict(self):\n return setType(self.translations, \"Translatable\")\n\n\ndef toDict(zuper, type):\n return setType(zuper.toDict(), type)\n\n\ndef setType(dict, type):\n key = \"__type__\"\n seperator = \".\"\n\n path = [type]\n if key in dict:\n path.insert(0, dict[key])\n dict[key] = seperator.join(path)\n return dict\n";
186 changes: 186 additions & 0 deletions dist/framework/processing/python/worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
let pyScript;
onmessage = (event) => {
const { eventType } = event.data;
switch (eventType) {
case 'initialise':
initialise().then(() => {
self.postMessage({ eventType: 'initialiseDone' });
});
break;
case 'loadScript':
loadScript(event.data.script);
self.postMessage({ eventType: 'loadScriptDone' });
break;
case 'firstRunCycle':
pyScript = self.pyodide.runPython(pyWorker());
runCycle(null);
break;
case 'nextRunCycle':
const { response } = event.data;
unwrap(response).then((userInput) => {
runCycle(userInput);
});
break;
default:
console.log('[ProcessingWorker] Received unsupported event: ', eventType);
}
};
function runCycle(userInput) {
scriptEvent = pyScript.send(userInput);
self.postMessage({
eventType: 'runCycleDone',
scriptEvent: scriptEvent.toJs({
create_proxies: false,
dict_converter: Object.fromEntries
})
});
}
function unwrap(response) {
return new Promise((resolve) => {
switch (response.prompt.__type__) {
case 'Event.Command.Prompt.FileInput':
copyFileToPyFS(response.userInput, resolve);
break;
default:
resolve(response.userInput);
}
});
}
function copyFileToPyFS(file, resolve) {
const reader = file.stream().getReader();
const pyFile = self.pyodide.FS.open(file.name, 'w');
const writeToPyFS = ({ done, value }) => {
if (done) {
resolve(file.name);
}
else {
self.pyodide.FS.write(pyFile, value, 0, value.length);
reader.read().then(writeToPyFS);
}
};
reader.read().then(writeToPyFS);
}
function initialise() {
importScripts('https://cdn.jsdelivr.net/pyodide/v0.21.2/full/pyodide.js');
return loadPyodide({
indexURL: 'https://cdn.jsdelivr.net/pyodide/v0.21.2/full/'
}).then((pyodide) => {
self.pyodide = pyodide;
return self.pyodide.loadPackage(['micropip', 'numpy', 'pandas']);
});
}
function loadScript(script) {
console.log('[ProcessingWorker] loadScript');
self.pyodide.runPython(pyPortApi);
self.pyodide.runPython(script);
}
const pyPortApi = `
class Event:
def toDict(self):
return setType({}, "Event")
class EndOfFlow(Event):
__slots__ = "result"
def __init__(self, result):
self.result = result
def translate_result(self):
print("translate")
data_output = []
for data in self.result:
df = data["data_frame"]
data_output.append({"id": data["id"], "data_frame": df.to_json()})
return {
"title": data["title"],
"data": data_output,
}
def toDict(self):
print("toDict2")
dict = toDict(super(), "EndOfFlow")
dict = dict | self.translate_result()
return dict
class Command(Event):
def toDict(self):
return toDict(super(), "Command")
class Prompt(Command):
__slots__ = "title", "description"
def __init__(self, title, description):
self.title = title
self.description = description
def toDict(self):
dict = toDict(super(), "Prompt")
dict["title"] = self.title.toDict()
dict["description"] = self.description.toDict()
return dict
class FileInput(Prompt):
__slots__ = "extensions"
def __init__(self, title, description, extensions):
super().__init__(title, description)
self.extensions = extensions
def toDict(self):
dict = toDict(super(), "FileInput")
dict["extensions"] = self.extensions
return dict
class RadioInput(Prompt):
def __init__(self, title, description, items):
super().__init__(title, description)
self.items = items
def toDict(self):
dict = toDict(super(), "RadioInput")
dict["items"] = self.items
return dict
class Translatable:
__slots__ = "translations"
def __init__(self):
self.translations = {}
def add(self, locale, text):
self.translations[locale] = text
return self
def toDict(self):
return setType(self.translations, "Translatable")
def toDict(zuper, type):
return setType(zuper.toDict(), type)
def setType(dict, type):
key = "__type__"
seperator = "."
path = [type]
if key in dict:
path.insert(0, dict[key])
dict[key] = seperator.join(path)
return dict
`;
function pyWorker() {
return `
from collections.abc import Generator
import json
import html
import pandas as pd
class ScriptWrapper(Generator):
def __init__(self, script):
self.script = script
def send(self, data):
print("toDict")
event = self.script.send(data)
return event.toDict()
def throw(self, type=None, value=None, traceback=None):
raise StopIteration
script = process()
ScriptWrapper(script)
`;
}
11 changes: 11 additions & 0 deletions dist/framework/processing/worker_engine.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import ProcessingEngine from '../abstractions/processing_engine';
export default class WorkerProcessingEngine implements ProcessingEngine {
eventListener: (event: any) => void;
worker: Worker;
constructor(worker: Worker);
start(): void;
loadScript(script: any): void;
firstRunCycle(): void;
nextRunCycle(response: any): void;
terminate(): void;
}
31 changes: 31 additions & 0 deletions dist/framework/processing/worker_engine.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
export default class WorkerProcessingEngine {
eventListener;
worker;
constructor(worker) {
this.eventListener = (event) => {
const eventString = JSON.stringify(event);
console.log('[ProcessingEngine] No event listener registered for event: ', eventString);
};
this.worker = worker;
this.worker.onerror = console.log;
this.worker.onmessage = (event) => {
console.log('[ProcessingEngine] Received event from worker: ', event.data.eventType);
this.eventListener(event);
};
}
start() {
this.worker.postMessage({ eventType: 'initialise' });
}
loadScript(script) {
this.worker.postMessage({ eventType: 'loadScript', script });
}
firstRunCycle() {
this.worker.postMessage({ eventType: 'firstRunCycle' });
}
nextRunCycle(response) {
this.worker.postMessage({ eventType: 'nextRunCycle', response });
}
terminate() {
this.worker.terminate();
}
}
9 changes: 9 additions & 0 deletions dist/framework/translatable.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default class Translatable {
translations: {
[key: string]: string;
};
defaultLocale: string;
add(locale: string, text: string): Translatable;
text(locale: string): string;
resolve(locale: string): string;
}
26 changes: 26 additions & 0 deletions dist/framework/translatable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import _ from 'lodash';
export default class Translatable {
translations = {};
defaultLocale = 'nl';
add(locale, text) {
this.translations[locale] = text;
return this;
}
text(locale) {
return _.escape(this.resolve(locale));
}
resolve(locale) {
const text = this.translations[locale];
if (text !== null) {
return text;
}
const defaultText = this.translations[this.defaultLocale];
if (defaultText !== null) {
return defaultText;
}
if (Object.values(this.translations).length > 0) {
return Object.values(this.translations)[0];
}
return '?text?';
}
}
15 changes: 15 additions & 0 deletions dist/framework/visualisation/element_ref.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export default class ElementRef {
el: Element;
constructor(el: Element);
setInnerText(text: string): void;
setInnerHTML(html: string): void;
onClick(handle: () => void): void;
onChange(handle: () => void): void;
selectedFile(): File | null;
reset(): void;
click(): void;
hide(): void;
show(): void;
child(childId: string): ElementRef;
childs(className: string): ElementRef[];
}
Loading

0 comments on commit ea02f26

Please sign in to comment.