Développement de pile complète - Récupération de données, visualisation avec D3 et déploiement avec Dokku

Développement de pile complète - Récupération de données, visualisation avec D3 et déploiement avec Dokku

Dans ce didacticiel, nous allons créer une application Web pour récupérer les données desNASDAQ-100 et les visualiser sous forme de graphique à bulles avec D3. Ensuite, pour couronner le tout, nous allons le déployer sur Digital Ocean via Dokku.

Les graphiques à bullesNote: sont parfaits pour visualiser des centaines de valeurs dans un petit espace. Cependant, ils sont plus difficiles à lire car il peut être difficile de différencier des cercles de taille similaire. Si vous travaillez avec seulement quelques valeurs, un graphique à barres est probablement la meilleure option car il est beaucoup plus facile à lire.

Principaux outils utilisés dans ce didacticiel: Python v2.7.8, Flask v0.10.1,Requests v2.4.1, D3 v3.4.11, Dokku v0.2.3 et Bower v1.3.9

Commencez par localiser et télécharger le fichier_app_boilerplate.zip à partir de cerepo. Ce fichier contient un passe-partout Flask. Une fois téléchargé, extrayez le fichier et les dossiers, activez un virtualenv, etinstall the dependencies with Pip:

pip install -r requirements.txt

Ensuite, testez pour vous assurer que cela fonctionne: lancez le serveur, ouvrez votre navigateur et accédez àhttp://localhost:5000/. Vous devriez voir "Bonjour tout le monde!" vous fixant.

Récupération des données

Créez une nouvelle route et une fonction d'affichage dans le fichierapp.py:

@app.route("/data")
def data():
    return jsonify(get_data())

Mettez à jour les importations:

from flask import Flask, render_template, jsonify
from stock_scraper import get_data

Ainsi, lorsque cette route est appelée, elle convertit la valeur retournée d'une fonction appeléeget_data() en JSON, puis la renvoie. Cette fonction réside dans un fichier appeléstock_scraper.py, ce qui - surprise! - récupère les données du NASDAQ-100.

Le script

Ajoutezstock_scraper.py au répertoire principal.

Your turn: créez le script vous-même, en suivant ces étapes:

  1. Téléchargez le CSV depuishttp://www.nasdaq.com/quotes/nasdaq-100-stocks.aspx?render=download.

  2. Récupérez les données pertinentes du CSV: nom du stock, symbole du stock, prix actuel, variation nette, variation en pourcentage, volume et valeur.

  3. Convertissez les données analysées en un dictionnaire Python.

  4. Renvoyez le dictionnaire.

Comment cela s'est-il passé? Besoin d'aide? Voyons une solution possible:

import csv
import requests


URL = "http://www.nasdaq.com/quotes/nasdaq-100-stocks.aspx?render=download"


def get_data():
    r = requests.get(URL)
    data = r.text
    RESULTS = {'children': []}
    for line in csv.DictReader(data.splitlines(), skipinitialspace=True):
        RESULTS['children'].append({
            'name': line['Name'],
            'symbol': line['Symbol'],
            'symbol': line['Symbol'],
            'price': line['lastsale'],
            'net_change': line['netchange'],
            'percent_change': line['pctchange'],
            'volume': line['share_volume'],
            'value': line['Nasdaq100_points']
        })
    return RESULTS

Que ce passe-t-il?

  1. Ici, nous récupérons l'URL via une requête GET puis convertissons l'objet Response,r, enunicode.

  2. Nous travaillons ensuite avec la bibliothèqueCSV pour convertir le texte délimité par des virgules en une instance de la classeDictReader(), qui mappe les données à un dictionnaire plutôt qu'à une liste.

  3. Enfin, après avoir parcouru les données en boucle, créé une liste de dictionnaires (où chaque dictionnaire représente un stock différent), nous renvoyons le dictRESULTS.

Note
Vous pouvez également utiliser une compréhension de dict pour créer les dictionnaires individuels. C'est une méthode beaucoup plus efficace, mais vous sacrifiez la lisibilité. Ton appel.

