App em Flask com IA/ML - desenvolvimento e deploy 100% free (PARTE 1)
Tags: flask, braziliandevs, python, webdev
Published: 2024-03-15 19:39:57

## 
Neste tutorial, desenvolvemos um pequeno e simples aplicativo web utilizando ferramentas gratuitas. Usamos Python e recursos de aprendizagem de máquina (_machine learning_ - ML) para realizar análises de suspeitas de plágio utilizando inteligência artificial (IA). Além disso, colocamos o app em produção, disponibilizando seu acesso para qualquer computador na web!

- **<a href="https://github.com/msc2020/checa-plagio-app">Acesse o código completo em: https://github.com/msc2020/checa-plagio-app</a>**

<br />

Vamos lá? ☕

<br />

## 1. O que vc acha do mini app abaixo?


![imagem gif do app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/brraj9wl1lzuexylfhat.gif)

<center> Veja o app rodando em **<a href="https://checaplagio-1-a4780033.deta.app">https://checaplagio-1-a4780033.deta.app/</a></center>**
<br />

**💻 Reflexão:** Como o app mostrado acima foi desenvolvido? Podemos criar nosso próprio app em Python usando tecnologias gratuitas (100% free)? Há uma maneira pratica de embutir funcionalidades com aprendizagem de máquina num app?

Se sua resposta foi `sim` para as três perguntas, talvez este post contribua mais para vc na parte do <u>caso de estudo</u>. Se vc está entre o `talvez` ou `não` como resposta em alguma delas, é bem provável que este post irá contribuir para sua bagagem em <u>desenvolvimento e deploy</u> de projetos com <u>ferramentas Python</u>.🍵

### 1.1. O que o app fará?
O web app que desenvolveremos e colocaremos em produção no <a href="deta.space/">Deta Space</a>, serve para <u>identificar se dois textos são "iguais" ou "não"</u>. Quem irá materializar isso que chamamos de "iguais", será um modelo de inteligência artificial (IA) gratuito e disponível no <a href="https://huggingface.co/">site da Hugging Face</a>. Acessaremos o modelo de IA por meio de uma API (_Application Programming Interface_).

### 1.2. O que é feito neste tutorial (PARTE 1 e <a href="">PARTE 2</a>) ?

- Configuração de um <u>ambiente virtual</u> com Python para rodar o micro-framework <u>Flask<u> (PARTE 1)

- Desenvolvimento da parte <u>lógica</u> do app usando os padrões de <u>templates</u>, <u>rotas</u> e <u>funções de visualização</u> do Flask (PARTE 1)

- Como embutir um <u>modelo de aprendizagem de máquina</u> (ML)  no app (PARTE 1)

- Realizar o <u>deploy</u> do app em um <u>ambiente de produção</u> (<a href="https://dev.to/msc2020/parte-2-aplicacao-flask-contendo-recursos-de-iaml-desenvolvimento-e-deploy-gratuitos-eaf-temp-slug-4881058?preview=fa03808150c88ba26d4d9eb19efb2f5242740fda2f5d533069bce37c5f37da90a2dcc4fa33a26528fca32523cfc80d3d9ce8c85f75c5a2153dee6d3b">PARTE 2</a>)

<br />
## 2. Como rodar localmente o app feito com o Flask? 

No desenvolvimento de software em Python é comum o uso de um <u>ambiente virtual</u> (<u>virtual environment</u>, `venv`) para isolar as bibliotecas que são pré-requisitos. As bibliotecas Python e suas dependências costumam funcionar <u>sem conflitos</u> quando se usa um ambiente virtual específico durante o desenvolvimento.

### 2.1. Criação e ativação do ambiente virtual local

Primeiramente, vamos criar uma pasta/diretório com o `nome-do-app`:

```shell
mkdir nome-do-app
cd nome-do-app
```
Fique à vontade para colocar o nome que desejar ao invés de `nome-do-app`. Esse será nosso diretório raiz.

