/
Gradebook.cpp
237 lines (196 loc) · 6.81 KB
/
Gradebook.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include "Gradebook.h"
using namespace std;
Gradebook::Gradebook()
{
}
Gradebook::~Gradebook()
{
}
void Gradebook::addCourse(
ifstream& file,
const string& courseName,
const int& year,
const Semester& semester)
{
int initialSize = getSize();
Course course = Course(*this, file, courseName, year, semester);
m_courses.insert(course);
int studentsAdded = getSize() - initialSize;
cout << "Successfully added "
<< course.m_courseName << " "
<< SemesterString[course.m_semester] << " "
<< course.m_year << endl;
cout << "Unique students in repository before adding: " << initialSize << endl;
cout << "Previously untracked students added (by User ID): " << studentsAdded << endl;
}
// print all data (for testing)
void Gradebook::printAll() const
{
for(set<Course>::const_iterator course = m_courses.begin(); course != m_courses.end(); ++course)
{
course->printAll();
}
}
void Gradebook::exportStudent(const string studentID, string saveLocation) const
{
vector<string> newFields;
vector<string> newData;
for(set<Course>::const_iterator course = m_courses.begin(); course != m_courses.end(); ++course)
{
string courseName = course->m_courseName;
int year = course->m_year;
string semester = SemesterString[course->m_semester];
int idField = 0;
int nameField = 0;
//get the user ID / Student ID field
for(Student::const_iterator field = course->m_fields.begin(); field != course->m_fields.end(); ++field)
{
string f = *field;
if (f.compare("Student Id") == 0 || f.compare("User ID") == 0) {
break;
}
else idField++;
}
//iterating through the students
for(vector<Student>::const_iterator student = course->m_students.begin(); student != course->m_students.end(); ++student)
{
Student s = *student;
//checking every student ID for a match
if (s[idField].compare(studentID) == 0)
{
if (newData.size() == 0) newData.push_back(s[idField]); //first entry of the created csv is always the id
if (newFields.size() == 0) newFields.push_back("User ID");
//get iterators for the fields and for the data
Student::const_iterator sit = s.begin();
Student::const_iterator f = course->m_fields.begin();
//we can iterate through both vectors together since the fields and data should always match at a given index
while ((sit != s.end()) && (f != course->m_fields.end()))
{
string fval = *f; //current header title
std:transform(fval.begin(), fval.end(), fval.begin(), ::tolower); //makes fval lower case for more accurate checking
//we only keep the fields that aren't related to the names and user ids
if (fval.find("name") == string::npos && fval.find("student id") == string::npos && fval.find("user id") == string::npos)
{
stringstream ss;
//building new header titles in the form courseName-semester-year-title
ss << courseName << "-" << semester << "-" << year << "-" << *f;
string sout = ss.str();
sout.erase(remove(sout.begin(), sout.end(), '\r'), sout.end()); //removes newlines
newFields.push_back(sout);
ss.str("");
string dout = *sit;
dout.erase(remove(dout.begin(), dout.end(), '\r'), dout.end()); //removes newlines
newData.push_back("\""+dout+"\"");
}
f++;
sit++;
}
}
}
}
if (newData.size() == 0) {
cout << "No match found for student " << studentID << endl;
return;
}
//writing out
ofstream outfile((saveLocation).c_str());
int ctr = 0;
//writing the fields
for (vector<string>::const_iterator fw = newFields.begin(); fw != newFields.end(); ++fw) {
outfile << *fw;
if (ctr != newFields.size()-1) outfile << ",";
ctr++;
}
ctr = 0;
outfile << endl;
//writing the data
for (vector<string>::const_iterator dr = newData.begin(); dr != newData.end(); ++dr) {
outfile << *dr;
if (ctr != newData.size()-1) outfile << ",";
ctr++;
}
outfile.close();
cout << "Exported student " << studentID << " to " << saveLocation << endl;
}
Gradebook::Course::Course(Gradebook& gb, ifstream& file, const string& courseName, const int& year, const Semester& semester)
:m_gb(gb), m_courseName(courseName), m_year(year), m_semester(semester)
{
string line;
while (getline(file, line)) // each line of the CSV
{
m_students.push_back(Student()); // add a student to the course
stringstream linestream(line);
while (!linestream.eof()) {
string record; // one comma-separated element
while(linestream.peek()==' ') linestream.get(); // remove space after comma
if(linestream.peek() == '"') // if the record is surrounded by quotes
{
linestream.get();
getline(linestream, record, '"');
while(linestream.peek() == '"') { // handle inner double quotes
record += linestream.get();
string piece;
getline(linestream, piece, '"');
record += piece;
}
if(linestream.peek()==',') linestream.get();
}
else
{
getline(linestream, record, ',');
}
m_students.back().push_back(record);
}
}
file.close();
// Move first element to m_fields
m_fields = *m_students.begin();
m_students.erase(m_students.begin());
//takes the user IDs for every students and adds to a set of all students in the gradebook for easy counting with no duplicates
int counter = 0; //for finding the user ID index
for (Student::const_iterator field = m_fields.begin(); field != m_fields.end(); ++field) {
string fval = *field; //current header title
std:transform(fval.begin(), fval.end(), fval.begin(), ::tolower); //makes fval lower case for more accurate checking
//finding the user id field
if (fval.find("student id") != string::npos || fval.find("user id") != string::npos) break; //stop when we hit the id field
else counter++; //stop when we find user id header
}
//add user IDs for all students to the set of student IDs
int studentCount = 0;
for (vector< Student >::const_iterator student = m_students.begin(); student != m_students.end(); ++student) {
studentCount++;
Student s = *student;
gb.student_set.insert(s[counter]);
}
cout << "\nData read for " << studentCount << " students." << endl;
}
void Gradebook::Course::printAll() const
{
cout << endl << SemesterString[m_semester] << " " << m_year << endl;
for(Student::const_iterator field = m_fields.begin(); field != m_fields.end(); ++field)
{
cout << *field << ", ";
}
cout << "\b \b " << endl;
for(vector< Student >::const_iterator student = m_students.begin(); student != m_students.end(); ++student)
{
for(Student::const_iterator item = student->begin(); item != student->end(); ++item)
{
cout << *item << ", ";
}
cout << "\b \b " << endl;
}
}
Gradebook::Course::~Course()
{
}
bool operator<(const Gradebook::Course& a, const Gradebook::Course& b)
{
if(a.m_year < b.m_year) return true;
if(a.m_year > b.m_year) return false;
return a.m_semester < b.m_semester;
}