Compare commits

...

3 Commits

Author SHA1 Message Date
MassiveAtoms
9ae07dac0a cleaned up imports and using directives 2019-07-23 12:05:40 -03:00
MassiveAtoms
20648487f7 cleaned up imports and using directives 2019-07-23 12:03:49 -03:00
MassiveAtoms
16a20ef6d3 commenting and doing some cleaning 2019-07-23 11:17:55 -03:00
20 changed files with 471 additions and 145 deletions

View File

@ -1,8 +1,8 @@
#include "headers/Customer.h" #include "headers/Customer.h"
// constructors // 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}, : id{auto_increment_db() + 1},
name{name_}, name{name_},
password{hash_password(password_)}, password{hash_password(password_)},
@ -32,7 +32,7 @@ void Customer::clock_in(int s_id) {
park_instances.push_back(pt); 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) { void Customer::clock_out(int s_id) {
park_instances[park_instances.size() - 1].clock_out(id, s_id); park_instances[park_instances.size() - 1].clock_out(id, s_id);
} }
@ -57,14 +57,12 @@ int Customer::parked_at() { return park_instances[park_instances.size() - 1].spo
void Customer::save_db() { void Customer::save_db() {
string statement{"insert into Customer values (, '', '', ,'', );"}; string statement{"insert into Customer values (, '', '', ,'', );"};
// after ( = 28)
statement.insert(43, to_string(role)); statement.insert(43, to_string(role));
statement.insert(41, telephone); statement.insert(41, telephone);
statement.insert(38, to_string(int(vehicle))); statement.insert(38, to_string(int(vehicle)));
statement.insert(36, password); statement.insert(36, password);
statement.insert(32, name); statement.insert(32, name);
statement.insert(29, to_string(id)); statement.insert(29, to_string(id));
// cout << statement;
SQLite::Transaction transaction(data::db); SQLite::Transaction transaction(data::db);
data::db.exec(statement); data::db.exec(statement);
transaction.commit(); transaction.commit();
@ -80,7 +78,6 @@ void Customer::update_db() {
statement.insert(57, to_string(int(vehicle))); statement.insert(57, to_string(int(vehicle)));
statement.insert(43, password); statement.insert(43, password);
statement.insert(28, name); statement.insert(28, name);
// cout << statement;
data::db.exec(statement); data::db.exec(statement);
} }

View File

@ -71,7 +71,7 @@
case 4: { case 4: {
cout << "Exiting...\n"; cout << "Exiting...\n";
Sleep(2000); sleep_for(seconds(2));
goto exit; goto exit;
break; break;
} }
@ -203,7 +203,7 @@
} }
case 4: { case 4: {
std::cout<<"Exiting..."; std::cout<<"Exiting...";
Sleep(2000); sleep_for(seconds(2));
goto exit; goto exit;
break; break;
} break; } break;
@ -383,13 +383,13 @@
if (ver == "YES" | ver == "Yes" | ver == "yes") if (ver == "YES" | ver == "Yes" | ver == "yes")
{ {
std::cout<<"Succes! Changes Saved."; std::cout<<"Succes! Changes Saved.";
Sleep(1000); sleep_for(seconds(1));
return true; return true;
} }
else else
{ {
std::cout<<"No changes committed."; std::cout<<"No changes committed.";
Sleep(1000); sleep_for(seconds(1));
return false; return false;
} }
} }

View File

