From 781dbaf7096ad41edc3e62cb63ec7f24543e352a Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 26 Jan 2024 20:21:43 +0300 Subject: [PATCH] bank: ckeckpoint --- bank.py | 280 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 280 insertions(+) create mode 100644 bank.py diff --git a/bank.py b/bank.py new file mode 100644 index 0000000..7b83dcd --- /dev/null +++ b/bank.py @@ -0,0 +1,280 @@ +""" + Имитация функций банка + + Условия задачи: + Проект онлайн - банк(В стадии разработки) + + Необходимо создать "онлайн" банк, у которого будут реализованы следующие функции. Разумеется, мы не создаем полноценный онлайн банк, это просто имитация. + 1. Регистрация + 2. Вход (Необходимо сделать, чтобы у каждого пользователя программы была возможность + После входа в случае если это происходит днем нужно вывести Добрый день!, если это происходит вечером, нужно вывести добрый вечер!, если ночью -- то нужно вывести Доброй ночи + 3. Открытие счета + 4. Пополнение(вводя данные кредитной карты) + При вводе кредитной карты необходимо проверять правильность ее ввода. + 5. Проверка идентификации пользователя (Пользователь при регистрации вводит свой e-mail, туда отправляется письмо со случайной цифрой, эту цифру необходимо ввести в программу. Если число сгенерированное совпадает, то мы говорим, что человек прошел верификацию + 6. Оплатить мобильный телефон (просто с телефона снимается определенная сумма денег) + 7. Показать рейтинг + 8. Сделать викторину(За каждый правильный ответ пользователю начисляется балл в рейтинг) + 9. Вывести на экран +""" + +import json +import os.path +from getpass import getpass +from dataclasses import dataclass, field, asdict +import hashlib +import uuid +import hmac +import datetime +import random + + +def get_hello_hour(): + now = datetime.datetime.now() + return ["Доброй ночи", "Доброе утро", "Добрый день", "Добрый вечер"][now.hour // 6] + + +def validate_credit_card(card_number: str) -> bool: + card_number = [int(num) for num in card_number] + checkDigit = card_number.pop(-1) + card_number.reverse() + card_number = [ + num * 2 if idx % 2 == 0 else num for idx, num in enumerate(card_number) + ] + card_number = [ + num - 9 if idx % 2 == 0 and num > 9 else num + for idx, num in enumerate(card_number) + ] + card_number.append(checkDigit) + checkSum = sum(card_number) + return checkSum % 10 == 0 + + +class Singleton(type): + _instances = {} + + def __call__(cls, *args, **kwargs): + if cls not in cls._instances: + cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) + return cls._instances[cls] + + +class SimpleDataBase(metaclass=Singleton): + __FILENAME = "bank.json" + + def __init__(self): + if os.path.isfile(self.__class__.__FILENAME): + with open(self.__class__.__FILENAME, "r", encoding="utf-8") as f: + self.data = json.load(f) + else: + self.data = {"users": {}, "accounts": {}, "rating": {}} + + def commit(self): + with open(self.__class__.__FILENAME, "w", encoding="utf-8") as f: + json.dump(self.data, f) + + def __getattr__(self, attr): + return self.data[attr] + + def __setattr__(self, attr, value): + self.data[attr] = value + + def __delattr__(self, attr): + del self.data[attr] + + +@dataclass +class User: + name: str + fio: str + email: str + verification_code: int + is_verifyed: bool = field(init=False) + salt: str = field(init=False) + hash: str = field(init=False) + + def __post_init__(self): + self.hash = "" + self.salt = "" + self.is_verifyed = False + + def set_password(self, password): + self.salt = uuid.uuid4().hex + self.hash = hashlib.sha512((password + self.salt).encode("utf-8")).hexdigest() + + def authenticate(self, password): + hash = hashlib.sha512((password + self.salt).encode("utf-8")).digest() + return hmac.compare_digest(hash, bytes.fromhex(self.hash)) + + def email_verification(self, code): + res = self.verification_code == code + if not self.is_verifyed: + self.is_verifyed = res + return self.is_verifyed + + def save(self): + users = Users() + users.save_user(self) + + +class Users(metaclass=Singleton): + def __init__(self): + self.db = SimpleDataBase() + + def get_user(self, name): + return self.db.users.get(name) + + def save_user(self, user): + self.db.users[user.name] = asdict(user) + + def add_user(self, name, fio, email, verification_code, password): + if self.get_user(name) is not None: + return False + user = User(name, fio, email, verification_code) + user.set_password(password) + self.save_user(user) + return True + + def authenticate(self, name, password): + user = self.get_user(name) + if user is None: + return False + return user.authenticate(password) + + +def fake_email_send(email, code): + with open(email, "w", encoding="utf-8") as f: + print(f"Ваш код: {code}", file=f) + + +def registration(): + print(f"{get_hello_hour()}! Давайте Вас зарегистрируем!") + fio = input("Ваше ФИО: ") + name = input("Ваш логин: ") + email = input("Ваш email: ") + verification_code = random.randint(0, 9) + password = getpass("Введите пароль: ") + # TODO: валидация данных и доп.вопросы + users = Users() + ok = users.add_user(name, fio, email, verification_code, password) + if ok: + print("Регистрация успешна, спасибо!") + fake_email_send(email, verification_code) + print( + "На Ваш email отправлен код идентификации (создан файл в текущей папке с именем=email)" + ) + users.db.commit() + exit(0) + else: + print("Ошибка, выберите другой логин!") + exit(1) + + +def login(): + print(f"{get_hello_hour()}! Добро пожаловать в систему банка!") + name = input("Ваш логин: ") + password = getpass("Введите пароль: ") + # TODO: валидация данных и доп.вопросы + users = Users() + user = users.authenticate(name, password) + if user: + print("Вы успешно вошли в систему!") + else: + print("Ошибка: Неправильный логин или пароль!") + exit(1) + return user + + +def user_verification(): + user = login() + users = Users() + code = int(input("Введите код идентификации: ")) + res = user.email_verification(code) + if res: + print("Вы успешно идентивицированы!") + users.db.commit() + else: + print("К сожалению код не подходит!") + if "да" == input("Введите да, чтобы отправить новый код: "): + user.verification_code = random.randint(0, 9) + fake_email_send(user.email, user.verification_code) + user.save() + print( + "На Ваш email отправлен код идентификации (создан файл в текущей папке с именем=email)" + ) + users.db.commit() + exit(0) + else: + exit(1) + + +def open_account(): + user = login() + db = SimpleDataBase() + if user.name in db.accounts: + print("Счет уже открыт") + exit(1) + db.accounts[user.name] = 0 + db.commit() + print("Счет успешно открыт") + + +def print_account_balance(): + user = login() + db = SimpleDataBase() + if user.name not in db.accounts: + print("Счет не открыт") + exit(1) + balance = round(db.accounts[user.name] / 100, 2) + print(f"На балансе: {balance} руб.") + + +def addition_from_card(): + user = login() + db = SimpleDataBase() + + if user.name not in db.accounts: + print("Сначала нужно открыть счет") + exit(2) + + is_valid = False + while not is_valid: + card = input("Введите номер карты: ") + is_valid = validate_credit_card(card) + if not is_valid: + if "да" != input("Введите да, чтобы попытаться ещё раз: "): + break + if not is_valid: + exit(1) + + amount = float(input("Введите сумму пополнения: ")) + # рубли и копейки комбинируем в int, чтобы избежать проблем с float + amount = int(amount * 100) + db.accounts[user.name] += amount + db.commit() + print("Баланс пополнен успешно") + + +def payment_for_phone(): + user = login() + db = SimpleDataBase() + + if user.name not in db.accounts: + print("Сначала нужно открыть счет") + exit(2) + + phone = input("Введите номер телефона: ") + # TODO: Валидация номера телефона + + amount = float(input("Введите сумму платежа: ")) + # рубли и копейки комбинируем в int, чтобы избежать проблем с float + amount = int(amount * 100) + db.accounts[user.name] -= amount + db.commit() + print("Платеж выполнен успешно, деньги (типа) отправлены на номер", phone) + + +# TODO: пункт 7 +# TODO: пункт 8 +# TODO: пункт 9 +# TODO: main