Skip to content

Commit

Permalink
[Feature:RainbowGrades] Display Extensions in RainbowGrades (#63)
Browse files Browse the repository at this point in the history
Previously, no information on a student's late day exceptions were
provided in rainbow grades. Now, all excused extension information is
collected and stored in each student object. A row was added to the main
rainbow grades table to display a student's total number of extensions,
and a breakout table was added underneath the main table to display
information about each individual extension. Additonally, a blue outline
was added to table cell with excused extensions and text giving
extension information is listed on hover.

Picture of changes to tables:

![image](https://github.com/Submitty/RainbowGrades/assets/143554378/fa8ab970-20bb-4382-a8c0-a1baf664297c)
Hover Feature:

![image](https://github.com/Submitty/RainbowGrades/assets/143554378/cf243ae3-5f67-4f19-b379-77f791e44a51)

---------

Co-authored-by: Jaeseok Kang <[email protected]>
Co-authored-by: Barb Cutler <Barb Cutler>
  • Loading branch information
taorif25 and ziesski authored Dec 13, 2023
1 parent c788ffc commit ae4d4dd
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 20 deletions.
39 changes: 38 additions & 1 deletion main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1387,7 +1387,13 @@ void load_student_grades(std::vector<Student*> &students) {
event = "Open";
s->set_event_grade_inquiry(true);
}
s->setGradeableItemGrade_border(g,which,score,event,late_days_charged,other_note,status);
int late_day_exceptions = itr2->value("late_day_exceptions",0);
std::string reason_for_exception = itr2->value("reason_for_exception","");
if (late_day_exceptions > 0) {
event = "Extension";
s->set_event_extension(true);
}
s->setGradeableItemGrade_border(g,which,score,event,late_days_charged,other_note,status,late_day_exceptions,reason_for_exception);
}
}

Expand Down Expand Up @@ -1736,6 +1742,35 @@ void loadAllowedLateDays(std::vector<Student*> &students) {
}
}

void SaveExtensionReports(const std::vector<Student*> &students) {
system ("mkdir -p student_extension_reports");
for (size_t i=0;i<students.size();i++) {
std::vector<std::tuple<ItemGrade,std::tuple<GRADEABLE_ENUM,int> > > gradeablesWithExtensions = students[i]->getItemsWithExceptions();
std::string username = students[i]->getUserName();
std::ofstream student_ostr("student_extension_reports/"+username+".html");
assert (student_ostr.good());
if (gradeablesWithExtensions.size() == 0) {
continue;
}
student_ostr << "<h3> Excused Absence Extensions for: " << username << "</h3>" << std::endl;
student_ostr << "<table cellpadding=5 style=\"border:1px solid #aaaaaa; background-color:#ffffff;\">" << std::endl;
student_ostr << "<tr><td>Gradeable</td><td align=center>Days Extended</td><td align=center>Reason</td><td></td></tr>" << std::endl;
for (size_t i2=0;i2<gradeablesWithExtensions.size();i2++) {
ItemGrade item = std::get<0>(gradeablesWithExtensions[i2]);
GRADEABLE_ENUM g = std::get<0>(std::get<1>(gradeablesWithExtensions[i2]));
int index = std::get<1>(std::get<1>(gradeablesWithExtensions[i2]));
std::string gradeable_id = GRADEABLES[g].getID(index);
std::string gradeable_name = "";
if (GRADEABLES[g].hasCorrespondence(gradeable_id)) {
gradeable_name = GRADEABLES[g].getCorrespondence(gradeable_id).second;
}
student_ostr << "<tr><td>" << gradeable_name << "</td><td align=center>"
<< item.getLateDayExceptions() << "</td><td align=center>"
<< item.getReasonForException() << "</td></tr>" << std::endl;
}
student_ostr << "</table>" << std::endl;
}
}

