diff --git a/Customer.cpp b/Customer.cpp index 450b425..f34c5bf 100644 --- a/Customer.cpp +++ b/Customer.cpp @@ -1,8 +1,8 @@ #include "headers/Customer.h" - // constructors -Customer::Customer(string name_, string password_, Vehicle_type vehicle_, string telephone_, int role_) +Customer::Customer(string name_, string password_, Vehicle_type vehicle_, string telephone_, + int role_) : id{auto_increment_db() + 1}, name{name_}, password{hash_password(password_)}, @@ -13,13 +13,14 @@ Customer::Customer(string name_, string password_, Vehicle_type vehicle_, string } Customer::Customer(int id_, string name_, string password_, Vehicle_type vehicle_, - vector instances, string telephone_) + vector instances, string telephone_, int role_) : id{id_}, name{name_}, password{password_}, vehicle{vehicle_}, park_instances{instances}, - telephone{telephone_} {} + telephone{telephone_}, + role{role_} {} // clock in/out methods // ==================================================================================== @@ -31,7 +32,7 @@ void Customer::clock_in(int s_id) { park_instances.push_back(pt); } -// edit last p_time object so end=now +// 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); } @@ -56,14 +57,12 @@ int Customer::parked_at() { return park_instances[park_instances.size() - 1].spo void Customer::save_db() { string statement{"insert into Customer values (, '', '', ,'', );"}; - // after ( = 28) 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)); - // cout << statement; SQLite::Transaction transaction(data::db); data::db.exec(statement); transaction.commit(); @@ -73,13 +72,12 @@ void Customer::update_db() { string statement = "UPDATE Customer SET name = '', password = '', " "vehicle = '', telephone = '', role = '' where id = '';"; - statement.insert(89, to_string(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); - // cout << statement; data::db.exec(statement); } diff --git a/Interface.cpp b/Interface.cpp index 06a6c34..94adafb 100644 --- a/Interface.cpp +++ b/Interface.cpp @@ -1,307 +1,442 @@ -#include "headers/Interface.h" + #include "headers/Interface.h" -// 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 + // 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); - */ - - 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:\n"; - std::getline(cin, password); - } - 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!"; - } -} - -void interface_member(vector& spots, Customer& c) { - cout << "\nLogged in succesfully!\n"; - cout << "select an option:\n [1] Parking\n[2]Monthly report\n"; - int option; - cin >> option; - cin.ignore(10000, '\n'); - switch (option) { - case 1: { - park(c, spots); - break; - } - case 2: { - cout << "Has not been implemented yet\n"; - break; - } - - default: - break; - } -} - -void interface_admin(vector& spots) { - cout << "\nWelcome to the admin interface.\n"; - cout << "\n[1] Reports & analytics"; - cout << "\n[2] Parking spots"; - cout << "\n[3] Make new user"; - 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 << "Enter option number: "; - int option_1; - cin >> option_1; + 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'); - switch (option_1) { + 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: { - report_all_spots(); + park(c, spots); break; } case 2: { - report_all_spots(true); + 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: { - report_single_spot(); - break; - } + 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: { - report_single_spot(true); + std::cout<<"Exiting..."; + sleep_for(seconds(2)); + goto exit; break; + } break; } - default: - break; - } - } - case 2: { - cout << "[1] See current status of parking spots\n"; - cout << "[2] Make new parking spot\n"; - cout << "Enter option number: "; - int option_2; - cin >> option_2; - cin.ignore(10000, '\n'); - switch (option_2) { - case 1: { - current_status_parkspots(spots); - break; - } - case 2: { - new_parkspot(spots); - } - default: - break; - } - } - case 3: { - cout << "[1] Make new customer\n"; - cout << "[2] Make new admin\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; - } - default: - break; - } - } - default: - break; - } -} -// --------- individual things. + default: + break; + } + goto begin; + exit:; -void park(Customer& c, vector& spots) { - cout << "You have selected parking option.\n"; - if (!(c.parked())) { - cout << "The following spots fit your vehicle and are available: "; - for (Park_spot i : spots) { - if (i.v_type == c.vehicle) { - cout << i.id << ", "; + } + + // --------- 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?"; - int parkID; - cin >> parkID; - cin.ignore(10000, '\n'); - for (Park_spot& i : spots) { - if (i.id == parkID) { - i.clock(c); - cout << "You have parked sucessfully!"; + 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 to customer login + 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 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]twoweeler\n[2] fourweeler\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\n"; - newcustomer.update_db(); -} + 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;*/ -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 newcustomer{name, password, Vehicle_type(vtype), telephone, role}; - cout << "\nNew customer sucessfully created\n"; - newcustomer.update_db(); -} - -void new_parkspot(vector& spots) { - cout << "What type of parking spot? \n[1] twoweeler\n[2] fourweeler\n"; - int vtype; - cin >> vtype; - cin.ignore(10000, '\n'); - Park_spot newspot{Vehicle_type(vtype)}; - spots.push_back(newspot); - cout << "New parking spot sucessfully created.\n"; -} - -// 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; -} - -// ------------------------------ report stuff - -void report_all_spots(bool weekly) { - pair period; - if (weekly) { - period = create_week_period(); // remove the pair - } else { - period = create_month_period(); // ^ + 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();} } - cout << "working timeperiods: " << period.first << ", " << period.second; // DEBUG + // time stuff----------------------------------------------------- - reports_from_allparkspots(period); // TODO: namechange of reports_from_allparkspots in query? -} + 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; + } -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(); + 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; } - 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; + 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; + } } - pair period; - if (weekly) { - pair period = create_week_period(); - } else { - pair period = create_month_period(); + + // ------------------------------ 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? } - reports_from_customer(customerID, period); -} \ No newline at end of file + + 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); + } \ No newline at end of file diff --git a/Park_spot.cpp b/Park_spot.cpp index 3850484..2332e52 100644 --- a/Park_spot.cpp +++ b/Park_spot.cpp @@ -8,14 +8,9 @@ Park_spot::Park_spot(Vehicle_type v_type_) } 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_} // TODO: think about how init parked? -{} + : parked_customer{parked}, id{id_}, v_type{v_type_}, taken{taken_} {} -// clock in en out, calls de juist(in/out) van de customer aan de hand van -// internal state van 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; @@ -46,9 +41,7 @@ void Park_spot::update_db() { } void Park_spot::save_db() { - //(int id, bool taken, int customer_id) string statement{"insert into Park_spot values ( , , , );"}; - // after ( = 28) statement.insert(36, to_string(int(v_type))); statement.insert(34, "NULL"); statement.insert(32, "0"); diff --git a/Park_time.cpp b/Park_time.cpp index 53811ef..2391834 100644 --- a/Park_time.cpp +++ b/Park_time.cpp @@ -17,7 +17,7 @@ Park_time::Park_time(int c_id, int s_id) save_db(); } /* -this one initializes with data from the database. should probably only be used in the query +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_) @@ -30,7 +30,6 @@ Park_time::Park_time(int id_, int customer_id_, int spot_id_, int start_, int du 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) { @@ -60,7 +59,8 @@ std::ostream& operator<<(std::ostream& os, const Park_time& pt) { os << "Customer # " << pt.customer_id << "at parking spot " << pt.spot_id << "\n"; os << "Clocked in :" << std::ctime(&start_); os << "clocked out : " << std::ctime(&end_); - os << "duration : " << pt.duration << "\n"; + float dur_h = pt.duration / 3600.0; + os << "duration : " << dur_h << " h\n"; os << "- - - - - - - - - - - - - - - - - - - -\n"; return os; } @@ -110,20 +110,7 @@ int Park_time::auto_increment_db() { return id; } -//------------------ test function to help test this - -void Wait(int sec) - -{ - /* -a wait function where 1 sec represents 1 hour irl. It has been used for testing -purposes mostly. TODO: Needs to be removed at completion of project, or seperated in a test -cpp/header - */ - std::this_thread::sleep_for(seconds{sec}); -} - -// text animtion duh +// text animtion void text_animation(const string& text, unsigned int pause_time) { for (const char m : text) // range loop; for each character in string { diff --git a/Query.cpp b/Query.cpp index 0dbac1d..b8b658e 100644 --- a/Query.cpp +++ b/Query.cpp @@ -25,29 +25,6 @@ vector query_parktimes_for_customer(int cid) { //--------------------------------------------- customers -vector query_customer_with_name(string name) { - /* - We use this instead of plain customers because: - 1. no error handling needed here if there are no customers - 2. multiple customers could be returned with the same name. - */ - vector result; - SQLite::Statement query(data::db, - "SELECT id, name, password, vehicle FROM Customer WHERE name = ?;"); - query.bind(1, name); - while (query.executeStep()) { - int id = query.getColumn(0); - string name_ = query.getColumn(1); - string password = query.getColumn(2); - int vehicle = query.getColumn(3); // cast to vehicle - string telephone = query.getColumn(4); - vector park_instances = query_parktimes_for_customer(id); - result.push_back( - Customer{id, name_, password, Vehicle_type(vehicle), park_instances, telephone}); - } - return result; -} - Customer query_customer_with_id(int id) { /* do not call this function if you are not certain a customer with this id exists. @@ -63,8 +40,10 @@ Customer query_customer_with_id(int id) { 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}; + Customer result{id, name, password, Vehicle_type(vehicle), park_instances, telephone, role}; return result; } } @@ -135,18 +114,20 @@ void reports_from_allparkspots(pair period) { } void current_status_parkspots(vector& spots) { + cout << "P.spot \t\tStatus\t\t Customer\n"; for (auto& i : spots) { - cout << "---------------------------\n"; - cout << "PS #" << i.id << "\n"; - cout << "Taken: " << ((i.taken) ? "true" : "false") << "\n"; + cout << "\n" << i.id << "\t\t" << ((i.taken) ? "true" : "false"); if (i.taken) { - cout << "Customer#" << i.parked_customer << " parked there\n"; + 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); @@ -159,11 +140,14 @@ vector reports_from_customer(int cid, pair period) { 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 << i; + 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; } \ No newline at end of file diff --git a/data.cpp b/data.cpp index 3a3737b..f3d1cbb 100644 --- a/data.cpp +++ b/data.cpp @@ -5,6 +5,7 @@ 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) { @@ -15,12 +16,13 @@ SQLite::Database start_db() { 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 + 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)"); + "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)"); diff --git a/encrypt.cpp b/encrypt.cpp index 06b5a90..d7385f6 100644 --- a/encrypt.cpp +++ b/encrypt.cpp @@ -3,17 +3,19 @@ string hash_password(string password) { /* Passing strings and converting to char* because I do not want to be forced - to use char * whenever I want to call the function. Low level stuff in the - function, the least possible low level stuff outside. + 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 + 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 is n_threads + 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); diff --git a/graph.PNG b/graph.PNG new file mode 100644 index 0000000..d058897 Binary files /dev/null and b/graph.PNG differ diff --git a/graph.png b/graph.png deleted file mode 100644 index 42b0120..0000000 Binary files a/graph.png and /dev/null differ diff --git a/headers/Customer.h b/headers/Customer.h index 3933fd7..7e483f8 100644 --- a/headers/Customer.h +++ b/headers/Customer.h @@ -3,7 +3,6 @@ #pragma once #include "Park_time.h" -#include "data.h" #include @@ -11,10 +10,11 @@ using std::vector; /* enum classes make it easy to represent categories. -So you can use something like Vehicle_type::car instead of 2. but under the -hood, it's still an int. This is here so you won't have to have global variables -for these categories, or worse, use magic numbers in the code. - +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 }; @@ -22,15 +22,9 @@ 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. -TODO: fix this. - -gen_monthly just prints out all the park_time objects in park_instances. -It should (and can safely) be removed, but it's here as a quick example of -report generation It has no logic to speak of that only generates report of -ptime objects of this month. -TODO: remove when have seperate report generation functions. - -save, update, delete and auto increment are the same as in park_time. +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 { @@ -41,9 +35,9 @@ class Customer { Vehicle_type vehicle; string telephone; int role; - Customer(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_); + vector instances, string telephone_, int role_); void clock_in(int s_id); void clock_out(int s_id); bool parked(); diff --git a/headers/Interface.h b/headers/Interface.h index b7171c3..7363c9e 100644 --- a/headers/Interface.h +++ b/headers/Interface.h @@ -1,7 +1,9 @@ #include "Query.h" + using std::cin; +//interface functions void interface(vector& spots); void interface_member(vector& spots, Customer& c); void interface_admin(vector& spots); @@ -9,11 +11,16 @@ 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 stuff +// report functions void report_single_spot(bool weekly = false); -void report_all_spots(bool weekly = false); \ No newline at end of file +void report_all_spots(bool weekly = false); +void report_customer(int customerID, bool weekly = false); + +// confirmation function +bool confirm(); \ No newline at end of file diff --git a/headers/Park_spot.h b/headers/Park_spot.h index d4ebf92..7c35e02 100644 --- a/headers/Park_spot.h +++ b/headers/Park_spot.h @@ -6,11 +6,12 @@ /* db representation: int id not null -bool taken not null -int customer_id (null) (many to one, foreign key, whatever) +int taken // the library seems to be having problems with bools as types. +int customer_id (nullable) + +Represents a parkspot. +Has the same kind of db functions, same kind of constructors as previous classes. -Dit representeert een parkeerplaats. Het heeft als internal state alleen dat t -bezet is of niet. */ class Park_spot { diff --git a/headers/Park_time.h b/headers/Park_time.h index 854392a..8003909 100644 --- a/headers/Park_time.h +++ b/headers/Park_time.h @@ -6,16 +6,12 @@ #include #include -#include -#include #include using namespace std::chrono; using std::cout; using std::flush; -using std::string; using std::to_string; -using std::chrono::milliseconds; using std::this_thread::sleep_for; /* @@ -44,6 +40,12 @@ auto_increment pulls the highest id stored in the db, to be used in the construc 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 { @@ -67,9 +69,6 @@ class Park_time { int start_to_int(); // helper }; -// test function -void Wait(int sec); - // function that slowly outputs each character one by one void text_animation(const string& text, unsigned int pause_time); diff --git a/headers/Query.h b/headers/Query.h index fc70a39..5b8a9ef 100644 --- a/headers/Query.h +++ b/headers/Query.h @@ -3,6 +3,8 @@ #pragma once #include "Park_spot.h" + +#include using std::pair; /*these are the functions that search the database and create objects from it. @@ -11,49 +13,21 @@ 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 and return them as -objects. +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 -The design desision to use vector instead of is for the following -reasons: - -1. some of these can potentially return more than one object. For example, 2 -customers who have the same name. - -2. I have no clue how many of you have done error handling in c++ -(try/catch/finally). -Ya boi is nice and doesn't want to bombard you with more new concepts than needed. -so now you'd do - -vector test = query_customer_with_name("Testman"); - -if (!test.size()) {print no customers found, do stuff} -else if (test.size() > 1) { do stuff to get the right one if you only need one -} - -instead of -try { - customer test = query_customer_with_name("Testman"); -} -catch(someException.probablycalled_not_found) {do_Stuff}; -catch(...) { - do stuff -} -finally{ - do more stuff -} - -3. Ya boi needs to brush up on how to create custom exceptions class, and it will complicate code -furhter. +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); -vector query_customer_with_name(string name); Customer query_customer_with_id(int id); Park_spot query_parkspot_with_id(int id, vector& parkspots); int query_role_customer(int id); diff --git a/headers/data.h b/headers/data.h index 0266d53..949c85a 100644 --- a/headers/data.h +++ b/headers/data.h @@ -9,7 +9,10 @@ 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. +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(); diff --git a/headers/encrypt.h b/headers/encrypt.h index 4fcc6e5..6e6797a 100644 --- a/headers/encrypt.h +++ b/headers/encrypt.h @@ -10,11 +10,14 @@ using std::string; /* hash_password takes the password, and encrypts it. This needs to be done, -because storing passwords in plaintext is BAD! +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) and -to see if the password stored and the given password match. +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); diff --git a/main.cpp b/main.cpp index 579c2c1..9a59b43 100644 --- a/main.cpp +++ b/main.cpp @@ -1,5 +1,5 @@ -#include "headers/Interface.h" +#include "headers/Interface.h" /* Code structure is like this: @@ -8,10 +8,10 @@ verify passwords 2. data.cpp and /header/data.h contain the code to start up the database. Originally, they were supposed to contain all the functions to save to the -database and query from the database. I had trouble doing that, (cyclical -includes) and some other issues. the other issues are gone due to the latest -refactor, but to make it like my original plan is going to take a few hours, and -I have done too much already to want to do more work unless needed. +database and query from the database. But there were issues, and we did stuff +in the meantime. Most of the original problems that prevented +that are fixed with the latest refactor, but it would take a bit of time to +place the functions there and test that they do what they do. The functions to save to a database have been integrated in the classes themself, and unless issues arrise from that I'm not changing that. Functions to get objects from the database are in Query.cpp en header. @@ -35,9 +35,13 @@ namesake. Cointain functions that search the database and return objects(P_time, P_spot, Customer) It is the least tested of the whole project, use with care. +7.Interface.cpp and header +contain all the functions needed to have an interface that's seen when the program is +running. + Explanation of what members do of P_time, P_spot, Customer are in the respective -headers. Explanations of how the member functions work(Or how I intended for -them to work) are in the respective .cpp files. void Wait(int sec) +headers. Explanations of how the member functions work(Or how we intended for +them to work) are in the respective .cpp files. */ static vector parking_spots = populate_spots(); @@ -45,19 +49,9 @@ static vector parking_spots = populate_spots(); static vector park_customers; int main() { - // state of db: - // er zijn 10 parkspots, 5 met biketype en 5 met pickup type - // er is een customer met id 1(testcustomer) met password "password" + while (true) { - cin.clear(); interface(parking_spots); - cout << "\nexit? Input 'y' or 'n'.."; - char n; - cin >> n; - cin.clear(); - if (n == 'y') { - break; - } } } diff --git a/oldtest.db3 b/oldtest.db3 deleted file mode 100644 index 4f0d862..0000000 Binary files a/oldtest.db3 and /dev/null differ diff --git a/park b/park deleted file mode 100755 index 6f8a7fd..0000000 Binary files a/park and /dev/null differ diff --git a/readme.md b/readme.md index 37b15b4..786bc36 100644 --- a/readme.md +++ b/readme.md @@ -14,12 +14,12 @@ Or click the build icon in vscode *shrugs* ######TO-DO List: - [x] Admin login -- [ ] Billing report menu -- [ ] Report from a specific month i.e. january -- [ ] Edit option in menu -- [ ] Option to confirm edit after input -- [ ] Divide menu in sub menus -- [ ] Analytics of customer (e.g. # customers with 2-wheeler or list of customer and telephone numbers etc.) +- [x] Billing report menu +- [x] Report from a specific month i.e. january +- [x] Edit option in menu +- [x] Option to confirm edit after input +- [x] Divide menu in sub menus +- [x] Analytics of customer (e.g. # customers with 2-wheeler or list of customer and telephone numbers etc.) This is a graph of how everything is connected. If you need to add functionality that doesn't fall in any of these, and you're unsure of what to include, you can decide something like this: @@ -37,5 +37,5 @@ Since customer also includes data, i don't have to include it again. Last example: -![header includes](graph.png) +![header includes](graph.PNG) diff --git a/test.db3 b/test.db3 index 6f05261..bdb8ef9 100644 Binary files a/test.db3 and b/test.db3 differ