Time to test: lancez le serveur, puis accédez àhttp://localhost:5000/data. Si tout s'est bien passé, vous devriez voir un objet contenant les données de stock pertinentes.

Avec les données à portée de main, nous pouvons maintenant travailler à les visualiser sur le front-end.

Visualisation

Avec HTML et CSS, nous utiliseronsBootstrap, Javascript / jQuery etD3 pour alimenter notre front-end. Nous utiliserons également l'outil de gestion des dépendances côté clientBower pour télécharger et gérer ces bibliothèques.

Your turn: Suivez lesinstallation instructions pour configurer Bower sur votre machine. Hint: You will need to install Node.js before you install Bower.

Prêt?

Tonnelle

Deux fichiers sont nécessaires pour faire fonctionner le bower -bower.json et.http://bower.io/docs/config/[bowerrc].

Ce dernier fichier est utilisé pour configurer Bower. Ajoutez-le au répertoire principal:

{
  "directory": "static/bower_components"
}

Cela spécifie simplement que nous voulons que les dépendances soient installées dans le répertoirebower_components (convention) dans le répertoirestatic de l'application.

Pendant ce temps, le premier fichier,bower.json, stocke le manifeste Bower - ce qui signifie qu'il contient des métadonnées sur les composants Bower ainsi que sur l'application elle-même. Le fichier peut être créé de manière interactive avec la commandebower init. Faites ça maintenant. Acceptez simplement tous les paramètres par défaut.

Maintenant, nous pouvons installer les dépendances.

$ bower install bootstrap#3.2.0 jquery#2.1.1 d3#3.4.11 --save

L'indicateur--save ajoute les packages au tableau de dépendancesbower.json. Vérifiez-le. Assurez-vous également que les versions de dépendance dansbower.json correspondent aux versions que nous avons spécifiées, c'est-à-direbootstrap#3.20.

Une fois nos dépendances installées, rendons-les accessibles dans notre application.

Mettre à jourindex.html



  
    Flask Stock Visualizer
    
    
    
  
  
    

D3

Avec autant de frameworks de visualisation de données, pourquoiD3? Eh bien, D3 est un niveau assez bas, donc il vous permet de créer le type de framework que vous souhaitez. Une fois que vous avez ajouté vos données au DOM, vous utilisez une combinaison de CSS3, HTML5 et SVG pour créer la visualisation réelle. Ensuite, vous pouvez ajouter de l'interactivité grâce auxtransitions pilotés par les données intégrés de D3.

Pour être juste, cette bibliothèque n'est pas pour tout le monde. Comme vous avez beaucoup de liberté pour construire ce que vous voulez, la courbe d'apprentissage est assez élevée. Si vous recherchez un démarrage rapide, consultezPython-NVD3, qui est un wrapper pour D3, utilisé pour rendre le travail avec D3 beaucoup, beaucoup plus facile. Cependant, nous ne l'utilisons pas pour ce didacticiel, car Python-NVD3 ne prend pas en charge les graphiques à bulles.

Your turn: Parcourez les D3intro tutorial.

Passons maintenant au code.

Installer

Ajoutez le code suivant àmain.js:

// Custom JavaScript

$(function() {
  console.log('jquery is working!');
  createGraph();
});

function createGraph() {
  // Code goes here
}

Ici, après le chargement initial de la page, nous enregistrons «jquery is working!» Sur la console puis déclenchons une fonction appeléecreateGraph(). Testez cela. Lancez le serveur puis accédez àhttp://localhost:5000/ et avec la console JavaScript ouverte, actualisez la page. Vous devriez voir le texte «jquery fonctionne!» Si tout s'est bien passé.

Ajoutez la balise suivante au fichierindex.html, dans la balise<div> qui a unid decontainer (après la ligne 10), pour contenir le graphique à bulles D3:

Configuration principale

Ajoutez le code suivant à la fonctioncreateGraph() dansmain.js:

