Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch workflow by project id(rebased after #87) #89

Merged
merged 7 commits into from
May 22, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 32 additions & 13 deletions frontend/src/api/experiments/Experiments.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,42 @@
import axios from 'axios'

import { BASE_URL } from 'const/API'
import { RunPostData } from 'api/run/Run'
import { EdgeDict, NodeDict, OutputPathsDTO, RunPostData } from 'api/run/Run'
import { EXPERIMENTS_STATUS } from 'store/slice/Experiments/ExperimentsType'

export type ExperimentsDTO = {
[uid: string]: ExperimentDTO
}

export type ExperimentDTO = {
function: {
[uid: string]: {
name: string
success: string
unique_id: string
hasNWB: boolean
}
export type FunctionsDTO = {
[nodeId: string]: {
name: string
success: string
unique_id: string
hasNWB: boolean
message?: string
started_at?: string
finished_at?: string
outputPaths?: OutputPathsDTO
}
}

export type ExperimentDTO = {
function: FunctionsDTO
name: string
success: string
timestamp: string
success?: EXPERIMENTS_STATUS
started_at: string
finished_at?: string
unique_id: string
hasNWB: boolean
edgeDict: EdgeDict
nodeDict: NodeDict
}

export async function getExperimentsApi(): Promise<ExperimentsDTO> {
const response = await axios.get(`${BASE_URL}/experiments`)
export async function getExperimentsApi(
projectId: string,
): Promise<ExperimentsDTO> {
const response = await axios.get(`${BASE_URL}/experiments/${projectId}`)
return response.data
}

@@ -49,6 +61,13 @@ export async function importExperimentByUidApi(
return response.data
}

export async function fetchExperimentApi(
projectId: string,
): Promise<ExperimentDTO> {
const response = await axios.get(`${BASE_URL}/experiments/fetch/${projectId}`)
return response.data
}

export async function downloadExperimentNwbApi(uid: string, nodeId?: string) {
const path =
nodeId != null
22 changes: 15 additions & 7 deletions frontend/src/api/run/Run.ts
Original file line number Diff line number Diff line change
@@ -42,16 +42,20 @@ export interface AlgorithmNodePostData extends AlgorithmNodeData {
param: ParamMap
}

export async function runApi(data: RunPostData): Promise<string> {
const response = await axios.post(`${BASE_URL}/run`, data)
export async function runApi(
projectId: string,
data: RunPostData,
): Promise<string> {
const response = await axios.post(`${BASE_URL}/run/${projectId}`, data)
return response.data
}

export async function runByUidApi(
projectId: string,
uid: string,
data: Omit<RunPostData, 'name'>,
): Promise<string> {
const response = await axios.post(`${BASE_URL}/run/${uid}`, data)
const response = await axios.post(`${BASE_URL}/run/${projectId}/${uid}`, data)
return response.data
}

@@ -72,12 +76,16 @@ export type OutputPathsDTO = {
}

export async function runResult(data: {
projectId: string
uid: string
pendingNodeIdList: string[]
}): Promise<RunResultDTO> {
const { uid, pendingNodeIdList } = data
const response = await axios.post(`${BASE_URL}/run/result/${uid}`, {
pendingNodeIdList,
})
const { projectId, uid, pendingNodeIdList } = data
const response = await axios.post(
`${BASE_URL}/run/result/${projectId}/${uid}`,
{
pendingNodeIdList,
},
)
return response.data
}
12 changes: 6 additions & 6 deletions frontend/src/components/Table/index.tsx
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ import ReactPaginate from 'react-paginate'
import KeyboardArrowLeftIcon from '@mui/icons-material/KeyboardArrowLeft'
import ChevronRightIcon from '@mui/icons-material/ChevronRight'
import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward'
import { DataProject } from 'pages/Projects'
import { ProjectType } from 'store/slice/Project/ProjectType'

export type Column = {
width?: number | string
@@ -14,14 +14,14 @@ export type Column = {
align?: string
filter?: boolean
render?: (
item: DataProject,
item: ProjectType,
value?: string | object | number,
index?: number,
) => JSX.Element | null | undefined
}

type TableComponentProps = {
data?: DataProject[]
data?: ProjectType[]
orderBy?: 'ASC' | 'DESC'
orderKey?: string
className?: string
@@ -51,8 +51,8 @@ const TableComponent: FC<TableComponentProps> = (props) => {
const pageCount = (paginate?.total || 0) / (paginate?.page_size || 1)

const renderCol = useCallback(
(col: Column, item: DataProject, index: number) => {
const value = item[(col.name || col.dataIndex || '') as keyof DataProject]
(col: Column, item: ProjectType, index: number) => {
const value = item[(col.name || col.dataIndex || '') as keyof ProjectType]
if (col.render) return col.render(item, value, index)
return value || null
},
@@ -191,7 +191,7 @@ const Td = styled('td')(({ theme }) => ({
padding: theme.spacing(2),
borderBottom: '1px solid rgba(224, 224, 224, 1)',
maxWidth: 155,
wordBreak: 'break-word'
wordBreak: 'break-word',
}))

const NoData = styled(Typography)({
17 changes: 10 additions & 7 deletions frontend/src/pages/AccountManager/index.tsx
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ import InputError from '../../components/common/InputError'
import SelectError from '../../components/common/SelectError'
import { createUser, deleteUser, editUser, listUser } from 'api/auth'
import { useUser } from 'providers'
import { DataProject } from 'pages/Projects'
import { ProjectType } from 'store/slice/Project/ProjectType'
import { isAdmin, optionsRole } from 'utils/auth'
import Loading from '../../components/common/Loading'

@@ -46,7 +46,8 @@ const ModalComponent: FC<ModalComponentProps> = ({
}) => {
const regex =
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
const regexPassword = /^(?=.*\d)(?=.*[!#$%&()*+,-./@_|])(?=.*[a-zA-Z]).{6,255}$/
const regexPassword =
/^(?=.*\d)(?=.*[!#$%&()*+,-./@_|])(?=.*[a-zA-Z]).{6,255}$/
const [formData, setFormData] = useState<{ [key: string]: string }>(
dataEdit || initState,
)
@@ -278,8 +279,8 @@ const AccountManager = () => {
nextPageToken.push(data.next_page_token)
}
const newData = data.data.map((item: any) => {
const name = optionsRole.find(role => item.role === role.code)?.name
return {...item, role: name}
const name = optionsRole.find((role) => item.role === role.code)?.name
return { ...item, role: name }
})
setData(newData)
setPaginate((pre) => ({
@@ -303,10 +304,12 @@ const AccountManager = () => {
setOpenDelete(true)
}

const onForgotPassword = (data: DataProject) => {
const code = optionsRole.find(role => String(role.name) === String(data.role))?.code
const onForgotPassword = (data: ProjectType) => {
const code = optionsRole.find(
(role) => String(role.name) === String(data.role),
)?.code
//todo call api
setDataEdit({...data, role: code})
setDataEdit({ ...data, role: code })
setIsOpenModal(true)
}

38 changes: 7 additions & 31 deletions frontend/src/pages/Projects/index.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,17 @@
import { Box, Button, styled } from '@mui/material'
import { useCallback, useMemo, useState } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import TableComponent, { Column } from '../../components/Table'
import { useNavigate } from 'react-router-dom'
import ModalDeleteAccount from 'components/ModalDeleteAccount'

export type DataProject = {
id: number | string
uid?: number | string
name: string
project_type: number
image_count: number
created_time: string
updated_time: string
role?: string | number
}
import { selectProjectList } from 'store/slice/Project/ProjectSelector'
import { deleteProject } from 'store/slice/Project/ProjectSlice'

const Projects = () => {
const navigate = useNavigate()
const dispatch = useDispatch()
const projects = useSelector(selectProjectList)
const [idDelete, setIdDelete] = useState<number | string | undefined>()
const [data, setData] = useState<DataProject[]>([
{
id: '1',
name: 'prj name 1',
created_time: '2023-03-10 09:19',
updated_time: '2023-03-10 09:19',
image_count: 3,
project_type: 0,
},
{
id: '2',
name: 'prj name 2',
created_time: '2023-03-10 09:19',
updated_time: '2023-03-10 09:19',
image_count: 3,
project_type: 1,
},
])

const onEdit = useCallback((id: number | string) => {
navigate(`/projects/new-project?id=${id}`)
@@ -66,7 +42,7 @@ const Projects = () => {
const onDeleteSubmit = () => {
const id = idDelete
setIdDelete(undefined)
setData(data.filter((e) => e.id !== id))
dispatch(deleteProject(id))
}

const handleCloseDelete = () => {
@@ -133,7 +109,7 @@ const Projects = () => {
</BoxButton>
<TableComponent
paginate={{ total: 100, page: 1, page_size: 10 }}
data={data}
data={projects}
columns={columns}
/>
</ProjectsWrapper>
22 changes: 21 additions & 1 deletion frontend/src/store/slice/AlgorithmNode/AlgorithmNodeSlice.ts
Original file line number Diff line number Diff line change
@@ -7,7 +7,10 @@ import {
} from '../FlowElement/FlowElementSlice'
import { isNodeData } from '../FlowElement/FlowElementUtils'
import { NODE_TYPE_SET } from '../FlowElement/FlowElementType'
import { importExperimentByUid } from '../Experiments/ExperimentsActions'
import {
fetchExperiment,
importExperimentByUid,
} from '../Experiments/ExperimentsActions'
import { getAlgoParams } from './AlgorithmNodeActions'
import { ALGORITHM_NODE_SLICE_NAME, AlgorithmNode } from './AlgorithmNodeType'
import { isAlgorithmNodePostData } from 'api/run/RunUtils'
@@ -87,6 +90,23 @@ export const algorithmNodeSlice = createSlice({
})
return newState
})
.addCase(fetchExperiment.fulfilled, (state, action) => {
const newState: AlgorithmNode = {}
Object.values(action.payload.nodeDict)
.filter(isAlgorithmNodePostData)
.forEach((node) => {
if (node.data != null) {
newState[node.id] = {
name: node.data.label,
functionPath: node.data.path,
params: node.data.param,
isUpdated: false,
}
}
})
return newState
})
.addCase(fetchExperiment.rejected, (_state, _action) => initialState)
.addMatcher(
isAnyOf(run.fulfilled, runByCurrentUid.fulfilled),
(state, action) => {
30 changes: 26 additions & 4 deletions frontend/src/store/slice/Experiments/ExperimentsActions.ts
Original file line number Diff line number Diff line change
@@ -5,15 +5,37 @@ import {
deleteExperimentByUidApi,
importExperimentByUidApi,
deleteExperimentByListApi,
ExperimentDTO,
fetchExperimentApi,
} from 'api/experiments/Experiments'
import { RunPostData } from 'api/run/Run'
import { EXPERIMENTS_SLICE_NAME } from './ExperimentsType'
import { selectCurrentProjectId } from '../Project/ProjectSelector'
import { ThunkApiConfig } from 'store/store'

export const getExperiments = createAsyncThunk<ExperimentsDTO, undefined>(
`${EXPERIMENTS_SLICE_NAME}/getExperiments`,
async (_, thunkAPI) => {
export const getExperiments = createAsyncThunk<
ExperimentsDTO,
undefined,
ThunkApiConfig
>(`${EXPERIMENTS_SLICE_NAME}/getExperiments`, async (_, thunkAPI) => {
const projectId = selectCurrentProjectId(thunkAPI.getState())
if (projectId) {
try {
const response = await getExperimentsApi(projectId)
return response
} catch (e) {
return thunkAPI.rejectWithValue(e)
}
} else {
return thunkAPI.rejectWithValue('project id does not exist.')
}
})

export const fetchExperiment = createAsyncThunk<ExperimentDTO, string>(
`${EXPERIMENTS_SLICE_NAME}/fetchExperiment`,
async (projectId, thunkAPI) => {
try {
const response = await getExperimentsApi()
const response = await fetchExperimentApi(projectId)
return response
} catch (e) {
return thunkAPI.rejectWithValue(e)
19 changes: 12 additions & 7 deletions frontend/src/store/slice/Experiments/ExperimentsSelectors.ts
Original file line number Diff line number Diff line change
@@ -51,14 +51,19 @@ export const selectExperimentHasNWB = (uid: string) => (state: RootState) =>
export const selectExperimentStatus =
(uid: string) =>
(state: RootState): EXPERIMENTS_STATUS => {
const functions = selectExperimentList(state)[uid].functions
const statusList = Object.values(functions).map((f) => f.status)
if (statusList.findIndex((status) => status === 'error') >= 0) {
return 'error'
} else if (statusList.findIndex((status) => status === 'running') >= 0) {
return 'running'
const experiment = selectExperimentList(state)[uid]
if (experiment.status) {
return experiment.status
} else {
return 'success'
const functions = selectExperimentList(state)[uid].functions
const statusList = Object.values(functions).map((f) => f.status)
if (statusList.findIndex((status) => status === 'error') >= 0) {
return 'error'
} else if (statusList.findIndex((status) => status === 'running') >= 0) {
return 'running'
} else {
return 'success'
}
}
}

Loading