---
# System prepended metadata

title: 'Web сервисы, фреймворки, API'
tags: [production-ml]

---

---
slideOptions:
  theme: white
  transition: slide
tags: production-ml
title: Web сервисы, фреймворки, API
---
<style>
.reveal {
  font-size: 28px;
}
/* .reveal p {
    text-align: left;
 } */
.reveal section img { 
    background:none; 
    border:none; 
    box-shadow:none; 
 }
/*  .reveal .polina { 
    border:10; 
 } */
</style>

# Web сервисы, фреймворки, API
<!-- [v1] -->
Белов Виталий, весна 2021

[TOC]

---

# API

*Application Programming Interface*

 Это способ коммуникации програмных компонентов
 
![](https://i.imgur.com/JFr6Imq.png)

- Внутренние API (собственных библиотек) - для коммуникации микросервисов внутри приложения/компании
- Внешние API (веб-сервисов) - позволяют получить доступ к сервису сторонним разработчикам через интернет, используя HTTP или другие протоколы

Как согласовать различные API? - *REST* и *RPC*

---

## REST

- Как расшифровывается? - **RE**presentational **S**tate **T**ransfer
- Что это? - Набор [правил](http://www.restapitutorial.ru/lessons/whatisrest.html#uniform-interface), которые позволяют разного рода системам обмениваться данными и масштабировать приложение. Соответствие этим правилам - нестрогое 
- Как? - Используется HTTP протокол - прикладной уровень обмена данными по сети 

Если при проектировании правила REST соблюдены - такой протокол называется *RESTful*

----

RESTful API позволяет производить CRUD операции над всеми объектами, представленными в системе.

*CRUD* - <!--концепция программирования-->аббревиатура, которая описывает четыре базовых действия
- C - create
- R - read
- U - update
- D - delete

Пример CRUD API:
![](https://i.imgur.com/IrwHLYf.png)

<!--
Также вводится понятие **ресурса**:
Когда вы решаете, какие ресурсы буду в вашей системе, называйте их существительными. Другими словами, URI должен ссылаться на ресурс, а не на действие. 

Примеры ресурсов:

- Пользователи системы.
- История сообщений пользователя.
-->

----

<!-- ### REST API ![](https://i.imgur.com/U5y9wFQ.png =150x) -->



<!-- [Задание](https://starkovden.github.io/workshop-activities.html) -->

Конечная точка (*endpoint*) - это ресурс, расположенный на веб-сервере по определенному пути.

Endpoint приложения может выглядеть так:
![](https://i.imgur.com/Hn5zQu8.png)

----

В REST API CRUD соответствуют *post*, *get*, *put*, *delete*.
Ответ (Response) возвращается, как правило, в формате JSON или XML(реже).

![](https://i.imgur.com/DZak8uf.png =500x)

https://www.youtube.com/watch?v=LooL6_chvN4

---

- **get** - вернуть список объектов:

Request:
```shell
GET /api/train_samples
```
Response:
```shell
[
    {id:0, password: 'qwerty', times: 1601},
    {id:1, password: 'admin', times: 1920}
    ...
]
```

----

- **post** - добавить объект:

Request:
```shell
POST /api/train_samples/
```
Request object:
```shell
{password: '0000', times: 1000}
```
Response:
```python
{id:9, password: '0000', times: 1000}
```
id - назначится сам

----

- **put** - обновить выбранную запись:

Request:
```shell
PUT /api/train_samples/1
```
Request object:
```shell
{id:1, password: 'admin', times: 2000}
```
Response:
```shell
{id:1, password: 'admin', times: 2000}
```

----

- **delete** - удалить выбранный объект:

Request:
```shell
DELETE /api/train_samples/1
```

:::info 
:pushpin: [Еще пример описания API метода](https://gist.github.com/iros/3426278) 
:::

---

## Коды ответов

| Код | Название | Описание |
| -------- | -------- | -------- |
| 200      | OK                 | Запрос выполнен успешно |
| 201      | Created            | Возвращается при каждом создании ресурса в коллекции|
| 204      | No Content         | Нет содержимого. Это ответ на успешный запрос, например, после DELETE)       |

- https://habr.com/ru/post/440900/ - статья про коды и REST
- https://developer.mozilla.org/ru/docs/Web/HTTP/Status - полная таблица кодов 


----

| Код | Название | Описание |
| -------- | -------- | -------- |
| 400      | Bad Request        | Ошибка на стороне Клиента. Например, неправильный синтаксис запроса, неверные параметры запроса и т.д.|
| 401      | Unauthorized       | Клиент пытается работать с закрытым ресурсом без предоставления данных авторизации |
| 403      | Forbidden          | Сервер понял запрос, но отказывается его обрабатывать |
| 404      | Not found          | Запрашивается несуществующий ресурс |
| 405      | Method Not Allowed | Клиент пытался использовать метод, который недопустим для ресурса. Например, указан метод DELETE, но такого метода у ресурса нет|
| 500      | Server error|Общий ответ об ошибкtе на сервере, когда не подходит никакой другой код ошибки|

---

## Curl

<!-- https://flaviocopes.com/http-curl/ -->

- Что это? - Client URL, утилита командной строки
- Зачем? - позволяет выполнять запросы с различными параметрами и методами без перехода к веб-ресурсам в адресной строке браузера. Поддерживает протоколы HTTP, HTTPS, FTP, FTPS, SFTP и др.

----

### Установка
* В MacOS, Ubuntu - доступен из командной строки
* В Windows - требуется установка. [Инструкция](http://www.confusedbycode.comg/curl/#downloads). Можно также установить Git Bush. 

Проверить установку в WIN можно из командной строки *cmd* ➜ *curl -V* 
Если установлен, появится сообщение вида:

    `curl 7.55.1 (Windows) libcurl/7.55.1 WinSSL`

----

### Примеры запросов

* **Curl - GET запрос**
```curl
curl https://host.com
```
Метод GET - по умолчанию. Тот же результат получим, если вызовем так:
```curl
curl -X GET https://host.com
```
Чтобы получить ответ с заголовком:
```curl
curl https://host.com -i
```

Ответ будет содержать версию HTTP, код и статус ответа (например: HTTP/2 200 OK). Затем заголовки ответа, пустая строка и тело ответа.

----

* **Curl - POST запрос**
```curl
curl -X POST https://host.com
```

Используя передачу данных (URL-encoded):
```curl
curl -d "option=value_1&something=value_2" 
     -X POST https://host.com/
```

Здесь -d или --data - флаг, обозначающий передачу данных 

----

* **POST запрос, используя формат JSON**
```curl
curl -d '{"option": "val"}' 
     -H "Accept:application/json" 
     -X POST https://host.com/ 
```

Здесь -H или --header - флаг заголовока запроса на ресурс

Или можно передать json, как файл:
```curl
curl -d "@file.json"  
     -X POST https://host.com/ 
```

----

**Еще флаги:**
* `-u user:pass` - если на сервере требуется аутентификация
* `curl -verbose`, отображает подробности
* `-L`, поддержка redirect (если ресурс перемещен)
* `-O` - cохранить с тем же именем, -o data.json - со новым именем

<!--https://starkovden.github.io/understand-curl.html-->

[Документация](https://curl.se/docs/manpage.html)

---

## Недостатки/особенности REST API:

* Для каждого языка необходимость разработки своего API.
(Можно использовать Swagger - рассмотрим далее)
* JSON для передачи данных - не бинарный формат. Медленнее передача данных, но удобнее просматривать данные
* Протокол HTTP 1.1 - не поддерживает передачу потоковых данных

Данные недостатки учтены в **gRPC(Google Remote Procedure Call)** 

----

<!-- ---

## gPRC
Саша: закомментил, думаю, нам не надо про это рассказывать
-->

## gRPC 

Основан на **RPC** - вызове удаленного кода на других машинах. ![](https://i.imgur.com/BxyZuSA.png =280x)

**Отличия:**

* Генерация кода стандартными средствами. Используется компилятор **Protoc**, который генерирует код для множетсва языков, включая python
* Бинарный формат данных **Protobuf**, использует сжатие -> быстрее передача данных
* Протокол HTTP 2 (2015 год) -> потоковая передача данных, бинарный формат, выше скорость и пр.

----

**Что выбрать?:**
* Если важна скорость - gRPC
* Если монолитное приложение с доступом извне или браузер - REST API
* Распределенная система на микросервисах - gRPC
* Потоковые данные (например, с датчиков) - gRPC

[Быстрый старт и руководство gRPC для Python](https://grpc.io/docs/languages/python/)

---

## Open API ![](https://i.imgur.com/Ou2JKbZ.png =150x)

Общепринятым форматом для описания REST API на сегодняшний день является OpenAPI, который также известен как Swagger

Cпецификация представляет из себя единый файл в формате JSON или YAML, состоящий из трёх разделов:

1. Заголовок, содержащий название, описание и версию API, а также дополнительную информацию;
2. Описание всех ресурсов, включая их идентификаторы, HTTP-методы, все входные параметры, а также коды и форматы тела ответов;
3. Определения объектов в формате JSON Schema, которые могут использоваться как во входных параметрах, так и в ответах.

---

# Веб-фреймворки
## Flask  ![](https://i.imgur.com/yw7LlOi.png =100x) 

Что это? - Веб-фреймворк для Python

Почему выбираем его по-умолчанию?
- Минималистичный фреймворк
- Быстрое прототипирование
- Низкоуровневый фреймворк, после освоения будет проще разобраться с Django


Также, лучшим решением будет выбрать Flask, если:
* Разрабатывается микросерсисная архитектура
* Реализуется REST API без фронтенда
* Требуется гибкая кастомизация 


<!--
https://flask-restplus.readthedocs.io/en/stable/index.html

https://buildmedia.readthedocs.org/media/pdf/flask-russian-docs/latest/flask-russian-docs.pdf

https://flask-russian-docs.readthedocs.io/ru/latest/quickstart.html#debug-mode
-->

----

### Minimal Flask App
```python=
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'

if __name__ == '__main__':
    app.run()
```

Запустив приложение, получим соообщение:
```bash=
~ python app.py
Running on http://127.0.0.1:5000/
```

Localhost - c IP адресом 127.0.01 ➜ внутренняя сеть компьютера

----

### Параметры app.run() 

**1. Debug mode**:
```python=1
app.run(debug=True)
```

> - Cервер перезагружается сам при изменении кода
> - Позволяет работать с отладчиком
> - Не забыть отключить при развертывании сервиса

**2. Cделать сервер публично доступным**
```python=1
app.run(host='0.0.0.0')
```
По умолчанию - доступ local

----

### Шаблоны

Шаблон — файл с HTML-кодом и элементами разметки, которые позволяют выводить динамический контент.

Функция render_template() вызывает механизм шаблонов Jinja2, который поставляется в комплекте с Flask.

``` python
from flask import render_template
```

Шаблоны хранятся в директории /templates

----

Пример шаблона (/templates/index.html):

``` HTML
<html>
    <body>
        <h1>Prediction: {{ pred }}</h1>
    </body>
</html>
```

Пример кода, которые преобразует шаблон в HTML страницу (рендеринг):
```pyhon
from flask import Flask, request, render_template


app = Flask(__name__)

#some code

@app.route('/')
def index():
    return render_template('index.html', pred=model.prediction)
```

---

### Flask API

**Flask-RESTX** - это расширение для Flask, которое добавляет поддержку для быстрой разработки REST API.

Документация:
https://flask-restx.readthedocs.io/en/latest/index.html

Аналоги: *flask-restplus*, *flask-restful*


----

Простой пример приложения, реализующий API на Flask:

```python=3
from flask import Flask
from flask_restx import Api, Resource, fields

app = Flask(__name__)
api = Api(app)

passwords = []
a_password = api.model('Resource', {'password': fields.String})

@api.route('/password')
class Prediction(Resource):
    def get(self):
        return passwords

    @api.expect(a_password)
    def post(self):
        passwords.append(api.payload)
        return {'Result': 'pass added'}, 201
```

----

Flask-RESTX предоставляет набор инструментов для генерации документации с использованием Swagger. 

Документация **Swagger API** создается автоматически и доступна по корневому URL API:

![](https://i.imgur.com/RCr0bUH.png =800x)



<!--
### Blueprint

* Средство выделения функциональности.
* Позволяет переносить как отдельный блок в другие проекты.
-->

---

### Deploy

[Пошаговое руководство как развернуть Flask приложение на Heroku](https://devcenter.heroku.com/articles/getting-started-with-python)

На 4-й лекции более подробно будет разобран вопрос развертывания. Также будет полезно закрепить базовые представления о Git.

<!--
https://towardsdatascience.com/creating-restful-apis-using-flask-and-python-655bad51b24 -->

---

## Django ![](https://i.imgur.com/exGf2vi.png =150x)


- Что это? - еще один популярный фреймворк на python для разработки веб приложения или API.

Особенности:
* Встроенная Django Admin
* Встроенная защита от наиболее распространенных уязвимостей и атак, в частности: SQL-инъекции,CSRF,XSS, кликджекинг, и т.д.
* Поддержка ORM

Django - хороший выбор для быстрой разработки масштабируемого приложения. Но не лучший выбор для микросервисов, простого API-приложения без фронтенда и баз данных.


<!---

### Установка
```shell=1
pip install django
```
(используя виртуальное окружение)

Переходим в каталог проекта и создаем проект: 
``` shell=1
cd my_django_app
django-admin startproject my_site .
```
Последняя команда создаст структуру django проекта

### Тестовый запуск приложения
``` shell=1
cd my_site 
python manage.py runserver
```
Получим вывод:
```
Django version 3.0, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
```
Запустили Web-сервер для разработки Django!
-->

---

## FastAPI

**Преимущества:**
- встроенная документация API
- асинхронность
- валидация (pydantic)
- быстродействие

Установка:

```shell=1
pip install uvicorn fastapi pydantic
```

Интерактивная документация:

http://127.0.0.1:8000/docs

[Чуть подробнее о FastAPI на хабре](https://habr.com/ru/post/478620/)

---

## Streamlit
- Что это? Opensource Python фреймворк для быстрой разработки дашборда в проектах с машинным обучением, не требующий знания frоntend (HTML, CSS и JavaScript)

### Установка
```shell=1
pip install streamlit
```

### Особенности

1. Виджеты
    * checkboxes
    * selectBox
    * slider
    * multiSelect (tags)
2. Визуализация
    * matplotlib
    * сomponent for rendering Folium maps.

----

<iframe width="100%" height="500" src="https://share.streamlit.io/streamlit/demo-self-driving" frameborder="0"></iframe>

---

# UI

- Что это? User Interface, пользовательский интерфейс
- Зачем? - помочь пользователю, организовав комфортное и, по возможности, интуитивно понятное взаимодействие с сайтом. Включает перечень оформленных графических элементов (кнопок, чекбоксов, селекторов и т.д.)

---

![](https://i.imgur.com/4rUOWCq.png)

---

 - **HTML** - язык гипертекстовой разметки, определяет содержание и структуру веб-контента
 [Справочник HTML](https://webref.ru/HTML)
- **CSS** - язык иерархических правил, используемый для представления внешнего вида страницы
[Справочник CSS](https://webref.ru/css)


---

**Bootstrap** 

Фреймворк, позволяющий быстро создавать адаптивный сайт. Включает набор инструментов для создания сайтов и веб-приложений. Содержит HTML и CSS-шаблоны оформления для типографики, веб-форм, кнопок и прочих компонентов веб-интерфейса

https://getbootstrap.com/docs/5.0/getting-started/introduction/

[Описание на русском](https://bootstrap-4.ru/)

----

Базовый шаблон, подключающий bootstrap:
``` html=1
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">

    <title>Hello, world!</title>
  </head>
  <body>
    <h1>Hello, world!</h1>
  </body>
</html>
```

---
<!--
## Полезные ссылки

Поиграть с игрушечным api:
https://jsonplaceholder.typicode.com/
-->

---

# Семинар

1.  Пробуем работу с **curl** на примере сайта https://reqres.in/:
    
- **get** запрос 
```curl
curl https://reqres.in/api/users/2
curl https://reqres.in/api/users/2 -i
curl https://reqres.in/api/users/2 -I
```

отправляем вывод в файл:
```curl
curl -o test_curl.txt https://reqres.in/api/users/2
```

- **post** запрос
```curl
curl -X POST  https://reqres.in/api/users -d "name=morpheus&job=datascientist" -i
```

    используя json:

```curl
curl -d '{"name": "Ivan", "job: "data scientist"}' -H "Accept:application/json" -X POST https://reqres.in/api/users -i
```

---

- **delete** запрос

```curl
curl -X DELETE https://reqres.in/api/users/1 -i
```
получаем код 204 и пустое тело

---

2. **Flask** 

- Изучаем файл [app.py](https://gitlab.com/production-ml/password_app/-/blob/master/app.py)
- И шаблон [index.html](https://gitlab.com/production-ml/password_app/-/blob/master/templates/index.html)

3. **Flask API**

- Ветка с игрушечным примером, как добавить API в проект:

https://gitlab.com/production-ml/password_app/-/blob/test_api/app.py

- Запустить приложение и посмотреть работу **Swagger**

Добавить запись с помощью curl:
```curl
curl -X POST "http://0.0.0.0:5000/password" -H "accept: application/json" -H "Content-Type: application/json" -d "{ \"password\": \"qwerty\"}"
```

Посмотреть консоль, где запущено приложение. Должно быть вида:
```
127.0.0.1 - - [16/Mar/2021 16:57:43] "GET /swagger.json HTTP/1.1" 200 -
127.0.0.1 - - [16/Mar/2021 16:59:31] "POST /password HTTP/1.1" 201 -
127.0.0.1 - - [16/Mar/2021 17:00:31] "POST /password HTTP/1.1" 400 -
```

4. **Streamlit**

- Пример сервиса в отдельной ветке:

https://gitlab.com/production-ml/password_app/-/blob/streamlit_try/app.py

- Поднимаем сервис локально:
```
streamlit run app.py
```
![](https://i.imgur.com/aGCMar3.png =400x)

<!--
## Небольшой блиц: 
(*Как идея)

- основные опера
 -->
<!--
https://rapidapi.com/community/api/open-weather-map?endpoint=apiendpoint_f719676c-072b-4a2d-ad2e-78f8375ea9c8

## RapidAPI
После развертывания на сервере (например, Heroku) дает возможность опубликовать ваш собственный API

Также предоставляет маркетплейс с более чем 10 000 API: 

![](https://i.imgur.com/W1d8anM.png)
:::info
:pushpin: [Каталог Machine Learning  RapidAPI](https://rapidapi.com/category/Machine%20Learning) 
:::
 -->
 
 ---
 
 # Home work
 
1.  Если вы использовали Jupyter для соревнования, перенести проект в PyCharm / VSCode
2.  Подготовить пакетное окружение Pipenv и установить в него необходимые пакеты. (Желательно, сразу разделяя на зависимости, необходимые только для разработки, от зависимостей, необходимых для фактической работы базового кода с помощью аргумента  --dev)
4.  Используя классы, подготовить методы для предсказания модели (predict) и ее переобучения (fit).
5.  С помощью Flask API реализовать на локальной машине возможность получить результат предсказания модели в формате json.
6.  *Опционально добавить графический веб-интерфейс (используя Flask)
 
 