From 560d5d7e99abd7bd6841e00f0c04baa27f76b4c5 Mon Sep 17 00:00:00 2001 From: MassiveAtoms Date: Tue, 23 Jul 2019 12:34:06 -0300 Subject: [PATCH] monoliticc file --- CMakeLists.txt | 18 +- bigsource.cpp | 972 +++++++++++++++++++++++ headers/bigheader.h | 229 ++++++ headers/{ => old}/Customer.h | 0 headers/{ => old}/Interface.h | 0 headers/{ => old}/Park_spot.h | 0 headers/{ => old}/Park_time.h | 0 headers/{ => old}/Query.h | 0 headers/{ => old}/data.h | 0 headers/{ => old}/encrypt.h | 0 main.cpp | 21 +- Customer.cpp => old/Customer.cpp | 198 ++--- Interface.cpp => old/Interface.cpp | 0 Park_spot.cpp => old/Park_spot.cpp | 0 Park_time.cpp => old/Park_time.cpp | 0 Query.cpp => old/Query.cpp | 0 data.cpp => old/data.cpp | 0 newParkManne.cpp => old/newParkManne.cpp | 0 18 files changed, 1303 insertions(+), 135 deletions(-) create mode 100644 bigsource.cpp create mode 100644 headers/bigheader.h rename headers/{ => old}/Customer.h (100%) rename headers/{ => old}/Interface.h (100%) rename headers/{ => old}/Park_spot.h (100%) rename headers/{ => old}/Park_time.h (100%) rename headers/{ => old}/Query.h (100%) rename headers/{ => old}/data.h (100%) rename headers/{ => old}/encrypt.h (100%) rename Customer.cpp => old/Customer.cpp (96%) rename Interface.cpp => old/Interface.cpp (100%) rename Park_spot.cpp => old/Park_spot.cpp (100%) rename Park_time.cpp => old/Park_time.cpp (100%) rename Query.cpp => old/Query.cpp (100%) rename data.cpp => old/data.cpp (100%) rename newParkManne.cpp => old/newParkManne.cpp (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 908e7e6..f344039 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,22 +13,8 @@ include_directories( add_executable(park main.cpp - - data.cpp - headers/data.h - encrypt.cpp - headers/encrypt.h - - Customer.cpp - headers/Customer.h - Park_spot.cpp - headers/Park_spot.h - Park_time.cpp - headers/Park_time.h - Query.cpp - headers/Query.h - Interface.cpp - headers/Interface.h + bigsource.cpp + headers/bigheader.h ) diff --git a/bigsource.cpp b/bigsource.cpp new file mode 100644 index 0000000..899b7a6 --- /dev/null +++ b/bigsource.cpp @@ -0,0 +1,972 @@ +#include "headers/bigheader.h" + +string hash_password(string password) { + /* + Passing strings and converting to char* because I do not want to be forced + to use char * whenever the function is called. + Low level stuff in the function, the least possible low level stuff outside. + This uses the password hashing algorithm Argon2 implemented by libsodium. + + DO NOT MODIFY memory_limit and cpu_limit after you add customers to the db. + When you do that, the hashed passwords can't be decrypted, and that would be + BAD. + + */ + const char* password_ = password.c_str(); + char hashed_password_[crypto_pwhash_STRBYTES]; + int memory_limit = 3.2e+7; // 3.2e7 = 32e6 = 32 mb + int cpu_limit = 1; // this somewhat resembles n_threads, but is not a 1 to 1 match. + + int result = + crypto_pwhash_str(hashed_password_, password_, strlen(password_), cpu_limit, memory_limit); + string hashed_password{hashed_password_}; + + return hashed_password; +} + +bool verify_password(string hashed_password, string unhashed_password) { + /* + this verifies the password. It's encryption magic and don't question it. + */ + const char* password_ = unhashed_password.c_str(); + const char* hashed_password_ = hashed_password.c_str(); + + if (crypto_pwhash_str_verify(hashed_password_, password_, strlen(password_)) != 0) { + return false; + } else { + return true; + } +} + +namespace data { + +SQLite::Database start_db() { + /* + Opens the database, creates it if it can't find the file. + Then creates tables if they don't exist + */ + SQLite::Database db("test.db3", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE); + while (sodium_init() < 0) { + std::cout << "SODIUM NOT WORKING"; + /* + This shouldn't be here, really, but I can't think of a better place + where it runs at least once. This seeds the random generator needed for + salts and other stuff, and needs to be run at least once when working + with any libsodium function. And since this definitely needs to be run at least once, why + not include it here? you can't (well, shouldn't be able to) login into anything if this + doesn't run, since you need to compare passwords to login, and then running the program is + as futile as not opening the db correctly. + */ + } + db.exec( + "create table if not exists Customer (id integer primary key, name " + "text, password text, vehicle int, telephone text, role int)"); + db.exec( + "create table if not exists Park_spot (id integer primary key, taken " + "int, customer_id int, vehicle_type int)"); + db.exec( + "create table if not exists Park_time (id integer primary key, " + "customer_id int, spot_id int, start int, end int, duration int)"); + + return db; +} +} // namespace data + +/* +initializes everything, id is auto incremented from what's stored in the db. +inmediately saves to db upon creation. +Also, this weird syntax is called an initializer list, and is the preffered +method of how to initialize members. It has a measurable performance increase +because it uses move semantics instead of copy semantics. +https://www.geeksforgeeks.org/when-do-we-use-initializer-list-in-c/ + */ +Park_time::Park_time(int c_id, int s_id) + : customer_id{c_id}, + spot_id{s_id}, + duration{0}, + start{high_resolution_clock::now()}, + id{auto_increment_db() + 1} { + save_db(); +} +/* +this one initializes with data from the database. should only be used in the query +functions. + */ +Park_time::Park_time(int id_, int customer_id_, int spot_id_, int start_, int duration_) + : id{id_}, customer_id{customer_id_}, spot_id{spot_id_}, duration{duration_} { + start = time_point(seconds(start_)); + end = time_point(seconds(start_ + duration_)); +} + +/* +simple checking if customer is clocking out at the right spot. +sets end(time of clocking out) and calculates the duration. +updates the info in the database. + */ +void Park_time::clock_out(int c_id, int s_id) { + + if (c_id != customer_id) { + cout << "wrong customer id, you are at the wrong location"; + return; + } + if (s_id != spot_id) { + cout << "Wrong spot id, you're at the wrong location"; + return; + } + + if (!duration) { + end = high_resolution_clock::now(); + duration = duration_cast(end - start).count(); // use mins later + update_db(); + + } else { + cout << "Already clocked out. Something is wrong \n"; + } +} + +std::ostream& operator<<(std::ostream& os, const Park_time& pt) { + std::time_t start_ = system_clock::to_time_t(pt.start); + std::time_t end_ = system_clock::to_time_t(pt.end); + os << "- - - - - - - - - - - - - - - - - - - -\n"; + os << "Customer # " << pt.customer_id << "at parking spot " << pt.spot_id << "\n"; + os << "Clocked in :" << std::ctime(&start_); + os << "clocked out : " << std::ctime(&end_); + float dur_h = pt.duration / 3600.0; + os << "duration : " << dur_h << " h\n"; + os << "- - - - - - - - - - - - - - - - - - - -\n"; + return os; +} +// mostly a helper function to ease the conversion from timepoint to int +// for storing in the db +int Park_time::start_to_int() { + auto start_to_epoch = start.time_since_epoch(); + auto start_value = duration_cast(start_to_epoch); + int start_seconds = start_value.count(); + return start_seconds; +} + +// db funcs +// ----------------------------------------------------------------------------- + +void Park_time::save_db() { + /* + this creates a sql statement and then executes it + */ + string statement{"insert into Park_time values ( , , , , , );"}; + statement.insert(41, "NULL"); + statement.insert(39, "NULL"); + statement.insert(37, to_string(start_to_int())); + statement.insert(35, to_string(spot_id)); + statement.insert(33, to_string(customer_id)); + statement.insert(31, to_string(id)); + SQLite::Transaction transaction(data::db); + data::db.exec(statement); + transaction.commit(); +} +// same as above +void Park_time::update_db() { + string statement = "UPDATE Park_time SET end = , duration = where id = '';"; + statement.insert(53, to_string(id)); + statement.insert(40, to_string(duration)); + statement.insert(27, to_string(start_to_int() + duration)); + data::db.exec(statement); +} + +// to get id on first save to db +int Park_time::auto_increment_db() { + SQLite::Statement max_id(data::db, "select max(id) from Park_time;"); + int id = 0; + max_id.executeStep(); + id = max_id.getColumn(0); + max_id.reset(); + return id; +} + +// text animtion +void text_animation(const string& text, unsigned int pause_time) { + for (const char m : text) // range loop; for each character in string + { + cout << m << flush; + sleep_for(milliseconds(pause_time)); + } +} + +// constructors +Customer::Customer(string name_, string password_, Vehicle_type vehicle_, string telephone_, + int role_) + : id{auto_increment_db() + 1}, + name{name_}, + password{hash_password(password_)}, + vehicle{vehicle_}, + telephone{telephone_}, + role{role_} { + save_db(); +} + +Customer::Customer(int id_, string name_, string password_, Vehicle_type vehicle_, + vector instances, string telephone_, int role_) + : id{id_}, + name{name_}, + password{password_}, + vehicle{vehicle_}, + park_instances{instances}, + telephone{telephone_}, + role{role_} {} + +// clock in/out methods +// ==================================================================================== +/* +Create a p_time object with start=now and adds to vector + */ +void Customer::clock_in(int s_id) { + Park_time pt{id, s_id}; + park_instances.push_back(pt); +} + +// edit last p_time object in park_instances so end=now +void Customer::clock_out(int s_id) { + park_instances[park_instances.size() - 1].clock_out(id, s_id); +} + +bool Customer::parked() { + if (!park_instances.size()) { + return false; + } + if ((park_instances[park_instances.size() - 1].duration)) { + // if duration of the last parktime == 0, meaning + // that the customer has not clocked out + return false; + } else { + return true; + } +} + +int Customer::parked_at() { return park_instances[park_instances.size() - 1].spot_id; } + +//================================================================================================ +// functions that interact with the database + +void Customer::save_db() { + string statement{"insert into Customer values (, '', '', ,'', );"}; + statement.insert(43, to_string(role)); + statement.insert(41, telephone); + statement.insert(38, to_string(int(vehicle))); + statement.insert(36, password); + statement.insert(32, name); + statement.insert(29, to_string(id)); + SQLite::Transaction transaction(data::db); + data::db.exec(statement); + transaction.commit(); +} + +void Customer::update_db() { + string statement = + "UPDATE Customer SET name = '', password = '', " + "vehicle = '', telephone = '', role = '' where id = '';"; + statement.insert(98, to_string(id)); + statement.insert(84, to_string(role)); + statement.insert(73, telephone); + statement.insert(57, to_string(int(vehicle))); + statement.insert(43, password); + statement.insert(28, name); + data::db.exec(statement); +} + +void Customer::delete_db() { + string statement = "delete from Customer where id= ;"; + statement.insert(statement.length() - 2, to_string(id)); + SQLite::Transaction transaction(data::db); + data::db.exec(statement); + transaction.commit(); +} + +int Customer::auto_increment_db() { + SQLite::Statement max_id(data::db, "select max(id) from Customer;"); + int id = 0; + max_id.executeStep(); + id = max_id.getColumn(0); + max_id.reset(); + return id; +} + +Park_spot::Park_spot(Vehicle_type v_type_) + : parked_customer{0}, id{auto_increment_db() + 1}, taken{false}, v_type{v_type_} { + save_db(); +} + +Park_spot::Park_spot(int id_, bool taken_, int parked, Vehicle_type v_type_) + : parked_customer{parked}, id{id_}, v_type{v_type_}, taken{taken_} {} + +// clock in en out, calls de correct customer.clock_x depending on internal state of the spot +void Park_spot::clock(Customer& c_customer) { + if (!taken) { + parked_customer = c_customer.id; + taken = true; + c_customer.clock_in(id); + update_db(); + } else { + taken = false; + c_customer.clock_out(id); + parked_customer = 0; + update_db(); + } +} + +// --------------------- db functs + +void Park_spot::update_db() { + string statement = "UPDATE Park_spot SET taken = '', customer_id = '' where id = '';"; + statement.insert(63, to_string(id)); + if (taken) { + statement.insert(49, to_string(parked_customer)); + statement.insert(30, "1"); + } else { + statement.insert(49, "NULL"); + statement.insert(30, "0"); + } + data::db.exec(statement); +} + +void Park_spot::save_db() { + string statement{"insert into Park_spot values ( , , , );"}; + statement.insert(36, to_string(int(v_type))); + statement.insert(34, "NULL"); + statement.insert(32, "0"); + statement.insert(30, to_string(id)); + SQLite::Transaction transaction(data::db); + data::db.exec(statement); + transaction.commit(); +} + +void Park_spot::delete_db() { + string statement = "delete from Park_spot where id= ;"; + statement.insert(statement.length() - 2, to_string(id)); + SQLite::Transaction transaction(data::db); + data::db.exec(statement); + transaction.commit(); +} + +int Park_spot::auto_increment_db() { + SQLite::Statement max_id(data::db, "select max(id) from Park_spot;"); + int id = 0; + max_id.executeStep(); + id = max_id.getColumn(0); + max_id.reset(); + return id; +} + +vector query_parktimes_for_customer(int cid) { + /* + This is needed to initialize the park_instances for the customer constructor + that is supposed to create a customer from data in the db. + This should not be called on on it's own outside query_customer(); + */ + vector park_times; + + SQLite::Statement query(data::db, "SELECT * FROM Park_time WHERE customer_id = ?;"); + query.bind(1, cid); + while (query.executeStep()) { + int id = query.getColumn(0); + int spot_id = query.getColumn(2); + int start = query.getColumn(3); + int duration = query.getColumn(5); + + Park_time result{id, cid, spot_id, start, duration}; + park_times.push_back(result); + } + query.reset(); + return park_times; +} + +//--------------------------------------------- customers + +Customer query_customer_with_id(int id) { + /* do not call this function if you are not certain a customer with this id + exists. + // the only legitimate caller of this function is query_parkspot_x + // there is no error handling in this function + // for when this function doesn't find the customer with this id !!!! + */ + + SQLite::Statement query(data::db, "SELECT * FROM Customer WHERE id = ?;"); + query.bind(1, id); + while (query.executeStep()) { + string name = query.getColumn(1); + string password = query.getColumn(2); + int vehicle = query.getColumn(3); // cast to vehicle + string telephone = query.getColumn(4); + int role = query.getColumn(5); + + vector park_instances = query_parktimes_for_customer(id); + Customer result{id, name, password, Vehicle_type(vehicle), park_instances, telephone, role}; + return result; + } +} + +int query_role_customer(int id) { + SQLite::Statement query(data::db, "SELECT * FROM Customer WHERE id = ?;"); + query.bind(1, id); + while (query.executeStep()) { + int role = query.getColumn(5); + return role; + } +} + +//------------------------------- parkspot info + +// -- parkspots info, report gen + +Park_spot query_parkspot_with_id(int id, vector& parkspots) { + for (Park_spot& i : parkspots) { + if (i.id == id) { + return i; + } + } +} + +void reports_from_parkspot(int spotid, pair period) { + vector park_times; + SQLite::Statement query(data::db, + "SELECT * FROM Park_time WHERE spot_id = ? AND start > ? AND end < ?;"); + query.bind(1, spotid); + query.bind(2, period.first); + query.bind(3, period.second); + while (query.executeStep()) { + int id = query.getColumn(0); + int cid = query.getColumn(1); + int start = query.getColumn(3); + int duration = query.getColumn(5); + Park_time result{id, cid, spotid, start, duration}; + park_times.push_back(result); + } + query.reset(); + + for (auto i : park_times) { + cout << i; + } +} + +void reports_from_allparkspots(pair period) { + vector park_times; + SQLite::Statement query(data::db, "SELECT * FROM Park_time WHERE start > ? AND end < ?;"); + query.bind(1, period.first); + query.bind(2, period.second); + + while (query.executeStep()) { + int id = query.getColumn(0); + int cid = query.getColumn(1); + int spotid = query.getColumn(2); + int start = query.getColumn(3); + int duration = query.getColumn(5); + Park_time result{id, cid, spotid, start, duration}; + park_times.push_back(result); + } + query.reset(); + + for (auto i : park_times) { + cout << i; + } +} + +void current_status_parkspots(vector& spots) { + cout << "P.spot \t\tStatus\t\t Customer\n"; + for (auto& i : spots) { + cout << "\n" << i.id << "\t\t" << ((i.taken) ? "true" : "false"); + if (i.taken) { + cout << "\t\t" << i.parked_customer; + } + } + cout << "\n"; +} + +vector reports_from_customer(int cid, pair period) { + vector park_times; + int verhicle = int(query_customer_with_id(cid).vehicle); + float sum = 0; + SQLite::Statement query( + data::db, "SELECT * FROM Park_time WHERE customer_id = ? AND start > ? AND end < ?;"); + query.bind(1, cid); + query.bind(2, period.first); + query.bind(3, period.second); + while (query.executeStep()) { + int id = query.getColumn(0); + int spotid = query.getColumn(2); + int start = query.getColumn(3); + int duration = query.getColumn(5); + Park_time result{id, cid, spotid, start, duration}; + park_times.push_back(result); + sum += duration / 3600; + } + query.reset(); + + for (auto i : park_times) { + cout << std::setprecision(2) << i; + sum += i.duration / 3600.0; + } + cout << "Your fees for this month: $" << std::setprecision(4) << sum * verhicle << "\n"; + return park_times; +} + +// I added it to pass spots, because the parking options need it to check where +// is free parking_spots is declared in main, and if i declare it +// liberal use of +// cin.ignore(10000, '\n'); +// so it skips to the next newline, in essence clearing the cin buffer + +void interface(vector& spots) { + /* + string introduction = "P A R K M A N N E"; //logo animation, disable during testing + text_animation(introduction, 50); + */ + __label__ exit; + system("CLS"); + cout << "\nWelcome to the parking system. Please login..."; + int id; + string password; + cout << "\nEnter your id: "; + cin >> id; + cin.ignore(10000, '\n'); + Customer c = query_customer_with_id(id); + cout << "\nEnter your password: "; + std::getline(cin, password); + + while (!(verify_password(c.password, password))) { + cout << "ERROR: wrong password. Please retype your password or type [x] to exit:\n"; + std::getline(cin, password); + if (password == "x") + goto exit; + } + if (query_role_customer(id) == 1) { + interface_admin(spots); + } else if (query_role_customer(id) == 0) { + interface_member(spots, c); + } else { + cout << "ERROR ROLE_INVALID!"; + } +exit:; +} + +void interface_member(vector& spots, Customer& c) { + __label__ begin, exit; + cout << "\nLogged in succesfully!\n"; + +begin: + system("CLS"); + cout << "Hello! " << c.name + << ", please select an option:\n[1]Parking\n[2]Monthly report\n" + "[3]Edit information\n[4]Exit\n"; + int option; + cin >> option; + cin.ignore(10000, '\n'); + switch (option) { + case 1: { + park(c, spots); + break; + } + case 2: { + report_customer(c.id); + string lol; + std::cout << "Enter any character to continue..."; + std::cin >> lol; + break; + } + + case 3: { + edit_information(c); + break; + } + + case 4: { + cout << "Exiting...\n"; + sleep_for(seconds(2)); + goto exit; + break; + } + + default: + break; + } + goto begin; +exit:; +} + +void interface_admin(vector& spots) { + __label__ begin, exit; +begin: + system("CLS"); + cout << "\nWelcome to the admin interface\n"; + cout << "\n[1] Reports & analytics"; + cout << "\n[2] Parking spots"; + cout << "\n[3] Make new user"; + cout << "\n[4] Exit"; + cout << "\nEnter option number: "; + int option; + cin >> option; + cin.ignore(10000, '\n'); + switch (option) { + case 1: { + cout << "[1] See monthly report of ALL parking spots\n"; + cout << "[2] See weekly report of ALL parking spots\n"; + cout << "[3] See monthly report of a specific parking spot\n"; + cout << "[4] See weekly report of a specific parking spot\n"; + cout << "[5] See monthly report of a specific customer\n"; + cout << "[6] See weekly report of a specific customer\n"; + cout << "[7] Return\n"; + cout << "Enter option number: "; + int option_1; + cin >> option_1; + cin.ignore(10000, '\n'); + switch (option_1) { + case 1: { + report_all_spots(); + break; + } + case 2: { + report_all_spots(true); + break; + } + case 3: { + report_single_spot(); + break; + } + case 4: { + report_single_spot(true); + break; + } + case 5: { + report_customer(0); + break; + } + case 6: { + report_customer(0, true); + break; + } + case 7: { + goto begin; + break; + } + default: + break; + } + string lol; + std::cout << "Enter any character to continue..."; + std::cin >> lol; + break; + } + case 2: { + cout << "[1] See current status of parking spots\n"; + cout << "[2] Make new parking spot\n"; + cout << "[3] Return\n"; + cout << "Enter option number: "; + int option_2; + cin >> option_2; + cin.ignore(10000, '\n'); + switch (option_2) { + case 1: { + current_status_parkspots(spots); + string lol; + std::cout << "Enter any character to continue..."; + std::cin >> lol; + break; + } + case 2: { + new_parkspot(spots); + break; + } + case 3: { + goto begin; + break; + } + default: + break; + } + + break; + } + case 3: { + system("CLS"); + cout << "[1] Make new customer\n"; + cout << "[2] Make new admin\n"; + cout << "[3] Return\n"; + cout << "Enter option number: "; + int option_3; + cin >> option_3; + cin.ignore(10000, '\n'); + switch (option_3) { + case 1: { + new_customer(); + break; + } + case 2: { + new_admin(); + break; + } + case 3: { + goto begin; + break; + } + default: + break; + } + case 4: { + std::cout << "Exiting..."; + sleep_for(seconds(2)); + goto exit; + break; + } break; + } + + default: + break; + } + goto begin; +exit:; +} + +// --------- individual things. + +void park(Customer& c, vector& spots) { + __label__ exit; + cout << "You have selected parking option.\n"; + if (!(c.parked())) { + cout << "The following spots fit your vehicle and are available: \n"; + for (Park_spot i : spots) { + if ((i.v_type == c.vehicle) & (i.taken == false)) { + cout << i.id << ", "; + } + } + + cout << "\nWhere do you want to park? Or type [0] to exit."; + int parkID; + cin >> parkID; + if (!parkID) + goto exit; + cin.ignore(10000, '\n'); + for (Park_spot& i : spots) { + if (i.id == parkID) { + if (confirm()) { + i.clock(c); + cout << "You have parked sucessfully!"; + } + } + } + + } else { + cout << "You are parked at spot " << c.parked_at() + << ", do you want to clock out?\n[1] Yes\n[2] No"; + int answer = 0; + cin >> answer; + cin.ignore(10000, '\n'); + if (answer) { + query_parkspot_with_id(c.parked_at(), spots).clock(c); + cout << "You have sucessfully clocked out."; + } else { + cout << "OK, have a nice day."; + } + } +exit:; +} + +void new_customer() { + int vtype; + string name; + string password; + string telephone; + int role = 0; + cout << "\nWhat's the name of the customer? "; + std::getline(cin, name); + cout << "\nWhat's the vehicle type? \n[1]Twowheeler\n[2] Fourwheeler\n"; + cin >> vtype; + cin.ignore(10000, '\n'); + cout << "What's the telephone number? +"; + std::getline(cin, telephone); + cout << "\nWhat's the password? "; + std::getline(cin, password); + Customer newcustomer{name, password, Vehicle_type(vtype), telephone, role}; + cout << "\nNew customer sucessfully created with ID:" << newcustomer.id << "\n"; + if (confirm()) + newcustomer.update_db(); +} + +void new_admin() { + int vtype = 2; // revision required! Needs to be set to NULL + string name; + string password; + string telephone; + int role = 1; + cout << "\nWhat's the name of the admin? "; + std::getline(cin, name); + cout << "\nWhat's the telephone number? +"; + std::getline(cin, telephone); + cout << "\nWhat's the password?"; + std::getline(cin, password); + Customer newadmin{name, password, Vehicle_type(vtype), telephone, role}; + cout << "\nNew customer sucessfully created with ID=" << newadmin.id << "\n"; + if (confirm()) + newadmin.update_db(); +} + +void new_parkspot(vector& spots) { + cout << "What type of parking spot? \n[1] Two-wheeler\n[2] Four-wheeler\n"; + int vtype; + cin >> vtype; + cin.ignore(10000, '\n'); + Park_spot newspot{Vehicle_type(vtype)}; + if (confirm()) { + spots.push_back(newspot); + cout << "New parking spot sucessfully created.\n"; + } +} + +void edit_information(Customer& c) { + string string0; + int int0; + /*std::cout<<"\nInput to update name or press [0] to keep name:\n"; + std::getline(cin,string0); + if (string0=="0"); + else c.name=string0;*/ + + std::cout << "\n Input to update vehicle to [1]Two-Wheeler," + "[2]Four-Wheeler or press [0] to keep vehicle type:\n"; + std::cin >> int0; + if (!int0) + ; + else + c.vehicle = Vehicle_type(int0); + cin.ignore(); + std::cout << "\n Input to update password or press [0] to keep current password:\n"; + std::getline(cin, string0); + if (string0 == "0") + ; + else + c.password = hash_password(string0); + + std::cout << "\n Input to update phone number or press [0] to keep current number:\n"; + std::getline(cin, string0); + if (string0 == "0") + ; + else + c.telephone = string0; + c.role = 0; + if (confirm()) { + c.update_db(); + } +} + +// time stuff----------------------------------------------------- + +pair create_month_period() { + std::time_t t = std::time(0); + std::tm* date = std::localtime(&t); + int month, year = 0; + cout << "Which month do you want a report on?[6 2018 for June 2018]\n"; + cin >> month >> year; + date->tm_year = year - 1900; + date->tm_mday = 1; + date->tm_mon = month - 1; + pair period; + period.first = mktime(date); + date->tm_mon = month; + period.second = mktime(date); + return period; +} + +pair create_week_period() { + std::time_t t = std::time(0); + std::tm* date = std::localtime(&t); + int day, month, year = 0; + cout << "Which month do you want a report on?[ 20 6 2018 for June 20th, 2018]\n"; + cin >> day >> month >> year; + date->tm_year = year - 1900; + date->tm_mday = day; + date->tm_mon = month - 1; + date->tm_hour = 0; + date->tm_min = 0; + pair period; + period.first = mktime(date); + period.second = period.first + 604800; // plus 7 days in seconds. + return period; +} + +bool confirm(void) { + string ver; + std::cout << "\nAre you sure you want to commit these actions?" + "\n[No] Revert." + "\n[Yes] Commit."; + std::cin >> ver; + if (ver == "YES" | ver == "Yes" | ver == "yes") { + std::cout << "Succes! Changes Saved."; + sleep_for(seconds(1)); + return true; + } else { + std::cout << "No changes committed."; + sleep_for(seconds(1)); + return false; + } +} + +// ------------------------------ report stuff + +void report_all_spots(bool weekly) { + pair period; + if (weekly) { + period = create_week_period(); // remove the pair + } else { + period = create_month_period(); // ^ + } + + cout << "working timeperiods: " << period.first << ", " << period.second; // DEBUG + + reports_from_allparkspots(period); // TODO: namechange of reports_from_allparkspots in query? +} + +void report_single_spot(bool weekly) { + cout << "Which parking spot would you like a report on?\n"; + cout << "Parking spot ID: "; + int spotID; + cin >> spotID; + cin.ignore(10000, '\n'); + pair period; + if (weekly) { + period = create_week_period(); // remove the pair + } else { + period = create_month_period(); + } + reports_from_parkspot(spotID, period); +} + +void report_customer(int customerID, bool weekly) { + // use report_customer(0) to make interactive + // so admin can call the interactive version, but customer can only call + // report_customer(own_cid) + if (!customerID) { + cout << "What customer do you want a report on? ID: "; + cin >> customerID; + } + pair period; + if (weekly) { + period = create_week_period(); + } else { + period = create_month_period(); + } + reports_from_customer(customerID, period); +} + +/* +Why is this not in query.cpp? Because somehow, it errors out when it's there. +The error message indicates it is a memory issue but I suspect it's a +concurrency issue. Do not move this. + */ + +vector populate_spots() { + vector spots; + SQLite::Statement query(data::db, "SELECT * FROM Park_spot WHERE id > 0;"); + while (query.executeStep()) { + int id = query.getColumn(0); + int taken = query.getColumn(1); + int cid = query.getColumn(2); + Vehicle_type vtype = Vehicle_type(int(query.getColumn(3))); + spots.push_back({id, taken, cid, vtype}); + } + return spots; +} diff --git a/headers/bigheader.h b/headers/bigheader.h new file mode 100644 index 0000000..ef7638a --- /dev/null +++ b/headers/bigheader.h @@ -0,0 +1,229 @@ +#ifndef ENCRYPT_H +#define ENCRYPT_H +#pragma once + +#include "../thirdparty/SQLiteCpp/include/SQLiteCpp/SQLiteCpp.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using std::pair; +using namespace std::chrono; +using std::cin; +using std::cout; +using std::flush; +using std::string; +using std::to_string; +using std::vector; +using std::this_thread::sleep_for; +/* +hash_password takes the password, and encrypts it. This needs to be done, +because storing passwords in plaintext is BAD, no matter if it's just for a school project! + +verify_password takes in a password and the hashed password, and then does magic encryption +stuff(no, not really. It basically hashes the password with the same salt and other parameters, but +that's not that important to know) and to see if the password stored and the given password match. +call these whenever you are working with passwords. +so to check if passwords match, use something like verifypassword(customer.password, +someplainpassword) see libsodium documentation for more info + */ + +string hash_password(string password); +bool verify_password(string hashed_password, string unhashed_password); + +namespace data { + +/* +start_db is the function that opens the database, and +if the necesary tables are not there, creates them. +db is the database, and is static to avoid multiple redefinition errors, + because multiple cpp files import this header. + TODO: remove this namespace, we didn't add more functions here like originally planned. + + */ +SQLite::Database start_db(); +static SQLite::Database db = start_db(); + +} // namespace data + +/* + + +Record of who parked at what park_spot and at what time. +public interface------------------------------------------- + +The constructors. one for creating new customers, the other one used by the +query functions to construct the object from information stored in the database. + +clock_out is the function that gets called from customer.clock_out(). +It verifies that the customer is clocking out at the correct parkspot, and saves +the current time of clocking out in end. It also calculates duration so it +doesn't have to be calculated more than once. + +operator<< is << overload, can(should) be used for report generation. + + +// implementation stuff------------------------ +start and end are time points representing when someone clocks in and out. they're from the chrono +namespace. + +save and update save and update info in the database. +auto_increment pulls the highest id stored in the db, to be used in the constructor. + +start_to_int() is used to convert the start timepoint to an integer that can be saved in the +database SQL datetime and chrono datetime don't seem the most compatible. + +We choose chrono because it's the recomended way from c++11 onwards, and is more typesafe and +acurate https://stackoverflow.com/questions/36095323/what-is-the-difference-between-chrono-and-ctime +but, it does not have parsing and formatting for human readable time. +It will get that in c++20, but that's a little too late for us :( +So for now, conversion to/from ctime objects it is.... + +*/ + +class Park_time { + public: + Park_time(int c_id, int s_id); + Park_time(int id_, int customer_id_, int spot_id_, int start_, int duration_); + int id; + int customer_id; + int spot_id; + int duration; + + void clock_out(int c_id, int s_id); + friend std::ostream& operator<<(std::ostream& os, const Park_time& pt); + + private: + high_resolution_clock::time_point start; + high_resolution_clock::time_point end; + void save_db(); + void update_db(); + int auto_increment_db(); // helper + int start_to_int(); // helper +}; + +// function that slowly outputs each character one by one +void text_animation(const string& text, unsigned int pause_time); + +/* +enum classes make it easy to represent categories. +So you can use something like Vehicle_type::twowheeler instead of 2 in code, so you know it's that. +but under the hood, it's still an int. +This is so you don't have to polute the global namespace with unnecesary variables. +enum classes do not permit implicit conversion between int and the enum class, and are in the +Enumclass:: scope in contrast to plain enums. https://en.cppreference.com/w/cpp/language/enum + */ +enum class Vehicle_type { twoweeler = 1, fourweeler = 2 }; + +/* +Customer constructors do the same stuff as all the other constructors. +clock_in and out create and modify park_time objects and store them to +park_instances. Technically, now that we have a working db, we don't need it. +It might have some performance benefits to keeping it, though. +TODO: test or fix this. +save, update, delete and auto increment are the same as in park_time but for customers. + */ + +class Customer { + public: + int id; + string name; + string password; + Vehicle_type vehicle; + string telephone; + int role; + Customer(string name_, string password_, Vehicle_type vehicle_, string telephone_, int role_); + Customer(int id_, string name_, string password_, Vehicle_type vehicle_, + vector instances, string telephone_, int role_); + void clock_in(int s_id); + void clock_out(int s_id); + bool parked(); + int parked_at(); + + void update_db(); + void delete_db(); + + private: + vector park_instances; + void save_db(); + int auto_increment_db(); +}; + +class Park_spot { + public: + int id; + bool taken; + int parked_customer; + Vehicle_type v_type; + + Park_spot(Vehicle_type v_type_); + Park_spot(int id_, bool taken_, int parked, Vehicle_type v_type_); + void clock(Customer& c_customer); + + private: + void save_db(); + void update_db(); + void delete_db(); + int auto_increment_db(); +}; + +/*these are the functions that search the database and create objects from it. +query_parktimes_for_customer searches for the parktimes that are needed in +customer initialisaiton. generally, i see no use outside of that. + +query_customer_with_name searches for customer data by name. +NOTE: query_customer_with_name has been removed, nothing is using it +query_customer_with_id does what the above does, but with id. + +query_parkspot_with_id does what the above do, but with a vector and not to the db. + +populate_spots is used to query for all the park_spots in db and return them in a vector. +We can keep that in memory to reduce calls to the db, but increasing the memory footprint of this +program + +reports_from_x functions query the db for parktimes with various conditions +current_status_parkspots takes in a vector and outputs the status of them + + */ + +vector query_parktimes_for_customer(int cid); +Customer query_customer_with_id(int id); +Park_spot query_parkspot_with_id(int id, vector& parkspots); +int query_role_customer(int id); + +vector populate_spots(); + +void reports_from_parkspot(int spotid, pair period); +void reports_from_allparkspots(pair period); +void current_status_parkspots(vector& spots); +vector reports_from_customer(int cid, pair period); + +// interface functions +void interface(vector& spots); +void interface_member(vector& spots, Customer& c); +void interface_admin(vector& spots); +void park(Customer& c, vector& spots); +void new_customer(); +void new_admin(); +void new_parkspot(vector& spots); +void edit_information(Customer&); + +// time creation +pair create_month_period(); +pair create_week_period(); + +// report functions +void report_single_spot(bool weekly = false); +void report_all_spots(bool weekly = false); +void report_customer(int customerID, bool weekly = false); + +// confirmation function +bool confirm(); +#endif \ No newline at end of file diff --git a/headers/Customer.h b/headers/old/Customer.h similarity index 100% rename from headers/Customer.h rename to headers/old/Customer.h diff --git a/headers/Interface.h b/headers/old/Interface.h similarity index 100% rename from headers/Interface.h rename to headers/old/Interface.h diff --git a/headers/Park_spot.h b/headers/old/Park_spot.h similarity index 100% rename from headers/Park_spot.h rename to headers/old/Park_spot.h diff --git a/headers/Park_time.h b/headers/old/Park_time.h similarity index 100% rename from headers/Park_time.h rename to headers/old/Park_time.h diff --git a/headers/Query.h b/headers/old/Query.h similarity index 100% rename from headers/Query.h rename to headers/old/Query.h diff --git a/headers/data.h b/headers/old/data.h similarity index 100% rename from headers/data.h rename to headers/old/data.h diff --git a/headers/encrypt.h b/headers/old/encrypt.h similarity index 100% rename from headers/encrypt.h rename to headers/old/encrypt.h diff --git a/main.cpp b/main.cpp index 9a59b43..f2ea451 100644 --- a/main.cpp +++ b/main.cpp @@ -1,5 +1,5 @@ -#include "headers/Interface.h" +#include "headers/bigheader.h" /* Code structure is like this: @@ -54,22 +54,3 @@ int main() { interface(parking_spots); } } - -/* -Why is this not in query.cpp? Because somehow, it errors out when it's there. -The error message indicates it is a memory issue but I suspect it's a -concurrency issue. Do not move this. - */ - -vector populate_spots() { - vector spots; - SQLite::Statement query(data::db, "SELECT * FROM Park_spot WHERE id > 0;"); - while (query.executeStep()) { - int id = query.getColumn(0); - int taken = query.getColumn(1); - int cid = query.getColumn(2); - Vehicle_type vtype = Vehicle_type(int(query.getColumn(3))); - spots.push_back({id, taken, cid, vtype}); - } - return spots; -} diff --git a/Customer.cpp b/old/Customer.cpp similarity index 96% rename from Customer.cpp rename to old/Customer.cpp index f34c5bf..3d010c6 100644 --- a/Customer.cpp +++ b/old/Customer.cpp @@ -1,99 +1,99 @@ -#include "headers/Customer.h" - -// constructors -Customer::Customer(string name_, string password_, Vehicle_type vehicle_, string telephone_, - int role_) - : id{auto_increment_db() + 1}, - name{name_}, - password{hash_password(password_)}, - vehicle{vehicle_}, - telephone{telephone_}, - role{role_} { - save_db(); -} - -Customer::Customer(int id_, string name_, string password_, Vehicle_type vehicle_, - vector instances, string telephone_, int role_) - : id{id_}, - name{name_}, - password{password_}, - vehicle{vehicle_}, - park_instances{instances}, - telephone{telephone_}, - role{role_} {} - -// clock in/out methods -// ==================================================================================== -/* -Create a p_time object with start=now and adds to vector - */ -void Customer::clock_in(int s_id) { - Park_time pt{id, s_id}; - park_instances.push_back(pt); -} - -// edit last p_time object in park_instances so end=now -void Customer::clock_out(int s_id) { - park_instances[park_instances.size() - 1].clock_out(id, s_id); -} - -bool Customer::parked() { - if (!park_instances.size()) { - return false; - } - if ((park_instances[park_instances.size() - 1].duration)) { - // if duration of the last parktime == 0, meaning - // that the customer has not clocked out - return false; - } else { - return true; - } -} - -int Customer::parked_at() { return park_instances[park_instances.size() - 1].spot_id; } - -//================================================================================================ -// functions that interact with the database - -void Customer::save_db() { - string statement{"insert into Customer values (, '', '', ,'', );"}; - statement.insert(43, to_string(role)); - statement.insert(41, telephone); - statement.insert(38, to_string(int(vehicle))); - statement.insert(36, password); - statement.insert(32, name); - statement.insert(29, to_string(id)); - SQLite::Transaction transaction(data::db); - data::db.exec(statement); - transaction.commit(); -} - -void Customer::update_db() { - string statement = - "UPDATE Customer SET name = '', password = '', " - "vehicle = '', telephone = '', role = '' where id = '';"; - statement.insert(98, to_string(id)); - statement.insert(84, to_string(role)); - statement.insert(73, telephone); - statement.insert(57, to_string(int(vehicle))); - statement.insert(43, password); - statement.insert(28, name); - data::db.exec(statement); -} - -void Customer::delete_db() { - string statement = "delete from Customer where id= ;"; - statement.insert(statement.length() - 2, to_string(id)); - SQLite::Transaction transaction(data::db); - data::db.exec(statement); - transaction.commit(); -} - -int Customer::auto_increment_db() { - SQLite::Statement max_id(data::db, "select max(id) from Customer;"); - int id = 0; - max_id.executeStep(); - id = max_id.getColumn(0); - max_id.reset(); - return id; -} +#include "headers/Customer.h" + +// constructors +Customer::Customer(string name_, string password_, Vehicle_type vehicle_, string telephone_, + int role_) + : id{auto_increment_db() + 1}, + name{name_}, + password{hash_password(password_)}, + vehicle{vehicle_}, + telephone{telephone_}, + role{role_} { + save_db(); +} + +Customer::Customer(int id_, string name_, string password_, Vehicle_type vehicle_, + vector instances, string telephone_, int role_) + : id{id_}, + name{name_}, + password{password_}, + vehicle{vehicle_}, + park_instances{instances}, + telephone{telephone_}, + role{role_} {} + +// clock in/out methods +// ==================================================================================== +/* +Create a p_time object with start=now and adds to vector + */ +void Customer::clock_in(int s_id) { + Park_time pt{id, s_id}; + park_instances.push_back(pt); +} + +// edit last p_time object in park_instances so end=now +void Customer::clock_out(int s_id) { + park_instances[park_instances.size() - 1].clock_out(id, s_id); +} + +bool Customer::parked() { + if (!park_instances.size()) { + return false; + } + if ((park_instances[park_instances.size() - 1].duration)) { + // if duration of the last parktime == 0, meaning + // that the customer has not clocked out + return false; + } else { + return true; + } +} + +int Customer::parked_at() { return park_instances[park_instances.size() - 1].spot_id; } + +//================================================================================================ +// functions that interact with the database + +void Customer::save_db() { + string statement{"insert into Customer values (, '', '', ,'', );"}; + statement.insert(43, to_string(role)); + statement.insert(41, telephone); + statement.insert(38, to_string(int(vehicle))); + statement.insert(36, password); + statement.insert(32, name); + statement.insert(29, to_string(id)); + SQLite::Transaction transaction(data::db); + data::db.exec(statement); + transaction.commit(); +} + +void Customer::update_db() { + string statement = + "UPDATE Customer SET name = '', password = '', " + "vehicle = '', telephone = '', role = '' where id = '';"; + statement.insert(98, to_string(id)); + statement.insert(84, to_string(role)); + statement.insert(73, telephone); + statement.insert(57, to_string(int(vehicle))); + statement.insert(43, password); + statement.insert(28, name); + data::db.exec(statement); +} + +void Customer::delete_db() { + string statement = "delete from Customer where id= ;"; + statement.insert(statement.length() - 2, to_string(id)); + SQLite::Transaction transaction(data::db); + data::db.exec(statement); + transaction.commit(); +} + +int Customer::auto_increment_db() { + SQLite::Statement max_id(data::db, "select max(id) from Customer;"); + int id = 0; + max_id.executeStep(); + id = max_id.getColumn(0); + max_id.reset(); + return id; +} diff --git a/Interface.cpp b/old/Interface.cpp similarity index 100% rename from Interface.cpp rename to old/Interface.cpp diff --git a/Park_spot.cpp b/old/Park_spot.cpp similarity index 100% rename from Park_spot.cpp rename to old/Park_spot.cpp diff --git a/Park_time.cpp b/old/Park_time.cpp similarity index 100% rename from Park_time.cpp rename to old/Park_time.cpp diff --git a/Query.cpp b/old/Query.cpp similarity index 100% rename from Query.cpp rename to old/Query.cpp diff --git a/data.cpp b/old/data.cpp similarity index 100% rename from data.cpp rename to old/data.cpp diff --git a/newParkManne.cpp b/old/newParkManne.cpp similarity index 100% rename from newParkManne.cpp rename to old/newParkManne.cpp