lindev.fr

Aller au contenu | Aller au menu | Aller à la recherche

15 sept. 2014

Réparer son virtualenv aprés un update Systéme/Libs

Après une belle maj de votre système, vous avez, au moment de vous remettre au travail, un sympathique message du genre

ImportError: No module named datetime

detatime !! Cette lib fait pourtant partie du standard de python ! Le fait est que lorsque l'on met à jour python, les liens utilisés à la création du venv sont hs !

Ici c'est la lib datetime qui a été remonté, mais ça peu être n'importe laquelle ..

Solution

Il ne m'a pas fallu longtemps pour résoudre ce problème tout bête, ( beaucoup de cas similaires sur le net ).

Réinitialiser le virtualenv

Par exemple avec mon venv nommé foo

virtualenv /home/cdsl/.virtualenvs/foo
New python executable in /home/foo/.virtualenvs/creasoft3/bin/python
Installing setuptools............done.
Installing pip...............done.

Et voilà ..

27 août 2014

Django déploiement avec Nginx Gunicorn et Supervisord

Je vois souvent passer des questions sur le forums ou autres fils de discussion concernant la mise en production d'un projet Django.
Ici je vais présenter une façon de faire, utilisant Nginx Gunicorn et Supervisord .

Prérequis

Sur une Débian toute fraiche,

Nginx

sudo apt-get install nginx

Virtualenv

Car nous travaillons toujours avec un environnement virtuel, dans lequel nous allons y installer les dépendances de notre projet.
Ce qui permet de ne pas avoir de conflit de version avec d'autres éventuels projets déjà en prod.

pip install virtualenv virtualenvwrapper

Note : Si vous n'avez pas l'outil pip de disponible, installez-le comme ceci

sudo apt-get install python-setuptools
sudo easy_install pip

Puis dans votre fichier ~/.bashrc , ajoutez les lignes suivantes, pour l'autocompletion

export VIRTUALENVWRAPPER_VIRTUALENV_ARGS='--no-site-package'
export WORKON_HOME=$HOME/.virtualenvs
source /usr/local/bin/virtualenvwrapper.sh

Gunicorn

Pourquoi pas utiliser uWSGI ? humm ... car je préfère Gunicorn qui me donne toute satisfaction sur mes projets en prod. donc ... pourquoi pas !

sudo apt-get install libevent-dev
pip install gunicorn

Supervisor

Installation simple :

sudo pip install supervisor

Récupération du fichier de conf par défaut

echo_supervisord_conf > /etc/supervisord.conf

Dans le fichier de conf, afin de pouvoir manager votre projet via l'interface web, il faut dé-commenter et paramétrer les lignes suivantes

[inet_http_server]         ; inet (TCP) server disabled by default
port=*:9001        ; (ip_address:port specifier, *:port for all iface)
username=user              ; (default is no username (open server))
password=123               ; (default is no password (open server))

Note: Changer le username et password évidemment

L'interface web sera disponible à l'adresse : http://ipduserveur:9001

Pour le lancement automatique au démarrage du système, vous pouvez utiliser ces scripts d'init:

  1. debian init supervisor
  2. debian init supervisor

Installer votre projet

Notre environnement est prêt, nous allons commencer par installer le projet Django dans l’environnement virtuel.

Création du virtualenv

mkvirtualenv monprojet

Si vous utilisez git, cloner votre projet, sinon, copier le à l'endroit que vous souhaitez, ( pour l'exemple ce sera /var/www/monprojet )

Installation des dépendances du projet

si vous utilisez également les environnements virtuels pour développer ( ce que je conseille ) vous pouvez alors enregistrer la liste des libs python installées dans cette environnement, afin de pouvoir également les installer ( avec la même version ) sur un autre serveur.

Pour avoir la liste des libs :

pip freeze > requirements.txt

puis sur votre serveur, pour installer les libs depuis un export freeze :

pip install -r requirements.txt

Configuration de Django

Il vous faudra peut-être toucher un peu à votre fichier settings.py pour le passer du mode debug au mode production.
Personnellement j'utilise une autre subtilité qui me permet de ne pas avoir à toucher au fichier settings.py ( j'expliquerai celà dans un autre billet ) .

N'oubliez pas de vérifier le paramètre STATIC_ROOT

STATIC_ROOT = '/var/www/static_monprojet/'

C'est le répertoire ou seront copiés les fichiers statiques du projet, pour ensuite être servis par Nginx

Ce répertoire DOIT EXISTER