Dentro da pasta criada (`nome-do-app`), criaremos o ambiente virtual. Para isso usamos o seguinte comando que deve ser digitado em um `terminal` (`bash`, `shell` ou `prompt`) que possua o Python 3 instalado:
```shell
python -m venv venv_app
```
Após criar o ambiente virtual (`venv_app`), precisamos <u>ativá-lo</u>:

- no Linux:
```shell
source venv_app/bin/activate
```
- no Windows:
```prompt
 venv\Scripts\activate
```

**Nota:** Os arquivos que estão dentro de `venv_app` são gerados automaticamente no momento em que criamos o ambiente virtual. Portanto, não precisamos nos preocupar com eles.

### 2.2 Instalação das dependências
Agora que já estamos <u>com o ambiente virtual local ativado</u>, devemos instalar neste ambiente os pré-requisitos (`requirements.txt`) que serão usados.

O conteúdo do arquivo `requirements.txt` é:
```Python
# requirements.txt

Flask
Flask-Bootstrap # extensão para bootstrap (css, styles, navbar, etc)
Flask-Moment # extensão para manipular datetime
Flask-WTF # extensão para web forms
python-dotenv # gerenciador de variáveis env
requests
waitress
```
Para instalar no Python as bibliotecas desse arquivo `.txt` digitamos o seguinte comando no terminal:

```shell
pip install -r requirements.txt
```

Estrutura dos arquivos até o momento:
```shell
nome-do-app/
│
├── venv_app/
│
└── requirements.txt
```

<br />

## 3. Primeira versão do app

Seguindo, mais ou menos, a <a href="https://flask.palletsprojects.com/en/3.0.x/">documentação do Flask</a>, agora criaremos os **templates** (`*.html`), os **endpoints** e as **funções de visualização** como parte do código para o app. Com esses esses três elementos conseguimos implementar a <u>lógica de negócio</u> que desejarmos no app.

Os usuários vão chamar/rodar as funcionalidades do app através da inserção de textos num elemento html chamado `TextArea`e de cliques em um <a href="https://developer.mozilla.org/pt-BR/docs/Web/HTML/Element/button">`botão` html (`<button>`)</a>.

A parte visual do app será feita com a extensão do pacote Bootstrap para o Flask (`Flask-Bootstrap`). 

**Nota:** É usual, ao invés de usarmos diretamente uma ferramenta como por ex., o <a href="https://getbootstrap.com/">Bootstrap</a>, usarmos sua <a href="https://flask.palletsprojects.com/en/3.0.x/extensiondev/"> preferir sua  extensão para o Flask</a> (quando houver a extensão). Isso ajuda a integrar Flask e outras ferramentas.

### 3.1. Script principal (`app.py`)

Agora devemos criar o seguinte arquivo Python, chamado `app.py`, na raiz da pasta `nome-do-app`. É possível criar o arquivo manualmente, através de cliques, ou, se tivermos no Linux, pelo terminal com:
```shell
touch app.py
```
Esse é o código de `app.py`:

