diff --git a/frontend/build/static/sample_media/bar_chart.png b/frontend/build/static/sample_media/bar_chart.png
deleted file mode 100644
index 6a6a72b9c..000000000
Binary files a/frontend/build/static/sample_media/bar_chart.png and /dev/null differ
diff --git a/frontend/build/static/sample_media/bar_chart_1.png b/frontend/build/static/sample_media/bar_chart_1.png
new file mode 100644
index 000000000..33843027b
Binary files /dev/null and b/frontend/build/static/sample_media/bar_chart_1.png differ
diff --git a/frontend/build/static/sample_media/bar_chart_1_thumb.png b/frontend/build/static/sample_media/bar_chart_1_thumb.png
new file mode 100644
index 000000000..3e4a3cdde
Binary files /dev/null and b/frontend/build/static/sample_media/bar_chart_1_thumb.png differ
diff --git a/frontend/build/static/sample_media/bar_chart_2.png b/frontend/build/static/sample_media/bar_chart_2.png
new file mode 100644
index 000000000..aa02c144e
Binary files /dev/null and b/frontend/build/static/sample_media/bar_chart_2.png differ
diff --git a/frontend/build/static/sample_media/bar_chart_2_thumb.png b/frontend/build/static/sample_media/bar_chart_2_thumb.png
new file mode 100644
index 000000000..acc58d050
Binary files /dev/null and b/frontend/build/static/sample_media/bar_chart_2_thumb.png differ
diff --git a/frontend/build/static/sample_media/bar_chart_3.png b/frontend/build/static/sample_media/bar_chart_3.png
new file mode 100644
index 000000000..cb48f6d84
Binary files /dev/null and b/frontend/build/static/sample_media/bar_chart_3.png differ
diff --git a/frontend/build/static/sample_media/bar_chart_3_thumb.png b/frontend/build/static/sample_media/bar_chart_3_thumb.png
new file mode 100644
index 000000000..f071e4e57
Binary files /dev/null and b/frontend/build/static/sample_media/bar_chart_3_thumb.png differ
diff --git a/frontend/build/static/sample_media/pie_chart.png b/frontend/build/static/sample_media/pie_chart_thumb.png
similarity index 100%
rename from frontend/build/static/sample_media/pie_chart.png
rename to frontend/build/static/sample_media/pie_chart_thumb.png
diff --git a/frontend/build/static/sample_media/pixel_image.png b/frontend/build/static/sample_media/pixel_image.png
index f2fbc62c1..309749fc8 100644
Binary files a/frontend/build/static/sample_media/pixel_image.png and b/frontend/build/static/sample_media/pixel_image.png differ
diff --git a/frontend/build/static/sample_media/pixel_image_thumb.png b/frontend/build/static/sample_media/pixel_image_thumb.png
new file mode 100644
index 000000000..2c10996fe
Binary files /dev/null and b/frontend/build/static/sample_media/pixel_image_thumb.png differ
diff --git a/frontend/src/components/Database/DatabaseCells.tsx b/frontend/src/components/Database/DatabaseCells.tsx
index bc62aa494..229a905ad 100644
--- a/frontend/src/components/Database/DatabaseCells.tsx
+++ b/frontend/src/components/Database/DatabaseCells.tsx
@@ -1,4 +1,4 @@
-import { Box, Pagination, styled } from '@mui/material'
+import { Box, Input, Pagination, styled } from '@mui/material'
import { ChangeEvent, useCallback, useEffect, useMemo, useState } from 'react'
import { useSearchParams } from 'react-router-dom'
import DialogImage from '../common/DialogImage'
@@ -12,6 +12,7 @@ import { DataGridPro } from '@mui/x-data-grid-pro'
import {
DatabaseType,
DATABASE_SLICE_NAME,
+ ImageUrls,
} from '../../store/slice/Database/DatabaseType'
import { useSelector, useDispatch } from 'react-redux'
import { RootState } from '../../store/store'
@@ -26,7 +27,37 @@ type CellProps = {
user?: Object
}
-const columns = (handleOpenDialog: (value: string[]) => void) => [
+let timeout: NodeJS.Timeout | undefined = undefined
+
+const columns = (handleOpenDialog: (value: ImageUrls[], expId?: string) => void) => [
+ {
+ field: 'experiment_id',
+ headerName: 'Experiment ID',
+ filterOperators: [
+ {
+ label: 'Contains', value: 'contains',
+ InputComponent: ({applyValue, item}: any) => {
+ return {
+ if(timeout) clearTimeout(timeout)
+ timeout = setTimeout(() => {
+ applyValue({...item, value: e.target.value})
+ }, 300)
+ }
+ } />
+ }
+ },
+ ],
+ type: "string",
+ width: 160,
+ renderCell: (params: { row: DatabaseType }) => params.row?.experiment_id,
+ },
+ {
+ field: 'id',
+ headerName: 'Cell ID',
+ width: 160,
+ filterable: false,
+ renderCell: (params: { value: number }) => params.value,
+ },
{
field: 'brain_area',
headerName: 'Brain area',
@@ -74,7 +105,7 @@ const columns = (handleOpenDialog: (value: string[]) => void) => [
onClick={() => handleOpenDialog([cell_image_url])}
>
{
const [dataDialog, setDataDialog] = useState<{
type: string
- data: string | string[] | undefined
+ data?: string | string[]
+ expId?: string
+ nameCol?: string
}>({
type: '',
data: undefined,
})
+
const [searchParams, setParams] = useSearchParams()
const dispatch = useDispatch()
- const pagiFilter = useMemo(() => {
- return `limit=${dataExperiments.limit}&offset=${dataExperiments.offset}`
- }, [dataExperiments.limit, dataExperiments.offset])
+ const pagiFilter = useCallback(
+ (page?: number) => {
+ return `limit=${dataExperiments.limit}&offset=${
+ page ? page - 1 : dataExperiments.offset
+ }`
+ },
+ [dataExperiments.limit, dataExperiments.offset],
+ )
- const exp_id = searchParams.get('exp_id')
+ const id = searchParams.get('id')
const offset = searchParams.get('offset')
const limit = searchParams.get('limit')
- const sort = searchParams.get('sort')
+ const sort = searchParams.getAll('sort')
const dataParams = useMemo(() => {
return {
- exp_id: Number(exp_id) || undefined,
- offset: Number(offset) || 0,
- limit: Number(limit) || 50,
+ exp_id: Number(id) || undefined,
sort: sort || [],
+ limit: Number(limit) || 50,
+ offset: Number(offset) || 0,
}
- }, [offset, limit, sort, exp_id])
+ //eslint-disable-next-line
+ }, [offset, limit, JSON.stringify(sort), id])
const dataParamsFilter = useMemo(
() => ({
+ experiment_id: searchParams.get('experiment_id') || undefined,
brain_area: searchParams.get('brain_area') || undefined,
cre_driver: searchParams.get('cre_driver') || undefined,
reporter_line: searchParams.get('reporter_line') || undefined,
@@ -143,8 +184,12 @@ const DatabaseCells = ({ user }: CellProps) => {
//eslint-disable-next-line
}, [dataParams, user, dataParamsFilter])
- const handleOpenDialog = (data: string[]) => {
- setDataDialog({ type: 'image', data })
+ const handleOpenDialog = (data: ImageUrls[] | ImageUrls, expId?: string, graphTitle?: string) => {
+ let newData: string | (string[]) = []
+ if(Array.isArray(data)) {
+ newData = data.map(d => d.url);
+ } else newData = data.url
+ setDataDialog({ type: 'image', data: newData, expId: expId, nameCol: graphTitle })
}
const handleCloseDialog = () => {
@@ -162,7 +207,7 @@ const DatabaseCells = ({ user }: CellProps) => {
const handlePage = (e: ChangeEvent, page: number) => {
const filter = getParamsData()
setParams(
- `${filter}&sort=${dataParams.sort[0]}&sort=${dataParams.sort[1]}&${pagiFilter}`,
+ `${filter}&sort=${dataParams.sort[0]}&sort=${dataParams.sort[1]}&${pagiFilter(page)}`,
)
}
@@ -170,11 +215,11 @@ const DatabaseCells = ({ user }: CellProps) => {
(rowSelectionModel: GridSortModel) => {
const filter = getParamsData()
if (!rowSelectionModel[0]) {
- setParams(`${filter}&sort=&sort=&${pagiFilter}`)
+ setParams(`${filter}&sort=&sort=&${pagiFilter()}`)
return
}
setParams(
- `${filter}&sort=${rowSelectionModel[0].field}&sort=${rowSelectionModel[0].sort}&${pagiFilter}`,
+ `${filter}&sort=${rowSelectionModel[0].field}&sort=${rowSelectionModel[0].sort}&${pagiFilter()}`,
)
},
//eslint-disable-next-line
@@ -193,7 +238,7 @@ const DatabaseCells = ({ user }: CellProps) => {
}
const { sort } = dataParams
setParams(
- `${filter}&sort=${sort[0] || ''}&sort=${sort[1] || ''}&${pagiFilter}`,
+ `${filter}&sort=${sort[0] || ''}&sort=${sort[1] || ''}&${pagiFilter()}`,
)
}
@@ -205,16 +250,21 @@ const DatabaseCells = ({ user }: CellProps) => {
filterable: false,
sortable: false,
renderCell: (params: { row: DatabaseType }) => {
+ const {row} = params
+ const {graph_urls} = row
+ const graph_url = graph_urls[index]
+ if(!graph_url) return null
return (
-
- {params.row.graph_urls?.[index]?.[0] ? (
-
- ) : null}
+ handleOpenDialog(graph_url, params.row.experiment_id, graphTitle)}
+ >
+
)
},
@@ -248,6 +298,11 @@ const DatabaseCells = ({ user }: CellProps) => {
filter: {
filterModel: {
items: [
+ {
+ field: 'experiment_id',
+ operator: 'contains',
+ value: dataParamsFilter.experiment_id,
+ },
{
field: 'brain_area',
operator: 'contains',
@@ -283,6 +338,8 @@ const DatabaseCells = ({ user }: CellProps) => {
{loading ? : null}
diff --git a/frontend/src/components/Database/DatabaseExperiments.tsx b/frontend/src/components/Database/DatabaseExperiments.tsx
index 88dded77d..21b3ea247 100644
--- a/frontend/src/components/Database/DatabaseExperiments.tsx
+++ b/frontend/src/components/Database/DatabaseExperiments.tsx
@@ -1,13 +1,14 @@
-import { Box, Pagination, styled } from '@mui/material'
+import { Box, DialogTitle, FormControl, FormControlLabel, Input, Pagination, Radio, RadioGroup, styled } from '@mui/material'
import { ChangeEvent, useCallback, useEffect, useMemo, useState } from 'react'
import { useNavigate, useSearchParams } from 'react-router-dom'
-import DialogImage from '../common/DialogImage'
-import LaunchIcon from '@mui/icons-material/Launch'
+import ContentPasteSearchIcon from '@mui/icons-material/ContentPasteSearch';
import Button from '@mui/material/Button'
import Dialog from '@mui/material/Dialog'
import DialogActions from '@mui/material/DialogActions'
import DialogContent from '@mui/material/DialogContent'
import DialogContentText from '@mui/material/DialogContentText'
+import { DataGrid, GridRenderCellParams, GridRowParams } from '@mui/x-data-grid'
+import DialogImage from '../common/DialogImage'
import SwitchCustom from '../common/SwitchCustom'
import {
GridEnrichedColDef,
@@ -20,6 +21,7 @@ import GroupsIcon from '@mui/icons-material/Groups'
import {
DatabaseType,
DATABASE_SLICE_NAME,
+ ImageUrls,
} from '../../store/slice/Database/DatabaseType'
import { useSelector, useDispatch } from 'react-redux'
import { RootState } from '../../store/store'
@@ -30,13 +32,38 @@ import {
} from '../../store/slice/Database/DatabaseActions'
import Loading from 'components/common/Loading'
import { TypeData } from 'store/slice/Database/DatabaseSlice'
+import CancelIcon from '@mui/icons-material/Cancel'
+
+export type Data = {
+ id: number
+ fields: {
+ brain_area: string
+ cre_driver: string
+ reporter_line: string
+ imaging_depth: number
+ }
+ experiment_id: string
+ attributes: string
+ cell_image_urls: string[]
+ graph_urls: string[]
+ share_type: number
+ publish_status: number
+ created_time: string
+ updated_time: string
+}
type PopupAttributesProps = {
- data: string
+ data?: string | (string[])
open: boolean
handleClose: () => void
role?: boolean
handleChangeAttributes: (e: any) => void
+ exp_id?: string
+}
+
+type PopupType = {
+ open: boolean
+ handleClose: () => void
}
type DatabaseProps = {
@@ -44,21 +71,33 @@ type DatabaseProps = {
cellPath: string
}
+let timeout: NodeJS.Timeout | undefined = undefined
+
const columns = (
handleOpenAttributes: (value: string) => void,
- handleOpenDialog: (value: string[]) => void,
+ handleOpenDialog: (value: ImageUrls[], exp_id?: string) => void,
cellPath: string,
- navigate: (
- path: string,
- params: { [key: string]: string | undefined },
- ) => void,
+ navigate: (path: string) => void,
) => [
{
field: 'experiment_id',
headerName: 'Experiment ID',
width: 160,
- filterable: false,
- sort: 'asc',
+ filterOperators: [
+ {
+ label: 'Contains', value: 'contains',
+ InputComponent: ({applyValue, item}: any) => {
+ return {
+ if(timeout) clearTimeout(timeout)
+ timeout = setTimeout(() => {
+ applyValue({...item, value: e.target.value})
+ }, 300)
+ }
+ } />
+ }
+ },
+ ],
+ type: "string",
renderCell: (params: { row: DatabaseType }) => params.row?.experiment_id,
},
{
@@ -116,12 +155,12 @@ const columns = (
sortable: false,
renderCell: (params: { row: DatabaseType }) => (
- navigate(cellPath, { exp_id: params.row?.experiment_id })
+ navigate(`${cellPath}?experiment_id=${params.row?.experiment_id}` )
}
>
-
+
),
},
@@ -131,33 +170,175 @@ const columns = (
width: 160,
filterable: false,
sortable: false,
- renderCell: (params: { row: DatabaseType }) => (
- handleOpenDialog(params.row?.cell_image_urls)}
- >
- {params.row?.cell_image_urls?.length > 0 && (
-
- )}
-
+ renderCell: (params: { row: DatabaseType }) => {
+ return (
+ handleOpenDialog(params.row?.cell_image_urls)}
+ >
+ {params.row?.cell_image_urls?.length > 0 && (
+
+ )}
+
+ )
+ },
+ },
+]
+
+const columnsShare = (handleShareFalse: (parmas: GridRenderCellParams) => void) => [
+ {
+ field: "name",
+ headerName: "Name",
+ minWidth: 140,
+ renderCell: (params: GridRenderCellParams) => (
+ {params.row.name}
+ ),
+ },
+ {
+ field: "lab",
+ headerName: "Lab",
+ minWidth: 280,
+ renderCell: (params: GridRenderCellParams) => (
+ {params.row.email}
+ ),
+ },
+ {
+ field: "email",
+ headerName: "Email",
+ minWidth: 280,
+ renderCell: (params: GridRenderCellParams) => (
+ {params.row.email}
),
},
+ {
+ field: "share",
+ headerName: "",
+ minWidth: 130,
+ renderCell: (params: GridRenderCellParams) => {
+ if(!params.row.share) return ""
+ return (
+
+ )
+ }
+ },
+]
+
+const dataShare = [
+ {
+ id: 1,
+ name: "User 1",
+ lab: "Labxxxx",
+ email: "aaaaa@gmail.com",
+ share: false
+ },
+ {
+ id: 2,
+ name: "User 2",
+ lab: "Labxxxx",
+ email: "aaaaa@gmail.com",
+ share: true
+ },
+ {
+ id: 3,
+ name: "User 3",
+ lab: "Labxxxx",
+ email: "aaaaa@gmail.com",
+ share: true
+ }
]
+const PopupShare = ({open, handleClose}: PopupType) => {
+ const [value, setValue] = useState("Organization")
+ const [tableShare, setTableShare] = useState(dataShare)
+
+ const handleShareTrue = (params: GridRowParams) => {
+ if(params.row.share) return
+ const index = tableShare.findIndex(item => item.id === params.id)
+ setTableShare(pre => {
+ pre[index].share = true
+ return pre
+ })
+ }
+
+ const handleShareFalse = (params: GridRenderCellParams) => {
+ const indexSearch = tableShare.findIndex(item => item.id === params.id)
+ const newData = tableShare.map((item, index) => {
+ if(index === indexSearch) return {...item, share: false}
+ return item
+ })
+ setTableShare(newData)
+ }
+
+ const handleValue = (event: ChangeEvent) => {
+ setValue((event.target as HTMLInputElement).value);
+ }
+
+ if(!open) return null;
+
+ return (
+
+
+ Share Database record
+ Experiment ID: XXXXXX
+
+
+
+ } label={"Share for Organization"} />
+ } label={"Share for Users"} />
+
+
+
+
+ {
+ value !== "Organization" ?
+ <>
+ Permitted users
+
+ >
+ : null
+ }
+
+
+
+
+
+
+
+ )
+}
+
const PopupAttributes = ({
data,
open,
handleClose,
role = false,
handleChangeAttributes,
+ exp_id
}: PopupAttributesProps) => {
return (
@@ -185,26 +366,30 @@ const PopupAttributes = ({
)
}
-
const DatabaseExperiments = ({ user, cellPath }: DatabaseProps) => {
- const type: keyof TypeData = user ? 'private' : 'public'
- const { data: dataExperiments, loading } = useSelector(
- (state: RootState) => ({
- data: state[DATABASE_SLICE_NAME].data[type],
- loading: state[DATABASE_SLICE_NAME].loading,
- }),
- )
+ const [openShare, setOpenShare] = useState(false)
const [dataDialog, setDataDialog] = useState<{
type: string
- data: string | string[] | undefined
+ data?: string | string[]
+ expId?: string
+ nameCol?: string
}>({
type: '',
data: undefined,
})
+
const [searchParams, setParams] = useSearchParams()
- const navigate = useNavigate()
const dispatch = useDispatch()
+ const navigate = useNavigate()
+
+ const type: keyof TypeData = user ? 'private' : 'public'
+ const { data: dataExperiments, loading } = useSelector(
+ (state: RootState) => ({
+ data: state[DATABASE_SLICE_NAME].data[type],
+ loading: state[DATABASE_SLICE_NAME].loading,
+ }),
+ )
const pagiFilter = useCallback(
(page?: number) => {
@@ -230,6 +415,7 @@ const DatabaseExperiments = ({ user, cellPath }: DatabaseProps) => {
const dataParamsFilter = useMemo(
() => ({
+ experiment_id: searchParams.get('experiment_id') || undefined,
brain_area: searchParams.get('brain_area') || undefined,
cre_driver: searchParams.get('cre_driver') || undefined,
reporter_line: searchParams.get('reporter_line') || undefined,
@@ -248,8 +434,12 @@ const DatabaseExperiments = ({ user, cellPath }: DatabaseProps) => {
//eslint-disable-next-line
}, [dataParams, user, dataParamsFilter])
- const handleOpenDialog = (data: string[]) => {
- setDataDialog({ type: 'image', data })
+ const handleOpenDialog = (data: ImageUrls[] | ImageUrls, expId?: string, graphTitle?: string) => {
+ let newData: string | (string[]) = []
+ if(Array.isArray(data)) {
+ newData = data.map(d => d.url);
+ } else newData = data.url
+ setDataDialog({ type: 'image', data: newData, expId: expId, nameCol: graphTitle })
}
const handleCloseDialog = () => {
@@ -257,13 +447,17 @@ const DatabaseExperiments = ({ user, cellPath }: DatabaseProps) => {
}
const handleOpenAttributes = (data: string) => {
- setDataDialog({ type: 'attribute', data })
+ setDataDialog({ type: 'attribute', data})
}
const handleChangeAttributes = (event: any) => {
setDataDialog((pre) => ({ ...pre, data: event.target.value }))
}
+ const handleOpenShare = () => {
+ setOpenShare(true)
+ }
+
const getParamsData = () => {
const dataFilter = Object.keys(dataParamsFilter)
.filter((key) => (dataParamsFilter as any)[key])
@@ -328,16 +522,21 @@ const DatabaseExperiments = ({ user, cellPath }: DatabaseProps) => {
sortable: false,
filterable: false,
renderCell: (params: { row: DatabaseType }) => {
+ const {row} = params
+ const {graph_urls} = row
+ const graph_url = graph_urls[index]
+ if(!graph_url) return null
return (
-
- {params.row.graph_urls[index] ? (
-
- ) : null}
+ handleOpenDialog(graph_url, row.experiment_id, graphTitle)}
+ >
+
)
},
@@ -355,7 +554,7 @@ const DatabaseExperiments = ({ user, cellPath }: DatabaseProps) => {
sortable: false,
filterable: false,
renderCell: (params: { row: DatabaseType }) => (
-
+
),
@@ -395,8 +594,7 @@ const DatabaseExperiments = ({ user, cellPath }: DatabaseProps) => {
columns={
user
? ([...columnsTable, ...ColumnPrivate] as any)
- : (columnsTable as any)
- }
+ : (columnsTable as any)}
rows={dataExperiments?.items || []}
hideFooter={true}
filterMode={'server'}
@@ -414,6 +612,11 @@ const DatabaseExperiments = ({ user, cellPath }: DatabaseProps) => {
filter: {
filterModel: {
items: [
+ {
+ field: 'experiment_id',
+ operator: 'contains',
+ value: dataParamsFilter.experiment_id,
+ },
{
field: 'brain_area',
operator: 'is',
@@ -449,28 +652,43 @@ const DatabaseExperiments = ({ user, cellPath }: DatabaseProps) => {
{loading ? : null}
+ setOpenShare(false)}
+ />
)
}
-const DatabaseExperimentsWrapper = styled(Box)(({ theme }) => ({
+const DatabaseExperimentsWrapper = styled(Box)(() => ({
width: '100%',
height: 'calc(100vh - 250px)',
}))
-const Content = styled('textarea')(({ theme }) => ({
+const Content = styled('textarea')(() => ({
width: 400,
height: 'fit-content',
}))
+const DialogCustom = styled(Dialog)(({ theme }) => ({
+ "& .MuiDialog-container": {
+ "& .MuiPaper-root": {
+ width: "70%",
+ maxWidth: "890px",
+ },
+ },
+}))
+
export default DatabaseExperiments
diff --git a/frontend/src/components/common/DialogImage.tsx b/frontend/src/components/common/DialogImage.tsx
index becca2577..ef0bbce17 100644
--- a/frontend/src/components/common/DialogImage.tsx
+++ b/frontend/src/components/common/DialogImage.tsx
@@ -3,12 +3,14 @@ import {Box, styled} from "@mui/material";
type DialogImageProps = {
open: boolean
- data?: string[] | string
+ data?: string | (string[])
handleCloseDialog: () => void
+ expId?: string
+ nameCol?: string
}
-const DialogImage = ({data, handleCloseDialog, open}: DialogImageProps) => {
- if(!data) return <>>
+const DialogImage = ({data, handleCloseDialog, open, expId, nameCol}: DialogImageProps) => {
+ if(!data) return null
return (
<>
{
@@ -25,11 +27,14 @@ const DialogImage = ({data, handleCloseDialog, open}: DialogImageProps) => {
}}
>
{
- typeof data === "string" ?
+ !Array.isArray(data) ?
+ Expriment ID: {expId}
+ {nameCol}
+
{
Share Workspace
- アクセス許可ユーザー
+ Permitted users
Sequence:
DUMMY_EXPERIMENTS_CELL_IMAGE_URLS = [
ImageInfo(
url="http://localhost:8000/static/sample_media/pixel_image.png",
- thumb_url="http://localhost:8000/static/sample_media/pixel_image.png",
+ thumb_url="http://localhost:8000/static/sample_media/pixel_image_thumb.png",
)
for _ in range(5)
]
@@ -106,20 +106,20 @@ def experiment_transformer(items: Sequence) -> Sequence:
# TODO: set dummy data.
DUMMY_EXPERIMENTS_GRAPH_URLS = [
ImageInfo(
- url="http://localhost:8000/static/sample_media/bar_chart.png",
- thumb_url="http://localhost:8000/static/sample_media/bar_chart.png",
+ url=f"http://localhost:8000/static/sample_media/bar_chart_{(_ % 3) + 1}.png",
+ thumb_url=f"http://localhost:8000/static/sample_media/bar_chart_{(_ % 3) + 1}.png",
)
- for _ in DUMMY_EXPERIMENTS_GRAPH_TITLES
+ for _, __ in enumerate(DUMMY_EXPERIMENTS_GRAPH_TITLES)
]
# TODO: set dummy data.
DUMMY_CELLS_GRAPH_URLS = [
ImageInfo(
- url="http://localhost:8000/static/sample_media/bar_chart.png",
- thumb_url="http://localhost:8000/static/sample_media/bar_chart.png",
+ url=f"http://localhost:8000/static/sample_media/bar_chart_{(_ % 3) + 1}.png",
+ thumb_url=f"http://localhost:8000/static/sample_media/bar_chart_{(_ % 3) + 1}.png",
params={"param1": 10, "param2": 20},
)
- for _ in DUMMY_CELLS_GRAPH_TITLES
+ for _, __ in enumerate(DUMMY_CELLS_GRAPH_TITLES)
]
@@ -135,7 +135,7 @@ async def search_public_experiments(
sortOptions: SortOptions = Depends(),
db: Session = Depends(get_db),
):
- sort_column = getattr(optinist_model.Experiment, sortOptions.sort[0] or "id")
+ sa_sort_list = sortOptions.get_sa_sort_list(sa_table=optinist_model.Experiment)
# TODO: set dummy data.
graph_titles = DUMMY_EXPERIMENTS_GRAPH_TITLES
@@ -150,11 +150,7 @@ async def search_public_experiments(
)
)
.group_by(optinist_model.Experiment.id)
- .order_by(
- sort_column.desc()
- if sortOptions.sort[1] == SortDirection.desc
- else sort_column.asc()
- ),
+ .order_by(*sa_sort_list),
transformer=experiment_transformer,
additional_data={"header": ExpDbExperimentHeader(graph_titles=graph_titles)},
)
@@ -173,7 +169,10 @@ async def search_public_cells(
sortOptions: SortOptions = Depends(),
db: Session = Depends(get_db),
):
- sort_column = getattr(optinist_model.Cell, sortOptions.sort[0] or "id")
+ sa_sort_list = sortOptions.get_sa_sort_list(
+ sa_table=optinist_model.Cell,
+ mapping={"experiment_id": optinist_model.Experiment.experiment_id},
+ )
query = (
select(optinist_model.Cell, optinist_model.Experiment.experiment_id)
.join(
@@ -187,11 +186,7 @@ async def search_public_cells(
)
)
)
- query = query.group_by(optinist_model.Cell.id).order_by(
- sort_column.desc()
- if sortOptions.sort[1] == SortDirection.desc
- else sort_column.asc()
- )
+ query = query.group_by(optinist_model.Cell.id).order_by(*sa_sort_list)
# TODO: set dummy data.
graph_titles = DUMMY_CELLS_GRAPH_TITLES
@@ -218,7 +213,7 @@ async def search_db_experiments(
sortOptions: SortOptions = Depends(),
current_user: User = Depends(get_current_user),
):
- sort_column = getattr(optinist_model.Experiment, sortOptions.sort[0] or "id")
+ sa_sort_list = sortOptions.get_sa_sort_list(sa_table=optinist_model.Experiment)
query = select(optinist_model.Experiment).join(
common_model.Organization,
optinist_model.Experiment.organization_id == common_model.Organization.id,
@@ -252,11 +247,7 @@ async def search_db_experiments(
)
)
.group_by(optinist_model.Experiment.id)
- .order_by(
- sort_column.desc()
- if sortOptions.sort[1] == SortDirection.desc
- else sort_column.asc()
- )
+ .order_by(*sa_sort_list)
)
# TODO: set dummy data.
@@ -284,7 +275,10 @@ async def search_db_cells(
sortOptions: SortOptions = Depends(),
current_user: User = Depends(get_current_user),
):
- sort_column = getattr(optinist_model.Cell, sortOptions.sort[0] or "id")
+ sa_sort_list = sortOptions.get_sa_sort_list(
+ sa_table=optinist_model.Cell,
+ mapping={"experiment_id": optinist_model.Experiment.experiment_id},
+ )
query = (
select(optinist_model.Cell, optinist_model.Experiment.experiment_id)
.join(
@@ -325,11 +319,7 @@ async def search_db_cells(
)
)
.group_by(optinist_model.Cell.id)
- .order_by(
- sort_column.desc()
- if sortOptions.sort[1] == SortDirection.desc
- else sort_column.asc()
- )
+ .order_by(*sa_sort_list)
)
# TODO: set dummy data.
diff --git a/studio/app/optinist/schemas/base.py b/studio/app/optinist/schemas/base.py
index 852abfb54..1100a545a 100644
--- a/studio/app/optinist/schemas/base.py
+++ b/studio/app/optinist/schemas/base.py
@@ -1,8 +1,9 @@
from enum import Enum
-from typing import List
+from typing import Dict, List, Union
from fastapi import Query
from pydantic import dataclasses
+from sqlmodel.main import SQLModelMetaclass
class SortDirection(str, Enum):
@@ -16,3 +17,23 @@ class SortOptions:
default=(None, SortDirection.asc),
description="field-0: sort column
field-1: order ('asc' or 'desc')",
)
+
+ def get_sa_sort_list(
+ self, sa_table, mapping: Dict[str, Union[str, SQLModelMetaclass]] = None
+ ) -> List:
+ sort_list = []
+ for i in range(0, len(self.sort), 2):
+ sort_field, sort_type = self.sort[i] or "id", self.sort[i + 1]
+
+ sort_column = (
+ mapping.get(sort_field) or sort_field if mapping else sort_field
+ )
+ if isinstance(sort_column, str):
+ sort_column = getattr(sa_table, sort_column)
+
+ sort_list.append(
+ sort_column.desc()
+ if sort_type and sort_type == SortDirection.desc
+ else sort_column.asc()
+ )
+ return sort_list