Wilane' Weblog

jaZZ ... just another Zine Zblog

RFID

écrit par Ousmane Wilane, le 03/04/10 17:59.

Une petite présentation RFID avec le Kit Phidget comme application. Le Pdf est disponible ici et les sources Latex/Beamer sont ici.

Django 1.2 au frigo

écrit par Ousmane Wilane, le 06/01/10 09:53.

Django 1.2 alpha 1 a été publié hier et marque le début du gèle avec quelques fonctionnalités qui n’ont pas pu atterrir à temps dans le tronc (souvent pour de très bonnes raisons, ces gars sont des chefs ! Merci !). Vous pouvez jeter un coup d’oeil aux notes de publication

Protection CSRF

Pas mal de modifications en ces terres de sable mouvant. Si vous n’êtes pas intéressé par les détails il faut juste vous rappeler que par défaut vous aurez besoin de la tag csrf_token dans vos formulaires:

  <form method="POST">{% csrf_token %}
  {# comme d'habitude ici #}
  </form> 

Tag de template if

Plus besoin de grandes contorsions pour faire un peu de logiques d’affichage, la tag if est devenu super extra méga futée:

{% if mavar == "x" %}
  mavar est donc x. corollaire if(not)equal
{% endif %}

{% if mavar != "x" %}
  zut ! mavar n'est pas x
{% endif %}

{% if "bc" in "abcdef" %}
  Ben puisque "bc" est dans "abcdef"
{% endif %}

Vous pouvez utilisez les autres opérateurs classiques (<, >, <=, >=) telle que vous les connaissez par ailleurs. Notez que ces opérateurs peuvent être combiné pour exprimer des expressions complexes, il faudra alors faire attention à la précédence des opérateurs puise que les parenthèses ne sont toujours pas acceptées dans les tags de template.

Formats locaux avec I18N

Il est maintenant facile d’accéder aux formats sensible à la configuration locale:

from datetime import datetime
from django.utils import formats

formats.date_format(datetime.now(), 'DATETIME_FORMAT')
#u'Jan. 6, 2010, 9:22 a.m.'

date_format = formats.get_format('DATE_FORMAT')
datetime_format = formats.get_format('DATETIME_FORMAT')
time_format = formats.get_format('TIME_FORMAT')

date_format,datetime_format,time_format
#('N j, Y', 'N j, Y, P', 'P')

MultiDB

Si vous avez besoin d’accéder à plusieurs bases dans votre application, rien de plus simple, Django à fait peau neuve pour le plaisir de vos doigts et de vos yeux. En lieu et place des paramètres de configuration que vous connaissiez déjà il suffit juste de mettre:

DATABASES = {
    'default': {
        'NAME': 'mabase',
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'USER': 'toto',
        'PASSWORD': 'passerp0s'
    },
    'utilisateurs': {
        'NAME': 'mabaseuser'
        'ENGINE': 'django.db.backends.mysql',
        'USER': 'nono',
        'PASSWORD': 'passersurtoutp0s'
    }
}

# ET pour sauver dans la base mysql un objet par exemple:
mon_obj.save(using='mabaseuser')

Les cas d’utilisation sont légions, vous pouvez par exemple songer à une base répliquée en temps réel où les répliquât seraient utilisés comme base de lecture pour désengorger la base principale, vous pouvez aussi songer au sharding et autres bases de données hérités. Toute les API autour de l’ORM se sont mis sur leurs 31, 5 jours après la saint-sylvestre pour l’accueil de multidb pour votre grand plaisir.

Autres gâteries

  • Validation de modèles (utilisable en l’état avec les ModelForm)
  • Les modèles peuvent utiliser un BigInt 64 bit
  • Un bon raccourcis de sortie lorsque les tests rencontrent un pépin: Lève vite le camp !
  • Cache des templates
  • Votre EmailBackend si vous voulez faire vos trucs à vous
  • Le framework des Messages juste pétillant
  • Champs en lecture seul dans l’admin
  • … Et bien d’autres délires encore plus chanmé …

Le Bébé est attendu le 09/03/2010, merci à tous de tester et de remonter les anomalies constatées.

Django MultiDB

écrit par Ousmane Wilane, le 31/12/09 10:01.

Pour ceux qui ne suivent pas le développement de Django de près, le projet GSoc 2009 MultiDB de Alex Gaynor a foulé le sol du trunk, et comme un bonheur n’arrive jamais seul, PostgreSQL 8.5 alpha3 est publié avec un support natif d’une réplication maître-esclave.

Configuration séléctive avec WL

écrit par Ousmane Wilane, le 13/12/09 11:05.

Mon compte gmail est configuré pour me faire suivre tous mes courriels à mon adresse habituel. Donc en principe j’ai juste besoin de configurer mon serveur IMAP normal et je reçois tous les courriels qui passent par mes filtres .dovecot.sieve et sont rangés auto-magiquement dans les dossiers qui vont bien.

Pour mes courriels sortants il arrive que je veuille les envoyer depuis mon compte gmail (ce que je peut faire avec mon propre serveur mais il y a des chances que nombre de serveurs le considèrent alors comme un courriel indésirable, Google par exemple, soumet ces courriels à la modération). Je veut en particulier que tous les courriels que j’envoie vers le domaine googlegoups.com passent par les serveurs SMTP de Google. La plupart des ML qui m’intéressent sont maintenant sur ce domaine.

Avec WL on peut utiliser des comparaisons d’en-tête et même des expressions Lisp pour décider de la config du courriel sortant au moment où on fait C-c C-c, il suffit juste d’ajouter à votre .wl le bout de code elisp suivant:

(setq
 wl-template-alist
 '(("default"
    (wl-smtp-connection-type . 'starttls)
    (wl-smtp-authenticate-type . "plain"))
   ("gmail"
    ("From" . "NomComplet <MonLogin@gmail.com>")
    (template . "default")
    (wl-smtp-posting-user . "MonLogin")
    (wl-smtp-posting-server . "smtp.gmail.com")
    (wl-smtp-posting-port . 587))
   ("MonServeur"
    ("From" . "NomComplet <MonLogin@MonDomaine.MonTLD>")
    (wl-smtp-posting-user . "MonLogin")
    (wl-smtp-posting-server . "mon.serveur.smtp")
    (wl-smtp-posting-port . 587)))
 wl-draft-config-matchone t
 wl-draft-config-alist
 '(("^From: .*MonLogin@gmail\\.com"
    (template . "gmail"))
   (reply "^To: .*googlegroups\\.com"
    (template . "gmail"))
   ("^From: .*MonLogin@MonDomaine\\.MonTLD"
    (template . "MonServeur"))))

