Created
February 13, 2023 22:22
-
-
Save kspillane/2d9c3e35448d78e14d203224160a4984 to your computer and use it in GitHub Desktop.
Code for my required assignment for Study.com CS 112. A HRMS implemented in C++
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Study.com - CS 112 Intro to Programming in C++ Assignment - HR Resource Manager.cpp : This file contains the 'main' function.Program execution begins and ends there. | |
* by Kyle Spillane - spillman@gmail.com | |
* | |
* This program implements a HR Information System as requested by the assignment prompt. | |
* | |
* Main Program Operation: | |
* | |
* When launched, the program will first check if erm_data.txt exists in the working directory. | |
* If not, it will prompt the user to login with a master administrator password | |
* | |
* Master Administrator Password is : 4dm1n | |
* | |
* This is onlly done if the file does not exist, or if the file does exist, but no users are defined in the file. | |
* If an admin login is done, the program will prompt the user to generate an initial user, who will be set as HR. | |
* After the user is generated and saved to the file, the user is prompted to login as a valid user, and the csv data | |
* file is read and each row (employee object) is loaded into vector<Employee> employees. User interactions simply | |
* manipulate the objects in the vector, which is fully written back to the file after each manipulation. | |
* | |
* Once logged in, the user will have access to main functions, based on their user type, as descriped by the assignment prompt. | |
* | |
* Custom Classes and General Program Structure: | |
* | |
* Employee data is stored in a csv file, each row an employee, each field an object member. | |
* Employee data is stored using the Employee class as a container to organize the data members. | |
* | |
* The UserInteraction class handles the interaction betwene the user and the program. To keep things simple, the program's main | |
* loop is implemented in the UserInteraction class constructor. | |
* | |
* TODOs (for program improvement, outside of the scope of the assignment): | |
* | |
* 1. Hash password data - Currently user passwords are stored in the clear. I considered using the openssl | |
* libraries to implement a SHA2 hasing function, but that is probably outside the scope of this class. | |
* | |
* 2. Use something other than csv file to hold data. If we get say 100 employees, this program will bog down. | |
* I'd use either some sort of RDBMS or at least sqlite to handle it. If we used SQL, we coudl simplify | |
* many program functions, since searching, modifying, and viewing records is included in SQL. Again, beyons the scope here. | |
* | |
* 3. Since ther was no requirements for storing the data, the data is primary fields, so it is possible | |
* that two records could be identical. Using SQL with primary keys would help resolve this, or adding extra | |
* code to check for this would be do-able, but it wasn't mentioned in the prompt, so I am not going to do it. | |
* | |
* 4. There lots of room for reducing code redundancy, I think I have a reasonable amount for what was asked, but | |
* table and meny generating codes could be rolled into their own functions and called just to format output. | |
* | |
* NOTE ABOUT THE STUDENT: | |
* | |
* My background is mostly in interpreted languages like Bash, Powershell, and Python, although I did have to | |
* take a course in x86 assembly when I was an engineering student. This is the most I have ever done | |
* in a C family language, and while I feel I could have done this in half the time if I was writing in in Python | |
* that's simply due to lack of experience. | |
* | |
* Overall, I enjoyed this project and am proud of it. I might try to pick up some knowledge in C#! | |
* | |
*/ | |
// Libraries used | |
#include <iostream> | |
#include <string> | |
#include <vector> | |
#include <algorithm> | |
#include <fstream> | |
#include <sstream> | |
#include <iomanip> | |
#include <cctype> | |
#include <limits> | |
using namespace std; | |
//Enumerated User Type, to keep it easy to read | |
enum UserType { | |
HR, | |
Management, | |
Individual | |
}; | |
//Converts string to UserType datatype | |
UserType stringToUserType(const std::string& input) { | |
if (input == "0") return UserType::HR; | |
else if (input == "1") return UserType::Management; | |
else if (input == "2") return UserType::Individual; | |
else return UserType::Individual; // Return least priv. type as the default value | |
} | |
//Employee class holds employee object data | |
class Employee { | |
public: | |
string firstname; | |
string lastname; | |
string id; | |
UserType role; | |
string login; | |
string password; | |
}; | |
//UserInteraction class handles | |
//the events of the user interacting | |
//with the program. | |
class UserInteraction { | |
//This class members indicate which user | |
//is currently logged in to the system | |
UserType active_user; | |
string active_user_name; | |
string active_user_id; | |
//Master employee list | |
vector<Employee> employees; | |
//open_file checks for existence of te data file | |
//creates it if it is not present | |
//if it is present, reads data out of it | |
//and loads data into master Employee list, employees | |
void open_file() { | |
fstream file; | |
file.open("erm_data.txt", ios::in); | |
if (!file.is_open()) { | |
cout << "No data file found! Attempting to create one." << endl; | |
ofstream newfile("erm_data.txt"); //Create the data file | |
newfile.close(); | |
file.open("erm_data.txt", ios::in); | |
if (!file.is_open()) { | |
//If we are here, the file couldn't be created. Probably a permissions issue | |
cout << "There was a problem creating the data file!" << endl << "This program will now exit." << endl << endl; | |
cout << "Press any key to exit..."; | |
exit(1); | |
}; | |
string user_pass; | |
cout << "Since there are no valid users, you must login as an admin to create a new HR user." << endl; | |
cout << "Enter Administrator Password: "; | |
getline(cin, user_pass); | |
//Prompt for master admin login | |
if (user_pass != "4dm1n") { | |
cout << "You have entered an incorrect administrator password." << endl; | |
cout << "Please refer to head comments in source code for the default." << endl; | |
cout << "Press any key to exit..." << endl; | |
cin; | |
exit(1); | |
}; | |
//Initial user setup and force user to be HR | |
Employee initial_user = create_user(); | |
initial_user.role = HR; | |
employees.push_back(initial_user); | |
if (save_data()) { | |
cout << "Data File Initialized" << endl; | |
}; | |
return; | |
}; | |
Employee datain; //Temp employee object to be used for passing data from file to master vector | |
string line; | |
//Read the data file | |
while (getline(file, line)) { | |
stringstream s(line); | |
string field; | |
//Break each row into a stream of comma seperated values, and load each field into a member | |
getline(s, field, ','); | |
datain.firstname = field; | |
getline(s, field, ','); | |
datain.lastname = field; | |
getline(s, field, ','); | |
datain.id = field; | |
getline(s, field, ','); | |
datain.role = stringToUserType(field); //Converts from string to custom type | |
getline(s, field, ','); | |
datain.login = field; | |
getline(s, field); | |
datain.password = field; | |
//Load the new Employee type into the master employee list | |
employees.push_back(datain); | |
}; | |
file.close(); | |
if (employees.size() < 1) { | |
// The file is present, but there's no users in it, for some reason, create one | |
cout << "There are no employees in the data. You must login as administrator and create one who will be the HR user." << endl; | |
string user_pass; | |
cout << "Enter Administrator Password: "; | |
getline(cin, user_pass); | |
if (user_pass != "4dm1n") { | |
cout << "You have entered an incorrect administrator password." << endl; | |
cout << "Please refer to head comments in source code for the default." << endl; | |
cout << "Press any key to exit..." << endl; | |
cin; | |
exit(1); | |
}; | |
Employee initial_user = create_user(); | |
initial_user.role = HR; | |
employees.push_back(initial_user); | |
if (save_data()) { | |
cout << "Data File Initialized" << endl; | |
}; | |
return; | |
} | |
return; | |
}; | |
//This function saves the current master employee vector back to the data file | |
bool save_data() { | |
static const char* const outfile = "erm_data.txt"; | |
ofstream fout; | |
fout.open(outfile); | |
cout << "Saving data file to disk." << endl; | |
if (!fout) { | |
cout << "There was a problem opening the file for writing!" << endl << "This program will now exit." << endl << endl; | |
cout << "Press any key to exit..."; | |
exit(1); | |
} | |
//Iterate through each object in the vector | |
for (const auto& items : employees) { | |
//Write each object member to file as comma serpated fields, one per tow | |
fout << items.firstname << ',' << items.lastname << ',' << items.id << ',' << items.role << ',' << items.login << ',' << items.password << '\n'; | |
} | |
cout << "Data file saved." << endl; | |
return 1; | |
}; | |
//This function creates a new user and rerturns the user object | |
Employee create_user() { | |
Employee user; // Temp object to pass entered data to the employees vector | |
string linein; | |
cout << endl << "Enter Employee Information" << endl; | |
cout << "Enter Employee First Name: "; | |
getline(cin, user.firstname); | |
cout << "Enter Employee Last Name: "; | |
getline(cin, user.lastname); | |
cout << "Enter employee ID: "; | |
getline(cin, user.id); | |
string input; | |
do { | |
cout << "Enter employee role (1 for HR, 2 for Manager, 3 for Individual Contributor): "; | |
getline(cin, input); | |
} while (input != "1" && input != "2" && input != "3"); | |
//do...while to ensure role entry is valid | |
switch (stoi(input)) | |
{ | |
case 1: | |
user.role = HR; | |
break; | |
case 2: | |
user.role = Management; | |
break; | |
case 3: | |
user.role = Individual; | |
break; | |
default: | |
break; | |
} | |
cout << "Enter Username for login: "; | |
getline(cin, user.login); | |
cout << "Enter Password for login: "; | |
getline(cin, user.password); | |
return user; | |
} | |
//This function handles user logins | |
void user_login() { | |
string user_login, user_pass; | |
cout << "This is a restricted system. Only authorized users may login" << endl; | |
cout << "" << endl; | |
cout << "Enter User Login: "; | |
getline(cin, user_login); | |
cout << "Enter Password: "; | |
getline(cin, user_pass); | |
//This loop iterates through the vector | |
//looking for a matching login | |
//if one is found, we know who is currently | |
//using the program | |
for (const auto& items : employees) { | |
if (items.login == user_login) { //We have a valid user login | |
if (items.password == user_pass) { //Password matches | |
active_user = items.role; //Get the logged in user's role type and hold it | |
active_user_name = items.firstname + " " + items.lastname; | |
active_user_id = items.id; | |
return; | |
} | |
} | |
} | |
cout << "The entered login information is not correct." << endl << "The program will now exit." << endl; | |
exit(1); | |
} | |
//This function allows any employee to view their own employee record | |
void view_Self() { | |
//Iterate through employee records until we find the right one. | |
for (const auto& items : employees) { | |
if (items.id == active_user_id) { | |
//We have found the logged in user | |
cout << "\033c" << endl; | |
cout << "Here is your employee information : " << endl; | |
cout << left << setw(15) << "First Name" << left << setw(15) << "Last Name" << left << setw(5) << "ID" << left << setw(15) << "Role" << left << setw(15) << "Login" << left << setw(15) << "Password\n" << endl; | |
cout << left << setw(15) << items.firstname << left << setw(15) << items.lastname << left << setw(5) << items.id << left << setw(15) << displayUserType(items.role) << left << setw(15) << items.login << left << setw(15) << items.password << endl; | |
cout << "\nPress ENTER to return to the main menu..." << endl; | |
getchar(); | |
return; | |
} | |
} | |
} | |
//Function to handle employee deletion | |
void delete_Employee() { | |
int i = 0; | |
string input; | |
cout << "\033c" << endl; | |
cout << "Employee Master List: \n" << endl; | |
//Build the list of employees | |
cout << left << setw(15) << "First Name" << left << setw(15) << "Last Name" << left << setw(5) << "ID" << left << setw(15) << "Role" << left << setw(15) << "Login" << left << setw(15) << "Password\n" << endl; | |
for (const auto& items : employees) { | |
i++; // Added to begining of each line, to indicate the index of employee we want to delete | |
cout << i << ")\t" << left << setw(15) << items.firstname << left << setw(15) << items.lastname << left << setw(5) << items.id << left << setw(15) << displayUserType(items.role) << left << setw(15) << items.login << left << setw(15) << items.password << endl; | |
} | |
cout << "\nSelect the number of the employee you wish to delete" << endl; | |
cout << "or enter a letter to cancel delete." << endl; | |
cout << "Entry: "; | |
cin >> input; | |
if (stoi(input) <= i) { | |
confirmDelete(stoi(input)); | |
} | |
return; | |
} | |
//This function verifies a deletion of employee record, and handles the deletion | |
void confirmDelete(int index) { | |
index--; //Vector index starts at 0, but row counts starts at 1 | |
string input; | |
cout << "\nAre you sure you want to delete employee id " << employees[index].id << " with name " << employees[index].firstname << ' ' << employees[index].lastname << "?" << endl; | |
cout << "Confirm by entering the employee id : "; | |
getline(cin, input); // Verify we really want to do this | |
if (input == employees[index].id) { | |
employees.erase(employees.begin() + index); //Delete the object at the selected index | |
cout << "Employee removed from Master List." << endl; | |
save_data(); | |
cout << "\nPress ENTER to return to the main menu" << endl; | |
getchar(); | |
} | |
else { | |
cout << "Incorrect Employee ID - User Not Deleted" << endl; | |
cout << "Press ENTER to return to the main menu." << endl; | |
getchar(); | |
} | |
} | |
//This funcrtion allows HR or Management to view all employee data | |
void view_All() { | |
cout << "\033c" << endl; | |
cout << "\n\nEmployee Master List: \n" << endl; | |
cout << left << setw(15) << "First Name" << left << setw(15) << "Last Name" << left << setw(5) << "ID" << left << setw(15) << "Role" << left << setw(15) << "Login" << left << setw(15) << "Password\n" << endl; | |
//Display each vector object on a line | |
for (const auto& items : employees) { | |
cout << left << setw(15) << items.firstname << left << setw(15) << items.lastname << left << setw(5) << items.id << left << setw(15) << displayUserType(items.role) << left << setw(15) << items.login << left << setw(15) << items.password << endl; | |
} | |
cout << "\nPress ENTER to return to the main menu..." << endl; | |
getchar(); | |
return; | |
} | |
// This function allows HR to modify an employee record | |
void modify_User() { | |
int i = 0; | |
string input; | |
cout << "\033c" << endl; | |
cout << "Employee Master List: \n" << endl; | |
cout << left << setw(15) << "First Name" << left << setw(15) << "Last Name" << left << setw(5) << "ID" << left << setw(15) << "Role" << left << setw(15) << "Login" << left << setw(15) << "Password\n" << endl; | |
for (const auto& items : employees) { | |
i++; //effectively i is the index for current item, effectively employees[i] | |
cout << i << ")" << left << setw(15) << items.firstname << left << setw(15) << items.lastname << left << setw(5) << items.id << left << setw(15) << displayUserType(items.role) << left << setw(15) << items.login << left << setw(15) << items.password << endl; | |
} | |
cout << "\nSelect the number of the employee you wish to modify." << endl; | |
cout << "Entry: "; | |
cin >> input; | |
if (stoi(input) <= i) { | |
confirmModify(stoi(input)); | |
} | |
return; | |
} | |
// Thius function actually handles the employee data modification | |
void confirmModify(int index) { | |
index--; // Because base index in the vector is 0 | |
Employee user; | |
cout << "\nYou are modifying employee id " << employees[index].id << " with name " << employees[index].firstname << ' ' << employees[index].lastname << "?" << endl; | |
cout << "Simply press enter to use the current data [] for a field." << endl; | |
cout << endl << "Enter Employee Information" << endl; | |
cout << "Enter Employee First Name: [ " << employees[index].firstname << " ]: "; | |
cin.ignore(numeric_limits<streamsize>::max(), '\n'); //Clear the newline out of the inout buffer | |
getline(cin, user.firstname); | |
cout << "Enter Employee Last Name: [ " << employees[index].lastname << " ]: "; | |
getline(cin, user.lastname); | |
cout << "Enter employee ID: [ " << employees[index].id << " ]: "; | |
getline(cin, user.id); | |
string input; | |
do { | |
cout << "Enter employee role (1 for HR, 2 for Manager, 3 for Individual Contributor) [ " << employees[index].role << " ]: "; | |
getline(cin, input); | |
if (input == "") { break; } | |
} while (input != "1" && input != "2" && input != "3"); | |
if (input != "") { // Don't update role if nothing was entered | |
switch (stoi(input)) | |
{ | |
case 1: | |
user.role = HR; | |
break; | |
case 2: | |
user.role = Management; | |
break; | |
case 3: | |
user.role = Individual; | |
break; | |
default: | |
break; | |
} | |
} | |
cout << "Enter Username for login: [ " << employees[index].login << " ]: "; | |
getline(cin, user.login); | |
cout << "Enter Password for login: [ " << employees[index].password << " ]: "; | |
getline(cin, user.password); | |
//These if blocks are checking to see if no info was entered | |
//to the responses. If not, don't update, thus keeping the current value | |
if (user.firstname != "") { | |
employees[index].firstname = user.firstname; | |
} | |
if (user.firstname != "") { | |
employees[index].firstname = user.firstname; | |
} | |
if (user.id != "") { | |
employees[index].id = user.id; | |
} | |
if (user.login != "") { | |
employees[index].login = user.login; | |
} | |
if (user.password != "") { | |
employees[index].password = user.password; | |
} | |
cout << "Employee record updated." << endl; | |
save_data(); | |
cout << "\nPress ENTER to return to the main menu" << endl; | |
cin; | |
} | |
// This function allows a user to search for user by name or id | |
void search_User() { | |
string search; | |
cout << "\033c" << endl; | |
cout << "You can search for employee records by name or id." << endl; | |
cout << "\nEnter EITHER an employee's full first OR last name, OR an employee id number."; | |
cout << endl << "Entry: "; | |
getline(cin, search); | |
transform(search.begin(), search.end(), search.begin(), ::tolower); //Convert input to lowercase for case insensitive searching | |
cout << "\n\nEmployee Search Results List: \n" << endl; | |
cout << left << setw(15) << "First Name" << left << setw(15) << "Last Name" << left << setw(5) << "ID" << left << setw(15) << "Role" << left << setw(15) << "Login" << left << setw(15) << "Password\n" << endl; | |
//Iterate through the items in the array, checking the search term against firstname, lastname, or id data members | |
for (const auto& items : employees) { | |
//This section converts the id/firstname values to lower case so we can search case insensityive | |
string id = items.id; | |
transform(id.begin(), id.end(), id.begin(), ::tolower); | |
string firstname = items.firstname; | |
transform(firstname.begin(), firstname.end(), firstname.begin(), ::tolower); | |
string lastname = items.lastname; | |
transform(lastname.begin(), lastname.end(), lastname.begin(), ::tolower); | |
//Display the record if the search term is a subset of id or name fields - for matching partials - Thanks Stack Overflow! | |
if (id.find(search) != string::npos || firstname.find(search) != string::npos || lastname.find(search) != string::npos) { | |
cout << left << setw(15) << items.firstname << left << setw(15) << items.lastname << left << setw(5) << items.id << left << setw(15) << displayUserType(items.role) << left << setw(15) << items.login << left << setw(15) << items.password << endl; | |
} | |
} | |
cout << "\nPress ENTER to return to the main menu..." << endl; | |
getchar(); | |
} | |
// This functions generates the main menu | |
void run_menu() { | |
string input; | |
do | |
{ | |
cout << "\033c"; //This escapre sequence clears the console and resets the cursor to top-left | |
cout << "You are logged in as " + active_user_name + " - Role type: " << displayUserType() << endl; | |
cout << "You have the following options available, please make a selection" << endl << endl; | |
cout << "1) View your own employee record." << endl; | |
//The following conditionals are used to restrict meny view by user role | |
if (active_user != Individual) { //HR & Manager Only | |
cout << "2) View All Employees" << endl; | |
cout << "3) Search for an employee." << endl; | |
if (active_user != Management) { // HR Only | |
cout << "4) Modify an existing employee record" << endl; | |
cout << "5) Add a new employee record" << endl; | |
cout << "6) Delete an employee record" << endl; | |
} | |
} | |
cout << "\n8) Switch User" << endl; | |
cout << "9) Quit Program" << endl; | |
cout << endl << "Please enter a selection: "; | |
getline(cin, input); | |
//Menu option handler | |
//We verify permissions here, not showing the options isnt enough security | |
switch (stoi(input)) { | |
case 1: | |
view_Self(); | |
break; | |
case 2: | |
if (active_user == Management || active_user == HR) { | |
view_All(); | |
} | |
break; | |
case 3: | |
if (active_user == Management || active_user == HR) { | |
search_User(); | |
} | |
break; | |
case 4: | |
if(active_user == HR) { | |
modify_User(); | |
} | |
break; | |
case 5: | |
if (active_user == HR) { | |
employees.push_back(create_user()); | |
save_data(); | |
cout << endl << "New employee record added and saved." << endl; | |
cout << "Press ENTER to return to the main manu..." << endl; | |
getchar(); | |
} | |
break; | |
case 6: | |
if (active_user == HR) { | |
delete_Employee(); | |
} | |
break; | |
case 8: | |
user_login(); // Re-login as a different user | |
break; | |
case 9: //Return out of constructor, thus exiting the program | |
return; | |
default: //Go back to the meny display loop if they didnt hit a correct option | |
break; | |
} | |
} while (true); //Infinite Loop to kepe the menu up | |
} | |
public: | |
//Class constructor handles the main program loop | |
UserInteraction() { | |
open_file(); //Open and read, or create new, file | |
user_login(); //Handle user login | |
run_menu(); //Generate the menu and loop it | |
} | |
// This function effectively converts the current user enumerated type to a string | |
string displayUserType(UserType u) { | |
switch (u) | |
{ | |
case HR: | |
return "HR"; | |
break; | |
case Management: | |
return "Manager"; | |
break; | |
case Individual: | |
return "Individual"; | |
break; | |
default: | |
return "Unknown User Type"; | |
break; | |
} | |
} | |
//Overload function, to show just the current user role type | |
string displayUserType() { | |
switch (active_user) | |
{ | |
case HR: | |
return "HR"; | |
break; | |
case Management: | |
return "Manager"; | |
break; | |
case Individual: | |
return "Individual"; | |
break; | |
default: | |
return "Unknown User Type"; | |
break; | |
} | |
} | |
}; | |
int main() { | |
//main program function, creates UserInteraction object | |
//which handles main program loop and functions | |
UserInteraction user; //Create user interaction object, which handles the main program interaction | |
return 0; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment