From 268469f66174aa2a148fb583ef5d8708e00085d6 Mon Sep 17 00:00:00 2001 From: Haihan Lin Date: Thu, 11 Jun 2020 14:35:08 -0600 Subject: [PATCH 1/3] add route for preview. Preview is similar to dashboard, but uses a different top user control. --- frontend/src/App.tsx | 9 +- frontend/src/Components/Utilities/SideBar.tsx | 2 - .../src/Components/Utilities/UserControl.tsx | 7 + frontend/src/Preview.tsx | 331 ++++++++++++++++++ 4 files changed, 345 insertions(+), 4 deletions(-) create mode 100644 frontend/src/Preview.tsx diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 4f54e519..43e9c0cd 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,10 +1,11 @@ -import React, { FC, useEffect, useState, Component } from 'react'; +import React, { FC } from 'react'; import Store from './Interfaces/Store' import { inject, observer } from 'mobx-react'; import Dashboard from './Dashboard'; import { BrowserRouter, Switch, Route, Redirect } from 'react-router-dom' import Login from './LogIn' +import Preview from './Preview'; interface OwnProps { store?: Store; @@ -20,12 +21,16 @@ const App: FC = ({ store }: Props) => { {/* */} - + { // if (isLoggedIn) return // else return return }} /> + { + return + }} /> + // diff --git a/frontend/src/Components/Utilities/SideBar.tsx b/frontend/src/Components/Utilities/SideBar.tsx index 1de71fe0..3a8fdbef 100644 --- a/frontend/src/Components/Utilities/SideBar.tsx +++ b/frontend/src/Components/Utilities/SideBar.tsx @@ -23,7 +23,6 @@ const SideBar: FC = ({ store }: Props) => { currentSelectSet, currentOutputFilterSet, currentSelectPatientGroup, - currentSelectPatient, filterSelection } = store!; const [procedureList, setProcedureList] = useState([]); const [maxCaseCount, setMaxCaseCount] = useState(0); @@ -37,7 +36,6 @@ const SideBar: FC = ({ store }: Props) => { const data = await res.json(); const result = data.result - //TODO this needs to check if the filterSelection is not empty let tempMaxCaseCount = 0; let tempItemUnselected: any[] = []; diff --git a/frontend/src/Components/Utilities/UserControl.tsx b/frontend/src/Components/Utilities/UserControl.tsx index 8fb67c6b..eae7e50d 100644 --- a/frontend/src/Components/Utilities/UserControl.tsx +++ b/frontend/src/Components/Utilities/UserControl.tsx @@ -340,6 +340,13 @@ const UserControl: FC = ({ store }: Props) => { + + + + Preview Mode + + + { store!.isLoggedIn = false; }} > Log Out diff --git a/frontend/src/Preview.tsx b/frontend/src/Preview.tsx new file mode 100644 index 00000000..bd804d3c --- /dev/null +++ b/frontend/src/Preview.tsx @@ -0,0 +1,331 @@ +import React, { FC, useState, useEffect } from 'react'; +import { inject, observer } from 'mobx-react'; +import Store from './Interfaces/Store'; +import { LayoutElement } from './Interfaces/ApplicationState'; +import { Button, Tab, Grid, GridColumn, Container, Modal, Message, Icon, Menu, Checkbox } from 'semantic-ui-react'; +import { actions } from '.'; +import DumbbellChartVisualization from './Components/DumbbellChart/DumbbellChartVisualization'; +import BarChartVisualization from './Components/BarChart/BarChartVisualization'; +import ScatterPlotVisualization from './Components/Scatterplot/ScatterPlotVisualization'; +import HeatMapVisualization from './Components/HeatMapChart/HeatMapVisualization'; +import InterventionPlotVisualization from './Components/InterventionPlot/InterventionPlotVisualization'; +import DetailView from './Components/Utilities/DetailView'; +import LineUpWrapper from './Components/LineUpWrapper'; +import PatientComparisonWrapper from './Components/PatientComparisonWrapper'; +import UserControl from './Components/Utilities/UserControl'; +import SideBar from './Components/Utilities/SideBar'; +import styled from 'styled-components'; +import { Responsive, WidthProvider } from "react-grid-layout"; +import './App.css' +import 'react-grid-layout/css/styles.css' +import { NavLink } from 'react-router-dom'; + +interface OwnProps { + store?: Store +} +type Props = OwnProps; + +const Preview: FC = ({ store }: Props) => { + + const { layoutArray, dateRange, showZero } = store!; + + const [hemoData, setHemoData] = useState([]) + + const [loadingModalOpen, setloadingModalOpen] = useState(true) + + async function cacheHemoData() { + const resHemo = await fetch("http://localhost:8000/api/hemoglobin"); + const dataHemo = await resHemo.json(); + const resultHemo = dataHemo.result; + const resTrans = await fetch(`http://localhost:8000/api/request_transfused_units?transfusion_type=ALL_UNITS&date_range=${dateRange}`) + const dataTrans = await resTrans.json(); + let transfused_dict = {} as any; + + let result: { + CASE_ID: number, + VISIT_ID: number, + PATIENT_ID: number, + ANESTHOLOGIST_ID: number, + SURGEON_ID: number, + YEAR: number, + QUARTER: string, + MONTH: string, + DATE: Date | null, + PRBC_UNITS: number, + FFP_UNITS: number, + PLT_UNITS: number, + CRYO_UNITS: number, + CELL_SAVER_ML: number, + HEMO: number[] + }[] = []; + + + dataTrans.forEach((element: any) => { + transfused_dict[element.case_id] = { + PRBC_UNITS: element.transfused_units[0] || 0, + FFP_UNITS: element.transfused_units[1] || 0, + PLT_UNITS: element.transfused_units[2] || 0, + CRYO_UNITS: element.transfused_units[3] || 0, + CELL_SAVER_ML: element.transfused_units[4] || 0 + }; + }); + + resultHemo.map((ob: any, index: number) => { + if (transfused_dict[ob.CASE_ID]) { + const transfusedResult = transfused_dict[ob.CASE_ID]; + result.push({ + CASE_ID: ob.CASE_ID, + VISIT_ID: ob.VISIT_ID, + PATIENT_ID: ob.PATIENT_ID, + ANESTHOLOGIST_ID: ob.ANESTHOLOGIST_ID, + SURGEON_ID: ob.SURGEON_ID, + YEAR: ob.YEAR, + PRBC_UNITS: transfusedResult.PRBC_UNITS, + FFP_UNITS: transfusedResult.FFP_UNITS, + PLT_UNITS: transfusedResult.PLT_UNITS, + CRYO_UNITS: transfusedResult.CRYO_UNITS, + CELL_SAVER_ML: transfusedResult.CELL_SAVER_ML, + HEMO: ob.HEMO, + QUARTER: ob.QUARTER, + MONTH: ob.MONTH, + DATE: ob.DATE + }) + } + }) + + result = result.filter((d: any) => d); + console.log("hemo data done") + setHemoData(result) + setloadingModalOpen(false) + + } + + useEffect(() => { + cacheHemoData(); + }, []); + + + const createElement = (layout: LayoutElement, index: number) => { + switch (layout.plot_type) { + case "DUMBBELL": + return ( +
+ +
+ ); + case "VIOLIN": + return ( +
+ +
+ ); + case "SCATTER": + return (
+ +
); + + case "HEATMAP": + return (
+ +
); + + case "INTERVENTION": + return (
+
); + + } + + } + + const colData = { + lg: 2, + md: 2, + sm: 2, + xs: 2, + xxs: 2 + }; + + const generateGrid = () => { + let output = layoutArray.map(d => ({ w: d.w, h: d.h, x: d.x, y: d.y, i: d.i })) + const newStuff = output.map(d => ({ ...d })) + return newStuff + } + + const panes = [{ + menuItem: 'Main', pane: + + + + {layoutArray.map((layoutE, i) => { + return createElement(layoutE, i); + })} + + + + + + + + }, + { + menuItem: 'LineUp', pane: + +
+
+ }, { + menuItem: 'Selected Patients', + pane: + + + + + }] + + return ( + + + + + Show Zero Transfused } + /> + + + + Customize Mode + + + + { store!.isLoggedIn = false; }} > + Log Out + + + + + + + + + + + + + + + + + + Just one second + We are fetching required data. + + + + + + + ); + +} + +export default inject('store')(observer(Preview)) +const LayoutDiv = styled.div` + width: 100vw; + height: 100vh; +`; + +const SpecialPaddingColumn = styled(Grid.Column)` + &&&&&{padding-left:5px;} +`; \ No newline at end of file From 0d4da8b01858170c2f57463e2fdfe19552257006 Mon Sep 17 00:00:00 2001 From: Haihan Lin Date: Thu, 11 Jun 2020 15:49:15 -0600 Subject: [PATCH 2/3] both preview and customize mode are under dashboard so that the state is preserved. Seperate out layout generator to remove duplicated code. --- frontend/src/App.tsx | 15 +- .../src/Components/Utilities/UserControl.tsx | 9 +- frontend/src/Dashboard.tsx | 188 +--------------- frontend/src/Interfaces/Store.ts | 1 + frontend/src/LayoutGenerator.tsx | 201 +++++++++++++++++ frontend/src/Preview.tsx | 211 ++---------------- 6 files changed, 244 insertions(+), 381 deletions(-) create mode 100644 frontend/src/LayoutGenerator.tsx diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 43e9c0cd..d222893b 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,4 +1,4 @@ -import React, { FC } from 'react'; +import React, { FC, useEffect, useState, Component } from 'react'; import Store from './Interfaces/Store' import { inject, observer } from 'mobx-react'; import Dashboard from './Dashboard'; @@ -14,7 +14,7 @@ interface OwnProps { type Props = OwnProps; const App: FC = ({ store }: Props) => { - const { isLoggedIn } = store! + const { isLoggedIn, previewMode } = store! return ( @@ -25,12 +25,15 @@ const App: FC = ({ store }: Props) => { { // if (isLoggedIn) return // else return - return - }} /> - { - return + if (previewMode) { + return + } + else { + return + } }} /> + {/* {}}/> */} // diff --git a/frontend/src/Components/Utilities/UserControl.tsx b/frontend/src/Components/Utilities/UserControl.tsx index eae7e50d..3868ad28 100644 --- a/frontend/src/Components/Utilities/UserControl.tsx +++ b/frontend/src/Components/Utilities/UserControl.tsx @@ -13,7 +13,7 @@ import { interventionChartType, presetOptions, stateUpdateWrapperUseJSON, dumbbellValueOptions, scatterYOptions, typeDiction } from "../../PresetsProfile"; import ClipboardJS from 'clipboard'; -import { NavLink } from 'react-router-dom' +import { NavLink, Redirect } from 'react-router-dom' import { getCookie } from "../../Interfaces/UserManagement"; interface OwnProps { store?: Store; @@ -140,7 +140,7 @@ const UserControl: FC = ({ store }: Props) => { const regularMenu = ( - + ); diff --git a/frontend/src/Dashboard.tsx b/frontend/src/Dashboard.tsx index bef6a7b3..3579e284 100644 --- a/frontend/src/Dashboard.tsx +++ b/frontend/src/Dashboard.tsx @@ -1,23 +1,14 @@ import React, { FC, useState, useEffect } from 'react'; import { inject, observer } from 'mobx-react'; import Store from './Interfaces/Store'; -import { LayoutElement } from './Interfaces/ApplicationState'; -import { Button, Tab, Grid, GridColumn, Container, Modal, Message, Icon } from 'semantic-ui-react'; -import { actions } from '.'; -import DumbbellChartVisualization from './Components/DumbbellChart/DumbbellChartVisualization'; -import BarChartVisualization from './Components/BarChart/BarChartVisualization'; -import ScatterPlotVisualization from './Components/Scatterplot/ScatterPlotVisualization'; -import HeatMapVisualization from './Components/HeatMapChart/HeatMapVisualization'; -import InterventionPlotVisualization from './Components/InterventionPlot/InterventionPlotVisualization'; -import DetailView from './Components/Utilities/DetailView'; -import LineUpWrapper from './Components/LineUpWrapper'; -import PatientComparisonWrapper from './Components/PatientComparisonWrapper'; +import { Grid, Container, Modal, Message, Icon } from 'semantic-ui-react'; import UserControl from './Components/Utilities/UserControl'; import SideBar from './Components/Utilities/SideBar'; import styled from 'styled-components'; -import { Responsive, WidthProvider } from "react-grid-layout"; import './App.css' -import 'react-grid-layout/css/styles.css' +//import 'react-grid-layout/css/styles.css' +import LayoutGenerator from './LayoutGenerator'; +import { timeFormat } from 'd3'; interface OwnProps { store?: Store @@ -26,7 +17,6 @@ type Props = OwnProps; const Dashboard: FC = ({ store }: Props) => { - const { layoutArray, dateRange } = store!; const [hemoData, setHemoData] = useState([]) @@ -36,7 +26,7 @@ const Dashboard: FC = ({ store }: Props) => { const resHemo = await fetch("http://localhost:8000/api/hemoglobin"); const dataHemo = await resHemo.json(); const resultHemo = dataHemo.result; - const resTrans = await fetch(`http://localhost:8000/api/request_transfused_units?transfusion_type=ALL_UNITS&date_range=${dateRange}`) + const resTrans = await fetch(`http://localhost:8000/api/request_transfused_units?transfusion_type=ALL_UNITS&date_range=${[timeFormat("%d-%b-%Y")(new Date(2014, 0, 1)), timeFormat("%d-%b-%Y")(new Date(2019, 11, 31))]}`) const dataTrans = await resTrans.json(); let transfused_dict = {} as any; @@ -104,170 +94,6 @@ const Dashboard: FC = ({ store }: Props) => { }, []); - const createElement = (layout: LayoutElement, index: number) => { - switch (layout.plot_type) { - case "DUMBBELL": - return ( -
- -
- ); - case "VIOLIN": - return ( -
- -
- ); - case "SCATTER": - return (
- -
); - - case "HEATMAP": - return (
- -
); - - case "INTERVENTION": - return (
-
); - - } - - } - - const colData = { - lg: 2, - md: 2, - sm: 2, - xs: 2, - xxs: 2 - }; - - const generateGrid = () => { - let output = layoutArray.map(d => ({ w: d.w, h: d.h, x: d.x, y: d.y, i: d.i })) - const newStuff = output.map(d => ({ ...d })) - return newStuff - } - - const panes = [{ - menuItem: 'Main', pane: - - - - {layoutArray.map((layoutE, i) => { - return createElement(layoutE, i); - })} - - - - - - - - }, - { - menuItem: 'LineUp', pane: - -
-
- }, { - menuItem: 'Selected Patients', - pane: - - - - - }] - return ( @@ -278,9 +104,7 @@ const Dashboard: FC = ({ store }: Props) => { - + diff --git a/frontend/src/Interfaces/Store.ts b/frontend/src/Interfaces/Store.ts index 07c593f9..762432ef 100644 --- a/frontend/src/Interfaces/Store.ts +++ b/frontend/src/Interfaces/Store.ts @@ -44,6 +44,7 @@ export default class Store { @observable currentSelectPatientGroup: number[] = defaultState.currentSelectPatientGroup; @observable isLoggedIn: boolean = false; + @observable previewMode: boolean = false; // @observable csrftoken: string | null = "" } diff --git a/frontend/src/LayoutGenerator.tsx b/frontend/src/LayoutGenerator.tsx new file mode 100644 index 00000000..1e851f5c --- /dev/null +++ b/frontend/src/LayoutGenerator.tsx @@ -0,0 +1,201 @@ +import React, { + FC +} from "react"; +import { inject, observer } from "mobx-react"; +import { Tab, Grid, GridColumn, Button } from "semantic-ui-react"; +import { actions } from "."; +import DetailView from "./Components/Utilities/DetailView"; +import LineUpWrapper from "./Components/LineUpWrapper"; +import PatientComparisonWrapper from "./Components/PatientComparisonWrapper"; +import Store from "./Interfaces/Store"; +import { LayoutElement } from "./Interfaces/ApplicationState"; +import DumbbellChartVisualization from "./Components/DumbbellChart/DumbbellChartVisualization"; +import BarChartVisualization from "./Components/BarChart/BarChartVisualization"; +import ScatterPlotVisualization from "./Components/Scatterplot/ScatterPlotVisualization"; +import HeatMapVisualization from "./Components/HeatMapChart/HeatMapVisualization"; +import InterventionPlotVisualization from "./Components/InterventionPlot/InterventionPlotVisualization"; +import { Responsive, WidthProvider } from "react-grid-layout"; +import 'react-grid-layout/css/styles.css' +interface OwnProps { + hemoData: any; + store?: Store +} + + + +export type Props = OwnProps; + + + +const LayoutGenerator: FC = ({ hemoData, store }: Props) => { + const { layoutArray } = store! + + const createElement = (layout: LayoutElement, index: number) => { + console.log(layout) + switch (layout.plot_type) { + case "DUMBBELL": + return ( +
+ +
+ ); + case "VIOLIN": + return ( +
+ +
+ ); + case "SCATTER": + return (
+ +
); + + case "HEATMAP": + return (
+ +
); + + case "INTERVENTION": + return (
+
); + + } + + } + + const colData = { + lg: 2, + md: 2, + sm: 2, + xs: 2, + xxs: 2 + }; + const generateGrid = () => { + let output = layoutArray.map(d => ({ w: d.w, h: d.h, x: d.x, y: d.y, i: d.i })) + const newStuff = output.map(d => ({ ...d })) + return newStuff + } + const panes = [{ + menuItem: 'Main', pane: + + + + {layoutArray.map((layoutE, i) => { + return createElement(layoutE, i); + })} + + + + + + + + }, + { + menuItem: 'LineUp', pane: + +
+
+ }, { + menuItem: 'Selected Patients', + pane: + + + + + }] + return +} + + +export default inject("store")(observer(LayoutGenerator)); \ No newline at end of file diff --git a/frontend/src/Preview.tsx b/frontend/src/Preview.tsx index bd804d3c..f4035ad5 100644 --- a/frontend/src/Preview.tsx +++ b/frontend/src/Preview.tsx @@ -1,43 +1,45 @@ import React, { FC, useState, useEffect } from 'react'; import { inject, observer } from 'mobx-react'; import Store from './Interfaces/Store'; -import { LayoutElement } from './Interfaces/ApplicationState'; -import { Button, Tab, Grid, GridColumn, Container, Modal, Message, Icon, Menu, Checkbox } from 'semantic-ui-react'; -import { actions } from '.'; -import DumbbellChartVisualization from './Components/DumbbellChart/DumbbellChartVisualization'; -import BarChartVisualization from './Components/BarChart/BarChartVisualization'; -import ScatterPlotVisualization from './Components/Scatterplot/ScatterPlotVisualization'; -import HeatMapVisualization from './Components/HeatMapChart/HeatMapVisualization'; -import InterventionPlotVisualization from './Components/InterventionPlot/InterventionPlotVisualization'; -import DetailView from './Components/Utilities/DetailView'; -import LineUpWrapper from './Components/LineUpWrapper'; -import PatientComparisonWrapper from './Components/PatientComparisonWrapper'; -import UserControl from './Components/Utilities/UserControl'; +import { Button, Grid, Container, Modal, Message, Icon, Menu, Checkbox } from 'semantic-ui-react'; +import { actions, provenance } from '.'; + import SideBar from './Components/Utilities/SideBar'; import styled from 'styled-components'; -import { Responsive, WidthProvider } from "react-grid-layout"; + import './App.css' -import 'react-grid-layout/css/styles.css' +//import 'react-grid-layout/css/styles.css' import { NavLink } from 'react-router-dom'; +import LayoutGenerator from './LayoutGenerator'; +import { timeFormat } from 'd3'; interface OwnProps { store?: Store + provenanceState?: string; } type Props = OwnProps; -const Preview: FC = ({ store }: Props) => { +const Preview: FC = ({ store, provenanceState }: Props) => { - const { layoutArray, dateRange, showZero } = store!; + const { showZero } = store!; const [hemoData, setHemoData] = useState([]) const [loadingModalOpen, setloadingModalOpen] = useState(true) + useEffect(() => { + if (provenanceState) { + console.log(provenanceState) + provenance.importState(provenanceState) + } + } + , [provenanceState]) + async function cacheHemoData() { const resHemo = await fetch("http://localhost:8000/api/hemoglobin"); const dataHemo = await resHemo.json(); const resultHemo = dataHemo.result; - const resTrans = await fetch(`http://localhost:8000/api/request_transfused_units?transfusion_type=ALL_UNITS&date_range=${dateRange}`) + const resTrans = await fetch(`http://localhost:8000/api/request_transfused_units?transfusion_type=ALL_UNITS&date_range=${[timeFormat("%d-%b-%Y")(new Date(2014, 0, 1)), timeFormat("%d-%b-%Y")(new Date(2019, 11, 31))]}`) const dataTrans = await resTrans.json(); let transfused_dict = {} as any; @@ -97,178 +99,12 @@ const Preview: FC = ({ store }: Props) => { console.log("hemo data done") setHemoData(result) setloadingModalOpen(false) - } useEffect(() => { cacheHemoData(); }, []); - - const createElement = (layout: LayoutElement, index: number) => { - switch (layout.plot_type) { - case "DUMBBELL": - return ( -
- -
- ); - case "VIOLIN": - return ( -
- -
- ); - case "SCATTER": - return (
- -
); - - case "HEATMAP": - return (
- -
); - - case "INTERVENTION": - return (
-
); - - } - - } - - const colData = { - lg: 2, - md: 2, - sm: 2, - xs: 2, - xxs: 2 - }; - - const generateGrid = () => { - let output = layoutArray.map(d => ({ w: d.w, h: d.h, x: d.x, y: d.y, i: d.i })) - const newStuff = output.map(d => ({ ...d })) - return newStuff - } - - const panes = [{ - menuItem: 'Main', pane: - - - - {layoutArray.map((layoutE, i) => { - return createElement(layoutE, i); - })} - - - - - - - - }, - { - menuItem: 'LineUp', pane: - -
-
- }, { - menuItem: 'Selected Patients', - pane: - - - - - }] - return ( @@ -281,9 +117,10 @@ const Preview: FC = ({ store }: Props) => { /> - + {/* Customize Mode - + */} +