@ -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_) Park_spot::Park_spot(int id_, bool taken_, int parked, Vehicle_type v_type_)
: parked_customer{parked}, : parked_customer{parked}, id{id_}, v_type{v_type_}, taken{taken_} {}
id{id_},
v_type{v_type_},
taken{taken_} // TODO: think about how init parked?
{}
// clock in en out, calls de juist(in/out) van de customer aan de hand van // clock in en out, calls de correct customer.clock_x depending on internal state of the spot
// internal state van taken
void Park_spot::clock(Customer& c_customer) { void Park_spot::clock(Customer& c_customer) {
if (!taken) { if (!taken) {
parked_customer = c_customer.id; parked_customer = c_customer.id;
@ -46,9 +41,7 @@ void Park_spot::update_db() {
} }
void Park_spot::save_db() { void Park_spot::save_db() {
//(int id, bool taken, int customer_id)
string statement{"insert into Park_spot values ( , , , );"}; string statement{"insert into Park_spot values ( , , , );"};
// after ( = 28)
statement.insert(36, to_string(int(v_type))); statement.insert(36, to_string(int(v_type)));
statement.insert(34, "NULL"); statement.insert(34, "NULL");
statement.insert(32, "0"); statement.insert(32, "0");

View File

@ -17,7 +17,7 @@ Park_time::Park_time(int c_id, int s_id)
save_db(); 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. functions.
*/ */
Park_time::Park_time(int id_, int customer_id_, int spot_id_, int start_, int duration_) 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. simple checking if customer is clocking out at the right spot.
sets end(time of clocking out) and calculates the duration. sets end(time of clocking out) and calculates the duration.
updates the info in the database. updates the info in the database.
*/ */
void Park_time::clock_out(int c_id, int s_id) { void Park_time::clock_out(int c_id, int s_id) {
@ -111,20 +110,7 @@ int Park_time::auto_increment_db() {
return id; return id;
} }
//------------------ test function to help test this // text animtion
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
void text_animation(const string& text, unsigned int pause_time) { void text_animation(const string& text, unsigned int pause_time) {
for (const char m : text) // range loop; for each character in string for (const char m : text) // range loop; for each character in string
{ {

View File

@ -25,30 +25,6 @@ vector<Park_time> query_parktimes_for_customer(int cid) {
//--------------------------------------------- customers //--------------------------------------------- customers
vector<Customer> 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<Customer> 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);
int role = query.getColumn(5);
vector<Park_time> park_instances = query_parktimes_for_customer(id);
result.push_back(
Customer{id, name_, password, Vehicle_type(vehicle), park_instances, telephone, role});
}
return result;
}
Customer query_customer_with_id(int id) { Customer query_customer_with_id(int id) {
/* do not call this function if you are not certain a customer with this id /* do not call this function if you are not certain a customer with this id
exists. exists.
@ -164,7 +140,7 @@ vector<Park_time> reports_from_customer(int cid, pair<int, int> period) {
int duration = query.getColumn(5); int duration = query.getColumn(5);
Park_time result{id, cid, spotid, start, duration}; Park_time result{id, cid, spotid, start, duration};
park_times.push_back(result); park_times.push_back(result);
sum += duration/3600; sum += duration / 3600;
} }
query.reset(); query.reset();

BIN
_test.db3

Binary file not shown.

View File

@ -5,6 +5,7 @@ namespace data {
SQLite::Database start_db() { SQLite::Database start_db() {
/* /*
Opens the database, creates it if it can't find the file. 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); SQLite::Database db("test.db3", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
while (sodium_init() < 0) { while (sodium_init() < 0) {
@ -15,7 +16,8 @@ SQLite::Database start_db() {
salts and other stuff, and needs to be run at least once when working 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 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 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( db.exec(

View File

@ -3,17 +3,19 @@
string hash_password(string password) { string hash_password(string password) {
/* /*
Passing strings and converting to char* because I do not want to be forced 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 to use char * whenever the function is called.
function, the least possible low level stuff outside. Low level stuff in the function, the least possible low level stuff outside.
This uses the password hashing algorithm Argon2 implemented by libsodium. 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. 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 When you do that, the hashed passwords can't be decrypted, and that would be
BAD BAD.
*/ */
const char* password_ = password.c_str(); const char* password_ = password.c_str();
char hashed_password_[crypto_pwhash_STRBYTES]; char hashed_password_[crypto_pwhash_STRBYTES];
int memory_limit = 3.2e+7; // 3.2e7 = 32e6 = 32 mb 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 = int result =
crypto_pwhash_str(hashed_password_, password_, strlen(password_), cpu_limit, memory_limit); crypto_pwhash_str(hashed_password_, password_, strlen(password_), cpu_limit, memory_limit);

BIN
graph.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

394
graph.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -3,7 +3,6 @@
#pragma once #pragma once
#include "Park_time.h" #include "Park_time.h"
#include "data.h"
#include <vector> #include <vector>
@ -11,10 +10,11 @@ using std::vector;
/* /*
enum classes make it easy to represent categories. enum classes make it easy to represent categories.
So you can use something like Vehicle_type::car instead of 2. but under the So you can use something like Vehicle_type::twowheeler instead of 2 in code, so you know it's that.
hood, it's still an int. This is here so you won't have to have global variables but under the hood, it's still an int.
for these categories, or worse, use magic numbers in the code. 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 }; 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. 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 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. park_instances. Technically, now that we have a working db, we don't need it.
TODO: fix this. It might have some performance benefits to keeping it, though.
TODO: test or fix this.
gen_monthly just prints out all the park_time objects in park_instances. save, update, delete and auto increment are the same as in park_time but for customers.
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.
*/ */
class Customer { class Customer {

View File

@ -1,6 +1,5 @@
#include "Query.h" #include "Query.h"
#include <stdlib.h>
#include <synchapi.h>
using std::cin; using std::cin;

View File

@ -6,11 +6,12 @@
/* /*
db representation: db representation:
int id not null int id not null
bool taken not null int taken // the library seems to be having problems with bools as types.
int customer_id (null) (many to one, foreign key, whatever) 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 { class Park_spot {

View File

@ -6,16 +6,12 @@
#include <chrono> #include <chrono>
#include <ctime> #include <ctime>
#include <iostream>
#include <string>
#include <thread> #include <thread>
using namespace std::chrono; using namespace std::chrono;
using std::cout; using std::cout;
using std::flush; using std::flush;
using std::string;
using std::to_string; using std::to_string;
using std::chrono::milliseconds;
using std::this_thread::sleep_for; 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 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. 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 { class Park_time {
@ -67,9 +69,6 @@ class Park_time {
int start_to_int(); // helper int start_to_int(); // helper
}; };
// test function
void Wait(int sec);
// function that slowly outputs each character one by one // function that slowly outputs each character one by one
void text_animation(const string& text, unsigned int pause_time); void text_animation(const string& text, unsigned int pause_time);

View File

@ -3,7 +3,8 @@
#pragma once #pragma once
#include "Park_spot.h" #include "Park_spot.h"
#include <iomanip>
#include <iomanip>
using std::pair; using std::pair;
/*these are the functions that search the database and create objects from it. /*these are the functions that search the database and create objects from it.
@ -12,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. customer initialisaiton. generally, i see no use outside of that.
query_customer_with_name searches for customer data by name. 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_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 populate_spots is used to query for all the park_spots in db and return them in a vector.
objects. 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<T> instead of <T> is for the following reports_from_x functions query the db for parktimes with various conditions
reasons: current_status_parkspots takes in a vector and outputs the status of them
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<Customer> 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.
*/ */
vector<Park_time> query_parktimes_for_customer(int cid); vector<Park_time> query_parktimes_for_customer(int cid);
vector<Customer> query_customer_with_name(string name);
Customer query_customer_with_id(int id); Customer query_customer_with_id(int id);
Park_spot query_parkspot_with_id(int id, vector<Park_spot>& parkspots); Park_spot query_parkspot_with_id(int id, vector<Park_spot>& parkspots);
int query_role_customer(int id); int query_role_customer(int id);

View File

@ -9,7 +9,10 @@ namespace data {
/* /*
start_db is the function that opens the database, and start_db is the function that opens the database, and
if the necesary tables are not there, creates them. 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(); SQLite::Database start_db();
static SQLite::Database db = start_db(); static SQLite::Database db = start_db();

View File

@ -10,11 +10,14 @@
using std::string; using std::string;
/* /*
hash_password takes the password, and encrypts it. This needs to be done, 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 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 stuff(no, not really. It basically hashes the password with the same salt and other parameters, but
to see if the password stored and the given password match. 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); string hash_password(string password);

View File

@ -8,10 +8,10 @@ verify passwords
2. data.cpp and /header/data.h contain the code to start up the database. 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 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 database and query from the database. But there were issues, and we did stuff
includes) and some other issues. the other issues are gone due to the latest in the meantime. Most of the original problems that prevented
refactor, but to make it like my original plan is going to take a few hours, and that are fixed with the latest refactor, but it would take a bit of time to
I have done too much already to want to do more work unless needed. 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 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 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. 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, 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. 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 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 headers. Explanations of how the member functions work(Or how we intended for
them to work) are in the respective .cpp files. void Wait(int sec) them to work) are in the respective .cpp files.
*/ */
static vector<Park_spot> parking_spots = populate_spots(); static vector<Park_spot> parking_spots = populate_spots();

BIN
park

Binary file not shown.

View File

@ -14,12 +14,12 @@ Or click the build icon in vscode *shrugs*
######TO-DO List: ######TO-DO List:
- [x] Admin login - [x] Admin login
- [ ] Billing report menu - [x] Billing report menu
- [ ] Report from a specific month i.e. january - [x] Report from a specific month i.e. january
- [ ] Edit option in menu - [x] Edit option in menu
- [ ] Option to confirm edit after input - [x] Option to confirm edit after input
- [ ] Divide menu in sub menus - [x] Divide menu in sub menus
- [ ] Analytics of customer (e.g. # customers with 2-wheeler or list of customer and telephone numbers etc.) - [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. 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: 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: Last example:
![header includes](graph.png) ![header includes](graph.svg)