Модуль 3
84,00ч

Создание Web-сервисов на Python

Целью изучения модуля «Создание Web-сервисов на Python» является формирование компетенций слушателей в области создания и управления web-сервисами на основе Python, работы с различными инструментами сбора данных.

Задачи Модуля 3:
1) сформировать умение работать с библиотекой requests;
2) сформировать умение работать с регулярными выражениями из Python, выполнять сложный поиск и замену при помощи регулярных выражений;
3) сформировать умение извлекать и изменять данные при помощи модуля Beautiful Soup, использовать API для получения данных со сторонних сайтов;
4) сформировать умение создавать и изменять базы данных и таблицы в MySQL, получать данные из баз и таблиц в MySQL;
5) сформировать умение создавать приложение на Django, работать с Django-шаблонизатором, работать с базой данных при помощи Django ORM;
6) сформировать умение отправлять данные из браузера, валидировать данные на клиентской стороне, валидировать данные на серверной стороне, проводить аутентификацию и авторизацию при помощи Django;
7) сформировать умение создавать чат-бота на базе Telegram, работать с системой Git, раскладывать проект на облачный хостинг Heroku.
Часов в программе
20,00 часов
лекции
22,00 часа
практика
30,00 часов
самостоятельная
12,00 часов
промежуточная аттестация
84,00 часа
всего
Материально-технические условия реализации программы:
Вид занятий: Лекция
Требуемое ПО:
Zoom
Браузер Chrome
Microsoft Visual Studio 2010 или выше
Вид занятий: Практическая работа
Требуемое ПО:
Zoom
Браузер Chrome
Microsoft Visual Studio 2010 или выше
Вид занятий: Промежуточная (итоговая) аттестация
Требуемое ПО:
Zoom
Браузер Chrome
Microsoft Visual Studio 2010 или выше
Информационные ресуры
Документация Postgres про сравнение строк - https://postgrespro.ru/docs/postgrespro/9.5/functions-matching
Документация Postgres про другие функции работы со строками - https://postgrespro.ru/docs/postgrespro/9.5/functions-string
Тестер регулярных выражений - https://www.regextester.com
Интерактивный учебник по SQL -http://www.sql-tutorial.ru/ru/content.html
Введение в анализ данных с помощью Pandas - https://habr.com/ru/post/196980/
Начало работы с Power BI - https://docs.microsoft.com/ru-ru/power-bi/fundamentals/desktop-getting-started
Образовательные ресуры
https://www.coursera.org/learn/python-for-web/home/welcome

Учебно-методические материалы

Методы, формы и технологии

Методы обучения:
- лекция;
- практическая работа под руководством учителя;
- самостоятельная практическая работа;
- изучение литературы по теме.
Методы контроля:
- выполнение практических занятий по темам лекций;
- выполнение итогового задания.
Формы организации учебных занятий:
- лекция;
- вебинар с элементами практической работы и разбора теоретического материала.
Формы организации учебной деятельности:
- групповая работа;
- индивидуальная работа.
Дистанционные образовательные технологии:
- использование образовательных интернет-ресурсов;
- использование ресурсов, созданных преподавателем (ноутбуки для решения задач по программированию);
- WEB-консультации и другие.

Методические разработки

Задание для промежуточной (итоговой) аттестации
Проект
Проект предполагает очную защиту перед экзаменационной комиссией. Перед комиссией студент готовит материалы согласно заданию и направляет экзаменационной комиссии не менее чем за 2 дня до назначенной даты экзамена
Вашей задачей будет реализовать на Django сервер управления умным домом, имеющий web-интерфейс для настройки и ручного управления, который будет производить периодический опрос датчиков и осуществлять автоматическую реакцию в случае определенных ситуаций, используя API контроллера умного дома
Авторы курса сделали виртуальные контроллер, датчики и устройства, которыми он управляет. Зайдя на сайт и зарегистрировавшись, вы получите уникальный ключ (KEY), который нужно будет использовать при работе с API контроллера. Используя этот же ключ можно получить изображение с “виртуальной камеры” умного дома, на которой будет наглядно видно, какие устройства работают или почему сработал тот или иной датчик, и вручную управлять устройствами. Документацию по API можно посмотреть на этом же сайте.



