Skip to content

Commit

Permalink
Merge pull request #4059 from MisRob/fix-missing-completion
Browse files Browse the repository at this point in the history
Add completion criteria to the side panel
  • Loading branch information
marcellamaki authored May 8, 2023
2 parents 32847f3 + d5c41c6 commit 1797ae1
Show file tree
Hide file tree
Showing 6 changed files with 443 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import each from 'jest-each';
import {
floatOrIntRegex,
getCorrectAnswersIndices,
mapCorrectAnswers,
updateAnswersToQuestionType,
isImportedContent,
importedChannelLink,
secondsToHms,
getCompletionCriteriaLabels,
} from '../utils';
import router from '../router';
import { RouteNames } from '../constants';
import { AssessmentItemTypes } from 'shared/constants';
import { MasteryModelsNames } from 'shared/leUtils/MasteryModels';
import { AssessmentItemTypes, CompletionCriteriaModels } from 'shared/constants';

describe('channelEdit utils', () => {
describe('imported content', () => {
Expand Down Expand Up @@ -451,4 +455,221 @@ describe('channelEdit utils', () => {
].forEach(v => expect(floatOrIntRegex.test(v)).toBe(false));
});
});

describe(`secondsToHms`, () => {
it(`converts 0 seconds to '00:00'`, () => {
expect(secondsToHms(0)).toBe('00:00');
});

it(`converts seconds to 'mm:ss' when it's less than one hour`, () => {
expect(secondsToHms(3599)).toBe('59:59');
});

it(`converts seconds to 'hh:mm:ss' when it's exactly one hour`, () => {
expect(secondsToHms(3600)).toBe('01:00:00');
});

it(`converts seconds to 'hh:mm:ss' when it's more than one hour`, () => {
expect(secondsToHms(7323)).toBe('02:02:03');
});
});

describe(`getCompletionCriteriaLabels`, () => {
describe(`for 'reference' completion criteria`, () => {
it(`returns 'Reference material' completion label and empty duration label`, () => {
expect(
getCompletionCriteriaLabels({
extra_fields: {
options: {
completion_criteria: {
model: CompletionCriteriaModels.REFERENCE,
},
},
},
})
).toEqual({
completion: 'Reference material',
duration: '-',
});
});
});

describe(`for 'time' completion criteria`, () => {
it(`returns 'When time spent is equal to duration' completion label and human-readable duration label`, () => {
expect(
getCompletionCriteriaLabels({
extra_fields: {
options: {
completion_criteria: {
model: CompletionCriteriaModels.TIME,
},
},
},
suggested_duration: 3820,
})
).toEqual({
completion: 'When time spent is equal to duration',
duration: '01:03:40',
});
});
});

describe(`for 'approximate time' completion criteria`, () => {
it(`returns 'When time spent is equal to duration' completion label`, () => {
expect(
getCompletionCriteriaLabels({
extra_fields: {
options: {
completion_criteria: {
model: CompletionCriteriaModels.APPROX_TIME,
},
},
},
suggested_duration: 1859,
}).completion
).toBe('When time spent is equal to duration');
});

it(`returns 'Short activity' duration label for a short activity`, () => {
expect(
getCompletionCriteriaLabels({
extra_fields: {
options: {
completion_criteria: {
model: CompletionCriteriaModels.APPROX_TIME,
},
},
},
suggested_duration: 1860,
}).duration
).toBe('Short activity');
});

it(`returns 'Long activity' duration label for a long activity`, () => {
expect(
getCompletionCriteriaLabels({
extra_fields: {
options: {
completion_criteria: {
model: CompletionCriteriaModels.APPROX_TIME,
},
},
},
suggested_duration: 1861,
}).duration
).toBe('Long activity');
});
});

describe(`for 'pages' completion criteria`, () => {
it(`returns 'Viewed in its entirety' completion label and empty duration label`, () => {
expect(
getCompletionCriteriaLabels({
extra_fields: {
options: {
completion_criteria: {
model: CompletionCriteriaModels.PAGES,
threshold: '100%',
},
},
},
})
).toEqual({
completion: 'Viewed in its entirety',
duration: '-',
});
});
});

describe(`for 'determined by resource' completion criteria`, () => {
it(`returns 'Determined by the resource' completion label and empty duration label`, () => {
expect(
getCompletionCriteriaLabels({
extra_fields: {
options: {
completion_criteria: {
model: CompletionCriteriaModels.DETERMINED_BY_RESOURCE,
},
},
},
})
).toEqual({
completion: 'Determined by the resource',
duration: '-',
});
});
});

describe(`for 'mastery' completion criteria`, () => {
it(`returns 'Goal: m out of n' completion label and empty duration label for 'm of n' mastery`, () => {
expect(
getCompletionCriteriaLabels({
extra_fields: {
options: {
completion_criteria: {
model: CompletionCriteriaModels.MASTERY,
threshold: {
mastery_model: MasteryModelsNames.M_OF_N,
m: 4,
n: 5,
},
},
},
},
})
).toEqual({
completion: 'Goal: 4 out of 5',
duration: '-',
});
});

it(`returns 'Goal: 100% correct' completion label and empty duration label for 'do all' mastery`, () => {
expect(
getCompletionCriteriaLabels({
extra_fields: {
options: {
completion_criteria: {
model: CompletionCriteriaModels.MASTERY,
threshold: {
mastery_model: MasteryModelsNames.DO_ALL,
},
},
},
},
})
).toEqual({
completion: 'Goal: 100% correct',
duration: '-',
});
});

each([
[2, MasteryModelsNames.NUM_CORRECT_IN_A_ROW_2],
[3, MasteryModelsNames.NUM_CORRECT_IN_A_ROW_3],
[5, MasteryModelsNames.NUM_CORRECT_IN_A_ROW_5],
[10, MasteryModelsNames.NUM_CORRECT_IN_A_ROW_10],
]).it(
`returns 'Goal: %s in a row' completion label and empty duration label for '%s' mastery`,
(num, mastery_model) => {
expect(
getCompletionCriteriaLabels({
extra_fields: {
options: {
completion_criteria: {
model: CompletionCriteriaModels.MASTERY,
threshold: {
mastery_model,
},
},
},
},
})
).toEqual({
completion: `Goal: ${num} in a row`,
duration: '-',
});
}
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -138,15 +138,21 @@
showEachActivityIcon
/>
</DetailsRow>
<DetailsRow v-if="isExercise" :label="$tr('completion')">
<span v-if="noMasteryModel" class="red--text">

<DetailsRow :label="translateMetadataString('completion')">
<span v-if="isExercise && noMasteryModel" class="red--text">
<Icon color="red" small>error</Icon>
<span class="mx-1">{{ $tr('noMasteryModelError') }}</span>
</span>
<span v-else>
{{ masteryCriteria }}
{{ completion }}
</span>
</DetailsRow>

<DetailsRow :label="translateMetadataString('duration')">
{{ duration }}
</DetailsRow>

<DetailsRow
v-if="!isTopic"
:label="translateMetadataString('category')"
Expand Down Expand Up @@ -332,11 +338,12 @@
import sortBy from 'lodash/sortBy';
import { mapActions, mapGetters } from 'vuex';
import camelCase from 'lodash/camelCase';
import { isImportedContent, importedChannelLink } from '../utils';
import { isImportedContent, importedChannelLink, getCompletionCriteriaLabels } from '../utils';
import FilePreview from '../views/files/FilePreview';
import { ContentLevels, Categories, AccessibilityCategories } from '../../shared/constants';
import AssessmentItemPreview from './AssessmentItemPreview/AssessmentItemPreview';
import ContentNodeValidator from './ContentNodeValidator';
import {
getAssessmentItemErrors,
getNodeLicenseErrors,
Expand All @@ -360,7 +367,6 @@
titleMixin,
metadataTranslationMixin,
} from 'shared/mixins';
import { MasteryModelsNames } from 'shared/leUtils/MasteryModels';
import { ContentKindsNames } from 'shared/leUtils/ContentKinds';
export default {
Expand Down Expand Up @@ -405,6 +411,12 @@
node() {
return this.getContentNode(this.nodeId);
},
completion() {
return getCompletionCriteriaLabels(this.node).completion;
},
duration() {
return getCompletionCriteriaLabels(this.node).duration;
},
files() {
return sortBy(this.getContentNodeFiles(this.nodeId), f => f.preset.order);
},
Expand Down Expand Up @@ -459,19 +471,6 @@
importedChannelName() {
return this.node.original_channel_name;
},
masteryCriteria() {
if (!this.isExercise) {
return '';
}
const masteryModel = this.node.extra_fields.mastery_model;
if (!masteryModel) {
return this.defaultText;
} else if (masteryModel === MasteryModelsNames.M_OF_N) {
return this.$tr('masteryMofN', this.node.extra_fields);
}
return this.translateConstant(masteryModel);
},
sortedTags() {
return orderBy(this.node.tags, ['count'], ['desc']);
},
Expand Down Expand Up @@ -663,9 +662,7 @@
},
$trs: {
questions: 'Questions',
masteryMofN: 'Goal: {m} out of {n}',
details: 'Details',
completion: 'Completion',
showAnswers: 'Show answers',
questionCount: '{value, number, integer} {value, plural, one {question} other {questions}}',
description: 'Description',
Expand All @@ -692,6 +689,11 @@
fileSize: 'Size',
// Validation strings
/* eslint-disable kolibri/vue-no-unused-translations */
noLearningActivityError: 'Missing learning activity',
noCompletionCriteriaError: 'Missing completion criteria',
noDurationError: 'Missing duration',
/* eslint-enable kolibri/vue-no-unused-translations */
noLicenseError: 'Missing license',
noCopyrightHolderError: 'Missing copyright holder',
noLicenseDescriptionError: 'Missing license description',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
CompletionDropdownMap,
DurationDropdownMap,
nonUniqueValue,
SHORT_LONG_ACTIVITY_MIDPOINT,
} from 'shared/constants';
import Checkbox from 'shared/views/form/Checkbox';
import { MasteryModelsNames } from 'shared/leUtils/MasteryModels';
Expand All @@ -123,7 +124,6 @@
const DEFAULT_SHORT_ACTIVITY = 600;
const DEFAULT_LONG_ACTIVITY = 3000;
const SHORT_LONG_ACTIVITY_MIDPOINT = 1860;
const defaultCompletionCriteriaModels = {
[ContentKindsNames.VIDEO]: CompletionCriteriaModels.TIME,
Expand Down Expand Up @@ -393,7 +393,7 @@
showCorrectCompletionOptions() {
if (this.kind) {
return CompletionOptionsDropdownMap[this.kind].map(model => ({
text: this.$tr(model),
text: this.translateMetadataString(model),
value: CompletionDropdownMap[model],
}));
}
Expand All @@ -402,7 +402,7 @@
selectableDurationOptions() {
return [
{
text: this.$tr(DurationDropdownMap.EXACT_TIME),
text: this.translateMetadataString(DurationDropdownMap.EXACT_TIME),
value: 'exactTime',
},
{
Expand Down Expand Up @@ -514,15 +514,6 @@
},
},
$trs: {
/* eslint-disable kolibri/vue-no-unused-translations */
allContent: 'Viewed in its entirety',
completeDuration: 'When time spent is equal to duration',
determinedByResource: 'Determined by the resource',
goal: 'When goal is met',
practiceQuiz: 'Practice quiz',
reference: 'Reference material',
/* eslint-enable */
exactTime: 'Time to complete',
referenceHint:
'Progress will not be tracked on reference material unless learners mark it as complete',
learnersCanMarkComplete: 'Allow learners to mark as complete',
Expand Down
Loading

0 comments on commit 1797ae1

Please sign in to comment.