542 lines
16 KiB
C++
542 lines
16 KiB
C++
// License: AGPLv3 or later. https://www.gnu.org/licenses/licenses.html
|
|
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
# include "general_functions.h"
|
|
# include "abc.h"
|
|
|
|
using namespace std;
|
|
|
|
const int MAX_ATTEMPTS_ALLOWED = 3;
|
|
|
|
input_files_name perform_checks_on_args(
|
|
int argc, char *argv[])
|
|
{
|
|
if (argc != 5)
|
|
{
|
|
cerr << "Expected 5 arguments, received " << argc
|
|
<< " argument(s). Please provide exactly 5 arguments and "
|
|
"in correct order i.e.:\n"
|
|
"1. Program name/path (received automatically)\n"
|
|
"2. Students File such as Students.txt\n"
|
|
"3. Subjects File such as Subjects.txt\n"
|
|
"4. Teachers File such as Teachers.txt\n"
|
|
"5. Output-file such as Results.txt\n"
|
|
"\n"
|
|
"Run program similar to this:\n"
|
|
"$ ./ABC Students.txt Subjects.txt Teachers.txt "
|
|
"Results.txt\n";
|
|
exit(1);
|
|
}
|
|
else
|
|
{
|
|
input_files_name args;
|
|
args.students = argv[1];
|
|
args.subjects = argv[2];
|
|
args.teachers = argv[3];
|
|
args.results = argv[4];
|
|
return args;
|
|
}
|
|
}
|
|
|
|
input_files_textlines read_input_files(
|
|
input_files_name file_names)
|
|
{
|
|
input_files_textlines files_textlines;
|
|
files_textlines.students = get_textfile_lines(
|
|
file_names.students);
|
|
files_textlines.subjects = get_textfile_lines(
|
|
file_names.subjects);
|
|
files_textlines.teachers = get_textfile_lines(
|
|
file_names.teachers);
|
|
|
|
print_three_newlines();
|
|
|
|
return files_textlines;
|
|
}
|
|
|
|
vector<student> generate_students_struct(
|
|
vector<string> students_lines)
|
|
{
|
|
cout << "Processing students file text:" << endl;
|
|
|
|
vector<student> students;
|
|
int line_number = 0;
|
|
for (auto student_line:students_lines)
|
|
{
|
|
line_number++;
|
|
|
|
// Ignoring empty line. Giving no warning/error for it either.
|
|
if (student_line.size() == 0)
|
|
continue;
|
|
|
|
try
|
|
{
|
|
student a_student;
|
|
string ability_string;
|
|
string consistency_string;
|
|
string subject_sring;
|
|
|
|
istringstream line(student_line);
|
|
getline(line, a_student.name, ',');
|
|
getline(line, a_student.code, ',');
|
|
getline(line, ability_string, ',');
|
|
a_student.ability = stoi(ability_string);
|
|
getline(line, consistency_string, ',');
|
|
a_student.consistency = stoi(consistency_string);
|
|
getline(line, a_student.program, ':');
|
|
while(getline(line, subject_sring, ','))
|
|
{
|
|
int subject_code = stoi(subject_sring);
|
|
if (subject_code > 0)
|
|
a_student.subjects.push_back(subject_code);
|
|
else
|
|
throw "Anything that you can";
|
|
}
|
|
|
|
if (a_student.name.size() == 0
|
|
or a_student.code.size() != 6
|
|
or is_string_some_positive_number(
|
|
a_student.code) == false
|
|
or a_student.ability < 0
|
|
or a_student.ability > 100
|
|
or a_student.consistency < 0
|
|
or a_student.consistency > 15
|
|
or a_student.program.size() == 0
|
|
or a_student.subjects.size() == 0)
|
|
{
|
|
throw "All the things that you can";
|
|
}
|
|
|
|
students.push_back(a_student);
|
|
|
|
cout << "Student Name: " << a_student.name << '\n'
|
|
<< "Code: " << a_student.code << '\n'
|
|
<< "Ability: " << a_student.ability << '\n'
|
|
<< "Consistency: " << a_student.consistency << '\n'
|
|
<< "Program Name: " << a_student.program << '\n'
|
|
<< "Subjects code: ";
|
|
for (auto subject_code: a_student.subjects)
|
|
{
|
|
cout << subject_code << " ";
|
|
}
|
|
cout << '\n' << endl;
|
|
|
|
}
|
|
catch(...)
|
|
{
|
|
cerr << "Non-Fatal Error Occured while processing student"
|
|
<< " file lines into student structs. However as per "
|
|
<< "specifiction, skipping proccesing anymore "
|
|
<< "students.\n"
|
|
<< "Line number: " << line_number << " is not in "
|
|
<< "proper format.\n"
|
|
<< "Actual Line:\n"
|
|
<< student_line << "\n"
|
|
<< "Expected line format:\n"
|
|
<< "Name,Student code,Ability,Consistency,Program "
|
|
<< "name:Subject list\n"
|
|
<< "Where,\n"
|
|
<< "1. Name is an non-empty string.\n"
|
|
<< "2. Student code is 6 *digit* string.\n"
|
|
<< "3. Ability is an integer in range 0 to 100.\n"
|
|
<< "4. Consistency is an integer in range 0 to 15.\n"
|
|
<< "5. Program name is an non-empty string.\n"
|
|
<< "6. Subject list is the list of comma-seprated "
|
|
<< "positive integers representing subject codes.\n";
|
|
break;
|
|
}
|
|
}
|
|
|
|
print_three_newlines();
|
|
|
|
return students;
|
|
}
|
|
|
|
vector<subject> generate_subjects_struct(
|
|
vector<string> subjects_lines)
|
|
{
|
|
cout << "Processing subjects file text:" << endl;
|
|
|
|
vector<subject> subjects;
|
|
int line_number = 0;
|
|
for (auto subject_line: subjects_lines)
|
|
{
|
|
line_number++;
|
|
|
|
// Ignoring empty line. Giving no warning/error for it either.
|
|
if (subject_line.size() == 0)
|
|
continue;
|
|
|
|
try
|
|
{
|
|
subject a_subject;
|
|
string difficulty_string;
|
|
string variability_string;
|
|
|
|
istringstream line(subject_line);
|
|
getline(line, a_subject.name, ',');
|
|
getline(line, difficulty_string, ',');
|
|
a_subject.difficulty = stoi(difficulty_string);
|
|
getline(line, variability_string);
|
|
a_subject.variability = stoi(variability_string);
|
|
|
|
if (a_subject.name.size() == 0
|
|
or a_subject.difficulty < -15
|
|
or a_subject.difficulty > 15
|
|
or a_subject.variability < -3
|
|
or a_subject.variability > 3)
|
|
{
|
|
throw "Buzzwords";
|
|
}
|
|
|
|
subjects.push_back(a_subject);
|
|
|
|
cout << "Subject Name: " << a_subject.name << '\n'
|
|
<< "Difficulty: " << a_subject.difficulty << '\n'
|
|
<< "Variability: " << a_subject.variability << '\n'
|
|
<< endl;
|
|
|
|
}
|
|
catch (...)
|
|
{
|
|
cerr << "Fatal error occured while processing subjects "
|
|
<< "file lines into subject structs. Line: "
|
|
<< line_number << " is not in proper format.\n"
|
|
<< "Actual line:\n"
|
|
<< subject_line << "\n"
|
|
<< "Expected line format: \n"
|
|
<< "Name,Difficulty,Variability\n"
|
|
<< "Where,\n"
|
|
<< "1. Name is a non-empty string.\n"
|
|
<< "2. Diffuculty is interger in range -15 to 15.\n"
|
|
<< "3. Variability is integer in range -3 to 3.\n";
|
|
exit(3);
|
|
}
|
|
}
|
|
|
|
print_three_newlines();
|
|
|
|
return subjects;
|
|
}
|
|
|
|
vector<teacher> generate_teachers_struct(
|
|
vector<string> teachers_lines)
|
|
{
|
|
cout << "Processing teachers file text:" << endl;
|
|
|
|
vector<teacher> teachers;
|
|
int line_number = 0;
|
|
for (auto teacher_line: teachers_lines)
|
|
{
|
|
line_number++;
|
|
|
|
// Ignoring empty line. Giving no warning/error for it either.
|
|
if (teacher_line.size() == 0)
|
|
continue;
|
|
|
|
try
|
|
{
|
|
teacher a_teacher;
|
|
istringstream line(teacher_line);
|
|
string toughness_string;
|
|
string variability_string;
|
|
string subject_string;
|
|
|
|
getline(line, a_teacher.name, ',');
|
|
getline(line, toughness_string, ',');
|
|
a_teacher.toughness = stoi(toughness_string);
|
|
getline(line, variability_string, ':');
|
|
a_teacher.variability = stoi(variability_string);
|
|
while(getline(line, subject_string, ','))
|
|
{
|
|
int subject_code = stoi(subject_string);
|
|
if (subject_code > 0)
|
|
a_teacher.subjects.push_back(subject_code);
|
|
else
|
|
throw "Cheesy praises at teacher";
|
|
}
|
|
|
|
if (a_teacher.name.size() == 0
|
|
or a_teacher.toughness < -15
|
|
or a_teacher.toughness > 15
|
|
or a_teacher.variability < -3
|
|
or a_teacher.variability > 3
|
|
or a_teacher.subjects.size() == 0)
|
|
{
|
|
throw "some swag";
|
|
}
|
|
|
|
cout << "Teacher Name: " << a_teacher.name << '\n'
|
|
<< "Toughness: " << a_teacher.toughness << '\n'
|
|
<< "Variability: " << a_teacher.variability << '\n'
|
|
<< endl;
|
|
|
|
teachers.push_back(a_teacher);
|
|
}
|
|
catch (...)
|
|
{
|
|
cerr << "Fatal error occured while processing teachers "
|
|
<< "file lines into teacher structs. Line: "
|
|
<< line_number << " is not in proper format.\n"
|
|
<< "Actual line:\n"
|
|
<< teacher_line << "\n"
|
|
<< "Expected line format: \n"
|
|
<< "Name,Toughness,Variability:Subjects List\n"
|
|
<< "Where,\n"
|
|
<< "1. Name is a non-empty string.\n"
|
|
<< "2. Toughness is interger in range -15 to 15.\n"
|
|
<< "3. Variability is integer in range -3 to 3.\n"
|
|
<< "4. Subects list is comma-seprated list of positive"
|
|
<< "integers representing subjects codes.\n";
|
|
exit(4);
|
|
}
|
|
}
|
|
|
|
print_three_newlines();
|
|
|
|
return teachers;
|
|
}
|
|
|
|
void add_teachers_vector_index_number_to_subjects_struct(
|
|
vector<teacher> &teachers,
|
|
vector<subject> &subjects)
|
|
{
|
|
for (int i=0; i < teachers.size(); i++)
|
|
{
|
|
for (auto subject_code: teachers[i].subjects)
|
|
{
|
|
if (subject_code <= subjects.size())
|
|
{
|
|
/* -1 and +1 since indexes starts from 0.
|
|
*/
|
|
subjects[subject_code-1].teachers.push_back(i+1);
|
|
}
|
|
else
|
|
{
|
|
cerr << "Fatal error. Subject code " << subject_code
|
|
<< " of teacher: " << i+1 << " i.e. "
|
|
<< teachers[i].name << " is more than total "
|
|
<< "number of subjects i.e. " << subjects.size()
|
|
<< '\n';
|
|
exit(5);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void process_students_and_generate_results(
|
|
vector<student> &students,
|
|
vector<subject> &subjects,
|
|
vector<teacher> &teachers)
|
|
{
|
|
cout << "Processing students results:" << endl;
|
|
|
|
int student_number = 0;
|
|
for (auto &a_student: students)
|
|
{
|
|
student_number++;
|
|
int attempts = 0;
|
|
|
|
cout << "Processing student " << student_number << " i.e. "
|
|
<< a_student.name << ":" << endl;
|
|
|
|
for (int i=0; i < a_student.subjects.size(); i++)
|
|
{
|
|
attempts++;
|
|
result a_result;
|
|
|
|
a_result.subject_code = a_student.subjects[i];
|
|
if (a_result.subject_code > subjects.size())
|
|
{
|
|
cerr << "Fatal error. Subject code "
|
|
<< a_result.subject_code << " of student: "
|
|
<< student_number << " i.e. " << a_student.name
|
|
<< " is more than total number of subjects i.e. "
|
|
<< subjects.size() << '\n';
|
|
exit(5);
|
|
}
|
|
// -1 since index starts at 0
|
|
auto &a_subject = subjects[a_result.subject_code-1];
|
|
a_result.subject_name = a_subject.name;
|
|
|
|
a_result.teacher_code = pick_teacher_randomly(
|
|
a_subject.teachers);
|
|
// -1 since index starts at 0
|
|
auto &a_teacher = teachers[a_result.teacher_code-1];
|
|
a_result.teacher_name = a_teacher.name;
|
|
|
|
int attempts_based_marks_modifier = (attempts-1)*5;
|
|
a_result.mean = (
|
|
a_student.ability
|
|
- a_subject.difficulty
|
|
- a_teacher.toughness
|
|
+ attempts_based_marks_modifier);
|
|
|
|
a_result.standard_deviation = (
|
|
a_student.consistency
|
|
- a_subject.variability
|
|
- a_teacher.variability);
|
|
a_result.standard_deviation = absolute_number(
|
|
a_result.standard_deviation);
|
|
|
|
int minimum_possible_marks = (
|
|
a_result.mean - a_result.standard_deviation);
|
|
minimum_possible_marks = bigger_number(
|
|
minimum_possible_marks, 0);
|
|
minimum_possible_marks = smaller_number(
|
|
minimum_possible_marks, 100);
|
|
|
|
int maximum_possible_marks = (
|
|
a_result.mean + a_result.standard_deviation);
|
|
maximum_possible_marks = smaller_number(
|
|
maximum_possible_marks, 100);
|
|
maximum_possible_marks = bigger_number(
|
|
maximum_possible_marks, 0);
|
|
|
|
int marks = random_int(
|
|
minimum_possible_marks,
|
|
maximum_possible_marks);
|
|
a_result.marks = marks;
|
|
a_result.grade = return_grade_for_float((float)marks);
|
|
a_student.results.push_back(a_result);
|
|
|
|
cout << a_result.subject_name << " ("
|
|
<< a_result.subject_code << ") by "
|
|
<< a_result.teacher_name << " ("
|
|
<< a_result.teacher_code << ") Attempt: "
|
|
<< attempts << " Mean: "
|
|
<< a_result.mean << " Standard Deviation: "
|
|
<< a_result.standard_deviation << " Maximum: "
|
|
<< maximum_possible_marks << " Minimum: "
|
|
<< minimum_possible_marks << " Marks: "
|
|
<< a_result.marks << " Grade: " << a_result.grade
|
|
<< endl;
|
|
|
|
// Supplementary provided.
|
|
if (marks >= 45 and marks < 50)
|
|
{
|
|
int supplementary_marks_modifier = 5;
|
|
a_result.mean += supplementary_marks_modifier;
|
|
marks = random_int(
|
|
minimum_possible_marks,
|
|
maximum_possible_marks)
|
|
+ supplementary_marks_modifier;
|
|
a_result.marks = marks;
|
|
|
|
if (marks >= 50)
|
|
{
|
|
a_result.grade = "SP";
|
|
}
|
|
else
|
|
a_result.grade = "F";
|
|
|
|
a_student.results.push_back(a_result);
|
|
|
|
cout << "Supplementary Marks: " << a_result.marks
|
|
<< " Grade: " << a_result.grade << endl;
|
|
}
|
|
|
|
// Fail.
|
|
if (a_result.grade == "F")
|
|
{
|
|
/* Max attempts allowed reached. Excluded from college.
|
|
* No more subjects are taught.
|
|
*/
|
|
if (attempts >= MAX_ATTEMPTS_ALLOWED)
|
|
{
|
|
a_student.overall_marks += marks;
|
|
cout << endl; //cosmetic hotfix
|
|
a_student.overall_grade_or_status = "F";
|
|
a_student.subjects_studied += 1;
|
|
break;
|
|
}
|
|
// Repeat same subject
|
|
else
|
|
{
|
|
i--;
|
|
}
|
|
}
|
|
// Study next subject with attempt reset to 0.
|
|
else
|
|
{
|
|
a_student.overall_marks += marks;
|
|
attempts = 0;
|
|
a_student.subjects_studied += 1;
|
|
}
|
|
|
|
// line between every subject attempt
|
|
cout << endl;
|
|
}
|
|
|
|
a_student.percentage = ((float)
|
|
((a_student.overall_marks*100)/\
|
|
a_student.subjects_studied)/100.0);
|
|
|
|
if (a_student.overall_grade_or_status == "?")
|
|
{
|
|
a_student.overall_grade_or_status =\
|
|
return_grade_for_float(a_student.percentage);
|
|
}
|
|
|
|
cout << "Overall Percentage: " << a_student.percentage
|
|
<< " Overall Grade: " << a_student.overall_grade_or_status
|
|
<< '\n' << endl;
|
|
}
|
|
}
|
|
|
|
void write_results_to_output_file(
|
|
vector<student> &students, std::string results_file_name)
|
|
{
|
|
ofstream results_file(results_file_name);
|
|
|
|
if (!results_file.good())
|
|
{
|
|
cerr << "Fatal Error: Could not write results to output-file:"
|
|
<< results_file_name << "\n"
|
|
<< "Possibly do not have write acces to directory\n";
|
|
exit(6);
|
|
}
|
|
|
|
for (auto a_student: students)
|
|
{
|
|
results_file << a_student.code << "-" << a_student.name << ","\
|
|
<< a_student.program << "-" << a_student.percentage << "-"\
|
|
<< a_student.overall_grade_or_status << ":";
|
|
for (auto a_result:a_student.results)
|
|
{
|
|
results_file << a_result.subject_code << "-"\
|
|
<< a_result.subject_name << ","\
|
|
<< a_result.teacher_code << "-"\
|
|
<< a_result.teacher_name << ","\
|
|
<< a_result.mean << "+-"\
|
|
<< a_result.standard_deviation << "=>"\
|
|
<< a_result.marks << "-" << a_result.grade << ";";
|
|
}
|
|
results_file << "\n";
|
|
}
|
|
results_file.close();
|
|
}
|
|
|
|
int pick_teacher_randomly(vector<int> teachers_codes)
|
|
{
|
|
int selected_teacher_index = random_int(
|
|
0, (teachers_codes.size()-1));
|
|
return teachers_codes[selected_teacher_index];
|
|
}
|
|
|
|
string return_grade_for_float(float percentage)
|
|
{
|
|
if (percentage >= 85.0)
|
|
return "HD";
|
|
else if (percentage >= 75.0)
|
|
return "D";
|
|
else if (percentage >= 65.0)
|
|
return "C";
|
|
else if (percentage >= 50.0)
|
|
return "P";
|
|
else
|
|
return "F";
|
|
}
|