ASDV-Cpp/Assignments/MP4_CalebFontenot/userData.cpp

261 lines
8.7 KiB
C++

//
// Created by caleb on 4/10/24.
//
#include <fstream>
#include <cstdio>
#include <ctime>
#include <boost/algorithm/string.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/serialization/vector.hpp>
#include <iostream>
#include <ranges>
#include "userData.h"
#include "simpleMenu.h"
std::tm enterDate() {
std::string temp;
std::tm tm;
std::printf("Enter day: ");
std::getline(std::cin, temp);
std::sscanf(temp.c_str(), "%i", &tm.tm_mday);
std::printf("Enter month: ");
std::getline(std::cin, temp);
std::sscanf(temp.c_str(), "%i", &tm.tm_mon);
std::printf("Enter year: ");
std::getline(std::cin, temp);
std::sscanf(temp.c_str(), "%i", &tm.tm_year);
tm.tm_mon -= 1;
tm.tm_year -= 1900;
tm.tm_hour = 0, tm.tm_min = 0, tm.tm_sec = 0;
return tm;
}
userData enterRecord() {
auto pUserData = new userData();
std::printf("Entering a new record...\n");
std::printf("Enter name (spaces allowed): ");
std::getline(std::cin, pUserData->name);
std::printf("Enter address: ");
std::getline(std::cin, pUserData->address);
std::printf("Enter city: ");
std::getline(std::cin, pUserData->city);
std::printf("Enter state: ");
std::getline(std::cin, pUserData->state);
std::printf("Enter ZIP code: ");
std::getline(std::cin, pUserData->zip);
std::printf("Enter phone #: ");
std::getline(std::cin, pUserData->phone);
while (true) {
std::printf("Enter account balance: $");
std::string temp;
std::getline(std::cin, temp);
try {
pUserData->accountBal = std::stod(temp);
if (pUserData->accountBal < 0) {
throw std::invalid_argument("less than zero");
} else {
break;
}
} catch (const std::invalid_argument &e) {
std::cerr << "Invalid input for balance. Please enter a valid number." << std::endl;
}
}
std::printf("Entering date of last payment...\n");
std::tm tm = enterDate();
pUserData->dateOfLastPayment = std::mktime(&tm);
return *pUserData;
}
userData *findRecord(const std::vector<userData>& data) {
std::printf("Here's the list of entries: \n%s\n", displayData(data).c_str());
while (true) {
std::printf("Enter the name for the entry you want to search for\n(we're searching for substrings, so you don't have to type out the full thing): ");
std::string userInput;
std::getline(std::cin, userInput);
// attempt to find entry
std::printf("Looking for %s...\n", userInput.c_str());
for (auto& entry : data) {
std::string lowerName = boost::algorithm::to_lower_copy(entry.name);
if (lowerName.find(boost::algorithm::to_lower_copy(userInput)) != std::string::npos) {
std::printf("%s\nIs this the entry you wanted (y/n)? ", displayData(entry).c_str());
std::getline(std::cin, userInput);
boost::algorithm::to_lower(userInput);
if (userInput == "y") {
return &entry; // Return pointer to the found entry directly
}
}
}
printf("Unable to find entry... Press enter to continue.");
std::getline(std::cin, userInput);
boost::algorithm::to_lower(userInput);
}
}
void deleteEntry(std::vector<userData>& data) {
std::printf("What entry would you like to delete?");
userData *entryToDelete = findRecord(data);
for(int i = 0; i < data.size(); ++i) {
if (entryToDelete->name == data[i].name) {
data.erase(data.begin() + i);
}
}
updateData(data);
}
void changeEntry(std::vector<userData>& data) {
std::printf("What entry would you like to modify?");
userData* selectedEntry = findRecord(data);
std::vector<std::string> menuItems = structContents(selectedEntry);
int selection = simpleMenu(menuItems, "What data would you like to modify?");
switch (selection) {
default:
break;
case 9: {
std::printf("Old value: %s\nEnter new name (spaces allowed): ", selectedEntry->name.c_str());
std::getline(std::cin, selectedEntry->name);
break;
}
case 1: {
std::printf("Old value: %s\nEnter new address: ", selectedEntry->address.c_str());
std::getline(std::cin, selectedEntry->address);
break;
}
case 2: {
std::printf("Old value: %s\nEnter new city: ", selectedEntry->city.c_str());
std::getline(std::cin, selectedEntry->city);
break;
}
case 3: {
std::printf("Old value: %s\nEnter new state: ", selectedEntry->state.c_str());
std::getline(std::cin, selectedEntry->state);
break;
}
case 4: {
std::printf("Old value: %s\nEnter new ZIP: ", selectedEntry->zip.c_str());
std::getline(std::cin, selectedEntry->zip);
break;
}
case 5: {
std::printf("Old value: %s\nEnter new phone #: ", selectedEntry->phone.c_str());
std::getline(std::cin, selectedEntry->phone);
break;
}
case 6: {
while (true) {
std::printf("Old value: %.2lf\nEnter new balance: $", selectedEntry->accountBal);
std::string temp;
std::getline(std::cin, temp);
try {
selectedEntry->accountBal = std::stod(temp);
if (selectedEntry->accountBal < 0) {
throw std::invalid_argument("less than zero");
} else {
break;
}
} catch (const std::invalid_argument &e) {
std::cerr << "Invalid input for balance. Please enter a valid number." << std::endl;
}
}
break;
}
case 7: {
std::printf("Old value: %s\nEntering new date of last payment...\n",
std::put_time(std::localtime(&selectedEntry->dateOfLastPayment), "%c"));
std::tm tm = enterDate();
selectedEntry->dateOfLastPayment = std::mktime(&tm);
break;
}
}
updateData(data);
}
std::vector<std::string> structContents(const userData *entry) {
std::vector<std::string> structItems;
structItems.emplace_back("Name: " + entry->name);
structItems.emplace_back("Address: " + entry->address);
structItems.emplace_back("City: " + entry->city);
structItems.emplace_back("State: " + entry->state);
structItems.emplace_back("ZIP: " + entry->zip);
structItems.emplace_back("Phone #: " + entry->phone);
// Format account balance
std::stringstream ss;
ss << "Account Balance: $" << std::fixed << std::setprecision(2) << entry->accountBal;
structItems.emplace_back(ss.str());
// Get gregorian calendar date of unix timestamp
structItems.emplace_back("Date of Last Payment: " + std::string(ctime(&entry->dateOfLastPayment)));
return structItems;
}
std::string displayData(const userData& entry) {
std::vector<std::string> data = structContents(&entry);
std::stringstream ss;
for(auto str: data) {
ss << str << "\n";
}
return ss.str();
}
std::string displayData(std::vector<userData> data) {
std::string returnString = "";
//std::printf("vector size: %i\n", static_cast<int>(data.size()));
for (int i = 0; i < data.size(); ++i) {
userData currentUserData = data[i];
returnString.append(DASHES).append(" entry ").append(std::to_string(i + 1)).append(DASHES).append("\n");
returnString.append(displayData(currentUserData));
}
return returnString;
}
inline bool fileExists (const std::string& name) {
std::ifstream f(name.c_str());
return f.good();
}
void updateData(std::vector<userData> data) {
// delete the old data
std::remove("data.dat");
// Serialize the vector
std::stringstream ss;
boost::archive::text_oarchive oa(ss);
oa << data;
// Save the contents
std::ofstream outputFile("data.dat");
if (outputFile.is_open()) {
outputFile << ss.str();
outputFile.close();
}
}
std::vector<userData> getData() {
std::vector<userData> data;
if (!fileExists("data.dat")) {
std::printf("Warning! No datafile detected! Prompting for data entry...\n");
auto pUserData = enterRecord();
data.emplace_back(pUserData);
updateData(data);
} else {
// Deserialize and read the data from the file
std::ifstream inputFile("data.dat", std::ios::binary);
boost::archive::text_iarchive ia(inputFile);
ia >> data;
}
return data;
}