Skip to content

Commit

Permalink
[cheater-feature]: update suspected-cheaters route to use average dur…
Browse files Browse the repository at this point in the history
…ation as a metric
  • Loading branch information
george-misan committed Apr 26, 2024
1 parent 7774e55 commit 0faa462
Show file tree
Hide file tree
Showing 12 changed files with 83 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ CREATE TABLE suspected_cheaters (
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
deleted_at TIMESTAMP WITH TIME ZONE,
total_duration INTEGER,
total_duration_seconds INTEGER,
total_points INTEGER NOT NULL
);
CREATE TRIGGER set_timestamp BEFORE
Expand All @@ -15,15 +15,15 @@ COMMENT ON COLUMN suspected_cheaters.user_id IS 'The user_id of the student bein
COMMENT ON COLUMN suspected_cheaters.created_at IS 'Timestamp when the record was created.';
COMMENT ON COLUMN suspected_cheaters.updated_at IS 'Timestamp when the record was updated.';
COMMENT ON COLUMN suspected_cheaters.deleted_at IS 'Timestamp when the record was deleted. If null, the record is not deleted.';
COMMENT ON COLUMN suspected_cheaters.total_duration IS 'The total duration the student spend completing the course.';
COMMENT ON COLUMN suspected_cheaters.total_duration_seconds IS 'The total duration the student spend completing the course.';
COMMENT ON COLUMN suspected_cheaters.total_points IS 'The total points the student acquired in the course.';
CREATE TABLE course_student_average (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
course_instance_id UUID NOT NULL REFERENCES course_instances,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
deleted_at TIMESTAMP WITH TIME ZONE,
average_duration INTEGER,
average_duration_seconds INTEGER,
average_points INTEGER NOT NULL
);
CREATE TRIGGER set_timestamp BEFORE
Expand All @@ -33,7 +33,7 @@ COMMENT ON COLUMN course_student_average.course_instance_id IS 'A unique, stable
COMMENT ON COLUMN course_student_average.created_at IS 'Timestamp when the record was created.';
COMMENT ON COLUMN course_student_average.updated_at IS 'Timestamp when the record was updated.';
COMMENT ON COLUMN course_student_average.deleted_at IS 'Timestamp when the record was deleted. If null, the record is not deleted.';
COMMENT ON COLUMN course_student_average.average_duration IS 'The average duration all student spent completing the course.';
COMMENT ON COLUMN course_student_average.average_duration_seconds IS 'The average duration all student spent completing the course.';
COMMENT ON COLUMN course_student_average.average_points IS 'The average points all students acquired in the course.';
--

