Skip to content
Snippets Groups Projects
Commit fd76ab81 authored by mateusz-winiarczyk's avatar mateusz-winiarczyk
Browse files

feat(export): MIN-162 select annotation

parent 33770a77
No related branches found
No related tags found
2 merge requests!223reset the pin numbers before search results are fetch (so the results will be...,!88feat(export): MIN-162 select annotation
Showing
with 193 additions and 0 deletions
import { ZOD_SEED } from '@/constants';
// eslint-disable-next-line import/no-extraneous-dependencies
import { createFixture } from 'zod-fixture';
import { statisticsSchema } from '../statisticsSchema';
export const statisticsFixture = createFixture(statisticsSchema, {
seed: ZOD_SEED,
});
import { z } from 'zod';
export const statisticsSchema = z.object({
elementAnnotations: z.record(z.string(), z.number()),
publications: z.number(),
reactionAnnotations: z.record(z.string(), z.number()),
});
...@@ -34,4 +34,5 @@ export const apiPath = { ...@@ -34,4 +34,5 @@ export const apiPath = {
getConfigurationOptions: (): string => 'configuration/options/', getConfigurationOptions: (): string => 'configuration/options/',
getOverlayBioEntity: ({ overlayId, modelId }: { overlayId: number; modelId: number }): string => getOverlayBioEntity: ({ overlayId, modelId }: { overlayId: number; modelId: number }): string =>
`projects/${PROJECT_ID}/overlays/${overlayId}/models/${modelId}/bioEntities/`, `projects/${PROJECT_ID}/overlays/${overlayId}/models/${modelId}/bioEntities/`,
getStatisticsById: (projectId: string): string => `projects/${projectId}/statistics/`,
}; };
...@@ -69,3 +69,17 @@ export const drawerSearchChemicalsStepTwoFixture: DrawerState = { ...@@ -69,3 +69,17 @@ export const drawerSearchChemicalsStepTwoFixture: DrawerState = {
reactionDrawerState: {}, reactionDrawerState: {},
bioEntityDrawerState: {}, bioEntityDrawerState: {},
}; };
export const openedExportDrawerFixture: DrawerState = {
isOpen: true,
drawerName: 'export',
searchDrawerState: {
currentStep: 0,
stepType: 'none',
selectedValue: undefined,
listOfBioEnitites: [],
selectedSearchElement: '',
},
reactionDrawerState: {},
bioEntityDrawerState: {},
};
...@@ -18,6 +18,7 @@ import { getSearchData } from '../search/search.thunks'; ...@@ -18,6 +18,7 @@ import { getSearchData } from '../search/search.thunks';
import { setPerfectMatch } from '../search/search.slice'; import { setPerfectMatch } from '../search/search.slice';
import { getSessionValid } from '../user/user.thunks'; import { getSessionValid } from '../user/user.thunks';
import { getConfigurationOptions } from '../configuration/configuration.thunks'; import { getConfigurationOptions } from '../configuration/configuration.thunks';
import { getStatisticsById } from '../statistics/statistics.thunks';
interface InitializeAppParams { interface InitializeAppParams {
queryData: QueryData; queryData: QueryData;
...@@ -48,6 +49,9 @@ export const fetchInitialAppData = createAsyncThunk< ...@@ -48,6 +49,9 @@ export const fetchInitialAppData = createAsyncThunk<
// Check if auth token is valid // Check if auth token is valid
dispatch(getSessionValid()); dispatch(getSessionValid());
// Fetch data needed for export
dispatch(getStatisticsById(PROJECT_ID));
/** Trigger search */ /** Trigger search */
if (queryData.searchValue) { if (queryData.searchValue) {
dispatch(setPerfectMatch(queryData.perfectMatch)); dispatch(setPerfectMatch(queryData.perfectMatch));
......
...@@ -16,6 +16,7 @@ import { REACTIONS_STATE_INITIAL_MOCK } from '../reactions/reactions.mock'; ...@@ -16,6 +16,7 @@ import { REACTIONS_STATE_INITIAL_MOCK } from '../reactions/reactions.mock';
import { SEARCH_STATE_INITIAL_MOCK } from '../search/search.mock'; import { SEARCH_STATE_INITIAL_MOCK } from '../search/search.mock';
import { RootState } from '../store'; import { RootState } from '../store';
import { USER_INITIAL_STATE_MOCK } from '../user/user.mock'; import { USER_INITIAL_STATE_MOCK } from '../user/user.mock';
import { STATISTICS_STATE_INITIAL_MOCK } from '../statistics/statistics.mock';
export const INITIAL_STORE_STATE_MOCK: RootState = { export const INITIAL_STORE_STATE_MOCK: RootState = {
search: SEARCH_STATE_INITIAL_MOCK, search: SEARCH_STATE_INITIAL_MOCK,
...@@ -35,4 +36,5 @@ export const INITIAL_STORE_STATE_MOCK: RootState = { ...@@ -35,4 +36,5 @@ export const INITIAL_STORE_STATE_MOCK: RootState = {
contextMenu: CONTEXT_MENU_INITIAL_STATE, contextMenu: CONTEXT_MENU_INITIAL_STATE,
cookieBanner: COOKIE_BANNER_INITIAL_STATE_MOCK, cookieBanner: COOKIE_BANNER_INITIAL_STATE_MOCK,
user: USER_INITIAL_STATE_MOCK, user: USER_INITIAL_STATE_MOCK,
statistics: STATISTICS_STATE_INITIAL_MOCK,
}; };
import { DEFAULT_ERROR } from '@/constants/errors';
import { StatisticsState } from './statistics.types';
export const STATISTICS_STATE_INITIAL_MOCK: StatisticsState = {
data: undefined,
loading: 'idle',
error: DEFAULT_ERROR,
};
import { PROJECT_ID } from '@/constants';
import {
ToolkitStoreWithSingleSlice,
createStoreInstanceUsingSliceReducer,
} from '@/utils/createStoreInstanceUsingSliceReducer';
import { mockNetworkResponse } from '@/utils/mockNetworkResponse';
import { HttpStatusCode } from 'axios';
import { waitFor } from '@testing-library/react';
import { statisticsFixture } from '@/models/fixtures/statisticsFixture';
import { StatisticsState } from './statistics.types';
import statisticsReducer from './statistics.slice';
import { apiPath } from '../apiPath';
import { getStatisticsById } from './statistics.thunks';
const mockedAxiosClient = mockNetworkResponse();
const INITIAL_STATE: StatisticsState = {
data: undefined,
loading: 'idle',
error: { name: '', message: '' },
};
describe('statistics reducer', () => {
let store = {} as ToolkitStoreWithSingleSlice<StatisticsState>;
beforeEach(() => {
store = createStoreInstanceUsingSliceReducer('statistics', statisticsReducer);
});
it('should match initial state', () => {
const action = { type: 'unknown' };
expect(statisticsReducer(undefined, action)).toEqual(INITIAL_STATE);
});
it('should update store after successful getStatisticById query', async () => {
mockedAxiosClient
.onGet(apiPath.getStatisticsById(PROJECT_ID))
.reply(HttpStatusCode.Ok, statisticsFixture);
const { type } = await store.dispatch(getStatisticsById(PROJECT_ID));
const { data, loading, error } = store.getState().statistics;
expect(type).toBe('statistics/getStatisticsById/fulfilled');
waitFor(() => {
expect(loading).toEqual('pending');
});
expect(loading).toEqual('succeeded');
expect(error).toEqual({ message: '', name: '' });
expect(data).toEqual(statisticsFixture);
});
it('should update store after failed getStatisticById query', async () => {
mockedAxiosClient
.onGet(apiPath.getStatisticsById(PROJECT_ID))
.reply(HttpStatusCode.NotFound, undefined);
const { type } = await store.dispatch(getStatisticsById(PROJECT_ID));
const { loading } = store.getState().statistics;
expect(type).toBe('statistics/getStatisticsById/rejected');
waitFor(() => {
expect(loading).toEqual('pending');
});
expect(loading).toEqual('failed');
});
});
import { ActionReducerMapBuilder } from '@reduxjs/toolkit';
import { StatisticsState } from './statistics.types';
import { getStatisticsById } from './statistics.thunks';
export const getStatisticsByIdReducer = (
builder: ActionReducerMapBuilder<StatisticsState>,
): void => {
builder.addCase(getStatisticsById.pending, state => {
state.loading = 'pending';
});
builder.addCase(getStatisticsById.fulfilled, (state, action) => {
state.data = action.payload;
state.loading = 'succeeded';
});
builder.addCase(getStatisticsById.rejected, state => {
state.loading = 'failed';
});
};
import { createSelector } from '@reduxjs/toolkit';
import { rootSelector } from '../root/root.selectors';
export const statisticsSelector = createSelector(rootSelector, state => state.statistics);
export const loadingStatisticsSelector = createSelector(statisticsSelector, state => state.loading);
export const statisticsDataSelector = createSelector(
statisticsSelector,
statistics => statistics?.data,
);
export const elementAnnotationsSelector = createSelector(
statisticsDataSelector,
statistics => statistics?.elementAnnotations,
);
import { createSlice } from '@reduxjs/toolkit';
import { StatisticsState } from './statistics.types';
import { getStatisticsByIdReducer } from './statistics.reducers';
const initialState: StatisticsState = {
data: undefined,
loading: 'idle',
error: { name: '', message: '' },
};
export const statisticsSlice = createSlice({
name: 'statistics',
initialState,
reducers: {},
extraReducers: builder => {
getStatisticsByIdReducer(builder);
},
});
export default statisticsSlice.reducer;
import { axiosInstance } from '@/services/api/utils/axiosInstance';
import { Statistics } from '@/types/models';
import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
import { createAsyncThunk } from '@reduxjs/toolkit';
import { statisticsSchema } from '@/models/statisticsSchema';
import { apiPath } from '../apiPath';
export const getStatisticsById = createAsyncThunk(
'statistics/getStatisticsById',
async (id: string): Promise<Statistics | undefined> => {
const response = await axiosInstance.get<Statistics>(apiPath.getStatisticsById(id));
const isDataValid = validateDataUsingZodSchema(response.data, statisticsSchema);
return isDataValid ? response.data : undefined;
},
);
import { FetchDataState } from '@/types/fetchDataState';
import { Statistics } from '@/types/models';
export type StatisticsState = FetchDataState<Statistics>;
...@@ -23,6 +23,7 @@ import { ...@@ -23,6 +23,7 @@ import {
configureStore, configureStore,
} from '@reduxjs/toolkit'; } from '@reduxjs/toolkit';
import { mapListenerMiddleware } from './map/middleware/map.middleware'; import { mapListenerMiddleware } from './map/middleware/map.middleware';
import statisticsReducer from './statistics/statistics.slice';
export const reducers = { export const reducers = {
search: searchReducer, search: searchReducer,
...@@ -42,6 +43,7 @@ export const reducers = { ...@@ -42,6 +43,7 @@ export const reducers = {
user: userReducer, user: userReducer,
configuration: configurationReducer, configuration: configurationReducer,
overlayBioEntity: overlayBioEntityReducer, overlayBioEntity: overlayBioEntityReducer,
statistics: statisticsReducer,
}; };
export const middlewares = [mapListenerMiddleware.middleware]; export const middlewares = [mapListenerMiddleware.middleware];
......
...@@ -24,6 +24,7 @@ import { reactionSchema } from '@/models/reaction'; ...@@ -24,6 +24,7 @@ import { reactionSchema } from '@/models/reaction';
import { reactionLineSchema } from '@/models/reactionLineSchema'; import { reactionLineSchema } from '@/models/reactionLineSchema';
import { referenceSchema } from '@/models/referenceSchema'; import { referenceSchema } from '@/models/referenceSchema';
import { sessionSchemaValid } from '@/models/sessionValidSchema'; import { sessionSchemaValid } from '@/models/sessionValidSchema';
import { statisticsSchema } from '@/models/statisticsSchema';
import { targetSchema } from '@/models/targetSchema'; import { targetSchema } from '@/models/targetSchema';
import { z } from 'zod'; import { z } from 'zod';
...@@ -53,3 +54,4 @@ export type Login = z.infer<typeof loginSchema>; ...@@ -53,3 +54,4 @@ export type Login = z.infer<typeof loginSchema>;
export type ConfigurationOption = z.infer<typeof configurationOptionSchema>; export type ConfigurationOption = z.infer<typeof configurationOptionSchema>;
export type OverlayBioEntity = z.infer<typeof overlayBioEntitySchema>; export type OverlayBioEntity = z.infer<typeof overlayBioEntitySchema>;
export type Color = z.infer<typeof colorSchema>; export type Color = z.infer<typeof colorSchema>;
export type Statistics = z.infer<typeof statisticsSchema>;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment