Aplicación de Datos

¡tú puedes!

Presentación por Gabriela "Gaba" Rodriguez / @gaba / Open News Fellow 2014

¿Qué es una aplicación de noticias?

¿Qué vamos a hacer?

Jugar con conjunto de datos desde http://espanaenllamas.es/

http://gabelula.github.io/first-news-app-espanaenllamas/build/

Pre-requisitos

Linea de Comandos

En Windows: prompt

En Linux y Mac: Console y Terminal

Editor de Texto

Un editor de texto plano. No sirve Word pues agrega formato y otras cosas al texto que no necesitamos. Recomendaciones: notepad, sublime, gedit

Git y GitHub

En Windows http://windows.github.com

En Mac http://mac.github.com

Crear cuenta en http://github.com

Python - version 2.7

En Windows https://www.python.org/download/windows

Instrucciones http://docs.python-guide.org/en/latest/starting/install/win/

Pip o easy_install

un manejador de packetes para python

Instrucciones para windows: http://docs.python-guide.org/en/latest/starting/install/win/

En Mac: $ brew install pip

En Linux: $ apt-get install pip

virtualenv

manejador de ambientes virtuales, para tener control sobre el software instalado y usado para crear la aplicación

En Windows: $ c:\Python27\Scripts\pip install virtualenv

En Mac: $ pip install virtualenv

Empezando por crear el ambiente

Abrir la terminal, ir a donde se quiere crear la aplicación y crear un ambiente virtual llamado mi-app.

$ virtualenv mi-app

Activar el ambiente virtual

En Mac/Linux


							$ cd mi-app
						

							$ . bin/activate
						

En Windows


							$ cd Scripts
						

							$ activate
						

							$ cd ..
						

¡Hola Git! Crear repositorio GIT local


							$ git init repo
						

							$ cd repo
						

Enviar el repo a GitHub

En Windows, abrir GitHub y publicar repositorio en GitHub..

En Linux y Mac, crear repositorio en GitHub. Ir a tu directorio local (repo) y agregar el repositorio remoto recien creado.

git remote add origin https://github.com/mi-usuario-en-github/mi-app-datos.git

Crear tu primer archivo

un archivo en blanco README con una extension Markdown

En Mac o Linux

$ touch README.md

En Windows

$ start notepad++ README.md

Abrir README.md en tu editor preferido

Escribir algo como

							
									Mi primera aplicación de datos
									==============================
							
						

Guardarlo.

Agregarlo a Git

							
								$ git add README.md
							
						
							
								$ git commit -m "Primer commit"
							
						
							
								$ git push origin master
							
						

¡Hola Flask!

un micro framework para crear aplicaciones web en python

Usar pip para instalar Flask

$ pip install Flask

Crear archivo app.py

para configurar Flask

							
								# Mac y Linux:
								$ touch app.py
							
						

								# Windows:
								$ start notepad++ app.py
							
						

Abrir app.py en un editor

							
								from flask import Flask
								app = Flask(__name__)
							
						

Configurar para hacer la raíz de tu sitio

Usaremos un archivo llamado 'index.html' para colocar la información. Tenemos que especificarlo en app.py

							
								from flask import Flask
								from flask import render_template
								app = Flask(__name__)

								@app.route("/")
								def index():
								    return render_template('index.html')
							
						

Ir a linea de comandos

crear directorio templates en el mismo repositorio en que estamos.

							
								$ mkdir templates
							
						

Crear index.html

dentro del directorio templates

							
								# Macs y Linux:
								$ touch templates/index.html
							
						

								# Windows:
								$ start notepad++ templates/index.html
							
						

Volver a editar app.py

y agregarle lo siguiente para poder levantar el servidor local de Flask

							
								from flask import Flask
								from flask import render_template
								app = Flask(__name__)

								@app.route("/")
								def index():
								    return render_template('index.html')

								if __name__ == '__main__':
								    app.run(
								        host="0.0.0.0",
								        port=8000,
								        use_reloader=True,
								        debug=True,
								    )
							
						

Correr app.py en la linea de comandos

Mirar el resultado en un navegador en http://localhost:8000

							
								$ python app.py
							
						

Agrega tu trabajo a GIT

cancela lo que este corriendo de app.py (CTR-C) en la terminal

							
								$ git add .
							
						
							
								$ git commit -m "Flask app.py and first template"
							
						
							
								$ git push origin master
							
						

¡Hola HTML!

Comenzar nuevamente editando index.html. Reemplazar el contenido por el esqueleto de un archivo HTML


								
									<!doctype html>
									<html lang="es">
									    <head></head>
									    <body>
									        <h1>Incendios en España entre 2004 y 2014</h1>
									    </body>
									</html>
								

						

Commitear los cambios al repositorio

							
								$ git add templates/index.html
							
						
							
								$ git commit -m "Real HTML"
							
						
							
								$ git push origin master
							
						

Crear directorio para guardar archivos estáticos

Aqui guardaremos el CSV con nuestros datos

							
								$ mkdir static
							
						

Guardar archivo CSV

descargarlo desde https://github.com/gabelula/first-news-app-espanaenllamas/, carpeta static, archivo incendios.csv y guardarlo en directorio static. Agregarlo a git

							
								$ git add static
							
						
							
								$ git commit -m "Agregando fuente de datos CSV"
							
						
							
								$ git push origin master
							
						

Abrir app.py en editor de texto

Usaremos libreria csv para acceder a nuestros datos

							
								import csv
								from flask import Flask
								from flask import render_template
								app = Flask(__name__)

								csv_path = './static/incendio.csv'
								csv_obj = csv.DictReader(open(csv_path, 'r'))
								csv_list = list(csv_obj)

								@app.route("/")
								def index():
								    return render_template('index.html')

								if __name__ == '__main__':
								    app.run(
								        host="0.0.0.0",
								        port=8000,
								        use_reloader=True,
								        debug=True,
								    )

							
						

Pasarle la lista de datos del csv a index.html

							
								import csv
								from flask import Flask
								from flask import render_template
								app = Flask(__name__)

								csv_path = './static/la-riots-deaths.csv'
								csv_obj = csv.DictReader(open(csv_path, 'r'))
								csv_list = list(csv_obj)

								@app.route("/")
								def index():
								    return render_template('index.html',
								        object_list=csv_list,
								    )

								if __name__ == '__main__':
								    app.run(
								        host="0.0.0.0",
								        port=8000,
								        use_reloader=True,
								        debug=True,
								    )

							
						

Guardar app.py y editar index.html

Colocar la lista del csv en el index.html

								
									<!doctype html>
									<html lang="es">
											<head></head>
											<body>
													<h1>Incendios en España entre 2004 y 2014</h1>
													{{ object_list }}
											</body>
									</html>
								
						

En linea de comandos correr app.py y visitar http://localhost:8000 nuevamente

Vamos a darle formato a los datos en index.html

Estamos usando el lenguaje de templating jinja de Flask

<!doctype html> <html lang="es"> <head></head> <body> <h1>Incendios en España entre 2004 y 2014</h1> <table border=1 cellpadding=7> <tr> <th>Comunidad</th> </tr> {% for obj in object_list %} <tr> <td>{{ obj['COMUNIDAD'].decode('UTF-8') }}</td> </tr> {% endfor %} </table> </body> </html>

Refrescar el navegador con nuestra aplicación

Colocaremos más datos en la table.

<table border=1 cellpadding=7> <tr> <th>Superficie Forestal Quemada</th> <th>Fecha</th> <th>Muertos</th> <th>Heridos</th> <th>Comunidad</th> <th>Provincia</th> <th>Comarca</th> <th>Causa</th> <th>Perdidas</th> </tr> {% for obj in object_list %} <tr> <td>{{ obj['SUPQUEMADA'] }}</td> <td>{{ obj['FECHA'] }}</td> <td>{{ obj['MUERTOS'] }}</td> <td>{{ obj['HERIDOS'] }}</td> <td>{{ obj['COMUNIDAD'].decode('UTF-8') }}</td> <td>{{ obj['PROVINCIA'].decode('UTF-8') }}</td> <td>{{ obj['COMARCA'].decode('UTF-8') }}</td> <td>{{ obj['CAUSA'].decode('UTF-8') }}</td> <td>{{ obj['PERDIDAS'] }}</td> </tr> {% endfor %} </table>

Refrescar el navegador

y luego commitear tu trabajo

							
								$ git add .
								$ git commit -m "Creamos tabla de datos"
								$ git push origin master
							
						

Agreguemos página de detalles por incendio

Agregamos una nueva ruta en app.py

							
								import csv
								from flask import Flask
								from flask import render_template
								app = Flask(__name__)

								...

								@app.route("/")
								def index():
								    return render_template('index.html',
								                           object_list=csv_list
								                          )

								@app.route('//')
								def detail(number):
								    return render_template('detail.html')

								if __name__ == '__main__':
								  ...

							
						

Agreguemos el template detail.html

							
								# Macs y Linux:
								$ touch templates/detail.html
							
						
							
								# Windows:
								$ start notepad++ templates/detail.html
							
						

Editamos el archivo detail.html

Agregar algo simple. Y levantamos la página en el navegador con cualquier número. http://localhost:8000/2004210126/

							
								¡Hola España!
							
						

Buscamos el incendio

Conectamos el número en la URL con el número identificador real del incendio en el CSV.

Editamos app.py y convertimos la lista que tenemos en algo donde el incendio sea fácilmente buscable por identificador

							
								csv_path = './static/incendios.csv'
								csv_obj = csv.DictReader(open(csv_path, 'r'))
								csv_list = list(csv_obj)
								csv_dict = dict([[o['IDPIF'], o] for o in csv_list])
							
						

Editamos la función detail en app.py

para que conecte el número en la URL con el registro correspondiente.

							
								@app.route('//')
								def detail(number):
								    return render_template('detail.html',
								        object=csv_dict[number],
								    )
							
						

Volver a editar detail.html

<!doctype html> <html lang="es"> <head> <meta name="Incendios en España" content="text/html;" http-equiv="content-type" charset="utf-8"> </head> <body> <h1>{{ object['COMUNIDAD'].decode('UTF-8') }}</h1> </body> </html>

Reiniciar el servidor y refrescar el navegador del incendio

http://localhost:8000/2004210126/

							
								$ python app.py
							
						

Editar index.html para enlazar la página del incendio

En la tag table reemplazar la fila de

<td>{{ obj['IDPIF'] }}</td> por

<td><a href="{{ obj['IDPIF'] }}/">{{ obj['IDPIF'] }}</a></td>

Reiniciar el servidor de pruebas y refrescar el navegador

http://localhost:8000

							
								$ python app.py
							
						

Agregar el resto de los campos del incendio

en detail.html

<body> <h1>Incendio de {{ object['COMUNIDAD'].decode('UTF-8') }}</h1> <p>En la comunidad {{ object['COMUNIDAD'].decode('UTF-8') }}, provincia {{ object['PROVINCIA'].decode('UTF-8') }}, comarca {{ object['COMARCA'].decode('UTF-8') }}, municipio {{ object['MUNICIPIO'].decode('UTF-8') }} se quemó una superficie forestal de {{ object['SUPQUEMADA'] }}. Hubieron {{ object['MUERTOS'] }} muertos y {{ object['HERIDOS'] }} heridos. Se detectó en la fecha {{ object['FECHA'] }}. Se pudo controlar en {{ object['TIME_CTRL'] }} minutos y extinguir en {{ object['TIME_EXT'] }} minutos. La causa del incendio fue {{ object['CAUSA'].decode('UTF-8') }}. En la extinción del incendio participaron {{ object['PERSONAL'] }} personas, {{ object['PESADOS'] }} vehiculos pesados y {{ object['AEREOS'] }} medios aereos.</p> </body>

Commitear en GIT

							
								$ git add .
								$ git commit -m "Creamos una página por incendio."
								$ git push origin master
							
						

¡Hola Javascript!

Ahora vamos a colocar un mapa con los incendios usando una libreria de Javascript llamada Leaflet. Primero hay que importarla en el index.html

<head> <meta name="Incendios en España" content="text/html;" http-equiv="content-type" charset="utf-8"> <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.css" /> <script type="text/javascript" src="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.js?2"></script> </head>

Crear un elemento HTML para el mapa

y usar Leaflet para centrarlo en España

							
								
									
... aca esta la table ...

Transformar los datos que tenemos en GeoJSON, que lea Leaflet

Lo hacemos en el mismo Javascript

GeoJSON es un formato para guardar estructuras de datos geograficos junto a información no geografica

							
								    
							
						

Agregar una ventanita popup que muestre información de la superficie quemada

Sustituir

							
								var dataLayer = L.geoJson(data);
							
						

por

							
					      var dataLayer = L.geoJson(data, {
					          onEachFeature: function(feature, layer) {
					              layer.bindPopup(feature.properties.causa);
					          }
					      });
							
						

Y ahora envolver la superficie por un link al incendio

Sustituir lo anterior por

							
								            var dataLayer = L.geoJson(data, {
                onEachFeature: function(feature, layer) {
                    layer.bindPopup(
                        '' +
                            feature.properties.causa +
                        ''
                    );
                }
            });
							
						

Comitear los cambios

							
								$ git add .
								$ git commit -m "Agregamos un mapa a la página principal."
								$ git push origin master
							
						

Editar detail.html

para agregar un mapa especifico del incendio.

