monoliticc file

This commit is contained in:
MassiveAtoms 2019-07-23 12:34:06 -03:00
parent 1b35b0e261
commit 560d5d7e99
18 changed files with 1303 additions and 135 deletions

View File

@ -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
)

972
bigsource.cpp Normal file
View File

@ -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<system_clock>(seconds(start_));
end = time_point<system_clock>(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<seconds>(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<seconds>(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<Park_time> 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<Park_time> 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_time> 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_time> 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<Park_spot>& parkspots) {
for (Park_spot& i : parkspots) {
if (i.id == id) {
return i;
}
}
}
void reports_from_parkspot(int spotid, pair<int, int> period) {
vector<Park_time> 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<int, int> period) {
vector<Park_time> 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<Park_spot>& 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<Park_time> reports_from_customer(int cid, pair<int, int> period) {
vector<Park_time> 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<Park_spot>& 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<Park_spot>& 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<Park_spot>& 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<Park_spot>& 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<Park_spot>& 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<int, int> 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<int, int> period;
period.first = mktime(date);
date->tm_mon = month;
period.second = mktime(date);
return period;
}
pair<int, int> 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<int, int> 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<int, int> period;
if (weekly) {
period = create_week_period(); // remove the pair<int, int>
} 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<int, int> period;
if (weekly) {
period = create_week_period(); // remove the pair<int, int>
} 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<int, int> 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<Park_spot> populate_spots() {
vector<Park_spot> 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;
}

229
headers/bigheader.h Normal file
View File

@ -0,0 +1,229 @@
#ifndef ENCRYPT_H
#define ENCRYPT_H
#pragma once
#include "../thirdparty/SQLiteCpp/include/SQLiteCpp/SQLiteCpp.h"
#include <chrono>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <sodium.h>
#include <string>
#include <thread>
#include <vector>
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<Park_time> 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_time> 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<Park_time> query_parktimes_for_customer(int cid);
Customer query_customer_with_id(int id);
Park_spot query_parkspot_with_id(int id, vector<Park_spot>& parkspots);
int query_role_customer(int id);
vector<Park_spot> populate_spots();
void reports_from_parkspot(int spotid, pair<int, int> period);
void reports_from_allparkspots(pair<int, int> period);
void current_status_parkspots(vector<Park_spot>& spots);
vector<Park_time> reports_from_customer(int cid, pair<int, int> period);
// interface functions
void interface(vector<Park_spot>& spots);
void interface_member(vector<Park_spot>& spots, Customer& c);
void interface_admin(vector<Park_spot>& spots);
void park(Customer& c, vector<Park_spot>& spots);
void new_customer();
void new_admin();
void new_parkspot(vector<Park_spot>& spots);
void edit_information(Customer&);
// time creation
pair<int, int> create_month_period();
pair<int, int> 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

View File

@ -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<Park_spot> populate_spots() {
vector<Park_spot> 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;
}

View File

@ -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<Park_time> 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<Park_time> 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;
}