```Python
# app.py

## 1. carrega bibliotecas
from flask import Flask, render_template, url_for, session, redirect, flash
from flask_bootstrap import Bootstrap
from flask_moment import Moment

from flask_wtf import FlaskForm
from wtforms import SubmitField, TextAreaField
from wtforms.validators import InputRequired, Length

import os
from datetime import datetime
from utils.constants import *
import random

app = Flask(__name__)
app.config['SECRET_KEY'] = '12345'

bootstrap = Bootstrap(app)
moment = Moment(app)
N_RANDOM = random.randint(7, 300)
##

## 2. funções do modelo de ml
def preprocess_text(some_text: str):
    some_text_processed = ' '.join([word for word in some_text.split(' ') if len(word)])
    return some_text_processed

def run_model(original_text, suspect_text):
    import json
    import requests

    API_URL = os.environ.get('API_URL')
    API_TOKEN = os.environ.get('API_TOKEN')
    headers = {'Authorization': f'Bearer {API_TOKEN}'}
    payload = {'inputs': {'source_sentence': original_text, 'sentences': [suspect_text]}}

    try:
        response = requests.post(API_URL, headers=headers, json=payload)
        res = response.json()
    except Exception as e:
        res = [-1*random.randint(0, 100)/100] # para testes
        print(f'\nErro:\n{e}\n')

    return res

def check_plagiarism(original_text: str, suspect_text: str):
    is_fake = False    

    original_text_clean = preprocess_text(original_text)
    suspect_text_clean = preprocess_text(suspect_text)

    plagiarism_res = run_model(original_text_clean, suspect_text_clean)

    N_MAX_RETRY = 3
    n_retry = 1    
    if type(plagiarism_res) == dict:
        while 'error' in plagiarism_res.keys():
            import time
            try:
              time_waiting = 0.7*plagiarism_res["estimated_time"]
            except:
              time_waiting = 3
            time.sleep(time_waiting)
            plagiarism_res = run_model(original_text_clean, suspect_text_clean)
            n_retry += 1
            if n_retry >= N_MAX_RETRY:
                plagiarism_probab = -1*random.randint(0, 100)/100 # para testes
                break
            if type(plagiarism_res) != dict:
                plagiarism_probab = plagiarism_res[0]
                break
    else:
        plagiarism_probab = plagiarism_res[0]

    return round(plagiarism_probab, 2)
##

## 3. construtor para o objeto formulário
class NameForm(FlaskForm):
    original_text = TextAreaField('Texto Original',
        validators=[InputRequired(), Length(7, 1000)],
        render_kw={'class': 'form-control', 'rows':5,
        'placeholder': '\nEntre com um texto aqui. Por ex.:\n' + LOREM_IPSUM[:N_RANDOM] + ' ...'})

    suspect_text = TextAreaField('Texto Suspeito de Plágio',
        validators=[InputRequired(), Length(7, 1000)],
        render_kw={'class': 'form-control', 'rows':4,
        'placeholder': '\nEntre com outro texto aqui.'})

    submit = SubmitField('Checar')
##

## 4. endpoints e funções de visualização
@app.route('/', methods=['GET', 'POST'])
def index():
    form = NameForm()
    is_fraud = -1

    session['original_text'] = form.original_text.data
    session['suspect_text'] = form.suspect_text.data

    if form.validate_on_submit():
        original_text = session.get('original_text')
        suspect_text = session.get('suspect_text')
        plagiarism_probab = check_plagiarism(original_text, suspect_text)

        # para testes
        emph = ""
        if plagiarism_probab < 0:
            plagiarism_probab *= -1
            emph = '"'
        # checa probab
        if plagiarism_probab > 0.5:
            is_fraud = 1
            print('Parece plágio!')
        elif plagiarism_probab >= 0:
            is_fraud = 0
            print('Não parece plágio!')
        flash(f'\nTexto {emph}checado{emph}! {emph}Probabilidade de plágio{emph} = {plagiarism_probab*100:.2f} %.\n')        

        return render_template('index.html', form=form,
            user_name=session.get('original_text'), current_time=datetime.utcnow(),
            is_fraud=is_fraud)

    return render_template('index.html', form=form,
        user_name=session.get('original_text'), current_time=datetime.utcnow(),
        is_fraud=is_fraud)

@app.route('/how_to')
def how_to():
    return render_template('how_to.html', current_time=datetime.utcnow())
##

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

O script `app.py`, mostrado acima, tem **<u>4 partes</u>** principais:
- 1) carrega bibliotecas;
- 2) funções do modelo de ML;
- 3) construtor para o objeto formulário;
- 4) endpoints/rotas e funções de visualização.

A **parte 1** carrega as bibliotecas Python que usaremos. Como por ex., as extensões que facilitam a implementação de recursos do Bootstrap (`from flask_bootstrap import Bootstrap`) e a manipulação segura de formulários (`from flask_wtf import FlaskForm`).

A **parte 2** é onde embutimos o modelo de ML/IA no app. Adiante, falaremos um pouco mais desse modelo.

Já na **parte 3** configuramos os formulários do app.
Há benefícios/facilidades quando os formulários  podem representados por objetos Python (como aqui) ao invés de se implementar apenas com html. Por ex., fica mais prático manipular os atributos de um formulário e incluir camadas de seguranças.

Por fim, na **parte 4** definimos os endpoints (`/`, `/how_to`) e suas respectivas funções de visualização (`index()`e `how_to()`).

### 3.2. Separando as utilidades

Como usamos a chamada `from utils.constants import *` para <a href="https://12factor.net/pt_br/">deixar o código minimamente organizado</a>, devemos criar a pasta `utils` e dentro desta o arquivo `constants.py`:

```Python
# constants.py

