From 745ed14c56a136e70a8e46703bab9283f2c9aa95 Mon Sep 17 00:00:00 2001 From: Lucas Mathews Date: Fri, 17 May 2024 10:48:19 +0200 Subject: [PATCH] First Code Commit --- Dockerfile | 16 + Flask App/ARG/agent.py | 8 + Flask App/app.py | 19 + Flask App/main.py | 0 Flask App/templates/login.html | 17 + Flask App/templates/succes.html | 11 + Old/api.yml | 686 +++++++++++++++++ Old/class.py | 59 ++ Old/manager_account.py | 4 + Old/manager_transaction.py | 24 + Old/server.py | 196 +++++ __pycache__/class_account.cpython-312.pyc | Bin 0 -> 2136 bytes __pycache__/class_base.cpython-312.pyc | Bin 0 -> 333 bytes __pycache__/class_client.cpython-312.pyc | Bin 0 -> 2331 bytes __pycache__/class_transaction.cpython-312.pyc | Bin 0 -> 1982 bytes __pycache__/config.cpython-312.pyc | Bin 0 -> 404 bytes __pycache__/database.cpython-312.pyc | Bin 0 -> 4318 bytes __pycache__/manager.cpython-312.pyc | Bin 0 -> 16243 bytes __pycache__/manager_account.cpython-312.pyc | Bin 0 -> 1782 bytes __pycache__/manager_client.cpython-312.pyc | Bin 0 -> 4195 bytes .../manager_transaction.cpython-312.pyc | Bin 0 -> 1687 bytes __pycache__/operator_account.cpython-312.pyc | Bin 0 -> 1783 bytes __pycache__/operator_client.cpython-312.pyc | Bin 0 -> 821 bytes .../operator_transaction.cpython-312.pyc | Bin 0 -> 1688 bytes __pycache__/password.cpython-312.pyc | Bin 0 -> 755 bytes __pycache__/server.cpython-312.pyc | Bin 0 -> 1121 bytes api.py | 26 + api.yml | 701 ++++++++++++++++++ bank.db | Bin 0 -> 36864 bytes bank.ini | 20 + class_account.py | 31 + class_base.py | 6 + class_client.py | 38 + class_transaction.py | 28 + cli.py | 19 + config.py | 7 + database.py | 58 ++ manager.py | 249 +++++++ requirements.txt | 4 + 39 files changed, 2227 insertions(+) create mode 100644 Dockerfile create mode 100644 Flask App/ARG/agent.py create mode 100644 Flask App/app.py create mode 100644 Flask App/main.py create mode 100644 Flask App/templates/login.html create mode 100644 Flask App/templates/succes.html create mode 100644 Old/api.yml create mode 100644 Old/class.py create mode 100644 Old/manager_account.py create mode 100644 Old/manager_transaction.py create mode 100644 Old/server.py create mode 100644 __pycache__/class_account.cpython-312.pyc create mode 100644 __pycache__/class_base.cpython-312.pyc create mode 100644 __pycache__/class_client.cpython-312.pyc create mode 100644 __pycache__/class_transaction.cpython-312.pyc create mode 100644 __pycache__/config.cpython-312.pyc create mode 100644 __pycache__/database.cpython-312.pyc create mode 100644 __pycache__/manager.cpython-312.pyc create mode 100644 __pycache__/manager_account.cpython-312.pyc create mode 100644 __pycache__/manager_client.cpython-312.pyc create mode 100644 __pycache__/manager_transaction.cpython-312.pyc create mode 100644 __pycache__/operator_account.cpython-312.pyc create mode 100644 __pycache__/operator_client.cpython-312.pyc create mode 100644 __pycache__/operator_transaction.cpython-312.pyc create mode 100644 __pycache__/password.cpython-312.pyc create mode 100644 __pycache__/server.cpython-312.pyc create mode 100644 api.py create mode 100644 api.yml create mode 100644 bank.db create mode 100644 bank.ini create mode 100644 class_account.py create mode 100644 class_base.py create mode 100644 class_client.py create mode 100644 class_transaction.py create mode 100644 cli.py create mode 100644 config.py create mode 100644 database.py create mode 100644 manager.py create mode 100644 requirements.txt diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..db4f3a8 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +# Lucas Mathews - Fontys Student ID: 5023572 +# Banking System DockerFile + +FROM python:3.12.3 + +LABEL maintainer="522499@student.fontys.nl" + +WORKDIR /bank + +COPY / /bank/ + +EXPOSE 81 + +RUN pip install --no-cache-dir --upgrade -r /bank/requirements.txt + +ENTRYPOINT [ "python", "./api.py", "--host", "0.0.0.0", "--port", "81"] diff --git a/Flask App/ARG/agent.py b/Flask App/ARG/agent.py new file mode 100644 index 0000000..022c58e --- /dev/null +++ b/Flask App/ARG/agent.py @@ -0,0 +1,8 @@ +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument('--x', type=int, required=True) +parser.add_argument('--y', type=int, required=True) +args = parser.parse_args() +product = args.x * args.y +print(f"{args.x} * {args.y} = {product}") \ No newline at end of file diff --git a/Flask App/app.py b/Flask App/app.py new file mode 100644 index 0000000..2a0670b --- /dev/null +++ b/Flask App/app.py @@ -0,0 +1,19 @@ +from flask import Flask, render_template, request +import requests + +SERVER_URL = "http://127.0.0.1:8000" + +app = Flask(__name__) + +@app.route("/", methods=["GET"]) +def loginGET(): + return render_template("login.html") + +@app.route("/", methods=["POST"]) +def loginPOST(): + username = request.form["username"] + password = request.form["password"] + response = requests.get( f"{SERVER_URL}/login?username={username}&password={password}") + return render_template("succes.html", data = response.content) + +app.run() \ No newline at end of file diff --git a/Flask App/main.py b/Flask App/main.py new file mode 100644 index 0000000..e69de29 diff --git a/Flask App/templates/login.html b/Flask App/templates/login.html new file mode 100644 index 0000000..1617531 --- /dev/null +++ b/Flask App/templates/login.html @@ -0,0 +1,17 @@ + + + + + + Login + + +
+ + + + + +
+ + \ No newline at end of file diff --git a/Flask App/templates/succes.html b/Flask App/templates/succes.html new file mode 100644 index 0000000..f286872 --- /dev/null +++ b/Flask App/templates/succes.html @@ -0,0 +1,11 @@ + + + + + + Document + + +

{{data}}

