py_stepik/bank.py
2024-01-26 20:21:43 +03:00

281 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Имитация функций банка
Условия задачи:
Проект онлайн - банк(В стадии разработки)
Необходимо создать "онлайн" банк, у которого будут реализованы следующие функции. Разумеется, мы не создаем полноценный онлайн банк, это просто имитация.
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