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

feat: last ingestion time from data services #117

Merged
merged 34 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
d2afa20
temporal changes
noctillion Oct 10, 2023
89ee9bf
remove logs
noctillion Oct 10, 2023
2c0cff8
types for last ingestion by datatypes
noctillion Oct 11, 2023
485fa67
Remove unnecessary files for wes request
noctillion Oct 11, 2023
471083a
add store for last ingestion in datatypes
noctillion Oct 11, 2023
bc2c8d9
refactor gohan request
noctillion Oct 11, 2023
3d9e9ba
add gohan/data-types endpoint
noctillion Oct 11, 2023
9e55a48
refactor katsu datatypes retrieval
noctillion Oct 11, 2023
01de0f6
remove wes request
noctillion Oct 11, 2023
88d359b
configure stire for lastIngestionData
noctillion Oct 11, 2023
2c436ef
dispatch acction for retrieve
noctillion Oct 11, 2023
1efabb1
remove unnecessary dispatchs
noctillion Oct 11, 2023
d171b73
refactor LastIngestionInfo for new data
noctillion Oct 11, 2023
8829bf7
add partial urls for data-ty retrieval pron katsu and gohan
noctillion Oct 11, 2023
8d5411d
prettier
noctillion Oct 11, 2023
103cefa
Merge branch 'main' into features/last_data_ingestion_from_data-types
noctillion Oct 30, 2023
84261ae
Merge branch 'main' into features/last_data_ingestion_from_data-types
noctillion Oct 30, 2023
c8c53a8
remove wes last ingestion retrieval
noctillion Nov 8, 2023
e91b818
Refactor last ingestion store
noctillion Nov 10, 2023
ebee93d
lint
noctillion Nov 10, 2023
bf98a5f
Update LastIngestion.tsx
noctillion Nov 11, 2023
e413073
Set count to null from gohan data-types response
noctillion Nov 15, 2023
d48c0a2
correct comment
noctillion Nov 15, 2023
e91b57a
remove logs
noctillion Nov 15, 2023
4cee043
Add request handling for data-types
noctillion Nov 16, 2023
8525ad8
Add clinical data to translation
noctillion Nov 20, 2023
1bfae35
Remove redundant types
noctillion Nov 20, 2023
012932d
Refactor lastIngestion store
noctillion Nov 20, 2023
5025155
Refactor LastIngestion types
noctillion Nov 20, 2023
a6e6fe3
Refactor lastIngestion rendering
noctillion Nov 20, 2023
89eb3d5
lint
noctillion Nov 20, 2023
b39f43a
Refactor combine array creation and sorting
noctillion Nov 20, 2023
8a038e5
Update fetching status flags
noctillion Nov 20, 2023
4006fdc
Disable makeGetDataTypes dispatch
noctillion Nov 20, 2023
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
40 changes: 22 additions & 18 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const ConfigLogTemplate = `Config --
Static Files: %s
Client Name: %s
Katsu URL: %v
WES URL: %v
Gohan URL: %v
Bento Portal Url: %s
Port: %d
Translated: %t
Expand All @@ -37,7 +37,7 @@ type BentoConfig struct {
StaticFilesPath string `envconfig:"BENTO_PUBLIC_STATIC_FILES_PATH" default:"./www"`
ClientName string `envconfig:"BENTO_PUBLIC_CLIENT_NAME"`
KatsuUrl string `envconfig:"BENTO_PUBLIC_KATSU_URL"`
WesUrl string `envconfig:"BENTO_PUBLIC_WES_URL"`
GohanUrl string `envconfig:"BENTO_PUBLIC_GOHAN_URL"`
BentoPortalUrl string `envconfig:"BENTO_PUBLIC_PORTAL_URL"`
Port int `envconfig:"INTERNAL_PORT" default:"8090"`
Translated bool `envconfig:"BENTO_PUBLIC_TRANSLATED" default:"true"`
Expand Down Expand Up @@ -114,7 +114,7 @@ func main() {
cfg.StaticFilesPath,
cfg.ClientName,
cfg.KatsuUrl,
cfg.WesUrl,
cfg.GohanUrl,
cfg.BentoPortalUrl,
cfg.Port,
cfg.Translated,
Expand Down Expand Up @@ -176,14 +176,6 @@ func main() {
return c.JSON(http.StatusOK, result)
}

wesRequest := func(path string, qs url.Values, c echo.Context, rf responseFormatterFunc) error {
result, err := genericRequestJsonOnly(fmt.Sprintf("%s%s", cfg.WesUrl, path), qs, c, rf)
if err != nil {
return err
}
return c.JSON(http.StatusOK, result)
}

katsuRequestBasic := func(path string, c echo.Context) error {
return katsuRequest(path, nil, c, jsonDeserialize)
}
Expand All @@ -201,11 +193,14 @@ func main() {
return jsonFormattedData, nil
}

wesRequestWithDetailsAndPublic := func(c echo.Context) error {
qs := url.Values{}
qs.Add("with_details", "true")
qs.Add("public", "true")
return wesRequest("/runs", qs, c, jsonDeserialize)
gohanRequestPublic := func(c echo.Context) error {
noctillion marked this conversation as resolved.
Show resolved Hide resolved
path := fmt.Sprintf("/data-types")
result, err := genericRequestJsonOnly(fmt.Sprintf("%s%s", cfg.GohanUrl, path), nil, c, jsonDeserialize)
if err != nil {
return err
}

return c.JSON(http.StatusOK, result)
}

fetchAndSetKatsuPublic := func(c echo.Context, katsuCache *cache.Cache) (JsonLike, error) {
Expand Down Expand Up @@ -359,8 +354,6 @@ func main() {
return katsuRequestBasic("/api/public_search_fields", c)
})

e.GET("/wes-runs", wesRequestWithDetailsAndPublic)

e.GET("/provenance", func(c echo.Context) error {
// Query Katsu for datasets provenance
return katsuRequestBasic("/api/public_dataset", c)
Expand All @@ -382,6 +375,17 @@ func main() {
return c.String(http.StatusOK, string(data))
})

e.GET("/gohan/data-types", gohanRequestPublic)

e.GET("/katsu/data-types", func(c echo.Context) error {
noctillion marked this conversation as resolved.
Show resolved Hide resolved
data, err := katsuRequestFormattedData("/data-types", c)
if err != nil {
return err
}

return c.String(http.StatusOK, string(data))
})

// Run
e.Logger.Fatal(e.Start(fmt.Sprintf(":%d", cfg.Port)))
}
Expand Down
18 changes: 11 additions & 7 deletions src/js/components/Overview/LastIngestion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ import { useTranslation } from 'react-i18next';
import { DEFAULT_TRANSLATION } from '@/constants/configConstants';
import { useAppSelector } from '@/hooks';
import { getDataTypeLabel } from '@/types/dataTypes';
import { LastIngestionDataTypeResponse, DataResponseArray } from '@/types/lastIngestionDataTypeResponse';

const LastIngestionInfo: React.FC = () => {
const { t, i18n } = useTranslation(DEFAULT_TRANSLATION);
const lastEndTimesByDataType = useAppSelector((state) => state.ingestionData?.lastEndTimesByDataType) || {};

const lastEndTimesByDataType: DataResponseArray = useAppSelector((state) => state.lastIngestionData?.dataTypes) || [];

const queryableDataTypes = lastEndTimesByDataType.filter((item: LastIngestionDataTypeResponse) => item.queryable);
noctillion marked this conversation as resolved.
Show resolved Hide resolved

const formatDate = useCallback(
(dateString: string) => {
Expand All @@ -28,19 +32,19 @@ const LastIngestionInfo: React.FC = () => {
[i18n.language]
);

const hasData = Object.keys(lastEndTimesByDataType).length > 0;
const hasData = queryableDataTypes.length > 0;

return (
<Space direction="vertical" size={0}>
<Typography.Title level={3}>{t('Latest Data Ingestion')}</Typography.Title>
<Typography.Title level={3}>{t('Latest Data IngestionX')}</Typography.Title>
<Space direction="horizontal">
{hasData ? (
Object.entries(lastEndTimesByDataType).map(([dataType, endTime]) => (
<Card key={dataType}>
queryableDataTypes.map((dataType: LastIngestionDataTypeResponse) => (
<Card key={dataType.id}>
<Space direction="vertical">
<Typography.Text style={{ color: 'rgba(0,0,0,0.45)' }}>{t(getDataTypeLabel(dataType))}</Typography.Text>
<Typography.Text style={{ color: 'rgba(0,0,0,0.45)' }}>{getDataTypeLabel(dataType.id)}</Typography.Text>
<Typography.Text>
<CalendarOutlined /> {formatDate(endTime)}
<CalendarOutlined /> {dataType.last_ingested ? formatDate(dataType.last_ingested) : 'Not Available'}
</Typography.Text>
</Space>
</Card>
Expand Down
7 changes: 5 additions & 2 deletions src/js/components/TabbedDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import { makeGetAboutRequest } from '@/features/content/content.store';
import { makeGetDataRequestThunk } from '@/features/data/data.store';
import { makeGetSearchFields } from '@/features/search/query.store';
import { makeGetProvenanceRequest } from '@/features/provenance/provenance.store';
import { makeGetIngestionDataRequest } from '@/features/ingestion/ingestion.store';
import { getBeaconConfig } from '@/features/beacon/beaconConfig.store';
import { makeGetDataTypes } from '@/features/dataTypes/dataTypes.store';
import { fetchGohanData, fetchKatsuData } from '@/features/ingestion/lastIngestion.store';

import Loader from './Loader';
import PublicOverview from './Overview/PublicOverview';
Expand All @@ -35,7 +36,9 @@ const TabbedDashboard = () => {
dispatch(makeGetDataRequestThunk());
dispatch(makeGetSearchFields());
dispatch(makeGetProvenanceRequest());
dispatch(makeGetIngestionDataRequest());
dispatch(fetchKatsuData());
dispatch(fetchGohanData());
dispatch(makeGetDataTypes());
}, []);

const isFetchingOverviewData = useAppSelector((state) => state.data.isFetchingData);
Expand Down
3 changes: 2 additions & 1 deletion src/js/constants/configConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ export const publicOverviewUrl = '/overview';
export const searchFieldsUrl = '/fields';
export const katsuUrl = '/katsu';
export const provenanceUrl = '/provenance';
export const lastIngestionsUrl = '/wes-runs';
export const katsuLastIngestionsUrl = '/katsu/data-types';
export const gohanLastIngestionsUrl = '/gohan/data-types';

export const DEFAULT_TRANSLATION = 'default_translation';
export const NON_DEFAULT_TRANSLATION = 'translation';
Expand Down
57 changes: 0 additions & 57 deletions src/js/features/ingestion/ingestion.store.ts

This file was deleted.

75 changes: 75 additions & 0 deletions src/js/features/ingestion/lastIngestion.store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
import axios from 'axios';
import { katsuLastIngestionsUrl, gohanLastIngestionsUrl } from '@/constants/configConstants';
import { printAPIError } from '@/utils/error.util';

export interface DataTypeResponse {
count: number | null;
id: string;
label: string;
last_ingested: string | null;
queryable: boolean;
}

export type DataResponseArray = DataTypeResponse[];

// Async thunks to fetch data from the two endpoints
export const fetchKatsuData = createAsyncThunk('dataTypes/fetchKatsuData', (_, { rejectWithValue }) =>
axios
.get(katsuLastIngestionsUrl)
.then((res) => res.data)
.catch(printAPIError(rejectWithValue))
);

export const fetchGohanData = createAsyncThunk('dataTypes/fetchGohanData', (_, { rejectWithValue }) =>
axios
.get(gohanLastIngestionsUrl)
.then((res) => res.data)
.catch(printAPIError(rejectWithValue))
);

// Define the state structure
export interface DataTypeState {
isFetchingData: boolean;
dataTypes: DataResponseArray;
}

// Initialize the state
const initialDataTypeState: DataTypeState = {
isFetchingData: false,
dataTypes: [],
};

// Create a slice to manage the state
const DataTypeStore = createSlice({
name: 'dataTypes',
initialState: initialDataTypeState,
reducers: {},
extraReducers: (builder) => {
builder.addCase(fetchKatsuData.pending, (state) => {
state.isFetchingData = true;
});
builder.addCase(fetchGohanData.pending, (state) => {
state.isFetchingData = true;
});
builder.addCase(fetchKatsuData.fulfilled, (state, { payload }: PayloadAction<DataResponseArray>) => {
const uniqueIds = new Set(state.dataTypes.map((data) => data.id));
const newData = payload.filter((data) => !uniqueIds.has(data.id));
state.dataTypes = [...state.dataTypes, ...newData];
noctillion marked this conversation as resolved.
Show resolved Hide resolved
});
builder.addCase(fetchGohanData.fulfilled, (state, { payload }: PayloadAction<DataResponseArray>) => {
const uniqueIds = new Set(state.dataTypes.map((data) => data.id));
const newData = payload.filter((data) => !uniqueIds.has(data.id));
state.dataTypes = [...state.dataTypes, ...newData];
state.isFetchingData = false;
});
builder.addCase(fetchKatsuData.rejected, (state) => {
state.isFetchingData = false;
});
builder.addCase(fetchGohanData.rejected, (state) => {
state.isFetchingData = false;
noctillion marked this conversation as resolved.
Show resolved Hide resolved
});
},
});

export default DataTypeStore.reducer;
4 changes: 2 additions & 2 deletions src/js/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import configReducer from '@/features/config/config.store';
import contentReducer from '@/features/content/content.store';
import dataReducer from '@/features/data/data.store';
import queryReducer from '@/features/search/query.store';
import ingestionDataReducer from '@/features/ingestion/ingestion.store';
import lastIngestionDataReducer from '@/features/ingestion/lastIngestion.store';
import provenanceReducer from '@/features/provenance/provenance.store';
import beaconConfigReducer from './features/beacon/beaconConfig.store';
import beaconQueryReducer from './features/beacon/beaconQuery.store';
Expand All @@ -16,7 +16,7 @@ export const store = configureStore({
data: dataReducer,
query: queryReducer,
provenance: provenanceReducer,
ingestionData: ingestionDataReducer,
lastIngestionData: lastIngestionDataReducer,
beaconConfig: beaconConfigReducer,
beaconQuery: beaconQueryReducer,
},
Expand Down
9 changes: 9 additions & 0 deletions src/js/types/lastIngestionDataTypeResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export interface LastIngestionDataTypeResponse {
count: number | null;
id: string;
label: string;
last_ingested: string | null;
queryable: boolean;
}

export type DataResponseArray = LastIngestionDataTypeResponse[];
noctillion marked this conversation as resolved.
Show resolved Hide resolved
36 changes: 0 additions & 36 deletions src/js/types/lastIngestionResponse.ts

This file was deleted.