Skip to content

Commit

Permalink
Little improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
Maija Y committed Mar 18, 2024
1 parent d682352 commit b9029c8
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 64 deletions.
46 changes: 32 additions & 14 deletions services/headless-lms/models/src/exams.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,15 +184,7 @@ RETURNING id
Ok(res.id)
}

pub async fn edit(
conn: &mut PgConnection,
id: Uuid,
name: Option<&str>,
starts_at: Option<DateTime<Utc>>,
ends_at: Option<DateTime<Utc>>,
time_minutes: Option<i32>,
minimum_points_treshold: Option<i32>,
) -> ModelResult<()> {
pub async fn edit(conn: &mut PgConnection, id: Uuid, new_exam: NewExam) -> ModelResult<()> {
sqlx::query!(
"
UPDATE exams
Expand All @@ -204,11 +196,11 @@ SET name = COALESCE($2, name),
WHERE id = $1
",
id,
name,
starts_at,
ends_at,
time_minutes,
minimum_points_treshold,
new_exam.name,
new_exam.starts_at,
new_exam.ends_at,
new_exam.time_minutes,
new_exam.minimum_points_treshold,
)
.execute(conn)
.await?;
Expand Down Expand Up @@ -241,6 +233,32 @@ WHERE exams.organization_id = $1
Ok(res)
}

pub async fn get_organization_exam_with_exam_id(
conn: &mut PgConnection,
exam_id: Uuid,
) -> ModelResult<OrgExam> {
let res = sqlx::query_as!(
OrgExam,
"
SELECT id,
name,
instructions,
starts_at,
ends_at,
time_minutes,
organization_id,
minimum_points_treshold
FROM exams
WHERE exams.id = $1
AND exams.deleted_at IS NULL
",
exam_id
)
.fetch_one(conn)
.await?;
Ok(res)
}

pub async fn get_course_exams_for_organization(
conn: &mut PgConnection,
organization: Uuid,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,10 @@ pub async fn reset_exam_progress(
let started_at = Utc::now();
exams::update_exam_start_time(&mut conn, *exam_id, user.id, started_at).await?;

crate::prelude::models::exercise_slide_submissions::delete_exercise_submissions_with_exam_id_and_user_id(&mut conn, *exam_id, user.id).await?;
models::exercise_slide_submissions::delete_exercise_submissions_with_exam_id_and_user_id(
&mut conn, *exam_id, user.id,
)
.await?;

let token = authorize(&mut conn, Act::Teach, Some(user.id), Res::Exam(*exam_id)).await?;
token.authorized_ok(web::Json(()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,24 +174,9 @@ async fn edit_exam(
let mut tx = conn.begin().await?;

let exam = payload.0;
let token = authorize(
&mut tx,
Act::CreateCoursesOrExams,
Some(user.id),
Res::Exam(*exam_id),
)
.await?;
let token = authorize(&mut tx, Act::Edit, Some(user.id), Res::Exam(*exam_id)).await?;

models::exams::edit(
&mut tx,
*exam_id,
Some(exam.name.as_str()),
exam.starts_at,
exam.ends_at,
Some(exam.time_minutes),
Some(exam.minimum_points_treshold),
)
.await?;
models::exams::edit(&mut tx, *exam_id, exam).await?;

tx.commit().await?;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,23 @@ async fn get_org_exams(
token.authorized_ok(web::Json(exams))
}

/**
GET `/api/v0/main-frontend/organizations/{exam_id}/fetch_org_exam
*/
#[instrument(skip(pool))]
pub async fn get_org_exam_with_exam_id(
pool: web::Data<PgPool>,
exam_id: web::Path<Uuid>,
user: AuthUser,
) -> ControllerResult<web::Json<OrgExam>> {
let mut conn = pool.acquire().await?;
let token = authorize(&mut conn, Act::Teach, Some(user.id), Res::Exam(*exam_id)).await?;

let exam = models::exams::get_organization_exam_with_exam_id(&mut conn, *exam_id).await?;

token.authorized_ok(web::Json(exam))
}

/**
POST `/api/v0/main-frontend/organizations/{organization_id}/exams` - Creates new exam for the organization.
*/
Expand Down Expand Up @@ -379,5 +396,9 @@ pub fn _add_routes(cfg: &mut ServiceConfig) {
web::get().to(get_course_exams),
)
.route("/{organization_id}/org_exams", web::get().to(get_org_exams))
.route(
"/{organization_id}/fetch_org_exam",
web::get().to(get_org_exam_with_exam_id),
)
.route("/{organization_id}/exams", web::post().to(create_exam));
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,7 @@ const ExamList: React.FC<React.PropsWithChildren<Props>> = ({
<li key={exam.id}>
<a href={`/org/${organizationSlug}/exams/${exam.id}`}>{exam.name}</a>
<br />
<a
href={`/manage/exams/${exam.id}?org-slug=${organizationSlug}&org-id=${organizationId}`}
>
{t("manage")}
</a>
<a href={`/manage/exams/${exam.id}`}>{t("manage")}</a>
</li>
)
})}
Expand Down
38 changes: 12 additions & 26 deletions services/main-frontend/src/pages/manage/exams/[id]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { useTranslation } from "react-i18next"
import EditExamDialog from "../../../../components/page-specific/manage/courses/id/exams/EditExamDialog"
import {
fetchExam,
fetchOrganizationExams,
fetchOrganization,
fetchOrgExam,
setCourse,
unsetCourse,
} from "../../../../services/backend/exams"
Expand All @@ -28,6 +29,16 @@ interface OrganizationPageProps {
const Organization: React.FC<React.PropsWithChildren<OrganizationPageProps>> = ({ query }) => {
const { t } = useTranslation()
const getExam = useQuery({ queryKey: [`exam-${query.id}`], queryFn: () => fetchExam(query.id) })
const organizationId = useQuery({
queryKey: [`organizations-${query.id}`],
queryFn: () => fetchOrgExam(query.id),
}).data?.organization_id

const organizationSlug = useQuery({
queryKey: [`organizations-${organizationId}`],
queryFn: () => fetchOrganization(organizationId ?? ""),
enabled: !!organizationId,
}).data?.slug

const [editExamFormOpen, setEditExamFormOpen] = useState(false)
const [newCourse, setNewCourse] = useState("")
Expand All @@ -46,11 +57,6 @@ const Organization: React.FC<React.PropsWithChildren<OrganizationPageProps>> = (
},
)

const queryString = window.location.search
const urlParams = new URLSearchParams(queryString)
const organizationSlug = urlParams.get("org-slug")
const organizationId = urlParams.get("org-id")

const unsetCourseMutation = useToastMutation(
({ examId, courseId }: { examId: string; courseId: string }) => {
return unsetCourse(examId, courseId)
Expand All @@ -65,26 +71,6 @@ const Organization: React.FC<React.PropsWithChildren<OrganizationPageProps>> = (
},
},
)

const getOrgExams = useQuery({
queryKey: ["organization-exams", organizationId],
queryFn: () => {
if (organizationId) {
return fetchOrganizationExams(organizationId)
} else {
return Promise.reject(new Error("Organization ID undefined"))
}
},
enabled: !!organizationId,
})

if (getOrgExams.isError) {
return <ErrorBanner variant={"readOnly"} error={getOrgExams.error} />
}

if (getOrgExams.isPending) {
return <Spinner variant={"medium"} />
}
return (
<div
className={css`
Expand Down
19 changes: 18 additions & 1 deletion services/main-frontend/src/services/backend/exams.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { CourseExam, Exam, ExamCourseInfo, NewExam, OrgExam } from "../../shared-module/bindings"
import {
CourseExam,
Exam,
ExamCourseInfo,
NewExam,
Organization,
OrgExam,
} from "../../shared-module/bindings"
import { isOrganization } from "../../shared-module/bindings.guard"
import { validateResponse } from "../../shared-module/utils/fetching"
import { mainFrontendClient } from "../mainFrontendClient"

export const createExam = async (organizationId: string, data: NewExam) => {
Expand All @@ -20,6 +29,10 @@ export const fetchExam = async (id: string): Promise<Exam> => {
return response.data
}

export const fetchOrgExam = async (examId: string): Promise<OrgExam> => {
const response = await mainFrontendClient.get(`/organizations/${examId}/fetch_org_exam`, {})
return response.data
}
export const fetchCourseExams = async (organizationId: string): Promise<Array<CourseExam>> => {
const response = await mainFrontendClient.get(`/organizations/${organizationId}/course_exams`)
return response.data
Expand All @@ -29,6 +42,10 @@ export const fetchOrganizationExams = async (organizationId: string): Promise<Ar
return (await mainFrontendClient.get(`/organizations/${organizationId}/org_exams`, {})).data
}

export const fetchOrganization = async (organizationId: string): Promise<Organization> => {
const response = await mainFrontendClient.get(`/organizations/${organizationId}`)
return validateResponse(response, isOrganization)
}
export const setCourse = async (examId: string, courseId: string): Promise<void> => {
const data: ExamCourseInfo = { course_id: courseId }
await mainFrontendClient.post(`/exams/${examId}/set`, data)
Expand Down

0 comments on commit b9029c8

Please sign in to comment.