import React from "react"; import { makeStyles, withStyles } from "@material-ui/core/styles"; import { Grid, CardContent, CardActions, Typography, Card, Button, Breadcrumbs, IconButton, LinearProgress, } from "@material-ui/core"; import { NavigateNextRounded, NavigateBeforeRounded, NavigateNext, ArrowBack, ArrowForward, SatelliteSharp, } from "@material-ui/icons/"; import { useState } from "state/State"; import VisualizerOptionSelectionGrid from "components/VisualizerOptionSelectionGrid"; import { getOptions } from "components/VisualizerOptionSelectionGrid"; import { getActiveStep, getSteps } from "containers/SummaryContainer"; import { StepperState, Models, Tasks, DatasetCategory, StepperStateOrder, Preprocessors, } from "state/StateTypes"; import { Actions } from "state/Actions"; import PlotsContainer from "./PlotsContainer"; import { useModelState } from "state/ModelState"; import { invokeNLP } from "js-ml/nlp"; import { invokeKNN } from "js-ml/knn"; import { invokeLinReg } from "js-ml/linReg"; import { datasetMetadata } from "static/datasets/metadata"; import StepperFinish from "components/StepperFinish"; const useStyles = makeStyles((theme) => ({ rootExplanation: { width: "100%", textAlign: "center", paddingTop: theme.spacing(1), boxShadow: "none", height: 100, }, rootActions: { width: "100%", height: "100%", maxHeight: 640, textAlign: "center", paddingTop: theme.spacing(1), position: "relative", border: "none", boxShadow: "none", }, rootActionsFinish: { width: "100%", height: "100%", maxHeight: 900, textAlign: "center", paddingTop: theme.spacing(1), position: "relative", border: "none", boxShadow: "none", }, title: { fontSize: "2vw", fontWeight: 300, marginTop: -5, }, fullWidth: { width: "100%", }, fullHeight: { height: "100%", }, rightMargin: { marginRight: 24 }, visualizerHeight: { height: "calc(100% - 92px)", }, // next and back buttons button: { transition: "all 0.4s", border: "1px solid rgba(14,19,24,.2)", outline: "none !important", // backgroundColor: "blue", "&:hover": { backgroundColor: "white", boxShadow: "0 0 0 2px #00c4cc, inset 0 0 0 2px #ffffff", borderColor: "white", }, }, nextButton: { transition: "all 0.4s", border: "1px solid rgba(14,19,24,.2)", outline: "none !important", backgroundImage: "linear-gradient(45deg, #7b2be8, #3284d7)", "&:hover": { backgroundColor: "white", boxShadow: "0 0 0 2px #00c4cc, inset 0 0 0 2px #ffffff", borderColor: "white", }, }, nextButtonDisabled: { border: "1px solid white", outline: "none !important", backgroundColor: "white", transition: "all 0.4s", }, arrow: { color: "white", }, })); const taskFormatter = (task) => task === Tasks.NATURAL_LANGUAGE ? "NLP" : task; const datasetFormatter = (category, sample) => { if (category === DatasetCategory.CUSTOM) { return category; } return datasetMetadata?.[sample]?.title ?? category; }; const modelFormatter = (model) => { switch (model) { case Models.KNN: return "KNN"; case Models.NEURAL_NETWORK_FF: return "Neural"; case Models.LINEAR_REGRESSION: return "Linear"; case Models.ORDINAL_REGRESSION: return "Ordinal"; case Models.POISSON_REGRESSION: return "Poisson"; default: return null; } }; const nlpModelFormatter = (models) => { if (models.length === 1) { return models; } const newModels = models.map((model) => { if (model === Models.ENTITY_RECOGNITION) { return "Entity"; } if (model === Models.SENTIMENT_ANALYSIS) { return "Sentiment"; } }); return newModels; }; const preprocessorFormatter = (preprocessors) => { if (preprocessors.length === 1) { return preprocessors; } const newPre = preprocessors.map((pre) => { switch (pre) { case Preprocessors.TEXT_CLEANING: return "Clean"; case Preprocessors.NORMALIZATION: return "Norm"; case Preprocessors.PCA: return "PCA"; default: return null; } }); return newPre; }; const BorderLinearProgress = withStyles((theme) => ({ root: { height: 10, borderRadius: 5, margin: "0px 28px 0px 28px", }, colorPrimary: { backgroundColor: theme.palette.grey[theme.palette.type === "light" ? 200 : 700], }, bar: { borderRadius: 5, backgroundImage: "linear-gradient(to right, #00c3cc, #7c2ae8)", }, }))(LinearProgress); function VisualizerContainer() { const classes = useStyles(); const steps = getSteps(); const { state, dispatch } = useState(); const { model_state, model_dispatch } = useModelState(); function getProgressBarValue() { if (state.stepper_finish) { return 200; } return (100 * (getActiveStep(state) + 1)) / StepperStateOrder.length; } const getIsSelected = (value) => [ state.task, state.dataset_category, state.sample_dataset, state.model, ...state.nlp_models, ...state.preprocessors, ].includes(value); const handleNext = async () => { dispatch({ type: Actions.STEPPER_HANDLE_NEXT, model_state, model_dispatch, }); }; const handleBack = () => { dispatch({ type: Actions.STEPPER_HANDLE_PREVIOUS, }); }; const handleFinish = () => { dispatch({ type: Actions.HANDLE_STEPPER_FINISH, }); }; function isNextDisabled() { if ( state.stepper_state === StepperState.DATASET && state.dataset_category === DatasetCategory.SAMPLE && state.sample_dataset == null ) { return true; } if (state.stepper_state === StepperState.VISUALIZE) { return false; } return ( state.stepper_state !== StepperState.PREPROCESSORS && !getOptions(state).some((val) => getIsSelected(val.label)) ); } const SelectedOptionLabel = (index) => { let option = null; switch (index) { case 0: option = taskFormatter(state.task); break; case 1: option = datasetFormatter(state.dataset_category, state.sample_dataset); break; case 2: if (state.task === Tasks.NATURAL_LANGUAGE) { option = nlpModelFormatter(state.nlp_models).join(", "); } else { option = modelFormatter(state.model); } break; case 3: option = preprocessorFormatter(state.preprocessors).join(", "); break; case 4: option = "Visualize"; break; default: break; } return option; }; function getVizContent() { if (state.stepper_finish) { return <StepperFinish />; } if (state.stepper_state === StepperState.VISUALIZE) { return <PlotsContainer />; } return <VisualizerOptionSelectionGrid />; } return ( <Grid container direction="column" justify="flex-start" alignItems="center" className={classes.fullHeight} > {/* Nav Bar */} <Grid container direction="row" style={{ height: "82px", padding: "16px 28px 16px 28px", justifyContent: "center", }} > <Grid item style={{ float: "left", width: "50px" }}> {getActiveStep(state) > 0 ? ( <IconButton onClick={handleBack} className={classes.button} style={{ float: "left" }} component="span" > <ArrowBack /> </IconButton> ) : null} </Grid> <Grid item style={{ margin: "0 auto", alignSelf: "center", width: `${state.stepper_finish ? "130px" : "540px"}`, }} > <Breadcrumbs separator={<NavigateNext fontSize="small" />} aria-label="breadcrumb" > {StepperStateOrder.map((step, index) => { if (getActiveStep(state) === index && !state.stepper_finish) { return ( <Typography color="textPrimary" style={{ fontSize: "16px" }}> {String(index + 1) + ". " + getSteps()[index]} </Typography> ); } else if ( getActiveStep(state) > index && !state.stepper_finish ) { return ( <Typography color="textSecondary" style={{ fontSize: "16px" }} > {SelectedOptionLabel(index)} </Typography> ); } return null; })} {state.stepper_finish && ( <Typography color="textPrimary" style={{ fontSize: "18px" }}> Code Display </Typography> )} } </Breadcrumbs> </Grid> <Grid item style={{ float: "right" }}> {!state.stepper_finish && ( <IconButton disabled={isNextDisabled()} variant="contained" onClick={ getActiveStep(state) === steps.length - 1 ? handleFinish : handleNext } className={ isNextDisabled() ? classes.nextButtonDisabled : classes.nextButton } style={{ float: "right" }} > <ArrowForward className={ isNextDisabled() ? classes.arrowDisabled : classes.arrow } /> </IconButton> )} </Grid> </Grid> {/* Progress Bar */} <Grid item style={{ width: "100%" }}> <BorderLinearProgress variant="determinate" value={getProgressBarValue()} /> </Grid> <Grid className={`${classes.fullWidth} ${classes.visualizerHeight}`} item> <Card className={ state.stepper_finish ? classes.rootActionsFinish : classes.rootActions } > <CardContent className={`${classes.fullHeight} ${ state.stepper_state === StepperState.VISUALIZE ? classes.rightMargin : null }`} > {getVizContent()} </CardContent> </Card> </Grid> </Grid> ); } export default VisualizerContainer;