Skip to content

Commit

Permalink
Merge pull request #158 from MathisBurger/feature/scaling
Browse files Browse the repository at this point in the history
  • Loading branch information
MathisBurger authored Nov 20, 2024
2 parents 9e7147d + 396007c commit 07a56d9
Show file tree
Hide file tree
Showing 42 changed files with 691 additions and 226 deletions.
1 change: 1 addition & 0 deletions tasky/migrations/2024-11-19-090556_scaling_groups/down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-- There should be no way back here
14 changes: 14 additions & 0 deletions tasky/migrations/2024-11-19-090556_scaling_groups/up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
CREATE TABLE group_members (
group_id INTEGER NOT NULL,
member_id INTEGER NOT NULL,
PRIMARY KEY (group_id, member_id)
);

INSERT INTO group_members (group_id, member_id)
SELECT
g.id AS group_id,
unnest(g.members) AS member_id
FROM
groups g;

ALTER TABLE groups DROP COLUMN members;
1 change: 1 addition & 0 deletions tasky/migrations/2024-11-19-091626_groups_fix/down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-- This file should undo anything in `up.sql`
5 changes: 5 additions & 0 deletions tasky/migrations/2024-11-19-091626_groups_fix/up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
ALTER TABLE group_members
ADD CONSTRAINT fk_group_id
FOREIGN KEY (group_id)
REFERENCES groups(id)
ON DELETE CASCADE;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-- This file should undo anything in `up.sql`
14 changes: 14 additions & 0 deletions tasky/migrations/2024-11-19-174343_scaling_assignments/up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
CREATE TABLE assignment_completions (
assignment_id INTEGER REFERENCES assignments(id) ON DELETE CASCADE,
member_id INTEGER NOT NULL,
PRIMARY KEY (assignment_Id, member_id)
);

INSERT INTO assignment_completions (assignment_id, member_id)
SELECT
a.id AS assignment_id,
unnest(a.completed_by) AS member_id
FROM
assignments a;

ALTER TABLE assignments DROP COLUMN completed_by;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-- This file should undo anything in `up.sql`
14 changes: 14 additions & 0 deletions tasky/migrations/2024-11-19-211502_scalable_notifications/up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
CREATE TABLE notification_targets (
notification_id INTEGER NOT NULL REFERENCES notifications(id),
user_id INTEGER NOT NULL,
PRIMARY KEY (notification_id, user_id)
);

INSERT INTO notification_targets (notification_id, user_id)
SELECT
n.id AS notification_id,
unnest(n.targeted_users) AS user_id
FROM
notifications n;

