Compare commits
23 Commits
089e9115f1
...
6ba57049df
Author | SHA1 | Date | |
---|---|---|---|
6ba57049df | |||
0d6a22fb8b | |||
c81bddf79a | |||
8071bae4d9 | |||
30290fc317 | |||
b02994d041 | |||
6413749d76 | |||
c610a0572c | |||
2c3436ee94 | |||
5a88299f9e | |||
d3f3ebac3f | |||
e635d80869 | |||
434eaf0ce0 | |||
3941e43e7f | |||
983f1827e6 | |||
c3dbabe42d | |||
98db5681d8 | |||
c036cc43dd | |||
ea057e7401 | |||
b6967e665e | |||
287f9f4751 | |||
94e63e8f76 | |||
365b24e651 |
15
.idea/dataSources.xml
generated
Normal file
15
.idea/dataSources.xml
generated
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="budget.sqlite" uuid="bba32b4d-f424-4d0f-8ee6-c6e2f73d9010">
|
||||
<driver-ref>sqlite.xerial</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||
<jdbc-url>jdbc:sqlite:$USER_HOME$/.local/share/budget/budget.sqlite</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
<driver-properties>
|
||||
<property name="foreign_keys" value="true" />
|
||||
</driver-properties>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
6
.idea/discord.xml
generated
Normal file
6
.idea/discord.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DiscordProjectSettings">
|
||||
<option name="show" value="PROJECT_FILES" />
|
||||
</component>
|
||||
</project>
|
@ -3,16 +3,38 @@ project(budget)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
add_executable(${PROJECT_NAME} src/main.cpp src/main.h
|
||||
src/money/account.cpp src/money/account.h
|
||||
src/money/payment.cpp src/money/payment.h
|
||||
src/money/recept.cpp src/money/recept.h
|
||||
src/data/data.tpp src/data/data.h
|
||||
src/data/accountData.cpp src/data/accountData.h
|
||||
src/money/earning.cpp src/money/earning.h
|
||||
src/data/dateMoney.cpp src/data/dateMoney.h
|
||||
src/optHandlers/optHandler.cpp src/optHandlers/optHandler.h
|
||||
src/optHandlers/accountOptHandler.cpp src/optHandlers/accountOptHandler.h
|
||||
src/optHandlers/mainOptHandler.cpp src/optHandlers/mainOptHandler.h
|
||||
utilities/math.cpp utilities/math.h
|
||||
utilities/polynomialFunction.cpp utilities/polynomialFunction.h src/money/transaction.cpp src/money/transaction.h)
|
||||
set(SOURCES
|
||||
src/main.cpp
|
||||
src/optHandlers/mainOptHandler.cpp
|
||||
src/optHandlers/operation.cpp
|
||||
src/optHandlers/accountOperation.cpp
|
||||
src/optHandlers/createOperation.cpp
|
||||
src/optHandlers/earnOperation.cpp
|
||||
src/optHandlers/PaymentOperation.cpp
|
||||
src/database.cpp
|
||||
src/utilities.cpp)
|
||||
|
||||
set(HEADERS
|
||||
src/optHandlers/mainOptHandler.h
|
||||
src/optHandlers/operation.h
|
||||
src/optHandlers/accountOperation.h
|
||||
src/optHandlers/createOperation.h
|
||||
src/optHandlers/earnOperation.h
|
||||
src/optHandlers/PaymentOperation.h
|
||||
src/database.h
|
||||
src/exceptions/helpRequested.h
|
||||
src/exceptions/badValue.h
|
||||
src/utilities.h
|
||||
src/sqliteDb.h
|
||||
src/main.h)
|
||||
|
||||
|
||||
add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS})
|
||||
|
||||
install(TARGETS ${PROJECT_NAME}
|
||||
RUNTIME DESTINATION bin)
|
||||
|
||||
add_custom_target(uninstall
|
||||
COMMAND xargs rm -v < install_manifest.txt)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} sqlite3)
|
||||
|
48
README.md
Normal file
48
README.md
Normal file
@ -0,0 +1,48 @@
|
||||
Latest Build:
|
||||
|
||||
[](https://themissingcrowbar.com/teamcity/repository/downloadAll/Budget_Build/lastFinished/artifacts.zip)
|
||||
|
||||
BuildServer:
|
||||
|
||||
[](https://themissingcrowbar.com/teamcity/buildConfiguration/Budget_Build#all-projects)
|
||||
|
||||
Output of budget.
|
||||
|
||||
```
|
||||
Usage:
|
||||
budget <action> [options] ...
|
||||
Actions:
|
||||
-h --help Prints this.
|
||||
-a --account<=STRING> Management tools for an account.
|
||||
-c --create<=STRING> Creates a new account with NAME.
|
||||
-e --earn<=STRING> Add an earning to an account.
|
||||
-p --payment<=STRING> Add a payment to an account.
|
||||
Account Options: [-dvD]
|
||||
-d --delete Deletes specified account.
|
||||
--force-delete Deletes the specified account without confirmation.
|
||||
-v --value Gets the current value of the account.
|
||||
-D --description<=STRING> Changes the description of the account.
|
||||
Create Options: [-d]
|
||||
-d --description<=STRING> Sets a description for an account.
|
||||
Earn Options: -v [-drD]
|
||||
-v --value=<FlOAT> Value for earning.
|
||||
-d --description=<STRING> Description for earning.
|
||||
-r --receipt=<PATH> Path to file to store in DB as receipt.
|
||||
-D --date=<mm/dd/yyyyTHH:MM:SS> Date as dd/mm/yyyyTHH:MM:SS. Default will be today.
|
||||
Payment Options: -v [-drD]
|
||||
-v --value=<FlOAT> Value for payment.
|
||||
-d --description=<STRING> Description for payment.
|
||||
-r --receipt=<PATH> Path to file to store in DB as receipt.
|
||||
-D --date=<mm/dd/yyyyTHH:MM:SS> Date as dd/mm/yyyyTHH:MM:SS. Default will be today.
|
||||
```
|
||||
|
||||
Arguments are processed like blocks with each one terminated by the next Action. For example
|
||||
|
||||
```
|
||||
budget -cAcct -eAcct -v10.00 -r"./receipt.pdf" -pAcct -v5.50 -r"./payment.pdf"
|
||||
````
|
||||
|
||||
Does the following in order:
|
||||
Creates an account named Acct with no description.
|
||||
Earns 10.00 to it with a receipt.
|
||||
Pays 5.50 to it with a receipt.
|
@ -1,99 +0,0 @@
|
||||
//
|
||||
// Created by quentin on 8/4/22.
|
||||
//
|
||||
|
||||
#include "accountData.h"
|
||||
#include <iostream>
|
||||
|
||||
AccountData::AccountData(const std::string &file) : Data(file) {
|
||||
account = AccountData::createObject();
|
||||
}
|
||||
|
||||
AccountData::AccountData(const std::string &file, const std::string &name) : Data(file) {
|
||||
std::string json = R"({"name":")" + name + R"(","payments":[],"earnings":[]})";
|
||||
document.Parse(json.c_str());
|
||||
account = AccountData::createObject();
|
||||
std::cout << "Created account " << name << std::endl;
|
||||
}
|
||||
|
||||
Account AccountData::createObject() {
|
||||
if (isJsonCorrect()) {
|
||||
std::string name = document["name"].GetString();
|
||||
|
||||
std::list<Payment> payments;
|
||||
for (auto &domPayment : document["payments"].GetArray()) {
|
||||
Receipt receipt(domPayment["receipt"]["file"].GetString());
|
||||
double value = domPayment["value"].GetDouble();
|
||||
std::time_t date = domPayment["date"].GetInt64();
|
||||
std::tm tmTime{};
|
||||
std::memcpy(&tmTime, std::localtime(&date), sizeof(struct tm));
|
||||
|
||||
payments.emplace_back(value, receipt, tmTime);
|
||||
}
|
||||
|
||||
std::list<Earning> earnings;
|
||||
for (auto &domEarning : document["earnings"].GetArray()) {
|
||||
double value = domEarning["value"].GetDouble();
|
||||
std::time_t date = domEarning["date"].GetInt64();
|
||||
std::tm tmTime{};
|
||||
std::memcpy(&tmTime, std::localtime(&date), sizeof(struct tm));
|
||||
|
||||
earnings.emplace_back(value, tmTime);
|
||||
}
|
||||
|
||||
return Account(payments, earnings, name);
|
||||
}
|
||||
|
||||
std::string strAnswer;
|
||||
bool answer;
|
||||
while (true) {
|
||||
std::cout << "Account " + getFilePath() + " Is malformed, would you like to remove it? (Y/n): ";
|
||||
std::cin >> strAnswer;
|
||||
std::transform(strAnswer.begin(), strAnswer.end(), strAnswer.begin(), ::toupper);
|
||||
if (strAnswer == "Y" || strAnswer == "YES") {
|
||||
answer = true;
|
||||
break;
|
||||
}
|
||||
if (strAnswer == "N" || strAnswer == "NO") {
|
||||
answer = false;
|
||||
break;
|
||||
}
|
||||
std::cout << "Sorry, answer " + strAnswer + " not understood." << std::endl;
|
||||
}
|
||||
|
||||
return Account();
|
||||
}
|
||||
|
||||
bool AccountData::isJsonCorrect() {
|
||||
if (document.IsObject() &&
|
||||
document.HasMember("name") && document["name"].IsString() &&
|
||||
document.HasMember("payments") && document["payments"].IsArray() &&
|
||||
document.HasMember("earnings") && document["earnings"].IsArray()) {
|
||||
for (auto &domPayment : document["payments"].GetArray()) {
|
||||
if (!(domPayment.IsObject() &&
|
||||
domPayment.HasMember("receipt") && domPayment["receipt"].IsObject() &&
|
||||
domPayment["receipt"].HasMember("file") && domPayment["receipt"]["file"].IsString() &&
|
||||
domPayment.HasMember("value") && domPayment["value"].IsDouble() &&
|
||||
domPayment.HasMember("date") && domPayment["date"].IsInt64())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (auto &domEarning : document["earnings"].GetArray()) {
|
||||
if (!(domEarning.IsObject() &&
|
||||
domEarning.HasMember("value") && domEarning["value"].IsDouble() &&
|
||||
domEarning.HasMember("date") && domEarning["date"].IsInt64())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Account *AccountData::getAccount() {
|
||||
return &account;
|
||||
}
|
||||
|
||||
AccountData::AccountData() : Data("/dev/null") {}
|
@ -1,36 +0,0 @@
|
||||
//
|
||||
// Created by quentin on 8/4/22.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_ACCOUNTDATA_H
|
||||
#define BUDGET_ACCOUNTDATA_H
|
||||
|
||||
|
||||
#include "data.h"
|
||||
#include "../money/account.h"
|
||||
|
||||
static const char *jsonTemplate = "";
|
||||
|
||||
class AccountData : private Data<Account> {
|
||||
public:
|
||||
AccountData();
|
||||
|
||||
AccountData(const AccountData &) = default;
|
||||
|
||||
explicit AccountData(const std::string &file);
|
||||
|
||||
explicit AccountData(const std::string &file, const std::string &name);
|
||||
|
||||
Account *getAccount();
|
||||
|
||||
using Data<Account>::deleteObject;
|
||||
private:
|
||||
Account account;
|
||||
|
||||
Account createObject() override;
|
||||
|
||||
bool isJsonCorrect() override;
|
||||
};
|
||||
|
||||
|
||||
#endif //BUDGET_ACCOUNTDATA_H
|
@ -1,43 +0,0 @@
|
||||
//
|
||||
// Created by quentin on 8/4/22.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_DATA_H
|
||||
#define BUDGET_DATA_H
|
||||
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
#include <rapidjson/document.h>
|
||||
|
||||
template<class T>
|
||||
class Data {
|
||||
public:
|
||||
rapidjson::Document document;
|
||||
|
||||
virtual ~Data();
|
||||
|
||||
Data(const Data &data);
|
||||
|
||||
explicit Data(std::string file);
|
||||
|
||||
virtual T createObject() = 0;
|
||||
|
||||
std::string getFilePath();
|
||||
|
||||
void flushToFile();
|
||||
|
||||
void deleteObject();
|
||||
|
||||
private:
|
||||
const std::string fileName;
|
||||
const std::string homeDirectory = getpwuid(getuid())->pw_dir;
|
||||
std::string fileDirectory;
|
||||
bool flush = true;
|
||||
|
||||
virtual bool isJsonCorrect() = 0;
|
||||
};
|
||||
|
||||
#include "data.tpp"
|
||||
|
||||
#endif //BUDGET_DATA_H
|
@ -1,85 +0,0 @@
|
||||
//
|
||||
// Created by quentin on 8/4/22.
|
||||
//
|
||||
|
||||
|
||||
#include "data.h"
|
||||
|
||||
#include <utility>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <rapidjson/stringbuffer.h>
|
||||
#include <rapidjson/writer.h>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
template<class T>
|
||||
Data<T>::Data(std::string file) : fileName(std::move(file)) {
|
||||
// Create file if it doesnt exist
|
||||
std::ifstream chkExistIfs(getFilePath(), std::ios::in | std::ios::binary | std::ios::ate);
|
||||
if (chkExistIfs) {
|
||||
// File exists, were not creating one
|
||||
std::ifstream::pos_type fileSize = chkExistIfs.tellg();
|
||||
chkExistIfs.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<char> bytes(fileSize);
|
||||
chkExistIfs.read(bytes.data(), fileSize);
|
||||
|
||||
document.Parse(std::string(bytes.data(), fileSize).c_str());
|
||||
chkExistIfs.close();
|
||||
} else {
|
||||
// File doesnt exist we need to create one
|
||||
// This is the job of the derives constructor.
|
||||
chkExistIfs.close();
|
||||
};
|
||||
}
|
||||
|
||||
template<class T>
|
||||
std::string Data<T>::getFilePath() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void Data<T>::flushToFile() {
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||
document.Accept(writer);
|
||||
|
||||
std::ofstream file(getFilePath());
|
||||
file << buffer.GetString();
|
||||
file.close();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
Data<T>::~Data() {
|
||||
if (flush) {
|
||||
flushToFile();
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void Data<T>::deleteObject() {
|
||||
remove(getFilePath().c_str());
|
||||
flush = false;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
Data<T>::Data(const Data &data) : fileName(data.fileName) {
|
||||
// Create file if it doesnt exist
|
||||
std::ifstream chkExistIfs(getFilePath(), std::ios::in | std::ios::binary | std::ios::ate);
|
||||
if (chkExistIfs) {
|
||||
// File exists, were not creating one
|
||||
std::ifstream::pos_type fileSize = chkExistIfs.tellg();
|
||||
chkExistIfs.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<char> bytes(fileSize);
|
||||
chkExistIfs.read(bytes.data(), fileSize);
|
||||
|
||||
document.Parse(std::string(bytes.data(), fileSize).c_str());
|
||||
chkExistIfs.close();
|
||||
} else {
|
||||
// File doesnt exist we need to create one
|
||||
// This is the job of the derives constructor.
|
||||
chkExistIfs.close();
|
||||
};
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
//
|
||||
// Created by quentin on 8/12/22.
|
||||
//
|
||||
|
||||
#include "dateMoney.h"
|
||||
|
||||
|
||||
DateMoney::DateMoney(const double *value, tm *date) : value(value), date(date) {}
|
||||
|
||||
bool DateMoney::operator<(const DateMoney &rhs) const {
|
||||
return mktime(date) < mktime(rhs.date);
|
||||
}
|
||||
|
||||
bool DateMoney::operator>(const DateMoney &rhs) const {
|
||||
return rhs < *this;
|
||||
}
|
||||
|
||||
bool DateMoney::operator<=(const DateMoney &rhs) const {
|
||||
return !(rhs < *this);
|
||||
}
|
||||
|
||||
bool DateMoney::operator>=(const DateMoney &rhs) const {
|
||||
return !(*this < rhs);
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const DateMoney &money) {
|
||||
os << "Value: " << *money.value << " Date: " << money.date->tm_mon + 1 << "/" << money.date->tm_mday << "/"
|
||||
<< money.date->tm_year+1900;
|
||||
return os;
|
||||
}
|
||||
|
||||
const double *DateMoney::getValue() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
tm *DateMoney::getDate() const {
|
||||
return date;
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
//
|
||||
// Created by quentin on 8/12/22.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_DATEMONEY_H
|
||||
#define BUDGET_DATEMONEY_H
|
||||
|
||||
|
||||
#include <ctime>
|
||||
#include <ostream>
|
||||
|
||||
class DateMoney {
|
||||
public:
|
||||
DateMoney(const double *value, tm *date);
|
||||
|
||||
[[nodiscard]] const double *getValue() const;
|
||||
|
||||
[[nodiscard]] tm *getDate() const;
|
||||
|
||||
bool operator<(const DateMoney &rhs) const;
|
||||
|
||||
bool operator>(const DateMoney &rhs) const;
|
||||
|
||||
bool operator<=(const DateMoney &rhs) const;
|
||||
|
||||
bool operator>=(const DateMoney &rhs) const;
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &os, const DateMoney &money);
|
||||
|
||||
private:
|
||||
const double *value;
|
||||
tm *date;
|
||||
};
|
||||
|
||||
|
||||
#endif //BUDGET_DATEMONEY_H
|
250
src/database.cpp
Normal file
250
src/database.cpp
Normal file
@ -0,0 +1,250 @@
|
||||
//
|
||||
// Created by quentin on 1/18/23.
|
||||
//
|
||||
|
||||
#include "database.h"
|
||||
#include <sqlite3.h>
|
||||
#include <stdexcept>
|
||||
|
||||
bool Database::doesAccountExist(const std::string &account, sqlite3 *db) {
|
||||
sqlite3_stmt *stmt;
|
||||
int rc = sqlite3_prepare_v2(db, "SELECT * FROM account WHERE name = ?", -1, &stmt, nullptr);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
throw std::runtime_error("Failed to prepare account exist query.");
|
||||
|
||||
sqlite3_bind_text(stmt, 1, account.c_str(), -1, SQLITE_TRANSIENT);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
if (rc == SQLITE_ROW) {
|
||||
return true;
|
||||
} else if (rc == SQLITE_DONE) {
|
||||
return false;
|
||||
} else {
|
||||
throw std::runtime_error("Failed to step account existing statement.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Database::deleteAccount(const std::string &account, sqlite3 *db) {
|
||||
sqlite3_stmt *stmt;
|
||||
int rc = sqlite3_prepare_v2(db, "DELETE FROM account WHERE name = ?", -1, &stmt, nullptr);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
throw std::runtime_error("Failed to delete account " + account);
|
||||
|
||||
sqlite3_bind_text(stmt, 1, account.c_str(), -1, SQLITE_TRANSIENT);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
if (rc != SQLITE_DONE)
|
||||
throw std::runtime_error("Failed to delete account " + account);
|
||||
}
|
||||
|
||||
double Database::getValue(const std::string &account, sqlite3 *db) {
|
||||
sqlite3_stmt *stmt;
|
||||
int rc = sqlite3_prepare_v2(db, "SELECT id, cachedValue FROM account WHERE name = ?", -1, &stmt, nullptr);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
throw std::runtime_error("Failed preparing cashedValue " + account);
|
||||
|
||||
sqlite3_bind_text(stmt, 1, account.c_str(), -1, SQLITE_TRANSIENT);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
|
||||
if (rc == SQLITE_ROW) {
|
||||
double value;
|
||||
if (sqlite3_column_type(stmt, 1) == SQLITE_NULL) {
|
||||
value = cacheAccountValue(sqlite3_column_int64(stmt, 0), db);
|
||||
sqlite3_finalize(stmt);
|
||||
return value;
|
||||
}
|
||||
value = sqlite3_column_double(stmt, 1);
|
||||
sqlite3_finalize(stmt);
|
||||
return value;
|
||||
} else if (rc == SQLITE_DONE)
|
||||
throw std::runtime_error("Account doesnt exist? Shouldn't be possible in this call.");
|
||||
else
|
||||
throw std::runtime_error("Failed to step getValue statement.");
|
||||
}
|
||||
|
||||
double Database::cacheAccountValue(long long accountId, sqlite3 *db) {
|
||||
sqlite3_stmt *stmt;
|
||||
int rc = sqlite3_prepare_v2(db, "SELECT SUM(value) FROM ("
|
||||
"SELECT value * -1 as value FROM payment WHERE accountId = ? "
|
||||
"UNION ALL "
|
||||
"SELECT value FROM earning WHERE accountId = ?);", -1, &stmt, nullptr);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
throw std::runtime_error("Failed preparing get cashedValue " + std::to_string(accountId));
|
||||
|
||||
sqlite3_bind_int64(stmt, 1, accountId);
|
||||
sqlite3_bind_int64(stmt, 2, accountId);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
|
||||
double value;
|
||||
if (rc == SQLITE_ROW)
|
||||
value = sqlite3_column_double(stmt, 0);
|
||||
else if (rc == SQLITE_DONE)
|
||||
value = 0;
|
||||
else
|
||||
throw std::runtime_error("Failed get cashedValue " + std::to_string(accountId));
|
||||
|
||||
sqlite3_reset(stmt);
|
||||
rc = sqlite3_prepare_v2(db, "UPDATE account SET cachedValue = ? WHERE id = ?", -1, &stmt, nullptr);
|
||||
if (rc != SQLITE_OK)
|
||||
throw std::runtime_error("Failed preparing set cashedValue " + std::to_string(accountId));
|
||||
|
||||
sqlite3_bind_double(stmt, 1, value);
|
||||
sqlite3_bind_int64(stmt, 2, accountId);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
if (rc == SQLITE_DONE)
|
||||
return value;
|
||||
throw std::runtime_error("Failed to set cashedValue for " + std::to_string(accountId));
|
||||
}
|
||||
|
||||
double Database::cacheAccountValue(const std::string &account, sqlite3 *db) {
|
||||
sqlite3_stmt *stmt;
|
||||
int rc = sqlite3_prepare_v2(db, "SELECT SUM(value) FROM ("
|
||||
"SELECT value * -1 as value FROM payment WHERE accountId = "
|
||||
"(SELECT id FROM account where name = ?) "
|
||||
"UNION ALL "
|
||||
"SELECT value FROM earning WHERE accountId = "
|
||||
"(SELECT id FROM account where name = ?));", -1, &stmt, nullptr);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
throw std::runtime_error("Failed preparing get cashedValue " + account);
|
||||
|
||||
sqlite3_bind_text(stmt, 1, account.c_str(), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(stmt, 2, account.c_str(), -1, SQLITE_TRANSIENT);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
|
||||
double value;
|
||||
if (rc == SQLITE_ROW)
|
||||
value = sqlite3_column_double(stmt, 0);
|
||||
else if (rc == SQLITE_DONE)
|
||||
value = 0;
|
||||
else
|
||||
throw std::runtime_error("Failed get cashedValue " + account);
|
||||
|
||||
sqlite3_reset(stmt);
|
||||
rc = sqlite3_prepare_v2(db, "UPDATE account SET cachedValue = ? WHERE name = ?", -1, &stmt, nullptr);
|
||||
if (rc != SQLITE_OK)
|
||||
throw std::runtime_error("Failed preparing set cashedValue " + account);
|
||||
|
||||
sqlite3_bind_double(stmt, 1, value);
|
||||
sqlite3_bind_text(stmt, 2, account.c_str(), -1, SQLITE_TRANSIENT);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
if (rc == SQLITE_DONE)
|
||||
return value;
|
||||
throw std::runtime_error("Failed to set cashedValue for " + account);
|
||||
}
|
||||
|
||||
void Database::accountDescription(const std::string &account, const std::string &description, sqlite3 *db) {
|
||||
sqlite3_stmt *stmt;
|
||||
int rc = sqlite3_prepare_v2(db, "UPDATE account SET description = ? WHERE name = ?", -1, &stmt, nullptr);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
throw std::runtime_error("Failed preparing accountDescription statement");
|
||||
|
||||
sqlite3_bind_text(stmt, 1, description.c_str(), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(stmt, 2, account.c_str(), -1, SQLITE_TRANSIENT);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
if (rc != SQLITE_DONE)
|
||||
throw std::runtime_error("Failed to set description on " + account);
|
||||
}
|
||||
|
||||
void Database::createAccount(const std::string &account, const std::string &description, sqlite3 *db) {
|
||||
sqlite3_stmt *stmt;
|
||||
int rc = sqlite3_prepare_v2(db, "INSERT INTO account (name, description) VALUES (?, ?)", -1, &stmt, nullptr);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
throw std::runtime_error("Failed preparing createAccount statement");
|
||||
|
||||
sqlite3_bind_text(stmt, 1, account.c_str(), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(stmt, 2, description.c_str(), -1, SQLITE_TRANSIENT);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
if (rc != SQLITE_DONE)
|
||||
throw std::runtime_error("Failed to create account " + account);
|
||||
}
|
||||
|
||||
void Database::createAccount(const std::string &account, sqlite3 *db) {
|
||||
sqlite3_stmt *stmt;
|
||||
int rc = sqlite3_prepare_v2(db, "INSERT INTO account (name) VALUES (?)", -1, &stmt, nullptr);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
throw std::runtime_error("Failed preparing createAccount statement");
|
||||
|
||||
sqlite3_bind_text(stmt, 1, account.c_str(), -1, SQLITE_TRANSIENT);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
if (rc != SQLITE_DONE)
|
||||
throw std::runtime_error("Failed to create account " + account);
|
||||
}
|
||||
|
||||
long long int Database::earn(const std::string &account, long double &value, std::string &description, std::string &receipt,
|
||||
long long date, sqlite3 *db) {
|
||||
sqlite3_stmt *stmt;
|
||||
int rc = sqlite3_prepare_v2(db, "INSERT INTO earning (value, description, receipt, accountId, date) VALUES "
|
||||
"(?, ?, ?, (SELECT id FROM account WHERE name = ?), ?);",
|
||||
-1, &stmt, nullptr);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
throw std::runtime_error("Failed preparing earn statement");
|
||||
|
||||
sqlite3_bind_double(stmt, 1, value);
|
||||
sqlite3_bind_text(stmt, 2, (description.empty() ? nullptr : description.c_str()), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(stmt, 3, (receipt.empty() ? nullptr : receipt.c_str()), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(stmt, 4, (account.empty() ? nullptr : account.c_str()), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_int64(stmt, 5, date);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
if (rc != SQLITE_DONE)
|
||||
throw std::runtime_error("Failed to create earning");
|
||||
cacheAccountValue(account, db);
|
||||
|
||||
return sqlite3_last_insert_rowid(db);
|
||||
}
|
||||
|
||||
long long int Database::pay(const std::string &account, long double &value, std::string &description, std::string &receipt,
|
||||
long long date, sqlite3 *db) {
|
||||
sqlite3_stmt *stmt;
|
||||
int rc = sqlite3_prepare_v2(db, "INSERT INTO payment (value, description, receipt, accountId, date) VALUES "
|
||||
"(?, ?, ?, (SELECT id FROM account WHERE name = ?), ?);",
|
||||
-1, &stmt, nullptr);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
throw std::runtime_error("Failed preparing pay statement");
|
||||
|
||||
sqlite3_bind_double(stmt, 1, value);
|
||||
sqlite3_bind_text(stmt, 2, (description.empty() ? nullptr : description.c_str()), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(stmt, 3, (receipt.empty() ? nullptr : receipt.c_str()), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(stmt, 4, (account.empty() ? nullptr : account.c_str()), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_int64(stmt, 5, date);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
if (rc != SQLITE_DONE)
|
||||
throw std::runtime_error("Failed to create payment");
|
||||
cacheAccountValue(account, db);
|
||||
|
||||
return sqlite3_last_insert_rowid(db);
|
||||
}
|
150
src/database.h
Normal file
150
src/database.h
Normal file
@ -0,0 +1,150 @@
|
||||
//
|
||||
// Created by quentin on 1/18/23.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_DATABASE_H
|
||||
#define BUDGET_DATABASE_H
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <sqlite3.h>
|
||||
|
||||
class Database {
|
||||
public:
|
||||
/**
|
||||
* @brief Checks if an account exists in the database
|
||||
*
|
||||
* @param account The name of the account to check for
|
||||
* @param db The database connection to use
|
||||
*
|
||||
* @return True if the account exists, false otherwise
|
||||
*
|
||||
* @throws std::runtime_error If the database query fails
|
||||
*/
|
||||
static bool doesAccountExist(const std::string &account, sqlite3 *db);
|
||||
|
||||
/**
|
||||
* @brief Deletes an account from the database
|
||||
*
|
||||
* @param account The name of the account to delete
|
||||
* @param db The database connection to use
|
||||
*
|
||||
* @return True if the account was successfully deleted, false otherwise
|
||||
*
|
||||
* @throws std::runtime_error If the database query fails
|
||||
*/
|
||||
static void deleteAccount(const std::string &account, sqlite3 *db);
|
||||
|
||||
/**
|
||||
* @brief Retrieves the cached value of an account from the database
|
||||
*
|
||||
* @param account The name of the account to retrieve the value of
|
||||
* @param db The database connection to use
|
||||
*
|
||||
* @return The cached value of the account
|
||||
*
|
||||
* @throws std::runtime_error If the database query fails or the account doesn't exist
|
||||
*/
|
||||
static double getValue(const std::string &account, sqlite3 *db);
|
||||
|
||||
/**
|
||||
* @brief Caches the value of an account by calculating the sum of all payments and earnings
|
||||
*
|
||||
* @param accountId The ID of the account to cache the value of
|
||||
* @param db The database connection to use
|
||||
*
|
||||
* @return The cached value of the account
|
||||
*
|
||||
* @throws std::runtime_error If the database query fails
|
||||
*/
|
||||
static double cacheAccountValue(long long accountId, sqlite3 *db);
|
||||
|
||||
/**
|
||||
* @brief Caches the value of an account by calculating the sum of all payments and earnings
|
||||
*
|
||||
* @param account The name of the account to cache the value of
|
||||
* @param db The database connection to use
|
||||
*
|
||||
* @return The cached value of the account
|
||||
*
|
||||
* @throws std::runtime_error If the database query fails
|
||||
*/
|
||||
static double cacheAccountValue(const std::string &account, sqlite3 *db);
|
||||
|
||||
/**
|
||||
* @brief Sets a new description for an account
|
||||
*
|
||||
* @param account The account name
|
||||
* @param description The new description
|
||||
* @param db The database connection to use
|
||||
*
|
||||
* @throws std::runtime_error If the statement fails to prepare or the step fails to execute
|
||||
*/
|
||||
static void accountDescription(const std::string &account, const std::string &description, sqlite3 *db);
|
||||
|
||||
/**
|
||||
* @brief Creates a new account in the database with the given name and description
|
||||
*
|
||||
* @param account The name of the account to create
|
||||
* @param description The description of the account to create
|
||||
* @param db The sqlite3 database connection to use
|
||||
*
|
||||
* @throws std::runtime_error If the account could not be created in the database
|
||||
*/
|
||||
static void createAccount(const std::string &account, const std::string &description, sqlite3 *db);
|
||||
|
||||
/**
|
||||
* @brief Creates a new account in the database with the given name
|
||||
*
|
||||
* @param account The name of the account to create
|
||||
* @param db The sqlite3 database connection to use
|
||||
*
|
||||
* @throws std::runtime_error If the account could not be created in the database
|
||||
*/
|
||||
static void createAccount(const std::string &account, sqlite3 *db);
|
||||
|
||||
/**
|
||||
* @brief The function records an earning in the database
|
||||
*
|
||||
* @param account The name of the account to record the earning in
|
||||
* @param value The value of the earning
|
||||
* @param description A description of the earning, can be an empty string
|
||||
* @param receipt A receipt of the earning, can be an empty string
|
||||
* @param date The date of the earning in UNIX time
|
||||
* @param db The sqlite3 database connection
|
||||
*
|
||||
* @throws std::runtime_error If the earning could not be created in the database
|
||||
*
|
||||
* This function records an earning in the database by inserting a new row in the 'earning' table with the provided information.
|
||||
* The accountId is retrieved from the 'account' table using the provided account name.
|
||||
* If the query fails to prepare or execute, the function throws a runtime_error.
|
||||
*/
|
||||
static long long int
|
||||
earn(const std::string &account, long double &value, std::string &description, std::string &receipt,
|
||||
long long int date,
|
||||
sqlite3 *db);
|
||||
|
||||
/**
|
||||
* @brief The function records an payment in the database
|
||||
*
|
||||
* @param account The name of the account to record the payment in
|
||||
* @param value The value of the payment
|
||||
* @param description A description of the payment, can be an empty string
|
||||
* @param receipt A receipt of the payment, can be an empty string
|
||||
* @param date The date of the payment in UNIX time
|
||||
* @param db The sqlite3 database connection
|
||||
*
|
||||
* @throws std::runtime_error If the payment could not be created in the database
|
||||
*
|
||||
* This function records an payment in the database by inserting a new row in the 'payment' table with the provided information.
|
||||
* The accountId is retrieved from the 'account' table using the provided account name.
|
||||
* If the query fails to prepare or execute, the function throws a runtime_error.
|
||||
*/
|
||||
static long long int
|
||||
pay(const std::string &account, long double &value, std::string &description, std::string &receipt,
|
||||
long long int date,
|
||||
sqlite3 *db);
|
||||
};
|
||||
|
||||
|
||||
#endif //BUDGET_DATABASE_H
|
17
src/exceptions/badValue.h
Normal file
17
src/exceptions/badValue.h
Normal file
@ -0,0 +1,17 @@
|
||||
//
|
||||
// Created by quentin on 1/18/23.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_BADVALUE_H
|
||||
#define BUDGET_BADVALUE_H
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace Budget::Exceptions {
|
||||
class BadValue : public std::runtime_error {
|
||||
public:
|
||||
explicit BadValue(const std::string &what_arg) : std::runtime_error(what_arg) {}
|
||||
};
|
||||
}
|
||||
|
||||
#endif //BUDGET_BADVALUE_H
|
18
src/exceptions/helpRequested.h
Normal file
18
src/exceptions/helpRequested.h
Normal file
@ -0,0 +1,18 @@
|
||||
//
|
||||
// Created by quentin on 1/18/23.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_HELPREQUESTED_H
|
||||
#define BUDGET_HELPREQUESTED_H
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace Budget::Exceptions {
|
||||
class HelpRequested : public std::runtime_error {
|
||||
public:
|
||||
explicit HelpRequested(const std::string &what_arg) : std::runtime_error(what_arg) {}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif //BUDGET_HELPREQUESTED_H
|
139
src/main.cpp
139
src/main.cpp
@ -2,110 +2,79 @@
|
||||
// Created by quentin on 8/3/22.
|
||||
//
|
||||
|
||||
#include "main.h"
|
||||
#include "data/accountData.h"
|
||||
#include "optHandlers/accountOptHandler.h"
|
||||
#include "optHandlers/mainOptHandler.h"
|
||||
#include "../utilities/math.h"
|
||||
#include "exceptions/helpRequested.h"
|
||||
#include "exceptions/badValue.h"
|
||||
#include "sqliteDb.h"
|
||||
#include "main.h"
|
||||
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include <unordered_map>
|
||||
#include <sqlite3.h>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
using namespace Budget;
|
||||
|
||||
const std::string homeDirectory = getpwuid(getuid())->pw_dir;
|
||||
const std::string configD = homeDirectory + "/.config/budget/";
|
||||
const std::string storageD = homeDirectory + "/.local/share/budget/";
|
||||
const char* createTables = "CREATE TABLE account (id INTEGER CONSTRAINT account_pk PRIMARY KEY AUTOINCREMENT, name TEXT, description TEXT, cachedValue DOUBLE);"
|
||||
"CREATE UNIQUE INDEX account_name_uindex ON account (name);"
|
||||
"CREATE TABLE earning (id INTEGER CONSTRAINT earning_pk PRIMARY KEY AUTOINCREMENT, value DOUBLE NOT NULL, description TEXT, receipt TEXT, accountId INT NOT NULL REFERENCES account ON UPDATE CASCADE ON DELETE CASCADE, date INTEGER NOT NULL);"
|
||||
"CREATE INDEX earning_date_index ON earning (date DESC);"
|
||||
"CREATE TABLE payment (id INTEGER CONSTRAINT payment_pk PRIMARY KEY AUTOINCREMENT, value DOUBLE NOT NULL, description TEXT, receipt TEXT, accountId INT NOT NULL REFERENCES account ON UPDATE CASCADE ON DELETE CASCADE, date INTEGER NOT NULL);"
|
||||
"CREATE INDEX payment_date_index ON payment (date DESC);";
|
||||
|
||||
void createRequiredFolders() {
|
||||
std::filesystem::create_directory(configD);
|
||||
std::filesystem::create_directories(storageD);
|
||||
std::filesystem::create_directories(storageD + "accounts");
|
||||
std::filesystem::create_directories(storageD + "receipts");
|
||||
std::filesystem::create_directories(storageD + "receipts/payment");
|
||||
std::filesystem::create_directories(storageD + "receipts/earn");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
std::vector<char *> args(argv, argv + argc);
|
||||
|
||||
// Parse main options (-ah)
|
||||
OptHandlers::MainOptHandler mainOptHandler(args);
|
||||
mainOptHandler.parse();
|
||||
if (mainOptHandler.getSetOpts()->help)
|
||||
mainOptHandler.help();
|
||||
|
||||
createRequiredFolders();
|
||||
// Read all accounts saved and store them in accounts
|
||||
std::unordered_map<std::string, AccountData> accounts;
|
||||
for (const auto &file : std::filesystem::directory_iterator(
|
||||
homeDirectory + "/.local/share/budget/accounts")) {
|
||||
AccountData account(file.path());
|
||||
accounts.insert(std::pair(account.getAccount()->getName(), account));
|
||||
|
||||
sqlite3 *db;
|
||||
SqliteDb dbRAII(db);
|
||||
|
||||
int rc;
|
||||
rc = sqlite3_open(databaseFile.c_str(), &db);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
throw std::runtime_error("Error opening database connection.");
|
||||
|
||||
rc = sqlite3_exec(db, "PRAGMA foreign_keys = 1;", nullptr, nullptr, nullptr);
|
||||
if (rc != SQLITE_OK)
|
||||
throw std::runtime_error("Error enabling foreign_keys. Database might be malformed.");
|
||||
|
||||
rc = sqlite3_exec(db, "BEGIN", nullptr, nullptr, nullptr);
|
||||
if (rc != SQLITE_OK)
|
||||
throw std::runtime_error("Couldn't begin transaction");
|
||||
|
||||
rc = sqlite3_exec(db, createTables, nullptr, nullptr, nullptr);
|
||||
if (rc != SQLITE_OK)
|
||||
throw std::runtime_error("Couldn't create the tables");
|
||||
|
||||
std::vector<char *> args(argv, argv + argc);
|
||||
try {
|
||||
OptHandlers::MainOptHandler moh(args, db);
|
||||
std::queue<std::unique_ptr<OptHandlers::Operation>> *opts = &moh.operations;
|
||||
|
||||
while (!opts->empty()) {
|
||||
opts->front()->commit();
|
||||
opts->pop();
|
||||
}
|
||||
|
||||
} catch (const Budget::Exceptions::HelpRequested &e) {
|
||||
return 0;
|
||||
} catch (const Budget::Exceptions::BadValue &e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Parse account options if main options tells us to.
|
||||
if (mainOptHandler.getSetOpts()->account) {
|
||||
std::vector<char *> vec{argv[0]};
|
||||
vec.insert(vec.end(), args.begin() + mainOptHandler.getSetOpts()->accountArgStart, args.end());
|
||||
OptHandlers::AccountOptHandler accountOptHandler(vec);
|
||||
accountOptHandler.parse();
|
||||
rc = sqlite3_exec(db, "COMMIT", nullptr, nullptr, nullptr);
|
||||
if (rc != SQLITE_OK)
|
||||
throw std::runtime_error("Couldn't commit transaction");
|
||||
|
||||
// Do what we need to do for parsed options
|
||||
if (accountOptHandler.getSetOpts()->help) {
|
||||
accountOptHandler.help();
|
||||
}
|
||||
if (accountOptHandler.getSetOpts()->create) {
|
||||
auto a = accounts.find(accountOptHandler.getSetOpts()->delAccount);
|
||||
if (a == accounts.end()) {
|
||||
accounts.emplace(std::piecewise_construct,
|
||||
std::make_tuple(accountOptHandler.getSetOpts()->createAccount), std::make_tuple(
|
||||
storageD + "accounts/" + accountOptHandler.getSetOpts()->createAccount + ".json",
|
||||
accountOptHandler.getSetOpts()->createAccount));
|
||||
} else {
|
||||
std::cout << "Account " << accountOptHandler.getSetOpts()->delAccount << " already exists."
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
if (accountOptHandler.getSetOpts()->value) {
|
||||
auto a = accounts.find(accountOptHandler.getSetOpts()->valueAccount);
|
||||
if (a != accounts.end()) {
|
||||
int value = a->second.getAccount()->getValue();
|
||||
std::vector<DateMoney> timeline = a->second.getAccount()->getTimeline();
|
||||
printf("Account value: %d\n", value);
|
||||
printf("Last 10 transactions:\n");
|
||||
for (int i = 0; i < timeline.size() && i < 10; i++) {
|
||||
const double *amount = timeline[i].getValue();
|
||||
const tm *date = timeline[i].getDate();
|
||||
if (*amount <= 0) {
|
||||
// Red
|
||||
printf("Value: \033[31m%.2f\033[0m, ", *amount);
|
||||
}
|
||||
else {
|
||||
// Green
|
||||
printf("Value: \033[32m%.2f\033[0m, ", *amount);
|
||||
}
|
||||
printf("Date: %d/%d/%d\n", date->tm_mon+1, date->tm_mday, date->tm_year+1900);
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
if (accountOptHandler.getSetOpts()->del) {
|
||||
auto a = accounts.find(accountOptHandler.getSetOpts()->delAccount);
|
||||
if (a != accounts.end()) {
|
||||
accounts.erase(a);
|
||||
std::cout << "Deleted account: " << accountOptHandler.getSetOpts()->delAccount << std::endl;
|
||||
}
|
||||
}
|
||||
if (accountOptHandler.getSetOpts()->list) {
|
||||
std::cout << "Accounts: " << std::endl;
|
||||
for (auto &accountPair : accounts) {
|
||||
std::cout << accountPair.second.getAccount()->getName() << ": " << std::endl;
|
||||
std::cout << " Value: " << accountPair.second.getAccount()->getValue() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
11
src/main.h
11
src/main.h
@ -1,8 +1,17 @@
|
||||
//
|
||||
// Created by quentin on 8/12/22.
|
||||
// Created by quentin on 1/31/23.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_MAIN_H
|
||||
#define BUDGET_MAIN_H
|
||||
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
|
||||
const static std::string homeDirectory = getpwuid(getuid())->pw_dir;
|
||||
const static std::string configD = homeDirectory + "/.config/budget/";
|
||||
const static std::string storageD = homeDirectory + "/.local/share/budget/";
|
||||
const static std::string databaseFile = homeDirectory + "/.local/share/budget/budget.sqlite";
|
||||
|
||||
|
||||
#endif //BUDGET_MAIN_H
|
||||
|
@ -1,44 +0,0 @@
|
||||
//
|
||||
// Created by quentin on 8/4/22.
|
||||
//
|
||||
|
||||
#include "account.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
Account::Account(std::list<Payment> payments, std::list<Earning> earnings,
|
||||
std::string name) : payments(std::move(payments)), earnings(std::move(earnings)),
|
||||
name(std::move(name)) {}
|
||||
|
||||
Account::Account() {
|
||||
name = "";
|
||||
}
|
||||
|
||||
int Account::getValue() {
|
||||
int total = 0;
|
||||
for (auto &payment : payments) {
|
||||
total -= payment.value;
|
||||
}
|
||||
for (auto &earning : earnings) {
|
||||
total += earning.value;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
std::vector<DateMoney> Account::getTimeline() {
|
||||
std::vector<DateMoney> timeline;
|
||||
for (auto &payment : payments) {
|
||||
timeline.emplace_back(payment.getValue(), payment.getDate());
|
||||
}
|
||||
for (auto &earning : earnings) {
|
||||
timeline.emplace_back(&earning.value, earning.getDate());
|
||||
}
|
||||
std::sort(timeline.begin(), timeline.end(), std::greater<>());
|
||||
|
||||
return timeline;
|
||||
}
|
||||
|
||||
const std::string &Account::getName() const {
|
||||
return name;
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
//
|
||||
// Created by quentin on 8/4/22.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_ACCOUNT_H
|
||||
#define BUDGET_ACCOUNT_H
|
||||
|
||||
|
||||
#include "payment.h"
|
||||
#include "earning.h"
|
||||
#include "../data/dateMoney.h"
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class Account {
|
||||
public:
|
||||
Account(std::list<Payment> payments, std::list<Earning> earnings, std::string name);
|
||||
|
||||
Account();
|
||||
|
||||
int getValue();
|
||||
|
||||
std::vector<DateMoney> getTimeline();
|
||||
|
||||
[[nodiscard]] const std::string &getName() const;
|
||||
|
||||
private:
|
||||
std::list<Payment> payments;
|
||||
std::list<Earning> earnings;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
|
||||
#endif //BUDGET_ACCOUNT_H
|
@ -1,7 +0,0 @@
|
||||
//
|
||||
// Created by quentin on 8/11/22.
|
||||
//
|
||||
|
||||
#include "earning.h"
|
||||
|
||||
Earning::Earning(const double value, std::tm date) : Transaction(value, date) {}
|
@ -1,17 +0,0 @@
|
||||
//
|
||||
// Created by quentin on 8/11/22.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_EARNING_H
|
||||
#define BUDGET_EARNING_H
|
||||
|
||||
#include <ctime>
|
||||
#include "transaction.h"
|
||||
|
||||
class Earning : public Transaction {
|
||||
public:
|
||||
explicit Earning(double value, std::tm date);
|
||||
};
|
||||
|
||||
|
||||
#endif //BUDGET_EARNING_H
|
@ -1,19 +0,0 @@
|
||||
//
|
||||
// Created by quentin on 8/4/22.
|
||||
//
|
||||
|
||||
#include "payment.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
Payment::Payment(const double value, Receipt receipt, std::tm date) : Transaction(value, date), receipt(std::move(receipt)) {
|
||||
negativeValue = -value;
|
||||
}
|
||||
|
||||
Receipt &Payment::getReceipt() {
|
||||
return receipt;
|
||||
}
|
||||
|
||||
const double *Payment::getValue() {
|
||||
return &negativeValue;
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
//
|
||||
// Created by quentin on 8/4/22.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_PAYMENT_H
|
||||
#define BUDGET_PAYMENT_H
|
||||
|
||||
|
||||
#include "recept.h"
|
||||
#include "transaction.h"
|
||||
|
||||
class Payment : public Transaction {
|
||||
public:
|
||||
Payment(double value, Receipt receipt, std::tm date);
|
||||
|
||||
Receipt &getReceipt();
|
||||
|
||||
const double *getValue();
|
||||
|
||||
private:
|
||||
Receipt receipt;
|
||||
|
||||
double negativeValue;
|
||||
};
|
||||
|
||||
|
||||
#endif //BUDGET_PAYMENT_H
|
@ -1,9 +0,0 @@
|
||||
//
|
||||
// Created by quentin on 8/4/22.
|
||||
//
|
||||
|
||||
#include "recept.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
Receipt::Receipt(std::string file) : file(std::move(file)) {}
|
@ -1,21 +0,0 @@
|
||||
//
|
||||
// Created by quentin on 8/4/22.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_RECEPT_H
|
||||
#define BUDGET_RECEPT_H
|
||||
|
||||
|
||||
#include <string>
|
||||
|
||||
class Receipt {
|
||||
private:
|
||||
public:
|
||||
explicit Receipt(std::string file);
|
||||
|
||||
private:
|
||||
std::string file;
|
||||
};
|
||||
|
||||
|
||||
#endif //BUDGET_RECEPT_H
|
@ -1,11 +0,0 @@
|
||||
//
|
||||
// Created by quentin on 9/16/22.
|
||||
//
|
||||
|
||||
#include "transaction.h"
|
||||
|
||||
Transaction::Transaction(const double value, const tm &date) : value(value), date(date) {}
|
||||
|
||||
tm *Transaction::getDate() {
|
||||
return &date;
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
//
|
||||
// Created by quentin on 9/16/22.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_TRANSACTION_H
|
||||
#define BUDGET_TRANSACTION_H
|
||||
|
||||
#include <ctime>
|
||||
|
||||
class Transaction {
|
||||
public:
|
||||
Transaction(double value, const tm &date);
|
||||
|
||||
const double value;
|
||||
|
||||
tm *getDate();
|
||||
|
||||
private:
|
||||
std::tm date;
|
||||
};
|
||||
|
||||
|
||||
#endif //BUDGET_TRANSACTION_H
|
34
src/optHandlers/PaymentOperation.cpp
Normal file
34
src/optHandlers/PaymentOperation.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
//
|
||||
// Created by quentin on 1/17/23.
|
||||
//
|
||||
|
||||
#include <fstream>
|
||||
#include "PaymentOperation.h"
|
||||
#include "../database.h"
|
||||
#include "../exceptions/badValue.h"
|
||||
#include "../main.h"
|
||||
|
||||
using namespace Budget::OptHandlers;
|
||||
|
||||
void PaymentOperation::commit() {
|
||||
if (!Database::doesAccountExist(account, db))
|
||||
throw Budget::Exceptions::BadValue("Account " + account + " doesn't exist");
|
||||
|
||||
long long id = Database::pay(account, flags.value, flags.description, flags.receipt, flags.date, db);
|
||||
|
||||
if (!flags.receipt.empty()) {
|
||||
std::string extension;
|
||||
size_t pos = flags.receipt.find_last_of('.');
|
||||
if (pos != std::string::npos) {
|
||||
extension = flags.receipt.substr(pos);
|
||||
}
|
||||
|
||||
std::ifstream source(flags.receipt, std::ios::binary);
|
||||
std::ofstream dest(storageD + "receipts/payment/" + std::to_string(id) + extension, std::ios::binary);
|
||||
dest << source.rdbuf();
|
||||
source.close();
|
||||
dest.close();
|
||||
}
|
||||
}
|
||||
|
||||
PaymentOperation::PaymentOperation(sqlite3 *db, std::string account) : Operation(db), account(std::move(account)) {}
|
36
src/optHandlers/PaymentOperation.h
Normal file
36
src/optHandlers/PaymentOperation.h
Normal file
@ -0,0 +1,36 @@
|
||||
//
|
||||
// Created by quentin on 1/17/23.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_PAYMENTOPERATION_H
|
||||
#define BUDGET_PAYMENTOPERATION_H
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <ctime>
|
||||
#include "operation.h"
|
||||
|
||||
namespace Budget::OptHandlers {
|
||||
class PaymentOperation : public Operation {
|
||||
public:
|
||||
void commit() override;
|
||||
|
||||
explicit PaymentOperation(sqlite3 *db, std::string account);
|
||||
|
||||
struct Flags : public Operation::Flags {
|
||||
long double value;
|
||||
std::string description;
|
||||
std::string receipt;
|
||||
long long date = std::time(nullptr);
|
||||
};
|
||||
|
||||
|
||||
Flags flags;
|
||||
private:
|
||||
std::string account;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //BUDGET_PAYMENTOPERATION_H
|
35
src/optHandlers/accountOperation.cpp
Normal file
35
src/optHandlers/accountOperation.cpp
Normal file
@ -0,0 +1,35 @@
|
||||
//
|
||||
// Created by quentin on 1/17/23.
|
||||
//
|
||||
|
||||
#include "accountOperation.h"
|
||||
#include "../database.h"
|
||||
#include "../exceptions/badValue.h"
|
||||
#include "../utilities.h"
|
||||
|
||||
#include <utility>
|
||||
#include <iostream>
|
||||
|
||||
using namespace Budget::OptHandlers;
|
||||
|
||||
void AccountOperation::commit() {
|
||||
if (!Database::doesAccountExist(account, db))
|
||||
throw Budget::Exceptions::BadValue("Account " + account + " doesn't exist");
|
||||
|
||||
if (flags.del) {
|
||||
if (flags.forceDel || Utilities::confirm("Are you sure you'd like to delete " + account)) {
|
||||
Database::deleteAccount(account, db);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags.value) {
|
||||
std::cout << Database::getValue(account, db) << std::endl;
|
||||
}
|
||||
|
||||
if (!flags.description.empty()) {
|
||||
Database::accountDescription(account, flags.description, db);
|
||||
}
|
||||
}
|
||||
|
||||
AccountOperation::AccountOperation(sqlite3 *db, std::string account) : Operation(db), account(std::move(account)) {}
|
32
src/optHandlers/accountOperation.h
Normal file
32
src/optHandlers/accountOperation.h
Normal file
@ -0,0 +1,32 @@
|
||||
//
|
||||
// Created by quentin on 1/17/23.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_ACCOUNTOPERATION_H
|
||||
#define BUDGET_ACCOUNTOPERATION_H
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include "operation.h"
|
||||
|
||||
namespace Budget::OptHandlers {
|
||||
class AccountOperation : public Operation {
|
||||
public:
|
||||
void commit() override;
|
||||
|
||||
explicit AccountOperation(sqlite3 *db, std::string account);
|
||||
|
||||
struct Flags : public Operation::Flags {
|
||||
bool del = false;
|
||||
bool forceDel = false;
|
||||
bool value = false;
|
||||
std::string description;
|
||||
};
|
||||
|
||||
Flags flags;
|
||||
private:
|
||||
std::string account;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //BUDGET_ACCOUNTOPERATION_H
|
@ -1,70 +0,0 @@
|
||||
//
|
||||
// Created by quentin on 8/13/22.
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include "accountOptHandler.h"
|
||||
|
||||
using namespace Budget::OptHandlers;
|
||||
|
||||
|
||||
void AccountOptHandler::parse() {
|
||||
struct option longOpts[] = {
|
||||
{"help", no_argument, nullptr, 'h'},
|
||||
{"list", no_argument, nullptr, 'l'},
|
||||
{"delete", required_argument, nullptr, 'd'},
|
||||
{"create", required_argument, nullptr, 'c'},
|
||||
{"value", required_argument, nullptr, 'v'},
|
||||
};
|
||||
|
||||
while (true) {
|
||||
int opt = getopt_long(getArgc(), getArgv(), "hld:c:v:", longOpts, nullptr);
|
||||
|
||||
if (opt == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
setOpts.help = true;
|
||||
break;
|
||||
case 'l':
|
||||
setOpts.list = true;
|
||||
break;
|
||||
case 'd':
|
||||
setOpts.del = true;
|
||||
setOpts.delAccount = optarg;
|
||||
break;
|
||||
case 'c':
|
||||
setOpts.create = true;
|
||||
setOpts.createAccount = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
setOpts.value = true;
|
||||
setOpts.valueAccount = optarg;
|
||||
break;
|
||||
case '?':
|
||||
setOpts.help = true;
|
||||
setOpts.helpOut = stderr;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AccountOptHandler::help() {
|
||||
fprintf(setOpts.helpOut, "Help budget -a {-cvd account|-hl}\n");
|
||||
fprintf(setOpts.helpOut, " -h --help Output this message.\n");
|
||||
fprintf(setOpts.helpOut, " -l --list List available accounts.\n");
|
||||
fprintf(setOpts.helpOut, " -d --delete account Delete the specified account.\n");
|
||||
fprintf(setOpts.helpOut, " -c --create account Create a new account.\n");
|
||||
fprintf(setOpts.helpOut, " -v --value account Print the current value of account.\n");
|
||||
}
|
||||
|
||||
const AccountOptHandler::SetOpts *AccountOptHandler::getSetOpts() const {
|
||||
return &setOpts;
|
||||
}
|
||||
|
||||
AccountOptHandler::AccountOptHandler(const std::vector<char *> &argv) : OptHandler(argv) {}
|
||||
|
@ -1,41 +0,0 @@
|
||||
//
|
||||
// Created by quentin on 8/13/22.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_ACCOUNTOPTHANDLER_H
|
||||
#define BUDGET_ACCOUNTOPTHANDLER_H
|
||||
|
||||
|
||||
#include "optHandler.h"
|
||||
|
||||
namespace Budget::OptHandlers {
|
||||
class AccountOptHandler : public OptHandler {
|
||||
struct SetOpts {
|
||||
FILE *helpOut = stdout;
|
||||
bool help = false;
|
||||
bool list = false;
|
||||
bool del = false;
|
||||
char *delAccount{};
|
||||
bool create = false;
|
||||
char *createAccount{};
|
||||
bool value = false;
|
||||
char *valueAccount{};
|
||||
};
|
||||
|
||||
public:
|
||||
explicit AccountOptHandler(const std::vector<char *> &argv);
|
||||
|
||||
void parse() override;
|
||||
|
||||
void help() override;
|
||||
|
||||
[[nodiscard]] const SetOpts *getSetOpts() const;
|
||||
|
||||
private:
|
||||
SetOpts setOpts;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //BUDGET_ACCOUNTOPTHANDLER_H
|
22
src/optHandlers/createOperation.cpp
Normal file
22
src/optHandlers/createOperation.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
//
|
||||
// Created by quentin on 1/17/23.
|
||||
//
|
||||
|
||||
#include "createOperation.h"
|
||||
#include "../database.h"
|
||||
#include "../exceptions/badValue.h"
|
||||
|
||||
using namespace Budget::OptHandlers;
|
||||
|
||||
void CreateOperation::commit() {
|
||||
if (Database::doesAccountExist(account, db)) {
|
||||
throw Budget::Exceptions::BadValue("Account already exists, cant create " + account);
|
||||
}
|
||||
if (flags.description.empty()) {
|
||||
Database::createAccount(account, db);
|
||||
return;
|
||||
}
|
||||
Database::createAccount(account, flags.description, db);
|
||||
}
|
||||
|
||||
CreateOperation::CreateOperation(sqlite3 *db, std::string account) : Operation(db), account(std::move(account)) {}
|
29
src/optHandlers/createOperation.h
Normal file
29
src/optHandlers/createOperation.h
Normal file
@ -0,0 +1,29 @@
|
||||
//
|
||||
// Created by quentin on 1/17/23.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_CREATEOPERATION_H
|
||||
#define BUDGET_CREATEOPERATION_H
|
||||
|
||||
#include <string>
|
||||
#include "operation.h"
|
||||
|
||||
namespace Budget::OptHandlers {
|
||||
class CreateOperation : public Operation {
|
||||
public:
|
||||
void commit() override;
|
||||
|
||||
explicit CreateOperation(sqlite3 *db, std::string account);
|
||||
|
||||
struct Flags : public Operation::Flags {
|
||||
std::string description;
|
||||
};
|
||||
|
||||
Flags flags;
|
||||
private:
|
||||
std::string account;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif //BUDGET_CREATEOPERATION_H
|
34
src/optHandlers/earnOperation.cpp
Normal file
34
src/optHandlers/earnOperation.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
//
|
||||
// Created by quentin on 1/17/23.
|
||||
//
|
||||
|
||||
#include <fstream>
|
||||
#include "earnOperation.h"
|
||||
#include "../database.h"
|
||||
#include "../exceptions/badValue.h"
|
||||
#include "../main.h"
|
||||
|
||||
using namespace Budget::OptHandlers;
|
||||
|
||||
void EarnOperation::commit() {
|
||||
if (!Database::doesAccountExist(account, db))
|
||||
throw Budget::Exceptions::BadValue("Account " + account + " doesn't exist");
|
||||
|
||||
long long id = Database::pay(account, flags.value, flags.description, flags.receipt, flags.date, db);
|
||||
|
||||
if (!flags.receipt.empty()) {
|
||||
std::string extension;
|
||||
size_t pos = flags.receipt.find_last_of('.');
|
||||
if (pos != std::string::npos) {
|
||||
extension = flags.receipt.substr(pos);
|
||||
}
|
||||
|
||||
std::ifstream source(flags.receipt, std::ios::binary);
|
||||
std::ofstream dest(storageD + "receipts/receipt/" + std::to_string(id) + extension, std::ios::binary);
|
||||
dest << source.rdbuf();
|
||||
source.close();
|
||||
dest.close();
|
||||
}
|
||||
}
|
||||
|
||||
EarnOperation::EarnOperation(sqlite3 *db, std::string account) : Operation(db), account(std::move(account)) {}
|
34
src/optHandlers/earnOperation.h
Normal file
34
src/optHandlers/earnOperation.h
Normal file
@ -0,0 +1,34 @@
|
||||
//
|
||||
// Created by quentin on 1/17/23.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_EARNOPERATION_H
|
||||
#define BUDGET_EARNOPERATION_H
|
||||
|
||||
#include "operation.h"
|
||||
#include <string>
|
||||
#include <ctime>
|
||||
|
||||
namespace Budget::OptHandlers {
|
||||
class EarnOperation : public Operation {
|
||||
public:
|
||||
void commit() override;
|
||||
|
||||
explicit EarnOperation(sqlite3 *db, std::string account);
|
||||
|
||||
struct Flags : public Operation::Flags {
|
||||
long double value;
|
||||
std::string description;
|
||||
std::string receipt;
|
||||
long long date = std::time(nullptr);
|
||||
};
|
||||
|
||||
|
||||
Flags flags;
|
||||
private:
|
||||
std::string account;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //BUDGET_EARNOPERATION_H
|
@ -1,51 +1,331 @@
|
||||
//
|
||||
// Created by quentin on 8/13/22.
|
||||
// Created by quentin on 1/8/23.
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include "mainOptHandler.h"
|
||||
#include "accountOperation.h"
|
||||
#include "createOperation.h"
|
||||
#include "earnOperation.h"
|
||||
#include "PaymentOperation.h"
|
||||
#include "../exceptions/helpRequested.h"
|
||||
#include "../exceptions/badValue.h"
|
||||
#include <iostream>
|
||||
#include <getopt.h>
|
||||
#include <cstring>
|
||||
|
||||
using namespace Budget::OptHandlers;
|
||||
|
||||
|
||||
MainOptHandler::MainOptHandler(const std::vector<char *> &argv) : OptHandler(argv) {}
|
||||
|
||||
void MainOptHandler::parse() {
|
||||
struct option longOpts[] = {
|
||||
{"help", no_argument, nullptr, 'h'},
|
||||
{"account", no_argument, nullptr, 'a'},
|
||||
MainOptHandler::MainOptHandler(const std::vector<char *> &_argv, sqlite3 *db) : argv(_argv), db(db) {
|
||||
struct option actionLongOpts[] = {
|
||||
{"help", no_argument, nullptr, 'h'},
|
||||
{"account", required_argument, nullptr, 'a'},
|
||||
{"create", required_argument, nullptr, 'c'},
|
||||
{"earn", required_argument, nullptr, 'e'},
|
||||
{"payment", required_argument, nullptr, 'p'}
|
||||
};
|
||||
|
||||
while (true) {
|
||||
int opt = getopt_long(getArgc(), getArgv(), "ha", longOpts, nullptr);
|
||||
if (opt == -1) {
|
||||
int opt = getopt_long(argv.size(), argv.data(), "ha:c:e:p:", actionLongOpts, nullptr);
|
||||
if (opt == -1)
|
||||
break;
|
||||
}
|
||||
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
setOpts.help = true;
|
||||
break;
|
||||
help();
|
||||
throw Budget::Exceptions::HelpRequested("Help requested at main");
|
||||
case 'a':
|
||||
setOpts.account = true;
|
||||
setOpts.accountArgStart = optind - 1;
|
||||
return;
|
||||
case '?':
|
||||
setOpts.help = true;
|
||||
setOpts.helpOut = stderr;
|
||||
accountOptHandler(optarg);
|
||||
break;
|
||||
case 'c':
|
||||
createOptHandler(optarg);
|
||||
break;
|
||||
case 'e':
|
||||
earnOptHandler(optarg);
|
||||
break;
|
||||
case 'p':
|
||||
paymentOptHandler(optarg);
|
||||
break;
|
||||
case '?':
|
||||
help();
|
||||
throw Budget::Exceptions::HelpRequested("Help requested at main, unknown argument.");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainOptHandler::help() {
|
||||
fprintf(setOpts.helpOut, "Help budget {-ha}\n");
|
||||
fprintf(setOpts.helpOut, " -h --help Output this message.\n");
|
||||
fprintf(setOpts.helpOut, " -a --account Do budget -a -h for more info.\n");
|
||||
void MainOptHandler::accountOptHandler(std::string account) {
|
||||
struct option accountLongOpts[] = {
|
||||
{"help", no_argument, nullptr, 'h'},
|
||||
{"account", required_argument, nullptr, 'a'},
|
||||
{"create", required_argument, nullptr, 'c'},
|
||||
{"earn", required_argument, nullptr, 'e'},
|
||||
{"payment", required_argument, nullptr, 'p'},
|
||||
|
||||
{"delete", no_argument, nullptr, 'd'},
|
||||
{"force-delete", no_argument, nullptr, 'F'},
|
||||
{"value", no_argument, nullptr, 'v'},
|
||||
{"description", required_argument, nullptr, 'D'},
|
||||
};
|
||||
|
||||
auto acctOperation = std::make_unique<AccountOperation>(db, account);
|
||||
|
||||
while (true) {
|
||||
int opt = getopt_long(argv.size(), argv.data(), "ha:c:e:p:dFvD:", accountLongOpts, nullptr);
|
||||
if (opt == -1)
|
||||
break;
|
||||
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
help();
|
||||
throw Budget::Exceptions::HelpRequested("Help requested at account");
|
||||
case 'a':
|
||||
case 'c':
|
||||
case 'e':
|
||||
case 'p':
|
||||
optind--;
|
||||
operations.push(std::move(acctOperation));
|
||||
return;
|
||||
case 'd':
|
||||
acctOperation->flags.del = true;
|
||||
break;
|
||||
case 'F':
|
||||
acctOperation->flags.del = true;
|
||||
acctOperation->flags.forceDel = true;
|
||||
break;
|
||||
case 'v':
|
||||
acctOperation->flags.value = true;
|
||||
break;
|
||||
case 'D':
|
||||
acctOperation->flags.description = optarg;
|
||||
break;
|
||||
case '?':
|
||||
help();
|
||||
throw Budget::Exceptions::HelpRequested("Help requested at account, unknown argument.");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
operations.push(std::move(acctOperation));
|
||||
}
|
||||
|
||||
const MainOptHandler::SetOpts *MainOptHandler::getSetOpts() const {
|
||||
return &setOpts;
|
||||
void MainOptHandler::createOptHandler(std::string account) {
|
||||
struct option createLongOpts[] = {
|
||||
{"help", no_argument, nullptr, 'h'},
|
||||
{"account", required_argument, nullptr, 'a'},
|
||||
{"create", required_argument, nullptr, 'c'},
|
||||
{"earn", required_argument, nullptr, 'e'},
|
||||
{"payment", required_argument, nullptr, 'p'},
|
||||
|
||||
{"description", required_argument, nullptr, 'd'},
|
||||
};
|
||||
|
||||
auto createOperation = std::make_unique<CreateOperation>(db, account);
|
||||
|
||||
while (true) {
|
||||
int opt = getopt_long(argv.size(), argv.data(), "ha:c:e:p:d:", createLongOpts, nullptr);
|
||||
if (opt == -1)
|
||||
break;
|
||||
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
help();
|
||||
throw Budget::Exceptions::HelpRequested("Help requested at create");
|
||||
case 'a':
|
||||
case 'c':
|
||||
case 'e':
|
||||
case 'p':
|
||||
optind--;
|
||||
operations.push(std::move(createOperation));
|
||||
return;
|
||||
case 'd':
|
||||
createOperation->flags.description = optarg;
|
||||
break;
|
||||
case '?':
|
||||
help();
|
||||
throw Budget::Exceptions::HelpRequested("Help requested at create, unknown argument.");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
operations.push(std::move(createOperation));
|
||||
}
|
||||
|
||||
void MainOptHandler::earnOptHandler(std::string account) {
|
||||
struct option earnLongOpts[] = {
|
||||
{"help", no_argument, nullptr, 'h'},
|
||||
{"account", required_argument, nullptr, 'a'},
|
||||
{"create", required_argument, nullptr, 'c'},
|
||||
{"earn", required_argument, nullptr, 'e'},
|
||||
{"payment", required_argument, nullptr, 'p'},
|
||||
|
||||
{"value", required_argument, nullptr, 'v'},
|
||||
{"description", required_argument, nullptr, 'd'},
|
||||
{"receipt", required_argument, nullptr, 'r'},
|
||||
{"date", required_argument, nullptr, 'D'},
|
||||
};
|
||||
|
||||
auto earnOperation = std::make_unique<EarnOperation>(db, account);
|
||||
|
||||
while (true) {
|
||||
int opt = getopt_long(argv.size(), argv.data(), "ha:c:e:p:v:d:r:D:", earnLongOpts, nullptr);
|
||||
if (opt == -1)
|
||||
break;
|
||||
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
help();
|
||||
throw Budget::Exceptions::HelpRequested("Help requested at earn");
|
||||
case 'a':
|
||||
case 'c':
|
||||
case 'e':
|
||||
case 'p':
|
||||
optind--;
|
||||
operations.push(std::move(earnOperation));
|
||||
return;
|
||||
case 'v': {
|
||||
try {
|
||||
earnOperation->flags.value = std::stod(optarg);
|
||||
} catch (std::exception const &e) {
|
||||
help();
|
||||
std::cout << "Bad value value" << std::endl;
|
||||
throw Budget::Exceptions::BadValue("Bad value, cannot parse to decimal.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'd':
|
||||
earnOperation->flags.description = optarg;
|
||||
break;
|
||||
case 'r':
|
||||
earnOperation->flags.receipt = optarg;
|
||||
break;
|
||||
case 'D': {
|
||||
struct tm t{};
|
||||
time_t ts;
|
||||
memset(&t, 0, sizeof(struct tm));
|
||||
if (strptime(optarg, "%m/%d/%YT%H:%M:%S", &t) == nullptr) {
|
||||
help();
|
||||
std::cout << "Bad time value" << std::endl;
|
||||
throw Budget::Exceptions::BadValue("Bad Value, cannot parse to int.");
|
||||
}
|
||||
ts = mktime(&t);
|
||||
earnOperation->flags.date = (long long) ts;
|
||||
break;
|
||||
}
|
||||
case '?':
|
||||
help();
|
||||
throw Budget::Exceptions::HelpRequested("Help requested at earn, unknown argument.");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
operations.push(std::move(earnOperation));
|
||||
}
|
||||
|
||||
void MainOptHandler::paymentOptHandler(std::string account) {
|
||||
struct option paymentLongOpts[] = {
|
||||
{"help", no_argument, nullptr, 'h'},
|
||||
{"account", required_argument, nullptr, 'a'},
|
||||
{"create", required_argument, nullptr, 'c'},
|
||||
{"earn", required_argument, nullptr, 'e'},
|
||||
{"payment", required_argument, nullptr, 'p'},
|
||||
|
||||
{"value", required_argument, nullptr, 'v'},
|
||||
{"description", required_argument, nullptr, 'd'},
|
||||
{"receipt", required_argument, nullptr, 'r'},
|
||||
{"date", required_argument, nullptr, 'D'},
|
||||
};
|
||||
|
||||
auto payOperation = std::make_unique<PaymentOperation>(db, account);
|
||||
|
||||
while (true) {
|
||||
int opt = getopt_long(argv.size(), argv.data(), "ha:c:e:p:v:d:r:D:", paymentLongOpts, nullptr);
|
||||
if (opt == -1)
|
||||
break;
|
||||
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
help();
|
||||
throw Budget::Exceptions::HelpRequested("Help requested at payment");
|
||||
case 'a':
|
||||
case 'c':
|
||||
case 'e':
|
||||
case 'p':
|
||||
optind--;
|
||||
operations.push(std::move(payOperation));
|
||||
return;
|
||||
case 'v': {
|
||||
try {
|
||||
payOperation->flags.value = std::stod(optarg);
|
||||
} catch (std::exception const &e) {
|
||||
help();
|
||||
std::cout << "Bad value value" << std::endl;
|
||||
throw Budget::Exceptions::BadValue("Bad value, cannot parse to decimal.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'd':
|
||||
payOperation->flags.description = optarg;
|
||||
break;
|
||||
case 'r':
|
||||
payOperation->flags.receipt = optarg;
|
||||
break;
|
||||
case 'D': {
|
||||
struct tm t{};
|
||||
time_t ts;
|
||||
memset(&t, 0, sizeof(struct tm));
|
||||
if (strptime(optarg, "%m/%d/%YT%H:%M:%S", &t) == nullptr) {
|
||||
help();
|
||||
std::cout << "Bad time value" << std::endl;
|
||||
throw Budget::Exceptions::BadValue("Bad value, time invalid.");
|
||||
}
|
||||
ts = mktime(&t);
|
||||
payOperation->flags.date = (long long) ts;
|
||||
break;
|
||||
}
|
||||
case '?':
|
||||
help();
|
||||
throw Budget::Exceptions::HelpRequested("Help requested at payment");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
operations.push(std::move(payOperation));
|
||||
}
|
||||
|
||||
void MainOptHandler::help() {
|
||||
std::cout << "Output of budget.\n"
|
||||
"Usage:\n"
|
||||
" budget <action> [options] ...\n"
|
||||
"Actions:\n"
|
||||
" -h --help Prints this.\n"
|
||||
" -a --account<=STRING> Management tools for an account.\n"
|
||||
" -c --create<=STRING> Creates a new account with NAME.\n"
|
||||
" -e --earn<=STRING> Add an earning to an account.\n"
|
||||
" -p --payment<=STRING> Add a payment to an account.\n"
|
||||
"Account Options: [-dvD]\n"
|
||||
" -d --delete Deletes specified account.\n"
|
||||
" --force-delete Deletes the specified account without confirmation.\n"
|
||||
" -v --value Gets the current value of the account.\n"
|
||||
" -D --description<=STRING> Changes the description of the account.\n"
|
||||
"Create Options: [-d]\n"
|
||||
" -d --description<=STRING> Sets a description for an account.\n"
|
||||
"Earn Options: -v [-drD]\n"
|
||||
" -v --value=<FlOAT> Value for earning.\n"
|
||||
" -d --description=<STRING> Description for earning.\n"
|
||||
" -r --receipt=<PATH> Path to file to store in DB as receipt.\n"
|
||||
" -D --date=<mm/dd/yyyyTHH:MM:SS> Date as dd/mm/yyyyTHH:MM:SS. Default will be today.\n"
|
||||
"Payment Options: -v [-drD]\n"
|
||||
" -v --value=<FlOAT> Value for payment.\n"
|
||||
" -d --description=<STRING> Description for payment.\n"
|
||||
" -r --receipt=<PATH> Path to file to store in DB as receipt.\n"
|
||||
" -D --date=<mm/dd/yyyyTHH:MM:SS> Date as dd/mm/yyyyTHH:MM:SS. Default will be today.\n"
|
||||
"\n"
|
||||
"Arguments are processed like blocks with each one terminated by the next Action. For example\n"
|
||||
"\n"
|
||||
"budget -cAcct -eAcct -v10.00 -r\"./receipt.pdf\" -pAcct -v5.50 -r\"./payment.pdf\"\n"
|
||||
"\n"
|
||||
"Does the following in order:\n"
|
||||
"Creates an account named Acct with no description.\n"
|
||||
"Earns 10.00 to it with a receipt.\n"
|
||||
"Pays 5.50 to it with a receipt." << std::endl;
|
||||
}
|
||||
|
@ -1,32 +1,50 @@
|
||||
//
|
||||
// Created by quentin on 8/13/22.
|
||||
// Created by quentin on 1/8/23.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_MAINOPTHANDLER_H
|
||||
#define BUDGET_MAINOPTHANDLER_H
|
||||
|
||||
#include "optHandler.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <queue>
|
||||
#include <memory>
|
||||
#include "operation.h"
|
||||
|
||||
namespace Budget::OptHandlers {
|
||||
class MainOptHandler : public OptHandler {
|
||||
struct SetOpts {
|
||||
FILE *helpOut = stdout;
|
||||
bool help = false;
|
||||
bool account = false;
|
||||
int accountArgStart = -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class MainOptHandler
|
||||
* @brief Handles command line options for the main menu of the budget application
|
||||
*
|
||||
* The MainOptHandler class handles command line options passed to the budget application. It uses the getopt_long function
|
||||
* to parse the options and perform the corresponding actions.
|
||||
*/
|
||||
class MainOptHandler {
|
||||
public:
|
||||
explicit MainOptHandler(const std::vector<char *> &argv);
|
||||
/**
|
||||
* @brief Constructor for MainOptHandler
|
||||
*
|
||||
* @param argv Vector of arguments passed to the application
|
||||
* @param pSqlite3 The database connection to use
|
||||
*/
|
||||
explicit MainOptHandler(const std::vector<char *> &argv, sqlite3 *pSqlite3);
|
||||
|
||||
void parse() override;
|
||||
void help();
|
||||
|
||||
void help() override;
|
||||
|
||||
[[nodiscard]] const SetOpts *getSetOpts() const;
|
||||
std::queue<std::unique_ptr<Operation>> operations;
|
||||
|
||||
private:
|
||||
SetOpts setOpts;
|
||||
void accountOptHandler(std::string account);
|
||||
|
||||
void createOptHandler(std::string account);
|
||||
|
||||
void earnOptHandler(std::string account);
|
||||
|
||||
void paymentOptHandler(std::string account);
|
||||
|
||||
const std::vector<char *> &argv;
|
||||
|
||||
sqlite3 *db;
|
||||
};
|
||||
}
|
||||
|
||||
|
7
src/optHandlers/operation.cpp
Normal file
7
src/optHandlers/operation.cpp
Normal file
@ -0,0 +1,7 @@
|
||||
//
|
||||
// Created by quentin on 1/17/23.
|
||||
//
|
||||
|
||||
#include "operation.h"
|
||||
|
||||
Budget::OptHandlers::Operation::Operation(sqlite3 *db) : db(db) {}
|
26
src/optHandlers/operation.h
Normal file
26
src/optHandlers/operation.h
Normal file
@ -0,0 +1,26 @@
|
||||
//
|
||||
// Created by quentin on 1/17/23.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_OPERATION_H
|
||||
#define BUDGET_OPERATION_H
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
namespace Budget::OptHandlers {
|
||||
class Operation {
|
||||
public:
|
||||
explicit Operation(sqlite3 *db);
|
||||
|
||||
virtual void commit() = 0;
|
||||
|
||||
struct Flags {
|
||||
};
|
||||
|
||||
protected:
|
||||
sqlite3 *db{};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif //BUDGET_OPERATION_H
|
@ -1,20 +0,0 @@
|
||||
//
|
||||
// Created by quentin on 8/13/22.
|
||||
//
|
||||
|
||||
#include "optHandler.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
using namespace Budget::OptHandlers;
|
||||
|
||||
|
||||
OptHandler::OptHandler(std::vector<char *> argv) : argv(std::move(argv)) {}
|
||||
|
||||
int OptHandler::getArgc() {
|
||||
return argv.size();
|
||||
}
|
||||
|
||||
char **OptHandler::getArgv() {
|
||||
return argv.data();
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
//
|
||||
// Created by quentin on 8/13/22.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_OPTHANDLER_H
|
||||
#define BUDGET_OPTHANDLER_H
|
||||
|
||||
#include <getopt.h>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Budget::OptHandlers {
|
||||
class OptHandler {
|
||||
public:
|
||||
explicit OptHandler(std::vector<char *> argv);
|
||||
|
||||
virtual void parse() = 0;
|
||||
|
||||
int getArgc();
|
||||
|
||||
char **getArgv();
|
||||
|
||||
private:
|
||||
|
||||
std::vector<char *> argv;
|
||||
|
||||
virtual void help() = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif //BUDGET_OPTHANDLER_H
|
26
src/sqliteDb.h
Normal file
26
src/sqliteDb.h
Normal file
@ -0,0 +1,26 @@
|
||||
//
|
||||
// Created by quentin on 1/22/23.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_SQLITEDB_H
|
||||
#define BUDGET_SQLITEDB_H
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
class SqliteDb {
|
||||
public:
|
||||
explicit SqliteDb(sqlite3 *db) : m_db(db) {}
|
||||
|
||||
~SqliteDb() {
|
||||
sqlite3_close(m_db);
|
||||
}
|
||||
|
||||
explicit operator sqlite3 *() {
|
||||
return m_db;
|
||||
}
|
||||
|
||||
private:
|
||||
sqlite3 *m_db;
|
||||
};
|
||||
|
||||
#endif //BUDGET_SQLITEDB_H
|
26
src/utilities.cpp
Normal file
26
src/utilities.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
//
|
||||
// Created by quentin on 1/21/23.
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include "utilities.h"
|
||||
|
||||
bool Utilities::confirm(const std::string &question) {
|
||||
std::string input;
|
||||
std::cout << question << std::endl;
|
||||
std::cout << "Please enter 'y' or 'n': ";
|
||||
std::cin >> input;
|
||||
std::transform(input.begin(), input.end(), input.begin(), ::tolower);
|
||||
|
||||
while (input != "y" && input != "n" && input != "yes" && input != "no") {
|
||||
std::cout << "Invalid input. Please enter 'y' or 'n': ";
|
||||
std::cin >> input;
|
||||
std::transform(input.begin(), input.end(), input.begin(), ::tolower);
|
||||
}
|
||||
|
||||
if (input == "y" || input == "yes") {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
24
src/utilities.h
Normal file
24
src/utilities.h
Normal file
@ -0,0 +1,24 @@
|
||||
//
|
||||
// Created by quentin on 1/21/23.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_UTILITIES_H
|
||||
#define BUDGET_UTILITIES_H
|
||||
|
||||
|
||||
#include <string>
|
||||
|
||||
class Utilities {
|
||||
public:
|
||||
/**
|
||||
* @brief Asks the user to confirm an action with a yes or no question
|
||||
*
|
||||
* @param question The question to ask the user
|
||||
*
|
||||
* @return True if the user confirms the action, false otherwise
|
||||
*/
|
||||
static bool confirm(const std::string &question);
|
||||
};
|
||||
|
||||
|
||||
#endif //BUDGET_UTILITIES_H
|
@ -1,97 +0,0 @@
|
||||
//
|
||||
// Created by quentin on 9/13/22.
|
||||
//
|
||||
|
||||
#include "math.h"
|
||||
|
||||
//Polynomial Fit
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
|
||||
using namespace Budget::Utilities;
|
||||
|
||||
PolynomialFunction Math::polynomialFit(int degree, int numberOfPoints, const double x[], const double y[]) {
|
||||
//Array that will store the values of sigma(xi),sigma(xi^2),sigma(xi^3)....sigma(xi^2numberOfPoints)
|
||||
double X[2 * degree + 1];
|
||||
for (int i = 0; i < 2 * degree + 1; i++) {
|
||||
X[i] = 0;
|
||||
for (int j = 0; j < numberOfPoints; j++) {
|
||||
//consecutive positions of the array will store numberOfPoints,sigma(xi),sigma(xi^2),sigma(xi^3)....sigma(xi^2n)
|
||||
X[i] = X[i] + pow(x[j], i);
|
||||
}
|
||||
}
|
||||
//B is the Normal matrix(augmented) that will store the equations, 'a' is for value of the final coefficients
|
||||
double B[degree + 1][degree + 2], a[degree + 1];
|
||||
for (int i = 0; i <= degree; i++) {
|
||||
for (int j = 0; j <= degree; j++) {
|
||||
//Build the Normal matrix by storing the corresponding coefficients at the right positions except the last column of the matrix
|
||||
B[i][j] = X[i + j];
|
||||
}
|
||||
}
|
||||
|
||||
//Array to store the values of sigma(yi),sigma(xi*yi),sigma(xi^2*yi)...sigma(xi^degree*yi)
|
||||
double Y[degree + 1];
|
||||
for (int i = 0; i < degree + 1; i++) {
|
||||
Y[i] = 0;
|
||||
for (int j = 0; j < numberOfPoints; j++) {
|
||||
//consecutive positions will store sigma(yi),sigma(xi*yi),sigma(xi^2*yi)...sigma(xi^degree*yi)
|
||||
Y[i] = Y[i] + pow(x[j], i) * y[j];
|
||||
}
|
||||
}
|
||||
for (int i = 0; i <= degree; i++) {
|
||||
//load the values of Y as the last column of B(Normal Matrix but augmented)
|
||||
B[i][degree + 1] = Y[i];
|
||||
}
|
||||
|
||||
//degree is made degree+1 because the Gaussian Elimination part below was for degree equations, but here degree is the degree of polynomial and for degree degree we get degree+1 equations
|
||||
degree = degree + 1;
|
||||
//From now Gaussian Elimination starts(can be ignored) to solve the set of linear equations (Pivotisation)
|
||||
for (int i = 0; i < degree; i++) {
|
||||
for (int k = i + 1; k < degree; k++) {
|
||||
if (B[i][i] < B[k][i]) {
|
||||
for (int j = 0; j <= degree; j++) {
|
||||
double temp = B[i][j];
|
||||
B[i][j] = B[k][j];
|
||||
B[k][j] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//loop to perform the gauss elimination
|
||||
for (int i = 0; i < degree - 1; i++) {
|
||||
for (int k = i + 1; k < degree; k++) {
|
||||
double t = B[k][i] / B[i][i];
|
||||
for (int j = 0; j <= degree; j++) {
|
||||
//make the elements below the pivot elements equal to zero or elimnate the variables
|
||||
B[k][j] = B[k][j] - t * B[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//back-substitution
|
||||
for (int i = degree - 1; i >= 0; i--) {
|
||||
//x is an array whose values correspond to the values of x,y,z..
|
||||
//make the variable to be calculated equal to the rhs of the last equation
|
||||
a[i] = B[i][degree];
|
||||
for (int j = 0; j < degree; j++) {
|
||||
//then subtract all the lhs values except the coefficient of the variable whose value is being calculated
|
||||
if (j != i) {
|
||||
a[i] = a[i] - B[i][j] * a[j];
|
||||
}
|
||||
}
|
||||
//now finally divide the rhs by the coefficient of the variable to be calculated
|
||||
a[i] = a[i] / B[i][i];
|
||||
}
|
||||
std::cout << "\nThe values of the coefficients are as follows:\n";
|
||||
for (int i = 0; i < degree; i++)
|
||||
std::cout << "x^" << i << "=" << a[i] << std::endl; // Print the values of x^0,x^1,x^2,x^3,....
|
||||
std::cout << "\nHence the fitted Polynomial is given by:\ny=";
|
||||
for (int i = 0; i < degree; i++)
|
||||
std::cout << " + (" << a[i] << ")" << "x^" << i;
|
||||
std::cout << "\n";
|
||||
|
||||
std::vector<double> va(a, a + degree);
|
||||
return PolynomialFunction(va);
|
||||
}
|
||||
|
@ -1,19 +0,0 @@
|
||||
//
|
||||
// Created by quentin on 9/13/22.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_MATH_H
|
||||
#define BUDGET_MATH_H
|
||||
|
||||
|
||||
#include "polynomialFunction.h"
|
||||
|
||||
namespace Budget::Utilities {
|
||||
class Math {
|
||||
public:
|
||||
static PolynomialFunction polynomialFit(int degree, int numberOfPoints, const double x[], const double y[]);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif //BUDGET_MATH_H
|
@ -1,21 +0,0 @@
|
||||
//
|
||||
// Created by quentin on 9/13/22.
|
||||
//
|
||||
|
||||
#include "polynomialFunction.h"
|
||||
|
||||
#include <utility>
|
||||
#include <cmath>
|
||||
|
||||
using namespace Budget::Utilities;
|
||||
|
||||
PolynomialFunction::PolynomialFunction(std::vector<double> a) : a(std::move(a)) {}
|
||||
|
||||
double PolynomialFunction::get(double x) {
|
||||
double ret = 0;
|
||||
for (int i = 0; i < a.size(); ++i) {
|
||||
ret += a[i] * std::pow(x, i);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
//
|
||||
// Created by quentin on 9/13/22.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_POLYNOMIALFUNCTION_H
|
||||
#define BUDGET_POLYNOMIALFUNCTION_H
|
||||
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Budget::Utilities {
|
||||
class PolynomialFunction {
|
||||
private:
|
||||
public:
|
||||
explicit PolynomialFunction(std::vector<double> a);
|
||||
|
||||
double get(double x);
|
||||
|
||||
private:
|
||||
std::vector<double> a;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif //BUDGET_POLYNOMIALFUNCTION_H
|
Loading…
x
Reference in New Issue
Block a user