SN-Lab10.cc

// Dr. Norman's Solution to ENGG 233 Fall 2004 Lab 10
//
// Note that there will be another solution posted on Blackboard.
// Both solutions will do essentially the same things, but there may
// be some minor differences due to differences in interpretation of
// the Lab 10 instructions.

// GENERAL REMARKS:
//
//   (1) This program does not check for user input errors, such as
//   mistakes typing in info for a new student.  It also doesn't
//   check for incorrectly formatted input files.  These checks
//   would make the program safer for use, but would also require
//   a lot more code.
//
//   (2) The program is terminated after a few failed attempts to open
//   the input file.  An alternate strategy would be to let the program
//   continue with an initially empty vector of students, which would be
//   let the user try to create a new data file by adding some new students
//   to the vector.
//
//   (3) It is assumed that students don't have multi-word last names
//   or first names with spaces in them.
//
//   (4) The lab instructions don't say what to do with the
//   final_score and letter_grade members when the file input is done
//   or when a new student is added.  I chose to give these members
//   specific values (-1.0 and 'X') that could not be mistaken for
//   properly calculated values.  When FindStudent is displaying info
//   about a student, it checks to see whether the letter grade and
//   final score have been calculated.
//
//   (5) If two or more students have the same last name, FindStudent
//   just finds the one that is first in the list.  This would be bad
//   in a program given to real users!

#include <iostream>
#include <vector>
#include <string>
#include <cstdlib>
#include <fstream>

using namespace std;

struct student
{
  string last;
  string first;
  int id;
  double midterm;
  double final;
  vector<double> lab_scores;
  double final_score;
  char letter_grade;
};

// Size of lab_scores vector.
const int NUMBER_OF_LABS = 5;   

// How many tries should the user have to pick a valid file name?
const int MAX_OPEN_ATTEMPTS = 3;

// Values for final_score and letter_grade to indicate "not calculated yet".
const double INITIAL_FINAL_SCORE = -1.0;
const char INITIAL_LETTER_GRADE = 'X';

// Weights for components in a student's final score, and minimum scores
// for letter grades.
const double LAB_WEIGHT = 0.2;
const double MIDTERM_WEIGHT = 0.3;
const double FINAL_WEIGHT = 0.5;
const double MINIMUM_A_SCORE = 80.0;
const double MINIMUM_B_SCORE = 70.0;
const double MINIMUM_C_SCORE = 60.0;
const double MINIMUM_D_SCORE = 50.0;

void ReadData(vector <student> & section, int& num_Students,string& filename);
void WriteData(const vector <student> & section, 
               int num_Students, 
               const string& filename);
void AddNewStudent (vector <student> & section, int& num_Students);
void CalculateScore (vector <student> & section);
void FindStudent (const vector <student> & section);

int main()
{
  // This code is the same as what was given to students, except that
  // indentation and line breaks have been modified to make the code
  // look better in an editor window.
                
  vector<student> section;
  int num_Students=0;
  string filename;

  ReadData(section, num_Students, filename);
  char choice;
  do {
    cout << "What do you wish to do with the database?"<<endl;
    cout << "Input the relevant letter: Options are"<<endl;
    cout << "(A) Add new student" <<endl;
    cout << "(B) Calculate the final score and letter grade for the students"
         <<endl;
    cout<<"(C) Find a student and output their details"<<endl;
    cout<<"(D) End the program"<<endl;

    cin>>choice;
    switch (choice) {
    case ('A'):
      AddNewStudent(section, num_Students);
      break;
    case ('B'):
      CalculateScore(section);
      break;
    case ('C'):
      FindStudent(section);
      break;
    case ('D'):
      cout << "program ending" << endl;
      WriteData(section, num_Students, filename);
    }
  } while(choice != 'D');

  return 0;
}

void ReadData(vector<student> & section, int& num_Students, string& filename)
{
  // Attempt to get a file name and open the file.
  ifstream in; 
  cout << "Please enter the name of a file containing grade data: " << endl;
  cin >> filename;
  in.open(filename.c_str());

  // If the file did not get opened, give the user more chances to
  // pick a valid file name.
  int open_count = 1;           // So far, 1 try at opening the file.
  while (in.fail() && open_count < MAX_OPEN_ATTEMPTS) {
    cout << "Failed to open " << filename
         << "; please try another file name: " << endl;
    cin >> filename;
    in.clear();
    in.open(filename.c_str());
    open_count++;
  }
  if (in.fail()) {
    cout << "Program is quitting due to "
         << "repeated failures to open the input file." << endl;
    exit(1);
  }

  // Read all the data from the file into section, and assign safe values
  // to final score_and letter_grade members. Then close the file.
  in >> num_Students;
  section.resize(num_Students);
  for (int i = 0; i < num_Students; i++) {
    in >> section.at(i).first;
    in >> section.at(i).last;
    in >> section.at(i).id;
    in >> section.at(i).midterm;
    in >> section.at(i).final;
    section.at(i).lab_scores.resize(NUMBER_OF_LABS);
    for (int lab = 0; lab < NUMBER_OF_LABS; lab++)
      in >> section.at(i).lab_scores.at(lab);
    section.at(i).final_score = INITIAL_FINAL_SCORE;  
    section.at(i).letter_grade = INITIAL_LETTER_GRADE;
  }
  in.close();
}