int main(int argc, char* argv[]) {

Expand All @@ -1757,6 +1792,8 @@ int main(int argc, char* argv[]) {
LoadPolls(students);
SavePollReports(students);

SaveExtensionReports(students);

loadAllowedLateDays(students);

// ======================================================================
Expand Down
36 changes: 32 additions & 4 deletions output.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,7 @@ void start_table_output( bool /*for_instructor*/,
//Late days headers
student_data.push_back(counter); table.set(0,counter++,TableCell("ffffff","ALLOWED LATE DAYS"));
student_data.push_back(counter); table.set(0,counter++,TableCell("ffffff","USED LATE DAYS"));
student_data.push_back(counter); table.set(0,counter++,TableCell("ffffff","EXCUSED EXTENSIONS"));
student_data.push_back(counter); table.set(0,counter++,TableCell(grey_divider));
}
}
Expand Down Expand Up @@ -1033,12 +1034,17 @@ void start_table_output( bool /*for_instructor*/,
std::string status = this_student->getGradeableItemGrade(g,j).getStatus();
std::string event = this_student->getGradeableItemGrade(g,j).getEvent();
bool Academic_integrity = this_student->getGradeableItemGrade(g,j).getAcademicIntegrity();
std::string reason = this_student->getGradeableItemGrade(g,j).getReasonForException();
std::string gID = GRADEABLES[g].getID(j);
std::string userName = this_student->getUserName();
if (status.find("Bad") != std::string::npos) {
details += " " + status;
}
int late_days_used = this_student->getGradeableItemGrade(g,j).getLateDaysUsed();
int daysExtended = this_student->getGradeableItemGrade(g,j).getLateDayExceptions();
assert (color.size()==6);
table.set(myrow,counter++,TableCell(grade,color,1,details,late_days_used,visible,event,Academic_integrity));
std::string a = "right";
table.set(myrow,counter++,TableCell(grade,color,1,details,late_days_used,visible,event,Academic_integrity,a,1,0,reason,gID,userName,daysExtended));
}
table.set(myrow,counter++,TableCell(grey_divider));

Expand Down Expand Up @@ -1081,6 +1087,9 @@ void start_table_output( bool /*for_instructor*/,
int used = this_student->getUsedLateDays();
color = coloritcolor(allowed-used+2, 5+2, 3+2, 2+2, 1+2, 0+2);
table.set(myrow,counter++,TableCell(color,used,"",0,CELL_CONTENTS_VISIBLE,"right"));
int exceptions = this_student->getLateDayExceptions();
color = coloritcolor(exceptions,5,4,3,2,2);
table.set(myrow,counter++,TableCell(color,exceptions,"",0,CELL_CONTENTS_VISIBLE,"right"));
} else {
color="ffffff"; // default_color;
table.set(myrow,counter++,TableCell(color,""));
Expand All @@ -1089,6 +1098,7 @@ void start_table_output( bool /*for_instructor*/,
table.set(myrow,counter++,TableCell(color,""));
table.set(myrow,counter++,TableCell(color,""));
table.set(myrow,counter++,TableCell(color,""));
table.set(myrow,counter++,TableCell(color,""));
}
table.set(myrow,counter++,TableCell(grey_divider));
}
Expand Down Expand Up @@ -1188,7 +1198,7 @@ void end_table(std::ofstream &ostr, bool for_instructor, Student *s) {
// Description of border outline that are in effect
if (for_instructor || s != NULL)
{
if (for_instructor || (s != NULL && (s->get_event_bad_status() || s->get_event_grade_inquiry() || s->get_event_overridden() || s->get_event_academic_integrity())))
if (for_instructor || (s != NULL && (s->get_event_bad_status() || s->get_event_grade_inquiry() || s->get_event_overridden() || s->get_event_academic_integrity() || s->get_event_extension())))
{
ostr << "<style> .spacer {display: inline-block; width: 66px;} </style>\n";
ostr << "<table style=\"border:1px solid #aaaaaa; background-color:#FFFFFF;\">\n";
Expand All @@ -1210,7 +1220,18 @@ void end_table(std::ofstream &ostr, bool for_instructor, Student *s) {
ostr << "<span class=\"spacer\"></span>";
ostr << "</td>";
ostr << "<td style=\"border:1px solid #aaaaaa; background-color:#FFFFFF" << "; " << " \" align=\"" << "left" << "\">";
ostr << "<font size = \"-1\"> Grade override </font>";
ostr << "<font size = \"-1\"> Grade Override </font>";
ostr << "</td>";
ostr << "</tr>\n";
}
if (for_instructor || (s != NULL && s->get_event_extension()))
{
ostr << "<tr>\n";
ostr << "<td style=\"border:1px solid #aaaaaa; background-color:#FFFFFF" << "; " << "outline:4px solid #0066e0; outline-offset: -4px;" << " \" align=\"" << "left" << "\">";
ostr << "<span class=\"spacer\"></span>";
ostr << "</td>";
ostr << "<td style=\"border:1px solid #aaaaaa; background-color:#FFFFFF" << "; " << " \" align=\"" << "left" << "\">";
ostr << "<font size = \"-1\"> Excused Absence Extension </font>";
ostr << "</td>";
ostr << "</tr>\n";
}
Expand All @@ -1221,7 +1242,7 @@ void end_table(std::ofstream &ostr, bool for_instructor, Student *s) {
ostr << "<span class=\"spacer\"></span>";
ostr << "</td>";
ostr << "<td style=\"border:1px solid #aaaaaa; background-color:#FFFFFF" << "; " << " \" align=\"" << "left" << "\">";
ostr << "<font size = \"-1\"> Grade inquiry in progress </font>";
ostr << "<font size = \"-1\"> Grade Inquiry in Progress </font>";
ostr << "</td>";
ostr << "</tr>\n";
}
Expand All @@ -1243,6 +1264,13 @@ void end_table(std::ofstream &ostr, bool for_instructor, Student *s) {


if (s != NULL) {
std::ifstream istr2("student_extension_reports/"+s->getUserName()+".html");
if (istr2.good()) {
std::string tmp_s;
while (getline(istr2,tmp_s)) {
ostr << tmp_s;
}
}
std::ifstream istr("student_poll_reports/"+s->getUserName()+".html");
if (istr.good()) {
std::string tmp_s;
Expand Down
32 changes: 29 additions & 3 deletions student.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,15 @@ void Student::setGradeableItemGrade_AcademicIntegrity(GRADEABLE_ENUM g, int i, f
itr->second[i] = ItemGrade(value,late_days_used,note,status,temp,academic_integrity);
}

void Student::setGradeableItemGrade_border(GRADEABLE_ENUM g, int i, float value, const std::string &event, int late_days_used, const std::string &note, const std::string &status) {
void Student::setGradeableItemGrade_border(GRADEABLE_ENUM g, int i, float value, const std::string &event,
int late_days_used, const std::string &note, const std::string &status, int exceptions, const std::string &reason) {
assert (i >= 0 && i < GRADEABLES[g].getCount());
std::map<GRADEABLE_ENUM,std::vector<ItemGrade> >::iterator itr = all_item_grades.find(g);
assert (itr != all_item_grades.end());
assert (int(itr->second.size()) > i);
bool academic_integrity = false;

itr->second[i] = ItemGrade(value,late_days_used,note,status,event);
itr->second[i] = ItemGrade(value,late_days_used,note,status,event,academic_integrity,exceptions,reason);
}


Expand Down Expand Up @@ -211,7 +213,7 @@ float Student::GradeablePercent(GRADEABLE_ENUM g) const {
//if(!id.empty() && GRADEABLES[g].isReleased(id)){
if(!id.empty()){
m = GRADEABLES[g].getItemMaximum(id);
std::cout << "m" << m << std::endl;
// std::cout << "m" << m << std::endl;
}
float p = GRADEABLES[g].getItemPercentage(id);
float sm = GRADEABLES[g].getScaleMaximum(id);
Expand Down Expand Up @@ -429,6 +431,30 @@ int Student::getUsedLateDays() const {
return answer;
}

int Student::getLateDayExceptions() const {
int answer = 0;
for (std::map<GRADEABLE_ENUM,std::vector<ItemGrade> >::const_iterator itr = all_item_grades.begin(); itr != all_item_grades.end(); itr++) {
for (std::size_t i = 0; i < itr->second.size(); i++) {
answer += itr->second[i].getLateDayExceptions();
}
}
return answer;
}

std::vector<std::tuple<ItemGrade,std::tuple<GRADEABLE_ENUM,int> > > Student::getItemsWithExceptions() const {
std::vector<std::tuple<ItemGrade,std::tuple<GRADEABLE_ENUM,int> > > result;
for (std::map<GRADEABLE_ENUM,std::vector<ItemGrade> >::const_iterator itr = all_item_grades.begin(); itr != all_item_grades.end(); itr++) {
for (std::size_t i = 0; i < itr->second.size(); i++) {
if (itr->second[i].getLateDayExceptions()>0) {
std::tuple<GRADEABLE_ENUM,int> indexes{itr->first,i};
std::tuple<ItemGrade,std::tuple<GRADEABLE_ENUM,int> > itemInfo{itr->second[i],indexes};
result.push_back(itemInfo);
}
}
}
return result;
}

// =============================================================================================

float Student::overall_b4_academic_sanction() const {
Expand Down
15 changes: 13 additions & 2 deletions student.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ extern std::vector<float> GLOBAL_earned_late_days;

class ItemGrade {
public:
ItemGrade(float v, int ldu=0, const std::string& n="", const std::string &s="", const std::string &e="", bool ai=false) {
ItemGrade(float v, int ldu=0, const std::string& n="", const std::string &s="", const std::string &e="", bool ai=false, int de=0, const std::string& r="") {
value = v;
late_days_used = ldu;
note = n;
event = e;
academic_integrity = ai;
late_day_exceptions = de;
reason_for_exception = r;

if (s != "UNKONWN") {
status = s;
Expand Down Expand Up @@ -55,6 +57,8 @@ class ItemGrade {
return adjusted_value;
}
int getLateDaysUsed() const { return late_days_used; }
int getLateDayExceptions() const { return late_day_exceptions; }
const std::string& getReasonForException() const { return reason_for_exception; }
const std::string& getNote() const { return note; }
const std::string& getStatus() const { return status; }
const std::string& getEvent() const { return event; }
Expand All @@ -63,10 +67,12 @@ class ItemGrade {
private:
float value;
int late_days_used;
int late_day_exceptions;
bool academic_integrity;
std::string note;
std::string status;
std::string event;
std::string reason_for_exception;
};

//====================================================================
Expand Down Expand Up @@ -108,6 +114,8 @@ class Student {
int getPollsIncorrect() const;
float getPollPoints() const;
int getUsedLateDays() const;
int getLateDayExceptions() const;
std::vector<std::tuple<ItemGrade,std::tuple<GRADEABLE_ENUM,int> > > getItemsWithExceptions() const;
float getAcademicSanctionPenalty() const { return academic_sanction_penalty; }

void setCurrentAllowedLateDays(int d) { current_allowed_late_days = d; }
Expand Down Expand Up @@ -174,18 +182,20 @@ class Student {
void setTestZone(int which_test, const std::string &zone) { zones[which_test] = zone; }
void setGradeableItemGrade(GRADEABLE_ENUM g, int i, float value, int late_days_used=0, const std::string &note="",const std::string &status="");
void setGradeableItemGrade_AcademicIntegrity(GRADEABLE_ENUM g, int i, float value, bool academic_integrity, int late_days_used=0, const std::string &note="",const std::string &status="");
void setGradeableItemGrade_border(GRADEABLE_ENUM g, int i, float value, const std::string &event="", int late_days_used=0, const std::string &note="",const std::string &status="");
void setGradeableItemGrade_border(GRADEABLE_ENUM g, int i, float value, const std::string &event="", int late_days_used=0, const std::string &note="",const std::string &status="",int exceptions=0, const std::string &reason="");

void academic_sanction(const std::string &gradeable, float penalty);

void set_event_academic_integrity(bool value) {academic_integrity = value;}
void set_event_grade_inquiry(bool value) {grade_inquiry = value;}
void set_event_overridden(bool value) {overridden = value;}
void set_event_bad_status(bool value) {bad_status = value;}
void set_event_extension(bool value) {extension = value;}
bool get_event_academic_integrity() {return academic_integrity;}
bool get_event_grade_inquiry() {return grade_inquiry;}
bool get_event_overridden() {return overridden;}
bool get_event_bad_status() {return bad_status;}
bool get_event_extension() {return extension;}

// other grade-like data
void setNumericID(const std::string& r_id) { numeric_id = r_id; }
Expand Down Expand Up @@ -247,6 +257,7 @@ class Student {
bool grade_inquiry = false;
bool overridden = false;
bool bad_status = false;
bool extension = false;

// registration status
std::string section;
Expand Down
53 changes: 44 additions & 9 deletions table.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ TableCell::TableCell(const std::string& c, float d, int precision, const std::st


TableCell::TableCell(float d, const std::string& c, int precision, const std::string& n, int ldu,
CELL_CONTENTS_STATUS v,const std::string& e,bool ai, const std::string& a, int s, int /*r*/) {
CELL_CONTENTS_STATUS v,const std::string& e,bool ai, const std::string& a,
int s, int /*r*/,const std::string& reason,const std::string& gID,const std::string& userName, int daysExtended) {
assert (c.size() == 6);
assert (precision >= 0);
color=c;
Expand All @@ -93,17 +94,24 @@ TableCell::TableCell(float d, const std::string& c, int precision, const std::st
rotate = 0;
academic_integrity = ai;
event = e;
if (reason != "") {
hoverText = "class=\"hoverable-cell\" data-hover-text=\""+userName+" received a "+std::to_string(daysExtended)+" day extension due to "+reason+" on "+gID+"\" ";
}
else hoverText = "";
if (event == "Bad"){
bad_status = true;
override = inquiry = false;
override = inquiry = extension = false;
} else if ( event == "Overridden"){
override = true;
bad_status = inquiry = false;
bad_status = inquiry = extension = false;
} else if (event == "Open"){
inquiry = true;
bad_status = override = false;
bad_status = override = extension = false;
} else if (event == "Extension"){
extension = true;
inquiry = bad_status = override = false;
} else {
inquiry = bad_status = override = false;
inquiry = bad_status = override = extension = false;
}

}
Expand All @@ -120,15 +128,15 @@ std::ostream& operator<<(std::ostream &ostr, const TableCell &c) {
mark = "@";
} else if (c.override){
outline = "outline:4px solid #fcca03; outline-offset: -4px;";
} else if (c.extension){
outline = "outline:4px solid #0066e0; outline-offset: -4px;";
} else if (c.inquiry){
outline = "outline:4px dashed #1cfc03; outline-offset: -4px;";
} else if (c.bad_status){
outline = "outline:4px solid #fc0303; outline-offset: -4px;";
}

// ostr << "<td bgcolor=\"" << c.color << "\" align=\"" << c.align << "\">";
ostr << "<td style=\"border:1px solid #aaaaaa; background-color:#" << c.color << "; " << outline << " \" align=\"" << c.align << "\">";

ostr << "<td " << c.hoverText << "style=\"border:1px solid #aaaaaa; background-color:#" << c.color << "; " << outline << " \" align=\"" << c.align << "\">";
if (0) { //rotate == 90) {
ostr << "<div style=\"position:relative\"><p class=\"rotate\">";
}
Expand Down Expand Up @@ -236,10 +244,37 @@ void Table::output(std::ostream& ostr,
if (last_update != "") {
ostr << "<em>Information last updated: " << last_update << "</em><br>\n";
}

ostr << "<style>";
ostr << ".hoverable-cell {";
ostr << " position: relative;";
ostr << "}";
ostr << ".hoverable-cell:hover::before {";
ostr << " content: attr(data-hover-text);";
ostr << " position: absolute;";
ostr << " text-align: left;";
ostr << " left: 50%;";
ostr << " bottom: 85%;";
ostr << " width: 500%;";
ostr << " height: auto;";
ostr << " background-color: rgba(255, 255, 255, 0.88);"; // semi-opaque white background
ostr << " padding: 5px;";
ostr << " border: 1px solid #aaa;";
ostr << " z-index: 1;";
ostr << " display: flex;";
ostr << " align-items: left;";
ostr << " justify-content: left;";
ostr << " box-sizing: border-box;";
ostr << "}";
ostr << "</style>";


ostr << "&nbsp;<br>\n";
ostr << "<table style=\"border:1px solid #aaaaaa; background-color:#aaaaaa;\">\n";
}




if (transpose) {
for (std::vector<int>::iterator c = which_data.begin(); c != which_data.end(); c++) {
ostr << "<tr>\n";
Expand Down
Loading

0 comments on commit ae4d4dd

Please sign in to comment.