From 749c421364b192d375f94e9132ad77f4695aaec6 Mon Sep 17 00:00:00 2001 From: victor barbier <victor.barbier@recherche.gouv.fr> Date: Fri, 13 Oct 2023 16:11:35 +0200 Subject: [PATCH] feat(graph): Add absolute and relative toggle for retractations by publisher --- .../others/retractions/chart-by-publisher.js | 15 ++++++++- .../retractions/get-data-by-publisher.js | 31 +++++++++-------- src/translations/en.json | 4 +++ src/translations/fr.json | 4 +++ src/utils/chartFetchOptions.js | 1 + src/utils/chartOptions.js | 16 ++++++--- src/utils/helpers.js | 33 +++++++++++++++++++ 7 files changed, 84 insertions(+), 20 deletions(-) diff --git a/src/components/Charts/publications/others/retractions/chart-by-publisher.js b/src/components/Charts/publications/others/retractions/chart-by-publisher.js index 0456bd9b..25754555 100644 --- a/src/components/Charts/publications/others/retractions/chart-by-publisher.js +++ b/src/components/Charts/publications/others/retractions/chart-by-publisher.js @@ -1,3 +1,4 @@ +import { Toggle } from '@dataesr/react-dsfr'; import Highcharts from 'highcharts'; import HCExportingData from 'highcharts/modules/export-data'; import HCExporting from 'highcharts/modules/exporting'; @@ -22,15 +23,22 @@ const Chart = ({ domain, hasComments, hasFooter, id }) => { const chartRef = useRef(); const intl = useIntl(); const [chartComments, setChartComments] = useState(''); + const [isPercent, setPercent] = useState(false); const { observationSnaps } = useGlobals(); - const { data, isError, isLoading } = useGetData(observationSnaps, domain); + const { data, isError, isLoading } = useGetData( + observationSnaps, + domain, + isPercent, + ); const { categories, dataGraph } = data; const idWithDomain = withDomain(id, domain); + const optionsGraph = chartOptions[id].getOptions( idWithDomain, intl, categories, dataGraph, + isPercent, ); useEffect(() => { @@ -47,6 +55,11 @@ const Chart = ({ domain, hasComments, hasFooter, id }) => { isError={isError} isLoading={isLoading || !dataGraph} > + <Toggle + checked={isPercent} + label={intl.formatMessage({ id: 'app.proportion' })} + onChange={() => setPercent(!isPercent)} + /> <HighchartsReact highcharts={Highcharts} id={idWithDomain} diff --git a/src/components/Charts/publications/others/retractions/get-data-by-publisher.js b/src/components/Charts/publications/others/retractions/get-data-by-publisher.js index 23dc2c50..984f0c63 100644 --- a/src/components/Charts/publications/others/retractions/get-data-by-publisher.js +++ b/src/components/Charts/publications/others/retractions/get-data-by-publisher.js @@ -18,27 +18,30 @@ function useGetData(observationSnaps, domain = '', isPercent = false) { parameters: [lastObservationSnap], objectType: ['publications'], }); + const numberOfRetracted = (item) => item.by_retraction.buckets.find((i2) => i2.key === 1)?.doc_count ?? 0; const response = await Axios.post(ES_API_URL, query, HEADERS); - const buckets = response?.data?.aggregations?.by_publisher?.buckets?.sort( - (a, b) => b.doc_count - a.doc_count, - ); + const buckets = response?.data?.aggregations?.by_publisher?.buckets + ?.sort((a, b) => numberOfRetracted(b) - numberOfRetracted(a)) + .slice(0, 20); const categories = buckets.map((item) => item.key); - const dataGraph = [ - { - data: buckets.map( - (item) => ((item.by_retraction.buckets.find((i2) => i2.key === 1) - ?.doc_count ?? 0) - / item.doc_count) - * 100, - ), - }, - ]; + const dataGraph = { + data: buckets.map((item, catIndex) => ({ + y_tot: item.doc_count ?? 0, + y_abs: numberOfRetracted(item), + y_rel: (numberOfRetracted(item) / item.doc_count) * 10000, + y: isPercent + ? (numberOfRetracted(item) / item.doc_count) * 10000 + : numberOfRetracted(item), + x: catIndex, + publisher: categories[catIndex], + })), + }; return { categories, dataGraph, }; - }, [domain, lastObservationSnap]); + }, [domain, isPercent, lastObservationSnap]); useEffect(() => { async function getData() { diff --git a/src/translations/en.json b/src/translations/en.json index 5a539c66..b2bdd93d 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -906,6 +906,7 @@ "app.observational.end": "End of<br>the observational<br>study", "app.observationals": "observational studies", "app.project": "The project", + "app.proportion": "Proportion", "app.promoteurs.impact.chart-classement-pays.tooltip": "<b>Trials completed, in collaboration with {point.country}</b><br>{point.y:.2f}% share scientific results or publication ({point.y_abs} / {point.y_tot})", "app.promoteurs.impact.chart-repartition.tooltip": "<b>Trials completed in {point.year}, {series.name}</b><br>Trials contribution with {point.name}: {point.y:.2f}% of the trials ({point.y_abs} / {point.y_tot})", "app.publi.affiliations": "Affiliations", @@ -943,7 +944,10 @@ "app.publi.navigation.langues": "The languages of publication", "app.publi.navigation.voies": "The opening routes", "app.publi.nb-publications": "Number of publications", + "app.publi.nb-publications-retracted": "Number of retracted publications", + "app.national-publi.others.retractions.chart-by-publisher.tooltip": "<b>{point.publisher}</b><br>• Share of retracted publications :<br>{point.y_rel:.1f} ‱ ({point.y_abs} / {point.y_tot})", "app.publi.percent-publications-retracted": "Percent of retracted publications", + "app.publi.pertenthousand-publications-retracted": "Share of retracted publications (per ten thousand)", "app.publi.part-publications-archive": "Share of publications in open access on archive", "app.publi.percentage-publi": "Proportion of publications", "app.publi.percentage-publi-bealls": "Share of publications whose journal or<br>publisher is in the Beall's list", diff --git a/src/translations/fr.json b/src/translations/fr.json index e1412d8c..7b589e2b 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -1068,6 +1068,7 @@ "app.observational.end": "Fin de l'étude<br>observationnelle", "app.observationals": "études observationnelles", "app.project": "Le projet", + "app.proportion": "Proportion", "app.promoteurs.impact.chart-classement-pays.tooltip": "<b>Essais terminés, en collaboration avec {point.country}</b><br>{point.y:.2f} % partagent des résultats ou une publication scientifique ({point.y_abs} / {point.y_tot})", "app.promoteurs.impact.chart-repartition.tooltip": "<b>Essais terminés en {point.year}, {series.name}</b><br>Contribution des essais avec {point.name} : {point.y:.2f} % des essais ({point.y_abs} / {point.y_tot})", "app.publi.affiliations": "Affiliations", @@ -1105,7 +1106,10 @@ "app.publi.navigation.langues": "Les langues de publication", "app.publi.navigation.voies": "Les voies d'ouverture", "app.publi.nb-publications": "Nombre de publications", + "app.publi.nb-publications-retracted": "Nombre de publications retirées", + "app.national-publi.others.retractions.chart-by-publisher.tooltip": "<b>{point.publisher}</b><br>• Part de publications retirées :<br>{point.y_rel:.1f} ‱ ({point.y_abs} / {point.y_tot})", "app.publi.percent-publications-retracted": "Pourcentage de publications retirées", + "app.publi.pertenthousand-publications-retracted": "Part des publications retirées (pour dix mille)", "app.publi.part-publications-archive": "Part des publications en accès ouvert sur archive", "app.publi.percentage-publi": "Proportion de publications", "app.publi.percentage-publi-bealls": "Proportion de publications dont la revue ou<br>l'éditeur est dans la liste de Beall", diff --git a/src/utils/chartFetchOptions.js b/src/utils/chartFetchOptions.js index 6a4f8453..cb2d1fd3 100644 --- a/src/utils/chartFetchOptions.js +++ b/src/utils/chartFetchOptions.js @@ -2148,6 +2148,7 @@ export default function getFetchOptions({ by_publisher: { terms: { field: 'publisher_normalized.keyword', + size: 50, }, aggs: { by_retraction: { diff --git a/src/utils/chartOptions.js b/src/utils/chartOptions.js index 9e826c47..4729c53b 100644 --- a/src/utils/chartOptions.js +++ b/src/utils/chartOptions.js @@ -3,6 +3,7 @@ import { cleanNumber, getCSSValue, getPercentageYAxis, + getPertenthousandYAxis, getSource, getURLSearchParams, withtStudyType, @@ -4195,16 +4196,18 @@ export const chartOptions = { }, }, 'publi.others.retractions.chart-by-publisher': { - getOptions: (id, intl, categories, data, dataTitle) => { + getOptions: (id, intl, categories, graph, isPercent, dataTitle) => { const options = getGraphOptions({ id, intl, dataTitle }); options.chart.type = 'column'; options.xAxis = { title: { text: intl.formatMessage({ id: 'app.publishers' }) }, categories, }; - options.yAxis = getPercentageYAxis(); + options.yAxis = getPertenthousandYAxis(false, null, !isPercent); options.yAxis.title.text = intl.formatMessage({ - id: 'app.publi.percent-publications-retracted', + id: isPercent + ? 'app.publi.pertenthousand-publications-retracted' + : 'app.publi.nb-publications-retracted', }); options.legend.enabled = false; options.plotOptions = { @@ -4212,12 +4215,15 @@ export const chartOptions = { dataLabels: { enabled: true, formatter() { - return this.y === 0 ? '' : this.y.toFixed(3).concat(' %'); + if (isPercent) { + return this.y === 0 ? '' : this.y.toFixed(1).concat(' ‱'); + } + return this.y === 0 ? '' : this.y.toFixed(); }, }, }, }; - options.series = data; + options.series = [graph]; options.exporting.chartOptions.legend.enabled = false; return options; }, diff --git a/src/utils/helpers.js b/src/utils/helpers.js index 07b9f0d5..16f1518b 100644 --- a/src/utils/helpers.js +++ b/src/utils/helpers.js @@ -125,6 +125,39 @@ export function getPercentageYAxis( return axis; } +export function getPertenthousandYAxis( + showTotal = true, + max = null, + absolute = false, + precision = 0, +) { + const suffix = absolute ? '' : ' ‱'; + const axis = { + title: { text: '' }, + stackLabels: { + enabled: true, + formatter() { + return showTotal && this.total + ? this.total.toFixed(precision).concat(suffix) + : ''; + }, + style: { + fontWeight: 'bold', + fontSize: '15px', + }, + }, + labels: { + formatter() { + return this.axis.defaultLabelFormatter.call(this).concat(suffix); + }, + }, + }; + if (max) { + axis.max = max; + } + return axis; +} + /** * * @param keys