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

Issue 1321 add teacher solution tab #1333

Merged
merged 5 commits into from Aug 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
61 changes: 41 additions & 20 deletions app/Helpers/ExerciseHelper.php
Expand Up @@ -36,46 +36,67 @@ public static function getExercise(string $path): Exercise
return $exercise;
}

public static function getExerciseTests(Exercise $exercise): string
public static function exerciseHasTests(Exercise $exercise): bool
{
$underscoredExercisePath = $exercise->present()->underscorePath;

$path = implode(DIRECTORY_SEPARATOR, [
resource_path(),
'views',
'exercise',
'solution_stub',
sprintf('%s.blade.php', $underscoredExercisePath),
]);
return self::isPathExist($exercise, '.blade.php');
}

$fileContent = File::get($path);
public static function getExerciseTests(Exercise $exercise): string
{
$fileContent = self::getFileContent($exercise, '.blade.php');
[, $testsLines] = explode(';;; END', $fileContent);

return trim($testsLines);
}

public static function exerciseHasTests(Exercise $exercise): bool
public static function exerciseHasTeacherSolution(Exercise $exercise): bool
{
$underscoredExercisePath = self::underscoreExercisePath($exercise->path);
return self::isPathExist($exercise, '_solution.blade.php');
}

public static function getExerciseTeacherSolution(Exercise $exercise): string
{
$fileContent = self::getFileContent($exercise, '_solution.blade.php');

return trim($fileContent);
}

public static function underscoreExercisePath(string $path): string
{
return str_replace('.', '_', $path);
}

public static function getFullExerciseTitle(Exercise $exercise): string
{
return $exercise->present()->fullTitle;
}

private static function getPathExercise(string $underscoredExercisePath, string $file_name): string
{
$path = implode(DIRECTORY_SEPARATOR, [
resource_path(),
'views',
'exercise',
'solution_stub',
sprintf('%s.blade.php', $underscoredExercisePath),
sprintf('%s%s', $underscoredExercisePath, $file_name),
]);

return File::exists($path);
return $path;
}

public static function underscoreExercisePath(string $path): string
private static function isPathExist(Exercise $exercise, string $file_name): bool
{
return str_replace('.', '_', $path);
$underscoredExercisePath = self::underscoreExercisePath($exercise->path);
$path = self::getPathExercise($underscoredExercisePath, $file_name);

return File::exists($path);
}

public static function getFullExerciseTitle(Exercise $exercise): string
private static function getFileContent(Exercise $exercise, string $file_name): string
{
return $exercise->present()->fullTitle;
$underscoredExercisePath = $exercise->present()->underscorePath;
$path = self::getPathExercise($underscoredExercisePath, $file_name);
$fileContent = File::get($path);

return $fileContent;
}
}
6 changes: 6 additions & 0 deletions app/Http/Controllers/Api/ExerciseController.php
Expand Up @@ -18,6 +18,10 @@ public function show(Exercise $exercise): Response
$originalCode = $hasTests
? view(ExerciseHelper::getExerciseListingViewFilepath($exercise))->render()
: '';
$hasTeacherSolution = ExerciseHelper::exerciseHasTeacherSolution($exercise);
$teacherSolutionCode = $hasTeacherSolution
? ExerciseHelper::getExerciseTeacherSolution($exercise)
: '';

