Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ POFILES=locale/fr/LC_MESSAGES/loris.po \
modules/mri_violations/locale/hi/LC_MESSAGES/mri_violations.po \
modules/mri_violations/locale/es/LC_MESSAGES/mri_violations.po \
modules/statistics/locale/fr/LC_MESSAGES/statistics.po \
modules/statistics/locale/hi/LC_MESSAGES/statistics.po \
modules/statistics/locale/ja/LC_MESSAGES/statistics.po \
modules/statistics/locale/zh/LC_MESSAGES/statistics.po \
modules/server_processes_manager/locale/ja/LC_MESSAGES/server_processes_manager.po \
modules/server_processes_manager/locale/hi/LC_MESSAGES/server_processes_manager.po \
modules/module_manager/locale/fr/LC_MESSAGES/module_manager.po \
Expand Down
11 changes: 11 additions & 0 deletions modules/statistics/jsx/WidgetIndex.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, {useEffect, useState} from 'react';
import PropTypes from 'prop-types';
import Recruitment from './widgets/recruitment';
import StudyProgression from './widgets/studyprogression';
import AdminStats from './widgets/adminstats';
import {fetchData} from './Fetch';
import Modal from 'Modal';
import Loader from 'Loader';
Expand All @@ -13,6 +14,7 @@ import '../css/WidgetIndex.css';

import {setupCharts, unloadCharts} from './widgets/helpers/chartBuilder';
import jaStrings from '../locale/ja/LC_MESSAGES/statistics.json';
import hiStrings from '../locale/hi/LC_MESSAGES/statistics.json';
import frStrings from '../locale/fr/LC_MESSAGES/statistics.json';
import zhStrings from '../locale/zh/LC_MESSAGES/statistics.json';

