diff options
-rw-r--r-- | src/client/ClientRecommender.tsx | 83 | ||||
-rw-r--r-- | src/client/cognitive_services/CognitiveServices.ts | 24 | ||||
-rw-r--r-- | src/client/views/MainView.tsx | 1 | ||||
-rw-r--r-- | src/client/views/Recommendations.tsx | 21 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 21 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 26 | ||||
-rw-r--r-- | src/client/views/nodes/ImageBox.tsx | 16 |
7 files changed, 104 insertions, 88 deletions
diff --git a/src/client/ClientRecommender.tsx b/src/client/ClientRecommender.tsx index 63f85c737..a6d1a32b3 100644 --- a/src/client/ClientRecommender.tsx +++ b/src/client/ClientRecommender.tsx @@ -1,5 +1,5 @@ import { Doc } from "../new_fields/Doc"; -import { StrCast } from "../new_fields/Types"; +import { StrCast, Cast } from "../new_fields/Types"; import { List } from "../new_fields/List"; import { CognitiveServices } from "./cognitive_services/CognitiveServices"; import React = require("react"); @@ -8,30 +8,42 @@ import { observable, action, computed, reaction } from "mobx"; var assert = require('assert'); import "./ClientRecommender.scss"; import { JSXElement } from "babel-types"; +import { ToPlainText, RichTextField } from "../new_fields/RichTextField"; export interface RecommenderProps { title: string; } +export interface RecommenderDocument { + actualDoc: Doc; + vectorDoc: number[]; + score: number; +} + @observer export class ClientRecommender extends React.Component<RecommenderProps> { static Instance: ClientRecommender; - private docVectors: Set<number[]>; + private mainDoc?: RecommenderDocument; + private docVectors: Set<RecommenderDocument> = new Set(); @observable private corr_matrix = [[0, 0], [0, 0]]; constructor(props: RecommenderProps) { //console.log("creating client recommender..."); super(props); if (!ClientRecommender.Instance) ClientRecommender.Instance = this; - this.docVectors = new Set<number[]>(); - //this.corr_matrix = [[0, 0], [0, 0]]; + ClientRecommender.Instance.docVectors = new Set(); + //ClientRecommender.Instance.corr_matrix = [[0, 0], [0, 0]]; } @action public reset_docs() { - this.docVectors = new Set(); - this.corr_matrix = [[0, 0], [0, 0]]; + ClientRecommender.Instance.docVectors = new Set(); + ClientRecommender.Instance.corr_matrix = [[0, 0], [0, 0]]; + } + + public deleteDocs() { + console.log("deleting previews..."); } /*** @@ -67,11 +79,24 @@ export class ClientRecommender extends React.Component<RecommenderProps> { } } + public computeSimilarities() { + ClientRecommender.Instance.docVectors.forEach((doc: RecommenderDocument) => { + if (ClientRecommender.Instance.mainDoc) { + const distance = ClientRecommender.Instance.distance(ClientRecommender.Instance.mainDoc.vectorDoc, doc.vectorDoc, "euclidian"); + doc.score = distance; + } + } + ); + let doclist = Array.from(ClientRecommender.Instance.docVectors); + doclist.sort((a: RecommenderDocument, b: RecommenderDocument) => a.score - b.score); + return doclist; + } + /*** * Computes the mean of a set of vectors */ - public mean(paragraph: Set<number[]>) { + public mean(paragraph: Set<number[]>, dataDoc: Doc, mainDoc: boolean) { const n = 200; const num_words = paragraph.size; let meanVector = new Array<number>(n).fill(0); // mean vector @@ -82,14 +107,16 @@ export class ClientRecommender extends React.Component<RecommenderProps> { } }); meanVector = meanVector.map(x => x / num_words); - this.addToDocSet(meanVector); + const internalDoc: RecommenderDocument = { actualDoc: dataDoc, vectorDoc: meanVector, score: 0 }; + if (mainDoc) ClientRecommender.Instance.mainDoc = internalDoc; + ClientRecommender.Instance.addToDocSet(internalDoc); } return meanVector; } - private addToDocSet(vector: number[]) { - if (this.docVectors) { - this.docVectors.add(vector); + private addToDocSet(internalDoc: RecommenderDocument) { + if (ClientRecommender.Instance.docVectors) { + ClientRecommender.Instance.docVectors.add(internalDoc); } } @@ -97,9 +124,11 @@ export class ClientRecommender extends React.Component<RecommenderProps> { * Uses Cognitive Services to extract keywords from a document */ - public async extractText(dataDoc: Doc, extDoc: Doc) { - let data = StrCast(dataDoc.title); - //console.log(data); + public async extractText(dataDoc: Doc, extDoc: Doc, mainDoc: boolean = false) { + let fielddata = Cast(dataDoc.data, RichTextField); + let data: string; + fielddata ? data = fielddata[ToPlainText]() : data = ""; + console.log(data); let converter = (results: any) => { let keyterms = new List<string>(); results.documents.forEach((doc: any) => { @@ -108,7 +137,7 @@ export class ClientRecommender extends React.Component<RecommenderProps> { }); return keyterms; }; - await CognitiveServices.Text.Appliers.analyzer(extDoc, ["key words"], data, converter); + await CognitiveServices.Text.Appliers.analyzer(dataDoc, extDoc, ["key words"], data, converter, mainDoc); } /*** @@ -116,7 +145,7 @@ export class ClientRecommender extends React.Component<RecommenderProps> { */ @action - public createDistanceMatrix(documents: Set<number[]> = this.docVectors) { + public createDistanceMatrix(documents: Set<RecommenderDocument> = ClientRecommender.Instance.docVectors) { const documents_list = Array.from(documents); const n = documents_list.length; var matrix = new Array<number>(n).fill(0).map(() => new Array<number>(n).fill(0)); @@ -124,22 +153,22 @@ export class ClientRecommender extends React.Component<RecommenderProps> { var doc1 = documents_list[i]; for (let j = 0; j < n; j++) { var doc2 = documents_list[j]; - matrix[i][j] = this.distance(doc1, doc2, "euclidian"); + matrix[i][j] = ClientRecommender.Instance.distance(doc1.vectorDoc, doc2.vectorDoc, "euclidian"); } } - this.corr_matrix = matrix; + ClientRecommender.Instance.corr_matrix = matrix; return matrix; } @computed private get generateRows() { - const n = this.corr_matrix.length; + const n = ClientRecommender.Instance.corr_matrix.length; let rows: JSX.Element[] = []; for (let i = 0; i < n; i++) { let children: JSX.Element[] = []; for (let j = 0; j < n; j++) { - //let cell = React.createElement("td", this.corr_matrix[i][j]); - let cell = <td>{this.corr_matrix[i][j].toFixed(4)}</td>; + //let cell = React.createElement("td", ClientRecommender.Instance.corr_matrix[i][j]); + let cell = <td>{ClientRecommender.Instance.corr_matrix[i][j].toFixed(4)}</td>; children.push(cell); } //let row = React.createElement("tr", { children: children, key: i }); @@ -151,22 +180,22 @@ export class ClientRecommender extends React.Component<RecommenderProps> { render() { return (<div className="wrapper"> - <h3 >{this.props.title ? this.props.title : "hello"}</h3> + <h3 >{ClientRecommender.Instance.props.title ? ClientRecommender.Instance.props.title : "hello"}</h3> {/* <table className="space" > <tbody> <tr key="1"> - <td key="1">{this.corr_matrix[0][0].toFixed(4)}</td> - <td key="2">{this.corr_matrix[0][1].toFixed(4)}</td> + <td key="1">{ClientRecommender.Instance.corr_matrix[0][0].toFixed(4)}</td> + <td key="2">{ClientRecommender.Instance.corr_matrix[0][1].toFixed(4)}</td> </tr> <tr key="2"> - <td key="1">{this.corr_matrix[1][0].toFixed(4)}</td> - <td key="2">{this.corr_matrix[1][1].toFixed(4)}</td> + <td key="1">{ClientRecommender.Instance.corr_matrix[1][0].toFixed(4)}</td> + <td key="2">{ClientRecommender.Instance.corr_matrix[1][1].toFixed(4)}</td> </tr> </tbody> </table> */} <table className="space"> <tbody> - {this.generateRows} + {ClientRecommender.Instance.generateRows} </tbody> </table> </div>); diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts index 75d0760ed..874ee433d 100644 --- a/src/client/cognitive_services/CognitiveServices.ts +++ b/src/client/cognitive_services/CognitiveServices.ts @@ -263,29 +263,35 @@ export namespace CognitiveServices { export namespace Appliers { - export async function vectorize(keyterms: any) { + export async function vectorize(keyterms: any, dataDoc: Doc, mainDoc: boolean = false) { console.log("vectorizing..."); //keyterms = ["father", "king"]; let args = { method: 'POST', uri: Utils.prepend("/recommender"), body: { keyphrases: keyterms }, json: true }; 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); + if (wordvecs.length > 0) { + console.log("successful vectorization!"); + var vectorValues = new Set<number[]>(); + wordvecs.forEach((wordvec: any) => { + //console.log(wordvec.word); + vectorValues.add(wordvec.values as number[]); + }); + ClientRecommender.Instance.mean(vectorValues, dataDoc, mainDoc); + } // adds document to internal doc set + else { + console.log("unsuccessful :( word(s) not in vocabulary"); + } //console.log(vectorValues.size); }); } - export const analyzer = async (target: Doc, keys: string[], data: string, converter: Converter) => { + export const analyzer = async (dataDoc: Doc, target: Doc, keys: string[], data: string, converter: Converter, mainDoc: boolean = false) => { let results = await ExecuteQuery(Service.Text, Manager, data); console.log(results); let keyterms = converter(results); //target[keys[0]] = Docs.Get.DocumentHierarchyFromJson(results, "Key Word Analysis"); target[keys[0]] = keyterms; console.log("analyzed!"); - await vectorize(keyterms); + await vectorize(keyterms, dataDoc, mainDoc); }; } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 57eb30439..3a5795077 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -204,6 +204,7 @@ export class MainView extends React.Component { const targets = document.elementsFromPoint(e.x, e.y); if (targets && targets.length && targets[0].className.toString().indexOf("contextMenu") === -1) { ContextMenu.Instance.closeMenu(); + Recommendations.Instance.closeMenu(); } }); diff --git a/src/client/views/Recommendations.tsx b/src/client/views/Recommendations.tsx index 8569996b3..cf1974c69 100644 --- a/src/client/views/Recommendations.tsx +++ b/src/client/views/Recommendations.tsx @@ -10,8 +10,10 @@ import { returnFalse, emptyFunction, returnEmptyString, returnOne } from "../../ import { Transform } from "../util/Transform"; import { ObjectField } from "../../new_fields/ObjectField"; import { DocumentView } from "./nodes/DocumentView"; -import { DocumentType } from "../documents/Documents"; - +import { DocumentType } from '../documents/DocumentTypes'; +import { ClientRecommender } from "../ClientRecommender"; +import { DocServer } from "../DocServer"; +import { Id } from "../../new_fields/FieldSymbols"; export interface RecProps { documents: { preview: Doc, similarity: number }[]; @@ -28,6 +30,7 @@ export class Recommendations extends React.Component<{}> { @observable private _width: number = 0; @observable private _height: number = 0; @observable private _documents: { preview: Doc, score: number }[] = []; + private previewDocs: Doc[] = []; constructor(props: {}) { super(props); @@ -52,7 +55,8 @@ export class Recommendations extends React.Component<{}> { let returnXDimension = () => 50; let returnYDimension = () => 50; let scale = () => returnXDimension() / NumCast(renderDoc.nativeWidth, returnXDimension()); - let newRenderDoc = Doc.MakeDelegate(renderDoc); /// newRenderDoc -> renderDoc -> render"data"Doc -> TextProt + //let scale = () => 1; + //let newRenderDoc = Doc.MakeDelegate(renderDoc); /// newRenderDoc -> renderDoc -> render"data"Doc -> TextProt const docview = <div> {/* onPointerDown={action(() => { this._useIcons = !this._useIcons; @@ -62,7 +66,7 @@ export class Recommendations extends React.Component<{}> { onPointerLeave={action(() => this._displayDim = 50)} > */} <DocumentView fitToBox={StrCast(doc.type).indexOf(DocumentType.COL) !== -1} - Document={doc} + Document={renderDoc} addDocument={returnFalse} removeDocument={returnFalse} ScreenToLocalTransform={Transform.Identity} @@ -82,9 +86,10 @@ export class Recommendations extends React.Component<{}> { ContentScaling={scale} /> </div>; - const data = renderDoc.data; - if (data instanceof ObjectField) newRenderDoc.data = ObjectField.MakeCopy(data); - newRenderDoc.preview = true; + // const data = renderDoc.data; + // if (data instanceof ObjectField) newRenderDoc.data = ObjectField.MakeCopy(data); + // newRenderDoc.preview = true; + // this.previewDocs.push(newRenderDoc); return docview; } @@ -92,6 +97,8 @@ export class Recommendations extends React.Component<{}> { @action closeMenu = () => { this._display = false; + this.previewDocs.forEach(doc => DocServer.DeleteDocument(doc[Id])); + this.previewDocs = []; } @action diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 2d4775070..3cef93383 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -857,27 +857,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { input.click(); } }); - ContextMenu.Instance.addItem({ - description: "Recommender System", - event: async () => { - // if (!ClientRecommender.Instance) new ClientRecommender({ title: "Client Recommender" }); - let activedocs = this.getActiveDocuments(); - let allDocs = await SearchUtil.GetAllDocs(); - allDocs.forEach(doc => console.log(doc.title)); - // clears internal representation of documents as vectors - ClientRecommender.Instance.reset_docs(); - await Promise.all(allDocs.map((doc: Doc) => { - console.log(StrCast(doc.title)); - if (doc.type === DocumentType.IMG) { - console.log(doc.title); - const extdoc = doc.data_ext as Doc; - return ClientRecommender.Instance.extractText(doc, extdoc ? extdoc : doc); - } - })); - console.log(ClientRecommender.Instance.createDistanceMatrix()); - }, - icon: "brain" - }); layoutItems.push({ description: `${this.fitToBox ? "Unset" : "Set"} Fit To Container`, event: this.fitToContainer, icon: !this.fitToBox ? "expand-arrows-alt" : "compress-arrows-alt" }); layoutItems.push({ description: "reset view", event: () => { this.props.Document.panX = this.props.Document.panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" }); layoutItems.push({ diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 2a6e91272..f708a7a3a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -648,21 +648,31 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu if (!ClientRecommender.Instance) new ClientRecommender({ title: "Client Recommender" }); let documents: Doc[] = []; let allDocs = await SearchUtil.GetAllDocs(); - allDocs.forEach(doc => console.log(doc.title)); + //allDocs.forEach(doc => console.log(doc.title)); // clears internal representation of documents as vectors ClientRecommender.Instance.reset_docs(); await Promise.all(allDocs.map((doc: Doc) => { - if (doc.type === DocumentType.IMG) { - console.log(StrCast(doc.title)); - documents.push(doc); - const extdoc = doc.data_ext as Doc; - return ClientRecommender.Instance.extractText(doc, extdoc ? extdoc : doc); + let mainDoc: boolean = false; + const dataDoc = Doc.GetDataDoc(doc); + if (doc.type === DocumentType.TEXT) { + if (dataDoc === Doc.GetDataDoc(this.props.Document)) { + mainDoc = true; + console.log(StrCast(doc.title)); + } + if (!documents.includes(dataDoc)) { + documents.push(dataDoc); + const extdoc = doc.data_ext as Doc; + return ClientRecommender.Instance.extractText(doc, extdoc ? extdoc : doc, mainDoc); + } } })); console.log(ClientRecommender.Instance.createDistanceMatrix()); + const doclist = ClientRecommender.Instance.computeSimilarities(); let recDocs: { preview: Doc, score: number }[] = []; - for (let i = 0; i < documents.length; i++) { - recDocs.push({ preview: documents[i], score: i }); + // tslint:disable-next-line: prefer-for-of + for (let i = 0; i < doclist.length; i++) { + console.log(doclist[i].score); + recDocs.push({ preview: doclist[i].actualDoc, score: doclist[i].score }); } Recommendations.Instance.addDocuments(recDocs); Recommendations.Instance.displayRecommendations(e.pageX + 100, e.pageY); diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index ec35465eb..d94e92847 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -240,22 +240,6 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD } } - extractText = async () => { - //let activedocs = this.getActiveDocuments(); - let allDocs = await SearchUtil.GetAllDocs(); - allDocs.forEach(doc => console.log(doc.title)); - // clears internal representation of documents as vectors - ClientRecommender.Instance.reset_docs(); - await Promise.all(allDocs.map((doc: Doc) => { - //console.log(StrCast(doc.title)); - if (doc.type === DocumentType.IMG) { - const extdoc = doc.data_ext as Doc; - return ClientRecommender.Instance.extractText(doc, extdoc ? extdoc : doc); - } - })); - console.log(ClientRecommender.Instance.createDistanceMatrix()); - } - generateMetadata = (threshold: Confidence = Confidence.Excellent) => { let converter = (results: any) => { let tagDoc = new Doc; |