Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LDA Chart and Routine #1797

Merged
merged 14 commits into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"all-documents",
"classif-by",
"close-by",
"clustered-precomputed",
"count-all",
"count-by-fields",
"cross-by",
Expand Down
5 changes: 5 additions & 0 deletions src/app/custom/precomputers/precomputers-catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,10 @@
"id": "post-v1-lda",
"url": "https://data-computer.services.istex.fr/v1/lda",
"doc": "https://gitbucket.inist.fr/tdm/web-services/tree/master/data-computer#v1%252flda"
},
{
"id": "post-v1-lda-segment",
"url": "https://data-computer.services.istex.fr/v1/lda-segment",
"doc": "https://gitbucket.inist.fr/tdm/web-services/tree/master/data-computer#v1%252flda"
}
]
8 changes: 4 additions & 4 deletions src/app/custom/routines/routines-precomputed-catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
"recommendedWith": ["Network", "HeatMap"]
},
{
"id": "r-lda",
"url": "/api/run/lda-precomputed/",
"doc": "https://user-doc.lodex.inist.fr/lodex-user-documentation/configuration/routines/lda",
"recommendedWith": []
"id": "r-clustered-precomputed",
"url": "/api/run/clustered-precomputed/",
"doc": "https://user-doc.lodex.inist.fr/lodex-user-documentation/configuration/routines/clustered-precomputed",
"recommendedWith": ["ClusteredChart"]
},
{
"id": "r-tree-by",
Expand Down
10 changes: 9 additions & 1 deletion src/app/custom/translations.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -1017,4 +1017,12 @@ vega_variable_list You can use these variables to set the size dynamically: Vous
vega_variable_width Replace with the width of the chart container Remplacée par la largeur du conteneur du graphique
vega_variable_height Replace with the height of the chart container Remplacée par la hauteur du conteneur du graphique
theme Application theme Thème de l'application
"error_precomputed_data_empty" "Precomputed data is empty or unfinished. You might want to relaunch the action" "Les données précalculées sont vides ou non terminées. Vous pouvez relancer l'action"
"error_precomputed_data_empty" "Precomputed data is empty or unfinished. You might want to relaunch the action" "Les données précalculées sont vides ou non terminées. Vous pouvez relancer l'action"
"pc_post-v1-lda-segment_title" "Lda-Segment" "Lda-Segment"
"pc_post-v1-lda-segment_description" "Create a «lda» field consisting of 5 topics from the set of documents. Each topic contains a «word» field, which is composed of a list of 10 words that are most characteristic of the topic, as well as a «weight» field that corresponds to the weight associated with the topic in the document. The text must be in English." "Créer à partir de l'ensemble des documents un champ «lda» constitué de 5 topics. Chaque topic contient un champ «word», qui est composé d'une liste de 10 mots qui sont les plus caractéristiques du topic, ainsi que d'un champ «weight» qui correspond au poids associé au sujet dans le document. Le texte doit être en anglais."
r-clustered-precomputed_title clustered-precomputed clustered-precomputed
r-clustered-precomputed_description Retrieve precomputed data from precomputes like 'Lda-Segment' and display it in the Clustered chart Récupér les données précalculées des précalculs comme 'Lda-Segment' et les afficher dans un Diagramme à barres groupées
AlasDiablo marked this conversation as resolved.
Show resolved Hide resolved
formatClusteredChart Graph - Clustered chart Graphique - Diagramme à barres groupées
formatClusteredChartDescription Displays several bar charts depending on the category of the element and its weight Affiche plusieurs diagrammes à barres en fonction de la catégorie de l'élément et de sont poids
AlasDiablo marked this conversation as resolved.
Show resolved Hide resolved
format_x_axis_title Title of x axis Titre de l'axe x
format_y_axis_title Title of y axis Titre de l'axe y
8 changes: 8 additions & 0 deletions src/app/js/formats/formats.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import istexCitation from './istexCitation';
import istexRefbibs from './istexRefbibs';
import bubblePlot from './vega-lite/component/bubble-plot';
import flowMap from './vega/component/flow-map';
import clusteredChart from './vega-lite/component/clustered-chart';

export const FORMATS_CATALOG = [
{
Expand Down Expand Up @@ -346,4 +347,11 @@ export const FORMATS_CATALOG = [
component: flowMap,
type: 'chart',
},
{
name: 'formatClusteredChart',
description: 'formatClusteredChartDescription',
componentName: 'clusteredChart',
component: clusteredChart,
type: 'chart',
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { CustomActionVegaLite } from '../vega-lite-component';
import { VEGA_LITE_DATA_INJECT_TYPE_A } from '../../../chartsUtils';
import PropTypes from 'prop-types';
import React, { useMemo } from 'react';

const ClusteredChart = ({ data, topic, params }) => {
const values = useMemo(() => {
return data.filter(value => value.source === topic);
}, [data, topic]);

const spec = useMemo(() => {
const { colors, xTitle, yTitle } = params;
const specToReturn = {
$schema: 'https://vega.github.io/schema/vega-lite/v5.json',
config: { legend: { disable: true } },
title: topic,
encoding: {
y: { field: 'target', type: 'nominal', sort: null },
x: { field: 'weight', type: 'quantitative', sort: null },
},
layer: [
{
mark: 'bar',
encoding: {
color: {
field: 'weight',
scale: { range: colors.split(' ') },
},
},
},
{
mark: {
type: 'text',
align: 'left',
baseline: 'middle',
dx: 3,
},
encoding: {
text: {
field: 'weight',
type: 'quantitative',
format: '.4f',
},
},
},
],
width: 'container',
height: { step: 20 },
};

if (xTitle && xTitle !== '') {
specToReturn.encoding.x.title = xTitle;
}

if (yTitle && yTitle !== '') {
specToReturn.encoding.y.title = yTitle;
}

return specToReturn;
}, [values, topic, params]);
return (
<CustomActionVegaLite
spec={spec}
data={{
values,
}}
injectType={VEGA_LITE_DATA_INJECT_TYPE_A}
/>
);
};

ClusteredChart.propTypes = {
data: PropTypes.array.isRequired,
topic: PropTypes.string.isRequired,
params: PropTypes.shape({
colors: PropTypes.string.isRequired,
xTitle: PropTypes.string,
yTitle: PropTypes.string,
}),
};

export default ClusteredChart;
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from 'react';
import translate from 'redux-polyglot/translate';
import PropTypes from 'prop-types';
import { Box, TextField } from '@mui/material';

import { polyglot as polyglotPropTypes } from '../../../../propTypes';
import ColorPickerParamsAdmin from '../../../shared/ColorPickerParamsAdmin';
import { MONOCHROMATIC_DEFAULT_COLORSET } from '../../../colorUtils';
import updateAdminArgs from '../../../shared/updateAdminArgs';

export const defaultArgs = {
colors: MONOCHROMATIC_DEFAULT_COLORSET,
};

const ClusteredChartAdmin = props => {
const { args, p } = props;
const { colors, xTitle, yTitle } = args;

const handleColors = colors => {
updateAdminArgs('colors', colors || defaultArgs.colors, props);
};

const handleXAxisTitle = event => {
updateAdminArgs('xTitle', event.target.value, props);
};

const handleYAxisTitle = event => {
updateAdminArgs('yTitle', event.target.value, props);
};

return (
<Box
display="flex"
flexWrap="wrap"
justifyContent="space-between"
gap={2}
>
<ColorPickerParamsAdmin
colors={colors}
onChange={handleColors}
polyglot={p}
/>
<TextField
fullWidth
label={p.t('format_x_axis_title')}
onChange={handleXAxisTitle}
value={xTitle}
/>
<TextField
fullWidth
label={p.t('format_y_axis_title')}
onChange={handleYAxisTitle}
value={yTitle}
/>
</Box>
);
};

ClusteredChartAdmin.defaultProps = {
args: defaultArgs,
};

ClusteredChartAdmin.propTypes = {
args: PropTypes.shape({
colors: PropTypes.string,
xTitle: PropTypes.string,
yTitle: PropTypes.string,
}),
onChange: PropTypes.func.isRequired,
p: polyglotPropTypes.isRequired,
};

export default translate(ClusteredChartAdmin);
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import React, { useMemo } from 'react';
import compose from 'recompose/compose';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import _ from 'lodash';
import { Grid, Paper } from '@mui/material';

import injectData from '../../../injectData';
import { field as fieldPropTypes } from '../../../../propTypes';
import ClusteredChart from './ClusteredChart';

const ClusteredChartView = ({ data, colors, xTitle, yTitle }) => {
const { values } = data;

const topics = useMemo(() => {
return _.chain(values)
.map(value => value.source)
.uniq()
.sort((a, b) =>
a.localeCompare(b, 'fr', {
sensitivity: 'accent',
numeric: true,
usage: 'sort',
ignorePunctuation: true,
}),
)
.value();
}, [values]);

return (
<div style={{ margin: '12px' }}>
<Grid
container
justifyContent="center"
rowSpacing={1}
columnSpacing={1}
>
{topics.map(topic => (
<Grid key={topic} item xs={6}>
<Paper style={{ padding: '6px' }}>
<ClusteredChart
data={values}
topic={topic}
params={{
colors,
xTitle,
yTitle,
}}
/>
</Paper>
</Grid>
))}
</Grid>
</div>
);
};

const mapStateToProps = (state, { formatData }) => {
if (!formatData) {
return {
data: {
values: [],
},
};
}
return {
data: {
values: formatData,
},
};
};

ClusteredChartView.propTypes = {
field: fieldPropTypes.isRequired,
resource: PropTypes.object.isRequired,
data: PropTypes.any,
colors: PropTypes.string.isRequired,
xTitle: PropTypes.string,
yTitle: PropTypes.string,
};

export default compose(
injectData(),
connect(mapStateToProps),
)(ClusteredChartView);
12 changes: 12 additions & 0 deletions src/app/js/formats/vega-lite/component/clustered-chart/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import DefaultFormat from '../../../DefaultFormat';
import Icon from '../../VegaLiteIcon';
import Component from './ClusteredChartView';
import AdminComponent, { defaultArgs } from './ClusteredChartAdmin';

export default {
...DefaultFormat,
Component,
AdminComponent,
defaultArgs,
Icon,
};
57 changes: 57 additions & 0 deletions workers/routines/clustered-precomputed.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
; suppress inspection "DuplicateSectionInFile" for whole file
; suppress inspection "DuplicateKeyInSection" for whole file
; Export and format clustered precompute data into an valid format for vega-lite
prepend = delegate?file=../worker.ini
mimeType = application/json
label = clustered-precomputed

[use]
plugin = basics
plugin = lodex
plugin = analytics

[buildContext]
connectionStringURI = get('connectionStringURI')
precomputedName = get('precomputedName')

[env]
path = connectionStringURI
value = get('connectionStringURI')

path = precomputedName
value = get('precomputedName')

[LodexRunQuery]

[LodexFilterPrecomputed]

[assign]
path = id
value = fix(`["${self.source}","${self.target}"]`)

path = value
value = get('weight')

[reducing]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you can simplify using [dedupe]


[assign]
path = source
value = fix(JSON.parse(self.id)[1])

path = target
value = fix(JSON.parse(self.id)[0])

path = weight
value = fix(self.value[0])

[keep]
path = source
path = target
path = weight

[sort]
path = weight
reverse = true

[LodexOutput]
indent = true