Changes ------- - Bump frontend-platform to bring `intl-imports.js` script - Move all i18n imports into `src/i18n/index.js` so `intl-imports.js` can override it with latest translations - Add `atlas` into `make pull_translations` when `OPENEDX_ATLAS_PULL` environment variable is set. - Refactored i18n utils into own file to avoid overwriting them by atlas Refs: [FC-0012 project](https://openedx.atlassian.net/l/cp/XGS0iCcQ) implementing Translation Infrastructure OEP-58.
295 lines
9.0 KiB
JavaScript
295 lines
9.0 KiB
JavaScript
/* eslint-disable import/no-self-import */
|
|
import { StrictDict } from 'utils';
|
|
|
|
import { Headings, GradeFormats } from 'data/constants/grades';
|
|
import { formatDateForDisplay } from 'data/actions/utils';
|
|
import { getLocalizedSlash } from 'i18n/utils';
|
|
import simpleSelectorFactory from '../utils';
|
|
import * as module from './grades';
|
|
|
|
/**
|
|
* getRowsProcessed(historyEntryData)
|
|
* @param {object} historyEntryData - the data param from a bulk management history entry
|
|
* @return {string} - user-facing summary of total/skipped/failed rows.
|
|
*/
|
|
export const getRowsProcessed = ({
|
|
processed_rows: processed,
|
|
saved_rows: saved,
|
|
total_rows: total,
|
|
}) => {
|
|
const failed = processed - saved;
|
|
const skipped = total - processed;
|
|
const summaryEntries = [`${total} Students: ${saved} processed`];
|
|
if (skipped > 0) { summaryEntries.push(`${skipped} skipped`); }
|
|
if (failed > 0) { summaryEntries.push(`${failed} failed`); }
|
|
return summaryEntries.join(', ');
|
|
};
|
|
|
|
/**
|
|
* formatGradeOverrideForDisplay(historyArray)
|
|
* returns the grade override history results in display format.
|
|
* @param {object[]} historyArray - array of gradeOverrideHistoryResults
|
|
* @return {object[]} - display-formatted history results ({ date, grader, reason, adjustedGrade })
|
|
*/
|
|
export const formatGradeOverrideForDisplay = historyArray => historyArray.map(item => ({
|
|
date: formatDateForDisplay(new Date(item.history_date)),
|
|
grader: item.history_user,
|
|
reason: item.override_reason,
|
|
adjustedGrade: item.earned_graded_override,
|
|
}));
|
|
|
|
export const minGrade = '0';
|
|
export const maxGrade = '100';
|
|
|
|
/**
|
|
* formatMaxCourseGrade(percentGrade)
|
|
* Takes a percent grade and returns it unless it is equal to the max grade
|
|
* @param {string} percentGrade - grade percentage
|
|
* @return {string} percent grade or null
|
|
*/
|
|
export const formatMaxCourseGrade = (percentGrade) => (
|
|
(percentGrade === maxGrade) ? null : percentGrade
|
|
);
|
|
/**
|
|
* formatMinCourseGrade(percentGrade)
|
|
* Takes a percent grade and returns it unless it is equal to the min grade
|
|
* @param {string} percentGrade - grade percentage
|
|
* @return {string} percent grade or null
|
|
*/
|
|
export const formatMinCourseGrade = (percentGrade) => (
|
|
(percentGrade === minGrade) ? null : percentGrade
|
|
);
|
|
|
|
/**
|
|
* formatMaxAssignmentGrade(percentGrade, options)
|
|
* Takes a percent grade and returns it unless it is equal to the max grade or
|
|
* the assignment id is set.
|
|
* @param {string} percentGrade - grade percentage
|
|
* @param {object} options - options object ({ assignmentId });
|
|
* @return {string} percent grade or null
|
|
*/
|
|
export const formatMaxAssignmentGrade = (percentGrade, options) => (
|
|
(percentGrade === maxGrade || !options.assignmentId) ? null : percentGrade
|
|
);
|
|
|
|
/**
|
|
* formatMinAssignmentGrade(percentGrade, options)
|
|
* Takes a percent grade and returns it unless it is equal to the min grade or
|
|
* the assignment id is set.
|
|
* @param {string} percentGrade - grade percentage
|
|
* @param {object} options - options object ({ assignmentId });
|
|
* @return {string} percent grade or null
|
|
*/
|
|
export const formatMinAssignmentGrade = (percentGrade, options) => (
|
|
(percentGrade === minGrade || !options.assignmentId) ? null : percentGrade
|
|
);
|
|
|
|
/**
|
|
* headingMapper(category, label='All')
|
|
* Takes category and label filters and returns a method that will take a section breakdown
|
|
* and return the appropriate table headings.
|
|
* @param {string} category - assignment filter type
|
|
* @param {string} label - assignment filter label
|
|
* @return {string[]} - list of table headers
|
|
*/
|
|
export const headingMapper = (category, label = 'All') => {
|
|
const filters = {
|
|
all: section => section.label,
|
|
byCategory: section => section.label && section.category === category,
|
|
byLabel: section => section.label && section.label === label,
|
|
};
|
|
|
|
let filter;
|
|
if (label === 'All') {
|
|
filter = category === 'All' ? filters.all : filters.byCategory;
|
|
} else {
|
|
filter = filters.byLabel;
|
|
}
|
|
const {
|
|
username,
|
|
fullName,
|
|
email,
|
|
totalGrade,
|
|
} = Headings;
|
|
const filteredLabels = (entry) => entry.filter(filter).map(s => s.label);
|
|
|
|
return (entry) => (
|
|
entry
|
|
? [username, fullName, email, ...filteredLabels(entry), totalGrade]
|
|
: []
|
|
);
|
|
};
|
|
|
|
/**
|
|
* transformHistoryEntry(rawEntry)
|
|
* Takes a raw bulkManagementHistory entry and formats it for consumption
|
|
* @param {object} rawEntry - raw history entry to transform
|
|
* @return {object} - transformed history entry
|
|
* ({ timeUploaded, originalFilename, resultsSummary, ... })
|
|
*/
|
|
export const transformHistoryEntry = ({
|
|
modified,
|
|
original_filename: originalFilename,
|
|
data,
|
|
unique_id: courseId,
|
|
id,
|
|
...rest
|
|
}) => ({
|
|
timeUploaded: formatDateForDisplay(new Date(modified)),
|
|
originalFilename,
|
|
resultsSummary: {
|
|
rowId: id,
|
|
text: module.getRowsProcessed(data),
|
|
},
|
|
...rest,
|
|
});
|
|
|
|
/**
|
|
* roundGrade(val)
|
|
* Takes a number and rounds it to 2 decimal places
|
|
* defaults to 0
|
|
* @param {number=0} val - value to round.
|
|
* @return {number} - rounded number
|
|
*/
|
|
export const roundGrade = val => parseFloat((val || 0).toFixed(2));
|
|
export const subsectionGrade = StrictDict({
|
|
/**
|
|
* subsectionGrade.absolute(subsection)
|
|
* returns rounded {earned}/{possible} if attempted, else ${earned}
|
|
* @param {object} subsection - grade subsection entry
|
|
* @return {string} - absolute-formatted subsection grade string
|
|
*/
|
|
[GradeFormats.absolute]: (subsection) => {
|
|
const earned = module.roundGrade(subsection.score_earned);
|
|
const possible = module.roundGrade(subsection.score_possible);
|
|
return subsection.attempted ? `${earned}${getLocalizedSlash()}${possible}` : `${earned}`;
|
|
},
|
|
/**
|
|
* subsectionGrade.percent(subsection)
|
|
* returns rounded percent times 100
|
|
* @param {object} subsection - grade subsection entry
|
|
* @return {string} - percent-formatted subsection grade string
|
|
*/
|
|
[GradeFormats.percent]: (subsection) => (
|
|
module.roundGrade(subsection.percent * 100)
|
|
),
|
|
});
|
|
|
|
// Selectors
|
|
/**
|
|
* allGrades(state)
|
|
* returns the top-level redux grades state.
|
|
* @param {object} state - redux state
|
|
* @return {object} - redux grades state
|
|
*/
|
|
export const allGrades = ({ grades: { results } }) => results;
|
|
|
|
/**
|
|
* bulkImportError(state)
|
|
* returns the stringified bulkManagement import error messages.
|
|
* @param {object} state - redux state
|
|
* @return {string} - bulk import error messages joined into a display form
|
|
* (or empty string if there are none)
|
|
*/
|
|
export const bulkImportError = ({ grades: { bulkManagement } }) => (
|
|
(!!bulkManagement && bulkManagement.errorMessages)
|
|
? `Errors while processing: ${bulkManagement.errorMessages.join('; ')};`
|
|
: ''
|
|
);
|
|
|
|
/**
|
|
* bulkManagementHistory(state)
|
|
* returns the bulkManagement history entries from the grades state
|
|
* @param {object} state - redux state
|
|
* @return {object[]} - list of bulkManagement history entries
|
|
*/
|
|
export const bulkManagementHistory = ({ grades: { bulkManagement } }) => (
|
|
(bulkManagement.history || [])
|
|
);
|
|
|
|
/**
|
|
* bulkManagementHistoryEntries(state)
|
|
* returns transformed history entries from bulkManagement grades data.
|
|
* @param {object} state - redux state
|
|
* @return {object[]} - list of transformed bulkManagement history entries
|
|
*/
|
|
export const bulkManagementHistoryEntries = (state) => (
|
|
module.bulkManagementHistory(state).map(module.transformHistoryEntry)
|
|
);
|
|
|
|
/**
|
|
* getExampleSectionBreakdown(state)
|
|
* returns section breakdown of first grades result.
|
|
* @param {object} state - redux state
|
|
* @return {object[]} - section breakdown of first grades result.
|
|
*/
|
|
export const getExampleSectionBreakdown = ({ grades }) => (
|
|
(grades.results[0] || {}).section_breakdown || []
|
|
);
|
|
|
|
/**
|
|
* gradeOverrides(state)
|
|
* returns the gradeOverride history results
|
|
* @param {object} state - redux state
|
|
* @return {object[]} - grade override history result entries
|
|
*/
|
|
export const gradeOverrides = ({ grades }) => grades.gradeOverrideHistoryResults;
|
|
|
|
/**
|
|
* hasOverrideErrors(state)
|
|
* returns true iff gradeOverrideHistoryErrors are present
|
|
* @param {object} state - redux state
|
|
* @return {bool} - are there grade override history errors?
|
|
*/
|
|
export const hasOverrideErrors = ({ grades }) => !!grades.gradeOverrideHistoryError;
|
|
|
|
/**
|
|
* uploadSuccess(state)
|
|
* @param {object} state - redux state
|
|
* @return {bool} - is bulkManagement.uploadSuccess true?
|
|
*/
|
|
export const uploadSuccess = ({ grades: { bulkManagement } }) => (
|
|
!!bulkManagement && bulkManagement.uploadSuccess
|
|
);
|
|
|
|
const simpleSelectors = simpleSelectorFactory(
|
|
({ grades }) => grades,
|
|
[
|
|
'courseId',
|
|
'filteredUsersCount',
|
|
'totalUsersCount',
|
|
'gradeFormat',
|
|
'nextPage',
|
|
'prevPage',
|
|
'showSpinner',
|
|
'gradeOverrideCurrentEarnedGradedOverride',
|
|
'gradeOverrideHistoryError',
|
|
'gradeOriginalEarnedGraded',
|
|
'gradeOriginalPossibleGraded',
|
|
'nextPage',
|
|
'prevPage',
|
|
'showSuccess',
|
|
],
|
|
);
|
|
|
|
export default StrictDict({
|
|
bulkImportError,
|
|
formatGradeOverrideForDisplay,
|
|
formatMinAssignmentGrade,
|
|
formatMaxAssignmentGrade,
|
|
formatMaxCourseGrade,
|
|
formatMinCourseGrade,
|
|
hasOverrideErrors,
|
|
headingMapper,
|
|
|
|
roundGrade,
|
|
subsectionGrade,
|
|
|
|
...simpleSelectors,
|
|
allGrades,
|
|
bulkManagementHistoryEntries,
|
|
getExampleSectionBreakdown,
|
|
gradeOverrides,
|
|
uploadSuccess,
|
|
});
|