var width = 960; // chart width
var height = 700; // chart height
var format = d3.format(",d");  // convert value to integer
var color = d3.scale.category20();  // create ordinal scale with 20 colors
var sizeOfRadius = d3.scale.pow().domain([-100,100]).range([-50,50]);  // https://github.com/mbostock/d3/wiki/Quantitative-Scales#pow

Assurez-vous de consulter les commentaires de code pour une explantation ainsi que les D3documentationofficiels. Cherchez tout ce que vous ne comprenez pas. A coder must be self-reliant!

Configuration de bulle

var bubble = d3.layout.pack()
  .sort(null)  // disable sorting, use DOM tree traversal
  .size([width, height])  // chart layout size
  .padding(1)  // padding between circles
  .radius(function(d) { return 20 + (sizeOfRadius(d) * 30); });  // radius for each circle

Encore une fois, ajoutez le code ci-dessus à la fonctioncreateGraph() et vérifiez lesdocs pour toute question.

Configuration SVG

Ensuite, ajoutez le code suivant àcreateGraph(), qui sélectionne l'élément avec lesid dechart, puis ajoute les cercles avec un certain nombre d'attributs:

var svg = d3.select("#chart").append("svg")
  .attr("width", width)
  .attr("height", height)
  .attr("class", "bubble");

En continuant avec la fonctioncreateGraph(), nous devons maintenant récupérer les données, ce qui peut être fait de manière asynchrone avec D3.

Demander les données

// REQUEST THE DATA
d3.json("/data", function(error, quotes) {
  var node = svg.selectAll('.node')
    .data(bubble.nodes(quotes)
    .filter(function(d) { return !d.children; }))
    .enter().append('g')
    .attr('class', 'node')
    .attr('transform', function(d) { return 'translate(' + d.x + ',' + d.y + ')'});

    node.append('circle')
      .attr('r', function(d) { return d.r; })
      .style('fill', function(d) { return color(d.symbol); });

    node.append('text')
      .attr("dy", ".3em")
      .style('text-anchor', 'middle')
      .text(function(d) { return d.symbol; });
});

Nous avons donc atteint le point de terminaison/data que nous avons configuré précédemment pour renvoyer les données. Le reste de ce code ajoute simplement les bulles et le texte au DOM. Il s'agit du passe-partout standardcode, légèrement modifié pour nos données.

Info-bulles

Étant donné que nous avons peu de place sur le graphique, toujours dans la fonctioncreateGraph(), ajoutons des infobulles qui affichent des informations supplémentaires sur chaque action spécifique.

// tooltip config
var tooltip = d3.select("body")
  .append("div")
  .style("position", "absolute")
  .style("z-index", "10")
  .style("visibility", "hidden")
  .style("color", "white")
  .style("padding", "8px")
  .style("background-color", "rgba(0, 0, 0, 0.75)")
  .style("border-radius", "6px")
  .style("font", "12px sans-serif")
  .text("tooltip");

Ce ne sont que les styles CSS associés à l'infobulle. Nous devons encore ajouter les données réelles. Mettez à jour le code où nous ajoutons les cercles au DOM:

node.append("circle")
  .attr("r", function(d) { return d.r; })
  .style('fill', function(d) { return color(d.symbol); })

  .on("mouseover", function(d) {
    tooltip.text(d.name + ": $" + d.price);
    tooltip.style("visibility", "visible");
  })
  .on("mousemove", function() {
    return tooltip.style("top", (d3.event.pageY-10)+"px").style("left",(d3.event.pageX+10)+"px");
  })
  .on("mouseout", function(){return tooltip.style("visibility", "hidden");});

Testez ceci, accédez àhttp://localhost:5000/. Maintenant, lorsque vous passez la souris sur un cercle, vous verrez des métadonnées sous-jacentes - le nom de la société et le cours de l'action.

Your turn: ajoutez plus de métadonnées. Quelles autres données pensez-vous pertinentes? Pensez à ce que nous affichons ici - la variation relative des prix. Vous pourriez peut-être calculer le prix précédent et afficher:

  1. Prix ​​actuel

  2. Changement relatif

  3. Prix ​​précédent

Refactor