LOREM_IPSUM = '''
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Elementum nibh tellus molestie nunc non blandit massa enim nec. Egestas maecenas pharetra convallis posuere morbi leo urna molestie.
'''
```

Caso esteja usando um terminal Linux, podemos usar os seguintes comandos:
```shell
mkdir utils
touch utils/constants.py
```

Estrutura atualizada dos arquivos do projeto:
```shell
nome-do-app/
│
├── utils/
│   └── constants.py
│
├── venv_app/
│   
├── requirements.txt
└── app.py
```

<br/><br/>
### 3.2. Templates (`*.html`)

Usaremos três templates para a construção das telas do app: `base.html`; `index.html` e `how_to.html`.

Como o próprio nome diz, o `base.html` servirá de <u>base</u> para os outros templates html. Nesse arquivo usamos recursos do `Bootstrap` para que a navegação no app seja a melhor possível.

Já os templates `index()` e `how_to()`, são aqueles chamados diretamente pelo `app.py`. O primeiro organiza as chamadas da tela principal (`Home`), enquanto o segundo exibe uma imagem animada explicando como usar o app.

**Nota:** Embora não haja muitos segredos, a definição de <a href="https://dicionario.priberam.org/plagiar">plágio</a> pode variar, se é que existe uma única definição. Talvez, variáveis como <u>todos os textos do mundo</u>, <u>influência política</u>, <u>legislação do país</u> e <u>compromisso científico/"vergonha na cara"</u> possam ser levadas em conta na modelagem.

🤖 No entanto, priorizamos pela <u>simplicidade</u>. Portanto, a funcionalidade principal do app é que ele possa realizar a <u>comparação automática (com IA)</u> entre um texto original e um outro suspeito de `plágio` deste original.

📄 Poderemos incluir melhorias e correções depois que a primeira versão do app estiver em produção. Então, sem mais <a href="https://dicionario.priberam.org/delonga">delongas</a>, na raiz do projeto criemos uma pasta chamada `templates`.

```shell
mkdir templates
```
A seguir, devemos criar os arquivos `base.html`, `index.html` e `how_to.html` dentro da pasta `templates`. No Linux, podemos novamente usar o comando `touch`:
```shell
touch templates/base.html templates/index.html templates/how_to.html
```
Abaixo temos os conteúdos dos arquivos `*.html`. Note que, como usamos o Flask, esses contêm códigos de <a href="https://palletsprojects.com/p/jinja/">`Jinja`</a>. Essa engine/"linguagem" permite a manipulação de variáveis, listas, objetos, etc. misturando Python e html.

```html
<!-- base.html -->

{% extends 'bootstrap/base.html' %}

{% block title%}{% endblock %}

{% block head %}
{{ super() }}
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
{% endblock %}

{% block scripts %}
{{ super() }}
{{ moment.include_moment() }}
{{ moment.locale('pt') }}
{% endblock %}

{% block navbar %}
<ul class="nav nav-pills col-md-3 col-md-offset-8" style="margin-top:5px">
  <li role="presentation" class="active"><a href="{{ url_for('index') }}">Home</a></li>
  <li role="presentation" ><a href="{{ url_for('how_to') }}">Como Usar</a></li>
  <li role="presentation" class="disabled"><a href="#">Sobre</a></li>
</ul>
{% endblock %}

{% block content %}
<div class="container">
    <div class="page-header">
        <h3>Entre com os textos a serem comparados:</h3>
        {{ wtf.quick_form(form) }}
    </div>    
