示例#1
0
    def __init__(self, config: Optional[str] = None):
        create_db()
        fill_db()

        # parse config if exists
        try:
            _config_path = Path(config)
            with open(_config_path) as file:
                _config = yaml.safe_load(file)["config"]
        except TypeError:
            LOGGER.INFO("No config file specified. Falling back to default")
            _config = {}
        except FileNotFoundError:
            LOGGER.INFO("Config file not found. Falling back to default")
            _config = {}

        self.name = _config.get("name", "BankLite")  # defaults to BankLite
        self.currency = _config.get("currency", "USD")  # defaults to USD
        self.initial_reserve = _config.get("initial_reserve", 1e9)  # defaults to 1 billion

        self.conn = sqlite3.connect("banklite.db", check_same_thread=False)

        # main services
        self.ledger_service = LedgerService(conn=self.conn)
        self.account_service = AccountService(conn=self.conn)
        self.customer_service = CustomerService(conn=self.conn)

        # create reserve account
        self.reserve_account = self.account_service.create_reserve_account()

        # fill reserve with initial deposit
        self.ledger_service.record_entry(
            account=self.reserve_account, amount=self.initial_reserve
        )
示例#2
0
    def list_accounts(
            self, customer_id: Union[str,
                                     int]) -> Union[List[BaseAccount], None]:
        """

        :param customer_id: ID used for identifying a customer account
        :return:
        """
        LOGGER.INFO("SearchCustomerAccounts(customer_id=%s)", customer_id)
        try:
            data = self.conn.execute(
                f"SELECT * FROM accounts WHERE customer_id = {customer_id}"
            ).fetchall()
        except IndexError:
            LOGGER.DEBUG("CustomerNotFound(id=%s)", customer_id)
            return None

        ls = []
        for row in data:
            acct_type = row[3]
            if acct_type == 1:
                ls.append(CheckingAccount(row[0], row[1]))
            else:
                ls.append(SavingsAccount(row[0], row[1], row[-1]))

        return ls
示例#3
0
    def open_account(
        self,
        acct_type: str,
        customer_id: str,
        deposit_amount: float,
        savings_rate: Optional[float] = 0.0,
    ) -> Union[BaseAccount, None]:
        """
        Factory object creator for account objects

        :param acct_type: Either `checking` or `savings`
        :param customer_id: ID of customer opening account
        :param deposit_amount: Amount to deposit (must be positive value)
        :param savings_rate: If the account is a savings account, a savings
                             rate must be passed in
        :return: Account object
        """
        if acct_type == "checking":
            account = self.customer_service.open_checking_account(customer_id, deposit_amount)
        elif acct_type == "savings":
            account = self.customer_service.open_savings_account(
                customer_id, deposit_amount, savings_rate
            )
        else:
            LOGGER.DEBUG("Invalid account type: %s", acct_type)
            return None

        return account
示例#4
0
    def create_commercial_customer(self, customer_zip: int, phone_number: str,
                                   email: str, ein: str,
                                   name: str) -> CommercialCustomer:
        """

        :param customer_zip: Customer's zip code of operation
        :param phone_number: Customer's main office or financial office number
        :param email: Customer's main office of financial office email
        :param ein: Customer's EIN
        :param name: Customer's operating name
        """
        customer = CommercialCustomer(create_id(), customer_zip, phone_number,
                                      email, ein, name)

        if self.customer_exists(customer):
            return self.get_customer_by_id(customer.customer_id)

        LOGGER.INFO("CustomerCreation(customer_id=%s)", customer.customer_id)
        self.conn.execute(f"""
            INSERT INTO customers
            VALUES ({customer.customer_id}, DATETIME('now'), '{customer.ein}',
            NULL, '{customer.first_name}', NULL, '{customer.zip}',
            '{customer.phone}', '{customer.email}')
            """)
        self.conn.commit()
        return customer
示例#5
0
    def create_retail_customer(
        self,
        customer_zip: int,
        phone_number: str,
        email: str,
        ssn: str,
        first_name: str,
        last_name: str,
    ) -> RetailCustomer:
        """
        Creates a customer entry according to information passed in

        :param customer_zip: Zip code associated with customer
        :param phone_number: Phone number associated with customer
        :param email: Email associated with customer
        :param ssn: Social security number for customer
        :param first_name: Customer's first name
        :param last_name: Customer's last name
        """
        customer = RetailCustomer(create_id(), customer_zip, phone_number,
                                  email, ssn, first_name, last_name)

        if self.customer_exists(customer):
            return self.get_customer_by_id(customer.customer_id)

        LOGGER.INFO("CustomerCreation(customer_id=%s)", customer.customer_id)
        self.conn.execute(f"""
            INSERT INTO customers
            VALUES ({customer.customer_id}, DATETIME('now'), NULL, '{customer.ssn}',
            '{customer.first_name}', '{customer.last_name}', '{customer.zip}',
            '{customer.phone}', '{customer.email}')
            """)
        self.conn.commit()
        return customer
