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

Dog course and quizzes fixes #1262

Merged
merged 6 commits into from
Apr 11, 2024
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@ const SelectCourseLanguage: React.FC<React.PropsWithChildren<CourseTranslationsL
queryKey: [formatLanguageVersionsQueryKey(currentCourseId ?? ""), currentCourseId],
queryFn: () => fetchCourseLanguageVersions(currentCourseId ?? ""),
})
const courseVersionsList = useCourseLanguageVersionsList.data?.filter(
(course) => !course.is_draft,
)
const courseVersionsList = useCourseLanguageVersionsList.data

const langCode = courseVersionsList?.find(
(course) => course.id === selectedLangCourseId,
Expand Down

This file was deleted.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

52 changes: 21 additions & 31 deletions services/headless-lms/models/src/exercise_task_gradings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -552,37 +552,27 @@ pub async fn get_user_exercise_task_gradings_by_module_and_exercise_type(
let res: Vec<CustomViewExerciseTaskGrading> = sqlx::query_as!(
CustomViewExerciseTaskGrading,
r#"
SELECT id,
created_at,
exercise_id,
exercise_task_id,
feedback_json,
feedback_text
FROM exercise_task_gradings g
WHERE deleted_at IS NULL
AND g.exercise_task_id IN (
SELECT distinct (t.id)
FROM exercise_tasks t
WHERE t.exercise_type = $2
AND deleted_at IS NULL
AND t.exercise_slide_id IN (
SELECT s.exercise_slide_id
FROM exercise_slide_submissions s
WHERE s.user_id = $1
AND s.course_instance_id = $4
AND deleted_at IS NULL
AND s.exercise_id IN (
SELECT id
FROM exercises e
WHERE e.chapter_id IN (
SELECT id
FROM chapters c
WHERE c.course_module_id = $3
AND deleted_at IS NULL
)
)
)
)
SELECT etg.id,
etg.created_at,
etg.exercise_id,
etg.exercise_task_id,
etg.feedback_json,
etg.feedback_text
FROM exercise_task_gradings etg
JOIN exercise_tasks et ON etg.exercise_task_id = et.id
JOIN exercise_task_submissions ets ON etg.exercise_task_submission_id = ets.id
JOIN exercise_slide_submissions ess ON ets.exercise_slide_submission_id = ess.id
JOIN exercises e ON ess.exercise_id = e.id
JOIN chapters c ON e.chapter_id = c.id
WHERE etg.deleted_at IS NULL
AND et.deleted_at IS NULL
AND et.exercise_type = $2
AND ess.user_id = $1
AND ess.course_instance_id = $4
AND ess.deleted_at IS NULL
AND e.deleted_at IS NULL
AND c.deleted_at IS NULL
AND c.course_module_id = $3
"#,
user_id,
exercise_type,
Expand Down
32 changes: 32 additions & 0 deletions services/headless-lms/models/src/library/copying.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ WHERE id = $2;

copy_peer_review_configs(&mut tx, copied_course.id, course_id).await?;

copy_material_references(&mut tx, copied_course.id, course_id).await?;

tx.commit().await?;
Ok(copied_course)
}
Expand Down Expand Up @@ -779,6 +781,36 @@ AND deleted_at IS NULL;
Ok(())
}

async fn copy_material_references(
tx: &mut PgConnection,
namespace_id: Uuid,
parent_id: Uuid,
) -> ModelResult<()> {
// Copy material references
sqlx::query!(
"
INSERT INTO material_references (
citation_key,
course_id,
id,
reference
)
SELECT citation_key,
$1,
uuid_generate_v5($1, id::text),
reference
FROM material_references
WHERE course_id = $2
AND deleted_at IS NULL;
",
namespace_id,
parent_id,
)
.execute(&mut *tx)
.await?;
Ok(())
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -679,7 +679,7 @@ async fn get_public_top_level_pages(
}

/**
GET `/api/v0/course-material/courses/:id/language-versions` - Returns all language versions of the same course. Since this is for course material, this does not include draft courses.
GET `/api/v0/course-material/courses/:id/language-versions` - Returns all language versions of the same course. Since this is for course material, this does not include draft courses. To make developing new courses easier, we include draft courses if the course the request for is a draft course and the teacher has a permission to access it.
*/
#[instrument(skip(pool))]
async fn get_all_course_language_versions(
Expand All @@ -691,22 +691,26 @@ async fn get_all_course_language_versions(
let token = skip_authorize();
let course = models::courses::get_course(&mut conn, *course_id).await?;

let mut language_versions =
models::courses::get_all_language_versions_of_course(&mut conn, &course)
.await?
.into_iter()
.filter(|c| !c.is_draft)
.collect::<Vec<_>>();
let unfiltered_language_versions =
models::courses::get_all_language_versions_of_course(&mut conn, &course).await?;

let language_versions = unfiltered_language_versions
.clone()
.into_iter()
.filter(|c| !c.is_draft)
.collect::<Vec<_>>();

if !language_versions.iter().any(|c| c.id == course.id) {
// The course the language version was requested for is likely a draft course.
if let Some(user_id) = user.map(|u| u.id) {
authorize_access_to_course_material(&mut conn, Some(user_id), *course_id).await?;
let access_draft_course_token =
authorize_access_to_course_material(&mut conn, Some(user_id), *course_id).await?;
info!(
"Course {} not found in language versions of course {}. Adding it back to the list.",
course.id, course.id
"Course {} the language version was requested for is a draft course. Including all draft courses in the response.",
course.id,
);
language_versions.push(course);
return access_draft_course_token
.authorized_ok(web::Json(unfiltered_language_versions));
} else {
return Err(ControllerError::new(
ControllerErrorType::Unauthorized,
Expand Down
29 changes: 25 additions & 4 deletions services/quizzes/src/components/ParsedText/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import dynamic from "next/dynamic"
import React from "react"
import React, { useMemo } from "react"
import "katex/dist/katex.min.css"

export interface TextNodeProps {
Expand All @@ -21,6 +21,7 @@ interface ParsedTextProps {
parseLatex?: boolean
parseMarkdown?: boolean
inline?: boolean
addDotToEnd?: boolean
}

const ParsedText: React.FC<ParsedTextProps> = ({
Expand All @@ -29,15 +30,35 @@ const ParsedText: React.FC<ParsedTextProps> = ({
parseLatex = false,
parseMarkdown = false,
inline = false,
addDotToEnd = false,
}) => {
if (text === null) {
const withDotIfNeeded = useMemo(() => {
if (text === null || text === undefined) {
return null
}
if (!addDotToEnd) {
return text
}
const trimmedText = text.trim()
if (
!trimmedText.endsWith(".") &&
!trimmedText.endsWith("!") &&
!trimmedText.endsWith("?") &&
!trimmedText.endsWith("]")
) {
return trimmedText + "."
}
return text
}, [addDotToEnd, text])
if (withDotIfNeeded === null) {
return null
}
if (errorText && !isValidText(parseLatex, parseMarkdown, text)) {

if (errorText && !isValidText(parseLatex, parseMarkdown, withDotIfNeeded)) {
return <div>{errorText}</div>
}

const parsedText = formatText(parseLatex, parseMarkdown, text, inline)
const parsedText = formatText(parseLatex, parseMarkdown, withDotIfNeeded, inline)

return <TextNode inline={inline} text={parsedText} />
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ const RowSubmissionFeedback: React.FC<React.PropsWithChildren<RowSubmissionFeedb
padding: 0.5rem 0px 0.5rem 0.5rem;
`}
>
<p>{feedback}</p>
<ParsedText inline parseLatex parseMarkdown addDotToEnd text={feedback} />
</div>
) : null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,17 +153,9 @@ const SubmissionFeedback: React.FC<{
const customItemFeedback = useMemo(() => {
const customItemFeedback = itemFeedback.quiz_item_feedback?.trim()
// If feedback on model solution is defined, this feedback takes precedence as the user is allowed to see the model solution and the teacher wants to show a custom message on the model solution
let messageOnModelSolution = itemModelSolution?.messageOnModelSolution ?? null
const messageOnModelSolution = itemModelSolution?.messageOnModelSolution ?? null
if (messageOnModelSolution !== null && messageOnModelSolution.trim() !== "") {
messageOnModelSolution = messageOnModelSolution.trim()
if (
!messageOnModelSolution?.endsWith(".") &&
!messageOnModelSolution?.endsWith("!") &&
!messageOnModelSolution?.endsWith("?")
) {
return messageOnModelSolution + "."
}
return messageOnModelSolution
return messageOnModelSolution.trim()
}
if (
customItemFeedback === "" ||
Expand All @@ -172,14 +164,6 @@ const SubmissionFeedback: React.FC<{
) {
return null
}
if (
!customItemFeedback?.endsWith(".") &&
!customItemFeedback?.endsWith("!") &&
!customItemFeedback?.endsWith("?") &&
!customItemFeedback?.endsWith("]")
) {
return customItemFeedback + "."
}
return customItemFeedback
}, [itemFeedback.quiz_item_feedback, itemModelSolution?.messageOnModelSolution])

Expand All @@ -203,7 +187,7 @@ const SubmissionFeedback: React.FC<{
<BullhornMegaphone size={20} weight="bold" color="7A3F75" />{" "}
<span>
{mapScoreToFeedback(userScore)}{" "}
<ParsedText inline parseLatex parseMarkdown text={customItemFeedback} />
<ParsedText inline parseLatex parseMarkdown addDotToEnd text={customItemFeedback} />
</span>
</div>
)
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ test("multiple-choice course material column test", async ({ page, headless }, t
testInfo,
snapshotName: "course-material-multiple-choice-after-success-click-column-single",
waitForTheseToBeVisibleAndStable: [
frame.locator(`text="Correct! This is indeed the first answer"`),
frame.locator(`text="Correct! This is indeed the first answer."`),
],
screenshotTarget: frame,
clearNotifications: true,
Expand All @@ -83,9 +83,7 @@ test("multiple-choice course material column test", async ({ page, headless }, t
headless,
testInfo,
snapshotName: "course-material-multiple-choice-after-failure-click-column-single",
waitForTheseToBeVisibleAndStable: [
frame.locator(`text="Incorrect. This is not the first answer"`),
],
waitForTheseToBeVisibleAndStable: [frame.getByText(`Incorrect. This is not the first answer.`)],

screenshotTarget: frame,
clearNotifications: true,
Expand Down
Loading
Loading