ALTER TABLE notifications DROP COLUMN targeted_users;
7 changes: 5 additions & 2 deletions tasky/src/models/assignment.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::notification::NotificationRepository;
use super::{Paginate, PaginatedModel, DB};
use crate::schema::assignments::dsl;
use crate::schema::{self, group_members};
use chrono::NaiveDateTime;
use diesel::associations::HasTable;
use diesel::dsl::not;
Expand Down Expand Up @@ -59,7 +60,6 @@ pub struct Assignment {
pub group_id: i32,
pub description: String,
pub language: AssignmentLanguage,
pub completed_by: Vec<Option<i32>>,
pub file_structure: Option<serde_json::Value>,
pub runner_cpu: String,
pub runner_memory: String,
Expand Down Expand Up @@ -166,7 +166,10 @@ impl AssignmentRepository {
dsl::assignments
.left_join(crate::schema::groups::table)
.left_join(crate::schema::solutions::table)
.filter(crate::schema::groups::dsl::members.contains(vec![Some(student_id)]))
.left_join(
schema::group_members::table.on(schema::groups::id.eq(group_members::group_id)),
)
.filter(group_members::dsl::member_id.eq(student_id))
.filter(not(crate::schema::solutions::dsl::submitter_id
.eq(student_id)
.and(
Expand Down
58 changes: 58 additions & 0 deletions tasky/src/models/assignment_completion.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use diesel::prelude::*;
use diesel::{
deserialize::Queryable, prelude::Insertable, BoolExpressionMethods, ExpressionMethods,
QueryDsl, Selectable,
};

use crate::schema::assignment_completions;

use super::{Paginate, PaginatedModel, DB};

#[derive(Queryable, Selectable, Clone, Insertable)]
#[diesel(primary_key(assignment_id, member_id))]
#[diesel(table_name = assignment_completions)]
#[diesel(check_for_backend(diesel::pg::Pg))]
pub struct AssignmentCompletion {
pub assignment_id: i32,
pub member_id: i32,
}

pub struct AssignmentCompletionRepository;

impl AssignmentCompletionRepository {
/// Checks whether a user has completed assignment
pub fn is_completed_by(assignment_id: i32, member_id: i32, conn: &mut DB) -> bool {
assignment_completions::dsl::assignment_completions
.filter(
assignment_completions::assignment_id
.eq(assignment_id)
.and(assignment_completions::member_id.eq(member_id)),
)
.first::<AssignmentCompletion>(conn)
.optional()
.expect("Cannot fetch is completed state")
.is_some()
}

/// Creates a new completion in the system
pub fn create_completion(comp: AssignmentCompletion, conn: &mut DB) {
diesel::insert_into(assignment_completions::table)
.values(comp)
.execute(conn)
.expect("Cannot insert assignment completion");
}

/// Gets all completion IDs for assignment
pub fn get_completion_ids_for_assignment(
assignment_id: i32,
page: i64,
conn: &mut DB,
) -> PaginatedModel<i32> {
assignment_completions::dsl::assignment_completions
.filter(assignment_completions::assignment_id.eq(assignment_id))
.select(assignment_completions::member_id)
.paginate(page)
.load_and_count_pages::<i32>(conn)
.expect("Cannot load completions")
}
}
4 changes: 2 additions & 2 deletions tasky/src/models/code_comment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ impl CodeCommentRepository {
let solution = SolutionRepository::get_solution_by_id(create.solution_id, conn).unwrap();
let group = GroupRepository::get_by_id(create.group_id, conn).unwrap();
let targeted_users = match solution.submitter_id == create.commentor {
true => vec![Some(group.tutor)],
false => vec![Some(solution.submitter_id)],
true => vec![group.tutor],
false => vec![solution.submitter_id],
};
NotificationRepository::create_notification(
&CreateNotification {
Expand Down
82 changes: 39 additions & 43 deletions tasky/src/models/group.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use super::group_join_request::GroupJoinRequestRepository;
use super::Paginate;
use super::{PaginatedModel, DB};
use crate::schema;
use crate::schema::group_members;
use crate::schema::groups::dsl;
use chrono::NaiveDateTime;
use diesel::dsl::count_star;
use diesel::pg::Pg;
use diesel::prelude::*;
use diesel::{associations::HasTable, dsl::not};
use serde::{Deserialize, Serialize};
Expand All @@ -28,7 +27,6 @@ pub enum JoinRequestPolicy {
pub struct Group {
pub id: i32,
pub title: String,
pub members: Vec<Option<i32>>,
pub tutor: i32,
pub join_policy: JoinRequestPolicy,
pub created_at: NaiveDateTime,
Expand All @@ -42,7 +40,6 @@ pub struct Group {
pub struct CreateGroup {
pub title: String,
pub tutor: i32,
pub members: Vec<i32>,
pub join_policy: JoinRequestPolicy,
}

Expand Down Expand Up @@ -90,11 +87,13 @@ impl GroupRepository {
/// Gets all groups a user is not member or tutor of
pub fn get_groups_for_member(member_id: i32, conn: &mut DB) -> Vec<Group> {
dsl::groups
.left_join(group_members::table)
.filter(
dsl::tutor
.eq(member_id)
.or(dsl::members.contains(vec![Some(member_id)])),
.or(group_members::member_id.eq(member_id)),
)
.select(Group::as_select())
.get_results::<Group>(conn)
.expect("Cannot fetch groups for member")
}
Expand All @@ -106,11 +105,13 @@ impl GroupRepository {
conn: &mut DB,
) -> PaginatedModel<Group> {
let result = dsl::groups
.left_join(group_members::table)
.filter(
dsl::tutor
.eq(member_id)
.or(dsl::members.contains(vec![Some(member_id)])),
.or(group_members::member_id.eq(member_id)),
)
.select(Group::as_select())
.group_by((dsl::id, dsl::verified))
.order(dsl::verified.desc())
.paginate(page)
Expand Down Expand Up @@ -138,25 +139,43 @@ impl GroupRepository {
.map(|x| x.group_id)
.collect();

let total = dsl::groups
.into_boxed()
.filter(apply_search_filter(
member_id,
requested.clone(),
search.clone(),
))
.select(count_star())
.get_result::<i64>(conn)
.expect("Result cannot be fetched");
let base_predicate = not(dsl::tutor
.eq(member_id)
.or(dsl::id.eq_any(requested))
.or(group_members::dsl::member_id.eq(member_id)));

let results = dsl::groups
let total_base_query = dsl::groups
.left_join(group_members::dsl::group_members)
.select(count_star())
.filter(base_predicate.clone())
.into_boxed();

let total = match search.clone() {
None => total_base_query
.get_result::<i64>(conn)
.expect("Result cannot be fetched"),
Some(search_value) => total_base_query
.filter(dsl::title.like(format!("%{}%", search_value)))
.get_result::<i64>(conn)
.expect("Result cannot be fetched"),
};

let results_base_query = dsl::groups
.left_join(group_members::dsl::group_members)
.group_by((dsl::id, dsl::verified))
.into_boxed()
.filter(apply_search_filter(member_id, requested, search))
.select(Group::as_select())
.filter(base_predicate)
.order(dsl::verified.desc())
.limit(50)
.offset((page - 1) * 50)
.load::<Group>(conn);
.into_boxed();

let results = match search {
None => results_base_query.load::<Group>(conn),
Some(search_value) => results_base_query
.filter(dsl::title.like(format!("%{}%", search_value)))
.load::<Group>(conn),
};

if results.is_err() {
return PaginatedModel {
Expand All @@ -180,26 +199,3 @@ impl GroupRepository {
.expect("Cannot delete group");
}
}

fn apply_search_filter(
member_id: i32,
requested: Vec<i32>,
search: Option<String>,
) -> Box<dyn BoxableExpression<schema::groups::table, Pg, SqlType = diesel::sql_types::Bool>> {
let base_predicate = not(dsl::tutor
.eq(member_id)
.or(dsl::id.eq_any(requested))
.or(dsl::members.contains(vec![Some(member_id)])));
let query: Box<
dyn BoxableExpression<schema::groups::table, Pg, SqlType = diesel::sql_types::Bool>,
> = if let Some(ref search_value) = search {
Box::new(
dsl::title
.like(format!("%{}%", search_value))
.and(base_predicate),
)
} else {
Box::new(base_predicate)
};
query
}
2 changes: 1 addition & 1 deletion tasky/src/models/group_join_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ impl GroupJoinRequestRepository {
&CreateNotification {
title: "Join request rejected".to_string(),
content: "One of your join requests has been rejected".to_string(),
targeted_users: vec![Some(req.requestor)],
targeted_users: vec![req.requestor],
},
conn,
);
Expand Down
Loading

0 comments on commit 07a56d9

Please sign in to comment.