Expand All @@ -44,7 +44,7 @@ CREATE TABLE suspected_cheaters_exercise_list (
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
deleted_at TIMESTAMP WITH TIME ZONE,
exercise_id UUID REFERENCES exercises NOT NULL,
duration INTEGER,
duration_seconds INTEGER,
points INTEGER NOT NULL,
attempts INTEGER NOT NULL,
status activity_progress NOT NULL DEFAULT 'initialized'
Expand All @@ -58,7 +58,7 @@ COMMENT ON COLUMN suspected_cheaters_exercise_list.updated_at IS 'Timestamp when
COMMENT ON COLUMN suspected_cheaters_exercise_list.created_at IS 'Timestamp when the record was created.';
COMMENT ON COLUMN suspected_cheaters_exercise_list.deleted_at IS 'Timestamp when the record was deleted. If null, the record is not deleted.';
COMMENT ON COLUMN suspected_cheaters_exercise_list.exercise_id IS 'Exercise Id of an exercise completed by the suspected student.';
COMMENT ON COLUMN suspected_cheaters_exercise_list.duration IS 'The duration a suspected student used in completing an exercise.';
COMMENT ON COLUMN suspected_cheaters_exercise_list.duration_seconds IS 'The duration a suspected student used in completing an exercise.';
COMMENT ON COLUMN suspected_cheaters_exercise_list.points IS 'The points a suspected student received from completing an exercise.';
COMMENT ON COLUMN suspected_cheaters_exercise_list.attempts IS 'The number of times a student attempt an exercise.';
COMMENT ON COLUMN suspected_cheaters_exercise_list.status IS 'The status of an exercise.';
Expand All @@ -68,7 +68,7 @@ CREATE TABLE exercise_student_average (
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
deleted_at TIMESTAMP WITH TIME ZONE,
average_duration INTEGER,
average_duration_seconds INTEGER,
average_points INTEGER NOT NULL
);
CREATE TRIGGER set_timestamp BEFORE
Expand All @@ -79,7 +79,7 @@ COMMENT ON COLUMN exercise_student_average.exercise_id IS 'The exercise_id of th
COMMENT ON COLUMN exercise_student_average.created_at IS 'Timestamp when the record was created.';
COMMENT ON COLUMN exercise_student_average.updated_at IS 'Timestamp when the record was updated.';
COMMENT ON COLUMN exercise_student_average.deleted_at IS 'Timestamp when the record was deleted. If null, the record is not deleted.';
COMMENT ON COLUMN exercise_student_average.average_duration IS 'The average duration a all student used in completing an exercise.';
COMMENT ON COLUMN exercise_student_average.average_duration_seconds IS 'The average duration a all student used in completing an exercise.';
COMMENT ON COLUMN exercise_student_average.average_points IS 'The average points all student received from completing an exercise.';
CREATE TABLE cheater_thresholds (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
Expand All @@ -88,7 +88,7 @@ CREATE TABLE cheater_thresholds (
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
deleted_at TIMESTAMP WITH TIME ZONE,
points INTEGER NOT NULL,
duration INTEGER
duration_seconds INTEGER
);
CREATE TRIGGER set_timestamp BEFORE
UPDATE ON cheater_thresholds FOR EACH ROW EXECUTE PROCEDURE trigger_set_timestamp();
Expand All @@ -99,4 +99,4 @@ COMMENT ON COLUMN cheater_thresholds.created_at IS 'Timestamp when the record wa
COMMENT ON COLUMN cheater_thresholds.updated_at IS 'Timestamp when the record was updated.';
COMMENT ON COLUMN cheater_thresholds.deleted_at IS 'Timestamp when the record was deleted. If null, the record is not deleted.';
COMMENT ON COLUMN cheater_thresholds.points IS 'The score threshold of the course.';
COMMENT ON COLUMN cheater_thresholds.duration IS 'The duration threshold of the course.';
COMMENT ON COLUMN cheater_thresholds.duration_seconds IS 'The duration threshold of the course.';

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

This file was deleted.

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.

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.

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

38 changes: 19 additions & 19 deletions services/headless-lms/models/src/course_instances.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ pub struct CourseInstance {
pub support_email: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
#[cfg_attr(feature = "ts_rs", derive(TS))]
pub struct CourseAverageDuration {
pub average_duration: Option<i64>,
}
// #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
// #[cfg_attr(feature = "ts_rs", derive(TS))]
// pub struct CourseAverageDuration {
// pub average_duration_seconds: Option<i64>,
// }

impl CourseInstance {
pub fn is_open(&self) -> bool {
Expand Down Expand Up @@ -755,49 +755,49 @@ WHERE user_id = $1
pub async fn get_course_average_duration(
conn: &mut PgConnection,
course_instance_id: Uuid,
) -> ModelResult<CourseAverageDuration> {
let res = sqlx::query_as!(
CourseAverageDuration,
r#"
) -> ModelResult<Option<i64>> {
let res = sqlx::query!(
"
SELECT
AVG(EXTRACT(EPOCH FROM cmc.completion_date - ce.created_at))::int8 AS average_duration
AVG(EXTRACT(EPOCH FROM cmc.completion_date - ce.created_at))::int8 AS average_duration_seconds
FROM course_instance_enrollments ce
JOIN course_module_completions cmc ON cmc.course_instance_id = ce.course_instance_id
WHERE ce.course_instance_id = $1
AND ce.deleted_at IS NULL
AND cmc.deleted_at IS NULL;
"#,
",
course_instance_id
)
.fetch_one(conn)
.await?;
Ok(res)

Ok(res.average_duration_seconds)
}

pub async fn get_student_duration(
conn: &mut PgConnection,
user_id: Uuid,
course_instance_id: Uuid,
) -> ModelResult<CourseAverageDuration> {
let res = sqlx::query_as!(
CourseAverageDuration,
r#"
) -> ModelResult<Option<i64>> {
let res = sqlx::query!(
"
SELECT
EXTRACT(EPOCH FROM cmc.completion_date - ce.created_at)::int8 AS average_duration
COALESCE(EXTRACT(EPOCH FROM cmc.completion_date - ce.created_at)::int8, 0) AS student_duration_seconds
FROM course_instance_enrollments ce
JOIN course_module_completions cmc ON cmc.course_instance_id = ce.course_instance_id
AND cmc.user_id = ce.user_id
WHERE ce.course_instance_id = $1
AND ce.user_id = $2
AND ce.deleted_at IS NULL
AND cmc.deleted_at IS NULL;
"#,
",
course_instance_id,
user_id
)
.fetch_one(conn)
.await?;
Ok(res)

Ok(res.student_duration_seconds)
}

#[cfg(test)]
Expand Down
18 changes: 9 additions & 9 deletions services/headless-lms/models/src/suspected_cheaters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ pub struct SuspectedCheaters {
pub created_at: DateTime<Utc>,
pub deleted_at: Option<DateTime<Utc>>,
pub updated_at: Option<DateTime<Utc>>,
pub total_duration: Option<i32>, // Represented in milliseconds
pub total_duration_seconds: Option<i32>, // Represented in seconds
pub total_points: i32,
}

#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "ts_rs", derive(TS))]
pub struct ThresholdData {
pub points: i32,
pub duration: Option<i32>,
pub duration_seconds: Option<i32>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
Expand All @@ -28,26 +28,26 @@ pub struct Threshold {
pub updated_at: DateTime<Utc>,
pub deleted_at: Option<DateTime<Utc>>,
pub points: i32,
pub duration: Option<i32>,
pub duration_seconds: Option<i32>,
}

pub async fn insert(
conn: &mut PgConnection,
user_id: Uuid,
total_duration: Option<i32>,
total_duration_seconds: Option<i32>,
total_points: i32,
) -> ModelResult<()> {
sqlx::query!(
"
INSERT INTO suspected_cheaters (
user_id,
total_duration,
total_duration_seconds,
total_points
)
VALUES ($1, $2, $3)
",
user_id,
total_duration,
total_duration_seconds,
total_points
)
.fetch_one(conn)
Expand All @@ -58,20 +58,20 @@ pub async fn insert(
pub async fn insert_thresholds(
conn: &mut PgConnection,
course_instance_id: Uuid,
duration: Option<i32>,
duration_seconds: Option<i32>,
points: i32,
) -> ModelResult<()> {
sqlx::query!(
"
INSERT INTO cheater_thresholds (
course_instance_id,
duration,
duration_seconds,
points
)
VALUES ($1, $2, $3)
",
course_instance_id,
duration,
duration_seconds,
points
)
.execute(conn)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ async fn insert_suspected_cheaters(

// all do this for a single student and compare

let average_duration =
let average_duration_seconds =
models::course_instances::get_course_average_duration(&mut conn, course_instance_id)
.await?;

Expand Down Expand Up @@ -566,7 +566,7 @@ async fn insert_suspected_cheaters(
.await?
.unwrap_or(0.0);

let student_duration = models::course_instances::get_student_duration(
let student_duration_seconds = models::course_instances::get_student_duration(
&mut conn,
completion.user_id,
course_instance_id,
Expand All @@ -577,10 +577,7 @@ async fn insert_suspected_cheaters(
continue;
}

let completion_average_duration = student_duration.average_duration;
let average_duration_value = average_duration.average_duration;

if completion_average_duration > average_duration_value {
if student_duration_seconds > average_duration_seconds {
models::suspected_cheaters::insert(
&mut conn,
completion.user_id,
Expand Down

0 comments on commit 0faa462

Please sign in to comment.