From a9d4449906e4fe2a0b42b9bd20cbb54a4effb737 Mon Sep 17 00:00:00 2001 From: Shannon <38598412+swang1111@users.noreply.github.com> Date: Sun, 9 Jul 2023 23:32:10 -0700 Subject: [PATCH 1/7] Add CSV to AIMs button in icon bar --- package-lock.json | 12 ++++++------ src/components/annotationSearch/index.jsx | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index bddff4d9a..a2e7a6c93 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ejs", - "version": "1.0.0", + "version": "1.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ejs", - "version": "1.0.0", + "version": "1.1.0", "dependencies": { "aimapi": "github:RubinLab/aimapi-js#feat/support-wadors-metadata", "axios": "^0.21.4", @@ -20528,9 +20528,9 @@ } }, "node_modules/typescript": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", - "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, "peer": true, "bin": { @@ -20538,7 +20538,7 @@ "tsserver": "bin/tsserver" }, "engines": { - "node": ">=14.17" + "node": ">=4.2.0" } }, "node_modules/unbox-primitive": { diff --git a/src/components/annotationSearch/index.jsx b/src/components/annotationSearch/index.jsx index 8c656a86b..70cdae95c 100644 --- a/src/components/annotationSearch/index.jsx +++ b/src/components/annotationSearch/index.jsx @@ -19,7 +19,7 @@ import { RiCloseCircleFill } from 'react-icons/ri'; import { FcAbout, FcClearFilters } from 'react-icons/fc'; -import { BiSearch, BiX, BiTrash, BiDownload, BiPlay } from 'react-icons/bi'; +import { BiSearch, BiX, BiTrash, BiDownload, BiPlay, BiUpload } from 'react-icons/bi'; import { BsEyeFill } from 'react-icons/bs'; import { AiOutlineSortAscending, AiOutlineSortDescending } from 'react-icons/ai'; import ReactTooltip from 'react-tooltip'; @@ -1284,6 +1284,7 @@ const AnnotationSearch = props => { {/* {showProjects && ( { setShowProjects(false) }} />)} */} + {(showPlugins && mode !== 'teaching') && (
Date: Mon, 10 Jul 2023 21:01:01 -0700 Subject: [PATCH 2/7] Create csv2aim folder and add upload functionality --- src/components/annotationSearch/index.jsx | 3 ++- src/components/csv2aim/csv2aim.jsx | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 src/components/csv2aim/csv2aim.jsx diff --git a/src/components/annotationSearch/index.jsx b/src/components/annotationSearch/index.jsx index 70cdae95c..4e21f5f71 100644 --- a/src/components/annotationSearch/index.jsx +++ b/src/components/annotationSearch/index.jsx @@ -35,6 +35,7 @@ import { clearSelection, selectAnnotation, updateSearchTableIndex, refreshPage } import AnnotationDownloadModal from '../searchView/annotationDownloadModal'; import UploadModal from '../searchView/uploadModal'; import DeleteAlert from '../management/common/alertDeletionModal'; +import CSV2AIM from '../csv2aim/csv2aim' import { getPluginsForProject, addPluginsToQueue, @@ -1284,7 +1285,7 @@ const AnnotationSearch = props => { {/* {showProjects && ( { setShowProjects(false) }} />)} */} - +
{(showPlugins && mode !== 'teaching') && (
{ + setUploadClicked(false); + }; + + return ( + <> + + {uploadClicked && ( + setUploadClicked(false)} + onResolve={handleSubmitUpload} + /> + )} + + ); +} \ No newline at end of file From 29a23527873690f9f71772a1ada910d1ca485f5c Mon Sep 17 00:00:00 2001 From: Shannon <38598412+swang1111@users.noreply.github.com> Date: Tue, 11 Jul 2023 00:35:04 -0700 Subject: [PATCH 3/7] Create UploadCSV component --- src/components/csv2aim/csv2aim.jsx | 4 +- src/components/csv2aim/uploadcsv.jsx | 197 +++++++++++++++++++++++++++ 2 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 src/components/csv2aim/uploadcsv.jsx diff --git a/src/components/csv2aim/csv2aim.jsx b/src/components/csv2aim/csv2aim.jsx index fc9d84a19..f7e5c4df8 100644 --- a/src/components/csv2aim/csv2aim.jsx +++ b/src/components/csv2aim/csv2aim.jsx @@ -1,6 +1,6 @@ import React, { Component, useState } from "react"; import { BiUpload } from 'react-icons/bi'; -import UploadModal from '../searchView/uploadModal'; +import UploadCSV from './uploadcsv'; export default function CSV2AIM() { const [uploadClicked, setUploadClicked] = useState(false); @@ -13,7 +13,7 @@ export default function CSV2AIM() { <> {uploadClicked && ( - setUploadClicked(false)} onResolve={handleSubmitUpload} /> diff --git a/src/components/csv2aim/uploadcsv.jsx b/src/components/csv2aim/uploadcsv.jsx new file mode 100644 index 000000000..a33ae650c --- /dev/null +++ b/src/components/csv2aim/uploadcsv.jsx @@ -0,0 +1,197 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import PropTypes from 'prop-types'; +import { Modal, Button } from 'react-bootstrap'; +import { + getProjects, + uploadFileToProject +} from '../../services/projectServices'; +import { uploadFileToSubject } from '../../services/subjectServices'; +import { uploadFileToStudy } from '../../services/studyServices'; +import { uploadFileToSeries } from '../../services/seriesServices'; +import { getTemplates } from '../annotationsList/action'; + +let mode; + +class UploadCSV extends React.Component { + mode = sessionStorage.getItem('mode'); + state = { + tiff: false, + osirix: false, + projects: [], + files: [], + projectID: '' + }; + + onSelect = e => { + const { name, checked } = e.target; + this.setState({ [name]: checked }); + }; + + componentDidMount = async () => { + try { + const { pid } = this.props; + if (mode !== 'lite') { + let { data: projects } = await getProjects(); + for (let i = 0; i < projects.length; i++) { + if (projects[i].id === 'all') { + projects.splice(i, 1); + i = i - 1; + continue; + } + if (projects[i].id === 'nonassigned') { + projects.splice(i, 1); + i = i - 1; + continue; + } + } + + const nonSelectablePid = pid === 'nonassigned' || pid === 'all'; + const projectID = + nonSelectablePid && projects.length > 0 + ? projects[0].id + : !nonSelectablePid + ? pid + : ''; + this.setState({ projects, projectID }); + } + } catch (err) { + console.error(err); + } + }; + + onSelectFile = e => { + this.setState({ files: Array.from(e.target.files) }); + }; + + onUpload = () => { + let { + selectedPatients, + selectedStudies, + selectedSeries, + clearTreeData, + onResolve, + onCancel, + onSubmit, + clearTreeExpand + } = this.props; + selectedPatients = Object.values(selectedPatients); + selectedStudies = Object.values(selectedStudies); + selectedSeries = Object.values(selectedSeries); + + const promises = []; + const projectID = this.props.projectID + ? this.props.projectID + : this.state.projectID; + const formData = new FormData(); + this.state.files.forEach((file, index) => { + formData.append(`file${index + 1}`, file); + }); + const config = { + headers: { + 'content-type': 'multipart/form-data' + } + }; + + if (onSubmit) onSubmit(); + if (selectedPatients.length > 0) { + selectedPatients.forEach(el => + promises.push(uploadFileToSubject(formData, config, el)) + ); + } else if (selectedStudies.length > 0) { + selectedStudies.forEach(el => + promises.push(uploadFileToStudy(formData, config, el)) + ); + } else if (selectedSeries.length > 0) { + selectedSeries.forEach(el => + promises.push(uploadFileToSeries(formData, config, el)) + ); + } else { + promises.push(uploadFileToProject(formData, config, projectID)); + } + + Promise.all(promises) + .then(() => { + if (clearTreeData) { + // clearTreeData(); + localStorage.setItem('treeData', JSON.stringify({})); + clearTreeExpand(); + } + this.props.dispatch(getTemplates()); + if (onResolve) onResolve(); + }) + .catch(err => { + console.error(err); + if (onResolve) onResolve(); + }); + onCancel(); + this.setState({ projectID: '' }); + }; + + selectProject = e => { + this.setState({ projectID: e.target.value }); + }; + + renderUploadFileButton = () => { + return ( +
+ Select file: + +
+ ); + }; + + render = () => { + let disabled = this.state.files.length === 0; + let className = 'alert-upload'; + className = this.props.className + ? `${className} ${this.props.className}` + : className; + const { projects } = this.state; + return ( + + + Upload CSV + + + {this.renderUploadFileButton()} + + + + + + + ); + }; +} + +UploadCSV.propTypes = { + onCancel: PropTypes.func.isRequired, + clearTreeData: PropTypes.func, + onResolve: PropTypes.func, + onSubmit: PropTypes.func, + pid: PropTypes.string, + selectedPatients: PropTypes.object, + selectedSeries: PropTypes.object, + selectedStudies: PropTypes.object, + clearTreeExpand: PropTypes.func + +}; + +const mapStateToProps = state => { + return { + selectedPatients: state.annotationsListReducer.selectedPatients, + selectedStudies: state.annotationsListReducer.selectedStudies, + selectedSeries: state.annotationsListReducer.selectedSeries + }; +}; + +export default connect(mapStateToProps)(UploadCSV); From 01a961eaca1d1c52b3bf896d46fd60d53f1e2d74 Mon Sep 17 00:00:00 2001 From: Shannon <38598412+swang1111@users.noreply.github.com> Date: Wed, 26 Jul 2023 20:43:56 -0700 Subject: [PATCH 4/7] send csv to backend --- src/components/csv2aim/csvServices.js | 13 ++++ src/components/csv2aim/uploadcsv.jsx | 92 +++++++++++---------------- 2 files changed, 49 insertions(+), 56 deletions(-) create mode 100644 src/components/csv2aim/csvServices.js diff --git a/src/components/csv2aim/csvServices.js b/src/components/csv2aim/csvServices.js new file mode 100644 index 000000000..dbd2e7404 --- /dev/null +++ b/src/components/csv2aim/csvServices.js @@ -0,0 +1,13 @@ +import http from "../../services/httpService"; + +export function uploadCsv(csvData, config) { + let url = http.apiUrl() + "/processCsv"; +// const aimData = new FormData(); +// aimData.append("file", csv); +// const config = { +// headers: { +// "content-type": "multipart/form-data", +// }, +// }; + return http.post(url, csvData, config); +} \ No newline at end of file diff --git a/src/components/csv2aim/uploadcsv.jsx b/src/components/csv2aim/uploadcsv.jsx index a33ae650c..be686156a 100644 --- a/src/components/csv2aim/uploadcsv.jsx +++ b/src/components/csv2aim/uploadcsv.jsx @@ -2,13 +2,7 @@ import React from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import { Modal, Button } from 'react-bootstrap'; -import { - getProjects, - uploadFileToProject -} from '../../services/projectServices'; -import { uploadFileToSubject } from '../../services/subjectServices'; -import { uploadFileToStudy } from '../../services/studyServices'; -import { uploadFileToSeries } from '../../services/seriesServices'; +import { uploadCsv }from './csvServices'; import { getTemplates } from '../annotationsList/action'; let mode; @@ -28,37 +22,37 @@ class UploadCSV extends React.Component { this.setState({ [name]: checked }); }; - componentDidMount = async () => { - try { - const { pid } = this.props; - if (mode !== 'lite') { - let { data: projects } = await getProjects(); - for (let i = 0; i < projects.length; i++) { - if (projects[i].id === 'all') { - projects.splice(i, 1); - i = i - 1; - continue; - } - if (projects[i].id === 'nonassigned') { - projects.splice(i, 1); - i = i - 1; - continue; - } - } - - const nonSelectablePid = pid === 'nonassigned' || pid === 'all'; - const projectID = - nonSelectablePid && projects.length > 0 - ? projects[0].id - : !nonSelectablePid - ? pid - : ''; - this.setState({ projects, projectID }); - } - } catch (err) { - console.error(err); - } - }; + // componentDidMount = async () => { + // try { + // const { pid } = this.props; + // if (mode !== 'lite') { + // let { data: projects } = await getProjects(); + // for (let i = 0; i < projects.length; i++) { + // if (projects[i].id === 'all') { + // projects.splice(i, 1); + // i = i - 1; + // continue; + // } + // if (projects[i].id === 'nonassigned') { + // projects.splice(i, 1); + // i = i - 1; + // continue; + // } + // } + + // const nonSelectablePid = pid === 'nonassigned' || pid === 'all'; + // const projectID = + // nonSelectablePid && projects.length > 0 + // ? projects[0].id + // : !nonSelectablePid + // ? pid + // : ''; + // this.setState({ projects, projectID }); + // } + // } catch (err) { + // console.error(err); + // } + // }; onSelectFile = e => { this.setState({ files: Array.from(e.target.files) }); @@ -80,9 +74,9 @@ class UploadCSV extends React.Component { selectedSeries = Object.values(selectedSeries); const promises = []; - const projectID = this.props.projectID - ? this.props.projectID - : this.state.projectID; + // const projectID = this.props.projectID + // ? this.props.projectID + // : this.state.projectID; const formData = new FormData(); this.state.files.forEach((file, index) => { formData.append(`file${index + 1}`, file); @@ -94,21 +88,7 @@ class UploadCSV extends React.Component { }; if (onSubmit) onSubmit(); - if (selectedPatients.length > 0) { - selectedPatients.forEach(el => - promises.push(uploadFileToSubject(formData, config, el)) - ); - } else if (selectedStudies.length > 0) { - selectedStudies.forEach(el => - promises.push(uploadFileToStudy(formData, config, el)) - ); - } else if (selectedSeries.length > 0) { - selectedSeries.forEach(el => - promises.push(uploadFileToSeries(formData, config, el)) - ); - } else { - promises.push(uploadFileToProject(formData, config, projectID)); - } + promises.push(uploadCsv(formData, config)) Promise.all(promises) .then(() => { From 4a4d400ba0aa9f6c29d8aa4a4744c35048a89083 Mon Sep 17 00:00:00 2001 From: Shannon <38598412+swang1111@users.noreply.github.com> Date: Wed, 2 Aug 2023 17:42:55 -0700 Subject: [PATCH 5/7] move csv http call to annotationServices.js --- src/components/csv2aim/csvServices.js | 13 ------------- src/components/csv2aim/uploadcsv.jsx | 2 +- src/services/annotationServices.js | 12 ++++++++++++ 3 files changed, 13 insertions(+), 14 deletions(-) delete mode 100644 src/components/csv2aim/csvServices.js diff --git a/src/components/csv2aim/csvServices.js b/src/components/csv2aim/csvServices.js deleted file mode 100644 index dbd2e7404..000000000 --- a/src/components/csv2aim/csvServices.js +++ /dev/null @@ -1,13 +0,0 @@ -import http from "../../services/httpService"; - -export function uploadCsv(csvData, config) { - let url = http.apiUrl() + "/processCsv"; -// const aimData = new FormData(); -// aimData.append("file", csv); -// const config = { -// headers: { -// "content-type": "multipart/form-data", -// }, -// }; - return http.post(url, csvData, config); -} \ No newline at end of file diff --git a/src/components/csv2aim/uploadcsv.jsx b/src/components/csv2aim/uploadcsv.jsx index be686156a..ecae4682f 100644 --- a/src/components/csv2aim/uploadcsv.jsx +++ b/src/components/csv2aim/uploadcsv.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import { Modal, Button } from 'react-bootstrap'; -import { uploadCsv }from './csvServices'; +import { uploadCsv }from '../../services/annotationServices'; import { getTemplates } from '../annotationsList/action'; let mode; diff --git a/src/services/annotationServices.js b/src/services/annotationServices.js index 8c436efde..a73c451f7 100644 --- a/src/services/annotationServices.js +++ b/src/services/annotationServices.js @@ -205,3 +205,15 @@ export function uploadSegmentation(segmentation, segName, projectId = "lite") { // }; // return http.post(url, segData, config); // } + +export function uploadCsv(csvData, config) { + let url = http.apiUrl() + "/processCsv"; +// const aimData = new FormData(); +// aimData.append("file", csv); +// const config = { +// headers: { +// "content-type": "multipart/form-data", +// }, +// }; + return http.post(url, csvData, config); +} \ No newline at end of file From d4b02886dbcbfd92f24c65b8cba1f90a56217f10 Mon Sep 17 00:00:00 2001 From: Shannon <38598412+swang1111@users.noreply.github.com> Date: Wed, 2 Aug 2023 23:41:46 -0700 Subject: [PATCH 6/7] clean up code --- src/components/csv2aim/uploadcsv.jsx | 38 ---------------------------- 1 file changed, 38 deletions(-) diff --git a/src/components/csv2aim/uploadcsv.jsx b/src/components/csv2aim/uploadcsv.jsx index ecae4682f..44a629b97 100644 --- a/src/components/csv2aim/uploadcsv.jsx +++ b/src/components/csv2aim/uploadcsv.jsx @@ -5,8 +5,6 @@ import { Modal, Button } from 'react-bootstrap'; import { uploadCsv }from '../../services/annotationServices'; import { getTemplates } from '../annotationsList/action'; -let mode; - class UploadCSV extends React.Component { mode = sessionStorage.getItem('mode'); state = { @@ -22,38 +20,6 @@ class UploadCSV extends React.Component { this.setState({ [name]: checked }); }; - // componentDidMount = async () => { - // try { - // const { pid } = this.props; - // if (mode !== 'lite') { - // let { data: projects } = await getProjects(); - // for (let i = 0; i < projects.length; i++) { - // if (projects[i].id === 'all') { - // projects.splice(i, 1); - // i = i - 1; - // continue; - // } - // if (projects[i].id === 'nonassigned') { - // projects.splice(i, 1); - // i = i - 1; - // continue; - // } - // } - - // const nonSelectablePid = pid === 'nonassigned' || pid === 'all'; - // const projectID = - // nonSelectablePid && projects.length > 0 - // ? projects[0].id - // : !nonSelectablePid - // ? pid - // : ''; - // this.setState({ projects, projectID }); - // } - // } catch (err) { - // console.error(err); - // } - // }; - onSelectFile = e => { this.setState({ files: Array.from(e.target.files) }); }; @@ -74,9 +40,6 @@ class UploadCSV extends React.Component { selectedSeries = Object.values(selectedSeries); const promises = []; - // const projectID = this.props.projectID - // ? this.props.projectID - // : this.state.projectID; const formData = new FormData(); this.state.files.forEach((file, index) => { formData.append(`file${index + 1}`, file); @@ -93,7 +56,6 @@ class UploadCSV extends React.Component { Promise.all(promises) .then(() => { if (clearTreeData) { - // clearTreeData(); localStorage.setItem('treeData', JSON.stringify({})); clearTreeExpand(); } From 9b2b5d0873ad071a4bcc6c7fbd527ef9382a5ca8 Mon Sep 17 00:00:00 2001 From: Shannon <38598412+swang1111@users.noreply.github.com> Date: Thu, 3 Aug 2023 00:05:52 -0700 Subject: [PATCH 7/7] remove unnecessary props --- src/components/csv2aim/uploadcsv.jsx | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/components/csv2aim/uploadcsv.jsx b/src/components/csv2aim/uploadcsv.jsx index 44a629b97..9ddfe11fe 100644 --- a/src/components/csv2aim/uploadcsv.jsx +++ b/src/components/csv2aim/uploadcsv.jsx @@ -1,5 +1,4 @@ import React from 'react'; -import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import { Modal, Button } from 'react-bootstrap'; import { uploadCsv }from '../../services/annotationServices'; @@ -26,18 +25,12 @@ class UploadCSV extends React.Component { onUpload = () => { let { - selectedPatients, - selectedStudies, - selectedSeries, clearTreeData, onResolve, onCancel, onSubmit, clearTreeExpand } = this.props; - selectedPatients = Object.values(selectedPatients); - selectedStudies = Object.values(selectedStudies); - selectedSeries = Object.values(selectedSeries); const promises = []; const formData = new FormData(); @@ -121,19 +114,8 @@ UploadCSV.propTypes = { onResolve: PropTypes.func, onSubmit: PropTypes.func, pid: PropTypes.string, - selectedPatients: PropTypes.object, - selectedSeries: PropTypes.object, - selectedStudies: PropTypes.object, clearTreeExpand: PropTypes.func }; -const mapStateToProps = state => { - return { - selectedPatients: state.annotationsListReducer.selectedPatients, - selectedStudies: state.annotationsListReducer.selectedStudies, - selectedSeries: state.annotationsListReducer.selectedSeries - }; -}; - -export default connect(mapStateToProps)(UploadCSV); +export default UploadCSV;