<!doctype html> <html lang="es"> <head> <meta name="Incendios en España" content="text/html;" http-equiv="content-type" charset="utf-8"> <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.css" /> <script type="text/javascript" src="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.js?2"></script> </head> <body> <div id="map" style="width:100%; height:300px;"></div> <h1>Incendio de {{ object['COMUNIDAD'].decode('UTF-8') }}</h1> <p>En la comunidad {{ object['COMUNIDAD'].decode('UTF-8') }}, provincia {{ object['PROVINCIA'].decode('UTF-8') }}, comarca {{ object['COMARCA'].decode('UTF-8') }}, municipio {{ object['MUNICIPIO'].decode('UTF-8') }} se quemó una superficie forestal de {{ object['SUPQUEMADA'] }}. Hubieron {{ object['MUERTOS'] }} muertos y {{ object['HERIDOS'] }} heridos. Se detectó en la fecha {{ object['FECHA'] }}. Se pudo controlar en {{ object['TIME_CTRL'] }} minutos y extinguir en {{ object['TIME_EXT'] }} minutos. La causa del incendio fue {{ object['CAUSA'].decode('UTF-8') }}. En la extinción del incendio participaron {{ object['PERSONAL'] }} personas, {{ object['PESADOS'] }} vehiculos pesados y {{ object['AEREOS'] }} medios aereos.</p> <script type="text/javascript"> var map = L.map('map').setView([{{ object['LONGITUD'] }}, {{ object['LATITUD'] }}], 16); var mapquestLayer = new L.TileLayer('http://{s}.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.png', { maxZoom: 18, attribution: 'Data, imagery and map information provided by <a href="http://open.mapquest.co.uk" target="_blank">MapQuest</a>,<a href="http://www.openstreetmap.org/" target="_blank">OpenStreetMap</a> and contributors.', subdomains: ['otile1','otile2','otile3','otile4'] }); map.addLayer(mapquestLayer); var marker = L.marker([{{ object['LONGITUD'] }}, {{ object['LATITUD'] }}]).addTo(map); </script> </body> </html>

Comitear los cambios

							
								$ git add .
								$ git commit -m "Agregamos un mapa a la página de detalle."
								$ git push origin master
							
						

¡Hola Internet!

Ahora publicaremos nuestra aplicación de datos en Internet. Vamos a usar Frozen Flask, una libreria de Python que crea una página estatica por cada página que hicimos.

Usar pip para instalar Frosen Flask

							
								$ pip install Frozen-Flask
							
						

Crear un archivo freeze.py

							
								# Mac y Linux:
								$ touch freeze.py
								# Windows:
								$ start notepad++ freeze.py
							
						

Editar freeze.py

							
								from flask_frozen import Freezer
								from app import app
								freezer = Freezer(app)

								if __name__ == '__main__':
								    freezer.freeze()
							
						

Correrlo desde linea de comandos

Esto creara un directorio build con un monton de archivos estaticos.

							
								$ python freeze.py
							
						

Usar el navegador

para mirar uno de los archivos generados en build. Lo único que paso a estaticos fueron las páginas de index.html. Tenemos que editar freeze.py para incluir detail.html

							
								from flask_frozen import Freezer
								from app import app, csv_list
								freezer = Freezer(app)

								@freezer.register_generator
								def detail():
								    for row in csv_list:
								        yield {'number': row['IDPIF']}

								if __name__ == '__main__':
								    freezer.freeze()
							
						

Correrlo desde linea de comandos

Ahora se generan muchos más archivos en build.

							
								$ python freeze.py
							
						

Comiteamos los cambios

							
								$ git add .
								$ git commit -m "Congelamos la app"
								$ git push origin master
							
						

Finalmente publiquemos la app en github-pages

Para esto sólo tenemos que crear un branch gh-pages en nuestro repositorio.

							
								$ git checkout -b gh-pages # Create the new branch
								$ git merge master # Pull in all the code from the master branch
								$ git push origin gh-pages # Push up to GitHub from your new branch
							
						

Luego de unos minutos tu app estara publicada en

http://tunombredeusuario.github.io/mi-app/build/index.html

Tutoriales y Referencias en linea

  1. Git y GitHub Pro GIT.
  2. Esta presentación en mi primera aplicación de datos
  3. La inspiración para este taller viene del mismo dado por Ben Welsh durante NICAR 2014.
  4. Tutorial de Flask (en ingles)
  5. Documentación sobre Python y Pip
  6. Leaflet
  7. Como hostear paginas en Github Pages

¿Comentarios?

Por @gaba