Une fois le répertoire créé, nous allons y "placer/lier" les fichiers statiques du projet

python manage.py collectstatic --link

Perso je préfère y mettre des liens symboliques .. ( les gouts et les couleurs ... )

Liaison du projet avec Gunicorn

Nous allons créer un fichier dans notre projet qui sera utilisé pour le lancement du/des process Gunicorn ( vous devrez adapter les valeurs dans ce script )

vim /var/www/monprojet/monprojet/gunicorn.sh

Et voici le contenu

#!/bin/bash
  set -e
  NUM_WORKERS=2
  USER=www-data
  GROUP=www-data
  ADDRESS=127.0.0.1:5002
  cd /var/www/monprojet
  source /home/monuser/.virtualenvs/monprojet/bin/activate
  exec gunicorn monprojet.wsgi:application -w $NUM_WORKERS --bind=$ADDRESS \
    --user=$USER --group=$GROUP --log-level=debug

Puis on le rend exécutable

chmod 777 /var/www/monprojet/monprojet/gunicorn.sh

Configuration Supervisord

Notre projet est prêt, afin de lancer Gunicorn automatiquement, nous allons utiliser supervisord, qui en plus de s'occuper de démarrer le projet automatiquement au démarrage du système, va aussi le relancer en cas de crash, gérer les logs et vous donner la main pour arrêter ré-demarrer les process, via l'interface web ou en mode console.

supervisord.png

Ajoutez à la fin du fichier de configuration /etc/supervisord.conf les lignes suivantes ( paramètres à adapter selon votre cas )

[program:guni_monprojet]
directory=/var/www/monprojet/monprojet
user = www-data
autostart=true
autorestart=true
stdout_logfile=/var/log/monprojet.log
redirect_stderr=true
stopsignal=QUIT
command = /var/www/monprojet/monprojet/monprojet.sh

Ne reste plus qu'à redémarrer supervisor pour prendre en compte la nouvelle config.

Pour le lancer manuellement

sudo supervisord -c /etc/supervisord.conf

Vhost Nginx

