+ bank.py

This commit is contained in:
Dmitry Belyaev 2024-01-27 01:45:24 +03:00
parent 781dbaf709
commit e9cca1d053

290
bank.py
View File

@ -1,7 +1,8 @@
""" # Имитация функций банка
Имитация функций банка # задача: https://stepik.org/lesson/967334/step/7?unit=973876
# решение: https://gitea.b4tman.ru/temp/py_stepik/src/branch/master/bank.py
Условия задачи: """
Проект онлайн - банк(В стадии разработки) Проект онлайн - банк(В стадии разработки)
Необходимо создать "онлайн" банк, у которого будут реализованы следующие функции. Разумеется, мы не создаем полноценный онлайн банк, это просто имитация. Необходимо создать "онлайн" банк, у которого будут реализованы следующие функции. Разумеется, мы не создаем полноценный онлайн банк, это просто имитация.
@ -29,6 +30,9 @@ import datetime
import random import random
session = {}
def get_hello_hour(): def get_hello_hour():
now = datetime.datetime.now() now = datetime.datetime.now()
return ["Доброй ночи", "Доброе утро", "Добрый день", "Добрый вечер"][now.hour // 6] return ["Доброй ночи", "Доброе утро", "Добрый день", "Добрый вечер"][now.hour // 6]
@ -73,13 +77,23 @@ class SimpleDataBase(metaclass=Singleton):
with open(self.__class__.__FILENAME, "w", encoding="utf-8") as f: with open(self.__class__.__FILENAME, "w", encoding="utf-8") as f:
json.dump(self.data, f) json.dump(self.data, f)
@staticmethod
def _is_data(attr):
return "data" == attr
def __getattr__(self, attr): def __getattr__(self, attr):
if self.__class__._is_data(attr):
return super().__getattr__(attr)
return self.data[attr] return self.data[attr]
def __setattr__(self, attr, value): def __setattr__(self, attr, value):
if self.__class__._is_data(attr):
return super().__setattr__(attr, value)
self.data[attr] = value self.data[attr] = value
def __delattr__(self, attr): def __delattr__(self, attr):
if self.__class__._is_data(attr):
return super().__delattr__(attr)
del self.data[attr] del self.data[attr]
@ -98,15 +112,15 @@ class User:
self.salt = "" self.salt = ""
self.is_verifyed = False self.is_verifyed = False
def set_password(self, password): def set_password(self, password: str):
self.salt = uuid.uuid4().hex self.salt = uuid.uuid4().hex
self.hash = hashlib.sha512((password + self.salt).encode("utf-8")).hexdigest() self.hash = hashlib.sha512((password + self.salt).encode("utf-8")).hexdigest()
def authenticate(self, password): def authenticate(self, password: str) -> bool:
hash = hashlib.sha512((password + self.salt).encode("utf-8")).digest() hash = hashlib.sha512((password + self.salt).encode("utf-8")).digest()
return hmac.compare_digest(hash, bytes.fromhex(self.hash)) return hmac.compare_digest(hash, bytes.fromhex(self.hash))
def email_verification(self, code): def email_verification(self, code: int) -> bool:
res = self.verification_code == code res = self.verification_code == code
if not self.is_verifyed: if not self.is_verifyed:
self.is_verifyed = res self.is_verifyed = res
@ -116,18 +130,28 @@ class User:
users = Users() users = Users()
users.save_user(self) users.save_user(self)
@classmethod
def from_dict(cls, data: dict):
user = cls(*(data[k] for k in "name fio email verification_code".split()))
for k in "is_verifyed salt hash".split():
setattr(user, k, data[k])
return user
class Users(metaclass=Singleton): class Users(metaclass=Singleton):
def __init__(self): def __init__(self):
self.db = SimpleDataBase() self.db = SimpleDataBase()
def get_user(self, name): def get_user(self, name: str) -> User:
return self.db.users.get(name) data = self.db.users.get(name)
return data and User.from_dict(data)
def save_user(self, user): def save_user(self, user: User):
self.db.users[user.name] = asdict(user) self.db.users[user.name] = asdict(user)
def add_user(self, name, fio, email, verification_code, password): def add_user(
self, name: str, fio: str, email: str, verification_code: int, password: str
) -> bool:
if self.get_user(name) is not None: if self.get_user(name) is not None:
return False return False
user = User(name, fio, email, verification_code) user = User(name, fio, email, verification_code)
@ -135,11 +159,11 @@ class Users(metaclass=Singleton):
self.save_user(user) self.save_user(user)
return True return True
def authenticate(self, name, password): def authenticate(self, name: str, password: str) -> User | None:
user = self.get_user(name) user = self.get_user(name)
if user is None: if user is None:
return False return False
return user.authenticate(password) return user.authenticate(password) and user or None
def fake_email_send(email, code): def fake_email_send(email, code):
@ -148,6 +172,8 @@ def fake_email_send(email, code):
def registration(): def registration():
"""Регистрация"""
print(f"{get_hello_hour()}! Давайте Вас зарегистрируем!") print(f"{get_hello_hour()}! Давайте Вас зарегистрируем!")
fio = input("Ваше ФИО: ") fio = input("Ваше ФИО: ")
name = input("Ваш логин: ") name = input("Ваш логин: ")
@ -164,13 +190,19 @@ def registration():
"На Ваш email отправлен код идентификации (создан файл в текущей папке с именем=email)" "На Ваш email отправлен код идентификации (создан файл в текущей папке с именем=email)"
) )
users.db.commit() users.db.commit()
exit(0)
session["user"] = users.get_user(name)
else: else:
print("Ошибка, выберите другой логин!") print("Ошибка, выберите другой логин!")
exit(1)
def login(): def login() -> User | None:
"""Вход"""
if "user" in session:
print(f"Вы уже вошли в систему, {session['user'].fio}!")
return session["user"]
print(f"{get_hello_hour()}! Добро пожаловать в систему банка!") print(f"{get_hello_hour()}! Добро пожаловать в систему банка!")
name = input("Ваш логин: ") name = input("Ваш логин: ")
password = getpass("Введите пароль: ") password = getpass("Введите пароль: ")
@ -178,20 +210,42 @@ def login():
users = Users() users = Users()
user = users.authenticate(name, password) user = users.authenticate(name, password)
if user: if user:
print("Вы успешно вошли в систему!") print(f"Вы успешно вошли в систему, {user.fio}!")
else: else:
print("Ошибка: Неправильный логин или пароль!") print("Ошибка: Неправильный логин или пароль!")
exit(1) return None
session["user"] = user
return user return user
def logout():
"""Выход пользователя"""
if "user" not in session:
print("Для того чтобы выйти из системы, сначала нужно в неё войти :)")
return
user = session["user"]
del session["user"]
print(f"До новых встреч, {user.fio}!")
def user_verification(): def user_verification():
"""Идентификация пользователя"""
user = login() user = login()
if user is None:
return
if user.is_verifyed:
print("Вы уже идентифцированы!")
return
users = Users() users = Users()
code = int(input("Введите код идентификации: ")) code = int(input("Введите код идентификации: "))
res = user.email_verification(code) res = user.email_verification(code)
if res: if res:
print("Вы успешно идентивицированы!") print("Вы успешно идентифицированы!")
users.db.commit() users.db.commit()
else: else:
print("К сожалению код не подходит!") print("К сожалению код не подходит!")
@ -200,52 +254,82 @@ def user_verification():
fake_email_send(user.email, user.verification_code) fake_email_send(user.email, user.verification_code)
user.save() user.save()
print( print(
"На Ваш email отправлен код идентификации (создан файл в текущей папке с именем=email)" f"На Ваш email отправлен код идентификации!\n\t(создан файл в текущей папке с именем={user.email})"
) )
users.db.commit() users.db.commit()
exit(0)
else: else:
exit(1) print("Выход")
def open_account(): def open_account():
"""Открытие счета"""
user = login() user = login()
if user is None:
return
db = SimpleDataBase() db = SimpleDataBase()
if user.name in db.accounts: if user.name in db.accounts:
print("Счет уже открыт") print("Счет уже открыт")
exit(1) return
db.accounts[user.name] = 0 db.accounts[user.name] = 0
db.commit() db.commit()
print("Счет успешно открыт") print("Счет успешно открыт")
def print_account_balance(): def print_account_details():
"""Информация о пользователе"""
user = login() user = login()
if user is None:
return
print("\n - - - ")
print("Ваше ФИО:", user.fio)
print("Ваш email:", user.email)
print(
"Ваш статус:", user.is_verifyed and "Идентифицирован" or "Ожидает идентификации"
)
db = SimpleDataBase() db = SimpleDataBase()
if user.name not in db.accounts: if user.name not in db.accounts:
print("Счет не открыт") print("Счет не открыт")
exit(1) else:
balance = round(db.accounts[user.name] / 100, 2) balance = round(db.accounts[user.name] / 100, 2)
print(f"На балансе: {balance} руб.") print(f"На балансе: {balance} руб.")
if user.name not in db.rating:
print("У Вас ещё нет рейтинга")
else:
print(f"Ваш рейтинг: {db.rating[user.name]} баллов")
print("\n - - - ")
def addition_from_card(): def addition_from_card():
"""Пополнение баланса с карты"""
user = login() user = login()
if user is None:
return
db = SimpleDataBase() db = SimpleDataBase()
if user.name not in db.accounts: if user.name not in db.accounts:
print("Сначала нужно открыть счет") print("Сначала нужно открыть счет")
exit(2) return
is_valid = False is_valid = False
while not is_valid: while not is_valid:
card = input("Введите номер карты: ") card = input("Введите номер карты: ")
is_valid = validate_credit_card(card) is_valid = validate_credit_card(card)
if not is_valid: if not is_valid:
print("Номер не правильный")
if "да" != input("Введите да, чтобы попытаться ещё раз: "): if "да" != input("Введите да, чтобы попытаться ещё раз: "):
break break
if not is_valid: if not is_valid:
exit(1) print("Выход")
return
amount = float(input("Введите сумму пополнения: ")) amount = float(input("Введите сумму пополнения: "))
# рубли и копейки комбинируем в int, чтобы избежать проблем с float # рубли и копейки комбинируем в int, чтобы избежать проблем с float
@ -256,12 +340,17 @@ def addition_from_card():
def payment_for_phone(): def payment_for_phone():
"""Оплата мобильного телефона"""
user = login() user = login()
if user is None:
return
db = SimpleDataBase() db = SimpleDataBase()
if user.name not in db.accounts: if user.name not in db.accounts:
print("Сначала нужно открыть счет") print("Сначала нужно открыть счет")
exit(2) return
phone = input("Введите номер телефона: ") phone = input("Введите номер телефона: ")
# TODO: Валидация номера телефона # TODO: Валидация номера телефона
@ -274,7 +363,142 @@ def payment_for_phone():
print("Платеж выполнен успешно, деньги (типа) отправлены на номер", phone) print("Платеж выполнен успешно, деньги (типа) отправлены на номер", phone)
# TODO: пункт 7 def quiz():
# TODO: пункт 8 """Рейтинговая викторина"""
# TODO: пункт 9
# TODO: main print(' (тема: "Финансовая грамотность")')
questions = [
(
"""Финансовую защиту благосостояния семьи обеспечивает капитал:
а) резервный
б) текущий
в) инвестиционный""",
"а",
),
(
"""В соответствии с законом о страховании вкладчик получит право на возмещение по своим вкладам в банке в случае:
а) потери доверия к банку у населения
б) отзыва у банка лицензии
в) повышения инфляции""",
"б",
),
(
"""Инфляция:
а) повышение заработной платы бюджетникам
б) повышение покупательной способности денег
в) снижение покупательной способности денег""",
"в",
),
(
"""Кредит, выдаваемый под залог объекта, который приобретается (земельный участок, дом, квартира), называется:
а) ипотечный
б) потребительский
в) целевой""",
"а",
),
(
"""Счет до востребования с минимальной процентной ставкой, то есть текущий счет, открывается для карты:
а) кредитной
б) дебетовой с овердрафтом
в) дебетовой""",
"в",
),
(
"""Фондовый рынок — это место, где:
а) продаются и покупаются строительные материалы
б) продаются и покупаются ценные бумаги
в) продаются и покупаются продукты питания""",
"б",
),
(
"""Биржа — это место, где:
а) продаются и покупаются автомобили
б) продаются и покупаются ценные бумаги
в) место заключения сделок между покупателями и продавцами""",
"в",
),
(
"""Страховые выплаты компенсируются в случае:
а) материального ущерба
б) морального ущерба
в) желания страхователя получить прибыль""",
"а",
),
(
"""Выплачиваемая нынешним пенсионерам и формируемая пенсионерам будущим трудовая пенсия по старости, выплачиваемая государством:
а) добавочная
б) второстепенная
в) базовая""",
"в",
),
(
"""Выплачиваемая нынешним пенсионерам и формируемая пенсионерам будущим трудовая пенсия по старости, выплачиваемая государством:
а) главная
б) накопительная
в) дополнительная""",
"б",
),
]
user = login()
if user is None:
return
db = SimpleDataBase()
if user.name in db.rating:
print(f"Ваш текущий рейтинг: {db.rating[user.name]} баллов")
if "выход" == input("Введите 'выход', для того чтобы выйти из тестирования: "):
return
score = 0
random.shuffle(questions)
for question, q_answer in questions:
print("- - - - - - - - - - - -\n")
print(f"Вопрос: \n{question}\n")
answer = input("Ваш ответ: ")
if answer.strip().lower() == q_answer.strip().lower():
print(f"Верно")
score += 1
else:
print("Не верно")
print(f"Результат: {score} балла")
old_rating = db.rating.get(user.name, 0)
if old_rating < score:
db.rating[user.name] = score
print(f"Ваш рейтинг обновлен и составляет: {score} балла")
db.commit()
else:
print("Вы набрали меньший балл, и ваш рейтинг оставлен без изменений")
def main():
modes = [
registration,
login,
print_account_details,
open_account,
user_verification,
addition_from_card,
payment_for_phone,
quiz,
logout,
]
while True:
print("Выберите режим:")
for i, func in enumerate(modes, 1):
print(f"{i} - {func.__doc__}")
print(" [ Для выхода введите любую другую строку или нажмите Ввод ] ")
i = input("Ваш выбор: ")
if not i.isdigit() or not 0 < int(i) <= len(modes):
print("Выход")
return
mode = modes[int(i) - 1]
print(f"Режим: {mode.__doc__}")
mode()
# не пишу тут if __name__ == "__main__":
# чтобы можно было вставить например в ipython или ptpython и т.д.
main()