void WriteData(const vector<student> & section, 
               int num_Students, 
               const string& filename)
{
  // Try to open the file, then check for failure.
  ofstream ofs;
  ofs.open((filename).c_str());
  if (ofs.fail()) {
    cout << "WARNING: Not able to save data to " << filename << endl;
    return;
  }

  // Write the data and close the file.
  ofs << num_Students << '\n';
  for (int i = 0; i < num_Students; i++) {
    ofs << section.at(i).first;
    ofs << ' ' << section.at(i).last;
    ofs << ' ' << section.at(i).id;
    ofs << ' ' << section.at(i).midterm;
    ofs << ' ' << section.at(i).final;
    for (int lab = 0; lab < NUMBER_OF_LABS; lab++)
      ofs << ' ' << section.at(i).lab_scores.at(lab);
    ofs << '\n';
  }
  ofs.close();
}

void AddNewStudent (vector<student> & section, int& num_Students)
{
  // No checks for input failure!  If the user makes a typing mistake
  // the program will be in serious trouble.
  student new_student;
  cout << "Please enter new student's first name: " ;
  cin >> new_student.first;
  cout << "Please enter last name: " ;
  cin >> new_student.last;
  cout << "Please enter ID number: " ;
  cin >> new_student.id;
  cout << "Please enter midterm mark: " ;
  cin >> new_student.midterm;
  cout << "Please enter final exam mark: " ;
  cin >> new_student.final;
  cout << "Please enter " << NUMBER_OF_LABS 
       << " lab marks, separated by spaces:\n";
  new_student.lab_scores.resize(NUMBER_OF_LABS);
  for (int lab = 0; lab < NUMBER_OF_LABS; lab++)
    cin >> new_student.lab_scores.at(lab);
  new_student.final_score = INITIAL_FINAL_SCORE;
  new_student.letter_grade = INITIAL_LETTER_GRADE;

  section.push_back(new_student);
  num_Students++;
}

void CalculateScore (vector<student> & section)
{
  // This is a straightforward application of the rules given in the
  // Lab 10 instructions.

  for (int i = 0; i < section.size(); i++) {
    double course_total, lab_total = 0;
    course_total = FINAL_WEIGHT * section.at(i).final;
    course_total += MIDTERM_WEIGHT * section.at(i).midterm;
    for (int lab = 0; lab < NUMBER_OF_LABS; lab++)
      lab_total += section.at(i).lab_scores.at(lab);
    course_total += LAB_WEIGHT * lab_total;
    section.at(i).final_score = course_total;
    if (course_total >= MINIMUM_A_SCORE)
      section.at(i).letter_grade = 'A';
    else if (course_total >= MINIMUM_B_SCORE)
      section.at(i).letter_grade = 'B';
    else if (course_total >= MINIMUM_C_SCORE)
      section.at(i).letter_grade = 'C';
    else if (course_total >= MINIMUM_D_SCORE)
      section.at(i).letter_grade = 'D';
    else
      section.at(i).letter_grade = 'F';
  }
}

void FindStudent (const vector<student> & section)
{
  // Show all last names to user, then ask for a last name as input.
  cout << "Last names of students are:\n";
  for (int i = 0; i < section.size(); i++)
    cout << "  " << section.at(i).last << endl;
  string name_to_find;
  cout << "Please enter the last name of a student: ";
  cin >> name_to_find;

  // Search for first appearance of the last name in section.  (This
  // should be FIXED to handle the possibility that two students have
  // the same last name.)
  int i_of_name = 0;
  while (i_of_name < section.size() 
         && name_to_find != section.at(i_of_name).last)
    i_of_name++;

  // If a match was found, display information for that student.
  if (i_of_name == section.size())
    cout << "Sorry, last name " << name_to_find 
         << " is not in the list." << endl;
  else {
    cout << "\nFull name: " << section.at(i_of_name).first
         << ' ' << section.at(i_of_name).last << endl;
    if (section.at(i_of_name).letter_grade == INITIAL_LETTER_GRADE)
      cout << "Final score and letter grade not yet calculated.\n\n";
    else
      cout << "Final score: " << section.at(i_of_name).final_score
           << ".  Letter grade: " << section.at(i_of_name).letter_grade
           << ".\n\n";
  }
}

Generated by GNU enscript 1.6.1.