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..d0260cf 100644
--- a/backend/core/views/product.py
+++ b/backend/core/views/product.py
@@ -57,15 +57,40 @@ 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)
+ 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/settings.py b/backend/pzserver/settings.py
index 2d84942..3d692ee 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/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/EmailField.js b/frontend/components/EmailField.js
new file mode 100644
index 0000000..5b2bf70
--- /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/NNeighbors.js b/frontend/components/NNeighbors.js
new file mode 100644
index 0000000..4bf5b35
--- /dev/null
+++ b/frontend/components/NNeighbors.js
@@ -0,0 +1,36 @@
+import React, { useState, useEffect } from 'react'
+import TextField from '@mui/material/TextField'
+import PropTypes from 'prop-types'
+
+const NNeighbors = ({ nNeighbors, onChange }) => {
+ const [localNNeighbors, setLocalNNeighbors] = useState(nNeighbors)
+
+ useEffect(() => {
+ setLocalNNeighbors(nNeighbors)
+ }, [nNeighbors])
+
+ const handleNeighborsChange = event => {
+ const inputValue = event.target.value.replace(/[^\d]/g, '')
+ const newValue = parseInt(inputValue, 10) || 1
+ setLocalNNeighbors(newValue)
+ onChange(newValue)
+ }
+
+ return (
+
+ )
+}
+
+NNeighbors.propTypes = {
+ nNeighbors: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
+ .isRequired,
+ onChange: PropTypes.func.isRequired
+}
+
+export default NNeighbors
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/components/SearchRadius.js b/frontend/components/SearchRadius.js
new file mode 100644
index 0000000..d65a0f7
--- /dev/null
+++ b/frontend/components/SearchRadius.js
@@ -0,0 +1,30 @@
+import TextField from '@mui/material/TextField'
+import PropTypes from 'prop-types'
+import React from 'react'
+
+function SearchRadius({ searchRadius, onChange }) {
+ const formattedRadius = searchRadius.toLocaleString('en-US', {
+ minimumFractionDigits: 1,
+ maximumFractionDigits: 1
+ })
+
+ const handleRadiusChange = event => {
+ onChange(parseFloat(event.target.value))
+ }
+
+ return (
+
+ )
+}
+
+SearchRadius.propTypes = {
+ searchRadius: PropTypes.number.isRequired,
+ onChange: PropTypes.func.isRequired
+}
+
+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..5e0cb88 100644
--- a/frontend/components/TsmData.js
+++ b/frontend/components/TsmData.js
@@ -1,85 +1,115 @@
+import Box from '@mui/material/Box'
+import Radio from '@mui/material/Radio'
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 { getProductsSpecz } 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')
+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, isLoading } = useQuery(
+ ['productData', { filters, query, page, pageSize }],
+ () =>
+ getProductsSpecz({
+ filters,
+ page,
+ page_size: pageSize,
+ sort: [{ field: 'created_at', sort: 'desc' }],
+ search: query
+ }),
+ {
+ staleTime: Infinity,
+ refetchInterval: false,
+ retry: false
+ }
+ )
+
+ const handleRowSelection = rowId => {
+ setSelectedRowId(rowId)
+ if (onProductSelect) {
+ onProductSelect(rowId)
}
}
-]
-function DataTable({ rows }) {
+ const columns = [
+ {
+ field: 'select',
+ headerName: '',
+ renderCell: params => (
+ handleRowSelection(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 (
-
- row.id}
- pageSizeOptions={[5, 10]}
- />
-
+ <>
+
+ row.id || row.unique_key}
+ rows={data?.results || []}
+ columns={columns}
+ paginationMode="server"
+ page={page}
+ pageSize={pageSize}
+ rowCount={data?.count || 0}
+ onPageChange={newPage => setPage(newPage)}
+ onPageSizeChange={newPageSize => setPageSize(newPageSize)}
+ rowsPerPageOptions={[10]}
+ disableColumnMenu
+ disableColumnSelector
+ loading={isLoading}
+ localeText={{
+ noRowsLabel: isLoading ? 'Loading...' : 'No products found'
+ }}
+ onRowClick={params => handleRowSelection(params.row.id)}
+ />
+
+ >
)
}
-DataTable.propTypes = {
- rows: PropTypes.arrayOf(PropTypes.object).isRequired
+DataTableWrapper.propTypes = {
+ filters: PropTypes.object,
+ query: PropTypes.string,
+ onProductSelect: PropTypes.func
}
-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'
- }
-]
-
-export default function App() {
- return
+DataTableWrapper.defaultProps = {
+ filters: {},
+ query: ''
}
+
+export default DataTableWrapper
diff --git a/frontend/pages/specz_catalogs.js b/frontend/pages/specz_catalogs.js
index b24392f..b586136 100644
--- a/frontend/pages/specz_catalogs.js
+++ b/frontend/pages/specz_catalogs.js
@@ -1,11 +1,198 @@
-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 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, Typography } from '@mui/material'
import Breadcrumbs from '@mui/material/Breadcrumbs'
-import Link from '@mui/material/Link'
import Grid from '@mui/material/Grid'
import IconButton from '@mui/material/IconButton'
-import InfoIcon from '@mui/icons-material/Info'
-import ArrowBackIos from '@mui/icons-material/ArrowBackIos'
+import Link from '@mui/material/Link'
+import React from 'react'
function SpeczCatalogs() {
return (
diff --git a/frontend/pages/training_set_maker.js b/frontend/pages/training_set_maker.js
index c261d08..cdc25bd 100644
--- a/frontend/pages/training_set_maker.js
+++ b/frontend/pages/training_set_maker.js
@@ -1,47 +1,379 @@
-import React from 'react'
-import { Typography, Box } from '@mui/material'
+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 Link from '@mui/material/Link'
+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 InfoIcon from '@mui/icons-material/Info'
-import ArrowBackIos from '@mui/icons-material/ArrowBackIos'
+import Link from '@mui/material/Link'
+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 { useTheme } from '@mui/system'
+import React, { useEffect, useState } from 'react'
+import NNeighbors from '../components/NNeighbors'
+import SearchField from '../components/SearchField'
+import SearchRadius from '../components/SearchRadius'
+import TsmData from '../components/TsmData'
+import { getPipelineByName } 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 [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 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)
+ }
+ }
+
+ const fetchReleases = async () => {
+ try {
+ const releasesData = await getReleases()
+
+ 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)
+ }
+ }
+
+ fetchPipelineData()
+ fetchReleases()
+ }, [])
+
+ const handleClearForm = () => {
+ setCombinedCatalogName('')
+ setData(initialData.system_config)
+ setSelectedLsstCatalog('')
+ setIsSubmitting(false)
+ }
+
+ const handleRun = async () => {
+ 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
+ }
+
+ const sanitizedCatalogName = combinedCatalogName
+ .normalize('NFD')
+ .replace(/[\u0300-\u036f]/g, '')
+ .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)
+ 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 = () => {
+ setSnackbarOpen(false)
+ setIsSubmitting(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
-
- Training Set Maker
-
-
-
- window.history.back()}
- >
-
-
- Training Set Maker
-
-
-
-
-
-
- Coming soon...
-
-
+
+
+
+
+
+ Home
+
+
+ Pipelines
+
+ Training Set Maker
+
+
+
+
+ Training Set Maker
+
+
+
+
+
+
+
+
+
+ 1. Training set name:
+
+ *
+
+
+ setCombinedCatalogName(event.target.value)}
+ error={isSubmitting && combinedCatalogName.trim() === ''}
+ helperText={
+ isSubmitting && combinedCatalogName.trim() === ''
+ ? 'This field is required.'
+ : ''
+ }
+ />
+
+
+
+
+
+
+
+
+
+ 2. Select the Spec-z Catalog for the cross-matching:
+
+ setSearch(query)} />
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 3. Select the Objects catalog (photometric data):
+
+
+
+
+
+
+ 4. Select the cross-matching configuration choices:
+
+
+
+
+ The threshold distance in arcseconds beyond which neighbors
+ are not added:
+
+ {
+ setData({
+ ...data,
+ param: {
+ ...data.param,
+ crossmatch: {
+ ...data.param.crossmatch,
+ radius_arcsec: value
+ }
+ }
+ })
+ }}
+ />
+
+
+
+
+
+
+ The number of neighbors to find within each point:
+
+ {
+ setData({
+ ...data,
+ param: {
+ ...data.param,
+ crossmatch: {
+ ...data.param.crossmatch,
+ n_neighbors: value
+ }
+ }
+ })
+ }}
+ reset={false}
+ />
+
+
+
+
+
+ In case of multiple spec-z measurements for the same object:
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ({ color: '#fff', zIndex: theme.zIndex.drawer + 1 })}
+ open={isLoading}
+ >
+
+
+
+
+
+
+
+
)
}
diff --git a/frontend/services/pipeline.js b/frontend/services/pipeline.js
new file mode 100644
index 0000000..6911579
--- /dev/null
+++ b/frontend/services/pipeline.js
@@ -0,0 +1,5 @@
+import { api } from './api'
+
+export const getPipelineByName = ({ name }) => {
+ return api.get(`/api/pipelines/?name=${name}`)
+}
\ No newline at end of file
diff --git a/frontend/services/process.js b/frontend/services/process.js
new file mode 100644
index 0000000..377c8f9
--- /dev/null
+++ b/frontend/services/process.js
@@ -0,0 +1,16 @@
+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
diff --git a/frontend/services/product.js b/frontend/services/product.js
index 9bd2cab..6a77f43 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)
}
@@ -212,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
diff --git a/frontend/services/release.js b/frontend/services/release.js
new file mode 100644
index 0000000..60cd6be
--- /dev/null
+++ b/frontend/services/release.js
@@ -0,0 +1,6 @@
+/* eslint-disable camelcase */
+import { api } from './api'
+
+export const getReleases = () => {
+ return api.get('/api/releases/').then(res => res.data)
+}
\ No newline at end of file