from pathlib import Path import sys sys.path.append(str(Path(__file__).parent.parent.parent)) from flask import request from flask_restx import Namespace, Resource, fields, reqparse from finance_watcher_dataclasses.import_dataclasses import AccountRow from finance_watcher_dataclasses.enums import AccountTypeEnum as AccountTypeEnum from database.finance_database import FinanceDatabase from helpers.helper_functions import ( json_success_response, json_error_response, get_date_from_string, get_string_from_date ) from datetime import datetime namespace = Namespace( 'account', description='API endpoints related to Account resource' ) user_id_parser = reqparse.RequestParser() user_id_parser.add_argument('user_id', required=True, type=int, help='id of the user') account_payload = namespace.model("Payload", { "name": fields.String(required=True, description='name of the account'), "account_type_id": fields.Integer(required=True, description='id of the account type'), "user_id": fields.Integer(required=True, description='id of the user'), }, required=True, description='account details to insert') totals_payload = namespace.model("Payload", { "user_id": fields.Integer(required=True, description='id of the user'), "account_totals": fields.List(fields.Nested(namespace.model("data", { "account_id":fields.Integer(required=True, description='id of account'), "total":fields.Integer(required=True, description='total amount on the account'), "date":fields.String(required=False, description='date of the transaction') })), required=True, description="list of account totals") }) @namespace.route('') class AccountAPI(Resource): """ API for the Account resource. """ def post(self): """ Inserts Account into the database. """ payload = request.json finance_db = FinanceDatabase() user = finance_db.get_dashboard_user(payload['user_id']) if user is None: return json_error_response( "ERROR: User does not exist." ) account = finance_db.insert_account( name=payload['name'], account_type_id=payload['account_type_id'], dashboard_user_id=payload['user_id'] ) if account is None: return json_error_response( f"ERROR: Failed to insert the account '{payload['name']}'" ) return json_success_response({ 'id': account.id, 'user_id': account.dashboard_user_id, 'name': account.name, 'account_type_id': account.account_type_id, 'created_date': get_string_from_date(account.created_date), 'updated_date': get_string_from_date(account.updated_date) if account.updated_date else 'null' }) @namespace.route('/type/') class AccountTypeAPI(Resource): """ API for Account Type """ def get(self, name: str): """ Gets Account Type resource. :param type: type of an account """ finance_db = FinanceDatabase() account_type_enum = AccountTypeEnum.get_account_type_from_str(name) if account_type_enum is None: return json_error_response( f"ERROR: Account type '{name}' is not valid." ) account_type = finance_db.get_account_type(account_type_enum) if account_type is None: return json_error_response( f"ERROR: Failed to get account type '{name}'." ) return json_success_response({ 'id': account_type.id, 'name': account_type.name, 'is_income': 'True' if account_type.is_income else 'False', 'is_expense': 'True' if account_type.is_expense else 'False' }) @namespace.route('/totals') class AccountTotalsAPI(Resource): """ API to get Account totals. """ @namespace.expect(user_id_parser) def get(self): """ Gets account totals from a User. """ args = user_id_parser.parse_args() user_id = args['user_id'] finance_db = FinanceDatabase() user = finance_db.get_dashboard_user(user_id) if user is None: return json_error_response( "ERROR: User does not exist." ) account_totals = finance_db.get_all_accounts_and_latest_totals(user_id) return json_success_response([ { 'account_id': account_id, 'account_name': account_totals[0], 'total': account_totals[1] } for account_id, account_totals in account_totals.items() ]) @namespace.expect(totals_payload) def post(self): """ Inserts a Transaction into the database. """ payload = request.json finance_db = FinanceDatabase() account_ids = [values['account_id'] for values in payload['account_totals']] connected = finance_db.check_account_connection( payload['user_id'], account_ids = account_ids ) if not connected: return json_error_response( "ERROR: An account is not connected to this user." ) totals_to_insert = [] for values in payload['account_totals']: date = values.get('date') if date is None: date = datetime.now() else: date = get_date_from_string(date) account_row = AccountRow( account_id=values['account_id'], date_to_amount={ date: values['total'], } ) totals_to_insert.append(account_row) if not finance_db.insert_account_totals( totals_to_insert ): return json_error_response( "ERROR: Failed to insert transactions." ) return json_success_response() @namespace.route('/latest_percentages') class AccountPercentagesAPI(Resource): """ API to get the latest Account Percentages. """ @namespace.expect(user_id_parser) def get(self): """ Gets latest account percentages from a User. """ args = user_id_parser.parse_args() user_id = args['user_id'] finance_db = FinanceDatabase() user = finance_db.get_dashboard_user(user_id) if user is None: return json_error_response( "ERROR: User does not exist." ) account_percentages = finance_db.get_latest_account_percentages(user_id) return json_success_response([ { 'account_name': account, 'percentage': percentage } for account, percentage in account_percentages.items() ])