Skip to content

Commit

Permalink
Merge branch 'development' into add-in-progress-computation
Browse files Browse the repository at this point in the history
  • Loading branch information
liadaram1 committed Apr 1, 2022
2 parents 8837239 + b874c44 commit 006cd9c
Show file tree
Hide file tree
Showing 11 changed files with 99 additions and 28 deletions.
2 changes: 1 addition & 1 deletion packages/server/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "sogrim-server"
version = "1.0.3"
version = "1.0.4"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
82 changes: 64 additions & 18 deletions packages/server/src/core/bank_rule/specialization_groups.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,40 +11,55 @@ use super::BankRuleHandler;
// sg = specialization_group
// sgs = specialization_groups

fn is_valid_assignment(
fn get_groups_indices(course_id_to_sg_index: &HashMap<CourseId, usize>) -> Vec<usize> {
let mut uniques = HashSet::new();
let mut indices = course_id_to_sg_index
.clone()
.into_values()
.collect::<Vec<_>>();
indices.retain(|e| uniques.insert(*e));
indices
}

fn get_complete_sgs_indices(
sgs: &[SpecializationGroup],
groups_indices: &[usize],
course_id_to_sg_index: &HashMap<CourseId, usize>,
) -> bool {
) -> Vec<usize> {
let groups_indices = get_groups_indices(course_id_to_sg_index);
let mut complete_sgs_indices = Vec::new();
for sg_index in groups_indices {
// check there are enough courses in this specialization group
if (course_id_to_sg_index
.values()
.filter(|group| *group == sg_index)
.filter(|&&group| group == sg_index)
.count())
< sgs[*sg_index].courses_sum
< sgs[sg_index].courses_sum
{
// There are not enough courses in this assignment to complete sg requirement
return false;
// There are not enough courses in this sg to complete the requirement
continue;
}
// check if the user completed the mandatory courses in sg
if let Some(mandatory) = &sgs[*sg_index].mandatory {
if let Some(mandatory) = &sgs[sg_index].mandatory {
let mut complete_mandatory = true;
for courses in mandatory {
let mut completed_current_demand = false;
for (course_id, group) in course_id_to_sg_index {
// check if the user completed one of courses
if group == sg_index && courses.contains(course_id) {
if *group == sg_index && courses.contains(course_id) {
completed_current_demand = true;
break;
}
}
if !completed_current_demand {
return false;
complete_mandatory = false;
}
}
if complete_mandatory {
complete_sgs_indices.push(sg_index);
}
}
}
true
complete_sgs_indices
}

// This function is looking for a valid assignment for the courses which fulfill the sgs requirements
Expand All @@ -53,13 +68,20 @@ fn find_valid_assignment_for_courses(
sgs: &[SpecializationGroup],
groups_indices: &[usize],
optional_sgs_for_course: &HashMap<CourseId, Vec<usize>>, // list of all optional sgs for each course
current_best_match: &mut HashMap<CourseId, usize>, // the best match of sgs
course_id_to_sg_index: &mut HashMap<CourseId, usize>,
course_index: usize, // course_index-th element in optional_sgs_for_course
) -> Option<HashMap<CourseId, usize>> {
if course_index >= optional_sgs_for_course.len() {
if is_valid_assignment(sgs, groups_indices, course_id_to_sg_index) {
let complete_sgs_indices = get_complete_sgs_indices(sgs, course_id_to_sg_index);
if complete_sgs_indices.len() >= groups_indices.len() {
return Some(course_id_to_sg_index.clone());
}
let complete_sgs_for_current_best_match = get_complete_sgs_indices(sgs, current_best_match);
if complete_sgs_indices.len() > complete_sgs_for_current_best_match.len() {
current_best_match.clear();
current_best_match.extend(course_id_to_sg_index.to_owned());
}
return None;
}
if let Some((course_id, optional_groups)) = optional_sgs_for_course.iter().nth(course_index) {
Expand All @@ -69,6 +91,7 @@ fn find_valid_assignment_for_courses(
sgs,
groups_indices,
optional_sgs_for_course,
current_best_match,
course_id_to_sg_index,
course_index + 1,
) {
Expand All @@ -83,6 +106,7 @@ fn get_sgs_courses_assignment(
sgs: &[SpecializationGroup],
groups_indices: &[usize],
courses: &[CourseId],
best_match: &mut HashMap<CourseId, usize>,
) -> Option<HashMap<CourseId, usize>> {
let mut optional_sgs_for_course = HashMap::<CourseId, Vec<usize>>::new();
for course_id in courses {
Expand All @@ -103,6 +127,7 @@ fn get_sgs_courses_assignment(
sgs,
groups_indices,
&optional_sgs_for_course,
best_match,
&mut courses_assignment,
0,
)
Expand All @@ -115,9 +140,10 @@ fn generate_sgs_subsets(
sg_index: usize,
groups_indices: &mut Vec<usize>,
courses: &[CourseId],
best_match: &mut HashMap<CourseId, usize>,
) -> Option<HashMap<CourseId, usize>> {
if groups_indices.len() == required_number_of_groups {
return get_sgs_courses_assignment(sgs, groups_indices, courses);
return get_sgs_courses_assignment(sgs, groups_indices, courses, best_match);
}

if sg_index >= sgs.len() {
Expand All @@ -132,6 +158,7 @@ fn generate_sgs_subsets(
sg_index + 1,
groups_indices,
courses,
best_match,
) {
return Some(valid_assignment);
}
Expand All @@ -144,20 +171,25 @@ fn generate_sgs_subsets(
sg_index + 1,
groups_indices,
courses,
best_match,
)
}

fn run_exhaustive_search(
sgs: &SpecializationGroups,
courses: Vec<CourseId>, // list of all courses the user completed in specialization groups bank
) -> Option<HashMap<CourseId, usize>> {
) -> HashMap<CourseId, usize> {
let mut best_match = HashMap::new();
generate_sgs_subsets(
&sgs.groups_list,
sgs.groups_number,
0,
&mut Vec::new(),
&courses,
&mut best_match,
)
.or(Some(best_match))
.unwrap() // unwraping is safe since the line above always returns Some(_)
}

impl<'a> BankRuleHandler<'a> {
Expand All @@ -166,6 +198,19 @@ impl<'a> BankRuleHandler<'a> {
sgs: &SpecializationGroups,
completed_groups: &mut Vec<String>,
) -> f32 {
// All courses which might be in SOME specialization group should get its name assigned to them
// later on, if we find a valid assignment for said courses with a DIFFERENT specialization group,
// we will simply re-assign the specialization group name.
for sg in sgs.groups_list.iter() {
for course_id in sg.course_list.iter() {
if let Some(course_status) =
self.degree_status.get_mut_course_status(course_id.as_str())
{
course_status.set_specialization_group_name(&sg.name);
}
}
}

let credit_info = self.iterate_course_list();
let mut completed_courses = Vec::new();
for (course_id_in_list, course_id_done_by_user) in credit_info.handled_courses {
Expand All @@ -180,12 +225,13 @@ impl<'a> BankRuleHandler<'a> {
}

let valid_assignment_for_courses = run_exhaustive_search(sgs, completed_courses);

let complete_sgs_indices =
get_complete_sgs_indices(&sgs.groups_list, &valid_assignment_for_courses);
// The set is to prevent duplications
let mut sgs_names = HashSet::new();
if let Some(valid_assignment) = valid_assignment_for_courses {
for (course_id, sg_index) in valid_assignment {
if let Some(course_status) = self.degree_status.get_mut_course_status(&course_id) {
for (course_id, sg_index) in valid_assignment_for_courses {
if let Some(course_status) = self.degree_status.get_mut_course_status(&course_id) {
if complete_sgs_indices.contains(&sg_index) {
course_status.set_specialization_group_name(&sgs.groups_list[sg_index].name);
sgs_names.insert(&sgs.groups_list[sg_index].name);
}
Expand Down
3 changes: 2 additions & 1 deletion packages/server/src/core/bank_rule/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -596,5 +596,6 @@ async fn test_specialization_group() {
create_bank_rule_handler!(&mut degree_status, bank_name, course_list, 0.0, 0);
let mut completed_groups = Vec::<String>::new();
handle_bank_rule_processor.specialization_group(&sgs, &mut completed_groups);
assert!(completed_groups.is_empty());
assert_eq!(completed_groups.len(), 1);
assert!(completed_groups.contains(&"מערכות נבונות".to_string()));
}
1 change: 1 addition & 0 deletions packages/server/src/core/degree_status/compute_bank.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ impl<'a> DegreeStatusHandler<'a> {
completed = groups_done_list.len() >= specialization_groups.groups_number;
msg = Some(messages::completed_specialization_groups_msg(
&groups_done_list,
specialization_groups.groups_number,
));
}
Rule::Wildcard(_) => {
Expand Down
10 changes: 6 additions & 4 deletions packages/server/src/core/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,13 @@ pub fn completed_chain_msg(chain: &[String]) -> String {
msg
}

pub fn completed_specialization_groups_msg(groups: &[String]) -> String {
let mut msg = if groups.len() == SINGLE as usize {
"השלמת את קבוצת ההתמחות: ".to_string()
pub fn completed_specialization_groups_msg(groups: &[String], needed: usize) -> String {
let mut msg = if groups.len() == ZERO as usize {
"לא השלמת אף קבוצת התמחות".to_string()
} else if groups.len() == SINGLE as usize {
format!("השלמת קבוצת התמחות אחת (מתוך {}): ", needed)
} else {
format!("השלמת {} קבוצות התמחות: ", groups.len())
format!("השלמת {} (מתוך {}) קבוצות התמחות: ", groups.len(), needed)
};
for group in groups {
if group == groups.last().unwrap() {
Expand Down
2 changes: 1 addition & 1 deletion packages/sogrim-app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sogrim-app",
"version": "1.0.3",
"version": "1.0.4",
"private": true,
"dependencies": {
"@emotion/react": "^11.8.2",
Expand Down
8 changes: 8 additions & 0 deletions packages/sogrim-app/src/components/Footer/changes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ export type VersionChanges = {
};

export const versionChanges: VersionChanges[] = [
{
version: "v1.0.4",
changes: [
"לרשימת המסלולים נוסף המסלול הארבע שנתי!",
"תוקן באג שבו לפעמים מחיקת קורס גרמה למחיקה של הקורס הלא נכון.",
"סגירת התואר היא כעת יותר.. חגיגית ;)",
],
},
{
version: "v1.0.3",
changes: ["ניתן כעת לייצא את נתוני התואר לקובץ להורדה."],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,15 @@ const BankRequirmentCourseRowComp: React.FC<BankRequirmentCourseProps> = ({
size="small"
/>
</Tooltip>
{course.sg_name && (
<Chip
label={course.sg_name}
sx={{ minWidth: "55px" }}
color="info"
variant="outlined"
size="small"
/>
)}
{course.state !== "הושלם" && (
<Chip
label={course.state}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export const createData = (
state: string,
grade?: string,
type?: string,
sg_name?: string,
msg?: string
): RowData => {
return {
Expand All @@ -57,6 +58,7 @@ export const createData = (
type,
state,
semester,
sg_name,
msg,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface RowData {
type?: string;
grade?: string;
semester: string;
sg_name?: string;
msg?: string;
}

Expand Down
7 changes: 4 additions & 3 deletions packages/sogrim-app/src/stores/DataStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ export class DataStore {
course.state,
this.displayContent(course.grade),
this.displayContent(course.type),
course.specialization_group_name,
course.additional_msg
)
)
Expand Down Expand Up @@ -199,9 +200,9 @@ export class DataStore {
deleteCourseInUserDetails = (rowData: RowData, semester: string) => {
const courseList = this.userDetails?.degree_status.course_statuses ?? [];
const idx = courseList.findIndex(
(course) =>
course.course._id === rowData.courseNumber &&
semester === rowData.semester
(courseStatus) =>
courseStatus.course._id === rowData.courseNumber &&
courseStatus.semester === rowData.semester
);
const newCourseList = [...courseList];
newCourseList.splice(idx, 1);
Expand Down

0 comments on commit 006cd9c

Please sign in to comment.