From ae412f3a260211ceb59f3098ded0faaedac8cd68 Mon Sep 17 00:00:00 2001 From: Jandson Vitorino Date: Mon, 22 Jan 2024 10:56:58 -0300 Subject: [PATCH 01/17] Combine Spec-z Catalogs and Training Set Maker page --- frontend/components/EmailField.js | 53 +++++++ frontend/components/SearchRadius.js | 47 ++++++ frontend/components/SpeczData.js | 82 +++++----- frontend/components/TsmData.js | 88 ++++++----- frontend/pages/specz_catalogs.js | 206 +++++++++++++++++++++++- frontend/pages/training_set_maker.js | 226 ++++++++++++++++++++++++++- 6 files changed, 617 insertions(+), 85 deletions(-) create mode 100644 frontend/components/EmailField.js create mode 100644 frontend/components/SearchRadius.js diff --git a/frontend/components/EmailField.js b/frontend/components/EmailField.js new file mode 100644 index 0000000..7575be1 --- /dev/null +++ b/frontend/components/EmailField.js @@ -0,0 +1,53 @@ +import React, { useState, useEffect } from 'react' +import TextField from '@mui/material/TextField' +import PropTypes from 'prop-types' + +function EmailField({ initialValue = '', onEmailChange, onClearForm }) { + const [email, setEmail] = useState(initialValue) + const [isValidEmail, setIsValidEmail] = useState(true) + + const handleEmailChange = event => { + const newEmail = event.target.value + setEmail(newEmail) + onEmailChange(newEmail) + } + + const handleBlur = () => { + if (email.trim() !== '') { + validateEmail(email) + } + } + + const validateEmail = value => { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ + const isValid = emailRegex.test(value) + setIsValidEmail(isValid) + } + + useEffect(() => { + setEmail(initialValue) + setIsValidEmail(true) + }, [onClearForm, initialValue]) + + return ( +
+ +
+ ) +} + +EmailField.propTypes = { + initialValue: PropTypes.string, + onEmailChange: PropTypes.func, + onClearForm: PropTypes.func +} + +export default EmailField diff --git a/frontend/components/SearchRadius.js b/frontend/components/SearchRadius.js new file mode 100644 index 0000000..ee635af --- /dev/null +++ b/frontend/components/SearchRadius.js @@ -0,0 +1,47 @@ +import React, { useState, useEffect } from 'react' +import TextField from '@mui/material/TextField' +import PropTypes from 'prop-types' + +const SearchRadius = ({ searchRadius, onChange, reset }) => { + const formatSearchRadius = value => { + if (/^\d+\.\d*$/.test(value)) { + return value + } else { + return value.replace(/[^\d.]/g, '') + } + } + + const [localSearchRadius, setLocalSearchRadius] = useState( + formatSearchRadius(searchRadius) + ) + + useEffect(() => { + setLocalSearchRadius(formatSearchRadius(searchRadius)) + }, [reset, searchRadius]) + + const handleRadiusChange = event => { + const inputValue = event.target.value + + if (/^\d+(\.\d*)?$/.test(inputValue)) { + setLocalSearchRadius(inputValue) + onChange(event) + } + } + + return ( + + ) +} + +SearchRadius.propTypes = { + searchRadius: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + onChange: PropTypes.func, + reset: PropTypes.bool +} + +export default SearchRadius diff --git a/frontend/components/SpeczData.js b/frontend/components/SpeczData.js index 9a23f24..905983d 100644 --- a/frontend/components/SpeczData.js +++ b/frontend/components/SpeczData.js @@ -1,3 +1,5 @@ +import * as React from 'react' +import { getProducts } from '../services/product' import { DataGrid } from '@mui/x-data-grid' import moment from 'moment' import { Box } from '@mui/material' @@ -30,13 +32,29 @@ const columns = [ } ] +async function fetchData(filters, query) { + try { + const response = await getProducts({ + filters, + page: 0, + page_size: 25, + sort: [{ field: 'created_at', sort: 'desc' }], + search: query + }) + + return response.results + } catch (error) { + console.error(error) + throw error + } +} + function DataTable({ rows }) { return ( - + row.id} pageSizeOptions={[5, 10]} checkboxSelection /> @@ -48,39 +66,33 @@ DataTable.propTypes = { rows: PropTypes.arrayOf(PropTypes.object).isRequired } -const rows = [ - { - id: 1, - display_name: 'Jandson 1', - uploaded_by: 'User1', - created_at: '2023-01-01' - }, - { - id: 2, - display_name: 'Jandson 2', - uploaded_by: 'User2', - created_at: '2023-02-15' - }, - { - id: 3, - display_name: 'Jandson 3', - uploaded_by: 'User3', - created_at: '2023-03-22' - }, - { - id: 4, - display_name: 'Jandson V', - uploaded_by: 'User1', - created_at: '2023-04-10' - }, - { - id: 5, - display_name: 'Jandson Try', - uploaded_by: 'User2', - created_at: '2023-05-05' - } -] +function DataTableWrapper({ filters, query }) { + const [rows, setRows] = React.useState([]) + + React.useEffect(() => { + const fetchAndSetData = async () => { + try { + const data = await fetchData(filters, query) + setRows(data) + } catch (error) { + console.error(error) + } + } + + fetchAndSetData() + }, [filters, query]) -export default function App() { return } + +DataTableWrapper.propTypes = { + filters: PropTypes.object, + query: PropTypes.string +} + +DataTableWrapper.defaultProps = { + filters: {}, + query: '' +} + +export default DataTableWrapper diff --git a/frontend/components/TsmData.js b/frontend/components/TsmData.js index eceffca..8a4acfe 100644 --- a/frontend/components/TsmData.js +++ b/frontend/components/TsmData.js @@ -1,3 +1,5 @@ +import * as React from 'react' +import { getProducts } from '../services/product' import { DataGrid } from '@mui/x-data-grid' import moment from 'moment' import { Box } from '@mui/material' @@ -30,15 +32,27 @@ const columns = [ } ] +async function fetchData(filters, query) { + try { + const response = await getProducts({ + filters, + page: 0, + page_size: 25, + sort: [{ field: 'created_at', sort: 'desc' }], + search: query + }) + + return response.results + } catch (error) { + console.error(error) + throw error + } +} + function DataTable({ rows }) { return ( - - row.id} - pageSizeOptions={[5, 10]} - /> + + ) } @@ -47,39 +61,33 @@ DataTable.propTypes = { rows: PropTypes.arrayOf(PropTypes.object).isRequired } -const rows = [ - { - id: 1, - display_name: 'Jandson 1', - uploaded_by: 'User1', - created_at: '2023-01-01' - }, - { - id: 2, - display_name: 'Jandson 2', - uploaded_by: 'User2', - created_at: '2023-02-15' - }, - { - id: 3, - display_name: 'Jandson 3', - uploaded_by: 'User3', - created_at: '2023-03-22' - }, - { - id: 4, - display_name: 'Jandson V', - uploaded_by: 'User1', - created_at: '2023-04-10' - }, - { - id: 5, - display_name: 'Jandson Try', - uploaded_by: 'User2', - created_at: '2023-05-05' - } -] +function DataTableWrapper({ filters, query }) { + const [rows, setRows] = React.useState([]) + + React.useEffect(() => { + const fetchAndSetData = async () => { + try { + const data = await fetchData(filters, query) + setRows(data) + } catch (error) { + console.error(error) + } + } + + fetchAndSetData() + }, [filters, query]) -export default function App() { return } + +DataTableWrapper.propTypes = { + filters: PropTypes.object, + query: PropTypes.string +} + +DataTableWrapper.defaultProps = { + filters: {}, + query: '' +} + +export default DataTableWrapper diff --git a/frontend/pages/specz_catalogs.js b/frontend/pages/specz_catalogs.js index 3cdac39..ea77795 100644 --- a/frontend/pages/specz_catalogs.js +++ b/frontend/pages/specz_catalogs.js @@ -1,11 +1,207 @@ -import React from 'react' -import { Typography, Box } from '@mui/material' +import InfoIcon from '@mui/icons-material/Info' +import Box from '@mui/material/Box' +import Button from '@mui/material/Button' +import Card from '@mui/material/Card' +import CardContent from '@mui/material/CardContent' +import Grid from '@mui/material/Grid' +import IconButton from '@mui/material/IconButton' +import MenuItem from '@mui/material/MenuItem' +import Paper from '@mui/material/Paper' +import Select from '@mui/material/Select' +import Snackbar from '@mui/material/Snackbar' +import SnackbarContent from '@mui/material/SnackbarContent' +import TextField from '@mui/material/TextField' +import Typography from '@mui/material/Typography' +import React, { useState } from 'react' +import EmailField from '../components/EmailField' +import SearchField from '../components/SearchField' +import SearchRadius from '../components/SearchRadius' +import SpeczData from '../components/SpeczData' +import useStyles from '../styles/pages/products' function SpeczCatalogs() { + const classes = useStyles() + + const [combinedCatalogName, setCombinedCatalogName] = useState('') + const [search, setSearch] = React.useState('') + const filters = React.useState() + const [selectedSpeczCatalogs, setSelectedSpeczCatalogs] = useState([]) + const [searchRadius, setSearchRadius] = useState('1.0') + const [selectedOption, setSelectedOption] = useState('keepAll') + const [email, setEmail] = useState('') + const [snackbarOpen, setSnackbarOpen] = useState(false) + + const handleCatalogNameChange = event => { + setCombinedCatalogName(event.target.value) + } + + const handleSpeczCatalogsChange = event => { + setSelectedSpeczCatalogs(event.target.value) + } + + const handleSearchRadiusChange = event => { + const newValue = parseFloat(event.target.value) + setSearchRadius(isNaN(newValue) ? '' : newValue.toString()) + } + + const handleEmailChange = newEmail => { + setEmail(newEmail) + } + + const handleClearForm = () => { + setCombinedCatalogName('') + setSelectedSpeczCatalogs([]) + setSearchRadius('1.0') + setSelectedOption('keepAll') + setEmail('') + } + + const handleRun = () => { + setSnackbarOpen(true) + } + + const handleSnackbarClose = () => { + setSnackbarOpen(false) + } return ( - - Coming soon... - + + + + + + Combine Spec-z Catalogs + + + + + + + + + 1. Combined catalog name: + + + + + + + + + + 2. Select the Spec-z Catalogs to include in your sample: + + + + + + + setSearch(query)} /> + + + + + + + + + + + + + 3. Select the cross-matching configuration choices: + + + + + Search Radius (arcsec):{' '} + + + + + + In case of multiple spec-z measurements for the same object: + {' '} + + + + + + 4. Enter an email address to be notified when the process is + complete (opcional): + + + + + + + + + + + + + + + + ) } diff --git a/frontend/pages/training_set_maker.js b/frontend/pages/training_set_maker.js index 30fabef..8a3b98b 100644 --- a/frontend/pages/training_set_maker.js +++ b/frontend/pages/training_set_maker.js @@ -1,11 +1,227 @@ -import React from 'react' -import { Typography, Box } from '@mui/material' +import InfoIcon from '@mui/icons-material/Info' +import Box from '@mui/material/Box' +import Button from '@mui/material/Button' +import Card from '@mui/material/Card' +import CardContent from '@mui/material/CardContent' +import Grid from '@mui/material/Grid' +import IconButton from '@mui/material/IconButton' +import MenuItem from '@mui/material/MenuItem' +import Paper from '@mui/material/Paper' +import Select from '@mui/material/Select' +import Snackbar from '@mui/material/Snackbar' +import SnackbarContent from '@mui/material/SnackbarContent' +import TextField from '@mui/material/TextField' +import Typography from '@mui/material/Typography' +import React, { useState } from 'react' +import EmailField from '../components/EmailField' +import SearchField from '../components/SearchField' +import SearchRadius from '../components/SearchRadius' +import TsmData from '../components/TsmData' +import useStyles from '../styles/pages/products' function TrainingSetMaker() { + const classes = useStyles() + + const [combinedCatalogName, setCombinedCatalogName] = useState('') + const [search, setSearch] = React.useState('') + const filters = React.useState() + const [selectedSpeczCatalogs, setSelectedTrainingSet] = useState([]) + const [searchRadius, setSearchRadius] = useState('1.0') + const [selectedOption, setSelectedOption] = useState('pickOne') + const [email, setEmail] = useState('') + const [snackbarOpen, setSnackbarOpen] = useState(false) + const [selectedLsstCatalog, setSelectedLsstCatalog] = useState('DP0.2') + + const handleCatalogNameChange = event => { + setCombinedCatalogName(event.target.value) + } + + const handleSpeczCatalogsChange = event => { + setSelectedTrainingSet(event.target.value) + } + + const handleSearchRadiusChange = event => { + const newValue = parseFloat(event.target.value) + setSearchRadius(isNaN(newValue) ? '' : newValue.toString()) + } + + const handleLsstCatalogChange = event => { + setSelectedLsstCatalog(event.target.value) + } + + const handleEmailChange = newEmail => { + setEmail(newEmail) + } + + const handleClearForm = () => { + setCombinedCatalogName('') + setSelectedTrainingSet([]) + setSearchRadius('1.0') + setSelectedOption('pickOne') + setEmail('') + setSelectedLsstCatalog('DP0.2') + } + + const handleRun = () => { + setSnackbarOpen(true) + } + + const handleSnackbarClose = () => { + setSnackbarOpen(false) + } return ( - - Coming soon... - + + + + + + Training Set Maker + + + + + + + + + + 1. Combined catalog name: + + + + + + + + + + + 2. Select the Spec-z Catalogs to include in your sample: + + + + + + + setSearch(query)} /> + + + + + + + + + + + + + + 3. Select the LSST objects catalog: + + + + + + + 4. Select the cross-matching configuration choices: + + Search Radius (arcsec):{' '} + + + + + In case of multiple spec-z measurements for the same object: + {' '} + + + + + + + + 5. Enter an email address to be notified when the process is + complete (opcional): + + + + + + + + + + + + + + + + ) } From 0c8b122b67c503af131d57a89c02210ecd771c52 Mon Sep 17 00:00:00 2001 From: Jandson Vitorino Date: Sun, 28 Jan 2024 20:48:50 -0300 Subject: [PATCH 02/17] removal of the filter check box, where there was no need for it as the table already does this filtering, and small adjustments. --- frontend/pages/specz_catalogs.js | 56 +++++++++-------------- frontend/pages/training_set_maker.js | 68 +++++++++++++--------------- 2 files changed, 53 insertions(+), 71 deletions(-) diff --git a/frontend/pages/specz_catalogs.js b/frontend/pages/specz_catalogs.js index ea77795..0881847 100644 --- a/frontend/pages/specz_catalogs.js +++ b/frontend/pages/specz_catalogs.js @@ -17,15 +17,14 @@ import EmailField from '../components/EmailField' import SearchField from '../components/SearchField' import SearchRadius from '../components/SearchRadius' import SpeczData from '../components/SpeczData' -import useStyles from '../styles/pages/products' +import { useTheme } from '@mui/system' function SpeczCatalogs() { - const classes = useStyles() + const theme = useTheme() const [combinedCatalogName, setCombinedCatalogName] = useState('') const [search, setSearch] = React.useState('') const filters = React.useState() - const [selectedSpeczCatalogs, setSelectedSpeczCatalogs] = useState([]) const [searchRadius, setSearchRadius] = useState('1.0') const [selectedOption, setSelectedOption] = useState('keepAll') const [email, setEmail] = useState('') @@ -35,10 +34,6 @@ function SpeczCatalogs() { setCombinedCatalogName(event.target.value) } - const handleSpeczCatalogsChange = event => { - setSelectedSpeczCatalogs(event.target.value) - } - const handleSearchRadiusChange = event => { const newValue = parseFloat(event.target.value) setSearchRadius(isNaN(newValue) ? '' : newValue.toString()) @@ -50,7 +45,6 @@ function SpeczCatalogs() { const handleClearForm = () => { setCombinedCatalogName('') - setSelectedSpeczCatalogs([]) setSearchRadius('1.0') setSelectedOption('keepAll') setEmail('') @@ -63,12 +57,22 @@ function SpeczCatalogs() { const handleSnackbarClose = () => { setSnackbarOpen(false) } + + const styles = { + root: { + transition: 'box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms', + borderRadius: '4px', + padding: theme.spacing(3), + flex: '1 1 0%' + } + } + return ( - + - + Combine Spec-z Catalogs - + 1. Combined catalog name: - - 2. Select the Spec-z Catalogs to include in your sample: - - - - - - + + + 2. Select the Spec-z Catalogs to include in your sample: + setSearch(query)} /> @@ -133,7 +120,7 @@ function SpeczCatalogs() { - + 3. Select the cross-matching configuration choices: @@ -153,7 +140,6 @@ function SpeczCatalogs() { - name - upload by - created at - - - - + + + 2. Select the Spec-z Catalogs to include in your sample: + setSearch(query)} /> @@ -149,9 +136,19 @@ function TrainingSetMaker() { onChange={handleLsstCatalogChange} sx={{ marginLeft: '16px' }} > + + DP0.1 + DP0.2 - DP1 - DP2 + + DP1 + + + DP2 + + + DR1 + @@ -173,7 +170,6 @@ function TrainingSetMaker() { @@ -167,18 +175,22 @@ function TrainingSetMaker() { 4. Select the cross-matching configuration choices: - + + {' '} + {/* Espaço adicionado */} Search Radius (arcsec):{' '} - + + {' '} + {/* Espaço adicionado */} n neighbors:{' '} - @@ -189,10 +201,10 @@ function TrainingSetMaker() { value={selectedOption} onChange={event => setSelectedOption(event.target.value)} > - Keep the closet only + Keep the closest only Keep all - Compute mean redshift all candidates + Compute mean redshift for all candidates @@ -202,7 +214,7 @@ function TrainingSetMaker() { 5. Enter an email address to be notified when the process is - complete (opcional): + complete (optional): Date: Mon, 7 Oct 2024 21:08:53 +0000 Subject: [PATCH 07/17] Enhancements to pipeline components and improvements to the main pipeline interface --- frontend/components/EmailField.js | 106 +++++++-------- frontend/components/NNeighbors.js | 10 +- frontend/components/SearchRadius.js | 15 +-- frontend/components/TsmData.js | 103 +++++++------- frontend/pages/specz_catalogs.js | 19 --- frontend/pages/training_set_maker.js | 193 ++++++++++++++------------- frontend/services/pipiline.js | 5 + frontend/services/process.js | 27 ++++ frontend/services/release.js | 6 + 9 files changed, 263 insertions(+), 221 deletions(-) create mode 100644 frontend/services/pipiline.js create mode 100644 frontend/services/process.js create mode 100644 frontend/services/release.js diff --git a/frontend/components/EmailField.js b/frontend/components/EmailField.js index 7575be1..5b2bf70 100644 --- a/frontend/components/EmailField.js +++ b/frontend/components/EmailField.js @@ -1,53 +1,53 @@ -import React, { useState, useEffect } from 'react' -import TextField from '@mui/material/TextField' -import PropTypes from 'prop-types' - -function EmailField({ initialValue = '', onEmailChange, onClearForm }) { - const [email, setEmail] = useState(initialValue) - const [isValidEmail, setIsValidEmail] = useState(true) - - const handleEmailChange = event => { - const newEmail = event.target.value - setEmail(newEmail) - onEmailChange(newEmail) - } - - const handleBlur = () => { - if (email.trim() !== '') { - validateEmail(email) - } - } - - const validateEmail = value => { - const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ - const isValid = emailRegex.test(value) - setIsValidEmail(isValid) - } - - useEffect(() => { - setEmail(initialValue) - setIsValidEmail(true) - }, [onClearForm, initialValue]) - - return ( -
- -
- ) -} - -EmailField.propTypes = { - initialValue: PropTypes.string, - onEmailChange: PropTypes.func, - onClearForm: PropTypes.func -} - -export default EmailField +// import React, { useState, useEffect } from 'react' +// import TextField from '@mui/material/TextField' +// import PropTypes from 'prop-types' + +// function EmailField({ initialValue = '', onEmailChange, onClearForm }) { +// const [email, setEmail] = useState(initialValue) +// const [isValidEmail, setIsValidEmail] = useState(true) + +// const handleEmailChange = event => { +// const newEmail = event.target.value +// setEmail(newEmail) +// onEmailChange(newEmail) +// } + +// const handleBlur = () => { +// if (email.trim() !== '') { +// validateEmail(email) +// } +// } + +// const validateEmail = value => { +// const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ +// const isValid = emailRegex.test(value) +// setIsValidEmail(isValid) +// } + +// useEffect(() => { +// setEmail(initialValue) +// setIsValidEmail(true) +// }, [onClearForm, initialValue]) + +// return ( +//
+// +//
+// ) +// } + +// EmailField.propTypes = { +// initialValue: PropTypes.string, +// onEmailChange: PropTypes.func, +// onClearForm: PropTypes.func +// } + +// export default EmailField diff --git a/frontend/components/NNeighbors.js b/frontend/components/NNeighbors.js index 063382a..6c893dc 100644 --- a/frontend/components/NNeighbors.js +++ b/frontend/components/NNeighbors.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types' const NNeighbors = ({ nNeighbors, onChange, reset }) => { const formatNNeighbors = value => { - if (/^\d+(\.\d+)?$/.test(value)) { + if (/^\d*\.?\d*$/.test(value)) { return value } else { return value.replace(/[^\d.]/g, '') @@ -20,9 +20,10 @@ const NNeighbors = ({ nNeighbors, onChange, reset }) => { }, [reset, nNeighbors]) const handleNeighborsChange = event => { - const inputValue = event.target.value + const inputValue = formatNNeighbors(event.target.value) + const newValue = Math.min(parseFloat(inputValue) || 0, 90) setLocalNNeighbors(inputValue) - onChange(event) + onChange(newValue) } return ( @@ -31,13 +32,14 @@ const NNeighbors = ({ nNeighbors, onChange, reset }) => { variant="outlined" value={localNNeighbors} onChange={handleNeighborsChange} + inputProps={{ inputMode: 'decimal', pattern: '[0-9]*\\.?[0-9]*' }} /> ) } NNeighbors.propTypes = { nNeighbors: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - onChange: PropTypes.func, + onChange: PropTypes.func.isRequired, reset: PropTypes.bool } diff --git a/frontend/components/SearchRadius.js b/frontend/components/SearchRadius.js index ee635af..01c4d9e 100644 --- a/frontend/components/SearchRadius.js +++ b/frontend/components/SearchRadius.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types' const SearchRadius = ({ searchRadius, onChange, reset }) => { const formatSearchRadius = value => { - if (/^\d+\.\d*$/.test(value)) { + if (/^\d*\.?\d*$/.test(value)) { return value } else { return value.replace(/[^\d.]/g, '') @@ -20,12 +20,10 @@ const SearchRadius = ({ searchRadius, onChange, reset }) => { }, [reset, searchRadius]) const handleRadiusChange = event => { - const inputValue = event.target.value - - if (/^\d+(\.\d*)?$/.test(inputValue)) { - setLocalSearchRadius(inputValue) - onChange(event) - } + const inputValue = formatSearchRadius(event.target.value) + const newValue = Math.min(parseFloat(inputValue) || 0, 90) + setLocalSearchRadius(inputValue) + onChange(newValue) } return ( @@ -34,13 +32,14 @@ const SearchRadius = ({ searchRadius, onChange, reset }) => { variant="outlined" value={localSearchRadius} onChange={handleRadiusChange} + inputProps={{ inputMode: 'decimal', pattern: '[0-9]*\\.?[0-9]*' }} /> ) } SearchRadius.propTypes = { searchRadius: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - onChange: PropTypes.func, + onChange: PropTypes.func.isRequired, reset: PropTypes.bool } diff --git a/frontend/components/TsmData.js b/frontend/components/TsmData.js index 8a4acfe..2c05cd0 100644 --- a/frontend/components/TsmData.js +++ b/frontend/components/TsmData.js @@ -1,9 +1,10 @@ -import * as React from 'react' -import { getProducts } from '../services/product' +import { Alert, Box } from '@mui/material' import { DataGrid } from '@mui/x-data-grid' -import moment from 'moment' -import { Box } from '@mui/material' import PropTypes from 'prop-types' +import * as React from 'react' +import { useQuery } from 'react-query' +import moment from 'moment' +import { getProducts } from '../services/product' const columns = [ { @@ -23,7 +24,7 @@ const columns = [ headerName: 'Created at', sortable: true, width: 200, - valueFormatter: params => { + valueFormatter: (params) => { if (!params.value) { return '' } @@ -32,52 +33,62 @@ const columns = [ } ] -async function fetchData(filters, query) { - try { - const response = await getProducts({ +export default function DataTableWrapper({ filters, query }) { + const [page, setPage] = React.useState(0) + const [pageSize, setPageSize] = React.useState(10) + + const { data, status, error, isLoading } = useQuery( + ['productData', { filters, query, page, pageSize }], + () => getProducts({ filters, - page: 0, - page_size: 25, + page, + page_size: pageSize, sort: [{ field: 'created_at', sort: 'desc' }], search: query - }) - - return response.results - } catch (error) { - console.error(error) - throw error - } -} - -function DataTable({ rows }) { - return ( - - - + }), + { + staleTime: Infinity, + refetchInterval: false, + retry: false + } ) -} - -DataTable.propTypes = { - rows: PropTypes.arrayOf(PropTypes.object).isRequired -} -function DataTableWrapper({ filters, query }) { - const [rows, setRows] = React.useState([]) + if (error) return Error loading data. Please try again. - React.useEffect(() => { - const fetchAndSetData = async () => { - try { - const data = await fetchData(filters, query) - setRows(data) - } catch (error) { - console.error(error) - } - } + const filteredData = data?.results?.filter(product => product.product_type_name === 'Spec-z Catalog') || [] - fetchAndSetData() - }, [filters, query]) - - return + return ( + <> + + setPage(newPage)} + onPageSizeChange={(newPageSize) => setPageSize(newPageSize)} + rowsPerPageOptions={[5, 10, 25]} + disableColumnMenu + disableColumnSelector + loading={isLoading} + localeText={{ + noRowsLabel: filteredData.length === 0 && !isLoading ? 'No products found' : 'Loading...', + }} + /> + + + {`Showing ${filteredData.length} products`} + + + ) } DataTableWrapper.propTypes = { @@ -88,6 +99,4 @@ DataTableWrapper.propTypes = { DataTableWrapper.defaultProps = { filters: {}, query: '' -} - -export default DataTableWrapper +} \ No newline at end of file diff --git a/frontend/pages/specz_catalogs.js b/frontend/pages/specz_catalogs.js index 48b14d4..e9e72a3 100644 --- a/frontend/pages/specz_catalogs.js +++ b/frontend/pages/specz_catalogs.js @@ -15,7 +15,6 @@ import SnackbarContent from '@mui/material/SnackbarContent' import TextField from '@mui/material/TextField' import Typography from '@mui/material/Typography' import React, { useState } from 'react' -import EmailField from '../components/EmailField' import SearchField from '../components/SearchField' import SearchRadius from '../components/SearchRadius' import SpeczData from '../components/SpeczData' @@ -29,7 +28,6 @@ function SpeczCatalogs() { const filters = React.useState() const [searchRadius, setSearchRadius] = useState('1.0') const [selectedOption, setSelectedOption] = useState('keepAll') - const [email, setEmail] = useState('') const [snackbarOpen, setSnackbarOpen] = useState(false) const handleCatalogNameChange = event => { @@ -41,15 +39,10 @@ function SpeczCatalogs() { setSearchRadius(isNaN(newValue) ? '' : newValue.toString()) } - const handleEmailChange = newEmail => { - setEmail(newEmail) - } - const handleClearForm = () => { setCombinedCatalogName('') setSearchRadius('1.0') setSelectedOption('keepAll') - setEmail('') } const handleRun = () => { @@ -161,18 +154,6 @@ function SpeczCatalogs() {
- - - 4. Enter an email address to be notified when the process is - complete (opcional): - - - - - + + + + This pipeline uses LSDB developed by LINCC. Please check out the + software documentation on{' '} + + LSDB Read the Docs page + + . + + +
diff --git a/frontend/services/pipiline.js b/frontend/services/pipiline.js new file mode 100644 index 0000000..618f7b7 --- /dev/null +++ b/frontend/services/pipiline.js @@ -0,0 +1,5 @@ +import { api } from './api' + +export const getPipeline = ({ }) => { + return api.get('/api/pipeline/') +} diff --git a/frontend/services/process.js b/frontend/services/process.js new file mode 100644 index 0000000..981c4c0 --- /dev/null +++ b/frontend/services/process.js @@ -0,0 +1,27 @@ +{ + display_name: "combinedCatalogName", + pipeline: 1, + used_config: { + "param": { + "crossmatch": { + "n_neighbors": 1, + "radius_arcsec": 1.0, + "output_catalog_name": "tsm_cross_001" + }, + "duplicate_criteria": "closest" // all // In case of multiple spec-z measurements for the same object: + }, + } + release: "1", // Select the LSST objects catalog + inputs: [ + + ] + +} + + +// TSM só pode ter um input (criar uma regra para tratar) +// O campo release é obrigatório + + +// O Combine não tem release +// o numero de um input terá que ser > 1 (se for < ou = a 1, cancela o submit) \ No newline at end of file diff --git a/frontend/services/release.js b/frontend/services/release.js new file mode 100644 index 0000000..bcfc130 --- /dev/null +++ b/frontend/services/release.js @@ -0,0 +1,6 @@ +import { api } from './api' + + +export const getReleases = ({ }) => { + return api.get('/api/releases/').then(res => res.data) +} \ No newline at end of file From c2bba38550b56941ae39a97757c5a5a662c7f503 Mon Sep 17 00:00:00 2001 From: jandsonrj Date: Tue, 8 Oct 2024 13:40:37 +0000 Subject: [PATCH 08/17] fix eslint errors --- frontend/components/TsmData.js | 37 ++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/frontend/components/TsmData.js b/frontend/components/TsmData.js index 2c05cd0..164675f 100644 --- a/frontend/components/TsmData.js +++ b/frontend/components/TsmData.js @@ -24,7 +24,7 @@ const columns = [ headerName: 'Created at', sortable: true, width: 200, - valueFormatter: (params) => { + valueFormatter: params => { if (!params.value) { return '' } @@ -37,15 +37,16 @@ export default function DataTableWrapper({ filters, query }) { const [page, setPage] = React.useState(0) const [pageSize, setPageSize] = React.useState(10) - const { data, status, error, isLoading } = useQuery( + const { data, error, isLoading } = useQuery( ['productData', { filters, query, page, pageSize }], - () => getProducts({ - filters, - page, - page_size: pageSize, - sort: [{ field: 'created_at', sort: 'desc' }], - search: query - }), + () => + getProducts({ + filters, + page, + page_size: pageSize, + sort: [{ field: 'created_at', sort: 'desc' }], + search: query + }), { staleTime: Infinity, refetchInterval: false, @@ -53,27 +54,33 @@ export default function DataTableWrapper({ filters, query }) { } ) - if (error) return Error loading data. Please try again. + if (error) { + return Error loading data. Please try again. + } - const filteredData = data?.results?.filter(product => product.product_type_name === 'Spec-z Catalog') || [] + const filteredData = + data?.results?.filter( + product => product.product_type_name === 'Spec-z Catalog' + ) || [] return ( <> row.id || row.unique_key} rows={filteredData} columns={columns} paginationMode="server" page={page} pageSize={pageSize} - onPageChange={(newPage) => setPage(newPage)} - onPageSizeChange={(newPageSize) => setPageSize(newPageSize)} + onPageChange={newPage => setPage(newPage)} + onPageSizeChange={newPageSize => setPageSize(newPageSize)} rowsPerPageOptions={[5, 10, 25]} disableColumnMenu disableColumnSelector loading={isLoading} localeText={{ - noRowsLabel: filteredData.length === 0 && !isLoading ? 'No products found' : 'Loading...', + noRowsLabel: isLoading ? 'Loading...' : 'No products found' }} /> @@ -99,4 +106,4 @@ DataTableWrapper.propTypes = { DataTableWrapper.defaultProps = { filters: {}, query: '' -} \ No newline at end of file +} From fe492f6d382e5009fd72a4deed19dacd9a095e63 Mon Sep 17 00:00:00 2001 From: jandsonrj Date: Thu, 10 Oct 2024 03:10:48 +0000 Subject: [PATCH 09/17] Refactor: Move release-related functions to a new release.js file Removed release functions from product.js to improve code organization. Created a new release.js file that contains the getReleases function. --- frontend/components/ReleaseSelect.js | 2 +- frontend/services/product.js | 4 ---- frontend/services/release.js | 4 ++-- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/frontend/components/ReleaseSelect.js b/frontend/components/ReleaseSelect.js index 147518f..43f97f9 100644 --- a/frontend/components/ReleaseSelect.js +++ b/frontend/components/ReleaseSelect.js @@ -1,7 +1,7 @@ import React, { useState, useEffect } from 'react' import PropTypes from 'prop-types' import MenuItem from '@mui/material/MenuItem' -import { getReleases } from '../services/product' +import { getReleases } from '../services/release' import { TextField } from '@mui/material' // export default function ReleaseSelect({ value, onChange, disabled }) { diff --git a/frontend/services/product.js b/frontend/services/product.js index 9bd2cab..ac605b8 100644 --- a/frontend/services/product.js +++ b/frontend/services/product.js @@ -3,10 +3,6 @@ import forIn from 'lodash/forIn' import { api } from './api' // import isEmpty from 'lodash/isEmpty' -export const getReleases = ({ }) => { - return api.get('/api/releases/').then(res => res.data) -} - export const getProductTypes = ({ }) => { return api.get('/api/product-types/').then(res => res.data) } diff --git a/frontend/services/release.js b/frontend/services/release.js index bcfc130..60cd6be 100644 --- a/frontend/services/release.js +++ b/frontend/services/release.js @@ -1,6 +1,6 @@ +/* eslint-disable camelcase */ import { api } from './api' - -export const getReleases = ({ }) => { +export const getReleases = () => { return api.get('/api/releases/').then(res => res.data) } \ No newline at end of file From 883da205df4cf5ac42afb9f5fcfc2099697bcc00 Mon Sep 17 00:00:00 2001 From: jandsonrj Date: Thu, 10 Oct 2024 05:47:08 +0000 Subject: [PATCH 10/17] ssome adjustments to the components and correction when consuming the pipelines api --- frontend/components/NNeighbors.js | 32 ++---- frontend/components/SearchRadius.js | 39 ++----- frontend/components/TsmData.js | 75 ++++++++----- frontend/pages/training_set_maker.js | 101 ++++++++++-------- .../services/{pipiline.js => pipeline.js} | 4 +- 5 files changed, 123 insertions(+), 128 deletions(-) rename frontend/services/{pipiline.js => pipeline.js} (63%) diff --git a/frontend/components/NNeighbors.js b/frontend/components/NNeighbors.js index 6c893dc..4bf5b35 100644 --- a/frontend/components/NNeighbors.js +++ b/frontend/components/NNeighbors.js @@ -2,27 +2,17 @@ import React, { useState, useEffect } from 'react' import TextField from '@mui/material/TextField' import PropTypes from 'prop-types' -const NNeighbors = ({ nNeighbors, onChange, reset }) => { - const formatNNeighbors = value => { - if (/^\d*\.?\d*$/.test(value)) { - return value - } else { - return value.replace(/[^\d.]/g, '') - } - } - - const [localNNeighbors, setLocalNNeighbors] = useState( - formatNNeighbors(nNeighbors) - ) +const NNeighbors = ({ nNeighbors, onChange }) => { + const [localNNeighbors, setLocalNNeighbors] = useState(nNeighbors) useEffect(() => { - setLocalNNeighbors(formatNNeighbors(nNeighbors)) - }, [reset, nNeighbors]) + setLocalNNeighbors(nNeighbors) + }, [nNeighbors]) const handleNeighborsChange = event => { - const inputValue = formatNNeighbors(event.target.value) - const newValue = Math.min(parseFloat(inputValue) || 0, 90) - setLocalNNeighbors(inputValue) + const inputValue = event.target.value.replace(/[^\d]/g, '') + const newValue = parseInt(inputValue, 10) || 1 + setLocalNNeighbors(newValue) onChange(newValue) } @@ -32,15 +22,15 @@ const NNeighbors = ({ nNeighbors, onChange, reset }) => { variant="outlined" value={localNNeighbors} onChange={handleNeighborsChange} - inputProps={{ inputMode: 'decimal', pattern: '[0-9]*\\.?[0-9]*' }} + inputProps={{ inputMode: 'numeric', pattern: '[0-9]*', min: 1, step: 1 }} /> ) } NNeighbors.propTypes = { - nNeighbors: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - onChange: PropTypes.func.isRequired, - reset: PropTypes.bool + nNeighbors: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) + .isRequired, + onChange: PropTypes.func.isRequired } export default NNeighbors diff --git a/frontend/components/SearchRadius.js b/frontend/components/SearchRadius.js index 01c4d9e..0457d4f 100644 --- a/frontend/components/SearchRadius.js +++ b/frontend/components/SearchRadius.js @@ -1,46 +1,27 @@ -import React, { useState, useEffect } from 'react' -import TextField from '@mui/material/TextField' +import React from 'react' import PropTypes from 'prop-types' +import TextField from '@mui/material/TextField' -const SearchRadius = ({ searchRadius, onChange, reset }) => { - const formatSearchRadius = value => { - if (/^\d*\.?\d*$/.test(value)) { - return value - } else { - return value.replace(/[^\d.]/g, '') - } - } - - const [localSearchRadius, setLocalSearchRadius] = useState( - formatSearchRadius(searchRadius) - ) - - useEffect(() => { - setLocalSearchRadius(formatSearchRadius(searchRadius)) - }, [reset, searchRadius]) +function SearchRadius({ searchRadius, onChange }) { + const formattedRadius = searchRadius.toFixed(1) const handleRadiusChange = event => { - const inputValue = formatSearchRadius(event.target.value) - const newValue = Math.min(parseFloat(inputValue) || 0, 90) - setLocalSearchRadius(inputValue) - onChange(newValue) + onChange(parseFloat(event.target.value)) } return ( ) } SearchRadius.propTypes = { - searchRadius: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - onChange: PropTypes.func.isRequired, - reset: PropTypes.bool + searchRadius: PropTypes.number.isRequired, + onChange: PropTypes.func.isRequired } export default SearchRadius diff --git a/frontend/components/TsmData.js b/frontend/components/TsmData.js index 164675f..032bcbb 100644 --- a/frontend/components/TsmData.js +++ b/frontend/components/TsmData.js @@ -1,4 +1,6 @@ -import { Alert, Box } from '@mui/material' +import Alert from '@mui/material/Alert' +import Radio from '@mui/material/Radio' +import Box from '@mui/material/Box' import { DataGrid } from '@mui/x-data-grid' import PropTypes from 'prop-types' import * as React from 'react' @@ -6,36 +8,10 @@ import { useQuery } from 'react-query' import moment from 'moment' import { getProducts } from '../services/product' -const columns = [ - { - field: 'display_name', - headerName: 'Name', - sortable: true, - flex: 1 - }, - { - field: 'uploaded_by', - headerName: 'Uploaded By', - flex: 1, - sortable: false - }, - { - field: 'created_at', - headerName: 'Created at', - sortable: true, - width: 200, - valueFormatter: params => { - if (!params.value) { - return '' - } - return moment(params.value).format('YYYY-MM-DD') - } - } -] - -export default function DataTableWrapper({ filters, query }) { +const DataTableWrapper = ({ filters, query }) => { const [page, setPage] = React.useState(0) const [pageSize, setPageSize] = React.useState(10) + const [selectedRowId, setSelectedRowId] = React.useState(null) const { data, error, isLoading } = useQuery( ['productData', { filters, query, page, pageSize }], @@ -63,6 +39,44 @@ export default function DataTableWrapper({ filters, query }) { product => product.product_type_name === 'Spec-z Catalog' ) || [] + const columns = [ + { + field: 'select', + headerName: '', + renderCell: params => ( + setSelectedRowId(params.row.id)} + /> + ), + width: 50 + }, + { + field: 'display_name', + headerName: 'Name', + sortable: true, + flex: 1 + }, + { + field: 'uploaded_by', + headerName: 'Uploaded By', + flex: 1, + sortable: false + }, + { + field: 'created_at', + headerName: 'Created at', + sortable: true, + width: 200, + valueFormatter: params => { + if (!params.value) { + return '' + } + return moment(params.value).format('YYYY-MM-DD') + } + } + ] + return ( <> @@ -82,6 +96,7 @@ export default function DataTableWrapper({ filters, query }) { localeText={{ noRowsLabel: isLoading ? 'Loading...' : 'No products found' }} + onRowClick={params => setSelectedRowId(params.row.id)} /> { - async function fetchData() { + const fetchData = async () => { try { - const apiUrl = - process.env.REACT_APP_API_URL || 'http://localhost/api/pipelines/' - const response = await fetch(apiUrl) - const data = await response.json() - const pipelineData = data.results[0].system_config + const response = await getPipeline() + const pipelineData = response.data.results[0].system_config const defaultSearchRadius = - pipelineData.param.crossmatch.radius_arcsec || 1.0 - const defaultNNeighbors = - pipelineData.param.crossmatch.n_neighbors || 1.0 + parseFloat(pipelineData.param.crossmatch.radius_arcsec) || 1.0 + const defaultNNeighbors = pipelineData.param.crossmatch.n_neighbors || 1 + const defaultDuplicateCriteria = + pipelineData.param.duplicate_criteria || 'closest' setSearchRadius(Math.min(defaultSearchRadius, 90)) setNNeighbors(Math.min(defaultNNeighbors, 90)) - setCombinedCatalogName( - pipelineData.param.crossmatch.output_catalog_name || '' - ) - setSelectedOption(pipelineData.param.duplicate_criteria || 'pickOne') + setSelectedOption(defaultDuplicateCriteria) } catch (error) { - console.error('Erro ao buscar dados da API', error) + console.error('Error fetching data from API', error) } } @@ -63,17 +61,32 @@ function TrainingSetMaker() { const handleClearForm = () => { setCombinedCatalogName('') setSearchRadius(1.0) - setNNeighbors(1.0) - setSelectedOption('pickOne') + setNNeighbors(1) + setSelectedOption('closest') setSelectedLsstCatalog('DP0.2') + setIsSubmitting(false) } const handleRun = () => { + setIsSubmitting(true) + + if (combinedCatalogName.trim() === '') { + setSnackbarMessage( + 'Your process has not been submitted. Please fill in the training set name.' + ) + setSnackbarColor(theme.palette.warning.main) + setSnackbarOpen(true) + return + } + + setSnackbarMessage('Your process has been submitted successfully.') + setSnackbarColor(theme.palette.success.main) setSnackbarOpen(true) } const handleSnackbarClose = () => { setSnackbarOpen(false) + setIsSubmitting(false) } const styles = { @@ -98,6 +111,7 @@ function TrainingSetMaker() { Training Set Maker + Training Set Maker @@ -110,6 +124,7 @@ function TrainingSetMaker() { + @@ -123,9 +138,9 @@ function TrainingSetMaker() { variant="outlined" value={combinedCatalogName} onChange={event => setCombinedCatalogName(event.target.value)} - error={combinedCatalogName.trim() === ''} + error={isSubmitting && combinedCatalogName.trim() === ''} helperText={ - combinedCatalogName.trim() === '' + isSubmitting && combinedCatalogName.trim() === '' ? 'This field is required.' : '' } @@ -139,6 +154,7 @@ function TrainingSetMaker() { + @@ -147,6 +163,7 @@ function TrainingSetMaker() { setSearch(query)} /> + @@ -154,6 +171,7 @@ function TrainingSetMaker() { + 3. Select the Objects catalog (photometric data): @@ -178,6 +196,7 @@ function TrainingSetMaker() { + 4. Select the cross-matching configuration choices: @@ -194,14 +213,20 @@ function TrainingSetMaker() { /> + The number of neighbors to find within each point: - + + In case of multiple spec-z measurements for the same object: @@ -210,44 +235,25 @@ function TrainingSetMaker() { onChange={event => setSelectedOption(event.target.value)} sx={{ ml: '16px' }} > - Keep the closest only + Keep the closest only Keep all + - - - - - This pipeline uses LSDB developed by LINCC. Please check out the - software documentation on{' '} - - LSDB Read the Docs page - - . - - - + diff --git a/frontend/services/pipiline.js b/frontend/services/pipeline.js similarity index 63% rename from frontend/services/pipiline.js rename to frontend/services/pipeline.js index 618f7b7..aef104e 100644 --- a/frontend/services/pipiline.js +++ b/frontend/services/pipeline.js @@ -1,5 +1,5 @@ import { api } from './api' export const getPipeline = ({ }) => { - return api.get('/api/pipeline/') -} + return api.get('/api/pipelines/') +} \ No newline at end of file From 417264bc524d52726e9189c0d8a3bfe6dfeeb1f4 Mon Sep 17 00:00:00 2001 From: jandsonrj Date: Thu, 10 Oct 2024 16:51:34 +0000 Subject: [PATCH 11/17] more feat --- frontend/pages/training_set_maker.js | 53 ++++++++++++++++------------ frontend/services/process.js | 8 ----- 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/frontend/pages/training_set_maker.js b/frontend/pages/training_set_maker.js index 44368df..e095335 100644 --- a/frontend/pages/training_set_maker.js +++ b/frontend/pages/training_set_maker.js @@ -21,6 +21,7 @@ import SearchField from '../components/SearchField' import SearchRadius from '../components/SearchRadius' import TsmData from '../components/TsmData' import { getPipeline } from '../services/pipeline' +import { getReleases } from '../services/release' function TrainingSetMaker() { const theme = useTheme() @@ -30,13 +31,14 @@ function TrainingSetMaker() { const [nNeighbors, setNNeighbors] = useState(1) const [selectedOption, setSelectedOption] = useState('closest') const [snackbarOpen, setSnackbarOpen] = useState(false) - const [selectedLsstCatalog, setSelectedLsstCatalog] = useState('DP0.2') + const [selectedLsstCatalog, setSelectedLsstCatalog] = useState('') const [isSubmitting, setIsSubmitting] = useState(false) const [snackbarMessage, setSnackbarMessage] = useState('') const [snackbarColor, setSnackbarColor] = useState(theme.palette.warning.main) + const [releases, setReleases] = useState([]) useEffect(() => { - const fetchData = async () => { + const fetchPipelineData = async () => { try { const response = await getPipeline() const pipelineData = response.data.results[0].system_config @@ -51,11 +53,26 @@ function TrainingSetMaker() { setNNeighbors(Math.min(defaultNNeighbors, 90)) setSelectedOption(defaultDuplicateCriteria) } catch (error) { - console.error('Error fetching data from API', error) + console.error('Error fetching pipeline data from API', error) } } - fetchData() + const fetchReleases = async () => { + try { + const response = await getReleases() + const fetchedReleases = response.data.results + setReleases(fetchedReleases) + + if (fetchedReleases.length > 0) { + setSelectedLsstCatalog(fetchedReleases[0].name) + } + } catch (error) { + console.error('Error fetching releases from API', error) + } + } + + fetchPipelineData() + fetchReleases() }, []) const handleClearForm = () => { @@ -63,7 +80,7 @@ function TrainingSetMaker() { setSearchRadius(1.0) setNNeighbors(1) setSelectedOption('closest') - setSelectedLsstCatalog('DP0.2') + setSelectedLsstCatalog('') setIsSubmitting(false) } @@ -130,7 +147,7 @@ function TrainingSetMaker() { 1. Training set name: - {' *'} + * 3. Select the Objects catalog (photometric data): @@ -258,12 +267,10 @@ function TrainingSetMaker() { anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} open={snackbarOpen} onClose={handleSnackbarClose} - autoHideDuration={3000} > diff --git a/frontend/services/process.js b/frontend/services/process.js index 981c4c0..b82b88b 100644 --- a/frontend/services/process.js +++ b/frontend/services/process.js @@ -17,11 +17,3 @@ ] } - - -// TSM só pode ter um input (criar uma regra para tratar) -// O campo release é obrigatório - - -// O Combine não tem release -// o numero de um input terá que ser > 1 (se for < ou = a 1, cancela o submit) \ No newline at end of file From 958191995f8e1c22c24af108d392689b37a2996f Mon Sep 17 00:00:00 2001 From: jandsonrj Date: Fri, 11 Oct 2024 19:09:41 -0300 Subject: [PATCH 12/17] fixes errors --- backend/pzserver/settings.py | 3 ++ frontend/components/TsmData.js | 22 ++++++---- frontend/pages/training_set_maker.js | 65 +++++++++++++++++++++++----- frontend/services/process.js | 33 +++++++------- 4 files changed, 84 insertions(+), 39 deletions(-) diff --git a/backend/pzserver/settings.py b/backend/pzserver/settings.py index cdd7909..65e6f0f 100644 --- a/backend/pzserver/settings.py +++ b/backend/pzserver/settings.py @@ -159,6 +159,9 @@ UPLOAD_DIR = os.getenv("UPLOAD_DIR", MEDIA_URL) +# Criando VA APPEND +APPEND_SLASH=False + # Default primary key field type # https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field diff --git a/frontend/components/TsmData.js b/frontend/components/TsmData.js index 032bcbb..d91818b 100644 --- a/frontend/components/TsmData.js +++ b/frontend/components/TsmData.js @@ -1,4 +1,3 @@ -import Alert from '@mui/material/Alert' import Radio from '@mui/material/Radio' import Box from '@mui/material/Box' import { DataGrid } from '@mui/x-data-grid' @@ -8,12 +7,12 @@ import { useQuery } from 'react-query' import moment from 'moment' import { getProducts } from '../services/product' -const DataTableWrapper = ({ filters, query }) => { +const DataTableWrapper = ({ filters, query, onProductSelect }) => { const [page, setPage] = React.useState(0) const [pageSize, setPageSize] = React.useState(10) const [selectedRowId, setSelectedRowId] = React.useState(null) - const { data, error, isLoading } = useQuery( + const { data, isLoading } = useQuery( ['productData', { filters, query, page, pageSize }], () => getProducts({ @@ -30,13 +29,17 @@ const DataTableWrapper = ({ filters, query }) => { } ) - if (error) { - return Error loading data. Please try again. + const handleRowSelection = rowId => { + setSelectedRowId(rowId) + if (onProductSelect) { + onProductSelect(rowId) + } } const filteredData = data?.results?.filter( - product => product.product_type_name === 'Spec-z Catalog' + product => + product.product_type_name === 'Spec-z Catalog' && product.status === 1 ) || [] const columns = [ @@ -46,7 +49,7 @@ const DataTableWrapper = ({ filters, query }) => { renderCell: params => ( setSelectedRowId(params.row.id)} + onChange={() => handleRowSelection(params.row.id)} /> ), width: 50 @@ -96,7 +99,7 @@ const DataTableWrapper = ({ filters, query }) => { localeText={{ noRowsLabel: isLoading ? 'Loading...' : 'No products found' }} - onRowClick={params => setSelectedRowId(params.row.id)} + onRowClick={params => handleRowSelection(params.row.id)} />
{ DataTableWrapper.propTypes = { filters: PropTypes.object, - query: PropTypes.string + query: PropTypes.string, + onProductSelect: PropTypes.func } DataTableWrapper.defaultProps = { diff --git a/frontend/pages/training_set_maker.js b/frontend/pages/training_set_maker.js index e095335..9df0ce3 100644 --- a/frontend/pages/training_set_maker.js +++ b/frontend/pages/training_set_maker.js @@ -21,20 +21,22 @@ import SearchField from '../components/SearchField' import SearchRadius from '../components/SearchRadius' import TsmData from '../components/TsmData' import { getPipeline } from '../services/pipeline' +import { submitProcess } from '../services/process' import { getReleases } from '../services/release' function TrainingSetMaker() { const theme = useTheme() const [combinedCatalogName, setCombinedCatalogName] = useState('') const [search, setSearch] = useState('') + const [selectedProductId, setSelectedProductId] = useState(null) const [searchRadius, setSearchRadius] = useState(1.0) const [nNeighbors, setNNeighbors] = useState(1) const [selectedOption, setSelectedOption] = useState('closest') const [snackbarOpen, setSnackbarOpen] = useState(false) - const [selectedLsstCatalog, setSelectedLsstCatalog] = useState('') const [isSubmitting, setIsSubmitting] = useState(false) const [snackbarMessage, setSnackbarMessage] = useState('') const [snackbarColor, setSnackbarColor] = useState(theme.palette.warning.main) + const [selectedLsstCatalog, setSelectedLsstCatalog] = useState('') const [releases, setReleases] = useState([]) useEffect(() => { @@ -59,12 +61,17 @@ function TrainingSetMaker() { const fetchReleases = async () => { try { - const response = await getReleases() - const fetchedReleases = response.data.results - setReleases(fetchedReleases) + const releasesData = await getReleases() - if (fetchedReleases.length > 0) { - setSelectedLsstCatalog(fetchedReleases[0].name) + if (Array.isArray(releasesData.results)) { + const fetchedReleases = releasesData.results + setReleases(fetchedReleases) + + if (fetchedReleases.length > 0) { + setSelectedLsstCatalog(fetchedReleases[0].name) + } + } else { + console.error('No results found in the API response') } } catch (error) { console.error('Error fetching releases from API', error) @@ -96,9 +103,40 @@ function TrainingSetMaker() { return } - setSnackbarMessage('Your process has been submitted successfully.') - setSnackbarColor(theme.palette.success.main) - setSnackbarOpen(true) + const sanitizedCatalogName = combinedCatalogName.replace(/[^\w\s]/gi, '') + + // Tentei criar o json + const processData = { + display_name: sanitizedCatalogName, + pipeline: '1', + used_config: { + param: { + crossmatch: { + n_neighbors: nNeighbors, + radius_arcsec: searchRadius, + output_catalog_name: sanitizedCatalogName + }, + duplicate_criteria: selectedOption + } + }, + release: '1', + inputs: [selectedProductId] + } + // tentativa de envio do json via POST + submitProcess(processData) + .then(response => { + console.log(response.status) + setSnackbarMessage('Your process has been submitted successfully.') + setSnackbarColor(theme.palette.success.main) + }) + .catch(error => { + console.error('Error submitting the process:', error) + setSnackbarMessage('There was an error submitting your process.') + setSnackbarColor(theme.palette.error.main) + }) + .finally(() => { + setSnackbarOpen(true) + }) } const handleSnackbarClose = () => { @@ -184,7 +222,10 @@ function TrainingSetMaker() { - + @@ -193,13 +234,13 @@ function TrainingSetMaker() { 3. Select the Objects catalog (photometric data): diff --git a/frontend/services/process.js b/frontend/services/process.js index b82b88b..377c8f9 100644 --- a/frontend/services/process.js +++ b/frontend/services/process.js @@ -1,19 +1,16 @@ -{ - display_name: "combinedCatalogName", - pipeline: 1, - used_config: { - "param": { - "crossmatch": { - "n_neighbors": 1, - "radius_arcsec": 1.0, - "output_catalog_name": "tsm_cross_001" - }, - "duplicate_criteria": "closest" // all // In case of multiple spec-z measurements for the same object: - }, - } - release: "1", // Select the LSST objects catalog - inputs: [ +import { api } from './api' - ] - -} +export const submitProcess = (processData) => { + return api.post('/api/processes/', processData) + .then(response => { + if (response.status === 201) { + return response + } else { + throw new Error('Failed to submit process') + } + }) + .catch(error => { + console.error('Error response:', error.response || error.message) + throw error + }) +} \ No newline at end of file From ce6ad7373a4b255acdaaa4bd517b065c3eb11a60 Mon Sep 17 00:00:00 2001 From: jandsonrj Date: Tue, 15 Oct 2024 15:51:21 -0300 Subject: [PATCH 13/17] Adjustments of pipeline components and fields. --- backend/core/views/__init__.py | 1 + backend/core/views/product.py | 19 +++ backend/pzserver/urls.py | 3 +- frontend/components/TsmData.js | 29 ++--- frontend/pages/training_set_maker.js | 176 ++++++++++++++++++--------- frontend/services/pipeline.js | 4 +- frontend/services/product.js | 51 ++++++++ 7 files changed, 199 insertions(+), 84 deletions(-) diff --git a/backend/core/views/__init__.py b/backend/core/views/__init__.py index a12f595..5533463 100644 --- a/backend/core/views/__init__.py +++ b/backend/core/views/__init__.py @@ -2,6 +2,7 @@ from core.views.pipeline import PipelineViewSet from core.views.process import ProcessViewSet from core.views.product import ProductViewSet +from core.views.product import ProductSpeczViewSet from core.views.product_content import ProductContentViewSet from core.views.product_file import ProductFileViewSet from core.views.product_type import ProductTypeViewSet diff --git a/backend/core/views/product.py b/backend/core/views/product.py index 65cf08c..5c5b2ee 100644 --- a/backend/core/views/product.py +++ b/backend/core/views/product.py @@ -66,6 +66,25 @@ def filter_release(self, queryset, name, value): return queryset.filter(query) +class ProductSpeczViewSet(viewsets.ReadOnlyModelViewSet): + """ Esse endpoint retorna apenas os produtos cujo o product type é = a 'spec-z' e o status é = a 1 + """ + queryset = Product.objects.filter(product_type__name = 'specz_catalog', status = 1) + serializer_class = ProductSerializer + search_fields = [ + "display_name", + "user__username", + "user__first_name", + "user__last_name", + ] + filterset_class = ProductFilter + ordering_fields = [ + "id", + "display_name", + "product_type", + "created_at", + ] + ordering = ["-created_at"] class ProductViewSet(viewsets.ModelViewSet): queryset = Product.objects.all() serializer_class = ProductSerializer diff --git a/backend/pzserver/urls.py b/backend/pzserver/urls.py index fe6c47b..e3665f0 100644 --- a/backend/pzserver/urls.py +++ b/backend/pzserver/urls.py @@ -18,7 +18,7 @@ OrchestrationInfoView, OrchestrationPipelinesView, PipelineViewSet, ProcessViewSet, ProductContentViewSet, ProductFileViewSet, ProductTypeViewSet, ProductViewSet, - ReleaseViewSet, UserViewSet) + ProductSpeczViewSet, ReleaseViewSet, UserViewSet) from django.conf import settings from django.contrib import admin @@ -33,6 +33,7 @@ route.register(r"releases", ReleaseViewSet, basename="releases") route.register(r"product-types", ProductTypeViewSet, basename="product_types") route.register(r"products", ProductViewSet, basename="products") +route.register(r"products-specz", ProductSpeczViewSet, basename="products_specz") route.register(r"product-contents", ProductContentViewSet, basename="product_contents") route.register(r"product-files", ProductFileViewSet, basename="product_files") diff --git a/frontend/components/TsmData.js b/frontend/components/TsmData.js index d91818b..5e0cb88 100644 --- a/frontend/components/TsmData.js +++ b/frontend/components/TsmData.js @@ -1,11 +1,11 @@ -import Radio from '@mui/material/Radio' import Box from '@mui/material/Box' +import Radio from '@mui/material/Radio' import { DataGrid } from '@mui/x-data-grid' +import moment from 'moment' import PropTypes from 'prop-types' import * as React from 'react' import { useQuery } from 'react-query' -import moment from 'moment' -import { getProducts } from '../services/product' +import { getProductsSpecz } from '../services/product' const DataTableWrapper = ({ filters, query, onProductSelect }) => { const [page, setPage] = React.useState(0) @@ -15,7 +15,7 @@ const DataTableWrapper = ({ filters, query, onProductSelect }) => { const { data, isLoading } = useQuery( ['productData', { filters, query, page, pageSize }], () => - getProducts({ + getProductsSpecz({ filters, page, page_size: pageSize, @@ -36,12 +36,6 @@ const DataTableWrapper = ({ filters, query, onProductSelect }) => { } } - const filteredData = - data?.results?.filter( - product => - product.product_type_name === 'Spec-z Catalog' && product.status === 1 - ) || [] - const columns = [ { field: 'select', @@ -85,14 +79,15 @@ const DataTableWrapper = ({ filters, query, onProductSelect }) => { row.id || row.unique_key} - rows={filteredData} + rows={data?.results || []} columns={columns} paginationMode="server" page={page} pageSize={pageSize} + rowCount={data?.count || 0} onPageChange={newPage => setPage(newPage)} onPageSizeChange={newPageSize => setPageSize(newPageSize)} - rowsPerPageOptions={[5, 10, 25]} + rowsPerPageOptions={[10]} disableColumnMenu disableColumnSelector loading={isLoading} @@ -102,16 +97,6 @@ const DataTableWrapper = ({ filters, query, onProductSelect }) => { onRowClick={params => handleRowSelection(params.row.id)} /> - - {`Showing ${filteredData.length} products`} - ) } diff --git a/frontend/pages/training_set_maker.js b/frontend/pages/training_set_maker.js index 9df0ce3..fe14a09 100644 --- a/frontend/pages/training_set_maker.js +++ b/frontend/pages/training_set_maker.js @@ -1,9 +1,11 @@ import InfoIcon from '@mui/icons-material/Info' +import Backdrop from '@mui/material/Backdrop' import Box from '@mui/material/Box' import Breadcrumbs from '@mui/material/Breadcrumbs' import Button from '@mui/material/Button' import Card from '@mui/material/Card' import CardContent from '@mui/material/CardContent' +import CircularProgress from '@mui/material/CircularProgress' import Grid from '@mui/material/Grid' import IconButton from '@mui/material/IconButton' import Link from '@mui/material/Link' @@ -20,7 +22,7 @@ import NNeighbors from '../components/NNeighbors' import SearchField from '../components/SearchField' import SearchRadius from '../components/SearchRadius' import TsmData from '../components/TsmData' -import { getPipeline } from '../services/pipeline' +import { getPipelineByName } from '../services/pipeline' import { submitProcess } from '../services/process' import { getReleases } from '../services/release' @@ -29,31 +31,33 @@ function TrainingSetMaker() { const [combinedCatalogName, setCombinedCatalogName] = useState('') const [search, setSearch] = useState('') const [selectedProductId, setSelectedProductId] = useState(null) - const [searchRadius, setSearchRadius] = useState(1.0) - const [nNeighbors, setNNeighbors] = useState(1) - const [selectedOption, setSelectedOption] = useState('closest') const [snackbarOpen, setSnackbarOpen] = useState(false) const [isSubmitting, setIsSubmitting] = useState(false) const [snackbarMessage, setSnackbarMessage] = useState('') const [snackbarColor, setSnackbarColor] = useState(theme.palette.warning.main) const [selectedLsstCatalog, setSelectedLsstCatalog] = useState('') + const [isLoading, setIsLoading] = useState(false) const [releases, setReleases] = useState([]) + const [initialData, setInitialData] = useState({ + param: { + crossmatch: { + n_neighbors: 1, + radius_arcsec: 1, + output_catalog_name: 'tsm_cross_001' + }, + duplicate_criteria: 'closest' + } + }) + const [data, setData] = useState(initialData) useEffect(() => { const fetchPipelineData = async () => { try { - const response = await getPipeline() - const pipelineData = response.data.results[0].system_config - - const defaultSearchRadius = - parseFloat(pipelineData.param.crossmatch.radius_arcsec) || 1.0 - const defaultNNeighbors = pipelineData.param.crossmatch.n_neighbors || 1 - const defaultDuplicateCriteria = - pipelineData.param.duplicate_criteria || 'closest' - - setSearchRadius(Math.min(defaultSearchRadius, 90)) - setNNeighbors(Math.min(defaultNNeighbors, 90)) - setSelectedOption(defaultDuplicateCriteria) + const response = await getPipelineByName({ name: 'training_set_maker' }) + const pipelineData = response.data.results[0] + + setInitialData(pipelineData) + setData(pipelineData.system_config) } catch (error) { console.error('Error fetching pipeline data from API', error) } @@ -84,14 +88,12 @@ function TrainingSetMaker() { const handleClearForm = () => { setCombinedCatalogName('') - setSearchRadius(1.0) - setNNeighbors(1) - setSelectedOption('closest') + setData(initialData.system_config) setSelectedLsstCatalog('') setIsSubmitting(false) } - const handleRun = () => { + const handleRun = async () => { setIsSubmitting(true) if (combinedCatalogName.trim() === '') { @@ -103,40 +105,57 @@ function TrainingSetMaker() { return } - const sanitizedCatalogName = combinedCatalogName.replace(/[^\w\s]/gi, '') - - // Tentei criar o json - const processData = { - display_name: sanitizedCatalogName, - pipeline: '1', - used_config: { - param: { - crossmatch: { - n_neighbors: nNeighbors, - radius_arcsec: searchRadius, - output_catalog_name: sanitizedCatalogName - }, - duplicate_criteria: selectedOption - } - }, - release: '1', - inputs: [selectedProductId] - } - // tentativa de envio do json via POST - submitProcess(processData) - .then(response => { - console.log(response.status) - setSnackbarMessage('Your process has been submitted successfully.') - setSnackbarColor(theme.palette.success.main) - }) - .catch(error => { - console.error('Error submitting the process:', error) - setSnackbarMessage('There was an error submitting your process.') + const sanitizedCatalogName = combinedCatalogName + .trim() + .replace(/[\s*,\-*]+/g, '_') + + try { + const pipelineId = initialData.id + + const selectedRelease = releases.find( + release => release.name === selectedLsstCatalog + ) + const releaseId = selectedRelease ? selectedRelease.id : null + + if (!releaseId) { + setSnackbarMessage('No valid release selected.') setSnackbarColor(theme.palette.error.main) - }) - .finally(() => { setSnackbarOpen(true) - }) + return + } + + // Create the JSON object + const processData = { + display_name: sanitizedCatalogName, + pipeline: pipelineId, + used_config: { + param: { + crossmatch: { + n_neighbors: data.param.crossmatch.n_neighbors, + radius_arcsec: data.param.crossmatch.radius_arcsec, + output_catalog_name: sanitizedCatalogName + }, + duplicate_criteria: data.param.duplicate_criteria + } + }, + release: releaseId, + inputs: [selectedProductId] + } + + // tentativa de envio do json via POST + setIsLoading(true) + await submitProcess(processData) + setSnackbarMessage('Your process has been submitted successfully.') + setSnackbarColor(theme.palette.success.main) + handleClearForm() + } catch (error) { + console.error('Error submitting the process:', error) + setSnackbarMessage('There was an error submitting your process.') + setSnackbarColor(theme.palette.error.main) + } finally { + setIsLoading(false) + setSnackbarOpen(true) + } } const handleSnackbarClose = () => { @@ -153,6 +172,8 @@ function TrainingSetMaker() { } } + console.log(data) + return ( @@ -258,8 +279,19 @@ function TrainingSetMaker() { are not added: { + setData({ + ...data, + param: { + ...data.param, + crossmatch: { + ...data.param.crossmatch, + radius_arcsec: value + } + } + }) + }} /> @@ -270,8 +302,19 @@ function TrainingSetMaker() { The number of neighbors to find within each point: { + setData({ + ...data, + param: { + ...data.param, + crossmatch: { + ...data.param.crossmatch, + n_neighbors: value + } + } + }) + }} reset={false} />
@@ -281,12 +324,20 @@ function TrainingSetMaker() { In case of multiple spec-z measurements for the same object: @@ -304,6 +355,13 @@ function TrainingSetMaker() { + ({ color: '#fff', zIndex: theme.zIndex.drawer + 1 })} + open={isLoading} + > + + + { - return api.get('/api/pipelines/') +export const getPipelineByName = ({ name }) => { + return api.get(`/api/pipelines/?name=${name}`) } \ No newline at end of file diff --git a/frontend/services/product.js b/frontend/services/product.js index ac605b8..6a77f43 100644 --- a/frontend/services/product.js +++ b/frontend/services/product.js @@ -208,3 +208,54 @@ export const createProductFile = (product_id, file, role, onUploadProgress) => { onUploadProgress: onUploadProgress }) } + + +export const getProductsSpecz = ({ + filters = {}, + search = '', + page = 0, + page_size = 25, + sort = [] +}) => { + // Esse endpoint retorna apenas os catalogos cujo o product type é = 'spec-z' e status = 1 + let ordering = null + + // Ordenação no DRF + // https://www.django-rest-framework.org/api-guide/filtering/#orderingfilter + if (sort.length === 1) { + ordering = sort[0].field + + if (sort[0].sort === 'desc') { + ordering = '-' + ordering + } + } + // Paginação no DRF + // https://www.django-rest-framework.org/api-guide/pagination/#pagenumberpagination + // Django não aceita pagina 0 por isso é somado 1 ao numero da página. + page += 1 + + // Todos os Query Params + const params = { page, page_size, ordering, search } + + // Filtros no DRF + // https://django-filter.readthedocs.io/en/stable/guide/rest_framework.html + // cada filtro que tiver valor deve virar uma propriedade no objeto params + // Só aplica os filtros caso não tenha um search dessa forma a busca é feita em todos os registros. + // o filtro official_product deve ser enviado no search também. + if (search === '') { + forIn(filters, function (value, key) { + if (key === 'release' && value === '0') { + params.release__isnull = true + params.release = null + } else { + if (value != null) { + params[key] = value + } + } + }) + } + + params.official_product = filters.official_product + + return api.get('/api/products-specz/', { params }).then(res => res.data) +} \ No newline at end of file From 602d51e0b28fccad385d1dbc60157d6b7dc32f5c Mon Sep 17 00:00:00 2001 From: jandsonrj Date: Tue, 15 Oct 2024 16:00:18 -0300 Subject: [PATCH 14/17] remove console.log --- frontend/pages/training_set_maker.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/pages/training_set_maker.js b/frontend/pages/training_set_maker.js index fe14a09..e121317 100644 --- a/frontend/pages/training_set_maker.js +++ b/frontend/pages/training_set_maker.js @@ -172,8 +172,6 @@ function TrainingSetMaker() { } } - console.log(data) - return ( From 960ffdb0d79d272a4d1a47872abbf70c4c1afe38 Mon Sep 17 00:00:00 2001 From: jandsonrj Date: Tue, 15 Oct 2024 16:09:48 -0300 Subject: [PATCH 15/17] hiding specz catalog screen --- frontend/pages/specz_catalogs.js | 399 +++++++++++++++++-------------- 1 file changed, 224 insertions(+), 175 deletions(-) diff --git a/frontend/pages/specz_catalogs.js b/frontend/pages/specz_catalogs.js index e9e72a3..b586136 100644 --- a/frontend/pages/specz_catalogs.js +++ b/frontend/pages/specz_catalogs.js @@ -1,185 +1,234 @@ +// import InfoIcon from '@mui/icons-material/Info' +// import Box from '@mui/material/Box' +// import Button from '@mui/material/Button' +// import Card from '@mui/material/Card' +// import CardContent from '@mui/material/CardContent' +// import Grid from '@mui/material/Grid' +// import IconButton from '@mui/material/IconButton' +// import Link from '@mui/material/Link' +// import Breadcrumbs from '@mui/material/Breadcrumbs' +// import MenuItem from '@mui/material/MenuItem' +// import Paper from '@mui/material/Paper' +// import Select from '@mui/material/Select' +// import Snackbar from '@mui/material/Snackbar' +// import SnackbarContent from '@mui/material/SnackbarContent' +// import TextField from '@mui/material/TextField' +// import Typography from '@mui/material/Typography' +// import React, { useState } from 'react' +// import SearchField from '../components/SearchField' +// import SearchRadius from '../components/SearchRadius' +// import SpeczData from '../components/SpeczData' +// import { useTheme } from '@mui/system' + +// function SpeczCatalogs() { +// const theme = useTheme() + +// const [combinedCatalogName, setCombinedCatalogName] = useState('') +// const [search, setSearch] = React.useState('') +// const filters = React.useState() +// const [searchRadius, setSearchRadius] = useState('1.0') +// const [selectedOption, setSelectedOption] = useState('keepAll') +// const [snackbarOpen, setSnackbarOpen] = useState(false) + +// const handleCatalogNameChange = event => { +// setCombinedCatalogName(event.target.value) +// } + +// const handleSearchRadiusChange = event => { +// const newValue = parseFloat(event.target.value) +// setSearchRadius(isNaN(newValue) ? '' : newValue.toString()) +// } + +// const handleClearForm = () => { +// setCombinedCatalogName('') +// setSearchRadius('1.0') +// setSelectedOption('keepAll') +// } + +// const handleRun = () => { +// setSnackbarOpen(true) +// } + +// const handleSnackbarClose = () => { +// setSnackbarOpen(false) +// } + +// const styles = { +// root: { +// transition: 'box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms', +// borderRadius: '4px', +// padding: theme.spacing(3), +// flex: '1 1 0%' +// } +// } + +// return ( +// +// +// +// +// +// Home +// +// +// Pipelines +// +// Combine Spec-z Catalogs +// +// +// +// Combine Spec-z Catalogs +// +// +// +// +// +// +// +// +// 1. Combined catalog name: +// +// +// +// +// +// +// +// +// +// +// 2. Select the Spec-z Catalogs to include in your sample: +// +// setSearch(query)} /> +// +// +// +// +// +// +// +// +// + +// +// +// 3. Select the cross-matching configuration choices: +// +// + +// +// Search Radius (arcsec):{' '} +// +// + +// +// +// In case of multiple spec-z measurements for the same object: +// {' '} +// +// + +// +// +// +// +// +// +// +// +// +// +// +// +// ) +// } + +// export default SpeczCatalogs + +import ArrowBackIos from '@mui/icons-material/ArrowBackIos' import InfoIcon from '@mui/icons-material/Info' -import Box from '@mui/material/Box' -import Button from '@mui/material/Button' -import Card from '@mui/material/Card' -import CardContent from '@mui/material/CardContent' +import { Box, Typography } from '@mui/material' +import Breadcrumbs from '@mui/material/Breadcrumbs' import Grid from '@mui/material/Grid' import IconButton from '@mui/material/IconButton' import Link from '@mui/material/Link' -import Breadcrumbs from '@mui/material/Breadcrumbs' -import MenuItem from '@mui/material/MenuItem' -import Paper from '@mui/material/Paper' -import Select from '@mui/material/Select' -import Snackbar from '@mui/material/Snackbar' -import SnackbarContent from '@mui/material/SnackbarContent' -import TextField from '@mui/material/TextField' -import Typography from '@mui/material/Typography' -import React, { useState } from 'react' -import SearchField from '../components/SearchField' -import SearchRadius from '../components/SearchRadius' -import SpeczData from '../components/SpeczData' -import { useTheme } from '@mui/system' +import React from 'react' function SpeczCatalogs() { - const theme = useTheme() - - const [combinedCatalogName, setCombinedCatalogName] = useState('') - const [search, setSearch] = React.useState('') - const filters = React.useState() - const [searchRadius, setSearchRadius] = useState('1.0') - const [selectedOption, setSelectedOption] = useState('keepAll') - const [snackbarOpen, setSnackbarOpen] = useState(false) - - const handleCatalogNameChange = event => { - setCombinedCatalogName(event.target.value) - } - - const handleSearchRadiusChange = event => { - const newValue = parseFloat(event.target.value) - setSearchRadius(isNaN(newValue) ? '' : newValue.toString()) - } - - const handleClearForm = () => { - setCombinedCatalogName('') - setSearchRadius('1.0') - setSelectedOption('keepAll') - } - - const handleRun = () => { - setSnackbarOpen(true) - } - - const handleSnackbarClose = () => { - setSnackbarOpen(false) - } - - const styles = { - root: { - transition: 'box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms', - borderRadius: '4px', - padding: theme.spacing(3), - flex: '1 1 0%' - } - } - return ( - - - - - - Home - - - Pipelines - - Combine Spec-z Catalogs - - - - Combine Spec-z Catalogs - - - - - - - - - 1. Combined catalog name: - - - - - - - - - - - 2. Select the Spec-z Catalogs to include in your sample: - - setSearch(query)} /> - - - - - - - - - - - - - 3. Select the cross-matching configuration choices: - - - - - Search Radius (arcsec):{' '} - - - - - - In case of multiple spec-z measurements for the same object: - {' '} - - - - - - - - - - - - - - - + + + + Home + + + Pipelines + + Combine Spec-z Catalogs + + + + window.history.back()} + > + + + Combine Spec-z Catalogs + + + + + + + Coming soon... + + ) } From 147fb2b8ac5c15aa856b0409072181be85cbaf96 Mon Sep 17 00:00:00 2001 From: jandsonrj Date: Tue, 15 Oct 2024 18:09:44 -0300 Subject: [PATCH 16/17] correction in the float value of radius_arcsec that should display a dot instead of a comma --- frontend/components/SearchRadius.js | 9 ++++++--- frontend/pages/training_set_maker.js | 4 +++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/frontend/components/SearchRadius.js b/frontend/components/SearchRadius.js index 0457d4f..d65a0f7 100644 --- a/frontend/components/SearchRadius.js +++ b/frontend/components/SearchRadius.js @@ -1,9 +1,12 @@ -import React from 'react' -import PropTypes from 'prop-types' import TextField from '@mui/material/TextField' +import PropTypes from 'prop-types' +import React from 'react' function SearchRadius({ searchRadius, onChange }) { - const formattedRadius = searchRadius.toFixed(1) + const formattedRadius = searchRadius.toLocaleString('en-US', { + minimumFractionDigits: 1, + maximumFractionDigits: 1 + }) const handleRadiusChange = event => { onChange(parseFloat(event.target.value)) diff --git a/frontend/pages/training_set_maker.js b/frontend/pages/training_set_maker.js index e121317..cdc25bd 100644 --- a/frontend/pages/training_set_maker.js +++ b/frontend/pages/training_set_maker.js @@ -106,8 +106,10 @@ function TrainingSetMaker() { } const sanitizedCatalogName = combinedCatalogName + .normalize('NFD') + .replace(/[\u0300-\u036f]/g, '') .trim() - .replace(/[\s*,\-*]+/g, '_') + .replace(/[\s*,\-*/]+/g, '_') try { const pipelineId = initialData.id From 238972c6262d318c13948f8faf2cb6db90bf42de Mon Sep 17 00:00:00 2001 From: Cristiano Singulani Date: Tue, 15 Oct 2024 19:32:54 -0300 Subject: [PATCH 17/17] Fixed small bug --- backend/core/views/product.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/backend/core/views/product.py b/backend/core/views/product.py index 5c5b2ee..d0260cf 100644 --- a/backend/core/views/product.py +++ b/backend/core/views/product.py @@ -57,19 +57,23 @@ def filter_name(self, queryset, name, value): return queryset.filter(query) def filter_type_name(self, queryset, name, value): - query = format_query_to_char(name, value, ["product_type__display_name", "product_type__name"]) + query = format_query_to_char( + name, value, ["product_type__display_name", "product_type__name"] + ) return queryset.filter(query) def filter_release(self, queryset, name, value): - query = format_query_to_char(name, value, ["release__display_name", "release__name"]) + query = format_query_to_char( + name, value, ["release__display_name", "release__name"] + ) return queryset.filter(query) class ProductSpeczViewSet(viewsets.ReadOnlyModelViewSet): - """ Esse endpoint retorna apenas os produtos cujo o product type é = a 'spec-z' e o status é = a 1 - """ - queryset = Product.objects.filter(product_type__name = 'specz_catalog', status = 1) + """Esse endpoint retorna apenas os produtos cujo o product type é = a 'spec-z' e o status é = a 1""" + + queryset = Product.objects.filter(product_type__name="specz_catalog", status=1) serializer_class = ProductSerializer search_fields = [ "display_name", @@ -85,6 +89,8 @@ class ProductSpeczViewSet(viewsets.ReadOnlyModelViewSet): "created_at", ] ordering = ["-created_at"] + + class ProductViewSet(viewsets.ModelViewSet): queryset = Product.objects.all() serializer_class = ProductSerializer