From 7a98518379c702a0a20d44d705eb41a2934cc4fe Mon Sep 17 00:00:00 2001 From: Alexey Chubukov Date: Sat, 25 Apr 2020 01:36:36 +0300 Subject: [PATCH 1/3] Virst version of Patient class --- homework/My_logger.py | 58 +++++++++ homework/config.py | 10 +- homework/patient.py | 266 ++++++++++++++++++++++++++++++++++++++++-- tests/test_patient.py | 3 + 4 files changed, 322 insertions(+), 15 deletions(-) create mode 100644 homework/My_logger.py diff --git a/homework/My_logger.py b/homework/My_logger.py new file mode 100644 index 0000000..6509cb6 --- /dev/null +++ b/homework/My_logger.py @@ -0,0 +1,58 @@ +from functools import wraps +import logging + + +formatter = logging.Formatter( + '[%(asctime)s] - %(levelname)s - %(funcName)s - line %(lineno)d - %(module)s - %(message)s') + +good_log = logging.getLogger("logger_access") +good_log.setLevel(logging.INFO) +handler_access = logging.FileHandler("access.log") +handler_access.setFormatter(formatter) +good_log.addHandler(handler_access) + + +bad_log = logging.getLogger("logger_error") +bad_log.setLevel(logging.ERROR) +handler_error = logging.FileHandler("error.log") +handler_error.setFormatter(formatter) +bad_log.addHandler(handler_error) + + + + +def init_log(func): + @wraps(func) + def wrapper(*args, **kwargs): + result = exception = None + try: + result = func(*args, **kwargs) + good_log.info(f"Поступил новый пациент: {args[0]}") + return result + except Exception as err: + bad_log.error(f"Ошибка записи пациента: {err}") + exception = err + if exception is not None: + raise exception + return wrapper + + +def log(func): + @wraps(func) + def wrapper(*args, **kwargs): + result = exception = None + try: + result = func(*args, **kwargs) + return result + except Exception as err: + bad_log.error(f"{err}") + exception = err + if exception is not None: + raise exception + return wrapper + + + + + + diff --git a/homework/config.py b/homework/config.py index 955b991..04abdea 100644 --- a/homework/config.py +++ b/homework/config.py @@ -1,7 +1,7 @@ -GOOD_LOG_FILE = "good_log.txt" -ERROR_LOG_FILE = "error_log.txt" -CSV_PATH = "csv.csv" -PHONE_FORMAT = "79160000000" # Здесь запишите телефон +7-916-000-00-00 в том формате, в котором вы храните телефоны +GOOD_LOG_FILE = "access.log" +ERROR_LOG_FILE = "error.log" +CSV_PATH = "Patients.csv" +PHONE_FORMAT = "+7-916-000-00-11" # Здесь запишите телефон +7-916-000-00-00 в том формате, в котором вы храните телефоны PASSPORT_TYPE = "паспорт" # тип документа, когда он паспорт PASSPORT_FORMAT = "0000 000000" # Здесь запишите номер парспорта 0000 000000 в том формате, в котором вы его храните @@ -9,5 +9,5 @@ INTERNATIONAL_PASSPORT_TYPE = "заграничный паспорт" # тип документа, если это загран INTERNATIONAL_PASSPORT_FORMAT = "00 0000000" # формат хранения заграна для номера 00 0000000 -DRIVER_LICENSE_TYPE = "водительское удостоверение" # тип документа, если это водительское удостоверение +DRIVER_LICENSE_TYPE = "водительские права" # тип документа, если это водительское удостоверение DRIVER_LICENSE_FORMAT = "00 00 000000" # формат хранения номера ВУ diff --git a/homework/patient.py b/homework/patient.py index dad2526..db5f883 100644 --- a/homework/patient.py +++ b/homework/patient.py @@ -1,17 +1,263 @@ +#from hw2.python_developer_hw2.homework import My_logger +from homework import My_logger +import re + class Patient: - def __init__(self, *args, **kwargs): - pass - def create(*args, **kwargs): - raise NotImplementedError() + first_name = NotImplemented + last_name = NotImplemented + + + @My_logger.init_log + def __init__(self, name, surname, birth_date, phone_number, doc_type, doc_number): + self.good_log = My_logger.logging.getLogger("logger_access") + self.bad_log = My_logger.logging.getLogger("logger_error") + + + self._first_name = name + self._last_name = surname + self.birth_date = birth_date + self.phone = phone_number + self.document_type = doc_type + self.document_id = doc_number + + #def __del__(self): + # self.handler_access.close() + # self.handler_error.close() + #self.good_log.removeHandler(self.handler_access) + #self.bad_log.removeHandler(self.handler_error) + #del self.good_log + #del self.bad_log + + def __str__(self): + out = f"{self._first_name} " \ + + f"{self._last_name} " \ + + f"{self.birth_date} " \ + + f"{self.phone} " \ + + f"{self.document_type} " \ + + f"{self.document_id}" + return out + + + + @property + def first_name(self): + return self.__first_name + + @first_name.setter + @My_logger.log + def first_name(self, name): + raise AttributeError("Нельзя изменять значения поля first_name") + + + @property + def last_name(self): + return self.__last_name + + @last_name.setter + @My_logger.log + def last_name(self, name): + raise AttributeError("Нельзя изменять значения поля last_name") + + + @property + def _first_name(self): + return self.__first_name + + @_first_name.setter + def _first_name(self, name): + if not isinstance(name, str): + raise TypeError("Неправильный тип данных поля first_name") + if name.isalpha() != True: + raise ValueError("Name must contain only characters") + self.__first_name = name + + @property + def _last_name(self): + return self.__last_name + + @_last_name.setter + def _last_name(self, surname): + if not isinstance(surname, str): + raise TypeError("Неправильный тип данных поля last_name") + if surname.isalpha() != True: + raise ValueError("Surname must contain only characters") + self.__last_name = surname + + @property + def birth_date(self): + return self.__birth_date + + @birth_date.setter + @My_logger.log + def birth_date(self, date): + if not isinstance(date, str): + raise TypeError("Неправильный тип данных поля birth_date") + format1 = re.search("^(\d{2}[\-|\.| ]?){2}\d{4}$", date) is not None + format2 = re.search("^\d{4}[\-|\.| ]?\d{2}[\-|\.| ]\d{2}$", date) is not None + if not (format1 or format2): + raise ValueError("Неправильная дата рождения") + day, mounth, year = '', '', '' + if format1: + day, mounth, year = date[0:2], date[3:5], date[6:] + assert (2020 - int(year)) < 150, "Неправильная дата рождения" + else: + year, mounth, day = date[0:4], date[5:7], date[8:] + assert (2020 - int(year)) < 150, "Неправильная дата рождения" + try: + self.birth_date + self.good_log.info(f"Изменение поля birth_date объекта {self}") + self.__birth_date = '-'.join([year, mounth, day]) + except AttributeError: + self.__birth_date = '-'.join([year, mounth, day]) + - def save(self): - pass + @property + def phone(self): + return self.__phone + + @phone.setter + @My_logger.log + def phone(self, phone): + if not isinstance(phone, str): + raise TypeError("Неправильный тип данных поля phone") + if re.search("[\+7|8]?([\(| |\-]?[\d]{3}[\)| |\-]?){2}([\-| ]?[\d]{2}[\-| ]?){2}", phone) is None: + raise ValueError("Неправильный номер телефона") + phone = ''.join(filter(str.isdigit, phone)) + if len(phone) < 11: + phone = '7' + phone + phone = f'+7-{phone[1:4]}-{phone[4:7]}-{phone[7:9]}-{phone[9:11]}' + try: + self.phone + self.good_log.info(f"Изменение поля phone объекта {self}") + self.__phone = phone + except AttributeError: + self.__phone = phone + + @property + def document_type(self): + return self.__document_type + + @document_type.setter + @My_logger.log + def document_type(self, document): + if not isinstance(document, str): + raise TypeError("Неправильный тип данных поля document_type") + document = document.lower() + if document not in {"паспорт", "заграничный паспорт", "водительские права"}: + raise ValueError("Неправильный тип документов") + try: + self.document_type + self.good_log.info(f"Изменение поля document_type объекта {self}") + self.__document_type = document + except AttributeError: + self.__document_type = document + + + @property + def document_id(self): + return self.__document_id + + @document_id.setter + @My_logger.log + def document_id(self, doc_number): + if not isinstance(doc_number, str): + raise TypeError("Неправильный тип данных поля document_id") + if self.document_type == "паспорт": + if re.search("^([\d][ |\-|\-|\/]?){10}$", doc_number) is None: + raise ValueError("Неправильный номер паспорта") + doc_number = ''.join(filter(str.isdigit, doc_number)) + doc_number = doc_number[:4] + " " + doc_number[4:] + if self.document_type == "заграничный паспорт": + if re.search("^([\d][ |\-|\-|\/]?){9}$", doc_number) is None: + raise ValueError("Неправильный номер заграничного паспорта") + doc_number = ''.join(filter(str.isdigit, doc_number)) + doc_number = doc_number[:2] + " " + doc_number[2:] + if self.document_type == "водительские права": + if re.search("^([\d][ |\-|\-|\/]?){10}$", doc_number) is None: + raise ValueError("Неправильный номер прав") + doc_number = ''.join(filter(str.isdigit, doc_number)) + doc_number = doc_number[:2] + " " + doc_number[2:4] + " " + doc_number[4:] + try: + self.document_id + self.good_log.info(f"Изменение поля document_id объекта {self}") + self.__document_id = doc_number + except AttributeError: + self.__document_id = doc_number + + + @classmethod + def create(cls, name, surname, birth_date, phone_number, doc_type, doc_number): + return cls(name, surname, birth_date, phone_number, doc_type, doc_number) + + def save(self, filename="Patients.csv"): + data = [self._first_name, self._last_name, self.birth_date, self.phone, self.document_type, self.document_id] + data = ','.join(data) + data += '\n' + try: + with open(filename, 'a') as file: + file.write(data) + self.good_log.info(f"Пациент {self} записан в файл") + except Exception as err: + self.bad_log.error(f"Ошибка записи в файл: {err}") + + +class Iterator: + """Итератор для прохождения по коллекции""" + + def __init__(self, collection, filename, limit=None): + self.collection = collection + self.limit = limit + self.file = open(filename, 'r') + + def __iter__(self): + return self + + def __next__(self): + try: + line = self.file.readline() + line = line.rstrip('\n') + assert line is not "" + if self.limit is not None: + assert self.limit != 0 + self.limit -= 1 + return Patient(*line.split(',')) + except AssertionError: + raise StopIteration + + + def __del__(self): + self.file.close() class PatientCollection: - def __init__(self, log_file): - pass - def limit(self, n): - raise NotImplementedError() + def __init__(self, filename='Patients.csv'): + self.good_log = My_logger.logging.getLogger("logger_access") + self.bad_log = My_logger.logging.getLogger("logger_error") + self.filename = filename + self.data = self.read_patients_from_file(filename) + + def __getitem__(self, key): + return self.data[key] + + + def __iter__(self): + return Iterator(self.data, self.filename) + + def limit(self, limit=None): + return Iterator(self.data,self.filename, limit) + + + @staticmethod + def read_patients_from_file(filename): + data = [] + try: + with open(filename, 'r') as file: + for line in file: + p = Patient.Patient(*line.split(',')) + data.append(p) + except Exception: + self.bad_log.error(f"Ошибка чтения пациентов из файла {filename}") + finally: + return tuple(data) diff --git a/tests/test_patient.py b/tests/test_patient.py index 125bd59..e4a2dda 100644 --- a/tests/test_patient.py +++ b/tests/test_patient.py @@ -8,6 +8,7 @@ from homework.config import GOOD_LOG_FILE, ERROR_LOG_FILE, CSV_PATH, PHONE_FORMAT, PASSPORT_TYPE, PASSPORT_FORMAT, \ INTERNATIONAL_PASSPORT_FORMAT, INTERNATIONAL_PASSPORT_TYPE, DRIVER_LICENSE_TYPE, DRIVER_LICENSE_FORMAT from homework.patient import Patient +from homework import My_logger from tests.constants import GOOD_PARAMS, OTHER_GOOD_PARAMS, WRONG_PARAMS, PATIENT_FIELDS @@ -39,6 +40,8 @@ def setup(): def teardown(): for file in [GOOD_LOG_FILE, ERROR_LOG_FILE, CSV_PATH]: + My_logger.handler_access.close() + My_logger.handler_error.close() os.remove(file) From c1ffac733502c8369a1b38c0ce33f33bac1dd6a9 Mon Sep 17 00:00:00 2001 From: Alexey Chubukov Date: Sun, 26 Apr 2020 23:44:54 +0300 Subject: [PATCH 2/3] Ivan`s changes fixed --- homework/My_logger.py | 33 ++----- homework/patient.py | 202 ++++++++++++++++++++---------------------- 2 files changed, 104 insertions(+), 131 deletions(-) diff --git a/homework/My_logger.py b/homework/My_logger.py index 6509cb6..4b3bf07 100644 --- a/homework/My_logger.py +++ b/homework/My_logger.py @@ -18,37 +18,20 @@ handler_error.setFormatter(formatter) bad_log.addHandler(handler_error) - - - -def init_log(func): - @wraps(func) - def wrapper(*args, **kwargs): - result = exception = None - try: - result = func(*args, **kwargs) - good_log.info(f"Поступил новый пациент: {args[0]}") - return result - except Exception as err: - bad_log.error(f"Ошибка записи пациента: {err}") - exception = err - if exception is not None: - raise exception - return wrapper - - def log(func): @wraps(func) def wrapper(*args, **kwargs): - result = exception = None try: - result = func(*args, **kwargs) - return result + func(*args, **kwargs) + except TypeError as err: + bad_log.error(f"Ошибка записи пациента: {err}") + raise err + except ValueError as err: + bad_log.error(f"Ошибка записи пациента: {err}") + raise err except Exception as err: bad_log.error(f"{err}") - exception = err - if exception is not None: - raise exception + raise err return wrapper diff --git a/homework/patient.py b/homework/patient.py index db5f883..261b007 100644 --- a/homework/patient.py +++ b/homework/patient.py @@ -1,33 +1,33 @@ -#from hw2.python_developer_hw2.homework import My_logger from homework import My_logger +import logging import re class Patient: + """Класс содержит основные данные о пациенте""" first_name = NotImplemented last_name = NotImplemented - - @My_logger.init_log def __init__(self, name, surname, birth_date, phone_number, doc_type, doc_number): - self.good_log = My_logger.logging.getLogger("logger_access") - self.bad_log = My_logger.logging.getLogger("logger_error") - - - self._first_name = name - self._last_name = surname - self.birth_date = birth_date - self.phone = phone_number - self.document_type = doc_type - self.document_id = doc_number - - #def __del__(self): - # self.handler_access.close() - # self.handler_error.close() - #self.good_log.removeHandler(self.handler_access) - #self.bad_log.removeHandler(self.handler_error) - #del self.good_log - #del self.bad_log + self.good_log = logging.getLogger("logger_access") + self.bad_log = logging.getLogger("logger_error") + try: + self.first_name_ = name + self.last_name_ = surname + self.birth_date = birth_date + self.phone = phone_number + self.document_type = doc_type + self.document_id = doc_number + self.good_log.info(f"Поступил новый пациент: {self}") + except TypeError as err: + self.bad_log.error(f"Ошибка записи пациента: {err}") + raise err + except ValueError as err: + self.bad_log.error(f"Ошибка записи пациента: {err}") + raise err + except Exception as err: + self.bad_log.error(f"{err}") + raise err def __str__(self): out = f"{self._first_name} " \ @@ -42,7 +42,7 @@ def __str__(self): @property def first_name(self): - return self.__first_name + return self._first_name @first_name.setter @My_logger.log @@ -52,7 +52,7 @@ def first_name(self, name): @property def last_name(self): - return self.__last_name + return self._last_name @last_name.setter @My_logger.log @@ -61,143 +61,138 @@ def last_name(self, name): @property - def _first_name(self): - return self.__first_name + def first_name_(self): + return self._first_name - @_first_name.setter - def _first_name(self, name): + @first_name_.setter + def first_name_(self, name): if not isinstance(name, str): - raise TypeError("Неправильный тип данных поля first_name") - if name.isalpha() != True: - raise ValueError("Name must contain only characters") - self.__first_name = name + raise TypeError(f"Неправильный тип данных поля first_name {type(name)} is not str") + if not name.isalpha(): + raise ValueError(f"Имя должно содержать только символы {name}") + self._first_name = name @property - def _last_name(self): - return self.__last_name + def last_name_(self): + return self._last_name - @_last_name.setter - def _last_name(self, surname): + @last_name_.setter + def last_name_(self, surname): if not isinstance(surname, str): - raise TypeError("Неправильный тип данных поля last_name") - if surname.isalpha() != True: - raise ValueError("Surname must contain only characters") - self.__last_name = surname + raise TypeError(f"Неправильный тип данных поля last_name {type(surname)} is not str") + if not surname.isalpha(): + raise ValueError(f"Фамилия должна содержать только символы {surname}") + self._last_name = surname @property def birth_date(self): - return self.__birth_date + return self._birth_date @birth_date.setter @My_logger.log def birth_date(self, date): if not isinstance(date, str): - raise TypeError("Неправильный тип данных поля birth_date") - format1 = re.search("^(\d{2}[\-|\.| ]?){2}\d{4}$", date) is not None - format2 = re.search("^\d{4}[\-|\.| ]?\d{2}[\-|\.| ]\d{2}$", date) is not None - if not (format1 or format2): - raise ValueError("Неправильная дата рождения") - day, mounth, year = '', '', '' - if format1: + raise TypeError(f"Неправильный тип данных поля birth_date {type(date)} is not str") + format1 = re.search("^(\d{2}[\-|\.| ]?){2}\d{4}$", date) is None + format2 = re.search("^\d{4}[\-|\.| ]?\d{2}[\-|\.| ]\d{2}$", date) is None + if format1 and format2: + raise ValueError(f"Неправильная дата рождения {date}") + if not format1: day, mounth, year = date[0:2], date[3:5], date[6:] assert (2020 - int(year)) < 150, "Неправильная дата рождения" else: year, mounth, day = date[0:4], date[5:7], date[8:] assert (2020 - int(year)) < 150, "Неправильная дата рождения" - try: - self.birth_date + if hasattr(self, '_birth_date'): self.good_log.info(f"Изменение поля birth_date объекта {self}") - self.__birth_date = '-'.join([year, mounth, day]) - except AttributeError: - self.__birth_date = '-'.join([year, mounth, day]) + self._birth_date = '-'.join([year, mounth, day]) @property def phone(self): - return self.__phone + return self._phone @phone.setter @My_logger.log def phone(self, phone): if not isinstance(phone, str): - raise TypeError("Неправильный тип данных поля phone") + raise TypeError(f"Неправильный тип данных поля phone {type(phone)} is not str") if re.search("[\+7|8]?([\(| |\-]?[\d]{3}[\)| |\-]?){2}([\-| ]?[\d]{2}[\-| ]?){2}", phone) is None: - raise ValueError("Неправильный номер телефона") + raise ValueError(f"Неправильный номер телефона {phone}") phone = ''.join(filter(str.isdigit, phone)) if len(phone) < 11: phone = '7' + phone phone = f'+7-{phone[1:4]}-{phone[4:7]}-{phone[7:9]}-{phone[9:11]}' - try: - self.phone + if hasattr(self, '_phone'): self.good_log.info(f"Изменение поля phone объекта {self}") - self.__phone = phone - except AttributeError: - self.__phone = phone + self._phone = phone @property def document_type(self): - return self.__document_type + return self._document_type @document_type.setter @My_logger.log def document_type(self, document): if not isinstance(document, str): - raise TypeError("Неправильный тип данных поля document_type") + raise TypeError(f"Неправильный тип данных поля document_type {type(document)} is not str") document = document.lower() if document not in {"паспорт", "заграничный паспорт", "водительские права"}: - raise ValueError("Неправильный тип документов") - try: - self.document_type + raise ValueError(f"Неправильный тип документов {document}") + if hasattr(self, '_document_type'): self.good_log.info(f"Изменение поля document_type объекта {self}") - self.__document_type = document - except AttributeError: - self.__document_type = document + self._document_type = document @property def document_id(self): - return self.__document_id + return self._document_id @document_id.setter @My_logger.log def document_id(self, doc_number): if not isinstance(doc_number, str): - raise TypeError("Неправильный тип данных поля document_id") + raise TypeError(f"Неправильный тип данных поля document_id {type(doc_number)} is not str") if self.document_type == "паспорт": if re.search("^([\d][ |\-|\-|\/]?){10}$", doc_number) is None: - raise ValueError("Неправильный номер паспорта") + raise ValueError(f"Неправильный номер паспорта {doc_number}") doc_number = ''.join(filter(str.isdigit, doc_number)) doc_number = doc_number[:4] + " " + doc_number[4:] if self.document_type == "заграничный паспорт": if re.search("^([\d][ |\-|\-|\/]?){9}$", doc_number) is None: - raise ValueError("Неправильный номер заграничного паспорта") + raise ValueError(f"Неправильный номер заграничного паспорта {doc_number}") doc_number = ''.join(filter(str.isdigit, doc_number)) doc_number = doc_number[:2] + " " + doc_number[2:] if self.document_type == "водительские права": if re.search("^([\d][ |\-|\-|\/]?){10}$", doc_number) is None: - raise ValueError("Неправильный номер прав") + raise ValueError(f"Неправильный номер прав {doc_number}") doc_number = ''.join(filter(str.isdigit, doc_number)) doc_number = doc_number[:2] + " " + doc_number[2:4] + " " + doc_number[4:] - try: - self.document_id + if hasattr(self, '_document_id'): self.good_log.info(f"Изменение поля document_id объекта {self}") - self.__document_id = doc_number - except AttributeError: - self.__document_id = doc_number + self._document_id = doc_number - @classmethod - def create(cls, name, surname, birth_date, phone_number, doc_type, doc_number): - return cls(name, surname, birth_date, phone_number, doc_type, doc_number) + @staticmethod + def create(*args): + return Patient(*args) def save(self, filename="Patients.csv"): - data = [self._first_name, self._last_name, self.birth_date, self.phone, self.document_type, self.document_id] + data = [self.first_name, self.last_name, self.birth_date, self.phone, self.document_type, self.document_id] data = ','.join(data) data += '\n' try: - with open(filename, 'a') as file: + with open(filename, 'a', encoding="utf-8") as file: file.write(data) self.good_log.info(f"Пациент {self} записан в файл") + except FileExistsError as err: + self.bad_log.error(f'Файл {filename} уже существует. {err}') + except FileNotFoundError as err: + self.bad_log.error(f'Файла {filename} не существует. {err}') + except IsADirectoryError as err: + self.bad_log.error(f'{filename} это директория {err}') + except PermissionError as err: + self.bad_log.error(f'У вас недостаточно прав для операции записи. {err}') except Exception as err: self.bad_log.error(f"Ошибка записи в файл: {err}") @@ -205,26 +200,23 @@ def save(self, filename="Patients.csv"): class Iterator: """Итератор для прохождения по коллекции""" - def __init__(self, collection, filename, limit=None): - self.collection = collection + def __init__(self, filename, limit=None): self.limit = limit - self.file = open(filename, 'r') + self.file = open(filename, 'r', encoding="utf-8") def __iter__(self): return self def __next__(self): - try: - line = self.file.readline() - line = line.rstrip('\n') - assert line is not "" - if self.limit is not None: - assert self.limit != 0 - self.limit -= 1 - return Patient(*line.split(',')) - except AssertionError: + line = self.file.readline() + line = line.rstrip('\n') + if line == "": raise StopIteration - + if (self.limit is not None) and self.limit == 0: + raise StopIteration + if self.limit is not None: + self.limit -= 1 + return Patient(*line.split(',')) def __del__(self): self.file.close() @@ -233,8 +225,8 @@ def __del__(self): class PatientCollection: def __init__(self, filename='Patients.csv'): - self.good_log = My_logger.logging.getLogger("logger_access") - self.bad_log = My_logger.logging.getLogger("logger_error") + self.good_log = logging.getLogger("logger_access") + self.bad_log = logging.getLogger("logger_error") self.filename = filename self.data = self.read_patients_from_file(filename) @@ -243,21 +235,19 @@ def __getitem__(self, key): def __iter__(self): - return Iterator(self.data, self.filename) + return Iterator(self.filename) def limit(self, limit=None): - return Iterator(self.data,self.filename, limit) - + return Iterator(self.filename, limit) - @staticmethod - def read_patients_from_file(filename): + def read_patients_from_file(self, filename): data = [] try: with open(filename, 'r') as file: for line in file: - p = Patient.Patient(*line.split(',')) + p = Patient(*line.split(',')) data.append(p) - except Exception: - self.bad_log.error(f"Ошибка чтения пациентов из файла {filename}") + except Exception as err: + self.bad_log.error(f"Ошибка {err} чтения пациентов из файла {filename}") finally: return tuple(data) From 8874d2d0efd9850cd5f632cc8e04d4eb3c5c8b87 Mon Sep 17 00:00:00 2001 From: Alexey Chubukov Date: Sat, 9 May 2020 13:47:40 +0300 Subject: [PATCH 3/3] HW3 v1.0 --- homework/My_logger.py | 31 +++++++- homework/cli.py | 52 +++++++++++++ homework/patient.py | 173 +++++++++++++++++++----------------------- 3 files changed, 157 insertions(+), 99 deletions(-) create mode 100644 homework/cli.py diff --git a/homework/My_logger.py b/homework/My_logger.py index 4b3bf07..9a3cb7e 100644 --- a/homework/My_logger.py +++ b/homework/My_logger.py @@ -1,5 +1,6 @@ from functools import wraps import logging +import sqlite3 formatter = logging.Formatter( @@ -18,11 +19,20 @@ handler_error.setFormatter(formatter) bad_log.addHandler(handler_error) -def log(func): + +def my_logging_decorator(func): @wraps(func) def wrapper(*args, **kwargs): try: - func(*args, **kwargs) + result = func(*args, **kwargs) + if result is None and func.__name__ == "__init__": + good_log.info(f"Поступил новый пациент: {args[0]}") + return + if result is None or func.__name__ == "read_patients_from_db": + return + # Смотрим флаг изменения поля + if result.alter_flag == 1: + good_log.info(f"Изменение поля {result.name} объекта {args[0]}") except TypeError as err: bad_log.error(f"Ошибка записи пациента: {err}") raise err @@ -34,6 +44,23 @@ def wrapper(*args, **kwargs): raise err return wrapper +def db_decorator(func): + @wraps(func) + def wrapper(*args, **kwargs): + result = None + try: + result = func(*args, **kwargs) + if result is None: + good_log.info(f"Пациент {args[0]} записан в бд") + except sqlite3.IntegrityError as err: + bad_log.error(f'пользователь {args[0]} уже существует. {err}') + except sqlite3.OperationalError as err: + bad_log.error(f'Данной таблицы не существует. {err}') + except Exception as err: + bad_log.error(f"Ошибка записи в бд: {err}") + return result + return wrapper + diff --git a/homework/cli.py b/homework/cli.py new file mode 100644 index 0000000..65bd6fb --- /dev/null +++ b/homework/cli.py @@ -0,0 +1,52 @@ +import patient +import click +import sqlite3 + +@click.group() +def cli(): + pass + +@cli.command() +def count(): + collection = patient.PatientCollection() + print(f"Количество пациентов в БД: {len(collection.data)} ") + +@cli.command() +@click.argument('limit', default=10) +def show(limit): + pc = patient.PatientCollection() + for p in pc.limit(limit): + print(p) + + + +@cli.command() +@click.argument('first_name') +@click.argument('last_name') +@click.option('--birth-date', help='Birth date of Patient', type= str) +@click.option('--phone', help='Mobile phone of Patient', type= str) +@click.option('--document-type', help='Document type of Patient', type= str) +@click.option('--document-number', help='Document number of Patient', type=str) +def create(first_name, last_name, birth_date, phone, document_type, document_number): + p = patient.Patient(first_name, last_name, birth_date, phone, document_type, document_number) + p.save() + print(f'Записан пациент: {p}') + +if __name__=="__main__": + conn = sqlite3.connect("Covid.db") + cursor = conn.cursor() + cursor.execute(""" + CREATE TABLE IF NOT EXISTS patients ( + first_name varchar(15), + last_name varchar(15), + birth_date char(10), + phone char(16), + document_type varchar(19), + document_id varchar(12), + PRIMARY KEY (document_id) + ) + """) + conn.commit() + cursor.close() + conn.close() + cli() diff --git a/homework/patient.py b/homework/patient.py index 261b007..d8fb0e3 100644 --- a/homework/patient.py +++ b/homework/patient.py @@ -1,37 +1,24 @@ -from homework import My_logger -import logging +import My_logger +#from homework import My_logger +import sqlite3 import re +from collections import namedtuple class Patient: """Класс содержит основные данные о пациенте""" - first_name = NotImplemented - last_name = NotImplemented - + @My_logger.my_logging_decorator def __init__(self, name, surname, birth_date, phone_number, doc_type, doc_number): - self.good_log = logging.getLogger("logger_access") - self.bad_log = logging.getLogger("logger_error") - try: - self.first_name_ = name - self.last_name_ = surname - self.birth_date = birth_date - self.phone = phone_number - self.document_type = doc_type - self.document_id = doc_number - self.good_log.info(f"Поступил новый пациент: {self}") - except TypeError as err: - self.bad_log.error(f"Ошибка записи пациента: {err}") - raise err - except ValueError as err: - self.bad_log.error(f"Ошибка записи пациента: {err}") - raise err - except Exception as err: - self.bad_log.error(f"{err}") - raise err + self.first_name = name + self.last_name = surname + self.birth_date = birth_date + self.phone = phone_number + self.document_type = doc_type + self.document_id = doc_number def __str__(self): - out = f"{self._first_name} " \ - + f"{self._last_name} " \ + out = f"{self.first_name} " \ + + f"{self.last_name} " \ + f"{self.birth_date} " \ + f"{self.phone} " \ + f"{self.document_type} " \ @@ -45,51 +32,37 @@ def first_name(self): return self._first_name @first_name.setter - @My_logger.log + @My_logger.my_logging_decorator def first_name(self, name): - raise AttributeError("Нельзя изменять значения поля first_name") - - - @property - def last_name(self): - return self._last_name - - @last_name.setter - @My_logger.log - def last_name(self, name): - raise AttributeError("Нельзя изменять значения поля last_name") - - - @property - def first_name_(self): - return self._first_name - - @first_name_.setter - def first_name_(self, name): + if "_first_name" in self.__dict__: + raise AttributeError("Нельзя изменять значения поля first_name") if not isinstance(name, str): raise TypeError(f"Неправильный тип данных поля first_name {type(name)} is not str") if not name.isalpha(): raise ValueError(f"Имя должно содержать только символы {name}") - self._first_name = name + self.__dict__["_first_name"] = name @property - def last_name_(self): + def last_name(self): return self._last_name - @last_name_.setter - def last_name_(self, surname): + @last_name.setter + @My_logger.my_logging_decorator + def last_name(self, surname): + if "_last_name" in self.__dict__: + raise AttributeError("Нельзя изменять значения поля last_name") if not isinstance(surname, str): raise TypeError(f"Неправильный тип данных поля last_name {type(surname)} is not str") if not surname.isalpha(): raise ValueError(f"Фамилия должна содержать только символы {surname}") - self._last_name = surname + self.__dict__["_last_name"] = surname @property def birth_date(self): return self._birth_date @birth_date.setter - @My_logger.log + @My_logger.my_logging_decorator def birth_date(self, date): if not isinstance(date, str): raise TypeError(f"Неправильный тип данных поля birth_date {type(date)} is not str") @@ -103,9 +76,11 @@ def birth_date(self, date): else: year, mounth, day = date[0:4], date[5:7], date[8:] assert (2020 - int(year)) < 150, "Неправильная дата рождения" + alter_date = False if hasattr(self, '_birth_date'): - self.good_log.info(f"Изменение поля birth_date объекта {self}") + alter_date = True self._birth_date = '-'.join([year, mounth, day]) + return namedtuple("_", "alter_flag name")(alter_date,'birth_date') @property @@ -113,7 +88,7 @@ def phone(self): return self._phone @phone.setter - @My_logger.log + @My_logger.my_logging_decorator def phone(self, phone): if not isinstance(phone, str): raise TypeError(f"Неправильный тип данных поля phone {type(phone)} is not str") @@ -123,25 +98,29 @@ def phone(self, phone): if len(phone) < 11: phone = '7' + phone phone = f'+7-{phone[1:4]}-{phone[4:7]}-{phone[7:9]}-{phone[9:11]}' + alter_phone = False if hasattr(self, '_phone'): - self.good_log.info(f"Изменение поля phone объекта {self}") + alter_phone = True self._phone = phone + return namedtuple("_", "alter_flag name")(alter_phone, 'phone') @property def document_type(self): return self._document_type @document_type.setter - @My_logger.log + @My_logger.my_logging_decorator def document_type(self, document): if not isinstance(document, str): raise TypeError(f"Неправильный тип данных поля document_type {type(document)} is not str") document = document.lower() if document not in {"паспорт", "заграничный паспорт", "водительские права"}: raise ValueError(f"Неправильный тип документов {document}") + alter_doc_type = False if hasattr(self, '_document_type'): - self.good_log.info(f"Изменение поля document_type объекта {self}") + alter_doc_type = True self._document_type = document + return namedtuple("_", "alter_flag name")(alter_doc_type, 'document_type') @property @@ -149,7 +128,7 @@ def document_id(self): return self._document_id @document_id.setter - @My_logger.log + @My_logger.my_logging_decorator def document_id(self, doc_number): if not isinstance(doc_number, str): raise TypeError(f"Неправильный тип данных поля document_id {type(doc_number)} is not str") @@ -168,86 +147,86 @@ def document_id(self, doc_number): raise ValueError(f"Неправильный номер прав {doc_number}") doc_number = ''.join(filter(str.isdigit, doc_number)) doc_number = doc_number[:2] + " " + doc_number[2:4] + " " + doc_number[4:] + alter_doc_id = False if hasattr(self, '_document_id'): - self.good_log.info(f"Изменение поля document_id объекта {self}") + alter_doc_id = True self._document_id = doc_number + return namedtuple("_", "alter_flag name")(alter_doc_id, 'document_id') + @staticmethod def create(*args): return Patient(*args) - def save(self, filename="Patients.csv"): + @My_logger.db_decorator + def save(self, db_name='patients'): data = [self.first_name, self.last_name, self.birth_date, self.phone, self.document_type, self.document_id] - data = ','.join(data) - data += '\n' try: - with open(filename, 'a', encoding="utf-8") as file: - file.write(data) - self.good_log.info(f"Пациент {self} записан в файл") - except FileExistsError as err: - self.bad_log.error(f'Файл {filename} уже существует. {err}') - except FileNotFoundError as err: - self.bad_log.error(f'Файла {filename} не существует. {err}') - except IsADirectoryError as err: - self.bad_log.error(f'{filename} это директория {err}') - except PermissionError as err: - self.bad_log.error(f'У вас недостаточно прав для операции записи. {err}') - except Exception as err: - self.bad_log.error(f"Ошибка записи в файл: {err}") + conn = sqlite3.connect("Covid.db") + cursor = conn.cursor() + cursor.execute("""INSERT INTO patients VALUES ('{0}','{1}','{2}','{3}','{4}','{5}')""".format(*data)) + conn.commit() + finally: + cursor.close() + conn.close() class Iterator: """Итератор для прохождения по коллекции""" - def __init__(self, filename, limit=None): + def __init__(self, db_name, limit=None): self.limit = limit - self.file = open(filename, 'r', encoding="utf-8") + self.conn = sqlite3.connect(db_name) + self.cursor = self.conn.cursor() + self.id = 1 def __iter__(self): return self def __next__(self): - line = self.file.readline() - line = line.rstrip('\n') - if line == "": + line = self.cursor.execute(f'SELECT * FROM patients where rowid={self.id}') + line = line.fetchone() + if line is None: raise StopIteration if (self.limit is not None) and self.limit == 0: raise StopIteration if self.limit is not None: self.limit -= 1 - return Patient(*line.split(',')) + self.id += 1 + return Patient(*list(line)) def __del__(self): - self.file.close() + self.cursor.close() + self.conn.close() class PatientCollection: - def __init__(self, filename='Patients.csv'): - self.good_log = logging.getLogger("logger_access") - self.bad_log = logging.getLogger("logger_error") - self.filename = filename - self.data = self.read_patients_from_file(filename) + def __init__(self, db_name='Covid.db'): + self.db_name = db_name + self.data = self.read_patients_from_db(self.db_name) def __getitem__(self, key): return self.data[key] def __iter__(self): - return Iterator(self.filename) + return Iterator(self.db_name) def limit(self, limit=None): - return Iterator(self.filename, limit) + return Iterator(self.db_name, limit) - def read_patients_from_file(self, filename): + @My_logger.db_decorator + def read_patients_from_db(self, db_name): data = [] try: - with open(filename, 'r') as file: - for line in file: - p = Patient(*line.split(',')) - data.append(p) - except Exception as err: - self.bad_log.error(f"Ошибка {err} чтения пациентов из файла {filename}") + conn = sqlite3.connect(db_name) + cursor = conn.cursor() + for p in cursor.execute("SELECT * FROM patients"): + data.append(Patient(*list(p))) finally: - return tuple(data) + cursor.close() + conn.close() + return tuple(data) + \ No newline at end of file