Skip to content

Commit

Permalink
Merge pull request #223 from bento-platform/refact/data-type-labels-u…
Browse files Browse the repository at this point in the history
…sing-i18n

refact: use i18n for data type / entity labels
  • Loading branch information
davidlougheed authored Nov 14, 2024
2 parents 9e76b35 + e10976b commit f84e5a2
Show file tree
Hide file tree
Showing 15 changed files with 94 additions and 71 deletions.
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"redux-thunk": "^2.4.1"
},
"devDependencies": {
"@types/json-schema": "^7.0.15",
"@types/react-dom": "^18.3.1",
"@typescript-eslint/eslint-plugin": "^7.16.1",
"@typescript-eslint/parser": "^7.16.1",
Expand Down
3 changes: 2 additions & 1 deletion src/js/components/Beacon/BeaconCommon/BeaconQueryFormUi.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
BUTTON_STYLE,
CARD_STYLES,
} from '@/constants/beaconConstants';
import { T_PLURAL_COUNT } from '@/constants/i18n';

const STARTER_FILTER = { index: 1, active: true };
const VARIANTS_FORM_ERROR_MESSAGE =
Expand Down Expand Up @@ -233,7 +234,7 @@ const BeaconQueryFormUi = ({
{hasVariants && (
<Col xs={24} lg={12}>
<Card
title={t('entities.Variants')}
title={t('entities.variant', T_PLURAL_COUNT)}
style={CARD_STYLE}
styles={CARD_STYLES}
extra={
Expand Down
6 changes: 2 additions & 4 deletions src/js/components/BentoAppRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,8 @@ const BentoAppRouter = () => {
dispatch(makeGetAboutRequest());
dispatch(fetchGohanData());
dispatch(makeGetServiceInfoRequest());
if (isAuthenticated) {
dispatch(makeGetDataTypes());
}
}, [dispatch, isAuthenticated]);
dispatch(makeGetDataTypes());
}, [dispatch]);

if (isAutoAuthenticating || projectsStatus === RequestStatus.Pending) {
return <Loader />;
Expand Down
15 changes: 8 additions & 7 deletions src/js/components/Overview/Counts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { InfoCircleOutlined, TeamOutlined } from '@ant-design/icons';
import { BiDna } from 'react-icons/bi';

import ExpSvg from '../Util/ExpSvg';
import { T_PLURAL_COUNT } from '@/constants/i18n';
import { BOX_SHADOW, COUNTS_FILL } from '@/constants/overviewConstants';
import { useAppSelector, useTranslationFn } from '@/hooks';

Expand All @@ -25,19 +26,19 @@ const Counts = () => {
// Break down help into multiple sentences inside an array to make translation a bit easier.
const data = [
{
title: 'Individuals',
entity: 'individual',
help: ['individual_help_1'],
icon: <TeamOutlined />,
count: counts.individuals,
},
{
title: 'Biosamples',
entity: 'biosample',
help: ['biosample_help_1'],
icon: <BiDna />,
count: counts.biosamples,
},
{
title: 'Experiments',
entity: 'experiment',
help: ['experiment_help_1', 'experiment_help_2'],
icon: <ExpSvg />,
count: counts.experiments,
Expand All @@ -48,17 +49,17 @@ const Counts = () => {
<>
<Typography.Title level={3}>{t('Counts')}</Typography.Title>
<Space wrap>
{data.map(({ title, help, icon, count }, i) => {
const titleTransl = t(`entities.${title}`);
{data.map(({ entity, help, icon, count }, i) => {
const title = t(`entities.${entity}`, T_PLURAL_COUNT);
return (
<Card key={i} style={{ ...styles.countCard, height: isFetchingData ? 138 : 114 }}>
<Statistic
title={
<Space>
{titleTransl}
{title}
{help && (
<Popover
title={titleTransl}
title={title}
content={
<CountsHelp>
{help.map((h, i) => (
Expand Down
4 changes: 2 additions & 2 deletions src/js/components/Overview/LastIngestion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { CalendarOutlined } from '@ant-design/icons';
import { useTranslation } from 'react-i18next';

import { useAppSelector } from '@/hooks';
import { getDataTypeLabel } from '@/types/dataTypes';

import type { LastIngestionDataTypeResponse } from '@/types/lastIngestionDataTypeResponse';
import { T_PLURAL_COUNT } from '@/constants/i18n';
import { BOX_SHADOW } from '@/constants/overviewConstants';

const LastIngestionInfo: React.FC = () => {
Expand Down Expand Up @@ -48,7 +48,7 @@ const LastIngestionInfo: React.FC = () => {
<Card style={BOX_SHADOW} key={dataType.id}>
<Space direction="vertical">
<Typography.Text style={{ color: 'rgba(0,0,0,0.45)' }}>
{t(getDataTypeLabel(dataType.id))}
{t(`entities.${dataType.id}`, T_PLURAL_COUNT)}
</Typography.Text>
<Typography.Text>
<CalendarOutlined />{' '}
Expand Down
7 changes: 4 additions & 3 deletions src/js/components/Search/SearchResultsCounts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Skeleton, Space, Statistic } from 'antd';
import { TeamOutlined } from '@ant-design/icons';
import { BiDna } from 'react-icons/bi';

import { T_PLURAL_COUNT } from '@/constants/i18n';
import { COUNTS_FILL } from '@/constants/overviewConstants';
import { NO_RESULTS_DASHES } from '@/constants/searchConstants';
import ExpSvg from '@/components/Util/ExpSvg';
Expand Down Expand Up @@ -58,7 +59,7 @@ const SearchResultsCounts = ({
].join(' ')}
>
<Statistic
title={t('entities.Individuals')}
title={t('entities.individual', T_PLURAL_COUNT)}
value={
hasInsufficientData
? t(message ?? '')
Expand All @@ -71,13 +72,13 @@ const SearchResultsCounts = ({
/>
</div>
<Statistic
title={t('entities.Biosamples')}
title={t('entities.biosample', T_PLURAL_COUNT)}
value={hasInsufficientData || (isBeaconNetwork && !biosampleCount) ? NO_RESULTS_DASHES : biosampleCount}
valueStyle={STAT_STYLE}
prefix={<BiDna />}
/>
<Statistic
title={t('entities.Experiments')}
title={t('entities.experiment', T_PLURAL_COUNT)}
value={hasInsufficientData || (isBeaconNetwork && !experimentCount) ? NO_RESULTS_DASHES : experimentCount}
valueStyle={STAT_STYLE}
prefix={<ExpSvg />}
Expand Down
7 changes: 4 additions & 3 deletions src/js/components/Search/SearchResultsPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { LeftOutlined } from '@ant-design/icons';
import { PieChart } from 'bento-charts';

import { PORTAL_URL } from '@/config';
import { T_PLURAL_COUNT, T_SINGULAR_COUNT } from '@/constants/i18n';
import { BOX_SHADOW, PIE_CHART_HEIGHT } from '@/constants/overviewConstants';
import { useTranslationFn } from '@/hooks';
import type { DiscoveryResults } from '@/types/data';
Expand Down Expand Up @@ -33,7 +34,7 @@ const SearchResultsPane = ({
() => [
{
dataIndex: 'id',
title: t('entities.Individual'),
title: t('entities.individual', T_SINGULAR_COUNT),
render: (id: string) => (
<a href={`${PORTAL_URL}/data/explorer/individuals/${id}`} target="_blank" rel="noreferrer">
{id}
Expand Down Expand Up @@ -85,7 +86,7 @@ const SearchResultsPane = ({
<>
<Col xs={24} lg={10}>
<Typography.Title level={5} style={{ marginTop: 0 }}>
{t('entities.Biosamples')}
{t('entities.biosample', T_PLURAL_COUNT)}
</Typography.Title>
{!hasInsufficientData && biosampleChartData.length ? (
<PieChart data={biosampleChartData} height={PIE_CHART_HEIGHT} sort={true} dataMap={translateMap} />
Expand All @@ -95,7 +96,7 @@ const SearchResultsPane = ({
</Col>
<Col xs={24} lg={10}>
<Typography.Title level={5} style={{ marginTop: 0 }}>
{t('entities.Experiments')}
{t('entities.experiment', T_PLURAL_COUNT)}
</Typography.Title>
{!hasInsufficientData && experimentChartData.length ? (
<PieChart data={experimentChartData} height={PIE_CHART_HEIGHT} sort={true} dataMap={translateMap} />
Expand Down
4 changes: 4 additions & 0 deletions src/js/constants/i18n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// false count – just need the highest form of plural
// - see https://www.i18next.com/translation-function/plurals
export const T_PLURAL_COUNT = { count: 100 };
export const T_SINGULAR_COUNT = { count: 1 };
6 changes: 5 additions & 1 deletion src/js/features/config/config.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@ export const makeGetServiceInfoRequest = createAsyncThunk<
condition(_, { getState }) {
const { serviceInfoStatus } = getState().config;
const cond = serviceInfoStatus === RequestStatus.Idle;
if (!cond) console.debug(`makeGetServiceInfoRequest(), but a prior attempt gave status: ${serviceInfoStatus}`);
if (!cond) {
console.debug(
`makeGetServiceInfoRequest() was attempted, but a prior attempt gave status: ${serviceInfoStatus}`
);
}
return cond;
},
}
Expand Down
49 changes: 30 additions & 19 deletions src/js/features/dataTypes/dataTypes.store.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,45 @@
import axios from 'axios';
import type { PayloadAction } from '@reduxjs/toolkit';
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';

import type { RootState } from '@/store';
import type { BentoServiceDataType } from '@/types/dataTypes';
import { RequestStatus } from '@/types/requests';
import { authorizedRequestConfig } from '@/utils/requests';

// TODO: find a way to allow this without an auth token
export const makeGetDataTypes = createAsyncThunk<
object,
BentoServiceDataType[],
void,
{
rejectValue: string;
state: RootState;
}
>('dataTypes/makeGetDataTypes', async (_, { getState }) => {
// Not scoped currently - this is a way to get all data types in an instance from service registry, but it may make
// sense in the future to forward query params to nested calls depending on scope value especially in the case of
// count handling. TBD - TODO: figure this out
const res = await axios.get('/api/service-registry/data-types', authorizedRequestConfig(getState()));
return res.data;
});
>(
'dataTypes/makeGetDataTypes',
async (_, { getState }) => {
// Not scoped currently - this is a way to get all data types in an instance from service registry, but it may make
// sense in the future to forward query params to nested calls depending on scope value especially in the case of
// count handling. TBD - TODO: figure this out
const res = await axios.get('/api/service-registry/data-types', authorizedRequestConfig(getState()));
return res.data;
},
{
condition(_, { getState }) {
const { status } = getState().dataTypes;
const cond = status === RequestStatus.Idle;
if (!cond) console.debug(`makeGetDataTypes() was attempted, but a prior attempt gave status: ${status}`);
return cond;
},
}
);

export type DataTypesState = {
isFetching: boolean;
dataTypes: object;
status: RequestStatus;
dataTypesById: Record<string, BentoServiceDataType>;
};

const initialState: DataTypesState = {
isFetching: false,
dataTypes: {},
status: RequestStatus.Idle,
dataTypesById: {},
};

const dataTypes = createSlice({
Expand All @@ -37,14 +48,14 @@ const dataTypes = createSlice({
reducers: {},
extraReducers(builder) {
builder.addCase(makeGetDataTypes.pending, (state) => {
state.isFetching = true;
state.status = RequestStatus.Pending;
});
builder.addCase(makeGetDataTypes.fulfilled, (state, { payload }: PayloadAction<object>) => {
state.isFetching = false;
state.dataTypes = { ...payload };
builder.addCase(makeGetDataTypes.fulfilled, (state, { payload }) => {
state.status = RequestStatus.Fulfilled;
state.dataTypesById = Object.fromEntries(payload.map((dt) => [dt.id, dt]));
});
builder.addCase(makeGetDataTypes.rejected, (state) => {
state.isFetching = false;
state.status = RequestStatus.Rejected;
});
},
});
Expand Down
2 changes: 2 additions & 0 deletions src/js/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { LS_OPENID_CONFIG_KEY, AuthReducer as auth, OIDCReducer as openIdConfigu
import configReducer from '@/features/config/config.store';
import contentReducer from '@/features/content/content.store';
import dataReducer from '@/features/data/data.store';
import dataTypesReducer from '@/features/dataTypes/dataTypes.store';
import queryReducer from '@/features/search/query.store';
import lastIngestionDataReducer from '@/features/ingestion/lastIngestion.store';
import beaconReducer from './features/beacon/beacon.store';
Expand All @@ -32,6 +33,7 @@ export const store = configureStore({
config: configReducer,
content: contentReducer,
data: dataReducer,
dataTypes: dataTypesReducer,
query: queryReducer,
lastIngestionData: lastIngestionDataReducer,
beacon: beaconReducer,
Expand Down
28 changes: 9 additions & 19 deletions src/js/types/dataTypes.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,12 @@
export enum DataTypes {
phenopacket = 'phenopacket',
experiment = 'experiment',
experiment_result = 'experiment_result',
variant = 'variant',
}
import type { JSONSchema7 } from 'json-schema';

export const DataTypesLabels = {
[DataTypes.phenopacket]: 'entities.Phenopackets',
[DataTypes.experiment]: 'entities.Experiments',
[DataTypes.experiment_result]: 'entities.Experiment Results',
[DataTypes.variant]: 'entities.Variants',
export type BentoDataType = {
id: string;
label: string;
queryable: boolean;
schema: JSONSchema7;
metadata_schema: JSONSchema7;
count?: number;
};

export const getDataTypeLabel = (dataTypeString: string): string => {
const dataTypesValues = Object.values(DataTypes) as string[];
if (dataTypesValues.includes(dataTypeString)) {
const dataType: DataTypes = DataTypes[dataTypeString as keyof typeof DataTypes];
return DataTypesLabels[dataType];
}
return 'Unknown Data Type';
};
export type BentoServiceDataType = BentoDataType & { service_base_url: string };
16 changes: 10 additions & 6 deletions src/public/locales/en/default_translation_en.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@
"Portal": "Portal",
"missing": "missing",
"entities": {
"Phenopackets": "Clinical Data",
"Individual": "Individual",
"Individuals": "Individuals",
"phenopacket_one": "Phenopacket",
"phenopacket_other": "Clinical Data",
"individual_one": "Individual",
"individual_other": "Individuals",
"individual_help_1": "Individuals represent a specific person / organism, and may have one or multiple associated biosamples, each with associated experiments.",
"Biosamples": "Biosamples",
"biosample_one": "Biosample",
"biosample_other": "Biosamples",
"biosample_help_1": "Biosamples are usually biological material extracted from a specific individual.",
"Experiments": "Experiments",
"experiment_one": "Experiment",
"experiment_other": "Experiments",
"experiment_help_1": "Experiments are a process done to a specific sample, e.g., whole-genome sequencing.",
"experiment_help_2": "One lab experiment may result in multiple experiment records inside the portal, such as with multiplexed samples.",
"Variants": "Variants"
"variant_one": "Variant",
"variant_other": "Variants"
},
"in": "in",
"Get Data": "Get Data",
Expand Down
16 changes: 10 additions & 6 deletions src/public/locales/fr/default_translation_fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@
"Portal": "Portail",
"missing": "manquant(s)",
"entities": {
"Phenopackets": "Données cliniques",
"Individual": "Participant",
"Individuals": "Participants",
"phenopacket_one": "Phenopacket",
"phenopacket_other": "Données cliniques",
"individual_one": "Participant",
"individual_other": "Participants",
"individual_help_1": "Les participants représentent une personne/un organisme spécifique et peuvent avoir un ou plusieurs échantillons biologiques associés, chacun avec des expériences associées.",
"Biosamples": "Échantillons biologiques",
"biosample_one": "Échantillon biologique",
"biosample_other": "Échantillons biologiques",
"biosample_help_1": "Les échantillons biologiques sont généralement du matériel biologique extrait d’un participant spécifique.",
"Experiments": "Expériences",
"experiment_one": "Expérience",
"experiment_other": "Expériences",
"experiment_help_1": "Les expériences sont un processus effectué sur un échantillon spécifique, par exemple le séquençage du génome entier.",
"experiment_help_2": "Une expérience par un laboratoire peut donner lieu à plusieurs enregistrements d'expériences dans le portail, par exemple avec des échantillons multiplexés.",
"Variants": "Variants"
"variant_one": "Variant",
"variant_other": "Variants"
},
"in": "en",
"Get Data": "Obtenir",
Expand Down

0 comments on commit f84e5a2

Please sign in to comment.