Tracking dans une application CodeIgniter

Introduction

Le tracking est utilisé par Google, Xiti, etc. pour tracer la navigation d’une personne depuis sont PC. Par exemple, les recherches faite sur Google pour trouver un livre seront stockées et réutilisées pour vous proposer des produits annexes (livres du même auteur, du même genre, autres livres achetés par ceux qui ont acheté le livre recherché, etc.).

L’objectif de cet article est de mettre en place un système de tracking (= stockage de navigation), dans une application web en CodeIgniter, sur des actions ciblées (clic sur des boutons ou des liens). Une fois ces actions stockées, il faudra faire une analyse des pages consultées et adapter les pages aux actions réalisées = « mettre en pratique l’expérience utilisateur ».

 

L’UX Design

Cette discipline revient à adapter l’environnement des utilisateurs (les écrans, les boutons) selon la façon dont ils évoluent.

L’interface utilisateur mets en avant la marque sur le produit, comme une bouteille de liquide classique. L’expérience utilisateur est une bouteille retournée. L’utilisation du produit est facilité.

 

Déléguer le tracking ailleurs

L’application utilise un serveur distant pour intégrer des données de tracking.

Une simple application de tracking (sur un serveur de tracking) permet de stocker une trace à chaque fois qu’on appelle un WebService (= une URL) :

  • Quel utilisateur est suivi (son identifiant suffit)
  • Quelle application est tracée (un code unique)
  • La date et heure d’enregistrement
  • Une codification qui décrit l’action, l’écran, le bouton ou le lien

En utilisant le générateur de code, cette application est rapidement créée. Les écrans sont simples, mais suffisants. Cette application externalisée peut être utilisée pour tracer d’autres applications, faire des campagnes avec remise à zéro des traces, etc.

Il reste à créer le WebService qui sera appelé pour créer une trace.

 

Permettre l’ajout d’une trace

Dans l’application de tracking, j’insère le code qui permet d’ajouter une trace dans la base de données, avec 3 paramètres :

  • l’identifiant de l’utilisateur dans son application (inconnu ailleurs)
  • le code de l’application (connu par l’application tracée et par cette application de tracking)
  • la codification qui décrit l’action

La date et heure sera automatiquement insérée à chaque appel.

Code :

	public function append($trcidusr, $applbcde, $trclbmar){

	    // 1. Retrouver l'application selon le code qui a été fourni
	    $applications = $this->applicationservice->getAllBy_applbcde($this->db, $applbcde);
	    if( count($applications) == 1 ){
	        $application = end($applications);
	    }else{
	        $data['data'] = ["status" => 404,
	            "message" => "Unknown application where code = " . $applbcde
	        ];
	        $this->load->view('json/jsonifyData_view', $data);
	        return;
	    }

	    // 2. Ajouter une trace avec date et heure = maintenant
	    $model = new TraceModel();
	    $model->trcidusr = $trcidusr;
	    $model->trctisto = date('Y-m-d G:i:s');
	    $model->trcidapp = $application->appidapp;
	    $model->trclbmar = $trclbmar;
	    $this->traceservice->insertNew($this->db, $model);

	    // 3. Retourner la trace insérée, au format JSON
	    $data['trace'] = $model;
	    $this->load->view('json/jsonifyData_view', $data);

	}

Ce WebService sera appelé en GET, selon l’URL suivante :

http://<<server>>/index.php/trace/createtracejson/append/1/DIET-IMPULSE/welcome.index.success

 

Appel du WebService dans votre application

