diff --git a/bin/deploy_storyq b/bin/deploy_storyq index 61878f6..d36e73c 100755 --- a/bin/deploy_storyq +++ b/bin/deploy_storyq @@ -1,4 +1,4 @@ npm run build -rm -rf storyq -mv build storyq -rsync -av storyq codap-server.concord.org:public_html \ No newline at end of file +rm -rf storyqV2 +mv build storyqV2 +rsync -av storyqV2 codap-server.concord.org:public_html \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a4ddb06..b615017 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1204,9 +1204,9 @@ "integrity": "sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg==" }, "@devexpress/utils": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@devexpress/utils/-/utils-1.3.3.tgz", - "integrity": "sha512-fQdPPq5snZtNhWVp/I75xE4VvHWC3ApQC19AuMnDuhKnHMKqkA4f5nSEkUatBcvt3bhbLw0Bki4KuKtDb65xTA==", + "version": "1.3.10", + "resolved": "https://registry.npmjs.org/@devexpress/utils/-/utils-1.3.10.tgz", + "integrity": "sha512-cq1bxLqbvhfoXDjLBZQAG7RBdxiVKhTxZnmY7hT4ZNPBMV7xESO0Q5xHH8rHaiPdXAxLJKciOtibaC7DxO86wA==", "requires": { "tslib": "2.0.1" }, @@ -1218,6 +1218,17 @@ } } }, + "@devextreme/vdom": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@devextreme/vdom/-/vdom-1.2.2.tgz", + "integrity": "sha512-e8WCJ9u/5cZ2onWzWwVFhRKjbO5EmQaZBNLwWrbZwpKKLHmXEwC5lOJtQ6onDXPAhDfZDX8jDin9G+DDX4SSbg==", + "requires": { + "inferno": "^7.4.6", + "inferno-compat": "^7.4.6", + "inferno-create-element": "^7.4.6", + "inferno-hydrate": "^7.4.6" + } + }, "@eslint/eslintrc": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", @@ -2944,14 +2955,6 @@ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "requires": { - "safer-buffer": "~2.1.0" - } - }, "asn1.js": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", @@ -2994,11 +2997,6 @@ } } }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", @@ -3061,16 +3059,6 @@ "postcss-value-parser": "^4.1.0" } }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" - }, "axe-core": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.3.3.tgz", @@ -3602,14 +3590,6 @@ "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=" }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "requires": { - "tweetnacl": "^0.14.3" - } - }, "bfj": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz", @@ -4022,11 +4002,6 @@ "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.3.0.tgz", "integrity": "sha512-/4YgnZS8y1UXXmC02xD5rRrBEu6T5ub+mQHLNRj0fzTRbgdBYhsNo2V5EqwgqrExjxsjtF/OpAKAMkKsxbD5XQ==" }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -4208,14 +4183,6 @@ "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==" }, - "combined-stream": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", - "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, "commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -4836,19 +4803,6 @@ "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.7.tgz", "integrity": "sha512-VvdQIPGdWP0SqFXghj79Wf/5LArmreyMsGLa6FG6iC4t3j7j5s71TrwWmT/4akbDQIqjfACkLZmjXhA7g2oUZw==" }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "dasherize": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dasherize/-/dasherize-2.0.0.tgz", - "integrity": "sha1-bYCcnNDPe7iVLYD8hPoT1H3bEwg=" - }, "data-urls": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", @@ -5079,21 +5033,21 @@ } }, "devexpress-diagram": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/devexpress-diagram/-/devexpress-diagram-2.0.11.tgz", - "integrity": "sha512-ZxLLBOQJuv54Keh4hcSK17YodOQ4PBvhgrAmqPiWI29winEa+Q6lFYwTakPpGa72FYAmFekwgtac8/WOS/x2ZQ==", + "version": "2.1.25", + "resolved": "https://registry.npmjs.org/devexpress-diagram/-/devexpress-diagram-2.1.25.tgz", + "integrity": "sha512-ZyWkstgtCXIvRX+LWqIBW8kdSPZP1Voq/GYLDuUbotf0uorsxKN5dUd21qgrV+zwyX+zfZrWGLcu5shLrkAc+Q==", "requires": { - "@devexpress/utils": "1.3", + "@devexpress/utils": "1.3.10", "es6-object-assign": "^1.1.0" } }, "devexpress-gantt": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/devexpress-gantt/-/devexpress-gantt-2.0.18.tgz", - "integrity": "sha512-tmcZPd1V4NccrO4ei9zcMMuxGCdReEIrS0KVxyu9H4DMEIQyW4qLrffgyfoCbWoJcOZbGeSwfcq1i4b44YYzCw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/devexpress-gantt/-/devexpress-gantt-3.0.4.tgz", + "integrity": "sha512-diAZ6t46Yj09FAceUowlHgv83QWt54/CihDGS3CHoV3BxrHFK0X2T1D40urc9S/tOAG4GXXD1YnLBaamN+op4A==", "requires": { "@devexpress/utils": "1.3.2", - "tslib": "2.0.1" + "tslib": "2.1.0" }, "dependencies": { "@devexpress/utils": { @@ -5102,34 +5056,46 @@ "integrity": "sha512-Ii7hpz6ItNMLKz23qQOlHom/k0e1TSnIQKCwZlKuYBwpIEBX5I/PQxvAwbdBnu5EZZSkdjnhzxl6luhl/k4LdA==", "requires": { "tslib": "2.0.1" + }, + "dependencies": { + "tslib": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", + "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==" + } } }, "tslib": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", - "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" } } }, "devextreme": { - "version": "20.2.5", - "resolved": "https://registry.npmjs.org/devextreme/-/devextreme-20.2.5.tgz", - "integrity": "sha512-KPMq/y+zlPu3JOxdaV0TvqA9AzDKqljpuuhetLc9k6IFRTRhMa2yWiHvePp/FZmfWhumIDIj1AEVedUySDlCUQ==", - "requires": { - "devexpress-diagram": "2.0.11", - "devexpress-gantt": "2.0.18", - "devextreme-quill": "~0.9.8", + "version": "21.1.5", + "resolved": "https://registry.npmjs.org/devextreme/-/devextreme-21.1.5.tgz", + "integrity": "sha512-/mauCqWs7ajOrWZ5nOSMyY+L3PThTAAGdKJiWcEzU2LuXxkDkcZyn/uSBU/WhGdcaneqifKKECkP+YYoXIWQTA==", + "requires": { + "@babel/runtime": "^7.12.1", + "@devextreme/vdom": "1.2.2", + "devexpress-diagram": "2.1.25", + "devexpress-gantt": "3.0.4", + "devextreme-quill": "~1.3.0", + "inferno": "^7.4.6", + "inferno-compat": "^7.4.6", + "inferno-create-element": "^7.4.6", + "inferno-hydrate": "^7.4.6", "jszip": "^2.0.0 || ^3.0.0", - "preact": "^10.4.5", "rrule": "2.6.6", - "showdown": "^1.8.6", - "turndown": "^6.0.0" + "showdown": "^1.9.1", + "turndown": "~7.0.0" } }, "devextreme-quill": { - "version": "0.9.8", - "resolved": "https://registry.npmjs.org/devextreme-quill/-/devextreme-quill-0.9.8.tgz", - "integrity": "sha512-acrAE+ukbRjoacC/r+bRNhCo/xTOxcnNhZ2KUtxoOuiaqPN9WEK4HCfcv44G9JIKPQCmZUFr6O/AYg1fgOYwrA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/devextreme-quill/-/devextreme-quill-1.3.1.tgz", + "integrity": "sha512-zhtbAogQnOOA9b/4ymJGB8C2FeirftLJ7QwliV6I7B0lW9HZBAw6YJ0Z+J8poy1IMtf84S5kvQR63e2VidUEpQ==", "requires": { "core-js": "^3.6.5", "eventemitter3": "^4.0.0", @@ -5138,22 +5104,13 @@ "lodash.merge": "^4.5.0", "parchment": "2.0.0-dev.2", "quill-delta": "4.2.2" - }, - "dependencies": { - "core-js": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.9.1.tgz", - "integrity": "sha512-gSjRvzkxQc1zjM/5paAmL4idJBFzuJoo+jDjF1tStYFMV2ERfD02HhahhCGXUyHxQRG4yFKVSdO6g62eoRMcDg==" - } } }, "devextreme-react": { - "version": "20.2.5", - "resolved": "https://registry.npmjs.org/devextreme-react/-/devextreme-react-20.2.5.tgz", - "integrity": "sha512-exrMx2TjszwGOqNPByr0bKYKsqlYnoJwEjS45c4NYp5Bpe+0WZuMwW0RzWTBqGrJfiwbVfLCb8t+adHwKajAeA==", + "version": "21.1.5", + "resolved": "https://registry.npmjs.org/devextreme-react/-/devextreme-react-21.1.5.tgz", + "integrity": "sha512-wU4cBsFvkFv729AK2oeJske57F3qTASfGx14h7D+2G8r8lQw7meyQJ72HFJmlreEtkiD0WxrjOvt2DTdTkoKkw==", "requires": { - "dasherize": "^2.0.0", - "dot": "^1.1.2", "prop-types": "^15.6.1" } }, @@ -5281,6 +5238,11 @@ } } }, + "domino": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/domino/-/domino-2.1.6.tgz", + "integrity": "sha512-3VdM/SXBZX2omc9JF9nOPCtDaYQ67BGp5CoLpIQlO2KCAPETs8TcDHacF26jXadGbvUteZzRTeos2fhID5+ucQ==" + }, "domutils": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", @@ -5290,11 +5252,6 @@ "domelementtype": "1" } }, - "dot": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dot/-/dot-1.1.3.tgz", - "integrity": "sha512-/nt74Rm+PcfnirXGEdhZleTwGC2LMnuKTeeTIlI82xb5loBBoXNYzr2ezCroPSMtilK8EZIfcNZwOcHN+ib1Lg==" - }, "dot-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", @@ -5345,15 +5302,6 @@ "stream-shift": "^1.0.0" } }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -6501,11 +6449,6 @@ } } }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, "extend-shallow": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", @@ -6584,11 +6527,6 @@ } } }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, "fast-deep-equal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", @@ -6807,11 +6745,6 @@ "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, "fork-ts-checker-webpack-plugin": { "version": "4.1.6", "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-4.1.6.tgz", @@ -6928,16 +6861,6 @@ } } }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -7068,14 +6991,6 @@ "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "^1.0.0" - } - }, "glob": { "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", @@ -7158,20 +7073,6 @@ "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" - } - }, "harmony-reflect": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", @@ -7612,16 +7513,6 @@ } } }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, "https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", @@ -7766,6 +7657,78 @@ "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" }, + "inferno": { + "version": "7.4.10", + "resolved": "https://registry.npmjs.org/inferno/-/inferno-7.4.10.tgz", + "integrity": "sha512-L/qPVapN/b4WSrQND6fN0LOvhIeCVpGFQRbDplZvovOJoxRRZyE21k92tL/C76hQVFOp2FIgjZ7fjy9AnKPS+A==", + "requires": { + "inferno-shared": "7.4.10", + "inferno-vnode-flags": "7.4.10", + "opencollective-postinstall": "^2.0.3" + } + }, + "inferno-clone-vnode": { + "version": "7.4.10", + "resolved": "https://registry.npmjs.org/inferno-clone-vnode/-/inferno-clone-vnode-7.4.10.tgz", + "integrity": "sha512-mVNyOGInJj5mkvGXwL2KcqttJlhKPQkvejNvVOvuq4ozeeFDkPXk4jqgjl0YSRk2gnFtJR5u4J1yCUWDPXr6ng==", + "requires": { + "inferno": "7.4.10" + } + }, + "inferno-compat": { + "version": "7.4.10", + "resolved": "https://registry.npmjs.org/inferno-compat/-/inferno-compat-7.4.10.tgz", + "integrity": "sha512-Qm8EMCgi+/1ucqYUMDw/vINkrpY2jCt9rLnCNNBX215qfQkt3zGuu2i2v1CjSCZ0/QP+H/VBy3Q5VsQ20Ir2Ow==", + "requires": { + "inferno": "7.4.10", + "inferno-clone-vnode": "7.4.10", + "inferno-create-class": "7.4.10", + "inferno-create-element": "7.4.10", + "inferno-extras": "7.4.10" + } + }, + "inferno-create-class": { + "version": "7.4.10", + "resolved": "https://registry.npmjs.org/inferno-create-class/-/inferno-create-class-7.4.10.tgz", + "integrity": "sha512-ql6JCBo3hQ/leFwbU9eGFoa/D8gizvWb+fKwziqa/3x+qAbwpeOxIwwujDpdkO+LIm+hwpOtLKdDWxSlkk09hA==", + "requires": { + "inferno": "7.4.10" + } + }, + "inferno-create-element": { + "version": "7.4.10", + "resolved": "https://registry.npmjs.org/inferno-create-element/-/inferno-create-element-7.4.10.tgz", + "integrity": "sha512-Gvq0FHL7qHofYjItVkpsOJtr8f2Ok1kxftBedbQb1fCUpHIEwiUvJSh+HX83ZunVwE9tisoPc/ddQPYFJ+x72Q==", + "requires": { + "inferno": "7.4.10" + } + }, + "inferno-extras": { + "version": "7.4.10", + "resolved": "https://registry.npmjs.org/inferno-extras/-/inferno-extras-7.4.10.tgz", + "integrity": "sha512-fdXtG8XEo8HKC1VI11RJJS4cuPFIgVWQrah1ngiFjc9SPZKov1szreqC43L9qcJ/2k05hxLNE13t887pXBfCRA==", + "requires": { + "inferno": "7.4.10" + } + }, + "inferno-hydrate": { + "version": "7.4.10", + "resolved": "https://registry.npmjs.org/inferno-hydrate/-/inferno-hydrate-7.4.10.tgz", + "integrity": "sha512-bHJo7wd0ZKAmRlzoHqBjGhEgmOYFBh9LL58bIOeOXiuuyXJFUA6tP/vW91sx7j68K9Zq36SMwtbb/QnQ7R4mug==", + "requires": { + "inferno": "7.4.10" + } + }, + "inferno-shared": { + "version": "7.4.10", + "resolved": "https://registry.npmjs.org/inferno-shared/-/inferno-shared-7.4.10.tgz", + "integrity": "sha512-d7wlcW8NhchfX4vSg+6k9/FwFHAooo81GfWZtnDXtUvZNS4WEMaPH2j1YV6VnN4X3R0850dHRxR7830PdKh4Iw==" + }, + "inferno-vnode-flags": { + "version": "7.4.10", + "resolved": "https://registry.npmjs.org/inferno-vnode-flags/-/inferno-vnode-flags-7.4.10.tgz", + "integrity": "sha512-OzfnqXrJx8Rl3FtyjhdFk7gzuCLMPCbDTiO8Bz0lw6P3ngW9Md5N5LPeg7Jz510PM0NisScQtxxHPwsFQxDIfw==" + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -8064,11 +8027,6 @@ "isobject": "^3.0.1" } }, - "is-potential-custom-element-name": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz", - "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=" - }, "is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -8147,11 +8105,6 @@ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, "istanbul-lib-coverage": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", @@ -9737,11 +9690,6 @@ "esprima": "^4.0.0" } }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" - }, "jsdom": { "version": "16.7.0", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", @@ -9821,11 +9769,6 @@ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -9836,11 +9779,6 @@ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, "json3": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", @@ -9863,17 +9801,6 @@ "universalify": "^2.0.0" } }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, "jsx-ast-utils": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz", @@ -10048,11 +9975,6 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, - "lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" - }, "lodash.template": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", @@ -10117,9 +10039,9 @@ } }, "luxon": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.26.0.tgz", - "integrity": "sha512-+V5QIQ5f6CDXQpWNICELwjwuHdqeJM1UenlZWx5ujcRMc9venvluCjFb4t5NYLhb6IhkbMVOxzVuOqkgMxee2A==", + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.0.tgz", + "integrity": "sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==", "optional": true }, "magic-string": { @@ -10418,6 +10340,24 @@ "minimist": "^1.2.5" } }, + "mobx": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.3.3.tgz", + "integrity": "sha512-JoNU50rO6d1wHwKPJqKq4rmUMbYnI9CsJmBo+Cu4exBYenFvIN77LWrZENpzW6reZPADtXMmB1DicbDSfy8Clw==" + }, + "mobx-react": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-7.2.0.tgz", + "integrity": "sha512-KHUjZ3HBmZlNnPd1M82jcdVsQRDlfym38zJhZEs33VxyVQTvL77hODCArq6+C1P1k/6erEeo2R7rpE7ZeOL7dg==", + "requires": { + "mobx-react-lite": "^3.2.0" + } + }, + "mobx-react-lite": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-3.2.1.tgz", + "integrity": "sha512-hwURgfmP2apX3HQrB55V9DN47kuN3C6KlQvI5UIfJRibXma72C/JudcNt2r9dWjAdFMrcZoz1ivvtXMCkJ2aQA==" + }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -10691,11 +10631,6 @@ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==" }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -10859,6 +10794,11 @@ "is-wsl": "^2.1.1" } }, + "opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==" + }, "opn": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", @@ -12256,11 +12196,6 @@ "uniq": "^1.0.1" } }, - "preact": { - "version": "10.5.12", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.5.12.tgz", - "integrity": "sha512-r6siDkuD36oszwlCkcqDJCAKBQxGoeEGytw2DGMD5A/GGdu5Tymw+N2OBXwvOLxg6d1FeY8MgMV3cc5aVQo4Cg==" - }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -12392,11 +12327,6 @@ "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" }, - "psl": { - "version": "1.1.31", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", - "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==" - }, "public-encrypt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", @@ -12457,11 +12387,6 @@ "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - }, "query-string": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", @@ -13363,13 +13288,6 @@ "requires": { "luxon": "^1.21.3", "tslib": "^1.10.0" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } } }, "rsvp": { @@ -13865,11 +13783,6 @@ "locate-path": "^3.0.0" } }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" - }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -13879,14 +13792,6 @@ "path-exists": "^3.0.0" } }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "requires": { - "p-try": "^2.0.0" - } - }, "p-locate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", @@ -13895,16 +13800,6 @@ "p-limit": "^2.0.0" } }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" - }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", @@ -13933,11 +13828,6 @@ "strip-ansi": "^5.0.0" } }, - "y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==" - }, "yargs": { "version": "14.2.3", "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz", @@ -13957,9 +13847,9 @@ } }, "yargs-parser": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz", - "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==", + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.3.tgz", + "integrity": "sha512-/MVEVjTXy/cGAjdtQf8dW3V9b97bPN7rNn8ETj6BmAQL7ibC7O1Q9SPJbGjgh3SlwoBNXMzj/ZGIj8mBgl12YA==", "requires": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" @@ -14339,22 +14229,6 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, "ssri": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", @@ -14412,11 +14286,6 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, - "stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" - }, "stream-browserify": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", @@ -15088,290 +14957,14 @@ "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, "turndown": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/turndown/-/turndown-6.0.0.tgz", - "integrity": "sha512-UVJBhSyRHCpNKtQ00mNWlYUM/i+tcipkb++F0PrOpt0L7EhNd0AX9mWEpL2dRFBu7LWXMp4HgAMA4OeKKnN7og==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/turndown/-/turndown-7.0.0.tgz", + "integrity": "sha512-G1FfxfR0mUNMeGjszLYl3kxtopC4O9DRRiMlMDDVHvU1jaBkGFg4qxIyjIk2aiKLHyDyZvZyu4qBO2guuYBy3Q==", "requires": { - "jsdom": "^16.2.0" - }, - "dependencies": { - "abab": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", - "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==" - }, - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" - }, - "acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "requires": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - } - }, - "acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==" - }, - "browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" - }, - "cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==" - }, - "cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "requires": { - "cssom": "~0.3.6" - }, - "dependencies": { - "cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" - } - } - }, - "data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "requires": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - } - }, - "domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "requires": { - "webidl-conversions": "^5.0.0" - }, - "dependencies": { - "webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==" - } - } - }, - "escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", - "requires": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - } - }, - "html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "requires": { - "whatwg-encoding": "^1.0.5" - } - }, - "jsdom": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.4.0.tgz", - "integrity": "sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w==", - "requires": { - "abab": "^2.0.3", - "acorn": "^7.1.1", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.2.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.0", - "domexception": "^2.0.1", - "escodegen": "^1.14.1", - "html-encoding-sniffer": "^2.0.1", - "is-potential-custom-element-name": "^1.0.0", - "nwsapi": "^2.2.0", - "parse5": "5.1.1", - "request": "^2.88.2", - "request-promise-native": "^1.0.8", - "saxes": "^5.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^3.0.1", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0", - "ws": "^7.2.3", - "xml-name-validator": "^3.0.0" - } - }, - "nwsapi": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", - "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==" - }, - "parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==" - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - } - } - }, - "request-promise-core": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", - "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", - "requires": { - "lodash": "^4.17.19" - } - }, - "request-promise-native": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", - "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", - "requires": { - "request-promise-core": "1.1.4", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" - }, - "dependencies": { - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - } - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "optional": true - }, - "symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" - }, - "tough-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", - "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", - "requires": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "tr46": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz", - "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==", - "requires": { - "punycode": "^2.1.1" - } - }, - "w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "requires": { - "browser-process-hrtime": "^1.0.0" - } - }, - "webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==" - }, - "whatwg-url": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.4.0.tgz", - "integrity": "sha512-vwTUFf6V4zhcPkWp/4CQPr1TW9Ml6SF4lVyaIMBdJw5i6qUUJ1QWM4Z6YYVkfka0OUIzVo/0aNtGVGk256IKWw==", - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^2.0.2", - "webidl-conversions": "^6.1.0" - } - }, - "ws": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", - "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==" - } + "domino": "^2.1.6" } }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - }, "type": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", @@ -15433,9 +15026,9 @@ } }, "typescript": { - "version": "3.3.3333", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.3.3333.tgz", - "integrity": "sha512-JjSKsAfuHBE/fB2oZ8NxtRTk5iGcg6hkYXMnZ3Wc+b2RSqejEqTaem11mHASMnFilHrax3sLK0GDzcJrekZYLw==" + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.7.tgz", + "integrity": "sha512-MmQdgo/XenfZPvVLtKZOq9jQQvzaUAUpcKW8Z43x9B2fOm4S5g//tPtMweZUIP+SoBqrVPEIm+dJeQ9dfO0QdA==" }, "unbox-primitive": { "version": "1.0.1", @@ -15752,16 +15345,6 @@ "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==" }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, "vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", diff --git a/package.json b/package.json index a0c9325..43bfe3f 100644 --- a/package.json +++ b/package.json @@ -11,14 +11,16 @@ "@types/pluralize": "0.0.29", "@types/react": "16.8.6", "@types/react-dom": "16.8.2", - "devextreme": "20.2.5", - "devextreme-react": "20.2.5", + "devextreme": "21.1", + "devextreme-react": "21.1", "iframe-phone": "^1.3.1", + "mobx": "^6.3.3", + "mobx-react": "^7.2.0", "pluralize": "^8.0.0", "react": "^16.13.1", "react-dom": "^16.13.1", "react-scripts": "4.0.3", - "typescript": "3.3.3333" + "typescript": "3.7.7" }, "scripts": { "start": "react-scripts start", diff --git a/src/classification_manager.tsx b/src/classification_manager.tsx deleted file mode 100755 index ac74277..0000000 --- a/src/classification_manager.tsx +++ /dev/null @@ -1,735 +0,0 @@ -/** - * Classification manager allows the user to choose an existing model and use it to classify - * phrases in a dataset. - */ - -import React, {Component} from 'react'; -import codapInterface, {CODAP_Notification} from "./lib/CodapInterface"; -import { - getCaseCount, getCollectionNames, - getDatasetInfoWithFilter, isAModel, isNotAModel, deselectAllCasesIn, entityInfo, scrollCaseTableToRight -} from './lib/codap-helper'; -import {wordTokenizer} from "./lib/one_hot"; -import './storyq.css'; -import {LogitPrediction} from './lib/logit_prediction'; -import TextFeedbackManager, {TFMStorage} from "./text_feedback_manager"; -import Button from 'devextreme-react/button'; -import {SelectBox} from "devextreme-react/select-box"; -import {computeKappa} from "./utilities"; - -// import tf from "@tensorflow/tfjs"; - -export interface Classification_StorageCallbackFuncs { - createStorageCallback: () => any, - restoreStorageCallback: (iStorage: any) => void -} - -export interface Classification_Props { - status: string, - setStorageCallbacks: (iCallbacks: Classification_StorageCallbackFuncs) => void -} - -interface ClassificationStorage { - modelsDatasetName: string, - modelToplevelName: string, - modelCaseIndex: number, - modelCollectionName: string, - modelCategories: string[], - modelColumnFeatures: string[], - targetDatasetInfo: entityInfo, - targetDatasetName: string | null, - targetCollectionName: string, - targetAttributeName: string, - targetClassAttributeName: string, - status: string - textFeedbackManagerStorage: TFMStorage -} - -interface ClassificationModel { - features: string[], - weights: number[], - caseIDs: number[], - usages: number[][], - labels: string[], - positiveLabel: string, - modelColumnFeatures: string[], - threshold: number, - constantWeightTerm: number, - predictor: any -} - -const kFeatureIDsAttributeName = 'featureIDs'; - -export class ClassificationManager extends Component { - [indexindex: string]: any; - - private modelDatasetInfoArray: entityInfo[] = []; - private modelsDatasetName: string = ''; - private modelToplevelNames: string[] = []; - private modelToplevelName: string = ''; - private modelCaseIndex: number = 0; - private modelCollectionName = 'models'; - private modelCategories: string[] = []; - private modelColumnFeatures: string[] = []; - private targetDatasetInfoArray: entityInfo[] = []; - private targetDatasetInfo: entityInfo = {name: '', title: '', id: -1}; - private targetCollectionNames: string[] = []; - private targetCollectionName = ''; - private targetAttributeName = ''; - private targetAttributeNames: string[] = []; - private targetClassAttributeName = ''; - private targetPredictedLabelAttributeName = 'classification'; - private targetProbabilityAttributeName = 'probability of positive'; - private subscriberIndex: number = -1; - private textFeedbackManager: TextFeedbackManager | null = null; - private restoredStatus: string = ''; - private results: { - targetTitle: string, modelName: string, - numPositive: number, numNegative: number, - accuracy: number, kappa: number - }; - - constructor(props: Classification_Props) { - super(props); - this.state = { - status: props.status, - count: 0 - }; - this.handleNotification = this.handleNotification.bind(this); - this.createStorage = this.createStorage.bind(this); - this.restoreStorage = this.restoreStorage.bind(this); - props.setStorageCallbacks({ - createStorageCallback: this.createStorage, - restoreStorageCallback: this.restoreStorage - }); - this.results = { - targetTitle: '', modelName: '', numPositive: 0, numNegative: 0, - accuracy: 0, kappa: 0 - }; - } - - public async componentDidMount() { - this.props.setStorageCallbacks({ - createStorageCallback: this.createStorage, - restoreStorageCallback: this.restoreStorage - }); - await this.updateTargetNames(); - this.subscriberIndex = codapInterface.on('notify', '*', '', this.handleNotification); - this.setState({count: this.state.count + 1}); - if (this.restoredStatus !== '') { - this.setState({status: this.restoredStatus}); - this.restoredStatus = ''; - } - } - - public componentWillUnmount() { - codapInterface.off(this.subscriberIndex); - this.getTextFeedbackManager().closeTextComponent(); - } - - public createStorage(): ClassificationStorage { - return { - modelsDatasetName: this.modelsDatasetName, - modelToplevelName: this.modelToplevelName || '', - modelCaseIndex: this.modelCaseIndex, - modelCollectionName: this.modelCollectionName, - modelCategories: this.modelCategories, - modelColumnFeatures: this.modelColumnFeatures, - targetDatasetInfo: this.targetDatasetInfo, - targetDatasetName: null, - targetCollectionName: this.targetCollectionName, - targetAttributeName: this.targetAttributeName, - targetClassAttributeName: this.targetClassAttributeName, - status: this.state.status, - textFeedbackManagerStorage: this.getTextFeedbackManager().createStorage() - } - } - - public restoreStorage(iStorage: ClassificationStorage) { - if (typeof iStorage.targetDatasetName === 'string') { // backward compatibility - iStorage.targetDatasetInfo = {name: iStorage.targetDatasetName, title: iStorage.targetDatasetName, id: -1}; - iStorage.targetDatasetName = null; - } - this.modelsDatasetName = iStorage.modelsDatasetName; - this.modelToplevelName = iStorage.modelToplevelName || ''; - this.modelCaseIndex = iStorage.modelCaseIndex; - this.modelCollectionName = iStorage.modelCollectionName || ''; - this.modelCategories = iStorage.modelCategories || []; - this.modelColumnFeatures = iStorage.modelColumnFeatures || []; - this.targetDatasetInfo = iStorage.targetDatasetInfo || {name: '', title: '', id: -1}; - this.targetCollectionName = iStorage.targetCollectionName || ''; - this.targetAttributeName = iStorage.targetAttributeName || ''; - this.targetClassAttributeName = iStorage.targetClassAttributeName || ''; - this.restoredStatus = iStorage.status || 'testing'; - this.getTextFeedbackManager().restoreStorage(iStorage.textFeedbackManagerStorage); - } - - private async getModelToplevelNames(): Promise { - let tCollectionNames = await getCollectionNames(this.modelsDatasetName), - tModelTopLevelCases: any = await codapInterface.sendRequest({ - "action": "get", - "resource": `dataContext[${this.modelsDatasetName}].collection[${tCollectionNames[0]}].caseFormulaSearch[true]` - }).catch(() => { - console.log('Error getting model cases'); - } - ); - return tModelTopLevelCases.values.map((aCase: any) => { - return aCase.values['Model']; - }); - } - - private async getTargetCollectionNames(): Promise { - return await getCollectionNames(this.targetDatasetInfo.name); - } - - private async getTargetAttributeNames(): Promise { - const tCollNames = await this.getTargetCollectionNames(); - if (tCollNames.length === 0) - return []; - this.targetCollectionName = tCollNames[tCollNames.length - 1]; - const tListResult: any = await codapInterface.sendRequest( - { - action: 'get', - resource: `dataContext[${this.targetDatasetInfo.name}].collection[${this.targetCollectionName}].attributeList` - } - ) - .catch((reason) => { - console.log('Error getting attribute names because', reason); - }); - console.log('getTargetAttributeNames, success', tListResult.success); - if (tListResult.success) { - return tListResult.values.map((iValue: any) => { - return iValue.name; - }); - } else return []; - } - - public getConstructedFeatureNames(): string[] { - return []; - } - - /** - * Lazy instantiation since we can't properly initizlize until we've chosen a model and dataset of phrases - */ - private getTextFeedbackManager(): TextFeedbackManager { - if (!this.textFeedbackManager) { - this.textFeedbackManager = new TextFeedbackManager(this.modelCategories, this.targetAttributeName); - } else { - this.textFeedbackManager.targetAttributeName = this.targetAttributeName; - this.textFeedbackManager.targetCategories = this.modelCategories; - } - return this.textFeedbackManager; - } - - private async getModelCategories(): Promise { - let tResult: string[] = []; - if (this.modelsDatasetName !== '') { - let tCollectionNames = await getCollectionNames(this.modelsDatasetName), - tModelInfoResult: any = await codapInterface.sendRequest({ - action: 'get', - resource: `dataContext[${this.modelsDatasetName}].collection[${tCollectionNames[0]}].caseByIndex[ - ${this.modelCaseIndex}]` - }); - tResult = JSON.parse(tModelInfoResult.values.case.values['Classes'] || '[]'); - } - return tResult; - } - - /** - * We update as many name lists as necessary to be able to display them as choices in the UI. - * @private - */ - private async updateTargetNames() { - - async function allValuesAreInArray(iDatasetName: string, collectionName: string, - iAttrName: string, iCategories: string[]) { - let tFormula = `\`${iAttrName}\`!=\"${iCategories[0]}\" and \`${iAttrName}\`!=\"${iCategories[1]}\"`; - // console.log(tFormula); - let tCasesResult: any = await codapInterface.sendRequest({ - action: 'get', - resource: `dataContext[${iDatasetName}].collection[${collectionName}].caseFormulaSearch[${tFormula}]` - }); - return tCasesResult.values.length === 0; - } - - this.modelDatasetInfoArray = await getDatasetInfoWithFilter(isAModel); - if (this.modelsDatasetName === '') { - if (this.modelDatasetInfoArray.length > 0) - this.modelsDatasetName = this.modelDatasetInfoArray[0].name; - } - if (this.modelsDatasetName !== '') { - this.modelToplevelNames = await this.getModelToplevelNames(); - if (this.modelToplevelName === '' && this.modelToplevelNames.length > 0) { - this.modelToplevelName = this.modelToplevelNames[0]; - this.modelCaseIndex = 0; - } else if (this.modelToplevelName !== '') - this.modelCaseIndex = this.modelToplevelNames.indexOf(this.modelToplevelName); - } - this.targetDatasetInfoArray = await getDatasetInfoWithFilter(isNotAModel); - if (this.targetDatasetInfo.name === '') { - if (this.targetDatasetInfoArray.length > 0) - this.targetDatasetInfo = this.targetDatasetInfoArray[0]; - } - if (this.targetDatasetInfo.name !== '') { - this.targetCollectionNames = await this.getTargetCollectionNames(); - if (this.targetCollectionName === '' && this.targetCollectionNames.length > 0) - this.targetCollectionName = this.targetCollectionNames[this.targetCollectionNames.length - 1]; - this.targetAttributeNames = await this.getTargetAttributeNames(); - if (this.targetAttributeName === '' && this.targetAttributeNames.length > 0) - this.targetAttributeName = this.targetAttributeNames[0]; - if (this.targetClassAttributeName === '' && this.targetAttributeNames.length > 1) { - this.modelCategories = await this.getModelCategories(); - let tCandidate = this.targetAttributeNames[1]; - if (allValuesAreInArray(this.targetDatasetInfo.name, this.targetCollectionName, tCandidate, this.modelCategories)) - this.targetClassAttributeName = tCandidate; - } - } - console.log('targetAttributeName', this.targetAttributeName); - this.setState({count: this.count + 1}); - } - - /** - * - * @param iNotification (from CODAP) - */ - private async handleNotification(iNotification: CODAP_Notification) { - if (iNotification.action === 'notify') { - if (iNotification.values.operation === 'dataContextCountChanged') { - this.modelDatasetInfoArray = await getDatasetInfoWithFilter(isAModel); - this.targetDatasetInfoArray = await getDatasetInfoWithFilter(isNotAModel); - // Force next render to allow choices if they exist - this.modelsDatasetName = ''; - this.targetDatasetInfo = {name: '', title: '', id: -1}; - this.setState({count: this.state.count + 1}); - } else if (iNotification.values.operation === 'selectCases') { - // @ts-ignore - let tDataContextName: string = iNotification.resource && iNotification.resource.match(/\[(.+)]/)[1]; - if (tDataContextName === this.modelsDatasetName && !this.isSelectingFeatures) { - this.isSelectingTargetPhrases = true; - await this.getTextFeedbackManager().handleFeatureSelection(this); - this.isSelectingTargetPhrases = false; - } else if (tDataContextName === this.targetDatasetInfo.name && !this.isSelectingTargetPhrases) { - this.isSelectingFeatures = true; - await this.getTextFeedbackManager().handleTargetSelection(this); - this.isSelectingFeatures = false; - } - } else if (['titleChange', 'updateAttributes'].includes(iNotification.values.operation)) { - this.updateTargetNames(); - } else if (iNotification.resource === `dataContextChangeNotice[${this.targetDatasetInfo.name}]` && - iNotification.values.operation === 'createCases') { - await this.classify(); - } - } - } - - private async addAttributesToTarget() { - // Add the predicted label attribute to the target collection - await codapInterface.sendRequest( - { - action: 'create', - resource: `dataContext[${this.targetDatasetInfo.name}].collection[${this.targetCollectionName}].attribute`, - values: [ - { - name: this.targetPredictedLabelAttributeName, - description: 'The label predicted by the model' - }, - { - name: this.targetProbabilityAttributeName, - description: 'The probability predicted by the model that the classification is positive', - precision: 5 - }, - { - name: kFeatureIDsAttributeName, - hidden: true - } - ] - } - ) - .catch((reason) => { - console.log('Error adding target attributes because', reason); - }); - await scrollCaseTableToRight(this.targetDatasetInfo.name); - } - - /** - * Extract relevant info from model dataset and use it to classify phrases in target dataset - * @private - */ - private async classify() { - let this_ = this, - tModel: ClassificationModel = { - features: [], - weights: [], - caseIDs: [], - labels: [], - positiveLabel: '', - modelColumnFeatures: [], - usages: [], - threshold: 0.5, - constantWeightTerm: 0, - predictor: null - }, - tLabelValues: { id: number, values: any }[] = []; - - async function buildModelFromDataset() { - let tModelCollectionNames = await getCollectionNames(this_.modelsDatasetName), - tModelParentCollectionName = tModelCollectionNames[0]; - this_.modelCollectionName = tModelCollectionNames.pop() || 'cases'; - let tCaseCount = await getCaseCount(this_.modelsDatasetName, this_.modelCollectionName); - for (let i = 0; i < tCaseCount; i++) { - const tGetResult: any = await codapInterface.sendRequest({ - "action": "get", - "resource": `dataContext[${this_.modelsDatasetName}].collection[${this_.modelCollectionName}].caseByIndex[${i}]` - }) - .catch(() => { - console.log('unable to get feature'); - }); - - tModel.features.push(tGetResult.values.case.values['feature']); - tModel.weights.push(tGetResult.values.case.values['weight']); - tModel.caseIDs.push(tGetResult.values.case.id); - tModel.usages.push([]); - } - const tParentCaseResult: any = await codapInterface.sendRequest({ - "action": "get", - "resource": - `dataContext[${this_.modelsDatasetName}].collection[${tModelParentCollectionName}].caseByIndex[${this_.modelCaseIndex}]` - }) - .catch((e) => { - console.log(`Error ${e} while getting parent case`); - }); - let tLabels = tParentCaseResult.values.case.values['Classes'] || `["negative", "positive"]`; - tModel.labels = JSON.parse(tLabels); - tModel.positiveLabel = tParentCaseResult.values.case.values['Target Class'] || `positive`; - this_.targetPredictedLabelAttributeName = tParentCaseResult.values.case.values['Predicted Column Name'] || `classification`; - this_.targetProbabilityAttributeName = 'probability of ' + tModel.positiveLabel; - this_.modelCategories = tModel.labels; // Needed to pass to TextFeedbackManager - let tColumnFeatures = tParentCaseResult.values.case.values['Column Features'] || `[]`; - tModel.modelColumnFeatures = tColumnFeatures.split(',').map((iFeature: string) => { - return iFeature.trimLeft(); - }); - this_.modelColumnFeatures = tModel.modelColumnFeatures; // So they get saved and used to get values - tModel.threshold = tParentCaseResult.values.case.values['Threshold']; - tModel.constantWeightTerm = tParentCaseResult.values.case.values['Constant Weight']; - tModel.predictor = new LogitPrediction(tModel.constantWeightTerm, tModel.weights, tModel.threshold); - } - - async function addUsagesAttributeToModelDataset() { - await codapInterface.sendRequest({ - action: 'create', - resource: `dataContext[${this_.modelsDatasetName}].collection[${this_.modelCollectionName}].attribute`, - values: [ - { - name: 'usages', - description: 'IDs of cases with phrase that use this feature', - hidden: true - } - ] - } - ) - } - - async function classifyEachPhrase() { - let tPhraseCount = await getCaseCount(this_.targetDatasetInfo.name, this_.targetCollectionName), - tCorrect = 0, - tMatrix = {posPos: 0, negPos: 0, posNeg: 0, negNeg: 0}, - tNegativeLabel = tModel.positiveLabel === tModel.labels[0] ? tModel.labels[1] : tModel.labels[0]; - for (let i = 0; i < tPhraseCount; i++) { - const tGetResult: any = await codapInterface.sendRequest({ - "action": "get", - "resource": `dataContext[${this_.targetDatasetInfo.name}].collection[${this_.targetCollectionName}].caseByIndex[${i}]` - }) - .catch(() => { - console.log('unable to get target phrase'); - }); - let tPhraseID = tGetResult.values.case.id, - tPhrase = tGetResult.values.case.values[this_.targetAttributeName], - tActual = this_.targetClassAttributeName === '' ? '' : - tGetResult.values.case.values[this_.targetClassAttributeName], - tGiven = Array(tModel.features.length).fill(0), - tFeatureIDs: number[] = []; - // Find the index of each feature the phrase - wordTokenizer(tPhrase, false).forEach((iFeature) => { - let tIndex = tModel.features.indexOf(iFeature); - if (tIndex >= 0) { // We've found a feature - // Mark it in the array - tGiven[tIndex] = 1; - // Add the case ID to the list of featureIDs for this phrase - tFeatureIDs.push(tModel.caseIDs[tIndex]); - tModel.usages[tIndex].push(tPhraseID); - } - }); - // The column features are names of attributes we expect to find having values true or false - tModel.modelColumnFeatures.forEach(iFeature => { - if (tGetResult.values.case.values[iFeature]) { - let tIndex = tModel.features.indexOf(iFeature); - if (tIndex >= 0) { - // Mark it in the array - tGiven[tIndex] = 1; - // Add the case ID to the list of featureIDs for this phrase - tFeatureIDs.push(tModel.caseIDs[tIndex]); - tModel.usages[tIndex].push(tPhraseID); - } - } - }); - let tCaseValues: { [key: string]: string } = {}, - tPrediction = tModel.predictor.predict(tGiven); - tCaseValues[this_.targetPredictedLabelAttributeName] = tPrediction.class ? - tModel.positiveLabel : tNegativeLabel; - tCaseValues[this_.targetProbabilityAttributeName] = tPrediction.probability; - tCaseValues[kFeatureIDsAttributeName] = JSON.stringify(tFeatureIDs); - tLabelValues.push({ - id: tGetResult.values.case.id, - values: tCaseValues - }); - // Increment results - let tActualBool = tActual === tModel.positiveLabel; - if (tPrediction.class) { - this_.results.numPositive++; - if (tActualBool) - tMatrix.posPos++; - else - tMatrix.negPos++; - } else { - this_.results.numNegative++; - if (tActualBool) - tMatrix.posNeg++; - else - tMatrix.negNeg++; - } - if (tActualBool === tPrediction.class) { - tCorrect++; - } - } - // Send the values to CODAP - await codapInterface.sendRequest({ - action: 'update', - resource: `dataContext[${this_.targetDatasetInfo.name}].collection[${this_.targetCollectionName}].case`, - values: tLabelValues - }); - // Add the usages to each feature case - let tFeatureUpdates = tModel.caseIDs.map((iID, iIndex) => { - return { - id: iID, - values: {usages: JSON.stringify(tModel.usages[iIndex])} - } - }); - await codapInterface.sendRequest({ - action: 'update', - resource: `dataContext[${this_.modelsDatasetName}].collection[${this_.modelCollectionName}].case`, - values: tFeatureUpdates - }); - if (this_.targetClassAttributeName !== '') { - let computedKappa = computeKappa( tPhraseCount, tMatrix.posPos, tMatrix.negNeg, - tMatrix.posPos + tMatrix.posNeg, tMatrix.posPos + tMatrix.negPos); - this_.results.accuracy = Number(computedKappa.observed.toFixed(3)); - this_.results.kappa = Number(computedKappa.kappa.toFixed(3)); - } - } - - function wipeResults() { - this_.results = { - targetTitle: '', - modelName: '', - numPositive: 0, - numNegative: 0, - accuracy: 0, - kappa: 0 - } - } - - /** - * Setting results will cause them to be displayed - */ - function reportResults() { - this_.results.targetTitle = this_.targetDatasetInfo.title; - this_.results.modelName = this_.modelToplevelName; - this_.setState({count: this_.state.count + 1}) - } - - wipeResults(); - await deselectAllCasesIn(this.targetDatasetInfo.name); - await deselectAllCasesIn(this.modelsDatasetName); - await buildModelFromDataset(); - await addUsagesAttributeToModelDataset(); - await this.addAttributesToTarget(); - await classifyEachPhrase(); - reportResults(); - await this.getTextFeedbackManager().addTextComponent(); - } - - private renderForActiveState() { - const kNoModels = 'No models to choose from', - kNoTargets = 'No phrases to classify found'; - let this_ = this; - - function entityPropertyControl(entityInfoArray: entityInfo[], propName: string, prompt: string, noneFoundPrompt: string) { - if (entityInfoArray.length === 1) - this_[propName] = entityInfoArray[0]; - if (entityInfoArray.length === 0) { - return ( -