示例#6
0
    def get_reserve_balance(self) -> float:
        """

        :return: The total amount available in the reserve
        """
        reserve_balance = self.conn.execute(
            "SELECT SUM(amount) FROM ledger").fetchone()
        LOGGER.INFO("GetReserveBalance(%f)", reserve_balance[0])
        return reserve_balance[0]
示例#7
0
def create_db(db_file: Optional[str] = "banklite.db") -> None:
    """
    Create database and tables for banklite system

    :param db_file: Name of file (defaults to "banklite.db"
    """
    # remove db files if not already cleaned up
    if _db_exists():
        db_files = list(Path.cwd().rglob("*.db"))
        for file in db_files:
            LOGGER.DEBUG("FileDeleted(%s)", file.name)
            file.unlink()

    # create database
    with sqlite3.connect(db_file) as conn:
        for model in ALL_MODELS:
            LOGGER.INFO("TableCreated(%s)", model.table_name)
            conn.execute(_create_table(model))
示例#8
0
 def customer_exists(self, customer: BaseCustomer):
     cust_len = len(
         self.conn.execute(
             f"SELECT * FROM customers WHERE id={customer.customer_id}").
         fetchall())
     exists = True if cust_len != 0 else False
     if exists:
         LOGGER.DEBUG("CustomerAlreadyExists(account_id=%s)",
                      customer.customer_id)
     return exists
示例#9
0
def fill_db(db_file: Optional[str] = "banklite.db") -> None:
    """
    Insert values into tables on initiation of banking system

    :param db_file: Name of file (defaults to "banklite.db"
    """
    # create database
    with sqlite3.connect(db_file) as conn:
        for model in ALL_MODELS:
            if model.inserts:
                LOGGER.INFO("TableFilled(%s)", model.table_name)
                conn.execute(_insert_to_table(model))
示例#10
0
    def get_accounts_by_id(
        self, acct_ids: List[str]
    ) -> List[Union[ReserveAccount, CheckingAccount, SavingsAccount]]:
        """
        Return an account object and associated data based on the accounts ID

        :param acct_ids: List of IDs for accounts to return
        :return: Account object associated with ID
        """
        for acct_id in acct_ids:
            LOGGER.INFO("SearchAccount(id=%s)", acct_id)

        ls = []

        data = self.conn.execute(f"""
                SELECT *
                FROM accounts
                WHERE id in (
                    {','.join([acct_id for acct_id in acct_ids])}
                )
            """).fetchall()

        if len(data) > 0:
            for acct in data:
                acct_type = acct[3]
                if acct_type == 0:
                    ls.append(ReserveAccount())
                elif acct_type == 1:
                    ls.append(CheckingAccount(acct[0], acct[1]))
                else:
                    ls.append(SavingsAccount(acct[0], acct[1], acct[-1]))
        else:
            for acct_id in acct_ids:
                LOGGER.DEBUG("AccountNotFound(id=%s)", acct_id)

        for acct_id in acct_ids:
            if acct_id not in [acct.account_id for acct in ls]:
                LOGGER.DEBUG("AccountNotFound(id=%s)", acct_id)

        return ls
示例#11
0
    def get_account_by_id(
        self, acct_id: str
    ) -> Union[ReserveAccount, CheckingAccount, SavingsAccount, None]:
        """
        Return an account object and associated data based on the accounts ID

        :param acct_id: ID of account to return
        :return: Account object associated with ID
        """
        LOGGER.INFO("SearchAccount(id=%s)", acct_id)
        try:
            data = self.conn.execute(
                f"SELECT * FROM accounts WHERE id={acct_id}").fetchall()[0]
            acct_type = data[3]
            if acct_type == 0:
                return ReserveAccount()
            elif acct_type == 1:
                return CheckingAccount(data[0], data[1])
            else:
                return SavingsAccount(data[0], data[1], data[-1])
        except IndexError:
            LOGGER.DEBUG("AccountNotFound(id=%s)", acct_id)
            return None