Dans votre application CodeIgniter, il faut ajouter la bibliothèque permettant de lancer des requêtes HTTP. J’ai réussi à intégrer Curl (https://github.com/philsturgeon/codeigniter-curl) mais ce n’est plus maintenu. D’autres bibliothèques sont disponibles.

En plus, il faut ajouter un « helper » qui mettra les bons paramètres via un simple appel de fonction :

if (!function_exists('trackData')) {
    function trackData($controller, $codeMarquage){
        // avoid tracking on localhost
        if( strpos(base_url(), "localhost") > 0 ){
            return true;
        }

        $controller->load->library('curl');
        $user_id = 0;
        if( $controller->session->userdata('user_id') ){
            $user_id = $controller->session->userdata('user_id');
        }
        if($codeMarquage[0] != '/'){
            $codeMarquage = '/' . $codeMarquage;
        }

        log_message('debug', TRACKING_URL . $user_id . TRACKING_APP_CODE . $codeMarquage);
        try {
            return $controller->curl->simple_get(TRACKING_URL . $user_id . TRACKING_APP_CODE . $codeMarquage);
        }  catch (Exception $e) {
            log_message('error', $e);
        } finally {

        }
	}
}

 

Notes :

  1. En mode « local », il est inutile de tracer.
  2. En cas de plantage de l’appel au WebService, loguer une erreur.
  3. On récupère l’identifiant de l’utilisateur dans la session.
  4. 2 données sont stockées dans les constantes de l’application : le code de l’application, l’URL de l’application de tracking

 

Dans le contrôler, on trace l’authentification, point de démarrage du tracking pour cet utilisateur :

$this->session->set_userdata('user_id', $userID);
log_message('debug','[welcome.php] : user connected: '.$userID);
trackData($this,'/welcome.index.success');

 

Analyses

C’est à vous de jouer !

Avec les données recueillies, il faut maintenant comprendre comment les utilisateurs interagissent dans votre application.

  • Est-ce que le parcours est optimum ? (le temps entre chaque action permet de suivre les clics)
  • Est-ce qu’une fonctionnalité est inutilisée ? (pas ou peu de tracking dessus)

Corrigez votre application, purgez les données stockées pour votre application et refaites une campagne de tracking pour avoir le résultat.

 

Envoi de SMS par un robot

Le principe, les outils

Je désire envoyer un SMS à tous les adhérents de mon association, pour leur communiquer une date d’un évènement sportif. Le mail ne fait plus d’effet ; il n’est pas lu, pas ouvert, pas arrivé…

J’opte pour un petit programme qui enverra en masse les SMS par mon smartphone. L’outil SMSGatewayMe me propose cette solution, gratuite, avec une API en PHP et une application à installer sur mon Smartphone.

 

La solution en détails

Depuis mon PC, mon programme (= mon robot) contacte par WebService la plateforme SMSGatewayMe, avec des messages du type « Envoyer le texto ‘Salut, il y a un barbecue ce week-end. tchao ! (association AAA)’ à ’06.12.34.56.78′, depuis mon smartphone, id=13245 ».

La plateforme SMSGatewayMe stocke cette info dans sa base de données, avec l’id du smartphone, le message, le destinataire.

L’application sur mon smartphone contacte régulièrement la plateforme SMSGatewayMe : « Est-ce qu’il y a des SMS à envoyer pour le smartphone id=12345 ?« . Elle lui retourne en réponse le SMS à envoyer.

L’application sur mon smartphone crée un nouveau SMS, visible depuis mes messages de mon smartphone. Les personnes me répondent directement.

 

La technique

Après avoir créé un compte sur https://smsgateway.me/, il faut récupérer le fichier « smsGateway.php ». Regardons le programme « run.php » :

<?php
include "smsGateway.php";

function main(){
  $smsGateway = new SmsGateway('moi-meme@gmail.com', 'monMot2Passe');
  $deviceID = 12345;

  $message = "Salut,
il y a un barbecue ce week-end.
tchao !
(association AAA)";

  $row = 1;
  if (($handle = fopen("envoi-sms.csv", "r")) !== FALSE) {
    while (($data = fgetcsv($handle, 1000, ";")) !== FALSE) {
      //echo "----". $data . "\n";
      $nbChamps = count($data);
      //echo "$nbChamps champs dans la ligne #$row\n";
      if($nbChamps < 2) {
        echo "fini\n";
        return;
      }
      $user = $data[0];
      $adresse = $data[1];
      $tel = $data[2];
      if( $user == "Nom" ){
        //echo "ligne d'entete\n";
        continue;
      }
      echo "Envoi d'un SMS vers ".$user." --> ".$tel."\n";
      $result = $smsGateway->sendMessageToNumber($tel, $message, $deviceID);

      $row++;
    }
    fclose($handle);
  }
  echo "--\n";

}

main();
?>

Le fichier « envoi-sms.csv » est un tableur Excel exporté en CSV, avec les colonnes suivantes : « Nom », « Adresse », « Téléphone ». Il y a une ligne d’entête pour mieux se repérer.

Enfin, une fois que tout est prêt, il suffit de lancer la commande suivante :

php run.php

 

Remarques

Ok, l’évènement n’est pas très sportif, passons…

Le nombre de caractères est limité. Faites des tests pour vous assurer que les sauts de ligne sont correctement placés, les caractères spécieux sont bien interprétés, le compte est bien configuré, etc.

Génération de PDF avec CodeIgniter

Introduction

Dans votre application, vous désirez générer un fichier PDF avec vos données, votre charte graphique, etc. Voici un exemple d’utilisation d’une bibliothèque, qui permet de faire :

  • une entête
  • un pied de page
  • ajouter une pagination (numéro de la page en cours ; nombre de page du document)
  • insérer des images
  • insérer du texte HTML

 http://hulan.info/item/html2pdf-convert-your-xhtml-to-pdf-easily

 

Démarrage

Voici comment procéder pour installer cette bibliothèque dans votre projet :

  • Copier dans application/libraries les fichiers :
    • Fpdf_rotate.php
    • Fpdf.php
    • Pdf_html.php
    • Pdf.php
  • Copier dans application/third_party les fichiers :
    • tout le répertoire « fpdf/fonts »
  • Modifier le fichier application/config/config.php avec ces lignes :
//FPDF FONT DIRECTORY
$config['fonts_path'] = APPPATH.'third_party/fpdf/fonts/';

 

 

Générer le PDF depuis votre controller

Définir le point d’entrée du controller (classique CodeIgniter), par exemple « getAttestation() » :

public function getAttestation(){

 

Définir le répertoire des polices :

define('FPDF_FONTPATH',$this->config->item('fonts_path'));

 

Charger les bibliothèques :

$this->load->library(array('fpdf','fpdf_rotate','pdf', 'pdf_html'));

 

Mettre du contenu dans le PDF :

$pdf = new PDF_HTML();

$pdf->Open();
$pdf->SetTopMargin(10);
$pdf->AliasNbPages();
$pdf->AddPage();
$pdf->SetFont('Arial', '', 12);
$pdf->SetDrawColor(0);

$pdf->SetTitle('Attestation');
$pdf->Ln(5);
$pdf->SetFontSize(16);
$pdf->x = 70;
$pdf->Write(0, utf8_decode( "OK") );
$pdf->Output('', 'I'); // I : inline file

 

et terminer la fonction « getAttestation() »:

}

Lorsqu’on lance l’URL de ce controller dans le navigateur, un fichier PDF est généré :

 

Le fichier a pour titre « Attestation », comme prévu par le code.

CodeIgniter + React JS

Introduction

ReactJS est une bibliothèque prometteuse pour changer la façon dont les applications web sont conçues. J’ai retenu une chose importante, ce qui fait la différence par rapport à un appel AJAX simple et classique, c’est que React compare le DOM de la page avant l’appel et après l’appel pour appliquer les changements uniquement sur les modifications.

Il est donc inutile de faire recharger une page complète en cas d’ajout d’item dans une liste, ou même de l’ajouter « à la main » (avec un « append » en jQuery). React s’occupe de faire ces changements.

Soudain, on a envie de passer par cette bibliothèque pour tous les petites modifications à faire sur un écran (notification, compteur) et en exploitant cette technologie à son paroxysme, toute l’application y passe.

 

Quels impacts pour une application MVC ?

Si on prend React avec le framework « Flux », le MVC est mort. Dommage ! Faut-il recoder mes applications avec un autre paradigme ? Pas sûr.

Par contre, une application CodeIgniter qui désire utiliser React doit fondamentalement revoir la signification du V, pour la « Vue ».

En théorie, la vue, c’est ce que que les utilisateurs « voient », donc le HTML qui résulte du serveur hébergeant l’application.

Avec React, la Vue au sens HTML n’est plus à gérer par l’application. React s’en chargera.

Version classique du MVC de CodeIgniter (ou d’autres frameworks)

 

Casser la vue !

Oui, il faut casser la vue. L’application CodeIgniter ne fera que des interactions en manipulant des données JSON, et n’ayant que pour interface technique des WebServices REST.

Une autre application pourra se charger de la vue, des interactions utilisateur et des actions vers le serveur (liste des items, ajout, modification, suppression d’un item). Elle appellera l’application CodeIgniter pour le stockage en base de données, les règles métier, etc.

La partie « vue » de l’application est supportée par une autre application dédiée à l’interaction utilisateur. L’avantage est que si vous avez une application mobile à réaliser, elle pourra interagir avec cette application CodeIgniter en utilisant les mêmes WebServices.

Avec React, la vue peut supportée par une autre application.

Conclusion

La question qu’on peut se poser est la suivante : est-ce utile de s’appuyer sur un Framework MVC pour mon application s’il n’y a plus de Vue ?

S’il y a du boulot côté Modèle ou Controller (ou service) = des objets complexes, des règles métiers, la réponse est oui.

Si c’est une représentation HTML des objets simples en base de données, avec peu de règles métier, la réponse est non.

Données d’un capteur vers un serveur Web

Introduction

Lors de mon précédent article, j’évoque l’envoi des données via Wifi depuis un ensemble de composants autour d’une carte Arduino. Or, la programmation n’est pas très facile pour donner plus d’intelligence à ces capteurs. Je suis donc reparti du capteur de température et de pression DHT11, mais relié à un Raspberry PI.


DHT11 - Cảm biến nhiệt độ và độ ẩm https://images.duckduckgo.com/iu/?u=http%3A%2F%2Fwww.openmediacentre.com.au%2Ffileadmin%2Fuser_upload%2Ftx_onqcatalogue%2Fproduct%2Fraspberry-pi-model-b_80_thumbimg1.jpg&f=1
(les photos ne sont pas à l’échelle)
Prix des composants :

  • Raspberry Pi 2 starter kit (avec les câbles, l’alimentation, une carte SD, des câbles, des radiateurs, un boitier, etc) : 75 € sur Amazon
  • DHT11 (capteur nu) : 2.20 € sur Amazon
  • Dongle Wifi (sur port USB) : quelques Euros (sur eBay)

La programmation est faite en script SH et Python dans le Raspberry et je dispose d’un serveur PHP qui héberge les données (mon PC).

 

Vue du Raspberry avec les câbles pour clavier, souris, écran (inutiles en mode normal, mais nécessaire pour la programmation) + le composant DHT11, relié par 3 fils.

 

Vue des composants minimum pour que les données soient envoyées après avoir tout programmé. Il faudra juste prévoir une boite pour protéger le Raspberry et laisser le capteur « à l’air libre ».

 

Protocole de communication – 1. Souscription

Le Raspberry dispose d’un Dongle Wifi pour se connecter au serveur. J’utilise donc l’adresse MAC du Wifi pour souscrire au serveur selon ce principe :

Dans ces échanges, l’adresse MAC sert de clé. Chaque unité de communication devrait avoir une adresse MAC unique sur le réseau.

Une fois le capteur identifié sur le serveur, il n’est pas encore actif. Le serveur a un capteur en attente de paramétrage manuel (donner un nom compréhensible par un humain, un picto, un groupe, une catégorie, etc…). Tant que cette étape n’est pas faite, les données ne sont pas transmises. Le capteur passe à l’état « Activé » lorsque le paramétrage est terminé.

 

Protocole de communication – 2. Envoi des données

Le serveur est maintenant prêt à réceptionner les données du capteur.

Dans le cas du DHT11, il y a 2 données à chaque envoi : température (en °C) et humidité (en %). Le Raspberry ne fait qu’un appel au serveur pour ces 2 informations.

 

Montage électronique

Le montage sur les PINs du Raspberry sont faits directement car le DHT11 accepte un voltage de 3.3v, fourni par le PIN numéro 1. Si cette alimentation directe n’est pas assez stable, on pourra utiliser un régulateur de tension (AMS1117, 4.70 € les 10 pièces sur Amazon), prenant en entrée du 5v et donnant en sortie du 3.3v stabilisé (monté avec un condensateur de 470µF). Ce sera surtout le cas si on utilise une autre source d’énergie qu’une prise de courant (pile, accu, batterie de voiture, capteurs solaires, etc).

 

Le DHT11 comme capteur

Le DHT11 est un composant assez instable, qui ne garanti pas toujours la bonne lecture des données. Il semble que d’autres capteurs soient plus efficaces, peu onéreux, fonctionnant avec une plus grande plage de voltage : TMP36GT9, DS18B20, etc.

Le DHT11 est par contre bien documenté, pour les bidouilleurs comme moi, devant programmer en Python.

 

Copies d’écran du serveur

Liste des capteurs

 

 

Détail d’un capteur d’humidité

 

Pour aller plus loin

A partir de ce système, il est possible d’équiper toute la maison, d’avoir des capteurs partout et de suivre les courbes. Le budget à prévoir reste collé au prix du Raspberry (carte nue à 40 €), car les capteurs ne coûtent que quelques Euros. D’autres solutions voient le jour, avec des cartes électroniques programmables, comme TESSEL (avec Wifi intégré) : https://www.tessel.io/

D’autres capteurs pourront être ajoutés, sous réserve de pouvoir lire les données (sur le même principe), depuis les ports GPIO du Raspberry. Grâce au grand nombre de PINs disponibles, on pourrait ajouter d’autres capteurs à un même Raspberry, recueillant ainsi un lot de données. Pour une pièce de la maison : température, humidité, ensoleillement, concentration CO2, odeur (oui, pour faire remonter le niveau de puanteur du placard à chaussures), méthane, détecteur de mouvement, etc.

 

Codeigniter 3 chez free.fr

Codeigniter 3

J’utilise le framework Codeigniter pour mes projets personnels, avec mon générateur de code. Sa simplicité d’utilisation et sa rapidité de mise en œuvre restent un atout majeur pour de petites applications web.

En voulant faire une requête de groupe, comme ceci, je me suis confronté à un problème :

select a, b from table1, table2
where table1.c1 = table2.c1
and c1 > 10
or (c2 > 20 and c3 > 30);

 

Les opérations avec les parenthèses sont réalisables de 2 façons :

  1. Faire la requête en SQL pure et la lancer dans CodeIgniter. C’est assez moche et doit être réservé à des requêtes complexes, visant à faire travailler le moteur du SGBD. La requête sera travaillée dans un MySQL Workbench pour être sûr du résultat. On restera alors sur une base de donnée d’un certain type (ex : MySQL), sans pouvoir passer un jour à un autre type (ex: SQLite).
  2. Passer à CodeIgniter 3 pour utiliser la fonctionnalité de regroupements dans les requêtes : voir la documentation dans CodeIgniter

Cette seconde méthode exploite pleinement le SQLBD tout en utilisant le QueryBuilder du Framework. Sauf que pour ça, il faut passer à CodeIgniter 3.

 

Et free dans tout ça ?

L’hébergeur free.fr est gratuit, oui. Mais il est aussi :

  • lent au chargement
  • limité dans l’accès FTP sur le nombre de sessions ouvertes, ce qui est assez pénalisant à l’usage
  • limité dans les bases de données par site (1 seule)
  • ancien, dans les techno qu’il nous mets à disposition
  • permissif, non sécurisé (piratage de compte vécu)
  • sans support (newsgroup étant le seul media d’échange, il reste sans réponse)

Pour que CodeIgniter 3 fonctionne, il doit utiliser PHP 5.2 minimum. Or, chez free, il est à la version 5.1 – dommage !

Pour ces multiples raisons, j’ai ouvert un autre site chez WebHost et j’ai fait un test chez eux.

 

Résultats

A part l’interface de gestion assez « old-scool », on retrouve ses repères. Ouverture de site, ouverture de compte mail, création de base de données MySQL, accès FTP : tout ça se fait très facilement et rapidement.

Mon choix est fait, adieu free, enfin pas pour tout. Je garde mon blog, même si je me sens dans une toute petite boite, sans pouvoir étendre mes jambes ou mes bras (version de WordPress assez ancienne, ne pouvant pas se mettre à jour, pas d’accès aux serveurs externes pour mettre un captcha, etc…).

 

Raspberry Pi + Camera : pilotage par un smartphone

Intérêt

Le raspberry est une petite boite contenant l’équivalent d’un PC assez puissant pour faire quelques petites choses intéressantes. L’objectif est de brancher le raspberry sur une source d’alimentation (dans la voiture, près de la télé, sous un télescope) et de piloter quelques actions depuis un smartphone.

Exemples d’application :

  • Près d’un point de surveillance de la maison : Caméra de surveillance, avec enregistrement des vidéos sur une clé USB
  • Dans la voiture : Enregistrement de la conduite de manière automatique. en cas de litiges, vous aurez un enregistrement vidéo
  • Sous un télescope : avec une webcam derrière l’objectif pour faire la mise au point et une série de prises de vue enregistrées sur la clé USB
  • Près d’un moniteur ou d’une télé : pour lancer une présentation PowerPoint / PDF, pour jouer des vidéos, avec la webcam pour prendre des photos

Tout ça, avec un smartphone en guise de télécommande Wifi.

 

Configuration matérielle pour le démarrage

Voici le matériel nécessaire pour configurer le Raspberry complètement :

  • Un clavier USB
  • Une souris USB
  • Un écran
  • Une clé USB
  • Un accès à Internet

 

Configuration logicielle pour le démarrage

  1. Installation de l’image Ubuntu Mate pour Raspberry
  2. Installation des services :
    1. LAMP (Apache + PHP + MySQL), avec changement du user apache
    2. openssh-server (pour accès shell par SSH)
    3. usbmount (auto-mount les clés USB)
  3. Configuration des points de montage : pour la clé USB :  « uid=1000,noauto,user »
  4. Désactivation du firewall Ubuntu
  5. Installation d’un serveur FTP et SSH pour déposer les fichiers depuis un autre PC et lancer des lignes de commandes

 

Configuration logicielle étape 2

Cette étape se fait depuis un PC pour plus de confort. On a donc mis le Raspberry sur un réseau interne :

  • Un PC connecté via RJ45 à un routeur
  • Le Raspberry connecté en RJ45 au routeur

Par la suite, le câble RJ45 sera supprimé.

 

Le clavier, la souris et l’écran peuvent être supprimés après avoir installé les serveurs SSH et FTP.

L’installation d »es logiciels continue :

  1. Installation d’une application web pour accéder au Raspberry depuis un smartphone (PHP avec CodeIgniter) – Réalisée par mes soins ; cf chapitre « Application web pour contrôler le Raspberry » ci-dessous
  2. Changement de la politique de sécurité pour autoriser l’arret et reboot par un utilisateur quelconque (/usr/share/polkit-1/actions/org.freedesktop.login1.policy)
  3. Connexion automatique d’un user (pour ne pas avoir de mire de connexion)
  4. Ajout d’un script à la connexion pour lancer « xhost + » (pour que d’autres machines puissent ouvrir des fenêtres dans la session ouverte)
  5. Activation du dongle Wifi en mode AccessPoint (point d’accès Wifi) et paramétrage du DHCP

 

Application web pour contrôler le Raspberry

Cette application web permettra de piloter le Rasbberry depuis un smartphone. Elle est développée en PHP avec le Framework CodeIgniter. Un template HTML permet d’avoir une charte graphique en Responsive Design : http://binarycart.com/bclivedemos/01-05-2014/v1/bs-binary-admin/index.html

 

Fonctionnalités basiques de l’application PHP :

  • Etat du raspberry : charge machine, type de processeur, version du noyau, version et nom du système (Ubuntu)
  • Lancement de scripts SH (exemple : lancer une présentation avec un fichier PDF précis)
  • Parcours dans les répertoires
  • Caméra : prise de photo, enregistrement d’une vidéo
  • Arrêt et relance du raspberry

 

Utilisation avec un smartphone

Après avoir mis sous tension le raspberry, le smartphone peut s’y connecter par wifi. Un simple navigateur permet d’accéder aux fonctionnalités. Le smartphone devient une télécommande.

 

Copies d’écran sur smartphone

Page de connexion

Page d’accueil : description de la machine

Menu

Page des scripts à lancer

Page des médias USB connectés et détectés

Parcours des fichiers du Raspberry

Page de la gestion de la caméra : prendre une photo, prendre des vidéos

Page d’arrêt / relance du Raspberry

Kivy : accès à l’appareil photo sous Android

Introduction

Depuis une application Kivy, il est possible d’appeler des objets Android et les faire interagir en utilisant Python. Pour ce faire, une interface est disponible sous le nom de « jnius ». En effet, c’est très pratique et génial…

Voici un exemple d’utilisation de l’appareil photo, le tout réutilisable dans vos applications Kivy, car dans un module à part (genre « Service »).

 

Appel de l’appareil photo dans l’application Kivy

Attention, avant de procéder à l’appel de caméra pour prendre une photo, il faut savoir une chose : l’application Kivy se mets en pause. Pour ce faire, il est nécessaire que l’instance de App définisse la méthode « pause » et renvoie « True ». C’est un peu comme si Kivy disant « je mets en pause l’appli, est-ce que j’en ait le droit ? ». Si « True », je passe à la suite. Si « False », je stoppe.

 

Le service d’appel de la photo est défini comme ceci (basé sur le tuto « takepicture ») :

from jnius import autoclass, cast
from android import activity
from functools import partial
from kivy.clock import Clock
import os

Intent = autoclass('android.content.Intent')
PythonActivity = autoclass('org.renpy.android.PythonActivity')
MediaStore = autoclass('android.provider.MediaStore')
Uri = autoclass('android.net.Uri')
Environment = autoclass('android.os.Environment') 

class CameraService(object):
    '''
    Service pour acceder a la camera du device.
    Attention, seules les images JPG sont autorisées.
    '''

    def __init__(self, savingPath = None, aFilename = None):
        '''
        Constructeur
        savingPath doit se terminer par le separateur de répertoire
        '''
        if savingPath is None:
            savingPath = Environment.getExternalStorageDirectory().getPath()
        self.path = savingPath
        self.filename = aFilename
        self.picture = None

    def __define_new_filename(self):
        index = 0
        while True:
            index += 1
            fn = os.path.join(self.path, 'takepicture{}.jpg'.format(index))
            if not os.path.exists(fn):
                return fn

    def take_picture(self, aCallbackFunction):
        self.callbackFunction = aCallbackFunction
        if self.filename is None:
            self.filename = self.__define_new_filename()
        uri = Uri.parse('file://' + os.path.join(self.path, self.filename) )
        uri = cast('android.os.Parcelable', uri)

        activity.bind(on_activity_result=self.__on_activity_result)

        intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
        intent.putExtra(MediaStore.EXTRA_OUTPUT, uri)
        PythonActivity.mActivity.startActivityForResult(intent, 0x123)

    def __on_activity_result(self, requestCode, resultCode, intent):
        if requestCode == 0x123:
            Clock.schedule_once(partial(self.__add_picture, self.filename), 0)

    def __add_picture(self, fn, *args):
        self.picture = fn
        self.callbackFunction(self.picture)
        activity.unbind(on_activity_result=self.__on_activity_result)

 

Son appel se fait dans une instance de Screen, par exemple :

class myScreen(Screen):
    #...

    def btn_take_photo(self):
        camera = CameraService()
        camera.take_picture(self.set_picture)

    def set_picture(self, newPicture_name):
        self.logLabel.text = "Photo : %s" % str(newPicture_name)

Lorsqu’un bouton est cliqué, la méthode « btn_take_photo() » est appelée. Celle-ci fait appel au service (sans paramètre dans ce cas) et lance la prise de photo avec un paramètre : la méthode à appeler lorsque l’utilisateur aura terminé : « self.set_picture ».

Cette méthode prend un paramètre : le nom du fichier sauvegardé par l’appareil photo du smartphone. Attention, seul le format JPEG est autorisé…

 

Conclusion

Pour reprendre cette photo, je vous conseille de la réduire avec PIL pour éviter d’avoir une image en 4128 x 2322 pixels.

im = Image.open(filename)
width, height = im.size
im.thumbnail( (width/4,height/4) , Image.ANTIALIAS)
im.save(filename,quality=95)

Kivy : icones de l’ActionBar trop petites

Introduction

Avec la version 1.8 de Kivy, une nouvelle fonctionnalité a été ajoutée : ActionBar. API : http://kivy.org/docs/api-kivy.uix.actionbar.html

Vous verrez que ce Widget permet de définir une barre (en haut de l’écran) avec des boutons d’actions. Ceci a été fait dans l’environnement Android : http://developer.android.com/guide/topics/ui/actionbar.html

Et même un guide d’implémentation a été rédigé pour nous aider à rester ergonomique : http://developer.android.com/design/patterns/actionbar.html

 

Problématique et solution

La volonté de Kivy est plutôt bonne, mais avec les téléphones haute résolution, il y a des petits problèmes : les icones restent (très) petites.

l’API de Kivy nous donne une piste : il est possible de définir les images pour les icônes « Back » et l’icône de l’application. La solution tient en quelques lignes dans le fichier « .kv » (2 lignes en gras ci-dessous) :

        ActionBar:
            pos_hint: {'top':1}
            ActionView:
                use_separator: True
                ActionPrevious:
                    title: 'Client'
                    #with_previous: False
                    on_press: root.manager.go_back()
                    previous_image: "images/ic_action_previous_item.png"
                    app_icon: "images/ic_action_icon.png"

                ActionOverflow:

                ActionButton:
                    text: 'Refresh'
                    icon: "images/ic_action_refresh.png"
                    on_press: root.refresh()

L’image pour le retour à l’écran précédent :

previous_image: "images/ic_action_previous_item.png"

 

L’image de l’application :

app_icon: "images/ic_action_icon.png"

Bonus

Pour rester graphiquement proche d’une application Android, on pourra utiliser les images fournies par l’API : Android_Design_Icons_20131106.zip (cliquez pour télécharger le pack d’icones de http://developer.android.com)

 

Attention, ces 2 images sont redéfinies, mais je n’ai pas réussi à redéfinir les images pour le widget TreeView. Il faudra se contenter de petites icônes pour cette fois…

 

CodeIgniter : Validation de formulaire

Introduction

CodeIgniter propose une fonctionnalité de validation de formulaire coté serveur, à inclure dans le code PHP du controler. Voici une description de ce qu’il faut faire, avec l’exemple de la page de login fournie avec le framework.

 

Constructeur du Controler

Dans mon cas, le constructeur fait appel aux bibliothèques, helpers, models, etc à charger à chaque appel du controler.

class Welcome extends CI_Controller {
    function __construct(){
        parent::__construct();
        $this->load->library('session');
        $this->load->helper('url');
        $this->load->database();
        $this->load->library('form_validation');
        $this->load->model('Album_model');
    }

Dans ce cas, il faut ajouter l’appel à la bibliothèque « form_validation » :

 

Controler « /index »

Opérations à faire:

  • Définir les tags HTML qui contiendront l’erreur avec la méthode « set_error_delimiters() »
  • Définir les règles que doivent respecter les champs (la description de l’API de CodeIgniter est complète sur chaque règle)
  • Lancer la validation des règles et tester le code retour, avec « $this->form_validation->run() »
  • Si la validation des règles retourne FALSE, le formulaire ne peut pas être accepté, on renvoie l’utilisateur vers la page d’origine « welcome_message »

 

Résultat :

    public function index(){
        $this->form_validation->set_error_delimiters('<div class="error">', '</div>');
        $this->form_validation->set_rules('login', 'lang:login', 'trim|required|encode_php_tags|xss_clean');
        $this->form_validation->set_rules('password', 'lang:password', 'trim|required|encode_php_tags|xss_clean');

        if($this->form_validation->run() == FALSE){
            $this->load->view('welcome_message');
            return;
        }

        // Formulaire validé
        // ...

Paramètres de set_rules() :

  • Le nom du champ à tester (name= »XXX » dans le rendu HTML)
  • Le message d’erreur à présenter. S’il y a « lang: », il faudra retrouver le message correspondant dans le fichier de traduction
  • Une chaîne de caractères avec l’ensemble des règles à appliquer. Des règles « custom » sont possibles, si un test complexe doit être réalisé.

 

Fichier de traduction « form_validation_lang.php »

Ce fichier doit contenir les libellés à présenter en cas d’erreur, avec la syntaxe suivante :

$lang['required']    = "Le champ %s est requis.";

Le texte « %s » sera remplacé par le nom du champ dans un autre fichier de langue. Par exemple, dans le fichier « messages_lang.php » qui définit l’ensemble des libellés de génériques à chaque application (boutons des CRUDs, champs du formulaire de login), on aura donc :

$lang['login'] = "Identifiant";

 

La vue

Si la validation n’est pas faite, la valeur saisie est ré-affichée grâce au code « set_value(‘login’) » :

<input type="text" value="<?php echo set_value('login'); ?>" id="login" name="login">

 

Conclusion

La gestion des langues est très pratique pour rendre l’application complètement traduite, y compris les messages d’erreur de validation générés par le framework.

Il est possible d’ajouter des paramètres dans le message d’erreur. Par exemple : « La valeur du champ <Temps passé> doit être supérieure à 10 minutes« . Le paramètre « 10  » est paramétrable dans la règle. Il me reste à tester son comportement et sa syntaxe.

La validation côté client devrait avoir son pendant coté serveur, afin d’éviter un chargement de page complet. Aujourd’hui, il me semble qu’il est nécessaire de le réaliser  classiquement en JavaScript…