aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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
11 files changed, 266 insertions, 23 deletions
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