{prompt}{noneFoundPrompt}

- ) - } else { - return ( -
- -
-
- ); - } - } - - function stringPropertyControl(listOfNames: string[], propName: string, prompt: string, noneFoundPrompt: string) { - if (listOfNames.length === 1) - this_[propName] = listOfNames[0]; - if (listOfNames.length === 0) { - return ( -

{prompt}{noneFoundPrompt}

- ) - } else { - return ( - - ); - } - } - - function modelDatasetsControl() { - return stringPropertyControl(this_.modelDatasetInfoArray.map(iInfo => iInfo.name), - 'modelsDatasetName', 'Models dataset: ', - kNoModels); - } - - function modelNamesControl() { - return stringPropertyControl(this_.modelToplevelNames, 'modelToplevelName', 'Model to use: ', - kNoModels); - } - - function targetControl() { - return entityPropertyControl(this_.targetDatasetInfoArray, 'targetDatasetInfo', - 'Dataset to use for classification: ', - kNoTargets); - } - - function getTargetAttributeControl() { - if (this_.targetDatasetInfo.name === '') - return ''; - return (stringPropertyControl(this_.targetAttributeNames, - 'targetAttributeName', 'Column to classify: ', 'No columns found')); - } - - function doItButton() { - if (this_.targetDatasetInfo.name === '' && this_.modelsDatasetName === '') { - return ( -
-
-

Cannot classify without both a model and phrases to classify.

-
- ); - } else if (this_.targetDatasetInfo.name === '') { - return ( -
-
-

Cannot classify without phrases to classify.

-
- ); - } else if (this_.modelsDatasetName === '') { - return ( -
-
-

Cannot classify without a model.

-
- ); - } else { - return ( -
-
- -
- ); - } - - } - - function getResults() { - - function getAccuracyAndKappa() { - return (this_.targetClassAttributeName === '' ? '' : - (
-
  • Accuracy = {this_.results.accuracy}
  • -
  • Kappa = {this_.results.kappa}
  • -
    - )); - } - - if (this_.results.targetTitle === '') - return ''; - else { - return ( -
    -
    -

    Results of classifying {this_.results.targetTitle} using - {this_.results.modelName}

    -
      -
    • {this_.results.numPositive} were classified as {this_.modelCategories[1]}
    • -
    • {this_.results.numNegative} were classified as {this_.modelCategories[0]}
    • - {getAccuracyAndKappa()} -
    -
    - ); - } - } - - return (
    - {modelDatasetsControl()} - {modelNamesControl()} - {targetControl()} - {getTargetAttributeControl()} - {doItButton()} - {getResults()} -
    ) - } - - public render() { - return ( -
    - {this.renderForActiveState()} -
    - ) - } -} diff --git a/src/components/component_utilities.tsx b/src/components/component_utilities.tsx new file mode 100644 index 0000000..2c722aa --- /dev/null +++ b/src/components/component_utilities.tsx @@ -0,0 +1,27 @@ +/** + * These utilities are used by more than one component + */ + +import React from "react"; +import {SelectBox} from "devextreme-react/select-box"; +import {action} from "mobx"; + +export function choicesMenu(iPrompt: string, iPlaceHolder: string, iHint:string, + iChoices: string[], iValue: string, iNoDataText:string, iCallback: (choice: string) => void) { + return ( +
    + {iPrompt}: + iCallback(e))} + width={'100%'} + > + +
    + ) +} diff --git a/src/components/feature_component.tsx b/src/components/feature_component.tsx new file mode 100644 index 0000000..45fd85e --- /dev/null +++ b/src/components/feature_component.tsx @@ -0,0 +1,309 @@ +/** + * This component provides the space for a user to construct and edit a feature + */ + +import React, {Component} from "react"; +import { + Feature, + featureDescriptors, + kKindOfThingOptionList, + kKindOfThingOptionPunctuation, + kKindOfThingOptionText, + SearchDetails +} from "../stores/store_types_and_constants"; +import {DomainStore} from "../stores/domain_store"; +import {observer} from "mobx-react"; +import {UiStore} from "../stores/ui_store"; +import {TextBox} from "devextreme-react"; +import {action, toJS} from "mobx"; +import {SelectBox} from "devextreme-react/select-box"; +import {stopWords} from "../lib/stop_words"; +import {CheckBox} from "devextreme-react/check-box"; +import {SQ} from "../lists/lists"; +import Button from "devextreme-react/button"; +import {NumberBox} from "devextreme-react/number-box"; + +interface FeatureComponentInfo { + subscriberIndex: number +} + +export interface FeatureComponentProps { + uiStore: UiStore + domainStore: DomainStore + feature: Feature + shortened: boolean +} + +export const FeatureComponent = observer(class FeatureComponent extends Component { + + private featureInfo: FeatureComponentInfo; + + constructor(props: any) { + super(props); + this.state = { + count: 0 + }; + this.featureInfo = {subscriberIndex: -1} + } + + async updateFeaturesDataset(iFeature: Feature) { + if (!iFeature.inProgress) { + await this.props.domainStore.targetStore.addOrUpdateFeatureToTarget(iFeature, true) + switch (iFeature.info.kind) { + case 'search': + case 'count': + case 'column': + await this.props.domainStore.updateNonNtigramFeaturesDataset() + break + case 'ngram': + await this.props.domainStore.updateNgramFeatures() + break + } + } + } + + render() { + const this_ = this + + /* + function nameBox() { + return ( + { + tFeature.name = e.value + await this_.updateFeaturesDataset(tFeature) + })} + value={tFeature.name} + onFocusOut={action(() => { + tFeature.name = this_.props.domainStore.featureStore.guaranteeUniqueFeatureName(tFeature.name) + })} + maxLength={20} + /> + ) + } + */ + + function kindOfContainsChoice() { + tFeatureDescriptors.featureKinds[2].items = this_.props.domainStore.targetStore.targetColumnFeatureNames.map(iColumnName => { + return { + name: iColumnName, + value: `{"kind": "column", "details": {"columName":"${iColumnName}"}}`, + key: 'Choose other columns as features' + } + }) + // @ts-ignore + tFeatureDescriptors.featureKinds[1].items[0].disabled = this_.props.domainStore.featureStore.hasNgram() + return ( + { + tFeature.infoChoice = e.value + tFeature.info.kind = JSON.parse(e.value).kind + tFeature.info.details = Object.assign(tFeature.info.details || {}, JSON.parse(e.value).details) + if (tFeature.info.kind === 'ngram') { + tFeature.info.frequencyThreshold = 4 + tFeature.info.ignoreStopWords = true + } else if (tFeature.info.kind === 'column') { + tFeature.name = JSON.parse(e.value).details['columName'] + tFeature.type = 'column' + } + // await this_.updateFeaturesDataset(tFeature) + })} + /> + ) + } + + function kindOfThingContainedChoice() { + if (tFeature.info.kind === 'search') { + return ( + { + (tFeature.info.details as SearchDetails).what = e.value + await this_.updateFeaturesDataset(tFeature) + })} + /> + ) + } + } + + function freeFormTextBox() { + const tContainsDetails = tFeature.info.details as SearchDetails + if (tContainsDetails && tContainsDetails.what === kKindOfThingOptionText) { + return ( + { + tContainsDetails.freeFormText = e.value + await this_.updateFeaturesDataset(tFeature) + })} + value={tContainsDetails.freeFormText} + maxLength={100} + /> + ) + } + } + + function punctuationChoice() { + const tContainsDetails = tFeature.info.details as SearchDetails + if (tContainsDetails && tContainsDetails.what === kKindOfThingOptionPunctuation) { + return ( + { + tContainsDetails.punctuation = e.value + await this_.updateFeaturesDataset(tFeature) + })} + value={tContainsDetails.punctuation} + maxLength={1} + /> + ) + } + } + + function wordListChoice() { + const tContainsDetails = tFeature.info.details as SearchDetails + if (tContainsDetails && tContainsDetails.what === kKindOfThingOptionList) { + const tWordListSpecs = this_.props.domainStore.featureStore.wordListSpecs, + tWordListDatasetNames = tWordListSpecs.map(iDataset => { + return iDataset.datasetName; + }), + tLists = Object.keys(SQ.lists).concat(tWordListDatasetNames) + return ( + { + const tWordListSpec = tWordListSpecs.find((iSpec) => { + return iSpec.datasetName === option + }) + let tAttributeName = '' + if (tWordListSpec) { + tAttributeName = tWordListSpec.firstAttributeName + } + (tFeature.info.details as SearchDetails).wordList = { + datasetName: option, + firstAttributeName: tAttributeName + } + })} + /> + ) + } + } + + function ngramSettings() { + if (tFeature.info.kind === 'ngram') + return (
    + { + tFeature.info.ignoreStopWords = e + }) + } + /> +
    + Ignore words that appear fewer than + { + tFeature.info.frequencyThreshold = e.value + })} + onEnterKey={() => { + // this.blurInput(); + }} + /> + times +
    + {/* + { + return tFeature.info.frequencyThreshold + }} + setter={action((newValue: number) => { + tFeature.info.frequencyThreshold = newValue + }) + } + /> +*/} +
    ) + } + + const tFeature = this.props.feature + const tFeatureDescriptors = toJS(featureDescriptors) + const tContainsOption = tFeature.infoChoice ? tFeature.infoChoice : '' + const tKindOption = tFeature.info.details ? (tFeature.info.details as SearchDetails).what : '' + + if (!this.props.shortened) { + return ( +
    + {kindOfContainsChoice()} + {kindOfThingContainedChoice()} + {freeFormTextBox()} + {punctuationChoice()} + {wordListChoice()} + {ngramSettings()} +
    + ) + } else { + const tHint = tFeature.chosen ? SQ.hints.featureTableCheckboxRemove : + SQ.hints.featureTableCheckboxAdd + return ( +
    + { + await this.props.domainStore.featureStore.toggleChosenFor(tFeature) + console.log(`type = ${tFeature.type}; chosen = ${tFeature.chosen}`) + if( tFeature.type === 'unigram' && tFeature.chosen) + this.props.domainStore.updateNgramFeatures() + })} + hint={tHint} + /> +

    {tFeature.name}

    +
    + ) + } + } + } +) \ No newline at end of file diff --git a/src/components/feature_constructor.tsx b/src/components/feature_constructor.tsx new file mode 100644 index 0000000..18890e0 --- /dev/null +++ b/src/components/feature_constructor.tsx @@ -0,0 +1,47 @@ +/** + * This component provides the space for a user to construct a new feature + */ + +import React, {Component} from "react"; +import {DomainStore} from "../stores/domain_store"; +import {observer} from "mobx-react"; +import {UiStore} from "../stores/ui_store"; +import {FeatureComponent} from "./feature_component"; + +interface FeatureConstructorInfo { + subscriberIndex: number +} + +export interface FeatureConstructorProps { + uiStore: UiStore + domainStore: DomainStore +} + +export const FeatureConstructor = observer(class FeatureConstructor extends Component { + + private featureConstructorInfo: FeatureConstructorInfo; + + constructor(props: any) { + super(props); + this.state = { + count: 0 + }; + this.featureConstructorInfo = {subscriberIndex: -1} + } + + render() { + const tFeatureUnderConstruction = this.props.domainStore.featureStore.featureUnderConstruction + if (tFeatureUnderConstruction.inProgress) { + return ( +
    + +
    + ) + } else return '' + } +}) \ No newline at end of file diff --git a/src/components/feature_list.tsx b/src/components/feature_list.tsx new file mode 100644 index 0000000..fdbaafd --- /dev/null +++ b/src/components/feature_list.tsx @@ -0,0 +1,54 @@ +/** + * This component lists the constructed features and provides an interface for choosing, deleting, and editing them + */ + +import React, {Component} from "react"; +import {DomainStore} from "../stores/domain_store"; +import {observer} from "mobx-react"; +import {UiStore} from "../stores/ui_store"; +import {FeatureComponent} from "./feature_component"; + +interface FeatureListInfo { + subscriberIndex: number +} + +export interface FeatureListProps { + uiStore: UiStore + domainStore: DomainStore +} + +export const FeatureList = observer(class FeatureList extends Component { + + private featureListInfo: FeatureListInfo; + + constructor(props: any) { + super(props); + this.state = { + count: 0 + }; + this.featureListInfo = {subscriberIndex: -1} + } + + getList() { + const tFeatureList = this.props.domainStore.featureStore.features + return tFeatureList.map((iFeature, iIndex) => { + return + }) + } + + render() { + return ( +
    +
    + {this.getList()} +
    +
    + ) + } +}) diff --git a/src/components/feature_pane.tsx b/src/components/feature_pane.tsx new file mode 100644 index 0000000..a5f54a0 --- /dev/null +++ b/src/components/feature_pane.tsx @@ -0,0 +1,132 @@ +/** + * This component provides the space for a user to construct and edit features + */ + +import React, {Component} from "react"; +import {DomainStore} from "../stores/domain_store"; +import {observer} from "mobx-react"; +import {UiStore} from "../stores/ui_store"; +import {Button} from "devextreme-react"; +import {FeatureConstructor} from "./feature_constructor"; +import {action} from "mobx"; +import {FeatureList} from "./feature_list"; +import {starterFeature} from "../stores/store_types_and_constants"; +import {SQ} from "../lists/lists"; + +interface FeaturePaneState { + count: number, +} + +interface FeaturePaneInfo { + subscriberIndex: number +} + +export interface Feature_Props { + uiStore: UiStore + domainStore: DomainStore +} + +export const FeaturePane = observer(class FeaturePane extends Component { + + private featurePaneInfo: FeaturePaneInfo; + + constructor(props: any) { + super(props); + this.state = { + count: 0 + }; + this.featurePaneInfo = {subscriberIndex: -1} + } + + getButtons() { + const tFeatureStore = this.props.domainStore.featureStore, + tFeatureUnderConstruction = tFeatureStore.featureUnderConstruction, + tInProgress = tFeatureUnderConstruction.inProgress, + tButtonLabel = tFeatureUnderConstruction.inProgress ? 'Cancel' : 'Add Features', + tButtonHint = tFeatureUnderConstruction.inProgress ? SQ.hints.featureCancel : + SQ.hints.featureAdd, + tAddButton = + ( + ), + tDoneButton = tFeatureUnderConstruction.inProgress ? ( + + ) : '' + return ( +
    + {tAddButton} + {tDoneButton} +
    + ) + } + + render() { + const this_ = this, + tFeatureStore = this.props.domainStore.featureStore + + function featureInstructions() { + if (!tFeatureStore.featureUnderConstruction.inProgress) { + const + tFeatures =tFeatureStore.features, + tInstructions = tFeatures.length === 0 ? +

    What features of the training data should StoryQ use to train the model?

    : +

    Add more features or go to this_.props.domainStore.setPanel(2))} + style={{cursor: 'pointer'}} + > + Trainingto train your model.

    + return ( +
    + {tInstructions} +
    + ) + } + } + + return ( +
    + {featureInstructions()} + + {this.getButtons()} + +
    + ); + } +}) \ No newline at end of file diff --git a/src/components/feature_panel.tsx b/src/components/feature_panel.tsx new file mode 100644 index 0000000..dbde753 --- /dev/null +++ b/src/components/feature_panel.tsx @@ -0,0 +1,49 @@ +/** + * This component provides the space for a user to construct and edit features + */ + +import React, {Component} from "react"; +import {DomainStore} from "../stores/domain_store"; +import {observer} from "mobx-react"; +import {UiStore} from "../stores/ui_store"; +import {TargetInfoPane} from "./target_info_pane"; +import {FeaturePane} from "./feature_pane"; + +export interface Feature_Props { + uiStore: UiStore + domainStore: DomainStore +} + +export const FeaturePanel = observer(class FeaturePanel extends Component { + + constructor(props: any) { + super(props); + this.state = { + count: 0 + }; + } + + async componentDidMount() { + await this.updateFeaturesDataset(); + await this.props.domainStore.featureStore.updateWordListSpecs() + } + + async updateFeaturesDataset() { + await this.props.domainStore.updateNonNtigramFeaturesDataset() + } + + render() { + + return ( +
    + + +
    + ); + } +}) \ No newline at end of file diff --git a/src/numeric_input.tsx b/src/components/numeric_input.tsx similarity index 98% rename from src/numeric_input.tsx rename to src/components/numeric_input.tsx index 7ba230c..6a572e9 100644 --- a/src/numeric_input.tsx +++ b/src/components/numeric_input.tsx @@ -33,6 +33,7 @@ export class NumericInput extends Component {