From e430a043eeacd3ea0d5c8fd88f313e388b0db902 Mon Sep 17 00:00:00 2001 From: exeynod Date: Tue, 28 Apr 2020 12:54:25 +0300 Subject: [PATCH 1/7] All test passed --- homework/config.py | 14 +-- homework/log.py | 19 +++ homework/patient.py | 198 +++++++++++++++++++++++++++++-- tests/test_patient.py | 6 +- tests/test_patient_collection.py | 16 +-- 5 files changed, 227 insertions(+), 26 deletions(-) create mode 100644 homework/log.py diff --git a/homework/config.py b/homework/config.py index 955b991..093bc24 100644 --- a/homework/config.py +++ b/homework/config.py @@ -1,13 +1,13 @@ -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 = "good_log.log" +ERROR_LOG_FILE = "error_log.log" +CSV_PATH = "data.csv" +PHONE_FORMAT = "9160000000" # Здесь запишите телефон +7-916-000-00-00 в том формате, в котором вы храните телефоны PASSPORT_TYPE = "паспорт" # тип документа, когда он паспорт -PASSPORT_FORMAT = "0000 000000" # Здесь запишите номер парспорта 0000 000000 в том формате, в котором вы его храните +PASSPORT_FORMAT = "0000000000" # Здесь запишите номер парспорта 0000 000000 в том формате, в котором вы его храните INTERNATIONAL_PASSPORT_TYPE = "заграничный паспорт" # тип документа, если это загран -INTERNATIONAL_PASSPORT_FORMAT = "00 0000000" # формат хранения заграна для номера 00 0000000 +INTERNATIONAL_PASSPORT_FORMAT = "000000000" # формат хранения заграна для номера 00 0000000 DRIVER_LICENSE_TYPE = "водительское удостоверение" # тип документа, если это водительское удостоверение -DRIVER_LICENSE_FORMAT = "00 00 000000" # формат хранения номера ВУ +DRIVER_LICENSE_FORMAT = "0000000000" # формат хранения номера ВУ diff --git a/homework/log.py b/homework/log.py new file mode 100644 index 0000000..b982659 --- /dev/null +++ b/homework/log.py @@ -0,0 +1,19 @@ +import logging + +# info_logger_setup +info_logger = logging.getLogger('Info_Logger') +info_logger.setLevel(logging.INFO) +fh_info = logging.FileHandler('good_log.log', encoding='utf-8') +fh_info.setLevel(logging.INFO) +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +fh_info.setFormatter(formatter) +info_logger.addHandler(fh_info) + +# error_logger_setup +error_logger = logging.getLogger('Error_Logger') +error_logger.setLevel(logging.ERROR) +fh_error = logging.FileHandler('error_log.log', encoding='utf-8') +fh_error.setLevel(logging.ERROR) +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +fh_error.setFormatter(formatter) +error_logger.addHandler(fh_error) \ No newline at end of file diff --git a/homework/patient.py b/homework/patient.py index dad2526..b90d5fc 100644 --- a/homework/patient.py +++ b/homework/patient.py @@ -1,17 +1,199 @@ +from homework.log import error_logger, info_logger +import csv + +csv_path = 'data.csv' + + class Patient: - def __init__(self, *args, **kwargs): - pass + @staticmethod + def valid_date(date): + import time + try: + time.strptime(date, '%Y-%m-%d') + res = '-' + except ValueError: + try: + time.strptime(date, '%Y.%m.%d') + res = '.' + except ValueError: + error_logger.error(f'{date}: invalid date format') + res = None + return res + + @staticmethod + def valid_phone(phone): + res = phone.replace('+', '').replace('-', '').replace('(', '').replace(')', '').replace(' ', '') + if len(res) == 11 and res[0] in {'7', '8'}: + res = res[1:] + return res + if len(res) == 10: + return res + error_logger.error(f'{phone} Invalid phone format') + return None + + @staticmethod + def valid_document(document_type, document_id): + document = document_id.replace(' ', '').replace('-', '').replace('/', '') + if not document.isdigit(): + error_logger.error(f'{document_id}: invalid document id') + return False + if document_type in {'водительское удостоверение', 'паспорт'}: + return len(document) == 10 + if document_type == 'заграничный паспорт': + return len(document) == 9 + error_logger.error(f'{document_id}: invalid document id') + return False + + def __init__(self, fname, lname, birth_date, phone, document_type, document_id): + for string in (fname, lname, birth_date, phone, document_id, document_type): + if not isinstance(string, str): + error_logger.error(f'{string} must be string') + raise TypeError + for word in (fname, lname): + if not word.isalpha(): + error_logger.error(f'{word} must be an alpha') + raise ValueError + self.__fname = fname + self.__lname = lname + sep = self.valid_date(birth_date) + if sep: + self._bd = '-'.join(birth_date.split(sep)) + else: + raise ValueError('Invalid date format') + self._ph = self.valid_phone(phone) + if not self.phone: + raise ValueError('Invalid phone format') + if not self.valid_document(document_type, document_id): + raise ValueError('Invalid document id') + if document_type not in {'паспорт', 'заграничный паспорт', 'водительское удостоверение'}: + raise ValueError('Invalid document type') + self._dt = document_type + self._did = document_id.replace(' ', '').replace('-', '').replace('/', '') + info_logger.info('Successfully added new patient') + + @property + def document_type(self): + return self._dt + + @document_type.setter + def document_type(self, val): + if not isinstance(val, str): + error_logger.error(f'{val} must be string') + raise TypeError + if val not in {'паспорт', 'заграничный паспорт', 'водительское удостоверение'}: + error_logger.error(f'{val}: invalid document type') + raise ValueError + info_logger.info(f'{self._dt} will be changed to {val}') + self._dt = val + + @property + def document_id(self): + return self._did + + @document_id.setter + def document_id(self, val): + if not isinstance(val, str): + error_logger.error(f'{val} must be string') + raise TypeError + if not self.valid_document(self._dt, val): + error_logger.error(f'{val} invalid document id') + raise ValueError('Wrong document') + info_logger.info(f'{self._did} will be changed to {val}') + self._did = val - def create(*args, **kwargs): - raise NotImplementedError() + @property + def first_name(self): + return self.__fname + + @first_name.setter + def first_name(self, val): + error_logger.error('First name assignment forbidden') + raise AttributeError('First name assignment forbidden') + + @property + def last_name(self): + return self.__lname + + @last_name.setter + def last_name(self, val): + error_logger.error('Last name assignment forbidden') + raise AttributeError('Last name assignment forbidden') + + @property + def birth_date(self): + return self._bd + + @birth_date.setter + def birth_date(self, val): + if not isinstance(val, str): + error_logger.error(f'{val} must be string') + raise TypeError('Invalid date type. Need str') + sep = self.valid_date(val) + if sep: + self._bd = '-'.join(val.split(sep)) + else: + error_logger.error(f'{val}: invalid date format') + raise ValueError('Invalid date format') + info_logger.info(f'{self._bd} will be changed to {val}') + + @property + def phone(self): + return self._ph + + @phone.setter + def phone(self, val): + if not isinstance(val, str): + error_logger.error(f'{val} must be string') + raise TypeError('Invalid phone type. Need str') + self._ph = self.valid_phone(val) + if not self._ph: + error_logger.error(f'{val}: invalid phone format') + raise ValueError('Invalid phone format') + info_logger.info(f'{self._ph} will be changed to {val}') + + def create(fname, lname, birth_date, phone, document_type, document_id): + return Patient(fname, lname, birth_date, phone, document_type, document_id) def save(self): - pass + try: + with open(csv_path, "a", newline='', encoding='utf-8') as csv_file: + writer = csv.writer(csv_file, delimiter='|') + writer.writerow([self.first_name, self.last_name, self.birth_date, self.phone, + self.document_type, self.document_id]) + except FileExistsError: + error_logger.error(f'Raise FileExistsError in save() with {self}') + except FileNotFoundError: + error_logger.error(f'Raise FileNotFoundError in save() with {self}') + except IsADirectoryError: + error_logger.error(f'Raise IsADirectoryError in save() with {self}') + except PermissionError: + error_logger.error(f'Raise PermissionError in save() with {self}') + else: + info_logger.info(f'patient {self} was successfully added to file') class PatientCollection: - def __init__(self, log_file): - pass + def __init__(self, data_file): + file = open(data_file, encoding='utf-8') + reader = csv.reader(file, delimiter='|') + self.data = [] + for row in reader: + self.data.append(Patient(*row)) def limit(self, n): - raise NotImplementedError() + counter = 0 + with open(csv_path, 'r', encoding='utf-8') as file: + while True: + line = file.readline() + if not line or counter == n: + break + yield Patient(*line.strip().split('|')) + counter += 1 + + def __iter__(self): + with open(csv_path, 'r', encoding='utf-8') as file: + while True: + line = file.readline() + if not line: + break + yield Patient(*line.strip().split('|')) \ No newline at end of file diff --git a/tests/test_patient.py b/tests/test_patient.py index 125bd59..a442599 100644 --- a/tests/test_patient.py +++ b/tests/test_patient.py @@ -31,13 +31,13 @@ def wrapper(*args, **kwargs): return deco -def setup(): +def setup_module(__main__): for file in [GOOD_LOG_FILE, ERROR_LOG_FILE, CSV_PATH]: with open(file, 'w', encoding='utf-8') as f: f.write('') -def teardown(): +def teardown_module(__name__): for file in [GOOD_LOG_FILE, ERROR_LOG_FILE, CSV_PATH]: os.remove(file) @@ -180,7 +180,7 @@ def test_wrong_type_assignment(patient, field, param): )) @check_log_size("error", increased=True) @check_log_size("good") -def test_wrong_type_assignment(patient, field, param): +def test_wrong_value_assignment(patient, field, param): try: setattr(patient, field, param) assert False, f"ValueError for {field} assignment not invoked" diff --git a/tests/test_patient_collection.py b/tests/test_patient_collection.py index d1036e5..9536eff 100644 --- a/tests/test_patient_collection.py +++ b/tests/test_patient_collection.py @@ -7,17 +7,17 @@ from tests.constants import PATIENT_FIELDS GOOD_PARAMS = ( - ("Кондрат", "Рюрик", "1971-01-31", "79160000000", PASSPORT_TYPE, "0228 000000"), - ("Евпатий", "Коловрат", "1972-01-31", "79160000001", PASSPORT_TYPE, "0228 000001"), + ("Кондрат", "Рюрик", "1971-01-11", "79160000000", PASSPORT_TYPE, "0228 000000"), + ("Евпатий", "Коловрат", "1972-01-11", "79160000001", PASSPORT_TYPE, "0228 000001"), ("Ада", "Лавлейс", "1978-01-21", "79160000002", PASSPORT_TYPE, "0228 000002"), - ("Миртл", "Плакса", "1880-01-31", "79160000003", PASSPORT_TYPE, "0228 000003"), - ("Евлампия", "Фамилия", "1999-01-31", "79160000004", PASSPORT_TYPE, "0228 000004"), - ("Кузя", "Кузьмин", "2000-01-31", "79160000005", PASSPORT_TYPE, "0228 000005"), - ("Гарри", "Поттер", "2020-01-31", "79160000006", PASSPORT_TYPE, "0228 000006"), - ("Рон", "Уизли", "1900-04-31", "79160000007", PASSPORT_TYPE, "0228 000007"), + ("Миртл", "Плакса", "1880-01-11", "79160000003", PASSPORT_TYPE, "0228 000003"), + ("Евлампия", "Фамилия", "1999-01-21", "79160000004", PASSPORT_TYPE, "0228 000004"), + ("Кузя", "Кузьмин", "2000-01-21", "79160000005", PASSPORT_TYPE, "0228 000005"), + ("Гарри", "Поттер", "2020-01-11", "79160000006", PASSPORT_TYPE, "0228 000006"), + ("Рон", "Уизли", "1900-04-20", "79160000007", PASSPORT_TYPE, "0228 000007"), ("Билл", "Гейтс", "1978-12-31", "79160000008", PASSPORT_TYPE, "0228 000008"), ("Владимир", "Джугашвили", "1912-01-31", "79160000009", PASSPORT_TYPE, "0228 000009"), - ("Вован", "ДеМорт", "1978-11-31", "79160000010", PASSPORT_TYPE, "0228 000010"), + ("Вован", "ДеМорт", "1978-11-30", "79160000010", PASSPORT_TYPE, "0228 000010"), ("Гопник", "Районный", "1978-01-25", "79160000011", PASSPORT_TYPE, "0228 000011"), ("Фёдор", "Достоевский", "1978-01-05", "79160000012", PASSPORT_TYPE, "0228 000012"), ) From 63340421a2ef3b33d10de14737ecd865541efcd2 Mon Sep 17 00:00:00 2001 From: exeynod Date: Thu, 30 Apr 2020 12:06:41 +0300 Subject: [PATCH 2/7] =?UTF-8?q?=D0=9D=D0=B5=20=D1=81=D0=BE=D0=B2=D1=81?= =?UTF-8?q?=D0=B5=D0=BC=20=D0=BF=D0=BE=D0=BD=D1=8F=D0=BB=20=D0=BA=D0=B0?= =?UTF-8?q?=D0=BA=20=D0=B7=D0=B0=D0=BA=D1=80=D1=8B=D1=82=D1=8C=20=D0=BB?= =?UTF-8?q?=D0=BE=D0=B3=20=D0=B2=20=D1=81=D0=B2=D0=BE=D0=B5=D0=BC=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=B4=D0=B5,=20=D0=BF=D0=BE=D1=8D=D1=82=D0=BE=D0=BC?= =?UTF-8?q?=D1=83=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20=D0=BA?= =?UTF-8?q?=D0=BE=D1=81=D1=82=D1=8B=D0=BB=D1=8C,=20=D1=87=D1=82=D0=BE?= =?UTF-8?q?=D0=B1=D1=8B=20=D0=B8=D0=B7=D0=B1=D0=B5=D0=B6=D0=B0=D1=82=D1=8C?= =?UTF-8?q?=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D1=83=20=D0=B4=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D1=83=D0=BF=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_patient.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_patient.py b/tests/test_patient.py index a442599..f811c96 100644 --- a/tests/test_patient.py +++ b/tests/test_patient.py @@ -39,7 +39,10 @@ def setup_module(__main__): def teardown_module(__name__): for file in [GOOD_LOG_FILE, ERROR_LOG_FILE, CSV_PATH]: - os.remove(file) + try: + os.remove(file) + except: + print('Error access') @check_log_size("error") From 3fb767dc29f1a29513a1ed508c16ccd821901660 Mon Sep 17 00:00:00 2001 From: exeynod Date: Sat, 2 May 2020 11:56:05 +0300 Subject: [PATCH 3/7] Refactored code --- homework/patient.py | 250 ++++++++++++++++------------------------ homework/vald_values.py | 39 +++++++ 2 files changed, 139 insertions(+), 150 deletions(-) create mode 100644 homework/vald_values.py diff --git a/homework/patient.py b/homework/patient.py index b90d5fc..b214865 100644 --- a/homework/patient.py +++ b/homework/patient.py @@ -1,158 +1,115 @@ +from itertools import islice +import homework.vald_values as valid from homework.log import error_logger, info_logger import csv csv_path = 'data.csv' -class Patient: - @staticmethod - def valid_date(date): - import time - try: - time.strptime(date, '%Y-%m-%d') - res = '-' - except ValueError: - try: - time.strptime(date, '%Y.%m.%d') - res = '.' - except ValueError: - error_logger.error(f'{date}: invalid date format') - res = None - return res +class BasicDescriptor: + def __init__(self, name): + self.name = name - @staticmethod - def valid_phone(phone): - res = phone.replace('+', '').replace('-', '').replace('(', '').replace(')', '').replace(' ', '') - if len(res) == 11 and res[0] in {'7', '8'}: - res = res[1:] - return res - if len(res) == 10: - return res - error_logger.error(f'{phone} Invalid phone format') - return None + def __get__(self, instance, owner): + return instance.__dict__[self.name] + + def __set__(self, instance, value): + pass @staticmethod - def valid_document(document_type, document_id): - document = document_id.replace(' ', '').replace('-', '').replace('/', '') - if not document.isdigit(): - error_logger.error(f'{document_id}: invalid document id') - return False - if document_type in {'водительское удостоверение', 'паспорт'}: - return len(document) == 10 - if document_type == 'заграничный паспорт': - return len(document) == 9 - error_logger.error(f'{document_id}: invalid document id') - return False - - def __init__(self, fname, lname, birth_date, phone, document_type, document_id): - for string in (fname, lname, birth_date, phone, document_id, document_type): - if not isinstance(string, str): - error_logger.error(f'{string} must be string') - raise TypeError - for word in (fname, lname): - if not word.isalpha(): - error_logger.error(f'{word} must be an alpha') - raise ValueError - self.__fname = fname - self.__lname = lname - sep = self.valid_date(birth_date) - if sep: - self._bd = '-'.join(birth_date.split(sep)) - else: - raise ValueError('Invalid date format') - self._ph = self.valid_phone(phone) - if not self.phone: - raise ValueError('Invalid phone format') - if not self.valid_document(document_type, document_id): - raise ValueError('Invalid document id') - if document_type not in {'паспорт', 'заграничный паспорт', 'водительское удостоверение'}: - raise ValueError('Invalid document type') - self._dt = document_type - self._did = document_id.replace(' ', '').replace('-', '').replace('/', '') - info_logger.info('Successfully added new patient') - - @property - def document_type(self): - return self._dt - - @document_type.setter - def document_type(self, val): - if not isinstance(val, str): - error_logger.error(f'{val} must be string') - raise TypeError - if val not in {'паспорт', 'заграничный паспорт', 'водительское удостоверение'}: - error_logger.error(f'{val}: invalid document type') - raise ValueError - info_logger.info(f'{self._dt} will be changed to {val}') - self._dt = val - - @property - def document_id(self): - return self._did - - @document_id.setter - def document_id(self, val): - if not isinstance(val, str): - error_logger.error(f'{val} must be string') - raise TypeError - if not self.valid_document(self._dt, val): - error_logger.error(f'{val} invalid document id') - raise ValueError('Wrong document') - info_logger.info(f'{self._did} will be changed to {val}') - self._did = val - - @property - def first_name(self): - return self.__fname - - @first_name.setter - def first_name(self, val): - error_logger.error('First name assignment forbidden') - raise AttributeError('First name assignment forbidden') - - @property - def last_name(self): - return self.__lname - - @last_name.setter - def last_name(self, val): - error_logger.error('Last name assignment forbidden') - raise AttributeError('Last name assignment forbidden') - - @property - def birth_date(self): - return self._bd - - @birth_date.setter - def birth_date(self, val): + def check_type(val): if not isinstance(val, str): - error_logger.error(f'{val} must be string') - raise TypeError('Invalid date type. Need str') - sep = self.valid_date(val) - if sep: - self._bd = '-'.join(val.split(sep)) + s = f'{val} must be a string not {type(val)}' + error_logger.error(s) + raise TypeError(s) + + +class ImmutableData(BasicDescriptor): + def __set__(self, instance, value): + self.check_type(value) + if self.name not in instance.__dict__: + if value.isalpha(): + instance.__dict__[self.name] = value + else: + s = f'{value} must be an alpha' + error_logger.error(s) + raise ValueError(s) + else: + s = 'Name changes are forbidden' + error_logger.error(s) + raise AttributeError(s) + + +class MutableData(BasicDescriptor): + def __init__(self, name): + super().__init__(name) + if self.name == 'phone': + self.validate = valid.valid_phone + elif self.name == 'birth_date': + self.validate = valid.valid_date + elif self.name == 'document_type': + self.validate = valid.valid_document_type + else: + self.validate = valid.valid_document + + def __set__(self, instance, value): + self.check_type(value) + new_val = self.validate(value) + if new_val: + if self.name in instance.__dict__: + info_logger.info(f'{self.name} set to {new_val}') + instance.__dict__[self.name] = new_val else: - error_logger.error(f'{val}: invalid date format') - raise ValueError('Invalid date format') - info_logger.info(f'{self._bd} will be changed to {val}') + s = f'{value} invalid data format' + error_logger.error(s) + raise ValueError(s) + + +class DocDescriptor(BasicDescriptor): + def __set__(self, instance, value): + if self.name == "document_id": + self.check_type(value) + new_doc = valid.valid_document((instance.document_type, value)) + if new_doc: + if self.name in instance.__dict__: + info_logger.info(f'{self.name} set to {new_doc}') + instance.__dict__[self.name] = new_doc + else: + s = 'Invalid document id' + error_logger.error(s) + raise ValueError(s) + elif self.name == "document_type": + self.check_type(value) + new_type = valid.valid_document_type(value) + if new_type: + if self.name in instance.__dict__: + info_logger.info("Type was changed") + instance.__dict__[self.name] = value + else: + error_logger.error(f"Invalid document type: {value}") + raise ValueError("Invalid document type") - @property - def phone(self): - return self._ph - @phone.setter - def phone(self, val): - if not isinstance(val, str): - error_logger.error(f'{val} must be string') - raise TypeError('Invalid phone type. Need str') - self._ph = self.valid_phone(val) - if not self._ph: - error_logger.error(f'{val}: invalid phone format') - raise ValueError('Invalid phone format') - info_logger.info(f'{self._ph} will be changed to {val}') +class Patient: + first_name = ImmutableData('first_name') + last_name = ImmutableData('last_name') + birth_date = MutableData('birth_date') + phone = MutableData('phone') + document_id = DocDescriptor('document_id') + document_type = DocDescriptor('document_type') + + def __init__(self, first_name, last_name, birth_date, phone, document_type, document_id): + self.first_name = first_name + self.last_name = last_name + self.birth_date = birth_date + self.phone = phone + self.document_type = document_type + self.document_id = document_id + info_logger.info(f'{first_name} {last_name} patient created') - def create(fname, lname, birth_date, phone, document_type, document_id): - return Patient(fname, lname, birth_date, phone, document_type, document_id) + @staticmethod + def create(first_name, last_name, birth_date, phone, document_type, document_id): + return Patient(first_name, last_name, birth_date, phone, document_type, document_id) def save(self): try: @@ -181,14 +138,7 @@ def __init__(self, data_file): self.data.append(Patient(*row)) def limit(self, n): - counter = 0 - with open(csv_path, 'r', encoding='utf-8') as file: - while True: - line = file.readline() - if not line or counter == n: - break - yield Patient(*line.strip().split('|')) - counter += 1 + return islice(self, n) def __iter__(self): with open(csv_path, 'r', encoding='utf-8') as file: @@ -196,4 +146,4 @@ def __iter__(self): line = file.readline() if not line: break - yield Patient(*line.strip().split('|')) \ No newline at end of file + yield Patient(*line.strip().split('|')) diff --git a/homework/vald_values.py b/homework/vald_values.py new file mode 100644 index 0000000..8cb0611 --- /dev/null +++ b/homework/vald_values.py @@ -0,0 +1,39 @@ +def valid_date(date): + import time + try: + time.strptime(date, '%Y-%m-%d') + res = '-' + except ValueError: + try: + time.strptime(date, '%Y.%m.%d') + res = '.' + except ValueError: + return None + return '-'.join(date.split(res)) + + +def valid_phone(phone): + res = phone.replace('+', '').replace('-', '').replace('(', '').replace(')', '').replace(' ', '') + if len(res) == 11 and res[0] in {'7', '8'}: + res = res[1:] + return res + if len(res) == 10: + return res + return None + + +def valid_document(document): + document_id = document[1].replace(' ', '').replace('-', '').replace('/', '') + if not document_id.isdigit(): + return None + if document[0] in {'водительское удостоверение', 'паспорт'} and len(document_id) == 10: + return document_id + if document[0] == 'заграничный паспорт' and len(document_id) == 9: + return document_id + return None + + +def valid_document_type(document_type): + if document_type in {'водительское удостоверение', 'паспорт', 'заграничный паспорт'}: + return document_type + return None From b1ec27308ab1dedf6526d3b9f7550b6ae77c385d Mon Sep 17 00:00:00 2001 From: exeynod Date: Sat, 2 May 2020 14:46:37 +0300 Subject: [PATCH 4/7] Introduced decorators --- homework/log.py | 37 ++++++++++++++++++++++++++++++++++++- homework/patient.py | 43 +++++++++++++++++-------------------------- 2 files changed, 53 insertions(+), 27 deletions(-) diff --git a/homework/log.py b/homework/log.py index b982659..9af40ca 100644 --- a/homework/log.py +++ b/homework/log.py @@ -16,4 +16,39 @@ fh_error.setLevel(logging.ERROR) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') fh_error.setFormatter(formatter) -error_logger.addHandler(fh_error) \ No newline at end of file +error_logger.addHandler(fh_error) + + +def set_logger(func): + def wrapper(self, instance, value): + new_value = value + try: + func(self, instance, value) + except TypeError: + instance.error_logger.error(f'{value} must by string') + raise TypeError + except ValueError: + instance.error_logger.error(f'Wrong format : {value}') + raise ValueError + except AttributeError: + instance.error_logger.error(f'Try to set {self.name} of {instance}') + raise AttributeError + return wrapper + + +def save_logger(func): + def wrapper(self, *args, **kwargs): + try: + func(self, *args, **kwargs) + except FileExistsError: + self.error_logger.error(f'Raise FileExistsError in save() with {self}') + except FileNotFoundError: + self.error_logger.error(f'Raise FileNotFoundError in save() with {self}') + except IsADirectoryError: + self.error_logger.error(f'Raise IsADirectoryError in save() with {self}') + except PermissionError: + self.error_logger.error(f'Raise PermissionError in save() with {self}') + else: + self.info_logger.info(f'patient {self} was successfully added to file') + + return wrapper \ No newline at end of file diff --git a/homework/patient.py b/homework/patient.py index b214865..1429b35 100644 --- a/homework/patient.py +++ b/homework/patient.py @@ -1,6 +1,7 @@ from itertools import islice import homework.vald_values as valid -from homework.log import error_logger, info_logger +import logging +from homework.log import set_logger, save_logger import csv csv_path = 'data.csv' @@ -20,11 +21,12 @@ def __set__(self, instance, value): def check_type(val): if not isinstance(val, str): s = f'{val} must be a string not {type(val)}' - error_logger.error(s) + logging.getLogger('Error_Logger').error(s) raise TypeError(s) class ImmutableData(BasicDescriptor): + @set_logger def __set__(self, instance, value): self.check_type(value) if self.name not in instance.__dict__: @@ -32,11 +34,9 @@ def __set__(self, instance, value): instance.__dict__[self.name] = value else: s = f'{value} must be an alpha' - error_logger.error(s) raise ValueError(s) else: s = 'Name changes are forbidden' - error_logger.error(s) raise AttributeError(s) @@ -52,41 +52,40 @@ def __init__(self, name): else: self.validate = valid.valid_document + @set_logger def __set__(self, instance, value): self.check_type(value) new_val = self.validate(value) if new_val: if self.name in instance.__dict__: - info_logger.info(f'{self.name} set to {new_val}') + logging.getLogger('Info_Logger').info(f'{self.name} set to {new_val}') instance.__dict__[self.name] = new_val else: s = f'{value} invalid data format' - error_logger.error(s) raise ValueError(s) class DocDescriptor(BasicDescriptor): + @set_logger def __set__(self, instance, value): if self.name == "document_id": self.check_type(value) new_doc = valid.valid_document((instance.document_type, value)) if new_doc: if self.name in instance.__dict__: - info_logger.info(f'{self.name} set to {new_doc}') + logging.getLogger('Info_Logger').info(f'{self.name} set to {new_doc}') instance.__dict__[self.name] = new_doc else: s = 'Invalid document id' - error_logger.error(s) raise ValueError(s) elif self.name == "document_type": self.check_type(value) new_type = valid.valid_document_type(value) if new_type: if self.name in instance.__dict__: - info_logger.info("Type was changed") + logging.getLogger('Info_Logger').info("Type was changed") instance.__dict__[self.name] = value else: - error_logger.error(f"Invalid document type: {value}") raise ValueError("Invalid document type") @@ -99,34 +98,26 @@ class Patient: document_type = DocDescriptor('document_type') def __init__(self, first_name, last_name, birth_date, phone, document_type, document_id): + self.info_logger = logging.getLogger('Info_Logger') + self.error_logger = logging.getLogger('Error_Logger') self.first_name = first_name self.last_name = last_name self.birth_date = birth_date self.phone = phone self.document_type = document_type self.document_id = document_id - info_logger.info(f'{first_name} {last_name} patient created') + self.info_logger.info(f'{first_name} {last_name} patient created') @staticmethod def create(first_name, last_name, birth_date, phone, document_type, document_id): return Patient(first_name, last_name, birth_date, phone, document_type, document_id) + @save_logger def save(self): - try: - with open(csv_path, "a", newline='', encoding='utf-8') as csv_file: - writer = csv.writer(csv_file, delimiter='|') - writer.writerow([self.first_name, self.last_name, self.birth_date, self.phone, - self.document_type, self.document_id]) - except FileExistsError: - error_logger.error(f'Raise FileExistsError in save() with {self}') - except FileNotFoundError: - error_logger.error(f'Raise FileNotFoundError in save() with {self}') - except IsADirectoryError: - error_logger.error(f'Raise IsADirectoryError in save() with {self}') - except PermissionError: - error_logger.error(f'Raise PermissionError in save() with {self}') - else: - info_logger.info(f'patient {self} was successfully added to file') + with open(csv_path, "a", newline='', encoding='utf-8') as csv_file: + writer = csv.writer(csv_file, delimiter='|') + writer.writerow([self.first_name, self.last_name, self.birth_date, self.phone, + self.document_type, self.document_id]) class PatientCollection: From fa4d4e0f3fdb3b2b11f03e827b39b043a253b978 Mon Sep 17 00:00:00 2001 From: exeynod Date: Sat, 2 May 2020 15:39:26 +0300 Subject: [PATCH 5/7] Added mysql integration --- .mysql/root.crt | 59 +++++++++++++++++++++++++++++++++++ homework/.mysql/root.crt | 59 +++++++++++++++++++++++++++++++++++ homework/patient.py | 64 ++++++++++++++++++++++++++++---------- homework/table_creation.py | 18 +++++++++++ 4 files changed, 183 insertions(+), 17 deletions(-) create mode 100644 .mysql/root.crt create mode 100644 homework/.mysql/root.crt create mode 100644 homework/table_creation.py diff --git a/.mysql/root.crt b/.mysql/root.crt new file mode 100644 index 0000000..cd9e9e3 --- /dev/null +++ b/.mysql/root.crt @@ -0,0 +1,59 @@ +-----BEGIN CERTIFICATE----- +MIIE3TCCAsWgAwIBAgIKPxb5sAAAAAAAFzANBgkqhkiG9w0BAQ0FADAfMR0wGwYD +VQQDExRZYW5kZXhJbnRlcm5hbFJvb3RDQTAeFw0xNzA2MjAxNjQ0MzdaFw0yNzA2 +MjAxNjU0MzdaMFUxEjAQBgoJkiaJk/IsZAEZFgJydTEWMBQGCgmSJomT8ixkARkW +BnlhbmRleDESMBAGCgmSJomT8ixkARkWAmxkMRMwEQYDVQQDEwpZYW5kZXhDTENB +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqgNnjk0JKPcbsk1+KG2t +eM1AfMnEe5RkAJuBBuwVV49snhcvO1jhKBx/pCnjr6biICc1/oAFDVgU8yVYYPwp +WZ2vH3ZtscjJ/RAT/NS9OKKG7kKknhFhVYxua5xhoIQmm6usBNYYiTcWoFm1eHC8 +I9oddOLSscZYbh3unVRvt+3V+drVmUx9oSUKpqMgfysiv1MN6zB3vq9TFkbhz53E +k0tEcV+W2NnDaeFhLKy284FDKLvOdTDj1EDsSAihxl7sNEKpupNuhgyy2siOqUb+ +d5mO/CRfaAKGg3E6hDM3pEi48E506dJdjPXWfHKSvuguMLRlb2RWdVocRZuyWxOh +0QIDAQABo4HkMIHhMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRMU5uItjx+ +TOicX1+ovC1Xq2PSnzAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8E +BAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBSrucX/oe/mUx0zOSKE +0XbUN04tajBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vY3Jscy55YW5kZXgucnUv +WWFuZGV4SW50ZXJuYWxSb290Q0EvWWFuZGV4SW50ZXJuYWxSb290Q0EuY3JsMA0G +CSqGSIb3DQEBDQUAA4ICAQAsR5Lb4Pv2FD0Kk+4oc1GEOnehxKLsQtdV81nrU+IV +l9pr2oNMdi8lwIolvHZRllLM4Ba5AcRH6YJ5fe7AjKm+5EdSkhqVWo2UOllRCbtS +wmL50+erOAkxstSlRkO6b8x1L0MOBKv54E5YcQ/Wwt27ldSb6RkEmJBGvmxObAaf +5zc51pqSqao9tnldYaCblEQ/Zmy43FliIpa2eUJoh8DqK8bVo2gcI3wbQ32tWs9u +wvKk8fo4lAdhCwhv+QHuqau1VAY9hPU106bsFIDUmijTMxjAobKBi6CkIX6EbNHU +Jv4DzYVLlDd2y0CADdn2F6I70xpCBn5cquSGuvFbqZjQDmIHwb7WQSxadkiGRWfc +zVTnmiHjJONJJIpE2t+FOV3hc+8o98OzOtNaH2QQ9j6dnKvtIGKGFeNSDp0vXPOi +QhHiIyuB7eWx+g2whktQ74UCpGDSXYnEW3s8w5wezVWIEmouq7q4rCEkTNvJ7Ico +43AgUdPzAFS2zYktw1C+cbUALM8smvXbXrXOBzMmscjIhtXvLMrpPeh23VfdJfQB +0rN2BmRCLUE8JOV+o0k98XMm83oN+lGkL1l+hyoj3ok1uI3JrsWOcDyjOds3ptcN +KimJLm27ndjcxDNo/iA6gefMJuCxFRaqI+eF4P0jSkMgnnQqZkvLGFuHCw8eRDhm +bw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFGTCCAwGgAwIBAgIQJMM7ZIy2SYxCBgK7WcFwnjANBgkqhkiG9w0BAQ0FADAf +MR0wGwYDVQQDExRZYW5kZXhJbnRlcm5hbFJvb3RDQTAeFw0xMzAyMTExMzQxNDNa +Fw0zMzAyMTExMzUxNDJaMB8xHTAbBgNVBAMTFFlhbmRleEludGVybmFsUm9vdENB +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAgb4xoQjBQ7oEFk8EHVGy +1pDEmPWw0Wgw5nX9RM7LL2xQWyUuEq+Lf9Dgh+O725aZ9+SO2oEs47DHHt81/fne +5N6xOftRrCpy8hGtUR/A3bvjnQgjs+zdXvcO9cTuuzzPTFSts/iZATZsAruiepMx +SGj9S1fGwvYws/yiXWNoNBz4Tu1Tlp0g+5fp/ADjnxc6DqNk6w01mJRDbx+6rlBO +aIH2tQmJXDVoFdrhmBK9qOfjxWlIYGy83TnrvdXwi5mKTMtpEREMgyNLX75UjpvO +NkZgBvEXPQq+g91wBGsWIE2sYlguXiBniQgAJOyRuSdTxcJoG8tZkLDPRi5RouWY +gxXr13edn1TRDGco2hkdtSUBlajBMSvAq+H0hkslzWD/R+BXkn9dh0/DFnxVt4XU +5JbFyd/sKV/rF4Vygfw9ssh1ZIWdqkfZ2QXOZ2gH4AEeoN/9vEfUPwqPVzL0XEZK +r4s2WjU9mE5tHrVsQOZ80wnvYHYi2JHbl0hr5ghs4RIyJwx6LEEnj2tzMFec4f7o +dQeSsZpgRJmpvpAfRTxhIRjZBrKxnMytedAkUPguBQwjVCn7+EaKiJfpu42JG8Mm ++/dHi+Q9Tc+0tX5pKOIpQMlMxMHw8MfPmUjC3AAd9lsmCtuybYoeN2IRdbzzchJ8 +l1ZuoI3gH7pcIeElfVSqSBkCAwEAAaNRME8wCwYDVR0PBAQDAgGGMA8GA1UdEwEB +/wQFMAMBAf8wHQYDVR0OBBYEFKu5xf+h7+ZTHTM5IoTRdtQ3Ti1qMBAGCSsGAQQB +gjcVAQQDAgEAMA0GCSqGSIb3DQEBDQUAA4ICAQAVpyJ1qLjqRLC34F1UXkC3vxpO +nV6WgzpzA+DUNog4Y6RhTnh0Bsir+I+FTl0zFCm7JpT/3NP9VjfEitMkHehmHhQK +c7cIBZSF62K477OTvLz+9ku2O/bGTtYv9fAvR4BmzFfyPDoAKOjJSghD1p/7El+1 +eSjvcUBzLnBUtxO/iYXRNo7B3+1qo4F5Hz7rPRLI0UWW/0UAfVCO2fFtyF6C1iEY +/q0Ldbf3YIaMkf2WgGhnX9yH/8OiIij2r0LVNHS811apyycjep8y/NkG4q1Z9jEi +VEX3P6NEL8dWtXQlvlNGMcfDT3lmB+tS32CPEUwce/Ble646rukbERRwFfxXojpf +C6ium+LtJc7qnK6ygnYF4D6mz4H+3WaxJd1S1hGQxOb/3WVw63tZFnN62F6/nc5g +6T44Yb7ND6y3nVcygLpbQsws6HsjX65CoSjrrPn0YhKxNBscF7M7tLTW/5LK9uhk +yjRCkJ0YagpeLxfV1l1ZJZaTPZvY9+ylHnWHhzlq0FzcrooSSsp4i44DB2K7O2ID +87leymZkKUY6PMDa4GkDJx0dG4UXDhRETMf+NkYgtLJ+UIzMNskwVDcxO4kVL+Hi +Pj78bnC5yCw8P5YylR45LdxLzLO68unoXOyFz1etGXzszw8lJI9LNubYxk77mK8H +LpuQKbSbIERsmR+QqQ== +-----END CERTIFICATE----- diff --git a/homework/.mysql/root.crt b/homework/.mysql/root.crt new file mode 100644 index 0000000..cd9e9e3 --- /dev/null +++ b/homework/.mysql/root.crt @@ -0,0 +1,59 @@ +-----BEGIN CERTIFICATE----- +MIIE3TCCAsWgAwIBAgIKPxb5sAAAAAAAFzANBgkqhkiG9w0BAQ0FADAfMR0wGwYD +VQQDExRZYW5kZXhJbnRlcm5hbFJvb3RDQTAeFw0xNzA2MjAxNjQ0MzdaFw0yNzA2 +MjAxNjU0MzdaMFUxEjAQBgoJkiaJk/IsZAEZFgJydTEWMBQGCgmSJomT8ixkARkW +BnlhbmRleDESMBAGCgmSJomT8ixkARkWAmxkMRMwEQYDVQQDEwpZYW5kZXhDTENB +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqgNnjk0JKPcbsk1+KG2t +eM1AfMnEe5RkAJuBBuwVV49snhcvO1jhKBx/pCnjr6biICc1/oAFDVgU8yVYYPwp +WZ2vH3ZtscjJ/RAT/NS9OKKG7kKknhFhVYxua5xhoIQmm6usBNYYiTcWoFm1eHC8 +I9oddOLSscZYbh3unVRvt+3V+drVmUx9oSUKpqMgfysiv1MN6zB3vq9TFkbhz53E +k0tEcV+W2NnDaeFhLKy284FDKLvOdTDj1EDsSAihxl7sNEKpupNuhgyy2siOqUb+ +d5mO/CRfaAKGg3E6hDM3pEi48E506dJdjPXWfHKSvuguMLRlb2RWdVocRZuyWxOh +0QIDAQABo4HkMIHhMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRMU5uItjx+ +TOicX1+ovC1Xq2PSnzAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8E +BAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBSrucX/oe/mUx0zOSKE +0XbUN04tajBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vY3Jscy55YW5kZXgucnUv +WWFuZGV4SW50ZXJuYWxSb290Q0EvWWFuZGV4SW50ZXJuYWxSb290Q0EuY3JsMA0G +CSqGSIb3DQEBDQUAA4ICAQAsR5Lb4Pv2FD0Kk+4oc1GEOnehxKLsQtdV81nrU+IV +l9pr2oNMdi8lwIolvHZRllLM4Ba5AcRH6YJ5fe7AjKm+5EdSkhqVWo2UOllRCbtS +wmL50+erOAkxstSlRkO6b8x1L0MOBKv54E5YcQ/Wwt27ldSb6RkEmJBGvmxObAaf +5zc51pqSqao9tnldYaCblEQ/Zmy43FliIpa2eUJoh8DqK8bVo2gcI3wbQ32tWs9u +wvKk8fo4lAdhCwhv+QHuqau1VAY9hPU106bsFIDUmijTMxjAobKBi6CkIX6EbNHU +Jv4DzYVLlDd2y0CADdn2F6I70xpCBn5cquSGuvFbqZjQDmIHwb7WQSxadkiGRWfc +zVTnmiHjJONJJIpE2t+FOV3hc+8o98OzOtNaH2QQ9j6dnKvtIGKGFeNSDp0vXPOi +QhHiIyuB7eWx+g2whktQ74UCpGDSXYnEW3s8w5wezVWIEmouq7q4rCEkTNvJ7Ico +43AgUdPzAFS2zYktw1C+cbUALM8smvXbXrXOBzMmscjIhtXvLMrpPeh23VfdJfQB +0rN2BmRCLUE8JOV+o0k98XMm83oN+lGkL1l+hyoj3ok1uI3JrsWOcDyjOds3ptcN +KimJLm27ndjcxDNo/iA6gefMJuCxFRaqI+eF4P0jSkMgnnQqZkvLGFuHCw8eRDhm +bw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFGTCCAwGgAwIBAgIQJMM7ZIy2SYxCBgK7WcFwnjANBgkqhkiG9w0BAQ0FADAf +MR0wGwYDVQQDExRZYW5kZXhJbnRlcm5hbFJvb3RDQTAeFw0xMzAyMTExMzQxNDNa +Fw0zMzAyMTExMzUxNDJaMB8xHTAbBgNVBAMTFFlhbmRleEludGVybmFsUm9vdENB +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAgb4xoQjBQ7oEFk8EHVGy +1pDEmPWw0Wgw5nX9RM7LL2xQWyUuEq+Lf9Dgh+O725aZ9+SO2oEs47DHHt81/fne +5N6xOftRrCpy8hGtUR/A3bvjnQgjs+zdXvcO9cTuuzzPTFSts/iZATZsAruiepMx +SGj9S1fGwvYws/yiXWNoNBz4Tu1Tlp0g+5fp/ADjnxc6DqNk6w01mJRDbx+6rlBO +aIH2tQmJXDVoFdrhmBK9qOfjxWlIYGy83TnrvdXwi5mKTMtpEREMgyNLX75UjpvO +NkZgBvEXPQq+g91wBGsWIE2sYlguXiBniQgAJOyRuSdTxcJoG8tZkLDPRi5RouWY +gxXr13edn1TRDGco2hkdtSUBlajBMSvAq+H0hkslzWD/R+BXkn9dh0/DFnxVt4XU +5JbFyd/sKV/rF4Vygfw9ssh1ZIWdqkfZ2QXOZ2gH4AEeoN/9vEfUPwqPVzL0XEZK +r4s2WjU9mE5tHrVsQOZ80wnvYHYi2JHbl0hr5ghs4RIyJwx6LEEnj2tzMFec4f7o +dQeSsZpgRJmpvpAfRTxhIRjZBrKxnMytedAkUPguBQwjVCn7+EaKiJfpu42JG8Mm ++/dHi+Q9Tc+0tX5pKOIpQMlMxMHw8MfPmUjC3AAd9lsmCtuybYoeN2IRdbzzchJ8 +l1ZuoI3gH7pcIeElfVSqSBkCAwEAAaNRME8wCwYDVR0PBAQDAgGGMA8GA1UdEwEB +/wQFMAMBAf8wHQYDVR0OBBYEFKu5xf+h7+ZTHTM5IoTRdtQ3Ti1qMBAGCSsGAQQB +gjcVAQQDAgEAMA0GCSqGSIb3DQEBDQUAA4ICAQAVpyJ1qLjqRLC34F1UXkC3vxpO +nV6WgzpzA+DUNog4Y6RhTnh0Bsir+I+FTl0zFCm7JpT/3NP9VjfEitMkHehmHhQK +c7cIBZSF62K477OTvLz+9ku2O/bGTtYv9fAvR4BmzFfyPDoAKOjJSghD1p/7El+1 +eSjvcUBzLnBUtxO/iYXRNo7B3+1qo4F5Hz7rPRLI0UWW/0UAfVCO2fFtyF6C1iEY +/q0Ldbf3YIaMkf2WgGhnX9yH/8OiIij2r0LVNHS811apyycjep8y/NkG4q1Z9jEi +VEX3P6NEL8dWtXQlvlNGMcfDT3lmB+tS32CPEUwce/Ble646rukbERRwFfxXojpf +C6ium+LtJc7qnK6ygnYF4D6mz4H+3WaxJd1S1hGQxOb/3WVw63tZFnN62F6/nc5g +6T44Yb7ND6y3nVcygLpbQsws6HsjX65CoSjrrPn0YhKxNBscF7M7tLTW/5LK9uhk +yjRCkJ0YagpeLxfV1l1ZJZaTPZvY9+ylHnWHhzlq0FzcrooSSsp4i44DB2K7O2ID +87leymZkKUY6PMDa4GkDJx0dG4UXDhRETMf+NkYgtLJ+UIzMNskwVDcxO4kVL+Hi +Pj78bnC5yCw8P5YylR45LdxLzLO68unoXOyFz1etGXzszw8lJI9LNubYxk77mK8H +LpuQKbSbIERsmR+QqQ== +-----END CERTIFICATE----- diff --git a/homework/patient.py b/homework/patient.py index 1429b35..f440af3 100644 --- a/homework/patient.py +++ b/homework/patient.py @@ -2,10 +2,19 @@ import homework.vald_values as valid import logging from homework.log import set_logger, save_logger -import csv +import mysql.connector csv_path = 'data.csv' +config = { + 'user': 'xxx', + 'password': 'xxx', + 'database': 'xxx', + 'host': 'xxx.net', + 'port': '3306', + 'ssl_ca': '.mysql/root.crt', +} + class BasicDescriptor: def __init__(self, name): @@ -114,27 +123,48 @@ def create(first_name, last_name, birth_date, phone, document_type, document_id) @save_logger def save(self): - with open(csv_path, "a", newline='', encoding='utf-8') as csv_file: - writer = csv.writer(csv_file, delimiter='|') - writer.writerow([self.first_name, self.last_name, self.birth_date, self.phone, - self.document_type, self.document_id]) + cnx = mysql.connector.connect(**config) + cur = cnx.cursor(buffered=True) + cur.execute('SELECT id FROM covid') + args = [i[0] for i in cur.fetchall()] + print(args) + if not args: + num = 0 + else: + num = args[-1] + 1 + cur.execute('INSERT INTO covid (id, first_name, last_name, ' + 'birth_date, phone, document_type, document_id) VALUES(%s, %s, %s, %s, %s, %s, %s)', (num, + self.first_name, self.last_name, self.birth_date, self.phone, self.document_type, self.document_id)) + cnx.commit() + cur.close() + cnx.close() + + def __str__(self): + return ' '.join([self.first_name, self.last_name, self.phone, self.birth_date, + self.document_type, self.document_id]) + + def __repr__(self): + return self.__str__() class PatientCollection: - def __init__(self, data_file): - file = open(data_file, encoding='utf-8') - reader = csv.reader(file, delimiter='|') - self.data = [] - for row in reader: - self.data.append(Patient(*row)) + def __init__(self): + cnx = mysql.connector.connect(**config) + cur = cnx.cursor(buffered=True) + cur.execute('Select * FROM covid') + self.data = [Patient(*human[1:]) for human in cur.fetchall()] + cur.close() + cnx.close() def limit(self, n): return islice(self, n) def __iter__(self): - with open(csv_path, 'r', encoding='utf-8') as file: - while True: - line = file.readline() - if not line: - break - yield Patient(*line.strip().split('|')) + cnx = mysql.connector.connect(**config) + cur = cnx.cursor(buffered=True) + cur.execute('SELECT * FROM covid') + for human in cur.fetchall(): + yield Patient(*human[1:]) + cur.close() + cnx.close() + diff --git a/homework/table_creation.py b/homework/table_creation.py new file mode 100644 index 0000000..eb229be --- /dev/null +++ b/homework/table_creation.py @@ -0,0 +1,18 @@ +import mysql.connector + +config = { + 'user': 'xxx', + 'password': 'xx', + 'database': 'xxx', + 'host': 'xxx.net', + 'port': '3306', + 'ssl_ca': '.mysql/root.crt', +} + +cnx = mysql.connector.connect(**config) +cur = cnx.cursor(buffered=True) +cur.execute('CREATE TABLE covid (id int(11), first_name varchar(128), last_name varchar(128), ' + 'birth_date varchar(64), phone varchar(64), document_type varchar(64), document_id varchar(64))') +cnx.commit() +cur.close() +cnx.close() \ No newline at end of file From 6266149253f33b33b6fed9867d091f2266517cfe Mon Sep 17 00:00:00 2001 From: exeynod Date: Sat, 2 May 2020 20:52:02 +0300 Subject: [PATCH 6/7] Cli added, connection config moved to separate hidden file --- homework/cli.py | 54 +++++++++++++++++++++++++++++++++++++++++++++ homework/patient.py | 25 +++++---------------- 2 files changed, 59 insertions(+), 20 deletions(-) create mode 100644 homework/cli.py diff --git a/homework/cli.py b/homework/cli.py new file mode 100644 index 0000000..6fe800a --- /dev/null +++ b/homework/cli.py @@ -0,0 +1,54 @@ +import click +from patient import Patient +from connection_config import config +import mysql.connector + + +@click.group() +def cli(): + pass + + +@cli.command('create') +@click.argument("first_name") +@click.argument("last_name") +@click.option('--birth-date') +@click.option('--phone') +@click.option('--document-type') +@click.option('--document-number', nargs=2) +def create(first_name, last_name, birth_date, phone, document_type, document_number): + Patient(*[first_name, last_name, birth_date, phone, document_type, ''.join(document_number)]).save() + click.echo('Patient created') + + +@cli.command('show') +@click.argument('num', default=10, type=int) +def show(num): + cnx = mysql.connector.connect(**config) + cur = cnx.cursor(buffered=True) + cur.execute(f'SELECT * FROM covid WHERE id < {num}') + res = [str(Patient(*patient[1:])) for patient in cur.fetchall()] + click.echo('\n'.join(res)) + cur.close() + cnx.close() + + +@cli.command('count') +def count(): + cnx = mysql.connector.connect(**config) + cur = cnx.cursor(buffered=True) + cur.execute('SELECT MAX(id) FROM covid LIMIT 1') + num = cur.fetchone() + num = num[0] + 1 if num[0] or num[0] == 0 else 0 + click.echo(f'{num} patients in the table') + cur.close() + cnx.close() + + +cli.add_command(create) +cli.add_command(show) +cli.add_command(count) + + +if __name__ == '__main__': + cli() \ No newline at end of file diff --git a/homework/patient.py b/homework/patient.py index f440af3..8d9dc12 100644 --- a/homework/patient.py +++ b/homework/patient.py @@ -1,21 +1,12 @@ from itertools import islice -import homework.vald_values as valid +import vald_values as valid import logging -from homework.log import set_logger, save_logger +from log import set_logger, save_logger import mysql.connector +from connection_config import config csv_path = 'data.csv' -config = { - 'user': 'xxx', - 'password': 'xxx', - 'database': 'xxx', - 'host': 'xxx.net', - 'port': '3306', - 'ssl_ca': '.mysql/root.crt', -} - - class BasicDescriptor: def __init__(self, name): self.name = name @@ -125,13 +116,8 @@ def create(first_name, last_name, birth_date, phone, document_type, document_id) def save(self): cnx = mysql.connector.connect(**config) cur = cnx.cursor(buffered=True) - cur.execute('SELECT id FROM covid') - args = [i[0] for i in cur.fetchall()] - print(args) - if not args: - num = 0 - else: - num = args[-1] + 1 + cur.execute('SELECT MAX(id) FROM covid LIMIT 1') + num = cur.fetchone()[0] + 1 if cur.fetchone()[0] else 0 cur.execute('INSERT INTO covid (id, first_name, last_name, ' 'birth_date, phone, document_type, document_id) VALUES(%s, %s, %s, %s, %s, %s, %s)', (num, self.first_name, self.last_name, self.birth_date, self.phone, self.document_type, self.document_id)) @@ -167,4 +153,3 @@ def __iter__(self): yield Patient(*human[1:]) cur.close() cnx.close() - From 95ca8148ececbb95c0bdf2a734d2583fb0b5e807 Mon Sep 17 00:00:00 2001 From: exeynod Date: Sun, 3 May 2020 17:34:05 +0300 Subject: [PATCH 7/7] Different data format, exceptions caught, optimized showing data --- homework/cli.py | 75 ++++++++++++++++++++++++++++++++------------- homework/log.py | 2 +- homework/patient.py | 13 ++++++-- 3 files changed, 65 insertions(+), 25 deletions(-) diff --git a/homework/cli.py b/homework/cli.py index 6fe800a..1fab0f0 100644 --- a/homework/cli.py +++ b/homework/cli.py @@ -1,9 +1,45 @@ import click -from patient import Patient +from patient import Patient, PatientCollection from connection_config import config import mysql.connector +class GetAllOption(click.Option): + def __init__(self, *args, **kwargs): + self.save_other_options = kwargs.pop('save_other_options', True) + nargs = kwargs.pop('nargs', -1) + assert nargs == -1, 'nargs, if set, must be -1 not {}'.format(nargs) + super(GetAllOption, self).__init__(*args, **kwargs) + self._previous_parser_process = None + self._eat_all_parser = None + + def add_to_parser(self, parser, ctx): + def parser_process(value, state): + done = False + value = [value] + if self.save_other_options: + # grab everything up to the next option + while state.rargs and not done: + for prefix in self._eat_all_parser.prefixes: + if state.rargs[0].startswith(prefix): + done = True + if not done: + value.append(state.rargs.pop(0)) + else: + value += state.rargs + state.rargs[:] = [] + value = tuple(value) + self._previous_parser_process(value, state) + res = super(GetAllOption, self).add_to_parser(parser, ctx) + for name in self.opts: + our_parser = parser._long_opt.get(name) or parser._short_opt.get(name) + if our_parser: + self._eat_all_parser = our_parser + self._previous_parser_process = our_parser.process + our_parser.process = parser_process + break + return res + @click.group() def cli(): pass @@ -12,37 +48,32 @@ def cli(): @cli.command('create') @click.argument("first_name") @click.argument("last_name") -@click.option('--birth-date') -@click.option('--phone') -@click.option('--document-type') -@click.option('--document-number', nargs=2) +@click.option('--birth-date', prompt='Birth date') +@click.option('--phone', cls=GetAllOption, prompt='Phone number') +@click.option('--document-type', cls=GetAllOption, + prompt='Document type (паспор/водительское удостоверение/заграничный паспорт)') +@click.option('--document-number', cls=GetAllOption, prompt='Document id') def create(first_name, last_name, birth_date, phone, document_type, document_number): - Patient(*[first_name, last_name, birth_date, phone, document_type, ''.join(document_number)]).save() - click.echo('Patient created') + try: + Patient(*[first_name, last_name, birth_date, ' '.join(phone), ' '.join(document_type), + ''.join(document_number)]).save() + except ValueError: + click.echo('Creation aborted, wrong data format') + else: + click.echo('Patient created') @cli.command('show') @click.argument('num', default=10, type=int) def show(num): - cnx = mysql.connector.connect(**config) - cur = cnx.cursor(buffered=True) - cur.execute(f'SELECT * FROM covid WHERE id < {num}') - res = [str(Patient(*patient[1:])) for patient in cur.fetchall()] - click.echo('\n'.join(res)) - cur.close() - cnx.close() + click.echo(PatientCollection(num)) @cli.command('count') def count(): - cnx = mysql.connector.connect(**config) - cur = cnx.cursor(buffered=True) - cur.execute('SELECT MAX(id) FROM covid LIMIT 1') - num = cur.fetchone() - num = num[0] + 1 if num[0] or num[0] == 0 else 0 - click.echo(f'{num} patients in the table') - cur.close() - cnx.close() + res = len(PatientCollection()) + add = 's' if res > 1 or res == 0 else '' + click.echo(f'{res} patient{add} in the table') cli.add_command(create) diff --git a/homework/log.py b/homework/log.py index 9af40ca..c71ed3d 100644 --- a/homework/log.py +++ b/homework/log.py @@ -25,7 +25,7 @@ def wrapper(self, instance, value): try: func(self, instance, value) except TypeError: - instance.error_logger.error(f'{value} must by string') + instance.error_logger.error(f'{value} must be a string') raise TypeError except ValueError: instance.error_logger.error(f'Wrong format : {value}') diff --git a/homework/patient.py b/homework/patient.py index 8d9dc12..bbb3a75 100644 --- a/homework/patient.py +++ b/homework/patient.py @@ -134,10 +134,13 @@ def __repr__(self): class PatientCollection: - def __init__(self): + def __init__(self, num=None): cnx = mysql.connector.connect(**config) cur = cnx.cursor(buffered=True) - cur.execute('Select * FROM covid') + if not num or num <= 0: + cur.execute('Select * FROM covid') + else: + cur.execute(f'SELECT * FROM covid LIMIT {num}') self.data = [Patient(*human[1:]) for human in cur.fetchall()] cur.close() cnx.close() @@ -153,3 +156,9 @@ def __iter__(self): yield Patient(*human[1:]) cur.close() cnx.close() + + def __str__(self): + return '\n'.join([str(patient) for patient in self.data]) + + def __len__(self): + return len(self.data)