From 54b5463be3614eefede8141fef6d0511b1784518 Mon Sep 17 00:00:00 2001 From: josephthaliath Date: Tue, 18 Oct 2022 15:27:00 +0530 Subject: [PATCH] Pipeline view and status view functionality Issue-Id: AIMLFW-2 Signed-off-by: josephthaliath Change-Id: Ib750d927071443823ae14f378372d0979486225d --- src/components/home/HomePage.js | 53 +++++++ src/components/home/pipelines/UploadPipeline.js | 131 ++++++++++++++++ src/components/home/status/Checkbox.js | 37 +++++ src/components/home/status/Popup.js | 40 +++++ src/components/home/status/StatusPageRows.js | 194 ++++++++++++++++++++++++ src/components/home/status/StepsState.css | 65 ++++++++ src/components/home/status/StepsState.js | 109 +++++++++++++ src/components/home/status/TrainingJobInfo.js | 147 ++++++++++++++++++ 8 files changed, 776 insertions(+) create mode 100644 src/components/home/HomePage.js create mode 100644 src/components/home/pipelines/UploadPipeline.js create mode 100644 src/components/home/status/Checkbox.js create mode 100644 src/components/home/status/Popup.js create mode 100644 src/components/home/status/StatusPageRows.js create mode 100644 src/components/home/status/StepsState.css create mode 100644 src/components/home/status/StepsState.js create mode 100644 src/components/home/status/TrainingJobInfo.js diff --git a/src/components/home/HomePage.js b/src/components/home/HomePage.js new file mode 100644 index 0000000..a3f0749 --- /dev/null +++ b/src/components/home/HomePage.js @@ -0,0 +1,53 @@ +// ================================================================================== + +// Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// ================================================================================== + +import React from 'react'; +import CreateTrainingJob from './create/CreateTrainingJob'; +import StatusPageRows from './status/StatusPageRows'; +import UploadPipelineForm from './pipelines/UploadPipeline' +import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'; +import NavbarComponent from './navbar/NavbarComponent'; + +class HomePage extends React.Component { + + render() { + return ( + <> + + + + + + + + + + + ); + } +} + +export default HomePage; + + +class Home extends React.Component{ + render() { + return <>; + } +} + diff --git a/src/components/home/pipelines/UploadPipeline.js b/src/components/home/pipelines/UploadPipeline.js new file mode 100644 index 0000000..ba4f530 --- /dev/null +++ b/src/components/home/pipelines/UploadPipeline.js @@ -0,0 +1,131 @@ +// ================================================================================== + +// Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// ================================================================================== + +import React from 'react' +import Form from 'react-bootstrap/Form' +import Button from 'react-bootstrap/Button' +import axios from 'axios'; +import * as CONSTANTS from '../common/Constants' + +class UploadPipelineForm extends React.Component { + constructor(props) { + super(props); + + this.state = { + fileName: '', + plName: '', + UCMgr_baseUrl: CONSTANTS.UCMgr_baseUrl + } + this.handleInputChange = this.handleInputChange.bind(this); + } + + handleInputChange(event) { + console.log(event) + this.setState({ + fileName: event.target.files[0] + }) + console.log('handleInputChange',this.fileName) + } + + handlePlNameChange = (event) => { + this.setState({ + plName: event.target.value + }) + } + + + + resetFrom = (event)=> { + this.setState({ + fileName: '', + plName: '', + }) + console.log(this.state) + event.target.value = null; + } + + handleSubmit = event => { + const data = new FormData() + data.append('file', this.state.fileName) + + let url = this.state.UCMgr_baseUrl + "/pipelines/" + this.state.plName + "/upload"; + + //Option-3 + axios.post(url,data + ).then(res => { + console.log('Pipeline responsed ', res) + console.log('Status responsed ', res.status) + if(res.status === 200) { + console.log('Pipeline uploaded ', res.status) + alert(res.data.result) + this.resetFrom(event) + + } else { + console.log('Upload pipeline error:' , res) + } + }) + .catch(error => { + console.log('Error in uploading pipeline',error.response) + alert("Pipeline upload failed: " + error.response.data.result) + }) + .then(function () { + // always executed + }) + + console.log('something') + event.preventDefault(); + } + + handleCreatePipeline = event => { + console.log('handleCreatePipeline clicked..', event) + window.open(CONSTANTS.notebook_url + '/tree', "_blank") + } + + render() { + return ( + <> +
+ + {' '} + +
+ + Pipeline Name* + + + + + + + +
+
+ + + ); + } +} + +export default UploadPipelineForm; \ No newline at end of file diff --git a/src/components/home/status/Checkbox.js b/src/components/home/status/Checkbox.js new file mode 100644 index 0000000..c2a19ff --- /dev/null +++ b/src/components/home/status/Checkbox.js @@ -0,0 +1,37 @@ +// ================================================================================== + +// Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// ================================================================================== + +import React from 'react' + +export const Checkbox = React.forwardRef( + ({ indeterminate, ...rest }, ref) => { + const defaultRef = React.useRef() + const resolvedRef = ref || defaultRef + + React.useEffect(() => { + resolvedRef.current.indeterminate = indeterminate + }, [resolvedRef, indeterminate]) + + return ( + <> + + + ) + } + ) + \ No newline at end of file diff --git a/src/components/home/status/Popup.js b/src/components/home/status/Popup.js new file mode 100644 index 0000000..1d8add9 --- /dev/null +++ b/src/components/home/status/Popup.js @@ -0,0 +1,40 @@ +// ================================================================================== + +// Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// ================================================================================== + + +import React from 'react'; +import {Modal} from 'react-bootstrap'; + +function Popup(props){ + return ( + <> + + + + {props.title} + + + + {props.children} + + + + ); +} + +export default Popup; \ No newline at end of file diff --git a/src/components/home/status/StatusPageRows.js b/src/components/home/status/StatusPageRows.js new file mode 100644 index 0000000..713dbf9 --- /dev/null +++ b/src/components/home/status/StatusPageRows.js @@ -0,0 +1,194 @@ +// ================================================================================== + +// Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// ================================================================================== + +import React, { useMemo, useState, useEffect } from 'react'; +import BTable from 'react-bootstrap/Table'; +import Button from 'react-bootstrap/Button'; +import { useTable, useRowSelect } from 'react-table'; +import {UCMgr_baseUrl} from '../common/Constants'; +import axios from 'axios'; +import { Checkbox } from './Checkbox'; +import Popup from './Popup'; +import TrainingJobInfo from './TrainingJobInfo'; +import StepsState from './StepsState'; + +const StatusPageRows = () => { + + const [trainingJobs, setTrainingJobs] = useState([]) + + const [stepsStatePopup, setStepsStatePopup] = useState(false); + const [stepsStateTrainingJobAndVersion, setStepsStateTrainingJobNameAndVersion] = useState(null); + const closeStepsStatePopup = () => setStepsStatePopup(false); + + const [infoPopup, setInfoPopup] = useState(false); + const [infoTrainingJobAndVersion, setInfoTrainingJobNameAndVersion] = useState(null); + const closeInfoPopup = () => setInfoPopup(false); + + useEffect(() => { + console.log('useEffect'); + fetchTrainingJobs(); + const timer = setInterval(async ()=>{ + fetchTrainingJobs(); + }, 5000); + return ()=>clearInterval(timer); + }, []); + + + + const fetchTrainingJobs = async () => { + console.log('fetchTrainingJobs UCMgr_baseUrl', UCMgr_baseUrl) + try { + const result = await axios.get(`${UCMgr_baseUrl}/trainingjobs/latest`); + console.log('fetchTrainingJobs Result', result); + console.log('Training Jobs are --> ', result.data.trainingjobs) + setTrainingJobs(result.data.trainingjobs); + + } catch (e) { + console.error(e) + } + } + + + const handleStepStateClick = (trainingjob_name, version) => { + setStepsStateTrainingJobNameAndVersion({ + trainingjob_name : trainingjob_name, + version : version + }); + setStepsStatePopup(true); + }; + + const handleInfoClick = (trainingjob_name, version) => { + setInfoTrainingJobNameAndVersion({ + trainingjob_name : trainingjob_name, + version : version + }); + setInfoPopup(true); + }; + + const columns = useMemo(() => [ + { + id: 'selection', + Header: ({ getToggleAllRowsSelectedProps }) => ( +
+ +
+ ), + Cell: ({ row }) => ( +
+ +
+ ), + }, + { + id : 'trainingjob_name', + Header : 'Training Job Name', + accessor : 'trainingjob_name' + }, + { + id : 'version', + Header : 'Version', + accessor : 'version' + }, + { + id : 'overall_status', + Header : 'Overall Status', + accessor : row => row.overall_status === 'IN_PROGRESS' ? 'IN PROGRESS' : row.overall_status + }, + { + id : 'stepsState', + Header : 'Detailed Status', + Cell : ({row}) => { + return ( + + ); + } + }, + { + id : 'info', + Header : 'Info', + Cell : ({row}) => { + return ( + + ); + } + } + ], []); + const data = useMemo(() => trainingJobs, [trainingJobs]); + + const { + getTableProps, + headerGroups, + rows, + prepareRow, + selectedFlatRows, + toggleAllRowsSelected + } = useTable( + { + columns, + data, + autoResetSelectedRows : false + }, + useRowSelect + ) + + return ( + <> + + + + + + {headerGroups.map(headerGroup => ( + + {headerGroup.headers.map(column => ( + + {column.render('Header')} + + ))} + + ))} + + + + {rows.map((row, i) => { + prepareRow(row) + return ( + + {row.cells.map(cell => { + return ( + + {cell.render('Cell')} + + ) + })} + + ) + })} + + + + + + + + + + ); +} + +export default StatusPageRows; diff --git a/src/components/home/status/StepsState.css b/src/components/home/status/StepsState.css new file mode 100644 index 0000000..c382efd --- /dev/null +++ b/src/components/home/status/StepsState.css @@ -0,0 +1,65 @@ + /* ================================================================================== + + Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + ================================================================================== */ + +.step-progressbar-wrapper{ + +} + +.state{ + background-color: white; + border: 2px solid black; + height: 30px; + width: 30px; + border-radius: 50%; + display: flex; + justify-content: center; + align-items: center; +} + +.step{ + max-width: 400px; +} + +.step-progress-bar-box{ + display: flex; + align-items: center; + column-gap: 20px; + border: 2px solid pink; + padding: 10px; +} + +.step-progress-bar-box-line{ + border-right: 3px solid black; + height: 50px; + width: 50%; +} + +.container-for-step-progress-bar-box-line-and-message{ + display: flex; + column-gap: 20px; +} + +.step-progress-bar-box-line-messsage{ + display: flex; + justify-content: center; + align-items: center; +} + + + + diff --git a/src/components/home/status/StepsState.js b/src/components/home/status/StepsState.js new file mode 100644 index 0000000..b1ae15b --- /dev/null +++ b/src/components/home/status/StepsState.js @@ -0,0 +1,109 @@ +// ================================================================================== + +// Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// ================================================================================== + +import React, { useEffect, useState } from 'react'; +import axios from "axios"; +import './StepsState.css'; +import { UCMgr_baseUrl } from '../common/Constants'; + +const StepsState = (props) => { + const [boxesState, setBoxesState] = useState([]); + const [connectionsState, setConnectionsState] = useState([]); + const [boxes, setBoxes] = useState([]); + useEffect(()=>{ + const periodicTask = async () => { + let res = null; + try{ + res = await axios.get(`${UCMgr_baseUrl}/trainingjobs/${props.trainingjob_name_and_version.trainingjob_name}/${props.trainingjob_name_and_version.version}/steps_state`); + } + catch(error){ + console.log(error); + } + const json_from_backend = res.data; + console.log(`response for ${UCMgr_baseUrl}/trainingjobs/${props.trainingjob_name_and_version.trainingjob_name}/${props.trainingjob_name_and_version.version}/steps_state` , res); + const newBoxesState = []; + const newConnectionsState = []; + const newBoxes = ['Data extraction' , 'Training', 'Trained Model']; + + + newBoxesState.push(json_from_backend.DATA_EXTRACTION); + newBoxesState.push(json_from_backend.TRAINING); + newBoxesState.push(json_from_backend.TRAINED_MODEL); + if(json_from_backend.hasOwnProperty('DEPLOYMENT')){ + newBoxesState.push(json_from_backend.DEPLOYMENT); + newBoxes.push('Deployment'); + } + setBoxesState(newBoxesState); + setBoxes(newBoxes); + + newConnectionsState.push(json_from_backend.DATA_EXTRACTION_AND_TRAINING); + newConnectionsState.push(json_from_backend.TRAINING_AND_TRAINED_MODEL); + if(json_from_backend.hasOwnProperty('TRAINED_MODEL_AND_DEPLOYMENT')){ + newConnectionsState.push(json_from_backend.TRAINED_MODEL_AND_DEPLOYMENT); + } + setConnectionsState(newConnectionsState); + }; + periodicTask(); + const timer = setInterval( async () => { + periodicTask(); + }, 5000); + return () => clearInterval(timer); + + + },[props.trainingjob_name_and_version]); + + return ( +
+ { + boxes.map((item, index) => { + return ( +
+
+
+ {boxesState[index] === 'NOT_STARTED' ? index + 1 : + boxesState[index] === 'FINISHED' ? '✔' : + boxesState[index] === 'FAILED' ? '❌' : + '⌛'} +
+
+ {item} +
+
+ { index + 1 !== boxes.length && + +
+
+
+ { + connectionsState[index] === 'NOT_STARTED' ? 'Not started' : + connectionsState[index] === 'FINISHED' ? '✔ Finished' : + connectionsState[index] === 'FAILED' ? '❌ Failed' : + '⌛ In progress' + } +
+
+ } +
+ ) + }) + } +
+ ); +}; + +export default StepsState; \ No newline at end of file diff --git a/src/components/home/status/TrainingJobInfo.js b/src/components/home/status/TrainingJobInfo.js new file mode 100644 index 0000000..2b81a5c --- /dev/null +++ b/src/components/home/status/TrainingJobInfo.js @@ -0,0 +1,147 @@ +// ================================================================================== + +// Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// ================================================================================== + +import React, { useEffect, useState } from 'react'; +import { Form } from 'react-bootstrap'; +import axios from 'axios'; +import { UCMgr_baseUrl } from '../common/Constants'; +import {convertToCommaSeparatedString, getDatalakeNameWithoutConversion} from '../common/CommonMethods'; + +const TrainingJobInfo = (props) => { + const [trainingJobName, setTrainingJobName] = useState(""); + const [version, setVersion] = useState(""); + const [description, setDescription] = useState(""); + const [featureNames, setFeatureNames] = useState(""); + const [pipeLineName, setPipelineName] = useState(""); + const [experimentName, setExperimentName] = useState(""); + const [featureFilter, setFeatureFilter] = useState(""); + const [hyperParameters, setHyperParameters] = useState(""); + const [metrics, setMetrics] = useState(""); + const [enableVersioning, setEnableVersioning] = useState(false); + const [pipelineVersion, setPipelineVersion] = useState(""); + const [datalakeSource, setDatalakeSource] = useState(""); + const [modelUrl, setModelUrl] = useState(""); + const [_measurement, set_measurement] = useState(""); + const [bucket, setBucket] = useState(""); + + useEffect(()=>{ + try{ + axios.get(`${UCMgr_baseUrl}/trainingjobs/${props.trainingjob_name_and_version.trainingjob_name}/${props.trainingjob_name_and_version.version}`) + .then(response => { + console.log(`response for ${UCMgr_baseUrl}/trainingjobs/${props.trainingjob_name_and_version.trainingjob_name}/${props.trainingjob_name_and_version.version}`, response); + console.log(response.data); + setTrainingJobName(response.data.trainingjob.trainingjob_name); + setVersion(response.data.trainingjob.version); + setDescription(response.data.trainingjob.description); + setFeatureNames(response.data.trainingjob.feature_list); + setPipelineName(response.data.trainingjob.pipeline_name); + setExperimentName(response.data.trainingjob.experiment_name); + setFeatureFilter(response.data.trainingjob.query_filter); + setHyperParameters(convertToCommaSeparatedString(response.data.trainingjob.arguments)); + setMetrics(response.data.trainingjob.accuracy); + setEnableVersioning(response.data.trainingjob.enable_versioning); + setPipelineVersion(response.data.trainingjob.pipeline_version === response.data.trainingjob.pipeline_name ? "1" : response.data.trainingjob.pipeline_version); + setDatalakeSource(getDatalakeNameWithoutConversion(response.data.trainingjob.datalake_source)); + setModelUrl(response.data.trainingjob.model_url); + set_measurement(response.data.trainingjob._measurement); + setBucket(response.data.trainingjob.bucket); + }) + .catch(error => { + console.log(error); + }); + } + catch(error){ + console.log(error); + } + },[props.trainingjob_name_and_version]); + + + return ( + <> +
+ + Training Job Name + + + + Version + + + + Description + + + + Feature Names + + + + Pipeline Name + + + + Experiment Name + + + + Feature Filter + + + + Hyper Parameters + + + + Metrics + + + + + + + Pipeline Version + + + + Datalake Source + + + { + datalakeSource === "Influx DB" && + <> + + _measurement + + + + bucket + + + + } + + Model URL + + +
+ + ); +} + +export default TrainingJobInfo; \ No newline at end of file -- 2.16.6