Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save kspillane/2d9c3e35448d78e14d203224160a4984 to your computer and use it in GitHub Desktop.
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++
/*
* 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