En gros je définit une liste contenant des modèles de configuration avec un modèle par défaut contenant le dénominateur commun de tous les autre modèles, ensuite je définit les règles d’utilisation de ces modèles en fonction de critères, et c’est tout.

Si vous souhaitez que ceci soit évalué lorsque vous préparez un brouillon:

(add-hook 'wl-mail-setup-hook 'wl-draft-config-exec)

Si vous souhaitez qu’il soit évalué après une ré-edition d’un brouillon (E) que vous auriez sauvegarder par exemple:

(add-hook 'wl-draft-reedit-hook 'wl-draft-config-exec)

Si vous décidez d’envoyer un brouillon (C-c C-c) et qu’ensuite vous annulez l’action pour ré-éditer le brouillon par exemple, si vous souhaitez ré-évaluer (wl-draft-config-alist), il vous suffit de faire C-c C-e (wl-draft-config-exec) explicitement.

Tout ceci est bien documenté en amérique. Vous remarquerez que les critères peuvent naturellement être définit par rapport au dossier de courriel dans lequel vous vous trouvez.

TwIt et l'historique

écrit par Ousmane Wilane, le 09/10/09 18:27.

Un autre truc que j’aimes avec certains clients Identica/Twitter c’est le fait qu’ils vous ramènent là où vous en étiez la dernière fois que vous avez lu les tweets. Pour faire la même chose avec TwIt, j’ai ajouté une fonction (dirty hack comme on dis) permettant de bookmarker un Tweet (sauvegarder son id/status twit-save-id-at-point) et une fonction permettant de charger les tweets depuis l’id bookmarqué twit-show-recent-tweets-from-last-id. La touche q qui est utilisé par défaut pour quitter est remplacée par une fonction twit-quit qui sauvegarde le tweet actuel avant d’invoquer burry-buffer:

  ("W" . twit-save-id-at-point)
  ("q" . twit-quit)

