Skip to content

Commit 91f37af

Browse files
committed
adding table ingestion
1 parent eb5d9f8 commit 91f37af

File tree

9 files changed

+213
-49
lines changed

9 files changed

+213
-49
lines changed

web-server/js/slycat-table-ingestion-react.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export default function SlycatTableIngestion(props) {
2525

2626
function selectAll(e) {
2727
let property = e.target.dataset.property;
28+
let batchRadio = [];
2829
for (let [index, variableSelected] of selected.entries()) {
2930
if (variableSelected) {
3031
// Find the radio button that needs to be selected based on its name and value attributes
@@ -33,10 +34,14 @@ export default function SlycatTableIngestion(props) {
3334
);
3435
// Fire the onChange handler only if radio button is not disabled
3536
if (!radio.disabled) {
37+
batchRadio.push(radio);
3638
props.onChange({ currentTarget: radio });
3739
}
3840
}
3941
}
42+
if (props.onBatchChange) {
43+
props.onBatchChange({ batchTarget: batchRadio });
44+
}
4045
}
4146

4247
function select(event, varIndex) {

web-server/plugins/slycat-cca/js/components/CCAWizard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export const CCAWizard = (params: CCAWizardParams) => {
2828

2929
React.useEffect(() => {
3030
if (modalOpen) {
31-
handleWizardSetup()
31+
handleWizardSetup();
3232
}
3333
}, [pid, statePid, stateMid]);
3434

web-server/plugins/slycat-cca/js/components/CCAWizardSteps.tsx

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,18 @@ import { CCAWizardNavItems } from "./CCANavItems";
77
import { useAppSelector } from "./wizard-store/hooks";
88
import { selectTab, TabNames } from "./wizard-store/reducers/cCAWizardSlice";
99
import { CCALocalBrowserTab } from "./wizard-tabs/CCSLocalBrowserTab";
10+
import { CCATableIngestion } from "./wizard-tabs/CCATableIngestion";
1011

1112
export const CCAWizardSteps = () => {
1213
// The `state` arg is correctly typed as `RootState` already
13-
const tabName = useAppSelector(selectTab)
14+
const tabName = useAppSelector(selectTab);
1415
return (
1516
<div className="modal-body">
16-
<CCAWizardNavItems/>
17+
<CCAWizardNavItems />
1718
<div className="tab-content">
18-
<CCAWizardDataSelectionTab hidden={!(tabName === TabNames.CCA_DATA_WIZARD_SELECTION_TAB)}/>
19-
<CCALocalBrowserTab hidden={!(tabName === TabNames.CCA_LOCAL_BROWSER_TAB)}/>
19+
<CCAWizardDataSelectionTab hidden={!(tabName === TabNames.CCA_DATA_WIZARD_SELECTION_TAB)} />
20+
<CCALocalBrowserTab hidden={!(tabName === TabNames.CCA_LOCAL_BROWSER_TAB)} />
21+
<CCATableIngestion hidden={!(tabName === TabNames.CCA_TABLE_INGESTION)} />
2022
<div hidden={true}>
2123
<form role="form">
2224
{/* <slycat-remote-controls
@@ -53,25 +55,6 @@ export const CCAWizardSteps = () => {
5355
<slycat-parser-controls params="parser:parser,category:'table'"></slycat-parser-controls> */}
5456
</div>
5557
</div>
56-
57-
<div hidden={!(tabName === TabNames.CCA_TABLE_INGESTION)}>
58-
{/* <slycat-table-ingestion
59-
params="
60-
variables: attributes,
61-
properties: [{name: 'Classification', type: 'select', values: ['Input','Output','Neither']}]
62-
"
63-
></slycat-table-ingestion> */}
64-
<form role="form">
65-
<div className="form-group mt-3">
66-
<div className="form-check pl-1">
67-
<label>
68-
<input type="checkbox" data-bind="checked: scale_inputs" /> Scale to unit variance
69-
</label>
70-
</div>
71-
</div>
72-
</form>
73-
</div>
74-
7558
<div hidden={true}>
7659
<form data-bind="submit: name_model" id="new-cca-name-model-form" noValidate>
7760
{/* <slycat-model-controls

web-server/plugins/slycat-cca/js/components/CCAWizardUtils.tsx

Lines changed: 106 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@
33
retains certain rights in this software. */
44
import * as React from "react";
55
import { useAppDispatch, useAppSelector } from "./wizard-store/hooks";
6+
import { produce } from "immer";
67
import {
8+
Attribute,
79
resetCCAWizard,
810
selectDataLocation,
911
selectFileUploaded,
1012
selectMid,
1113
selectPid,
1214
selectTab,
15+
setAttributes,
1316
setFileUploaded,
1417
setMid,
1518
setPid,
@@ -19,6 +22,7 @@ import {
1922
import client from "js/slycat-web-client";
2023
import fileUploader from "js/slycat-file-uploader-factory";
2124
import * as dialog from "js/slycat-dialog";
25+
import { useSelector } from "node_modules/react-redux/dist/react-redux";
2226

2327
/**
2428
* A hook for controlling how the back and continue buttons work based on the current redux state
@@ -60,7 +64,9 @@ export const useCCAWizardFooter = () => {
6064
const backButton = (
6165
<button
6266
key="back button"
63-
style={{visibility: tabName === TabNames.CCA_DATA_WIZARD_SELECTION_TAB ? "hidden": "visible"}}
67+
style={{
68+
visibility: tabName === TabNames.CCA_DATA_WIZARD_SELECTION_TAB ? "hidden" : "visible",
69+
}}
6470
className="btn btn-light mr-auto"
6571
onClick={handleBack}
6672
>
@@ -78,7 +84,10 @@ export const useCCAWizardFooter = () => {
7884
Continue {fileUploaded.toString()}
7985
</button>
8086
);
81-
return React.useMemo(() => [backButton, nextButton], [fileUploaded, tabName, dataLocation, dispatch]);
87+
return React.useMemo(
88+
() => [backButton, nextButton],
89+
[fileUploaded, tabName, dataLocation, dispatch],
90+
);
8291
};
8392

8493
/**
@@ -139,7 +148,65 @@ export const useHandleClosingCallback = (
139148
};
140149

141150
/**
142-
* handle file submission
151+
* callback function for when a file is done uploading for gathering and setting all the file meta data
152+
* @returns a memoized function to call once uploading a file is done
153+
*/
154+
const useFileUploadSuccess = () => {
155+
const mid = useAppSelector(selectMid);
156+
const dispatch = useAppDispatch();
157+
return React.useCallback(
158+
(
159+
setProgress: (status: number) => void,
160+
setProgressStatus: (status: string) => void,
161+
setUploadStatus: (status: boolean) => void,
162+
) => {
163+
setProgress(95);
164+
setProgressStatus("Finishing...");
165+
client.get_model_arrayset_metadata({
166+
mid: mid,
167+
aid: "data-table",
168+
arrays: "0",
169+
statistics: "0/...",
170+
success: function (metadata: any) {
171+
setProgress(100);
172+
setProgressStatus("Finished");
173+
const attributes: Attribute[] = (metadata?.arrays[0]?.attributes as [])?.map(
174+
(attribute: any, index) => {
175+
const constant = metadata.statistics[index].unique === 1;
176+
const string = attribute.type == "string";
177+
let tooltip = "";
178+
if (string) {
179+
tooltip =
180+
"This variable's values contain strings, so it cannot be included in the analysis.";
181+
} else if (constant) {
182+
tooltip =
183+
"This variable's values are all identical, so it cannot be included in the analysis.";
184+
}
185+
return {
186+
index: index,
187+
name: attribute.name,
188+
type: attribute.type,
189+
"Axis Type": constant || string ? "" : "Input",
190+
constant: constant,
191+
disabled: constant || string,
192+
hidden: false,
193+
selected: false,
194+
lastSelected: false,
195+
tooltip: tooltip,
196+
};
197+
},
198+
);
199+
dispatch(setAttributes(attributes ?? []));
200+
setUploadStatus(true);
201+
},
202+
});
203+
},
204+
[mid, dispatch],
205+
);
206+
};
207+
208+
/**
209+
* handle local file submission
143210
*/
144211
export const useHandleLocalFileSubmit = (): [
145212
(file: File, parser: string | undefined, setUploadStatus: (status: boolean) => void) => void,
@@ -148,6 +215,7 @@ export const useHandleLocalFileSubmit = (): [
148215
] => {
149216
const mid = useAppSelector(selectMid);
150217
const pid = useAppSelector(selectPid);
218+
const fileUploadSuccess = useFileUploadSuccess();
151219
const [progress, setProgress] = React.useState<number>(0);
152220
const [progressStatus, setProgressStatus] = React.useState("");
153221
const handleSubmit = React.useCallback(
@@ -179,6 +247,7 @@ export const useHandleLocalFileSubmit = (): [
179247
setProgress(100);
180248
setProgressStatus("File upload complete");
181249
setUploadStatus(true);
250+
fileUploadSuccess(setProgress, setProgressStatus, setUploadStatus);
182251
},
183252
error: function () {
184253
setUploadStatus(false);
@@ -221,3 +290,37 @@ export const useSetUploadStatus = () => {
221290
[dispatch],
222291
);
223292
};
293+
294+
/**
295+
* A function to handle effects of selection on the radio buttons in the ingestion tab for CCA
296+
* @param attributes from redux
297+
* @returns memoized onChange function to handle radio button selection
298+
*/
299+
export const useHandleTableIngestionOnChange = (attributes: Attribute[]) => {
300+
const dispatch = useAppDispatch();
301+
return React.useCallback(
302+
(input: any) => {
303+
// this function is overloaded to handle batching so we need to check for target or batchTarget
304+
if (input?.target && (input as any)?.target?.name && (input as any)?.target?.value) {
305+
const nextAttributes = produce(attributes, (draftState) => {
306+
draftState[input?.target?.name] = {
307+
...draftState[input?.target?.name],
308+
"Axis Type": input?.target?.value,
309+
};
310+
});
311+
dispatch(setAttributes(nextAttributes));
312+
} else if (input?.batchTarget && input?.batchTarget?.length > 0) {
313+
const nextAttributes = produce(attributes, (draftState) => {
314+
input?.batchTarget.forEach((row: any) => {
315+
draftState[row?.name] = {
316+
...draftState[row?.name],
317+
"Axis Type": row?.value,
318+
};
319+
});
320+
});
321+
dispatch(setAttributes(nextAttributes));
322+
}
323+
},
324+
[attributes, dispatch],
325+
);
326+
};

web-server/plugins/slycat-cca/js/components/slycat-local-browser/SlycatLocalBrowser.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ import * as React from "react";
55
import { useHandleLocalFileSubmit } from "../CCAWizardUtils";
66
import { SlycatParserControls } from "../slycat-parser-controls/SlycatParserControls";
77

8-
export const SlycatLocalBrowser = (props: { setUploadStatus: (status: boolean) => void, disabled?: boolean }) => {
9-
const { setUploadStatus, disabled } = props;
8+
export const SlycatLocalBrowser = (props: { setUploadStatus: (status: boolean) => void }) => {
9+
const { setUploadStatus } = props;
10+
const [fileSelected, setFileSelected] = React.useState<boolean>(false);
1011
const [file, setFile] = React.useState<File | undefined>(undefined);
1112
const [parser, setParser] = React.useState<string | undefined>(undefined);
1213
const [handleSubmit, progress, progressStatus] = useHandleLocalFileSubmit();
1314
const handleFileSelected = (e: React.ChangeEvent<HTMLInputElement>): void => {
1415
if (e.target.files !== null && e.target.files.length >= 1) {
16+
setFileSelected(true);
1517
setFile(e.target.files[0]);
1618
}
1719
};
@@ -29,19 +31,19 @@ export const SlycatLocalBrowser = (props: { setUploadStatus: (status: boolean) =
2931
<SlycatParserControls setParser={setParser} />
3032
<button
3133
key="Upload File To Server"
32-
disabled={disabled ?? false }
33-
style={{visibility: progress<=0? 'visible': 'hidden'}}
34+
disabled={!fileSelected}
35+
style={{ visibility: progress <= 0 ? "visible" : "hidden" }}
3436
className="btn btn-primary"
3537
data-toggle="tooltip"
36-
data-placement="top"
38+
data-placement="top"
3739
title="You must selected a file before continuing."
3840
onClick={React.useCallback(() => {
3941
if (file) {
4042
handleSubmit(file, parser, setUploadStatus);
4143
}
4244
}, [file, handleSubmit])}
4345
>
44-
Upload File parser
46+
{fileSelected ? `Upload File: ${file?.name}` : "Select A File"}
4547
</button>
4648
<div className="progress" style={{ visibility: progress > 0 ? undefined : "hidden" }}>
4749
<div

web-server/plugins/slycat-cca/js/components/slycat-parser-controls/SlycatParserControls.tsx

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,37 @@
33
retains certain rights in this software. */
44
import * as React from "react";
55
import parsers from "js/slycat-parsers";
6-
import _ from "lodash";
6+
import { includes } from "lodash";
77
import { handleParserChange } from "../CCAWizardUtils";
88

99
export const SlycatParserControls = (props: {
1010
setParser: React.Dispatch<React.SetStateAction<string | undefined>>;
1111
}) => {
1212
const { setParser } = props;
13-
const filteredParsers: [{ type: () => string }] = parsers
13+
const filteredParsers: [{ type: () => string; label: () => string }] = parsers
1414
.available()
15-
?.filter((parser: { categories: () => string }) => _.includes(parser.categories(), "table"));
15+
?.filter((parser: { categories: () => string }) => includes(parser.categories(), "table"));
1616
React.useEffect(() => {
1717
if (filteredParsers !== undefined && filteredParsers.length > 0) {
18-
console.log('reset', filteredParsers[0].type());
18+
console.log("reset", filteredParsers[0].type());
1919
setParser(filteredParsers[0].type());
2020
}
2121
}, [filteredParsers.length]);
2222
return (
2323
<div className="form-group row">
2424
<label className="col-sm-2 col-form-label">Filetype</label>
2525
<div className="col-sm-10">
26-
<select id="slycat-model-parser" className="form-control" onChange={handleParserChange(setParser)}>
27-
{filteredParsers.map((parser: { type: () => string }) => {
28-
return <option key={parser.type()}>{parser.type()}</option>;
26+
<select
27+
id="slycat-model-parser"
28+
className="form-control"
29+
onChange={handleParserChange(setParser)}
30+
>
31+
{filteredParsers.map((parser: { type: () => string; label: () => string }) => {
32+
return (
33+
<option key={parser.type()} value={parser.type()}>
34+
{parser.label()}
35+
</option>
36+
);
2937
})}
3038
</select>
3139
</div>

0 commit comments

Comments
 (0)