Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
323b382
feat: review route
hrsy Oct 13, 2021
b2cbe24
refactor: create the setCorrectness results method
hrsy Oct 13, 2021
36a0ae9
fix: PR comments
hrsy Oct 14, 2021
a52ff86
Merge pull request #364 from LambdaBird/feature/SBL-501/SBL-541/revie…
hrsy Oct 15, 2021
025f82b
fix: fix auth UI bugs
hrsy Oct 12, 2021
92c45ec
feat: show a meaningful message on HTTP 400
hrsy Oct 11, 2021
7c9789d
fix: PR comments
hrsy Oct 11, 2021
92824d0
fix: the undefined file bugfix
hrsy Oct 13, 2021
7f0ce56
feat: paragraph plugin title
hrsy Oct 14, 2021
182c847
fix: PR comments
hrsy Oct 15, 2021
b1f0c45
fix: enroll modal flash
hrsy Oct 12, 2021
71db808
fix: PR comments
hrsy Oct 12, 2021
07d92df
fix: enroll modal flash bugfix
hrsy Oct 13, 2021
3509dca
fix: PR comments
hrsy Oct 14, 2021
fc5261d
fix: make lesson learn flow work correctly
vitalikprac Oct 18, 2021
b27066b
feat: remove icon from Warning block
vitalikprac Oct 18, 2021
51d4acd
feat: add ignoring punctuation in closed question
vitalikprac Oct 18, 2021
a17e94f
fix: add border bottom to text input (Student view)
vitalikprac Oct 18, 2021
3f5867a
feat: remove space before Lesson name (LessonEdit)
vitalikprac Oct 18, 2021
3a23020
feat: add spaceing between Header and Intro block
vitalikprac Oct 18, 2021
c075023
fix: make Results fully work
vitalikprac Oct 18, 2021
7d495f9
feat: allow empty answer, add {} to hint (Fill The Gap)
vitalikprac Oct 18, 2021
4e3df41
feat: improve blocks menu (add Search input)
vitalikprac Oct 12, 2021
ce97e28
fix: input focus() after Tab after block
vitalikprac Oct 13, 2021
f036c7b
refactor: change import paths, add jsdoc to improve readability retur…
vitalikprac Oct 15, 2021
fb3c962
feat: change sarch input padding
vitalikprac Oct 18, 2021
b9ed087
feat: add showing editor js menu based on page location
vitalikprac Oct 18, 2021
c3274c0
Revert "(SBL-604) Feature: Remove space before Lesson name (LessonEdit)"
vitalikprac Oct 19, 2021
1c649c5
refactor: move lesson funnel story to statistics directory
shafua Oct 8, 2021
6b6d095
feat: BarSpark and SparkBars stat atoms
shafua Oct 12, 2021
e62b344
refactor: rename and move to atoms distribution spark (resolve spark)
shafua Oct 19, 2021
f501bde
feat: lesson funnel design improvements
shafua Oct 19, 2021
e50b7b1
fix: cohorts out of range
shafua Oct 19, 2021
64a8d47
fix: alignment of count
shafua Oct 19, 2021
3cd765c
fix: progress collection
shafua Oct 19, 2021
7a1f7a6
fix: style of distribution spark
shafua Oct 19, 2021
13dbde1
fix: keywords placeholder with localization
vitalikprac Oct 19, 2021
896e91e
fix: add to finished lesson and course View button instead of Continue
vitalikprac Oct 19, 2021
bc26d2f
fix: change space of blocks to 16px, icons - 24px
vitalikprac Oct 20, 2021
2e045c9
fix: enroll to lesson without modal and navigate to lesson (course le…
vitalikprac Oct 20, 2021
7a628fd
fix: force focus to block after 'Esc' when tools menu is open
vitalikprac Oct 20, 2021
1017a12
feat: make all 'Draft' lessons 'CourseOnly' when Publish Course
vitalikprac Oct 21, 2021
e4c486d
fix: make cursor aligned correctly (EditorJs)
vitalikprac Oct 20, 2021
6228c96
fix: remove query pagination for Courses (due to Lessons pagination n…
vitalikprac Oct 21, 2021
708cd2e
feat: remove 'Example' placeholder from Fill The Gap (StudentView)
vitalikprac Oct 19, 2021
519abc6
feat: add spacing under Video block (Student view)
vitalikprac Oct 19, 2021
4fc4ae3
feat: disable not working button and links (LessonEdit,CourseEdit)
vitalikprac Oct 19, 2021
6c92e17
fix: add missing page titles
vitalikprac Oct 20, 2021
85e779d
feat: change from course,lesson id to name
vitalikprac Oct 21, 2021
2d40705
fix: remove Warning icon plugin (editorjs)
vitalikprac Oct 25, 2021
45d324b
feat: change Editor js layout
vitalikprac Oct 26, 2021
5efa8d6
feat: add Review to Students results
vitalikprac Oct 27, 2021
7d774e2
Merge branch 'develop' into feature/SBL-501/results_review_mechanics
vitalikprac Oct 27, 2021
cfb0acb
Merge branch 'feature/SBL-501/results_review_mechanics' into feature/…
vitalikprac Oct 27, 2021
137c08d
refactor: remove unused body properties
vitalikprac Oct 28, 2021
583917c
Merge pull request #408 from LambdaBird/feature/SBL-501/SBL-540/revie…
vitalikprac Oct 28, 2021
e243280
fix: incorrect rate correctness, 0 instead of not rendering files
vitalikprac Oct 28, 2021
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
59 changes: 45 additions & 14 deletions api/seeds/testData/lessons.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,33 @@ export const russian = {
},
};

export const ukrainian = {
id: 10005,
name: 'Ukrainian',
status: 'Public',
_blocks: {
_indexesOfInteractive: [0],
_current: [
{
id: '0b7e5d54-a78c-4340-abec-ee08713d43bc',
block_id: '7142e20a-0d30-47e5-aea8-546e1ec5e395',
_revisions: [
{
content: {
data: {
question: 'Graded question',
},
},
type: 'gradedQuestion',
answer: {},
revision: '06421c44-a853-4708-8f40-81c55a0e8862',
},
],
},
].map(assingParents),
},
};

export const french = {
id: 20003,
name: 'French',
Expand Down Expand Up @@ -163,13 +190,14 @@ export const lessons = [
literature,
french,
russian,
ukrainian,
].map((lesson) => ({
id: lesson.id,
name: lesson.name,
status: lesson.status,
}));

export const lessonBlockStructure = [math, french, russian].reduce(
export const lessonBlockStructure = [math, french, russian, ukrainian].reduce(
(structure, lesson) => {
const lessonStructure = lesson._blocks._current.map((structureItem) => ({
id: structureItem.id,
Expand All @@ -184,21 +212,24 @@ export const lessonBlockStructure = [math, french, russian].reduce(
[],
);

export const blocks = [math, french, russian].reduce((blocksList, lesson) => {
const lessonBlocks = lesson._blocks._current.reduce(
(revisions, structureItem) => {
const itemBlocks = structureItem._revisions.map((block) => ({
...block,
block_id: structureItem.block_id,
}));
export const blocks = [math, french, russian, ukrainian].reduce(
(blocksList, lesson) => {
const lessonBlocks = lesson._blocks._current.reduce(
(revisions, structureItem) => {
const itemBlocks = structureItem._revisions.map((block) => ({
...block,
block_id: structureItem.block_id,
}));

return [...revisions, ...itemBlocks];
},
[],
);
return [...revisions, ...itemBlocks];
},
[],
);

return [...blocksList, ...lessonBlocks];
}, []);
return [...blocksList, ...lessonBlocks];
},
[],
);

function assingParents(structureItem, index, list) {
const parent = list[index - 1] || null;
Expand Down
1 change: 1 addition & 0 deletions api/src/config/lessonService.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ export const lessonServiceErrors = {

export const lessonServiceMessages = {
LESSON_MSG_SUCCESS_ENROLL: 'messages.success_enroll',
LESSON_MSG_SUCCESS_REVIEW: 'messages.success_review',
};
4 changes: 4 additions & 0 deletions api/src/models/Result.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ class Result extends BaseModel {
throw new BadRequestError(error);
}
}

static setCorrectness({ resultId, correctness, meta }) {
return this.query().findById(resultId).patch({ correctness, meta });
}
}

export default Result;
1 change: 1 addition & 0 deletions api/src/models/UserRole.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ class UserRole extends BaseModel {
.modifyGraph('results', (builder) => {
builder.where('lesson_id', resourceId);
builder.withGraphFetched('block');
builder.orderBy('created_at');
});
}
return query;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const options = {
schema: {
params: { $ref: 'paramsLessonId#' },
body: {
type: 'object',
properties: {
resultId: { type: 'string' },
correctness: { type: 'number' },
},
required: ['resultId', 'correctness'],
},
response: {
'4xx': { $ref: '4xx#' },
'5xx': { $ref: '5xx#' },
},
},
async onRequest(req) {
await this.auth({ req });
},
async preHandler({ user, params }) {
const { resources, roles } = this.config.globals;

await this.access({
userId: user.id,
resourceId: params.lessonId,
resourceType: resources.LESSON.name,
roleId: roles.MAINTAINER.id,
});
},
};

async function handler({ body, user }) {
const {
models: { Result },
config: {
lessonService: { lessonServiceMessages: messages },
},
} = this;

await Result.setCorrectness({
resultId: body.resultId,
correctness: body.correctness,
meta: {
reviewer: user.id,
reviewedAt: new Date().toISOString(),
},
});

return { message: messages.LESSON_MSG_SUCCESS_REVIEW };
}

export default { options, handler };
7 changes: 7 additions & 0 deletions api/src/services/lessons-management/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import getAllStudents from './controllers/getAllStudents';
import studentsOptions from './controllers/studentsOptions';
import updateStatus from './controllers/updateStatus';
import statusOptions from './controllers/statusOptions';
import reviewStudentReply from './controllers/reviewStudentReply';

export async function router(instance) {
instance.get('/lessons', getLessons.options, getLessons.handler);
Expand Down Expand Up @@ -63,4 +64,10 @@ export async function router(instance) {
studentsOptions.options,
studentsOptions.handler,
);

instance.post(
'/review/:lessonId',
reviewStudentReply.options,
reviewStudentReply.handler,
);
}
97 changes: 93 additions & 4 deletions api/test/integration/lessonsManagementService.spec.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
/* eslint-disable no-underscore-dangle */
import { v4 } from 'uuid';

import build from '../../src/app';

import {
teacherMike,
teacherNathan,
defaultPassword,
studentJohn,
teacherMike,
teacherNathan,
} from '../../seeds/testData/users';
import { french } from '../../seeds/testData/lessons';
import { french, ukrainian } from '../../seeds/testData/lessons';

import { authorizeUser, createLesson, prepareLessonFromSeed } from './utils';

import { userServiceErrors as errors } from '../../src/config';
import {
lessonServiceMessages,
userServiceErrors as errors,
} from '../../src/config';

describe('Maintainer flow', () => {
const testContext = {};
Expand Down Expand Up @@ -532,4 +536,89 @@ describe('Maintainer flow', () => {
expect(payload.total).toBe(0);
});
});

describe('Result review mechanics', () => {
let lessonWithGradedQuestion;
let gradedQuestionResult;
const CORRECTNESS = 0.4;

beforeAll(async () => {
lessonWithGradedQuestion = await createLesson({
app: testContext.app,
credentials: teacherCredentials,
body: prepareLessonFromSeed(ukrainian),
});

await testContext.studentRequest({
url: `lessons/${lessonWithGradedQuestion.lesson.id}/enroll`,
});

await testContext.studentRequest({
url: `learn/lessons/${lessonWithGradedQuestion.lesson.id}/reply`,
body: {
action: 'start',
},
});

await testContext.studentRequest({
url: `learn/lessons/${lessonWithGradedQuestion.lesson.id}/reply`,
body: {
action: 'response',
blockId:
lessonWithGradedQuestion.lesson.blocks[
ukrainian._blocks._indexesOfInteractive[0]
].blockId,
revision:
lessonWithGradedQuestion.lesson.blocks[
ukrainian._blocks._indexesOfInteractive[0]
].revision,
reply: {
files: [],
value: 'answer',
},
},
});

const response = await testContext.request({
method: 'GET',
url: `lessons/${lessonWithGradedQuestion.lesson.id}/students`,
});

const [student] = JSON.parse(response.payload).students;
[, gradedQuestionResult] = student.results;
});

it('correctness should be null after student`s response', () => {
expect(gradedQuestionResult.correctness).toBe(null);
});

it('should successfully set correctness', async () => {
const response = await testContext.request({
url: `review/${lessonWithGradedQuestion.lesson.id}`,
body: {
resultId: gradedQuestionResult.id,
correctness: CORRECTNESS,
},
});

const payload = JSON.parse(response.payload);

expect(response.statusCode).toBe(200);
expect(payload).toMatchObject({
message: lessonServiceMessages.LESSON_MSG_SUCCESS_REVIEW,
});
});

it('correctness should be changed successfully', async () => {
const response = await testContext.request({
method: 'GET',
url: `lessons/${lessonWithGradedQuestion.lesson.id}/students`,
});

const [student] = JSON.parse(response.payload).students;
const [, newResult] = student.results;

expect(newResult.correctness).toBe(CORRECTNESS);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import T from 'prop-types';

import LearnContext from '@sb-ui/contexts/LearnContext';
import ResultItem from '@sb-ui/pages/Teacher/LessonStudents/LessonResults/ResultItem';
import { BLOCKS_TYPE_LIST_RATED } from '@sb-ui/pages/Teacher/LessonStudents/LessonResults/ResultItem/constants';
import { useBlockIcons } from '@sb-ui/pages/Teacher/LessonStudents/LessonResults/useBlockIcons';
import BlockElement from '@sb-ui/pages/User/LearnPage/BlockElement';
import { LearnWrapper } from '@sb-ui/pages/User/LearnPage/LearnPage.styled';
Expand All @@ -14,37 +15,44 @@ const InteractiveResults = ({ interactiveResults }) => {
return (
<LearnContext.Provider value={{ handleInteractiveClick: () => {} }}>
<S.Collapse>
{interactiveResults.map(({ block, correctness, time, data }) => {
const isResult = !!correctness || correctness === 0;
return (
<S.Panel
key={block.blockId}
$isResult={isResult}
header={
<ResultItem
showCircle={isResult}
icons={blockIcons}
block={block}
correctness={correctness}
time={time}
/>
}
>
{isResult && (
<LearnWrapper>
<BlockElement
element={{
blockId: block.id,
...block,
reply: data,
isSolved: true,
}}
{interactiveResults.map(
({ block, correctness, time, data, lessonId, id }) => {
const isResult =
!!correctness ||
correctness === 0 ||
BLOCKS_TYPE_LIST_RATED.includes(block?.type);
return (
<S.Panel
key={block?.blockId}
$isResult={isResult}
header={
<ResultItem
id={id}
showCircle={isResult}
icons={blockIcons}
block={block}
correctness={correctness}
time={time}
lessonId={lessonId}
/>
</LearnWrapper>
)}
</S.Panel>
);
})}
}
>
{isResult && (
<LearnWrapper>
<BlockElement
element={{
blockId: block?.blockId,
...block,
reply: data,
isSolved: true,
}}
/>
</LearnWrapper>
)}
</S.Panel>
);
},
)}
</S.Collapse>
</LearnContext.Provider>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ export const Panel = styled(PanelAntd).attrs({
.ant-collapse-header {
pointer-events: ${(props) => (props.$isResult ? 'auto' : 'none')};
}
.ant-collapse-content {
pointer-events: all;
}
pointer-events: none;
padding: 0.5rem 1rem;
`;
Loading