示例#12
0
    def record_entry(self, account: BaseAccount, amount: float) -> None:
        """
        Record entry in ledger table

        :param account: Account associated with ledger entry
        :param amount: Amount to record in ledger
        """
        tx_id = create_id()
        LOGGER.INFO("LedgerEntry(%s, %s)", account.account_id, str(amount))
        self.conn.execute(f"""
            INSERT INTO ledger
            VALUES ({tx_id}, {account.account_id}, DATETIME('now'), {amount})
            """)
        self.conn.commit()
示例#13
0
    def get_customer_by_id(
        self, customer_id: Union[str, int]
    ) -> Union[RetailCustomer, CommercialCustomer, None]:
        """

        :param customer_id: ID used for identifying a customer account
        :return: Customer object
        """
        LOGGER.INFO("SearchCustomer(customer_id=%s)", customer_id)
        try:
            data = self.conn.execute(
                f"SELECT * FROM customers WHERE id = {customer_id}").fetchall(
                )[0]
            LOGGER.INFO("CustomerFound(customer_id=%s)", data[0])
            if data[2] is None:
                return RetailCustomer(customer_id, data[6], data[7], data[8],
                                      data[3], data[4], data[5])
            elif data[2] is not None and data[3] is None:
                return CommercialCustomer(customer_id, data[6], data[7],
                                          data[8], data[2], data[4])
        except IndexError:
            LOGGER.DEBUG("CustomerNotFound(id=%s)", customer_id)
            return None
示例#14
0
    def account_exists(self, acct: BaseAccount) -> bool:
        """
        Checks if the account passed in exists

        :param acct: Account to lookup
        :return: Boolean indication of if the account already exists or not
        """
        acct_len = len(
            self.conn.execute(
                f"SELECT * FROM accounts WHERE id={acct.account_id}").fetchall(
                ))
        exists = True if acct_len != 0 else False
        if exists:
            LOGGER.DEBUG("AccountExists(account_id=%s)", acct.account_id)
        return exists
示例#15
0
    def create_savings_account(self, customer: BaseCustomer,
                               rate: float) -> SavingsAccount:
        acct_id = create_id()
        acct = SavingsAccount(acct_id, customer.customer_id, rate)

        if self.account_exists(acct):
            return self.get_account_by_id(acct_id=acct_id)

        LOGGER.INFO("AccountCreation(account_id=%s)", acct.account_id)
        self.conn.execute(f"""
            INSERT INTO accounts
            VALUES(
                {acct.account_id}, {acct.customer_id}, DATETIME('NOW'), {acct.type}, {acct.rate}
            )
            """)
        self.conn.commit()
        return acct
示例#16
0
    def create_reserve_account(self) -> ReserveAccount:
        """
        Creates a record for the special reserve account

        """
        acct = ReserveAccount()

        if self.account_exists(acct):
            return self.get_account_by_id(acct_id="1")

        LOGGER.INFO("AccountCreation(account_id=%s)", acct.account_id)
        self.conn.execute(f"""
            INSERT INTO accounts
            VALUES({acct.account_id}, NULL, DATETIME('NOW'), {acct.type}, {acct.rate})
            """)
        self.conn.commit()
        return acct
示例#17
0
    def create_new_customer(
        self, customer_type: str, **kwargs
    ) -> Union[BaseCustomer, RetailCustomer, CommercialCustomer, None]:
        """
        Factory object creator for customer objects

        :param customer_type: Either `retail` or `commercial`
        :return: Customer object
        """
        if customer_type == "retail":
            customer = self.customer_service.create_retail_customer(**kwargs)
        elif customer_type == "commercial":
            customer = self.customer_service.create_commercial_customer(**kwargs)
        else:
            LOGGER.DEBUG("Invalid customer type: %s", customer_type)
            return None

        return customer
示例#18
0
    def create_checking_account(self,
                                customer: BaseCustomer) -> CheckingAccount:
        """
        Creates a record for a new checking account associated
        with the customer passed in

        :param customer: Customer associated with new account
        """

        acct_id = create_id()
        acct = CheckingAccount(acct_id, customer.customer_id)

        if self.account_exists(acct):
            return self.get_account_by_id(acct_id=acct_id)

        LOGGER.INFO("AccountCreation(account_id=%s)", acct.account_id)
        self.conn.execute(f"""
            INSERT INTO accounts
            VALUES(
                {acct.account_id}, {acct.customer_id}, DATETIME('NOW'), {acct.type}, {acct.rate}
            )
            """)
        self.conn.commit()
        return acct