actions Et si nous voulions simplement visualiser les actions avec un indice modifié pondéré en fonction de la valeur marchande - le NASDAQ-100 Pointscolumn - supérieur à 0,1?

Ajoutez un conditionnel à la fonctionget_data():

def get_data():
    r = requests.get(URL)
    data = r.text
    RESULTS = {'children': []}
    for line in csv.DictReader(data.splitlines(), skipinitialspace=True):
        if float(line['Nasdaq100_points']) > .01:
            RESULTS['children'].append({
                'name': line['Name'],
                'symbol': line['Symbol'],
                'symbol': line['Symbol'],
                'price': line['lastsale'],
                'net_change': line['netchange'],
                'percent_change': line['pctchange'],
                'volume': line['share_volume'],
                'value': line['Nasdaq100_points']
            })
    return RESULTS

Maintenant, augmentons le rayon de chaque bulle, dans la section de configuration de bulle demain.js; modifiez le code en conséquence:

// Radius for each circle
.radius(function(d) { return 20 + (sizeOfRadius(d) * 60); });

CSS

Enfin, ajoutons quelques styles de base àmain.css:

body {
  padding-top: 20px;
  font: 12px sans-serif;
  font-weight: bold;
}

Tu as l'air bien? Prêt à déployer?

Déployer

Dokku est une plate-forme en tant que service (PaaS) open-source, semblable à Heroku, alimentée par Docker. Une fois la configuration effectuée, vous pouvez y pousser votre application avec Git.

Nous utilisonsDigital Ocean comme hôte. Commençons.

Configurer l'océan numérique

Sign up pour un compte, si vous n'en avez pas déjà un. Suivez ensuite cesguide pour ajouter une clé publique.

Créez une nouvelle droplet - spécifiez un nom, une taille et un emplacement. Pour l'image, cliquez sur l'onglet «Applications» et sélectionnez l'application Dokku. Assurez-vous de sélectionner votre clé SSH.

Une fois créé, terminez la configuration en entrant l'IP du Droplet nouvellement créé dans votre navigateur, ce qui vous amènera à l'écran de configuration de Dokku. Confirmez que la clé publique est correcte, puis cliquez sur «Terminer la configuration».

Maintenant, le VPS peut accepter les push.

Déployer la configuration

  1. Créez un fichier Proc avec le code suivant:web: gunicorn app:app. (Ce fichier contient la commande qui doit être exécutée pour démarrer le processus Web.)

  2. Installer gunicorn:pip install gunicorn

  3. Mettre à jour le fichierrequirements.txt:pip freeze > requirements.txt

  4. Initialiser un nouveau dépôt Git local:git init

  5. Ajouter une télécommande:git remote add dokku [email protected]:app_name (Assurez-vous d'ajouter votre propre adresse IP.)

Mettre à jourapp.py:

if __name__ == '__main__':
    port = int(os.environ.get('PORT', 5000))
    app.run(host='0.0.0.0', port=port)

Donc, nous essayons d'abord de récupérer le port de l'environnement de l'application, et s'il n'est pas trouvé, il prend par défaut le port 5000.

Assurez-vous également de mettre à jour les importations:

import os

Déployer!

Validez vos modifications, puis appuyez sur:git push dokku master. Si tout s'est bien passé, vous devriez voir l'URL de l'application dans votre terminal:

=====> Application deployed:
       http://192.241.208.61:49155

Testez-le. Accédez àhttp://192.241.208.61:49155. (Encore une fois, assurez-vous d'ajouter votre propre adresse IP avec le bon port.) Vous devriez voir votre application en direct! (Voir l'image en haut de ce post pour un aperçu.)

Prochaines étapes

Vous voulez faire passer cela au niveau supérieur? Ajoutez les fonctionnalités suivantes à l'application:

  1. La gestion des erreurs

  2. Tests unitaires

  3. Test d'intégration

  4. Intégration / livraison continue

Ces fonctionnalités (et bien plus!) Seront incluses dans la prochaine édition des coursReal Python, à venir début octobre 2014!

Commentez ci-dessous si vous avez des questions.

À votre santé!