Expand All @@ -25,10 +27,12 @@ import zhStrings from '../locale/zh/LC_MESSAGES/statistics.json';
const WidgetIndex = (props) => {
const [recruitmentData, setRecruitmentData] = useState({});
const [studyProgressionData, setStudyProgressionData] = useState({});
const [adminStatsData, setAdminStatsData] = useState({});
const [modalChart, setModalChart] = useState(null);
const {t, i18n} = useTranslation();
useEffect( () => {
i18n.addResourceBundle('ja', 'statistics', jaStrings);
i18n.addResourceBundle('hi', 'statistics', hiStrings);
i18n.addResourceBundle('fr', 'statistics', frStrings);
i18n.addResourceBundle('zh', 'statistics', zhStrings);
}, []);
Expand Down Expand Up @@ -273,6 +277,7 @@ const WidgetIndex = (props) => {
);
setRecruitmentData(data);
setStudyProgressionData(data);
setAdminStatsData(data);
};
setup().catch(
(error) => {
Expand Down Expand Up @@ -368,6 +373,11 @@ const WidgetIndex = (props) => {
showChart ={showChart}
updateFilters ={updateFilters}
/>
{loris.userHasPermission('user_account_multisite') && <AdminStats
data ={adminStatsData}
baseURL ={props.baseURL}
showChart ={showChart}
/>}
</>
);
};
Expand Down Expand Up @@ -452,3 +462,4 @@ const exportChartAsImage = (chartId) => {
'data:image/svg+xml;base64,'
+ btoa(unescape(encodeURIComponent(svgData)));
};

147 changes: 147 additions & 0 deletions modules/statistics/jsx/widgets/adminstats.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import React, {useEffect, useState} from 'react';
import PropTypes from 'prop-types';
import i18n from 'I18nSetup';
import Loader from 'Loader';
import Panel from 'Panel';
import {setupCharts} from './helpers/chartBuilder';
import {useTranslation} from 'react-i18next';
import jaStrings from '../../locale/ja/LC_MESSAGES/statistics.json';
import hiStrings from '../../locale/hi/LC_MESSAGES/statistics.json';
import zhStrings from '../../locale/zh/LC_MESSAGES/statistics.json';
import frStrings from '../../locale/fr/LC_MESSAGES/statistics.json';

/**
* AdminStats - a widget containing admin account statistics.
*
* @param {object} props
* @return {JSX.Element}
*/
const AdminStats = (props) => {
const {t} = useTranslation();
const [loading, setLoading] = useState(true);
let json = props.data;

const [chartDetails, setChartDetails] = useState({
'adminStats': {
'userregistrations_bydate': {
sizing: 11,
title: t('User Registrations', {ns: 'statistics'}),
filters: '',
chartType: 'line',
dataType: 'line',
label: t('Registrations', {ns: 'statistics'}),
legend: '',
options: {line: 'line'},
chartObject: null,
includeTotal: false,
titlePrefix: 'Month',
Comment thread
skarya22 marked this conversation as resolved.
Outdated
dateFormat: '%m-%Y',
},
'uniquelogins_bymonth': {
sizing: 11,
title: t('Unique Monthly Logins', {ns: 'statistics'}),
filters: '',
chartType: 'line',
dataType: 'line',
label: t('Unique Logins', {ns: 'statistics'}),
legend: '',
options: {line: 'line'},
chartObject: null,
includeTotal: false,
titlePrefix: 'Month',
Comment thread
skarya22 marked this conversation as resolved.
Outdated
dateFormat: '%m-%Y',
},
},
});

useEffect(() => {
i18n.addResourceBundle('ja', 'statistics', jaStrings);
i18n.addResourceBundle('zh', 'statistics', zhStrings);
i18n.addResourceBundle('hi', 'statistics', hiStrings);
i18n.addResourceBundle('fr', 'statistics', frStrings);

let newdetails = {...chartDetails};
newdetails['adminStats']['userregistrations_bydate']['title']
= t('User Registrations', {ns: 'statistics'});
newdetails['adminStats']['userregistrations_bydate']['label']
= t('Registrations', {ns: 'statistics'});

Comment thread
skarya22 marked this conversation as resolved.
Outdated
newdetails['adminStats']['uniquelogins_bymonth']['title']
= t('Unique Monthly Logins', {ns: 'statistics'});
newdetails['adminStats']['uniquelogins_bymonth']['label']
= t('Unique Logins', {ns: 'statistics'});
setChartDetails(newdetails);
Comment thread
skarya22 marked this conversation as resolved.
}, []);

const showChart = (section, chartID) => {
return props.showChart(section, chartID, chartDetails, setChartDetails);
};

useEffect(() => {
if (json && Object.keys(json).length !== 0) {
const newChartDetails = {
'adminStats': {
...chartDetails.adminStats,
'userregistrations_bydate': {
...chartDetails.adminStats.userregistrations_bydate,
data: json['adminstats']?.['User Registrations'],
},
'uniquelogins_bymonth': {
...chartDetails.adminStats.uniquelogins_bymonth,
data: json['adminstats']?.['Unique Monthly Logins'],
},
},
};

setLoading(false);
setTimeout(() => {
setupCharts(
t,
false,
newChartDetails,
t('Total', {ns: 'loris'})
).then((data) => {
setChartDetails(data);
});
}, 0);
json = props.data;
}
}, [props.data]);

const title = (subtitle) => t('Admin stats', {ns: 'statistics'})
+ ' — ' + t(subtitle, {ns: 'statistics'});

return loading ? <Panel title='Admin stats'><Loader/></Panel> : (
<Panel
title={t('Admin stats', {ns: 'statistics'})}
id='statistics_adminstats'
onChangeView={() => {
setupCharts(t, false, chartDetails, t('Total', {ns: 'loris'}));
}}
views={[
{
content:
showChart('adminStats', 'userregistrations_bydate'),
title: title('User Registrations'),
},
{
content:
showChart('adminStats', 'uniquelogins_bymonth'),
title: title('Unique Monthly Logins'),
},
]}
/>
);
};

AdminStats.propTypes = {
data: PropTypes.object,
baseURL: PropTypes.string,
showChart: PropTypes.func,
};

AdminStats.defaultProps = {
data: {},
};

export default AdminStats;
79 changes: 52 additions & 27 deletions modules/statistics/jsx/widgets/helpers/chartBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,15 @@ const createBarChart = (t, labels, columns, id, targetModal, colours, dataType)
return newChart;
}

const createLineChart = (data, columns, id, label, targetModal, titlePrefix) => {
const createLineChart = (
data,
columns,
id,
label,
targetModal,
titlePrefix,
dateFormat,
) => {
// Calculate grand total across all data points for percentage calculation
let grandTotal = 0;
if (data && data.datasets) {
Expand All @@ -167,6 +175,22 @@ const createLineChart = (data, columns, id, label, targetModal, titlePrefix) =>
}
}
}
const xFormat = dateFormat || (id.includes('bymonth') ? '%m-%Y' : null);
const axis = xFormat ? {
x: {
type: 'timeseries',
tick: {
format: xFormat,
rotate: -65,
multiline: true,
},
},
y: {
max: maxY(data),
label: label,
},
} : undefined;