Dernier point, la création du vhost de Nginx, pour diriger les requêtes vers Gunicorn qui écoute sur le port 5002 ( il est aussi possible d'utiliser un fichier socket à la place )

vim /etc/nginx/sites-enabled/monprojet

Et voilà le contenu ( fonctionnel, mais vous pouvez l'adapter )

upstream us_monprojet {
        server 127.0.0.1:5002;
}


server {
        listen 80;

        root /var/www/monprojet;

        gzip             on;
        gzip_min_length  1000;
        gzip_proxied     expired no-cache no-store private auth;
        gzip_types       text/plain application/xml text/css text/javascript application/x-javascript application/x-shockwave-flash video/x-flv;
        gzip_disable     "MSIE [1-6]\.";


        server_name monprojet.com;
        charset utf-8;

        client_max_body_size 75M;


        location ~ /\.ht {
            deny  all;
        }

        location /favicon.ico {
                alias /var/www/monprojet/monprojet/static/favicon.ico;

                if (-f $request_filename) {
                        access_log off;
                        expires max;
                }

        }


        location /media {
                alias /var/www/monprojet/monprojet/media;

                if (-f $request_filename) {
                        access_log off;
                        expires max;
                }

        }

        location /static {
                alias /var/www/static_monprojet/;

                if (-f $request_filename) {
                        access_log off;
                        expires max;
                }

        }
location / {

                if (-f $request_filename) {
                        access_log off;
                        expires max;
                }       

                #gunicornParams
                if (!-f $request_filename) {
                        proxy_pass         http://us_monprojet;
                        break;
                }       
                proxy_redirect     off;
                proxy_set_header   Host             $host;
                proxy_set_header   X-Real-IP        $remote_addr;
                proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;

        }       
}       

Ne reste plus qu'à activer le vhost, et relancer nginx

ln -s /etc/nginx/sites-available/monprojet /etc/nginx/sites-enabled/
service nginx reload

01 août 2014

Proxy Socks sous android NON Rooté

En cette période de vacances, nous sommes nombreux à nous connecter depuis nos terminaux ( smartphone, tablette .. ) pour surfer, parfois travailler , dépanner .
Il est vrai que ces appareils sont formidables sur ce point ( le coté nomade ). Mais le revers de la médaille, c'est la sécurité !
Lorsque vous vous connectez à une borne wifi ( gratuite ou non ), qui vous assure que votre navigation n'est pas épiée ?
Voilà pour le coté sécurité, mais il peut être aussi utile de se connecter à son réseau local depuis l'extérieur.
Nous allons pour cela nous connecter via un serveur proxy de type socks.
Maintenant peu importe ce qui motive l'utilisation d'une telle connexion, voyons comment faire pour nous y connecter depuis un client Android non chrooté

Les apps à installer

  1. juiceSSH
  2. Firefox

JuiceSSH est un client SSH qui fonctionne vraiment bien, gratuit pour les fonctionnalités de base, mais payant pour faire une redirection de port. Vous allez donc devoir l'acheter, mais l'investissement en vaut la peine.
D'autres ont utilisé ConnectBOT qui lui est à 100% gratuit, mais de mon coté il plantait régulièrement je n'ai donc pas insisté.

Configuration de juiceSSH

Nous allons commencer par créer une connexion SSH de base, le tout en image :

2014_08_01_12.06.59.png

2014_08_01_12.08.03.png

  • Nickname : nom de la connexion
  • Type : SSH
  • Adresse : Adresse du serveur SSH
  • Identity : Login pour se connecter au serveur SSH
  • Port : port à utiliser pour se connecter au serveur SSH

Configuration de la redirection de port

Allez dans l'onglet PORT FORWARD, et nous allons utiliser la connexion SSH précédemment crée, comme ceci.

2014_08_01_12.08.40.png

Il est ensuite possible de mettre une icône sur votre page principale, pour se connecter plus rapidement au serveur proxy SOCKS

2014_08_01_12.08.57.png

Configuration de Firefox

Pour utiliser le serveur proxy ( après s'y être connecté ), dans firefox, ouvrez un nouvel onglet et entrez dans la barre d'adresse :

about:config


Voici les 5 paramètres à configurer comme ceci

2014_08_01_12.05.51.png 2014_08_01_12.06.11.png

Tests

Voilà maintenant vous pouvez tester le bon fonctionnement en entrant l'url suivante : http://www.whatismyip.com/, qui devrait vous afficher l'ip du serveur SSH à partir duquel vos requêtes sont envoyés .

Bonnes vacances .

16 juil. 2014

Django, générer du xlsx

Les exports ... on est tous amené dans un projet à devoir se farcir des exports de données pour de l'analyse comptable, statistiques ou de l'analyse de production.

Bien souvent je ne cherchai pas trop loin pour sortir les données ( il faut dire que ce n'est pas ce qu'il y a de plus sexy comme travail ), j'optais pour un vulgaire fichier CSV!
Bien pratique, et rapide à sortir mais il faut avouer que ce n'est pas ce qu'il y a de plus présentable aux clients finaux, et il faut vendre le fait que la personne qui va exploiter ce genre d'export, va devoir commencer par structurer le fichier csv pour le rendre exploitable . ( et je ne parle même pas des encodages ! )

Bref ... ça fait porc !

xlsxwriter

Je me suis donc tourné vers une lib très bien documentée et qui fonctionne à merveille . xlsxwriter
Qui plus est très rapide à mettre en place et à prendre en main .

voyez vous même avec l'exemple N°1 de la documentation

import xlsxwriter

# Create a workbook and add a worksheet.
workbook = xlsxwriter.Workbook('Expenses01.xlsx')
worksheet = workbook.add_worksheet()

# Some data we want to write to the worksheet.
expenses = (
    ['Rent', 1000],
    ['Gas',   100],
    ['Food',  300],
    ['Gym',    50],
)

# Start from the first cell. Rows and columns are zero indexed.
row = 0
col = 0

# Iterate over the data and write it out row by row.
for item, cost in (expenses):
    worksheet.write(row, col,     item)
    worksheet.write(row, col + 1, cost)
    row += 1

# Write a total using a formula.
worksheet.write(row, 0, 'Total')
worksheet.write(row, 1, '=SUM(B1:B4)')

workbook.close()

Voilà pour la lib xlsxwriter, je ne vais pas vous faire un tutoriel de la doc qui est très bien faite .
Par contre, regardons comment l'intégrer à une vue Django .

Django

L'objectif, est de générer le fichier xlsx ci-dessus, depuis une vue Django, mais sans enregistrer le fichier sur le disque du serveur pour ensuite le servir, non, nous allons le générer en mémoire et l'envoyer avec les entêtes qui vont bien pour avoir une belle boite de dialogue comme ceci

xlsx.png

StringIO et cStringIO

Afin d'éviter de générer un fichier sur le disque à chaque exportation, nous allons travailler en mémoire exclusivement.
Pour y parvenir, nous allons utiliser la lib StringIO ou cStringIO si elle est dispo.
StringIO permet de travailler sur un object qui réagit comme un fichier text ( read, write, ... ) sauf qu'on est en mémoire et non sur le disque .

Voilà à quoi va ressembler la vue Django :

try:
    from cStringIO import StringIO
except ImportError:
    from StringIO import StringIO
buffer=StringIO()

    
import xlsxwriter

def export(self):
    """ Export Démo  """

    # Create a workbook and add a worksheet.
    workbook = xlsxwriter.Workbook(buffer, {'constant_memory': True})
    worksheet = workbook.add_worksheet()

    # Some data we want to write to the worksheet.
    expenses = (
        ['Rent', 1000],
        ['Gas',   100],
        ['Food',  300],
        ['Gym',    50],
    )

    # Start from the first cell. Rows and columns are zero indexed.
    row = 0
    col = 0

    # Iterate over the data and write it out row by row.
    for item, cost in (expenses):
        worksheet.write(row, col,     item)
        worksheet.write(row, col + 1, cost)
        row += 1

    # Write a total using a formula.
    worksheet.write(row, 0, 'Total')
    worksheet.write(row, 1, '=SUM(B1:B4)')

    workbook.close()


    #Reponse envoyée au navigateur
    response = HttpResponse(buffer.getvalue(), mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
    response['Content-Disposition'] = "attachment; filename=test_export.xlsx"

    return response

Petite liste des types mime .. toujours pratique .

Et voilà le travail .. simple et efficace .
Maintenant ne vous reste plus qu'à mettre en forme vos exports afin qu'ils soient immédiatement exploitables par les principaux intéressés.

C'est pas grand chose, mais ça fait du bien de partager ;)
Ch.

30 juin 2014

Jquery.tableSorter, persistance du tri

Jquery TableSorter

Est un plugin jquery très efficace qui vous permet d'avoir les fonctions de tri sur un tableau en quelques lignes !
Sa mise en place est extrêmement simple, il reconnait pas mal de formats ( monétaire, date .. ) et pour couronner le tout, il est livré avec quelques de widgets intéressants pour aller encore plus loin .

Dans un récent développement, j'ai utilisé ce plugin pour pouvoir trier un tableau, mais j'avais besoin d'une persistance de ce tri dans le temps. J'ai été étonné qu'il n'y ai pas de widget en natif pour ce cas, mais comme ce plugin est bien fichu, il suffit d'étendre ses fonctionnalités avec un nouveau widget !
Je vous partage donc le code du widget qui me permet de sauvegarder le tri dans un cookie ( Dépend du plugin Jquery-cookie )

PS: si vous avez plusieurs tableaux, il y aura un enregistrement par tableau .

widget-sortPersist.js

;(function($){
"use strict";
var ts = $.tablesorter;

  ts.addWidget({
    // give the widget an id
    id: "sortPersist",
    // format is called when the on init and when a
    // sorting has finished
    format: function(table, thisWidget, c) {
 
      // Cookie info
      var cookieName = 'MY_SORT_COOKIE';
      var cookie = $.cookie(cookieName);
      var options = {path: '/'};
 
      var data = {};
      var sortList = table.config.sortList;
      var tableId = $(table).attr('id');
      var cookieExists = (typeof(cookie) != "undefined"
          && cookie != null);
 
      // If the existing sortList isn't empty, set it into the cookie
      // and get out
      if (sortList.length > 0) {
        if (cookieExists) {
          data = $.parseJSON(cookie);
        }
        data[tableId] = sortList;
        $.cookie(cookieName, JSON.stringify(data), options);
      }
 
      // Otherwise...
      else {
        if (cookieExists) {
 
          // Get the cookie data
          var data = $.parseJSON($.cookie(cookieName));
 
          // If it exists
          if (typeof(data[tableId]) != "undefined"
              && data[tableId] != null) {
 
            // Get the list
            sortList = data[tableId];
 
            // And finally, if the list is NOT empty, trigger
            // the sort with the new list
            if (sortList.length > 0) {
              $(table).trigger("sorton", [sortList]);
            }
          }
        }
      }
    }
  });
  

})(jQuery);

Pour l'utiliser, il vous suffit :

  1. d'inclure le widget dans votre page html
  2. d'activer le widget dans l'appel de tablesorter
$("#montableau").tablesorter({widgets: ['sortPersist']});

Attention

Attention ce code possède une dépendance : Jquery-cookie

Ch.

- page 1 de 28