From 9275ed378ff7766539a13545c14ec44b6df43998 Mon Sep 17 00:00:00 2001 From: Juan Nicolas Herrera <54152074+juanNH@users.noreply.github.com> Date: Fri, 25 Sep 2026 14:14:36 -0300 Subject: [PATCH 1/3] Add miRBase links and metric explanations --- .../static/frontend/eslint.config.mjs | 7 +++ .../BiomarkerTrainedModelsTable.tsx | 27 ++++++++-- .../ModelDetailsPanels.tsx | 32 +++++++++-- .../SamplesAndGroupsInferenceTable.tsx | 8 ++- .../SamplesAndTimeInferenceCharts.tsx | 5 ++ .../SamplesAndTimeInferenceTable.tsx | 8 ++- .../GeneOntologyCytoscapeChart.tsx | 14 ++++- .../gene-ontology/GeneOntologyPanel.tsx | 8 ++- .../genes/GeneAssociationsNetworkPanel.tsx | 2 +- .../StatisticalValidationMenu.tsx | 2 +- .../result/SamplesAndGroupsTable.tsx | 7 ++- ...tatisticalValidationResultBestFeatures.tsx | 12 ++++- .../StatisticalValidationResultHeatMap.tsx | 14 ++++- ...StatisticalValidationResultKaplanMeier.tsx | 11 ++-- .../StatisticalValidationResultMetrics.tsx | 22 +++++--- .../src/components/biomarkers/types.ts | 2 +- .../gene-gem-details/MiRNAExtraData.tsx | 54 ++++++++----------- .../MiRNATargetInteractionPanel.tsx | 8 +++ .../static/frontend/src/locales/en.ts | 48 +++++++++++++---- .../static/frontend/src/locales/es.ts | 48 +++++++++++++---- 20 files changed, 253 insertions(+), 86 deletions(-) diff --git a/src/frontend/static/frontend/eslint.config.mjs b/src/frontend/static/frontend/eslint.config.mjs index cffcd606..67fda82e 100644 --- a/src/frontend/static/frontend/eslint.config.mjs +++ b/src/frontend/static/frontend/eslint.config.mjs @@ -35,6 +35,13 @@ export default defineConfig([ 'no-undef': 'off', camelcase: 'off', curly: ['error', 'all'], + 'no-restricted-syntax': [ + 'warn', + { + selector: ':matches(FunctionDeclaration, FunctionExpression, ArrowFunctionExpression)[async=true]', + message: 'Prefer promise chains with .then()/.catch() over async/await and try/catch for asynchronous operations.' + } + ], 'no-multi-spaces': 'error', 'padding-line-between-statements': [ 'warn', diff --git a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/BiomarkerTrainedModelsTable.tsx b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/BiomarkerTrainedModelsTable.tsx index 7c22fe09..40125d0d 100644 --- a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/BiomarkerTrainedModelsTable.tsx +++ b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/BiomarkerTrainedModelsTable.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react' import { PaginatedTable, PaginationCustomFilter } from '../../common/PaginatedTable' -import { Biomarker, BiomarkerState, TrainedModelForTable, TrainedModelState } from '../types' +import { Biomarker, BiomarkerState, FitnessFunction, TrainedModelForTable, TrainedModelState } from '../types' import { Button, Form, Header, Icon, Modal, Table } from 'semantic-ui-react' import { TableCellWithTitle } from '../../common/TableCellWithTitle' import { alertGeneralError, formatDateLocale, getDjangoHeader } from '../../../utils/util_functions' @@ -14,6 +14,7 @@ import { StopExperimentButton } from '../../pipeline/all-experiments-view/StopEx import { DeleteButton } from '../../common/DeleteButton' import { TableCellSources } from '../../common/TableCellSources' import { useIntl } from 'react-intl' +import { InfoPopup } from '../../pipeline/experiment-result/gene-gem-details/InfoPopup' declare const urlBiomarkerTrainedModels: string declare const urlStopTrainedModel: string @@ -221,8 +222,16 @@ export const BiomarkerTrainedModelsTable = (props: BiomarkerTrainedModelsPanelPr { name: intl.formatMessage({ id: 'common.state' }), serverCodeToSort: 'state', width: 1 }, { name: intl.formatMessage({ id: 'biomarkerTrainedModelsTable.header.model' }), serverCodeToSort: 'fitness_function', width: 1 }, { name: intl.formatMessage({ id: 'common.date' }), serverCodeToSort: 'created' }, - { name: intl.formatMessage({ id: 'common.metric' }), serverCodeToSort: 'fitness_metric' }, - { name: intl.formatMessage({ id: 'biomarkerTrainedModelsTable.header.bestCVMetric' }), serverCodeToSort: 'best_fitness_value' }, + { + name: intl.formatMessage({ id: 'common.metric' }), + serverCodeToSort: 'fitness_metric', + infoPopupContent: intl.formatMessage({ id: 'trainedModels.info.fitnessMetric' }) + }, + { + name: intl.formatMessage({ id: 'biomarkerTrainedModelsTable.header.bestCVMetric' }), + serverCodeToSort: 'best_fitness_value', + infoPopupContent: intl.formatMessage({ id: 'trainedModels.info.bestScore' }) + }, { name: intl.formatMessage({ id: 'biomarkerTrainedModelsTable.header.datasets' }) }, ...actionColumn @@ -261,6 +270,11 @@ export const BiomarkerTrainedModelsTable = (props: BiomarkerTrainedModelsPanelPr mapFunction={(trainedModel: TrainedModelForTable) => { const isInProcess = trainedModel.state === TrainedModelState.IN_PROCESS || trainedModel.state === TrainedModelState.WAITING_FOR_QUEUE + const scoreInfoId = trainedModel.fitness_metric === 'Log Likelihood' + ? 'modelDetails.info.bestLogLikelihood' + : trainedModel.fitness_function === FitnessFunction.CLUSTERING + ? 'modelDetails.info.bestClusteringCIndex' + : 'modelDetails.info.bestCIndex' return ( {trainedModel.fitness_metric ?? '-'} - {trainedModel.best_fitness_value ? trainedModel.best_fitness_value.toFixed(4) : '-'} + + {trainedModel.best_fitness_value != null ? trainedModel.best_fitness_value.toFixed(4) : '-'} + {trainedModel.best_fitness_value != null && ( + + )} + { const intl = useIntl() + const bestScoreInfoId = props.fitness_function === FitnessFunction.CLUSTERING + ? (props.data as ClusteringModelDetails).scoring_method === ClusteringScoringMethod.LOG_LIKELIHOOD + ? 'modelDetails.info.bestLogLikelihood' + : 'modelDetails.info.bestClusteringCIndex' + : 'modelDetails.info.bestCIndex' return ( <> @@ -30,6 +36,7 @@ const GeneralMetrics = (props: GeneralMetricsProps) => { {intl.formatMessage({ id: 'common.model' })} + { - {intl.formatMessage({ id: 'modelDetails.general.bestFitnessValue' })} {props.data.best_fitness.toFixed(4)} + {intl.formatMessage({ id: 'modelDetails.general.bestFitnessValue' })} {props.data.best_fitness != null ? props.data.best_fitness.toFixed(4) : '-'} + @@ -52,6 +60,7 @@ const GeneralMetrics = (props: GeneralMetricsProps) => { {intl.formatMessage({ id: 'common.randomState' })} {props.data.random_state ?? '-'} + @@ -78,6 +87,7 @@ const ClusteringModelDetailsPanel = (props: ClusteringModelDetailsPanelProps) => {intl.formatMessage({ id: 'common.algorithm' })} + @@ -88,6 +98,7 @@ const ClusteringModelDetailsPanel = (props: ClusteringModelDetailsPanelProps) => {intl.formatMessage({ id: 'common.scoringMethod' })} + @@ -96,7 +107,10 @@ const ClusteringModelDetailsPanel = (props: ClusteringModelDetailsPanelProps) => - {intl.formatMessage({ id: 'common.nClusters' })} {props.data.n_clusters} + + {intl.formatMessage({ id: 'common.nClusters' })} {props.data.n_clusters} + + @@ -122,6 +136,7 @@ const SVMModelDetailsPanel = (props: SVMModelDetailsPanelProps) => { Kernel: + @@ -132,6 +147,7 @@ const SVMModelDetailsPanel = (props: SVMModelDetailsPanelProps) => { {intl.formatMessage({ id: 'modelDetails.svm.task' })} + @@ -158,13 +174,19 @@ const RFModelDetailsPanel = (props: RFModelDetailsPanelProps) => { - {intl.formatMessage({ id: 'modelDetails.rf.numberOfEstimators' })} {props.data.n_estimators} + + {intl.formatMessage({ id: 'modelDetails.rf.numberOfEstimators' })} {props.data.n_estimators} + + - {intl.formatMessage({ id: 'modelDetails.rf.maxDepth' })} {props.data.max_depth ?? '-'} + + {intl.formatMessage({ id: 'modelDetails.rf.maxDepth' })} {props.data.max_depth ?? '-'} + + diff --git a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/inference/SamplesAndGroupsInferenceTable.tsx b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/inference/SamplesAndGroupsInferenceTable.tsx index c774d6eb..f09080a0 100644 --- a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/inference/SamplesAndGroupsInferenceTable.tsx +++ b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/inference/SamplesAndGroupsInferenceTable.tsx @@ -50,7 +50,13 @@ export const SamplesAndGroupsInferenceTable = (props: SamplesAndGroupsInferenceT headers={[ { name: intl.formatMessage({ id: 'common.sample' }), serverCodeToSort: 'sample', width: 3, textAlign: 'center' }, - { name: intl.formatMessage({ id: 'common.cluster' }), serverCodeToSort: 'cluster', width: 2, textAlign: 'center' } + { + name: intl.formatMessage({ id: 'common.cluster' }), + serverCodeToSort: 'cluster', + width: 2, + textAlign: 'center', + infoPopupContent: intl.formatMessage({ id: 'metricInfo.clusterId' }) + } ]} queryParams={{ diff --git a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/inference/SamplesAndTimeInferenceCharts.tsx b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/inference/SamplesAndTimeInferenceCharts.tsx index cb3eb9b1..abfd1f98 100644 --- a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/inference/SamplesAndTimeInferenceCharts.tsx +++ b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/inference/SamplesAndTimeInferenceCharts.tsx @@ -9,6 +9,7 @@ import { ResultPlaceholder } from '../stat-validations/result/ResultPlaceholder' import { BoxPlotChart } from '../../../pipeline/experiment-result/gene-gem-details/stats/BoxPlotChart' import { COLOR_YELLOW_FILL, COLOR_YELLOW_STROKE } from '../../../../utils/constants' import { useIntl } from 'react-intl' +import { InfoPopup } from '../../../pipeline/experiment-result/gene-gem-details/InfoPopup' declare const urlClinicalSourceAddOrEditInferenceExperiment: string declare const urlUnlinkClinicalSourceInferenceExperiment: string @@ -128,6 +129,10 @@ export const SamplesAndTimeInferenceCharts = (props: SamplesAndTimeInferenceChar return (
+
+ {intl.formatMessage({ id: 'inference.timeTable.columns.predictedTime' })} + +
( diff --git a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/inference/SamplesAndTimeInferenceTable.tsx b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/inference/SamplesAndTimeInferenceTable.tsx index 65dd11c4..451514a8 100644 --- a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/inference/SamplesAndTimeInferenceTable.tsx +++ b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/inference/SamplesAndTimeInferenceTable.tsx @@ -51,7 +51,13 @@ export const SamplesAndTimeInferenceTable = (props: SamplesAndTimeInferenceTable headers={[ { name: intl.formatMessage({ id: 'common.sample' }), serverCodeToSort: 'sample', width: 3, textAlign: 'center' }, - { name: intl.formatMessage({ id: 'inference.timeTable.columns.predictedTime' }), serverCodeToSort: 'prediction', width: 2, textAlign: 'center' } + { + name: intl.formatMessage({ id: 'inference.timeTable.columns.predictedTime' }), + serverCodeToSort: 'prediction', + width: 2, + textAlign: 'center', + infoPopupContent: intl.formatMessage({ id: 'metricInfo.inferencePrediction' }) + } ]} queryParams={{ diff --git a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/molecules/gene-ontology/GeneOntologyCytoscapeChart.tsx b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/molecules/gene-ontology/GeneOntologyCytoscapeChart.tsx index 34db0c30..cb79cf29 100644 --- a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/molecules/gene-ontology/GeneOntologyCytoscapeChart.tsx +++ b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/molecules/gene-ontology/GeneOntologyCytoscapeChart.tsx @@ -7,6 +7,8 @@ import { OntologyRelationTermToTermFilter, CytoscapeElements, OntologyType, GoTe import { Button, Form, Grid } from 'semantic-ui-react' import '../../../../../css/cytoscape.css' import { useIntl } from 'react-intl' +import { InputLabel } from '../../../../common/InputLabel' +import { InfoPopup } from '../../../../pipeline/experiment-result/gene-gem-details/InfoPopup' declare const urlGOTermToTerms: string @@ -228,13 +230,21 @@ export const GeneOntologyCytoscapeChart = (props: GeneOntologyCytoscapeChartProp ))} + + + )} type='number' value={termsRelatedToTermForm.general_depth} onChange={(e) => handleChangesInForm('general_depth', parseInt(e.target.value))} /> + + + )} type='number' value={termsRelatedToTermForm.hierarchical_depth_to_children} onChange={(e) => handleChangesInForm('hierarchical_depth_to_children', parseInt(e.target.value))} diff --git a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/molecules/gene-ontology/GeneOntologyPanel.tsx b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/molecules/gene-ontology/GeneOntologyPanel.tsx index ac4b01f6..5d1d97f7 100644 --- a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/molecules/gene-ontology/GeneOntologyPanel.tsx +++ b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/molecules/gene-ontology/GeneOntologyPanel.tsx @@ -8,6 +8,8 @@ import { GeneOntologyCytoscapeChart } from './GeneOntologyCytoscapeChart' import { GORelationType, GeneToTermFilterType, GeneToTermForm, GeneToTermSearchParams, GoTerm, OntologyType, TermsRelatedToGene } from './types' import { debounce } from 'lodash' import { useIntl } from 'react-intl' +import { InputLabel } from '../../../../common/InputLabel' +import { InfoPopup } from '../../../../pipeline/experiment-result/gene-gem-details/InfoPopup' // Defined in biomarkers.html declare const urlGOGeneToTerms: string @@ -151,7 +153,11 @@ export const GeneOntologyPanel = (props: GeneOntologyPanelProps) => { {termsRelatedToGeneForm.filter_type === GeneToTermFilterType.ENRICHMENT && ( <> + + + )} type='number' value={termsRelatedToGeneForm.p_value_threshold} onChange={(_e, { value }) => handleChangesInForm('p_value_threshold', value)} diff --git a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/molecules/genes/GeneAssociationsNetworkPanel.tsx b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/molecules/genes/GeneAssociationsNetworkPanel.tsx index bc4312cf..2fae23d6 100644 --- a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/molecules/genes/GeneAssociationsNetworkPanel.tsx +++ b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/molecules/genes/GeneAssociationsNetworkPanel.tsx @@ -358,7 +358,7 @@ export const GeneAssociationsNetworkPanel = ({ selectedGene }: GeneAssociationsN - The combined score is computed by combining the probabilities from the different evidence channels and corrected for the probability of randomly observing an interaction. For a more detailed description please see{' '} + {intl.formatMessage({ id: 'geneAssociations.infoPopup.text' })}{' '} von Mering, et al. Nucleic Acids Res. 2005 diff --git a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/stat-validations/StatisticalValidationMenu.tsx b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/stat-validations/StatisticalValidationMenu.tsx index 2d94acf6..27140452 100644 --- a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/stat-validations/StatisticalValidationMenu.tsx +++ b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/stat-validations/StatisticalValidationMenu.tsx @@ -30,7 +30,7 @@ export const StatisticalValidationMenu = (props: StatisticalValidationMenuProps) {intl.formatMessage({ id: 'statValidationMenu.bestFeatures' })} { headers={[ { name: intl.formatMessage({ id: 'common.sample' }), serverCodeToSort: 'sample', width: 3 }, - { name: intl.formatMessage({ id: 'common.cluster' }), serverCodeToSort: 'cluster', width: 2 } + { + name: intl.formatMessage({ id: 'common.cluster' }), + serverCodeToSort: 'cluster', + width: 2, + infoPopupContent: intl.formatMessage({ id: 'metricInfo.clusterId' }) + } ]} queryParams={{ statistical_validation_pk: props.selectedStatisticalValidation.id }} customFilters={[ diff --git a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/stat-validations/result/StatisticalValidationResultBestFeatures.tsx b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/stat-validations/result/StatisticalValidationResultBestFeatures.tsx index 885243bf..5d234b38 100644 --- a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/stat-validations/result/StatisticalValidationResultBestFeatures.tsx +++ b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/stat-validations/result/StatisticalValidationResultBestFeatures.tsx @@ -6,6 +6,7 @@ import { alertGeneralError } from '../../../../../utils/util_functions' import ReactApexChart from 'react-apexcharts' import { ResultPlaceholder } from '../result/ResultPlaceholder' import { useIntl } from 'react-intl' +import { InfoPopup } from '../../../../pipeline/experiment-result/gene-gem-details/InfoPopup' /** Epsilon to add to the min/max value of coefficients. */ const EPSILON = 1 @@ -146,8 +147,15 @@ export const StatisticalValidationResultBestFeatures = (props: StatisticalValida {loading && } - {(!loading && statValidationData !== null) && - } + {(!loading && statValidationData !== null) && ( + <> +
+ {intl.formatMessage({ id: 'common.coefficient' })} + +
+ + + )} ) } diff --git a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/stat-validations/result/StatisticalValidationResultHeatMap.tsx b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/stat-validations/result/StatisticalValidationResultHeatMap.tsx index 8fcd1809..af4d4def 100644 --- a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/stat-validations/result/StatisticalValidationResultHeatMap.tsx +++ b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/stat-validations/result/StatisticalValidationResultHeatMap.tsx @@ -5,6 +5,8 @@ import ky from 'ky' import { alertGeneralError } from '../../../../../utils/util_functions' import { ResultPlaceholder } from './ResultPlaceholder' import { Heatmap } from '../heatmap/Heatmap' +import { InfoPopup } from '../../../../pipeline/experiment-result/gene-gem-details/InfoPopup' +import { useIntl } from 'react-intl' declare const urlStatisticalValidationHeatMap: string @@ -20,6 +22,7 @@ interface StatisticalValidationResultHeatMapProps { * @returns Component */ export const StatisticalValidationResultHeatMap = (props: StatisticalValidationResultHeatMapProps) => { + const intl = useIntl() const abortController = useRef(new AbortController()) const [loading, setLoading] = useState(false) const [heatMapData, setHeatMapData] = useState>(null) @@ -77,8 +80,15 @@ export const StatisticalValidationResultHeatMap = (props: StatisticalValidationR {loading && } - {(!loading && heatMapData !== null) && - } + {(!loading && heatMapData !== null) && ( + <> +
+ {intl.formatMessage({ id: 'metricInfo.expression.label' })} + +
+ + + )} ) } diff --git a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/stat-validations/result/StatisticalValidationResultKaplanMeier.tsx b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/stat-validations/result/StatisticalValidationResultKaplanMeier.tsx index d3c6ed4b..c751e5b4 100644 --- a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/stat-validations/result/StatisticalValidationResultKaplanMeier.tsx +++ b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/stat-validations/result/StatisticalValidationResultKaplanMeier.tsx @@ -244,14 +244,19 @@ export const StatisticalValidationResultKaplanMeier = (props: StatisticalValidat )} - {/* TODO: add InfoPopups for every metric and their interpretation. */} {kaplanMeierData ? kaplanMeierData.concordance_index.toFixed(3) : '-'} - {intl.formatMessage({ id: 'kaplanMeier.metric.cIndex' })} + + {intl.formatMessage({ id: 'kaplanMeier.metric.cIndex' })} + + {kaplanMeierData ? kaplanMeierData.log_likelihood.toFixed(3) : '-'} - {intl.formatMessage({ id: 'kaplanMeier.metric.partialLogLikelihood' })} + + {intl.formatMessage({ id: 'kaplanMeier.metric.partialLogLikelihood' })} + + {/* Samples and clusters modal. */} diff --git a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/stat-validations/result/StatisticalValidationResultMetrics.tsx b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/stat-validations/result/StatisticalValidationResultMetrics.tsx index 9cb24bd1..4aa18de3 100644 --- a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/stat-validations/result/StatisticalValidationResultMetrics.tsx +++ b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/stat-validations/result/StatisticalValidationResultMetrics.tsx @@ -6,6 +6,7 @@ import { alertGeneralError } from '../../../../../utils/util_functions' import { Header, Placeholder, Segment, Statistic } from 'semantic-ui-react' import { ModelDetailsPanel } from '../../ModelDetailsPanels' import { useIntl } from 'react-intl' +import { InfoPopup } from '../../../../pipeline/experiment-result/gene-gem-details/InfoPopup' declare const urlStatisticalValidationMetrics: string @@ -106,16 +107,25 @@ export const StatisticalValidationResultMetrics = (props: StatisticalValidationR
{intl.formatMessage({ id: 'statValidationMetrics.header.validationMetrics' })}
- {statValidationData.mean_squared_error ? statValidationData.mean_squared_error.toFixed(3) : '-'} - {intl.formatMessage({ id: 'statValidationMetrics.metric.mse' })} + {statValidationData.mean_squared_error != null ? statValidationData.mean_squared_error.toFixed(3) : '-'} + + {intl.formatMessage({ id: 'statValidationMetrics.metric.mse' })} + + - {statValidationData.c_index ? statValidationData.c_index.toFixed(3) : '-'} - {intl.formatMessage({ id: 'statValidationMetrics.metric.cIndex' })} + {statValidationData.c_index != null ? statValidationData.c_index.toFixed(3) : '-'} + + {intl.formatMessage({ id: 'kaplanMeier.metric.cIndex' })} + + - {statValidationData.r2_score ? statValidationData.r2_score.toFixed(3) : '-'} - {intl.formatMessage({ id: 'statValidationMetrics.metric.r2Score' })} + {statValidationData.r2_score != null ? statValidationData.r2_score.toFixed(3) : '-'} + + {intl.formatMessage({ id: 'statValidationMetrics.metric.r2Score' })} + + )} diff --git a/src/frontend/static/frontend/src/components/biomarkers/types.ts b/src/frontend/static/frontend/src/components/biomarkers/types.ts index 98d2b66f..dad81a22 100644 --- a/src/frontend/static/frontend/src/components/biomarkers/types.ts +++ b/src/frontend/static/frontend/src/components/biomarkers/types.ts @@ -534,7 +534,7 @@ interface KaplanMeierResultData { /** Data which is present in all the TrainedModels. */ interface GeneralModelDetails { - best_fitness: number, + best_fitness: Nullable, model: FitnessFunction random_state: Nullable } diff --git a/src/frontend/static/frontend/src/components/pipeline/experiment-result/gene-gem-details/MiRNAExtraData.tsx b/src/frontend/static/frontend/src/components/pipeline/experiment-result/gene-gem-details/MiRNAExtraData.tsx index 344d1df0..4c10887a 100644 --- a/src/frontend/static/frontend/src/components/pipeline/experiment-result/gene-gem-details/MiRNAExtraData.tsx +++ b/src/frontend/static/frontend/src/components/pipeline/experiment-result/gene-gem-details/MiRNAExtraData.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef, useState } from 'react' +import React, { useEffect, useState } from 'react' import ky from 'ky' import { Button, Grid, Header, Icon, Placeholder, PlaceholderLine, PlaceholderParagraph } from 'semantic-ui-react' import { DjangoMiRNADataJSON } from '../../../../utils/django_interfaces' @@ -30,42 +30,32 @@ export const MiRNAExtraData = (props: MiRNAExtraDataProps) => { const [miRNAData, setMiRNAData] = useState>(null) const [loading, setLoading] = useState(true) - const abortController = useRef(new AbortController()) const showNoDataHeader = props.showNoDataHeader ?? true - /** - * Function to get MiRNA data - */ - const getMiRNAData = () => { + /** Gets miRNA data whenever the selected molecule changes. */ + useEffect(() => { + const abortController = new AbortController() setLoading(true) + setMiRNAData(null) const searchParams: KySearchParams = { mirna: props.miRNA } - ky.get(urlMiRNAData, { signal: abortController.current.signal, searchParams }).then((response) => { - response.json().then((jsonResponse) => { - setMiRNAData(jsonResponse) - }).catch((err) => { - console.log('Error parsing JSON ->', err) - }) + ky.get(urlMiRNAData, { signal: abortController.signal, searchParams }).json().then((jsonResponse) => { + setMiRNAData(jsonResponse) }).catch((err) => { - console.log('Error getting studies ->', err) + if (!abortController.signal.aborted) { + console.log('Error getting miRNA data ->', err) + } }).finally(() => { - setLoading(false) + if (!abortController.signal.aborted) { + setLoading(false) + } }) - } - - /** - * effect to get MirNA data when component is mount - */ - useEffect(() => { - getMiRNAData() - return () => { - abortController.current.abort() - } - }, []) + return () => abortController.abort() + }, [props.miRNA]) if (!miRNAData) { if (!showNoDataHeader) { @@ -100,15 +90,13 @@ export const MiRNAExtraData = (props: MiRNAExtraDataProps) => { } const mirbaseURL = miRNAData.links.find((link) => link.source === 'mirbase')?.url - const othersLinks = miRNAData.links.filter((link) => link.source !== 'mirbase') - // Sorts descendant to put MIMAT format first const miRNAAliases = miRNAData.aliases.sort((a, b) => b.localeCompare(a)).join(' / ') return ( - +
@@ -122,19 +110,19 @@ export const MiRNAExtraData = (props: MiRNAExtraDataProps) => {
)} - {(othersLinks.length > 0) && ( + {(miRNAData.links.length > 0) && ( - {othersLinks.map((link) => ( + {miRNAData.links.map((link) => ( ))} diff --git a/src/frontend/static/frontend/src/components/pipeline/experiment-result/gene-gem-details/MiRNATargetInteractionPanel.tsx b/src/frontend/static/frontend/src/components/pipeline/experiment-result/gene-gem-details/MiRNATargetInteractionPanel.tsx index 262e28a4..ea02ae64 100644 --- a/src/frontend/static/frontend/src/components/pipeline/experiment-result/gene-gem-details/MiRNATargetInteractionPanel.tsx +++ b/src/frontend/static/frontend/src/components/pipeline/experiment-result/gene-gem-details/MiRNATargetInteractionPanel.tsx @@ -10,6 +10,7 @@ import { ListOfElementsWithHeader } from './ListOfElementsWithHeader' import { PubmedButton } from './PubmedButton' import { TryAgainSegment } from '../../../common/TryAgainSegment' import { useIntl, IntlShape } from 'react-intl' +import { InfoPopup } from './InfoPopup' declare const urlMiRNAInteraction: string @@ -151,6 +152,13 @@ class MiRNATargetInteractionPanel extends React.Component diff --git a/src/frontend/static/frontend/src/locales/en.ts b/src/frontend/static/frontend/src/locales/en.ts index 44325c81..e5d4c72c 100644 --- a/src/frontend/static/frontend/src/locales/en.ts +++ b/src/frontend/static/frontend/src/locales/en.ts @@ -118,18 +118,20 @@ export default { 'inference.charts.noAttribute': 'No clinical attribute selected', 'inference.charts.selectAttribute': 'Please, select one in the right Select.', // 9.SamplesAndTimeInferenceTable.tsx - 'inference.timeTable.columns.predictedTime': 'Predicted time', + 'inference.timeTable.columns.predictedTime': 'Prediction', 'inference.timeTable.filter.range.label': 'Range', 'inference.timeTable.filter.range.placeholder': 'Filter by range', 'inference.timeTable.menu.table': 'Table', 'inference.timeTable.menu.charts': 'Charts', - 'inference.timeTable.menu.table.info': 'Table with all the samples and their predicted hazard/survival time', + 'inference.timeTable.menu.table.info': 'Table with each sample and its model prediction', 'inference.timeTable.menu.charts.info': 'Shows some charts with the samples and their predicted hazard/survival time grouped by some condition', 'inference.timeTable.addRangeLabels': 'Add Range labels', // 10.molecules/GeneOntologyCytoscapeChart.tsx 'geneOntology.form.ontologyType': 'Ontology type', 'geneOntology.form.generalDepth': 'General depth', + 'geneOntology.form.generalDepth.info': 'Maximum graph depth around the term. Larger values show more distant related terms; smaller values keep the view focused. Neither is inherently better.', 'geneOntology.form.hierarchicalDepth': 'Hierarchical depth to children', + 'geneOntology.form.hierarchicalDepth.info': 'How many descendant levels to show. Larger values include more child terms but make the graph denser; neither is inherently better.', 'geneOntology.form.toRoot': 'To root', 'geneOntology.relation.hasPart': 'Has part', 'geneOntology.relation.isA': 'Is a', @@ -141,6 +143,7 @@ export default { 'geneOntology.panel.filterType.union': 'Union', 'geneOntology.panel.filterType.enrichment': 'Enrichment', 'geneOntology.panel.pValueThreshold': 'P-value threshold', + 'geneOntology.panel.pValueThreshold.info': 'Maximum p-value accepted for enrichment. A smaller threshold is stricter and returns fewer terms; a larger one is more permissive.', 'geneOntology.panel.correctionMethod': 'Correction method', 'geneOntology.panel.correctionMethod.analytical': 'Analytical', 'geneOntology.panel.correctionMethod.bonferroni': 'Bonferroni', @@ -166,7 +169,7 @@ export default { 'geneAssociations.relation.database': 'Database', 'geneAssociations.relation.coExpression': 'Co-expression', 'geneAssociations.form.minCombinedScore': 'Min combined score', - 'geneAssociations.infoPopup.text': 'The combined score is computed by combining the probabilities from the different evidence channels and corrected for the probability of randomly observing an interaction. For a more detailed description please see von Mering, et al. Nucleic Acids Res. 2005', + 'geneAssociations.infoPopup.text': 'Higher STRING combined scores mean greater confidence that an association exists, not a stronger biological effect. A higher minimum shows fewer, higher-confidence links. See', // 14.GeneInformation.tsx 'geneInformation.context.gene': 'for this gene', 'geneInformation.summary': 'Summary', @@ -218,7 +221,7 @@ export default { 'samplesAndGroupsTable.filter.cluster.placeholder': 'Filter by cluster', // 22.StatisticalValidationResultBestFeatures.tsx 'statValidationBestFeatures.axis.molecule': 'Molecule', - 'statValidationBestFeatures.context.features': 'significant features found for this statistical validation', + 'statValidationBestFeatures.context.features': 'Cox coefficients found for this statistical validation', // 23.StatisticalValidationResultKaplanMeier.tsx 'kaplanMeier.axis.time': 'Time', 'kaplanMeier.axis.probability': 'Probability', @@ -241,12 +244,12 @@ export default { 'newStatValidation.step.trainedModel': 'Step 1: Trained model', 'newStatValidation.step.validationDatasets': 'Step 2: Validation datasets', // 26.StatisticalValidationMenu.tsx - 'statValidationMenu.bestFeatures': 'Most significant features', - 'statValidationMenu.bestFeatures.info': 'Most significant features for the survival analysis', + 'statValidationMenu.bestFeatures': 'Largest absolute Cox coefficients', + 'statValidationMenu.bestFeatures.info': 'Molecules with the largest absolute Cox coefficients. Magnitude indicates association strength per unit, not statistical significance.', 'statValidationMenu.kaplanMeier': 'Kaplan-Meier', - 'statValidationMenu.kaplanMeier.info': 'curve showing survival or hazard ratio', + 'statValidationMenu.kaplanMeier.info': 'Estimated survival probability over time by group. A higher curve means greater estimated survival at the same time.', 'statValidationMenu.heatmap': 'Heatmap', - 'statValidationMenu.heatmap.info': 'Heatmap for every sample and molecule', + 'statValidationMenu.heatmap.info': 'Molecule measurements by sample. Colors show lower and higher values on the displayed scale; their desirability depends on context.', // 27.StatisticalValidationsTable.tsx 'statValidationsTable.stopValidation.header': 'Stop statistical validation', @@ -269,7 +272,7 @@ export default { 'newClusteringForm.placeholder.scoringMethod': 'Select a method', 'newClusteringForm.info.algorithm': 'K-Means: Groups data by minimizing intra-cluster variance; effective for RNA and miRNA. Spectral Clustering: Uses graph-based similarity to identify complex patterns; ideal for methylation and CNA. BK-Means: A hierarchical variation of K-Means, suitable for layered clustering of clinical and multi-omics datasets. Ward’s Method: Minimizes variance in hierarchical clustering; well-suited for combining RNA and methylation data.', 'newClusteringForm.info.metric': 'Cox Regression: A proportional hazards model to identify associations between multi-omics features and clinical outcomes. Log-Rank Test: A non-parametric test to compare survival distributions; currently not available.', - 'newClusteringForm.info.scoringMethod': 'C-Index: A measure of concordance between predicted and observed survival outcomes; higher values indicate better performance. Log Likelihood: The probability of observing the data given the model; lower values indicate better performance.', + 'newClusteringForm.info.scoringMethod': 'C-Index ranks survival outcomes; higher is better (0.5 is near chance). Cox partial log-likelihood measures fit; higher (less negative) is better for the same data.', 'newClusteringForm.info.randomState': 'The seed used by the random number generator to ensure reproducibility.', 'newClusteringForm.info.penalizer': 'Useful when clinical data has few samples or events; increases robustness and avoids NaN values.', // 29.NewRFModelForm.tsx @@ -307,7 +310,7 @@ export default { 'biomarkerTrainedModelsTable.confirm.deleteBiomarker': 'Are you sure you want to delete the Biomarker {modelName}?', 'biomarkerTrainedModelsTable.header.trainedModels': 'Trained models', 'biomarkerTrainedModelsTable.header.model': 'Model', - 'biomarkerTrainedModelsTable.header.bestCVMetric': 'Best CV metric', + 'biomarkerTrainedModelsTable.header.bestCVMetric': 'Best model score', 'biomarkerTrainedModelsTable.header.datasets': 'Datasets', 'biomarkerTrainedModelsTable.header.actions': 'Actions', 'biomarkerTrainedModelsTable.filter.modelType': 'Model type', @@ -321,6 +324,29 @@ export default { 'modelDetails.svm.task': 'Task:', 'modelDetails.rf.numberOfEstimators': 'Number of estimators:', 'modelDetails.rf.maxDepth': 'Max. depth:', + 'metricInfo.mse': 'Mean squared difference between model output and observed survival time. Lower is better (0 is exact), but a risk score is not a time prediction; compare only like for like.', + 'metricInfo.cIndex': 'How well predicted risk ranks comparable survival outcomes. About 0.5 is chance; 1 is perfect. Higher is better.', + 'metricInfo.r2': 'Fit of model output to observed survival time: 1 is exact, 0 matches predicting the mean, and negative is worse. Interpret cautiously when output is a risk score.', + 'metricInfo.partialLogLikelihood': 'Average Cox partial log-likelihood. Higher (less negative) means better fit on the same data; scores from different datasets are not directly comparable.', + 'metricInfo.coxCoefficient': 'Cox log-hazard coefficient: positive means higher molecule values are associated with higher risk; negative means lower risk. Larger absolute values mean stronger per-unit association, not necessarily better outcomes.', + 'metricInfo.expression.label': 'Molecule values', + 'metricInfo.expression': 'Each cell is a molecule measurement in one sample. Higher or lower is not universally better; compare values for the same molecule and measurement scale.', + 'metricInfo.clusterId': 'Arbitrary group identifier. A larger number does not mean better, worse, or higher risk; compare the groups\' survival curves.', + 'metricInfo.inferencePrediction': 'Random Forest and ranking SVM return risk scores: higher means greater predicted event risk. Regression SVM returns transformed survival-time scores: higher suggests longer survival. Compare only within the same model.', + 'trainedModels.info.fitnessMetric': 'The score used to select the model: C-Index ranks survival risk; Cox partial log-likelihood measures fit. Both favor higher values.', + 'trainedModels.info.bestScore': 'Score recorded for the selected model during training or validation. Higher is better for these metrics; compare models on the same data and metric.', + 'modelDetails.info.model': 'Algorithm used to generate predictions; the label itself has no better or worse direction.', + 'modelDetails.info.bestCIndex': 'C-Index recorded for the selected model during training or validation. Higher is better; about 0.5 is chance and 1 is perfect.', + 'modelDetails.info.bestClusteringCIndex': 'Cox C-Index recorded for the selected clustering model during training or validation. Higher is better; about 0.5 is chance.', + 'modelDetails.info.bestLogLikelihood': 'Cox partial log-likelihood recorded for the selected clustering model during training or validation. Higher (less negative) is better on the same data.', + 'modelDetails.info.randomState': 'Random seed used to reproduce a run. A larger or smaller number does not mean better performance.', + 'modelDetails.info.algorithm': 'Clustering method used to form groups. No method is always better; compare validation results.', + 'modelDetails.info.scoringMethod': 'Score used to select cluster settings: higher C-Index or higher Cox partial log-likelihood is better on the same data.', + 'modelDetails.info.nClusters': 'Number of groups formed. More groups give finer divisions, but do not necessarily improve survival separation.', + 'modelDetails.info.kernel': 'SVM similarity function. Its name has no score direction; compare validation performance.', + 'modelDetails.info.task': 'Ranking predicts relative risk; regression predicts transformed survival time. The task label has no better or worse direction.', + 'modelDetails.info.nEstimators': 'Number of trees in the forest. More trees can stabilize predictions but cost more computation; more is not always better.', + 'modelDetails.info.maxDepth': 'Maximum tree depth. Greater depth allows more complex patterns but can overfit; no universal best value.', // 34.BiomarkerStateLabel 'biomarkerState.completed': 'The experiment is complete', 'biomarkerState.finishedWithError': 'The experiment has finished with errors. Try again', @@ -1112,7 +1138,7 @@ export default { 'miRNAInteractionPanel.gene': 'Gene', 'miRNAInteractionPanel.sources': 'Sources', 'miRNAInteractionPanel.mirDIPScore': 'mirDIP score', - 'miRNAInteractionPanel.mirDIPScoreInfo': 'mirDIP scores of human microRNA target predictions were ranked according to four confidence classes (Very high, High, Medium and Low)', + 'miRNAInteractionPanel.mirDIPScoreInfo': 'Higher mirDIP scores mean greater confidence in a predicted miRNA–gene interaction; lower scores mean weaker evidence. A prediction is not experimental proof.', 'miRNAInteractionPanel.mirDIPScoreClass': 'mirDIP score class', 'miRNAInteractionPanel.pubmed': 'Pubmed', diff --git a/src/frontend/static/frontend/src/locales/es.ts b/src/frontend/static/frontend/src/locales/es.ts index 49e072dd..cbf0a9a5 100644 --- a/src/frontend/static/frontend/src/locales/es.ts +++ b/src/frontend/static/frontend/src/locales/es.ts @@ -118,18 +118,20 @@ export default { 'inference.charts.noAttribute': 'No se seleccionó ningún atributo clínico', 'inference.charts.selectAttribute': 'Por favor, selecciona uno en el selector de la derecha.', // 9.SamplesAndTimeInferenceTable.tsx - 'inference.timeTable.columns.predictedTime': 'Tiempo predicho', + 'inference.timeTable.columns.predictedTime': 'Predicción', 'inference.timeTable.filter.range.label': 'Rango', 'inference.timeTable.filter.range.placeholder': 'Filtrar por rango', 'inference.timeTable.menu.table': 'Tabla', 'inference.timeTable.menu.charts': 'Gráficos', - 'inference.timeTable.menu.table.info': 'Tabla con todas las muestras y su tiempo de riesgo/supervivencia predicho', + 'inference.timeTable.menu.table.info': 'Tabla con cada muestra y la predicción del modelo', 'inference.timeTable.menu.charts.info': 'Muestra gráficos con las muestras y su tiempo de riesgo/supervivencia predicho agrupado por alguna condición', 'inference.timeTable.addRangeLabels': 'Agregar etiquetas de rango', // 10.molecules/GeneOntologyCytoscapeChart.tsx 'geneOntology.form.ontologyType': 'Tipo de ontología', 'geneOntology.form.generalDepth': 'Profundidad general', + 'geneOntology.form.generalDepth.info': 'Profundidad máxima del grafo alrededor del término. Un valor mayor muestra términos más lejanos; uno menor mantiene la vista acotada. Ninguno es mejor por sí mismo.', 'geneOntology.form.hierarchicalDepth': 'Profundidad jerárquica hacia hijos', + 'geneOntology.form.hierarchicalDepth.info': 'Cantidad de niveles descendientes que se muestran. Un valor mayor incluye más términos hijos, pero hace el grafo más denso; ninguno es mejor por sí mismo.', 'geneOntology.form.toRoot': 'Hasta la raíz', 'geneOntology.relation.hasPart': 'Tiene parte', 'geneOntology.relation.isA': 'Es un', @@ -141,6 +143,7 @@ export default { 'geneOntology.panel.filterType.union': 'Unión', 'geneOntology.panel.filterType.enrichment': 'Enriquecimiento', 'geneOntology.panel.pValueThreshold': 'Umbral de valor p', + 'geneOntology.panel.pValueThreshold.info': 'Valor p máximo aceptado para enriquecimiento. Un umbral menor es más estricto y devuelve menos términos; uno mayor es más permisivo.', 'geneOntology.panel.correctionMethod': 'Método de corrección', 'geneOntology.panel.correctionMethod.analytical': 'Analítico', 'geneOntology.panel.correctionMethod.bonferroni': 'Bonferroni', @@ -166,7 +169,7 @@ export default { 'geneAssociations.relation.database': 'Base de datos', 'geneAssociations.relation.coExpression': 'Co-expresión', 'geneAssociations.form.minCombinedScore': 'Puntuación combinada mínima', - 'geneAssociations.infoPopup.text': 'La puntuación combinada se calcula combinando las probabilidades de los diferentes canales de evidencia y se corrige por la probabilidad de observar una interacción al azar. Para una descripción más detallada consulte von Mering, et al. Nucleic Acids Res. 2005', + 'geneAssociations.infoPopup.text': 'Una puntuación combinada STRING mayor indica más confianza en la asociación, no un efecto biológico más fuerte. Un umbral mínimo mayor muestra menos asociaciones, pero de mayor confianza. Ver', // 14.GeneInformation 'geneInformation.context.gene': 'para este gen', 'geneInformation.summary': 'Resumen', @@ -221,7 +224,7 @@ export default { 'samplesAndGroupsTable.search.label': 'Muestra', // 22.StatisticalValidationResultBestFeatures.tsx 'statValidationBestFeatures.axis.molecule': 'Molécula', - 'statValidationBestFeatures.context.features': 'características significativas para esta validación estadística', + 'statValidationBestFeatures.context.features': 'coeficientes de Cox para esta validación estadística', // 23.StatisticalValidationResultKaplanMeier.tsx 'kaplanMeier.axis.time': 'Tiempo', 'kaplanMeier.axis.probability': 'Probabilidad', @@ -245,11 +248,11 @@ export default { 'newStatValidation.step.trainedModel': 'Paso 1: Modelo entrenado', 'newStatValidation.step.validationDatasets': 'Paso 2: Conjuntos de validación', // 26.StatisticalValidationMenu.tsx - 'statValidationMenu.bestFeatures': 'Características más significativas', - 'statValidationMenu.bestFeatures.info': 'Características más significativas para el análisis de supervivencia', - 'statValidationMenu.kaplanMeier.info': 'Curva mostrando supervivencia o hazard ratio', + 'statValidationMenu.bestFeatures': 'Coeficientes de Cox de mayor valor absoluto', + 'statValidationMenu.bestFeatures.info': 'Moléculas con los coeficientes de Cox de mayor valor absoluto. La magnitud indica asociación por unidad, no significancia estadística.', + 'statValidationMenu.kaplanMeier.info': 'Probabilidad estimada de supervivencia por grupo a lo largo del tiempo. Una curva más alta indica mayor supervivencia estimada al mismo tiempo.', 'statValidationMenu.heatmap': 'Mapa de calor', - 'statValidationMenu.heatmap.info': 'Mapa de calor para cada muestra y molécula', + 'statValidationMenu.heatmap.info': 'Mediciones moleculares por muestra. Los colores indican valores menores o mayores en la escala mostrada; si son favorables depende del contexto.', // 27.StatisticalValidationsTable.tsx 'statValidationsTable.stopValidation.header': 'Detener validación estadística', 'statValidationsTable.stopValidation.confirm': '¿Está seguro de que desea detener la validación estadística {name}?', @@ -271,7 +274,7 @@ export default { 'newClusteringForm.placeholder.scoringMethod': 'Seleccione un método', 'newClusteringForm.info.algorithm': 'K-Means: agrupa datos minimizando la varianza intra-cluster; útil para RNA y miRNA. Spectral Clustering: usa similitud basada en grafos para patrones complejos; ideal para metilación y CNA. BK-Means: variación jerárquica de K-Means, adecuada para datasets clínicos y multi-ómicos. Método de Ward: minimiza la varianza en clustering jerárquico; útil para combinar RNA y metilación.', 'newClusteringForm.info.metric': 'Cox Regression: modelo de riesgos proporcionales para asociaciones entre multi-ómicos y resultados clínicos. Log-Rank Test: prueba no paramétrica para comparar distribuciones de supervivencia; actualmente no disponible.', - 'newClusteringForm.info.scoringMethod': 'C-Index: medida de concordancia entre resultados predichos y observados; valores altos indican mejor desempeño. Log Likelihood: probabilidad de observar los datos dado el modelo; valores bajos indican mejor desempeño.', + 'newClusteringForm.info.scoringMethod': 'El índice C ordena los resultados de supervivencia; más alto es mejor (0,5 se acerca al azar). La log-verosimilitud parcial de Cox mide el ajuste; más alta (menos negativa) es mejor sobre los mismos datos.', 'newClusteringForm.info.randomState': 'Semilla usada por el generador aleatorio para asegurar reproducibilidad.', 'newClusteringForm.info.penalizer': 'Útil cuando hay pocas muestras o eventos observados; aumenta la robustez del modelo evitando problemas con valores NaN.', // 29.NewRFModelForm.tsx @@ -309,7 +312,7 @@ export default { 'biomarkerTrainedModelsTable.confirm.deleteBiomarker': '¿Está seguro de que desea eliminar el biomarcador {modelName}?', 'biomarkerTrainedModelsTable.header.trainedModels': 'Modelos entrenados', 'biomarkerTrainedModelsTable.header.model': 'Modelo', - 'biomarkerTrainedModelsTable.header.bestCVMetric': 'Mejor métrica de validación cruzada', + 'biomarkerTrainedModelsTable.header.bestCVMetric': 'Mejor puntaje del modelo', 'biomarkerTrainedModelsTable.header.datasets': 'Conjuntos de datos', 'biomarkerTrainedModelsTable.header.actions': 'Acciones', 'biomarkerTrainedModelsTable.filter.modelType': 'Tipo de modelo', @@ -323,6 +326,29 @@ export default { 'modelDetails.svm.task': 'Tarea:', 'modelDetails.rf.numberOfEstimators': 'Número de estimadores:', 'modelDetails.rf.maxDepth': 'Profundidad máxima:', + 'metricInfo.mse': 'Diferencia cuadrática media entre la salida del modelo y el tiempo de supervivencia observado. Menor es mejor (0 es exacto), pero un puntaje de riesgo no predice tiempo; compará solo casos equivalentes.', + 'metricInfo.cIndex': 'Mide si el riesgo predicho ordena correctamente resultados de supervivencia comparables. Cerca de 0,5 equivale al azar; 1 es perfecto. Mayor es mejor.', + 'metricInfo.r2': 'Ajuste de la salida del modelo al tiempo observado: 1 es exacto, 0 equivale a predecir la media y un valor negativo es peor. Interpretar con cautela si la salida es un puntaje de riesgo.', + 'metricInfo.partialLogLikelihood': 'Log-verosimilitud parcial promedio de Cox. Mayor (menos negativa) indica mejor ajuste sobre los mismos datos; no se comparan directamente conjuntos distintos.', + 'metricInfo.coxCoefficient': 'Coeficiente log-riesgo de Cox: positivo indica asociación entre un valor molecular mayor y mayor riesgo; negativo, menor riesgo. Un valor absoluto mayor indica asociación más fuerte por unidad, no necesariamente mejor pronóstico.', + 'metricInfo.expression.label': 'Valores moleculares', + 'metricInfo.expression': 'Cada celda es una medición molecular en una muestra. Mayor o menor no es universalmente mejor; compará valores de la misma molécula y escala.', + 'metricInfo.clusterId': 'Identificador arbitrario de grupo. Un número mayor no implica mejor, peor ni mayor riesgo; compará las curvas de supervivencia de los grupos.', + 'metricInfo.inferencePrediction': 'Random Forest y SVM de ranking devuelven puntajes de riesgo: más alto indica mayor riesgo predicho. SVM de regresión devuelve puntajes de tiempo de supervivencia transformado: más alto sugiere mayor supervivencia. Compará solo dentro del mismo modelo.', + 'trainedModels.info.fitnessMetric': 'Puntaje usado para elegir el modelo: el índice C ordena el riesgo de supervivencia y la log-verosimilitud parcial de Cox mide el ajuste. En ambos, mayor es mejor.', + 'trainedModels.info.bestScore': 'Puntaje registrado para el modelo elegido durante entrenamiento o validación. Mayor es mejor para estas métricas; compará modelos con los mismos datos y métrica.', + 'modelDetails.info.model': 'Algoritmo usado para generar predicciones. Su nombre no tiene una dirección mejor o peor.', + 'modelDetails.info.bestCIndex': 'Índice C registrado para el modelo elegido durante entrenamiento o validación. Mayor es mejor; cerca de 0,5 equivale al azar y 1 es perfecto.', + 'modelDetails.info.bestClusteringCIndex': 'Índice C de Cox registrado para el clustering elegido durante entrenamiento o validación. Mayor es mejor; cerca de 0,5 equivale al azar.', + 'modelDetails.info.bestLogLikelihood': 'Log-verosimilitud parcial de Cox registrada para el clustering elegido durante entrenamiento o validación. Mayor (menos negativa) es mejor sobre los mismos datos.', + 'modelDetails.info.randomState': 'Semilla aleatoria para reproducir una ejecución. Un número mayor o menor no implica mejor desempeño.', + 'modelDetails.info.algorithm': 'Método usado para formar grupos. Ninguno es siempre mejor; compará los resultados de validación.', + 'modelDetails.info.scoringMethod': 'Puntaje usado para elegir parámetros de clustering: un índice C mayor o una log-verosimilitud parcial de Cox mayor es mejor sobre los mismos datos.', + 'modelDetails.info.nClusters': 'Cantidad de grupos formados. Más grupos dan divisiones más finas, pero no garantizan mejor separación de supervivencia.', + 'modelDetails.info.kernel': 'Función de similitud de SVM. Su nombre no tiene una dirección mejor o peor; compará el desempeño validado.', + 'modelDetails.info.task': 'Ranking predice riesgo relativo; regresión predice tiempo de supervivencia transformado. El tipo de tarea no tiene una dirección mejor o peor.', + 'modelDetails.info.nEstimators': 'Cantidad de árboles del bosque. Más árboles pueden estabilizar predicciones, pero cuestan más cómputo; más no siempre es mejor.', + 'modelDetails.info.maxDepth': 'Profundidad máxima de los árboles. Mayor profundidad permite patrones más complejos, pero puede sobreajustar; no hay un valor universalmente mejor.', // 34.BiomarkerStateLabel 'biomarkerState.completed': 'El experimento está completo', 'biomarkerState.finishedWithError': 'El experimento terminó con errores. Intenta nuevamente', @@ -1164,7 +1190,7 @@ export default { 'miRNAInteractionPanel.gene': 'Gen', 'miRNAInteractionPanel.sources': 'Fuentes', 'miRNAInteractionPanel.mirDIPScore': 'Puntuación mirDIP', - 'miRNAInteractionPanel.mirDIPScoreInfo': 'Las puntuaciones mirDIP de las predicciones de objetivos de microARN humanos se clasifican en cuatro niveles de confianza (Muy alta, Alta, Media y Baja)', + 'miRNAInteractionPanel.mirDIPScoreInfo': 'Una puntuación mirDIP mayor indica más confianza en la interacción miRNA-gen predicha; una menor, evidencia más débil. La predicción no prueba la interacción experimentalmente.', 'miRNAInteractionPanel.mirDIPScoreClass': 'Clase de puntuación mirDIP', 'miRNAInteractionPanel.pubmed': 'PubMed', From 7347116ff3265948b71fd8761890cce1c5fa98a0 Mon Sep 17 00:00:00 2001 From: Juan Nicolas Herrera <54152074+juanNH@users.noreply.github.com> Date: Fri, 25 Sep 2026 14:23:42 -0300 Subject: [PATCH 2/3] Fix Biomarker translations --- .../inference/InferenceExperimentsTable.tsx | 2 +- .../stat-validations/StatisticalValidationsTable.tsx | 2 +- .../biomarker-details-modal/trained-models/NewRFModelForm.tsx | 4 ++-- .../trained-models/NewSVMModelForm.tsx | 2 +- .../trained-models/NewTrainedModelModal.tsx | 2 +- src/frontend/static/frontend/src/locales/en.ts | 1 + src/frontend/static/frontend/src/locales/es.ts | 4 +++- 7 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/inference/InferenceExperimentsTable.tsx b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/inference/InferenceExperimentsTable.tsx index 1693f136..e2867c4b 100644 --- a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/inference/InferenceExperimentsTable.tsx +++ b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/inference/InferenceExperimentsTable.tsx @@ -167,7 +167,7 @@ export const InferenceExperimentsTable = (props: InferenceExperimentsTableProps) {/* Modal to confirm deleting the InferenceExperiment */} {getDeletionConfirmModal()} - headerTitle={intl.formatMessage({ id: 'inference.experiments.table.title' })} + headerTitle={intl.formatMessage({ id: 'inference.table.title' })} headers={[ { name: intl.formatMessage({ id: 'common.name' }), serverCodeToSort: 'name', width: 3 }, { name: intl.formatMessage({ id: 'common.description' }), serverCodeToSort: 'description', width: 4 }, diff --git a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/stat-validations/StatisticalValidationsTable.tsx b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/stat-validations/StatisticalValidationsTable.tsx index b1a03e1b..b2d5c714 100644 --- a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/stat-validations/StatisticalValidationsTable.tsx +++ b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/stat-validations/StatisticalValidationsTable.tsx @@ -186,7 +186,7 @@ export const StatisticalValidationsTable = (props: StatisticalValidationsTablePr searchPlaceholder={intl.formatMessage({ id: 'common.search' }) + ' ' + intl.formatMessage({ id: 'common.name' }) + ' / ' + intl.formatMessage({ id: 'common.description' })} urlToRetrieveData={urlBiomarkerStatisticalValidations} customElements={[ - + diff --git a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/trained-models/NewRFModelForm.tsx b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/trained-models/NewRFModelForm.tsx index 0b27fdc0..343a6ddc 100644 --- a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/trained-models/NewRFModelForm.tsx +++ b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/trained-models/NewRFModelForm.tsx @@ -75,7 +75,7 @@ export const NewRFModelForm = (props: NewSVMModelFormProps) => { /> )} - placeholder={intl.formatMessage({ id: 'common.integerNumber' })} + placeholder={intl.formatMessage({ id: 'common.integerPlaceholder' })} type='number' min={3} name='maxDepth' @@ -97,7 +97,7 @@ export const NewRFModelForm = (props: NewSVMModelFormProps) => { /> )} - placeholder={intl.formatMessage({ id: 'common.integerNumber' })} + placeholder={intl.formatMessage({ id: 'common.integerPlaceholder' })} type='number' step={1} min={0} diff --git a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/trained-models/NewSVMModelForm.tsx b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/trained-models/NewSVMModelForm.tsx index 1bd32569..17b07991 100644 --- a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/trained-models/NewSVMModelForm.tsx +++ b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/trained-models/NewSVMModelForm.tsx @@ -77,7 +77,7 @@ export const NewSVMModelForm = (props: NewSVMModelFormProps) => { /> )} - placeholder={intl.formatMessage({ id: 'common.integerNumber' })} + placeholder={intl.formatMessage({ id: 'common.integerPlaceholder' })} type='number' step={1} min={0} diff --git a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/trained-models/NewTrainedModelModal.tsx b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/trained-models/NewTrainedModelModal.tsx index f95be817..92647acb 100644 --- a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/trained-models/NewTrainedModelModal.tsx +++ b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/trained-models/NewTrainedModelModal.tsx @@ -450,7 +450,7 @@ export const NewTrainedModelModal = (props: NewTrainedModelModalProps) => { /> )} - placeholder={intl.formatMessage({ id: 'common.integerNumber' })} + placeholder={intl.formatMessage({ id: 'common.integerPlaceholder' })} type='number' step={1} min={3} diff --git a/src/frontend/static/frontend/src/locales/en.ts b/src/frontend/static/frontend/src/locales/en.ts index e5d4c72c..fdab788b 100644 --- a/src/frontend/static/frontend/src/locales/en.ts +++ b/src/frontend/static/frontend/src/locales/en.ts @@ -239,6 +239,7 @@ export default { 'statValidationMetrics.header.validationMetrics': 'Validation metrics', 'statValidationMetrics.metric.mse': 'MSE', 'statValidationMetrics.metric.r2Score': 'R2 score', + 'statValidationMetrics.header.model': 'Model', // 25.NewStatisticalValidationModal.tsx 'newStatValidation.header.create': 'Create new statistical validation', 'newStatValidation.step.trainedModel': 'Step 1: Trained model', diff --git a/src/frontend/static/frontend/src/locales/es.ts b/src/frontend/static/frontend/src/locales/es.ts index cbf0a9a5..388b0bfc 100644 --- a/src/frontend/static/frontend/src/locales/es.ts +++ b/src/frontend/static/frontend/src/locales/es.ts @@ -33,6 +33,7 @@ export default { 'common.basicData': 'Datos básicos', 'common.descriptionOptional': 'Descripción (opcional)', 'common.continue': 'Continuar', + 'common.numberOfFolds': 'Número de particiones', // common Tables and states 'common.state': 'Estado', 'common.cluster': 'Cluster', @@ -160,7 +161,7 @@ export default { 'geneOntology.panel.table.term': 'Término', 'geneOntology.panel.table.ontologyType': 'Tipo de ontología', // 12GENES.ActionableCancerGenesPanel.tsx - 'actionableGenes.panel.title': 'Panel de genes de cáncer accionables', + 'actionableCancerGenes.panel.title': 'Panel de genes de cáncer accionables', // 13.GeneAssociationsNetworkPanel 'geneAssociations.relation.fusion': 'Fusión', 'geneAssociations.relation.coOccurrence': 'Co-ocurrencia', @@ -182,6 +183,7 @@ export default { 'pathwaysInformation.context.gene': 'para este gen', 'pathwaysInformation.context.pathways': 'para este gen', // 17.MethylationInformation.tsx + 'methylationInformation.context.site': 'para este sitio de metilación', 'methylationInformation.noDetails': 'No se encontraron detalles para este sitio de metilación', 'methylationInformation.infoPopup.aliases': 'Alias de metilación y posición cromosómica', 'methylationInformation.header': 'Información de metilación', From 044b67d1dcd1da162c70d2078ec6eef4573005da Mon Sep 17 00:00:00 2001 From: Juan Nicolas Herrera <54152074+juanNH@users.noreply.github.com> Date: Tue, 29 Sep 2026 21:46:43 -0300 Subject: [PATCH 3/3] Refactor async operations to promises --- .../src/components/assistant/ChatPanel.tsx | 34 +++++++++------- .../components/biomarkers/BiomarkersPanel.tsx | 2 +- .../genes/GeneAssociationsNetworkPanel.tsx | 39 +++++++++---------- .../SelectDropDownSingleMolecule.tsx | 4 +- .../BiomarkerFromCorrelationModal.tsx | 2 +- 5 files changed, 41 insertions(+), 40 deletions(-) diff --git a/src/frontend/static/frontend/src/components/assistant/ChatPanel.tsx b/src/frontend/static/frontend/src/components/assistant/ChatPanel.tsx index faa55ec2..496922d7 100644 --- a/src/frontend/static/frontend/src/components/assistant/ChatPanel.tsx +++ b/src/frontend/static/frontend/src/components/assistant/ChatPanel.tsx @@ -274,23 +274,27 @@ const ChatPanel = (props: ChatPanelProps) => { } loadConversations() - }).catch(async err => { - let detail = err?.message ?? String(err) - - if (err?.response) { - try { - const body = await err.response.json() - detail = body?.detail ?? body?.error ?? JSON.stringify(body) - } catch { - detail = `HTTP ${err.response.status}` - } + }).catch(err => { + const appendErrorMessage = (detail: string) => { + console.error('Error sending message:', detail, err) + setMessages(prev => [...prev, { + role: 'assistant', + content: `Error: ${detail}`, + }]) + } + + if (!err?.response) { + appendErrorMessage(err?.message ?? String(err)) + return } - console.error('Error sending message:', detail, err) - setMessages(prev => [...prev, { - role: 'assistant', - content: `Error: ${detail}`, - }]) + return err.response.json().then( + (body: { detail?: string; error?: string }) => { + const detail = body?.detail ?? body?.error ?? JSON.stringify(body) ?? String(body) + appendErrorMessage(detail) + }, + () => appendErrorMessage(`HTTP ${err.response.status}`) + ) }).finally(() => { setIsLoading(false) }) diff --git a/src/frontend/static/frontend/src/components/biomarkers/BiomarkersPanel.tsx b/src/frontend/static/frontend/src/components/biomarkers/BiomarkersPanel.tsx index df86fa7d..437309cd 100644 --- a/src/frontend/static/frontend/src/components/biomarkers/BiomarkersPanel.tsx +++ b/src/frontend/static/frontend/src/components/biomarkers/BiomarkersPanel.tsx @@ -949,7 +949,7 @@ class BiomarkersPanel extends React.Component => { + handleGeneSymbols = (molecules: string[]): void => { this.setState(prevState => { const moleculesSectionPreload = { ...prevState.formBiomarker.moleculesSection, diff --git a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/molecules/genes/GeneAssociationsNetworkPanel.tsx b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/molecules/genes/GeneAssociationsNetworkPanel.tsx index 2fae23d6..fd046348 100644 --- a/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/molecules/genes/GeneAssociationsNetworkPanel.tsx +++ b/src/frontend/static/frontend/src/components/biomarkers/biomarker-details-modal/molecules/genes/GeneAssociationsNetworkPanel.tsx @@ -296,28 +296,27 @@ export const GeneAssociationsNetworkPanel = ({ selectedGene }: GeneAssociationsN cy.fit(undefined, 30) } - const getRelatedGenes = async (gene: BiomarkerMolecule, score: number) => { + const getRelatedGenes = (gene: BiomarkerMolecule, score: number) => { abortControllerRef.current?.abort() - abortControllerRef.current = new AbortController() - - try { - const response = await ky.get(urlGeneAssociationsNetwork, { - searchParams: { - gene_id: gene.identifier, - min_combined_score: score - } as any, - signal: abortControllerRef.current.signal - }) - - const data = await response.json<{ data: CytoscapeResponseData }>() - applyElements(data.data) - } catch (err) { - if (!abortControllerRef.current?.signal.aborted) { - alertGeneralError() - } + const abortController = new AbortController() + abortControllerRef.current = abortController + + return ky.get(urlGeneAssociationsNetwork, { + searchParams: { + gene_id: gene.identifier, + min_combined_score: score + } as any, + signal: abortController.signal + }) + .json<{ data: CytoscapeResponseData }>() + .then(data => applyElements(data.data)) + .catch(err => { + if (!abortController.signal.aborted) { + alertGeneralError() + } - console.log('Error getting experiment', err) - } + console.log('Error getting experiment', err) + }) } useEffect(() => { diff --git a/src/frontend/static/frontend/src/components/biomarkers/modalContentBiomarker/manualForm/newBiomarkerForm/SelectDropDownSingleMolecule.tsx b/src/frontend/static/frontend/src/components/biomarkers/modalContentBiomarker/manualForm/newBiomarkerForm/SelectDropDownSingleMolecule.tsx index 517a466f..e0cd3828 100644 --- a/src/frontend/static/frontend/src/components/biomarkers/modalContentBiomarker/manualForm/newBiomarkerForm/SelectDropDownSingleMolecule.tsx +++ b/src/frontend/static/frontend/src/components/biomarkers/modalContentBiomarker/manualForm/newBiomarkerForm/SelectDropDownSingleMolecule.tsx @@ -30,9 +30,7 @@ export const SelectDropDownSingleMolecule = (props: SelectDropDownSingleMolecule /** Makes the query to Modulector/Bio-API to retrieve molecules. */ const makeSearchRequest = useCallback( - debounce(async (search: string) => { - await handleSearchNewData(search) - }, 1000), + debounce((search: string) => handleSearchNewData(search), 1000), [] ) diff --git a/src/frontend/static/frontend/src/components/pipeline/experiment-result/BiomarkerFromCorrelationModal.tsx b/src/frontend/static/frontend/src/components/pipeline/experiment-result/BiomarkerFromCorrelationModal.tsx index 92e482e2..f746c87a 100644 --- a/src/frontend/static/frontend/src/components/pipeline/experiment-result/BiomarkerFromCorrelationModal.tsx +++ b/src/frontend/static/frontend/src/components/pipeline/experiment-result/BiomarkerFromCorrelationModal.tsx @@ -803,7 +803,7 @@ class BiomarkerFromCorrelationModal extends React.Component => { + handleGeneSymbols = (molecules: string[]): void => { const moleculesSectionPreload = { ...this.state.formBiomarker.moleculesSection, [this.state.formBiomarker.moleculeSelected]: {