Устройства, подключенные к контроллеру, доступны на запись (обычно true - включить/открыть, false - выключить/закрыть, но бывают варианты). И датчики и устройства доступны на чтение. Устройства, при чтении с них, работают как датчики и возвращают свое состояние, которое может отличаться от записанного.
Устройства (запись):
air_conditioner – Кондиционер (true – вкл, false – выкл). При включении постепенно понижает температуру в спальне, пока она не достигнет 16 градусов и сильнее охладить уже не может.
bedroom_light – Лампа в спальне (true – вкл, false – выкл).
bathroom_light – Лампа в ванной (true – вкл, false – выкл).
curtains – Занавески string (“open” – открыть, “close” – закрыть).
boiler – Бойлер (true – вкл, false – выкл). При включении постепенно повышает температуру воды, пока она не достигнет 90 градусов. Для работы должен быть открыт входной кран холодной воды.
cold_water – Входной кран холодной воды (true – открыть, false – закрыть). Позволяет открыть/перекрыть подачу холодной воды в квартиру
hot_water – Входной кран горячей воды (true – открыть, false – закрыть).
washing_machine – Стиральная машина string (“on” – вкл, “off” – выкл). При включении начинает стирать, потом самостоятельно отключается. Может сломаться и протечь.
Датчики (чтение):
air_conditioner – Кондиционер. (true – вкл, false – выкл).
bedroom_temperature – Температура в спальне. Int (0 – 80).
bedroom_light – Лампа в спальне. (true – вкл, false – выкл).
smoke_detector – Датчик задымления на потолке. (true – задымление, false – нет).
bedroom_presence – Датчик присутствия в спальне. (true – есть человек, false – нет).
bedroom_motion – Датчик движения в спальне. (true – есть движение, false – нет).
curtains – Занавески. string (“open” – открыты, “close” – закрыты, “slightly_open” – приоткрыты вручную).
outdoor_light – Датчик освещенности за окном (0 – 100).
boiler – Бойлер. (true – вкл, false – выкл).
boiler_temperature – Температура горячей воды бойлере. Int (0 – 100 / null). Если перекрыта холодная вода, то воды в бойлере нет, и датчик возвращает null.
cold_water – Входной кран холодной воды. (true – открыт, false – закрыт).
hot_water – Входной кран горячей воды. (true – открыт, false – закрыт).
bathroom_light – Лампа в ванной. (true – вкл, false – выкл).
bathroom_presence – Датчик присутствия в ванной. (true – есть человек, false – нет).
bathroom_motion – Датчик движения в ванной. (true – есть движение, false – нет)
washing_machine – Стиральная машина. string (“on” – вкл, “off” – выкл, “broken” – сломана).
leak_detector – Датчик протечки воды (true – протечка, false – сухо).
Приложение студента
Используя приложенный к этому заданию “скелет” приложения на Django (файл student.zip), вам нужно реализовать свой интерфейс управления умным домом.


По адресу / должна открываться веб-форма, со следующими контролами:
bedroom_target_temperature – input type=number, желаемая температура в спальне, запоминать настройку в базе, текущую настройку из базы выводить на форму. Допустимое значение от 16 до 50, default: 21
hot_water_target_temperature – input type=number, желаемая температура горячей воды в доме, запоминать настройку в базе, текущую настройку из базы выводить на форму. Допустимое значение от 24 до 90, default: 80
bedroom_light – checkbox, включает/выключает свет в спальне, синхронизировать значение с контроллером
bathroom_light – checkbox, включает/выключает свет в ванной, синхронизировать значение с контроллером
Там же отображать текущие значения всех датчиков, прочитанные из контроллера. Для рендеринга шаблона прочитанные из контроллера значения должны быть в словаре в context.data.
Реализовать автоматически опрос контроллера в фоне каждую секунду (django celery) и реакцию на некоторые события.
Реакция на события:
Если есть протечка воды (leak_detector=true), закрыть холодную (cold_water=false) и горячую (hot_water=false) воду и отослать письмо в момент обнаружения.
Если холодная вода (cold_water) закрыта, немедленно выключить бойлер (boiler) и стиральную машину (washing_machine) и ни при каких условиях не включать их, пока холодная вода не будет снова открыта.
Если горячая вода имеет температуру (boiler_temperature) меньше чем hot_water_target_temperature - 10%, нужно включить бойлер (boiler), и ждать пока она не достигнет температуры hot_water_target_temperature + 10%, после чего в целях экономии энергии бойлер нужно отключить
Если шторы частично открыты (curtains == “slightly_open”), то они находятся на ручном управлении - это значит их состояние нельзя изменять автоматически ни при каких условиях.
Если на улице (outdoor_light) темнее 50, открыть шторы (curtains), но только если не горит лампа в спальне (bedroom_light). Если на улице (outdoor_light) светлее 50, или горит свет в спальне (bedroom_light), закрыть шторы. Кроме случаев когда они на ручном управлении
Если обнаружен дым (smoke_detector), немедленно выключить следующие приборы [air_conditioner, bedroom_light, bathroom_light, boiler, washing_machine], и ни при каких условиях не включать их, пока дым не исчезнет.
Если температура в спальне (bedroom_temperature) поднялась выше bedroom_target_temperature + 10% - включить кондиционер (air_conditioner), и ждать пока температура не опустится ниже bedroom_target_temperature - 10%, после чего кондиционер отключить.
Опрос контроллера и отправка ему ответа должны происходить внутри функции core.tasks.smart_home_manager. Эта функция должна вызываться периодически с интервалом в 5 секунд, например, с помощью celery. В начала своей работы функция запрашивает данные из контроллера, используя requests.get в API, затем анализирует настройки пользователя по желаемой температуре из БД, и текущую ситуацию, и в конце, если требуется коррекция, делает requests.post в API с командами для контроллера, если необходимо отправить письмо, то отправляет его.
Для отсылки писем нужно использовать send_mail из django.core.mail, а в settings нужно задать настройки EMAIL_HOST, EMAIL_PORT и другие EMAIL_*, так чтобы во время разработки вы отправляли письма через какую-нибудь почтовую систему и могли проверить их работу. Во время проверки задания на сервере эти настройки будут переопределены.
Для сохранения настроек в базе, нужно использовать модель Settings (name, value), заготовка для которой уже есть приложенных исходниках.
Веб-форма для настройки и управления умным домом должна открываться в корне сайта, содержать 4 input’а c именами (name=...): bedroom_target_temperature, hot_water_target_temperature, bedroom_light, bathroom_light.
В settings нужно добавить пременные SMART_HOME_API_URL и SMART_HOME_ACCESS_TOKEN и задать их значения, затем использовать их для взаимодействия с умным домом. Еще можно добавить EMAIL_RECEPIENT, в котором задать получателя писем от системы.
В приложенных исходниках есть несколько тестов django tests, которые тестируют некоторые базовые вещи. После того, как реализуете функционал, обазательно запустите manage.py test. На бою мы проверяем задание аналогичным образом через тесты контроллера по урлу /, и ручной вызов core.tasks.smart_home_manager.
Что будем проверять
Во время разработки вы можете в реальном времени наблюдать на сайте за реакцией умного дома на управляющие воздействия вашего приложения, а также руками выставлять показания датчиков для моделирования различных ситуаций.
Во время проверки на сервере интернет будет недоступен, requests.get будет заменен на mock, который будет возвращать приложению разные состояния контроллера (показания датчиков), а тесты будут проверять результат работы в requests.post и факт отправки письма, если оно требуется. Также будет проверяться работа веб-формы. Например если будет передана низкая температура горячей воды, в ответ будет ожидаться команда на включение бойлера, если он выключен. Но если к предыдущему примеру добавить еще и обнаруженную протечку, или пожар, то команда на включение бойлера подаваться не должна, а наоборот бойлеру должна подаваться команда на выключение, если он включен. Все возможные пограничные ситуации описаны в разделе “Реакция на события”. В случае если внешний сервер вернул ошибку или не отвечает нужно вернуть страницу с ошибкой со статус кодом 502.
Вам необходимо реализовать весь проект основываясь на скелете приложенном к этому заданию. Весь код должен находиться в приложении core. После реализации заархивируйте содержимое папки core в zip архив и отправьте на проверку.
Реализовывать валидацию данных можно с использованием библиотек
marshmallow
jsonschema
или с помощью класса Forms. Также в проекте установлен фреймворк для тестирования py.test https://docs.pytest.org/en/latest/

