177 lines
6.1 KiB
Python
177 lines
6.1 KiB
Python
import math
|
||
from decimal import Decimal, InvalidOperation
|
||
import unicodedata
|
||
|
||
|
||
class Worker:
|
||
SURNAME_SEPARATORS = {"-", "'"}
|
||
|
||
def __init__(self, surname: str, position: str, salary: float):
|
||
self.surname = None
|
||
self.position = None
|
||
self.salary = None
|
||
self.set_surname(surname)
|
||
self.set_position(position)
|
||
self.set_salary(salary)
|
||
|
||
# -----------------------
|
||
# проверка фамилии
|
||
def set_surname(self, surname: str):
|
||
self.surname = self.validate_surname(surname)
|
||
|
||
@staticmethod
|
||
def validate_surname(surname: str) -> str:
|
||
surname = unicodedata.normalize("NFKC", surname.strip())
|
||
surname = surname.replace("’", "'").replace("`", "'").replace("−", "-")
|
||
|
||
if len(surname) < 2:
|
||
raise ValueError("Фамилия слишком короткая")
|
||
|
||
if surname[0] in Worker.SURNAME_SEPARATORS or surname[-1] in Worker.SURNAME_SEPARATORS:
|
||
raise ValueError("Фамилия не может начинаться или заканчиваться разделителем")
|
||
|
||
previous_is_separator = False
|
||
for char in surname:
|
||
if char in Worker.SURNAME_SEPARATORS:
|
||
if previous_is_separator:
|
||
raise ValueError("Фамилия содержит подряд идущие разделители")
|
||
previous_is_separator = True
|
||
continue
|
||
|
||
if not char.isalpha():
|
||
raise ValueError("Фамилия может содержать только буквы, дефис и апостроф")
|
||
|
||
previous_is_separator = False
|
||
|
||
parts: list[str] = []
|
||
token = ""
|
||
for char in surname:
|
||
if char in Worker.SURNAME_SEPARATORS:
|
||
parts.append(token)
|
||
parts.append(char)
|
||
token = ""
|
||
else:
|
||
token += char
|
||
parts.append(token)
|
||
|
||
normalized_parts = [part[:1].upper() + part[1:].lower() if part not in Worker.SURNAME_SEPARATORS else part for part in parts]
|
||
return "".join(normalized_parts)
|
||
|
||
# проверка должности
|
||
def set_position(self, position: str):
|
||
self.position = self.validate_position(position)
|
||
|
||
@staticmethod
|
||
def validate_position(position: str) -> str:
|
||
position = position.strip()
|
||
if len(position) < 2:
|
||
raise ValueError("Должность слишком короткая")
|
||
if not all(x.isalpha() or x.isspace() for x in position):
|
||
raise ValueError("Должность должна содержать только буквы и пробелы")
|
||
return position.lower()
|
||
|
||
# проверка оклада
|
||
def set_salary(self, salary: float):
|
||
self.salary = self.validate_salary(salary)
|
||
|
||
@staticmethod
|
||
def validate_salary(salary: float) -> float:
|
||
if not isinstance(salary, (int, float)):
|
||
raise ValueError("Оклад должен быть числом")
|
||
if not math.isfinite(salary):
|
||
raise ValueError("Оклад не может быть NaN или бесконечностью")
|
||
|
||
salary_decimal = Decimal(str(salary))
|
||
if salary_decimal.as_tuple().exponent < -2:
|
||
raise ValueError("Оклад должен содержать не более 2 знаков после запятой")
|
||
|
||
if salary < 1000:
|
||
raise ValueError("Оклад слишком маленький (<1000)")
|
||
if salary > 10_000_000:
|
||
raise ValueError("Оклад слишком большой (>10_000_000)")
|
||
return float(salary)
|
||
|
||
# метод №1 — увеличить оклад на 15%
|
||
def increase_salary(self):
|
||
self.salary *= 1.15
|
||
|
||
# метод №2 — если фамилия начинается с "Иван", присвоить должность "инженер"
|
||
def assign_engineer_if_ivan(self):
|
||
if self.surname.lower().startswith("иван"):
|
||
self.position = "инженер"
|
||
|
||
# строка информации об объекте
|
||
def info(self) -> str:
|
||
return f"Фамилия: {self.surname}, Должность: {self.position}, Оклад: {self.salary:.2f}"
|
||
|
||
|
||
# -----------------------
|
||
# функции ввода с проверкой
|
||
def input_surname() -> str:
|
||
while True:
|
||
surname = input("Введите фамилию: ").strip()
|
||
try:
|
||
return Worker.validate_surname(surname)
|
||
except ValueError as e:
|
||
print("Ошибка:", e)
|
||
|
||
|
||
def input_position() -> str:
|
||
while True:
|
||
position = input("Введите должность: ").strip()
|
||
try:
|
||
return Worker.validate_position(position)
|
||
except ValueError as e:
|
||
print("Ошибка:", e)
|
||
|
||
|
||
def input_salary() -> float | None:
|
||
while True:
|
||
salary_str = input("Введите оклад: ").strip().replace(",", ".")
|
||
try:
|
||
salary_decimal = Decimal(salary_str)
|
||
except InvalidOperation:
|
||
print("Ошибка: оклад должен быть числом")
|
||
continue
|
||
|
||
try:
|
||
return Worker.validate_salary(float(salary_decimal))
|
||
except ValueError as e:
|
||
print("Ошибка:", e)
|
||
|
||
|
||
def input_worker() -> Worker:
|
||
surname = input_surname()
|
||
position = input_position()
|
||
salary = input_salary()
|
||
return Worker(surname, position, salary)
|
||
|
||
|
||
# -----------------------
|
||
def main():
|
||
print("Создание работника с клавиатуры:")
|
||
worker_from_input = input_worker()
|
||
|
||
# работники-константы
|
||
worker1 = Worker("Иванов", "менеджер", 50000)
|
||
worker2 = Worker("Петров", "аналитик", 60000)
|
||
|
||
workers = [worker_from_input, worker1, worker2]
|
||
|
||
print("\nИсходные данные:")
|
||
for w in workers:
|
||
print(w.info())
|
||
|
||
# обработка данных
|
||
for w in workers:
|
||
w.increase_salary()
|
||
w.assign_engineer_if_ivan()
|
||
|
||
print("\nПосле обработки:")
|
||
for w in workers:
|
||
print(w.info())
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|