budget/pfbudget/database.py
2021-05-27 22:37:50 +01:00

182 lines
4.8 KiB
Python

import sqlite3
import logging
import logging.config
import pathlib
if not pathlib.Path("logs").is_dir():
pathlib.Path("logs").mkdir()
logging.config.fileConfig("logging.conf")
logger = logging.getLogger("pfbudget.transactions")
__DB_NAME = "data.db"
CREATE_TRANSACTIONS_TABLE = """
CREATE TABLE IF NOT EXISTS transactions (
date TEXT NOT NULL,
description TEXT,
bank TEXT,
value REAL NOT NULL,
category TEXT
);
"""
CREATE_VACATIONS_TABLE = """
CREATE TABLE IF NOT EXISTS vacations (
start TEXT NOT NULL,
end TEXT NOT NULL
)
"""
CREATE_BACKUPS_TABLE = """
CREATE TABLE IF NOT EXISTS backups (
datetime TEXT NOT NULL,
file TEXT NOT NULL
)
"""
CREATE_BANKS_TABLE = """
CREATE TABLE banks (
name TEXT NOT NULL PRIMARY KEY,
url TEXT
)
"""
ADD_TRANSACTION = """
INSERT INTO transactions (date, description, bank, value, category) values (?,?,?,?,?)
"""
UPDATE_CATEGORY = """
UPDATE transactions
SET category = (?)
WHERE date = (?) AND description = (?) AND bank = (?) AND value = (?)
"""
DUPLICATED_TRANSACTIONS = """
SELECT COUNT(*), date, description, bank, value
FROM transactions
GROUP BY date, description, bank, value
HAVING COUNT(*) > 1
"""
SORTED_TRANSACTIONS = """
SELECT *
FROM transactions
ORDER BY (?)
"""
SELECT_TRANSACTIONS_BETWEEN_DATES = """
SELECT *
FROM transactions
WHERE date BETWEEN (?) AND (?)
"""
SELECT_TRANSACTIONS_BY_CATEGORY = """
SELECT *
FROM transactions
WHERE category = (?)
"""
SELECT_TRANSACTION_BY_PERIOD = """
SELECT EXTRACT((?) FROM date) AS (?), date, description, bank, value
FROM transactions
"""
class DBManager:
"""SQLite DB connection manager"""
def __init__(self, db):
self.db = db
self.__create_tables(
(
("transactions", CREATE_TRANSACTIONS_TABLE),
("vacations", CREATE_VACATIONS_TABLE),
("backups", CREATE_BACKUPS_TABLE),
)
)
def __execute(self, query, params=None):
ret = None
try:
con = sqlite3.connect(self.db)
with con:
if params:
ret = con.execute(query, params).fetchall()
logger.debug(f"[{self.db}] < {query}{params}")
else:
ret = con.execute(query).fetchall()
logger.debug(f"[{self.db}] < {query}")
if ret:
logger.debug(f"[{self.db}] > {ret}")
except sqlite3.Error:
logger.exception(f"Error while executing [{self.db}] < {query}")
finally:
con.close()
return ret
def __executemany(self, query, list_of_params):
ret = None
try:
con = sqlite3.connect(self.db)
with con:
ret = con.executemany(query, list_of_params).fetchall()
logger.debug(f"[{self.db}] < {query}{list_of_params}")
except sqlite3.Error:
logger.exception(
f"Error while executing [{self.db}] < {query} {list_of_params}"
)
finally:
con.close()
return ret
def __create_tables(self, tables):
for table_name, query in tables:
logger.info(f"Creating table if it doesn't exist {table_name}")
self.__execute(query)
def select_all(self):
logger.info(f"Reading all transactions from {self.db}")
return self.__execute("SELECT * FROM transactions")
def add_transaction(self, transaction):
logger.info(f"Adding {transaction} into {self.db}")
self.__execute(ADD_TRANSACTION, transaction)
def add_transactions(self, transactions):
logger.info(f"Adding {len(transactions)} into {self.db}")
self.__executemany(ADD_TRANSACTION, transactions)
def update_category(self, transaction):
logger.info(f"Update {transaction} category")
self.__execute(UPDATE_CATEGORY, (transaction[4], *transaction[:4]))
def get_duplicated_transactions(self):
logger.info("Get duplicated transactions")
return self.__execute(DUPLICATED_TRANSACTIONS)
def get_sorted_transactions(self, key):
logger.info(f"Get transactions sorted by {key}")
return self.__execute(SORTED_TRANSACTIONS, key)
def get_daterange(self, start, end):
logger.info(f"Get transactions from {start} to {end}")
return self.__execute(SELECT_TRANSACTIONS_BETWEEN_DATES, (start, end))
def get_category(self, value):
logger.info(f"Get transaction where category = {value}")
return self.__execute(SELECT_TRANSACTIONS_BY_CATEGORY, (value,))
def get_by_period(self, period):
logger.info(f"Get transactions by {period}")
return self.__execute(SELECT_TRANSACTION_BY_PERIOD, period)
def query(self, query, params=None):
logger.info(f"Executing {query} with params={params}")
return self.__execute(query, params)