Материалы курса

Примеры заданий по модулю 3
Практические задания

Тема 1. Общее представление о WEB
В этом задании вы научитесь работать с библиотекой requests (https://requests.kennethreitz.org/en/master/), а также научитесь работать с API сервиса VK и его документаций, что является достаточно частой задачей разработчика.
Необходимо написать клиент к API VK , который будет считать распределение возрастов друзей для указанного пользователя. То есть на вход подается username или user_id пользователя, на выходе получаем список пар (<возраст>, <количество друзей с таким возрастом>), отсортированный по убыванию по второму ключу (количество друзей) и по возрастанию по первому ключу (возраст). Например:

[(26, 8), (21, 6), (22, 6), (40, 2), (19, 1), (20, 1)]

Для выполнения задания необходимо использовать шаблон проекта: https://github.com/alexopryshko/coursera_assignment_tmp
Решение должно быть файлом req/friends.py. В этом файле представлен шаблон функции calc_age, реализацию которой нужно написать.
Для этого вам понадобятся два метода API VK:
1. Метод для получения id пользователя (https://vk.com/dev/users.get). Он необходим, так как на вход может подаваться username пользователя. URL запроса к API VK: https://api.vk.com/method/users.get
2. Метод для получения списка друзей пользователя (https://vk.com/dev/friends.get). URL запроса к API VK: https://api.vk.com/method/friends.get
Для доступа к этим методам вам понадобится “Сервисный ключ доступа”:
https://vk.com/dev/access_token?f=3.%20%D0%A1%D0%B5%D1%80%D0%B2%D0%B8%D1%81%D0%BD%D1%8B%D0%B9%20%D0%BA%D0%BB%D1%8E%D1%87%20%D0%B4%D0%BE%D1%81%D1%82%D1%83%D0%BF%D0%B0
Получение сервисного ключа:
1. Создать новое приложение, перейдя по ссылке https://vk.com/apps?act=manage
2. После создания приложения, перейти в раздел “настройки” и скопировать “Сервисный ключ доступа”.
Если нет возможности получить сервисный ключ, то можно использовать уже созданный:

ACCESS_TOKEN = '17da724517da724517da72458517b8abce117da17da72454d235c274f1a2be5f45ee711'

Для получения id пользователя по username или user_id:
https://api.vk.com/method/users.get?v=5.71&access_token=[token]&user_ids=[user_id]
Для получения списка друзей:
https://api.vk.com/method/friends.get?v=5.71&access_token=[token]&user_id=[user_id]&fields=bdate
При решении задания обратите внимание на несколько моментов.
 В запросе мы используем версию API VK - «5.71».
 В запросе получения списка друзей добавлен ключ fields=bdate. Он необходим для того, чтобы API сразу вернуло пользователей с датами рождения.
 При анализе ответа, полученного методом friends.get, можно заметить, что bdate есть не у всех пользователей и у некоторых в bdate отсутствует год рождения. Поэтому необходимо пропускать этот случай. Примеры возможных значений: "bdate":"6.6", "bdate":"25.8.1993".Для вычисления возраста, необходимо взять текущий год , и вычесть из него год рождения пользователя, полученный из API (без учета месяца и числа).
В своем решении вы можете (если необходимо) определять дополнительные функции и импортировать модули.

Тема 2. Сбор данных со сторонних сайтов
Написать программу, которая найдет в тексте ряд простых арифметических выражений и подсчитает их.
Любое выражение начинается с имени переменной a, b или c, затем может идти + или -, затем идет =, затем может идти имя переменной a, b или c, а затем может идти + или - и целое число. Если в правой части выражения нет переменной, то число может быть без знака + или -
Не бывает названий переменных, кроме a, b или c, и действий, кроме описанных тут. Не бывает пробелов вокруг знаков. В тексте не встречаются некорректные выражения, в которых справа от = нет ни переменной, ни числа. Таким образом, список типов выражений, которые могут встречаться, выглядит примерно так:
a=1, a=+1, a=-1, a=b, a=b+100, a=b-100, b+=10, b+=+10, b+=-10, b+=b, b+=b+100, b+=b-100, c-=101, c-=+101, c-=-101, c-=b, c-=b+101, c-=b-101
Выражения могут встречаться внутри текста, например loremc-=a+10ipsuma-=adb+=10olorsitamet.
В вашу функцию calculate(data, findall) будет передан словарь с начальными значениями переменных a, b и c: data = {"a":1, "b":2, "c": 3} и ссылка на функцию findall, а вы должны вернуть такой же словарь с новыми значениями для a, b и c.
Работает findall() аналогично re.findall(), только у нее всего один параметр – регулярное выражение. А текст, в котором она будет искать выражения, она знает сама (см. приложенный архив, чтобы понять, о чем речь). С помощью findall() нужно как минимум найти все выражения в тексте, а как максимум найти их и разбить на группы так, чтобы было удобно их обработать. Если findall() будет вызвано больше одного раза или если размер списка, который она вернет, будет отличаться от количества выражений в тексте, тест провалится.
Эталонное решение занимает 11 строк, не содержит импортов, не использует eval, а регулярное выражение находит выражения и бьет каждое на четыре группы (некоторые группы для некоторых выражений оказываются пустыми):
1. Имя переменной слева.
2. Знак перед = (если есть).
3. Имя переменной справа (если есть).
4. Число (если есть) со знаком (если есть).
Это позволяет легко (буквально в одну строку) посчитать правую часть, а потом, в зависимости от наличия знака перед =, произвести действие с левой частью. Однако ваш алгоритм может быть другим, требуется только выполнить ограничения на вызов findall() и оставить сигнатуру calculate() неизменной.
Вы должны скачать архив к этому уроку, изучить оба файла и переписать функцию calculate() внутри regexp.py. Для проверки вашего решения локально запустите python test.py. На сервере задание будет проверяться похожим образом, но реализация findall() будет иной (с проверкой всех ограничений), и тестов будет несколько. Решением будет файл regexp.py, который надо загрузить на сервер для проверки.

Тема 3. Beautiful Soup и работа с API
Часть 1:
В этом задании вам необходимо реализовать парсер для сбора статистики со страниц Википедии. Чтобы упростить вашу задачу, необходимые страницы уже скачаны и сохранены на файловой системе в директории wiki/ (Например, страница https://en.wikipedia.org/wiki/Stone_Age сохранена файле wiki/Stone_Age). Архив страниц вы можете скачать ниже:


Парсер реализован в виде функции parse, которая принимает на вход один параметр: path_to_file — путь до файла, содержащий html код страницы википедии. Гарантируется, что такой путь существует. Ваша задача — прочитать файл, пройтись Beautiful Soup по статье, найти её тело (это <div id="bodyContent">) и внутри него подсчитать:
1. Количество картинок (img) с шириной (width) не меньше 200. Например: <img width="200">, но не <img> и не <img width="199">
2. Количество заголовков (h1, h2, h3, h4, h5, h6), первая буква текста внутри которых соответствует заглавной букве E, T или C. Например: <h1>End</h1> или <h5><span>Contents</span></h5>, но не <h1>About</h1> и не <h2>end</h2> и не <h3><span>1</span><span>End</span></h3>
3. Длину максимальной последовательности ссылок, между которыми нет других тегов, открывающихся или закрывающихся. Например: <p><span><a></a></span>, <a></a>, <a></a></p> - тут 2 ссылки подряд, т.к. закрывающийся span прерывает последовательность. <p><a><span></span></a>, <a></a>, <a></a></p> - а тут 3 ссылки подряд, т.к. span находится внутри ссылки, а не между ссылками.
4. Количество списков (ul, ol), не вложенных в другие списки. Например: <ol><li></li></ol>, <ul><li><ol><li></li></ol></li></ul> - два не вложенных списка (и один вложенный)
Результатом работы функции parse будет список четырех чисел, посчитанных по формулам выше. В качестве шаблона для вашего решения используйте следующий код:

from bs4 import BeautifulSoup
import unittest

def parse(path_to_file):
# Поместите ваш код здесь.
# ВАЖНО!!!
# При открытии файла, добавьте в функцию open необязательный параметр
# encoding='utf-8', его отсутствие в коде будет вызвать падение вашего
# решения на грейдере с ошибкой UnicodeDecodeError
return [imgs, headers, linkslen, lists]

class TestParse(unittest.TestCase):
def test_parse(self):
test_cases = (
('wiki/Stone_Age', [13, 10, 12, 40]),
('wiki/Brain', [19, 5, 25, 11]),
('wiki/Artificial_intelligence', [8, 19, 13, 198]),
('wiki/Python_(programming_language)', [2, 5, 17, 41]),
('wiki/Spectrogram', [1, 2, 4, 7]),)

for path, expected in test_cases:
with self.subTest(path=path, expected=expected):
self.assertEqual(parse(path), expected)

if __name__ == '__main__':
unittest.main()

В пункте про последовательность ссылок вы можете ошибиться с результатом, если решите использовать метод find_next(). Обратите внимание, что хотя find_next находит тег, идущий сразу за текущим, этот тег может оказаться вложенным в текущий, а не быть его следующим соседом. Возможно, нужно использовать другой метод или алгоритм.
Так же, не упустите момент, что данные во всех пунктах нужно искать внутри <div id="bodyContent">, а не по всей странице.


Пример работы функции parse:

>>> parse("wiki/Stone_Age")

>>> [13, 10, 12, 40]

Несколько других примеров работы вы можете посмотреть в тестах из шаблона кода выше.
Во время проверки на сервере будут доступны только стандартные модули и bs4, сеть не доступна. Ваше решение будет проверяться, как на наборе страниц из приложенного архива, так и на дополнительном наборе страниц википедии.
"Важное замечание! Не используйте для сбора статистики по странице регулярные выражения. Beautiful Soup в своей работе использует специализированные парсеры, которые позволяют корректно обрабатывать невалидные (содержащие ошибки, например: незакрытый тег) html страницы. Статистика, собранная с помощью модуля re, на таких страницах будет возвращать не верное значение."

Часть 2:
В этом задании продолжаем работать со страницами из wikipedia. Необходимо реализовать механизм сбора статистики по нескольким страницам. Сложность задачи состоит в том, что сначала нужно будет определить страницы, с которых необходимо собирать статистику. В качестве входных данных служат названия двух статей(страниц). Гарантируется, что файлы обеих статей есть в папке wiki и из первой статьи можно попасть в последнюю, переходя по ссылкам только на те статьи, копии которых есть в папке wiki.
Например, на вход подаются страницы: Stone_Age и Python_(programming_language). В статье Stone_Age есть ссылка на Brain, в ней на Artificial_intelligence, а в ней на Python_(programming_language) и это кратчайший путь от Stone_Age до Python_(programming_language). Ваша задача — найти самый короткий путь (гарантируется, что существует только один путь минимальной длины), а затем с помощью функции parse из предыдущего задания собрать статистику по всем статьям в найденном пути.
Результат нужно вернуть в виде словаря, ключами которого являются имена статей, а значениями списки со статистикой. Для нашего примера правильный результат будет:

{ 'Stone_Age': [13, 10, 12, 40],
'Brain': [19, 5, 25, 11],
'Artificial_intelligence': [8, 19, 13, 198],
'Python_(programming_language)': [2, 5, 17, 41]
}

Вам необходимо реализовать две функции: build_bridge и get_statistics

def build_bridge(path, start_page, end_page):
"""возвращает список страниц, по которым можно перейти по ссылкам со start_page на
end_page, начальная и конечная страницы включаются в результирующий список"""

# напишите вашу реализацию логики по вычисления кратчайшего пути здесь

def get_statistics(path, start_page, end_page):
"""собирает статистику со страниц, возвращает словарь, где ключ - название страницы,
значение - список со статистикой страницы"""

# получаем список страниц, с которых необходимо собрать статистику
pages = build_bridge(path, start_page, end_page)
# напишите вашу реализацию логики по сбору статистики здесь

return statistic

Обе функции принимают на вход три параметра:path - путь до директории с сохраненными файлами из wikipedia,start_page - название начальной страницы,end_page - название конечной страницы.
Функция build_bridge вычисляет кратчайший путь и возвращает список страниц в том порядке, в котором происходят переходы. Начальная и конечная страницы включаются в результирующий список. В случае, если название стартовой и конечной страницы совпадают, то результирующий список должен содержать только стартовую страницу. Получить все ссылки на странице можно разными способами, в том числе и с помощью регулярных выражений, например так:

with open(os.path.join(path, page), encoding="utf-8") as file:
links = re.findall(r"(?<=/wiki/)[\w()]+", file.read())

Обратите внимание, что что на страницах wikipedia могут встречаться ссылки на страницы, которых нет в директории wiki, такие ссылки должны игнорироваться.
Пример работы функции build_bridge:

>>> result = build_bridge('wiki/', 'The_New_York_Times', 'Stone_Age')
>>> print(result)
['The_New_York_Times', 'London', 'Woolwich', 'Iron_Age', 'Stone_Age']

Функция get_statistics использует функцию parse и собирает статистику по страницам, найденным с помощью функции build_bridge. Пример работы функции get_statistics:

>>> from pprint import pprint
>>> result = get_statistics('wiki/', 'The_New_York_Times', "Binyamina_train_station_suicide_bombing")
>>> pprint(result)
{'Binyamina_train_station_suicide_bombing': [1, 3, 6, 21],
'Haifa_bus_16_suicide_bombing': [1, 4, 15, 23],
'Second_Intifada': [9, 13, 14, 84],
'The_New_York_Times': [5, 9, 8, 42]}

Вы можете использовать для проверки вашего решения тесты:

# Набор тестов для проверки студентами решений по заданию "Практическое задание
# по Beautiful Soup - 2". По умолчанию файл с решением называется solution.py,
# измените в импорте название модуля solution, если файл с решением имеет другое имя.

import unittest

from solution import build_bridge, get_statistics

STATISTICS = {
'Artificial_intelligence': [8, 19, 13, 198],
'Binyamina_train_station_suicide_bombing': [1, 3, 6, 21],
'Brain': [19, 5, 25, 11],
'Haifa_bus_16_suicide_bombing': [1, 4, 15, 23],
'Hidamari_no_Ki': [1, 5, 5, 35],
'IBM': [13, 3, 21, 33],
'Iron_Age': [4, 8, 15, 22],
'London': [53, 16, 31, 125],
'Mei_Kurokawa': [1, 1, 2, 7],
'PlayStation_3': [13, 5, 14, 148],
'Python_(programming_language)': [2, 5, 17, 41],
'Second_Intifada': [9, 13, 14, 84],
'Stone_Age': [13, 10, 12, 40],
'The_New_York_Times': [5, 9, 8, 42],
'Wild_Arms_(video_game)': [3, 3, 10, 27],
'Woolwich': [15, 9, 19, 38]}

TESTCASES = (
('wiki/', 'Stone_Age', 'Python_(programming_language)',
['Stone_Age', 'Brain', 'Artificial_intelligence', 'Python_(programming_language)']),

('wiki/', 'The_New_York_Times', 'Stone_Age',
['The_New_York_Times', 'London', 'Woolwich', 'Iron_Age', 'Stone_Age']),

('wiki/', 'Artificial_intelligence', 'Mei_Kurokawa',
['Artificial_intelligence', 'IBM', 'PlayStation_3', 'Wild_Arms_(video_game)',
'Hidamari_no_Ki', 'Mei_Kurokawa']),

('wiki/', 'The_New_York_Times', "Binyamina_train_station_suicide_bombing",
['The_New_York_Times', 'Second_Intifada', 'Haifa_bus_16_suicide_bombing',
'Binyamina_train_station_suicide_bombing']),

('wiki/', 'Stone_Age', 'Stone_Age',
['Stone_Age', ]),
)

class TestBuildBrige(unittest.TestCase):
def test_build_bridge(self):
for path, start_page, end_page, expected in TESTCASES:
with self.subTest(path=path,
start_page=start_page,
end_page=end_page,
expected=expected):
result = build_bridge(path, start_page, end_page)
self.assertEqual(result, expected)

class TestGetStatistics(unittest.TestCase):
def test_build_bridge(self):
for path, start_page, end_page, expected in TESTCASES:
with self.subTest(path=path,
start_page=start_page,
end_page=end_page,
expected=expected):
result = get_statistics(path, start_page, end_page)
self.assertEqual(result, {page: STATISTICS[page] for page in expected})

if __name__ == '__main__':
unittest.main()

Ваше решение должно содержать реализацию функций get_statistics, build_bridge и parse. Вы можете дополнительно объявить в коде другие функции, если этого требует логика вашего решения.

Тема 4. Хранение данных. SQL / NoSQL
Подготовка к заданию
Скачайте архив:

Внутри архива mysql_sample.zip находится файл sales.sql, который создаст базу данных test с тремя таблицами и заполнит их данными.
1. Если у вас установлен и запущен mysql 5.7 сервер и клиент, в mysql есть пользователь root с паролем passw, вы можете загрузить приложенный файл командой:

$ mysql -uroot -ppassw < sales.sql

После этого можно подключиться к серверу с помощью MySQL Workbench или команды:

$ mysql -uroot -ppassw test

2. Если у вас установлен docker, то можно установить и запустить mysql 5.7 так:

$ docker run --name mysql57 -p 3306:3306 --rm -de MYSQL_ROOT_PASSWORD=passw mysql:5.7

загрузить приложенный файл:

$ docker exec -i mysql57 mysql -uroot -ppassw < sales.sql

после этого можно подключиться к серверу с помощью MySQL Workbench или команды:

$ docker exec -it mysql57 mysql -uroot -ppassw test

Для корректного отображения русских букв надо выполнить SQL-запрос: set names utf8

Задание
База данных test содержит три таблицы: store(склады), product (товары) и sale(продажи). Если с данными в таблицах store, product все очевидно, то в таблице продаж sale каждая строчка – это данные о продаже товара(product_id), со склада(store_id), в количестве(quantity), на общую сумму(total) и датой продажи(date).

Таблицы заполнены некоторыми тестовыми данными. Вам дается 10 заданий, в каждом необходимо написать ровно один запрос SELECT (возможно, с вложенными подзапросами), чтобы получить требуемую выборку. Далее необходимо сравнить результат, который возвращает ваш SELECT, с тем, что приводится в качестве правильного результата к заданию. Они должны совпадать.
После того как вы напишете все 10 запросов, их нужно добавить в файл results.sql. Строка каждого запроса должна располагаться строго под соответствующим комментарием (с его номером). Как будет происходить проверка решения?Тестовая система содержит аналогичную по структуре базу данных, но с другим набором тестовых данных. Запросы из файла с вашим решением будут выполнены по одному к базе данных, а результаты сверены с эталонными.

Тема 5. Веб интерфейсы с Django и Bootstrap
Часть 1
В этом задании вы научитесь формировать данные для шаблона (контекст), передавать эти параметры в шаблон и изучите базовые основы языка шаблонизации Django.
Для выполнения задания необходимо использовать шаблон проекта https://github.com/alexopryshko/coursera_assignment_tmp
В views.py нужно создать функцию echo(request), которая принимает request и возвращает HttpResponse всегда со статусом 200, а также она должна возвращать эхо, переданных в запросе, параметров и значение заголовка X-Print-Statement. View должна быть доступна по пути /template/echo/
View которые необходимо отредактировать находятся в template/views.py, template - template/templates/echo.html.
Решение должно быть zip архивом. В архиве должно содержаться два файла, которые вы редактировали ранее (обратите внимание, название файлов должно быть именно таким): views.py, echo.html
Примеры запросов и ответов:



Замечание: заголовки находятся в МЕТА параметрах запроса. https://docs.djangoproject.com/en/2.0/ref/request-response/#django.http.HttpRequest.META

Часть 2
В этом задании вы научитесь создавать темплейт фильтры. Фильтры часто используются на практике, например, когда необходимо привести данные к конкретному виду (https://docs.djangoproject.com/en/1.11/ref/templates/builtins/), в частности:
 изменить формат даты
 Привести строку к нижнему регистру
Для выполнения задания необходимо использовать шаблон проекта https://github.com/alexopryshko/coursera_assignment_tmp
Templatetags которые необходимо отредактировать находятся в template/templatetags/extras.py. Решение должно быть файлом extras.py.
2.1. Фильтр inc. Необходимо в файле extras.py создать фильтр “inc“ который принимает 2 аргумента: 1-й - число которое нужно увеличить, 2-й - на сколько нужно увеличить первое число. Пример использования фильтра “inc“ представлен в файле template/templates/filters.html
2.2. Тег division. Необходимо в файле extras.py создать тег “division“ (то есть тег для деления), который принимает 3 параметра: 1-ый - делимое, 2-ой - делитель, 3-ий — флаг определяющий тип возвращаемого значения для результата деления (именованный аргумент to_int). Если переданное значение to_int равно False, необходимо выполнить вещественное деление. Если передано True результат вещественного деления необходимо привести к целому. Значение to_int по-умолчанию — False.
Обратите внимание, что делимое и делитель целые числа, но передаются в тег в формате string.
Пример использования тега “division“ представлен в файле template/templates/filters.html

{% division a b to_int=True %}
{% division a b %}

Часть 3
В этом задании вы научитесь работать с наследовании шаблонов. Наследование повсеместно используется. Например, header и footer у сайта одинаковый, не стоит дублировать код header и footer на каждой странице, имеет смысл сделать базовый шаблон и наследоваться от него.
Для выполнения задания необходимо использовать шаблон проекта https://github.com/alexopryshko/coursera_assignment_tmp
Шаблон, который необходимо отредактировать template/templates/extend.html. Решение должно быть файлом extend.html.
Нужно написать шаблон которые наследуется от template/templates/base.html и переопределяет блоки block_a, block_b. В блоках block_a, block_b должно остаться, то что было у родительского шаблона и дополнительно вывести параметр “a” и параметр “b”
Тема 6. Работа с данными пользователя
В этом задании вам требуется отправить POST запрос на следующий https://datasend.webpython.graders.eldf.ru/submissions/1/
В запросе должен содержаться заголовок Authorization со способом аутентификации Basic логином alladin и паролем opensesame закодированными в base64.

Authorization: Basic YWxsYWRpbjpvcGVuc2VzYW1l

Запрос можно отправить любым удобным для вас способом, но мы рекомендуем использовать библиотеку requests, так как она понадобится вам при выполнении последующих заданий.
В ответе на запрос вы получите инструкции для последующего запроса который приведет вас к специальному коду который является ответом на это задание.

Тема 7. Дополнительный инструментарий
В этом задании вы должны клонировать репозиторий, создать ветку, влить в нее существующие ветки, решить конфликты и во время решения конфликтов подправить код так, чтобы привести код из разных веток к одному стилю.
Задачи
1. Есть общедоступный репозиторий, вы должны клонировать его для локальной работы.
2. В проекте три ветки, в которых одновременно шла работа над разными алгоритмами шифрования: master, в которой лежит шифр Цезаря, и две ветки созданные от нее на разных стадиях работы, в которых лежит вариации шифра цезаря: rot13 и шифр Виженера. Вам нужно переключиться (checkout) в каждую из веток, посмотреть код и запустить тесты (python -m unittest -v), убедившись, что в каждой из веток тесты проходят.
3. Вам нужно вернуться в ветку master, создать от нее новую ветку dev и перейти туда.
4. В ветку dev вам нужно влить ветку с шифром rot13, с этим не должно возникнуть проблем, но не забудьте про ключ --no-ff. С момента ответвления от master, в ветках изменялись только разные файлы, поэтому конфликтов при слиянии не будет. После слияния, в dev должны быть шифр Цезаря и rot13, тесты на них, и они должны проходить.
5. Теперь в ветку dev нужно влить ветку с шифром Виженера, не забудьте про ключ --no-ff. Эта ветка была ответвлена от master на раннем этапе работы над программой, и в ней оказался старый вариант кода, который потом правился и в ней и master параллельно, но по разному, поэтому будут конфликты слияния. К тому же код в этой ветке слегка устарел, так как в master был существенно унифицирован код как шифров, так и тестов. Это привело к тому, что шифры Цезаря и rot13 используют общую кодовую базу для тестов, а шифр Виженера нет. При решении конфликтов в cipher.py и test.py имейте в виду, что код, который у вас в dev новее и лучше, и все конфликтующие изменения нужно оставить именно из него, а из Виженера взять только классы Vigenere и TestVigenere (и то, последний придется потом переделать почти полностью).
6. Убрав все конфликтующие изменения, не спешите делать commit, чтобы завершить merge. Сначала вам нужно привести код шифра Виженера и его теста к новой кодовой базе. Т.е. сделать их максимально похожими на код шифра Цезаря: переименовать методы шифрации и дешифрации в encode и decode, использовать cls.get_char, if, cls.alpha (из 26 букв) и генератор списка, а в тестах наследоваться от TestCipherMixin. Учтите, что теперь ключ шифрования в шифре Виженера не может содержать пробел, уберите его из строки ключа в тестах, а вот строка для шифрации, наоборот, может содержать любые символы, а не только буквы (и последняя версия шифра Цезаря прилетевшая из ветки rot13 с этим справляется)
7. После всех исправлений завершите merge с помощью commit.
8. Последним шагом перейдите в ветку master и влейте в нее dev, не забудьте необходимые ключи merge. Проблем не должно возникнуть. Еще раз запустите тесты, их должно быть 6 и они должны пройти.
9. Папку с проектом целиком запакуйте в zip-архив и отправьте на проверку. На сервере проект будет распакован и с помощью git проверено, что ветки объединялись именно так, как описано. Затем будет проверено, что классы Caesar и Vigenere наследуются от Cipher (Rot13 может не наследоваться), методы шифрования у всех классов называются encode и decode и действительно реализуют нужные алгоритмы шифрования, и вызывают при этом cls.get_char. Будет проверено, что все 6 тестов проходят, наследуются от TestCipherMixin и unittest.TestCase и определяют только методы encode и decode (могут содержать еще константы и атрибуты).
Вот для сравнения вывод git log --graph --oneline --decorate --all в ветке master, после того как проделано все описанное выше (наглядно показаны все ветки, откуда ответвились и куда влиты:

Учебная литература

Основная литература:
Think Python [Электронный ресурс] – Режим доступа - https://greenteapress.com/wp/think-python-2e/
Automate the Boring Stuff with Python [Электронный ресурс] – Режим доступа - https://automatetheboringstuff.com/
Dive Into Python 3 [Электронный ресурс] – Режим доступа -http://diveintopython3.problemsolving.io/
Problem Solving with Algorithms and Data Structures using Python [Электронный ресурс] – Режим доступа -https://runestone.academy/runestone/static/pythonds/index.html
Swaroop Chitlur. A Byte of Python [Электронный ресурс] – Режим доступа - https://wombat.org.ua/AByteOfPython/AByteofPythonRussian-2.02.pdf – 2020.
Федоров Д. Основы программирования на примере языка Python [Текст] : учебное пособие / Д. Федоров. - 2018.
Свейгарт Э. Автоматизация рутинных задач с помощью Python [Текст] : практическое руководство для начинающих / Эл Свейгарт. – 2017.

Дополнительная литература:
Маккинни У. Python и анализ данных [Текст] / У. Маккинни. – 2015.

Перечень учебно-методического обеспечения для самостоятельной работы обучающихся по дисциплине (модулю)
Бэрри П. Изучаем программирование на Python [Текст] / П. Бэрри. – 2017.
Савельев В. Статистика и котики [Текст] / Владимир Савельев. – 2018.
Бослав С. Статистика для всех [Текст] / Сара Бослав. – 2015.
Хамидуллин Р. Я. Теория вероятностей и математическая статистика [Текст] / Р.Я. Хамидуллин. – 2020.
Справочник по функциям DAX [Электронный ресурс] – Режим доступа - https://docs.microsoft.com/ru-ru/dax/dax-function-reference
Талер Р. Nudge. Архитектура выбора [Текст] / Ричард Талер. – 2017.
Желязны Дж. Говори на языке диаграмм [Текст] : пособие по визуальным коммуникациям / Джин Желязны. – 2020.

Перечень ресурсов информационно-телекоммуникационной сети "Интернет", необходимых для освоения дисциплины (модуля)
Документация Postgres про сравнение строк - https://postgrespro.ru/docs/postgrespro/9.5/functions-matching
Документация Postgres про другие функции работы со строками - https://postgrespro.ru/docs/postgrespro/9.5/functions-string
Тестер регулярных выражений - https://www.regextester.com
Интерактивный учебник по SQL -http://www.sql-tutorial.ru/ru/content.html
Введение в анализ данных с помощью Pandas - https://habr.com/ru/post/196980/
Начало работы с Power BI - https://docs.microsoft.com/ru-ru/power-bi/fundamentals/desktop-getting-started

Темы

Общее представление о WEB Сбор данных со сторонних сайтов Beautiful Soup и работа с API Хранение данных. SQL / NoSQL Веб интерфейсы с Django и Bootstrap Работа с данными пользователя Дополнительный инструментарий
Лекции
2,00ч
Практические занятия
4,00ч
Самостоятельная работа
4,00ч
Всего
10,00ч
Лекции
2,00ч
Практические занятия
2,00ч
Самостоятельная работа
2,00ч
Всего
6,00ч
Лекции
2,00ч
Практические занятия
2,00ч
Самостоятельная работа
2,00ч
Всего
6,00ч
Лекции
2,00ч
Практические занятия
2,00ч
Самостоятельная работа
4,00ч
Всего
8,00ч
Лекции
6,00ч
Практические занятия
6,00ч
Самостоятельная работа
8,00ч
Всего
20,00ч
Лекции
4,00ч
Практические занятия
4,00ч
Самостоятельная работа
6,00ч
Всего
14,00ч
Лекции
2,00ч
Практические занятия
2,00ч
Самостоятельная работа
4,00ч
Всего
8,00ч
Промежуточная аттестация 12,00 часов
Зачет