let newChart = c3.generate({
size: {
height: targetModal && 500,
Expand All @@ -175,25 +199,12 @@ const createLineChart = (data, columns, id, label, targetModal, titlePrefix) =>
bindto: targetModal ? targetModal : id,
data: {
x: 'x',
xFormat: id.includes('bymonth') && '%m-%Y',
xFormat: xFormat || undefined,
columns: columns,
type: 'area-spline',
},
spline: {interpolation: {type: 'monotone'}},
axis: id.includes('bymonth') && {
x: {
type: 'timeseries',
tick: {
format: '%m-%Y',
rotate: -65,
multiline: true,
},
},
y: {
max: maxY(data),
label: label,
},
},
axis: axis,
zoom: {
enabled: true,
},
Expand Down Expand Up @@ -309,15 +320,27 @@ const setupCharts = async (t, targetIsModal, chartDetails, totalLabel) => {
labels = chartData.labels;
colours = sexColours;
} else if (chart.dataType === 'line') {
columns = formatLineData(chartData, totalLabel);
columns = formatLineData(
chartData,
totalLabel,
chart.includeTotal !== false,
);
}
let chartObject = null;
if (chart.chartType === 'pie') {
chartObject = createPieChart(columns, `#${chartID}`, targetIsModal && '#dashboardModal', colours);
} else if (chart.chartType === 'bar') {
chartObject = createBarChart(t, labels, columns, `#${chartID}`, targetIsModal && '#dashboardModal', colours, chart.dataType);
} else if (chart.chartType === 'line') {
chartObject = createLineChart(chartData, columns, `#${chartID}`, chart.label, targetIsModal && '#dashboardModal', chart.titlePrefix);
chartObject = createLineChart(
chartData,
columns,
`#${chartID}`,
chart.label,
targetIsModal && '#dashboardModal',
chart.titlePrefix,
chart.dateFormat,
);
}
newChartDetails[section][chartID].data = chartData;
newChartDetails[section][chartID].chartObject = chartObject;
Expand All @@ -337,7 +360,7 @@ const setupCharts = async (t, targetIsModal, chartDetails, totalLabel) => {
* @param {object} data
* @return {*[]}
*/
const formatLineData = (data, totalLabel) => {
const formatLineData = (data, totalLabel, includeTotal = true) => {
const processedData = [];
const labels = [];
labels.push('x');
Expand All @@ -350,16 +373,18 @@ const formatLineData = (data, totalLabel) => {
dataset.push(data['datasets'][i].name);
processedData.push(dataset.concat(data['datasets'][i].data));
}
const totals = [];
totals.push(totalLabel);
for (let j = 0; j < data['datasets'][0].data.length; j++) {
let total = 0;
for (let i = 0; i < data['datasets'].length; i++) {
total += parseInt(data['datasets'][i].data[j]);
if (includeTotal) {
const totals = [];
totals.push(totalLabel);
for (let j = 0; j < data['datasets'][0].data.length; j++) {
let total = 0;
for (let i = 0; i < data['datasets'].length; i++) {
total += parseInt(data['datasets'][i].data[j]);
}
totals.push(total);
}
totals.push(total);
processedData.push(totals);
}
processedData.push(totals);
return processedData;
};

Expand Down
15 changes: 15 additions & 0 deletions modules/statistics/locale/fr/LC_MESSAGES/statistics.po
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ msgstr "Statistiques"
msgid "Study Progression"
msgstr "Progression des études"

msgid "Admin stats"
msgstr "Statistiques d'administration"

msgid "Summary"
msgstr "Résumé"

Expand Down Expand Up @@ -74,6 +77,18 @@ msgstr "Répartition selon le sexe biologique par site"
msgid "Candidate Age at Registration"
msgstr "Âge du candidat au moment de l'inscription"

msgid "User Registrations"
msgstr "Inscriptions d'utilisateurs"

msgid "Registrations"
msgstr "Inscriptions"

msgid "Unique Monthly Logins"
msgstr "Connexions mensuelles uniques"

msgid "Unique Logins"
msgstr "Connexions uniques"

msgid "Candidates registered"
msgstr "Candidats inscrits"

Expand Down
Loading
Loading