(defconst twit-friend-timeline-file-from-last-id
  (concat twit-base-url 
   "/statuses/friends_timeline.xml?count=200&since_id=%s"))
(defun twit-save-id-at-point ()
  (interactive)
  (setq twit-id (twit-get-text-property 'twit-id))
  " Let's store this in a file so we 
      can start it from here next time"
  (write-region twit-id nil "~/.twit-last-id")
)

(defun twit-quit ()
  " Let' do some pre-hook before burying the buffer"
  (interactive)
  (twit-save-id-at-point)
  (bury-buffer)
)


(defun twit-show-recent-tweets-from-last-id 
                             (&optional page)
  (interactive "P")
  (setq page (twit-check-page-prefix page))

  (with-temp-buffer 
   (setq retval (insert-file-contents "~/.twit-last-id") )
   (setq twit-id (buffer-string)))
  
  (pop-to-buffer
   (with-twit-buffer "*Twit-recent*"
     (twit-write-title "Recent Tweets (Page %s) [%s]\n"
       page (format-time-string "%c"))
     (twit-write-recent-tweets
       (twit-parse-xml (format twit-friend-timeline-\
                 file-from-last-id twit-id) "GET")))))

Trouvez le module patché ici

Navigation avec TwIt (Twitter mode pour Emacs)

écrit par Ousmane Wilane, le 05/10/09 16:49.

C’est pas un secret pour ceux qui me suivent sur Twitter (où je n’ai rien à dire en moyenne d’ailleurs, alors arrêtez de me suivre et suivez ceux qui ont des choses à dire et en plus ils sont légion) que j’utilise TwIt. Je suis bizarrement assez accroc à Twitter ces temps-ci, et lorsqu’il m’arrive de ne pas suivre mon Timeline pendant quelques temps, j’ai envie avec une seule touche de regarder les pages précédentes/suivantes jusqu’à la dernière page où je m’étais arrêté la dernière fois. Je peut utiliser C-u <page> M-x twit-show-recent-tweets mais c’est pas très pratique pour passer de la page suivante/précédente. J’ai ajouté quelques fonctions à twit.el pour utiliser les touches N et P pour passer naviguer entre les pages:

(defun twit-previous-page ()
  (interactive)
  (twit-jump-page 1)
)

(defun twit-next-page ()
  (interactive)
  (twit-jump-page -1)
)

(defun twit-jump-page (n)
  (goto-char 0)
  (if (re-search-forward 
        "Recent Tweets (Page \\([[:digit:]]+\\))")
       (setq curpage 
           (buffer-substring-no-properties 
             (match-beginning 1) 
             (match-end 1)))
       (setq curpage 1)
       )
  (twit-show-recent-tweets  
   (twit-check-page-prefix 
     (+ n (string-to-number curpage))
    ))
  )

Ajouter les lignes suivantes à twit-key-list:

    ("P" . twit-previous-page)
    ("N" . twit-next-page)

Trouvez le module patché ici

Oublié de joindre le fichier ? Wanderlust !

écrit par Ousmane Wilane, le 28/09/09 12:50.

Nous oublions tout le temps d’attacher des fichiers aux courriels après les avoir annoncé dans le corps du courriel, avec Wanderlust, vous pouvez vous le faire rappeler avant d’envoyer le courriel. Ce billet est entièrement programmé par djcb, j’ai juste ajouté la chaîne joint à l’expression régulière. Merci djcb. Vous trouverez sur le blog de djcb le hook utilisé pour vous rappelez que vous envoyez un courriel sans objet.

(defun my-wl-draft-attachment-check ()
  "if attachment is mention but none included, 
   warn the the user"
  (save-excursion
    (goto-char 0)
    (unless ;; don't we have an attachment?
	(re-search-forward "^Content-Disposition: 
                                attachment" nil t) 
      (when ;; no attachment; did we mention 
                                    an attachment?
	  ;; English or French
	  (re-search-forward "attach\|joint" nil t)
	(unless (y-or-n-p "Vous n'avez peut 
        être pas joint la pièce. Voulez-vous 
        quand même envoyer le brouillon ?")
          (error "Abort."))))))

(add-hook 'wl-mail-send-pre-hook 
                          'my-wl-draft-attachment-check)

Tutoriel Emacs

écrit par Ousmane Wilane, le 27/09/09 11:21.

A l’occasion de notre Software Freedom Day (2009) (Merci SFD pour les T-shirts et les CD/DVD) tenu le 26/09/2009, j’ai fait une petite démo d’utilisation d’Emacs, Wanderlust et twit.el, les 26 slides sont disponibles et le code source accessible à partir des slides. Les slides seront disponibles sur le site de mon LUG

Nouveautés Django 1.1

écrit par Ousmane Wilane, le 21/09/09 01:52.

Un petit survol des nouveautés de Django 1.1 sur http://guente.wilane.org. Les sources rst sont disponible, voir lien dans les slides. Voir l’excellente présentation d’Alex Gaynor lors de DjangoCon2009 sur les aggrégats

django.forms et datepicker

écrit par Ousmane Wilane, le 21/06/09 22:34.

jQuery dispose de pléthores de greffons avec une superbe façon de les packager depuis le site web avec les styles bien mariés au style de votre site. Parmi les greffons les plus célèbres on peut citer ui.effects.*, ui.accordeon, ui.dialog, ui.draggable, ui.droppable, ui.progressbar, ui.resizeable, ui.selectable, ui.slider, ui.sortable, ui.tabs et ui.datepicker.

La définition d’éléments de style avec Django est plutôt simple, et le deviendra encore plus si les travaux SoC 2009 de Zain Memon sur admin-ui venaient à être acceptés. Au niveau des éléments de formulaire, il suffit généralement de définir l’attribut `attrs' (dictionnaire) du widget que vous souhaitez associer à votre élément de formulaire:

#pour un champ date
forms.DateField(
            widget=forms.DateInput(attrs={'class':'datepicker'}, 
                                   format='%d-%m-%Y'),
            input_formats=['%d/%m/%Y %H:%M:%S',
                           '%d-%m-%Y %H:%M',
                           '%d-%m-%Y',
                           ],
            )

Une façon élégante d’affubler tous les champs de type date de l’attribut `class="datepicker"' est d’utiliser l’attribut de classe non documenté de la classe Form ou ModelForm `formfield_callback', par exemple:

def mon_datefield_perso(f):
    if isinstance(f, models.fields.DateField):
        return forms.DateField(
            widget=forms.DateInput(attrs={'class':'datepicker'}, 
                                   format='%d-%m-%Y'),
            )
    else:
        return f.formfield()

class MonForm(forms.ModelForm):
    formfield_callback = mon_datefield_perso
   
    class Meta:
        model = Monmodele

Une fois définit ce callback, il vous suffira d’utiliser dans votre template de base un appel générique à .datepicker() pour tous les éléments de la classe .datepicker:

$(function() {
    $('.datepicker').datepicker({inline: true});
});

La technique marche également pour un FormSet, *formset_factory utilise aussi ce type de callback (par défaut lambda f: f.formfield()) :

FactureFormSet = modelformset_factory(MonModele, 
                                      form=MonForm, 
                                      formfield_callback=mon_datefield_perso
                                     )