aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package-lock.json31
-rw-r--r--package.json6
-rw-r--r--src/.DS_Storebin10244 -> 10244 bytes
-rw-r--r--src/client/views/nodes/DataVizBox/ChartBox.tsx162
-rw-r--r--src/client/views/nodes/DataVizBox/DataVizBox.tsx61
-rw-r--r--src/client/views/nodes/DataVizBox/HistogramBox.tsx3
-rw-r--r--src/client/views/nodes/DataVizBox/HistogramBoxPrimitives.scss0
-rw-r--r--src/client/views/nodes/DataVizBox/HistogramBoxPrimitives.tsx16
-rw-r--r--src/client/views/nodes/DataVizBox/HistogramLabelPrimitives.scss0
-rw-r--r--src/client/views/nodes/DataVizBox/HistogramLabelPrimitives.tsx13
-rw-r--r--src/server/ApiManagers/DataVizManager.ts26
-rw-r--r--src/server/DataVizUtils.ts6
-rw-r--r--src/server/index.ts2
13 files changed, 303 insertions, 23 deletions
diff --git a/package-lock.json b/package-lock.json
index 3b9cda8bb..414b8abdc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2468,6 +2468,9 @@
"@xtuc/long": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
+<<<<<<< HEAD
+ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="
+=======
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
"dev": true
},
@@ -2480,6 +2483,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/D/-/D-1.0.0.tgz",
"integrity": "sha512-nQvrCBu7K2pSSEtIM0EEF03FVjcczCXInMt3moLNFbjlWx6bZrX72uT6/1uAXDbnzGUAx9gTyDiQ+vrFi663oA=="
+>>>>>>> master
},
"abab": {
"version": "2.0.6",
@@ -5059,6 +5063,14 @@
"integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
"dev": true
},
+<<<<<<< HEAD
+ "D": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/D/-/D-1.0.0.tgz",
+ "integrity": "sha512-nQvrCBu7K2pSSEtIM0EEF03FVjcczCXInMt3moLNFbjlWx6bZrX72uT6/1uAXDbnzGUAx9gTyDiQ+vrFi663oA=="
+ },
+=======
+>>>>>>> master
"d3-array": {
"version": "2.12.1",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
@@ -7442,8 +7454,12 @@
"estraverse": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+<<<<<<< HEAD
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="
+=======
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"dev": true
+>>>>>>> master
}
}
},
@@ -9476,7 +9492,11 @@
"resolved": "https://registry.npmjs.org/image-size-stream/-/image-size-stream-1.1.0.tgz",
"integrity": "sha1-Ivou2mbG31AQh0bacUkmSy0l+Gs=",
"requires": {
+<<<<<<< HEAD
+ "image-size": "git+https://github.com/netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1",
+=======
"image-size": "github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1",
+>>>>>>> master
"readable-stream": "^1.0.33",
"tryit": "^1.0.1"
},
@@ -12471,6 +12491,8 @@
"write-file-atomic": "^2.4.3"
},
"dependencies": {
+<<<<<<< HEAD
+=======
"JSONStream": {
"version": "1.3.5",
"bundled": true,
@@ -12479,6 +12501,7 @@
"through": ">=2.2.7 <3"
}
},
+>>>>>>> master
"abbrev": {
"version": "1.1.1",
"resolved": false,
@@ -15338,6 +15361,8 @@
}
}
},
+<<<<<<< HEAD
+=======
"string_decoder": {
"version": "1.3.0",
"bundled": true,
@@ -15351,6 +15376,7 @@
}
}
},
+>>>>>>> master
"stringify-package": {
"version": "1.0.1",
"resolved": false,
@@ -17506,6 +17532,11 @@
"use-memo-one": "^1.1.1"
}
},
+ "react-chartjs-2": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-4.2.0.tgz",
+ "integrity": "sha512-9Vm9Sg9XAKiR579/FnBkesofjW9goaaFLfS7XlGTzUJlWFZGSE6A/pBI6+i/bP3pobKZoFcWJdFnjShytToqXw=="
+ },
"react-color": {
"version": "2.19.3",
"resolved": "https://registry.npmjs.org/react-color/-/react-color-2.19.3.tgz",
diff --git a/package.json b/package.json
index 2e3ef0a07..87cd1c074 100644
--- a/package.json
+++ b/package.json
@@ -168,7 +168,12 @@
"bootstrap": "^4.6.1",
"browser-assert": "^1.2.1",
"bson": "^4.6.1",
+<<<<<<< HEAD
+ "canvas": "^2.9.0",
+ "chart.js": "^3.8.0",
+=======
"canvas": "^2.9.3",
+>>>>>>> master
"child_process": "^1.0.2",
"chrome": "^0.1.0",
"class-transformer": "^0.2.0",
@@ -264,6 +269,7 @@
"react-audio-waveform": "0.0.5",
"react-autosuggest": "^9.4.3",
"react-beautiful-dnd": "^13.1.0",
+ "react-chartjs-2": "^4.2.0",
"react-color": "^2.19.3",
"react-compound-slider": "^2.5.0",
"react-datepicker": "^3.8.0",
diff --git a/src/.DS_Store b/src/.DS_Store
index 4751acf44..69407d6c2 100644
--- a/src/.DS_Store
+++ b/src/.DS_Store
Binary files differ
diff --git a/src/client/views/nodes/DataVizBox/ChartBox.tsx b/src/client/views/nodes/DataVizBox/ChartBox.tsx
new file mode 100644
index 000000000..07f754637
--- /dev/null
+++ b/src/client/views/nodes/DataVizBox/ChartBox.tsx
@@ -0,0 +1,162 @@
+import { observer } from "mobx-react";
+import * as React from "react";
+import { Doc } from "../../../../fields/Doc";
+import {
+ Chart as ChartJS,
+ CategoryScale,
+ LinearScale,
+ BarElement,
+ Title,
+ Tooltip,
+ Legend,
+ InteractionItem,
+ PointElement,
+ LineElement,
+ } from 'chart.js';
+ import { Bar, getDatasetAtEvent, getElementAtEvent, Line } from 'react-chartjs-2';
+import { ChartJSOrUndefined } from "react-chartjs-2/dist/types";
+import { action, computed, observable } from "mobx";
+import { Cast, StrCast } from "../../../../fields/Types";
+
+
+export interface ChartBoxProps {
+ rootDoc: Doc;
+ pairs: {x: number, y:number}[];
+}
+
+export interface ChartJsData {
+ labels: number[];
+ datasets: {
+ label: string;
+ data: number[];
+ backgroundColor: string[];
+ }[]
+}
+
+ChartJS.register(
+ CategoryScale,
+ LinearScale,
+ BarElement,
+ Title,
+ Tooltip,
+ Legend,
+ PointElement,
+ LineElement
+ );
+
+const primaryColor = 'rgba(53, 162, 235, 0.5)';
+const selectedColor = 'rgba(255, 99, 132, 0.5)';
+
+@observer
+export class ChartBox extends React.Component<ChartBoxProps> {
+ private _chartRef: any = React.createRef<ChartJSOrUndefined<"bar", number[], string>>();
+
+ @observable private _prevColor: string | undefined= undefined;
+ @observable private _prevIndex: { dIndex: number, index: number} | undefined = undefined;
+ @observable private _chartJsData: ChartJsData | undefined= undefined;
+
+ @computed get currView() {
+ if (this.props.rootDoc._dataVizView) {
+ return StrCast(this.props.rootDoc._currChartView);
+ } else {
+ return "table";
+ }
+ }
+
+ constructor(props: any) {
+ super(props);
+ if (!this.props.rootDoc._currChartView) {
+ this.props.rootDoc._currChartView = "bar";
+ }
+ }
+
+ @computed get options() {
+ return {
+ responsive: true,
+ plugins: {
+ legend: {
+ position: 'top' as const,
+ },
+ title: {
+ display: true,
+ text: 'Bar Chart',
+ },
+ },
+ }
+ }
+
+ @action
+ generateChartJsData() {
+ if (this.props.rootDoc._chartData) {
+ // parse the string into a json object
+ this._chartJsData = JSON.parse(StrCast(this.props.rootDoc._chartData));
+ this._prevColor = StrCast(this.props.rootDoc._prevColor);
+ this._prevIndex = JSON.parse(StrCast(this.props.rootDoc._prevIndex));
+ return;
+ }
+ const labels = this.props.pairs.map(p => p.x);
+ const dataset = {
+ label: 'Dataset 1',
+ data: this.props.pairs.map(p => p.y),
+ backgroundColor: this.props.pairs.map(p => primaryColor),
+ }
+ const data = {
+ labels,
+ datasets: [dataset]
+ };
+ this._chartJsData = data;
+ }
+
+ componentDidMount() {
+ this.generateChartJsData();
+ }
+
+ @action
+ onClickChangeChart = (e: React.MouseEvent<HTMLButtonElement>) => {
+ e.preventDefault();
+ e.stopPropagation();
+ console.log(e.currentTarget.value);
+ this.props.rootDoc._currChartView = e.currentTarget.value.toLowerCase();
+ }
+
+ @action
+ onClick = (e: any) => {
+ e.preventDefault();
+ if (getDatasetAtEvent(this._chartRef.current, e).length == 0) return;
+ if (!this._chartJsData) return;
+ if (this._prevIndex && this._prevColor) {
+ this._chartJsData.datasets[this._prevIndex.dIndex].backgroundColor[this._prevIndex.index] = this._prevColor;
+ }
+
+ const currSelected = getElementAtEvent(this._chartRef.current, e);
+ const index = { datasetIndex: currSelected[0].datasetIndex, index: currSelected[0].index };
+ this._prevIndex = { dIndex: index.datasetIndex, index: index.index };
+ this._prevColor = this._chartJsData.datasets[index.datasetIndex].backgroundColor[index.index];
+ this._chartJsData.datasets[index.datasetIndex].backgroundColor[index.index] = selectedColor;
+ this._chartRef.current.update();
+ // stringify this._chartJsData
+ const strData = JSON.stringify(this._chartJsData);
+ this.props.rootDoc._chartData = strData;
+ this.props.rootDoc._prevColor = this._prevColor;
+ this.props.rootDoc._prevIndex = JSON.stringify(this._prevIndex);
+ }
+
+ render() {
+ if (this.props.pairs && this._chartJsData) {
+ return (
+ <div>
+ <div>
+ {this.props.rootDoc._currChartView == "line" ?
+ (<Line ref={this._chartRef} options={this.options} data={this._chartJsData} onClick={(e) => this.onClick(e)} />) :
+ (<Bar ref={this._chartRef} options={this.options} data={this._chartJsData} onClick={(e) => this.onClick(e)} />)
+ }
+ </div>
+ <button onClick={(e) => this.onClickChangeChart(e)} value="line">Line</button>
+ <button onClick={(e) => this.onClickChangeChart(e)} value="bar">Bar</button>
+ </div>
+ )
+ } else {
+ return <div></div>
+ }
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index 592723ee9..c5ad3c84d 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -1,9 +1,12 @@
import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
import * as React from "react";
-import { StrCast } from "../../../../fields/Types";
+import { Cast, StrCast } from "../../../../fields/Types";
+import { CsvField } from "../../../../fields/URLField";
+import { Utils } from "../../../../Utils";
import { ViewBoxBaseComponent } from "../../DocComponent";
import { FieldViewProps, FieldView } from "../FieldView";
+import { ChartBox } from "./ChartBox";
import "./DataVizBox.scss";
import { HistogramBox } from "./HistogramBox";
import { TableBox } from "./TableBox";
@@ -16,7 +19,7 @@ enum DataVizView {
@observer
export class DataVizBox extends ViewBoxBaseComponent<FieldViewProps>() {
- @observable private pairs: {x: number, y:number}[] = [{x: 1, y:2}];
+ @observable private pairs: {x: number, y:number}[] = [];
// TODO: nda - make this use enum values instead
// @observable private currView: DataVizView = DataVizView.TABLE;
@@ -38,40 +41,47 @@ export class DataVizBox extends ViewBoxBaseComponent<FieldViewProps>() {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DataVizBox, fieldKey); }
- @action
- private createPairs() {
- const xVals: number[] = [0, 1, 2, 3, 4, 5];
- // const yVals: number[] = [10, 20, 30, 40, 50, 60];
- const yVals: number[] = [1, 2, 3, 4, 5, 6];
- let pairs: {
- x: number,
- y:number
- }[] = [];
- if (xVals.length != yVals.length) return pairs;
- for (let i = 0; i < xVals.length; i++) {
- pairs.push({x: xVals[i], y: yVals[i]});
- }
- this.pairs = pairs;
- return pairs;
- }
-
@computed get selectView() {
switch(this.currView) {
case "table":
- return (<TableBox pairs={this.pairs} />)
+ return (<TableBox pairs={this.pairs} />);
case "histogram":
- return (<HistogramBox rootDoc={this.rootDoc} pairs={this.pairs}/>)
+ return (<ChartBox rootDoc={this.rootDoc} pairs={this.pairs}/>);
+ // case "histogram":
+ // return (<HistogramBox rootDoc={this.rootDoc} pairs={this.pairs}/>)
}
}
@computed get pairVals() {
- return this.createPairs();
+ return fetch("/csvData?uri=" + this.dataUrl?.url.href).then(res => res.json());
}
+ @computed get dataUrl() { return Cast(this.dataDoc[this.fieldKey], CsvField) }
+
componentDidMount() {
- this.createPairs();
+ // this.createPairs();
+ this.fetchData();
+ }
+
+ // async fetchData() {
+ // console.log(Cast(this.dataDoc[this.fieldKey], CsvField));
+ // const uri = this.dataUrl?.url.href;
+ // console.log(uri);
+ // const res = await fetch("/csvData?uri=" + uri);
+ // return await res.json();
+ // }
+
+ fetchData() {
+ const uri = this.dataUrl?.url.href;
+ fetch("/csvData?uri=" + uri).then(
+ res => res.json().then(
+ action(res => {
+ this.pairs = res;
+ })
+ ))
}
+
// handle changing the view using a button
@action changeViewHandler(e: React.MouseEvent<HTMLButtonElement>) {
e.preventDefault();
@@ -80,6 +90,11 @@ export class DataVizBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
render() {
+
+ if (this.pairs.length == 0) {
+ return <div>Loading...</div>
+ }
+
return (
<div className="dataViz">
<button onClick={(e) => this.changeViewHandler(e)}>Change View</button>
diff --git a/src/client/views/nodes/DataVizBox/HistogramBox.tsx b/src/client/views/nodes/DataVizBox/HistogramBox.tsx
index 00dc2ef46..4488323fe 100644
--- a/src/client/views/nodes/DataVizBox/HistogramBox.tsx
+++ b/src/client/views/nodes/DataVizBox/HistogramBox.tsx
@@ -3,6 +3,7 @@ import { observer } from "mobx-react";
import * as React from "react";
import { Doc } from "../../../../fields/Doc";
import { NumCast } from "../../../../fields/Types";
+import { DragManager } from "../../../util/DragManager";
import "./HistogramBox.scss";
interface HistogramBoxProps {
@@ -15,6 +16,8 @@ interface HistogramBoxProps {
export class HistogramBox extends React.Component<HistogramBoxProps> {
+ private _dropXDispoer?: DragManager.DragDropDisposer;
+ private _dropYDispoer?: DragManager.DragDropDisposer;
private origin = {x: 0.1 * this.width, y: 0.9 * this.height};
diff --git a/src/client/views/nodes/DataVizBox/HistogramBoxPrimitives.scss b/src/client/views/nodes/DataVizBox/HistogramBoxPrimitives.scss
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/client/views/nodes/DataVizBox/HistogramBoxPrimitives.scss
diff --git a/src/client/views/nodes/DataVizBox/HistogramBoxPrimitives.tsx b/src/client/views/nodes/DataVizBox/HistogramBoxPrimitives.tsx
new file mode 100644
index 000000000..ac1b7699b
--- /dev/null
+++ b/src/client/views/nodes/DataVizBox/HistogramBoxPrimitives.tsx
@@ -0,0 +1,16 @@
+import { observer } from "mobx-react";
+import * as React from "react";
+import { HistogramBox } from "./HistogramBox";
+
+export interface HistogramPrimitivesProps {
+ HistoBox: HistogramBox;
+}
+
+@observer
+export class HistogramBoxPrimitives extends React.Component<HistogramPrimitivesProps> {
+ render() {
+ return <div className="histogramboxprimitives-container">
+
+ </div>
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/DataVizBox/HistogramLabelPrimitives.scss b/src/client/views/nodes/DataVizBox/HistogramLabelPrimitives.scss
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/client/views/nodes/DataVizBox/HistogramLabelPrimitives.scss
diff --git a/src/client/views/nodes/DataVizBox/HistogramLabelPrimitives.tsx b/src/client/views/nodes/DataVizBox/HistogramLabelPrimitives.tsx
new file mode 100644
index 000000000..d3440dea1
--- /dev/null
+++ b/src/client/views/nodes/DataVizBox/HistogramLabelPrimitives.tsx
@@ -0,0 +1,13 @@
+import { observer } from "mobx-react";
+import * as React from "react";
+import { HistogramPrimitivesProps } from "./HistogramBoxPrimitives";
+import "./HistogramLabelPrimitives.scss";
+
+@observer
+export class HistogramLabelPrimitives extends React.Component<HistogramPrimitivesProps> {
+ render() {
+ return <div className="histogramLabelPrimitives-container">
+
+ </div>
+ }
+}
diff --git a/src/server/ApiManagers/DataVizManager.ts b/src/server/ApiManagers/DataVizManager.ts
new file mode 100644
index 000000000..0d43130d1
--- /dev/null
+++ b/src/server/ApiManagers/DataVizManager.ts
@@ -0,0 +1,26 @@
+import { csvParser, csvToString } from "../DataVizUtils";
+import { Method, _success } from "../RouteManager";
+import ApiManager, { Registration } from "./ApiManager";
+import { Directory, serverPathToFile } from "./UploadManager";
+import * as path from 'path';
+
+export default class DataVizManager extends ApiManager {
+ protected initialize(register: Registration): void {
+ register({
+ method: Method.GET,
+ subscription: "/csvData",
+ secureHandler: async ({ req, res }) => {
+ const uri = req.query.uri as string;
+
+ return new Promise<void>(resolve => {
+ const name = path.basename(uri);
+ const sPath = serverPathToFile(Directory.csv, name);
+ const parsedCsv = csvParser(csvToString(sPath));
+ _success(res, parsedCsv);
+ resolve();
+ });
+ }
+ });
+ }
+
+} \ No newline at end of file
diff --git a/src/server/DataVizUtils.ts b/src/server/DataVizUtils.ts
index 4fd0ca6ff..2528fb1ab 100644
--- a/src/server/DataVizUtils.ts
+++ b/src/server/DataVizUtils.ts
@@ -1,3 +1,5 @@
+import { readFileSync } from "fs";
+
export function csvParser(csv: string) {
const lines = csv.split("\n");
const headers = lines[0].split(",");
@@ -10,4 +12,8 @@ export function csvParser(csv: string) {
return obj;
});
return data;
+}
+
+export function csvToString(path: string) {
+ return readFileSync(path, 'utf8');
} \ No newline at end of file
diff --git a/src/server/index.ts b/src/server/index.ts
index f8c32103b..0acb7f20f 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -4,6 +4,7 @@ import * as mobileDetect from 'mobile-detect';
import * as path from 'path';
import * as qs from 'query-string';
import { log_execution } from "./ActionUtilities";
+import DataVizManager from "./ApiManagers/DataVizManager";
import DeleteManager from "./ApiManagers/DeleteManager";
import DownloadManager from './ApiManagers/DownloadManager';
import GeneralGoogleManager from "./ApiManagers/GeneralGoogleManager";
@@ -73,6 +74,7 @@ function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }:
new UtilManager(),
new GeneralGoogleManager(),
new GooglePhotosManager(),
+ new DataVizManager(),
];
// initialize API Managers