From af59d641022119e25402f1f13ae2c3f3eb4c20a2 Mon Sep 17 00:00:00 2001
From: ab <abdullah_ahmed@brown.edu>
Date: Mon, 16 Sep 2019 14:39:44 -0400
Subject: initial commit

---
 src/client/views/Recommendations.tsx | 172 +++++++++++++++++++----------------
 1 file changed, 92 insertions(+), 80 deletions(-)

(limited to 'src/client/views/Recommendations.tsx')

diff --git a/src/client/views/Recommendations.tsx b/src/client/views/Recommendations.tsx
index d0105ee18..ff6e66492 100644
--- a/src/client/views/Recommendations.tsx
+++ b/src/client/views/Recommendations.tsx
@@ -14,27 +14,37 @@ import { DocumentType } from '../documents/DocumentTypes';
 import { ClientRecommender } from "../ClientRecommender";
 import { DocServer } from "../DocServer";
 import { Id } from "../../new_fields/FieldSymbols";
+import { FieldView, FieldViewProps } from "./nodes/FieldView";
+import { DocumentManager } from "../util/DocumentManager";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { library } from "@fortawesome/fontawesome-svg-core";
+import { faBullseye, faLink } from "@fortawesome/free-solid-svg-icons";
+import { DocUtils } from "../documents/Documents";
 
 export interface RecProps {
     documents: { preview: Doc, similarity: number }[];
     node: Doc;
 }
 
+library.add(faBullseye, faLink);
+
 @observer
-export class Recommendations extends React.Component<{}> {
+export class RecommendationsBox extends React.Component<FieldViewProps> {
+
+    public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(RecommendationsBox, fieldKey); }
 
-    static Instance: Recommendations;
-    @observable private _display: boolean = false;
+    static Instance: RecommendationsBox;
+    // @observable private _display: boolean = false;
     @observable private _pageX: number = 0;
     @observable private _pageY: number = 0;
     @observable private _width: number = 0;
     @observable private _height: number = 0;
-    @observable private _documents: { preview: Doc, score: number }[] = [];
+    // @observable private _documents: { preview: Doc, score: number }[] = [];
     private previewDocs: Doc[] = [];
 
-    constructor(props: {}) {
+    constructor(props: FieldViewProps) {
         super(props);
-        Recommendations.Instance = this;
+        RecommendationsBox.Instance = this;
     }
 
     private DocumentIcon(doc: Doc) {
@@ -52,12 +62,12 @@ export class Recommendations extends React.Component<{}> {
                 };
             }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE });
         }
-        let returnXDimension = () => 50;
-        let returnYDimension = () => 50;
+        let returnXDimension = () => 150;
+        let returnYDimension = () => 150;
         let scale = () => returnXDimension() / NumCast(renderDoc.nativeWidth, returnXDimension());
         //let scale = () => 1;
         let newRenderDoc = Doc.MakeAlias(renderDoc); ///   newRenderDoc -> renderDoc -> render"data"Doc -> TextProt
