From c48345b74fadc558062ced96591f041eb4f2729d Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Thu, 1 Aug 2019 15:55:07 -0400 Subject: cognitive services --- src/client/views/nodes/ImageBox.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/client/views/nodes/ImageBox.tsx') diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 9a0615d68..879a91fa1 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -237,7 +237,9 @@ export class ImageBox extends DocComponent(ImageD results.map((face: CognitiveServices.Image.Face) => faceDocs.push(Docs.Get.DocumentHierarchyFromJson(face, `Face: ${face.faceId}`)!)); return faceDocs; }; - CognitiveServices.Image.Manager.analyzer(this.extensionDoc, ["faces"], this.url, Service.Face, converter); + if (this.url) { + CognitiveServices.Image.Appliers.ProcessImage(this.extensionDoc, ["faces"], this.url, Service.Face, converter); + } } generateMetadata = (threshold: Confidence = Confidence.Excellent) => { @@ -256,7 +258,9 @@ export class ImageBox extends DocComponent(ImageD tagDoc.confidence = threshold; return tagDoc; }; - CognitiveServices.Image.Manager.analyzer(this.extensionDoc, ["generatedTagsDoc"], this.url, Service.ComputerVision, converter); + if (this.url) { + CognitiveServices.Image.Appliers.ProcessImage(this.extensionDoc, ["generatedTagsDoc"], this.url, Service.ComputerVision, converter); + } } @action -- cgit v1.2.3-70-g09d2 From 40db4e50aaef7e012f652c8637e101b5a1a99d47 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 1 Aug 2019 16:21:29 -0400 Subject: fixed errors. --- src/client/views/TemplateMenu.tsx | 2 +- src/client/views/nodes/ImageBox.tsx | 31 ++++++++++++++----------------- 2 files changed, 15 insertions(+), 18 deletions(-) (limited to 'src/client/views/nodes/ImageBox.tsx') diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index c413650f0..e654a0644 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -39,7 +39,7 @@ export interface TemplateMenuProps { @observer export class TemplateMenu extends React.Component { @observable private _hidden: boolean = true; - dragRef = React.createRef(); + dragRef = React.createRef(); constructor(props: TemplateMenuProps) { super(props); diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 9a0615d68..17dc4184a 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -1,42 +1,39 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faImage, faFileAudio, faPaintBrush, faAsterisk } from '@fortawesome/free-solid-svg-icons'; -import { action, observable, computed, runInAction } from 'mobx'; +import { faEye } from '@fortawesome/free-regular-svg-icons'; +import { faAsterisk, faFileAudio, faImage, faPaintBrush } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { action, computed, observable, runInAction } from 'mobx'; import { observer } from "mobx-react"; import Lightbox from 'react-image-lightbox'; import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app -import { Doc, HeightSym, WidthSym, DocListCast } from '../../../new_fields/Doc'; +import { Doc, DocListCast, HeightSym, WidthSym } from '../../../new_fields/Doc'; import { List } from '../../../new_fields/List'; import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema'; -import { Cast, FieldValue, NumCast, StrCast, BoolCast } from '../../../new_fields/Types'; -import { ImageField, AudioField } from '../../../new_fields/URLField'; +import { ComputedField } from '../../../new_fields/ScriptField'; +import { BoolCast, Cast, FieldValue, NumCast, StrCast } from '../../../new_fields/Types'; +import { AudioField, ImageField } from '../../../new_fields/URLField'; +import { RouteStore } from '../../../server/RouteStore'; import { Utils } from '../../../Utils'; +import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices'; +import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; +import { CompileScript } from '../../util/Scripting'; import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from "../../views/ContextMenu"; import { ContextMenuProps } from '../ContextMenuItem'; import { DocComponent } from '../DocComponent'; import { InkingControl } from '../InkingControl'; import { positionSchema } from './DocumentView'; +import FaceRectangles from './FaceRectangles'; import { FieldView, FieldViewProps } from './FieldView'; import "./ImageBox.scss"; import React = require("react"); -import { RouteStore } from '../../../server/RouteStore'; -import { Docs, DocumentType } from '../../documents/Documents'; -import { DocServer } from '../../DocServer'; -import { Font } from '@react-pdf/renderer'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { CognitiveServices, Service, Tag, Confidence } from '../../cognitive_services/CognitiveServices'; -import FaceRectangles from './FaceRectangles'; -import { faEye } from '@fortawesome/free-regular-svg-icons'; -import { ComputedField } from '../../../new_fields/ScriptField'; -import { CompileScript } from '../../util/Scripting'; -import { thisExpression } from 'babel-types'; var requestImageSize = require('../../util/request-image-size'); var path = require('path'); const { Howl } = require('howler'); -library.add(faImage, faEye, faPaintBrush); +library.add(faImage, faEye as any, faPaintBrush); library.add(faFileAudio, faAsterisk); -- cgit v1.2.3-70-g09d2 From e65a75245617e2161ad5a75dd2b91461144dfdf7 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 3 Aug 2019 18:47:48 -0400 Subject: fixed marquee collection'ing with backgrounds. fixed rotating images. fixed marqueeing ink. --- src/client/views/InkingCanvas.tsx | 2 +- .../collections/collectionFreeForm/MarqueeView.tsx | 21 +++++++++++--------- src/client/views/nodes/FormattedTextBox.tsx | 8 ++++---- src/client/views/nodes/ImageBox.tsx | 10 +++++----- src/new_fields/Doc.ts | 23 +++++++++++++--------- 5 files changed, 36 insertions(+), 28 deletions(-) (limited to 'src/client/views/nodes/ImageBox.tsx') diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx index c4cd863d1..1c221e3df 100644 --- a/src/client/views/InkingCanvas.tsx +++ b/src/client/views/InkingCanvas.tsx @@ -176,7 +176,7 @@ export class InkingCanvas extends React.Component { } render() { - let svgCanvasStyle = InkingControl.Instance.selectedTool !== InkTool.None ? "canSelect" : "noSelect"; + let svgCanvasStyle = InkingControl.Instance.selectedTool !== InkTool.None && !this.props.Document.isBackground ? "canSelect" : "noSelect"; return (
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index ff96bd993..aad26efa0 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -226,15 +226,17 @@ export class MarqueeView extends React.Component } get ink() { - let container = this.props.container.Document; + let container = this.props.container.props.Document; let containerKey = this.props.container.props.fieldKey; - return Cast(container[containerKey + "_ink"], InkField); + let extensionDoc = Doc.resolvedFieldDataDoc(container, containerKey, "true"); + return Cast(extensionDoc.ink, InkField); } set ink(value: InkField | undefined) { - let container = Doc.GetProto(this.props.container.Document); + let container = Doc.GetProto(this.props.container.props.Document); let containerKey = this.props.container.props.fieldKey; - container[containerKey + "_ink"] = value; + let extensionDoc = Doc.resolvedFieldDataDoc(container, containerKey, "true"); + extensionDoc.ink = value; } @undoBatch @@ -247,7 +249,7 @@ export class MarqueeView extends React.Component this._commandExecuted = true; e.stopPropagation(); (e as any).propagationIsStopped = true; - this.marqueeSelect().map(d => this.props.removeDocument(d)); + this.marqueeSelect(false).map(d => this.props.removeDocument(d)); if (this.ink) { this.marqueeInkDelete(this.ink.inkData); } @@ -261,7 +263,7 @@ export class MarqueeView extends React.Component e.preventDefault(); (e as any).propagationIsStopped = true; let bounds = this.Bounds; - let selected = this.marqueeSelect(); + let selected = this.marqueeSelect(false); if (e.key === "c") { selected.map(d => { this.props.removeDocument(d); @@ -283,7 +285,8 @@ export class MarqueeView extends React.Component height: bounds.height, title: e.key === "s" || e.key === "S" ? "-summary-" : "a nested collection", }); - newCollection.data_ink = inkData ? new InkField(this.marqueeInkSelect(inkData)) : undefined; + let dataExtensionField = Doc.CreateDocumentExtensionForField(newCollection, "data"); + dataExtensionField.ink = inkData ? new InkField(this.marqueeInkSelect(inkData)) : undefined; this.marqueeInkDelete(inkData); if (e.key === "s") { @@ -367,7 +370,7 @@ export class MarqueeView extends React.Component } } - marqueeSelect() { + marqueeSelect(selectBackgrounds: boolean = true) { let selRect = this.Bounds; let selection: Doc[] = []; this.props.activeDocuments().filter(doc => !doc.isBackground).map(doc => { @@ -379,7 +382,7 @@ export class MarqueeView extends React.Component selection.push(doc); } }); - if (!selection.length) { + if (!selection.length && selectBackgrounds) { this.props.activeDocuments().map(doc => { var x = NumCast(doc.x); var y = NumCast(doc.y); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index e076efe18..6c97add76 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -674,7 +674,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe let self = this; let style = this.props.isOverlay ? "scroll" : "hidden"; let rounded = StrCast(this.props.Document.borderRounding) === "100%" ? "-rounded" : ""; - let interactive = InkingControl.Instance.selectedTool ? "" : "interactive"; + let interactive: "all" | "none" = InkingControl.Instance.selectedTool || this.props.Document.isBackground || + (this.props.Document.isButton && !this.props.isSelected()) ? "none" : "all"; Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey); return (
-
+
); } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 17dc4184a..0d9c2bb8a 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -314,14 +314,14 @@ export class ImageBox extends DocComponent(ImageD resize(srcpath: string, layoutdoc: Doc) { requestImageSize(srcpath) .then((size: any) => { - let aspect = size.height / size.width; let rotation = NumCast(this.dataDoc.rotation) % 180; - if (rotation === 90 || rotation === 270) aspect = 1 / aspect; - if (Math.abs(NumCast(layoutdoc.height) - size.height) > 1 || Math.abs(NumCast(layoutdoc.width) - size.width) > 1) { + let realsize = rotation === 90 || rotation === 270 ? { height: size.width, width: size.height } : size; + let aspect = realsize.height / realsize.width; + if (Math.abs(NumCast(layoutdoc.height) - realsize.height) > 1 || Math.abs(NumCast(layoutdoc.width) - realsize.width) > 1) { setTimeout(action(() => { layoutdoc.height = layoutdoc[WidthSym]() * aspect; - layoutdoc.nativeHeight = size.height; - layoutdoc.nativeWidth = size.width; + layoutdoc.nativeHeight = realsize.height; + layoutdoc.nativeWidth = realsize.width; }), 0); } }) diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index b70951040..b081588a3 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -341,19 +341,24 @@ export namespace Doc { return fieldExt && doc[fieldKey + "_ext"] instanceof Doc ? doc[fieldKey + "_ext"] as Doc : doc; } + export function CreateDocumentExtensionForField(doc: Doc, fieldKey: string) { + let docExtensionForField = new Doc(doc[Id] + fieldKey, true); + docExtensionForField.title = fieldKey + ".ext"; + docExtensionForField.extendsDoc = doc; // this is used by search to map field matches on the extension doc back to the document it extends. + docExtensionForField.type = DocumentType.EXTENSION; + let proto: Doc | undefined = doc; + while (proto && !Doc.IsPrototype(proto)) { + proto = proto.proto; + } + (proto ? proto : doc)[fieldKey + "_ext"] = new PrefetchProxy(docExtensionForField); + return docExtensionForField; + } + export function UpdateDocumentExtensionForField(doc: Doc, fieldKey: string) { let docExtensionForField = doc[fieldKey + "_ext"] as Doc; if (docExtensionForField === undefined) { setTimeout(() => { - docExtensionForField = new Doc(doc[Id] + fieldKey, true); - docExtensionForField.title = fieldKey + ".ext"; - docExtensionForField.extendsDoc = doc; // this is used by search to map field matches on the extension doc back to the document it extends. - docExtensionForField.type = DocumentType.EXTENSION; - let proto: Doc | undefined = doc; - while (proto && !Doc.IsPrototype(proto)) { - proto = proto.proto; - } - (proto ? proto : doc)[fieldKey + "_ext"] = new PrefetchProxy(docExtensionForField); + CreateDocumentExtensionForField(doc, field); }, 0); } else if (doc instanceof Doc) { // backward compatibility -- add fields for docs that don't have them already docExtensionForField.extendsDoc === undefined && setTimeout(() => docExtensionForField.extendsDoc = doc, 0); -- cgit v1.2.3-70-g09d2 From 38317d4a3687e02a727693b569dfc5980db81854 Mon Sep 17 00:00:00 2001 From: ab Date: Mon, 5 Aug 2019 16:15:59 -0400 Subject: recommender --- package.json | 1 + src/client/cognitive_services/CognitiveServices.ts | 3 ++- src/client/util/Recommender.tsx | 22 ++++++++++++++++++++++ src/client/views/nodes/ImageBox.tsx | 8 +++++--- 4 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 src/client/util/Recommender.tsx (limited to 'src/client/views/nodes/ImageBox.tsx') diff --git a/package.json b/package.json index d469f0c49..9012ff1f7 100644 --- a/package.json +++ b/package.json @@ -214,6 +214,7 @@ "typescript-collections": "^1.3.2", "url-loader": "^1.1.2", "uuid": "^3.3.2", + "word2vec": "^1.1.4", "xoauth2": "^1.2.0", "youtube": "^0.1.0" } diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts index 8aac20084..6afd2571a 100644 --- a/src/client/cognitive_services/CognitiveServices.ts +++ b/src/client/cognitive_services/CognitiveServices.ts @@ -251,8 +251,9 @@ export namespace CognitiveServices { analyzer: async (target: Doc, keys: string[], data: string, converter: Converter) => { let results = await ExecuteQuery(Service.Text, Manager, data); console.log(results); - converter(results); + let keyterms = converter(results); //target[keys[0]] = Docs.Get.DocumentHierarchyFromJson(results, "Key Word Analysis"); + target[keys[0]] = keyterms; console.log("analyzed!"); return null; } diff --git a/src/client/util/Recommender.tsx b/src/client/util/Recommender.tsx new file mode 100644 index 000000000..88d0c85f5 --- /dev/null +++ b/src/client/util/Recommender.tsx @@ -0,0 +1,22 @@ +import React = require("react"); +import { observer } from "mobx-react"; +import { observable, runInAction } from "mobx"; +var w2v = require('word2vec'); + +@observer +export default class Recommender extends React.Component { + + /*** + * Converts text to n-dimensional vector using pretrained word2vec model + */ + text_to_vec(text: string) { + + } + + render() { + return ( +
Recommender System!!!
+ ) + } + +} \ No newline at end of file diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index c12fae8e9..7388b532c 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -245,12 +245,14 @@ export class ImageBox extends DocComponent(ImageD let data = StrCast(this.dataDoc.title); console.log(data); let converter = (results: any) => { + let keyterms = new List(); results.documents.forEach((doc: any) => { - console.log(doc.keyPhrases); + let keyPhrases = doc.keyPhrases; + keyPhrases.map((kp: string) => keyterms.push(kp)); }); - return new Doc(); + return keyterms; }; - CognitiveServices.Text.Manager.analyzer(this.extensionDoc, ["key words", "key word strings"], data, converter); + CognitiveServices.Text.Manager.analyzer(this.extensionDoc, ["key words"], data, converter); } generateMetadata = (threshold: Confidence = Confidence.Excellent) => { -- cgit v1.2.3-70-g09d2 From 06b59a4ec2f1871846696da22928fc7d54ae02d6 Mon Sep 17 00:00:00 2001 From: ab Date: Tue, 6 Aug 2019 16:01:57 -0400 Subject: word2vec is functional --- package.json | 8 +++++- src/client/cognitive_services/CognitiveServices.ts | 18 ++++++++++++- src/client/views/nodes/ImageBox.tsx | 8 ++++-- src/server/Recommender.ts | 30 ++++++++++++++++++---- src/server/index.ts | 19 ++++++++++---- webpack.config.js | 5 +++- 6 files changed, 73 insertions(+), 15 deletions(-) (limited to 'src/client/views/nodes/ImageBox.tsx') diff --git a/package.json b/package.json index 9012ff1f7..44d5287bd 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,12 @@ "version": "1.0.0", "description": "", "main": "index.js", + "browser": { + "child_process": false + }, + "node": { + "child_process": "empty" + }, "scripts": { "start": "cross-env NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev -- src/server/index.ts", "debug": "cross-env NODE_OPTIONS=--max_old_space_size=8192 ts-node-dev --inspect -- src/server/index.ts", @@ -218,4 +224,4 @@ "xoauth2": "^1.2.0", "youtube": "^0.1.0" } -} +} \ No newline at end of file diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts index 6afd2571a..863236b60 100644 --- a/src/client/cognitive_services/CognitiveServices.ts +++ b/src/client/cognitive_services/CognitiveServices.ts @@ -6,6 +6,8 @@ import { RouteStore } from "../../server/RouteStore"; import { Utils } from "../../Utils"; import { InkData } from "../../new_fields/InkField"; import { UndoManager } from "../util/UndoManager"; +import requestPromise = require("request-promise"); +import { List } from "../../new_fields/List"; type APIManager = { converter: BodyConverter, requester: RequestExecutor, analyzer: AnalysisApplier }; type RequestExecutor = (apiKey: string, body: string, service: Service) => Promise; @@ -255,9 +257,23 @@ export namespace CognitiveServices { //target[keys[0]] = Docs.Get.DocumentHierarchyFromJson(results, "Key Word Analysis"); target[keys[0]] = keyterms; console.log("analyzed!"); + vectorize(keyterms); return null; - } + }, + + }; + function vectorize(keyterms: any) { + console.log("vectorizing..."); + keyterms = ["father", "king"]; + let args = { method: 'POST', uri: Utils.prepend("/recommender"), body: { keyphrases: keyterms }, json: true }; + requestPromise.post(args).then((value) => { + value.forEach((wordvec: any) => { + console.log(wordvec.word); + }); + }); + } + } } \ No newline at end of file diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 7388b532c..161226c0d 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -1,5 +1,5 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faImage, faFileAudio, faPaintBrush, faAsterisk } from '@fortawesome/free-solid-svg-icons'; +import { faImage, faFileAudio, faPaintBrush, faAsterisk, faBrain } from '@fortawesome/free-solid-svg-icons'; import { action, observable, computed, runInAction } from 'mobx'; import { observer } from "mobx-react"; import Lightbox from 'react-image-lightbox'; @@ -31,12 +31,14 @@ import { faEye } from '@fortawesome/free-regular-svg-icons'; import { ComputedField } from '../../../new_fields/ScriptField'; import { CompileScript } from '../../util/Scripting'; import { thisExpression } from 'babel-types'; +import { Recommender } from '../../../server/Recommender'; +import requestPromise = require('request-promise'); var requestImageSize = require('../../util/request-image-size'); var path = require('path'); const { Howl } = require('howler'); -library.add(faImage, faEye, faPaintBrush); +library.add(faImage, faEye, faPaintBrush, faBrain); library.add(faFileAudio, faAsterisk); @@ -253,6 +255,8 @@ export class ImageBox extends DocComponent(ImageD return keyterms; }; CognitiveServices.Text.Manager.analyzer(this.extensionDoc, ["key words"], data, converter); + // request recommender + //fetch(Utils.prepend("/recommender"), { body: body, method: "POST", headers: { "content-type": "application/json" } }).then((value) => console.log(value)); } generateMetadata = (threshold: Confidence = Confidence.Excellent) => { diff --git a/src/server/Recommender.ts b/src/server/Recommender.ts index 6d9ca6486..3c71f3aa1 100644 --- a/src/server/Recommender.ts +++ b/src/server/Recommender.ts @@ -3,25 +3,27 @@ var w2v = require('word2vec'); export class Recommender { private _model: any; + static Instance: Recommender; constructor() { console.log("creating recommender..."); + Recommender.Instance = this; } - public loadModel(): Promise { + private loadModel(): Promise { let self = this; return new Promise(res => { w2v.loadModel("./node_modules/word2vec/vectors.txt", function (err: any, model: any) { - console.log(err); - console.log(model); self._model = model; - console.log(model.similarity('father', 'mother')); res(model); }); }); } - public testModel() { + public async testModel() { + if (!this._model) { + await this.loadModel(); + } if (this._model) { let similarity = this._model.similarity('father', 'mother'); console.log(similarity); @@ -30,4 +32,22 @@ export class Recommender { console.log("model not found :("); } } + + public async testInstance(text: string) { + if (!this._model) { + await this.loadModel(); + } + console.log(text); + } + + public async vectorize(text: string[]) { + if (!this._model) { + await this.loadModel(); + } + if (this._model) { + let word_vecs = this._model.getVectors(text); + console.log(word_vecs[0]); + return word_vecs; + } + } } diff --git a/src/server/index.ts b/src/server/index.ts index dda5a870a..67087fc1f 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -52,11 +52,6 @@ const probe = require("probe-image-size"); var SolrNode = require('solr-node'); var shell = require('shelljs'); -let recommender = new Recommender(); -recommender.loadModel().then(() => { - recommender.testModel(); -}); - const download = (url: string, dest: fs.PathLike) => request.get(url).pipe(fs.createWriteStream(dest)); let youtubeApiKey: string; YoutubeApi.readApiKey((apiKey: string) => youtubeApiKey = apiKey); @@ -651,6 +646,20 @@ app.use(RouteStore.corsProxy, (req, res) => { }).pipe(res); }); +//// + +let recommender = new Recommender(); +recommender.testModel(); + +app.post("/recommender", async (req, res) => { + let keyphrases = req.body.keyphrases; + let wordvecs = await recommender.vectorize(keyphrases); + res.send(wordvecs); +}); + + +///// + app.get(RouteStore.delete, (req, res) => { if (release) { res.send("no"); diff --git a/webpack.config.js b/webpack.config.js index 5e0a6a883..6a14dfcda 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -93,5 +93,8 @@ module.exports = { warnings: true, errors: true } - } + }, + externals: [ + 'child_process' + ] }; \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 17b27d3575d3f91f461262e5ad72a457238d198a Mon Sep 17 00:00:00 2001 From: ab Date: Wed, 7 Aug 2019 16:28:51 -0400 Subject: correlation matrix completed --- package.json | 2 +- src/client/ClientRecommender.ts | 101 +++++++++++++++++++++ src/client/cognitive_services/CognitiveServices.ts | 22 +++-- src/client/views/MainView.tsx | 7 ++ .../collectionFreeForm/CollectionFreeFormView.tsx | 19 +++- src/client/views/nodes/ImageBox.tsx | 14 +-- src/server/Recommender.ts | 27 +++++- 7 files changed, 166 insertions(+), 26 deletions(-) create mode 100644 src/client/ClientRecommender.ts (limited to 'src/client/views/nodes/ImageBox.tsx') diff --git a/package.json b/package.json index 44d5287bd..ebb0c35a8 100644 --- a/package.json +++ b/package.json @@ -224,4 +224,4 @@ "xoauth2": "^1.2.0", "youtube": "^0.1.0" } -} \ No newline at end of file +} diff --git a/src/client/ClientRecommender.ts b/src/client/ClientRecommender.ts new file mode 100644 index 000000000..7ff79ab50 --- /dev/null +++ b/src/client/ClientRecommender.ts @@ -0,0 +1,101 @@ +import { Doc } from "../new_fields/Doc"; +import { StrCast } from "../new_fields/Types"; +import { List } from "../new_fields/List"; +import { CognitiveServices } from "./cognitive_services/CognitiveServices"; + + +var assert = require('assert'); + +export class ClientRecommender { + + static Instance: ClientRecommender; + private docVectors: Set; + + constructor() { + //console.log("creating client recommender..."); + ClientRecommender.Instance = this; + this.docVectors = new Set(); + } + + + /*** + * Computes the cosine similarity between two vectors in Euclidean space. + */ + + private distance(vector1: number[], vector2: number[]) { + assert(vector1.length === vector2.length, "Vectors are not the same length"); + var dotproduct = 0; + var mA = 0; + var mB = 0; + for (let i = 0; i < vector1.length; i++) { // here you missed the i++ + dotproduct += (vector1[i] * vector2[i]); + mA += (vector1[i] * vector1[i]); + mB += (vector2[i] * vector2[i]); + } + mA = Math.sqrt(mA); + mB = Math.sqrt(mB); + var similarity = (dotproduct) / ((mA) * (mB)); // here you needed extra brackets + return similarity; + } + + /*** + * Computes the mean of a set of vectors + */ + + public mean(paragraph: Set) { + const n = 200; + const num_words = paragraph.size; + let meanVector = new Array(n).fill(0); // mean vector + paragraph.forEach((wordvec: number[]) => { + for (let i = 0; i < n; i++) { + meanVector[i] += wordvec[i]; + } + }); + meanVector = meanVector.map(x => x / num_words); + this.addToDocSet(meanVector); + return meanVector; + } + + private addToDocSet(vector: number[]) { + if (this.docVectors) { + this.docVectors.add(vector); + } + } + + /*** + * Uses Cognitive Services to extract keywords from a document + */ + + public async extractText(dataDoc: Doc, extDoc: Doc) { + let data = StrCast(dataDoc.title); + //console.log(data); + let converter = (results: any) => { + let keyterms = new List(); + results.documents.forEach((doc: any) => { + let keyPhrases = doc.keyPhrases; + keyPhrases.map((kp: string) => keyterms.push(kp)); + }); + return keyterms; + }; + await CognitiveServices.Text.Manager.analyzer(extDoc, ["key words"], data, converter); + } + + /*** + * Creates distance matrix for all Documents analyzed + */ + + public createDistanceMatrix(documents: Set = this.docVectors) { + const documents_list = Array.from(documents); + const n = documents_list.length; + var matrix = new Array(n).fill(0).map(() => new Array(n).fill(0)); + for (let i = 0; i < n; i++) { + var doc1 = documents_list[i]; + for (let j = 0; j < n; j++) { + var doc2 = documents_list[j]; + matrix[i][j] = this.distance(doc1, doc2); + } + } + return matrix; + } + +} \ No newline at end of file diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts index 863236b60..cc366abc2 100644 --- a/src/client/cognitive_services/CognitiveServices.ts +++ b/src/client/cognitive_services/CognitiveServices.ts @@ -8,6 +8,7 @@ import { InkData } from "../../new_fields/InkField"; import { UndoManager } from "../util/UndoManager"; import requestPromise = require("request-promise"); import { List } from "../../new_fields/List"; +import { ClientRecommender } from "../ClientRecommender"; type APIManager = { converter: BodyConverter, requester: RequestExecutor, analyzer: AnalysisApplier }; type RequestExecutor = (apiKey: string, body: string, service: Service) => Promise; @@ -257,20 +258,21 @@ export namespace CognitiveServices { //target[keys[0]] = Docs.Get.DocumentHierarchyFromJson(results, "Key Word Analysis"); target[keys[0]] = keyterms; console.log("analyzed!"); - vectorize(keyterms); - return null; - }, - - + await vectorize(keyterms); + } }; - function vectorize(keyterms: any) { + async function vectorize(keyterms: any) { console.log("vectorizing..."); - keyterms = ["father", "king"]; + //keyterms = ["father", "king"]; let args = { method: 'POST', uri: Utils.prepend("/recommender"), body: { keyphrases: keyterms }, json: true }; - requestPromise.post(args).then((value) => { - value.forEach((wordvec: any) => { - console.log(wordvec.word); + await requestPromise.post(args).then(async (wordvecs) => { + var vectorValues = new Set(); + wordvecs.forEach((wordvec: any) => { + //console.log(wordvec.word); + vectorValues.add(wordvec.values as number[]); }); + ClientRecommender.Instance.mean(vectorValues); + //console.log(vectorValues.size); }); } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 2ecf5fd85..97964166a 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -39,6 +39,7 @@ import { FilterBox } from './search/FilterBox'; import { CollectionTreeView } from './collections/CollectionTreeView'; import { ClientUtils } from '../util/ClientUtils'; import { SchemaHeaderField, RandomPastel } from '../../new_fields/SchemaHeaderField'; +//import { DocumentManager } from '../util/DocumentManager'; @observer export class MainView extends React.Component { @@ -435,6 +436,12 @@ export class MainView extends React.Component {
; } + // clusterDocuments = () => { + // DocumentManager.Instance.DocumentViews(); + // } + + + @action diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 29f9b1429..9344b43d2 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,6 +1,6 @@ import { library } from "@fortawesome/fontawesome-svg-core"; import { faEye } from "@fortawesome/free-regular-svg-icons"; -import { faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faPaintBrush, faTable, faUpload } from "@fortawesome/free-solid-svg-icons"; +import { faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faPaintBrush, faTable, faUpload, faBrain } from "@fortawesome/free-solid-svg-icons"; import { action, computed } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCastAsync, HeightSym, WidthSym } from "../../../../new_fields/Doc"; @@ -37,8 +37,9 @@ import "./CollectionFreeFormView.scss"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); import v5 = require("uuid/v5"); +import { ClientRecommender } from "../../../ClientRecommender"; -library.add(faEye, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload); +library.add(faEye, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBrain); export const panZoomSchema = createSchema({ panX: "number", @@ -596,6 +597,20 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { input.click(); } }); + ContextMenu.Instance.addItem({ + description: "Recommender System", + event: async () => { + new ClientRecommender(); + let activedocs = this.getActiveDocuments(); + await Promise.all(activedocs.map((doc: Doc) => { + console.log(StrCast(doc.title)); + const extdoc = doc.data_ext as Doc; + return ClientRecommender.Instance.extractText(doc, extdoc ? extdoc : doc); + })); + console.log(ClientRecommender.Instance.createDistanceMatrix()); + }, + icon: "brain" + }); } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 161226c0d..660772c0e 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -31,7 +31,7 @@ import { faEye } from '@fortawesome/free-regular-svg-icons'; import { ComputedField } from '../../../new_fields/ScriptField'; import { CompileScript } from '../../util/Scripting'; import { thisExpression } from 'babel-types'; -import { Recommender } from '../../../server/Recommender'; +//import { Recommender } from '../../../server/Recommender'; import requestPromise = require('request-promise'); var requestImageSize = require('../../util/request-image-size'); var path = require('path'); @@ -244,17 +244,7 @@ export class ImageBox extends DocComponent(ImageD } extractText = () => { - let data = StrCast(this.dataDoc.title); - console.log(data); - let converter = (results: any) => { - let keyterms = new List(); - results.documents.forEach((doc: any) => { - let keyPhrases = doc.keyPhrases; - keyPhrases.map((kp: string) => keyterms.push(kp)); - }); - return keyterms; - }; - CognitiveServices.Text.Manager.analyzer(this.extensionDoc, ["key words"], data, converter); + //Recommender.Instance.extractText(this.dataDoc, this.extensionDoc); // request recommender //fetch(Utils.prepend("/recommender"), { body: body, method: "POST", headers: { "content-type": "application/json" } }).then((value) => console.log(value)); } diff --git a/src/server/Recommender.ts b/src/server/Recommender.ts index 3c71f3aa1..ea59703c3 100644 --- a/src/server/Recommender.ts +++ b/src/server/Recommender.ts @@ -1,4 +1,10 @@ +//import { Doc } from "../new_fields/Doc"; +//import { StrCast } from "../new_fields/Types"; +//import { List } from "../new_fields/List"; +//import { CognitiveServices } from "../client/cognitive_services/CognitiveServices"; + var w2v = require('word2vec'); +var assert = require('assert'); export class Recommender { @@ -10,6 +16,10 @@ export class Recommender { Recommender.Instance = this; } + /*** + * Loads pre-trained model from word2vec + */ + private loadModel(): Promise { let self = this; return new Promise(res => { @@ -20,6 +30,10 @@ export class Recommender { }); } + /*** + * Testing + */ + public async testModel() { if (!this._model) { await this.loadModel(); @@ -33,6 +47,10 @@ export class Recommender { } } + /*** + * Tests if instance exists + */ + public async testInstance(text: string) { if (!this._model) { await this.loadModel(); @@ -40,14 +58,21 @@ export class Recommender { console.log(text); } + /*** + * Uses model to convert words to vectors + */ + public async vectorize(text: string[]) { if (!this._model) { await this.loadModel(); } if (this._model) { let word_vecs = this._model.getVectors(text); - console.log(word_vecs[0]); return word_vecs; } } + + + + } -- cgit v1.2.3-70-g09d2