├── src
├── __init__.py
└── long_banking_system
│ ├── __init__.py
│ ├── interface.py
│ ├── admin.py
│ ├── main.py
│ ├── admins_data.json
│ ├── consts.py
│ ├── models.py
│ ├── messages.py
│ ├── sorts.py
│ ├── transactions.py
│ ├── utils.py
│ ├── account.py
│ └── services.py
├── requirements.txt
├── pyproject.toml
├── .github
└── workflows
│ └── ci.yml
├── LICENSE
├── .gitignore
├── README.md
└── tests
└── test_utils.py
/src/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/long_banking_system/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | bcrypt==3.2.2
2 | maskpass==0.3.6
--------------------------------------------------------------------------------
/src/long_banking_system/interface.py:
--------------------------------------------------------------------------------
1 | import os
2 | import time
3 |
4 |
5 | def clean_terminal_screen():
6 | """
7 | Cleans the terminal screen by performing a system
8 | clear command. Cls on windows and Clear on UNIX ones.
9 | """
10 |
11 | os.system("cls" if os.name == "nt" else "clear")
12 | time.sleep(0.2)
13 |
14 | def display_horizontal_line():
15 | """
16 | A pretty decorative horizontal line.
17 | """
18 |
19 | print("───────────────────────────────────────────────────────────────")
20 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "long_banking_system"
3 | version = "0.0.1"
4 | authors = [
5 | { name="Long_Chau_Do", email="dolong2110@gmail.com" },
6 | ]
7 | description = "Simple Online Banking System"
8 | readme = "README.md"
9 | requires-python = ">=3.8"
10 | classifiers = [
11 | "Programming Language :: Python :: 3",
12 | "License :: OSI Approved :: MIT License",
13 | "Operating System :: OS Independent",
14 | ]
15 |
16 | [project.urls]
17 | Homepage = "https://github.com/dolong2110/long-banking-system"
18 | Issues = "https://github.com/dolong2110/long-banking-system/issues"
19 |
--------------------------------------------------------------------------------
/src/long_banking_system/admin.py:
--------------------------------------------------------------------------------
1 | import account
2 | import messages
3 | import models
4 | import services
5 |
6 | if __name__ == '__main__':
7 | admin_data, user_index = account.authentication("admins")
8 | feedbacks_messages = None
9 | if admin_data and user_index != -1:
10 | feedbacks_messages = messages.MessageQueue()
11 |
12 | users = models.Users()
13 | while admin_data:
14 | admin_data, feedbacks_messages = services.admins_services(admin_data, users, user_index, feedbacks_messages)
15 |
16 | feedbacks_messages.update()
17 |
18 | print("Goodbye, see you soon!!!")
--------------------------------------------------------------------------------
/src/long_banking_system/main.py:
--------------------------------------------------------------------------------
1 | import account
2 | import messages
3 | import services
4 |
5 | if __name__ == '__main__':
6 | users_data, user_index = account.authentication("users")
7 | feedbacks_messages = None
8 | if users_data and user_index != -1:
9 | print("Hello world")
10 | feedbacks_messages = messages.MessageQueue()
11 |
12 | while users_data:
13 | users_data, feedbacks_messages = services.users_services(users_data, user_index, feedbacks_messages)
14 |
15 | if feedbacks_messages:
16 | feedbacks_messages.update()
17 |
18 | print("Thank you so much for using Long bank's app, see you soon!!!")
19 |
--------------------------------------------------------------------------------
/src/long_banking_system/admins_data.json:
--------------------------------------------------------------------------------
1 | [{"first_name": "Long", "middle_name": "Chau", "last_name": "Do", "gender": "male", "date_of_birth": "21/10/1998", "phone_number": "0975672596", "email": "dolong2110@gmail.com", "password": "$2b$12$V3VEajXIAbGMCqUk5KO/guCob6cmLpD6JH/NK/96uZlpNHw07ptsa", "balance": "0", "account_number": "40109", "issued_date": "05/08/2022"}, {"first_name": "Long", "middle_name": "Chau", "last_name": "Do", "gender": "male", "date_of_birth": "21/10/1998", "phone_number": "0975672596", "email": "dolong2110@gmail.com", "password": "$2b$12$m7x1YYZ3xmTcuDDDLVdc0OI20nZtGTIKeMJEzfY0spBHN3eg0YpBa", "balance": "0", "account_number": "46390", "issued_date": "05/08/2022"}, {"first_name": "Long", "middle_name": "Chau", "last_name": "Do", "gender": "male", "date_of_birth": "21/10/1998", "phone_number": "0975672596", "email": "dolong2110@gmail.com", "password": "$2b$12$1wuoISX4t2u4yvyINpDPYOEi6NR0RkBkbUz28XpFuNJW4GPoTo4Du", "balance": "0", "account_number": "66484", "issued_date": "05/08/2022"}]
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: Build and Test
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 |
8 | jobs:
9 | continuous-integration:
10 | name: Continuous-Integration
11 | runs-on: ubuntu-latest
12 | strategy:
13 | fail-fast: false
14 | steps:
15 | - uses: actions/checkout@v3
16 | - uses: actions/setup-python@v4
17 | with:
18 | python-version: 3.x
19 | - uses: actions/cache@v3
20 | with:
21 | path: ~/.cache/pip
22 | key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
23 | - name: Install dependencies
24 | run: |
25 | python -m pip install --upgrade pip setuptools six wheel
26 | python -m pip install pytest-cov -r requirements.txt
27 | - name: Run tests
28 | run: pytest --doctest-modules --ignore=project_euler/ --ignore=scripts/validate_solutions.py --cov-report=term-missing:skip-covered --cov=. .
29 | - if: ${{ success() }}
30 | run: scripts/build_directory_md.py 2>&1 | tee DIRECTORY.md
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Chau Long Do
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/long_banking_system/consts.py:
--------------------------------------------------------------------------------
1 | BANK_PREFIX = "21101998"
2 | ACCOUNT_CONFIGS = {"users": {"file_name": "users_data.json", "account_number_len": 10},
3 | "admins": {"file_name": "admins_data.json", "account_number_len": 5}}
4 | MESSAGES_DATA_PATH = "messages_data.json"
5 | FAILED_ATTEMPT = 5
6 | AUTHENTICATION_CHOICES = {"1", "2", "3"}
7 | USER_SERVICES_CHOICES = {"1", "2", "3", "4", "5", "6", "7"}
8 | ADMIN_SERVICES_CHOICES = {"1", "2", "3", "4", "5", "6", "7", "8"}
9 | USER_UPDATE_INFORMATION_CHOICES = {"1", "2", "3", "4", "5", "6", "7", "8"}
10 | USER_TRANSACTION_CHOICES = {"1", "2", "3", "4"}
11 | YES_NO_CHOICES = {"1", "2"}
12 | GENDER_SET_CHOICE = {"1": "male", "2": "female", "3": "others"}
13 | SALT_LEN = 12
14 | FIRST_NAME_MAX_LEN = 256
15 | MIDDLE_NAME_MAX_LEN = 128
16 | LAST_NAME_MAX_LEN = 128
17 | PHONE_NUMBER_LEN = 10
18 | MIN_PASSWORD_LEN = 8
19 | MAX_PASSWORD_LEN = 100
20 | MESSAGE_MAX_CHARACTERS = 1000
21 | MESSAGE_MAX_WORDS = 150
22 |
23 |
24 |
25 |
26 | # repeated strings
27 | FILE_NAME = "file_name"
28 | ACCOUNT_NUMBER_LEN = "account_number_len"
29 | FIRST_NAME = "first_name"
30 | MIDDLE_NAME = "middle_name"
31 | LAST_NAME = "last_name"
32 | GENDER = "gender"
33 | DATE_OF_BIRTH = "date_of_birth"
34 | PHONE_NUMBER = "phone_number"
35 | EMAIL = "email"
36 | PASSWORD = "password"
37 | BALANCE = "balance"
38 | ACCOUNT_NUMBER = "account_number"
39 | ISSUED_DATE = "issued_date"
40 | MESSAGE = "message"
41 | TIMESTAMP = "timestamp"
42 | TIME = "time"
43 |
44 | # ADMIN
45 | FIELDS_SEARCH = {"1": ACCOUNT_NUMBER, "2": BALANCE, "3": FIRST_NAME}
--------------------------------------------------------------------------------
/src/long_banking_system/models.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | import random
3 | from collections import defaultdict
4 |
5 |
6 | import sorts
7 | from consts import *
8 | import utils
9 |
10 |
11 | class Users:
12 |
13 | def __init__(self, privilege: str = "users"):
14 |
15 | self.privilege = privilege
16 | self.configs = ACCOUNT_CONFIGS[privilege]
17 | raw_data = utils.get_data_from_json(self.configs[FILE_NAME])
18 | sorting = sorts.Sorting(raw_data, ACCOUNT_NUMBER, "")
19 | self.data = sorting.arr if sorting.arr else []
20 | self.users_set = set([account[ACCOUNT_NUMBER] for account in self.data])
21 |
22 | def create_new(self, user_information: defaultdict[str]) -> int:
23 | """
24 | Create new user with the given information
25 | """
26 |
27 | new_account_number = self.generate_account_number(self.configs[ACCOUNT_NUMBER_LEN])
28 | while new_account_number in self.users_set:
29 | new_account_number = self.generate_account_number(self.configs[ACCOUNT_NUMBER_LEN])
30 |
31 | today_date = datetime.date.today().strftime("%d/%m/%Y")
32 | user_information[ACCOUNT_NUMBER] = new_account_number
33 | user_information[ISSUED_DATE] = today_date
34 | self.data.append(user_information)
35 | self.users_set.add(new_account_number)
36 | utils.write_data_to_json(self.data, self.configs[FILE_NAME])
37 |
38 | return len(self.data) - 1
39 |
40 |
41 | def delete_user(self, index: int) -> None:
42 | """
43 | Delete user that delete it from database and current data structure
44 | """
45 |
46 | account_number = self.data[index][ACCOUNT_NUMBER]
47 | self.data = self.data[:index] + self.data[index + 1:]
48 | self.users_set.remove(account_number)
49 | utils.write_data_to_json(self.data, self.configs[FILE_NAME])
50 |
51 |
52 | @staticmethod
53 | def generate_account_number(account_len: int):
54 | """
55 | Generates a new unique account number
56 | It includes first 8 numbers of the bank and 8 random numbers
57 | """
58 |
59 | new_number = []
60 | for _ in range(account_len):
61 | new_number.append(str(random.randint(0, 9)))
62 |
63 | return ''.join(new_number)
64 |
65 |
66 | def update_information(self, index: int, field: str, change: str):
67 | """
68 | Changes the information of user.
69 | """
70 |
71 | self.data[index][field] = change
72 | utils.write_data_to_json(self.data, self.configs[FILE_NAME])
73 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .env
106 | .venv
107 | env/
108 | venv/
109 | ENV/
110 | env.bak/
111 | venv.bak/
112 |
113 | # Spyder project settings
114 | .spyderproject
115 | .spyproject
116 |
117 | # Rope project settings
118 | .ropeproject
119 |
120 | # mkdocs documentation
121 | /site
122 |
123 | # mypy
124 | .mypy_cache/
125 | .dmypy.json
126 | dmypy.json
127 |
128 | # Pyre type checker
129 | .pyre/
130 |
131 | # folders, files in project
132 | .idea
133 | /bank-system-env
134 | users_data.json
135 | messages_data.json
136 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
🏦 Long Banking System 🏦
2 | using Python 🐍
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | ## 📃 Table of Contents:
12 | - [About Project](#-about-project)
13 | - [System Design](#-system-design)
14 | - [Screenshot](#-screenshot)
15 | - [Install](#-install)
16 |
17 |
18 |
19 | ## 🖋 About Project:
20 | 👉 An online banking system built using Python. This system allows the customer - user privilege, and banking staff/manager - admin privilege, to manage all transactions easily.
21 | 👉 It performs basic services of an online banking system: authentication, transaction, etc.
22 | 👉 It practices with many algorithm and data structures: queue, double linked-list, various sorting algorithms.
23 | 👉 As mentioned above, there are 2 users for this system:
24 | 1. **Admin**:
25 | - Can **do basic authentication** such as login, create, delete
26 | - Can **view and search customer profile**
27 | - Can **read customers' feedback**
28 | 2. **User**:
29 | - Can **do basic authentication** such as login, create, delete
30 | - Can **do the transactions**
31 | - Can **send the feedbacks**
32 |
33 | > Note: **Every user needs to create account and sign in to access the system. All the data must be stored in json (.json) files.**
34 |
35 | [](#-table-of-contents)
36 |
37 |
38 | ## 💻 System Design:
39 | 👉 In this online banking system, there are 11 different menus, namely:
40 | 1. **Main Menu**
41 | 2. **Admin**
42 | - Admin Login
43 | - Admin Menu
44 | - Admin New Profile
45 | - Admin Search Customer Data
46 | - Admin Search Customer Transaction
47 | 3. **Customer**
48 | - Customer Login
49 | - Customer Menu
50 | - Customer Deposit
51 | - Customer Withdraw
52 | - Customer View
53 |
54 | [](#-table-of-contents)
55 |
56 |
57 | ## 📷 Screenshot:
58 |
59 |
60 | [](#-table-of-contents)
61 |
62 |
63 | ## Install:
64 | - First clone the repo
65 |
66 | ```
67 | git clone https://github.com/dolong2110/long-banking-system.git
68 | ```
69 |
70 | - Install pip
71 |
72 | ```
73 | py -m pip install --upgrade pip
74 | py -m pip --version
75 | ```
76 |
77 | - Install virtualenv
78 |
79 | ```
80 | py -m pip install --user virtualenv
81 | ```
82 |
83 | - Move current directory to the project directory
84 |
85 | ```
86 | cd ..\long-banking-system
87 | ```
88 |
89 | - Create a virtual environment
90 |
91 | ```
92 | py -m venv bank-system-env
93 | ```
94 |
95 | - Activating the virtual environment
96 |
97 | ```
98 | .\env\Scripts\activate
99 | ```
100 |
101 | - Install all dependencies
102 |
103 | ```
104 | pip install -r requirements.txt
105 | ```
106 |
107 | - Run as an user
108 |
109 | ```
110 | py main.py
111 | ```
112 |
113 | - Run as an admin
114 |
115 | ```
116 | py admin.py
117 | ```
118 |
119 |
120 | [](#-table-of-contents)
121 |
122 |
123 | ## 🙌 Support me!
124 |
125 | 👉 If you find this project useful, **please ⭐ this repository 😆**!
126 |
127 | [](#-table-of-contents)
128 |
--------------------------------------------------------------------------------
/tests/test_utils.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | from src.long_banking_system.utils import *
4 |
5 | class Test(TestCase):
6 |
7 | # def test_greeting(self):
8 | # self.fail()
9 | #
10 | #
11 | # def test_generate_hashed_password(self):
12 | # self.fail()
13 | #
14 | #
15 | # def test_check_password(self):
16 | # self.fail()
17 |
18 |
19 | def test_is_valid_name(self):
20 | self.countTestCases()
21 | self.assertTrue(is_valid_name("long", 4))
22 | self.assertTrue(is_valid_name("도주용", 4))
23 | self.assertFalse(is_valid_name("long", 3)) # exceed length
24 | self.assertFalse(is_valid_name("long%", 6)) # contain special character
25 | self.assertFalse(is_valid_name("long#", 6)) # contain special character
26 | self.assertFalse(is_valid_name("""lon"g""", 6)) # contain special character
27 | self.assertFalse(is_valid_name("!long", 6)) # contain special character
28 | self.assertFalse(is_valid_name("long'", 6)) # contain special character
29 | self.assertFalse(is_valid_name("long1", 6)) # contain number
30 |
31 |
32 | # def test_get_temporal(self):
33 | # self.fail()
34 | #
35 | #
36 |
37 |
38 | def test_is_valid_day(self):
39 | self.assertTrue(is_valid_day([26, 10, 2003], [4, 8, 2022]))
40 | self.assertTrue(is_valid_day([18, 8, 1988], [4, 8, 2022]))
41 | self.assertFalse(is_valid_day([30, 2, 2018], [4, 8, 2022])) # invalid day in that month
42 | self.assertFalse(is_valid_day([31, 9, 2019], [4, 8, 2022])) # invalid day in that month
43 | self.assertFalse(is_valid_day([0, 12, 2010], [4, 8, 2022])) # invalid day in that month
44 | self.assertFalse(is_valid_day([40, 5, 1967], [4, 8, 2022])) # invalid day in that month
45 | self.assertFalse(is_valid_day([11, 15, 2008], [4, 8, 2022])) # invalid month
46 | self.assertFalse(is_valid_day([3, 30, 2004], [4, 8, 2022])) # invalid month
47 | self.assertFalse(is_valid_day([8, 0, 2009], [4, 8, 2022])) # invalid month
48 |
49 |
50 | # def test_is_valid_phone_number(self):
51 | # self.fail()
52 | #
53 | #
54 | # def test_is_valid_email(self):
55 | # self.fail()
56 | #
57 |
58 |
59 | def test_is_valid_password(self):
60 | self.assertTrue(is_valid_password("21101998Abc."))
61 | self.assertTrue(is_valid_password("26102003@Abcd"))
62 | self.assertTrue(is_valid_password("aA^U@UgVVE~An%eMxnzN#SAJ^^hG=p3CRuT875B`o6=%c36n*@=JW7c9mH~_SPPa%9"
63 | "nQAAvnLAvfPCjThG`QhtsG#gS=Wymkg#~q"))
64 | self.assertFalse(is_valid_password("21101998Abc")) # not have special characters
65 | self.assertFalse(is_valid_password("21101998abc.")) # not have upper case letters
66 | self.assertFalse(is_valid_password("26102003_abcd")) # not have upper case letters
67 | self.assertFalse(is_valid_password("abcd1234")) # not have special characters and upper case letters
68 | self.assertFalse(is_valid_password("dochautuan@gmail.com")) # not have upper letters
69 | self.assertFalse(is_valid_password("OPTIMIZATION@1234")) # not have lower letters
70 | self.assertFalse(is_valid_password("tuandz")) # not have enough characters
71 | self.assertFalse(is_valid_password("21101998Abc. ")) # not have space character
72 | self.assertFalse(is_valid_password("_D3ns3J+c/3rD=FJ/$#/-hh~y4svT$8Kuner-aF&KD23ZXLrL#w-%aVKwc'fiz5kM7i="
73 | "FRaHFyYB*6/aw8-rTda$RtWwxbUnwq`+tuan")) # having more than 100 characters
74 |
75 |
76 | # def test_is_valid_account_number(self):
77 | # self.fail()
78 | #
79 | #
80 | # def test_get_yes_no_choice(self):
81 | # self.fail()
82 | #
83 | #
84 | # def test_is_valid_balance(self):
85 | # self.fail()
86 | #
87 | #
88 | # def test_is_valid_message(self):
89 | # self.fail()
90 | #
91 | #
92 | # def test_proceed_next(self):
93 | # self.fail()
94 |
--------------------------------------------------------------------------------
/src/long_banking_system/messages.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 | from collections import defaultdict
3 | from typing import Optional
4 |
5 | from consts import *
6 | import utils
7 |
8 |
9 | class Node:
10 |
11 | def __init__(self, data: defaultdict[str], next = None, prev = None):
12 | self.data = data
13 | self.next = next
14 | self.prev = prev
15 |
16 |
17 | class MessageQueue:
18 |
19 | def __init__(self):
20 | head_data, tail_data = defaultdict(str), defaultdict(str)
21 | head_data["dummy"], tail_data["dummy"] = "head", "tail"
22 | self.head, self.tail = Node(head_data), Node(tail_data)
23 | self.head.next, self.tail.prev = self.tail, self.head
24 | self.size = 0
25 | self.make_queue()
26 |
27 | def make_queue(self):
28 | for data in utils.get_data_from_json(MESSAGES_DATA_PATH):
29 | node = Node(data)
30 | self._add(node)
31 |
32 | def get_front(self) -> Optional[Node]:
33 | if not self.size:
34 | return None
35 |
36 | return self.head.next
37 |
38 | def get_last(self) -> Optional[Node]:
39 | if not self.size:
40 | return None
41 |
42 | return self.tail.prev
43 |
44 | def enqueue(self, data: defaultdict[str]) -> None:
45 | self._add(Node(data))
46 |
47 | def dequeue(self) -> None:
48 | if not self.size:
49 | return None
50 |
51 | self._remove(self.head.next)
52 |
53 | def update(self) -> None:
54 | data_list = []
55 | # data_list = [data for data in utils.get_data_from_json(MESSAGES_DATA_PATH)]
56 | cur = self.head.next
57 | while cur != self.tail:
58 | data_list.append(cur.data)
59 | cur = cur.next
60 |
61 | utils.write_data_to_json(data_list, MESSAGES_DATA_PATH)
62 |
63 | def _add(self, node: Node) -> None:
64 | prev_node = self.tail.prev
65 | node.next, node.prev = self.tail, prev_node
66 | self.tail.prev, prev_node.next = node, node
67 | self.size += 1
68 |
69 | def _remove(self, node: Node):
70 | prev, nxt = node.prev, node.next
71 | prev.next, nxt.prev = nxt, prev
72 | self.size -= 1
73 |
74 |
75 |
76 | def add_message(messages: MessageQueue, account_number: str) -> MessageQueue:
77 | print("We are very appreciated to hear from your thinking, your satisfied, dis-satisfied, "
78 | "and the idea to help us improve!!!")
79 | print(f"Your message should be less than {MESSAGE_MAX_CHARACTERS} characters and "
80 | f"less than {MESSAGE_MAX_WORDS} words")
81 | failed_attempt = FAILED_ATTEMPT
82 | message = ""
83 | while failed_attempt:
84 | message = input("Please give a message here: ")
85 | if not utils.is_valid_message(message):
86 | failed_attempt -= 1
87 | print("Your message is too long, please try a shorter one!!!")
88 | print("You have %d try left!!!" % failed_attempt)
89 | else:
90 | break
91 |
92 | if not failed_attempt:
93 | print("You send invalid message many times, please wait a few minutes to try it again!!!")
94 | return messages
95 |
96 | time_now = datetime.now()
97 | timestamp = datetime.timestamp(time_now)
98 | message_data = defaultdict(str)
99 | message_data[ACCOUNT_NUMBER] = account_number
100 | message_data[MESSAGE] = message
101 | message_data[TIMESTAMP] = str(timestamp)
102 | message_data[TIME] = str(time_now)
103 | print(message_data)
104 |
105 | messages.enqueue(message_data)
106 |
107 | return messages
108 |
109 | def read_message(messages: MessageQueue) -> Optional[MessageQueue]:
110 | print("Let's see the users' feedback to improve our application")
111 | if not messages.size:
112 | print("There are no messages to read")
113 | utils.proceed_next()
114 | return messages
115 |
116 | message_data = messages.get_front().data
117 |
118 | messages.dequeue()
119 | print(f"Messages from user: {message_data[ACCOUNT_NUMBER]}")
120 | utils.proceed_next()
121 | print(f"Time message sent: {message_data[TIME]}")
122 | utils.proceed_next()
123 | print(f"Message: {message_data[MESSAGE]}")
124 | utils.proceed_next()
125 | print(message_data)
126 | utils.proceed_next()
127 |
128 | return messages
--------------------------------------------------------------------------------
/src/long_banking_system/sorts.py:
--------------------------------------------------------------------------------
1 | from typing import List
2 |
3 |
4 | class Sorting:
5 |
6 | def __init__(self, arr, field: str, method: str = ""):
7 | self.arr = arr
8 | self.field = field
9 | self.len = len(arr)
10 |
11 | if self.len < 20 or method == "insertion_sort":
12 | self.insertion_sort()
13 | return
14 |
15 | if method == "bubble":
16 | self.bubble_sort()
17 | return
18 |
19 | if self.len > 10**7 or method == "counting_sort":
20 | self.counting_sort()
21 | return
22 |
23 | self.heap_sort()
24 |
25 | def selection_sort(self) -> None:
26 | """
27 | Mutates lst so that it is sorted via selecting the minimum element and
28 | swapping it with the corresponding index
29 | Time complexity: O(n^2); Space complexity: O(1)
30 | Naive and not stable sorting
31 | """
32 | for i in range(self.len):
33 | min_index = i
34 | for j in range(i + 1, self.len):
35 | # Update minimum index
36 | if self.arr[j][self.field] < self.arr[min_index][self.field]:
37 | min_index = j
38 |
39 | # Swap current index with minimum element in rest of list
40 | self.arr[min_index], self.arr[i] = self.arr[i], self.arr[min_index]
41 |
42 | def bubble_sort(self) -> None:
43 | """
44 | Mutates lst so that it is sorted via swapping adjacent elements until
45 | the entire lst is sorted.
46 | Time complexity: O(n^2); Space complexity: O(1)
47 | Naive and stable sorting
48 | """
49 | has_swapped = True
50 | # if no swap occurred, lst is sorted
51 | while has_swapped:
52 | has_swapped = False
53 | for i in range(self.len - 1):
54 | if self.arr[i][self.field] > self.arr[i + 1][self.field]:
55 | # Swap adjacent elements
56 | self.arr[i], self.arr[i + 1] = self.arr[i + 1], self.arr[i]
57 | has_swapped = True
58 |
59 | def insertion_sort(self) -> None:
60 | """
61 | Mutates elements in a lst by inserting out of place elements into appropriate
62 | index repeatedly until lst is sorted
63 | Time complexity: O(n^2); Space complexity: O(1)
64 | Useful for small size array or nearly sorted array
65 | """
66 |
67 | for i in range(1, self.len):
68 | current_index = i
69 |
70 | while current_index > 0 and self.arr[current_index][self.field] < self.arr[current_index - 1][self.field]:
71 | # Swap elements that are out of order
72 | self.arr[current_index], self.arr[current_index - 1] = \
73 | self.arr[current_index - 1], self.arr[current_index]
74 | current_index -= 1
75 |
76 | def heap_sort(self) -> None:
77 | """
78 | Mutates elements in lst by utilizing the heap data structure
79 | Time complexity: O(NlogN); Space complexity: O()
80 | A direct optimization of selection sort
81 | """
82 |
83 | # Time Complexity: O(logN)
84 | def max_heapify(heap_size, index):
85 | left, right = 2 * index + 1, 2 * index + 2
86 | largest = index
87 | if left < heap_size and self.arr[left][self.field] > self.arr[largest][self.field]:
88 | largest = left
89 | if right < heap_size and self.arr[right][self.field] > self.arr[largest][self.field]:
90 | largest = right
91 | if largest != index:
92 | self.arr[index][self.field], self.arr[largest][self.field] = \
93 | self.arr[largest][self.field], self.arr[index][self.field]
94 | max_heapify(heap_size, largest)
95 |
96 | # heapify original lst
97 | for i in range(self.len // 2 - 1, -1, -1):
98 | max_heapify(self.len, i)
99 |
100 | # Time Complexity: O(N)
101 | # use heap to sort elements
102 | for i in range(self.len - 1, 0, -1):
103 | # swap last element with first element
104 | self.arr[i], self.arr[0] = self.arr[0], self.arr[i]
105 | # note that we reduce the heap size by 1 every iteration
106 | max_heapify(i, 0)
107 |
108 |
109 | def counting_sort(self) -> None:
110 | """
111 | Sorts a list of integers where minimum value is 0 and maximum value is k
112 | Time complexity: O(N+k) - N is size of input, k is the maximum number in arr; Space complexity: O(N)
113 | better when sorting numbers - id, user_index
114 | """
115 | k = max(self.arr)
116 | counts = [0 for _ in range(k + 1)]
117 | for element in self.arr:
118 | counts[element] += 1
119 |
120 | # we now overwrite our original counts with the starting index
121 | # of each element in the final sorted array
122 |
123 | starting_index = 0
124 | for i, count in enumerate(counts):
125 | counts[i] = starting_index
126 | starting_index += count
127 |
128 | sorted_lst = [0 for _ in range(self.len)]
129 |
130 | for element in self.arr:
131 | sorted_lst[counts[element]] = element
132 | # since we have placed an item in index counts[element], we need to
133 | # increment counts[element] index by 1 so the next duplicate element
134 | # is placed in appropriate index
135 | counts[element] += 1
136 |
137 | # common practice to copy over sorted list into original lst
138 | # it's fine to just return the sorted_lst at this point as well
139 | for i in range(self.len):
140 | self.arr[i] = sorted_lst[i]
141 |
142 | def binary_search(arr: List[dict], field: str, query: str) -> int:
143 | """
144 | A custom binary search implementation that:
145 | (1) Assumes the input_list to have elements of type object
146 | and then sorts by a common key in all those objects name
147 | "field"
148 | (2) Make the text lowercase and trims the text in the fields
149 | so for example "foo bar" can match "FooBar"
150 | """
151 |
152 | low, high = 0, len(arr) - 1
153 | while low <= high:
154 | mid = (low + high) // 2
155 | if arr[mid][field] > query:
156 | high = mid - 1
157 | elif arr[mid][field] < query:
158 | low = mid + 1
159 | else:
160 | return mid
161 |
162 | return -1
163 |
--------------------------------------------------------------------------------
/src/long_banking_system/transactions.py:
--------------------------------------------------------------------------------
1 | import sorts
2 | from consts import *
3 | import models
4 | import utils
5 |
6 |
7 | def transaction_services(users: models.Users, user_index: int) -> models.Users:
8 |
9 | print("Please choose which privilege of transaction you want!!!")
10 | print(" ┌─────────────┐ ╭───────────────────────────╮ ")
11 | print(" │ │ │ ▶︎ 1 • Check Balance │ ")
12 | print(" │ │ ├───────────────────────────┴╮ ")
13 | print(" │ │ │ ▶︎ 2 • Transfer Money │ ")
14 | print(" │ L O N G │ ├───────────────────────────┬╯ ")
15 | print(" │ T U A N │ │ ▶︎ 3 • Deposit Money │ ")
16 | print(" │ B A N K │ ├───────────────────────────┴─╮ ")
17 | print(" │ │ │ ▶︎ 4 • Withdraw Money │ ")
18 | print(" │ │ ├──────────────────┬──────────╯ ")
19 | print(" │ │ │ ▶︎ 5 • Exit │ ")
20 | print(" └─────────────┘ ╰──────────────────╯ ")
21 |
22 | failed_attempt = FAILED_ATTEMPT
23 | user_choice = ""
24 | while failed_attempt:
25 | user_choice = input("☞ Enter your choice: ")
26 | if user_choice not in USER_TRANSACTION_CHOICES:
27 | failed_attempt -= 1
28 | print("Wrong choice!!! Please choose only from 1 to 4")
29 | print("You have %d try left!!!" % failed_attempt)
30 | else:
31 | break
32 |
33 | if not failed_attempt:
34 | print("You enter wrong choice many times, please wait few minutes to do it again")
35 | return users
36 |
37 | if user_choice == "1":
38 | print(f"Your current account balance is: {users.data[user_index][BALANCE]}")
39 | utils.proceed_next()
40 |
41 | if user_choice == "2":
42 | users = transfer_money(users, user_index)
43 |
44 | if user_choice == "3":
45 | users = deposit_money(users, user_index)
46 |
47 | if user_choice == "4":
48 | users = withdraw_money(users, user_index)
49 |
50 | if user_choice == "5":
51 | print("Finish action!!!")
52 | return users
53 |
54 | return users
55 |
56 | def transfer_money(users: models.Users, user_index: int) -> models.Users:
57 | return _transfer_internal(users, user_index)
58 |
59 | def deposit_money(users, user_index: int) -> models.Users:
60 | deposit = _get_money(float('inf'))
61 | if not deposit:
62 | print("Failed to deposit money into bank")
63 | return users
64 |
65 | print(f"Do you want to proceed to transfer {deposit} into your bank account?")
66 | user_choice = utils.get_yes_no_choice()
67 | if not user_choice:
68 | print("Finish action!!!")
69 | return users
70 |
71 | if user_choice == "2":
72 | print("Finish action!!!")
73 | return users
74 |
75 | user = users.data[user_index]
76 | current_balance = user[BALANCE]
77 | expect_balance = float(current_balance) + float(deposit)
78 | print(f"After depositing your balance will be: {expect_balance}")
79 |
80 | print("Do you confirm to deposit?")
81 | user_choice = utils.get_yes_no_choice()
82 | if not user_choice:
83 | print("Finish action!!!")
84 | return users
85 |
86 | if user_choice == "2":
87 | print("Finish action!!!")
88 | return users
89 |
90 | users.update_information(user_index, BALANCE, str(expect_balance))
91 | print("Successfully deposit the money!!!")
92 | print(f"Your balance now is: {expect_balance}")
93 |
94 | return users
95 |
96 | def withdraw_money(users: models.Users, user_index: int) -> models.Users:
97 | user = users.data[user_index]
98 | current_balance = float(user[BALANCE])
99 | withdraw = _get_money(current_balance)
100 | if not withdraw:
101 | print("Failed to withdraw money from bank")
102 | return users
103 |
104 | print(f"Do you want to proceed to withdraw {withdraw} from your bank account?")
105 | user_choice = utils.get_yes_no_choice()
106 | if not user_choice:
107 | print("Finish action!!!")
108 | return users
109 |
110 | if user_choice == "2":
111 | print("Finish action!!!")
112 | return users
113 |
114 | expect_balance = float(current_balance) - float(withdraw)
115 | print(f"After withdrawing your balance will be: {expect_balance}")
116 |
117 | print("Do you confirm to withdraw?")
118 | user_choice = utils.get_yes_no_choice()
119 | if not user_choice:
120 | print("Finish action!!!")
121 | return users
122 |
123 | if user_choice == "2":
124 | print("Finish action!!!")
125 | return users
126 |
127 | users.update_information(user_index, BALANCE, str(expect_balance))
128 | print("Successfully withdraw the money!!!")
129 | print(f"Your balance now is: {expect_balance}")
130 |
131 | return users
132 |
133 | def _transfer_internal(users: models.Users, user_index: int) -> models.Users:
134 | failed_attempt = FAILED_ATTEMPT
135 | receiver_account_number = ""
136 | account_number = users.data[user_index][ACCOUNT_NUMBER]
137 | while failed_attempt:
138 | receiver_account_number = input("☞ Enter the received account number: ")
139 | if not utils.is_valid_account_number(receiver_account_number, "users"):
140 | failed_attempt -= 1
141 | print("Invalid account number!!!")
142 | print("You have %d try left!!!" % failed_attempt)
143 | elif receiver_account_number == account_number:
144 | failed_attempt -= 1
145 | print("It is your own account, please try it again!!!")
146 | print("You have %d try left!!!" % failed_attempt)
147 | elif receiver_account_number not in users.users_set:
148 | failed_attempt -= 1
149 | print("Account does not exist!!!")
150 | print("You have %d try left!!!" % failed_attempt)
151 | else:
152 | break
153 |
154 | if not failed_attempt:
155 | print("You enter wrong account number many times, please wait few minutes to do it again")
156 | return users
157 |
158 | receiver_index = sorts.binary_search(users.data, ACCOUNT_NUMBER, receiver_account_number)
159 |
160 | receiver = users.data[receiver_index]
161 | receiver_balance = float(receiver[BALANCE])
162 | receiver_name = receiver[LAST_NAME] + " " + receiver[MIDDLE_NAME] + " " + receiver[FIRST_NAME]
163 | print(f"Account number: {receiver_account_number}")
164 | print(f"Account holder: {receiver_name}")
165 |
166 | print("☞ Do you want to proceed? if YES press 1 or 2 if NO")
167 | user_choice = utils.get_yes_no_choice()
168 | if not user_choice:
169 | print("Finish action!!!")
170 | return users
171 |
172 | if user_choice == "2":
173 | print("Finish action!!!")
174 | return users
175 |
176 | user = users.data[user_index]
177 | user_balance = float(user[BALANCE])
178 | transfer_money = _get_money(user_balance)
179 | if not transfer_money:
180 | print("Finish action!!!")
181 | return users
182 |
183 | user_name = user[LAST_NAME] + " " + user[MIDDLE_NAME] + " " + user[FIRST_NAME]
184 |
185 | print("Transaction information!!!")
186 | print(f"Sender: {user_name}")
187 | print(f"Received account number: {receiver_account_number}")
188 | print(f"Receiver: {receiver_name}")
189 | print(f"Amount transfer: {transfer_money}")
190 |
191 | print("Do you confirm?")
192 | user_choice = utils.get_yes_no_choice()
193 | if not user_choice:
194 | print("Finish action!!!")
195 | return users
196 |
197 | if user_choice == "2":
198 | print("Finish action!!!")
199 | return users
200 |
201 | receiver_balance += transfer_money
202 | user_balance -= transfer_money
203 |
204 | users.update_information(user_index, BALANCE, str(user_balance))
205 | users.update_information(receiver_index, BALANCE, str(receiver_balance))
206 |
207 | print("Successfully transfer the money!!!")
208 | print(f"Your balance now is: {user_balance}")
209 |
210 | return users
211 |
212 | def _get_money(balance: float) -> float:
213 | failed_attempt = FAILED_ATTEMPT
214 | money = 0
215 | while failed_attempt:
216 | money = input("Please choose the amount of money you want to transfer: ")
217 | if not utils.is_valid_balance(money):
218 | failed_attempt -= 1
219 | print("Invalid money, it must be a number!!!")
220 | print("You have %d try left!!!" % failed_attempt)
221 | elif float(money) > balance:
222 | failed_attempt -= 1
223 | print("Exceeds your current balance, try another one!!!")
224 | print("You have %d try left!!!" % failed_attempt)
225 | else:
226 | break
227 |
228 | if not failed_attempt:
229 | print("You failed to choose money amount many times, please wait few minutes to do it again")
230 | return 0
231 |
232 | return float(money)
--------------------------------------------------------------------------------
/src/long_banking_system/utils.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | import json
3 | import string
4 | from os.path import exists
5 |
6 | import bcrypt
7 | from typing import List
8 |
9 | from consts import *
10 |
11 |
12 | def greeting() -> str:
13 | """
14 | determine current time and choose appropriate greeting sentence
15 | """
16 |
17 | current_time = datetime.datetime.now().time().strftime("%H:%M:%S")
18 | hour_min_sec = current_time.split(':')
19 | hours = int(hour_min_sec[0])
20 |
21 | if 2 <= hours < 12:
22 | return "Good morning!!!"
23 | if 12 <= hours < 18:
24 | return "Good afternoon!!!"
25 | if 18 <= hours < 22:
26 | return "Good evening!!!"
27 | return "Hello!!!"
28 |
29 | def generate_hashed_password(plain_text_password: str) -> str:
30 | """
31 | encrypted password with random salt using bcrypt algorithms
32 | """
33 |
34 | return bcrypt.hashpw(str.encode(plain_text_password), bcrypt.gensalt(SALT_LEN)).decode()
35 |
36 | def check_password(plain_text_password: str, hashed_password: str) -> bool:
37 | """
38 | check password with password saved in DB
39 | """
40 |
41 | return bcrypt.checkpw(plain_text_password.encode(), hashed_password.encode())
42 |
43 | def is_valid_name(name: str, max_len: int) -> bool:
44 | if len(name) > max_len or len(name) == 0:
45 | return False
46 |
47 | set_character = set(name)
48 | for character in set_character:
49 | if character in string.punctuation or character.isnumeric():
50 | return False
51 | return True
52 |
53 | def get_temporal(temporal: str, max_time: int) -> str:
54 | failed_attempt = FAILED_ATTEMPT
55 | time = ""
56 | while failed_attempt:
57 | time = input("☞ Please enter the %s you was born: " % temporal)
58 | if not time.isnumeric():
59 | failed_attempt -= 1
60 | print("Invalid type of year, you should enter a valid positive integer number")
61 | print("You have %d try left!!!" % failed_attempt)
62 | elif int(time) > max_time:
63 | failed_attempt -= 1
64 | print("Invalid year, you cannot born after %d" % max_time)
65 | print("You have %d try left!!!" % failed_attempt)
66 | else:
67 | break
68 |
69 | if not failed_attempt:
70 | print("You enter invalid choice many times, please wait a few minutes to try it again!!!")
71 | time = ""
72 |
73 | result = [""]
74 | if temporal == "year":
75 | result = ["0" for _ in range(4)]
76 | i = 3
77 | for digit in time[::-1]:
78 | result[i] = digit
79 | i -= 1
80 | else:
81 | result = ["0" for _ in range(2)]
82 | i = 1
83 | for digit in time[::-1]:
84 | result[i] = digit
85 | i -= 1
86 |
87 | return "".join(result)
88 |
89 | def is_valid_day(date: List[int], current_date: List[int]) -> bool:
90 |
91 | day, month, year = date
92 | if month < 1 or month > 12:
93 | print("You enter invalid month")
94 | return False
95 |
96 | max_day_in_month = 0
97 | if month in {1, 3, 5, 7, 8, 10, 12}:
98 | max_day_in_month = 31
99 | elif month in {4, 6, 9, 11}:
100 | max_day_in_month = 30
101 | elif year % 4 == 0 or year % 100 == 0 or year % 400 == 0:
102 | max_day_in_month = 29
103 | else:
104 | max_day_in_month = 28
105 |
106 | if day < 1 or day > max_day_in_month:
107 | print("You enter invalid day in that month")
108 | return False
109 |
110 | # need 18 or higher to create
111 | if year > current_date[2] - 18:
112 | print("You need to be 18 years old to create a new account")
113 | return False
114 |
115 | if year < current_date[2] - 18:
116 | return True
117 |
118 | if month > current_date[1]:
119 | print("You need to be 18 years old to create a new account")
120 | return False
121 |
122 | if month < current_date[1]:
123 | return True
124 |
125 | if day < current_date[0]:
126 | print("You need to be 18 years old to create a new account")
127 | return False
128 |
129 | return True
130 |
131 | def is_valid_phone_number(phone_number: str) -> bool:
132 | if len(phone_number) != PHONE_NUMBER_LEN:
133 | print("Phone number should have length %d\n" % 10)
134 | return False
135 |
136 | if phone_number[0] != "0":
137 | print("Phone number should start with 0\n")
138 | return False
139 |
140 | for digit in phone_number:
141 | if not digit.isnumeric():
142 | print("%s is not a number\n" % digit)
143 | return False
144 |
145 | return True
146 |
147 | def is_valid_email(email: str) -> bool:
148 |
149 | return True
150 |
151 | def is_valid_password(password: str) -> bool:
152 | if len(password) < MIN_PASSWORD_LEN:
153 | print("Password's length should be at least 8 characters!!!")
154 | return False
155 |
156 | if len(password) > MAX_PASSWORD_LEN:
157 | print("Password's length should be smaller or equal than 100 characters!!!")
158 | return False
159 |
160 | have_number, have_lowercase, have_uppercase, have_specical_character = False, False, False, False
161 | for char in password:
162 | if char == " ":
163 | return False
164 | if char.isnumeric():
165 | have_number = True
166 | elif char.islower():
167 | have_lowercase = True
168 | elif char.isupper():
169 | have_uppercase = True
170 | elif char in string.punctuation:
171 | have_specical_character = True
172 |
173 | return have_number and have_lowercase and have_uppercase and have_specical_character
174 |
175 | def is_valid_account_number(account_number: str, privilege: str) -> bool:
176 | if len(account_number) != ACCOUNT_CONFIGS[privilege][ACCOUNT_NUMBER_LEN]:
177 | return False
178 |
179 | for digit in account_number:
180 | if not digit.isnumeric():
181 | return False
182 |
183 | return True
184 |
185 | def get_yes_no_choice() -> str:
186 | print(" ┌─────────────┐ ╭─────────────────╮ ")
187 | print(" │ L O N G │ │ ▶︎ 1 • YES │ ")
188 | print(" │ T U A N │ ├────────────────┬╯ ")
189 | print(" │ B A N K │ │ ▶︎ 2 • NO │ ")
190 | print(" └─────────────┘ ╰────────────────╯ ")
191 |
192 | failed_attempt = FAILED_ATTEMPT
193 | user_choice = ""
194 | while failed_attempt:
195 | user_choice = input("☞ Enter your choice: ")
196 | if user_choice not in YES_NO_CHOICES:
197 | failed_attempt -= 1
198 | print("Wrong choice!!! Please choose only 1 or 2")
199 | print("You have %d try left!!!" % failed_attempt)
200 | else:
201 | break
202 |
203 | if not failed_attempt:
204 | print("You enter wrong choice many times, please wait few minutes to do it again")
205 | return ""
206 |
207 | return user_choice
208 |
209 | def is_valid_balance(balance: str) -> bool:
210 | for digit in balance:
211 | if not digit.isnumeric() and digit != ".":
212 | return False
213 |
214 | return True
215 |
216 | def is_valid_message(message: str) -> bool:
217 | if len(message) > MESSAGE_MAX_CHARACTERS:
218 | print(f"Your message have more than {MESSAGE_MAX_CHARACTERS} characters")
219 | return False
220 |
221 | words = message.split(" ")
222 | if len(words) > MESSAGE_MAX_WORDS:
223 | print(f"Your message have more than {MESSAGE_MAX_WORDS} characters")
224 | return False
225 |
226 | return True
227 |
228 | def proceed_next() -> None:
229 | _ = input("Press anything if you want to proceed to the next")
230 |
231 | def get_data_from_json(filename: str):
232 | """
233 | Reads information from the data file
234 | """
235 |
236 | if not exists(filename):
237 | return {}
238 |
239 | file = open(filename, "r")
240 | data = file.read()
241 |
242 | # convert string json to python object
243 | return json.loads(data)
244 |
245 | def write_data_to_json(data, filename: str) -> None:
246 | """
247 | Writes the data into the file with filename
248 | """
249 |
250 | file = open(filename, "w")
251 | json_data = json.dumps(data)
252 | file.write(json_data)
253 |
254 |
255 | if __name__ == '__main__':
256 | print(greeting())
257 | # print(generate_hashed_password("longdeptrai"))
258 | # print(check_password("longdeptrai", "$2b$12$nNeZrual6HCn2KSu8OroyenyizjXqckFn8UtOl5X.zkSAxFVO6/JS"))
259 | # # print(check_password("longdeptraii", "$2b$12$nNeZrual6HCn2KSu8OroyenyizjXqckFn8UtOl5X.zkSAxFVO6/JS"))
260 | # print(is_valid_name("""a"a""", 3))
261 | # print(is_valid_name("!asds", 6))
262 | # print(is_valid_name("long", 100))
263 | # print(is_valid_name("asdkljafjlaskd", 100))
264 | # print(is_valid_name("asdkljafjlaskd", 5))
265 | # print(is_valid_name("1234asdvsxa", 1000))
266 | # print(get_temporal("month", 12))
267 | # print(is_valid_password("21101998Abc!"))
268 | # print(is_valid_phone_number("0123456789"))
269 | # print(is_valid_phone_number("012345678"))
270 | # print(is_valid_phone_number("1123456789"))
271 | # print(is_valid_phone_number("012345678a"))
--------------------------------------------------------------------------------
/src/long_banking_system/account.py:
--------------------------------------------------------------------------------
1 | import time
2 | from collections import defaultdict
3 | from typing import List, Optional
4 |
5 | import maskpass
6 |
7 | import sorts
8 | from consts import *
9 | import utils
10 | import interface
11 |
12 | import models
13 |
14 | def authentication(privilege: str) -> (Optional[models.Users], int):
15 | """
16 | login or create new banking account
17 | """
18 |
19 | interface.clean_terminal_screen()
20 |
21 | print(utils.greeting())
22 | print(" Welcome to Long's Bank\n")
23 | print("Please choose 1 if you already have an account or 2 if you want to create a new one")
24 | print(" ┌─────────────┐ ╭──────────────────╮ ")
25 | print(" │ │ │ ▶︎ 1 • Login │ ")
26 | print(" │ L O N G │ ├──────────────────┴────────────╮ ")
27 | print(" │ T U A N │ │ ▶︎ 2 • Create New Account │ ")
28 | print(" │ B A N K │ ├──────────────────┬────────────╯ ")
29 | print(" │ │ │ ▶︎ 3 • Exit │ ")
30 | print(" └─────────────┘ ╰──────────────────╯ ")
31 |
32 |
33 | failed_attempt = FAILED_ATTEMPT
34 | user_choice = ""
35 | while failed_attempt:
36 | user_choice = input("☞ Enter your choice: ")
37 | if user_choice not in AUTHENTICATION_CHOICES:
38 | failed_attempt -= 1
39 | print("Wrong choice!!! Please choose only 1 to 3")
40 | print("You have %d try left!!!" % failed_attempt)
41 | else:
42 | break
43 |
44 | if not failed_attempt:
45 | print("You enter wrong choice many times, please wait few minutes to do it again")
46 | return None, -1
47 |
48 | users = models.Users(privilege)
49 | user_index = -1
50 |
51 | if user_choice == "1":
52 | if not users.data:
53 | print("There are no account yet!!!")
54 | return None, -1
55 |
56 | print("You want to login your account")
57 | account_number = ""
58 | failed_attempt = FAILED_ATTEMPT
59 | while failed_attempt:
60 | account_number = input("☞ Please enter your bank account: ")
61 | if account_number not in users.users_set:
62 | failed_attempt -= 1
63 | print("Account does not exist!!! Please enter your own account")
64 | print("You have %d try left!!!" % failed_attempt)
65 | else:
66 | break
67 |
68 | if not failed_attempt:
69 | print("You enter wrong choice many times, please wait few minutes to login again")
70 | return None, -1
71 |
72 | user_index = sorts.binary_search(users.data, ACCOUNT_NUMBER, account_number)
73 | if user_index == -1:
74 | print("You enter wrong choice many times, please wait few minutes to login again")
75 | return None, -1
76 |
77 | user = users.data[user_index]
78 | print(utils.greeting())
79 | print(" %s" % account_number)
80 | failed_attempt = FAILED_ATTEMPT
81 | hashed_password = user[PASSWORD]
82 | while failed_attempt:
83 | password = maskpass.askpass(prompt="☞ Please enter your password: ")
84 | if not utils.check_password(password, hashed_password):
85 | failed_attempt -= 1
86 | print("Wrong password!!! Please try another one")
87 | print("You have %d try left!!!" % failed_attempt)
88 | else:
89 | break
90 |
91 | if not failed_attempt:
92 | print("You enter wrong password many times, please wait few minutes to login again")
93 | return None, -1
94 |
95 | print("Successfully login!!!")
96 | print("Welcome back %s!!!" % user[FIRST_NAME])
97 |
98 | if user_choice == "2":
99 | print("You want to create new bank account online")
100 | print("Please enter required information correctly, make sure all information you provide are true")
101 |
102 | first_name = get_name(FIRST_NAME_MAX_LEN, FAILED_ATTEMPT, "first")
103 | if not first_name:
104 | return None, -1
105 |
106 | # Some people may not have middle name
107 | middle_name = ""
108 | middle_name = get_name(MIDDLE_NAME_MAX_LEN, FAILED_ATTEMPT, "middle")
109 |
110 | last_name = get_name(LAST_NAME_MAX_LEN, FAILED_ATTEMPT, "last")
111 | if not last_name:
112 | return None, -1
113 |
114 | gender = get_gender(GENDER_SET_CHOICE)
115 | if not gender:
116 | return None, -1
117 |
118 | date_of_birth = get_date_of_birth()
119 | if not date_of_birth:
120 | return None, -1
121 |
122 | phone_number = get_phone_number()
123 | if not phone_number:
124 | return None, -1
125 |
126 | email = get_email()
127 |
128 | password = get_password(utils.generate_hashed_password(""))
129 | if not password:
130 | return None, -1
131 |
132 | user_information = defaultdict(str)
133 | user_information[FIRST_NAME] = first_name
134 | user_information[MIDDLE_NAME] = middle_name
135 | user_information[LAST_NAME] = last_name
136 | user_information[GENDER] = gender
137 | user_information[DATE_OF_BIRTH] = date_of_birth
138 | user_information[PHONE_NUMBER] = phone_number
139 | user_information[EMAIL] = email
140 | user_information[PASSWORD] = password
141 | user_information[BALANCE] = "0"
142 | user_index = users.create_new(user_information)
143 |
144 | print("Successfully create new account!!!")
145 |
146 | if user_choice == "3":
147 | return None, -1
148 |
149 | print("Move to the next step")
150 |
151 | return users, user_index
152 |
153 | def get_name(name_max_len: int, failed_attempt: int, kind: str) -> str:
154 | name = ""
155 | while failed_attempt:
156 | name = input("☞ Please enter your %s name: " % kind)
157 | if not utils.is_valid_name(name, name_max_len):
158 | failed_attempt -= 1
159 | print("Invalid %s name!!! Please try your real valid %s name" % (kind, kind))
160 | print("You have %d try left!!!" % failed_attempt)
161 | else:
162 | break
163 |
164 | if not failed_attempt:
165 | print("You enter invalid %s name many times, please wait few minutes to create new account again")
166 | name = ""
167 |
168 | return name
169 |
170 | def get_gender(gender_list: List[str]) -> str:
171 | print("☞ Please choose among options below")
172 | print(" ┌─────────────┐ ╭──────────────────╮ ")
173 | print(" │ │ │ ▶︎ 1 • Male │ ")
174 | print(" │ L O N G │ ├──────────────────┴─╮ ")
175 | print(" │ T U A N │ │ ▶︎ 2 • Female │ ")
176 | print(" │ B A N K │ ├────────────────────┴╮ ")
177 | print(" │ │ │ ▶︎ 3 • Others │ ")
178 | print(" └─────────────┘ ╰─────────────────────╯ ")
179 |
180 | failed_attempt = FAILED_ATTEMPT
181 | gender = ""
182 | while failed_attempt:
183 | gender = input("☞ Please enter your choice about your gender: ")
184 | if gender not in gender_list:
185 | failed_attempt -= 1
186 | print("Invalid choice please read carefully, choose 1 for male, 2 for female and 3 for others")
187 | print("You have %d try left!!!" % failed_attempt)
188 | else:
189 | break
190 | if not failed_attempt:
191 | print("You enter invalid choice many times, please wait a few minutes to try it again!!!")
192 | return ""
193 |
194 | if gender == "1":
195 | return "male"
196 |
197 | if gender == "2":
198 | return "female"
199 |
200 | if gender == "3":
201 | return "others"
202 |
203 | return ""
204 |
205 | def get_date_of_birth() -> str:
206 | print("☞ Please enter the year, month, day when you was born respectively!!!")
207 | current_time = time.strftime("%d:%m:%Y")
208 | current_time_list = current_time.split(":")
209 | current_day, current_month, current_year = current_time_list
210 |
211 | year = utils.get_temporal("year", int(current_year))
212 | if not year:
213 | return ""
214 |
215 | month = utils.get_temporal("month", 12)
216 | if not month:
217 | return ""
218 |
219 | day = utils.get_temporal("day", 31)
220 | if not day:
221 | return ""
222 |
223 | if not utils.is_valid_day([int(day), int(month), int(year)],
224 | [int(current_day), int(current_month), int(current_year)]):
225 | return ""
226 |
227 | return day + "/" + month + "/" + year
228 |
229 | def get_phone_number() -> str:
230 | failed_attempt = FAILED_ATTEMPT
231 | phone_number = ""
232 | while failed_attempt:
233 | phone_number = input("☞ Please enter your phone number: ")
234 | if not utils.is_valid_phone_number(phone_number):
235 | failed_attempt -= 1
236 | print("Invalid phone number, please, try it again!!!")
237 | print("You have %d try left!!!" % failed_attempt)
238 | else:
239 | break
240 | if not failed_attempt:
241 | print("You enter invalid choice many times, please wait a few minutes to try it again!!!")
242 | phone_number = ""
243 |
244 | return phone_number
245 |
246 | def get_email() -> str:
247 | print("☞ Do you want to add your email? if YES press 1 or 2 if NO")
248 |
249 | user_choice = utils.get_yes_no_choice()
250 | if not user_choice:
251 | return ""
252 |
253 | if user_choice == "2":
254 | return ""
255 |
256 | email = ""
257 | failed_attempt = FAILED_ATTEMPT
258 | while failed_attempt:
259 | email = input("☞ Please enter your email: ")
260 | if not utils.is_valid_email(email):
261 | failed_attempt -= 1
262 | print("Invalid email, please, try it again!!!")
263 | print("You have %d try left!!!" % failed_attempt)
264 | else:
265 | break
266 |
267 | if not failed_attempt:
268 | print("You enter invalid choice many times, please wait a few minutes to try it again!!!")
269 | email = ""
270 |
271 | return email
272 |
273 | def get_password(previous_password: str) -> str:
274 | password = ""
275 | failed_attempt = FAILED_ATTEMPT
276 | while failed_attempt:
277 | password = maskpass.askpass(prompt="☞ Please enter your password. It should be at least 8 characters, "
278 | "contain numbers, lowercase, uppercase letters, and special characters: ")
279 | if not utils.is_valid_password(password):
280 | failed_attempt -= 1
281 | print("Invalid password. Please, try it again!!!")
282 | print("You have %d try left!!!" % failed_attempt)
283 | elif utils.check_password(password, previous_password):
284 | failed_attempt -= 1
285 | print("You enter same password with your previous password")
286 | print("You have %d try left!!!" % failed_attempt)
287 | else:
288 | break
289 |
290 | if not failed_attempt:
291 | print("You enter invalid choice many times, please wait a few minutes to try it again!!!")
292 | password = ""
293 |
294 | failed_attempt = FAILED_ATTEMPT
295 | while failed_attempt:
296 | re_enter_password = maskpass.askpass(prompt="☞ Please re-enter your password: ")
297 | if password != re_enter_password:
298 | failed_attempt -= 1
299 | print("Different with your previous password. Please, try it again!!!")
300 | print("You have %d try left!!!" % failed_attempt)
301 | else:
302 | break
303 |
304 | if not failed_attempt:
305 | print("You enter invalid choice many times, please wait a few minutes to try it again!!!")
306 | password = ""
307 |
308 | return utils.generate_hashed_password(password)
309 |
--------------------------------------------------------------------------------
/src/long_banking_system/services.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | import account
4 | import sorts
5 | from consts import *
6 | import interface
7 | import messages
8 | import models
9 | import transactions
10 | import utils
11 |
12 |
13 | def users_services(users: models.Users, user_index: int, feedbacks_messages: messages.MessageQueue) -> \
14 | (Optional[models.Users], Optional[messages.MessageQueue]):
15 | interface.clean_terminal_screen()
16 | interface.display_horizontal_line()
17 |
18 | print("What do you want to do next?")
19 | print("☞ Please choose among options below")
20 | print(" ┌─────────────┐ ╭───────────────────────────╮ ")
21 | print(" │ ╭┼┼╮ │ │ ▶︎ 1 • Get Information │ ")
22 | print(" │ ╰┼┼╮ │ ├───────────────────────────┴────╮ ")
23 | print(" │ ╰┼┼╯ │ │ ▶︎ 2 • Update Information │ ")
24 | print(" │ │ ├──────────────────────────────┬─╯ ")
25 | print(" │ L O N G │ │ ▶︎ 3 • Change Password │ ")
26 | print(" │ T U A N │ ├────────────────────────────┬─╯ ")
27 | print(" │ B A N K │ │ ▶︎ 4 • Delete Account │ ")
28 | print(" │ │ ├────────────────────────────┴────╮ ")
29 | print(" │ │ │ ▶︎ 5 • Perform Transaction │ ")
30 | print(" │ │ ├───────────────────────┬─────────╯ ")
31 | print(" │ ║│┃┃║║│┃║│║ │ │ ▶︎ 6 • Feedbacks │ ")
32 | print(" │ ║│┃┃║║│┃║│║ │ ├───────────────────────┴─╮ ")
33 | print(" │ │ │ ▶︎ 7 • Exit System │ ")
34 | print(" └─────────────┘ ╰─────────────────────────╯ ")
35 |
36 | failed_attempt = FAILED_ATTEMPT
37 | user_choice = ""
38 | while failed_attempt:
39 | user_choice = input("☞ Enter your choice: ")
40 | if user_choice not in USER_SERVICES_CHOICES:
41 | failed_attempt -= 1
42 | print("Wrong choice!!! Please choose only from 1 to 7")
43 | print("You have %d try left!!!" % failed_attempt)
44 | else:
45 | break
46 |
47 | if not failed_attempt:
48 | print("You enter wrong choice many times, please wait few minutes to do it again")
49 | return None, feedbacks_messages
50 |
51 | if user_choice == "1":
52 | _display_user_information(users.data[user_index])
53 |
54 | if user_choice == "2":
55 | users = _update_information(users, user_index)
56 |
57 | if user_choice == "3":
58 | users = _update_password(users, user_index)
59 |
60 | if user_choice == "4":
61 | users.delete_user(user_index)
62 | return None, feedbacks_messages
63 |
64 | if user_choice == "5":
65 | users = transactions.transaction_services(users, user_index)
66 |
67 | if user_choice == "6":
68 | feedbacks_messages = messages.add_message(feedbacks_messages, users.data[user_index][ACCOUNT_NUMBER])
69 |
70 | if user_choice == "7":
71 | return None, feedbacks_messages
72 |
73 | return users, feedbacks_messages
74 |
75 | def admins_services(admins: models.Users, users: models.Users, user_index: int,
76 | feedbacks_messages: messages.MessageQueue) -> \
77 | (Optional[models.Users], Optional[messages.MessageQueue]):
78 | interface.clean_terminal_screen()
79 | interface.display_horizontal_line()
80 |
81 | print("What operation do you want to do?")
82 | print("☞ Please choose among admins' options below")
83 | print(" ┌─────────────┐ ╭───────────────────────────╮ ")
84 | print(" │ ╭┼┼╮ │ │ ▶︎ 1 • Get Information │ ")
85 | print(" │ ╰┼┼╮ │ ├───────────────────────────┴────╮ ")
86 | print(" │ ╰┼┼╯ │ │ ▶︎ 2 • Update Information │ ")
87 | print(" │ │ ├──────────────────────────────┬─╯ ")
88 | print(" │ L O N G │ │ ▶︎ 3 • Change Password │ ")
89 | print(" │ T U A N │ ├────────────────────────────┬─╯ ")
90 | print(" │ B A N K │ │ ▶︎ 4 • Delete Account │ ")
91 | print(" │ │ ├────────────────────────────┴───────╮ ")
92 | print(" │ │ │ ▶︎ 5 • Get User's Information │ ")
93 | print(" │ │ ├────────────────────────────────────┴─╮ ")
94 | print(" │ │ │ ▶︎ 6 • Get Information by Field │ ")
95 | print(" │ │ ├────────────────────────────┬─────────╯ ")
96 | print(" │ ║│┃┃║║│┃║│║ │ │ ▶︎ 7 • Read Feedbacks │ ")
97 | print(" │ ║│┃┃║║│┃║│║ │ ├─────────────────────────┬──╯ ")
98 | print(" │ │ │ ▶︎ 8 • Exit System │ ")
99 | print(" └─────────────┘ ╰─────────────────────────╯ ")
100 |
101 | failed_attempt = FAILED_ATTEMPT
102 | admin_choice = ""
103 | while failed_attempt:
104 | admin_choice = input("☞ Enter your choice: ")
105 | if admin_choice not in ADMIN_SERVICES_CHOICES:
106 | failed_attempt -= 1
107 | print("Wrong choice!!! Please choose only from 1 to 8")
108 | print("You have %d try left!!!" % failed_attempt)
109 | else:
110 | break
111 |
112 | if not failed_attempt:
113 | print("You enter wrong choice many times, please wait few minutes to do it again")
114 | return None, feedbacks_messages
115 |
116 | if admin_choice == "1":
117 | _display_user_information(admins.data[user_index])
118 |
119 | if admin_choice == "2":
120 | admins = _update_information(admins, user_index)
121 |
122 | if admin_choice == "3":
123 | users = _update_password(admins, user_index)
124 |
125 | if admin_choice == "4":
126 | users.delete_user(user_index)
127 | return None, feedbacks_messages
128 |
129 | if admin_choice == "5":
130 | account_number = _get_account_number(users)
131 | if not account_number:
132 | return users, feedbacks_messages
133 |
134 | user_index = sorts.binary_search(users.data, ACCOUNT_NUMBER, account_number)
135 | if user_index == -1:
136 | print("Users not exists, please try again!!!")
137 | return users, feedbacks_messages
138 |
139 | _display_user_information(users.data[user_index])
140 |
141 | if admin_choice == "6":
142 | _get_user_information_by_field(users)
143 |
144 | if admin_choice == "7":
145 | feedbacks_messages = messages.read_message(feedbacks_messages)
146 |
147 | if admin_choice == "8":
148 | return None, feedbacks_messages
149 |
150 | return admins, feedbacks_messages
151 |
152 | def _display_user_information(user: dict) -> None:
153 | """
154 | Display user's information
155 | """
156 |
157 | interface.display_horizontal_line()
158 |
159 | print("Here is your information. You are welcome!!!")
160 | print("Full name: %s %s %s" % (user[LAST_NAME], user[MIDDLE_NAME], user[FIRST_NAME]))
161 | print("Gender: %s" % user[GENDER])
162 | print("Date of birth: %s" % user[DATE_OF_BIRTH])
163 | print("Phone number: %s" % user[PHONE_NUMBER])
164 | print("Email: %s" % user[EMAIL])
165 | print("Account number: %s" % user[ACCOUNT_NUMBER])
166 | print("Issued date: %s" % user[ISSUED_DATE])
167 |
168 | interface.display_horizontal_line()
169 | utils.proceed_next()
170 |
171 |
172 | def _update_information(users: models.Users, user_index: int) -> models.Users:
173 | print("What information you want to edit?")
174 | print("☞ Please choose among information listed below")
175 | print(" ┌─────────────┐ ╭────────────────────────╮ ")
176 | print(" │ ╭┼┼╮ │ │ ▶︎ 1 • First Name │ ")
177 | print(" │ ╰┼┼╮ │ ├────────────────────────┴╮ ")
178 | print(" │ ╰┼┼╯ │ │ ▶︎ 2 • Middle Name │ ")
179 | print(" │ │ ├───────────────────────┬─╯ ")
180 | print(" │ L O N G │ │ ▶︎ 3 • Last Name │ ")
181 | print(" │ T U A N │ ├────────────────────┬──╯ ")
182 | print(" │ B A N K │ │ ▶︎ 4 • Gender │ ")
183 | print(" │ │ ├────────────────────┴──────╮ ")
184 | print(" │ │ │ ▶︎ 5 • Date of Birth │ ")
185 | print(" │ │ ├──────────────────────────┬╯ ")
186 | print(" │ ║│┃┃║║│┃║│║ │ │ ▶︎ 6 • Phone Number │ ")
187 | print(" │ ║│┃┃║║│┃║│║ │ ├───────────────────┬──────╯ ")
188 | print(" │ │ │ ▶︎ 7 • Email │ ")
189 | print(" │ │ ├──────────────────┬╯ ")
190 | print(" │ │ │ ▶︎ 8 • Exit │ ")
191 | print(" └─────────────┘ ╰──────────────────╯ ")
192 |
193 | failed_attempt = FAILED_ATTEMPT
194 | user_choice = ""
195 | while failed_attempt:
196 | user_choice = input("☞ Enter your choice: ")
197 | if user_choice not in USER_UPDATE_INFORMATION_CHOICES:
198 | failed_attempt -= 1
199 | print("Wrong choice!!! Please choose only from 1 to 8")
200 | print("You have %d try left!!!" % failed_attempt)
201 | else:
202 | break
203 |
204 | if not failed_attempt:
205 | print("You enter wrong choice many times, please wait few minutes to do it again")
206 | return users
207 |
208 | if user_choice == "1":
209 | first_name = account.get_name(FIRST_NAME_MAX_LEN, FAILED_ATTEMPT, "first")
210 | if not first_name:
211 | return users
212 |
213 | users.update_information(user_index, FIRST_NAME, first_name)
214 | print("Successfully update your first name!!!")
215 |
216 | if user_choice == "2":
217 | middle_name = account.get_name(FIRST_NAME_MAX_LEN, FAILED_ATTEMPT, "middle")
218 | if not middle_name:
219 | return users
220 |
221 | users.update_information(user_index, MIDDLE_NAME, middle_name)
222 | print("Successfully update your middle name!!!")
223 |
224 | if user_choice == "3":
225 | last_name = account.get_name(FIRST_NAME_MAX_LEN, FAILED_ATTEMPT, "last")
226 | if not last_name:
227 | return users
228 |
229 | users.update_information(user_index, LAST_NAME, last_name)
230 | print("Successfully update your last name!!!")
231 |
232 | if user_choice == "4":
233 | gender = account.get_gender(GENDER_SET_CHOICE)
234 | if not gender:
235 | return users
236 |
237 | users.update_information(user_index, GENDER, gender)
238 | print("Successfully update your gender!!!")
239 |
240 | if user_choice == "5":
241 | date_of_birth = account.get_date_of_birth()
242 | if not date_of_birth:
243 | return users
244 |
245 | users.update_information(user_index, DATE_OF_BIRTH, date_of_birth)
246 | print("Successfully update your date of birth!!!")
247 |
248 | if user_choice == "6":
249 | phone_number = account.get_phone_number()
250 | if not phone_number:
251 | return users
252 |
253 | users.update_information(user_index, PHONE_NUMBER, phone_number)
254 | print("Successfully update your phone number!!!")
255 |
256 | if user_choice == "7":
257 | email = account.get_email()
258 | if not email:
259 | return users
260 |
261 | users.update_information(user_index, EMAIL, email)
262 | print("Successfully update your email!!!")
263 |
264 | if user_choice == "8":
265 | return users
266 |
267 | print("Please check your new information")
268 | _display_user_information(users.data[user_index])
269 |
270 | return users
271 |
272 | def _update_password(users: models.Users, user_index: int) -> models.Users:
273 | new_password = account.get_password(users.data[user_index][PASSWORD])
274 | if not new_password:
275 | return users
276 |
277 | users.update_information(user_index, PASSWORD, new_password)
278 | print("Successfully update new password")
279 |
280 | return users
281 |
282 | def _get_account_number(users: models.Users) -> str:
283 | failed_attempt = FAILED_ATTEMPT
284 | account_number = ""
285 | while failed_attempt:
286 | account_number = input("☞ Please enter the user's account number: ")
287 | if not utils.is_valid_account_number(account_number, "users"):
288 | failed_attempt -= 1
289 | print("Invalid account number, please, try it again!!!")
290 | print("You have %d try left!!!" % failed_attempt)
291 | elif account_number not in users.users_set:
292 | failed_attempt -= 1
293 | print("account number does not exist, please, try it again!!!")
294 | print("You have %d try left!!!" % failed_attempt)
295 | else:
296 | break
297 |
298 | if not failed_attempt:
299 | print("You enter invalid choice many times, please wait a few minutes to try it again!!!")
300 | phone_number = ""
301 |
302 | return account_number
303 |
304 | def _get_user_information_by_field(users: models.Users) -> None:
305 | print("☞ Please choose among options below")
306 | print(" ┌─────────────┐ ╭────────────────────────────╮ ")
307 | print(" │ │ │ ▶︎ 1 • Account Number │ ")
308 | print(" │ L O N G │ ├─────────────────────┬──────╯ ")
309 | print(" │ T U A N │ │ ▶︎ 2 • Balance │ ")
310 | print(" │ B A N K │ ├─────────────────────┴──╮ ")
311 | print(" │ │ │ ▶︎ 3 • First Name │ ")
312 | print(" └─────────────┘ ╰────────────────────────╯ ")
313 |
314 | failed_attempt = FAILED_ATTEMPT
315 | user_choice = ""
316 | while failed_attempt:
317 | user_choice = input("☞ Please enter the user's account number: ")
318 | if user_choice not in FIELDS_SEARCH:
319 | failed_attempt -= 1
320 | print(f"You cannot search by field: {field}, please, try it again!!!")
321 | print("You have %d try left!!!" % failed_attempt)
322 | else:
323 | break
324 |
325 | if not failed_attempt or not user_choice:
326 | print("You enter invalid choice many times, please wait a few minutes to try it again!!!")
327 | phone_number = ""
328 |
329 | field = FIELDS_SEARCH[user_choice]
330 | sort_data = sorts.Sorting(users.data, field, "").arr
331 | if not sort_data:
332 | print("There are no users at that time in database")
333 | return
334 |
335 | for user in sort_data:
336 | _display_user_information(user)
--------------------------------------------------------------------------------