</div>

<div class="container col-md-6 col-md-offset-3">
    {% if is_fraud != -1%}
        {% if is_fraud == 1 %}
        <p><center><b>Parece plágio!</b></center></p>
        <link rel="shortcut icon" href="{{ url_for('static', filename='no.ico') }}" type="image/x-icon">
        <link rel="icon" href="{{ url_for('static', filename='no.ico') }}" type="image/x-icon">
        {% elif is_fraud == 0 %}             
            <p><center><b>Não parece plágio!</b></center></p>
            <link rel="shortcut icon" href="{{ url_for('static', filename='yes.ico') }}" type="image/x-icon">
            <link rel="icon" href="{{ url_for('static', filename='yes.ico') }}" type="image/x-icon">
        {% endif %}
    {% endif %}
</div>
</div>

{% for message in get_flashed_messages() %}
    <div class="alert alert-warning col-md-6 col-md-offset-3">
        <button type="button" class="close" data-dismiss="alert">&times;</button>
        {{ message }}
    </div>
{% endfor %}
{% endblock %}

<hr>
<br>

{%  block body %}
{{ super() }}
<div class="container col-md-6 col-md-offset-3">
    <div>
        <small>
            <p><center>{{ moment(current_time).format('LL') }}</center></p>
        </small>
    </div>
</div>
{% endblock %}
```
<br/><br/>
```html
<!-- index.html -->

{% extends 'base.html' %}
{% import "bootstrap/wtf.html" as wtf %}

{% block title%}Checador de Plágio Simples - versão β{% endblock %}

{% block flash_message %}{% endblock %}
```
<br/><br/>
```html
<!-- how_to.html -->

{% extends 'bootstrap/base.html' %}

{% block title%}Como Usar o Checador de Plágio Simples - versão β{% endblock %}

{% block navbar %}
<ul class="nav nav-pills col-md-3 col-md-offset-8" style="margin-top:5px">
    <li role="presentation"><a href="{{ url_for('index') }}">Home</a></li>
    <li role="presentation" class="active"><a href="{{ url_for('how_to') }}">Como Usar</a></li>
    <li role="presentation" class="disabled"><a href="#">Sobre</a></li>
</ul>
{% endblock %}

