Flask em 40 Minutos ou Menos

Me
Eu faço perguntas em reuniões que eu não sei nada e reunião explode; não é de propósito
Eu falo rárpido

Motivação

  • Flask é super simples, mas poderoso.
  • Python é super simples, mas poderoso.
  • Ninguém fala como é colocar isso em produção.

A aplicação

Um sistema de atas de reunião onde as atas são arquivos MarkDown, com a data no nome.

Estrutura de diretórios

├── ata
│   ├── defaults.py
│   ├── __init__.py
│   ├── main.py
│   ├── static
│   │   └── style.css
│   └── templates
│       ├── entry.html
│       ├── entry_not_found.html
│       ├── index.html
│       ├── layout.html
│       └── page_not_found.html
├── contents
│   └── 2017-10-31.md
├── setup.py
├── MANIFEST.in
└── requirements.txt

├── ata

Módulo/fontes.

│   ├── defaults.py

Valores default da aplicação.

│   ├── __init__.py

__init__.py é necessário para indicar ao Python que o diretório pode se lido.

│   ├── main.py

O arquivo inicial da aplicação.

│   ├── static

Arquivos estáticos.

│   └── templates

Arquivos de template.

├── contents

Onde o conteúdo a ser apresentado está.

└── requirements.txt

Dependências do projeto.

requirements.txt

flask~=0.12.2
Markdown~=2.6.9

main.py

Imports

import os
import os.path

import markdown

from flask import Flask
from flask import render_template

main.py

A App

app = Flask(__name__)

main.py

Configuração

app.config.from_object('ata.defaults')
app.config.from_envvar('ATA_SETTINGS', silent=True)

main.py

A primeira rota.

@app.route('/')
def index():

main.py

O índice

    content = os.listdir(app.config['STORAGE'])
    data = []
    for entry in sorted(content, reverse=True):
        app.logger.debug('Found %s', entry)
        if not entry.endswith('.md'):
            continue

        with open(os.path.join(app.config['STORAGE'], entry)) as origin:
            content = origin.read()
            output = markdown.markdown(content)
            try:
                first_paragraph_end = output.index('

') paragraph = output[:first_paragraph_end] except ValuError: paragraph = output entry_name = entry[:entry.find('.md')] data.append((entry_name, output)) return render_template('index.html', data=data)

main.py

Entradas específicas

@app.route('/<entry_name>')
def show_entry(entry_name):
    filename = os.path.join(app.config['STORAGE'], entry_name + '.md')
    with open(filename) as origin:
        content = origin.read()
        output = markdown.markdown(content)

    return render_template('entry.html', output=output,
                           entry=entry_name)

main.py

Tratamento de erros.

@app.errorhandler(404)
def page_not_found():
    return render_template('page_not_found.html')

main.py

Tratamento de erros

@app.errorhandler(FileNotFoundError)
def entry_not_found(_):
    return render_template('entry_not_found.html')

defaults.py

DEBUG=True
STORAGE='/home/jbiason/src/ata/contents'

style.css

h1#header {
    background-color: black;
    color: white;
    padding: 10px;
}

.entry {
    margin-left: 30px;
    margin-right: 30px;
    margin-bottom: 15px;
}

templates

layout.html

<!doctype html>
<html>
    <head>
        <title>Atas de Reunião</title>
        <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
    </head>

    <body>
        <h1 id='header'>
            Atas de reuniao
        </h1>
        {% block maincontent %}{% endblock %}
    </body>
</html>

templates

index.html

{% extends "layout.html" %}
	{% block maincontent %}
		<div class='entry'>
			{% for entry, text in data %}
			<a href="{{ url_for('show_entry', entry_name=entry) }}">{{ entry }}</a>
				{{ text|safe }}
			{% endfor %}
		</div>
	{% endblock %}

templates

entry.html

{% extends "layout.html" %}
	{% block maincontent %}
		<div class="entry">
			<div class="title">{{ entry }}</div>
			{{ output|safe }}
		</div>
	{% endblock %}

templates

page_not_found.html

{% extends "layout.html" %}
	{% block maincontent %}
		<h2>Page not found</h2>
	{% endblock %}

templates

entry_not_found.html

{% extends "layout.html" %}
	{% block maincontent %}
		<h2>Entry not found</h2>
	{% endblock %}

setup.py

from setuptools import setup

with open('requirements.txt') as origin:
    requirements = origin.readlines()

setup(name='Ata',
      version='0.1',
      long_description='A Flask app',
      packages=['ata'],
      zip_safe=False,
      include_package_data=True,
      install_requires=requirements)

MANIFEST.in

recursive-include ata/templates *
recursive-include ata/static *

Rodando

(Dev server)


FLASK_APP=ata/main.py flask run
						

export FLASK_RUN=ata/main
flask run
						

Rodando de verdade

Dependências de sistema

sudo dnf install nginx uwsgi
sudo yum install nginx uwsgi
sudo apt-get install nginx uwsgi

Perguntas?