+ + \ No newline at end of file diff --git a/Old/api.yml b/Old/api.yml new file mode 100644 index 0000000..5939103 --- /dev/null +++ b/Old/api.yml @@ -0,0 +1,686 @@ +openapi: 3.0.3 +info: + title: Banking System - OpenAPI 3.0 + description: |- + This is the banking system API for the programming project. + contact: + email: 522499@student.fontys.nl + version: 1.0.11 +servers: + - url: / +tags: + - name: accounts + description: Everything about your Accounts + - name: user + description: Operations about user +paths: + /Accounts: + put: + tags: + - accounts + summary: Update an existing account + description: Update an existing account by Id + operationId: updateAccount + requestBody: + description: Update an existing bank account + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + application/xml: + schema: + $ref: '#/components/schemas/Account' + text/html: + schema: + $ref: '#/components/schemas/Account' + required: true + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + application/xml: + schema: + $ref: '#/components/schemas/Account' + '400': + description: Invalid ID supplied + '404': + description: Pet not found + '422': + description: Validation exception + security: + - petstore_auth: + - write:pets + - read:pets + post: + tags: + - accounts + summary: Add a new pet to the store + description: Add a new pet to the store + operationId: addPet + requestBody: + description: Create a new pet in the store + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + application/xml: + schema: + $ref: '#/components/schemas/Account' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/Account' + required: true + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + application/xml: + schema: + $ref: '#/components/schemas/Account' + '400': + description: Invalid input + '422': + description: Validation exception + security: + - petstore_auth: + - write:pets + - read:pets + /pet/findByStatus: + get: + tags: + - accounts + summary: Finds Pets by status + description: Multiple status values can be provided with comma separated strings + operationId: findPetsByStatus + parameters: + - name: status + in: query + description: Status values that need to be considered for filter + required: false + explode: true + schema: + type: string + default: available + enum: + - available + - pending + - sold + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Account' + application/xml: + schema: + type: array + items: + $ref: '#/components/schemas/Account' + '400': + description: Invalid status value + security: + - petstore_auth: + - write:pets + - read:pets + /pet/findByTags: + get: + tags: + - accounts + summary: Finds Pets by tags + description: Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. + operationId: findPetsByTags + parameters: + - name: tags + in: query + description: Tags to filter by + required: false + explode: true + schema: + type: array + items: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Account' + application/xml: + schema: + type: array + items: + $ref: '#/components/schemas/Account' + '400': + description: Invalid tag value + security: + - petstore_auth: + - write:pets + - read:pets + /pet/{petId}: + get: + tags: + - accounts + summary: Find pet by ID + description: Returns a single pet + operationId: getPetById + parameters: + - name: petId + in: path + description: ID of pet to return + required: true + schema: + type: integer + format: int64 + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + application/xml: + schema: + $ref: '#/components/schemas/Account' + '400': + description: Invalid ID supplied + '404': + description: Pet not found + security: + - api_key: [] + - petstore_auth: + - write:pets + - read:pets + post: + tags: + - accounts + summary: Updates a pet in the store with form data + description: '' + operationId: updatePetWithForm + parameters: + - name: petId + in: path + description: ID of pet that needs to be updated + required: true + schema: + type: integer + format: int64 + - name: name + in: query + description: Name of pet that needs to be updated + schema: + type: string + - name: status + in: query + description: Status of pet that needs to be updated + schema: + type: string + responses: + '400': + description: Invalid input + security: + - petstore_auth: + - write:pets + - read:pets + delete: + tags: + - accounts + summary: Deletes a pet + description: delete a pet + operationId: deletePet + parameters: + - name: api_key + in: header + description: '' + required: false + schema: + type: string + - name: petId + in: path + description: Pet id to delete + required: true + schema: + type: integer + format: int64 + responses: + '400': + description: Invalid pet value + security: + - petstore_auth: + - write:pets + - read:pets + /pet/{petId}/uploadImage: + post: + tags: + - accounts + summary: uploads an image + description: '' + operationId: uploadFile + parameters: + - name: petId + in: path + description: ID of pet to update + required: true + schema: + type: integer + format: int64 + - name: additionalMetadata + in: query + description: Additional Metadata + required: false + schema: + type: string + requestBody: + content: + application/octet-stream: + schema: + type: string + format: binary + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + security: + - petstore_auth: + - write:pets + - read:pets + /user: + post: + tags: + - user + summary: Create user + description: This can only be done by the logged in user. + operationId: createUser + requestBody: + description: Created user object + content: + application/json: + schema: + $ref: '#/components/schemas/User' + application/xml: + schema: + $ref: '#/components/schemas/User' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/User' + responses: + default: + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/User' + application/xml: + schema: + $ref: '#/components/schemas/User' + /user/createWithList: + post: + tags: + - user + summary: Creates list of users with given input array + description: Creates list of users with given input array + operationId: createUsersWithListInput + requestBody: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/User' + application/xml: + schema: + $ref: '#/components/schemas/User' + default: + description: successful operation + /user/login: + get: + tags: + - user + summary: Logs user into the system + description: '' + operationId: loginUser + parameters: + - name: username + in: query + description: The user name for login + required: false + schema: + type: string + - name: password + in: query + description: The password for login in clear text + required: false + schema: + type: string + responses: + '200': + description: successful operation + headers: + X-Rate-Limit: + description: calls per hour allowed by the user + schema: + type: integer + format: int32 + X-Expires-After: + description: date in UTC when token expires + schema: + type: string + format: date-time + content: + application/xml: + schema: + type: string + application/json: + schema: + type: string + '400': + description: Invalid username/password supplied + /user/logout: + get: + tags: + - user + summary: Logs out current logged in user session + description: '' + operationId: logoutUser + parameters: [] + responses: + default: + description: successful operation + /user/{username}: + get: + tags: + - user + summary: Get user by user name + description: '' + operationId: getUserByName + parameters: + - name: username + in: path + description: 'The name that needs to be fetched. Use user1 for testing. ' + required: true + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/User' + application/xml: + schema: + $ref: '#/components/schemas/User' + '400': + description: Invalid username supplied + '404': + description: User not found + put: + tags: + - user + summary: Update user + description: This can only be done by the logged in user. + operationId: updateUser + parameters: + - name: username + in: path + description: name that need to be deleted + required: true + schema: + type: string + requestBody: + description: Update an existent user in the store + content: + application/json: + schema: + $ref: '#/components/schemas/User' + application/xml: + schema: + $ref: '#/components/schemas/User' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/User' + responses: + default: + description: successful operation + delete: + tags: + - user + summary: Delete user + description: This can only be done by the logged in user. + operationId: deleteUser + parameters: + - name: username + in: path + description: The name that needs to be deleted + required: true + schema: + type: string + responses: + '400': + description: Invalid username supplied + '404': + description: User not found +components: + schemas: + Order: + type: object + properties: + id: + type: integer + format: int64 + example: 10 + petId: + type: integer + format: int64 + example: 198772 + quantity: + type: integer + format: int32 + example: 7 + shipDate: + type: string + format: date-time + status: + type: string + description: Order Status + example: approved + enum: + - placed + - approved + - delivered + complete: + type: boolean + xml: + name: order + Customer: + type: object + properties: + id: + type: integer + format: int64 + example: 100000 + username: + type: string + example: fehguy + address: + type: array + xml: + name: addresses + wrapped: true + items: + $ref: '#/components/schemas/Address' + xml: + name: customer + Address: + type: object + properties: + street: + type: string + example: 437 Lytton + city: + type: string + example: Palo Alto + state: + type: string + example: CA + zip: + type: string + example: '94301' + xml: + name: address + Category: + type: object + properties: + id: + type: integer + format: int64 + example: 1 + name: + type: string + example: Dogs + xml: + name: category + User: + type: object + properties: + id: + type: integer + format: int64 + example: 10 + username: + type: string + example: theUser + firstName: + type: string + example: John + lastName: + type: string + example: James + email: + type: string + example: john@email.com + password: + type: string + example: '12345' + phone: + type: string + example: '12345' + userStatus: + type: integer + description: User Status + format: int32 + example: 1 + xml: + name: user + Tag: + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: tag + Account: + required: + - name + - photoUrls + type: object + properties: + id: + type: integer + format: int64 + example: 10 + name: + type: string + example: doggie + category: + $ref: '#/components/schemas/Category' + photoUrls: + type: array + xml: + wrapped: true + items: + type: string + xml: + name: photoUrl + tags: + type: array + xml: + wrapped: true + items: + $ref: '#/components/schemas/Tag' + status: + type: string + description: pet status in the store + enum: + - available + - pending + - sold + xml: + name: pet + ApiResponse: + type: object + properties: + code: + type: integer + format: int32 + type: + type: string + message: + type: string + xml: + name: '##default' + requestBodies: + Pet: + description: Pet object that needs to be added to the store + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + application/xml: + schema: + $ref: '#/components/schemas/Account' + UserArray: + description: List of user object + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' + securitySchemes: + petstore_auth: + type: oauth2 + flows: + implicit: + authorizationUrl: https://petstore3.swagger.io/oauth/authorize + scopes: + write:pets: modify pets in your account + read:pets: read your pets + api_key: + type: apiKey + name: api_key + in: header \ No newline at end of file diff --git a/Old/class.py b/Old/class.py new file mode 100644 index 0000000..66fd34f --- /dev/null +++ b/Old/class.py @@ -0,0 +1,59 @@ + + + +class Transaction: + def __init__(self, trans_id, from_id, to_id, amount, time, date, description, t_type): + self.trans_id = trans_id + self.from_id = from_id + self.to_id = to_id + self.amount = amount + self.time = time + self.date = date + self.description = description + self.t_type = t_type + + +class Account: + def __init__(self, account_id, name, balance, created_t, created_d, last_modified, closed, closure_t, closure_d, + notes, transactions=None): + if transactions is None: + transactions = [] + self.account_id = account_id + self.name = name + self.balance = balance + self.created_t = created_t + self.created_d = created_d + self.last_modified = last_modified + self.closed = closed + self.closure_t = closure_t + self.closure_d = closure_d + self.notes = notes + self.transactions = transactions + + +class Customer: + def __init__(self, customer_id, f_name, l_name, phone, email, birthday, address, signup_d, signup_t, notes, + accounts=None): + if accounts is None: + accounts = [] + self.customer_id = customer_id + self.f_name = f_name + self.l_name = l_name + self.phone = phone + self.email = email + self.birthday = birthday + self.address = address + self.signup_d = signup_d + self.signup_t = signup_t + self.notes = notes + self.accounts = accounts + +#calculate the balance of an account +def calc_balance(account): + balance = 0 + for transaction in account.transactions: + if transaction.from_id == account.account_id: + balance -= transaction.amount + elif transaction.to_id == account.account_id: + balance += transaction.amount + return balance \ No newline at end of file diff --git a/Old/manager_account.py b/Old/manager_account.py new file mode 100644 index 0000000..3ce6dc4 --- /dev/null +++ b/Old/manager_account.py @@ -0,0 +1,4 @@ +# Lucas Mathews - Fontys Student ID: 5023572 +# Banking System Manager for Account Class - Version 1 + + diff --git a/Old/manager_transaction.py b/Old/manager_transaction.py new file mode 100644 index 0000000..37f5431 --- /dev/null +++ b/Old/manager_transaction.py @@ -0,0 +1,24 @@ +# Lucas Mathews - Fontys Student ID: 5023572 +# Banking System Manager for Transaction Class - Version 1 + + +def add_transaction(transaction_id, transaction_type, amount, timestamp, description, account_number, recipient_account_number): + from api import session, Transaction + new_transaction = Transaction(transaction_id, transaction_type, amount, timestamp, description, account_number, recipient_account_number) + session.add(new_transaction) + session.commit() + return new_transaction + +def delete_transaction(transaction_id:int): + DELETE_TRANSACTION = "DELETE FROM transaction WHERE transaction_id=?" + from api import session, Transaction + for transaction in session.query(Transaction).all(): + if transaction.transaction_id == transaction_id: + input(f"Are you sure you would like permanenty delete transaction ID: {transaction_id}? WARNING: This action can not be reversed. (Y/N) ") + if input == "Y"or input == "y": + session.execute(DELETE_TRANSACTION, (transaction_id)) + print(f"Transaction ID: {transaction_id} has been removed.") + else: + return f"Transaction ID: {transaction_id} has NOT been removed." + return + return f"Transaction ID: {transaction_id} is not found." \ No newline at end of file diff --git a/Old/server.py b/Old/server.py new file mode 100644 index 0000000..2c2aa68 --- /dev/null +++ b/Old/server.py @@ -0,0 +1,196 @@ +# Lucas Mathews - Fontys Student ID: 5023572 +# Banking System Classes - Version 2 + +############### +### Modules ### +############### + +import sqlite3 +import os.path +import datetime +import connexion +from config import CONFIG + +################# +### Functions ### +################# + + + + +############### +### Classes ### +############### + +class Manager: + def __init__(): + pass + + + +class Client: + def __init__(self, client_id, name, opening_timestamp, birthdate, address, phone_number, email:str, password, accounts, notes): + self.client_id = client_id + self.name = name + self.birthdate = birthdate + self.opening_timestamp = opening_timestamp + self.address = address + self.phone_number = phone_number + self.email = email + self.password = password + self.accounts = accounts + self.notes = notes + + #If you call a print function on the object, it will return the following string (does not include password for security reasons) + def __str__(self): + return f"Client ID: {self.client_id}, Name: {self.name}, Birthdate: {self.birthdate}, Address: {self.address}, Phone Number: {self.phone_number}, Email: {self.email}" + + #This function will return the account list + def get_accounts(self)->str: + return f"Accounts: {self.accounts}" + + #Change details (name, birthdate, address, phone number, email, password) + #Change password + +class Account: + def __init__(self, account_id, description, open_timestamp, account_type, balance, enabled, notes, transactions): + self.account_id = account_id + self.description = description + self.open_timestamp = open_timestamp + self.account_type = account_type + self.balance = balance + self.enabled = enabled + self.notes = notes + self.transactions = transactions + + #If you call a print function on the object, it will return the following string + def __str__(self): + return f"Account ID: {self.account_id}, Description: {self.description}, Open Timestamp: {self.open_timestamp}, Account Type: {self.account_type}, Balance: {self.balance}, Enabled: {self.enabled}, Notes: {self.notes}, Transactions: {self.transactions}" + + # This function will return the transaction history of an account + def transaction_history(self, account_id:int): + return self.transactions + + #This function will remove the account + def remove_account(self, account_id:int): + REMOVE_ACCOUNT = "DELETE FROM account WHERE account_id=?" + for account in self.accounts: + if account.balance != 0: #If the account has a balance, it can not be removed + return f"Account ID: {account_id} has a balance of {account.balance} and can not be removed." + if account.account_id == account_id: #Check if account exists + input(f"Are you sure you would like permanenty delete account ID: {account_id}? WARNING: This action can not be reversed. (Y/N) ") + if input == "Y"or input == "y": #If the user inputs Y or y, the account will be removed + db_conn = get_db_connection() + cursor = db_conn.cursor() + cursor.execute(REMOVE_ACCOUNT, (account_id, ) ) + db_conn.commit() + print(f"Account ID: {account_id} has been removed.") + else: + return f"Account ID: {account_id} has NOT been removed." + return + return f"Account ID: {account_id} is not found." + + #This function will return the account balance + def account_balance(account_id:int): + GET_ACCOUNT = "SELECT balance FROM account WHERE account_id = ?" + + db_conn = get_db_connection() + cursor = db_conn.cursor() + cursor.execute(GET_ACCOUNT, (account_id) ) + resultset = cursor.fetchall() + db_conn.close() + + if len(resultset) < 1: + return "Not found", 404 + elif len(resultset) > 2: + return "Too many results found.", 500 + +class Transaction: + def __init__(self, transaction_id, transaction_type, amount, timestamp, description, account_number, recipient_account_number = None): + self.transaction_id = transaction_id + self.transaction_type = transaction_type + self.amount = amount + self.timestamp = timestamp + self.description = description + self.account_number = account_number + self.recipient_account_number = recipient_account_number + + def __str__(self): + return f"Transaction ID: {self.transaction_id}, Transaction Type: {self.transaction_type}, Amount: {self.amount}, Timestamp: {self.timestamp}, Description: {self.description} From Account Number: {self.account_number}, Recipient Account Number: {self.recipient_account_number}" + +################ +### Database ### +################ + +def Database(): + CLIENT_TABLE_CREATION_QUERY = """ + CREATE TABLE IF NOT EXISTS client ( + client_id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + birthdate DATE NOT NULL, + opening_timestamp TIMESTAMP NOT NULL, + address TEXT NOT NULL, + phone_number TEXT NOT NULL, + email TEXT NOT NULL, + password TEXT NOT NULL, + notes TEXT NOT NULL + ) + """ + ACCOUNT_TABLE_CREATION_QUERY = """ + CREATE TABLE IF NOT EXISTS account ( + account_id INTEGER PRIMARY KEY AUTOINCREMENT, + description TEXT NOT NULL, + open_timestamp TIMESTAMP NOT NULL, + account_type TEXT NOT NULL, + balance REAL NOT NULL, + enabled BOOLEAN NOT NULL, + notes TEXT NOT NULL, + client_id INTEGER NOT NULL, + FOREIGN KEY (client_id) REFERENCES client(client_id) + ) + """ + TRANSACT_TABLE_CREATION_QUERY = """ + CREATE TABLE IF NOT EXISTS transact ( + transaction_id INTEGER PRIMARY KEY AUTOINCREMENT, + transaction_type TEXT NOT NULL, + amount REAL NOT NULL, + timestamp TIMESTAMP NOT NULL, + description TEXT NOT NULL, + account_id INTEGER NOT NULL, + recipient_account_id INTEGER, + FOREIGN KEY (account_id) REFERENCES account(account_id) + ) + """ + # Check if the database exists + if os.path.exists('bank.db'): + print("Database already exists.") + else: + print("Database does not exist. Creating database.") + + # Create the database and the tables if they do not exist, or connect to the database if it does exist + db_connection = sqlite3.connect('bank.db') + db_cursor = db_connection.cursor() + db_cursor.execute(CLIENT_TABLE_CREATION_QUERY) + db_cursor.execute(ACCOUNT_TABLE_CREATION_QUERY) + db_cursor.execute(TRANSACT_TABLE_CREATION_QUERY) + db_connection.commit() + + +################# +### Connexion ### +################# + +def API(): + app = connexion.App(__name__) + app.add_api('api.yml') + app.run(host=CONFIG["server"]["listen_address"], port=CONFIG["server"]["port"], debug=CONFIG["server"]["debug"]) + + + +################ +### Run Code ### +################ + +if __name__ == "__main__": + Database() + API() \ No newline at end of file diff --git a/__pycache__/class_account.cpython-312.pyc b/__pycache__/class_account.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..83ba42b9effcdccbddd207107774661781da56fe GIT binary patch literal 2136 zcma)7%}*Og6rcUF7h}MDBtRf&+eT3=xm2lCMT%0Y@Yy89R0<%mvb0@x$HeY_nAvqI zQOU231s*BZ%JGL;iJM-R~ zx4-oCBnXUOf4Y|UBtm{ir8Phg$MH144l#+zS;Xd4&gNC#7F59&RneAIi9;K2#q78m z=Lk<0h$%cIrby$5LbQb{SEO|8PgK%UkeGKFEo?ZqXeEfvxK`PAg4i9O6`YMAe#7zU z24z8f+I20eJ83Qur*)4S(N2+UAMIz*cL*gaXA+e+IaS~Z6-}N>rtqWK{(LNCB`O{A z(FSTfWMUnR95V3^rYmG*nrN@;4w){RY%@I}ljvC0Yj#r!*gi9P$VFev3u?;jc}UcL zvlrw*r7xWd;@1tsEjxZ7>(Rsu63v-bFqh%p-6dAAbym^7f#tajA?1R`L+uSzMLIa9z9<=5?K@&i0#uzjav}Or_CV?il7H-EtYvqw_$3cs= zZ8B)_mR2EYE_)!eBoM3@MIVY3ihdLWCcy*Q0`&Y%zFkZwf?}G|pXkB!1i17`ymLd@hePCNhX)jqyu}qmAJSFeh7Z<{W5= z(D*_gJ%$~K(at3(7~?SWzrh4r0A#)hvvZ8%=l#SE7O03~JL{(`P#wi~&QDpO zQi}2k)0)9*2TXSc>LGl0kun#(Jy5zbN*3Q=v~0QK4PI5) zSvXaiW5WO*hF)PKATisFme}cRN6ZAe{s98A-G6@9{K5Ust(~9Tcb}Rsr?Stcvb9f_ zYD;=;J^ymO{A|5k)9<|^>S)Qn;yi7IJ!+WEl6mZ?V4o^=SfZ5EbCU&(xYiUcDzVsSauA1 z%zyR*EWN0KV5{lqB3SWZD`BHJ8wYVf!gGYVVOW2mz|P@)1L7fR3{6yTAN3FKCin7B zFFf!6ta|fkU}X3Feyl$5QT5i*nX%e*7s=MATc>;jhZ@l|>hN4`&$c@RD!kYf+4+*ahIQGClNoC2$<~ dMkd@W_kzs4juCGBAMq?VCq6P?6U1^NRog literal 0 HcmV?d00001 diff --git a/__pycache__/class_base.cpython-312.pyc b/__pycache__/class_base.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..208f23be24fb6951f29fa3d251165ed7868dbba3 GIT binary patch literal 333 zcmYjLyGjE=6rI@(Y!o75BQ`Saf(En_5sUZ$8x_>TFd*a1-QCDMbY>%L=|3pemf~kv zTH0+VR(3+LN#)%T!3+0!&OMyBPG^>2JiTsY57V1-8n10+qD$%w_DrW+*rzuv3tQb=u_>KL_F2 z8gfs-Kq$epb$;w!?u9V}oAVu~;QxUkcm=ZL=skpq@9uRqZf_Z`4vg81-hMsMd;il8qqkN7+irf_yTVT1%X42*F_^x2ylca zD?}4+5KW|kuLOT=Ay^M&g1;ll2yXD4rEeLg8(ec()hxTACCk#OVrDp3TvBYR`h$Gz zzx^NJHwh&LP9p_g;|c;#sHpKYpb2-yr*HDq0KA*`A5aK+N^n34drD|PiFitQK#6)v zWI%~|N_0SpdrE9TNq9|Y#P7&eEe8u1+hkQkVRiXET6aZLF{m3URhd)K6o6H4|r;W^n}kD3(MRK}aEt zB8(x7Bcu@~5RL$360T@dedGT;!^XEtgYC&D>SFO-n=-qo8;VmYzGKq!ta_amm$fZb zajKSC%vz>Xx22b)B}J`J-C~lpAzd^#ECwT>K*~|IVp_UYt{3xEVbW=+U8Cp)%A_;J zE6gf0#emH%7tdK59TX%;)~M~N-X59_TGMK@UvFMGEVnB9FBRuO#c7qd9uab#%kmIb_)Gj_3|IL-`~YH>&topnmoTV&8B0{& zz!D4~mUt6`$=5M#^B-c#HlG!s1Ote5eg(sdu!bQgTos@sCn#8g0mL<-gdwFOMpS_v zgU?ybOyF1J%`3}pRF(}(+tRTgm*s0)ir&p6WZA*n)TazB%g}Sfi~Zjh4_<{sw-1{} z?Kr@f#G8?~6AZoS0e0*!0B(>@`grs0{nU}&_itzKiw{$;H!tqTMtA4-=Gw6n&GX$% zJM|*yle4X7&fLqkCtquc6Z^^ZZvJ+#oqWEzvOhkxw{T~!J$|}*c|Sh3`|4h{9e=jD zeCSDox;Hk|PM(6X>FK>U@2s@bufRM;CU$GL=h`Eg=7m4Sc;Ljr3>WAwsyFe~OvsJe z*K|dPyUnN%EE%qX8};v{68tzYJfi*=pTr*Si?<3-!e{;Mc!Qz;(5Ut-z$1a!bCC+lHu#@|nVD!fS3%A`0rT_o{ literal 0 HcmV?d00001 diff --git a/__pycache__/class_transaction.cpython-312.pyc b/__pycache__/class_transaction.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0a1c27c28f4e5339e4f1330059a37894e5fc0a3c GIT binary patch literal 1982 zcma)7OH3PA6n*pY2ZN16__h2dh|pT4B&{0MqNoaiLIL4cfkdk1G8tx`!Dc?@%@Ase z5*932bis~Ds1nPdcF{$bU1U?KQa46U1BhA>H^(cr^6t2iAFR|CkB_{3_imfLPp?_<8{#pWkMX`$pq1a zEux7u^qt@z&4f##RQOL+Qi2;Ewe(fPbi=a_%bSaCWYTo#B4sJg6>lpxRsBOg)^6Vi zeV0&@;WU!rH7+CYgo+wZLz?itcz%<|hTv}A|3D_nw;0>Al~{S%Nq(S z$;-6lCWCgTRHSZLF|1Y7aqFDCL2XAdif+9|ZI$JV-uDxVs-lBzt{MxJx$TUq`C^^| zbJ3Dkqkc9##LQr2PICaeWP{rvvS&cp;6dZ6>V^Q?Sth|If_7F@&_bYzHP0|;)oPKA z2x#G|twavE^g)DSld%SbI6?xU5h00i2_O}9MVsom|KVz5NYdGQxu*_i@7k2vS=~^a zrR--W9b@?iH2aCRsw$YR?6_q*C0lw|x~-^7RJWL9<)le7XE8Vd4$>S|mrP5y7E9SL zsKTW8z;~8n<|&ij&wkFVMWz@A^lWz2(rA#7z*wNFlT~%aw&g%22a6>)D$98@@5u6c z^W1qy3|84D2*mynV3Qno4{VFiq8-Q5b(CGlt)1KAH}%JF^nyqncl0CMUh{hU*0%Ut z^y(>`C2Fl`pkQjf68{7n7+NTC0d}vhJs4cD)CzoV2Md(GP%G~R3$#I@R@@5~Xo*6w z)y|CYHE555_sE?4;|?s)Dg|tcSJoc+HZcngkzP**k|s@#4NL3I15%KigJA(SHXx07 z0|WawcvOc5)W!j6?0j&5XqCp9Wk@6b07+?YfPn8DknU86=rt>AhToXtSPKXn(=D-9 zK&&sLMfQ4qeYOMLe+Gd1Ub?d1chr01u=hs!%20V|>X#3Wrtcq4-!D%oWo5an7mxI{ zLw&8h{MB_59uz+Ggc!=VWFe-w}BOxn-3ko0~3SXi~&Lh^%i>w_YVV40?NsQc7 z3@XcROqLBxTh(#jAj>POihgE^%d(>^=+svkwk5;93d}gZ8~^zxur{>532;FC%Q5K; z+sp&p*cQMRId1OVoH=RfD0dD#$vy3R)-th~K56SJcVGXZztZ+mS!_OuU*7rj@nR+3 zzd3%AY~A^Kf4q`>XLItTsr_-+lW?W!+UE4x2No+WLvXz3YPtW`)3+);Q)RL9RE&g1 zxYH&sR80ilo=!#Fn7yJaIy@{!DG(wC=EnR-VFCWz7>NVqFX%9 zU|s-($L8$s=jQ2ti=`+vF=ZvgXOQk+6OyfBLV;?FV{&p6OEO~o^HN=kGRsn9TvJMu z6H7Al^J3ic^GYg<6?7Gx5|cAhbMlK6^3xPN^V0H*fI5H*6hcyyGxG9t^3yA0B2yEK z6bylCgHv-;i%U|A6pUg5it^Ko5_5Ai^U`CS^HWlh1r&giS*gh-F%Wm_6;%G>u*uC& zDa}c>D*^>9$iu~IK;i>4BO~Ky4hBY%2b@YBGS@kkE^;be=2U55yTQQL&fUn};dPZk Jyodv+6aYsOYQX>i literal 0 HcmV?d00001 diff --git a/__pycache__/database.cpython-312.pyc b/__pycache__/database.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dff1d5c5355c52fdb653ded43461c38484352712 GIT binary patch literal 4318 zcmbtXOKjc76(!$aPhZQjVmq!wCrWIkro{ipaqP%aWXm7#00zuiY~NEkaUwp(M!H3ORAGLLZ1N6 z@baE>KXd2K=x?p9WeR?e{(90n*iKP@PhK{E9gWF%|H@I+AE^lnQ<%1?3Jqh%&eXD% zES<=*cCMDMD-rfvF%DA}m z>qitWeVpCYp(&^HC$yTjYvJ-vmJm!-nn??3#my&So^rI1=8ve4Gh5ss&5~x^`dQn< zOx>qS8${b~x7Ru<9ci>3_MTd2r8AxHvG>-xDqZQk)81F>u5?2dI_CQ7*zM0be*0T} zPTD@(yTf*VyQFdeygBf&0jnH*7Ef22xqa^+qo@b8_a}02PW=(?&Q0by{rRt(te9%B zET#GA-o()L{JikYX1Y$W68pF37%ac>JU1|P;sf{~ei0wSzrcs_OPIk&a4Y=1{II32 zzh&pYaJ+(h@T>T>l;bEqhI@hGb^Oa+aCG70m<2ETa6e!<%s(vsZ2MhUz#?EJECW{A zC04~6c&=d`uqW^vyRh4hPvST6DX@GRzl8@L7Js(K-S`Y32UFzGE|G5oau_^5jnD29 zc@B@@^MJen$cwx3_7WgR0eKmaS9XQG3dpm7yavy)A4R_Td|htVJWVarX|<1URbfLi zv0c(Txsi~J_R?{y=@Ar=Y2wUO9Rh+DpZI>&b!zB+;=y}i@bc*J$l17rQGg8OlQ`#~ z8VQ?*8t+Wl!5|x0@G?VgR{g;53y(0^B|hW0L0#0x3?^o(&J0rxK)lo!mi-%cH6SPY z`}^YpHl`OmJM4bOX;p2)_#|Kg_XINzZsdqL>n7tFa3OWet`aAJS#iKL-Gy}8JnkXK zM`i&2C%Q(}!Ermnc-lv`d7DfpF~qrXZ-In^W2uNqL=31@6A)AN8C>;|VH3<4#6$}| zVS-tbY9+xZ3Tafe`Cz#(JT?G82&ztB=L%fLAxm3`G?X9czOcvA5wGZy%o?)Fu@Wzf ztbr5}W}+pSrl2Ys3R^EOn8;@?Auzk-hh;(P=d^xa*R`?GL=va&Mqi#U_Fb6>hVUKJO z0jUx+H(54W2@{3YWW=)q!3q*(88U_A{I%+gu?z`O`Z*C2!b5U6n9KF#bTy(i(T+&5 zNK|Zzrm88LU>SzY6U|h3o-;&UP((>L3`tN72vd|yMdD1XD2fcJ=XgXc5nISmH9-KW zWe6DSMAV5PBTdo;OM%pw0^wByNhYrtnq`=nmvxg9704(wO#_Fe5uZT}cr_f;V%Q^@ zf}k3lz>1JfRsv60T}J||=?atrm?uOD+sDu%l$g&n$~-PM1l7-*DQfF8s_%lB=EELC z$GQOyu>>K5l^~@ok}%Km7ML>(OVu?i>=>ITPFlU2%%x4q#vhsLSz-H#15;!(4e3U5wTerEC`*DU%3%-eCzB9e zWM$q|VN>xEYv{yciO8vzVR43~sT&wT8_5L-(w~@7c*Js+sDKZG1zFM%%fo7!mMkbl zQmfyCm5pqw4~vBaq(d%w5wfaD25WGl%BtWwA+V0JX2#{~)nFETXbIWX2oAWgG~ra4 zVFCrdXE@z-3&0vwDO03BX#DIRGa}wGRl0cizdag;l zD)HmofQ_rL%1BgHQvetbn?$m}b=|NKD{8zB3$5u$*5bl=(s=#2SaIDe_JSYk<3H8T zjx+xI)%p4V>}Q+35xV;SYMFh#FU3XI@0*9_6=z&OPMSQjp;6VAy(Eco+m@iu^=ckm z#i_Vpx{g(y@$$(_E(I6rCrO;2_o`0dC3l-R>jz%kK0S>b=)iE8xQ;(P9T(~+S6nu2 zwwryjhva&Wb?=ab3R&g1J@_DQbk{>TGs zrLx+6?9=YmZZ6u#NBlrkKJz#?`1Ox|ycU(m)^p>)+Hq*b`lPaYnEOlj>S1lQUAt4b zzjuw&o>2Lyba-WPWi~1tU8jzHTcFA>JXri>XuYZTP9gE>&{OKAr__rb`FpK* zTfe0CuTuM0tccpbPQ40{<`-6m*UPWmDLyIh`AzSgViHFA{_CsReNp$()$FlPhu5;g zSH-q_x9;A0Fu7Jd^kwno)#A%*#hy>;wPNp+Qft&PwqBY@C)(%TkIJk1g)fFz^|44B zkH#mWw#oI<^{-0pzis}o`FF$7ivy2NN8|5BhVucRqhP8LsBXQ`(7?cJdkR+33)jXlNq3Zbrn3=7VT41npb&XhL`;GnPqw z8P6oXOk_5Fse>k?cW`9QN8UoT^efQ5O0p_=Ul2hk}98nPPy*aQnXx;#}+Ka^Xd$x1Xe8_hwEmcZwbzLYYLrp%XjGK2Cvuu{=1#JL#VyuH@-L6X9O=d|jP txzPZRCTa1tcN1rtne)x1Gjx(aX#Z&GOglp-na;{0m7|bprkUx?^)Cm~X3Agv zeBWwUKadGYNZUX1X7>Bue&5@-Z})xn`|G{^K}AJ5ha}!;AAGBo<9<#jTC!&gPlkDp zyTeJG#D}>t{yfi9*$_628P6M8*%%haOy^CkEQHNtmh%=?HifO{t!Qfsmz^(TdHMNr z2TVR4e4=qtv=6eV#oZH5FChRN_AeCMflC zUW0FHMZ>w4hOw3gNo+XM)^NP7q1S87SfxNb6dxH2WsH&NwTv|umj~(eGwump`Sf4|B zaRnNPeWBoRBpQwmO%9w31!Qq6+V+OVLa}&A7Pk$YkfTF#U~Ft8GBnU0l|uOp5tXB% zV0>UK5D5&0nX=)~JJQGyp47|BE?gO- z5s9(`G*h8Hf}bvzJ>adTCUqcq54Or%`)=-=ZTi-hkG7<$H>YgPY1_`EZD-21TM>4% zw-7{2J?YQB7p(kekL#Elr8Y%Ldlv0^dpWrhO+GVbjKEPwn3x!mGA5$0$t0-Qjk6Ch z6pDmoV9!t0axH4BaKvaf=>Z%c#BWXAocdZKWvfwyn&%ApAg=iBhYUf-SF0b$7gqEn z(n<3qKQNV{Qw+ zfBn6qMK;Ct8PMK$DSRck@9N+EDD_{Iv^ksR6a0)bZquqIgJc}huatyALob)$-#?LS zk>E!)ZKGX;99%+VQ397BTa>^hd=w>cR~E5w3N9hKC`qP|&DzsTW*Xxp=e0nx%7pxk zDJ08LdCH|qWib+si-Qo#(ih*v`}LYLLL>l5S9WPcjt`UI%dCiwhawO>eqE+yECET9 zL$O%KK0X|cg#3|-u}dKtU5^Du!e2bVZM^o3C59G|Z5h**iI6-g({yFZ21mjWY5q%- z85><0mHiRqvKf_f1Cd4|8;LX#@e@uj;_5Y`!D-`J%3O?=ff_k$wfcG3 zBcW@4O{4|$u}@_>p19O61XN849jnvEoUBe5G;~Z0ab0eglGSmV6_&Isp7*sJ;x6px zxM@?uG|Fr-?OHWYaF;QkG5!NL>PvkiMyi)<6GFn|*YBG*KGI`FN&8NDj^`#}%}p8v zPBJFAKj$Rj6Qg7z?0d~VuVHHKiL5#kgTsNyP)HJE6Tu+xIXDpxPqxaN(KDKPx}aBN z8Ol_Qh9yyZ+pS-Gh?n)+B9f zW+zj&rbXwfTkqa{cXoKe*|@m2@k8@BZI4`@JC56qhc5BQp1nVIZ(XXubxRJ;Q8imN zYn$7r*tXqmS8Tf!VVA1Fa-VYHR~*!*76T=rNW%3AAYY)Lw27UzB+R8uI*FIK&$G~- zw#IeCE@4SPD8_Y@LhC1CNEj1BTo?5TlLqL7m4rHa%R+LxEUtrhlq{j5lql1#Fi4sb768-yiAl0BU|M|{6Qpk__62>gGCj^zoiB7vRADA!`o&A3P(*~V4M}Yx z$z=e~+ANYe&?aVhQN^v5>Di1CLXZKDnS41U0)LR!ZRp-xo+n}p=Y^p#W4?SXAP>b< zT;gNqGUWusti+`kqoR0<22}qpCI%vs7z{+1fV>nE^-Ff3FL@V{th_7~mGW+?ssqVbwZ1ZDDHIODW*`L1 zuYwe){tC5L!{9O0@1r(<3IYt47J3%bCBBr@yMFWf+y+R!thk%K`p~77A^H}pHq4!y z>sGcLP&^0kw=15`1y|?KH0E21XF$0aP&|PJS76CWw^(v5D9?1Nd7HOUOFV9 zTBZ#N!>@s)p%f$&d{$}K0~m%6m=pYp9!)m@RRf$)eV zGTTsu=c{W~ucb&+s?$$;OjU}cM7&03{izJ54C&*|$sf4PBN6`u+;6!D6~}SJPJt{t z_Xbl7oBCUOZ|?)*M%9tbOSPc^D%B@A ziMn1KG16!=+@h^fy{WFyz}CT%wf4YLQz=FHV*XH`{OSi-ED6i60Slk7_;X6vfHPgK zL(y9N`gmc@S^T;zOh5(tb4u8ZGhNx{$uf`jM7dlWA!2R1EYm=jv-n``nS9U4tBG-P zW2Dw8TRU?w#jZnbA`?Phdr1J2{8?U6l3G-?sa4gJgi_UODN?DdC4(%z0YH|etZ%Q5 zN)uasXmA!s>;}lP&@D6Ve@1Fmuf5UnLHFXirW+j(?KO+F8*u7dT;EEk>lW9DI6aol z9@*Wud^dg6Y?U}yHD^;czoEGIE!g)hxzY8~%6w_12-_I;GUZu8ysTgT+Dp@;FWE2m zC3tA$Bczf4uRL`4;<~9xzPN_jN!=$ujR}|QL8ew@qoX;mLRFHcj7YQ|yX7Jg_)y`JPuy(l1IAT8p0c;(sZjId>`?xdZ*pPO#CLOK!HYtwQ zl;d#PaWd&RnR4{bbS_#PGuN}e_G^k~N6NYLo>6hWu2^43m13!0^sIf%@ns3ajm}5z z>iJ#i&HIv@_bDg((_00 ztGP<1FElT(rq;Fgf&K$@v&D$};wq^)+YI%V6u=v;k^{E4~+ zXT9c6Tzlx+m6#0M1Xr5Exmc!{Uwf`lQ@V1#(f z@^WS)FB!AEobkv@LYAkCv;;4ivOL|8%rhNV;Z{XS8!-f)8CP5AY6v`Q&TA>)mwjS{ z8CmYj6H4cBMNT`)cB2!&8B%e{WImD3?DWzpq;b&B(okWdjfip(J19qZnB|5TJ zqK3vnRNkakpMpTjl}4l0)cq_P*Eh}ACfDz}(Me`uy?4GfS-*!)#KpDnYLjcX-01wE zr=VQ3es0%CJzyWtHyrEc8s}@0>vt-3yOdpBiu>q-{pivPy5&+O=ct}-oU2Jzw<=Xz z=HF9n`xIfH3QtEox8A#~CHaRH9!zUN`b}4OhH$lNO|HsYU@x{P%^6qzbTC8G92lde zc{O%On9Vq{sMA(=UO`-ZWSo}rHLaViQFVR3FGap#v9(ExSso?Ai~#moT&B}7h}R&~ z&@;BYaTUmACz+jjm2w2lKgJRJ5lGHsvZX4U)0I1ul{@c8l**l{%G2q}K(aEBstnF_ zFIp;LUuJF3-HK;V%Gq|WU2*PLtovErTGY*Vr<;!?n~x}c{&e49vTqP3;#HWLzoM+0 zOi9YX954(KbHETICS_oD8c!L)>@)^wg@-aQuN#jFU|u%{1u(POE0O?9)D;rYU`QCH z%*ALKC~VMm&l(qG&N1WS#7mRsxd6fPF25G>E_9;~akrrChhQLIK)i)Tm?*SSlwb~1 zn4>6x>ndX56x_;^+?6G{x=zT|qKDPFGp!`?A6sr4KIjuL)@gn5OEQoNLgor_uOZOn z`^>^nC_fjfrO}=-s|%YlgEVum*tb}!D+YK~@Mp|eMx6-9 zLnRaUFR1CyL4f+~;^ynAijC=t)?`KNci&BK=}K6ur9V}1cBTUx9RRJMX~gtj z@O4H_IO#L_O>^(2Hy%iCJfQSnO83W-{W0+0G;|Z9-!z;eL{kQ402`#h3>dBwlQJ-M zm|s+3evx1<8>b2Tvhf(foH8&+g$cs`sNge!Nf}VTs>cME3xh$~ewl_eW*Vo=&M3((PhSJ$i{U?o^w}7n}WNVU;M5g z{E5INjH4?sH$_RZQL zpyKFG&g5hUEAwyKF%+3;W2STu;nZ8kKBQb6X;6-i<)VBLGt3ER(u*uoRT}oAlyQgzK-TJ*sAP!RK@2W|IWzt?jA9p%JDrN^)M-&Sk^B{-YL9~-?V0_$Kv(T&<>+gO8tL#KU2Q?)>l7u)y+e78+-^g)nV9qVf&4A z+C;=lWE03T@qVVQU*Kv(#*$qLm$#y~XYEt$^U&7HW!i{wpIZG52wu%-DK>EyJ@(fv`@9~F7 z8^6b&A#F?8Ys0>}!u zNL?dkkkvbq-=~VRL<(aSaI20ImqivxA8LUG7mMNpu2~;1CjSv?{{csg2=I#N)%4Bj zkNZ>3hP2b0bb3?H&Dcc5jLB}Hm*KUKu=1wRDa`Fk*Y8Ny@3=pd?l_n1IH!!ofwBm& z3Y1ZXc4rWk2lI?!l$ex(dCl-1F|Qdq2#=J3*^BM?VD=g>5tA}7$Bc1ejv1$jNf{yc zU54B~hTPK(xu;d+_6hQJJjfp~iU`(fji2*^c^54U^$zyldhD;IJ6ae&)PfFx2M1JR?4$A?b(sU zx~6B>W6o;o;&0d%ZL8C^hNP`wer?LOYjN}IH@Y9%n-=RfQoeSvaVzEZi{4$7H!g1P zru@}B%XL-5TtsQuq3q~X+(#DdM=0Rh#R9HtXCq2&%RJTt_A0_&HP%`&62W#U{1oA* zp9cz71n6ZkoNah%IQvZ^NLV%K$;OJFk3|D~;Zo-4GP{(f@LW@>ze-QYQXUe?SSd=H zQTzfMN~XMB+4q^}rHzkcFNs8cU%r#bTt9gN_fm11zrl$_jp4{4ge=524WXgT&F(;UgKf4}~ zb$_mX>-tC6@75;Q?MvGCeZ40ibNlX1-3vd!HmG9@_G3$B)XP#ih4{4vC`EWp1)Om( zR0uO&oc%in`O?MN!QTl&d^Vi&djQIYj!@`#$4Ci3Uc<--c%3CMQl?@AvmpO5TKpT1 z7zw^&ckEOf=ia7!HTO>{?#=~!CjsHeGC(N!SB#Vg6Ez|eH4;C~ zb~VtSm(%z+Vj6#3B3w(dwkRcxS`g>g{B1ycZH0tTQc&n>Ev|q3+J1*m^!^bf(!E>B z%tB&ic^nII~P;iEQCo zbOOIIghwui#PJY*HH={KW>Qp_xbr(yBV60|i~R>q`MP|aZDJoH&e-$8{!J{{$a@w4 zIbt)N*mQ1-&x<|i=Q8}H{1qBQ7s%9VhJRmApSt@|-}t)=mEJOJMIRr@jadChg5jNX zcg8dl8J~z_E7Lon-~|3WGL6fSdtQ^eHdLv;AAjGe1HRq^?R{N6zPzrO=B96&X>K(Z zRheDP&)3^`(BwaG#J&fDEtXYXim>`$g(mDeA_YG8i~@HT>+eVQ70Sv}N^d|pbqQu& zkUveHjQfn?m)J$rZ+PTfIcxlC;^WX656b?g^ruELE4pXyEXt%`S#vZqJ!oLF$3SaMoTZdy|UeajK~_QqTgKumjc*P%VLJrRN)~~ zOGE_87%?sEM=SQ@iQGvQ$B00?bMi?deMD$~nEW=8AV@}#MuPZvmd+zYVnirD$f708 z1(m-{r(|5w5Tk)OQ+Q0Q$uxCEza2ic?j4It6XDQ4c@DLZG+gW==#r7=`Tw+Xy!cbD z?x&phr(D%fILA*o=TA7>FU=h9d2HwTc7Dms^Lu8uKjDx)M!A+q-IBw^pEb_9pKz2f zIm-Fn^Ke0t-K&2>+0rX!zK8#?eTk!dZo8V_MH@Q5U$5p5det1eg`TjA$F4G5yH?X zQlp6<6{su9mmCIu+(c)TFIiXcLp=3I`QvQ{zC(CyHSnhyy<&Q7FX!K&r`bV$BD=ea cZnlH&ykpkQSaS_omYsiNF8E6h8Podz1En@$*Z=?k literal 0 HcmV?d00001 diff --git a/__pycache__/manager_account.cpython-312.pyc b/__pycache__/manager_account.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c186ef0087ff635d648dd231b82b7ea5a85d12a1 GIT binary patch literal 1782 zcmaJ?%}*Og6rcSt2CuOR=A%VKohniqQ$b3UDiR8@2si;HStt;tP`7B=9k5}&>&~vB zMvDqkq<*MUn-gvuBuhO-T=FNRr$*{!14waKNImq_8x1+-)Hmxj&?xFio{!(WdGp&h zzxVumb8`ei`=vj*uoyz9DA)fO*>o- z@-3QP+lJ*%0KtCK6iN#6-wb%fdtr-MR?alM@Q|vSMZ->pyatQV6+$U|E20h1d4i&8 zUW01p^Eo>yc@d>%JD1ZuNh6j@a|N3@iJ1jr%-Oj-v1}z@@S=4?yHp@vcwW&JLxl-3 zlzE+KUdS+QVtG-UDu$(~%!gIGQ8LKtTyWYaVo5**pR`v$ssow3z|_j)Yj?%JSoDPp-NjeeBLEu9|h1R`yy~H*WnYzE>5cuWoJL`a#+kyV+dcl=sE9s?_0% z9Y^2*QKla?cy0zD_Cmg(vVejFxpfeYCit@9K{f(BI{!x52zA*k{f}fu(5IUSt&5Jh z zjoZgOIb_FllMzx3?F8uxeiHSafVp(~77mqAfJe858gH?V zG;a%#LNR&m{7CwCdOD47Opf2dfmZRo(ez}xzJdf9Oi}hYRD^CVI#f#>JT@|b*IIE_ zv9N-Jv|!>~dkZUuhE>JDkQ#WNU`q1l3eo!328JlXC9{aFVlY`Xi@JvO+%mxh0+Kce z$Q^4$CpM`wsNHlah3^eb%471)0X&_}!KHq#{r4V7c?&MW7iKQXN$hbm9#;xD&J$vs zFzN7l%j46)?=&96;F-~0fI_16B_ov8s#9eo^m&oc5>yjir|cblEqkGyQ7GC_6uuy8 z(I#G~Ky$1P)pldGAkPgA50BrKr%!nc2O+mucKL;YvPbn|wFn$1AO?^r&f0@+16A7x zH)NJ+iLR}|2ZNQ)y+rbPqW4*%cQ0}A1&V|cUx%ww>-VW|Qe|aVdZ*g{Zl$Nv?e?ag zgnx_w8h29@?nKs2<{Zu;5hC@B*b+d#$w!uW%VV3!9Vve5(C9o literal 0 HcmV?d00001 diff --git a/__pycache__/manager_client.cpython-312.pyc b/__pycache__/manager_client.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c3567d0f90a0e0294fb50a8fdc0f4b38ac1ad5e6 GIT binary patch literal 4195 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 literal 0 HcmV?d00001 diff --git a/__pycache__/operator_client.cpython-312.pyc b/__pycache__/operator_client.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c8ed0f102cb2ad5aaa5521300de9f5da810e7e6d GIT binary patch literal 821 zcmYjPJ5L)y5Z*nX?X!&w2#{B^iqg19ltfhIshC56z#t%sf=;oHyD`4#Zr9pfgB%?a zk@ydgE@e>uMEW!?(JEc4bXKf1RGBj-Vx)QQ%-no8^P^fV6Qq?=t<|sz`IR|WWQ~pC zJv2^8NJ1KugzivELhG39SYZxwEi1H-?H#+9uN8i01!vS0>Tv{I2bFPyOjVI{$iqh{ zPKc(cMwF72rdFCu?KGbj(qihQrL_EQTzgnaD_f*S8!{`dO_)+J>fMY&V{<Q2fk#FF?S0x03g{DZ$pYb=_d)| z^6Dal>j8$3A0W^k0++rPay;n6PS2DP`Ph{=aPB;^nnQ6!eoo#zEq$5$db>a8en04M zc>VW5ACkV%{r$rMA+M|@Ye>k{vURj_UYY6JGw2Z_3n+sBi$f=~M@x0kzzd;=v`1=I z5d&--}9Gem^$ids+Z}_`s%XV*jCx$F(#)_nQ90z zXt+Xh3u7f@RYY+_&Zp-;t;uP$8@0SC;3oD#N<3sT!fbL8*D|Y-vzf>eM{O^hXNd^A pF}#p>F_z&_48sh96F^?H1P&21$eo2iCX2lneAJ#lR7VQe;pkSOSW@hjLe3x)R9_>7 zj8;(Z0_;)ZM9XV*4UybNQ~kFZS1^0aqLV zA9!D^C~Aul3_$D!`i#SXGRD<_bDH3@jtABWzzhCAnI*KD^?$6IKsRq9R1+O>)z^ui z^Qd#s?IE}z_jRA$-3B$$LEj-Qv~{#UhwQE+YuJy729O9t=-Y70SljD(1g)UmmJVT` z6K#tII)W_GU=|KlQ48%c;vq)?s(v|?~=#tmd6Pc@-d_ZcfUg)z5p;h<{6pKE=ypV2`E4CLQ zpA)rW6E9Szx?!h6O>1wnf(!YXF?n|ENKnp%wizJzHn%)H)aXC|KC)Xw0Z zbnc+*gxf#k&MNNAA_z+5uJbVTeZoIOsC$k-i1j}ZzH+`N+t#FXY_h^l$O$MhtQ{dQs2d z1H*m&^v*3;8g<1{+7IWJQ-ZH|Pk=g6cBMziH~9B{-Z5G-wMvm(qHlqT$=71Pl!m}@ W+|$kw_gbT;i@Uh3K0`40asL7y-?@JP literal 0 HcmV?d00001 diff --git a/__pycache__/password.cpython-312.pyc b/__pycache__/password.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d82b09c7efa9bbe47c5fe7cb934ae6fbd83cda6d GIT binary patch literal 755 zcmZ`$zi-qq7`1b`T(03-35kI&LVpr;m5A#}O{j!G;AsW5PgRGGxN|9Xdi_r#0>Q{8w{sL-@w|gv07^O&C)hmJ+=B4*mc^7jL>>#_U!_DjbHkpH4m%C zQ1UE^3NTxNpd_aoG#extg}^yUrA&C1l1(a$p-#?kaafTLUKrh+^^=nxK*fDZGHltl}0bKnh@)E#t4P!j95uO{Pi(_o)=_5BLKqdATnD literal 0 HcmV?d00001 diff --git a/__pycache__/server.cpython-312.pyc b/__pycache__/server.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c59d7116eb113c4f0b3759f949acf8876bae3359 GIT binary patch literal 1121 zcmZuwPiPfK7@v8&Z+G9C#6)R|F@;r%_zVrSV4+e9nkF#~jba+nbzvQM=e=w)f3~yx zq)%Fi(4t2Vf*w40@FHn%J$Ur)CF>uwgU~}yy@f_8c=DTP9ueAs{e6Gt`@Y%l_su*S z9P|;aZ|+UBE?NlvWsLr+Z7W*}lv^l8DP|}%vc@p4g*9%47Pmv2*TXt@LWjGdi_N~p zJl+T!+zc;->evu@{<- z9!wM^C5@?QXM$>fMk<=M#XRk5XG*dT7us1YRVLcnof9Q(Q>EP#QZh+31r2Wna~*w4ZyYga#FN4}S3^|EY(|MAxU*XtPXukO0( z1{QFPn%;u;NS?*5j8Sb1!l`x&sxAY44VD!p6>*kpJC~}|^^{)fw40W;mt|3^Jy1jK z1%UB15-9}z0r=G33?s$r@jTaF9Gil1tX-0(F&v{zOWIN$0X$nI&kMs+@a9HxELwtN zi->VjE=Q*X{aV4OqUp4gz|f?KW~3;)Mes>*f+Wk7NfpReFeh44K?Vc_-_c}QNG99e z=sYDV_!MFnDW}kb3OMCU!r-8}Cal(+;CwsgJ=llPQh?Y+4xOtvv{j{OcgRh@Yb~f1)p5gFf~OVDz6M c3+H%q6xjpUuRON){b9SmOmAAq9_!V93#}U$#{d8T literal 0 HcmV?d00001 diff --git a/api.py b/api.py new file mode 100644 index 0000000..5c2625f --- /dev/null +++ b/api.py @@ -0,0 +1,26 @@ +# Lucas Mathews - Fontys Student ID: 5023572 +# Banking System API + +############### +### Modules ### +############### + +import connexion # Imports connexion module +from config import CONFIG # Imports the configuration file +from manager import * # Imports the Manager file that contains the functions for the API + +################# +### Connexion ### +################# + +def API(): + app = connexion.FlaskApp(__name__) + app.add_api(CONFIG["api_file"]["name"]) + app.run(host=CONFIG["server"]["listen_ip"], port=CONFIG["server"]["port"], debug=CONFIG["server"]["debug"]) # Runs the API using the configuration file + +################ +### Run Code ### +################ + +if __name__ == "__main__": + API() \ No newline at end of file diff --git a/api.yml b/api.yml new file mode 100644 index 0000000..19b9cb2 --- /dev/null +++ b/api.yml @@ -0,0 +1,701 @@ +openapi: 3.0.3 +info: + title: Banking API + description: |- + Lucas Mathews - Fontys Student ID: 5023572 + contact: + email: 522499@student.fontys.nl + version: 2.0.0 +servers: + - url: http://127.0.0.1:81 +tags: + - name: client + description: Operations for Client Accounts + - name: account + description: Operations for Bank Accounts + - name: transaction + description: Operations for Transactions + - name: manager + description: Operations for Bank Managers +paths: + /Client/Login: + post: + tags: + - client + summary: Log in to the system + description: Log in to the system + operationId: manager.login_user + requestBody: + description: Credentials for logging in + content: + application/json: + schema: + type: object + properties: + username: + type: string + password: + type: string + required: true + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Client' + '400': + description: Invalid username/password supplied + '401': + description: Unauthorized + /Client/Logout: + post: + tags: + - client + summary: Log out from the system + description: Log out from the system + operationId: manager.logout_user + responses: + '200': + description: Successful operation + '401': + description: Unauthorized + /Client/Password: + put: + tags: + - client + summary: Change password + description: Change password + operationId: manager.change_password + parameters: + - name: client_id + in: query + description: ID of client to change password + required: true + schema: + type: integer + format: int32 + - name: password + in: query + description: New password + required: true + schema: + type: string + - name: new_password + in: query + description: New password + required: true + schema: + type: string + responses: + '200': + description: Password changed successfully + '400': + description: Old password incorrect + '404': + description: client_id not found + /Client: + post: + tags: + - client + summary: Add a new client + description: Add a new client to the system + operationId: manager.add_client + parameters: + - name: name + in: query + description: Client Name + required: true + schema: + type: string + - name: birthdate + in: query + description: Client Birthdate (dd-mm-yyyy) + required: true + schema: + type: string + - name: address + in: query + description: Client Address + required: false + schema: + type: string + - name: phone_number + in: query + description: Client Phone Number + required: true + schema: + type: string + - name: email + in: query + description: Client Email Address + required: true + schema: + type: string + - name: password + in: query + description: Client Email Address + required: true + schema: + type: string + - name: notes + in: query + description: Notes about client + required: false + schema: + type: string + responses: + '200': + description: "Client created" + '400': + description: Invalid input + '422': + description: Validation exception + put: + tags: + - client + summary: Update an existing client + description: Update an existing client Id + operationId: manager.update_client + requestBody: + description: Update an existing client's details + content: + application/json: + schema: + $ref: '#/components/schemas/Client' + required: true + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Client' + '400': + description: Invalid Client ID supplied + '404': + description: Client not found + '422': + description: Validation exception + get: + tags: + - client + summary: Get a client by ID + description: Get a client by ID + operationId: manager.get_client + parameters: + - name: client_id + in: query + description: ID of client to return + required: true + schema: + type: integer + format: int32 + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Client' + '400': + description: Invalid Client ID supplied + '404': + description: Client not found + delete: + tags: + - client + summary: Delete a client by ID + description: Delete a client by ID + operationId: manager.delete_client + parameters: + - name: client_id + in: query + description: ID of client to delete + required: true + schema: + type: string + format: int32 + responses: + '200': + description: Successful operation + '400': + description: Invalid Client ID supplied + '404': + description: Client not found + /Account: + post: + tags: + - account + summary: Add a new account + description: Add a new account to the system + operationId: manager.add_account + parameters: + - name: description + in: query + description: Account description + required: true + schema: + type: string + - name: account_type + in: query + description: Type of account + required: true + schema: + type: string + - name: notes + in: query + description: Notes about account + required: false + schema: + type: string + responses: + '200': + description: Successful operation + '400': + description: Invalid input + '422': + description: Validation exception + put: + tags: + - account + summary: Update an existing account + description: Update an existing account + operationId: manager.update_account + parameters: + - name: account_id + in: query + description: ID of account to update + required: true + schema: + type: integer + format: int32 + requestBody: + description: Update an existing account + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + required: true + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + '400': + description: Invalid Account ID supplied + '404': + description: Account not found + '422': + description: Validation exception + get: + tags: + - account + summary: Get an account by ID + description: Get an account by ID + operationId: manager.get_account + parameters: + - name: account_id + in: query + description: ID of account to return + required: true + schema: + type: integer + format: int32 + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + '400': + description: Invalid Account ID supplied + '404': + description: Account not found + delete: + tags: + - account + summary: Delete an account by ID + description: Delete an account by ID + operationId: manager.delete_account + parameters: + - name: account_id + in: query + description: ID of account to delete + required: true + schema: + type: string + format: int32 + responses: + '200': + description: Successful operation + '400': + description: Invalid account_id supplied + '404': + description: Account not found + /Transaction: + get: + tags: + - transaction + summary: Get a transaction by ID + description: Get a transaction by ID + operationId: manager.get_transaction + parameters: + - name: transaction_id + in: query + description: ID of transaction to return + required: true + schema: + type: integer + format: int32 + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Transaction' + '400': + description: Invalid Transaction ID supplied + '404': + description: Transaction not found + post: + tags: + - transaction + summary: Add a new transaction + description: Add a new transaction to the system + operationId: manager.add_transaction + parameters: + - name: amount + in: query + description: Amount of transaction + required: true + schema: + type: integer + format: int32 + - name: account_from + in: query + description: Account number the money paid from + required: true + schema: + type: string + - name: account_to + in: query + description: Recipient account number + required: true + schema: + type: string + - name: description + in: query + description: Description of transaction + required: false + schema: + type: string + responses: + '200': + description: Successful operation + '400': + description: Invalid input + '422': + description: Validation exception + /Transaction/History: + get: + tags: + - transaction + summary: Get transaction history + description: Get transaction history + operationId: manager.transaction_history + parameters: + - name: account_id + in: query + description: ID of account to return + required: true + schema: + type: integer + format: int32 + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Transaction' + '400': + description: Invalid input + '404': + description: No transactions found + /Manager/Interest: + post: + tags: + - manager + summary: Apply interest + description: Apply interest to account + operationId: manager.apply_interest + requestBody: + description: Apply interest to account + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + required: true + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + '400': + description: Invalid input + '422': + description: Validation exception + put: + tags: + - manager + summary: Apply fee + description: Apply fee to account + operationId: manager.apply_fee + requestBody: + description: Apply fee to account + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + required: true + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + '400': + description: Invalid input + '422': + description: Validation exception + /Manager/Clients: + get: + tags: + - manager + summary: Get all clients + description: Get all clients + operationId: manager.get_all_clients + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Client' + '400': + description: Invalid input + '404': + description: No clients found + /Manager/Accounts: + get: + tags: + - manager + summary: Get all accounts + description: Get all accounts + operationId: manager.get_all_accounts + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + '400': + description: Invalid input + '404': + description: No accounts found + /Manager/Transactions: + get: + tags: + - manager + summary: Get all transactions + description: Get all transactions + operationId: manager.get_all_transactions + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Transaction' + '400': + description: Invalid input + '404': + description: No transactions found + put: + tags: + - manager + summary: Update an existing transaction + description: Update an existing transaction + operationId: manager.update_transaction + requestBody: + description: Update an existing transaction + content: + application/json: + schema: + $ref: '#/components/schemas/Transaction' + required: true + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Transaction' + '400': + description: Invalid Transaction ID supplied + '404': + description: Transaction not found + '422': + description: Validation exception + /Manager/Hash: + get: + tags: + - manager + summary: Hash password + description: Pass a string through the hashing algorithm + operationId: manager.password_hash + parameters: + - name: password + in: query + description: Password to hash + required: true + schema: + type: string + responses: + '200': + description: Successful operation + content: + application/json: + schema: + type: string + '400': + description: Invalid input + '401': + description: Unauthorized + /Manager/Timestamp: + get: + tags: + - manager + summary: Get the timestamp + description: Gets the date and time in the appropriate format + operationId: manager.timestamp + responses: + '200': + description: Successful operation + content: + application/json: + schema: + type: string + '400': + description: Invalid input + '401': + description: Unauthorized +components: + schemas: + Client: + type: object + properties: + client_id: + type: integer + format: int32 + name: + type: string + birthdate: + type: string + opening_timestamp: + type: string + address: + type: string + phone_number: + type: string + email: + type: string + password: + type: string + notes: + type: string + enabled: + type: boolean + administator: + type: boolean + accounts: + type: array + example: + client_id: 1 + name: "Lucas Mathews" + birthdate: "21-05-1980" + opening_timestamp: "17-04-2022 16:21:12" + address: "Rachelsmolen 1, 5612MA, Eindhoven" + phone_number: "0612345678" + email: "john.d@fontys.nl" + password: "password" + notes: "This is a test client" + enabled: true + administator: false + accounts: [] + Account: + type: object + properties: + account_id: + type: integer + format: int32 + decription: + type: string + opening_timestamp: + type: string + account_type: + type: string + balance: + type: number + enabled: + type: boolean + notes: + type: string + transactons: + type: array + example: + account_id: 1 + description: "Savings Account" + opening_timestamp: "17-04-2022 16:21:12" + account_type: "Rachelsmolen 1, 5612MA, Eindhoven" + balance: 2314.23 + enabled: true + notes: "This is a savings account" + transactions: [] + Transaction: + type: object + properties: + transaction_id: + type: integer + format: int32 + transaction_type: + type: string + amount: + type: integer + format: int32 + timestamp: + type: string + description: + type: string + account_number: + type: string + recipient_account_number: + type: string + example: + transaction_id: 1 + transaction_type: "Deposit" + amount: 100.00 + timestamp: "17-04-2022 16:21:12" + description: "Deposit to Savings Account" + account_number: "NL12ABNA0123456789" + recipient_account_number: "NL12ABNA1234567890" + diff --git a/bank.db b/bank.db new file mode 100644 index 0000000000000000000000000000000000000000..9d458d41b97a93f61956a5b99b0b411609eed1b8 GIT binary patch literal 36864 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 Bg_r;U literal 0 HcmV?d00001 diff --git a/bank.ini b/bank.ini new file mode 100644 index 0000000..bb1cf16 --- /dev/null +++ b/bank.ini @@ -0,0 +1,20 @@ +[database] +name=bank.db +add_sample_data=True + +[api_file] +name=api.yml + +[server] +listen_ip=127.0.0.1 +port=81 +debug=True + +[frontend] +listen_ip=127.0.0.1 +port=80 +debug=True + +[api] +url=http://127.0.0.1:81/ + diff --git a/class_account.py b/class_account.py new file mode 100644 index 0000000..0347dfc --- /dev/null +++ b/class_account.py @@ -0,0 +1,31 @@ +# Lucas Mathews - Fontys Student ID: 5023572 +# Banking System Account Class + +from sqlalchemy import ForeignKey, Column, String, Integer, Boolean + + +from class_base import Base + +class Account(Base): + __tablename__ = 'accounts' + account_id = Column("account_id", String, primary_key=True) + description = Column("description", String) + open_timestamp = Column("open_timestamp", String) + account_type = Column("account_type", String) + balance = Column("balance", Integer) + enabled = Column("enabled", Boolean) + notes = Column("notes", String) + transactions = ("transactions", String, ForeignKey("transactions.transaction_id")) + + def __init__(self, account_id, description, open_timestamp, account_type, balance, enabled, notes, transactions): + self.account_id = account_id + self.description = description + self.open_timestamp = open_timestamp + self.account_type = account_type + self.balance = balance + self.enabled = enabled + self.notes = notes + self.transactions = transactions + + def __repr__(self): + return f"Account ID: {self.account_id}, Description: {self.description}, Open Timestamp: {self.open_timestamp}, Account Type: {self.account_type}, Balance: {self.balance}, Enabled: {self.enabled}, Notes: {self.notes}, Transactions: {self.transactions}" \ No newline at end of file diff --git a/class_base.py b/class_base.py new file mode 100644 index 0000000..0439352 --- /dev/null +++ b/class_base.py @@ -0,0 +1,6 @@ +# Lucas Mathews - Fontys Student ID: 5023572 +# Banking System Base Class + +from sqlalchemy.orm import declarative_base + +Base = declarative_base() \ No newline at end of file diff --git a/class_client.py b/class_client.py new file mode 100644 index 0000000..691fe43 --- /dev/null +++ b/class_client.py @@ -0,0 +1,38 @@ +# Lucas Mathews - Fontys Student ID: 5023572 +# Banking System Client Class + +from sqlalchemy import Column, String, Boolean + +from class_base import Base + +class Client(Base): + __tablename__ = 'clients' + client_id = Column("client_id", String, primary_key=True) + name = Column("name", String) + birthdate = Column("birthdate", String) + opening_timestamp = Column("opening_timestamp", String) + address = Column("address", String) + phone_number = Column("phone_number", String) + email = Column("email", String) + hash = Column("hash", String) + notes = Column("notes", String) + enabled = Column("enabled", Boolean) + administrator = Column("administrator", Boolean) + accounts = Column("accounts", String) + + def __init__(self, client_id, name, birthdate, opening_timestamp, address, phone_number, email, hash, notes, enabled, administrator, accounts): + self.client_id = client_id + self.name = name + self.birthdate = birthdate + self.opening_timestamp = opening_timestamp + self.address = address + self.phone_number = phone_number + self.email = email + self.hash = hash + self.notes = notes + self.enabled = enabled + self.administrator = administrator + self.accounts = accounts + + def __repr__(self): + return f"Client ID: {self.client_id}, Name: {self.name}, Birthdate: {self.birthdate}, Address: {self.address}, Phone Number: {self.phone_number}, Email: {self.email}, Enabled: {self.enabled}, Accounts: {self.accounts}" \ No newline at end of file diff --git a/class_transaction.py b/class_transaction.py new file mode 100644 index 0000000..f548778 --- /dev/null +++ b/class_transaction.py @@ -0,0 +1,28 @@ +# Lucas Mathews - Fontys Student ID: 5023572 +# Banking System Transaction Class + +from sqlalchemy import Column, String, Integer + +from class_base import Base + +class Transaction(Base): + __tablename__ = 'transactions' + transaction_id = Column("transaction_id", String, primary_key=True) + transaction_type = Column("transaction_type", String) + amount = Column("amount", Integer) + timestamp = Column("timestamp", String) + description = Column("description", String) + account_number = Column("account_number", Integer) + recipient_account_number = Column("recipient_account_number", Integer) + + def __init__(self, transaction_id, transaction_type, amount, timestamp, description, account_number, recipient_account_number = None): + self.transaction_id = transaction_id + self.transaction_type = transaction_type + self.amount = amount + self.timestamp = timestamp + self.description = description + self.account_number = account_number + self.recipient_account_number = recipient_account_number + + def __repr__(self): + return f"Transaction ID: {self.transaction_id}, Transaction Type: {self.transaction_type}, Amount: {self.amount}, Timestamp: {self.timestamp}, Description: {self.description} From Account Number: {self.account_number}, Recipient Account Number: {self.recipient_account_number}" \ No newline at end of file diff --git a/cli.py b/cli.py new file mode 100644 index 0000000..2c3411b --- /dev/null +++ b/cli.py @@ -0,0 +1,19 @@ +# Lucas Mathews - Fontys Student ID: 5023572 +# Banking System CLI Utility + +import requests + +SERVER_URL = "http://127.0.0.1:5000" + +def main(): + username = "john" + password = "doe" + print(f"Login with {username} and {password}:") + response = requests.get( f"{SERVER_URL}/login?username={username}&password={password}") + print(f"{response}, {response.content}") + + print(f"Logout:") + response = requests.get( f"{SERVER_URL}/logout") + print(f"{response}, {response.content}") + print(f"Closing") +main() \ No newline at end of file diff --git a/config.py b/config.py new file mode 100644 index 0000000..bcdb1a6 --- /dev/null +++ b/config.py @@ -0,0 +1,7 @@ +# Lucas Mathews - Fontys Student ID: 5023572 +# Banking System Config Parser + +import configparser + +CONFIG = configparser.ConfigParser() +CONFIG.read("bank.ini") diff --git a/database.py b/database.py new file mode 100644 index 0000000..2661569 --- /dev/null +++ b/database.py @@ -0,0 +1,58 @@ +# Lucas Mathews - Fontys Student ID: 5023572 +# Banking System Manager File + +import os.path + +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker + +#Import Config +from config import CONFIG + +# Check if the database exists +if os.path.exists(CONFIG["database"]["name"]): + print(f"Database {CONFIG["database"]["name"]} already exists.") +else: + print(f"Database {CONFIG["database"]["name"]} does not exist. Creating it now.") + +# Sets the database file to be used from the configuration file +db_url : str = "sqlite:///" + CONFIG["database"]["name"] +print(f"Database file set to: {db_url}") + +# Creates the database engine (does not create the database file if it already exists) +engine = create_engine(db_url, echo=True) + +#Import Classes +from class_base import Base # Imports the base class required by SQLAlchemy +from class_client import Client +from class_account import Account +from class_transaction import Transaction + +# Create the tables in the database +Base.metadata.create_all(bind=engine) # Creates the tables in the database from the classes + +# Creates a session to interact with the database +Session = sessionmaker(bind=engine) # Creates a session to interact with the database +session = Session() # Creates a session object + +# Add sample data if enabled in the configuration file if the database is empty +if CONFIG["database"]["add_sample_data"] == "True": # Checks if sample data addition is enabled + if session.query(Client).count() == 0: # Checks if the database is empty + print(f"Sample data addition is disabled because the database is not empty.") + print(f"Adding sample data to new database file {CONFIG["database"]["name"]}.") + session.add(Client("f9a16945-b570-4153-ba63-413f2cc2768a", "Lucas Mathews", "24/08/1998", "17/04/2024", "Rachelsmolen 1, 5612MA, Eindhoven", "0612345678", "522499@student.fontys.nl", "7835062ec36ed529fe22cc63baf3ec18d347dacb21c9801da8ba0848cc18efdf1e51717dd5b1240f7556aca3947aa0722452858be6002c1d46b1f1c311b0e9d8", "Notes", True, True, "1, 2")) + session.add(Client("5be2a74d-d55c-4de6-85a1-2ed6a355f2cd", "Rigby", "16/03/2018", "06/05/2024", "Rachelsmolen 1, 5612MA, Eindhoven", "0612345678", "522499@cat.fontys.nl", "d3e7df3c78682fbb51e8c6110b3926349bb426bc9834c640cd666519901aef3dfab7822d66fb2dd9e39eb5a8492f6801c2e17ba4c16b8fbcd159c036fe27d8bd", "Is a cat", True, False, "3")) + session.add(Account("4c227b02-348c-4611-99a2-8967680cdee6", "Savings Account", "17/04/2024", "Savings", 3000, True, "Savings account", "1")) + session.add(Account("b9d9b801-eaab-45be-a4d1-1f7b0bbf798f", "Spending Account", "17/04/2024", "Spending", 150, True, "Spending account", "1")) + session.add(Account("f182f186-0a88-4f98-8a02-3a568c65aba7", "Cat Account", "06/05/2024", "Cat Account", 497, True, "Food savings", "2")) + session.add(Transaction("9d989788-f983-4590-8de2-9aa0c8bec7d2", "Deposit", 5000, "17/04/2024", "Initial Deposit", 1, "23542335")) + session.add(Transaction("153cee93-51c7-4114-b9ef-e307fbf0bf87", "Deposit", 100, "17/04/2024", "Initial Deposit", 2, "23542335")) + session.add(Transaction("4bec761a-0f36-452f-a48a-127dcf526e47", "Deposit", 500, "06/05/2024", "Initial Deposit", 3, "23542335")) + session.add(Transaction("227a2a9e-a13b-484b-b037-78deeeb0258c", "Withdrawal", 2000, "06/05/2024", "Uni Fees", 3, "Fontys University")) + session.add(Transaction("7248a706-29a8-478b-a674-c12ebf9a904a", "Withdrawal", 50, "06/05/2024", "Groceries", 3, "Aldi")) + session.add(Transaction("ba367c28-41e6-4f8a-9bfa-3819f7b89a58", "Withdrawal", 3, "06/05/2024", "Treats", 3, "ZooPlus")) + session.commit() + else: + print(f"The database is not empty, skipping sample data addition.") +else: + print(f"Sample data addition is disabled.") \ No newline at end of file diff --git a/manager.py b/manager.py new file mode 100644 index 0000000..7ca7f79 --- /dev/null +++ b/manager.py @@ -0,0 +1,249 @@ +# Lucas Mathews - Fontys Student ID: 5023572 +# Banking System Manager File + +from class_client import Client +from class_account import Account +from class_transaction import Transaction +from flask import jsonify +import hashlib # hashlib for password hashing +import datetime # datetime for timestamps +import uuid # uuid for unique identifiers + + +from database import * # Importing the database connection + +############## +### System ### +############## + +def timestamp(): # Returns the current timestamp + return (datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + +def password_hash(password:str): # Converts a string to SHA512 hash + return hashlib.sha512(password.encode()).hexdigest() + +def generate_uuid(): # Generates a unique identifier for transactions + return str(uuid.uuid4()) + +def generate_uuid_short(): # Generates a short uuid + return str(uuid.uuid4())[:8] + +############## +### Client ### +############## + +def get_client(client_id:int): # Returns a specific client in the database + client = session.query(Client).filter_by(client_id=client_id).one_or_none() + if client is None: + return jsonify({"error": "Client not found"}), 404 + if client is not None: + return jsonify({"name": client.name, "birthdate": client.birthdate, "opening_timestamp": client.opening_timestamp, "address": client.address, "phone_number": client.phone_number, "email": client.email}), 200 + +def change_password(client_id, password:str, new_password:str): # Changes the password of a client + old_hash = password_hash(password) + new_hash = password_hash(new_password) + for client in session.query(Client).all(): + if client.client_id == client_id: + if client.hash == old_hash: + client.hash = new_hash + session.commit() + return "Password changed successfully.", 200 + return "Incorrect old password.", 400 + return f"client_id: {client_id} is not found.", 404 + +def add_client(name:str, birthdate:str, address:str, phone_number:str, email:str, password:str, **kwargs): # Adds a new client to the database + client_id = generate_uuid_short() + notes = kwargs.get("notes", None) + new_client = Client(client_id, name, birthdate, timestamp(), address, phone_number, email, password_hash(password), notes, 1, 0, None) + session.add(new_client) + session.commit() + return f"New client has been added: name: {name}, uuid: {client_id} ", 200 + +def delete_client(client_id): # Deletes a client from the database + for client in session.query(Client).all(): + if client.client_id == client_id: + if client.accounts == None: + session.delete(client) + session.commit() + return f"client_id: {client_id} has been removed.", 200 + else: + return f"client_id: {client_id} has active accounts and can not be removed.", 400 + return f"client_id: {client_id} is not found.", 404 + + + + + +def login_user(email:str, password:str): + for client in session.query(Client).all(): + if client.email == email and client.password == password: + return f"Welcome {client.name}." + return "Invalid email or password." + +def logout_user(): + return "You have been logged out." + + + +def update_client(client_id, name, birthdate, address, phone_number, email, notes): + for client in session.query(Client).all(): + if client.client_id == client_id: + client.name = name + client.birthdate = birthdate + client.address = address + client.phone_number = phone_number + client.email = email + client.notes = notes + session.commit() + return f"client_id: {client_id} has been updated." + return f"Client ID: {client_id} is not found." + + + +############### +### Account ### +############### + +def get_account(account_id:int): # Returns a specific account in the database + account = session.query(Account).filter_by(account_id=account_id).one_or_none() + if account is None: + return jsonify({"error": "Account not found"}), 404 + if account is not None: + for account in account: + return jsonify({"description": account.description, "account_type": account.account_type, "balance": account.balance, "enabled": account.enabled, "notes": account.notes}), 200 + +def add_account(description:str, account_type, **kwargs): # Adds a new account to the database + account_id = generate_uuid_short() + notes = kwargs.get("notes", None) + new_account = Account(account_id, description, timestamp(), account_type, 0, 1, notes, None) + session.add(new_account) + session.commit() + return f"New account has been added: description: {description}, uuid: {account_id} ", 200 + +def delete_account(account_id): # Deletes an account from the database + for account in session.query(Account).all(): + if account.account_id == account_id: + if account.balance == 0: + session.delete(account) + session.commit() + return f"account_id: {account_id} has been removed.", 200 + else: + return f"account_id: {account_id} has a balance and can not be removed.", 400 + return f"account_id: {account_id} is not found.", 404 + + + + +def update_account(account_id:int, update:dict): + for account in session.query(Account).all(): + if account.account_id == account_id: + account.description = update["description"] + account.account_type = update["account_type"] + account.balance = update["balance"] + account.enabled = update["enabled"] + account.notes = update["notes"] + session.commit() + return f"account_id: {update['account_id']} has been updated." + return f"account_id: {update['account_id']} is not found." + + + + +################### +### Transaction ### +################### + +def get_transaction(transaction_id:int): # Returns a specific transaction in the database + transaction = session.query(Transaction).filter_by(transaction_id=transaction_id).one_or_none() + if transaction is None: + return jsonify({"error": "Transaction not found"}), 404 + if transaction is not None: + return jsonify({"transaction_type": transaction.transaction_type, "amount": transaction.amount, "timestamp": transaction.timestamp, "description": transaction.description, "account_number": transaction.account_number, "recipient_account_number": transaction.recipient_account_number}), 200 + +def transaction_history(account_id:int): # Returns all transactions for a specific account + result = session.query(Transaction).filter(Transaction.account_number == account_id) + return jsonify([{"transaction_id": transaction.transaction_id, "transaction_type": transaction.transaction_type, "amount": transaction.amount, "timestamp": transaction.timestamp, "description": transaction.description, "account_number": transaction.account_number, "recipient_account_number": transaction.recipient_account_number} for transaction in result]), 200 + +def add_transaction(amount:int, account_from, account_to, **kwargs): # Adds a new transaction to the database + transaction_id = generate_uuid() + for account in session.query(Account).all(): + if account.account_id == account_from: + if account.balance < amount: + return f"Account ID: {account_from} does not have enough funds to transfer {amount}.", 401 + account.balance -= amount + transaction_type = "withdraw" + session.commit() + if account.account_id == account_to: + account.balance += amount + transaction_type = "transfer" + session.commit() + description = kwargs.get("description", None) + new_transaction = Transaction(transaction_id, transaction_type, amount, timestamp(), description, account_from, account_to) + session.add(new_transaction) + session.commit() + return f"New transaction has been added: description: {description}, uuid: {transaction_id} ", 200 + + + +##################### +### Administrator ### +##################### + +def get_all_clients(): # Returns all clients in the database + clients = session.query(Client).all() + return jsonify([{"client_id": client.client_id, "name": client.name, "birthdate": client.birthdate, "opening_timestamp": client.opening_timestamp, "address": client.address, "phone_number": client.phone_number, "email": client.email} for client in clients]) + +def get_all_accounts(): # Returns all accounts in the database + accounts = session.query(Account).all() + return jsonify([{"account_id": account.account_id, "description": account.description, "open_timestamp": account.open_timestamp, "account_type": account.account_type, "balance": account.balance, "enabled": account.enabled, "notes": account.notes} for account in accounts]) + +def get_all_transactions(): # Returns all transactions in the database + transactions = session.query(Transaction).all() + return jsonify([{"transaction_id": transaction.transaction_id, "transaction_type": transaction.transaction_type, "amount": transaction.amount, "timestamp": transaction.timestamp, "description": transaction.description, "account_number": transaction.account_number, "recipient_account_number": transaction.recipient_account_number} for transaction in transactions]) + + + +def update_transaction(transaction_id, transaction_type, amount, description, account_number, recipient_account_number): + for transaction in session.query(Transaction).all(): + if transaction.transaction_id == transaction_id: + transaction.transaction_type = transaction_type + transaction.amount = amount + transaction.description = description + transaction.account_number = account_number + transaction.recipient_account_number = recipient_account_number + session.commit() + return f"Transaction ID: {transaction_id} has been updated." + return f"Transaction ID: {transaction_id} is not found." + +def apply_interest(account_id:int, interest_rate:float): + for account in session.query(Account).filter(Account.account_id == account_id): + if account.account_id == account_id: + account.balance += account.balance * interest_rate + session.commit() + return f"Interest has been applied to Account ID: {account_id}." + return f"Account ID: {account_id} is not found." + +def apply_fee(account_id:int, fee:float): + for account in session.query(Account).all(): + if account.account_id == account_id: + account.balance -= fee + session.commit() + return f"Fee has been applied to Account ID: {account_id}." + return f"Account ID: {account_id} is not found." + +def delete_transaction(transaction_id:int): + DELETE_TRANSACTION = "DELETE FROM transaction WHERE transaction_id=?" + from api import session, Transaction + for transaction in session.query(Transaction).all(): + if transaction.transaction_id == transaction_id: + input(f"Are you sure you would like permanenty delete transaction ID: {transaction_id}? WARNING: This action can not be reversed. (Y/N) ") + if input == "Y"or input == "y": + session.execute(DELETE_TRANSACTION, (transaction_id)) + print(f"Transaction ID: {transaction_id} has been removed.") + else: + return f"Transaction ID: {transaction_id} has NOT been removed." + return + return f"Transaction ID: {transaction_id} is not found." + + + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f5cd4df --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +flask +connexion[swagger-ui]==2.14.2 +requests +sqlalchemy \ No newline at end of file