aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Recommendations.scss21
-rw-r--r--src/Recommendations.tsx28
-rw-r--r--src/client/util/SearchUtil.ts7
-rw-r--r--src/client/views/MainView.tsx2
-rw-r--r--src/client/views/Recommendations.scss65
-rw-r--r--src/client/views/Recommendations.tsx169
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx13
-rw-r--r--src/client/views/nodes/DocumentView.tsx34
-rw-r--r--src/client/views/nodes/ImageBox.tsx21
9 files changed, 297 insertions, 63 deletions
diff --git a/src/Recommendations.scss b/src/Recommendations.scss
deleted file mode 100644
index 5129a59d9..000000000
--- a/src/Recommendations.scss
+++ /dev/null
@@ -1,21 +0,0 @@
-.recommendation-content *{
- display: inline-block;
- margin: auto;
- border: 1px dashed grey;
- padding: 2px 2px;
-}
-
-.recommendation-content {
- float: left;
- border: 1px solid green;
- width: 200px;
- align-content: center;
-}
-
-.rec-scroll {
- overflow-y: scroll;
- height: 300px;
- width: auto;
- position: absolute;
- background: #cdcdcd;
-} \ No newline at end of file
diff --git a/src/Recommendations.tsx b/src/Recommendations.tsx
deleted file mode 100644
index ca1123ef9..000000000
--- a/src/Recommendations.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import { observer } from "mobx-react";
-import React = require("react");
-import { Doc } from "./new_fields/Doc";
-import { NumCast } from "./new_fields/Types";
-
-export interface RecProps {
- documents: { preview: string, similarity: number }[],
- node: Doc;
-}
-
-@observer
-export class Recommendations extends React.Component<RecProps> {
- render() {
- const transform = "translate(" + (NumCast(this.props.node.x) + 350) + "px, " + NumCast(this.props.node.y) + "px"
- return (
- <div className="rec-scroll" style={{ transform: transform }}>
- {this.props.documents.map(doc => {
- return (
- <div className="recommendation-content">
- <img src={doc.preview} />
- <div>{doc.similarity}</div>
- </div>
- )
- })}
- </div>
- )
- }
-} \ No newline at end of file
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index 1fce995d7..85e593529 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -81,12 +81,13 @@ export namespace SearchUtil {
export async function GetAllDocs() {
const query = "*";
let response = await rp.get(Utils.prepend('/search'), {
- qs: {
- q: query
- }
+ qs:
+ { start: 0, rows: 10000, q: query },
+
});
let result: IdSearchResult = JSON.parse(response);
const { ids, numFound, highlighting } = result;
+ console.log(ids.length);
const docMap = await DocServer.GetRefFields(ids);
const docs: Doc[] = [];
for (const id of ids) {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 419b15697..0b6fe3876 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -41,6 +41,7 @@ import { ClientUtils } from '../util/ClientUtils';
import { SchemaHeaderField, RandomPastel } from '../../new_fields/SchemaHeaderField';
//import { DocumentManager } from '../util/DocumentManager';
import { DictationManager } from '../util/DictationManager';
+import { Recommendations } from './Recommendations';
@observer
export class MainView extends React.Component {
@@ -581,6 +582,7 @@ export class MainView extends React.Component {
{this.mainContent}
<PreviewCursor />
<ContextMenu />
+ <Recommendations />
{this.nodesMenu()}
{this.miscButtons}
<PDFMenu />
diff --git a/src/client/views/Recommendations.scss b/src/client/views/Recommendations.scss
new file mode 100644
index 000000000..5d8f17e37
--- /dev/null
+++ b/src/client/views/Recommendations.scss
@@ -0,0 +1,65 @@
+@import "globalCssVariables";
+
+.rec-content *{
+ display: inline-block;
+ margin: auto;
+ width: 50;
+ height: 30px;
+ border: 1px dashed grey;
+ padding: 10px 10px;
+}
+
+.rec-content {
+ float: left;
+ width: inherit;
+ align-content: center;
+}
+
+.rec-scroll {
+ overflow-y: scroll;
+ overflow-x: hidden;
+ position: absolute;
+ // display: flex;
+ z-index: 10000;
+ box-shadow: gray 0.2vw 0.2vw 0.4vw;
+ // flex-direction: column;
+ background: whitesmoke;
+ padding-bottom: 10px;
+ border-radius: 15px;
+ border: solid #BBBBBBBB 1px;
+ width: 200px;
+ text-align: center;
+ max-height: 250px;
+ text-transform: uppercase;
+ color: grey;
+ letter-spacing: 2px;
+}
+
+.content {
+ padding: 10px;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+}
+
+.image-background {
+ pointer-events: none;
+ background-color: transparent;
+ width: 50%;
+ text-align: center;
+ height: 35px;
+ margin-left: 5px;
+}
+
+img{
+ width: 100%;
+ height: 100%;
+}
+
+.score {
+ // margin-left: 15px;
+ width: 50%;
+ height: 100%;
+ text-align: center;
+}
diff --git a/src/client/views/Recommendations.tsx b/src/client/views/Recommendations.tsx
new file mode 100644
index 000000000..8569996b3
--- /dev/null
+++ b/src/client/views/Recommendations.tsx
@@ -0,0 +1,169 @@
+import { observer } from "mobx-react";
+import React = require("react");
+import { observable, action } from "mobx";
+import Measure from "react-measure";
+import "./Recommendations.scss";
+import { Doc, DocListCast, WidthSym, HeightSym } from "../../new_fields/Doc";
+import { DocumentIcon } from "./nodes/DocumentIcon";
+import { StrCast, NumCast } from "../../new_fields/Types";
+import { returnFalse, emptyFunction, returnEmptyString, returnOne } from "../../Utils";
+import { Transform } from "../util/Transform";
+import { ObjectField } from "../../new_fields/ObjectField";
+import { DocumentView } from "./nodes/DocumentView";
+import { DocumentType } from "../documents/Documents";
+
+
+export interface RecProps {
+ documents: { preview: Doc, similarity: number }[];
+ node: Doc;
+}
+
+@observer
+export class Recommendations extends React.Component<{}> {
+
+ static Instance: Recommendations;
+ @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 }[] = [];
+
+ constructor(props: {}) {
+ super(props);
+ Recommendations.Instance = this;
+ }
+
+ private DocumentIcon(doc: Doc) {
+ let layoutresult = StrCast(doc.type);
+ let renderDoc = doc;
+ //let box: number[] = [];
+ if (layoutresult.indexOf(DocumentType.COL) !== -1) {
+ renderDoc = Doc.MakeDelegate(renderDoc);
+ let bounds = DocListCast(renderDoc.data).reduce((bounds, doc) => {
+ var [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)];
+ let [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()];
+ return {
+ x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y),
+ r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b)
+ };
+ }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE });
+ }
+ let returnXDimension = () => 50;
+ let returnYDimension = () => 50;
+ let scale = () => returnXDimension() / NumCast(renderDoc.nativeWidth, returnXDimension());
+ let newRenderDoc = Doc.MakeDelegate(renderDoc); /// newRenderDoc -> renderDoc -> render"data"Doc -> TextProt
+ const docview = <div>
+ {/* onPointerDown={action(() => {
+ this._useIcons = !this._useIcons;
+ this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE);
+ })}
+ onPointerEnter={action(() => this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE))}
+ onPointerLeave={action(() => this._displayDim = 50)} > */}
+ <DocumentView
+ fitToBox={StrCast(doc.type).indexOf(DocumentType.COL) !== -1}
+ Document={doc}
+ addDocument={returnFalse}
+ removeDocument={returnFalse}
+ ScreenToLocalTransform={Transform.Identity}
+ addDocTab={returnFalse}
+ renderDepth={1}
+ PanelWidth={returnXDimension}
+ PanelHeight={returnYDimension}
+ focus={emptyFunction}
+ backgroundColor={returnEmptyString}
+ selectOnLoad={false}
+ parentActive={returnFalse}
+ whenActiveChanged={returnFalse}
+ bringToFront={emptyFunction}
+ zoomToScale={emptyFunction}
+ getScale={returnOne}
+ ContainingCollectionView={undefined}
+ ContentScaling={scale}
+ />
+ </div>;
+ const data = renderDoc.data;
+ if (data instanceof ObjectField) newRenderDoc.data = ObjectField.MakeCopy(data);
+ newRenderDoc.preview = true;
+ return docview;
+
+ }
+
+ @action
+ closeMenu = () => {
+ this._display = false;
+ }
+
+ @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;
+ }
+
+ 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;
+ }
+
+ render() {
+ 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}</span>
+ </div>
+ );
+ })}
+
+ </div>
+ )
+ }
+
+ </Measure>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d1e8031fd..50f7e2dc8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -896,10 +896,13 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
allDocs.forEach(doc => console.log(doc.title));
// clears internal representation of documents as vectors
ClientRecommender.Instance.reset_docs();
- 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);
+ 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());
},
@@ -1013,7 +1016,7 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
const zoom = this.props.zoomScaling();// needs to be a variable outside of the <Measure> otherwise, reactions won't fire
return <div className={freeformclass} style={{ borderRadius: "inherit", transform: `translate(${cenx}px, ${ceny}px) scale(${zoom}, ${zoom}) translate(${panx}px, ${pany}px)` }}>
{this.props.children}
- <ClientRecommender title="Distance Matrix" />
+ {/* <ClientRecommender title="Distance Matrix" /> */}
</div>;
}
} \ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 24bcc0217..3ce4dbd4f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -40,9 +40,13 @@ import React = require("react");
import { DictationManager } from '../../util/DictationManager';
import { MainView } from '../MainView';
import requestPromise = require('request-promise');
-import { Recommendations } from '../../../Recommendations';
+import { Recommendations } from '../Recommendations';
+import { SearchUtil } from '../../util/SearchUtil';
+import { ClientRecommender } from '../../ClientRecommender';
+import { DocumentType } from '../../documents/Documents';
const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
+library.add(fa.faBrain);
library.add(fa.faTrash);
library.add(fa.faShare);
library.add(fa.faDownload);
@@ -610,6 +614,33 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
a.click();
}
});
+ cm.addItem({
+ description: "Recommender System",
+ event: async () => {
+ if (!ClientRecommender.Instance) new ClientRecommender({ title: "Client Recommender" });
+ let documents: Doc[] = [];
+ 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) => {
+ 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);
+ }
+ }));
+ console.log(ClientRecommender.Instance.createDistanceMatrix());
+ let recDocs: { preview: Doc, score: number }[] = [];
+ for (let i = 0; i < documents.length; i++) {
+ recDocs.push({ preview: documents[i], score: i });
+ }
+ Recommendations.Instance.addDocuments(recDocs);
+ Recommendations.Instance.displayRecommendations(e.pageX + 100, e.pageY);
+ },
+ icon: "brain"
+ });
cm.addItem({ description: "Delete", event: this.deleteClicked, icon: "trash" });
type User = { email: string, userDocumentId: string };
let usersMenu: ContextMenuProps[] = [];
@@ -758,7 +789,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
</div>
}
</div>
- <Recommendations documents={documents} node={this.props.Document}></Recommendations>
</div>
);
}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 73b892e26..45d389ba6 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -28,6 +28,9 @@ import FaceRectangles from './FaceRectangles';
import { FieldView, FieldViewProps } from './FieldView';
import "./ImageBox.scss";
import React = require("react");
+import { SearchUtil } from '../../util/SearchUtil';
+import { ClientRecommender } from '../../ClientRecommender';
+import { DocumentType } from '../../documents/Documents';
var requestImageSize = require('../../util/request-image-size');
var path = require('path');
const { Howl } = require('howler');
@@ -240,10 +243,20 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
}
}
- extractText = () => {
- //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));
+ 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) => {