-        newRenderDoc.height = 50;
+        newRenderDoc.height = NumCast(this.props.Document.documentIconHeight);
         newRenderDoc.autoHeight = false;
         const docview = <div>
             {/* onPointerDown={action(() => {
@@ -78,7 +88,8 @@ export class Recommendations extends React.Component<{}> {
                 PanelHeight={returnYDimension}
                 focus={emptyFunction}
                 backgroundColor={returnEmptyString}
-                selectOnLoad={false}
+                // selectOnLoad={false}
+                pinToPres={emptyFunction}
                 parentActive={returnFalse}
                 whenActiveChanged={returnFalse}
                 bringToFront={emptyFunction}
@@ -96,83 +107,84 @@ export class Recommendations extends React.Component<{}> {
 
     }
 
-    @action
-    closeMenu = () => {
-        this._display = false;
-        this.previewDocs.forEach(doc => DocServer.DeleteDocument(doc[Id]));
-        this.previewDocs = [];
-    }
-
-    @action
-    resetDocuments = () => {
-        this._documents = [];
-    }
-
-    @action
-    addDocuments = (documents: { preview: Doc, score: number }[]) => {
-        this._documents = documents;
-    }
-
-    @action
-    displayRecommendations(x: number, y: number) {
-        this._pageX = x;
-        this._pageY = y;
-        this._display = true;
-    }
+    // @action
+    // closeMenu = () => {
+    //     this._display = false;
+    //     this.previewDocs.forEach(doc => DocServer.DeleteDocument(doc[Id]));
+    //     this.previewDocs = [];
+    // }
+
+    // @action
+    // resetDocuments = () => {
+    //     this._documents = [];
+    // }
+
+    // @action
+    // displayRecommendations(x: number, y: number) {
+    //     this._pageX = x;
+    //     this._pageY = y;
+    //     this._display = true;
+    // }
 
     static readonly buffer = 20;
 
-    get pageX() {
-        const x = this._pageX;
-        if (x < 0) {
-            return 0;
-        }
-        const width = this._width;
-        if (x + width > window.innerWidth - Recommendations.buffer) {
-            return window.innerWidth - Recommendations.buffer - width;
-        }
-        return x;
-    }
-
-    get pageY() {
-        const y = this._pageY;
-        if (y < 0) {
-            return 0;
-        }
-        const height = this._height;
-        if (y + height > window.innerHeight - Recommendations.buffer) {
-            return window.innerHeight - Recommendations.buffer - height;
-        }
-        return y;
-    }
+    // get pageX() {
+    //     const x = this._pageX;
+    //     if (x < 0) {
+    //         return 0;
+    //     }
+    //     const width = this._width;
+    //     if (x + width > window.innerWidth - RecommendationsBox.buffer) {
+    //         return window.innerWidth - RecommendationsBox.buffer - width;
+    //     }
+    //     return x;
+    // }
+
+    // get pageY() {
+    //     const y = this._pageY;
+    //     if (y < 0) {
+    //         return 0;
+    //     }
+    //     const height = this._height;
+    //     if (y + height > window.innerHeight - RecommendationsBox.buffer) {
+    //         return window.innerHeight - RecommendationsBox.buffer - height;
+    //     }
+    //     return y;
+    // }
 
     render() {
-        if (!this._display) {
-            return null;
-        }
-        let style = { left: this.pageX, top: this.pageY };
+        // if (!this._display) {
+        //     return null;
+        // }
+        // let style = { left: this.pageX, top: this.pageY };
         //const transform = "translate(" + (NumCast(this.props.node.x) + 350) + "px, " + NumCast(this.props.node.y) + "px"
         return (
-            <Measure offset onResize={action((r: any) => { this._width = r.offset.width; this._height = r.offset.height; })}>
-                {({ measureRef }) => (
-                    <div className="rec-scroll" style={style} ref={measureRef}>
-                        <p>Recommendations</p>
-                        {this._documents.map(doc => {
-                            return (
-                                <div className="content">
-                                    <span className="image-background">
-                                        {this.DocumentIcon(doc.preview)}
-                                    </span>
-                                    <span className="score">{doc.score.toFixed(4)}</span>
-                                </div>
-                            );
-                        })}
-
-                    </div>
-                )
-                }
-
-            </Measure>
+            // <Measure offset onResize={action((r: any) => { this._width = r.offset.width; this._height = r.offset.height; })}>
+            // {({ measureRef }) => (
+            <div className="rec-scroll">
+                <p>Recommendations</p>
+                {DocListCast(this.props.Document.data).map(doc => {
+                    return (
+                        <div className="content">
+                            <span style={{ height: NumCast(this.props.Document.documentIconHeight) }} className="image-background">
+                                {this.DocumentIcon(doc)}
+                            </span>
+                            <span className="score">{NumCast(doc.score).toFixed(4)}</span>
+                            <div style={{ marginRight: 50 }} onClick={() => DocumentManager.Instance.jumpToDocument(doc, true, undefined, undefined, undefined, this.props.Document.sourceDocContext as Doc)}>
+                                <FontAwesomeIcon className="documentdecorations-icon" icon={"bullseye"} size="sm" />
+                            </div>
+                            <div style={{ marginRight: 50 }} onClick={() => DocUtils.MakeLink(this.props.Document.sourceDoc as Doc, doc, undefined, "User Selected Link", "Generated from Recommender", undefined)}>
+                                <FontAwesomeIcon className="documentdecorations-icon" icon={"link"} size="sm" />
+                            </div>
+                        </div>
+                    );
+                })}
+
+            </div>
+            // );
+            // }
+
+            // </Measure>
         );
     }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 19375927c677ad6c99c77d0c7dac17fe7a2712a9 Mon Sep 17 00:00:00 2001
From: ab <abdullah_ahmed@brown.edu>
Date: Mon, 16 Sep 2019 15:26:36 -0400
Subject: beginning to handle external interactions

---
 src/client/ClientRecommender.tsx                   | 26 +++++++++++++---------
 src/client/cognitive_services/CognitiveServices.ts | 12 ++++++----
 src/client/views/Recommendations.tsx               |  6 ++++-
 .../views/collections/CollectionSchemaCells.tsx    | 16 ++++++++++++-
 src/client/views/nodes/DocumentView.tsx            |  7 +++++-
 5 files changed, 49 insertions(+), 18 deletions(-)

(limited to 'src/client/views/Recommendations.tsx')

diff --git a/src/client/ClientRecommender.tsx b/src/client/ClientRecommender.tsx
index 217c89297..551047df0 100644
--- a/src/client/ClientRecommender.tsx
+++ b/src/client/ClientRecommender.tsx
@@ -139,7 +139,7 @@ export class ClientRecommender extends React.Component<RecommenderProps> {
         let fielddata = Cast(dataDoc.data, RichTextField);
         let data: string;
         fielddata ? data = fielddata[ToPlainText]() : data = "";
-        let converter = (results: any, data: string) => {
+        let converter = async (results: any, data: string) => {
             let keyterms = new List<string>(); // raw keywords
             let keyterms_counted = new List<string>(); // keywords, where each keyword is repeated as 
             let highKP: string[] = [""]; // most frequent 
@@ -167,10 +167,10 @@ export class ClientRecommender extends React.Component<RecommenderProps> {
             });
             this.highKP = highKP;
             console.log(highKP);
-            this.sendRequest(highKP);
-            return { keyterms: keyterms, keyterms_counted: keyterms_counted };
+            const values = await this.sendRequest(highKP);
+            return { keyterms: keyterms, keyterms_counted: keyterms_counted, values };
         };
-        await CognitiveServices.Text.Appliers.analyzer(dataDoc, extDoc, ["key words"], data, converter, mainDoc, internal);
+        return CognitiveServices.Text.Appliers.analyzer(dataDoc, extDoc, ["key words"], data, converter, mainDoc, internal);
     }
 
     private countFrequencies(keyphrase: string, paragraph: string) {
@@ -198,7 +198,9 @@ export class ClientRecommender extends React.Component<RecommenderProps> {
     private async sendRequest(keywords: string[]) {
         let query = "";
         keywords.forEach((kp: string) => query += " " + kp);
-        await this.arxivrequest(query);
+        return new Promise<any>(resolve => {
+            this.arxivrequest(query).then(resolve);
+        });
     }
 
     /**
@@ -207,7 +209,7 @@ export class ClientRecommender extends React.Component<RecommenderProps> {
 
     arxivrequest = async (query: string) => {
         let xhttp = new XMLHttpRequest();
-        let serveraddress = "http://export.arxiv.org/api"
+        let serveraddress = "http://export.arxiv.org/api";
         let endpoint = serveraddress + "/query?search_query=all:" + query + "&start=0&max_results=1";
         let promisified = (resolve: any, reject: any) => {
             xhttp.onreadystatechange = function () {
@@ -217,20 +219,22 @@ export class ClientRecommender extends React.Component<RecommenderProps> {
                     console.log(xml);
                     switch (this.status) {
                         case 200:
+                            let title: string = "Title";
+                            let url: string = "Url";
                             //console.log(result);
                             if (xml) {
                                 let titles = xml.getElementsByTagName("title");
                                 if (titles && titles.length > 1) {
-                                    let text = titles[1].childNodes[0].nodeValue;
-                                    console.log(text);
+                                    title = titles[1].childNodes[0].nodeValue!;
+                                    console.log(title);
                                 }
                                 let ids = xml.getElementsByTagName("id");
                                 if (ids && ids.length > 1) {
-                                    let text = ids[1].childNodes[0].nodeValue;
-                                    console.log(text);
+                                    url = ids[1].childNodes[0].nodeValue!;
+                                    console.log(url);
                                 }
                             }
-                            return resolve(result);
+                            return resolve({ title, url });
                         case 400:
                         default:
                             return reject(result);
diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts
index 8a58355a8..baafb63a1 100644
--- a/src/client/cognitive_services/CognitiveServices.ts
+++ b/src/client/cognitive_services/CognitiveServices.ts
@@ -15,7 +15,7 @@ type RequestExecutor = (apiKey: string, body: string, service: Service) => Promi
 type AnalysisApplier<D> = (target: Doc, relevantKeys: string[], data: D, ...args: any) => any;
 type BodyConverter<D> = (data: D) => string;
 type Converter = (results: any) => Field;
-type TextConverter = (results: any, data: string) => { keyterms: Field, keyterms_counted: Field };
+type TextConverter = (results: any, data: string) => Promise<{ keyterms: Field, keyterms_counted: Field, values: any }>;
 
 export type Tag = { name: string, confidence: number };
 export type Rectangle = { top: number, left: number, width: number, height: number };
@@ -288,11 +288,15 @@ export namespace CognitiveServices {
             export const analyzer = async (dataDoc: Doc, target: Doc, keys: string[], data: string, converter: TextConverter, mainDoc: boolean = false, internal: boolean = true) => {
                 let results = await ExecuteQuery(Service.Text, Manager, data);
                 console.log(results);
-                let keyterms = converter(results, data);
+                let { keyterms, values, keyterms_counted } = await converter(results, data);
                 //target[keys[0]] = Docs.Get.DocumentHierarchyFromJson(results, "Key Word Analysis");
-                target[keys[0]] = keyterms.keyterms;
+                target[keys[0]] = keyterms;
                 console.log("analyzed!");
-                if (internal) await vectorize(keyterms.keyterms_counted, dataDoc, mainDoc, data);
+                if (internal) {
+                    await vectorize(keyterms_counted, dataDoc, mainDoc, data);
+                } else {
+                    return values;
+                }
             };
 
             // export async function countFrequencies() 
diff --git a/src/client/views/Recommendations.tsx b/src/client/views/Recommendations.tsx
index ff6e66492..b7b1d84d0 100644
--- a/src/client/views/Recommendations.tsx
+++ b/src/client/views/Recommendations.tsx
@@ -158,11 +158,15 @@ export class RecommendationsBox extends React.Component<FieldViewProps> {
         // }
         // let style = { left: this.pageX, top: this.pageY };
         //const transform = "translate(" + (NumCast(this.props.node.x) + 350) + "px, " + NumCast(this.props.node.y) + "px"
+        let title = StrCast((this.props.Document.sourceDoc as Doc).title);
+        if (title.length > 15) {
+            title = title.substring(0, 15) + "...";
+        }
         return (
             // <Measure offset onResize={action((r: any) => { this._width = r.offset.width; this._height = r.offset.height; })}>
             // {({ measureRef }) => (
             <div className="rec-scroll">
-                <p>Recommendations</p>
+                <p>Recommendations for "{title}"</p>
                 {DocListCast(this.props.Document.data).map(doc => {
                     return (
                         <div className="content">
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 9c26a08f0..bf8c4b6f7 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -27,6 +27,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
 import { KeyCodes } from "../../northstar/utils/KeyCodes";
 import { undoBatch } from "../../util/UndoManager";
+import { List } from "lodash";
 
 library.add(faExpand);
 
@@ -86,10 +87,23 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
     }
 
     @action
-    onPointerDown = (e: React.PointerEvent): void => {
+    onPointerDown = async (e: React.PointerEvent): Promise<void> => {
         this.props.changeFocusedCellByIndex(this.props.row, this.props.col);
         this.props.setPreviewDoc(this.props.rowProps.original);
 
+        const data = await DocListCastAsync(this.props.Document.data);
+        if (data) {
+            let url: string;
+            if (url = StrCast(data[0].href)) {
+                try {
+                    new URL(url);
+                    const temp = window.open(url)!;
+                    temp.blur();
+                    window.focus();
+                } catch { }
+            }
+        }
+
         // this._isEditing = true;
         // this.props.setIsEditing(true);
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 2ae71f1da..a034bc1f4 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -45,6 +45,7 @@ import { RecommendationsBox } from '../Recommendations';
 import { SearchUtil } from '../../util/SearchUtil';
 import { ClientRecommender } from '../../ClientRecommender';
 import { DocumentType } from '../../documents/DocumentTypes';
+import { SchemaHeaderField } from '../../../new_fields/SchemaHeaderField';
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
 library.add(fa.faBrain);
@@ -774,7 +775,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         ClientRecommender.Instance.reset_docs();
         const doc = Doc.GetDataDoc(this.props.Document);
         const extdoc = doc.data_ext as Doc;
-        return ClientRecommender.Instance.extractText(doc, extdoc ? extdoc : doc, false);
+        const values = await ClientRecommender.Instance.extractText(doc, extdoc ? extdoc : doc, false);
+        const headers = [new SchemaHeaderField("title"), new SchemaHeaderField("href")];
+        const body = Docs.Create.FreeformDocument([], { title: values.title });
+        body.href = values.url;
+        CollectionDockingView.Instance.AddRightSplit(Docs.Create.SchemaDocument(headers, [body], { title: `Showing External Recommendations for "${StrCast(doc.title)}"` }), undefined);
     }
 
     onPointerEnter = (e: React.PointerEvent): void => { Doc.BrushDoc(this.props.Document); };
-- 
cgit v1.2.3-70-g09d2