{% block content %}
<div class="container">
    <div class="container col-md-9 col-md-offset-1">
    <p style="margin-top:7px"><h4>Siga os passos abaixo para usar este app.</h4><p>
        <div class="thumbnail">

            <img src="{{ url_for('static', filename='how_to.gif') }}">
            {# <img src="{{ url_for('static', filename='how_to.gif') }}" style="max-width:40%; height:auto;"> #}
        </div>
    </div>    
</div>
{% endblock %}
```
Após todas as etapas que fizemos até o momento, ficamos com a seguinte estrutura de arquivos na pasta raiz `nome-do-app`:

```shell
nome-do-app/
│
├── templates/
│   ├── base.html
│   ├── index.html
│   └── how_to.html
│
├── utils/
│   └── constants.py
│
├── venv_app/
│
├── requirements.txt
└── app.py
```

<br/><br/>

### 3.3. Inserindo imagens

Nos templates \*.html, foi usado uma imagem (`*.gif`) e ícones (`*.ico`) que aparecem na barra de título do html. Para usar esses arquivos, criamos uma pasta `static` na raiz do projeto (`nome-do-app`) e colocamos os mesmos dentro dessa pasta.

**Download das imagens e ícones:** Baixe as imagens do link abaixo ou use algumas de sua preferência: <a href="https://github.com/msc2020/checa-plagio-app/tree/main/static">link para baixar imagens e ícones</a>.

```shell
nome-do-app/
│
├── static
│   ├── favicon.ico
│   ├── how_to.gif
│   ├── no.ico
│   └── yes.ico
│
├── templates/
│   ├── base.html
│   ├── index.html
│   └── how_to.html
│
├── utils/
│   └── constants.py
│
├── venv_app/
│
├── requirements.txt
└── app.py
```

<br/><br/>

### 3.4. Rodar o app localmente

Agora o app está quase pronto. Para rodar a versão atual do app, digite o seguinte comando no terminal:

```shell
python app.py
```
Na sequência, se tudo der certo, vc poderá ver a tela principal do app no endereço e porta padrões do Flask: <a href="http://127.0.0.1:5000">http://127.0.0.1:5000</a>.

![Home - APP](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mbrfb149te7rkk2et9xc.png)

<br />

## 4. Configurando a API de ML ⌨️

No arquivo `app.py` há constantes que foram usadas para acessarmos a <a href="https://huggingface.co/tasks/sentence-similarity">API da Hugging Face</a>, responsável pela checagem do nível de semelhança entre os dois textos de entrada. Se os textos forem muito semelhantes, há uma alta probabilidade de plágio.

**Nota:** No <a href="https://huggingface.co/tasks/">site da Hugging Face</a> há inúmeros modelos de ML, casos de uso e datasets que podem servir como base para novos/futuros projetos. Por ex., atualmente, na área de Processamento de Linguagem Natural (NLP, em inglês), eles disponibilizam modelos para as mais diferentes tarefas na área.

![Hugging Face](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tw2tgq3gq2sdt62hs2vl.png)

<br />

### 4.1. Criando uma conta gratuita na Hugging Face 

Lembrando, o modelo de aprendizagem de máquina escolhido para nosso app deverá ter habilidades de verificar a semelhança entre dois textos. A Hugging Face disponibiliza um modelo que pode ajudar nessa tarefa, conhecido como <a href="https://huggingface.co/tasks/sentence-similarity">_Sentence Similarity_</a>.

#### Como usar um modelo da Hugging Face?
Para usarmos um modelo de _Sentence Similarity_ da Hugging Face via API precisamos ter um <u>token de acesso</u>. Após criarmos uma conta (gratuita) no site da Hugging Face, teremos acesso ao token.

![token - hugging face](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/otespg95q9tsm9q6vbu0.png)

<br/><br/>

Esse token é usado para se fazer requisições a API, conforme descrito no <a href="https://huggingface.co/tasks/sentence-similarity">site deles</a>.
```Python
...

API_URL = 'https://api-inference.huggingface.co/models/sentence-transformers/all-MiniLM-L6-v2'
headers = {'Authorization': f'Bearer {API_TOKEN}'}

...
```
Com o token e a URL de acesso ao modelo em mãos já podemos usar a API da Hugging Face.

🤔 Se incluirmos de forma <u>explícita</u> (<u>_hardcoded_</u>) os valores das constantes `API_URL` e `API_TOKEN` em nosso programa principal `app.py`, já conseguiremos realizar as avaliações da suspeita de plágio usando IA via API!

**Nota:** Mais detalhes sobre o modelo aqui escolhido podem ser encontrados em: <a href="https://huggingface.co/tasks/sentence-similarity">https://huggingface.co/tasks/sentence-similarity</a>.

## 4.2. Definindo as variáveis ambientes (`.env`)
Para inserir os valores de `API_URL` e `API_TOKEN` no arquivo `app.py` seguindo as <a href="https://12factor.net/pt_br/">boas práticas</a> iremos criar um arquivo chamado `.env` na raiz do projeto.

Ressaltamos que para cada usuário, teremos um token diferente. O token é um valor secreto e não é recomendável que ele faça parte dos códigos. Além do token, há valores que não são recomendados fazerem parte do código, mas serem chamados de forma <u>implícita</u>.

Como colocaremos o app desenvolvido em produção, seguiremos as recomendações de boas práticas sempre que possível. O arquivo `.env` deverá ser criado na raiz do projeto e ter a seguinte forma:

```shell
# .env

SECRET_KEY_FLASK='!@$%&<mudar-para-uma-secret-key>'
API_URL='https://api-inference.huggingface.co/models/sentence-transformers/all-MiniLM-L6-v2'
API_TOKEN='123456789!@$%&<mudar-para-seu-token>'
```

Como agora definimos o valor de `SECRET_KEY` no arquivo `.env`, devemos retirá-lo do `app.py`. Para isso, alteramos a seguinte linha neste arquivo:

- De:
```Python
app.config['SECRET_KEY'] = '12345'
```
- Para:
```Python
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY_FLASK')
```

**Atenção:** O valor de `API_TOKEN` deve ser alterado para o valor da sua conta na Hugging Face conforme mostramos [anteriormente](#token_hf).

Após criar o arquivo `.env` devemos incluir os seguintes comandos em algum lugar no início do arquivo `app.py`, de forma que os valores de `SECRET_KEY`, `API_URL` e `API_TOKEN` agora fique disponível no `app.py`, não sendo mais um valor nulo.

```Python
# app.py

...

from dotenv import load_dotenv, find_dotenv

load_dotenv(find_dotenv())

...

```

Os 3 pontinhos no trecho de código acima indicam que o restante do código permanece sem mudanças.


## 4.3. Preparando para o deploy

Antes de enviar para deploy em produção, devemos verificar se está tudo ok. Para isso rodamos o app desenvolvido localmente (`python app.py`) e acessamos o localhost (`http://127.0.0.1:5000`) para navegar nele.

### Boas práticas (novamente):
A fim de seguirmos as <a href="https://flask.palletsprojects.com/en/2.3.x/tutorial/deploy/">boas práticas</a> para por um app em produção, ao invés de rodarmos o app chamando diretamente com  `python app.py`, como comumemente é feito na etapa de <u>desenvolvimento</u>, usaremos o WSGI (<i>Web Server Gateway Interface</i>) `waitresss` como mediador da comunicação entre do servidor e o app em Flask. Com isso o app vai para produção de acordo com as recomendações.

Note que, o `waitress` já está presente no `requirements.txt`. Com o `waitress` ao invés de chamarmos diretamente o `app.py` para rodar nossa aplicação, iremos chamar o seguinte arquivo `main.py`, que deve ser criado na raiz do projeto:

```Python
# main.py

from waitress import serve

from app import *

if __name__ == '__main__':    
    local_host, local_port = '0.0.0.0', 8080
    print(f'\nServindo em {local_host}:{local_port}')
    serve(app, host=local_host, port=local_port)
```
Note que no script acima, definimos uma nova porta e endereço de acesso.

Quando um usuário acessar o app pelo navegador a url `http://0.0.0.0:8080`, ele estará se comunicando com o WSGI (_Web Server Gateway Interface_,`waitress`) que, por sua vez, se comunica com o servidor.

Estrutura de arquivos do projeto, após a inclusão do `.env` e do `main.py`:

```shell
nome-do-app/
│
├── .env
│
├── static
│   ├── favicon.ico
│   ├── how_to.gif
│   ├── no.ico
│   └── yes.ico
│
├── templates/
│   ├── base.html
│   ├── index.html
│   └── how_to.html
│
├── utils/
│   └── constants.py
│
├── venv_app/
│
├── requirements.txt
├── app.py
└── main.py
```

Agora executamos o seguinte comando para rodar o app:
```shell
python main.py
```

![APP - local](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sh54o9j19lqum6oovpf2.png)

**Pronto! Basta abrir o app pelo navegador: <a href="0.0.0.0:8080">0.0.0.0:8080</a>!**

Este post foi a PARTE 1 do tutorial composto por duas partes. Aqui vimos como desenvolver um app que utiliza recursos de IA para a tarefa de avaliar uma suspeita de plágio entre dois textos, rodando o app localmente, em em um computador pessoal. Na <a href="https://dev.to/msc2020/parte-2-aplicacao-flask-contendo-recursos-de-iaml-desenvolvimento-e-deploy-gratuitos-eaf-temp-slug-4881058?preview=fa03808150c88ba26d4d9eb19efb2f5242740fda2f5d533069bce37c5f37da90a2dcc4fa33a26528fca32523cfc80d3d9ce8c85f75c5a2153dee6d3b" >PARTE 2 </a>veremos como disponibilizar o app na Internet.