From c48345b74fadc558062ced96591f041eb4f2729d Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
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<FieldViewProps, ImageDocument>(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<FieldViewProps, ImageDocument>(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 <bcz@cs.brown.edu>
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<TemplateMenuProps> {
     @observable private _hidden: boolean = true;
-    dragRef = React.createRef<HTMLDivElement>();
+    dragRef = React.createRef<HTMLUListElement>();
 
     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 <zzzman@gmail.com>
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<InkCanvasProps> {
     }
 
     render() {
-        let svgCanvasStyle = InkingControl.Instance.selectedTool !== InkTool.None ? "canSelect" : "noSelect";
+        let svgCanvasStyle = InkingControl.Instance.selectedTool !== InkTool.None && !this.props.Document.isBackground ? "canSelect" : "noSelect";
         return (
             <div className="inkingCanvas">
                 <div className={`inkingCanvas-${svgCanvasStyle}`} onPointerDown={this.onPointerDown} />
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<MarqueeViewProps>
     }
 
     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<MarqueeViewProps>
             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<MarqueeViewProps>
             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<MarqueeViewProps>
                 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<MarqueeViewProps>
         }
     }
 
-    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<MarqueeViewProps>
                 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 (
             <div className={`formattedTextBox-cont-${style}`} ref={this._ref}
@@ -683,7 +684,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                     background: this.props.hideOnLeave ? "rgba(0,0,0,0.4)" : undefined,
                     opacity: this.props.hideOnLeave ? (this._entered || this.props.isSelected() || this.props.Document.libraryBrush ? 1 : 0.1) : 1,
                     color: this.props.color ? this.props.color : this.props.hideOnLeave ? "white" : "inherit",
-                    pointerEvents: interactive ? "all" : "none",
+                    pointerEvents: interactive,
                     fontSize: "13px"
                 }}
                 onKeyDown={this.onKeyPress}
@@ -694,12 +695,11 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                 onPointerUp={this.onPointerUp}
                 onPointerDown={this.onPointerDown}
                 onMouseDown={this.onMouseDown}
-                // tfs: do we need this event handler
                 onWheel={this.onPointerWheel}
                 onPointerEnter={this.onPointerEnter}
                 onPointerLeave={this.onPointerLeave}
             >
-                <div className={`formattedTextBox-inner${rounded}`} ref={this.createDropTarget} style={{ whiteSpace: "pre-wrap", pointerEvents: this.props.Document.isButton && !this.props.isSelected() ? "none" : "all" }} />
+                <div className={`formattedTextBox-inner${rounded}`} ref={this.createDropTarget} style={{ whiteSpace: "pre-wrap" }} />
             </div>
         );
     }
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<FieldViewProps, ImageDocument>(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 <abdullah_ahmed@brown.edu>
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<string, any>(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 (
+            <div>Recommender System!!!</div>
+        )
+    }
+
+}
\ 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<FieldViewProps, ImageDocument>(ImageD
         let data = StrCast(this.dataDoc.title);
         console.log(data);
         let converter = (results: any) => {
+            let keyterms = new List<string>();
             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 <abdullah_ahmed@brown.edu>
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<D> = { converter: BodyConverter<D>, requester: RequestExecutor, analyzer: AnalysisApplier };
 type RequestExecutor = (apiKey: string, body: string, service: Service) => Promise<string>;
@@ -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<FieldViewProps, ImageDocument>(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<any> {
+    private loadModel(): Promise<any> {
         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 <abdullah_ahmed@brown.edu>
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<number[]>;
+
+    constructor() {
+        //console.log("creating client recommender...");
+        ClientRecommender.Instance = this;
+        this.docVectors = new Set<number[]>();
+    }
+
+
+    /***
+     * 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<number[]>) {
+        const n = 200;
+        const num_words = paragraph.size;
+        let meanVector = new Array<number>(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<string>();
+            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<number[]> = 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<D> = { converter: BodyConverter<D>, requester: RequestExecutor, analyzer: AnalysisApplier };
 type RequestExecutor = (apiKey: string, body: string, service: Service) => Promise<string>;
@@ -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<number[]>();
+                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 {
         </div >;
     }
 
+    // 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<FieldViewProps, ImageDocument>(ImageD
     }
 
     extractText = () => {
-        let data = StrCast(this.dataDoc.title);
-        console.log(data);
-        let converter = (results: any) => {
-            let keyterms = new List<string>();
-            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<any> {
         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