return response([
'exercise' => [
Expand All @@ -26,6 +30,8 @@ public function show(Exercise $exercise): Response
'original_code' => $originalCode,
'test_code' => $testCode,
'has_tests' => $hasTests,
'has_teacher_solution' => $hasTeacherSolution,
'teacher_solution_code' => $teacherSolutionCode,
],
]);
}
Expand Down
1 change: 1 addition & 0 deletions resources/js/common/tabNameKeysMap.js
Expand Up @@ -2,4 +2,5 @@ export default {
editorKey: 'editor',
outputKey: 'output',
testForExerciseKey: 'testForExercise',
teacherSolutionKey: 'teacherSolution',
};
1 change: 1 addition & 0 deletions resources/js/common/tabNamesMap.js
Expand Up @@ -2,4 +2,5 @@ export default {
editor: 'editor',
output: 'output',
tests: 'tests',
teacherSolution: 'teacherSolution',
};
4 changes: 4 additions & 0 deletions resources/js/components/TabsBox.jsx
Expand Up @@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next';
import Editor from './Editor.jsx';
import Output from './Output.jsx';
import Tests from './Tests.jsx';
import TeacherSolution from './TeacherSolution.jsx';
dzencot marked this conversation as resolved.
Show resolved Hide resolved
import tabNames from '../common/tabNamesMap.js';
import { changeTab } from '../slices/tabsBoxSlice.js';
import locationMap from '../common/hashLocationMap.js';
Expand Down Expand Up @@ -51,6 +52,9 @@ const TabsBox = () => {
<Tab.Pane eventKey={tabNames.tests} bsPrefix="tab-pane h-100 w-100">
<Tests />
</Tab.Pane>
<Tab.Pane eventKey={tabNames.teacherSolution} bsPrefix="tab-pane h-100 w-100">
<TeacherSolution />
</Tab.Pane>
</Tab.Content>
</div>
</Tab.Container>
Expand Down
18 changes: 18 additions & 0 deletions resources/js/components/TeacherSolution.jsx
@@ -0,0 +1,18 @@
import React from 'react';
import { useSelector } from 'react-redux';
import Highlight from 'react-syntax-highlighter';
import { vs } from 'react-syntax-highlighter/dist/esm/styles/hljs';

const TeacherSolution = () => {
const { hasTeacherSolution, teacherSolutionCode } = useSelector((state) => state.exerciseInfo);

return hasTeacherSolution ? (
<div className="d-flex h-100">
<Highlight className="h-100" language="scheme" style={vs} showLineNumbers>
{teacherSolutionCode}
</Highlight>
</div>
) : null;
};

export default TeacherSolution;
1 change: 1 addition & 0 deletions resources/js/locales/en.js
Expand Up @@ -6,6 +6,7 @@ export default {
output: 'Output',
tests: 'Tests',
loading: 'Loading...',
teacherSolution: 'Solution',
editorContent: {
withTemplate: 'Write your solution here',
withoutTemplate: 'This exercise has no tests.\nAny solution is a right answer.',
Expand Down
1 change: 1 addition & 0 deletions resources/js/locales/ru.js
Expand Up @@ -6,6 +6,7 @@ export default {
output: 'Вывод',
tests: 'Тесты',
loading: 'Загрузка...',
teacherSolution: 'Решение',
editorContent: {
withTemplate: 'Введите свое решение',
withoutTemplate: 'Для этого упражнения нет проверок.\nЛюбое решение будет считаться успешным ответом.',
Expand Down
9 changes: 8 additions & 1 deletion resources/js/slices/exerciseInfoSlice.js
Expand Up @@ -11,11 +11,16 @@ const loadExerciseInfo = createAsyncThunk(
const url = routes.exerciseInfoPath(exerciseId);
const response = await axios.get(url);
/* eslint-disable camelcase */
const { prepared_code, test_code, has_tests } = response.data.exercise;
const {
prepared_code, test_code, has_tests,
teacher_solution_code, has_teacher_solution,
} = response.data.exercise;
return {
preparedCode: prepared_code,
hasTests: has_tests,
testCode: test_code,
hasTeacherSolution: has_teacher_solution,
teacherSolutionCode: teacher_solution_code,
};
/* eslint-enable camelcase */
},
Expand All @@ -28,6 +33,8 @@ const slice = createSlice({
preparedCode: '',
hasTests: false,
testCode: null,
hasTeacherSolution: false,
teacherSolutionCode: null,
},
reducers: {},
extraReducers: (builder) => {
Expand Down