diff --git a/Lectures/py_lec_10.pptx b/Lectures/py_lec_10.pptx
index 51d4f90..5281ba2 100644
Binary files a/Lectures/py_lec_10.pptx and b/Lectures/py_lec_10.pptx differ
diff --git a/Lectures/py_lec_12.pdf b/Lectures/py_lec_12.pdf
new file mode 100644
index 0000000..715cc4e
Binary files /dev/null and b/Lectures/py_lec_12.pdf differ
diff --git a/Lectures/py_lec_14.pptx b/Lectures/py_lec_14.pptx
index 83c5b35..3ec7c12 100644
Binary files a/Lectures/py_lec_14.pptx and b/Lectures/py_lec_14.pptx differ
diff --git a/Practice/AKapralova/KapralovaDz7.3.py b/Practice/AKapralova/KapralovaDz7.3.py
new file mode 100644
index 0000000..7820c5b
--- /dev/null
+++ b/Practice/AKapralova/KapralovaDz7.3.py
@@ -0,0 +1,45 @@
+class ATM:
+ def __init__(self, name, balance=0):
+ self._name = name
+ self._balance = float(balance)
+
+ def about_atm(self):
+ print(f"Банкомат: {self._name} Баланс: {self._balance}")
+
+ def operation(self):
+ print("Банкомат может осуществлять операции получения и выдачи наличных.")
+
+ def get_money(self, money):
+ self._balance += money
+
+ def give_money(self, money):
+ if self._balance - money < 0:
+ print(f"В Банкомате:{self._name} недостаточно денежных средств.")
+ else:
+ self._balance -= money
+
+
+class GetGiveATM(ATM):
+ pass
+
+
+class OnlineATM(ATM):
+ def operation(self):
+ super().operation()
+ print("Банкомат может осуществлять онлайн платежи.")
+
+ def online_payment(self):
+ print(f'{self._name}: Совершен онлайн платеж')
+
+
+b1 = GetGiveATM("Сбер", 1000)
+
+b2 = OnlineATM("ВТБ", 2000)
+b2.online_payment()
+
+lst = [b1, b2]
+for i in lst:
+ i.about_atm()
+ i.operation()
+ i.get_money(100)
+ i.give_money(200)
diff --git a/Practice/AKapralova/KapralovaDz8.1.py b/Practice/AKapralova/KapralovaDz8.1.py
new file mode 100644
index 0000000..07a7f7d
--- /dev/null
+++ b/Practice/AKapralova/KapralovaDz8.1.py
@@ -0,0 +1,30 @@
+# Реализовать итератор, который бы "читал" заданный текст по параграфам.
+# Символ параграфа задается отдельно.
+class ParagraphIterator:
+
+ def __init__(self, text, paragraph_symbol):
+ self.text = text
+ self.paragraph_symbol = paragraph_symbol
+ self.current_paragraph = 0
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ res = ''
+ if self.current_paragraph > len(self.text) - 1:
+ raise StopIteration
+ while self.text[self.current_paragraph] != self.paragraph_symbol:
+ res += self.text[self.current_paragraph]
+ self.current_paragraph += 1
+ if self.current_paragraph >= len(self.text):
+ break
+ else:
+ self.current_paragraph += 1
+ return res
+
+
+text = "Дама сдавала в багаж: Диван. Чемодан. Саквояж. Картину. Корзину. Картонку. И маленькую собачонку."
+
+for i in ParagraphIterator(text, "."):
+ print(i)
diff --git a/Practice/AKapralova/KapralovaDz8.2.py b/Practice/AKapralova/KapralovaDz8.2.py
new file mode 100644
index 0000000..c537c53
--- /dev/null
+++ b/Practice/AKapralova/KapralovaDz8.2.py
@@ -0,0 +1,9 @@
+# Написать генератор для построчного чтения файла.
+def read_by_line(file_path):
+ with open(file_path, 'r') as file:
+ for line in file:
+ yield line
+
+
+for line in read_by_line('2txt'):
+ print(line)
diff --git a/Practice/AKapralova/KapralovaDz8.3.py b/Practice/AKapralova/KapralovaDz8.3.py
new file mode 100644
index 0000000..dc5faa7
--- /dev/null
+++ b/Practice/AKapralova/KapralovaDz8.3.py
@@ -0,0 +1,20 @@
+# Напишите свой менеджер контекста, замеряющий и показывающий время исполнения кода внутри него.
+import time
+
+
+class Timer:
+ def __enter__(self):
+ self.start_time = time.time()
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ elapsed_time = time.time() - self.start_time
+ print(f"Время работы программы: {elapsed_time} сек.")
+
+
+with Timer():
+ # lst = [1, 3, 24, 2, 3, 7]
+ # lst.sort()
+ # print(lst)
+ enumerated_numbers = enumerate(range(1, 10001))
+ for i, j in enumerated_numbers:
+ print(i, j)
diff --git a/Practice/AKapralova/KapralovaDz8.4.py b/Practice/AKapralova/KapralovaDz8.4.py
new file mode 100644
index 0000000..cd42e9b
--- /dev/null
+++ b/Practice/AKapralova/KapralovaDz8.4.py
@@ -0,0 +1,12 @@
+import itertools
+
+
+lst1 = list(itertools.chain([1, 2, 3], [4, 5], [6, 7]))
+print(lst1)
+
+lst = ['hello', 'i', 'write', 'cool', 'code']
+lst2 = list(itertools.filterfalse(lambda x: len(x) < 5, lst))
+print(lst2)
+
+lst3 = list(itertools.combinations('password', 4))
+print(lst3)
diff --git a/Practice/AKapralova/KapralovaDz9.2.py b/Practice/AKapralova/KapralovaDz9.2.py
new file mode 100644
index 0000000..52863c5
--- /dev/null
+++ b/Practice/AKapralova/KapralovaDz9.2.py
@@ -0,0 +1,16 @@
+import datetime
+
+
+def fun_count(date1, date2):
+ count = 0
+ current_date = date1
+ while current_date <= date2:
+ if current_date.weekday() < 5:
+ count += 1
+ current_date += datetime.timedelta(days=1)
+ return count
+
+
+date1 = datetime.date(2018, 8, 21)
+date2 = datetime.date(2023, 8, 3)
+print(f"Количество рабочих дней: {fun_count(date1, date2)}")
diff --git a/Practice/AKapralova/KapralovaDz9.3.py b/Practice/AKapralova/KapralovaDz9.3.py
new file mode 100644
index 0000000..de35426
--- /dev/null
+++ b/Practice/AKapralova/KapralovaDz9.3.py
@@ -0,0 +1,19 @@
+import os
+import shutil
+import time
+
+
+folder_path = input("Укажите полный путь до папки: ")
+while True:
+ for root, dirs, files in os.walk(folder_path):
+ for file in files:
+ file_path = os.path.join(root, file)
+ file_age = time.time() - os.path.getctime(file_path)
+ if file_age > 60:
+ os.remove(file_path)
+ for dir in dirs:
+ dir_path = os.path.join(root, dir)
+ dir_age = time.time() - os.path.getctime(dir_path)
+ if dir_age > 120:
+ shutil.rmtree(dir_path)
+ time.sleep(10)
diff --git a/Practice/AKapralova/KapralovaDz9.4.py b/Practice/AKapralova/KapralovaDz9.4.py
new file mode 100644
index 0000000..510dae1
--- /dev/null
+++ b/Practice/AKapralova/KapralovaDz9.4.py
@@ -0,0 +1,41 @@
+import pickle
+import random
+
+
+class Human:
+ def __init__(self, name, surname, age, residence):
+ self.name = name
+ self.surname = surname
+ self.age = age
+ self.residence = residence
+
+ def __str__(self):
+ return f"{self.name} {self.surname}, возраст: {self.age}, проживает в городе {self.residence}."
+
+
+def create(num_instances):
+ first_names = ["Анна", "Юлия", "Полина", "Олеся", "Татьяна"]
+ last_names = ["Капралова", "Сидорова", "Смирнова", "Иванова", "Петрова"]
+ residences = ["Нижний Новгород", "Екатеринбург", "Москва", "Иваново", "Кстово"]
+ humans = []
+ for i in range(num_instances):
+ name = random.choice(first_names)
+ surname = random.choice(last_names)
+ age = random.randint(18, 25)
+ residence = random.choice(residences)
+ human = Human(name, surname, age, residence)
+ humans.append(human)
+ with open("human.data", "wb") as file:
+ pickle.dump(humans, file)
+
+
+def read():
+ with open("human.data", "rb") as file:
+ humans = pickle.load(file)
+ for human in humans:
+ print(human)
+
+
+if __name__ == "__main__":
+ create(5)
+ read()
diff --git a/Practice/Fedorov/HW_9/9+.1.py b/Practice/Fedorov/HW_9/9+.1.py
new file mode 100644
index 0000000..e5f6ac1
--- /dev/null
+++ b/Practice/Fedorov/HW_9/9+.1.py
@@ -0,0 +1,8 @@
+# Используя моржовый оператор, по заданному списку элементов lst = [“10”, “5”, “a”, “3”, “b”]
+# создать новый список, содержащий квадраты тех элементов исходного списка,
+# которые можно привести к типу Число и которые в численном представлении кратны 5.
+
+lst = ["10", "5", "a", "3", "b"]
+
+new_lst = [x**2 for i in lst if i.isdigit() and (x := int(i)) % 5 == 0]
+print(new_lst)
diff --git a/Practice/Fedorov/HW_9/9+.2.py b/Practice/Fedorov/HW_9/9+.2.py
new file mode 100644
index 0000000..dc9eb70
--- /dev/null
+++ b/Practice/Fedorov/HW_9/9+.2.py
@@ -0,0 +1,17 @@
+# Используя структурное сопоставление с шаблонами (match/case),
+# написать чат-бота, отвечающего на вопросы «Привет», «Как дела?», «Какая сегодня погода?»
+# заготовленными ответами, а на все остальные вопросы –
+# «Вопрос некорректен, попробуйте сформулировать его по-другому».
+
+while question := input("Задайте вопрос"):
+ match question.lower():
+ case "привет":
+ print("Привет, чё как ты?")
+ case "как дела?" | "как дела":
+ print("Хорошо")
+ case "какая сегодня погода":
+ print("Погода на сегодня, не знаю может спросим у Яндекса?")
+ case _:
+ print("Вопрос некорректен, попробуйте сформулировать его по-другому")
+
+
diff --git a/Practice/Fedorov/HW_9/9.1.py b/Practice/Fedorov/HW_9/9.1.py
new file mode 100644
index 0000000..a2b1a8f
--- /dev/null
+++ b/Practice/Fedorov/HW_9/9.1.py
@@ -0,0 +1,26 @@
+import re
+from pathlib import Path
+
+current_path = str(Path.cwd())
+need_path = current_path.replace("Fedorov\\HW_9", "") + "README.md"
+pattern1 = r"git [a-z]{3,}.+"
+pattern2 = r"[а-я].+"
+commands1 = re.compile(pattern1)
+repl_rus_simbols = re.compile(pattern2)
+lst = set()
+with open(need_path, encoding='utf-8') as myfile:
+ text = myfile.readline()
+ while text:
+ i = commands1.search(text)
+ if i:
+ i = i.group(0).lower().replace("\"", "").replace(",", "").replace("(", "")
+ if len(i) > 30:
+ m = repl_rus_simbols.search(i)
+ if m:
+ i = i.replace(m.group(0), "")
+ lst.add(i)
+ text = myfile.readline()
+
+for i in lst:
+ if i:
+ print(i)
diff --git a/Practice/Fedorov/HW_9/9.2.py b/Practice/Fedorov/HW_9/9.2.py
new file mode 100644
index 0000000..50d30be
--- /dev/null
+++ b/Practice/Fedorov/HW_9/9.2.py
@@ -0,0 +1,26 @@
+import datetime as dt
+
+
+def string_as_date(str_as_date):
+ return dt.datetime.strptime(str_as_date, "%d.%m.%Y").date()
+
+
+def work_day(start_date, end_date_include, lst_holidays):
+ start_date = string_as_date(start_date)
+ end_date = string_as_date(end_date_include)
+ new_holidays = [string_as_date(i) for i in lst_holidays]
+ if end_date.weekday() > 4 or end_date in new_holidays:
+ res = end_date - start_date
+ else:
+ res = end_date - start_date + dt.timedelta(days=1)
+ while start_date != end_date:
+ if start_date.weekday() > 4:
+ res -= dt.timedelta(days=1)
+ elif start_date in new_holidays:
+ res -= dt.timedelta(days=1)
+ start_date += dt.timedelta(days=1)
+ return res
+
+
+holidays = ("01.05.2023", "09.05.2023", "02.07.2023")
+print(work_day("30.06.2023", "30.06.2023", holidays))
diff --git a/Practice/Fedorov/HW_9/9.3.py b/Practice/Fedorov/HW_9/9.3.py
new file mode 100644
index 0000000..47ed9f7
--- /dev/null
+++ b/Practice/Fedorov/HW_9/9.3.py
@@ -0,0 +1,25 @@
+import os
+import datetime as dt
+
+
+def difference_time(path, minut):
+ return (dt.datetime.now() - dt.datetime.fromtimestamp(os.path.getctime(path)) > dt.timedelta(minutes=minut))
+
+
+tfs_path = input("Введите путь")
+while True:
+ for root, dirs, files in os.walk(tfs_path):
+ for file in files:
+ file_path = root + f"\\{file}"
+ if difference_time(file_path, 1):
+ try:
+ os.remove(file_path)
+ except OSError:
+ print(f"Файл:\n{file_path}\n- не может быть удален")
+ for folder in dirs:
+ folder_path = root + f"\\{folder}"
+ if difference_time(folder_path, 2):
+ try:
+ os.rmdir(folder_path)
+ except OSError:
+ print(f"Папка:\n{folder_path}\n- не может быть удалена")
diff --git a/Practice/Fedorov/HW_9/9.4.py b/Practice/Fedorov/HW_9/9.4.py
new file mode 100644
index 0000000..1e1b5c3
--- /dev/null
+++ b/Practice/Fedorov/HW_9/9.4.py
@@ -0,0 +1,40 @@
+import random
+import pickle
+
+my_dictionary = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+
+class Human:
+ def __init__(self, name="", surname="", age="", town="", district="", street=""):
+ self.name = name if name else self.random_simbols()
+ self.surname = surname if surname else self.random_simbols()
+ self.age = age if age else self.random_simbols()
+ self.town = town if town else self.random_simbols()
+ self.district = district if district else self.random_simbols()
+ self.street = street if street else self.random_simbols()
+
+ def about_me(self):
+ print(f"Имя: {self.name} Фамилия: {self.surname}")
+
+ @staticmethod
+ def random_simbols():
+ res = ""
+ while len(res) < 5:
+ res += random.choice(my_dictionary)
+ return res
+
+
+def human_in(count_human):
+ lst = [Human() for _ in range(count_human)]
+ with open("human.data", "wb") as file:
+ pickle.dump(lst, file, protocol=pickle.HIGHEST_PROTOCOL)
+
+
+def human_out():
+ with open("human.data", "rb") as file:
+ humans = pickle.load(file)
+ return humans
+
+
+human_in(5)
+print(human_out())
diff --git a/Practice/Fedorov/HW_9/human.data b/Practice/Fedorov/HW_9/human.data
new file mode 100644
index 0000000..50e5e38
Binary files /dev/null and b/Practice/Fedorov/HW_9/human.data differ
diff --git a/Practice/Kisunkina/lec 4/homework4.5.2.py b/Practice/Kisunkina/lec 4/homework4.5.2.py
new file mode 100644
index 0000000..db6a462
--- /dev/null
+++ b/Practice/Kisunkina/lec 4/homework4.5.2.py
@@ -0,0 +1,48 @@
+# Реализовать приложение, загадывающее целое число из заданного пользователем
+# диапазона и предлагающее пользователю это число угадать. Отгадывание числа должно
+# быть реализовано в цикле: пока пользователь не угадает, или не введет нечисловой символ,
+# продолжать опрос. Если пользователь вводит неправильное число, вывести подсказку:
+# больше оно или меньше загаданного.
+
+import random
+
+
+def check(text):
+ int_input_number = None
+ text_input = text
+ while int_input_number is None:
+ input_number = input(text_input)
+ if input_number.lstrip("-").isdigit():
+ int_input_number = int(input_number)
+ else:
+ text_input = "Это не число, введи еще раз!"
+ return int_input_number
+
+
+x = check("Введите начало диапазона целым числом: ")
+y = check("Введите конец диапазона целым числом: ")
+rand = None
+
+
+while x == y:
+ y = int(input(("Значение границы не могут быть равны! Введите конец диапазона "
+ "другим целым числом: ")))
+if x < y:
+ rand = int(random.randint(x, y))
+elif x > y:
+ rand = int(random.randint(y, x))
+
+
+num = check("Вы должны отгадать загаданное число из "
+ "указанного Вами диапазона. Введите это число: ")
+# обработали диапазон и запомнили рандомное значение
+# проинициализируем num так, чтобы он точно не был равен рандому
+
+while num != rand:
+ if num < rand:
+ num = int(input("Введенное число меньше загаданного, введите число ещё раз: "))
+ elif num > rand:
+ num = int(input("Введенное число больше загаданного, введите число ещё раз: "))
+print("Ура! Вы угадали!")
+
+
diff --git a/Practice/Kisunkina/lec 4/homework4.5.py b/Practice/Kisunkina/lec 4/homework4.5.py
new file mode 100644
index 0000000..ddf0eb7
--- /dev/null
+++ b/Practice/Kisunkina/lec 4/homework4.5.py
@@ -0,0 +1,39 @@
+# Реализовать приложение, загадывающее целое число из заданного пользователем
+# диапазона и предлагающее пользователю это число угадать. Отгадывание числа должно
+# быть реализовано в цикле: пока пользователь не угадает, или не введет нечисловой символ,
+# продолжать опрос. Если пользователь вводит неправильное число, вывести подсказку:
+# больше оно или меньше загаданного.
+
+import random
+
+
+def check(text):
+ input_number = input(text)
+ if input_number.lstrip("-").isdigit():
+ return int(input_number)
+ else:
+ print("Введенное значение не число")
+ return check(text)
+
+
+def diapazone(x, y):
+ if x < y:
+ return list(range(x, y + 1))
+ elif x > y:
+ return list(range(y, x + 1))
+ else:
+ y = check("Значение границы не могут быть равны! Введите конец диапазона другим целым числом: ")
+ return diapazone(x, y)
+
+x = check("Введите начало диапазона целым числом: ")
+y = check("Введите конец диапазона целым числом: ")
+rand = random.choice(diapazone(x, y))
+num = check("Вы должны отгадать загаданное число из указанного Вами диапазона. Введите это число: ")
+
+
+while num != rand:
+ if num < rand:
+ num = check("Введенное число меньше загаданного, введите число ещё раз: ")
+ else:
+ num = check("Введенное число больше загаданного, введите число ещё раз: ")
+print("Ура! Вы угадали!")
diff --git a/Practice/Kisunkina/lec 4/homework4.7.py b/Practice/Kisunkina/lec 4/homework4.7.py
new file mode 100644
index 0000000..5a42622
--- /dev/null
+++ b/Practice/Kisunkina/lec 4/homework4.7.py
@@ -0,0 +1,16 @@
+# Написать декоратор, выводящий "===========" до и после запуска функции.
+
+def decor_fun(function_to_decorate):
+ def inner_fun(*args, **kwargs):
+ print('"==========="')
+ res = function_to_decorate(*args, **kwargs)
+ print('"==========="')
+ return res
+ return inner_fun
+
+@decor_fun
+def alone():
+ print("Я просто функция, для проверки")
+
+
+alone()
diff --git a/Practice/Kisunkina/lec 4/homework4.8.py b/Practice/Kisunkina/lec 4/homework4.8.py
new file mode 100644
index 0000000..2ae5a94
--- /dev/null
+++ b/Practice/Kisunkina/lec 4/homework4.8.py
@@ -0,0 +1,28 @@
+# Написать приложение – игру "камень, ножницы, бумага".
+
+import random
+
+print("Вы попали в игру 'Камень, ножницы, бумага', Если надоест играть, напишите: хватит")
+comp = random.choice(["камень", "ножницы", "бумага"])
+
+while (answer := input("Выберете камень, ножницы или бумага: ").lower()) != "хватит":
+ print(f"Выбор компьютера: {comp}")
+ if answer == comp:
+ print("Ничья")
+ elif answer == "камень":
+ if comp == "ножницы":
+ print("Вы выиграли")
+ else:
+ print("Вы проиграли")
+ elif answer == "ножницы":
+ if comp == "Бумага":
+ print("Вы выиграли")
+ else:
+ print("Вы проиграли")
+ elif answer == "бумага":
+ if comp == "камень":
+ print("Вы выиграли")
+ else:
+ print("Вы проиграли")
+ else:
+ print("Вы ввели неверное значение, попробуйте ещё")
diff --git a/Practice/Kisunkina/lec 5/homework 5.1.py b/Practice/Kisunkina/lec 5/homework 5.1.py
new file mode 100644
index 0000000..a7635d8
--- /dev/null
+++ b/Practice/Kisunkina/lec 5/homework 5.1.py
@@ -0,0 +1,28 @@
+# Реализовать алгоритм сортировки выбором. Алгоритм состоит из следующих шагов:
+# найти наименьший элемент в массиве
+# поменять местами его и первый элемент в массиве
+# найти следующий наименьший элемент в массиве
+# и поменять местами его и второй элемент массива
+# продолжать это пока весь массив не будет отсортирован
+# arr = [0,3,24,2,3,7]
+# // здесь реализованный алгоритм
+# // на выходе должен получиться список, содержащий [0, 2, 3, 3, 7, 24]
+
+def sort_arr(arr):
+ for indx in range(len(arr)):
+ ost_indx = indx + 1
+ min_elem = arr[indx]
+ min_idx = indx
+ for i in range(len(arr) - ost_indx):
+ if arr[ost_indx] < min_elem:
+ min_elem = arr[ost_indx]
+ min_idx = ost_indx
+ ost_indx += 1
+ arr[indx], arr[min_idx] = arr[min_idx], arr[indx]
+ print(f'{arr}')
+ return arr
+
+
+if __name__ == '__main__':
+ arr = [0, 3, 24, 2, 3, 7]
+ sort_arr(arr)
diff --git a/Practice/Kisunkina/lec 5/homework 5.2.py b/Practice/Kisunkina/lec 5/homework 5.2.py
new file mode 100644
index 0000000..eca79c8
--- /dev/null
+++ b/Practice/Kisunkina/lec 5/homework 5.2.py
@@ -0,0 +1,16 @@
+# Написать и вызвать функцию, возвращающую первый повторившийся символ в переданном списке.
+# Например, для списка [2, 3, 4, 5, 3, 2] функция должна вернуть 3.
+
+def compare_arg(arr):
+ arr2 = set()
+ for elem in arr:
+ if elem in arr2:
+ print(f'Первое повторяющееся число {elem}')
+ return elem
+ else:
+ arr2.add(elem)
+
+
+if __name__ == '__main__':
+ arr = [2, 3, 4, 5, 3, 2]
+ compare_arg(arr)
diff --git a/Practice/Kisunkina/lec 5/homework 5.3.py b/Practice/Kisunkina/lec 5/homework 5.3.py
new file mode 100644
index 0000000..ee84220
--- /dev/null
+++ b/Practice/Kisunkina/lec 5/homework 5.3.py
@@ -0,0 +1,16 @@
+# Найти и заменить некие шаблоны в строке: есть строка с определенного вида форматированием,
+# необходимо заменить в этой строке все вхождения шаблонов на их значение из словаря.
+
+
+def zam(text, d):
+ for key in d:
+ text = text.replace(key, str(d[key]))
+ print(f'Раскрою тайну! {text}')
+ return text
+
+
+if __name__ == '__main__':
+ text = "Фильм на вечер: ***. Рейтинг: _. Рекомендация на просмотр от /."
+ print(text)
+ d = {"***": "Круэлла", "_": "7.6", "/": "75,6% зрителей"}
+ zam(text, d)
diff --git a/Practice/MLoginova/Loginova_10.1.py b/Practice/MLoginova/Loginova_10.1.py
new file mode 100644
index 0000000..f9a73ec
--- /dev/null
+++ b/Practice/MLoginova/Loginova_10.1.py
@@ -0,0 +1,15 @@
+# Написать функцию find_primes(end, start), которая ищет все простые числа в диапазоне от заданного числа
+# start (по умолчанию 3) до заданного числа end. Далее необходимо:
+# Запустить ее три раза последовательно в диапазоне от 3 до 10000, от 10001 до 20000, от 20001 до 30000.
+import time
+import Loginova_10
+
+
+if __name__ == "__main__":
+ data = [(3, 10000), (10001, 20000), (20001, 30000)]
+ start = time.perf_counter()
+ for i in range(len(data)):
+ Loginova_10.find_primes(*data[i])
+ print(f'Время вычислений в секундах: {time.perf_counter() - start}')
+# Время вычислений c использованием процессов в секундах: 0.030037499964237213
+
diff --git a/Practice/MLoginova/Loginova_10.1_multiprocessing.py b/Practice/MLoginova/Loginova_10.1_multiprocessing.py
new file mode 100644
index 0000000..81c0f44
--- /dev/null
+++ b/Practice/MLoginova/Loginova_10.1_multiprocessing.py
@@ -0,0 +1,24 @@
+import multiprocessing
+import time
+import Loginova_10
+
+
+if __name__ == "__main__":
+
+ data = [(3, 10000), (10001, 20000), (20001, 30000)]
+ start = time.perf_counter()
+ lst = []
+ for i in range(len(data)):
+ pr = multiprocessing.Process(target=Loginova_10.find_primes, args=data[i])
+ pr.start()
+ # Если забыть выполнить start для процессов, то аналогично потокам, мы не направим ОС команду на создание
+ # процесса
+ # код вернёт исключение AssertionError: can only join a started process
+ # дословно может присоединиться только к запущенному процессу
+ lst.append(pr)
+ for pr in lst:
+ pr.join()
+ # Если забыть выполнить join для процессов, то программа завершит свое выполнение, не дожидаясь завершения
+ # наших процессов
+ print(f'Время вычислений c использованием процессов в секундах: {time.perf_counter() - start}')
+# Время вычислений c использованием процессов в секундах: 0.03076120000332594
diff --git a/Practice/MLoginova/Loginova_10.1_threading.py b/Practice/MLoginova/Loginova_10.1_threading.py
new file mode 100644
index 0000000..f1ae456
--- /dev/null
+++ b/Practice/MLoginova/Loginova_10.1_threading.py
@@ -0,0 +1,23 @@
+import threading
+import time
+import Loginova_10
+
+if __name__ == "__main__":
+ data = [(3, 10000), (10001, 20000), (20001, 30000)]
+ start = time.perf_counter()
+ threads = []
+ for i in range(len(data)):
+ thr = threading.Thread(target=Loginova_10.find_primes, args=(*data[i],))
+ thr.start()
+ # Если забыть выполнить start для потоков, то мы не направим ОС команду на создание потока,
+ # код вернёт исключение RuntimeError: cannot join thread before it is started
+ # дословно невозможно присоединиться к потоку до его запуска
+ threads.append(thr)
+ for thr in threads:
+ thr.join()
+ # Если забыть выполнить join для потоков, то мы бросили наши len(data) потоков не дождавшись их объединения
+ # программа завершит свое выполнение, не дожидаясь завершения наших потоков в итоге замеряемое время
+ # для каждого потока будет неккоректным
+ print(f'Время вычислений c использованием потоков в секундах: {time.perf_counter() - start}')
+# Время вычислений c использованием потоков в секундах: 0.031701300060376525
+
diff --git a/Practice/MLoginova/Loginova_10.py b/Practice/MLoginova/Loginova_10.py
new file mode 100644
index 0000000..85df219
--- /dev/null
+++ b/Practice/MLoginova/Loginova_10.py
@@ -0,0 +1,13 @@
+def find_primes(start=3, end=None):
+ pr_chisla = []
+ while start <= end:
+ k = 2
+ sqrt = start ** 0.5
+ while k <= sqrt:
+ if start % k == 0:
+ break
+ k += 1
+ else:
+ pr_chisla.append(start)
+ start += 1
+ # print(pr_chisla)
diff --git a/Practice/MLoginova/Loginova_12.2.py b/Practice/MLoginova/Loginova_12.2.py
new file mode 100644
index 0000000..67e1956
--- /dev/null
+++ b/Practice/MLoginova/Loginova_12.2.py
@@ -0,0 +1,111 @@
+import sqlite3
+import os
+
+
+def configure_db(conn):
+ cur = conn.cursor()
+
+ # Создаем таблицу Manufacturers
+ cur.execute("CREATE TABLE Manufacturers"
+ " (Manufacturer_id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ " Manufacturer_name CHAR(128) NOT NULL)")
+
+ # Создаем таблицу Products
+ cur.execute("CREATE TABLE Products"
+ " (Product_id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ " Product_name CHAR(128) NOT NULL,"
+ " Manufacturer_id INTEGER,"
+ " FOREIGN KEY(Manufacturer_id) REFERENCES Manufacturers(Manufacturer_id))")
+
+ # Создаем таблицу Clients
+ cur.execute("CREATE TABLE Clients "
+ " (Client_id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ " Client_name CHAR(128) NOT NULL,"
+ " Client_surname CHAR(128))")
+
+ # Создаем таблицу Purchases
+ cur.execute("CREATE TABLE Purchases "
+ " (Purchase_id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ " Product_id INTEGER NOT NULL,"
+ " Client_id INTEGER NOT NULL,"
+ " Purchase_date CHAR(8),"
+ " FOREIGN KEY(product_id) REFERENCES Products(Product_id),"
+ " FOREIGN KEY(Client_id) REFERENCES Clients(Client_id))")
+
+# Добавление записей в таблицу Manufacturers
+def insert_manufacturers(conn, name):
+ cur = conn.cursor()
+ cur.execute("INSERT INTO Manufacturers (Manufacturer_name)"
+ " VALUES (:Manufacturer_name)",
+ {'Manufacturer_name': name})
+ conn.commit()
+
+# Добавление записей в таблицу Products
+def insert_products(conn, name, id):
+ cur = conn.cursor()
+ cur.execute("INSERT INTO Products (Product_name, Manufacturer_id)"
+ " VALUES (:Product_name, :Manufacturer_id)",
+ {'Product_name': name, 'Manufacturer_id': id})
+ conn.commit()
+
+# Добавление записей в таблицу Clients
+def insert_clients(conn, name, surname):
+ cur = conn.cursor()
+ cur.execute("INSERT INTO Clients (Client_name, Client_surname)"
+ " VALUES (:Client_name, :Client_surname)",
+ {'Client_name': name, 'Client_surname': surname})
+ conn.commit()
+
+ # Добавление записей в таблицу Purchases
+def insert_purchases(conn, product_id, client_id, date_op):
+ cur = conn.cursor()
+ cur.execute("INSERT INTO Purchases (Product_id, Client_id, Purchase_date )"
+ " VALUES (:Product_id, :Client_id, :Purchase_date)",
+ {'Product_id': product_id, 'Client_id': client_id, 'Purchase_date': date_op})
+ conn.commit()
+
+# Вывод информации о всех товарах с указанием информации о производителе
+def show_products_info(conn):
+ cur = conn.cursor()
+ cur.execute("SELECT P.Product_name, M.Manufacturer_name"
+ " FROM Products as P, Manufacturers as M "
+ " WHERE P. Manufacturer_id = M. Manufacturer_id")
+ print("Товары и их производители:")
+ for row in cur.fetchall():
+ print(dict(row))
+
+# Вывод информации о всех товарах, которые никто не покупал
+def show_products_ostatok(conn):
+ cur = conn.cursor()
+ cur.row_factory = None
+ cur.execute("SELECT Pr.Product_id, Pr.Product_name FROM Products as Pr left join Purchases as P "
+ "on Pr.Product_id = P.Product_id where p.Purchase_id is null")
+ print("Товары, которые никто не покупал:")
+ for row in cur:
+ print(row)
+
+
+if __name__ == "__main__":
+ db_name = "shop.db"
+ db_exists = os.path.exists(db_name)
+
+ with (sqlite3.connect(db_name)) as db:
+ db.row_factory = sqlite3.Row
+ if not db_exists:
+ configure_db(db)
+
+ insert_manufacturers(db, "LG")
+ insert_manufacturers(db, "Samsung")
+ insert_manufacturers(db, "Haier")
+ insert_products(db, "Холодильник", 3)
+ insert_products(db, "Телевизор", 1)
+ insert_products(db, "Микроволновка", 2)
+ insert_products(db, "Телефон", 1)
+ insert_clients(db, "Иван", "Иванов")
+ insert_clients(db, "Пётр", "Петров")
+ insert_purchases(db, 1, 1, '15.05.23')
+ insert_purchases(db, 2, 2, '15.05.23')
+
+ show_products_info(db)
+ print('/*********************************************************************************************************/')
+ show_products_ostatok(db)
diff --git a/Practice/MLoginova/Loginova_13.2.py b/Practice/MLoginova/Loginova_13.2.py
new file mode 100644
index 0000000..3266555
--- /dev/null
+++ b/Practice/MLoginova/Loginova_13.2.py
@@ -0,0 +1,47 @@
+import mongoengine as me
+
+
+class RegistryUL(me.Document):
+ inn = me.StringField(max_length=10, required=True)
+ name_organ = me.StringField(max_length=256, required=True)
+ type_ownership = me.StringField(max_length=50)
+ name_owner = me.StringField(max_length=256)
+ address = me.StringField(max_length=256)
+
+ def __repr__(self):
+ return (f"ИНН:{self.inn};\n Наименование организации:{self.name_organ};\n"
+ f"Форма собственности:{self.type_ownership};\n"
+ f"ФИО владельца:{self.name_owner};\n Адрес:{self.address}.")
+
+
+if __name__ == "__main__":
+ db_name = 'test'
+ with (me.connect(db_name)) as conn:
+ if RegistryUL.objects.count() == 0:
+ if (k := input("Отсутствуют документы в БД. Заполнить тестовыми данными? (y/n)\n")) == 'y':
+ org1 = RegistryUL(inn='1234567890',
+ name_organ='SUN',
+ type_ownership='ООО',
+ name_owner='Иванов И.И.',
+ address='Россия, г.Нижний Новгород, ул. Горького, д.65')
+ org1.save()
+ org2 = RegistryUL(inn='1234567891',
+ name_organ='Оазис',
+ type_ownership='ОАО',
+ name_owner='Петров И.И.',
+ address='Россия, г.Нижний Новгород, ул. Ильниская, д.45')
+ org2.save()
+ org3 = RegistryUL(inn='1234567892',
+ name_organ='Генезис',
+ type_ownership='ПАО',
+ name_owner='Сидоров И.И.',
+ address='Россия, г.Нижний Новгород, ул. Обухова, д.20')
+ org3.save()
+ else:
+ print(f"Документов в базе:{RegistryUL.objects.count()}. Выход")
+ if RegistryUL.objects.count() != 0:
+ print(f"Документов в базе:{RegistryUL.objects.count()}.")
+ input_inn = ' '
+ while len(input_inn := input("Введите ИНН ЮЛ(10 символов): ")) != 10:
+ print(f"Вы ввели ИНН из {len(input_inn)} символов, нужно из 10. Повторите ввод!")
+ print(f"Информация по ЮЛ по ИНН {input_inn}:\n {RegistryUL.objects.filter(inn=input_inn)}")
diff --git a/Practice/MLoginova/Loginova_14.py b/Practice/MLoginova/Loginova_14.py
new file mode 100644
index 0000000..aae9727
--- /dev/null
+++ b/Practice/MLoginova/Loginova_14.py
@@ -0,0 +1,21 @@
+class MyError(Exception):
+ pass
+
+class MyFun:
+ __roman_numbers = {'M': 1000, 'CM': 900, 'D': 500, 'CD': 400,
+ 'C': 100, 'XC': 90, 'L': 50, 'XL': 40,
+ 'X': 10, 'IX': 9, 'V': 5, 'IV': 4, 'I': 1}
+
+ def to_roman(self, number):
+ roman = ''
+ if not isinstance(number, int) or number not in range(1, 5001):
+ raise MyError('NonValidInput')
+ for i, value in self.__roman_numbers.items():
+ while number >= value:
+ roman += i
+ number -= value
+ return roman
+
+
+if __name__ == '__main__':
+ m1 = MyFun()
diff --git a/Practice/MLoginova/tests.py b/Practice/MLoginova/tests.py
new file mode 100644
index 0000000..100f03d
--- /dev/null
+++ b/Practice/MLoginova/tests.py
@@ -0,0 +1,14 @@
+import pytest
+import Loginova_14
+
+
+class TestMyProv:
+ _m_test = Loginova_14.MyFun()
+
+ def test_to_roman(self):
+ assert self._m_test.to_roman(3459) == 'MMMCDLIX'
+
+ @pytest.mark.parametrize("number", ['3459', 5521, -2])
+ def test_my_error1(self, number):
+ with pytest.raises(Loginova_14.MyError):
+ self._m_test.to_roman(number)
diff --git a/Practice/Ozerova/10/10.2.py b/Practice/Ozerova/10/10.2.py
new file mode 100644
index 0000000..e01edf0
--- /dev/null
+++ b/Practice/Ozerova/10/10.2.py
@@ -0,0 +1,30 @@
+import threading
+
+def fun(x, y):
+ return x + y
+
+def add3(arg_new):
+ for args in arg_new:
+ x, y = args
+ res = fun(x, y)
+ print(f'{x} + {y} = {res}')
+
+def add_integer(a):
+ int_thr = threading.Thread(target=add3, args=(a,))
+ int_thr.start()
+
+def add_string(b):
+ str_thr = threading.Thread(target=add3, args=(b,))
+ str_thr.start()
+
+def add_list(c):
+ l_thr = threading.Thread(target=add3, args=(c,))
+ l_thr.start()
+
+a = [(1, 2), (3,4)]
+b = [('hello', 'bye'), ('Ivan', 'Maria')]
+c = [([1, 2], [3, 4]), (['abc'], ['def'])]
+
+add_integer(a)
+add_string(b)
+add_list(c)
diff --git a/Practice/Ozerova/11/11.2.client.py b/Practice/Ozerova/11/11.2.client.py
new file mode 100644
index 0000000..df18c09
--- /dev/null
+++ b/Practice/Ozerova/11/11.2.client.py
@@ -0,0 +1,22 @@
+import socket
+import pickle
+
+class User:
+ def __init__(self, name, age):
+ self.name = name
+ self.age = age
+
+def send_user_info(name, age):
+ user = User(name, age)
+ data = pickle.dumps(user)
+
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.connect(('localhost', 12345))
+
+ s.sendall(data)
+ s.close()
+
+if __name__ == '__main__':
+ name = input('Введите имя пользователя: ')
+ age = int(input('Введите возраст пользователя: '))
+ send_user_info(name, age)
diff --git a/Practice/Ozerova/11/11.2.server.py b/Practice/Ozerova/11/11.2.server.py
new file mode 100644
index 0000000..a1c5762
--- /dev/null
+++ b/Practice/Ozerova/11/11.2.server.py
@@ -0,0 +1,30 @@
+import socket
+import pickle
+
+class User:
+ def __init__(self, name, age):
+ self.name = name
+ self.age = age
+
+def receive_user_info():
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.bind(('localhost', 12345))
+ s.listen(5)
+
+ while True:
+ conn, addr = s.accept()
+
+ data = conn.recv(1024)
+
+ if not data:
+ break
+
+ user = pickle.loads(data)
+
+ print(f'Имя пользователя: {user.name}')
+ print(f'Возраст пользователя: {user.age}')
+
+ conn.close()
+
+if __name__ == '__main__':
+ receive_user_info()
\ No newline at end of file
diff --git a/Practice/Ozerova/13/13.1 b/Practice/Ozerova/13/13.1
new file mode 100644
index 0000000..e2c52f7
--- /dev/null
+++ b/Practice/Ozerova/13/13.1
@@ -0,0 +1,24 @@
+2 примера: Интернет вещей и Аналитические системы
+Интернет вещей: В предметной области имеется огромное количество устройств,
+собирающих и передающих данные, NoSQL-подход является более предпочтительным.
+Причины:
+1. гибкость схемы данных, поскольку данные могут иметь различную структуру,
+так как они могут собираться с разных типов устройств.
+NoSQL, такие как MongoDB или Cassandra, позволяют хранить данные без строгих
+требований к схеме, что позволяет легко и быстро добавлять новые типы данных или изменять существующие.
+и 2. горизонтальное масштабирование: здесь важно, чтобы база данных могла масштабироваться горизонтально,
+чтобы обрабатывать большой объем данных и обеспечивать высокую производительность.
+NoSQL обычно предлагают схему распределенной архитектуры,
+что обеспечивает горизонтальное масштабирование и масштабируемость.
+
+Аналитические системы Big Data: данные имеют большой объем и требуется выполнение сложных
+аналитических запросов, NoSQL-подход также является более подходящим.
+Причины:
+1. гибкость схемы данных - данные могут меняться и иметь различную структуру, поскольку они могут
+быть собраны из различных источников данных. NoSQL базы данных позволяют гибко изменять
+схему данных и легко добавлять новые типы данных или изменять существующие без ограничений схемы данных.
+2. горизонтальное масштабирование: аналитические системы требуют обработки большого объема данных
+для выполнения сложных запросов и анализа больших наборов данных.
+NoSQL базы данных, такие как Apache HBase или Apache Cassandra, позволяют горизонтальное масштабирование
+для обработки большого объема данных и предоставляют высокую производительность.
+
diff --git a/Practice/Pripisnov/work/my6.1work.py b/Practice/Pripisnov/work/my6.1work.py
new file mode 100644
index 0000000..e65340f
--- /dev/null
+++ b/Practice/Pripisnov/work/my6.1work.py
@@ -0,0 +1,50 @@
+class Tank:
+ def __init__(self, name, health, damage):
+ self.name = name
+ self.health = health
+ self.damage = damage
+
+ def shoot(self, target):
+ print(f"{self.name} наносит {self.damage} урона {target.name}")
+ target.health -= self.damage
+
+
+class BattleField:
+ def __init__(self, width, height):
+ self.width = width
+ self.height = height
+ self.tanks = []
+
+ def add_tank(self, tank):
+ self.tanks.append(tank)
+ print(f"{tank.name} добавлен на поле боя")
+
+ def start_battle(self):
+ print("Битва началась!")
+ while len(self.tanks) > 1:
+ attacker = self.tanks[0]
+ defender = self.tanks[1]
+
+ attacker.shoot(defender)
+
+ if defender.health <= 0:
+ print(f"{defender.name} уничтожен!")
+ self.tanks.remove(defender)
+
+ self.tanks.reverse()
+
+ print(f"{self.tanks[0].name} победил в битве!")
+
+
+# Создание экземпляров классов
+tank1 = Tank("Тiger", 100, 20)
+tank2 = Tank("Leopard", 150, 15)
+tank3 = Tank("Т-34", 120, 25)
+
+battlefield = BattleField(10, 10)
+
+battlefield.add_tank(tank1)
+battlefield.add_tank(tank2)
+battlefield.add_tank(tank3)
+
+battlefield.start_battle()
diff --git a/Practice/Pripisnov/work/my6.2work.py b/Practice/Pripisnov/work/my6.2work.py
new file mode 100644
index 0000000..0e5024d
--- /dev/null
+++ b/Practice/Pripisnov/work/my6.2work.py
@@ -0,0 +1,63 @@
+class Duck:
+ color = "коричневый" # Общий атрибут color для всех экземпляров класса
+
+ def __init__(self, name, weight):
+ self.name = name
+ self.weight = weight
+
+ @staticmethod
+ def quack():
+ print("Crack")
+
+ @classmethod
+ def get_color(cls):
+ print(f"Цвет утки - {cls.color}")
+
+ def display_info(self):
+ print(f"Имя: {self.name}, Вес: {self.weight} кг")
+
+ def __repr__(self):
+ return f"Утка(имя={self.name}, вес={self.weight})"
+
+ def __lt__(self, other):
+ return self.weight < other.weight
+
+ def __gt__(self, other):
+ return self.weight > other.weight
+
+ def __eq__(self, other):
+ return self.weight == other.weight
+
+ def __ne__(self, other):
+ return self.weight != other.weight
+
+ def __add__(self, other):
+ new_weight = self.weight + other.weight
+ return Duck("Дональд Серошейный", new_weight)
+
+
+duck1 = Duck("Серая шейка", 1.5)
+duck2 = Duck("Дональд", 2.0)
+
+# Вызов статического метода
+Duck.quack()
+
+# Вызов классового метода
+Duck.get_color()
+
+# Вызов методов экземпляров
+duck1.display_info()
+duck2.display_info()
+
+# Вызов метода __repr__
+print(duck1)
+
+# Сравнение уток по весу
+print(duck1 > duck2)
+print(duck1 < duck2)
+print(duck1 == duck2)
+print(duck1 != duck2)
+
+# Использование метода __add__
+combined_duck = duck1 + duck2
+combined_duck.display_info()
diff --git a/Practice/Pripisnov/work/my6.3work.py b/Practice/Pripisnov/work/my6.3work.py
new file mode 100644
index 0000000..bfd8f7e
--- /dev/null
+++ b/Practice/Pripisnov/work/my6.3work.py
@@ -0,0 +1,33 @@
+import os
+import tempfile
+
+
+class WrapStrToFile:
+ def __init__(self):
+ self.filepath = tempfile.mktemp()
+
+ @property
+ def content(self):
+ try:
+ with open(self.filepath, 'r') as file:
+ return file.read()
+ except FileNotFoundError:
+ return "File doesn't exist"
+
+ @content.setter
+ def content(self, value):
+ with open(self.filepath, 'w') as file:
+ file.write(value)
+
+ @content.deleter
+ def content(self):
+ os.remove(self.filepath)
+
+
+wstf = WrapStrToFile()
+print(wstf.content)
+wstf.content = 'test str'
+print(wstf.content)
+wstf.content = 'text 2'
+print(wstf.content)
+del wstf.content
diff --git a/Practice/Svetlova/D.Z5.3.1.py b/Practice/Svetlova/D.Z5.3.1.py
new file mode 100644
index 0000000..dd05420
--- /dev/null
+++ b/Practice/Svetlova/D.Z5.3.1.py
@@ -0,0 +1,16 @@
+def zamena(x, y):
+ for key, value in y.items():
+ shablon = f"{{{key}}}"
+ x = x.replace(shablon, str(value))
+
+ return x
+
+
+ # Пример использования
+stroka_shablon = "Здравствуйте, {имя}! познает {имя2} ."
+dannie = {
+ "имя": "Елена",
+ "имя2": "Pyton"
+}
+rezult = zamena(stroka_shablon, dannie)
+print(rezult)
\ No newline at end of file
diff --git a/Practice/Svetlova/D.Z6.2.py b/Practice/Svetlova/D.Z6.2.py
new file mode 100644
index 0000000..24235a8
--- /dev/null
+++ b/Practice/Svetlova/D.Z6.2.py
@@ -0,0 +1,125 @@
+class Duck:
+ color = "yellow" # Общий атрибут цвет для всех экземпляров класса
+
+ def __init__(self, name, weight):
+ """
+ Конструктор класса Duck.
+
+ Args:
+ name (str): Имя утки.
+ weight (float): Вес утки.
+ """
+ self.name = name
+ self.weight = weight
+
+ @staticmethod
+ def crack():
+ """
+ Статический метод выводящий звук треска утки.
+ """
+ print("Crack!")
+
+ @classmethod
+ def get_color(cls):
+ """
+ Классовый метод выводящий цвет уток.
+ """
+ print(f"Цвет уток {cls.color}.")
+
+ def get_name(self):
+ """
+ Метод возвращающий имя утки.
+ """
+ return self.name
+
+ def get_weight(self):
+ """
+ Метод возвращающий вес утки.
+ """
+ return self.weight
+
+ def __lt__(self, other):
+ """
+ Метод сравнения текущей утки с другой по весу (<).
+
+ Args:
+ other (Duck): Другая утка для сравнения.
+
+ Returns:
+ bool: Результат сравнения.
+ """
+ return self.weight < other.weight
+
+ def __eq__(self, other):
+ """
+ Метод сравнения текущей утки с другой по весу (==).
+
+ Args:
+ other (Duck): Другая утка для сравнения.
+
+ Returns:
+ bool: Результат сравнения.
+ """
+ return self.weight == other.weight
+
+ def __gt__(self, other):
+ """
+ Метод сравнения текущей утки с другой по весу (>).
+
+ Args:
+ other (Duck): Другая утка для сравнения.
+
+ Returns:
+ bool: Результат сравнения.
+ """
+ return self.weight > other.weight
+
+ def __ne__(self, other):
+ """
+ Метод сравнения текущей утки с другой по весу (!=).
+
+ Args:
+ other (Duck): Другая утка для сравнения.
+
+ Returns:
+ bool: Результат сравнения.
+ """
+ return self.weight != other.weight
+
+ def __add__(self, other):
+ """
+ Метод сложения двух уток, возвращающий утку с суммарным весом.
+
+ Args:
+ other (Duck): Другая утка для сложения.
+
+ Returns:
+ Duck: Утка с суммарным весом.
+ """
+ total_weight = self.weight + other.weight
+ return Duck("Сложение 2 уток", total_weight)
+
+# Создание экземпляров уток
+duck1 = Duck("Утка 1", 1.5)
+duck2 = Duck("Утка 2", 2.0)
+
+# Вызов статического метода
+Duck.crack() # Выводит: Crack!
+
+# Вызов классового метода
+Duck.get_color() # Выводит: The color of ducks is yellow.
+
+# Вызов методов экземпляров
+print(duck1.get_name()) # Выводит: Утка 1
+print(duck2.get_weight()) # Выводит: 2.0
+
+# Примеры сравнения уток по весу
+print(duck1 < duck2) # Выводит: True
+print(duck1 == duck2) # Выводит: False
+print(duck1 > duck2) # Выводит: False
+print(duck1 != duck2) # Выводит: True
+
+# Пример сложения уток
+combined_duck = duck1 + duck2
+print(combined_duck.get_name()) # Выводит: сложение
+print(combined_duck.get_weight()) # Выводит: 3.5
\ No newline at end of file
diff --git a/Practice/Svetlova/DZ5.3.py b/Practice/Svetlova/DZ5.3.py
new file mode 100644
index 0000000..ca28e89
--- /dev/null
+++ b/Practice/Svetlova/DZ5.3.py
@@ -0,0 +1,16 @@
+def zamena(x, y):
+ for key, value in y.items():
+ shablon = "{" + key + "}"
+ x = x.replace(shablon, str(value))
+
+ return x
+
+
+# Пример использования
+строка_шаблон = "Здравствуйте, {имя}! познает {имя2} ."
+данные = {
+ "имя": "Елена",
+ "имя2": "Pyton"
+}
+результирующая_строка = zamena(строка_шаблон, данные)
+print(результирующая_строка)
\ No newline at end of file
diff --git a/Practice/Svetlova/DZ5.4.py b/Practice/Svetlova/DZ5.4.py
new file mode 100644
index 0000000..cccb5ce
--- /dev/null
+++ b/Practice/Svetlova/DZ5.4.py
@@ -0,0 +1,24 @@
+def remove_column(matrix, digit):
+ num_columns = len(matrix[0])
+ indices_to_remove = set()
+ for i in range(num_columns):
+ if any(row[i] == digit for row in matrix):
+ indices_to_remove.add(i)
+ new_matrix = []
+ for row in matrix:
+ new_row = [row[i] for i in range(num_columns) if i not in indices_to_remove]
+ new_matrix.append(new_row)
+ return new_matrix
+
+matrix = [[1, 2, 3],
+ [4, 5, 6],
+ [7, 8, 9]]
+
+new_matrix = remove_column(matrix, 3)
+
+for row in new_matrix:
+ print(row)
+'''
+тип контейнера set для indices_to_remove, что позволяет эффективно проверять наличие индексов во множестве с помощью оператора
+in. Это обеспечивает более быструю и оптимальную проверку наличия индексов столбцов для удаления.
+'''
\ No newline at end of file
diff --git a/Practice/Svetlova/DZ5.5.py b/Practice/Svetlova/DZ5.5.py
new file mode 100644
index 0000000..bab1f2f
--- /dev/null
+++ b/Practice/Svetlova/DZ5.5.py
@@ -0,0 +1,37 @@
+import os
+
+def replace_tab_with_spaces_or_tabs(file_path):
+ """
+ Реализует функциональность, которая сворачивает и разворачивает символы табуляции в файле.
+ На вход передается файл, в котором необходимо заменить все символы табуляции на 4 пробела
+ или заменить все комбинации из 4 символов пробела на символ табуляции в зависимости от опции,
+ указанной пользователем.
+ """
+
+ if not os.path.isfile(file_path):
+ print("Файл не существует.")
+ return
+
+ # Запрашиваем у пользователя выбор опции
+ option = input("Выберите опцию (1 - замена символов табуляции на 4 пробела, 2 - замена 4 пробелов на символ табуляции): ")
+
+ # Определяем символы для замены в соответствии с выбранной опцией
+ replacement_chars = ('\t', ' ' * 4) if option == '1' else (' ' * 4, '\t')
+
+ # Открываем файл для чтения и записи
+ with open(file_path, 'r+') as file:
+ content = file.read()
+
+ # Заменяем символы табуляции или пробелы в соответствии с выбранной опцией
+ content = content.replace(*replacement_chars)
+
+ # Возвращаемся в начало файла, очищаем его и записываем измененный контент
+ file.seek(0)
+ file.write(content)
+ file.truncate()
+
+# Путь к файлу, который нужно обработать
+file_path = 'text.txt'
+
+# Вызываем функцию для замены символов табуляции
+replace_tab_with_spaces_or_tabs(file_path)
\ No newline at end of file
diff --git a/Practice/Svetlova/DZ6.1.py b/Practice/Svetlova/DZ6.1.py
new file mode 100644
index 0000000..f2a44c6
--- /dev/null
+++ b/Practice/Svetlova/DZ6.1.py
@@ -0,0 +1,31 @@
+class Tank:
+ def __init__(self, name, health, damage):
+ """
+ Конструктор класса Tank.
+
+ Args:
+ name (str): Имя танка.
+ health (int): Здоровье танка.
+ damage (int): Урон, наносимый танком.
+ """
+ self.name = name
+ self.health = health
+ self.damage = damage
+
+ def shoot(self, target):
+ """
+ Метод для выстрела танка в цель.
+
+ Args:
+ target (Tank): Цель для атаки.
+ """
+ target.health -= self.damage
+ print(f"{self.name} нанес {self.damage} урона по {target.name}. {target.name} имеет {target.health} здоровья.")
+
+
+# Создание экземпляров танков
+tank1 = Tank("Танк 1", 100, 20)
+tank2 = Tank("Танк 2", 120, 15)
+
+# Пример использования метода shoot()
+tank1.shoot(tank2)
diff --git a/Practice/Svetlova/DZ6.3.py b/Practice/Svetlova/DZ6.3.py
new file mode 100644
index 0000000..e983f4b
--- /dev/null
+++ b/Practice/Svetlova/DZ6.3.py
@@ -0,0 +1,36 @@
+import tempfile
+import os
+
+
+class WrapStrToFile:
+ def __init__(self):
+ self._file_path = tempfile.mktemp()
+
+ @property
+ def content(self):
+ try:
+ with open(self._file_path, 'r') as file:
+ return file.read()
+ except FileNotFoundError:
+ return 'Файл еще не существует'
+
+ @content.setter
+ def content(self, value):
+ with open(self._file_path, 'w') as file:
+ file.write(value)
+
+ @content.deleter
+ def content(self):
+ if os.path.exists(self._file_path):
+ os.remove(self._file_path)
+
+
+
+wrap = WrapStrToFile()
+print(wrap.content) # Вывод: Файл еще не существует
+
+wrap.content = 'Привет, мир!'
+print(wrap.content) # Вывод: Привет, мир!
+
+del wrap.content
+print(wrap.content) # Вывод: Файл еще не существует
\ No newline at end of file
diff --git a/Practice/Svetlova/DZ7.1.py b/Practice/Svetlova/DZ7.1.py
new file mode 100644
index 0000000..4b7104e
--- /dev/null
+++ b/Practice/Svetlova/DZ7.1.py
@@ -0,0 +1,11 @@
+class Man:
+ def __init__(self, name):
+ self.name = name
+
+ def solve_task(self):
+ print("I'm not ready yet")
+
+
+# Пример использования класса
+user = Man("Anton")
+user.solve_task()
\ No newline at end of file
diff --git a/Practice/Svetlova/DZ7.2.py b/Practice/Svetlova/DZ7.2.py
new file mode 100644
index 0000000..ad93484
--- /dev/null
+++ b/Practice/Svetlova/DZ7.2.py
@@ -0,0 +1,27 @@
+import time
+from random import randint
+
+class Man:
+ def __init__(self, name):
+ self.name = name
+
+ def solve_task(self):
+ print("Я еще не готов")
+
+class Pupil(Man):
+ def solve_task(self):
+ thinking_time = randint(3, 6)
+ print(f"{self.name} размышляет...")
+ time.sleep(thinking_time)
+ print(f"{self.name} решил/а задачу")
+
+# Пример использования классов
+man = Man("Джон")
+man.solve_task() # Вывод: "Я еще не готов"
+
+pupil = Pupil("Алиса")
+pupil.solve_task()
+# Вывод:
+# "Алиса размышляет..."
+# [ожидание случайного времени от 3 до 6 секунд]
+# "Алиса решила задачу"
\ No newline at end of file
diff --git a/Practice/Svetlova/DZ7.3.py b/Practice/Svetlova/DZ7.3.py
new file mode 100644
index 0000000..d00d01b
--- /dev/null
+++ b/Practice/Svetlova/DZ7.3.py
@@ -0,0 +1,69 @@
+class ATM:
+ def __init__(self, cash):
+ self._cash = cash
+
+ @property
+ def cash(self):
+ return self._cash
+
+ def withdraw_cash(self, amount):
+ if amount <= self._cash:
+ self._cash -= amount
+ print(f"Снял {amount} наличными.")
+ else:
+ print("Недостаточно наличных средств.")
+
+ def deposit_cash(self, amount):
+ self._cash += amount
+ print(f"Внесенная {amount} наличными.")
+
+ def make_payment(self, amount):
+ self.withdraw_cash(amount)
+
+
+class OnlineATM(ATM):
+ def make_payment(self, amount):
+ if amount <= self.cash:
+ self._cash -= amount
+ print(f"Произвел онлайн-платеж в размере {amount}.")
+ else:
+ print("Недостаточно средств для онлайн-оплаты")
+
+
+def main():
+ atm1 = ATM(1000)
+ atm2 = OnlineATM(2000)
+ atm3 = ATM(500)
+
+ atm_list = [atm1, atm2, atm3]
+
+ for atm in atm_list:
+ print(f"ATM Тип: {type(atm).__name__}")
+ print(f"Доступные наличные: {atm.cash}")
+
+ atm.make_payment(500)
+
+ atm.withdraw_cash(200)
+ atm.deposit_cash(300)
+
+ print(f"Доступные деньги: {atm.cash}")
+ print()
+
+
+if __name__ == "__main__":
+ main()
+
+'''Вот описание всех изменений, внесенных в код:
+
+#Класс ATM:
+
+Добавлено свойство cash, которое предоставляет доступ только для чтения к атрибуту _cash.
+Метод make_payment() был добавлен, который вызывает метод withdraw_cash() для совершения платежа.
+Класс OnlineATM:
+
+Метод make_payment() переопределен для класса OnlineATM и заменен на make_online_payment(), который производит онлайн-платеж.
+Функция main():
+
+Удалена проверка isinstance(atm, OnlineATM) в цикле for.
+Вместо этого, вызывается метод make_payment() для всех объектов ATM в списке atm_list,
+и в зависимости от типа объекта будет использоваться соответствующая реализация платежа.'''
\ No newline at end of file
diff --git a/Practice/Svetlova/DZ8.1.py b/Practice/Svetlova/DZ8.1.py
new file mode 100644
index 0000000..a28a684
--- /dev/null
+++ b/Practice/Svetlova/DZ8.1.py
@@ -0,0 +1,31 @@
+class ParagraphIterator:
+ def __init__(self, text, paragraph_symbol):
+ self.text = text
+ self.paragraph_symbol = paragraph_symbol
+ self.start = 0
+ self.end = 0
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ if self.end >= len(self.text):
+ raise StopIteration
+
+ self.start = self.end
+ self.end = self.text.find(self.paragraph_symbol, self.start + 1)
+ if self.end == -1:
+ self.end = len(self.text)
+
+ paragraph = self.text[self.start:self.end].strip(self.paragraph_symbol)
+ return paragraph
+
+
+# Пример использования
+text = "Это первый параграф.§Это второй параграф.§Это третий параграф."
+paragraph_symbol = "§"
+
+paragraph_iterator = ParagraphIterator(text, paragraph_symbol)
+
+for paragraph in paragraph_iterator:
+ print(paragraph)
\ No newline at end of file
diff --git a/Practice/Svetlova/DZ8.2.py b/Practice/Svetlova/DZ8.2.py
new file mode 100644
index 0000000..affc03a
--- /dev/null
+++ b/Practice/Svetlova/DZ8.2.py
@@ -0,0 +1,11 @@
+def read_lines_from_file(filename):
+ with open(filename, 'r') as file:
+ for line in file:
+ yield line.rstrip('\n')
+
+# Пример использования
+
+filename = 'example.txt'
+
+for line in read_lines_from_file(filename):
+ print(line)
\ No newline at end of file
diff --git a/Practice/Svetlova/DZ8.3.py b/Practice/Svetlova/DZ8.3.py
new file mode 100644
index 0000000..7e746f1
--- /dev/null
+++ b/Practice/Svetlova/DZ8.3.py
@@ -0,0 +1,16 @@
+import time
+
+class ExecutionTimer:
+ def __enter__(self):
+ self.start_time = time.time()
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ end_time = time.time()
+ print(f"Execution time: {end_time - self.start_time} seconds")
+
+# Пример использования
+
+with ExecutionTimer():
+ # Код, время исполнения которого нужно замерить
+ number_list = range(1000000)
+ squared_list = [x**2 for x in number_list]
\ No newline at end of file
diff --git a/Practice/Svetlova/DZ8.4.py b/Practice/Svetlova/DZ8.4.py
new file mode 100644
index 0000000..69b05ca
--- /dev/null
+++ b/Practice/Svetlova/DZ8.4.py
@@ -0,0 +1,30 @@
+from itertools import chain, combinations, filterfalse, product
+
+def merge_arrays(arr1, arr2, arr3):
+ merged_array = list(chain(arr1, arr2, arr3))
+ return merged_array
+
+def filter_strings(strings):
+ filtered_strings = list(filterfalse(lambda s: len(s) < 5, strings))
+ return filtered_strings
+
+def generate_combinations(length):
+ password = 'password'
+ comb = list(combinations(password, length))
+ return comb
+
+# Пример использования
+array1 = [1, 2, 3]
+array2 = [4, 5]
+array3 = [6, 7]
+merged = merge_arrays(array1, array2, array3)
+print("Merged array:", merged)
+
+strings = ['hello', 'i', 'write', 'cool', 'code']
+filtered = filter_strings(strings)
+print("Filtered strings:", filtered)
+
+combinations_list = generate_combinations(4)
+print("Combinations:")
+for combination in combinations_list:
+ print(combination)
\ No newline at end of file
diff --git a/Practice/Svetlova/DZ9.2.py b/Practice/Svetlova/DZ9.2.py
new file mode 100644
index 0000000..f1f38d6
--- /dev/null
+++ b/Practice/Svetlova/DZ9.2.py
@@ -0,0 +1,19 @@
+import datetime
+
+def count_working_days(start_date, end_date):
+ days_count = 0
+ current_date = start_date
+
+ while current_date <= end_date:
+ if current_date.weekday() < 5:
+ days_count += 1
+ current_date += datetime.timedelta(days=1)
+
+ return days_count
+
+# Пример использования
+start_date = datetime.date(2023, 1, 1)
+end_date = datetime.date(2023, 12, 31)
+
+working_days = count_working_days(start_date, end_date)
+print("Количество рабочих дней:", working_days)
\ No newline at end of file
diff --git a/Practice/Svetlova/DZ9.3.py b/Practice/Svetlova/DZ9.3.py
new file mode 100644
index 0000000..774433a
--- /dev/null
+++ b/Practice/Svetlova/DZ9.3.py
@@ -0,0 +1,23 @@
+import os
+import shutil
+import time
+
+folder_path = input("Введите путь до папки: ")
+
+while True:
+ for root, dirs, files in os.walk(folder_path):
+ for file in files:
+ file_path = os.path.join(root, file)
+ file_age = time.time() - os.path.getctime(file_path)
+
+ if file_age > 60: # Удаляем файлы, если их возраст больше 1 минуты
+ os.remove(file_path)
+
+ for dir in dirs:
+ dir_path = os.path.join(root, dir)
+ dir_age = time.time() - os.path.getctime(dir_path)
+
+ if dir_age > 120: # Удаляем папки, если их возраст больше 2 минут
+ shutil.rmtree(dir_path)
+
+ time.sleep(10) # Пауза в 10 секунд между проверками
\ No newline at end of file
diff --git a/Practice/Svetlova/DZ9.4.py b/Practice/Svetlova/DZ9.4.py
new file mode 100644
index 0000000..271d01b
--- /dev/null
+++ b/Practice/Svetlova/DZ9.4.py
@@ -0,0 +1,39 @@
+import pickle
+import random
+
+class Human:
+ def __init__(self, name, surname, age, residence):
+ self.name = name
+ self.surname = surname
+ self.age = age
+ self.residence = residence
+
+ def __str__(self):
+ return f"{self.name} {self.surname}, {self.age} лет/года, живет в {self.residence}"
+
+def create_humans(num_instances):
+ first_names = ["John", "Alice", "Michael", "Emily", "David", "Olivia"]
+ last_names = ["Smith", "Johnson", "Brown", "Lee", "Wilson"]
+ residences = ["New York", "Los Angeles", "Chicago", "San Francisco", "Seattle"]
+
+ humans = []
+ for _ in range(num_instances):
+ name = random.choice(first_names)
+ surname = random.choice(last_names)
+ age = random.randint(18, 60)
+ residence = random.choice(residences)
+ human = Human(name, surname, age, residence)
+ humans.append(human)
+
+ with open("human.data", "wb") as file:
+ pickle.dump(humans, file)
+
+def read_humans():
+ with open("human.data", "rb") as file:
+ humans = pickle.load(file)
+ for human in humans:
+ print(human)
+
+if __name__ == "__main__":
+ create_humans(5) # Создаем 5 экземпляров Human и сохраняем их в файл
+ read_humans() # Читаем файл и выводим содержимое на печать
\ No newline at end of file
diff --git a/Practice/Tanacheva/task9.1.py b/Practice/Tanacheva/task9.1.py
new file mode 100644
index 0000000..da3f9dc
--- /dev/null
+++ b/Practice/Tanacheva/task9.1.py
@@ -0,0 +1,27 @@
+class Paragragh:
+
+ def __init__(self, text, symbol):
+ self._i = 0
+ self._text = text
+ self.symbol = symbol
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ res = ""
+ if self._i > len(self._text)-1:
+ raise StopIteration
+ while self._text[self._i] != self.symbol:
+ res += self._text[self._i]
+ self._i += 1
+ if self._i >= len(self._text):
+ break
+ else:
+ self._i += 1
+ return res
+
+
+text1 = "gsgs;dgssgsgssg;ssdgs;dsdgdsgfd; ; dbd"
+for i in Paragragh(text1, ";"):
+ print(i)
diff --git a/Practice/Tanacheva/task9.2.py b/Practice/Tanacheva/task9.2.py
new file mode 100644
index 0000000..acde13a
--- /dev/null
+++ b/Practice/Tanacheva/task9.2.py
@@ -0,0 +1,8 @@
+def myfilereader(filepath):
+ with open(filepath, "r") as f:
+ for i in f:
+ yield i
+
+
+for i in myfilereader("1.txt"):
+ print(i)
diff --git a/Practice/Tanacheva/task9.3.py b/Practice/Tanacheva/task9.3.py
new file mode 100644
index 0000000..ddbb18b
--- /dev/null
+++ b/Practice/Tanacheva/task9.3.py
@@ -0,0 +1,15 @@
+import time
+
+
+class TestManager:
+
+ def __enter__(self):
+ self.start_time = time.time()
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ print(f"Время исполнения функции: {time.time() - self.start_time}")
+
+
+with TestManager():
+ for i in range(100000):
+ print(i)
diff --git a/Useful/for_lec17/async_example.py b/Useful/for_lec17/async_example.py
new file mode 100644
index 0000000..5b43757
--- /dev/null
+++ b/Useful/for_lec17/async_example.py
@@ -0,0 +1,41 @@
+import asyncio
+
+res = 0
+
+
+# Производим вычисления
+async def calc_fun(num):
+ global res
+ while num >= 0:
+ print(num)
+ if num % 10 == 0:
+ res = num
+ num -= 1
+ await asyncio.sleep(0.1)
+
+
+# Сообщаем о статусе вычислений
+async def status_fun():
+ global res
+ prev_res = 0
+ while res > 0:
+ if res != prev_res:
+ print(f'{res} numbers remain')
+ prev_res = res
+ await asyncio.sleep(0.1)
+
+
+# if __name__ == '__main__':
+# ev_loop = asyncio.get_event_loop()
+# tasks = [ev_loop.create_task(calc_fun(10)), ev_loop.create_task(status_fun())]
+# futures = asyncio.wait(tasks)
+# ev_loop.run_until_complete(futures)
+# ev_loop.close()
+
+
+async def main():
+ tasks = [asyncio.create_task(calc_fun(100)), asyncio.create_task(status_fun())]
+ await asyncio.gather(*tasks)
+
+if __name__ == '__main__':
+ asyncio.run(main())
diff --git a/Useful/for_lec17/condition_example.py b/Useful/for_lec17/condition_example.py
new file mode 100644
index 0000000..fcf3b19
--- /dev/null
+++ b/Useful/for_lec17/condition_example.py
@@ -0,0 +1,23 @@
+# В одном потоке заполняем список, в другом - выводим длину этого списка
+from threading import Thread, Condition
+
+lst = []
+
+
+def print_len(my_cv):
+ global lst
+ with my_cv:
+ my_cv.wait_for(lambda: 'stop' in lst)
+ lst.remove('stop')
+ print(f'length of {lst} is {len(lst)}')
+
+
+if __name__ == '__main__':
+ cv = Condition()
+ t = Thread(target=print_len, args=(cv,))
+ t.start()
+ while 'stop' not in lst:
+ lst.append(input('Enter: '))
+ with cv:
+ cv.notify()
+ t.join()
diff --git a/Useful/for_lec17/event_example.py b/Useful/for_lec17/event_example.py
new file mode 100644
index 0000000..2c196a7
--- /dev/null
+++ b/Useful/for_lec17/event_example.py
@@ -0,0 +1,23 @@
+# В одном потоке заполняем список, в другом - выводим длину этого списка
+from threading import Thread, Event
+
+lst = []
+
+
+def print_len(my_ev):
+ global lst
+ if not my_ev.is_set():
+ print('Doing some stuff')
+ my_ev.wait()
+ lst.remove('stop')
+ print(f'length of {lst} is {len(lst)}')
+
+
+if __name__ == '__main__':
+ ev = Event() # is_set() == False, by default (т.е. ресурс занят)
+ t = Thread(target=print_len, args=(ev,))
+ t.start()
+ while 'stop' not in lst:
+ lst.append(input('Enter: '))
+ ev.set()
+ t.join()
diff --git a/Useful/for_lec17/file.log b/Useful/for_lec17/file.log
new file mode 100644
index 0000000..e02d4d0
--- /dev/null
+++ b/Useful/for_lec17/file.log
@@ -0,0 +1,2 @@
+2022-08-03 16:21:41,352-INFO-This will get logged
+2022-08-03 16:22:40,769-INFO-
diff --git a/Useful/for_lec17/infile.txt b/Useful/for_lec17/infile.txt
new file mode 100644
index 0000000..822b107
--- /dev/null
+++ b/Useful/for_lec17/infile.txt
@@ -0,0 +1,78 @@
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e
+9507774534 9507774535 30 t 8 9 4 hf 89 8 0 0 9 e 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d
+9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9503334534 9505554535 10 7 6 d 9 wq 7 6 d 9 wq 7 6 d
\ No newline at end of file
diff --git a/Useful/for_lec17/log_example.py b/Useful/for_lec17/log_example.py
new file mode 100644
index 0000000..b1c5b3c
--- /dev/null
+++ b/Useful/for_lec17/log_example.py
@@ -0,0 +1,9 @@
+# Больше примеров здесь: https://webdevblog.ru/logging-v-python/
+import logging
+
+s_handler = logging.StreamHandler()
+f_handler = logging.FileHandler('file.log')
+logging.basicConfig(format='%(asctime)s-%(levelname)s-%(message)s',
+ level=logging.INFO,
+ handlers=(s_handler, f_handler))
+logging.info('Полезная информация')
\ No newline at end of file
diff --git a/Useful/for_lec17/no_lock_example.py b/Useful/for_lec17/no_lock_example.py
new file mode 100644
index 0000000..b7664c1
--- /dev/null
+++ b/Useful/for_lec17/no_lock_example.py
@@ -0,0 +1,31 @@
+# Два потока запрашивают и выводят координаты точки на плоскости
+from threading import Thread
+import time
+
+
+class Base:
+ x = 0
+ y = 0
+
+
+def print_fun():
+ i = 0
+ while i < 2:
+ Base.x = input("2x: ")
+ Base.y = input("2y: ")
+ print(f'Thread 2: Base.x = {Base.x}, Base.y = {Base.y}')
+ i += 1
+ time.sleep(1)
+
+
+if __name__ == '__main__':
+ t = Thread(target=print_fun)
+ t.start()
+ i = 0
+ while i < 2:
+ Base.x = input("1x: ")
+ Base.y = input("1y: ")
+ print(f'Thread 1: Base.x = {Base.x}, Base.y = {Base.y}')
+ i += 1
+ time.sleep(1)
+ t.join()
diff --git a/Useful/for_lec17/outfile.txt b/Useful/for_lec17/outfile.txt
new file mode 100644
index 0000000..8d77d8c
--- /dev/null
+++ b/Useful/for_lec17/outfile.txt
@@ -0,0 +1 @@
+90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90397776589378917156, 90397776608394466225, 900, 64, 81, 16, 7921, 64, 0, 0, 81, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 90313367265116997156, 90355567017859066225, 100, 49, 36, 81, 49, 36, 81, 49, 36,
\ No newline at end of file
diff --git a/Useful/for_lec17/pool_example.py b/Useful/for_lec17/pool_example.py
new file mode 100644
index 0000000..12dd165
--- /dev/null
+++ b/Useful/for_lec17/pool_example.py
@@ -0,0 +1,18 @@
+from multiprocessing import Pool
+import time
+import os
+
+
+lst = []
+
+def cube(x):
+ lst.append(x)
+ print(f"This process {os.getpid()} processed values {lst}")
+ time.sleep(2)
+ return x**3
+
+if __name__ == '__main__':
+ pool = Pool(processes=5)
+ res = pool.map(cube, range(1,7))
+ # res = pool.starmap, если в функцию нужно передать несколько аргументов
+ print(res)
diff --git a/Useful/for_lec17/python_mp_pool.pdf b/Useful/for_lec17/python_mp_pool.pdf
new file mode 100644
index 0000000..fb1f53f
Binary files /dev/null and b/Useful/for_lec17/python_mp_pool.pdf differ
diff --git a/Useful/for_lec17/queue_example.py b/Useful/for_lec17/queue_example.py
new file mode 100644
index 0000000..a8307c1
--- /dev/null
+++ b/Useful/for_lec17/queue_example.py
@@ -0,0 +1,40 @@
+import threading
+import queue
+import time
+
+
+start = time.perf_counter()
+
+q = queue.Queue()
+
+
+def worker(): # функция выполняемая в дочернем потоке
+ with open('outfile.txt', 'w') as fout:
+ item = '0'
+ while item.isdigit():
+ item = q.get() # ожидаем появление элемента в очереди
+ print(f'get: {item = }')
+ if item.isdigit():
+ fout.write(f'{int(item) ** 2}, ')
+ print('task_done')
+ q.task_done() # уведомляем очередь о завершении обработки
+
+
+t = threading.Thread(target=worker)
+t.start()
+
+with open('infile.txt') as fin:
+ for line in fin:
+ print(f'main: line: {line}')
+ items = line.split()
+ for each in items:
+ print(f'main: each: {each}')
+ if each.isdigit():
+ q.put(each) # добавляем элемент в очередь и ничего не ждем,
+ q.join() # ожидаем обработки элементов в очереди
+ q.put('stop') # добавляем элемент в очередь и ничего не ждем,
+ q.join() # ожидаем обработки элементов в очереди
+
+t.join()
+
+print('Total time: {}'.format(time.perf_counter() - start))
\ No newline at end of file
diff --git a/Useful/for_lec17/rlock_example.py b/Useful/for_lec17/rlock_example.py
new file mode 100644
index 0000000..84a8fe6
--- /dev/null
+++ b/Useful/for_lec17/rlock_example.py
@@ -0,0 +1,35 @@
+# Два потока запрашивают и выводят координаты точки на плоскости
+from threading import Thread, RLock
+import time
+
+
+class Base:
+ x = 0
+ y = 0
+
+
+def print_fun(my_lock):
+ i = 0
+ while i < 2:
+ with my_lock:
+ Base.x = input("2x: ")
+ Base.y = input("2y: ")
+ print(f'Thread 2: Base.x = {Base.x}, Base.y = {Base.y}')
+ i += 1
+ time.sleep(1)
+
+
+if __name__ == '__main__':
+ lock = RLock()
+ t = Thread(target=print_fun, args=(lock,))
+ t.start()
+ i = 0
+ while i < 2:
+ with lock:
+ Base.x = input("1x: ")
+ Base.y = input("1y: ")
+ print(f'Thread 1: Base.x = {Base.x}, Base.y = {Base.y}')
+ i += 1
+ time.sleep(1)
+ t.join()
+
\ No newline at end of file
diff --git a/Useful/for_lec18/cherrypy_example_1.py b/Useful/for_lec18/cherrypy_example_1.py
new file mode 100644
index 0000000..89054b8
--- /dev/null
+++ b/Useful/for_lec18/cherrypy_example_1.py
@@ -0,0 +1,13 @@
+import cherrypy
+
+
+class HelloWorld:
+
+ @cherrypy.expose
+ def index(self):
+ return 'This is not easy!'
+
+
+if __name__ == '__main__':
+ cherrypy.config.update({'server.socket_port': 8099})
+ cherrypy.quickstart(HelloWorld())
diff --git a/Useful/for_lec18/cherrypy_example_2.py b/Useful/for_lec18/cherrypy_example_2.py
new file mode 100644
index 0000000..f18637d
--- /dev/null
+++ b/Useful/for_lec18/cherrypy_example_2.py
@@ -0,0 +1,17 @@
+import cherrypy
+
+
+class Hello:
+ @cherrypy.expose
+ def index(self):
+ with open('index.html', 'r') as f:
+ s = f.read()
+ return s.replace(r'{%_%}', 'name')
+
+ @cherrypy.expose
+ def hello(self, name):
+ return f'Hello, {name}'
+
+
+if __name__ == '__main__':
+ cherrypy.quickstart(Hello(), '/')
\ No newline at end of file
diff --git a/Useful/for_lec18/class_tcp_client.py b/Useful/for_lec18/class_tcp_client.py
new file mode 100644
index 0000000..e4d7476
--- /dev/null
+++ b/Useful/for_lec18/class_tcp_client.py
@@ -0,0 +1,26 @@
+import socket
+import random
+import time
+
+
+class TcpClient:
+ def __init__(self, host, port, name):
+ self.host = host
+ self.port = port
+ self.name = name
+ self._socket = None
+
+ def run(self):
+ self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self._socket.connect((self.host, self.port))
+ time.sleep(3)
+ self._socket.send(self.name.encode())
+ data = self._socket.recv(1024)
+ print(f'Received: {data.decode()}')
+ self._socket.close()
+
+
+if __name__ == '__main__':
+ name = 'Python client ' + str(random.randint(1, 1000))
+ myclient = TcpClient(host='127.0.0.1', port=5555, name=name)
+ myclient.run()
\ No newline at end of file
diff --git a/Useful/for_lec18/class_tcp_server.py b/Useful/for_lec18/class_tcp_server.py
new file mode 100644
index 0000000..f0be945
--- /dev/null
+++ b/Useful/for_lec18/class_tcp_server.py
@@ -0,0 +1,51 @@
+import threading
+import socket
+
+
+class ClientThread(threading.Thread):
+ def __init__(self, conn, addr):
+ super().__init__()
+ self._connection = conn
+ self._address = addr
+
+ def run(self):
+ print('Connection from address {}'.format(self._address))
+ data = self._connection.recv(1024)
+ print('Received {}'.format(data.decode()))
+ self._connection.send(data)
+ self._connection.close()
+ print('Closed connection from {}'.format(self._address))
+
+
+class TcpServer:
+ def __init__(self, host, port):
+ self.host = host
+ self.port = port
+ self._socket = None
+ self._running = False
+
+ def run(self):
+ self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ self._socket.bind((self.host, self.port))
+ self._socket.listen(5)
+ self._running = True
+
+ print('Server is up')
+ while self._running:
+ conn, addr = self._socket.accept()
+ ClientThread(conn, addr).start()
+
+ def stop(self):
+ self._running = False
+ self._socket.close()
+ print('Server is down')
+
+
+if __name__ == '__main__':
+ srv = TcpServer(host='127.0.0.1', port=5555)
+ try:
+ srv.run()
+ except KeyboardInterrupt:
+ srv.stop()
+
diff --git a/Useful/for_lec18/file_task_example/create_files.py b/Useful/for_lec18/file_task_example/create_files.py
new file mode 100644
index 0000000..21ca6c0
--- /dev/null
+++ b/Useful/for_lec18/file_task_example/create_files.py
@@ -0,0 +1,9 @@
+import random
+
+
+
+for i in range(50):
+ name = f"file_{i}"
+ with open(name, "w") as f:
+ f.write(str(random.randint(1, 1000)))
+
diff --git a/Useful/for_lec18/file_task_example/task.py b/Useful/for_lec18/file_task_example/task.py
new file mode 100644
index 0000000..2fb0110
--- /dev/null
+++ b/Useful/for_lec18/file_task_example/task.py
@@ -0,0 +1,37 @@
+import multiprocessing as mp
+import math
+import os
+
+
+def file_handler(fname_lst):
+ lst = []
+ for fname in fname_lst:
+ with open(fname) as f:
+ s = f.read().strip()
+ if (i := int(s)) % 10 == 0:
+ lst.append(i)
+ print(lst)
+
+
+if __name__ == "__main__":
+ fname_lst = []
+ for fname in os.listdir('.'):
+ if fname.startswith("file"):
+ fname_lst.append(fname)
+
+
+ num_of_proc = os.cpu_count() - 1
+ process_lst = []
+ num_per_proc = math.ceil(len(fname_lst) / num_of_proc)
+ start = 0
+ stop = num_per_proc
+ for i in range(num_of_proc):
+ slice = fname_lst[start:stop]
+ start = stop
+ stop += num_per_proc
+ p = mp.Process(target=file_handler, args=(slice,))
+ p.start()
+ process_lst.append(p)
+
+ for p in process_lst:
+ p.join()
diff --git a/Useful/for_lec18/http_codes.jpg b/Useful/for_lec18/http_codes.jpg
new file mode 100644
index 0000000..b4eff78
Binary files /dev/null and b/Useful/for_lec18/http_codes.jpg differ
diff --git a/Useful/for_lec18/index.html b/Useful/for_lec18/index.html
new file mode 100644
index 0000000..f96b633
--- /dev/null
+++ b/Useful/for_lec18/index.html
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Useful/for_lec18/mail_sender.py b/Useful/for_lec18/mail_sender.py
new file mode 100644
index 0000000..14e755a
--- /dev/null
+++ b/Useful/for_lec18/mail_sender.py
@@ -0,0 +1,20 @@
+from smtplib import SMTP_SSL
+from email.mime.text import MIMEText
+
+
+def send(message):
+ sender = 'python.course@mail.ru'
+ targets = ['orlov.soft.company@gmail.com']
+
+ msg = MIMEText(message)
+ msg['Subject'] = 'Simple subject'
+ msg['From'] = sender
+ msg['To'] = ', '.join(targets)
+
+ server = SMTP_SSL('smtp.mail.ru', 465)
+ server.login(sender, 'CVul9R2wTuFbdgomGjGK')
+ server.sendmail(sender, targets, msg.as_string())
+ server.quit()
+
+
+send("Привет2")
diff --git a/Useful/for_lec18/myhttpserver.py b/Useful/for_lec18/myhttpserver.py
new file mode 100644
index 0000000..e3b3978
--- /dev/null
+++ b/Useful/for_lec18/myhttpserver.py
@@ -0,0 +1,19 @@
+from http.server import BaseHTTPRequestHandler, HTTPServer
+
+
+class MyHandler(BaseHTTPRequestHandler):
+ # обработчик GET запросов
+ def do_GET(self):
+ self.send_response(200) # OK
+ self.send_header('Content-type', 'text/html')
+ self.end_headers()
+ # собственно html сообщение
+ self.wfile.write('Hello World!'.encode())
+
+
+if __name__ == '__main__':
+ port = 8080
+ server = HTTPServer(('127.0.0.1', port), MyHandler)
+ print('Started HTTP server on port: {}'.format(port))
+ # бесконечно ожидаем входящие http запросы
+ server.serve_forever()
\ No newline at end of file
diff --git a/Useful/for_lec18/pool_example.py b/Useful/for_lec18/pool_example.py
new file mode 100644
index 0000000..21a186b
--- /dev/null
+++ b/Useful/for_lec18/pool_example.py
@@ -0,0 +1,20 @@
+from multiprocessing import Pool
+import time
+import os
+
+
+lst = []
+
+
+def cube(x):
+ lst.append(x)
+ print(f"This process {os.getpid()} processed values {lst}")
+ time.sleep(2)
+ return x**3
+
+
+if __name__ == '__main__':
+ pool = Pool(processes=5)
+ res = pool.map(cube, range(1,7))
+ # res = pool.starmap, если в функцию нужно передать несколько аргументов
+ print(res)
diff --git a/Useful/for_lec18/ports_acqusition.txt b/Useful/for_lec18/ports_acqusition.txt
new file mode 100644
index 0000000..d38556c
--- /dev/null
+++ b/Useful/for_lec18/ports_acqusition.txt
@@ -0,0 +1,12 @@
+TCP
+->*127.0.0.1:12345*
+127.0.0.1:59830->127.0.0.1:12345
+
+127.0.0.1:59830<-127.0.0.1:55555
+127.0.0.1:59830->127.0.0.1:55555
+
+UDP
+->*127.0.0.1:12345*
+127.0.0.1:59830->127.0.0.1:12345
+
+127.0.0.1:59830<-127.0.0.1:12345
\ No newline at end of file
diff --git a/Useful/for_lec18/re_temp_example.py b/Useful/for_lec18/re_temp_example.py
new file mode 100644
index 0000000..b573368
--- /dev/null
+++ b/Useful/for_lec18/re_temp_example.py
@@ -0,0 +1,12 @@
+from urllib import request
+import re
+
+
+req = request.Request('https://yandex.ru/pogoda/nizhny-novgorod?utm_source=home&utm_medium=web&utm_campaign=informer&utm_content=main_informer&utm_term=title&lat=56.326887&lon=44.005986')
+response = request.urlopen(req)
+web_page = response.read().decode()
+print(web_page)
+
+s = re.compile(r'"Текущая температура ([+|−]?\d+)')
+res = re.findall(s, web_page)
+print(res)
diff --git a/Useful/for_lec18/tcp_client.py b/Useful/for_lec18/tcp_client.py
new file mode 100644
index 0000000..82b7244
--- /dev/null
+++ b/Useful/for_lec18/tcp_client.py
@@ -0,0 +1,12 @@
+import socket
+import pickle
+import user
+
+
+s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+
+host = '127.0.0.1'
+port = 12345
+s.connect((host, port)) # подключаемся к серверу
+print(pickle.loads(s.recv(1024))) # получаем и выводим данные, полученные от сервера
+s.close()
diff --git a/Useful/for_lec18/tcp_client1.py b/Useful/for_lec18/tcp_client1.py
new file mode 100644
index 0000000..d4bd795
--- /dev/null
+++ b/Useful/for_lec18/tcp_client1.py
@@ -0,0 +1,13 @@
+import socket
+
+s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+host = '127.0.0.1'
+port = 12345
+s.connect((host, port)) # подключаемся к серверу
+d = s.recv(1024) # получаем данные от сервера (1024 байта - размер буфера для данных)
+while d:
+ print(d.decode())
+ d = s.recv(1024)
+# преобразуем данные из байтового представления в строковое и выводим
+# (преобразование из utf-8 в ascii)
+s.close()
diff --git a/Useful/for_lec18/tcp_server.py b/Useful/for_lec18/tcp_server.py
new file mode 100644
index 0000000..0d080a0
--- /dev/null
+++ b/Useful/for_lec18/tcp_server.py
@@ -0,0 +1,21 @@
+import socket
+import pickle
+import user
+
+
+a = user.User('Ivan', 30)
+
+# 1-й параметр - семейство адресов, с которыми будет работать сокет
+# AF_INET соотвествует адресам IPv4
+# 2-й параметр - протокол транспортного уровня
+# SOCK_STREAM соотвествует протоколу TCP
+with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
+ host = '127.0.0.1'
+ port = 12345
+ s.bind((host, port))
+ s.listen(5) # Открываем порт на сервере (не более 5 клиентов одновременно)
+ while True:
+ conn, addr = s.accept()
+ with conn:
+ print('Server got connection from {}'.format(addr))
+ conn.send(pickle.dumps(a))
diff --git a/Useful/for_lec18/tcp_server1.py b/Useful/for_lec18/tcp_server1.py
new file mode 100644
index 0000000..6dfa1d9
--- /dev/null
+++ b/Useful/for_lec18/tcp_server1.py
@@ -0,0 +1,18 @@
+import socket
+
+# 1-й параметр - семейство адресов, с которыми будет работать сокет
+# AF_INET соответствует адресам IPv4
+# 2-й параметр - протокол транспортного уровня
+# SOCK_STREAM соотвествует протоколу TCP
+s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+host = '127.0.0.1' # '127.0.0.1' соответствует хосту, на котором запускается скрипт
+port = 12345
+s.bind((host, port))
+s.listen(5) # Открываем порт на сервере (не более 5 клиентов одновременно)
+while True:
+ conn, addr = s.accept()
+ print('Server got connection from {}'.format(addr))
+ # Преобразуем строку в набор байтов (ascii в utf-8) и отправляем
+ conn.send('Thank you for the connection'.encode())
+ conn.close()
+s.close()
diff --git a/Useful/for_lec18/udp_client.py b/Useful/for_lec18/udp_client.py
new file mode 100644
index 0000000..8d4b355
--- /dev/null
+++ b/Useful/for_lec18/udp_client.py
@@ -0,0 +1,12 @@
+import socket
+
+s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+
+host = '127.0.0.1'
+port = 12345
+# отправляем сообщение серверу без установки соединения
+s.sendto('client data'.encode(), (host, port))
+# получаем ответ от сервера
+data, addr = s.recvfrom(1024)
+print(data.decode())
+s.close()
\ No newline at end of file
diff --git a/Useful/for_lec18/udp_client_encryption.py b/Useful/for_lec18/udp_client_encryption.py
new file mode 100644
index 0000000..3a7df8c
--- /dev/null
+++ b/Useful/for_lec18/udp_client_encryption.py
@@ -0,0 +1,19 @@
+import socket
+
+
+def encrypt(string):
+ res = ""
+ for x in string:
+ res += str(ord(x)) + ' '
+ return res
+
+
+s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+host = '127.0.0.1'
+port = 12345
+# отправляем сообщение серверу без установки соединения
+s.sendto(encrypt('hello world').encode(), (host, port))
+# получаем ответ от сервера
+data, addr = s.recvfrom(1024)
+print(data.decode())
+s.close()
diff --git a/Useful/for_lec18/udp_server.py b/Useful/for_lec18/udp_server.py
new file mode 100644
index 0000000..b48d182
--- /dev/null
+++ b/Useful/for_lec18/udp_server.py
@@ -0,0 +1,14 @@
+import socket
+
+# AF_INET соотвествует адресам IPv4
+# SOCK_DGRAM соотвествует протоколу UDP
+s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+
+host = '127.0.0.1'
+port = 12345
+s.bind((host, port))
+while True:
+ data, addr = s.recvfrom(1024)
+ print('Server got data from client: {}'.format(data.decode()))
+ s.sendto('Thank you for the data'.encode(), addr)
+s.close()
\ No newline at end of file
diff --git a/Useful/for_lec18/udp_server_decryption.py b/Useful/for_lec18/udp_server_decryption.py
new file mode 100644
index 0000000..1c640a8
--- /dev/null
+++ b/Useful/for_lec18/udp_server_decryption.py
@@ -0,0 +1,26 @@
+import socket
+
+
+def decrypt(string):
+ res = ""
+ for x in string.split(' '):
+ if x.isdigit():
+ res += chr(int(x))
+ else:
+ res += x
+ return res
+
+
+# AF_INET соответствует адресам IPv4
+# SOCK_DGRAM соответствует протоколу UDP
+s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+host = '127.0.0.1'
+port = 12345
+s.bind((host, port))
+while True:
+ data, addr = s.recvfrom(1024) # размер буфера для данных – 1024 байта
+ res = data.decode()
+ print(f"{res}")
+ print(f'Server got data from client: {decrypt(res)}')
+ s.sendto('Thank you for the data'.encode(), addr)
+# s.close()
diff --git a/Useful/for_lec18/urllib_example.py b/Useful/for_lec18/urllib_example.py
new file mode 100644
index 0000000..d0b542e
--- /dev/null
+++ b/Useful/for_lec18/urllib_example.py
@@ -0,0 +1,11 @@
+from urllib import request
+import re
+
+
+req = request.Request('https://yandex.ru/pogoda/nizhny-novgorod?lat=56.326887&lon=44.005986')
+response = request.urlopen(req)
+web_page = response.read().decode()
+
+s = re.compile(r'Текущая температура([+|-]\d+) ')
+res = re.findall(s, web_page)
+print(res)
diff --git a/Useful/for_lec18/user.py b/Useful/for_lec18/user.py
new file mode 100644
index 0000000..d3c8dc6
--- /dev/null
+++ b/Useful/for_lec18/user.py
@@ -0,0 +1,6 @@
+class User:
+ def __init__(self, name, age):
+ self.name = name
+ self.age = age
+ def __repr__(self):
+ return 'User: name={}, age={}'.format(self.name, self.age)
diff --git a/Useful/for_lec18/xmlrpc_client.py b/Useful/for_lec18/xmlrpc_client.py
new file mode 100644
index 0000000..393f93e
--- /dev/null
+++ b/Useful/for_lec18/xmlrpc_client.py
@@ -0,0 +1,5 @@
+import xmlrpc.client
+
+proxy = xmlrpc.client.ServerProxy("http://localhost:8000/")
+print(f"3 is even: {proxy.is_even(3)}")
+print(f"100 is even: {proxy.is_even(100)}")
diff --git a/Useful/for_lec18/xmlrpc_server.py b/Useful/for_lec18/xmlrpc_server.py
new file mode 100644
index 0000000..3028d34
--- /dev/null
+++ b/Useful/for_lec18/xmlrpc_server.py
@@ -0,0 +1,12 @@
+from xmlrpc.server import SimpleXMLRPCServer
+
+
+def is_even(n):
+ return n % 2 == 0
+
+
+if __name__ == '__main__':
+ server = SimpleXMLRPCServer(("127.0.0.1", 8000))
+ print("Listening on port 8000...")
+ server.register_function(is_even, "is_even")
+ server.serve_forever()
diff --git "a/Useful/for_lec18/\320\274\320\276\320\264\320\265\320\273\320\270_osi_\320\270_tcp_ip.png" "b/Useful/for_lec18/\320\274\320\276\320\264\320\265\320\273\320\270_osi_\320\270_tcp_ip.png"
new file mode 100644
index 0000000..f2347b3
Binary files /dev/null and "b/Useful/for_lec18/\320\274\320\276\320\264\320\265\320\273\320\270_osi_\320\270_tcp_ip.png" differ
diff --git a/Useful/for_lec19/check_sqlalchemy.py b/Useful/for_lec19/check_sqlalchemy.py
new file mode 100644
index 0000000..5ba50ff
--- /dev/null
+++ b/Useful/for_lec19/check_sqlalchemy.py
@@ -0,0 +1,77 @@
+from sqlalchemy import create_engine
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy import Column, Integer, String
+from sqlalchemy.schema import CreateTable
+from sqlalchemy.orm import sessionmaker
+
+
+Base = declarative_base()
+
+# Создаем базу данных в памяти (без файла)
+engine = create_engine('sqlite://')
+print('База данных создана')
+
+# Создаем класс, отображающий таблицу
+class User(Base):
+ __tablename__ = 'users'
+
+ id = Column(Integer, primary_key=True)
+ name = Column(String)
+ fullname = Column(String)
+ password = Column(String)
+
+ def __repr__(self):
+ return "".format(
+ self.name, self.fullname, self.password)
+
+# Создаем схему
+Base.metadata.create_all(engine)
+print(CreateTable(User.__table__).compile(engine))
+print('Таблица создана')
+
+# Открываем сессию
+Session = sessionmaker(bind=engine)
+session = Session()
+print('Сессия открыта')
+
+# Создаем объект отображаемого класса
+ed_user = User(name='ed', fullname='Ed jones', password='edpassword')
+
+# Добавляем новый объект в базу
+session.add(ed_user)
+session.commit()
+print('Объект добавлен')
+
+# Проверяем, что получилось
+print(session.query(User).filter_by(name='ed').first())
+
+# Добавляем несколько объектов в базу
+session.add_all([
+ User(name='wendy', fullname='Wendy Williams', password='pwd'),
+ User(name='mary', fullname='Mary Contrary', password='qwerty'),
+ User(name='fred', fullname='Fred Flinstone', password='123')])
+session.commit()
+print('Объекты добавлены')
+
+# Проверяем, что получилось
+print('Объектов User в базе: {}'.format(session.query(User).count()))
+print(session.query(User).all())
+
+# Изменяем запись
+ed = session.query(User).filter_by(name='ed').first()
+ed.password = 'edsnewpassword'
+session.add(ed)
+session.commit()
+
+# Проверяем, что получилось
+#print(session.query(User).filter_by(name='ed').first())
+print('Объектов User в базе: {}'.format(session.query(User).count()))
+print(session.query(User).all())
+
+# Удаляем запись
+session.delete(ed)
+session.commit()
+
+# Проверяем, что получилось
+print('Объектов User в базе: {}'.format(session.query(User).count()))
+print(session.query(User).all())
diff --git a/Useful/for_lec19/check_sqlite.py b/Useful/for_lec19/check_sqlite.py
new file mode 100644
index 0000000..f817058
--- /dev/null
+++ b/Useful/for_lec19/check_sqlite.py
@@ -0,0 +1,94 @@
+import sqlite3
+
+
+# Подключаемся к базе данных
+# Файл базы данных (если он еще не создан будет создан автоматически
+conn = sqlite3.connect('sqlite.db')
+print('База данных открыта')
+
+# Создаем таблицу
+conn.execute('CREATE TABLE COMPANY'
+ ' (ID INT PRIMARY KEY NOT NULL,'
+ ' NAME TEXT NOT NULL,'
+ ' AGE INT NOT NULL,'
+ ' ADDRESS CHAR(50),'
+ ' SALARY REAL);')
+print('Таблица создана')
+
+# Добавляем данные
+conn.execute("INSERT INTO COMPANY (ID, NAME, AGE, ADDRESS, SALARY) "
+ "VALUES (1, 'Paul', 32, 'California', 20000.00)")
+conn.execute("INSERT INTO COMPANY (ID, NAME, AGE, ADDRESS, SALARY) "
+ "VALUES (2, 'Allen', 25, 'Texas', 15000.00)")
+conn.execute("INSERT INTO COMPANY (ID, NAME, AGE, ADDRESS, SALARY) "
+ "VALUES (3, 'Teddy', 23, 'Norway', 20000.00)")
+conn.execute("INSERT INTO COMPANY (ID, NAME, AGE, ADDRESS, SALARY) "
+ "VALUES (4, 'Mark', 25, 'Richmond', 65000.00)")
+conn.commit()
+print('Данные добавлены')
+
+# Читаем данные
+cursor = conn.cursor()
+print("############Test row_factory 1############")
+cursor.row_factory = sqlite3.Row
+cursor.execute('SELECT id, name, address, salary from COMPANY WHERE id = 1')
+for row in cursor:
+ print(dict(row))
+print("##############End of test 1###############")
+
+print("############Test row_factory 2############")
+cursor.row_factory = None
+cursor.execute('SELECT id, name, address, salary from COMPANY WHERE id = 1')
+for row in cursor:
+ print(row)
+print("##############End of test 2###############")
+
+# Изменяем данные
+conn.execute('UPDATE COMPANY set salary = 25000.00 WHERE id = 1')
+conn.commit()
+print('Данные изменены')
+# Проверяем, что получилось
+cursor = conn.execute('SELECT id, name, address, salary from '
+ 'COMPANY WHERE id = 1')
+print(cursor.fetchone())
+
+# Удаляем данные
+conn.execute('DELETE from COMPANY where id=2;')
+conn.commit()
+print('Данные удалены')
+# Проверяем, что получилось
+cursor = conn.execute('SELECT id, name, address, salary from COMPANY')
+for row in cursor:
+ print(row)
+
+# Изменяем данные
+conn.execute('UPDATE COMPANY set salary = 25000.00 WHERE id = 1')
+conn.commit()
+print('Данные снова изменены')
+
+# Проверяем, что получилось
+cursor = conn.execute('SELECT id, name, address, salary from '
+ 'COMPANY WHERE id = 1')
+print(cursor.fetchone())
+
+# При необходимости подставить в запрос значения, полученные в коде Python
+# НЕ следует использовать строковые операции (например, конкатенацию)
+# с текстом запроса!
+# Для подстановки параметров в запрос необходимо указать специальный символ
+# '?' на местах подстановки в тексте запроса, а параметры передать в кортеже
+# в соответствующем порядке в тот же метод execute().
+conn.execute("INSERT INTO COMPANY (ID, NAME, AGE, ADDRESS, SALARY)"
+ "VALUES (?, ?, ?, ?, ?)",
+ (5, 'Allen', 32, 'Texas', 15000.00))
+conn.commit()
+print('Новые данные добавлены')
+
+# Проверяем, что получилось
+cursor = conn.execute('SELECT id, name, address, salary from COMPANY')
+for row in cursor:
+ print(row)
+
+conn.execute('DROP TABLE COMPANY')
+print('Таблица удалена')
+
+conn.close()
diff --git a/Useful/for_lec19/cx_oracle_example.py b/Useful/for_lec19/cx_oracle_example.py
new file mode 100644
index 0000000..ac23d28
--- /dev/null
+++ b/Useful/for_lec19/cx_oracle_example.py
@@ -0,0 +1,10 @@
+import cx_Oracle
+
+
+host = '192.168.10.21'
+username = 'USER'
+password = '123456'
+port = 1521
+sid = 'mydb'
+dbname = cx_Oracle.makedsn(host, port, sid=sid)
+connection = cx_Oracle.connect(username, password, dbname)
diff --git a/Useful/for_lec19/db_client.py b/Useful/for_lec19/db_client.py
new file mode 100644
index 0000000..1b69106
--- /dev/null
+++ b/Useful/for_lec19/db_client.py
@@ -0,0 +1,91 @@
+import logging
+import csv
+import cx_Oracle
+import sqlite3
+
+
+DB_CODE = 'cp1251'
+
+
+def OutputTypeHandler(cursor, name, defaultType, size, precision, scale):
+ if defaultType == cx_Oracle.CLOB:
+ return cursor.var(cx_Oracle.LONG_STRING, arraysize=cursor.arraysize)
+ if defaultType == cx_Oracle.BLOB:
+ return cursor.var(cx_Oracle.LONG_BINARY, arraysize=cursor.arraysize)
+
+
+class DBClient(object):
+
+ def __init__(self, dbtype='sqlite', dbname='tmp.db',
+ username=None, password=None):
+ self.dbtype = dbtype
+ self.conn = self.connect(dbtype, dbname, username, password)
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ self.conn.close()
+
+
+ @staticmethod
+ def connect(dbtype, dbname, username=None, password=None):
+ connection = None
+ try:
+ logging.debug('Create connection to db: dbtype={}, dbname={}'.format(dbtype, dbname))
+ if dbtype == 'sqlite':
+ connection = sqlite3.connect(dbname)
+ connection.row_factory = sqlite3.Row
+ elif dbtype == 'oracle':
+ connection = cx_Oracle.connect(username, password, dbname)
+ connection.outputtypehandler = OutputTypeHandler
+ except Exception as err:
+ logging.critical('Error: cannot connect to db. {}'.format(err))
+ raise err
+ return connection
+
+ @staticmethod
+ def show_multi_data(headline, rows, csvfilename=None):
+ if csvfilename:
+ with open(csvfilename, 'wb') as csvfile:
+ writer = csv.DictWriter(csvfile, fieldnames=headline)
+ writer.writeheader()
+ for r in rows:
+ # d = {k: v if isinstance(v, bytes) else v for k, v in dict(r).items()}
+ # writer.writerow(d)
+ writer.writerow(dict(r))
+ else:
+ logging.info(headline)
+ for r in rows:
+ logging.info(' {},'.format([x.decode(DB_CODE) if isinstance(x, bytes) else x for x in r]))
+
+ def get_student_info(self):
+ cur = self.conn.cursor()
+ cur.execute('SELECT s.*, o.DESCRIPTION, o.INSERTED, os.STATUS_NAME '
+ 'FROM edu_student s, edu_object o, edu_object_status os '
+ 'WHERE s.OBJECT_ID = o.OBJECT_ID AND o.OBJECT_STATUS_ID = os.OBJECT_STATUS_ID')
+ col_names = [each[0] for each in cur.description]
+ cur.rowfactory = lambda *args: dict(zip([d[0] for d in cur.description], args))
+ return col_names, cur.fetchall()
+
+ def get_contract_info(self):
+ cur = self.conn.cursor()
+ cur.execute('SELECT co.CONTRACT_NUMBER, co.COST, co.BEGIN_DATE, co.END_DATE, '
+ 'co.CREATION_DATE, co.CUSTOMER, '
+ 'o2.OBJECT_NAME, o.DESCRIPTION, o.INSERTED, os.STATUS_NAME, '
+ 's.LAST_NAME, s.FIRST_NAME, s.SECOND_NAME '
+ 'FROM edu_contract co, edu_object o, edu_object o2, edu_student s, edu_object_status os '
+ 'WHERE o.OBJECT_ID = co.OBJECT_ID AND o2.OBJECT_ID = co.COURSE_ID AND os.OBJECT_STATUS_ID = o.OBJECT_STATUS_ID '
+ 'AND s.OBJECT_ID = co.STUDENT_ID')
+ col_names = [each[0] for each in cur.description]
+ cur.rowfactory = lambda *args: dict(zip([d[0] for d in cur.description], args))
+ return col_names, cur.fetchall()
+
+ def export_data_to_csv(self):
+ col_names, rows = self.get_student_info()
+ self.show_multi_data(col_names, rows, 'students.csv')
+ col_names, rows = self.get_contract_info()
+ self.show_multi_data(col_names, rows, 'contract.csv')
+ # self.show_multi_data(col_names, rows)
+
+
diff --git a/Useful/for_lec19/db_details.py b/Useful/for_lec19/db_details.py
new file mode 100644
index 0000000..f0ddbc8
--- /dev/null
+++ b/Useful/for_lec19/db_details.py
@@ -0,0 +1,59 @@
+import sqlite3
+import os
+
+
+class DBClient:
+ def __init__(self, dbname):
+ self._conn = sqlite3.connect(dbname)
+ self._conn.row_factory = sqlite3.Row
+ print("Соединение открылось")
+
+ def configure_db(self):
+ cur = self._conn.cursor()
+
+ # Создаем таблицу Details
+ cur.execute("CREATE TABLE Details"
+ " (Id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ " Name CHAR(128) NOT NULL,"
+ " Price INTEGER NOT NULL)")
+ self._conn.commit()
+
+
+ def insert_detail(self, id, name, price):
+ cur = self._conn.cursor()
+ cur.execute("INSERT INTO Details (Id, Name, Price)"
+ " VALUES (:id, :name, :price)",
+ {'id': id, 'name': name, 'price': price})
+ self._conn.commit()
+
+
+ def select_detail(self, detail_id):
+ cur = self._conn.cursor()
+ cur.execute("SELECT Id, Name, Price FROM Details WHERE Id = :detail_id",
+ {'detail_id': detail_id})
+ return cur.fetchone()
+
+ def __del__(self):
+ self._conn.close()
+ print("Соединение закрылось")
+
+
+if __name__ == "__main__":
+ db_name = "details.db"
+ db_exists = os.path.exists(db_name)
+
+ db_client = DBClient(db_name)
+
+ if not db_exists:
+ db_client.configure_db()
+
+ db_client.insert_detail(1, "Гайка", 50)
+ db_client.insert_detail(2, "Винт", 60)
+
+ while i := input("Введите номер детали: "):
+ num = int(i)
+ res = db_client.select_detail(num)
+ if res:
+ print(dict(res))
+ else:
+ print(f"Нет детали с номером {i}")
diff --git a/Useful/for_lec19/details_client.py b/Useful/for_lec19/details_client.py
new file mode 100644
index 0000000..63f822c
--- /dev/null
+++ b/Useful/for_lec19/details_client.py
@@ -0,0 +1,30 @@
+import socket
+import pickle
+import random
+import time
+
+
+class TcpClient:
+ def __init__(self, host, port):
+ self.host = host
+ self.port = port
+ self._socket = None
+
+ def run(self):
+ self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self._socket.connect((self.host, self.port))
+ while i := input("Введите номер детали (или stop): "):
+ if i == "stop":
+ self._socket.send(pickle.dumps(i))
+ break
+ else:
+ num = int(i)
+ self._socket.send(pickle.dumps(num))
+ data = self._socket.recv(1024)
+ print(f'Received: {pickle.loads(data)}')
+ self._socket.close()
+
+
+if __name__ == '__main__':
+ myclient = TcpClient(host='127.0.0.1', port=5555)
+ myclient.run()
\ No newline at end of file
diff --git a/Useful/for_lec19/details_server.py b/Useful/for_lec19/details_server.py
new file mode 100644
index 0000000..1a749d7
--- /dev/null
+++ b/Useful/for_lec19/details_server.py
@@ -0,0 +1,65 @@
+import threading
+import socket
+import pickle
+import db_details
+
+
+DB_NAME = "details.db"
+
+
+class ClientThread(threading.Thread):
+ def __init__(self, conn, addr):
+ super().__init__()
+ self._connection = conn
+ self._address = addr
+
+ def run(self):
+ stop = False
+ db_client = db_details.DBClient(DB_NAME)
+ while not stop:
+ request = self._connection.recv(1024)
+ num = pickle.loads(request)
+ if num == "stop":
+ stop = True
+ else:
+ res = db_client.select_detail(num)
+ if res:
+ data = dict(res)
+ else:
+ data = f"Нет детали с номером {num}"
+ self._connection.send(pickle.dumps(data))
+ self._connection.close()
+
+
+class TcpServer:
+ def __init__(self, host, port):
+ self.host = host
+ self.port = port
+ self._socket = None
+ self._running = False
+
+ def run(self):
+ self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ self._socket.bind((self.host, self.port))
+ self._socket.listen(5)
+ self._running = True
+
+ print('Server is up')
+ while self._running:
+ conn, addr = self._socket.accept()
+ ClientThread(conn, addr).start()
+
+ def stop(self):
+ self._running = False
+ self._socket.close()
+ print('Server is down')
+
+
+if __name__ == '__main__':
+ srv = TcpServer(host='127.0.0.1', port=5555)
+ try:
+ srv.run()
+ except KeyboardInterrupt:
+ srv.stop()
+
diff --git a/Useful/for_lec19/example_db_api.py b/Useful/for_lec19/example_db_api.py
new file mode 100644
index 0000000..9f37a90
--- /dev/null
+++ b/Useful/for_lec19/example_db_api.py
@@ -0,0 +1,242 @@
+# Импортируем библиотеку, соответствующую типу нашей базы данных
+import sqlite3
+# Импортируем os для проверки существования файла базы данных
+import os
+
+
+def configure_db(conn):
+ cur = conn.cursor()
+
+ # Создаем таблицу Employees
+ cur.execute("CREATE TABLE Employees"
+ " (Id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ " Name CHAR(128) NOT NULL,"
+ " Position CHAR(64) NOT NULL,"
+ " Bonus INTEGER DEFAULT 0,"
+ " Login CHAR(16) NOT NULL,"
+ " Password CHAR(16) NOT NULL)")
+
+ # Создаем таблицу Projects
+ cur.execute("CREATE TABLE Projects"
+ " (Id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ " Name CHAR(128) NOT NULL)")
+
+ # Создаем таблицу PositionSalary
+ cur.execute("CREATE TABLE PositionSalary"
+ " (Position CHAR(64) PRIMARY KEY NOT NULL,"
+ " Salary INTEGER NOT NULL)")
+
+ # Создаем таблицу EmployeeProject
+ cur.execute("CREATE TABLE EmployeeProject"
+ " (EmployeeId INTEGER,"
+ " ProjectId INTEGER,"
+ " PRIMARY KEY (EmployeeId, ProjectId))")
+
+
+# Добавление записей в таблицу Проект
+def insert_project(conn, name):
+ # Создаем курсор - специальный объект,
+ # который делает запросы и получает их результаты
+ cur = conn.cursor()
+ # Делаем INSERT запрос к базе данных, используя обычный SQL-синтаксис
+ cur.execute("INSERT INTO Projects (Name)"
+ " VALUES (:name)",
+ {'name': name})
+ # Если мы не просто читаем, но и вносим изменения в базу данных
+ # - необходимо сохранить транзакцию
+ conn.commit()
+
+
+# Добавление записей в таблицу ДолжностьОклад
+def insert_position(conn, position, salary):
+ cur = conn.cursor()
+ cur.execute("INSERT INTO PositionSalary (Position, Salary)"
+ " VALUES (:position, :salary)",
+ {'position': position, 'salary': salary})
+ conn.commit()
+
+# Добавление записей в таблицу Сотрудник
+def insert_employee(conn, name, position, bonus, login, pwd):
+ cur = conn.cursor()
+ cur.execute("INSERT INTO Employees (Name, Position, Bonus, Login, Password)"
+ " VALUES (:name, :position, :bonus, :login, :pwd)",
+ {'name': name, 'position': position, 'bonus': bonus,
+ 'login': login, 'pwd': pwd})
+ conn.commit()
+
+# Добавление записей в таблицу СотрудникПроект
+def add_employee_to_project(conn, employee_id, project_id):
+ cur = conn.cursor()
+ cur.execute("INSERT INTO EmployeeProject (EmployeeId, ProjectId)"
+ " VALUES (:employeeId, :projectId)",
+ {'employeeId': employee_id, 'projectId': project_id})
+ conn.commit()
+
+# Проверка наличия пользователя в базе данных
+# с указанным логином/пролем
+def authentication(conn, login, pwd):
+ cur = conn.cursor()
+ # Делаем SELECT запрос к базе данных, используя обычный SQL-синтаксис
+ cur.execute("SELECT E.Id, E.Name, E.Position, EP.ProjectId"
+ " FROM Employees AS E, EmployeeProject AS EP"
+ " WHERE E.Id = EP.EmployeeId"
+ " AND E.Login = :login AND E.Password = :pwd",
+ {'login': login, 'pwd': pwd})
+ # Получаем результат сделанного запроса
+ return cur.fetchone()
+
+def authentication2(conn, login, pwd):
+ cur = conn.cursor()
+ cur.execute("SELECT E.Id, E.Name, E.Position, EP.ProjectId"
+ " FROM Employees AS E, EmployeeProject AS EP"
+ " WHERE E.Id = EP.EmployeeId"
+ " AND E.Login = ? AND E.Password = ?",
+ (login, pwd))
+ return cur.fetchone()
+
+
+def bad_authentication(conn, login, pwd):
+ cur = conn.cursor()
+ # Неправильно:
+ cur.execute("SELECT E.Id, E.Name, E.Position, EP.ProjectId"
+ " FROM Employees AS E, EmployeeProject AS EP"
+ " WHERE E.Id = EP.EmployeeId"
+ " AND E.Login = {login} AND E.Password = {pwd}".
+ format(login=login, pwd=pwd))
+ # Тоже неправильно:
+ # cur.execute(f"SELECT E.Id, E.Name, E.Position, EP.ProjectId"
+ # f" FROM Employees AS E, EmployeeProject AS EP"
+ # f" WHERE E.Id = EP.EmployeeId"
+ # f" AND E.Login = {login} AND E.Password = {pwd}".
+ # format(login=login, pwd=pwd))
+ return cur.fetchone()
+
+
+# Вывод информации для менеджера проекта
+# Соединяем таблицы Employees, PositionSalary, EmployeeProject
+def show_manager_info(conn, project_id):
+ cur = conn.cursor()
+ cur.execute("SELECT E.Id, E.Name, P.Salary + E.Bonus As Pay"
+ " FROM Employees AS E, PositionSalary AS P, "
+ " EmployeeProject AS EP"
+ " WHERE E.Position = P.Position"
+ " AND E.Id = EP.EmployeeId"
+ " AND EP.ProjectId = :project_id",
+ {'project_id': project_id})
+ print("Информация для менеджера:")
+ for row in cur.fetchall():
+ print(dict(row))
+
+# Вывод информации для сотрудника
+# Соединяем таблицы Employees, PositionSalary
+def show_employee_info(conn, employee_id):
+ cur = conn.cursor()
+ cur.execute("SELECT E.Id, E.Name, P.Salary + E.Bonus As Pay"
+ " FROM Employees AS E, PositionSalary AS P"
+ " WHERE E.Position = P.Position"
+ " AND E.Id = :employee_id",
+ {'employee_id': employee_id})
+ print("Информация для сотрудника:")
+ for row in cur.fetchall():
+ print(dict(row))
+
+# Проверка наличия указанного сотрудника в указанном проекте
+def is_employee_in_project(conn, employee_id, project_id):
+ cur = conn.cursor()
+ cur.execute("SELECT EP.ProjectId"
+ " FROM EmployeeProject AS EP"
+ " WHERE EP.EmployeeId = :employee_id"
+ " AND EP.ProjectId = :project_id",
+ {'employee_id': employee_id, 'project_id': project_id})
+ return bool(cur.fetchone())
+
+# Изменение премии сотрудника
+def update_employee_bonus(conn, employee_id, new_bonus):
+ cur = conn.cursor()
+ # Делаем UPDATE запрос к базе данных, используя обычный SQL-синтаксис
+ cur.execute("UPDATE Employees"
+ " SET Bonus = :new_bonus"
+ " WHERE Id = :employee_id",
+ {'employee_id': employee_id, 'new_bonus': new_bonus})
+ conn.commit()
+
+
+# Удаление сотрудника из проекта (но не из базы данных)
+def delete_employee_from_project(conn, employee_id, project_id):
+ cur = conn.cursor()
+ # Делаем DELETE запрос к базе данных, используя обычный SQL-синтаксис
+ cur.execute("DELETE FROM EmployeeProject"
+ " WHERE EmployeeId = :employee_id"
+ " AND ProjectId = :project_id",
+ {'employee_id': employee_id, 'project_id': project_id})
+ conn.commit()
+
+
+def drop_db(conn):
+ cur = conn.cursor()
+ cur.execute("DROP TABLE Employees")
+ cur.execute("DROP TABLE Projects")
+ cur.execute("DROP TABLE PositionSalary")
+ cur.execute("DROP TABLE EmployeeProject")
+
+
+if __name__ == "__main__":
+ db_name = "example.db"
+ db_exists = os.path.exists(db_name)
+
+ # Подключаемся к базе данных
+ # Если файл базы данных еще не создан, он создастся автоматически.
+ # Если указать :memory: вместо имени файла - база будет создана
+ # в оперативной памяти без сохранения в файле.
+ conn = sqlite3.connect(db_name)
+ conn.row_factory = sqlite3.Row
+ if not db_exists:
+ configure_db(conn)
+
+ insert_project(conn, "Важный")
+ insert_project(conn, "Срочный")
+ insert_position(conn, "инженер", 50000)
+ insert_position(conn, "старший инженер", 51000)
+ insert_position(conn, "менеджер проекта", 100000)
+ insert_employee(conn, "Иванов И.И.", "инженер", 30000,
+ "ivanovi", "ivanov123")
+ insert_employee(conn, "Петров П.П.", "старший инженер", 50000,
+ "petrovp", "p1e2t3")
+ insert_employee(conn, "Сидоров С.С.", "менеджер проекта", 30000,
+ "sidorovs", "zayka88")
+ add_employee_to_project(conn, 1, 1)
+ add_employee_to_project(conn, 2, 1)
+ add_employee_to_project(conn, 2, 2)
+ add_employee_to_project(conn, 3, 2)
+
+ login = input("Логин: ")
+ pwd = input("Пароль: ")
+ res = authentication(conn, login, pwd)
+ if res:
+ user = dict(res)
+ print("Здравствуйте, {}".format(user['Name']))
+ if user['Position'] == "менеджер проекта":
+ show_manager_info(conn, user['ProjectId'])
+
+ id_upd = int(input("Изменение премии. ID сотрудника (0 - отмена): "))
+ if id_upd:
+ if (id_upd != user['Id'] and
+ is_employee_in_project(conn, id_upd, user['ProjectId'])):
+ new_bonus = input("Новая премия: ")
+ update_employee_bonus(conn, id_upd, new_bonus)
+ else:
+ print("Невозможно изменить премию для данного сотрудника")
+
+ id_del = int(input("Удаление сотрудника. ID сотрудника (0 - отмена): "))
+ if id_del:
+ if id_del != user['Id']:
+ delete_employee_from_project(conn, id_del, user['ProjectId'])
+ else:
+ print("Невозможно удалить данного сотрудника из проекта")
+ else:
+ show_employee_info(conn, user['Id'])
+ else:
+ print("Доступ запрещен")
+
+ # Не забываем закрыть соединение с базой данных после работы
+ conn.close()
diff --git a/Useful/for_lec19/example_sqlalchemy.py b/Useful/for_lec19/example_sqlalchemy.py
new file mode 100644
index 0000000..d6466df
--- /dev/null
+++ b/Useful/for_lec19/example_sqlalchemy.py
@@ -0,0 +1,274 @@
+import os
+from sqlalchemy import create_engine
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy import Column, Integer, String, ForeignKey
+from sqlalchemy.orm import sessionmaker
+from sqlalchemy import and_
+from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
+from sqlalchemy.orm.session import close_all_sessions
+
+Base = declarative_base()
+
+# Создаем класс, отображающий таблицу
+class Employee(Base):
+ __tablename__ = 'employee'
+ id = Column(Integer, primary_key=True)
+ name = Column(String, nullable=False)
+ position = Column(String, ForeignKey('position_salary.position'),
+ nullable=False)
+ bonus = Column(Integer, default=0)
+ login = Column(String, nullable=False, unique=True)
+ password = Column(String, nullable=False)
+ def __repr__(self):
+ return "".\
+ format(id=self.id, name=self.name,
+ pos=self.position, bon=self.bonus,
+ log=self.login, pwd=self.password)
+
+class Project(Base):
+ __tablename__ = 'project'
+ id = Column(Integer, primary_key=True)
+ name = Column(String, nullable=False)
+ def __repr__(self):
+ return "".format(id=self.id,
+ name=self.name)
+
+class PositionSalary(Base):
+ __tablename__ = 'position_salary'
+ position = Column(String, primary_key=True)
+ salary = Column(Integer, nullable=False)
+ def __repr__(self):
+ return "".format(
+ pos=self.position, sal=self.salary)
+
+class EmployeeProject(Base):
+ __tablename__ = 'employee_project'
+ employee_id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
+ project_id = Column(Integer, ForeignKey('project.id'), primary_key=True)
+ def __repr__(self):
+ return "".\
+ format(e_id=self.employee_id, p_id=self.project_id)
+
+
+class DBClient:
+
+ def __init__(self, dbtype='sqlite', dbname='/example.db',
+ username=None, password=None):
+ self._engine = self._get_engine(dbtype, dbname, username, password)
+
+ def __enter__(self):
+ self._session = sessionmaker(bind=self._engine)()
+ return self
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ close_all_sessions()
+
+ @staticmethod
+ def _get_engine(dbtype, dbname, username=None, password=None):
+ if username:
+ if password:
+ login = '{u}:{p}'.format(u=username, p=password)
+ else:
+ login = username
+ dbstr = '{l}@{db}'.format(l=login, db=dbname)
+ else:
+ dbstr = dbname
+
+ # Создаем базу данных
+ # Либо без файла (в оперативной памяти)
+ # Либо с файлом, указывая относительный путь к нему: engine = create_engine('sqlite:///mybase.db')
+ # Либо абсолютный путь: engine = create_engine(r'sqlite:///C:\Users\User\Desktop\mybase.db')
+ engine = create_engine('{}:///{}'.format(dbtype, dbstr))
+ return engine
+
+ def create_schema(self):
+ # Создаем схему
+ Base.metadata.create_all(self._engine)
+
+ def delete_schema(self):
+ # Удаляем схему
+ Base.metadata.drop_all(self._engine)
+
+ def insert_position(self, position, salary):
+ self._session.add(PositionSalary(position=position, salary=salary))
+ self._session.commit()
+
+ def insert_project(self, name):
+ p = Project(name=name)
+ self._session.add(p)
+ self._session.commit()
+ return p.id
+
+ def insert_employee(self, name, position, bonus, login, password):
+ e = Employee(name=name, position=position, bonus=bonus,
+ login=login, password=password)
+ self._session.add(e)
+ self._session.commit()
+ return e.id
+
+ def add_employee_to_project(self, employee_id, project_id):
+ self._session.add(EmployeeProject(employee_id=employee_id,
+ project_id=project_id))
+ self._session.commit()
+
+
+ def add_new_employee_to_project(self, name, position, bonus, login, password, project_id):
+ e = Employee(name=name, position=position, bonus=bonus,
+ login=login, password=password)
+ self._session.add(e)
+ self._session.commit()
+ self._session.add(EmployeeProject(employee_id=e.id,
+ project_id=project_id))
+ self._session.commit()
+
+
+ # Проверка наличия пользователя в базе данных
+ # с указанным логином/паролем
+ def authentication(self, login, password):
+ try:
+ res = self._session.query(Employee.id, Employee.name,
+ Employee.position, EmployeeProject.project_id).\
+ join(EmployeeProject, EmployeeProject.employee_id == Employee.id). \
+ filter(and_(Employee.login == login,
+ Employee.password == password)).\
+ one()
+ return res
+ except MultipleResultsFound:
+ print("Multiple Results Found")
+ except NoResultFound:
+ print("No Result Found")
+ return None
+
+ # Проверка наличия указанного сотрудника в указанном проекте
+ def is_employee_in_project(self, employee_id, project_id):
+ try:
+ res = self._session.query(EmployeeProject.project_id).\
+ filter(and_(EmployeeProject.employee_id == employee_id,
+ EmployeeProject.project_id == project_id)).\
+ one()
+ return True
+ except MultipleResultsFound:
+ print("Multiple Results Found")
+ except NoResultFound:
+ print("No Result Found")
+ return None
+
+
+ # Вывод информации для сотрудника
+ # Соединяем таблицы Employees, PositionSalary
+ def show_employee_info(self, employee_id):
+ res = self._session.query(Employee.id, Employee.name,
+ (PositionSalary.salary +
+ Employee.bonus).label("Pay")).\
+ filter(and_(Employee.position == PositionSalary.position,
+ Employee.id == employee_id)).\
+ all()
+ print("Информация для сотрудника:")
+ for row in res:
+ print(row)
+ return res
+
+ # Вывод информации для менеджера проекта
+ # Соединяем таблицы Employees, PositionSalary, EmployeeProject
+ def show_manager_info(self, project_id):
+ res = self._session.query(Employee.id, Employee.name,
+ (PositionSalary.salary +
+ Employee.bonus).label("Pay")). \
+ filter(and_(Employee.position == PositionSalary.position,
+ Employee.id == EmployeeProject.employee_id,
+ EmployeeProject.project_id == project_id)). \
+ all()
+ print("Информация для менеджера:")
+ for row in res:
+ print(row)
+ return res
+
+ # Изменение премии сотрудника
+ def update_employee_bonus(self, employee_id, new_bonus):
+ e = self._session.query(Employee).get(employee_id)
+ if e:
+ e.bonus = new_bonus
+ self._session.add(e)
+ self._session.commit()
+
+ # Удаление сотрудника из проекта (но не из базы данных)
+ def delete_employee_from_project(self, employee_id, project_id):
+ ep = self._session.query(EmployeeProject).get((employee_id, project_id))
+ if ep:
+ self._session.delete(ep)
+ self._session.commit()
+
+
+ def show_all(self):
+ print(self._session.query(PositionSalary).all())
+ print(self._session.query(Project).all())
+ print(self._session.query(Employee).all())
+ print(self._session.query(EmployeeProject).all())
+
+
+if __name__ == "__main__":
+ db_type = "sqlite"
+ db_name = "example.db"
+ db_exists = os.path.exists(db_name)
+
+ if not db_exists:
+ with DBClient(db_type, db_name) as dbc:
+ dbc.create_schema()
+
+ dbc.insert_position("инженер", 50000)
+ dbc.insert_position("старший инженер", 51000)
+ dbc.insert_position("менеджер проекта", 100000)
+
+ pid = dbc.insert_project("Важный")
+ eid = dbc.insert_employee("Иванов И.И.", "инженер", 30000,
+ "ivanovi", "ivanov123")
+ dbc.add_employee_to_project(eid, pid)
+
+ eid = dbc.insert_employee("Петров П.П.", "старший инженер", 50000,
+ "petrovp", "p1e2t3")
+ dbc.add_employee_to_project(eid, pid)
+
+ pid = dbc.insert_project("Срочный")
+ dbc.add_employee_to_project(eid, pid)
+
+ eid = dbc.insert_employee("Сидоров С.С.", "менеджер проекта", 30000,
+ "sidorovs", "zayka88")
+ dbc.add_employee_to_project(eid, pid)
+
+
+ login = input("Логин: ")
+ pwd = input("Пароль: ")
+ with DBClient(db_type, db_name) as dbc:
+ #dbc.show_all()
+ res = dbc.authentication(login, pwd)
+ if res:
+ user = res._asdict()
+ print("Здравствуйте, {}".format(user['name']))
+ if user['position'] == "менеджер проекта":
+ dbc.show_manager_info(user['project_id'])
+
+ id_upd = int(input("Изменение премии. ID сотрудника (0 - отмена): "))
+ if id_upd:
+ if (id_upd != user['id'] and
+ dbc.is_employee_in_project(id_upd, user['project_id'])):
+ new_bonus = input("Новая премия: ")
+ dbc.update_employee_bonus(id_upd, new_bonus)
+ print("Результат:")
+ dbc.show_manager_info(user['project_id'])
+ else:
+ print("Невозможно изменить премию для данного сотрудника")
+
+ id_del = int(input("Удаление сотрудника. ID сотрудника (0 - отмена): "))
+ if id_del:
+ if id_del != user['id']:
+ dbc.delete_employee_from_project(id_del, user['project_id'])
+ print("Результат:")
+ dbc.show_manager_info(user['project_id'])
+ else:
+ print("Невозможно удалить данного сотрудника из проекта")
+ else:
+ dbc.show_employee_info(user['id'])
+ else:
+ print("Доступ запрещен")
diff --git a/Useful/for_lec19/finance_db.py b/Useful/for_lec19/finance_db.py
new file mode 100644
index 0000000..8492d51
--- /dev/null
+++ b/Useful/for_lec19/finance_db.py
@@ -0,0 +1,61 @@
+import sqlite3
+
+
+# Подключаемся к базе данных
+# Файл базы данных (если он еще не создан будет создан автоматически
+conn = sqlite3.connect(':memory:')
+cur = conn.cursor()
+print('База данных открыта')
+
+# Создаем таблицу
+cur.execute('CREATE TABLE USERS'
+ ' (ID INTEGER PRIMARY KEY AUTOINCREMENT,'
+ ' LOGIN CHAR(50) NOT NULL,'
+ ' PASSWORD CHAR(50) NOT NULL,'
+ ' SALARY REAL);')
+print('Таблица создана')
+
+# Добавляем данные
+cur.execute("INSERT INTO USERS (LOGIN, PASSWORD, SALARY)"
+ "VALUES ('Ivan', 'coolpwd', 100000)")
+cur.execute("INSERT INTO USERS (LOGIN, PASSWORD, SALARY)"
+ "VALUES ('Petr', 'iampetr', 50000)")
+cur.execute("INSERT INTO USERS (LOGIN, PASSWORD, SALARY)"
+ "VALUES ('Simon', '123', 30000)")
+conn.commit()
+print('Данные добавлены')
+
+# Читаем данные
+cur.execute('SELECT id, login, password from USERS')
+for row in cur.fetchall():
+ print(row)
+
+login = input('Input login: ')
+password = input('Input password: ')
+
+# req = "SELECT login, salary from USERS WHERE login='{}' AND password='{}'".format(login, password)
+# print('This is request: {}'.format(req))
+# cur.execute(req)
+# print('Output data:')
+# rows = cur.fetchall()
+# if not len(rows):
+# print('incorrect login/password')
+# else:
+# for row in rows:
+# print(row)
+
+cursor = conn.execute("SELECT login, salary from USERS WHERE login=? AND password=?",
+ (login, password))
+print('Output data 2:')
+for row in cursor:
+ print(row)
+
+conn.execute('DROP TABLE USERS')
+print('Таблица удалена')
+
+conn.close()
+
+
+
+
+
diff --git a/Useful/for_lec19/test_db_client.py b/Useful/for_lec19/test_db_client.py
new file mode 100644
index 0000000..296661f
--- /dev/null
+++ b/Useful/for_lec19/test_db_client.py
@@ -0,0 +1,30 @@
+from unittest import mock
+import db_details
+
+
+class TestDBClient:
+ # рабочий вариант теста с патчингом БД для тестирования изолированного конструктора
+ def test_dbclient_init(self):
+ with mock.patch('db_details.sqlite3') as sqlite_mock:
+ connection_mock = mock.Mock()
+ sqlite_mock.connect.return_value = connection_mock
+ sqlite_mock.Row = "fake_row"
+ dbname = "fake_db"
+ db_client = db_details.DBClient(dbname)
+ sqlite_mock.connect.assert_called_once_with(dbname)
+ assert db_client._conn.row_factory == sqlite_mock.Row
+
+ # рабочий вариант теста DBClient в интеграции с БД
+ def test_data_add(self):
+ db_client = db_details.DBClient(":memory:")
+ db_client.configure_db()
+ id, name, price = 1, "Гайка", 50
+ db_client.insert_detail(id, name, price)
+ res = db_client.select_detail(id)
+ assert res
+ res_as_dict = dict(res)
+ assert res_as_dict['Id'] == id
+ assert res_as_dict['Name'] == name
+ assert res_as_dict['Price'] == price
+
+
diff --git a/Useful/for_lec20/check_redis.py b/Useful/for_lec20/check_redis.py
new file mode 100644
index 0000000..4b24d48
--- /dev/null
+++ b/Useful/for_lec20/check_redis.py
@@ -0,0 +1,67 @@
+import time
+import redis
+
+# Создаем подключение
+r = redis.Redis(host='localhost', port=6379, db=0)
+
+# Добавляем элемент - пару ключ-значение
+r.set('name', 'John')
+# Получаем значение по ключу
+print("r.get('name'): ", r.get('name'))
+# Определяем тип значения по ключу
+print("r.type('name'): ", r.type('name'))
+# Удаляем элемент по ключу
+r.delete('name')
+print("r.get('name'): ", r.get('name'))
+
+# set всегда добавляет значение строкового типа
+r.set('my_int', 2)
+print("r.get('my_int'): ", r.get('my_int'))
+print("r.type('my_int'): ", r.type('my_int'))
+# Инкрементируем значение по ключу, которое для данной
+# операции интерпретируется как 64-битное знаковое целое
+print("r.incr('my_int'): ", r.incr('my_int'))
+# Проверяем существование значения по ключу
+print("r.exists('my_int'): ", r.exists('my_int'))
+r.set('temp_value', 'value')
+# Задаем время жизни ключа (в секундах), по истечении
+# которого он будет автоматически удален с сервера
+r.expire('temp_value', 5)
+# Узнаем оставшееся время жизни ключа
+print("r.ttl('temp_value'): ", r.ttl('temp_value'))
+print("r.get('temp_value'): ", r.get('temp_value'))
+
+time.sleep(6)
+print("After 6 seconds...")
+
+print("r.ttl('temp_value'): ", r.ttl('temp_value'))
+print("r.get('temp_value'): ", r.get('temp_value'))
+print("r.exists('temp_value'): ", r.exists('temp_value'))
+
+# hashset - хэшсет
+# Добавляем пару ключ-значение в хэшсет user:1
+r.hset('user:1', 'name', 'John')
+# Добавляем еще одну пару ключ-значение в хэшсет user:1
+r.hset('user:1', 'email', 'john@gmail.com')
+print("r.hget('user:1', 'name'): ", r.hget('user:1', 'name'))
+print("r.hkeys('user:1'): ", r.hkeys('user:1'))
+print("r.hgetall('user:1'): ", r.hgetall('user:1'))
+# list - список # Добавляем элементы в список
+r.rpush('my_list', 'elem1')
+r.rpush('my_list', 'elem2')
+r.rpush('my_list', 'elem3')
+r.rpush('my_list', 'elem4')
+print("r.llen('my_list'): ", r.llen('my_list'))
+print("r.lindex('my_list', 0): ", r.lindex('my_list', 0))
+print("r.lrange('my_list', 1, 3): ", r.lrange('my_list', 1, 3))
+
+# Реализация паттерна издатель-подписчик
+p = r.pubsub(ignore_subscribe_messages=True)
+p.subscribe('my-chat')
+print("p.get_message(): ", p.get_message())
+r.publish('my-chat', 'user: Hello!')
+while True:
+ msg = p.get_message()
+ if msg:
+ print("p.get_message(): ", msg)
+ break
diff --git a/Useful/for_lec20/cqlsh_examples.txt b/Useful/for_lec20/cqlsh_examples.txt
new file mode 100644
index 0000000..d2604f0
--- /dev/null
+++ b/Useful/for_lec20/cqlsh_examples.txt
@@ -0,0 +1,61 @@
+cqlsh> use example;
+cqlsh:example> INSERT INTO users (username, fullname, regdate, status) VALUES ("ivanovi", "Иван Иванов", 01.01.2020, "Available");
+SyntaxException: line 1:73 no viable alternative at input ','
+cqlsh:example> INSERT INTO users (username, fullname, regdate, status) VALUES ("ivanovi", "Иван Иванов", "2020-01-01", "Available");
+SyntaxException: line 1:73 no viable alternative at input ','
+cqlsh:example> INSERT INTO users (username, fullname, regdate, status) VALUES ('ivanovi', 'Иван Иванов', '2020-01-01', 'Available');
+InvalidRequest: Error from server: code=2200 [Invalid query] message="Missing mandatory PRIMARY KEY part id"
+cqlsh:example> INSERT INTO users (id, username, fullname, regdate, status) VALUES (1, 'ivanovi', 'Иван Иванов', '2020-01-01', 'Available');
+cqlsh:example> INSERT INTO users (id, username, fullname, regdate, status) VALUES (2, 'petrovp', 'Петр Петров', '2020-02-02', 'Busy');
+cqlsh:example> SELECT username FROM users WHERE id = 1;
+
+ username
+----------
+ ivanovi
+
+(1 rows)
+cqlsh:example> SELECT * FROM users WHERE id = 1;
+
+ id | fullname | regdate | status | username
+----+-------------+------------+-----------+----------
+ 1 | Иван Иванов | 2020-01-01 | Available | ivanovi
+
+(1 rows)
+cqlsh:example> UPDATE users SET status = 'Busy' WHERE id = 1;
+cqlsh:example> SELECT * FROM users WHERE id = 1;
+
+ id | fullname | regdate | status | username
+----+-------------+------------+--------+----------
+ 1 | Иван Иванов | 2020-01-01 | Busy | ivanovi
+
+(1 rows)
+cqlsh:example> INSERT INTO users (id, username, fullname, regdate, status) VALUES (3, "orlovi", 'Илья Орлов', '2020-03-03', 'Available');
+SyntaxException: line 1:79 no viable alternative at input ','
+cqlsh:example> INSERT INTO users (id, username, fullname, regdate, status) VALUES (3, 'orlovi', 'Илья Орлов', '2020-03-03', 'Available');
+cqlsh:example> SELECT * FROM users WHERE id = 3;
+
+ id | fullname | regdate | status | username
+----+------------+------------+-----------+----------
+ 3 | Илья Орлов | 2020-03-03 | Available | orlovi
+
+(1 rows)
+cqlsh:example> SELECT * FROM users;
+
+ id | fullname | regdate | status | username
+----+-------------+------------+-----------+----------
+ 1 | Иван Иванов | 2020-01-01 | Busy | ivanovi
+ 2 | Петр Петров | 2020-02-02 | Busy | petrovp
+ 3 | Илья Орлов | 2020-03-03 | Available | orlovi
+
+(3 rows)
+cqlsh:example> DELETE FROM users WHERE id = 3;
+cqlsh:example> SELECT * FROM users;
+
+ id | fullname | regdate | status | username
+----+-------------+------------+--------+----------
+ 1 | Иван Иванов | 2020-01-01 | Busy | ivanovi
+ 2 | Петр Петров | 2020-02-02 | Busy | petrovp
+
+(2 rows)
+cqlsh:example>
+
diff --git a/Useful/for_lec20/mongodb_test.py b/Useful/for_lec20/mongodb_test.py
new file mode 100644
index 0000000..ec59fb1
--- /dev/null
+++ b/Useful/for_lec20/mongodb_test.py
@@ -0,0 +1,44 @@
+# Подключаемся к базе MongoDB на локальной машине
+import mongoengine as me
+
+
+# Объявляем коллекцию
+class User(me.Document):
+ email = me.StringField(required=True)
+ first_name = me.StringField(max_length=50)
+ last_name = me.StringField(max_length=50)
+
+ def __repr__(self):
+ return ("".format(self.first_name,
+ self.last_name,
+ self.email))
+
+
+conn = me.connect('test')
+print(conn)
+
+# Создаем документ
+ross = User(email='ross@example.com',
+ first_name='Ross',
+ last_name='Lawley')
+ross.save()
+print('Документов в базе: {}'.format(User.objects.count()))
+
+# Делаем запрос
+for i in range(5):
+ User(email='test@example.com',
+ first_name='User'+str(i),
+ last_name='Test').save()
+print(User.objects.filter(first_name='User3'))
+# Двойное нижнее подчеркивание используется для
+# задания регулярного выражения
+print(User.objects.filter(first_name__startswith='User'))
+
+# Удаляем запись в базе данных
+User.objects(first_name__startswith='User').delete()
+print('Документов в базе: {}'.format(User.objects.count()))
+
+# Удаляем все записи в базе данных
+User.objects.delete()
\ No newline at end of file
diff --git a/Useful/for_lec20/publisher.py b/Useful/for_lec20/publisher.py
new file mode 100644
index 0000000..cab44fd
--- /dev/null
+++ b/Useful/for_lec20/publisher.py
@@ -0,0 +1,4 @@
+import redis
+
+r = redis.Redis(host='localhost', port=6379, db=0)
+r.publish('my-chat', 'user: Hello!')
diff --git a/Useful/for_lec20/scylla_example.py b/Useful/for_lec20/scylla_example.py
new file mode 100644
index 0000000..d73608b
--- /dev/null
+++ b/Useful/for_lec20/scylla_example.py
@@ -0,0 +1,44 @@
+from cassandra.cluster import Cluster
+
+
+def insert_users(id, username, fullname, regdate, status):
+ session.execute(
+ "INSERT INTO users (id, username, fullname, regdate, status) VALUES (%s, %s, %s, %s, %s)",
+ (id, username, fullname, regdate, status)
+ )
+
+def select_users(id=None):
+ if id is None:
+ res = session.execute("SELECT * FROM users")
+ else:
+ res = session.execute("SELECT * FROM users WHERE id = %s", (id,))
+ if res:
+ res = [item for item in res]
+ return res
+
+def update_user_status(new_status, id=None):
+ if id is None:
+ session.execute("UPDATE users SET status = %s", (new_status))
+ else:
+ session.execute("UPDATE users SET status = %s WHERE id = %s", (new_status, id))
+
+def delete_users(id=None):
+ if id is None:
+ res = session.execute("DELETE FROM users")
+ else:
+ res = session.execute("DELETE FROM users WHERE id = %s", (id,))
+
+
+cluster = Cluster(['127.0.0.1'])
+session = cluster.connect('example')
+
+insert_users(1, 'ivanovi', 'Иван Иванов', '2020-01-01', 'Available')
+insert_users(2, 'petrovp', 'Петр Петров', '2020-02-02', 'Busy')
+insert_users(3, 'orlovi', 'Илья Орлов', '2020-03-03', 'Available')
+
+print(select_users())
+print(select_users(1))
+update_user_status('Busy', 1)
+print(select_users(1))
+delete_users(3)
+print(select_users())
diff --git a/Useful/for_lec20/subscriber.py b/Useful/for_lec20/subscriber.py
new file mode 100644
index 0000000..fc08ced
--- /dev/null
+++ b/Useful/for_lec20/subscriber.py
@@ -0,0 +1,10 @@
+import redis
+
+r = redis.Redis(host='localhost', port=6379, db=0)
+p = r.pubsub(ignore_subscribe_messages=True)
+p.subscribe('my-chat')
+while True:
+ msg = p.get_message()
+ if msg:
+ print("p.get_message(): ", msg)
+ break
diff --git a/Useful/for_lec21/check_myfile.py b/Useful/for_lec21/check_myfile.py
new file mode 100644
index 0000000..16bf191
--- /dev/null
+++ b/Useful/for_lec21/check_myfile.py
@@ -0,0 +1,16 @@
+from unittest import mock
+import myfile
+
+
+# def mock_print(arg):
+# mock_print.arg = arg
+#
+#
+# myfile.print = mock_print
+# myfile.mysum(10, 20)
+# assert mock_print.arg == 30
+
+
+myfile.print = mock.Mock()
+myfile.mysum(10, 20)
+myfile.print.assert_called_once_with(30)
diff --git a/Useful/for_lec21/cmd_manager/audit_manager.py b/Useful/for_lec21/cmd_manager/audit_manager.py
new file mode 100644
index 0000000..c0fc488
--- /dev/null
+++ b/Useful/for_lec21/cmd_manager/audit_manager.py
@@ -0,0 +1,7 @@
+class AuditManager:
+ def add_call(self, *args, **kwargs):
+ pass
+ def add_result(self, *args, **kwargs):
+ pass
+
+audit_service = AuditManager()
diff --git a/Useful/for_lec21/cmd_manager/cmd_manager.py b/Useful/for_lec21/cmd_manager/cmd_manager.py
new file mode 100644
index 0000000..d03eaa4
--- /dev/null
+++ b/Useful/for_lec21/cmd_manager/cmd_manager.py
@@ -0,0 +1,19 @@
+class AuditManager:
+ def add_call(self, *args, **kwargs):
+ pass
+ def add_result(self, *args, **kwargs):
+ pass
+
+
+audit_service = AuditManager()
+
+
+class CmdManager:
+ def set_service(self, service):
+ self.service = service
+ self.service.start()
+
+ def run_command(self, cmd):
+ audit_service.add_call(self.service.name, 'run', cmd)
+ result = self.service.run(cmd)
+ audit_service.add_result(self.service.name, 'run', result)
diff --git a/Useful/for_lec21/cmd_manager/test_cmd_manager.py b/Useful/for_lec21/cmd_manager/test_cmd_manager.py
new file mode 100644
index 0000000..c0e760c
--- /dev/null
+++ b/Useful/for_lec21/cmd_manager/test_cmd_manager.py
@@ -0,0 +1,40 @@
+import cmd_manager
+import unittest
+import unittest.mock as mock
+
+
+class TestCmdManager(unittest.TestCase):
+
+ def test_cmd_manager(self):
+ cmd_mgr = cmd_manager.CmdManager()
+
+ # Мы тестируем класс CmdManager, объект audit_service нас интересует
+ # постольку, поскольку к нему идут обращения от методов CmdManager.
+ # Эти обращения можно рассматривать как выходные данные тестируемого
+ # класса CmdManager.
+ with mock.patch('cmd_manager.audit_service') as audit_service_mock:
+ # Метод set_service объекта класса CmdManager принимает
+ # в качестве параметра объект service, о котором мы знаем только
+ # то, что у него есть атрибут name и метод run, который должен
+ # что-то возвращать. Подставим вместо service mock-объект.
+ service = mock.Mock()
+ service.name = 'MyService'
+ RESULT = 0
+ service.run.return_value = RESULT
+
+ cmd_mgr.set_service(service)
+ self.assertTrue(service.start.called)
+
+ CMD = 'status'
+ cmd_mgr.run_command(CMD)
+ # Используем assert-методы mock-объекта, которым мы подменили
+ # audit_service
+ audit_service_mock.add_call.assert_called_once_with(
+ 'MyService', 'run', CMD)
+ service.run.assert_called_once_with(CMD)
+ audit_service_mock.add_result.assert_called_once_with(
+ 'MyService', 'run', RESULT)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/Useful/for_lec21/cmd_manager/test_cmd_manager_2.py b/Useful/for_lec21/cmd_manager/test_cmd_manager_2.py
new file mode 100644
index 0000000..9e1dfa5
--- /dev/null
+++ b/Useful/for_lec21/cmd_manager/test_cmd_manager_2.py
@@ -0,0 +1,27 @@
+import cmd_manager
+import unittest
+import unittest.mock as mock
+
+
+class TestCmdManager(unittest.TestCase):
+
+ @mock.patch('cmd_manager.audit_service')
+ def test_cmd_manager(self, audit_service_mock):
+ cmd_mgr = cmd_manager.CmdManager()
+ service = mock.Mock()
+ service.name = 'MyService'
+ service.run.return_value = 0
+ cmd_mgr.set_service(service)
+ self.assertTrue(service.start.called)
+ cmd_mgr.run_command('status')
+ # Используем assert-методы mock-объекта, которым мы подменили
+ # audit_service
+ audit_service_mock.add_call.assert_called_once_with('MyService',
+ 'run', 'status')
+ service.run.assert_called_once_with('status')
+ audit_service_mock.add_result.assert_called_once_with('MyService',
+ 'run', 0)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/Useful/for_lec21/example/.coverage b/Useful/for_lec21/example/.coverage
new file mode 100644
index 0000000..ab6f96d
Binary files /dev/null and b/Useful/for_lec21/example/.coverage differ
diff --git a/Useful/for_lec21/example/my_ops.py b/Useful/for_lec21/example/my_ops.py
new file mode 100644
index 0000000..f77a26d
--- /dev/null
+++ b/Useful/for_lec21/example/my_ops.py
@@ -0,0 +1,6 @@
+def my_sum(a, b):
+ return a + b
+
+
+def my_dif(a, b):
+ return a - b
\ No newline at end of file
diff --git a/Useful/for_lec21/example/myfile.py b/Useful/for_lec21/example/myfile.py
new file mode 100644
index 0000000..23facc6
--- /dev/null
+++ b/Useful/for_lec21/example/myfile.py
@@ -0,0 +1,2 @@
+def fun(a, b):
+ return a + b
\ No newline at end of file
diff --git a/Useful/for_lec21/example/tasks.py b/Useful/for_lec21/example/tasks.py
new file mode 100644
index 0000000..6798371
--- /dev/null
+++ b/Useful/for_lec21/example/tasks.py
@@ -0,0 +1,23 @@
+def mylen(list_arg):
+ res = 0
+ for _ in list_arg:
+ res += 1
+ return res
+
+
+def myrange(start=0, stop=None, step=1):
+ if not stop:
+ stop = start
+ start = 0
+ if step == 0:
+ f = lambda a, b: False
+ elif step > 0:
+ f = lambda a, b: a < b
+ else:
+ f = lambda a, b: a > b
+ i = start
+ l = []
+ while f(i, stop):
+ l.append(i)
+ i += step
+ return l
diff --git a/Useful/for_lec21/example/test_my_ops.py b/Useful/for_lec21/example/test_my_ops.py
new file mode 100644
index 0000000..d590493
--- /dev/null
+++ b/Useful/for_lec21/example/test_my_ops.py
@@ -0,0 +1,33 @@
+import pytest
+import my_ops
+
+
+class TestMySum:
+ @pytest.fixture(
+ scope='function',
+ params=[(10, 20, 30), (-10, -20, -30), (10, 0, 10), (10, -10, 0)],
+ ids=lambda args: f"Test fun with args: {args}"
+ )
+ def parametrize_sum(self, request):
+ return request.param
+
+ def test_my_sum(self, parametrize_sum):
+ arg1, arg2, expected = parametrize_sum
+ res = my_ops.my_sum(arg1, arg2)
+ assert(res, expected)
+
+
+class TestMyDif:
+ @pytest.fixture(
+ scope='function',
+ params=[(10, 20, -20), (-10, -20, 10), (10, 0, 10), (10, -10, 20)],
+ ids=lambda args: f"Test fun with args: {args}"
+ )
+ def parametrize_dif(self, request):
+ return request.param
+
+
+ def test_my_dif(self, parametrize_dif):
+ arg1, arg2, expected = parametrize_dif
+ res = my_ops.my_dif(arg1, arg2)
+ assert(res, expected)
diff --git a/Useful/for_lec21/example/test_myfile.py b/Useful/for_lec21/example/test_myfile.py
new file mode 100644
index 0000000..9b41b66
--- /dev/null
+++ b/Useful/for_lec21/example/test_myfile.py
@@ -0,0 +1,25 @@
+import pytest
+from myfile import fun
+
+
+class TestMyFile:
+ @pytest.fixture(
+ scope='function',
+ params=[(10, 20, 30), (-10, -20, -30), (10, 0, 10), (10, -10, 0)],
+ ids=lambda args: f"Test fun with args: {args}"
+ )
+ def parametrizer(self, request):
+ return request.param
+
+ def test_fun(self, parametrizer):
+ arg1, arg2, res = parametrizer
+ assert(fun(arg1, arg2) == res)
+
+ # def test_fun_1(self):
+ # assert(fun(10, 20) == 30)
+ #
+ # def test_fun_2(self):
+ # assert(fun(-10, -20) == -30)
+ #
+ # def test_fun_3(self):
+ # assert(fun(10, 0) == 10)
diff --git a/Useful/for_lec21/example/test_myrange.py b/Useful/for_lec21/example/test_myrange.py
new file mode 100644
index 0000000..05f5a10
--- /dev/null
+++ b/Useful/for_lec21/example/test_myrange.py
@@ -0,0 +1,20 @@
+import pytest
+from tasks import myrange
+
+
+class TestMyrangeSuite:
+ @pytest.fixture(
+ scope='function',
+ params=[(0, 5), (10, 20), (10, 20, 3), (20, 10, -2), (20, 10, 0)],
+ ids=lambda args: f"Test with args: {args}"
+ )
+ def parametrize_myrange(self, request):
+ return request.param
+
+ def test_myrange(self, parametrize_myrange):
+ range_args = parametrize_myrange
+ assert myrange(*range_args) == list(range(*range_args))
+
+
+if __name__ == '__main__':
+ pytest.main()
diff --git a/Useful/for_lec21/example/test_tasks.py b/Useful/for_lec21/example/test_tasks.py
new file mode 100644
index 0000000..cbff4a2
--- /dev/null
+++ b/Useful/for_lec21/example/test_tasks.py
@@ -0,0 +1,19 @@
+import pytest
+from tasks import mylen, myrange
+
+
+class TestTasksSuite:
+ def test_mylen(self):
+ l = [1, 45, 32, 9]
+ assert(mylen(l) == 4)
+
+ def test_myrange(self):
+ assert(myrange(5) == list(range(5)))
+ assert(myrange(10, 20) == list(range(10, 20)))
+ assert(myrange(10, 20, 3) == list(range(10, 20, 3)))
+ assert(myrange(20, 10, -2) == list(range(20, 10, -2)))
+ assert(myrange(20, 10, 0) == list(range(20, 10, 0)))
+
+
+if __name__ == '__main__':
+ pytest.main()
diff --git a/Useful/for_lec21/example/test_tasks_new.py b/Useful/for_lec21/example/test_tasks_new.py
new file mode 100644
index 0000000..d027f79
--- /dev/null
+++ b/Useful/for_lec21/example/test_tasks_new.py
@@ -0,0 +1,24 @@
+import pytest
+from tasks import mylen, myrange
+
+
+class TestTasksSuite:
+ def test_mylen(self):
+ l = [1, 45, 32, 9]
+ assert(mylen(l) == 4)
+
+ @pytest.fixture(
+ scope='function',
+ params=[5, 10, 2],
+ ids=lambda args: f"Test with args: {args}"
+ )
+ def parametrizer(self, request):
+ return request.param
+
+ def test_myrange(self, parametrizer):
+ args = parametrizer
+ assert(myrange(args) == list(range(args)))
+
+
+if __name__ == '__main__':
+ pytest.main()
diff --git a/Useful/for_lec21/listener_mock_example.py b/Useful/for_lec21/listener_mock_example.py
new file mode 100644
index 0000000..e596213
--- /dev/null
+++ b/Useful/for_lec21/listener_mock_example.py
@@ -0,0 +1,43 @@
+import unittest.mock as mock
+
+
+# Тестируемый класс
+class Listener:
+ def __init__(self):
+ self.running = True
+
+ def listen_forever(self):
+ while self.running:
+ try:
+ msg = msg_broker.wait_message()
+ client.process_message(msg)
+ except Exception:
+ self.running = False
+
+# Подготовка к тестированию
+# Создаем объект тестируемого класса
+listener = Listener()
+
+# Заменяем зависимости на mock-объекты
+msg_broker = mock.Mock()
+client = mock.Mock()
+
+# Настраиваем поведение mock-объектов
+# При инициализации атрибута side_effect итерируемым объектом
+# при каждом обращении к wait_message будет возвращаться очередной
+# элемент итерируемого объекта (сначала 'message', затем Exception).
+msg_broker.wait_message.side_effect = ['message', Exception]
+
+def check_msg(msg):
+ assert msg == 'message'
+
+# При инициализации атрибута side_effect функциональным (callable)
+# объектом при каждом обращении к process_message будет вызываться
+# этот функциональный объект.
+client.process_message.side_effect = check_msg
+
+# Тестируем и выполняем проверки
+listener.listen_forever()
+assert listener.running is False
+client.process_message.assert_called_once_with('message')
+assert msg_broker.wait_message.call_count == 2
diff --git a/Useful/for_lec21/mock_example.py b/Useful/for_lec21/mock_example.py
new file mode 100644
index 0000000..172f00c
--- /dev/null
+++ b/Useful/for_lec21/mock_example.py
@@ -0,0 +1,26 @@
+def fun(a, b):
+ print((a + b) * 100)
+
+
+def mock_print(arg):
+ mock_print.arg = arg
+
+
+class CtxMgr:
+ def __enter__(self):
+ global print
+ self.bkp_print = print
+ print = mock_print
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ global print
+ print = self.bkp_print
+
+
+with CtxMgr():
+ fun(1, 2)
+
+
+assert(mock_print.arg == 300)
+print(mock_print.arg)
+
+
diff --git a/Useful/for_lec21/mock_simple_example.py b/Useful/for_lec21/mock_simple_example.py
new file mode 100644
index 0000000..e0f03f8
--- /dev/null
+++ b/Useful/for_lec21/mock_simple_example.py
@@ -0,0 +1,16 @@
+import unittest.mock as mock
+
+
+def fun(service):
+ service.show()
+ service.name = "Hello"
+ s = service.get()
+ return s
+
+
+m = mock.Mock()
+m.get.return_value = 100
+res = fun(m)
+assert(m.show.called is True)
+assert(res == 100)
+
diff --git a/Useful/for_lec21/myfile.py b/Useful/for_lec21/myfile.py
new file mode 100644
index 0000000..b2d14b0
--- /dev/null
+++ b/Useful/for_lec21/myfile.py
@@ -0,0 +1,4 @@
+def mysum(a, b):
+ print(a + b)
+
+
diff --git a/Useful/for_lec21/pytest_useful_options.txt b/Useful/for_lec21/pytest_useful_options.txt
new file mode 100644
index 0000000..9d88a1a
--- /dev/null
+++ b/Useful/for_lec21/pytest_useful_options.txt
@@ -0,0 +1,8 @@
+# pip install pytest-html - для генерации красивых отчетов
+# полезные опции pytest
+pytest - запуск всех тестов (файлов test_.*.py) по порядку
+pytest -p no:warnings - запуск тестов с отключенными warnings
+pytest --cov-report html:cov_html --cov ../../папка_с_исходниками - запуск тестов с формированием отчета о покрытии тестами модулей ядра.
+pytest --html=report.html --self-contained-html - запуск тестов с формированием self-contained html-отчета.
+pytest -s - отображение текущих логов во время теста (иначе логи будут доступны только после окончания теста).
+pytest test_tasks.py::TestTasksSuite:: - запуск конкретного теста
\ No newline at end of file
diff --git a/Useful/for_lec21/simple_examples/report.html b/Useful/for_lec21/simple_examples/report.html
new file mode 100644
index 0000000..89e1921
--- /dev/null
+++ b/Useful/for_lec21/simple_examples/report.html
@@ -0,0 +1,615 @@
+
+
+
+
+ Test Report
+
+
+
+ report.html
+ Report generated on 20-Oct-2022 at 20:52:34 by pytest-html v3.1.1
+ Environment
+
+
+ Packages
+ {"pluggy": "1.0.0", "py": "1.11.0", "pytest": "6.2.5"}
+
+ Platform
+ Windows-10-10.0.19041-SP0
+
+ Plugins
+ {"cov": "3.0.0", "html": "3.1.1", "metadata": "2.0.2"}
+
+ Python
+ 3.7.9
+ Summary
+ 12 tests ran in 0.37 seconds.
+ (Un)check the boxes to filter the results.
10 passed , 2 skipped , 2 failed , 0 errors , 0 expected failures , 0 unexpected passes
+ Results
+
+
+
+ Result
+ Test
+ Duration
+ Links
+
+ No results found. Try to check the filters
+
+
+ Failed
+ test_my_fun.py::TestMylen::test_mylen2
+ 0.00
+
+
+
+
+
+ Failed
+ test_string_methods.py::TestStringMethods::test_upper
+ 0.00
+
+
+
+
+
+ Skipped
+ test_skip.py::MyTestCase::test_format
+ 0.00
+
+
+
+
+
+ Skipped
+ test_skip.py::MyTestCase::test_nothing
+ 0.00
+
+
+
+
+
+ Passed
+ test_my_fun.py::TestMylen::test_mylen
+ 0.00
+
+
+
+
+
+ Passed
+ test_skip.py::MyTestCase::test_windows_support
+ 0.00
+
+
+
+
+
+ Passed
+ test_string_methods.py::TestStringMethods::test_isupper
+ 0.00
+
+
+
+
+
+ Passed
+ test_string_methods.py::TestStringMethods::test_split
+ 0.00
+
+
+
+
+
+ Passed
+ test_with_nose.py::TestWithNose::test_numbers_5_6
+ 0.00
+
+
+
+
+
+ Passed
+ test_with_nose.py::TestWithNose::test_strings_b_2
+ 0.00
+
+
+
+
+
+ Passed
+ test_with_nose.py::TestWithNose::test_zero_division
+ 0.00
+
+
+
+
+
+ Passed
+ test_with_pytest.py::TestWithPytestSuite::test_numbers_5_6
+ 0.00
+
+
+
+
+
+ Passed
+ test_with_pytest.py::TestWithPytestSuite::test_strings_b_2
+ 0.00
+
+
+
+
+
+ Passed
+ test_with_pytest.py::TestWithPytestSuite::test_zero_division
+ 0.00
+
+
+
\ No newline at end of file
diff --git a/Useful/for_lec21/simple_examples/test_listener.py b/Useful/for_lec21/simple_examples/test_listener.py
new file mode 100644
index 0000000..9ee12b3
--- /dev/null
+++ b/Useful/for_lec21/simple_examples/test_listener.py
@@ -0,0 +1,40 @@
+import unittest.mock as mock
+
+# Тестируемый класс
+class Listener:
+ def __init__(self):
+ self.running = True
+
+ def listen_forever(self):
+ while self.running:
+ try:
+ msg = msg_broker.wait_message()
+ client.process_message(msg)
+ except Exception:
+ self.running = False
+
+# Подготовка к тестированию
+# Создаем объект тестируемого класса
+listener = Listener()
+# Заменяем зависимости на mock-объекты
+msg_broker = mock.Mock()
+client = mock.Mock()
+# Настраиваем поведение mock-объектов
+# При инициализации атрибута side_effect итерируемым объектом
+# при каждом обращении к wait_message будет возвращаться очередной
+# элемент итерируемого объекта.
+MSG = 'message'
+msg_broker.wait_message.side_effect = [MSG, Exception]
+
+def check_msg(msg):
+ assert msg == MSG
+# При инициализации атрибута side_effect функциональным (callable)
+# объектом при каждом обращении к process_message будет вызываться
+# этот функциональный объект.
+client.process_message.side_effect = check_msg
+
+# Тестируем и выполняем проверки
+listener.listen_forever()
+assert listener.running == False
+client.process_message.assert_called_once_with('message')
+assert msg_broker.wait_message.call_count == 2
\ No newline at end of file
diff --git a/Useful/for_lec21/simple_examples/test_my_fun.py b/Useful/for_lec21/simple_examples/test_my_fun.py
new file mode 100644
index 0000000..9ff7fc2
--- /dev/null
+++ b/Useful/for_lec21/simple_examples/test_my_fun.py
@@ -0,0 +1,23 @@
+from unittest import TestCase
+
+
+class TestMylen(TestCase):
+ @classmethod
+ def setUpClass(cls):
+ print("Before test suite")
+
+ @classmethod
+ def tearDownClass(cls):
+ print("After test suite")
+
+ def setUp(self):
+ print("Before test")
+
+ def tearDown(self):
+ print("After test")
+
+ def test_mylen(self):
+ pass
+
+ def test_mylen2(self):
+ self.fail()
diff --git a/Useful/for_lec21/simple_examples/test_skip.py b/Useful/for_lec21/simple_examples/test_skip.py
new file mode 100644
index 0000000..803d23c
--- /dev/null
+++ b/Useful/for_lec21/simple_examples/test_skip.py
@@ -0,0 +1,18 @@
+import unittest
+import sys
+
+
+class MyTestCase(unittest.TestCase):
+ @unittest.skip("demonstrating skipping")
+ def test_nothing(self):
+ self.fail("shouldn't happen")
+
+ @unittest.skipIf(sys.version_info.minor != 6, "not supported in this python version")
+ def test_format(self):
+ # Tests that work for only a certain version of the library.
+ pass
+
+ @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows")
+ def test_windows_support(self):
+ # windows specific testing code
+ pass
diff --git a/Useful/for_lec21/simple_examples/test_string_methods.py b/Useful/for_lec21/simple_examples/test_string_methods.py
new file mode 100644
index 0000000..61d6d5f
--- /dev/null
+++ b/Useful/for_lec21/simple_examples/test_string_methods.py
@@ -0,0 +1,35 @@
+import unittest
+
+
+class TestStringMethods(unittest.TestCase):
+
+ def setUp(self):
+ print('start')
+
+ def tearDown(self):
+ print('end')
+
+ @classmethod
+ def setUpClass(cls):
+ print('class start')
+
+ @classmethod
+ def tearDownClass(cls):
+ print('class end')
+
+ def test_upper(self):
+ self.assertEqual('foo'.upper(), 'FOO')
+
+ def test_isupper(self):
+ self.assertTrue('FOO'.isupper())
+ self.assertFalse('Foo'.isupper())
+
+ def test_split(self):
+ s = 'hello world'
+ self.assertEqual(s.split(), ['hello', 'world'])
+ with self.assertRaises(TypeError):
+ s.split(2)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/Useful/for_lec21/simple_examples/test_with_nose.py b/Useful/for_lec21/simple_examples/test_with_nose.py
new file mode 100644
index 0000000..0b6ab82
--- /dev/null
+++ b/Useful/for_lec21/simple_examples/test_with_nose.py
@@ -0,0 +1,27 @@
+from nose.tools import assert_equals, raises
+
+
+class TestWithNose:
+ def setup(self):
+ print('Before test case')
+
+ def teardown(self):
+ print('After test case')
+
+ @classmethod
+ def setup_class(cls):
+ print('Before test suite')
+
+ @classmethod
+ def teardown_class(cls):
+ print('After test suite')
+
+ def test_numbers_5_6(self):
+ assert_equals(5 * 6, 30)
+
+ def test_strings_b_2(self):
+ assert_equals('b' * 2, 'bb')
+
+ @raises(ZeroDivisionError)
+ def test_zero_division(self):
+ a = 10 / 0
diff --git a/Useful/for_lec21/simple_examples/test_with_pytest.py b/Useful/for_lec21/simple_examples/test_with_pytest.py
new file mode 100644
index 0000000..e222e7a
--- /dev/null
+++ b/Useful/for_lec21/simple_examples/test_with_pytest.py
@@ -0,0 +1,27 @@
+import pytest
+
+
+class TestWithPytestSuite:
+ def setup(self):
+ print('Before test case')
+
+ def teardown(self):
+ print('After test case')
+
+ @classmethod
+ def setup_class(cls):
+ print('Before test suite')
+
+ @classmethod
+ def teardown_class(cls):
+ print('After test suite')
+
+ def test_numbers_5_6(self):
+ assert(5 * 6 == 30)
+
+ def test_strings_b_2(self):
+ assert('b' * 2 == 'bb')
+
+ def test_zero_division(self):
+ with pytest.raises(ZeroDivisionError):
+ a = 10 / 0
diff --git a/Useful/for_lec21/system_tests/check_selenium.py b/Useful/for_lec21/system_tests/check_selenium.py
new file mode 100644
index 0000000..f927aa4
--- /dev/null
+++ b/Useful/for_lec21/system_tests/check_selenium.py
@@ -0,0 +1,29 @@
+import os
+from selenium import webdriver
+from selenium.common.exceptions import TimeoutException
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+from selenium.webdriver.common.by import By
+
+
+os.environ['PATH'] += os.pathsep + r'C:\Soft\ForPython\chromedriver-win64'
+
+driver = webdriver.Chrome()
+
+driver.get('http://www.google.com')
+
+print(driver.title)
+
+inputElement = driver.find_element(by=By.NAME, value='q')
+
+inputElement.send_keys('cheese!')
+
+inputElement.submit()
+
+try:
+ WebDriverWait(driver, 10).until(EC.title_contains('cheese!'))
+ print(driver.title)
+except TimeoutException:
+ print('Timeout exception!')
+finally:
+ driver.quit()
diff --git a/Useful/for_lec21/system_tests/copy_file_task.py b/Useful/for_lec21/system_tests/copy_file_task.py
new file mode 100644
index 0000000..4448bb5
--- /dev/null
+++ b/Useful/for_lec21/system_tests/copy_file_task.py
@@ -0,0 +1,11 @@
+import os
+
+
+def copyfile(source, destination):
+ with open(source, 'r') as src:
+ with open(destination, 'x') as dst:
+ dst.writelines(src.readlines())
+
+
+if __name__ == '__main__':
+ copyfile('source.txt', 'destination.txt')
diff --git a/Useful/for_lec21/system_tests/log.html b/Useful/for_lec21/system_tests/log.html
new file mode 100644
index 0000000..1019e52
--- /dev/null
+++ b/Useful/for_lec21/system_tests/log.html
@@ -0,0 +1,2199 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Opening Robot Framework log failed
+
+ Verify that you have JavaScript enabled in your browser.
+ Make sure you are using a modern enough browser . If using Internet Explorer, version 11 is required.
+ Check are there messages in your browser's JavaScript error log . Please report the problem if you suspect you have encountered a bug.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Useful/for_lec21/system_tests/output.xml b/Useful/for_lec21/system_tests/output.xml
new file mode 100644
index 0000000..b8b6adf
--- /dev/null
+++ b/Useful/for_lec21/system_tests/output.xml
@@ -0,0 +1,117 @@
+
+
+
+
+
+http://www.google.com
+chrome
+options=add_experimental_option("excludeSwitches", ["enable-logging"])
+Opens a new browser instance to the optional ``url``.
+Arguments: [ 'http://www.google.com' | 'chrome' | options='add_experimental_option("excludeSwitches", ["enable-logging"])' ]
+Opening browser 'chrome' to base url 'http://www.google.com'.
+Started executable: `C:\Soft\ForPython\chromedriver-win64\chromedriver.EXE` in a child process with pid: 14208
+POST http://localhost:62820/session {"capabilities": {"firstMatch": [{}], "alwaysMatch": {"browserName": "chrome", "pageLoadStrategy": "normal", "goog:chromeOptions": {"excludeSwitches": ["enable-logging"], "extensions": [], "args": []}}}}
+Starting new HTTP connection (1): localhost:62820
+http://localhost:62820 "POST /session HTTP/1.1" 200 0
+Remote response: status=200 | data={"value":{"capabilities":{"acceptInsecureCerts":false,"browserName":"chrome","browserVersion":"115.0.5790.110","chrome":{"chromedriverVersion":"115.0.5790.102 (90efd4b0ad6aa15eeafcdabd5817ae939f7ba059-refs/branch-heads/5790_90@{#9})","userDataDir":"C:\\Users\\Stmadm\\AppData\\Local\\Temp\\scoped_dir14208_980171044"},"goog:chromeOptions":{"debuggerAddress":"localhost:62823"},"networkConnectionEnabled":false,"pageLoadStrategy":"normal","platformName":"windows","proxy":{},"setWindowRect":true,"strictFileInteractability":false,"timeouts":{"implicit":0,"pageLoad":300000,"script":30000},"unhandledPromptBehavior":"dismiss and notify","webauthn:extension:credBlob":true,"webauthn:extension:largeBlob":true,"webauthn:extension:minPinLength":true,"webauthn:extension:prf":true,"webauthn:virtualAuthenticators":true},"sessionId":"34371a5177e30e6abee2f1c95be64260"}} | headers=HTTPHeaderDict({'Content-Length': '862', 'Content-Type': 'application/json; charset=utf-8', 'cache-control': 'no-cache'})
+Finished Request
+POST http://localhost:62820/session/34371a5177e30e6abee2f1c95be64260/timeouts {"script": 5000}
+http://localhost:62820 "POST /session/34371a5177e30e6abee2f1c95be64260/timeouts HTTP/1.1" 200 0
+Remote response: status=200 | data={"value":null} | headers=HTTPHeaderDict({'Content-Length': '14', 'Content-Type': 'application/json; charset=utf-8', 'cache-control': 'no-cache'})
+Finished Request
+POST http://localhost:62820/session/34371a5177e30e6abee2f1c95be64260/timeouts {"implicit": 0}
+http://localhost:62820 "POST /session/34371a5177e30e6abee2f1c95be64260/timeouts HTTP/1.1" 200 0
+Remote response: status=200 | data={"value":null} | headers=HTTPHeaderDict({'Content-Length': '14', 'Content-Type': 'application/json; charset=utf-8', 'cache-control': 'no-cache'})
+Finished Request
+POST http://localhost:62820/session/34371a5177e30e6abee2f1c95be64260/timeouts {"pageLoad": 300000}
+http://localhost:62820 "POST /session/34371a5177e30e6abee2f1c95be64260/timeouts HTTP/1.1" 200 0
+Remote response: status=200 | data={"value":null} | headers=HTTPHeaderDict({'Content-Length': '14', 'Content-Type': 'application/json; charset=utf-8', 'cache-control': 'no-cache'})
+Finished Request
+POST http://localhost:62820/session/34371a5177e30e6abee2f1c95be64260/url {"url": "http://www.google.com"}
+http://localhost:62820 "POST /session/34371a5177e30e6abee2f1c95be64260/url HTTP/1.1" 200 0
+Remote response: status=200 | data={"value":null} | headers=HTTPHeaderDict({'Content-Length': '14', 'Content-Type': 'application/json; charset=utf-8', 'cache-control': 'no-cache'})
+Finished Request
+Opened browser with session id 34371a5177e30e6abee2f1c95be64260.
+Return: 1
+
+
+
+name=q
+Cheese!
+Types the given ``text`` into the text field identified by ``locator``.
+Arguments: [ 'name=q' | 'Cheese!' ]
+Typing text 'Cheese!' into text field 'name=q'.
+POST http://localhost:62820/session/34371a5177e30e6abee2f1c95be64260/elements {"using": "css selector", "value": "[name=\"q\"]"}
+http://localhost:62820 "POST /session/34371a5177e30e6abee2f1c95be64260/elements HTTP/1.1" 200 0
+Remote response: status=200 | data={"value":[{"element-6066-11e4-a52e-4f735466cecf":"6502A84DD8CC453C9B0E9D8AB0B58A46_element_2"}]} | headers=HTTPHeaderDict({'Content-Length': '96', 'Content-Type': 'application/json; charset=utf-8', 'cache-control': 'no-cache'})
+Finished Request
+POST http://localhost:62820/session/34371a5177e30e6abee2f1c95be64260/element/6502A84DD8CC453C9B0E9D8AB0B58A46_element_2/clear {"id": "6502A84DD8CC453C9B0E9D8AB0B58A46_element_2"}
+http://localhost:62820 "POST /session/34371a5177e30e6abee2f1c95be64260/element/6502A84DD8CC453C9B0E9D8AB0B58A46_element_2/clear HTTP/1.1" 200 0
+Remote response: status=200 | data={"value":null} | headers=HTTPHeaderDict({'Content-Length': '14', 'Content-Type': 'application/json; charset=utf-8', 'cache-control': 'no-cache'})
+Finished Request
+POST http://localhost:62820/session/34371a5177e30e6abee2f1c95be64260/element/6502A84DD8CC453C9B0E9D8AB0B58A46_element_2/value {"text": "Cheese!", "value": ["C", "h", "e", "e", "s", "e", "!"], "id": "6502A84DD8CC453C9B0E9D8AB0B58A46_element_2"}
+http://localhost:62820 "POST /session/34371a5177e30e6abee2f1c95be64260/element/6502A84DD8CC453C9B0E9D8AB0B58A46_element_2/value HTTP/1.1" 200 0
+Remote response: status=200 | data={"value":null} | headers=HTTPHeaderDict({'Content-Length': '14', 'Content-Type': 'application/json; charset=utf-8', 'cache-control': 'no-cache'})
+Finished Request
+Return: None
+
+
+
+Submits a form identified by ``locator``.
+Arguments: [ ]
+Submitting form 'None'.
+POST http://localhost:62820/session/34371a5177e30e6abee2f1c95be64260/elements {"using": "tag name", "value": "form"}
+http://localhost:62820 "POST /session/34371a5177e30e6abee2f1c95be64260/elements HTTP/1.1" 200 0
+Remote response: status=200 | data={"value":[{"element-6066-11e4-a52e-4f735466cecf":"6502A84DD8CC453C9B0E9D8AB0B58A46_element_18"}]} | headers=HTTPHeaderDict({'Content-Length': '97', 'Content-Type': 'application/json; charset=utf-8', 'cache-control': 'no-cache'})
+Finished Request
+GET http://localhost:62820/session/34371a5177e30e6abee2f1c95be64260/element/6502A84DD8CC453C9B0E9D8AB0B58A46_element_18/name {"id": "6502A84DD8CC453C9B0E9D8AB0B58A46_element_18"}
+http://localhost:62820 "GET /session/34371a5177e30e6abee2f1c95be64260/element/6502A84DD8CC453C9B0E9D8AB0B58A46_element_18/name HTTP/1.1" 200 0
+Remote response: status=200 | data={"value":"form"} | headers=HTTPHeaderDict({'Content-Length': '16', 'Content-Type': 'application/json; charset=utf-8', 'cache-control': 'no-cache'})
+Finished Request
+POST http://localhost:62820/session/34371a5177e30e6abee2f1c95be64260/execute/sync {"script": "/* submitForm */var form = arguments[0];\nwhile (form.nodeName != \"FORM\" && form.parentNode) {\n form = form.parentNode;\n}\nif (!form) { throw Error('Unable to find containing form element'); }\nif (!form.ownerDocument) { throw Error('Unable to find owning document'); }\nvar e = form.ownerDocument.createEvent('Event');\ne.initEvent('submit', true, true);\nif (form.dispatchEvent(e)) { HTMLFormElement.prototype.submit.call(form) }\n", "args": [{"element-6066-11e4-a52e-4f735466cecf": "6502A84DD8CC453C9B0E9D8AB0B58A46_element_18"}]}
+http://localhost:62820 "POST /session/34371a5177e30e6abee2f1c95be64260/execute/sync HTTP/1.1" 200 0
+Remote response: status=200 | data={"value":null} | headers=HTTPHeaderDict({'Content-Length': '14', 'Content-Type': 'application/json; charset=utf-8', 'cache-control': 'no-cache'})
+Finished Request
+Return: None
+
+
+
+10s
+2s
+Title Should Be
+Cheese! - Поиск в Google
+Runs the specified keyword and retries if it fails.
+Arguments: [ '10s' | '2s' | 'Title Should Be' | 'Cheese! - Поиск в Google' ]
+
+Cheese! - Поиск в Google
+Verifies that the current page title equals ``title``.
+Arguments: [ 'Cheese! - Поиск в Google' ]
+GET http://localhost:62820/session/34371a5177e30e6abee2f1c95be64260/title {}
+http://localhost:62820 "GET /session/34371a5177e30e6abee2f1c95be64260/title HTTP/1.1" 200 0
+Remote response: status=200 | data={"value":"Cheese! - Поиск в Google"} | headers=HTTPHeaderDict({'Content-Length': '42', 'Content-Type': 'application/json; charset=utf-8', 'cache-control': 'no-cache'})
+Finished Request
+Page title is 'Cheese! - Поиск в Google'.
+Return: None
+
+
+Return: None
+
+
+
+
+Test google search
+
+
+
+
+All Tests
+
+
+
+
+Test Robot Sellib
+
+
+
+
+
diff --git a/Useful/for_lec21/system_tests/report.html b/Useful/for_lec21/system_tests/report.html
new file mode 100644
index 0000000..970f905
--- /dev/null
+++ b/Useful/for_lec21/system_tests/report.html
@@ -0,0 +1,2462 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Opening Robot Framework report failed
+
+ Verify that you have JavaScript enabled in your browser.
+ Make sure you are using a modern enough browser . If using Internet Explorer, version 11 is required.
+ Check are there messages in your browser's JavaScript error log . Please report the problem if you suspect you have encountered a bug.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Useful/for_lec21/system_tests/test_copy_file.robot b/Useful/for_lec21/system_tests/test_copy_file.robot
new file mode 100644
index 0000000..32f60b3
--- /dev/null
+++ b/Useful/for_lec21/system_tests/test_copy_file.robot
@@ -0,0 +1,37 @@
+# Файл test_copy_file.robot
+
+*** Settings ***
+Documentation Check file actions
+
+Library OperatingSystem
+
+Test Setup On Test Setup
+Test Teardown On Test Teardown
+
+
+*** Variables ***
+${copy_script} python ./copy_file_task.py
+${src_file} ./source.txt
+${dst_file} ./destination.txt
+${exp_content} hello
+
+
+*** Keywords ***
+On Test Setup
+ Create File ${src_file} ${exp_content}
+ File Should Exist ${src_file}
+
+On Test Teardown
+ Remove File ${src_file}
+ Remove File ${dst_file}
+
+
+*** Test Cases ***
+Test File Copy
+ [Documentation] Test file copy script
+ [Tags] DEBUG
+ File Should Not Exist ${dst_file}
+ Run ${copy_script}
+ File Should Exist ${dst_file}
+ ${content}= Get File ${dst_file}
+ Should Be True '${content}' == '${exp_content}'
diff --git a/Useful/for_lec21/system_tests/test_robot_sellib.robot b/Useful/for_lec21/system_tests/test_robot_sellib.robot
new file mode 100644
index 0000000..bb56930
--- /dev/null
+++ b/Useful/for_lec21/system_tests/test_robot_sellib.robot
@@ -0,0 +1,13 @@
+*** Settings ***
+Documentation Test google search
+Library SeleniumLibrary
+
+
+*** Test Cases ***
+Test Google Search
+ Open Browser http://www.google.com chrome options=add_experimental_option("excludeSwitches", ["enable-logging"])
+ # Open Browser http://www.google.com chrome
+ Input Text name=q Cheese!
+ Submit Form
+ Wait Until Keyword Succeeds 10s 2s
+ ... Title Should Be Cheese! - Поиск в Google
diff --git a/Useful/for_lec21/test_listener.py b/Useful/for_lec21/test_listener.py
new file mode 100644
index 0000000..c6b5096
--- /dev/null
+++ b/Useful/for_lec21/test_listener.py
@@ -0,0 +1,36 @@
+import unittest.mock as mock
+import listener_mock_example as lme
+
+
+class TestListener:
+ MSG = 'message'
+
+ @staticmethod
+ def check_msg(msg):
+ assert msg == TestListener.MSG
+
+ def test_listen_forever(self):
+ # Подготовка к тестированию
+ # Создаем объект тестируемого класса
+ listener = lme.Listener()
+
+ # Заменяем зависимости на mock-объекты
+ lme.Listener.msg_broker = mock.Mock()
+ lme.Listener.client = mock.Mock()
+
+ # Настраиваем поведение mock-объектов
+ # При инициализации атрибута side_effect итерируемым объектом
+ # при каждом обращении к wait_message будет возвращаться очередной
+ # элемент итерируемого объекта (сначала 'message', затем Exception).
+ lme.Listener.msg_broker.wait_message.side_effect = [TestListener.MSG, Exception]
+
+ # При инициализации атрибута side_effect функциональным (callable)
+ # объектом при каждом обращении к process_message будет вызываться
+ # этот функциональный объект.
+ lme.Listener.client.process_message.side_effect = TestListener.check_msg
+
+ # Тестируем и выполняем проверки
+ listener.listen_forever()
+ assert listener.running is False
+ lme.Listener.client.process_message.assert_called_once_with(TestListener.MSG)
+ assert lme.Listener.msg_broker.wait_message.call_count == 2
diff --git a/Useful/for_lec22/kafka_example/consumer_example.py b/Useful/for_lec22/kafka_example/consumer_example.py
new file mode 100644
index 0000000..0170163
--- /dev/null
+++ b/Useful/for_lec22/kafka_example/consumer_example.py
@@ -0,0 +1,57 @@
+import multiprocessing.pool as mp_pool
+import kafka
+import kafka.errors as kafka_errors
+
+
+class LimitedMultiprocessingPool(mp_pool.Pool):
+ def get_pool_cache_size(self):
+ return len(self._cache)
+
+
+class MsgConsumer:
+ def __init__(self, proc_fun):
+ # Функция для обработки сообщения в дочернем процессе
+ self.proc_fun = proc_fun
+ # Клиент для чтения сообщений из Kafka
+ self.consumer = kafka.KafkaConsumer(
+ "example_topic",
+ enable_auto_commit=False,
+ bootstrap_servers=["127.0.0.1:9092"],
+ group_id="example",
+ client_id="example-backend",
+ )
+ # Лимит на количество сообщений, единовременно находящихся в пуле
+ self.pool_cache_limit = 20
+ # Флаг управляемой остановки приложения
+ self.stop_processing = False
+ # Пул обработчиков сообщений
+ self.pool = LimitedMultiprocessingPool(processes=10)
+
+ def handle_pool_cache_excess(self):
+ while self.pool.get_pool_cache_size() >= self.pool_cache_limit:
+ # Здесь можно предусмотреть sleep
+ pass
+
+ def main_loop(self):
+ while not self.stop_processing:
+ for msg in self.consumer:
+ if self.stop_processing:
+ break
+ try:
+ self.handle_pool_cache_excess()
+ self.consumer.commit()
+ except kafka_errors.CommitFailedError:
+ # Отлавливаем редкий, но возможный случай исключения при ребалансе
+ continue
+ self.pool.apply_async(self.proc_fun, (msg,))
+
+
+def fun(*args, **kwargs):
+ print(f"Received from Kafka: {args}")
+ for arg in args:
+ print(f"Received value: {getattr(arg, 'value').decode()}")
+
+
+if __name__ == "__main__":
+ consumer = MsgConsumer(fun)
+ consumer.main_loop()
diff --git a/Useful/for_lec22/kafka_example/full_consumer_example.py b/Useful/for_lec22/kafka_example/full_consumer_example.py
new file mode 100644
index 0000000..51d989a
--- /dev/null
+++ b/Useful/for_lec22/kafka_example/full_consumer_example.py
@@ -0,0 +1,89 @@
+import multiprocessing as mp
+import multiprocessing.pool as mp_pool
+import signal
+import time
+import kafka
+import kafka.errors as kafka_errors
+
+
+class LimitedMultiprocessingPool(mp_pool.Pool):
+ def get_pool_cache_size(self):
+ return len(self._cache)
+
+
+class MsgConsumer:
+ def __init__(self, proc_fun):
+ # Функция для обработки сообщения в дочернем процессе
+ self.proc_fun = proc_fun
+ # Клиент для чтения сообщений из Kafka
+ self.consumer = kafka.KafkaConsumer(
+ "labeling",
+ auto_offset_reset="earliest",
+ enable_auto_commit=False,
+ bootstrap_servers=["127.0.0.1:9092"],
+ group_id="labeling",
+ client_id="labeling-backend",
+ check_crcs="false",
+ consumer_timeout_ms=1000,
+ session_timeout_ms=30000,
+ request_timeout_ms=30500,
+ max_partition_fetch_bytes=2000000000
+ )
+ # Лимит на количество сообщений, единовременно находящихся в пуле
+ self.pool_cache_limit = 20
+ self.graceful_shutdown_timeout = 600
+ # Флаг управляемой остановки приложения
+ self.stop_processing = False
+ # Пул обработчиков сообщений
+ self.pool = LimitedMultiprocessingPool(processes=10)
+ # Обеспечиваем возможность остановки приложения по SIGTERM
+ signal.signal(signal.SIGTERM, self.set_stop_processing)
+
+ def set_stop_processing(self, *args, **kwargs):
+ self.stop_processing = True
+
+ def handle_pool_cache_excess(self):
+ while self.pool.get_pool_cache_size() >= self.pool_cache_limit:
+ # Здесь можно предусмотреть sleep
+ pass
+
+ def main_loop(self):
+ while not self.stop_processing:
+ for msg in self.consumer:
+ if self.stop_processing:
+ break
+ try:
+ self.handle_pool_cache_excess()
+ self.consumer.commit()
+ except kafka_errors.CommitFailedError:
+ # Отлавливаем редкий, но возможный случай исключения при ребалансе
+ continue
+ self.pool.apply_async(self.proc_fun, (msg,))
+
+ def graceful_shutdown(self):
+ try:
+ self.consumer.close() # Останавливаем клиента Kafka
+ self.pool.close() # Предотвращаем добавление новых задач в пул
+ graceful_shutdown_end = time.time() + self.graceful_shutdown_timeout
+ while graceful_shutdown_end > time.time():
+ active_child_proc_num = len(mp.active_children())
+ if active_child_proc_num == 0:
+ break
+ # Здесь можно предусмотреть sleep
+ else:
+ raise
+ except Exception as ex:
+ self.pool.terminate()
+ raise ex
+ finally:
+ self.pool.join()
+
+
+def fun(*args, **kwargs):
+ print("Do nothing")
+
+
+if __name__ == "__main__":
+ consumer = MsgConsumer(fun)
+ consumer.main_loop()
+ consumer.graceful_shutdown()
diff --git a/Useful/for_lec22/kafka_example/producer_example.py b/Useful/for_lec22/kafka_example/producer_example.py
new file mode 100644
index 0000000..34bf279
--- /dev/null
+++ b/Useful/for_lec22/kafka_example/producer_example.py
@@ -0,0 +1,9 @@
+import kafka
+
+
+if __name__ == "__main__":
+ producer = kafka.KafkaProducer(bootstrap_servers=["127.0.0.1:9092"])
+ producer.send("example_topic", 'example_message_1'.encode())
+ producer.send("example_topic", 'example_message_2'.encode())
+ # Для немедленной отправки без буферизации
+ producer.flush()
diff --git a/Useful/for_lec22/rabbit_example/publisher.py b/Useful/for_lec22/rabbit_example/publisher.py
new file mode 100644
index 0000000..382cc46
--- /dev/null
+++ b/Useful/for_lec22/rabbit_example/publisher.py
@@ -0,0 +1,14 @@
+import pika
+
+connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
+channel = connection.channel()
+
+
+channel.exchange_declare(exchange='logs', exchange_type='topic')
+
+for i in range(5):
+ channel.basic_publish(exchange='logs',
+ routing_key='2.hello',
+ body=f'Hello World {i}'.encode())
+print("Sent 'Hello World!'")
+connection.close()
\ No newline at end of file
diff --git a/Useful/for_lec22/rabbit_example/rec.py b/Useful/for_lec22/rabbit_example/rec.py
new file mode 100644
index 0000000..7c26b0e
--- /dev/null
+++ b/Useful/for_lec22/rabbit_example/rec.py
@@ -0,0 +1,20 @@
+import pika
+import time
+import random
+
+connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
+channel = connection.channel()
+channel.queue_declare(queue='hello3', durable=True)
+
+
+def callback(ch, method, properties, body):
+ print(f'Start processing {body.decode()}')
+ t = time.time()
+ time.sleep(random.randint(1, 5))
+ print(f'Processed for {time.time() - t:.2f} sec.')
+
+
+channel.basic_consume(queue='hello3', on_message_callback=callback, auto_ack=True)
+
+print('Waiting for messages. To exit press CTRL+C')
+channel.start_consuming()
diff --git a/Useful/for_lec22/rabbit_example/receiver.py b/Useful/for_lec22/rabbit_example/receiver.py
new file mode 100644
index 0000000..d26d94d
--- /dev/null
+++ b/Useful/for_lec22/rabbit_example/receiver.py
@@ -0,0 +1,29 @@
+# файл receive.py
+
+import pika
+import time
+import random
+
+connection = pika.BlockingConnection(
+ pika.ConnectionParameters(host='localhost'))
+channel = connection.channel()
+
+
+channel.queue_declare(queue='hello', durable=True)
+
+
+def callback(ch, method, properties, body):
+ print('Start processing {}'.format(body.decode()))
+ t = time.time()
+ time.sleep(random.randint(1, 5))
+ print(f'Processed for {time.time() - t:3.2f} sec.')
+ channel.basic_ack(delivery_tag=method.delivery_tag)
+
+
+channel.basic_qos(prefetch_count=1)
+channel.basic_consume(queue='hello',
+ on_message_callback=callback,
+ auto_ack=False)
+
+print('Waiting for messages. To exit press CTRL+C')
+channel.start_consuming()
\ No newline at end of file
diff --git a/Useful/for_lec22/rabbit_example/send.py b/Useful/for_lec22/rabbit_example/send.py
new file mode 100644
index 0000000..39d4cc8
--- /dev/null
+++ b/Useful/for_lec22/rabbit_example/send.py
@@ -0,0 +1,13 @@
+# файл send.py
+import pika
+
+connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
+channel = connection.channel()
+channel.queue_declare(queue='hello3', durable=True)
+
+for i in range(5):
+ channel.basic_publish(exchange='', routing_key='hello3',
+ body=f'Hello World {i}'.encode())
+
+print("Sent 'Hello World!'")
+connection.close()
diff --git a/Useful/for_lec22/rabbit_example/sender.py b/Useful/for_lec22/rabbit_example/sender.py
new file mode 100644
index 0000000..8a12860
--- /dev/null
+++ b/Useful/for_lec22/rabbit_example/sender.py
@@ -0,0 +1,15 @@
+import pika
+
+connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
+channel = connection.channel()
+
+
+channel.queue_declare(queue='hello', durable=True)
+
+for i in range(5):
+ channel.basic_publish(exchange='',
+ routing_key='hello',
+ body=f'Hello World {i}'.encode(),
+ properties=pika.BasicProperties(delivery_mode=2))
+print("Sent 'Hello World!'")
+connection.close()
\ No newline at end of file
diff --git a/Useful/for_lec22/rabbit_example/subscriber1.py b/Useful/for_lec22/rabbit_example/subscriber1.py
new file mode 100644
index 0000000..3eea55d
--- /dev/null
+++ b/Useful/for_lec22/rabbit_example/subscriber1.py
@@ -0,0 +1,30 @@
+# файл receive.py
+
+import pika
+import time
+import random
+
+NAME = 'Logger 1'
+
+connection = pika.BlockingConnection(
+ pika.ConnectionParameters(host='localhost'))
+channel = connection.channel()
+
+channel.exchange_declare(exchange='logs', exchange_type='topic')
+queue_name = 'hello_1'
+result = channel.queue_declare(queue=queue_name, exclusive=True)
+channel.queue_bind(exchange='logs', routing_key='*.hello', queue=queue_name)
+
+
+def callback(ch, method, properties, body):
+ print(f'Processing msg {body.decode()} by: {NAME}')
+ channel.basic_ack(delivery_tag=method.delivery_tag)
+
+
+channel.basic_qos(prefetch_count=1)
+channel.basic_consume(queue=queue_name,
+ on_message_callback=callback,
+ auto_ack=False)
+
+print('Waiting for messages. To exit press CTRL+C')
+channel.start_consuming()
diff --git a/Useful/for_lec22/rabbit_example/subscriber2.py b/Useful/for_lec22/rabbit_example/subscriber2.py
new file mode 100644
index 0000000..7434f4b
--- /dev/null
+++ b/Useful/for_lec22/rabbit_example/subscriber2.py
@@ -0,0 +1,30 @@
+# файл receive.py
+
+import pika
+import time
+import random
+
+NAME = 'Logger 2'
+
+connection = pika.BlockingConnection(
+ pika.ConnectionParameters(host='localhost'))
+channel = connection.channel()
+
+channel.exchange_declare(exchange='logs', exchange_type='topic')
+queue_name = 'hello_3'
+result = channel.queue_declare(queue=queue_name, exclusive=False)
+channel.queue_bind(exchange='logs', routing_key='*.bye', queue=queue_name)
+
+
+def callback(ch, method, properties, body):
+ print(f'Processing msg {body.decode()} by: {NAME}')
+ channel.basic_ack(delivery_tag=method.delivery_tag)
+
+
+channel.basic_qos(prefetch_count=1)
+channel.basic_consume(queue=queue_name,
+ on_message_callback=callback,
+ auto_ack=False)
+
+print('Waiting for messages. To exit press CTRL+C')
+channel.start_consuming()
diff --git a/Useful/for_lec23/MVC_and_MTV.png b/Useful/for_lec23/MVC_and_MTV.png
new file mode 100644
index 0000000..d4a5564
Binary files /dev/null and b/Useful/for_lec23/MVC_and_MTV.png differ
diff --git a/Useful/for_lec23/coolapp/__init__.py b/Useful/for_lec23/coolapp/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/Useful/for_lec23/coolapp/admin.py b/Useful/for_lec23/coolapp/admin.py
new file mode 100644
index 0000000..f4a53b7
--- /dev/null
+++ b/Useful/for_lec23/coolapp/admin.py
@@ -0,0 +1,5 @@
+from django.contrib import admin
+from .models import Film
+
+# Register your models here.
+admin.site.register(Film)
diff --git a/Useful/for_lec23/coolapp/apps.py b/Useful/for_lec23/coolapp/apps.py
new file mode 100644
index 0000000..a37f808
--- /dev/null
+++ b/Useful/for_lec23/coolapp/apps.py
@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class CoolappConfig(AppConfig):
+ default_auto_field = 'django.db.models.BigAutoField'
+ name = 'coolapp'
diff --git a/Useful/for_lec23/coolapp/forms.py b/Useful/for_lec23/coolapp/forms.py
new file mode 100644
index 0000000..d13f03a
--- /dev/null
+++ b/Useful/for_lec23/coolapp/forms.py
@@ -0,0 +1,9 @@
+from django import forms
+from .models import Film
+
+
+class FilmForm(forms.ModelForm):
+ class Meta:
+ model = Film
+ fields = ('name', 'desc', 'rate')
+# поля pub_date и id заполняются сами
diff --git a/Useful/for_lec23/coolapp/migrations/0001_initial.py b/Useful/for_lec23/coolapp/migrations/0001_initial.py
new file mode 100644
index 0000000..d45ad69
--- /dev/null
+++ b/Useful/for_lec23/coolapp/migrations/0001_initial.py
@@ -0,0 +1,23 @@
+# Generated by Django 3.2.8 on 2022-02-28 17:21
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Film',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=200)),
+ ('desc', models.TextField()),
+ ('pub_date', models.DateTimeField(auto_now_add=True, verbose_name='date published')),
+ ],
+ ),
+ ]
diff --git a/Useful/for_lec23/coolapp/migrations/0002_film_rate.py b/Useful/for_lec23/coolapp/migrations/0002_film_rate.py
new file mode 100644
index 0000000..0411521
--- /dev/null
+++ b/Useful/for_lec23/coolapp/migrations/0002_film_rate.py
@@ -0,0 +1,19 @@
+# Generated by Django 3.2.8 on 2022-02-28 17:40
+
+import django.core.validators
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('coolapp', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='film',
+ name='rate',
+ field=models.IntegerField(default=1, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(10)]),
+ ),
+ ]
diff --git a/Useful/for_lec23/coolapp/migrations/__init__.py b/Useful/for_lec23/coolapp/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/Useful/for_lec23/coolapp/models.py b/Useful/for_lec23/coolapp/models.py
new file mode 100644
index 0000000..f643ae6
--- /dev/null
+++ b/Useful/for_lec23/coolapp/models.py
@@ -0,0 +1,16 @@
+from django.db import models
+from django.core.validators import MaxValueValidator, MinValueValidator
+
+
+# Create your models here.
+class Film(models.Model):
+ name = models.CharField(max_length=200)
+ desc = models.TextField()
+ pub_date = models.DateTimeField('date published', auto_now_add=True)
+ rate = models.IntegerField(validators=[MinValueValidator(1), MaxValueValidator(10)], default=1)
+
+ def __repr__(self):
+ return f"Film {self.name}"
+
+ def __str__(self):
+ return f"Film {self.name} | Rate: {self.rate}"
diff --git a/Useful/for_lec23/coolapp/static/css/main.css b/Useful/for_lec23/coolapp/static/css/main.css
new file mode 100644
index 0000000..c1274c3
--- /dev/null
+++ b/Useful/for_lec23/coolapp/static/css/main.css
@@ -0,0 +1,3 @@
+html {
+ background-color: #faebd7; /* Цвет фона */
+}
diff --git a/Useful/for_lec23/coolapp/templates/coolapp/base.html b/Useful/for_lec23/coolapp/templates/coolapp/base.html
new file mode 100644
index 0000000..26c2ea3
--- /dev/null
+++ b/Useful/for_lec23/coolapp/templates/coolapp/base.html
@@ -0,0 +1,14 @@
+
+
+{% load static %}
+
+
+
+ About films
+
+
+
+ {% block content %}
+ {% endblock %}
+
+
diff --git a/Useful/for_lec23/coolapp/templates/coolapp/films.html b/Useful/for_lec23/coolapp/templates/coolapp/films.html
new file mode 100644
index 0000000..f017fe7
--- /dev/null
+++ b/Useful/for_lec23/coolapp/templates/coolapp/films.html
@@ -0,0 +1,16 @@
+{% extends 'coolapp/base.html' %}
+{% block content %}
+
+{% for film in films %}
+ {{ film.name }}
+ {{ film.desc }}
+{% if film.pub_date %}
+ Film date - {{ film.pub_date }}!
+{% else %}
+ Film date - Unknown!
+{% endif %}
+ {{ film.rate }}
+{% endfor %}
+
+{% endblock content %}
+
diff --git a/Useful/for_lec23/coolapp/templates/coolapp/index.html b/Useful/for_lec23/coolapp/templates/coolapp/index.html
new file mode 100644
index 0000000..0aa9fcc
--- /dev/null
+++ b/Useful/for_lec23/coolapp/templates/coolapp/index.html
@@ -0,0 +1,10 @@
+
+
+
+
+ Title
+
+
+Hello everybody
+
+
\ No newline at end of file
diff --git a/Useful/for_lec23/coolapp/templates/coolapp/new.html b/Useful/for_lec23/coolapp/templates/coolapp/new.html
new file mode 100644
index 0000000..23495c4
--- /dev/null
+++ b/Useful/for_lec23/coolapp/templates/coolapp/new.html
@@ -0,0 +1,9 @@
+{% extends 'coolapp/base.html' %}
+{% block content %}
+
+New film
+
+
+{% endblock content %}
\ No newline at end of file
diff --git a/Useful/for_lec23/coolapp/tests.py b/Useful/for_lec23/coolapp/tests.py
new file mode 100644
index 0000000..7ce503c
--- /dev/null
+++ b/Useful/for_lec23/coolapp/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/Useful/for_lec23/coolapp/urls.py b/Useful/for_lec23/coolapp/urls.py
new file mode 100644
index 0000000..552ff09
--- /dev/null
+++ b/Useful/for_lec23/coolapp/urls.py
@@ -0,0 +1,10 @@
+from django.urls import path
+from . import views
+
+
+urlpatterns = [
+ path('', views.index, name='index'),
+ path('new/', views.new, name='new'),
+ path('films/', views.films, name='films'),
+ path('/', views.new, name='new'),
+]
diff --git a/Useful/for_lec23/coolapp/views.py b/Useful/for_lec23/coolapp/views.py
new file mode 100644
index 0000000..07d3a83
--- /dev/null
+++ b/Useful/for_lec23/coolapp/views.py
@@ -0,0 +1,28 @@
+from django.shortcuts import render
+from .models import Film
+from .forms import FilmForm
+from django.shortcuts import redirect
+
+
+def index(request):
+ return render(request, 'coolapp/index.html')
+
+
+def films(request):
+ return render(request, 'coolapp/films.html',
+ {'films': Film.objects.all()})
+
+
+def new(request, film_id=None):
+ if request.method == "POST":
+ form = FilmForm(request.POST)
+ if form.is_valid():
+ film = form.save()
+ return redirect(f'/{film.id}', film=film)
+ if film_id:
+ film = Film.objects.get(id=film_id)
+ else:
+ film = Film()
+ return render(request, 'coolapp/new.html',
+ {'form': FilmForm(instance=film)})
+
diff --git a/Useful/for_lec23/coolsite/__init__.py b/Useful/for_lec23/coolsite/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/Useful/for_lec23/coolsite/asgi.py b/Useful/for_lec23/coolsite/asgi.py
new file mode 100644
index 0000000..b371d88
--- /dev/null
+++ b/Useful/for_lec23/coolsite/asgi.py
@@ -0,0 +1,16 @@
+"""
+ASGI config for coolsite project.
+
+It exposes the ASGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/
+"""
+
+import os
+
+from django.core.asgi import get_asgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'coolsite.settings')
+
+application = get_asgi_application()
diff --git a/Useful/for_lec23/coolsite/coolapp/__init__.py b/Useful/for_lec23/coolsite/coolapp/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/Useful/for_lec23/coolsite/coolapp/admin.py b/Useful/for_lec23/coolsite/coolapp/admin.py
new file mode 100644
index 0000000..a048687
--- /dev/null
+++ b/Useful/for_lec23/coolsite/coolapp/admin.py
@@ -0,0 +1,5 @@
+from django.contrib import admin
+from .models import Film
+
+admin.site.register(Film)
+
diff --git a/Useful/for_lec23/coolsite/coolapp/apps.py b/Useful/for_lec23/coolsite/coolapp/apps.py
new file mode 100644
index 0000000..a37f808
--- /dev/null
+++ b/Useful/for_lec23/coolsite/coolapp/apps.py
@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class CoolappConfig(AppConfig):
+ default_auto_field = 'django.db.models.BigAutoField'
+ name = 'coolapp'
diff --git a/Useful/for_lec23/coolsite/coolapp/forms.py b/Useful/for_lec23/coolsite/coolapp/forms.py
new file mode 100644
index 0000000..0f2b48c
--- /dev/null
+++ b/Useful/for_lec23/coolsite/coolapp/forms.py
@@ -0,0 +1,8 @@
+from django import forms
+from .models import Film
+
+
+class FilmForm(forms.ModelForm):
+ class Meta:
+ model = Film
+ fields = ('name', 'desc', 'rate')
diff --git a/Useful/for_lec23/coolsite/coolapp/migrations/0001_initial.py b/Useful/for_lec23/coolsite/coolapp/migrations/0001_initial.py
new file mode 100644
index 0000000..656d6cc
--- /dev/null
+++ b/Useful/for_lec23/coolsite/coolapp/migrations/0001_initial.py
@@ -0,0 +1,23 @@
+# Generated by Django 3.2.8 on 2021-10-14 16:41
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Film',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=200)),
+ ('desc', models.TextField()),
+ ('pub_date', models.DateTimeField(auto_now_add=True, verbose_name='date published')),
+ ],
+ ),
+ ]
diff --git a/Useful/for_lec23/coolsite/coolapp/migrations/0002_film_rate.py b/Useful/for_lec23/coolsite/coolapp/migrations/0002_film_rate.py
new file mode 100644
index 0000000..e60042b
--- /dev/null
+++ b/Useful/for_lec23/coolsite/coolapp/migrations/0002_film_rate.py
@@ -0,0 +1,19 @@
+# Generated by Django 3.2.8 on 2021-10-14 17:10
+
+import django.core.validators
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('coolapp', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='film',
+ name='rate',
+ field=models.IntegerField(default=1, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(10)]),
+ ),
+ ]
diff --git a/Useful/for_lec23/coolsite/coolapp/migrations/__init__.py b/Useful/for_lec23/coolsite/coolapp/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/Useful/for_lec23/coolsite/coolapp/models.py b/Useful/for_lec23/coolsite/coolapp/models.py
new file mode 100644
index 0000000..4361da5
--- /dev/null
+++ b/Useful/for_lec23/coolsite/coolapp/models.py
@@ -0,0 +1,12 @@
+from django.db import models
+from django.core.validators import MaxValueValidator, MinValueValidator
+
+
+class Film(models.Model):
+ name = models.CharField(max_length=200)
+ desc = models.TextField()
+ pub_date = models.DateTimeField('date published', auto_now_add=True)
+ rate = models.IntegerField(validators=[MinValueValidator(1), MaxValueValidator(10)], default=1)
+
+ def __str__(self):
+ return f"{self.name}: {self.desc}"
diff --git a/Useful/for_lec23/coolsite/coolapp/static/css/main.css b/Useful/for_lec23/coolsite/coolapp/static/css/main.css
new file mode 100644
index 0000000..cd4b7eb
--- /dev/null
+++ b/Useful/for_lec23/coolsite/coolapp/static/css/main.css
@@ -0,0 +1,3 @@
+html {
+ background-color: #ffff00; /* Цвет фона */
+}
diff --git a/Useful/for_lec23/coolsite/coolapp/templates/coolapp/base.html b/Useful/for_lec23/coolsite/coolapp/templates/coolapp/base.html
new file mode 100644
index 0000000..433b602
--- /dev/null
+++ b/Useful/for_lec23/coolsite/coolapp/templates/coolapp/base.html
@@ -0,0 +1,15 @@
+
+{% load static %}
+
+
+
+
+ About films
+
+
+
+ Table
+ {% block content %}
+ {% endblock %}
+
+
diff --git a/Useful/for_lec23/coolsite/coolapp/templates/coolapp/films.html b/Useful/for_lec23/coolsite/coolapp/templates/coolapp/films.html
new file mode 100644
index 0000000..eca3715
--- /dev/null
+++ b/Useful/for_lec23/coolsite/coolapp/templates/coolapp/films.html
@@ -0,0 +1,14 @@
+{% extends 'coolapp/base.html' %}
+{% block content %}
+
+{% for film in films %}
+ {{ film.name }}
+ {{ film.desc }}
+{% if film.pub_date %}
+ Film date - {{ film.pub_date }}!
+{% else %}
+ Film date - Unknown!
+{% endif %}
+ {{ film.rate }}
+{% endfor %}
+{% endblock content %}
diff --git a/Useful/for_lec23/coolsite/coolapp/templates/coolapp/index.html b/Useful/for_lec23/coolsite/coolapp/templates/coolapp/index.html
new file mode 100644
index 0000000..ae3ae8c
--- /dev/null
+++ b/Useful/for_lec23/coolsite/coolapp/templates/coolapp/index.html
@@ -0,0 +1,4 @@
+{% extends 'coolapp/base.html' %}
+{% block content %}
+Hello everybody!
+{% endblock content %}
\ No newline at end of file
diff --git a/Useful/for_lec23/coolsite/coolapp/templates/coolapp/new.html b/Useful/for_lec23/coolsite/coolapp/templates/coolapp/new.html
new file mode 100644
index 0000000..2d55c2b
--- /dev/null
+++ b/Useful/for_lec23/coolsite/coolapp/templates/coolapp/new.html
@@ -0,0 +1,7 @@
+{% extends 'coolapp/base.html' %}
+{% block content %}
+New film
+
+{% endblock content %}
\ No newline at end of file
diff --git a/Useful/for_lec23/coolsite/coolapp/tests.py b/Useful/for_lec23/coolsite/coolapp/tests.py
new file mode 100644
index 0000000..7ce503c
--- /dev/null
+++ b/Useful/for_lec23/coolsite/coolapp/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/Useful/for_lec23/coolsite/coolapp/urls.py b/Useful/for_lec23/coolsite/coolapp/urls.py
new file mode 100644
index 0000000..cde630f
--- /dev/null
+++ b/Useful/for_lec23/coolsite/coolapp/urls.py
@@ -0,0 +1,11 @@
+from django.urls import path
+from . import views
+
+
+urlpatterns = [
+ path('', views.index, name='index'),
+ path('bye', views.index2, name='index2'),
+ path('films/', views.films, name='films'),
+ path('new/', views.new, name='new'),
+ path('/', views.new, name='new'),
+]
diff --git a/Useful/for_lec23/coolsite/coolapp/views.py b/Useful/for_lec23/coolsite/coolapp/views.py
new file mode 100644
index 0000000..1aa9c06
--- /dev/null
+++ b/Useful/for_lec23/coolsite/coolapp/views.py
@@ -0,0 +1,30 @@
+from django.shortcuts import render
+from django.http import HttpResponse
+from django.shortcuts import redirect
+from .models import Film
+from .forms import FilmForm
+
+
+def index(request):
+ return render(request, 'coolapp/index.html')
+
+
+def index2(request):
+ return HttpResponse("Пока!!!")
+
+
+def films(request):
+ return render(request, 'coolapp/films.html', {'films': Film.objects.all()})
+
+
+def new(request, film_id=None):
+ if request.method == "POST":
+ form = FilmForm(request.POST)
+ if form.is_valid():
+ film = form.save()
+ return redirect(f'/{film.id}', film=film) # http://127.0.0.1:8000/1
+ if film_id:
+ film = Film.objects.get(id=film_id)
+ else:
+ film = Film()
+ return render(request, 'coolapp/new.html', {'form': FilmForm(instance=film)})
diff --git a/Useful/for_lec23/coolsite/coolsite/__init__.py b/Useful/for_lec23/coolsite/coolsite/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/Useful/for_lec23/coolsite/coolsite/asgi.py b/Useful/for_lec23/coolsite/coolsite/asgi.py
new file mode 100644
index 0000000..b371d88
--- /dev/null
+++ b/Useful/for_lec23/coolsite/coolsite/asgi.py
@@ -0,0 +1,16 @@
+"""
+ASGI config for coolsite project.
+
+It exposes the ASGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/
+"""
+
+import os
+
+from django.core.asgi import get_asgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'coolsite.settings')
+
+application = get_asgi_application()
diff --git a/Useful/for_lec23/coolsite/coolsite/settings.py b/Useful/for_lec23/coolsite/coolsite/settings.py
new file mode 100644
index 0000000..847315c
--- /dev/null
+++ b/Useful/for_lec23/coolsite/coolsite/settings.py
@@ -0,0 +1,128 @@
+"""
+Django settings for coolsite project.
+
+Generated by 'django-admin startproject' using Django 3.2.8.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/3.2/topics/settings/
+
+For the full list of settings and their values, see
+https://docs.djangoproject.com/en/3.2/ref/settings/
+"""
+
+from pathlib import Path
+import os
+
+# Build paths inside the project like this: BASE_DIR / 'subdir'.
+BASE_DIR = Path(__file__).resolve().parent.parent
+
+
+# Quick-start development settings - unsuitable for production
+# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
+
+# SECURITY WARNING: keep the secret key used in production secret!
+SECRET_KEY = 'django-insecure-3!gypw2h-deir9=mvsoyofzqvv&5%c^-iopf9qw$-nipu#7@vm'
+
+# SECURITY WARNING: don't run with debug turned on in production!
+DEBUG = True
+
+ALLOWED_HOSTS = []
+
+
+# Application definition
+
+INSTALLED_APPS = [
+ 'django.contrib.admin',
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.staticfiles',
+ 'coolapp'
+]
+
+MIDDLEWARE = [
+ 'django.middleware.security.SecurityMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.common.CommonMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+ 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
+
+ROOT_URLCONF = 'coolsite.urls'
+
+TEMPLATES = [
+ {
+ 'BACKEND': 'django.template.backends.django.DjangoTemplates',
+ 'DIRS': [],
+ 'APP_DIRS': True,
+ 'OPTIONS': {
+ 'context_processors': [
+ 'django.template.context_processors.debug',
+ 'django.template.context_processors.request',
+ 'django.contrib.auth.context_processors.auth',
+ 'django.contrib.messages.context_processors.messages',
+ ],
+ },
+ },
+]
+
+WSGI_APPLICATION = 'coolsite.wsgi.application'
+
+
+# Database
+# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
+
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.sqlite3',
+ 'NAME': BASE_DIR / 'db.sqlite3',
+ }
+}
+
+
+# Password validation
+# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [
+ {
+ 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+ },
+]
+
+
+# Internationalization
+# https://docs.djangoproject.com/en/3.2/topics/i18n/
+
+LANGUAGE_CODE = 'en-us'
+
+TIME_ZONE = 'Europe/Moscow'
+
+USE_I18N = True
+
+USE_L10N = True
+
+USE_TZ = True
+
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/3.2/howto/static-files/
+
+STATIC_URL = '/static/'
+STATIC_ROOT = os.path.join(BASE_DIR, 'static')
+
+# Default primary key field type
+# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
+
+DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
diff --git a/Useful/for_lec23/coolsite/coolsite/urls.py b/Useful/for_lec23/coolsite/coolsite/urls.py
new file mode 100644
index 0000000..0f3453d
--- /dev/null
+++ b/Useful/for_lec23/coolsite/coolsite/urls.py
@@ -0,0 +1,27 @@
+"""coolsite URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+ https://docs.djangoproject.com/en/3.2/topics/http/urls/
+Examples:
+Function views
+ 1. Add an import: from my_app import views
+ 2. Add a URL to urlpatterns: path('', views.home, name='home')
+Class-based views
+ 1. Add an import: from other_app.views import Home
+ 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
+Including another URLconf
+ 1. Import the include() function: from django.urls import include, path
+ 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
+"""
+from django.contrib import admin
+from django.urls import path, include
+
+
+urlpatterns = [
+ path('admin/', admin.site.urls),
+ path('coolapp/', include('coolapp.urls')), # path('coolapp/', coolapp.views.index)
+ path('', include('coolapp.urls')) # path('coolapp/', bye', coolapp.views.index2)
+]
+
+
+
diff --git a/Useful/for_lec23/coolsite/coolsite/wsgi.py b/Useful/for_lec23/coolsite/coolsite/wsgi.py
new file mode 100644
index 0000000..f938dff
--- /dev/null
+++ b/Useful/for_lec23/coolsite/coolsite/wsgi.py
@@ -0,0 +1,16 @@
+"""
+WSGI config for coolsite project.
+
+It exposes the WSGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/
+"""
+
+import os
+
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'coolsite.settings')
+
+application = get_wsgi_application()
diff --git a/Useful/for_lec23/coolsite/db.sqlite3 b/Useful/for_lec23/coolsite/db.sqlite3
new file mode 100644
index 0000000..f4b3ece
Binary files /dev/null and b/Useful/for_lec23/coolsite/db.sqlite3 differ
diff --git a/Useful/for_lec23/coolsite/manage.py b/Useful/for_lec23/coolsite/manage.py
new file mode 100644
index 0000000..445748c
--- /dev/null
+++ b/Useful/for_lec23/coolsite/manage.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+"""Django's command-line utility for administrative tasks."""
+import os
+import sys
+
+
+def main():
+ """Run administrative tasks."""
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'coolsite.settings')
+ try:
+ from django.core.management import execute_from_command_line
+ except ImportError as exc:
+ raise ImportError(
+ "Couldn't import Django. Are you sure it's installed and "
+ "available on your PYTHONPATH environment variable? Did you "
+ "forget to activate a virtual environment?"
+ ) from exc
+ execute_from_command_line(sys.argv)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/Useful/for_lec23/coolsite/settings.py b/Useful/for_lec23/coolsite/settings.py
new file mode 100644
index 0000000..39453ad
--- /dev/null
+++ b/Useful/for_lec23/coolsite/settings.py
@@ -0,0 +1,128 @@
+"""
+Django settings for coolsite project.
+
+Generated by 'django-admin startproject' using Django 3.2.8.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/3.2/topics/settings/
+
+For the full list of settings and their values, see
+https://docs.djangoproject.com/en/3.2/ref/settings/
+"""
+import os
+from pathlib import Path
+
+# Build paths inside the project like this: BASE_DIR / 'subdir'.
+BASE_DIR = Path(__file__).resolve().parent.parent
+
+
+# Quick-start development settings - unsuitable for production
+# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
+
+# SECURITY WARNING: keep the secret key used in production secret!
+SECRET_KEY = 'django-insecure-0&&x9i1z4cod!&t7k9_m1@ypc)i_1qn4qibbkk7c2r$8s=@isd'
+
+# SECURITY WARNING: don't run with debug turned on in production!
+DEBUG = True
+
+ALLOWED_HOSTS = []
+
+
+# Application definition
+
+INSTALLED_APPS = [
+ 'django.contrib.admin',
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.staticfiles',
+ 'coolapp'
+]
+
+MIDDLEWARE = [
+ 'django.middleware.security.SecurityMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.common.CommonMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+ 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
+
+ROOT_URLCONF = 'coolsite.urls'
+
+TEMPLATES = [
+ {
+ 'BACKEND': 'django.template.backends.django.DjangoTemplates',
+ 'DIRS': [],
+ 'APP_DIRS': True,
+ 'OPTIONS': {
+ 'context_processors': [
+ 'django.template.context_processors.debug',
+ 'django.template.context_processors.request',
+ 'django.contrib.auth.context_processors.auth',
+ 'django.contrib.messages.context_processors.messages',
+ ],
+ },
+ },
+]
+
+WSGI_APPLICATION = 'coolsite.wsgi.application'
+
+
+# Database
+# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
+
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.sqlite3',
+ 'NAME': BASE_DIR / 'db.sqlite3',
+ }
+}
+
+
+# Password validation
+# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [
+ {
+ 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+ },
+]
+
+
+# Internationalization
+# https://docs.djangoproject.com/en/3.2/topics/i18n/
+
+LANGUAGE_CODE = 'en-us'
+
+TIME_ZONE = 'Europe/Moscow'
+
+USE_I18N = True
+
+USE_L10N = True
+
+USE_TZ = True
+
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/3.2/howto/static-files/
+
+STATIC_URL = '/static/'
+STATIC_ROOT = os.path.join(BASE_DIR, 'static')
+
+
+# Default primary key field type
+# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
+
+DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
diff --git a/Useful/for_lec23/coolsite/urls.py b/Useful/for_lec23/coolsite/urls.py
new file mode 100644
index 0000000..71bb970
--- /dev/null
+++ b/Useful/for_lec23/coolsite/urls.py
@@ -0,0 +1,27 @@
+"""coolsite URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+ https://docs.djangoproject.com/en/3.2/topics/http/urls/
+Examples:
+Function views
+ 1. Add an import: from my_app import views
+ 2. Add a URL to urlpatterns: path('', views.home, name='home')
+Class-based views
+ 1. Add an import: from other_app.views import Home
+ 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
+Including another URLconf
+ 1. Import the include() function: from django.urls import include, path
+ 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
+"""
+from django.contrib import admin
+from django.urls import path
+from django.conf.urls import include
+
+
+urlpatterns = [
+ path('admin/', admin.site.urls),
+ path('coolapp/', include('coolapp.urls')),
+ path('', include('coolapp.urls')),
+ # path('coolapp/', views.index, name='index'),
+ # path('coolapp/index', views.index, name='index'),
+]
diff --git a/Useful/for_lec23/coolsite/wsgi.py b/Useful/for_lec23/coolsite/wsgi.py
new file mode 100644
index 0000000..f938dff
--- /dev/null
+++ b/Useful/for_lec23/coolsite/wsgi.py
@@ -0,0 +1,16 @@
+"""
+WSGI config for coolsite project.
+
+It exposes the WSGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/
+"""
+
+import os
+
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'coolsite.settings')
+
+application = get_wsgi_application()
diff --git a/Useful/for_lec23/db.sqlite3 b/Useful/for_lec23/db.sqlite3
new file mode 100644
index 0000000..a235376
Binary files /dev/null and b/Useful/for_lec23/db.sqlite3 differ
diff --git a/Useful/for_lec23/example_tests/tcp_server.py b/Useful/for_lec23/example_tests/tcp_server.py
new file mode 100644
index 0000000..741c29a
--- /dev/null
+++ b/Useful/for_lec23/example_tests/tcp_server.py
@@ -0,0 +1,54 @@
+"""# Написать Unit-тесты для TCP-сервера (код см. далее),
+# использовать mock, чтобы эмулировать действия клиента и создание потоков,
+# получить code coverage репорт в html формате."""
+
+import threading
+import socket
+
+
+class ClientThread(threading.Thread):
+ def __init__(self, conn, addr):
+ super().__init__()
+ self._connection = conn
+ self._address = addr
+
+ def run(self):
+ print(f'Connection from address {self._address}')
+ data = self._connection.recv(1024)
+ print(f'Received {data.decode()}')
+ self._connection.send(data)
+ self._connection.close()
+ print(f'Closed connection from {self._address}')
+
+
+class TcpServer:
+ def __init__(self, host, port):
+ self.host = host
+ self.port = port
+ self._socket = None
+ self._runnning = False
+
+ def run(self):
+ self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ self._socket.bind((self.host, self.port))
+ self._socket.listen(5)
+ self._runnning = True
+ print('Server is up')
+ while self._runnning:
+ conn, addr = self._socket.accept()
+ ClientThread(conn, addr).start()
+
+ def stop(self):
+ self._runnning = False
+ self._socket.close()
+ print('Server is down')
+
+
+if __name__ == '__main__':
+ srv = TcpServer(host='127.0.0.1', port=5555)
+ try:
+ srv.run()
+ except KeyboardInterrupt:
+ srv.stop()
+
\ No newline at end of file
diff --git a/Useful/for_lec23/example_tests/test_tcp_server.py b/Useful/for_lec23/example_tests/test_tcp_server.py
new file mode 100644
index 0000000..d18e2d8
--- /dev/null
+++ b/Useful/for_lec23/example_tests/test_tcp_server.py
@@ -0,0 +1,48 @@
+"""Написать Unit-тесты для TCP-сервера (код см. далее),
+использовать mock, чтобы эмулировать действия клиента и создание потоков,
+получить code coverage репорт в html формате."""
+
+import tcp_server
+import unittest
+from unittest import mock
+from itertools import tee
+
+
+class TestTcpServer(unittest.TestCase):
+ def test_tcpserver(self):
+ # Создаём объект тестируемого класса.
+ server = tcp_server.TcpServer(host='localhost', port=1234)
+
+ # Заменяем зависимости на mock-объекты
+ tcp_server.socket = mock.Mock()
+ tcp_server.ClientThread = mock.Mock()
+
+ # Настраиваем поведение mock-объекта socket.socket
+ socket_mock = mock.Mock()
+ hosts = [('ip1', 'port1'), ('ip2', 'port2'), ('ip3', 'port3'), StopIteration]
+ socket_mock.accept.side_effect = hosts
+ tcp_server.socket.socket.return_value = socket_mock
+
+ # Настраиваем поведение mock-объекта tcp_server.ClientThread
+ def thread_start():
+ ip, port = hosts[socket_mock.accept.call_count - 1]
+ print(f"New client connect - {ip}:{port}")
+ client_mock = mock.Mock()
+ client_mock.start.side_effect = thread_start
+ tcp_server.ClientThread.return_value = client_mock
+
+ # Тестируем и выполняем проверки
+ try:
+ self.assertFalse(server._runnning)
+ server.run()
+ self.assertTrue(server._runnning)
+ except StopIteration:
+ server.stop()
+ self.assertFalse(server._runnning)
+
+ assert socket_mock.accept.call_count == 4
+ assert client_mock.start.call_count == 3
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/Useful/for_lec23/manage.py b/Useful/for_lec23/manage.py
new file mode 100644
index 0000000..445748c
--- /dev/null
+++ b/Useful/for_lec23/manage.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+"""Django's command-line utility for administrative tasks."""
+import os
+import sys
+
+
+def main():
+ """Run administrative tasks."""
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'coolsite.settings')
+ try:
+ from django.core.management import execute_from_command_line
+ except ImportError as exc:
+ raise ImportError(
+ "Couldn't import Django. Are you sure it's installed and "
+ "available on your PYTHONPATH environment variable? Did you "
+ "forget to activate a virtual environment?"
+ ) from exc
+ execute_from_command_line(sys.argv)
+
+
+if __name__ == '__main__':
+ main()