#include "shared_grade_book_calculation_util.h" #include "shared_util.h" GRADE_BOOK_CONFIGURE global_grade_conf; static void get_min_max_avg(GRADE_LIST *list, ITEM *item) { double total =0; double small = 9999; double big = -9999; int count =0; GRADE *ptr; for(ptr = list->head; ptr; ptr = ptr->next) { if(ptr->score >= 0) { if(ptr->score < small) small = ptr->score; if(ptr->score > big) big = ptr->score; total += ptr->score; count++; } } if(count >0) { snprintf(item->min, MAX_GRADE_LEN +1, "%4.*f", test_precision(small), small); snprintf(item->max, MAX_GRADE_LEN +1, "%4.*f", test_precision(big), big); snprintf(item->avg, MAX_GRADE_LEN +1, "%4.*f", test_precision(total/(float)count), total/(float)count); } } static double look_up_points(int letter_id, LETTER_GRADE_LIST *list) { LETTER_GRADE *ptr; for(ptr = list->head; ptr; ptr = ptr->next) { if(ptr->letter_id == letter_id) return ptr->points; } return -1; } static void points_to_letter(double points, LETTER_GRADE_LIST *list, char *letter) { LETTER_GRADE *next; LETTER_GRADE *ptr; for(ptr = list->head; ptr; ptr = ptr->next) { next = ptr->next; if(next) { if(points <= ptr->points && points > next->points) { strncpy(letter, ptr->letter, MAX_GRADE_LEN +1); return; } } else { strncpy(letter, ptr->letter, MAX_GRADE_LEN +1); return; } } } static void get_letter_min_max_avg(ITEM *item) { double total =0; double small =9000000; double big =-9000000; double points =0; int count = 0; GRADE *ptr; for(ptr = item->grades->head; ptr;ptr = ptr->next) { if(ptr->letter_id > 0) { points = look_up_points(ptr->letter_id, item->letters); if(points < small) small = points; if(points > big) big = points; total += points; count++; } } if(count >0 && total >=0 ) { points_to_letter(small, item->letters, item->min); points_to_letter(big, item->letters, item->max); points_to_letter(total/(double)count, item->letters, item->avg); } } void get_items_min_avg_max(CONFIG_STRUCT *conf, const char *my_db) { ITEM *ptr; if(global_item_list && global_item_list->head) { for(ptr = global_item_list->head; ptr; ptr = ptr->next) { if(strcmp(ptr->grade_entered, "yes") ==0) { if(strcmp(ptr->is_letter_grade, "yes") == 0) { get_letter_grades(conf, ptr->item_id, my_db); ptr->letters = global_letter_grades; get_letter_min_max_avg(ptr); } else { if(ptr->grades) { get_min_max_avg(ptr->grades, ptr); } } } } } } static double get_max_letter_points(LETTER_GRADE_LIST *list) { double max = 0; LETTER_GRADE *ptr; for(ptr = list->head; ptr; ptr = ptr->next) { if(ptr->points > max) max = ptr->points; } return max; } void get_one_student_grade(double total, char *grade) { SCALE *ptr; if(global_scale_list && global_scale_list->head) { for(ptr = global_scale_list->head;ptr;ptr = ptr->next) { if(total >= ptr->start && total < ptr->end) strncpy(grade, ptr->grade, MAX_GRADE_LEN +1); } } } static int find_number_graded_items_for_one_user(int cat_id, TOTAL_GRADE *one_student) { GRADE *grades; int count = 0; ITEM *ptr; for(ptr = global_item_list->head; ptr; ptr = ptr->next) { if(ptr->cat_id == cat_id && strcmp(ptr->extra_credit, "no") ==0 && strcmp(ptr->grade_entered, "yes") ==0) { if(ptr->grades) { for(grades = ptr->grades->head; grades; grades = grades->next) { if(strcmp(grades->username, one_student->username) == 0) { if(strcmp(grades->pending, "no") == 0 && strcmp(grades->excuse, "no") ==0) count++; } } } } } return count; } static int get_lowest_item_id(TOTAL_GRADE *one_student, int cat_id) { double low = 9000000; double temp; ITEM *ptr; GRADE *grades; double letter_score; double letter_points; int item_id = -1; for(ptr = global_item_list->head;ptr; ptr = ptr->next) { if(ptr->cat_id == cat_id && strcmp(ptr->hide, "no") ==0) { if(ptr->grades) { for(grades = ptr->grades->head; grades; grades = grades->next) { if(strcmp(grades->username, one_student->username) == 0) { if(strcmp(grades->pending, "no") == 0 && strcmp(grades->excuse, "no") ==0 && strcmp(grades->drop, "no") ==0 && strcmp(ptr->extra_credit, "no") ==0) { if(strcmp(ptr->is_letter_grade, "no") == 0) { temp = grades->score / ptr->points; if(temp < low) { low = temp; item_id = grades->item_id; } } else { letter_score = look_up_points(grades->letter_id, ptr->letters); letter_points = get_max_letter_points(ptr->letters); temp = letter_score / letter_points; if(temp < low) { low = temp; item_id = grades->item_id; } } } } } } } } return item_id; } static void update_score_list(TOTAL_GRADE *one_student, int low_id) { ITEM *ptr; GRADE *grades; char name[MAX_PATH]; for(ptr = global_item_list->head;ptr; ptr = ptr->next) { if(ptr->item_id == low_id) { if(ptr->grades) { for(grades = ptr->grades->head; grades; grades = grades->next) { if(strcmp(one_student->username, grades->username) ==0) { strncpy(grades->drop, "yes", MAX_BOOLEN_LEN +1); snprintf(name, MAX_PATH, "score.%s.%d.drop", grades->username, ptr->item_id); cs_set_value(name, grades->drop); return; } } } } } } void mark_drop_lowest_scores(int cat_id, TOTAL_GRADE *one_student, int number_drop) { int i; int low_id; for(i =0; i 0) update_score_list(one_student, low_id); } } static void update_drop_lowest_score(TOTAL_GRADE *one_student) { int count = 0; CATEGORY *ptr; for(ptr = global_cat_list->head;ptr; ptr = ptr->next) { count =0; if(ptr->drop_lowest > 0 && ptr->number_graded_items >= ptr->drop_lowest +1) { count = find_number_graded_items_for_one_user(ptr->cat_id, one_student); if(count >= ptr->drop_lowest +1) { mark_drop_lowest_scores(ptr->cat_id, one_student, ptr->drop_lowest); } } } } static void get_basic_points(TOTAL_GRADE *one_student) { double total =0; double total_base =0; GRADE *grades; ITEM *ptr; CATEGORY *cat_ptr; char temp[MAX_PATH +1]; int count =0; for(cat_ptr = global_cat_list->head; cat_ptr; cat_ptr = cat_ptr->next) { if(cat_ptr->weight > 0) { for(ptr = global_item_list->head;ptr; ptr = ptr->next) { if(strcmp(ptr->hide, "no") ==0 && ptr->grades && cat_ptr->cat_id == ptr->cat_id) { for(grades = ptr->grades->head; grades; grades = grades->next) { if(strcmp(grades->username, one_student->username) == 0) { if(strcmp(grades->pending, "no") == 0 && strcmp(grades->excuse, "no") ==0 && strcmp(grades->drop, "no") == 0) { snprintf(temp, MAX_PATH +1, "has_total.%s", grades->username); cs_set_int_value(temp, 1); count++; if(strcmp(ptr->is_letter_grade, "no") == 0) { total += grades->score; if(strcmp(ptr->extra_credit, "no") == 0 ) total_base += ptr->points; } else { if(ptr->letters) { total += look_up_points(grades->letter_id, ptr->letters); if(strcmp(ptr->extra_credit, "no") == 0) total_base += get_max_letter_points(ptr->letters); } } } } } } } } } if(total_base > 0 && total >0 && count) { if(global_grade_conf.rounding_method == WHOLE_NUMBER) one_student->total = whole_number(total /total_base *100); else one_student->total = nearest_tenth(total /total_base *100); get_one_student_grade(one_student->total, one_student->grade); } else if(total >=0 && count) { if(global_grade_conf.rounding_method == WHOLE_NUMBER) one_student->total = whole_number(total); else one_student->total = nearest_tenth(total); get_one_student_grade(one_student->total, one_student->grade); } } static double get_one_cat_total(TOTAL_GRADE *one_student, int cat_id, int *base_has_value) { double total = 0; double total_base = 0; GRADE *grades; ITEM *ptr; char temp[MAX_PATH +1]; int count =0; for(ptr = global_item_list->head;ptr; ptr = ptr->next) { if(ptr->cat_id == cat_id && strcmp(ptr->hide, "no") == 0) { if(ptr->grades) { for(grades = ptr->grades->head; grades; grades = grades->next) { if(strcmp(grades->username, one_student->username) == 0) { if(strcmp(grades->pending, "no") == 0 && strcmp(grades->excuse, "no") ==0 && strcmp(grades->drop, "no") == 0) { count++; snprintf(temp, MAX_PATH +1, "has_total.%s", grades->username); cs_set_int_value(temp, 1); if(strcmp(ptr->is_letter_grade, "no") == 0) { total += grades->score; if(strcmp(ptr->extra_credit, "no") == 0 ) total_base += ptr->points; } else { if(ptr->letters) { total += look_up_points(grades->letter_id, ptr->letters); if(strcmp(ptr->extra_credit, "no") == 0) total_base += get_max_letter_points(ptr->letters); } } } } } } } } if(total > 0 && total_base > 0 && count) { *base_has_value = 1; return total / total_base; } else if(total >= 0 && count) { *base_has_value = 0; return total; } else { *base_has_value = 0; return -99999; } } static void get_category_weight(TOTAL_GRADE *one_student) { double one_cat_total =0; double total =0; CATEGORY *ptr; int base_has_value = 0; int count =0; for(ptr = global_cat_list->head; ptr; ptr = ptr->next) { if(ptr->weight >0) { one_cat_total = get_one_cat_total(one_student, ptr->cat_id, &base_has_value); if(one_cat_total >=0 && base_has_value) { total += one_cat_total * ptr->weight; count++; } else if(one_cat_total >=0) { total += one_cat_total; count++; } } } if(total >=0 && count) { if(global_grade_conf.rounding_method == WHOLE_NUMBER) one_student->total = whole_number(total); else one_student->total = nearest_tenth(total); get_one_student_grade(one_student->total, one_student->grade); } } static void get_user_total_grade(TOTAL_GRADE *one_student) { update_drop_lowest_score(one_student); if(global_grade_conf.scoring_method == BASIC_POINTS) get_basic_points(one_student); else get_category_weight(one_student); } TOTAL_GRADE *calculate_total_grades(P_NODE *person_list, CONFIG_STRUCT *conf, const char *my_db) { TOTAL_GRADE *head =0, *current=0, *ptr; P_NODE *p_ptr; if(!read_grade_book_conf(conf, &global_grade_conf)) { cs_set_int_value("should_set_cal_method", 1); return head; } if(global_grade_conf.scoring_method == NO_CAL) { cs_set_value("calculation_method", "no_cal"); return head; } else if(global_grade_conf.scoring_method == BASIC_POINTS) cs_set_value("calculation_method", "basic_points"); else cs_set_value("calculation_method", "category_weight"); get_scales(conf, my_db); for(p_ptr = person_list; p_ptr; p_ptr = p_ptr->next) { if(p_ptr->data.group != FACULTY && *(p_ptr->data.realname) != '*') { ptr = (TOTAL_GRADE*) malloc(sizeof(TOTAL_GRADE)); if(!ptr) cs_critical_error(ERR_MALLOC_FAILED, "calculate total grades"); strncpy(ptr->username, p_ptr->data.username, MAX_USERNAME +1); ptr->total = -99999; ptr->next =0; get_user_total_grade(ptr); if(!head) head = ptr; else current->next = ptr; current = ptr; } } return head; } double whole_number(double x) { double temp; temp = x - (int)x; if(temp >= 0.5) return ceil(x); else return floor(x); } double nearest_tenth(double x) { double temp, temp_x; temp_x = x * 10; temp = whole_number(temp_x); return temp /10; }