<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Benjamin BALET</title>
	<atom:link href="http://benjamin-balet.info/feed/" rel="self" type="application/rss+xml" />
	<link>https://benjamin-balet.info/</link>
	<description>Informatique, dévs et gadgets</description>
	<lastBuildDate>Sun, 22 Jun 2025 13:06:21 +0000</lastBuildDate>
	<language>fr-FR</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.1</generator>
	<item>
		<title>Comment utiliser un modèle de ML dans une application?</title>
		<link>https://benjamin-balet.info/developpement/comment-utiliser-un-modele-de-ml-dans-une-application/</link>
		
		<dc:creator><![CDATA[Benjamin BALET]]></dc:creator>
		<pubDate>Thu, 27 Feb 2020 12:24:29 +0000</pubDate>
				<category><![CDATA[Data Science]]></category>
		<category><![CDATA[Développement]]></category>
		<category><![CDATA[data science]]></category>
		<category><![CDATA[industrialisation]]></category>
		<category><![CDATA[intelligence artificielle]]></category>
		<category><![CDATA[machine learning]]></category>
		<category><![CDATA[python]]></category>
		<guid isPermaLink="false">https://benjamin-balet.info/?p=1360</guid>

					<description><![CDATA[<p>Comment déployer un modèle de machine learning dans une application informatique. Voici les bases techniques.</p>
<p>Cet article <a href="https://benjamin-balet.info/developpement/comment-utiliser-un-modele-de-ml-dans-une-application/">Comment utiliser un modèle de ML dans une application?</a> est apparu en premier sur <a href="https://benjamin-balet.info">Benjamin BALET</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2><span style="font-weight: 400;">Cette question est souvent éludée dans les formations de Data Science. Elle peut être aussi légitime pour les Data Scientists n’ayant pas d’expérience ou de connaissances plus générales en développement informatique.</span></h2>



<p><span style="font-weight: 400;">Imaginons que vous vous intéressiez à l’Intelligence Artificielle et que vous n’êtes pas peu fier d’avoir entraîné votre tout premier modèle. Prenons l’exemple d’une corrélation entre la vitesse d’affichage des pages d’un site web d’e-commerce et le montant moyen d’un panier (cela a été étudié de long en large : les sites d’e-commerce lents font fuir les acheteurs):</span></p>



<span id="more-1360"></span>



<div class="wp-block-image"><figure class="aligncenter"><img fetchpriority="high" decoding="async" width="384" height="248" src="https://benjamin-balet.info/wp-content/uploads/2020/02/scatter.png" alt="" class="wp-image-1361" srcset="https://benjamin-balet.info/wp-content/uploads/2020/02/scatter.png 384w, https://benjamin-balet.info/wp-content/uploads/2020/02/scatter-300x194.png 300w" sizes="(max-width: 384px) 100vw, 384px" /><figcaption>Corrélation Temps de réponse / Panier moyen </figcaption></figure></div>



<p><span style="font-weight: 400;">Un modèle de régression linéaire permettrait de prédire le montant moyen des achats pour un temps moyen d’affichage de 7 secondes (dans notre exemple cela tournerait autour de 70€).</span></p>



<pre class="wp-block-code"><code>import numpy as np
from sklearn.linear_model import LinearRegression

#Acquisition des données
pageSpeeds = np.random.normal(3.0, 1.0, 1000)
purchaseAmount = 100 - (pageSpeeds + np.random.normal(0, 0.1, 1000)) * 3

#Entraînement du modèle
model = LinearRegression()
model.fit(pageSpeeds.reshape(-1, 1), purchaseAmount)

#Prédiction
purchasePredict = model.predict([[7.0]])

print("Prediction={:.4f}".format(purchasePredict[0]))</code></pre>



<p><span style="font-weight: 400;">Décomposons maintenant le code d’un point de vue de la performance applicative. La partie la plus coûteuse concerne l’acquisition des données et l’entraînement du modèle.</span></p>



<figure class="wp-block-table"><table class=""><tbody><tr><td><strong>Activité</strong></td><td> <strong>Temps</strong></td><td><strong>%</strong></td></tr><tr><td> Acquisition des données</td><td> 0,0010</td><td>28%</td></tr><tr><td> Entrainement du modèle</td><td> 0,0024</td><td>65%</td></tr><tr><td> Prédiction</td><td> 0,0003</td><td>8%</td></tr></tbody></table></figure>



<p><span style="font-weight: 400;">Comment réutiliser plusieurs fois cet utilitaire de prédiction (en ligne de commande ou par un service web) ? Les données sur lesquelles nous avons entraîné le modèle variant peu, il serait dommage de répéter à chaque appel de l’utilitaire l’acquisition des données et l’entraînement du modèle. Dans le cas d’une application métier ou d’un site Internet, cela nuirait grandement aux performances. Et à plus forte raison avec des modèles plus lourds (deep learning, modèles itératifs, etc.).</span></p>



<h2 class="wp-block-heading">Qu’est que la sérialisation d’un objet ? </h2>



<p><span style="font-weight: 400;">Lorsque l’on crée un objet (l’instance d’une classe comme la variable </span><i><span style="font-weight: 400;">model</span></i><span style="font-weight: 400;"> dans notre code Python), il existe aussi longtemps que le programme dans lequel il a été créé est en vie. Dit autrement, lorsque le programme s’arrête, tous les objets créés en mémoire par ce programme disparaissent.</span></p>



<p><span style="font-weight: 400;">L’idée de la sérialisation est de rendre persistants (en les enregistrant sur le disque dur) certains objets dont on aurait besoin lorsque le programme démarre de nouveau. Dans le cas d’un service web cela concerne chaque appel au service.</span></p>



<p><span style="font-weight: 400;">Avec Python, la librairie de référence dépend du cas de figure. </span><a href="https://joblib.readthedocs.io/en/latest/"><span style="font-weight: 400;">Joblib</span></a><span style="font-weight: 400;"> est à privilégier si l’on doit sérialiser de grands tableaux de nombres <em>numpy</em>. Tandis que </span><a href="https://docs.python.org/3/library/pickle.html"><span style="font-weight: 400;">pickle</span></a><span style="font-weight: 400;"> se montre généralement plus rapide dans les autres cas de figure.</span></p>



<h2 class="wp-block-heading">Exemple d’un utilitaire en ligne de commande </h2>



<p><span style="font-weight: 400;">Nous travaillerons dans le même répertoire pour tous les exemples de code qui suivent. On commencera par découper notre code en deux parties:</span></p>
<ol>
<li style="font-weight: 400;"><span style="font-weight: 400;">Un utilitaire d’acquisition des données et d&rsquo;entraînement du modèle qu’on lancera de temps en temps afin de rafraîchir le modèle.</span></li>
<li style="font-weight: 400;"><span style="font-weight: 400;">Un utilitaire en ligne de commande que l’on utilisera après avoir entraîné le modèle une première fois et de manière beaucoup plus fréquente.</span></li>
</ol>



<p><span style="font-weight: 400;">Reprenons le code précédent afin de lui ajouter la sauvegarde du modèle dans un fichier du répertoire courant.</span></p>



<pre class="wp-block-code"><code>import joblib
import numpy as np
from sklearn.linear_model import LinearRegression

#Acquisition des données et entraînement du modèle
pageSpeeds = np.random.normal(3.0, 1.0, 1000)
purchaseAmount = 100 - (pageSpeeds + np.random.normal(0, 0.1, 1000)) * 3
model = LinearRegression()
model.fit(pageSpeeds.reshape(-1, 1), purchaseAmount)

#Sérialisation du modèle pour utilisation future
joblib.dump(model, "./model.joblib")</code></pre>



<p><span style="font-weight: 400;">Puis créons un deuxième utilitaire qui prendra en paramètre (par la ligne de commande) le temps de réponse et prédira le montant moyen d’achat. On l&rsquo;invoque dans le terminal de cette manière: </span><code><span style="font-weight: 400;">python predictor.py 7.0</span></code></p>



<pre class="wp-block-code"><code>import sys
import joblib

#Récupérer le temps de réponse passé en paramètre
responseTime = float(sys.argv[1])

#Charger le modèle depuis le fichier
model = joblib.load("./model.joblib")

#Prédire le montant moyen d'achat 
purchasePredict = model.predict([[responseTime]])
print("Prediction={:.4f}".format(purchasePredict[0]))</code></pre>



<h2 class="wp-block-heading">Utilisation dans un service web </h2>



<p><span style="font-weight: 400;">Le principe reste le même pour un service web. On conserve l’utilitaire d&rsquo;entraînement du modèle. Puis on crée avec le framework </span><a href="https://palletsprojects.com/p/flask/"><span style="font-weight: 400;">Flask</span></a><span style="font-weight: 400;"> (qu’il faudra installer au préalable avec </span><i><span style="font-weight: 400;">pip</span></i><span style="font-weight: 400;"> ou </span><i><span style="font-weight: 400;">conda</span></i><span style="font-weight: 400;">) une application web minimaliste.</span></p>



<pre class="wp-block-code"><code>import joblib
from flask import Flask
from flask import request

app = Flask(__name__)
@app.route('/&lt;perfRespTime>')
def index(perfRespTime):
    #Charger le modèle depuis le fichier
    model = joblib.load("./model.joblib")
    #Prédire le montant d'achat à l'aide du modèle
    purchasePredict = model.predict([[float(perfRespTime)]])
    #Afficher le résultat dans le navigateur
    return("Prediction={:.4f}".format(purchasePredict[0]))

app.run(host='localhost', port=5000)</code></pre>



<p><span style="font-weight: 400;">L’utilisation se passe en deux temps:</span></p>
<ol>
<li style="font-weight: 400;"><span style="font-weight: 400;">On lance un serveur web depuis la console: </span><code><span style="font-weight: 400;">python webservice.py</span></code><span style="font-weight: 400;"> ce script écoute sur le port numéro 5000 des appels via le protocole HTTP.</span></li>
<li style="font-weight: 400;"><span style="font-weight: 400;">Dans le navigateur on accède à l’URL </span><a href="http://localhost:5000/7.0"><span style="font-weight: 400;">http://localhost:5000/7.0</span></a><span style="font-weight: 400;"> (elle contient l’adresse du serveur web et le paramètre 7.0 qui correspond au temps de réponse pour lequel on souhaite prédire le montant moyen d’achat). Notez que des applications telles que </span><i><span style="font-weight: 400;">curl</span></i><span style="font-weight: 400;"> ou </span><a href="https://www.postman.com/"><span style="font-weight: 400;">postman</span></a><span style="font-weight: 400;"> peuvent être utilisées à la place du navigateur.</span></li>
</ol>



<div class="wp-block-image"><figure class="aligncenter"><img decoding="async" width="261" height="124" src="https://benjamin-balet.info/wp-content/uploads/2020/02/appel-ws_ml.png" alt="" class="wp-image-1365"/><figcaption>Appel à notre Web Service de prédiction</figcaption></figure></div>



<p><span style="font-weight: 400;">Il ne vous a sans doute pas échappé que le modèle était chargé à chaque appel du service web. En cas d’utilisation fréquente, on pourrait le mettre dans une variable statique ou globale (initialisée une fois pour toutes) ou utiliser les </span><a href="https://joblib.readthedocs.io/en/latest/auto_examples/memory_basic_usage.html"><span style="font-weight: 400;">fonctionnalités de cache de joblib</span></a><span style="font-weight: 400;">. Plusieurs choses seront alors à prendre en compte comme le nombre de services et modèles, ainsi que la quantité de mémoire disponible sur la machine.</span></p>



<h2 class="wp-block-heading">Aller plus loin</h2>



<p><span style="font-weight: 400;">Bien entendu, dans le cadre d’une application d&rsquo;entreprise, il faudra inclure notre script dans une solution plus robuste (en incluant dans un serveur web et en y ajoutant par exemple une méthode d&rsquo;authentification) et réfléchir à la mise à l’échelle. Si vous utilisez une plateforme cloud telle qu’AzureML sachez que les principes techniques et les bibliothèques python sont les même pour déployer un service web basé sur un modèle.</span></p>
<p>Cet article <a href="https://benjamin-balet.info/developpement/comment-utiliser-un-modele-de-ml-dans-une-application/">Comment utiliser un modèle de ML dans une application?</a> est apparu en premier sur <a href="https://benjamin-balet.info">Benjamin BALET</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Tester Jorani avec WAMP sur son ordinateur</title>
		<link>https://benjamin-balet.info/developpement/php/tester-lms-wamp-ordinateur/</link>
					<comments>https://benjamin-balet.info/developpement/php/tester-lms-wamp-ordinateur/#comments</comments>
		
		<dc:creator><![CDATA[Benjamin BALET]]></dc:creator>
		<pubDate>Wed, 15 Oct 2014 13:07:15 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<guid isPermaLink="false">http://benjamin-balet.info/?p=1318</guid>

					<description><![CDATA[<p>tutoriel expliquant comment installer LMS sur son ordinateur. Ce guide détaillé vous expliquera pas à pas comment le configurer. C'est un excellente entraînement pour un futur déploiement.</p>
<p>Cet article <a href="https://benjamin-balet.info/developpement/php/tester-lms-wamp-ordinateur/">Tester Jorani avec WAMP sur son ordinateur</a> est apparu en premier sur <a href="https://benjamin-balet.info">Benjamin BALET</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Dans cet article nous verrons comment télécharger, installer et configurer <a title="Site du projet Jorani" href="http://fr.jorani.org/" target="_blank">Jorani</a> (un outil gratuit de gestion des congés) sur un ordinateur Windows. Bien que les exemples s’appliquent à WAMP, les instructions sont valables pour les logiciels alternatifs (XAMP, EasyPHP, etc.).<span id="more-1318"></span></p>
<h2>Introduction</h2>
<p>Il existe une <a title="Démo du logiciel Leave Management System" href="http://demo.jorani.org/" target="_blank">démo en ligne</a> du logiciel Jorani, mais cette dernière ne permet pas de tester sereinement toutes les fonctionnalités offertes par ce logiciel de gestion des congés. Tout simplement parce que cette démo est publique. Il n’est donc pas possible d’empêcher un autre internaute de supprimer ou de valider ou de supprimer ses données de test. Il n’est pas non plus possible de tester des fonctionnalités telles que l’authentification LDAP.</p>
<h2>Prérequis</h2>
<ul>
<li>Télécharger et installer le logiciel <a title="Télécharger WAMP" href="http://www.wampserver.com/" target="_blank">WAMP </a>(ou une alternative).</li>
<li>Récupérer la <a title="ZIP de la version de Jorani en cours de développement" href="https://github.com/bbalet/jorani/archive/master.zip" target="_blank">dernière version du logiciel Jorani</a>.</li>
</ul>
<h2>Configurer WAMP</h2>
<h3>Configuration d’Apache</h3>
<h4>Activer la réécriture d’URL</h4>
<p>Jorani utilise la technique de réécriture des URL. Par exemple, l&rsquo;adresse</p>
<pre>http://localhost/lms/ index.php ?route=leaves/104</pre>
<p>Sera transformée en :</p>
<pre>http://localhost/lms/leaves/104</pre>
<p>Ce qui est quand même plus lisible. Mais pour cela, il faut activer le module Apache de réécriture des URLs. Faire apparaître l’icône WAMP dans la barre des tâches et faire un clic droit. Dans le menu Apache, choisir, Apache modules puis vérifier si rewrite_module est actif. Pour l’activer, il suffit de cliquer dessus et WAMP redémarrera.</p>
<p><figure id="attachment_1320" aria-describedby="caption-attachment-1320" style="width: 601px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-apache-rewrite-module.png" target="_blank"><img decoding="async" class="wp-image-1320 size-full" src="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-apache-rewrite-module.png" alt="Vérifier la présence ou activer le module rewrite d'Apache" width="601" height="392" srcset="https://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-apache-rewrite-module.png 601w, https://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-apache-rewrite-module-300x195.png 300w" sizes="(max-width: 601px) 100vw, 601px" /></a><figcaption id="caption-attachment-1320" class="wp-caption-text">Vérifier la présence ou activer le module rewrite d&rsquo;Apache</figcaption></figure></p>
<p>On peut aussi activer ce module en vérifiant que la ligne ci-dessous ne commence pas par le signe dièse (# transforme la ligne en commentaire) dans le fichier httpd.conf de votre répertoire d’installation d&rsquo;Apache.</p>
<pre>LoadModule rewrite_module modules/mod_rewrite.so</pre>
<h4>Permettre la surcharge de la configuration</h4>
<p>Jorani utilise des fichiers .htaccess afin de modifier quelques éléments de configuration d’Apache (par curiosité, vous pourriez ouvrir le fichier .htaccess qui est à la racine du projet LMS).</p>
<p>Ouvrir le fichier http.conf (soit par le raccourci accessible en faisant un clic droit sur l’icône WAMP de la barre des tâches, soit par l’explorateur en cherchant le répertoire d’installation d’Apache). Chercher le bloc de ligne ci-dessous. Le contenu peut varier, mais ce qui est important, c’est la présence de la directive <em>AllowOverride All</em> :</p>
<pre>&lt;Directory /&gt;
    Options +FollowSymLinks
    RewriteEngine On
    AllowOverride All
    Order deny,allow
    Deny from all
&lt;/Directory&gt;</pre>
<h3>Configuration de PHP</h3>
<p>Il est conseillé d’activer le module open_ssl. Pour vérifier si cette extension PHP est active, faire comme dans le paragraphe précédent sauf que le nom du menu est PHP puis PHP extensions :</p>
<p><figure id="attachment_1321" aria-describedby="caption-attachment-1321" style="width: 330px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-php-openssl-extension.png" target="_blank"><img loading="lazy" decoding="async" class="wp-image-1321 size-full" src="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-php-openssl-extension.png" alt="Vérifier la présence ou activer l'extension PHP open ssl" width="330" height="260" srcset="https://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-php-openssl-extension.png 330w, https://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-php-openssl-extension-300x236.png 300w" sizes="auto, (max-width: 330px) 100vw, 330px" /></a><figcaption id="caption-attachment-1321" class="wp-caption-text">Vérifier la présence ou activer l&rsquo;extension PHP open ssl</figcaption></figure></p>
<p>Si vous souhaitez tester la <a title="Configurer l’authentification LDAP de Jorani" href="http://benjamin-balet.info/developpement/php/configurer-lauthentification-ldap-lms/" target="_blank">connexion à LMS via l’annuaire LDAP</a> de votre entreprise, il faudra également vérifier que le module PHP LDAP (php_ldap) est bien activé.</p>
<h2>Installer Jorani</h2>
<p>Récupérer une <a title="Fichier ZIP contenant la version en cours de développement de Jorani" href="https://github.com/bbalet/jorani/archive/master.zip" target="_blank">version de Jorani depuis github</a>. Décompresser le fichier ZIP que vous avez téléchargé depuis github dans le répertoire web de WAMP (en général C:\wamp\www\). Voici ce que vous devriez obtenir :</p>
<p><figure id="attachment_1323" aria-describedby="caption-attachment-1323" style="width: 743px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-decompresser-zip-lms.png" target="_blank"><img loading="lazy" decoding="async" class="wp-image-1323 size-full" src="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-decompresser-zip-lms.png" alt="Résultat obtenu après décompression du fichier ZIP contenant LMS" width="743" height="408" srcset="https://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-decompresser-zip-lms.png 743w, https://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-decompresser-zip-lms-300x164.png 300w, https://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-decompresser-zip-lms-624x342.png 624w" sizes="auto, (max-width: 743px) 100vw, 743px" /></a><figcaption id="caption-attachment-1323" class="wp-caption-text">Résultat obtenu après décompression du fichier ZIP contenant LMS</figcaption></figure></p>
<p>Déplacer les fichiers si la profondeur du répertoire n’est pas la bonne (par exemple C:\wamp\www\lms\lms\application devrait être C:\wamp\www\lms\application).</p>
<h2>Installer la base de données</h2>
<p>WAMP contient une interface web d’administration des bases de données MySQL que l’on peut lancer directement depuis l’icône de la barre des tâches :</p>
<p><figure id="attachment_1324" aria-describedby="caption-attachment-1324" style="width: 264px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-lancer-phpmyadmin.png" target="_blank"><img loading="lazy" decoding="async" class="wp-image-1324 size-full" src="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-lancer-phpmyadmin.png" alt="Lancer PHPMyAdmin depuis l'icône WAMP de la barre des tâches" width="264" height="382" srcset="https://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-lancer-phpmyadmin.png 264w, https://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-lancer-phpmyadmin-207x300.png 207w" sizes="auto, (max-width: 264px) 100vw, 264px" /></a><figcaption id="caption-attachment-1324" class="wp-caption-text">Lancer PHPMyAdmin depuis l&rsquo;icône WAMP</figcaption></figure></p>
<p>Se connecter à phpMyAdmin, puis cliquer sur l’onglet « Bases de données » afin de créer une nouvelle base avec interclassement utf8_general_ci:</p>
<p><figure id="attachment_1325" aria-describedby="caption-attachment-1325" style="width: 516px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-creer-base-donnees-phpmyadmin.png" target="_blank"><img loading="lazy" decoding="async" class="wp-image-1325 size-full" src="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-creer-base-donnees-phpmyadmin.png" alt="Création de la base de données MySQL pour LMS" width="516" height="266" srcset="https://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-creer-base-donnees-phpmyadmin.png 516w, https://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-creer-base-donnees-phpmyadmin-300x154.png 300w" sizes="auto, (max-width: 516px) 100vw, 516px" /></a><figcaption id="caption-attachment-1325" class="wp-caption-text">Création de la base de données MySQL pour LMS</figcaption></figure></p>
<p>Note : les utilisateurs avancés peuvent créer un nouvel utilisateur, mais il faut qu’il ait les droits SELECT, INSERT, UPDATE, DELETE, EXECUTE sur la nouvelle base créée.</p>
<p>Une fois la base créée, la sélectionner (dans la barre latérale gauche qui liste les tables), puis cliquer sur l’onglet « Importer ». Choisir le fichier C:\wamp\www\lms\sql\lms.sql et cliquer sur le bouton « Exécuter » :</p>
<p><figure id="attachment_1326" aria-describedby="caption-attachment-1326" style="width: 620px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-phpmyadmin-importer-base.png" target="_blank"><img loading="lazy" decoding="async" class="wp-image-1326 size-full" src="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-phpmyadmin-importer-base.png" alt="Importer la structure et les données de la base de données de LMS" width="620" height="268" srcset="https://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-phpmyadmin-importer-base.png 620w, https://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-phpmyadmin-importer-base-300x129.png 300w" sizes="auto, (max-width: 620px) 100vw, 620px" /></a><figcaption id="caption-attachment-1326" class="wp-caption-text">Importer la structure et les données de la base de données de LMS</figcaption></figure></p>
<h2>Configurer Jorani</h2>
<p>Normalement, la configuration par défaut de Jorani est suffisante. Cependant, si vous avez choisi un autre nom de bas de données, créé un utilisateur spécifique ou qu’un mot de passe est obligatoire pour le compte root, vous devrez modifier le contenu du fichier de configuration dans C:\wamp\www\lms\application\config\database.php avec ce qui correspond à votre environnement.</p>
<h2>Tester les e-mails</h2>
<p>On n’a pas toujours un serveur SMTP sous la main, aussi je vous recommande un petit utilitaire pratique : <a title="Site du projet SMTP4DEV" href="https://smtp4dev.codeplex.com/" target="_blank">SMTP4DEV</a>.</p>
<p><figure id="attachment_1327" aria-describedby="caption-attachment-1327" style="width: 722px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-utiliser-smtp4dev.png" target="_blank"><img loading="lazy" decoding="async" class="wp-image-1327 size-full" src="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-utiliser-smtp4dev.png" alt="SMTP4DEV permet de simuler un serveur SMTP en tout simplicité" width="722" height="459" srcset="https://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-utiliser-smtp4dev.png 722w, https://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-utiliser-smtp4dev-300x190.png 300w, https://benjamin-balet.info/wp-content/uploads/2014/10/lms-installer-avec-wamp-utiliser-smtp4dev-624x396.png 624w" sizes="auto, (max-width: 722px) 100vw, 722px" /></a><figcaption id="caption-attachment-1327" class="wp-caption-text">SMTP4DEV permet de simuler un serveur SMTP en tout simplicité</figcaption></figure></p>
<p>&nbsp;</p>
<p>Avec la configuration par défaut de Jorani, vous n’avez rien d’autre à faire que de lancer l’utilitaire et Il réceptionnera les e-mails que vous pourrez visualiser à l’aide votre client e-mail préféré.</p>
<p>Si vous avez Python sous la main, vous pouvez également utiliser le module smtpd à l’aide de cette ligne de commande :</p>
<pre>python -m smtpd -n -c DebuggingServer localhost:25</pre>
<p>Les e-mails envoyés par Jorani seront alors affichés dans le terminal.</p>
<h2>Tester l’installation</h2>
<p>Il ne vous reste plus qu’à ouvrir votre navigateur et à accéder à la page <a title="Essayer l'installation locale de LMS" href="http://localhost/lms/" target="_blank">http://localhost/lms/</a></p>
<p>L’identifiant et le mot de passe sont, à chaque fois bbalet.</p>
<h2>Pour aller plus loin</h2>
<p>L’étape suivante consistera à déployer Jorani sur un serveur de votre entreprise ou bien chez votre hébergeur web. L’intérêt de l’article est de vous avoir présenté les principes techniques et les prérequis à son installation. Sachez que Jorani a été conçu et testé pour être compatible avec la plupart des produits du moment (MySQL, MariaDB, Apache, nginx, PHP 5.6 ou HHVM).</p>
<p>Voici d’autres ressources intéressantes :</p>
<ul>
<li><a title="Une application simple et gratuite de gestion des congés" href="http://benjamin-balet.info/developpement/php/application-simple-gratuite-gestion-conges/" target="_blank">Guide de démarrage rapide</a>.</li>
<li>Le <a title="Site officiel du projet Jorani" href="http://fr.jorani.org/" target="_blank">site du projet</a>.</li>
<li>Le <a title="Repository github du projet Jorani" href="https://github.com/bbalet/jorani" target="_blank">repository github du projet</a>.</li>
</ul>
<p>Cet article <a href="https://benjamin-balet.info/developpement/php/tester-lms-wamp-ordinateur/">Tester Jorani avec WAMP sur son ordinateur</a> est apparu en premier sur <a href="https://benjamin-balet.info">Benjamin BALET</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://benjamin-balet.info/developpement/php/tester-lms-wamp-ordinateur/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title>Configurer l’authentification LDAP de Jorani</title>
		<link>https://benjamin-balet.info/developpement/php/configurer-lauthentification-ldap-lms/</link>
		
		<dc:creator><![CDATA[Benjamin BALET]]></dc:creator>
		<pubDate>Tue, 14 Oct 2014 15:13:01 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<guid isPermaLink="false">http://benjamin-balet.info/?p=1311</guid>

					<description><![CDATA[<p>Configuration de LDAP dans LMS afin que la connexion des utilisateurs passe par l’annuaire d’entreprise et non par l’authentification intégrée.</p>
<p>Cet article <a href="https://benjamin-balet.info/developpement/php/configurer-lauthentification-ldap-lms/">Configurer l’authentification LDAP de Jorani</a> est apparu en premier sur <a href="https://benjamin-balet.info">Benjamin BALET</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Dans cet article, nous verrons comment configurer <a title="Site du projet Jorani" href="http://fr.jorani.org/" target="_blank" rel="noopener noreferrer">jorani</a> (un logiciel gratuit de gestion des congés et des heures supplémentaires) de manière à ce que les utilisateurs soient authentifiés par LDAP et non par Jorani. En d’autres termes, lors de la connexion à Jorani, l’application vérifiera le mot de passe stocké dans l’annuaire d’entreprise et pas celui stocké dans la base de données de Jorani. Veuillez noter que Jorani ne supporte pas la connexion par SSO.</p>
<p><span id="more-1311"></span></p>
<h1>Connexion à Jorani par annuaire LDAP</h1>
<p>Cet article concerne <a title="Site du projet Jorani" href="http://fr.jorani.org/" target="_blank" rel="noopener noreferrer">Jorani (un LMS, pour Leave Management System)</a>, un logiciel libre de gestion des congés et des heures supplémentaires. Que nous avions <a title="Une application simple et gratuite de gestion des congés" href="http://benjamin-balet.info/developpement/php/application-simple-gratuite-gestion-conges/" target="_blank" rel="noopener noreferrer">présenté dans un article précédent</a>.</p>
<h1>Principe technique</h1>
<h2>Authentification par base de données</h2>
<p>Dans le mode de connexion normal, Jorani vérifie que l’identifiant et le mot de passe saisis dans le formulaire de connexion existent dans la base de données de Jorani (LMS).</p>
<p><figure id="attachment_1312" aria-describedby="caption-attachment-1312" style="width: 500px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-ldap-authentification-normale.png" target="_blank" rel="noopener noreferrer"><img loading="lazy" decoding="async" class="wp-image-1312 size-full" src="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-ldap-authentification-normale.png" alt="Le mode d'authentification normale de LMS (sans LDAP)" width="500" height="161" srcset="https://benjamin-balet.info/wp-content/uploads/2014/10/lms-ldap-authentification-normale.png 500w, https://benjamin-balet.info/wp-content/uploads/2014/10/lms-ldap-authentification-normale-300x96.png 300w" sizes="auto, (max-width: 500px) 100vw, 500px" /></a><figcaption id="caption-attachment-1312" class="wp-caption-text">Le mode d&rsquo;authentification normale du LMS Jorani  (sans LDAP)</figcaption></figure></p>
<p>L’occasion de rappeler que même si Jorani est hébergé sur un environnement non sécurisé (accès en HTTP et pas en HTTPS), le logiciel contient des mécanismes de sécurité empêchant des tiers de voler votre mot de passe :</p>
<ul>
<li>Le mot de passe est crypté à l’aide de l’algorithme BCRYPT avant d’être stocké dans la base de données. Ainsi, si quelqu’un a accès à la base de données, il ne sera pas capable d’en extraire les mots de passe.</li>
<li>Lors de la phase de connexion, le mot de passe n’est pas envoyé en clair, mais l’échange du mot de passe entre le navigateur et le serveur est crypté par le protocole RSA.</li>
</ul>
<h2>Authentification par annuaire LDAP</h2>
<p>Dans le mode de connexion LDAP, Jorani vérifie l’existence de l’identifiant dans sa base interne et s’appuie sur l’annuaire d’entreprise afin de vérifier le mot de passe saisi.</p>
<p><figure id="attachment_1313" aria-describedby="caption-attachment-1313" style="width: 577px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-ldap-authentification-annuaire-ldap.png" target="_blank" rel="noopener noreferrer"><img loading="lazy" decoding="async" class="wp-image-1313 size-full" src="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-ldap-authentification-annuaire-ldap.png" alt="Authentification à LMS en se basant sur un annuaire LDAP" width="577" height="177" srcset="https://benjamin-balet.info/wp-content/uploads/2014/10/lms-ldap-authentification-annuaire-ldap.png 577w, https://benjamin-balet.info/wp-content/uploads/2014/10/lms-ldap-authentification-annuaire-ldap-300x92.png 300w" sizes="auto, (max-width: 577px) 100vw, 577px" /></a><figcaption id="caption-attachment-1313" class="wp-caption-text">Authentification au LMS Jorani en se basant sur un annuaire LDAP</figcaption></figure></p>
<p>Afin de vérifier que le mot de passe est correct, Jorani tente de faire ce que l’on appelle un <em>bind</em> ou liaison à l’annuaire LDAP en utilisant l’identifiant de connexion ainsi que le mot de passe saisi.</p>
<h1>Configurer Jorani</h1>
<h2>Prérequis</h2>
<h3>Information concernant l’annuaire de votre entreprise</h3>
<p>Avant de configurer Jorani, il faudra récupérer les informations de connexion auprès de la personne administrant l’annuaire de votre entreprise ou d’un collègue de travail ayant déjà configuré un logiciel utilisant LDAP pour l’authentification des utilisateurs.</p>
<h3>Activer le module LDAP de PHP</h3>
<p>Avant d’utiliser LDAP, <span style="color: #ff0000;"><strong>vous devez activer le <a title="Documentation officielle de PHP sur le module LDAP" href="http://php.net/manual/fr/ldap.setup.php" target="_blank" rel="noopener noreferrer"><span style="color: #ff0000;">module PHP de gestion des connexions LDAP</span></a></strong></span>.</p>
<p>Si Jorani est hébergé sur Internet, il est possible que votre hébergeur n’offre pas cette possibilité. Si Jorani est hébergé sur Internet et que votre annuaire d’entreprise est hébergé sur l’intranet de votre entreprise, il faut vous rapprocher de l’équipe gérant le réseau afin de vous assurer de la faisabilité technique de cette configuration.</p>
<h3>Correspondance entre LDAP et Jorani</h3>
<p>Pour que l’authentification fonctionne, il faut que l’identifiant stocké dans la base des utilisateurs de Jorani ait le même format que celui stocké dans l’annuaire de votre entreprise.</p>
<p>Par exemple « bbalet » est mon identifiant utilisateur dans Jorani, il faut aussi que cela soit mon identifiant dans LDAP. Si « bbalet » est mon login pour me connecter à Jorani, mais que mon identifiant dans l’annuaire d’entreprise est « benjamin.balet », cela ne fonctionnera pas.</p>
<h2>Configuration de LDAP dans LMS</h2>
<p>Dans l’emplacement d’installation, ouvrir le fichier <strong>application/config/config.php</strong> et chercher le groupe de lignes concernant la configuration de connexion par LDAP :</p>
<pre>$config['ldap_enabled'] = FALSE;
$config['ldap_host'] = '127.0.0.1';
$config['ldap_port'] = 389;
$config['ldap_basedn'] = 'uid=%s,ou=people,dc=company,dc=com';
</pre>
<p>Afin d’activer l’authentification via LDAP dans Jorani, il faut modifier ces variables ainsi :</p>
<ul>
<li><strong>ldap_enabled</strong> à TRUE</li>
<li><strong>ldap_host</strong> avec l’adresse de votre serveur LDAP.</li>
<li><strong>ldap_port</strong> avec le port de votre serveur LDAP.</li>
<li><strong>ldap_basedn</strong> en fonction de la manière dont les utilisateurs sont identifiés par l’annuaire. Il est important de ne pas supprimer les deux signes <em>%s</em> qui symbolisent l’emplacement du code utilisateur (ou identifiant de connexion) qui sera vérifié dans LDAP. Le contenu de cette chaîne de caractères est spécifique à votre entreprise et vous pouvez l’obtenir en contactant l’administrateur de l’annuaire LDAP de votre  entreprise.</li>
</ul>
<p>Il existe également un <a title="Lien vers la documentation (anglais)" href="https://github.com/bbalet/jorani/tree/master/docs/install#ldap" target="_blank" rel="noopener noreferrer">mode de connexion LDAP avancé</a> pour les cas où vos utilisateurs sont dans des endroits différents de l&rsquo;annuaire.</p>
<h1>Obtenir de l’aide</h1>
<p>Vous pouvez demander de l’aide sur le <a title="Groupe des utilisateurs de Jorani" href="https://groups.google.com/forum/?hl=fr#!forum/jorani" target="_blank" rel="noopener noreferrer">forum des utilisateurs de Jorani</a>, mais il sera difficile de vous assister sans avoir accès à votre annuaire LDAP. C’est pour cela qu’il faudra en premier lieu vous adresser aux administrateurs de l’annuaire de votre  entreprise et éventuellement à l’équipe réseau en cas de problèmes techniques.</p>
<p>Cet article <a href="https://benjamin-balet.info/developpement/php/configurer-lauthentification-ldap-lms/">Configurer l’authentification LDAP de Jorani</a> est apparu en premier sur <a href="https://benjamin-balet.info">Benjamin BALET</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Jorani application simple et gratuite de gestion des congés</title>
		<link>https://benjamin-balet.info/developpement/php/application-simple-gratuite-gestion-conges/</link>
					<comments>https://benjamin-balet.info/developpement/php/application-simple-gratuite-gestion-conges/#comments</comments>
		
		<dc:creator><![CDATA[Benjamin BALET]]></dc:creator>
		<pubDate>Tue, 07 Oct 2014 16:53:18 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[développement]]></category>
		<category><![CDATA[MySQL]]></category>
		<guid isPermaLink="false">http://benjamin-balet.info/?p=1297</guid>

					<description><![CDATA[<p>Application web gratuite de gestion des demandes de congés et de déclaration des heures supplémentaires. C’est un logiciel open source écrit en PHP MySQL simple à installer et à utiliser.</p>
<p>Cet article <a href="https://benjamin-balet.info/developpement/php/application-simple-gratuite-gestion-conges/">Jorani application simple et gratuite de gestion des congés</a> est apparu en premier sur <a href="https://benjamin-balet.info">Benjamin BALET</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Jorani est un LMS (pour Leave Management System). C&rsquo;est un logiciel libre dédié à un processus important des ressources humaines : la gestion des congés et des heures supplémentaires. Cet article vous en expliquera les fonctions principales.</p>
<p><span id="more-1297"></span></p>
<h1>Jorani solution open source pour gérer les congés</h1>
<p>Vous gérez une association ou une PME ? Vous avez un processus à base de formulaires papier et vous souhaitez passer en douceur à une solution informatique sans prise de tête ? Jorani est fait pour vous.  Conçu initialement pour une ONG employant environ 100 personnes et opérant dans plusieurs pays, son auteur a décidé de publier le code source sous une licence libre GPL. Ce qui veut dire que vous pouvez l’obtenir et l’installer gratuitement.</p>
<p>Comme vous le verrez dans les copies d’écrans, Jorani est disponible en anglais et en français. Passons maintenant en revue les points clés de la solution.</p>
<h2>Les contrats</h2>
<p>Dans Jorani, on peut gérer différents types de contrat avec des périodes annuelles de congés différentes. Par exemple, en France les congés courent du 1<sup>er</sup> juin au 31 mai, mais vous pourriez gérer un autre site à l’étranger  avec des périodes différentes.</p>
<p><figure id="attachment_1301" aria-describedby="caption-attachment-1301" style="width: 787px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-liste-contrats.png" target="_blank" rel="noopener noreferrer"><img loading="lazy" decoding="async" class="wp-image-1301 size-full" src="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-liste-contrats.png" alt="Liste des contrats" width="787" height="268" srcset="https://benjamin-balet.info/wp-content/uploads/2014/10/lms-liste-contrats.png 787w, https://benjamin-balet.info/wp-content/uploads/2014/10/lms-liste-contrats-300x102.png 300w, https://benjamin-balet.info/wp-content/uploads/2014/10/lms-liste-contrats-624x212.png 624w" sizes="auto, (max-width: 787px) 100vw, 787px" /></a><figcaption id="caption-attachment-1301" class="wp-caption-text">Liste des contrats</figcaption></figure></p>
<p>L’icône de calendrier permet – comme expliqué dans le paragraphe suivant – de définir les jours non travaillés. Tandis que l’icône de modification avancée permet de modifier les jours de congé auquel le contrat donne droit :</p>
<p><figure id="attachment_1302" aria-describedby="caption-attachment-1302" style="width: 728px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-crédits-absences-contrat.png" target="_blank" rel="noopener noreferrer"><img loading="lazy" decoding="async" class="wp-image-1302 size-full" src="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-crédits-absences-contrat.png" alt="Définir le nombre de jours de congés sur un contrat" width="728" height="286" srcset="https://benjamin-balet.info/wp-content/uploads/2014/10/lms-crédits-absences-contrat.png 728w, https://benjamin-balet.info/wp-content/uploads/2014/10/lms-crédits-absences-contrat-300x117.png 300w, https://benjamin-balet.info/wp-content/uploads/2014/10/lms-crédits-absences-contrat-624x245.png 624w" sizes="auto, (max-width: 728px) 100vw, 728px" /></a><figcaption id="caption-attachment-1302" class="wp-caption-text">Définir le nombre de jours de congés sur un contrat</figcaption></figure></p>
<p>Jorani permet également de saisir des droits individuels d’absence pour chacun des employés. Le nombre de jours saisis pour un employé peut être négatif, ce qui permet de gérer le cas des employés arrivant en cours de route. Ou le cas de certains pays où le droit à prendre des congés s’acquiert mois par mois.</p>
<h2>Les jours non travaillés</h2>
<p>Jorani permet de calculer automatiquement la durée d’une demande de congés. Encore faut-il définir quels sont les jours non travaillés. L’écran reproduit ci-dessous permet de le faire au jour le jour (en cliquant sur une case du calendrier) ou de définir une série (par exemple tous les samedis).</p>
<p><figure id="attachment_1303" aria-describedby="caption-attachment-1303" style="width: 935px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-jours-non-travailles.png" target="_blank" rel="noopener noreferrer"><img loading="lazy" decoding="async" class="wp-image-1303 size-full" src="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-jours-non-travailles.png" alt="Définition des jours non travaillés pour un contrat donné" width="935" height="707" srcset="https://benjamin-balet.info/wp-content/uploads/2014/10/lms-jours-non-travailles.png 935w, https://benjamin-balet.info/wp-content/uploads/2014/10/lms-jours-non-travailles-300x226.png 300w, https://benjamin-balet.info/wp-content/uploads/2014/10/lms-jours-non-travailles-624x471.png 624w" sizes="auto, (max-width: 935px) 100vw, 935px" /></a><figcaption id="caption-attachment-1303" class="wp-caption-text">Définition des jours non travaillés pour un contrat donné</figcaption></figure></p>
<p>On peut se servir de cet écran pour définir les RTT imposées (ici le 26/12 par exemple). Ou bien laisser les employés les poser.</p>
<p>Les jours non travaillés sont définis au niveau d’un contrat. Aussi vous pouvez gérer le cas des temps partiels à l’aide cet écran et en créant un contrat spécifique.</p>
<h2>Les droits individuels à congés</h2>
<p>Comme vu précédemment, rien ne vous empêche de saisir des durées négatives afin de rattraper les cas d’employés arrivés en cours d’année ou résoudre les problèmes de rattrapage de congés pris par anticipation. Dans cet exemple, le nombre de jours sera soustrait au nombre de jours accordés par le contrat. Par exemple, si mon contrat me donne droit à 20 jours de congé, je n’ai le droit qu’à 7 jours pour cette année.</p>
<p><figure id="attachment_1305" aria-describedby="caption-attachment-1305" style="width: 576px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-crédits-individuel-absences.png" target="_blank" rel="noopener noreferrer"><img loading="lazy" decoding="async" class="wp-image-1305 size-full" src="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-crédits-individuel-absences.png" alt="Jours d'absences (crédit individuel) autorisés pour un employé" width="576" height="180" srcset="https://benjamin-balet.info/wp-content/uploads/2014/10/lms-crédits-individuel-absences.png 576w, https://benjamin-balet.info/wp-content/uploads/2014/10/lms-crédits-individuel-absences-300x93.png 300w" sizes="auto, (max-width: 576px) 100vw, 576px" /></a><figcaption id="caption-attachment-1305" class="wp-caption-text">Jours d&rsquo;absences (crédit individuel) autorisés pour un employé</figcaption></figure></p>
<p>C’est cette même fonctionnalité qui gère les congés individuels (maternité, paternité, congés exceptionnels, décès d’un proche, mariage, etc.).</p>
<h2>La demande de congés</h2>
<p>J’en viens enfin à l’écran principal de l’outil : le formulaire de demande des congés. Il était toutefois nécessaire de décrire les fonctionnalités précédentes, car elles permettent à l’outil de fonctionner correctement.</p>
<p>Dans cette capture d’écran, nous voyons que la durée du congé est calculée automatiquement  (grâce aux jours travaillés que nous avons définis plus tôt). Un autre point important est que l’application prévient l’utilisateur en cas de dépassement. Toutefois, rien n’interdit à l’employé de créer cette demande. Son manager pouvant la valider ou la rejeter.</p>
<p><figure id="attachment_1306" aria-describedby="caption-attachment-1306" style="width: 643px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-demande-conges.png" target="_blank" rel="noopener noreferrer"><img loading="lazy" decoding="async" class="wp-image-1306 size-full" src="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-demande-conges.png" alt="Formulaire de demande d'absence" width="643" height="542" srcset="https://benjamin-balet.info/wp-content/uploads/2014/10/lms-demande-conges.png 643w, https://benjamin-balet.info/wp-content/uploads/2014/10/lms-demande-conges-300x252.png 300w, https://benjamin-balet.info/wp-content/uploads/2014/10/lms-demande-conges-624x525.png 624w" sizes="auto, (max-width: 643px) 100vw, 643px" /></a><figcaption id="caption-attachment-1306" class="wp-caption-text">Formulaire de demande d&rsquo;absence</figcaption></figure></p>
<p>Lorsque l’utilisateur crée sa demande, un e-mail est envoyé à son supérieur hiérarchique. Avec des liens pour valider ou rejeter la demande directement depuis l’email (Jorani présente aussi un écran avec la liste des demandes à examiner). Voici le processus de demande des congés :</p>
<p><figure id="attachment_1307" aria-describedby="caption-attachment-1307" style="width: 300px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-workflow-demande-conges.png" target="_blank" rel="noopener noreferrer"><img loading="lazy" decoding="async" class="wp-image-1307 size-medium" src="http://benjamin-balet.info/wp-content/uploads/2014/10/lms-workflow-demande-conges-300x262.png" alt="Processus de gestion de la demande d'absence" width="300" height="262" srcset="https://benjamin-balet.info/wp-content/uploads/2014/10/lms-workflow-demande-conges-300x262.png 300w, https://benjamin-balet.info/wp-content/uploads/2014/10/lms-workflow-demande-conges-624x546.png 624w, https://benjamin-balet.info/wp-content/uploads/2014/10/lms-workflow-demande-conges.png 937w" sizes="auto, (max-width: 300px) 100vw, 300px" /></a><figcaption id="caption-attachment-1307" class="wp-caption-text">Processus de gestion de la demande d&rsquo;absence</figcaption></figure></p>
<p>Il existe un processus similaire de gestion des heures supplémentaires. Une fois que la demande est validée, elle vient créditer le nombre de jours de congé de récupération d’un employé. À moins que vous souhaitiez gérer ce cas différemment.</p>
<h2>Calendriers</h2>
<p>Jorani contient plusieurs calendriers (individuel, d’équipe, global, etc.) permettant de visualiser qui est absent. Ce qui est très pratique, surtout que l’on peut superposer l’affichage des jours non travaillés :</p>
<p><figure id="attachment_1299" aria-describedby="caption-attachment-1299" style="width: 300px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2014/10/calendar-organization.png" target="_blank" rel="noopener noreferrer"><img loading="lazy" decoding="async" class="wp-image-1299 size-medium" src="http://benjamin-balet.info/wp-content/uploads/2014/10/calendar-organization-300x203.png" alt="calendrier global (niveau organisation)" width="300" height="203" srcset="https://benjamin-balet.info/wp-content/uploads/2014/10/calendar-organization-300x203.png 300w, https://benjamin-balet.info/wp-content/uploads/2014/10/calendar-organization-624x423.png 624w, https://benjamin-balet.info/wp-content/uploads/2014/10/calendar-organization.png 821w" sizes="auto, (max-width: 300px) 100vw, 300px" /></a><figcaption id="caption-attachment-1299" class="wp-caption-text">calendrier global (niveau organisation)</figcaption></figure></p>
<h2>Les autres fonctionnalités</h2>
<p>Voici une liste non exhaustive des autres fonctions que vous trouverez dans cet outil de gestion des congés :</p>
<ul>
<li>L’outil est entièrement gratuit (pas de modules payants qu&rsquo;il faudrait acquérir en plus de la version gratuite).</li>
<li>Les données vous appartiennent (rien n’est envoyé à un serveur tiers, tout reste sur votre serveur).</li>
<li>Dans Jorani, toutes les listes présentées à l’écran sont exportables sous Excel en un clic.</li>
<li>La sécurité est au cœur de la conception de Jorani. Si vous n’avez pas le droit de modifier un objet, vous ne le pourrez pas.</li>
<li>Un utilisateur avec les droits RH peut modifier n’importe quelle demande.</li>
<li>Jorani est très compatible. Vous pourrez l’installer sur un hébergement mutualisé, un serveur Apache ou nginx, avec MySQL ou MariaDB, avec HHVM ou n’importe quelle version de PHP 5.</li>
<li>Il est très facile d’y connecter Google Analytics afin de comprendre l’utilisation qu’en font vos employés.</li>
<li>Il y a une petite API pour connecter le système à des logiciels tiers.</li>
</ul>
<h2>Pour aller plus loin</h2>
<p>J’espère que cet article vous a donné envie d’adopter Jorani. Pour plus d’information, vous pouvez :</p>
<ul>
<li>Essayer la démonstration en ligne : <a title="Démo en ligne" href="http://demo.leave-management-system.org/" target="_blank" rel="noopener noreferrer">http://demo.leave-management-system.org</a></li>
<li>Aller sur le site officiel : <a title="Site officiel du projet" href="http://fr.jorani.org/" target="_blank" rel="noopener noreferrer">http://fr.jorani.org/</a></li>
<li>Récupérer les sources sur la forge Github : <a title="Sources et téléchargement" href="https://github.com/bbalet/jorani" target="_blank" rel="noopener noreferrer">https://github.com/bbalet/jorani</a></li>
</ul>
<h2>Contribuez !</h2>
<p>On l’oublie souvent, mais les logiciels libres sont le fruit du travail de bénévoles qui investissent du temps et un peu d’argent dans leurs créations. Ils ont donc besoin d’un petit coup de pouce. Vous pouvez améliorer le logiciel de différentes manières :</p>
<ul>
<li>Envoyez vos souhaits de fonctionnalités ou déclarez les défauts via le projet Github ou le formulaire de contact du site officiel.</li>
<li>Faites un don au projet.</li>
<li>Traduisez Jorani dans une autre langue.</li>
<li>Apportez votre pierre à l’édifice en développant une nouvelle fonctionnalité ou en améliorant le code existant.</li>
</ul>
<p>Cet article <a href="https://benjamin-balet.info/developpement/php/application-simple-gratuite-gestion-conges/">Jorani application simple et gratuite de gestion des congés</a> est apparu en premier sur <a href="https://benjamin-balet.info">Benjamin BALET</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://benjamin-balet.info/developpement/php/application-simple-gratuite-gestion-conges/feed/</wfw:commentRss>
			<slash:comments>16</slash:comments>
		
		
			</item>
		<item>
		<title>Chiffrement RSA partiel en golang et Javascript</title>
		<link>https://benjamin-balet.info/developpement/chiffrement-rsa-partiel-en-golang-javascript/</link>
		
		<dc:creator><![CDATA[Benjamin BALET]]></dc:creator>
		<pubDate>Wed, 18 Sep 2013 14:50:59 +0000</pubDate>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[golang]]></category>
		<category><![CDATA[Google go]]></category>
		<guid isPermaLink="false">http://benjamin-balet.info/?p=1273</guid>

					<description><![CDATA[<p>Comment crypter et décrypter en RSA avec JS et Go ?</p>
<p>Cet article <a href="https://benjamin-balet.info/developpement/chiffrement-rsa-partiel-en-golang-javascript/">Chiffrement RSA partiel en golang et Javascript</a> est apparu en premier sur <a href="https://benjamin-balet.info">Benjamin BALET</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>On peut être amené à ne <strong>crypter qu’une partie d’un message envoyé à un serveur</strong>. Par exemple avec un formulaire de connexion l’identifiant peut passer en clair, mais pas le mot de passe. Aujourd’hui, les hébergements avec du SSL ou un certificat auto-signé sont monnaie courante, mais comment faire avec une application Google Go hébergée sur le Cloud Google App Engine ?<span id="more-1273"></span></p>
<h2>Rappel concernant RSA</h2>
<p>Voici un exemple de chiffrement partiel d’un contenu à l’aide de l’algorithme asymétrique RSA. On rappelle qu&rsquo;avec RSA il y a deux clés :</p>
<ul>
<li>Une clé publique avec laquelle le client peut crypter des messages, mais pas les décrypter.</li>
<li>Une clé privée avec laquelle le serveur peut crypter et décrypter des messages (puisqu&rsquo;une clé privée RSA contient également la clé publique).</li>
</ul>
<h2>Générer les clés avec openssl</h2>
<p>La première étape consiste à générer une paire de clé publique et privée, puis à les stocker dans votre arborescence projet. On peut bricoler quelque chose avec golang ou utiliser openssl (allez voir sur le site, il existe maintenant des versions pour tous les OS) avec les commandes suivantes :</p>
<p><span style="font-family: 'courier new', courier;">openssl genrsa -out private.pem 1024</span></p>
<p><span style="font-family: 'courier new', courier;">openssl rsa -pubout -in private.pem -out public.pem</span></p>
<h2>Template et code Golang du formulaire de connexion</h2>
<p>Puis on créera un template pour un formulaire de login. Ce template utilisera la bibliothèque <b>jsencrypt</b> qui est une implémentation de l’algorithme RSA en Javascript. Le principe du code est le suivant :</p>
<ul>
<li>A l’appel de la page, Go remplira la variable public_key avec le contenu de la clé publique stockée côté serveur (et que vous avez générée plus tôt).</li>
<li>Côté client, on chiffre le mot de passe avec la clé publique et on envoie le contenu chiffré au serveur. Notez que le mot de passe en clair n’est pas transmis puisque le champ est à l’extérieur du formulaire. La bibliothèque <a title="Projet de la bibliothèque jsencrypt" href="https://github.com/travist/jsencrypt"><b>jsencrypt</b></a> se charge également d’encoder le contenu chiffré (qui est binaire) en base64 de manière à ce qu’il puisse transiter en tant que valeur du formulaire sans problème.</li>
</ul>
<p><span style="font-family: 'courier new', courier;">&lt;!doctype html&gt;</span></p>
<p><span style="font-family: 'courier new', courier;"><b>&lt;html&gt;</b></span></p>
<p><span style="font-family: 'courier new', courier;"><b>&lt;head&gt;</b></span></p>
<p><span style="font-family: 'courier new', courier;">    <b>&lt;meta</b> charset= »utf-8&Prime;<b>&gt;</b></span></p>
<p><span style="font-family: 'courier new', courier;">    <b>&lt;title&gt;{{.Title}}&lt;/title&gt;</b></span></p>
<p><span style="font-family: 'courier new', courier;">    <b>&lt;script</b> src= »/js/jsencrypt.min.js »<b>&gt;&lt;/script&gt;</b></span></p>
<p><span style="font-family: 'courier new', courier;">    <b>&lt;script</b> src= »/js/jquery-1.10.2.min.js »<b>&gt;&lt;/script&gt;</b></span></p>
<p><span style="font-family: 'courier new', courier;"><b>&lt;/head&gt;</b></span></p>
<p><span style="font-family: 'courier new', courier;"><b>&lt;body&gt;</b> </span></p>
<p><span style="font-family: 'courier new', courier;"><b>&lt;form</b> id= »target » action= »/send » method= »POST »<b>&gt;</b></span></p>
<p><span style="font-family: 'courier new', courier;">    <b>&lt;label</b> for= »User »<b>&gt;</b>User:<b>&lt;/label&gt;</b></span></p>
<p><span style="font-family: 'courier new', courier;">    <b>&lt;input</b> type= »text » name= »User » autofocus required <b>/&gt;&lt;br</b> <b>/&gt;</b></span></p>
<p><span style="font-family: 'courier new', courier;">    <b>&lt;input</b> type= »hidden » name= »CipheredValue » id= »CipheredValue » <b>/&gt;&lt;br</b> <b>/&gt;</b></span></p>
<p><span style="font-family: 'courier new', courier;"><b>&lt;/form&gt;</b></span></p>
<p><span style="font-family: 'courier new', courier;"><b>&lt;label</b> for= »Password »<b>&gt;</b>Password:<b>&lt;/label&gt;</b></span></p>
<p><span style="font-family: 'courier new', courier;"><b>&lt;input</b> type= »password » name= »Password » id= »Password » required <b>/&gt;&lt;br</b> <b>/&gt;</b></span></p>
<p><span style="font-family: 'courier new', courier;"><b>&lt;input</b> id= »encrypt » type= »button » value= »send » <b>/&gt;</b></span></p>
<p><span style="font-family: 'courier new', courier;">        <b>&lt;script&gt;</b></span></p>
<p><span style="font-family: 'courier new', courier;">    var public_key = « {{printf « %s » .Value}} »;</span></p>
<p><span style="font-family: 'courier new', courier;">        $(function() {</span></p>
<p><span style="font-family: 'courier new', courier;">        $(&lsquo;#encrypt&rsquo;).click(function() {</span></p>
<p><span style="font-family: 'courier new', courier;">        var encrypt = new JSEncrypt();</span></p>
<p><span style="font-family: 'courier new', courier;">        encrypt.setPublicKey(public_key);</span></p>
<p><span style="font-family: 'courier new', courier;">        var encrypted = encrypt.encrypt($(&lsquo;#Password&rsquo;).val());</span></p>
<p><span style="font-family: 'courier new', courier;">        $(&lsquo;#CipheredValue&rsquo;).val(encrypted);</span></p>
<p><span style="font-family: 'courier new', courier;">        $(&lsquo;#target&rsquo;).submit();</span></p>
<p><span style="font-family: 'courier new', courier;">        });</span></p>
<p><span style="font-family: 'courier new', courier;">    });    </span></p>
<p><span style="font-family: 'courier new', courier;">    <b>&lt;/script&gt;</b></span></p>
<p><span style="font-family: 'courier new', courier;">    <b>&lt;/body&gt;</b></span></p>
<p><span style="font-family: 'courier new', courier;"><b>&lt;/html&gt;</b></span></p>
<p>Le gestion de la page est ultra simple puisqu’il s’agit simplement de servir le template et de remplir la variable contenant la clé publique :</p>
<p><span style="font-family: 'courier new', courier;"><b>type</b> Page <b>struct</b> {</span></p>
<p><span style="font-family: 'courier new', courier;">    Title string</span></p>
<p><span style="font-family: 'courier new', courier;">    Value []byte</span></p>
<p><span style="font-family: 'courier new', courier;">}</span></p>
<p><span style="font-family: 'courier new', courier;"><b>func</b> homeHandler(w http.ResponseWriter, r *http.Request) {</span></p>
<p><span style="font-family: 'courier new', courier;">    // Read the public key</span></p>
<p><span style="font-family: 'courier new', courier;">    pemData, err := ioutil.ReadFile(publicKey)</span></p>
<p><span style="font-family: 'courier new', courier;">    <b>if</b> err != nil {</span></p>
<p><span style="font-family: 'courier new', courier;">        log.Fatalf(« read key file: %s », err)</span></p>
<p><span style="font-family: 'courier new', courier;">    }</span></p>
<p><span style="font-family: 'courier new', courier;">    <b>var</b> p = Page{Title: « Login », Value: pemData}</span></p>
<p><span style="font-family: 'courier new', courier;">    err = templates.ExecuteTemplate(w, « home.html », p)</span></p>
<p><span style="font-family: 'courier new', courier;">    <b>if</b> err != nil {</span></p>
<p><span style="font-family: 'courier new', courier;">        http.Error(w, err.Error(), http.StatusInternalServerError)</span></p>
<p><span style="font-family: 'courier new', courier;">    }</span></p>
<p><span style="font-family: 'courier new', courier;">}</span></p>
<h2>Décryptage du message chiffré avec RSA côté serveur</h2>
<p>Là où les choses se compliquent, c’est pour décoder le message côté serveur. Le code est assez long parce qu’il effectue toutes les actions suivantes :</p>
<ol>
<li>Lire le fichier contenu la clé privée.</li>
<li>Interpréter le format de fichier PEM et tester sa validité.</li>
<li>Décoder la clé privée.</li>
<li>Convertir le message crypté passé dans le formulaire de la base64 à du binaire.</li>
<li>Déchiffrer le message crypté (le mot de passe).</li>
</ol>
<p><span style="font-family: 'courier new', courier;"><b>func</b> sendHandler(w http.ResponseWriter, r *http.Request) {</span></p>
<p><span style="font-family: 'courier new', courier;">     // Read the private key</span></p>
<p><span style="font-family: 'courier new', courier;">    pemData, err := ioutil.ReadFile(privateKey)</span></p>
<p><span style="font-family: 'courier new', courier;">    <b>if</b> err != nil {</span></p>
<p><span style="font-family: 'courier new', courier;">        http.Error(w, err.Error(), http.StatusInternalServerError)</span></p>
<p><span style="font-family: 'courier new', courier;">        <b>return</b></span></p>
<p><span style="font-family: 'courier new', courier;">    }</span></p>
<p><span style="font-family: 'courier new', courier;"> </span></p>
<p><span style="font-family: 'courier new', courier;">    // Extract the PEM-encoded data block</span></p>
<p><span style="font-family: 'courier new', courier;">    block, _ := pem.Decode(pemData)</span></p>
<p><span style="font-family: 'courier new', courier;">    <b>if</b> block == nil {</span></p>
<p><span style="font-family: 'courier new', courier;">        http.Error(w, err.Error(), http.StatusInternalServerError)</span></p>
<p><span style="font-family: 'courier new', courier;">        <b>return</b></span></p>
<p><span style="font-family: 'courier new', courier;">    }</span></p>
<p><span style="font-family: 'courier new', courier;">    <b>if</b> got, want := block.Type, « RSA PRIVATE KEY »; got != want {</span></p>
<p><span style="font-family: 'courier new', courier;">        http.Error(w, err.Error(), http.StatusInternalServerError)</span></p>
<p><span style="font-family: 'courier new', courier;">        <b>return</b></span></p>
<p><span style="font-family: 'courier new', courier;">    }</span></p>
<p><span style="font-family: 'courier new', courier;"> </span></p>
<p><span style="font-family: 'courier new', courier;">    // Decode the RSA private key</span></p>
<p><span style="font-family: 'courier new', courier;">    priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)</span></p>
<p><span style="font-family: 'courier new', courier;">    <b>if</b> err != nil {</span></p>
<p><span style="font-family: 'courier new', courier;">        http.Error(w, err.Error(), http.StatusInternalServerError)</span></p>
<p><span style="font-family: 'courier new', courier;">        <b>return</b></span></p>
<p><span style="font-family: 'courier new', courier;">    }</span></p>
<p><span style="font-family: 'courier new', courier;"> </span></p>
<p><span style="font-family: 'courier new', courier;">    // Decode the Base64 into binary</span></p>
<p><span style="font-family: 'courier new', courier;">    cipheredValue, err := base64.StdEncoding.DecodeString(r.FormValue(« CipheredValue »))</span></p>
<p><span style="font-family: 'courier new', courier;">    <b>if</b> err != nil {</span></p>
<p><span style="font-family: 'courier new', courier;">        http.Error(w, err.Error(), http.StatusInternalServerError)</span></p>
<p><span style="font-family: 'courier new', courier;">        <b>return</b></span></p>
<p><span style="font-family: 'courier new', courier;">    }</span></p>
<p><span style="font-family: 'courier new', courier;"> </span></p>
<p><span style="font-family: 'courier new', courier;">    // Decrypt the data</span></p>
<p><span style="font-family: 'courier new', courier;">    <b>var</b> out []byte</span></p>
<p><span style="font-family: 'courier new', courier;">    out, err = rsa.DecryptPKCS1v15(rand.Reader, priv, cipheredValue)</span></p>
<p><span style="font-family: 'courier new', courier;">    <b>if</b> err != nil {</span></p>
<p><span style="font-family: 'courier new', courier;">        http.Error(w, err.Error(), http.StatusInternalServerError)</span></p>
<p><span style="font-family: 'courier new', courier;">    }</span></p>
<p><span style="font-family: 'courier new', courier;"> </span></p>
<p><span style="font-family: 'courier new', courier;">    <b>var</b> p = Page{Title: « Decrypt value », Value: out}</span></p>
<p><span style="font-family: 'courier new', courier;">  </span><span style="font-family: 'courier new', courier;">  err = templates.ExecuteTemplate(w, « send.html », p)</span></p>
<p><span style="font-family: 'courier new', courier;">    <b>if</b> err != nil {</span></p>
<p><span style="font-family: 'courier new', courier;">        http.Error(w, err.Error(), http.StatusInternalServerError)</span></p>
<p><span style="font-family: 'courier new', courier;">    }</span></p>
<p><span style="font-family: 'courier new', courier;">}</span></p>
<p>Il ne vous reste plus qu’à compléter cet extrait de code avec votre gestionnaire des utilisateurs. Par exemple, en vérifiant le mot de passe avec une valeur stockée en base de données.</p>
<p>Cet article <a href="https://benjamin-balet.info/developpement/chiffrement-rsa-partiel-en-golang-javascript/">Chiffrement RSA partiel en golang et Javascript</a> est apparu en premier sur <a href="https://benjamin-balet.info">Benjamin BALET</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>HTTP Rider le test de performance quick-and-dirty</title>
		<link>https://benjamin-balet.info/developpement/http-rider-le-test-de-performance-quick-and-dirty/</link>
		
		<dc:creator><![CDATA[Benjamin BALET]]></dc:creator>
		<pubDate>Sun, 18 Aug 2013 15:05:19 +0000</pubDate>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[Performances]]></category>
		<category><![CDATA[.net]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[tests]]></category>
		<guid isPermaLink="false">http://benjamin-balet.info/?p=1261</guid>

					<description><![CDATA[<p>Créez un test de charge en trois étapes à l'aide d'un assistant graphique</p>
<p>Cet article <a href="https://benjamin-balet.info/developpement/http-rider-le-test-de-performance-quick-and-dirty/">HTTP Rider le test de performance quick-and-dirty</a> est apparu en premier sur <a href="https://benjamin-balet.info">Benjamin BALET</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><strong>HTTP Rider</strong> est un outil capable d’enregistrer une liste de requêtes HTTP pour les rejouer ensuite. Il n’est pas possible de créer un parcours complexe avec des conditions ou des boucles. Cependant, il conviendra tout à fait aux cas où l’on souhaite obtenir un résultat rapide concernant les temps de réponse de certaines pages web. Voyons ce que l’on peut en tirer avec un exemple concret.<span id="more-1261"></span></p>
<h2>Un exemple concret</h2>
<p>Comme d’habitude, je me base sur OpenCart (<a href="http://www.opencart.com/">http://www.opencart.com/</a>), une application open source tournant sur une pile WAMP. Cette application en PHP est intéressante, parce qu’elle présente les cas d’usage les plus répandus (CRUD, recherche, liste, détail) et elle est livrée avec un jeu de données minimaliste.</p>
<h3>Jeu de données</h3>
<p><figure id="attachment_1264" aria-describedby="caption-attachment-1264" style="width: 758px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2013/08/HttpRider_datasource.png" target="_blank"><img loading="lazy" decoding="async" class="size-full wp-image-1264 " alt="Définir un jeu de données avec HTTP Rider" src="http://benjamin-balet.info/wp-content/uploads/2013/08/HttpRider_datasource.png" width="758" height="515" srcset="https://benjamin-balet.info/wp-content/uploads/2013/08/HttpRider_datasource.png 758w, https://benjamin-balet.info/wp-content/uploads/2013/08/HttpRider_datasource-300x203.png 300w" sizes="auto, (max-width: 758px) 100vw, 758px" /></a><figcaption id="caption-attachment-1264" class="wp-caption-text">Définir un jeu de données avec HTTP Rider</figcaption></figure></p>
<p>Il est possible d’importer dans le scénario un fichier CSV, une table ou un onglet Excel afin de l’utiliser dans l’étape d’édition du scénario. Ici, j’ai choisi de créer une liste d’identifiants d’articles différents afin d’introduire de la variation dans les données utilisées. Ce qui permet de ne pas obtenir de trop bons résultats à cause des différents caches applicatifs.</p>
<h3>Enregistreur</h3>
<p><figure id="attachment_1265" aria-describedby="caption-attachment-1265" style="width: 758px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2013/08/HttpRider_recorder.png" target="_blank"><img loading="lazy" decoding="async" class="size-full wp-image-1265 " alt="Enregistrer des actions utilisateurs avec HTTP Rider" src="http://benjamin-balet.info/wp-content/uploads/2013/08/HttpRider_recorder.png" width="758" height="515" srcset="https://benjamin-balet.info/wp-content/uploads/2013/08/HttpRider_recorder.png 758w, https://benjamin-balet.info/wp-content/uploads/2013/08/HttpRider_recorder-300x203.png 300w" sizes="auto, (max-width: 758px) 100vw, 758px" /></a><figcaption id="caption-attachment-1265" class="wp-caption-text">Enregistrer des actions utilisateurs avec HTTP Rider</figcaption></figure></p>
<p>HTTP Rider utilise Fiddler afin d’enregistrer tous les échanges http (seulement les requêtes GET et POST) ayant lieu sur la machine où il est utilisé. On peut définir des règles d’inclusion ou d’exclusion d’URL afin de filtrer ce qui sera enregistré (et donc rejoué plus tard lorsque l’on lancera le tir).</p>
<h3>Éditeur de script</h3>
<p><figure id="attachment_1266" aria-describedby="caption-attachment-1266" style="width: 758px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2013/08/HttpRider_design.png" target="_blank"><img loading="lazy" decoding="async" class="size-full wp-image-1266 " alt="Editeur de script HTTP Rider" src="http://benjamin-balet.info/wp-content/uploads/2013/08/HttpRider_design.png" width="758" height="515" srcset="https://benjamin-balet.info/wp-content/uploads/2013/08/HttpRider_design.png 758w, https://benjamin-balet.info/wp-content/uploads/2013/08/HttpRider_design-300x203.png 300w" sizes="auto, (max-width: 758px) 100vw, 758px" /></a><figcaption id="caption-attachment-1266" class="wp-caption-text">Editeur de script HTTP Rider</figcaption></figure></p>
<p>L’étape suivant celle de l’enregistrement permet de supprimer des requêtes que l’on aurait oublié de filtrer. On peut aussi utiliser des paramètres issus des données que l’on aurait importées précédemment. Ici je lie le paramètre « product_id » à la première colonne de mon jeu d’essai.</p>
<p>On notera qu’il est possible d’enregistrer le scénario (mono-script en fait).</p>
<h3>Exécution</h3>
<p><figure id="attachment_1267" aria-describedby="caption-attachment-1267" style="width: 749px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2013/08/HttpRider_loadtest.png" target="_blank"><img loading="lazy" decoding="async" class="size-full wp-image-1267 " alt="Résultats en temps réel du test de charge avec HTTP Rider" src="http://benjamin-balet.info/wp-content/uploads/2013/08/HttpRider_loadtest.png" width="749" height="514" srcset="https://benjamin-balet.info/wp-content/uploads/2013/08/HttpRider_loadtest.png 749w, https://benjamin-balet.info/wp-content/uploads/2013/08/HttpRider_loadtest-300x205.png 300w" sizes="auto, (max-width: 749px) 100vw, 749px" /></a><figcaption id="caption-attachment-1267" class="wp-caption-text">Résultats en temps réel du test de charge avec HTTP Rider</figcaption></figure></p>
<p>&nbsp;</p>
<p>La dernière étape permet de lancer le tir proprement dit. On peut fixer :</p>
<ul>
<li>Le nombre d’utilisateurs simulés en parallèle.</li>
<li>La durée du tir (Elapse time).</li>
<li>La pause à effectuer entre chaque requête http (think time).</li>
<li>La limite à ne pas dépasser lorsque l’on attend la réponse HTTP (puisque le timeout est une notion définie sur le navigateur des visiteurs).</li>
</ul>
<p>On obtient alors les temps de réponse des requêtes HTTP enregistrées. L’outil ne fournit pas d’autres rapports ou de possibilités d’export que ce que vous voyez sur cet écran.</p>
<h2>Comment est fait HTTP Rider ?</h2>
<ul>
<li>Il est en .Net et il ne tournera que sous Windows avec le framework 4.0.</li>
<li>Il se base sur le composant Fiddler (le couteau suisse indispensable du consultant en performance) pour la capture des trames HTTP. <a href="http://fiddler2.com/">http://fiddler2.com/</a></li>
<li>Il utilise HTML Agility pack pour manipuler les en-têtes et corps de requête <a href="http://htmlagilitypack.codeplex.com/">http://htmlagilitypack.codeplex.com/</a></li>
<li>Et d’autres composants open source.</li>
</ul>
<h2>Les points forts d’HTTP Rider</h2>
<ul>
<li>Assistant graphique permettant de créer un test de charge sans prétention, mais rapidement.</li>
<li>Possibilité de capturer les requêtes http depuis n’importe quel exécutable émettant des trames HTTP (merci Fiddler).</li>
<li>La possibilité d’utiliser un jeu de données afin d’introduire de la variation.</li>
<li>Possibilités d’inclusion et d’exclusion d’URL.</li>
</ul>
<h2>Les points faibles d’HTTP Rider</h2>
<ul>
<li>Ne supporte que le protocole HTTP (au niveau requête ou URL-based si on le comparait à LoadRunner).</li>
<li>Le code source n’est pas disponible.</li>
<li>C’est un projet expérimental (l’auteur n’a visiblement pas d’intention de le continuer ou de l’ouvrir aux contributeurs).</li>
<li>La dernière version date du 24/02/2012.</li>
<li>Limité aux verbes GET et POST.</li>
<li>Ne supporte pas les sessions TLS (HTTPS).</li>
<li>Pas de reporting (cependant, je pense que ce n’est pas son positionnement).</li>
</ul>
<h2>Conclusion</h2>
<p>HTTP Rider est un outil intéressant qui est comparable à Apache Benchmark, mais que l’on pourrait situer un cran au-dessus parce qu’il est doté d’une interface graphique permettant de faire la capture des échanges et d’avoir un résultat rapide. J’ai bien aimé la possibilité de lier un jeu de données depuis Excel en toute simplicité (encore un point fort par rapport à AB).</p>
<p>Attention au fait que cet outil ne permet de monter un scénario composé que d’un seul script. On peut cependant lancer plusieurs instances de l’outil sur une seule ou plusieurs machines.</p>
<p>Malheureusement l’outil ne va pas plus loin en termes de résultats (pas de graph de type « over time ») ou de définition du scénario (rampe, etc.). J’ai de gros doutes sur sa pérennité et il est vraiment dommage que le code source ne soit pas disponible.</p>
<p>En bref, si vous souhaitez faire un test de charge vite fait afin d’avoir une idée des temps de réponse sans la complexité d’un JMeter ou d’un LoadRunner, foncez avant que l’outil ne disparaisse du net.</p>
<h2>Pour aller plus loin</h2>
<ul>
<li>Le scénario et le jeu d’essai utilisés pour cet article sont disponibles <a title="Sources de cet article" href="http://benjamin-balet.info/extra/dev/HttpRider.zip">ici</a>.</li>
<li><a title="Page du projet HTTP rider sur CodePlaex" href="http://httprider.codeplex.com/" target="_blank">Lien vers la page du projet</a>.</li>
</ul>
<p>Cet article <a href="https://benjamin-balet.info/developpement/http-rider-le-test-de-performance-quick-and-dirty/">HTTP Rider le test de performance quick-and-dirty</a> est apparu en premier sur <a href="https://benjamin-balet.info">Benjamin BALET</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Test de performance avec Visual Studio 2013 Ultimate</title>
		<link>https://benjamin-balet.info/test-logiciel/test-de-performance-avec-visual-studio-2013-ultimate/</link>
		
		<dc:creator><![CDATA[Benjamin BALET]]></dc:creator>
		<pubDate>Sun, 11 Aug 2013 11:23:17 +0000</pubDate>
				<category><![CDATA[Performances]]></category>
		<category><![CDATA[Test Logiciel]]></category>
		<category><![CDATA[.net]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[performance]]></category>
		<guid isPermaLink="false">http://benjamin-balet.info/?p=1252</guid>

					<description><![CDATA[<p>Réalisez des tests de performance avec Visual Studio 2013</p>
<p>Cet article <a href="https://benjamin-balet.info/test-logiciel/test-de-performance-avec-visual-studio-2013-ultimate/">Test de performance avec Visual Studio 2013 Ultimate</a> est apparu en premier sur <a href="https://benjamin-balet.info">Benjamin BALET</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p style="text-align: left;" align="center">Visual Studio contient depuis quelques versions une fonctionnalité de test de charge des applications web. Cet article contient une description de haut niveau des fonctionnalités de la version <b>Visual Studio 2013 Ultimate</b> ainsi que quelques éléments de comparaison avec HP LoadRunner.<span id="more-1252"></span></p>
<h2><b>Niveau de protocole </b></h2>
<p>Si on avait à comparer avec LoadRunner, le protocole (dans le sens LoadRunner) serait HTTP (URL based). Il existe une possibilité de coder une script au niveau de l’interface (« Coded UI test » que l’on pourrait comparer au mode TruClient), mais cela est déconseillé par la documentation. Microsoft en parle comme d’une solution temporaire ou de dernier recours. A mon avis, cela ne doit pas très bien marcher ou consommer une énorme quantité de ressources système.</p>
<h2><b>Enregistreur de script</b></h2>
<p>Un add-in Internet Explorer permet d’enregistrer les actions de l’utilisateur d’une application web compatible avec IE. Ce qui est enregistré sont les actions effectuées au niveau du protocole HTTP (GET, POST…) et pas les actions au niveau du réseau ou utilisant un autre protocole. Par exemple, les interactions d’une applet Java en RMI ne seront pas enregistrées (si tant est que cela existe encore).</p>
<p><figure id="attachment_1254" aria-describedby="caption-attachment-1254" style="width: 535px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2013/08/visual_studio_load_test_web_performance_recorder_internet_explorer_addin.png" target="_blank"><img loading="lazy" decoding="async" class="size-full wp-image-1254 " alt="Enregistrer un script depuis internet explorer" src="http://benjamin-balet.info/wp-content/uploads/2013/08/visual_studio_load_test_web_performance_recorder_internet_explorer_addin.png" width="535" height="291" srcset="https://benjamin-balet.info/wp-content/uploads/2013/08/visual_studio_load_test_web_performance_recorder_internet_explorer_addin.png 535w, https://benjamin-balet.info/wp-content/uploads/2013/08/visual_studio_load_test_web_performance_recorder_internet_explorer_addin-300x163.png 300w" sizes="auto, (max-width: 535px) 100vw, 535px" /></a><figcaption id="caption-attachment-1254" class="wp-caption-text">Enregistrer un script depuis internet explorer</figcaption></figure></p>
<p>Si certaines actions n’ont pas été capturées par l’add-in, il est possible de les ajouter dans le modeleur graphique du script ou de les coder manuellement dans le code source généré (voir plus loin).</p>
<p>Il y a une fonctionnalité de détection des paramètres dynamiques, mais je ne l’ai jamais vu détecter quoi que ce soit durant mes tests. Elle est déclenchée suite à l’enregistrement du script ou à la demande. Elle n’est pas vraiment comparable à la fonctionnalité de corrélation automatique de LoadRunner qui a un taux de détection plus élevé.</p>
<h2><b>Scripting</b></h2>
<p>Une fois le script enregistré, on a accès à un modeleur (je l’appellerai comme ça dans la suite de l’article afin de le différencier d’un script codé) qui permet de modifier le script graphiquement. Par exemple, coupler les paramètres http à une source de données :</p>
<p><figure id="attachment_1255" aria-describedby="caption-attachment-1255" style="width: 306px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2013/08/visual_studio_load_test_web_performance_scripting.png" target="_blank"><img loading="lazy" decoding="async" class="size-full wp-image-1255 " alt="Modifier un script après son enregistrement" src="http://benjamin-balet.info/wp-content/uploads/2013/08/visual_studio_load_test_web_performance_scripting.png" width="306" height="416" srcset="https://benjamin-balet.info/wp-content/uploads/2013/08/visual_studio_load_test_web_performance_scripting.png 306w, https://benjamin-balet.info/wp-content/uploads/2013/08/visual_studio_load_test_web_performance_scripting-220x300.png 220w" sizes="auto, (max-width: 306px) 100vw, 306px" /></a><figcaption id="caption-attachment-1255" class="wp-caption-text">Modifier un script après son enregistrement</figcaption></figure></p>
<p>On peut convertir ce script en code C# ou Visual Basic (selon la nature du projet .Net). Ce qui ouvre toutes les possibilités du Framework .Net. Voici un court extrait de code:</p>
<p><tt> WebTestRequest request2 = new WebTestRequest("http://localhost/opencart/index.php");<br />
request2.Headers.Add(new WebTestRequestHeader("Referer", "http://localhost/opencart/"));<br />
request2.QueryStringParameters.Add("route", "product/category", false, false);<br />
request2.QueryStringParameters.Add("path", "20_26", false, false);<br />
yield return request2;<br />
request2 = null;<br />
</tt></p>
<h2><b>SLA et transactions</b></h2>
<p>Ce qu&rsquo;il est possible de faire:</p>
<ul>
<li>Il est possible de grouper une ou plusieurs requêtes HTTP au sein de transactions.</li>
<li>Il est possible de définir des temps de réponse attendus globaux ou au niveau des requêtes.</li>
<li>Il n’est pas possible de définir des SLA par palier de charge ou sur d’autres objectifs.</li>
</ul>
<p>Dans cet exemple d&rsquo;exécution, on remarque un dépassement des objectifs :</p>
<p><figure id="attachment_1256" aria-describedby="caption-attachment-1256" style="width: 750px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2013/08/Load-test-result.png" target="_blank"><img loading="lazy" decoding="async" class="size-full wp-image-1256 " alt="résultat de l'exécution unitaire d'un script" src="http://benjamin-balet.info/wp-content/uploads/2013/08/Load-test-result.png" width="750" height="462" srcset="https://benjamin-balet.info/wp-content/uploads/2013/08/Load-test-result.png 750w, https://benjamin-balet.info/wp-content/uploads/2013/08/Load-test-result-300x184.png 300w" sizes="auto, (max-width: 750px) 100vw, 750px" /></a><figcaption id="caption-attachment-1256" class="wp-caption-text">résultat de l&rsquo;exécution unitaire d&rsquo;un script</figcaption></figure></p>
<h2><b>Paramètres / Jeu d’essai</b></h2>
<p>Il est possible de définir une source de données et de l’utiliser dans le modeleur ou dans le code. Cependant, cette fonctionnalité n’est pas comparable à celle de LoadRunner. Ainsi il n’est pas possible graphiquement :</p>
<ul>
<li>D’asservir deux variables (« same line as »).</li>
<li>D’allouer des blocs de données par utilisateur virtuel.</li>
<li>De définir des règles de consommation des données (choix d’une ligne au hasard, consommation unique, arrêter en fin de table…).</li>
</ul>
<p>Tous ces cas d’usage devront être codés (voir plus loin avec les possibilités d’extension).</p>
<h2><b>Think time</b></h2>
<p>Les temps de pause sont liés aux requêtes HTTP et ils se configurent donc au niveau de la requête. Par exemple dans le code généré suivant :<br />
<tt><br />
WebTestRequest request1 = new WebTestRequest("http://localhost/opencart/index.php");<br />
request1.ThinkTime = 8;<br />
</tt><br />
On peut ensuite définir au niveau du scénario ce que l’on fera de ces temps d’attente (« as recorded », etc.).</p>
<h2><b>Possibilités d’extension</b></h2>
<p>Comme nous l’avons vu précédemment, les scripts peuvent être convertis en code .Net. On peut avoir accès à une API de test de performance depuis ce code. Ce qui permet de gérer plus finement les paramètres ou les différents objets de test (requêtes HTTP, règle de validation ou d’extraction…). Il existe deux API :</p>
<ul>
<li>Web Performance Test API : <a href="http://msdn.microsoft.com/en-us/library/ms182558(v=vs.120).aspx" target="_blank">http://msdn.microsoft.com/en-us/library/ms182558(v=vs.120).aspx</a></li>
<li>Load Test API : <a href="http://msdn.microsoft.com/en-us/library/ms182605(v=vs.120).aspx" target="_blank">http://msdn.microsoft.com/en-us/library/ms182605(v=vs.120).aspx</a></li>
</ul>
<p>Si vous venez de LoadRunner, ce qu’il faut comprendre, c’est que les deux modèles sont cloisonnés (Script : Web Performance Test API et Scénario : Load Test API). Ainsi, on ne peut pas aisément obtenir une information contextuelle au tir dans une instance de script (par exemple, le numéro d’itération dans le tir ou est-on dans un contexte de tir ou pas – e.g. dans LR en jouant sur la valeur de « Group Name »). Il faut coder !</p>
<p>Il y a deux possibilités de création de plugin :</p>
<ul>
<li>Load Test Plug-In (<a href="http://msdn.microsoft.com/en-us/library/ms243153(v=vs.120).aspx)" target="_blank">http://msdn.microsoft.com/en-us/library/ms243153(v=vs.120).aspx)</a>: Pour déclencher des actions à certains évènements du tir (début, fin, dépassement de SLA…). Un exemple simple serait de déclencher des actions en début ou en fin de tir.</li>
<li>Web Performance Test Plug-In (<a href="http://msdn.microsoft.com/en-us/library/ms243191(v=vs.120).aspx)" target="_blank">http://msdn.microsoft.com/en-us/library/ms243191(v=vs.120).aspx)</a>: Pour étendre les capacités du modeleur graphique (personnaliser le script sans coder). Ou pour créer du code réutilisable. Un exemple simple serait de générer un nombre aléatoire afin de l’utiliser en tant que paramètre.</li>
</ul>
<h2><b>Collecte des indicateurs</b></h2>
<p>Cette fonctionnalité est clairement orientée Microsoft. Ainsi, elle est seulement capable de collecter les indicateurs via WMI (ceux qui sont visibles depuis perfmon.exe). Elle ne supporte pas la collecte via SNMP ou <a title="Superviser une machine Linux par RSTATD" href="http://benjamin-balet.info/developpement/superviser-une-machine-linux-par-rstatd/" target="_blank">rpc.rstatd</a>. Cependant, il y aurait des possibilités (<a href="http://www.networkcircus.com/articles/20090606.html" target="_blank">http://www.networkcircus.com/articles/20090606.html</a>) que je n’ai pas testées :</p>
<ul>
<li>Le projet Open Pegassus a développé un utilitaire WMI Mapper <a href="https://collaboration.opengroup.org/pegasus/" target="_blank">https://collaboration.opengroup.org/pegasus/</a></li>
<li>Il y aurait un client WMI pour Linux <a href="http://www.krenger.ch/blog/wmi-commands-from-linux/" target="_blank">http://www.krenger.ch/blog/wmi-commands-from-linux/</a></li>
<li>Créer un indicateur personnalisé, comme expliqué dans le paragraphe suivant.</li>
</ul>
<p>D’un autre côté, cette intégration au monde Microsoft permet de bénéficier d’indicateurs par défaut sur les outils maison (IIS, SQL Server…).</p>
<h2><b>Créer un indicateur personnalisé</b></h2>
<p>Comme expliqué dans le paragraphe précédent, hormis les indicateurs de comportement du test de charge, tous les autres indicateurs ne peuvent provenir que de WMI. Si vous souhaitez créer des indicateurs personnalisés (issu d’une application, d’une extraction, etc.) il faut donc créer une nouvelle section et de nouveaux indicateurs WMI. Une solution est fournie ici <a href="http://stackoverflow.com/a/17117685" target="_blank">http://stackoverflow.com/a/17117685</a></p>
<h2><b>Scénario</b></h2>
<p>Les possibilités sont à peu près les mêmes qu’avec les outils concurrents. Il existe une fonctionnalité de mix des types de réseau (débit) et des navigateurs (user-agent). Les notions de pacing, durée (ou nombre d’itérations) et de rampe sont présentes par contre les utilisateurs virtuels sont arrêtés d’un coup.</p>
<p>On peut également diminuer ou augmenter la fréquence d’échantillonnage des indicateurs collectés. Ce qui est sympa si on souhaite avoir une granularité plus fine ou si on veut, au contraire, limiter le stress sur les machines testées ou les agents (i.e. les  injecteurs). En effet, certains indicateurs sont chers à obtenir en termes de ressources.</p>
<h2><b>Résultats et Reporting</b></h2>
<p>L’outil génère les graphiques classiques des tests de charge (temps de réponse moyen, taux d’erreurs, hits/s…) ainsi que les métriques systèmes via WMI. On peut même faire des calculs d’estimation de la répartition temps serveur / temps réseau, parce qu’il expose les indicateurs :</p>
<ul>
<li>Temps réseau : Avg. Connection Wait Time</li>
<li>Temps réseau (début du transfert) : Avg. First Byte Time</li>
<li>Temps global (requête + transfert) : Avg. Response Time</li>
</ul>
<h2><b>Exports Excel</b></h2>
<p>À condition que vous ayez lancé VS2013 en tant qu’administrateur, l’outil est capable de générer deux types de rapport Excel :</p>
<ul>
<li>Un rapport de tendance (suivi des performances à travers plusieurs tirs). Un exemple ici.</li>
<li>Un rapport de comparaison entre différents tirs. Un exemple ici.</li>
</ul>
<p>Je regrette que les graphiques du type « Over time » (comportement du système durant le tir) ne soit pas directement exportables sous la forme dans laquelle ils sont consultables dans VS2013 (il faut exporter les données du graph, puis reconstruire le graphique depuis Excel). Par exemple, le nombre de hits/s pour savoir si le système s’effondre ou pas :</p>
<p><figure id="attachment_1257" aria-describedby="caption-attachment-1257" style="width: 556px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2013/08/visual_studio_load_test_web_performance_live_result_graphs.png" target="_blank"><img loading="lazy" decoding="async" class="size-full wp-image-1257 " alt="Graphique en temps réel durant un tir" src="http://benjamin-balet.info/wp-content/uploads/2013/08/visual_studio_load_test_web_performance_live_result_graphs.png" width="556" height="213" srcset="https://benjamin-balet.info/wp-content/uploads/2013/08/visual_studio_load_test_web_performance_live_result_graphs.png 556w, https://benjamin-balet.info/wp-content/uploads/2013/08/visual_studio_load_test_web_performance_live_result_graphs-300x114.png 300w" sizes="auto, (max-width: 556px) 100vw, 556px" /></a><figcaption id="caption-attachment-1257" class="wp-caption-text">Graphique en temps réel durant un tir</figcaption></figure></p>
<h2><b>Infrastructure de test</b></h2>
<p>Il est possible de déporter l’exécution des différents composants sur plusieurs machines :</p>
<ul>
<li>Le contrôleur est le composant qui est le chef d’orchestre du tir. Il est aussi chargé de collecter tous les indicateurs et évènements du tir.</li>
<li>Les « Tests agents » (qu’on appelle injecteur ou Load Generators sur d’autres outils) sont en charge d’exécuter les scripts (ou tests) qui génèrent de la charge sur l’application à tester.</li>
</ul>
<p><figure id="attachment_1258" aria-describedby="caption-attachment-1258" style="width: 480px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2013/08/visual_studio_load_test_web_performance_test_infrastructure.png" target="_blank"><img loading="lazy" decoding="async" class="size-full wp-image-1258 " alt="Infrastructure de test de charge avec VS2013" src="http://benjamin-balet.info/wp-content/uploads/2013/08/visual_studio_load_test_web_performance_test_infrastructure.png" width="480" height="205" srcset="https://benjamin-balet.info/wp-content/uploads/2013/08/visual_studio_load_test_web_performance_test_infrastructure.png 480w, https://benjamin-balet.info/wp-content/uploads/2013/08/visual_studio_load_test_web_performance_test_infrastructure-300x128.png 300w" sizes="auto, (max-width: 480px) 100vw, 480px" /></a><figcaption id="caption-attachment-1258" class="wp-caption-text">Infrastructure de test de charge avec VS2013</figcaption></figure></p>
<p>Par défaut, les indicateurs de base (CPU, mémoire réellement disponible) sont ramenés dans le rapport du tir. Ainsi, on peut savoir si l’infrastructure de test tenait la route.</p>
<h2><b>Cloud-based performance testing</b></h2>
<p>Avec l’édition Visual Studio 2013 Ultimate MSDN, il serait possible de tester les sites Internet depuis le cloud et cela serait inclut dans le prix. Voir <a href="http://tfs.visualstudio.com/en-us/learn/load-testing" target="_blank">http://tfs.visualstudio.com/en-us/learn/load-testing</a></p>
<p>Cependant, il n’y a pas encore assez d’informations sur les éventuels coûts directs (et cachés) et les limites du système lorsque l’on sortira de la phase de Preview. En tout cas, les rapports sont moins riches que lors d’une exécution dans un intranet. Sont absentes par exemple les données système (CPU, mémoire…) ou les données avancées sur le réseau (temps de connexion, premier octet dans le buffer…).</p>
<h2><b>Conclusion</b></h2>
<p>Cet outil ne serait disponible qu’avec Visual Studio 2013 Ultimate MSDN (pour information, le prix public de Visual Studio 2012 Ultimate était de 16 944 €). Il n’y a pas de limite quant au nombre d’utilisateurs simulés (en termes de licence), contrairement aux autres outils de tests de charge où le coût peut être fonction du protocole utilisé (ici il n’y a que le protocole web) et/ou du nombre d’utilisateurs simulés.</p>
<p>Il est difficile de situer l’outil par rapport à la concurrence. Il est plus simple que JMeter (ou tsung). Il supporte moins de protocoles que JMeter, mais il est plus puissant si le système sous test est dans le monde Microsoft. Par contre, il n’est pas encore à la hauteur des solutions propriétaires spécialisées comme LoadRunner ou Borland/Microfocus.</p>
<p>Pour aller plus loin</p>
<ul>
<li><a title="Documentation sur les tests de charge avec Visual Studio 2013" href="http://msdn.microsoft.com/en-us/library/dd293540(v=vs.120).aspx" target="_blank">Le point d’entrée de la documentation</a>.</li>
<li>Télécharger la Preview de Visual Studio 2013.</li>
</ul>
<p>Cet article <a href="https://benjamin-balet.info/test-logiciel/test-de-performance-avec-visual-studio-2013-ultimate/">Test de performance avec Visual Studio 2013 Ultimate</a> est apparu en premier sur <a href="https://benjamin-balet.info">Benjamin BALET</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Une tablette de rechargement QI compatible avec le Google Nexus 4</title>
		<link>https://benjamin-balet.info/gadgets/une-tablette-de-rechargement-qi-compatible-avec-le-google-nexus-4/</link>
					<comments>https://benjamin-balet.info/gadgets/une-tablette-de-rechargement-qi-compatible-avec-le-google-nexus-4/#comments</comments>
		
		<dc:creator><![CDATA[Benjamin BALET]]></dc:creator>
		<pubDate>Wed, 03 Apr 2013 09:01:44 +0000</pubDate>
				<category><![CDATA[Gadgets]]></category>
		<category><![CDATA[Google Nexus 4]]></category>
		<guid isPermaLink="false">http://benjamin-balet.info/?p=1203</guid>

					<description><![CDATA[<p>Découvrez une tablette de rechargement sans contact compatible avec la norme IQ</p>
<p>Cet article <a href="https://benjamin-balet.info/gadgets/une-tablette-de-rechargement-qi-compatible-avec-le-google-nexus-4/">Une tablette de rechargement QI compatible avec le Google Nexus 4</a> est apparu en premier sur <a href="https://benjamin-balet.info">Benjamin BALET</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><a href="http://www.amazon.fr/gp/product/B007YPSWQS/ref=as_li_ss_il?ie=UTF8&amp;camp=1642&amp;creative=19458&amp;creativeASIN=B007YPSWQS&amp;linkCode=as2&amp;tag=benjamin-balet-21"><img loading="lazy" decoding="async" class="alignleft" style="border: 0px;" alt="" src="http://ws.assoc-amazon.fr/widgets/q?_encoding=UTF8&amp;ASIN=B007YPSWQS&amp;Format=_SL110_&amp;ID=AsinImage&amp;MarketPlace=FR&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=benjamin-balet-21" width="79" height="110" border="0" /></a></p>
<p>Alors que l’accessoire officiel de rechargement par induction se fait attendre sur le marché français, Maxell a sorti une tablette de rechargement par induction (sans contact ni branchement) compatible avec le nouveau Google Nexus 4. Je l&rsquo;ai testée pour vous.<span id="more-1203"></span></p>
<p><a href="http://www.amazon.fr/gp/product/B007YPSWQS/ref=as_li_ss_il?ie=UTF8&amp;camp=1642&amp;creative=19458&amp;creativeASIN=B007YPSWQS&amp;linkCode=as2&amp;tag=benjamin-balet-21"><br />
</a><img loading="lazy" decoding="async" style="border: none !important; margin: 0px !important;" alt="" src="http://www.assoc-amazon.fr/e/ir?t=benjamin-balet-21&amp;l=as2&amp;o=8&amp;a=B007YPSWQS" width="1" height="1" border="0" /></p>
<h2>Pas besoin d&rsquo;adaptateur ou de combine</h2>
<p>Le principe de rechargement avec une tablette à induction n&rsquo;est pas nouveau, mais il faut faire attention car beaucoup de solutions reposent sur l&rsquo;utilisation d&rsquo;une double coque (qui n&rsquo;est souvent pas fournie). Dans le cas de la tablette Maxell, pas de double coque : elle est compatible avec le Nexus 4 et il suffit de poser le mobile sur la tablette pour que le chargement de la batterie commence.</p>
<p><figure id="attachment_1206" aria-describedby="caption-attachment-1206" style="width: 512px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2013/03/tablette-QI-de-rechargement-sans-branchement-compatible-Google-Nexus-4.jpg"><img loading="lazy" decoding="async" class="size-full wp-image-1206 " title="tablette QI de rechargement sans branchement compatible Google Nexus 4" alt="tablette QI de rechargement sans branchement compatible Google Nexus 4" src="http://benjamin-balet.info/wp-content/uploads/2013/03/tablette-QI-de-rechargement-sans-branchement-compatible-Google-Nexus-4.jpg" width="512" height="304" srcset="https://benjamin-balet.info/wp-content/uploads/2013/03/tablette-QI-de-rechargement-sans-branchement-compatible-Google-Nexus-4.jpg 512w, https://benjamin-balet.info/wp-content/uploads/2013/03/tablette-QI-de-rechargement-sans-branchement-compatible-Google-Nexus-4-300x178.jpg 300w" sizes="auto, (max-width: 512px) 100vw, 512px" /></a><figcaption id="caption-attachment-1206" class="wp-caption-text">On pose le mobile, la diode s&rsquo;allume et le rechargement démarre</figcaption></figure></p>
<p>Oubliez aussi les tentatives de rechargement avec la plaque à induction de votre cuisinière : ça peut marcher, mais ça peut aussi détruire votre téléphone portable.</p>
<h2>Compatible avec les coques de protection</h2>
<p>Il ne vous a pas échappé que mon mobile est protégé par une coque en silicone. Il n&rsquo;est pas nécessaire de la retirer pour recharger le téléphone portable et les coques de protection n&rsquo;allogent pas la durée de chargement.</p>
<p><figure id="attachment_1205" aria-describedby="caption-attachment-1205" style="width: 512px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2013/03/Coque-silicone-pour-Nexus-4.jpg" target="_blank"><img loading="lazy" decoding="async" class="size-full wp-image-1205 " title="Coque silicone pour Nexus 4" alt="Coque silicone pour Nexus 4" src="http://benjamin-balet.info/wp-content/uploads/2013/03/Coque-silicone-pour-Nexus-4.jpg" width="512" height="408" srcset="https://benjamin-balet.info/wp-content/uploads/2013/03/Coque-silicone-pour-Nexus-4.jpg 512w, https://benjamin-balet.info/wp-content/uploads/2013/03/Coque-silicone-pour-Nexus-4-300x239.jpg 300w" sizes="auto, (max-width: 512px) 100vw, 512px" /></a><figcaption id="caption-attachment-1205" class="wp-caption-text">On peut charger le téléphone même avec une coque de protection</figcaption></figure></p>
<h2>Prix comparable avec l’accessoire officiel</h2>
<p>On attend encore la disponibilité sur le marché français, mais le prix devrait se situer dans les 60€ (Le prix est de 59USD aux USA auquel il faut ajouter la TVA). Le tapis de rechargement par induction est disponible sur Amazon. Voici également un lien vers la coque de protection si elle vous a plu (et une variante du même genre) :</p>
<p><a href="http://www.amazon.fr/gp/product/B007YPSWQS/ref=as_li_ss_il?ie=UTF8&amp;camp=1642&amp;creative=19458&amp;creativeASIN=B007YPSWQS&amp;linkCode=as2&amp;tag=benjamin-balet-21"><img decoding="async" alt="" src="http://ws.assoc-amazon.fr/widgets/q?_encoding=UTF8&amp;ASIN=B007YPSWQS&amp;Format=_SL110_&amp;ID=AsinImage&amp;MarketPlace=FR&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=benjamin-balet-21" border="0" /></a> <a href="http://www.amazon.fr/gp/product/B00AO70Q8A/ref=as_li_ss_il?ie=UTF8&amp;camp=1642&amp;creative=19458&amp;creativeASIN=B00AO70Q8A&amp;linkCode=as2&amp;tag=benjamin-balet-21"><img decoding="async" alt="" src="http://ws.assoc-amazon.fr/widgets/q?_encoding=UTF8&amp;ASIN=B00AO70Q8A&amp;Format=_SL110_&amp;ID=AsinImage&amp;MarketPlace=FR&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=benjamin-balet-21" border="0" /></a><img loading="lazy" decoding="async" style="border: none !important; margin: 0px !important;" alt="" src="http://www.assoc-amazon.fr/e/ir?t=benjamin-balet-21&amp;l=as2&amp;o=8&amp;a=B00AO70Q8A" width="1" height="1" border="0" /><br />
<img loading="lazy" decoding="async" style="border: none !important; margin: 0px !important;" alt="" src="http://www.assoc-amazon.fr/e/ir?t=benjamin-balet-21&amp;l=as2&amp;o=8&amp;a=B007YPSWQS" width="1" height="1" border="0" /> <a href="http://www.amazon.fr/gp/product/B00AGJO61Y/ref=as_li_ss_il?ie=UTF8&amp;camp=1642&amp;creative=19458&amp;creativeASIN=B00AGJO61Y&amp;linkCode=as2&amp;tag=computure-21"><img decoding="async" alt="" src="http://ws.assoc-amazon.fr/widgets/q?_encoding=UTF8&amp;ASIN=B00AGJO61Y&amp;Format=_SL110_&amp;ID=AsinImage&amp;MarketPlace=FR&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=computure-21" border="0" /></a><img loading="lazy" decoding="async" style="border: none !important; margin: 0px !important;" alt="" src="http://www.assoc-amazon.fr/e/ir?t=computure-21&amp;l=as2&amp;o=8&amp;a=B00AGJO61Y" width="1" height="1" border="0" /></p>
<p>Je l&rsquo;ai depuis plusieurs mois et j&rsquo;en suis totalement satisfait.</p>
<p>Cet article <a href="https://benjamin-balet.info/gadgets/une-tablette-de-rechargement-qi-compatible-avec-le-google-nexus-4/">Une tablette de rechargement QI compatible avec le Google Nexus 4</a> est apparu en premier sur <a href="https://benjamin-balet.info">Benjamin BALET</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://benjamin-balet.info/gadgets/une-tablette-de-rechargement-qi-compatible-avec-le-google-nexus-4/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Introduction à i18next la traduction côté client</title>
		<link>https://benjamin-balet.info/developpement/introduction-a-i18next-la-traduction-cote-client/</link>
		
		<dc:creator><![CDATA[Benjamin BALET]]></dc:creator>
		<pubDate>Wed, 03 Apr 2013 08:02:24 +0000</pubDate>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[javascript]]></category>
		<guid isPermaLink="false">http://benjamin-balet.info/?p=1213</guid>

					<description><![CDATA[<p>Gérez la traduction des IHM web côté client avec i18next</p>
<p>Cet article <a href="https://benjamin-balet.info/developpement/introduction-a-i18next-la-traduction-cote-client/">Introduction à i18next la traduction côté client</a> est apparu en premier sur <a href="https://benjamin-balet.info">Benjamin BALET</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p style="text-align: left;" align="center">Dans ce billet, nous allons voir une solution pour gérer la traduction des IHM web côté client avec <strong>i18next</strong>. i18next est une bibliothèque Javascript compatible avec les toolkits tels que jQuery ou mootools. La documentation de cette bibliothèque n’étant pas très intuitive, je vous propose de vous en expliquer le principe avec un exemple concret. Histoire de pouvoir démarrer rapidement.<span id="more-1213"></span></p>
<h2>Introduction</h2>
<p>Disons d’emblée que je ne suis pas trop fan de l’approche. Je préfère gérer la traduction côté serveur pour plusieurs raisons :</p>
<ul>
<li>Il y a plus d’outils (par exemple poedit) et de pratiques (comprendre plus d’exemples et de retour d’expérience) avec l’approche côté serveur.</li>
<li>Comme nous allons le voir plus loin, la libraire a besoin d’un serveur HTTP, elle ne peut pas être utilisée dans un projet 100% statique (par exemple un projet de type CD-ROM).</li>
<li>L’approche avec gettext est plus ou moins devenue standard. Mais pour l’instant HTML5 ne propose pas de solution de traduction standard du code HTML statique.</li>
</ul>
<p>Pourquoi diantre utiliser une telle bibliothèque (N’hésitez pas à commenter ce billet avec vos propres idées) ?</p>
<ul>
<li>Votre hébergeur vous fait payer le temps d’exécution, mais pas le trafic, ni le temps http statique (les GET de ressources statiques sont gratuites).</li>
<li>Vous souhaitez une implémentation 100% AJAX sans code côté serveur.</li>
<li>Pour une raison ou pour une autre de logique, la traduction n’est pas évidente à réaliser côté serveur et doit se faire côté client.</li>
</ul>
<h2>Exemple d’intégration</h2>
<h3>Organisation des fichiers</h3>
<p>Par défaut, la bibliothèque – après avoir déterminé la langue de l’utilisateur – enverra une requête HTTP pour récupérer un fichier de ressource de traduction en JSON. Votre projet doit contenir un répertoire « locales », puis un répertoire par langue. Dans ce répertoire il faut déposer un fichier « translation.json » contenant les traductions. Vous pouvez créer un répertoire « dev » dans le répertoire « locales ». Ce répertoire contiendrait le(s) fichier(s) de traduction par défaut (si la langue de l’utilisateur n’est pas traduite). Personnellement, je préfère utiliser le paramètre fallbackLng afin de préciser explicitement une langue par défaut.</p>
<p><figure id="attachment_1215" aria-describedby="caption-attachment-1215" style="width: 85px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2013/04/Organisation-du-projet-i18next.png" target="_blank"><img loading="lazy" decoding="async" class="size-full wp-image-1215 " alt="Organisation des fichiers du projet i18next" src="http://benjamin-balet.info/wp-content/uploads/2013/04/Organisation-du-projet-i18next.png" width="85" height="87" /></a><figcaption id="caption-attachment-1215" class="wp-caption-text">Organisation des fichiers du projet i18next</figcaption></figure></p>
<p>Bien sûr, la bibliothèque contient de nombreuses options vous permettant de modifier l’emplacement de ces fichiers. On peut aussi séparer les traductions dans des fichiers JSON différents. I18next permet, en effet, de raisonner en namespaces. Ce qui permet de regrouper les objets (boutons, labels…) dans un même fichier ou de découper les écrans de l’application en domaines. Tout dépend de la nature et du contexte de votre projet.</p>
<h3>Exemple de fichier de traduction</h3>
<p><figure id="attachment_1216" aria-describedby="caption-attachment-1216" style="width: 194px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2013/04/Fichier-JSON-contenant-une-traduction-pour-i18next.png" target="_blank"><img loading="lazy" decoding="async" class="size-full wp-image-1216 " alt="Fichier JSON contenant une traduction pour i18next" src="http://benjamin-balet.info/wp-content/uploads/2013/04/Fichier-JSON-contenant-une-traduction-pour-i18next.png" width="194" height="176" /></a><figcaption id="caption-attachment-1216" class="wp-caption-text">Fichier JSON contenant une traduction pour i18next</figcaption></figure></p>
<p>On peut organiser le fichier de ressource de traduction en classant les clés de traduction sous la forme d’une arborescence. Ça tombe bien, JSON le permet et la finalité est quand même de traduire des éléments innerText situé quelque part dans le DOM d’une page web.</p>
<p>Bien entendu l’arborescence de la clé de traduction (dans le fichier JSON) doit se retrouver dans la valeur de l’attribut data-i18n. Par exemple, la valeur de l’attribut data-i18n (voir plus bas, l’exemple de codeHTML/ Javascript) « nav.home » renvoie à la clé de traduction « nav » : { « home » : « … »}.</p>
<h3>Code Javascript minimal</h3>
<p>I18next n’a pas besoin de jQuery, mais si jQuery est chargée avant, I18next est augmenté des fonctionnalités de jQuery. Ce qui est quand même plus sympa pour sélectionner des éléments du DOM.</p>
<p><figure id="attachment_1217" aria-describedby="caption-attachment-1217" style="width: 630px" class="wp-caption alignnone"><a href="http://benjamin-balet.info/wp-content/uploads/2013/04/Code-javascript-minimal-i18next.png" target="_blank"><img loading="lazy" decoding="async" class="size-full wp-image-1217 " alt="Code javascript minimal i18next" src="http://benjamin-balet.info/wp-content/uploads/2013/04/Code-javascript-minimal-i18next.png" width="630" height="414" srcset="https://benjamin-balet.info/wp-content/uploads/2013/04/Code-javascript-minimal-i18next.png 630w, https://benjamin-balet.info/wp-content/uploads/2013/04/Code-javascript-minimal-i18next-300x197.png 300w" sizes="auto, (max-width: 630px) 100vw, 630px" /></a><figcaption id="caption-attachment-1217" class="wp-caption-text">Code javascript minimal i18next</figcaption></figure></p>
<p>On comprend que la bibliothèque va écraser le contenu innerText avec le contenu du fichier JSON à chaque fois qu’elle trouve une correspondance. Donc et pas soucis de clarté, le fichier HTMl peut être chargé avec des labels par défaut qui seront ensuite traduits par i18next.</p>
<h2>Principe de fonctionnement</h2>
<h3>Processus de détermination de la langue</h3>
<p>La bibliothèque tente de déterminer la langue de l’utilisateur dans l’ordre suivant :</p>
<ol>
<li>La langue est explicitement passée en paramètre de l’initialisation de la bibliothèque</li>
<li>La page en cours contient le code langage/pays dans le paramètre (?setLng=fr-FR).</li>
<li>Il y a un cookie (i18next) contenant le code langage/pays.</li>
<li>Langue définie dans les préférences utilisateur du navigateur.</li>
</ol>
<p>Ensuite et en ce qui concerne le chargement des ressources, l’ordre suivant est respecté :</p>
<ol>
<li>i18next essaye de charger un fichier JSON contenu dans un répertoire nommé avec le code langage/pays (par exemple, « fr_FR » pour le français parlé en France).</li>
<li>Essayer de charger un fichier JSON contenu dans un répertoire nommé avec le code langage seulement (par exemple, « fr » pour le français).</li>
<li>Si aucun fichier n’est trouvé, essayer de charger le fichier dans le répertoire nommé selon la variable fallbackLng ou qui s’appelle « dev ».</li>
</ol>
<h3>Chargement des ressources de traduction</h3>
<p>Le chargement est asynchrone, donc la bibliothèque ne peut pas être utilisée en local pour un contenu statique. Il faut un serveur web. Il n’est cependant pas nécessaire d’avoir du code côté serveur (en PHP, par exemple). La librairie se contenant de faire un HTTP GET (chargement Ajax, via requête HTTP).</p>
<p>L’aspect asynchrone de la bibliothèque fait que l’on ne peut appeler les fonctions de traduction qu’une fois les ressources chargées (après l’appel Ajax). Il faut passer par une fonction de callback comme dans cet exemple :</p>
<pre>i18n.init(function(t) {</pre>
<pre>alert(i18n.t("app.name"));</pre>
<pre>});</pre>
<p>On peut coupler l’utilisation de la librairie à jQuery pour la fonctionnalité de sélecteur (c.-à-d. sélectionner d’un coup tous les éléments d’une classe donnée ou d’un type donné…). Comme avec cet extrait de code où l’on demande la traduction de tous les éléments de classe « nav » :</p>
<pre>$(".nav").i18n();</pre>
<h2>Autres informations</h2>
<ul>
<li>La bibliothèque gère les formes plurielles.</li>
<li>On peut brancher une fonction de post processing pour une traduction.</li>
<li>On peut utiliser des formateurs de texte (sprintf, un peu en lien avec le point précédent).</li>
<li>Il est possible de faire de la substitution de variable (par exemple : « il y a __nbePommes__ pommes ») sans utiliser sprintf.</li>
<li>On peut stocker une valeur sur plusieurs lignes dans le fichier de ressource en JSON.</li>
<li>RTFM pour le reste des fonctions.</li>
</ul>
<h3>Alternatives</h3>
<ul>
<li>Jed qui se réclame proche de Gettext : <a href="http://slexaxton.github.com/Jed/">http://slexaxton.github.com/Jed/</a></li>
<li>Le toolkit Dojo contient un module i18n : <a href="http://dojotoolkit.org/reference-guide/1.8/dojo/i18n.html">http://dojotoolkit.org/reference-guide/1.8/dojo/i18n.html</a></li>
<li>Jsperanto : <a href="https://github.com/jpjoyal/jsperanto">https://github.com/jpjoyal/jsperanto</a></li>
</ul>
<h3>Liens utiles</h3>
<ul>
<li>Récupérer mon projet exemple en cliquant <a title="Récupérer les sources citées dans cet article" href="http://benjamin-balet.info/extra/dev/i18next.zip">ici</a>.</li>
<li>Téléchargement et documentation (en anglais) : <a href="http://jamuhl.github.com/i18next/index.html">http://jamuhl.github.com/i18next/index.html</a></li>
<li>Intégration avec Express et Jade (en anglais) : <a href="http://rbeere.tumblr.com/post/41212250036/internationalization-with-express-jade-and">http://rbeere.tumblr.com/post/41212250036/internationalization-with-express-jade-and</a></li>
<li>Intégration avec Backbone.js (en anglais) : <a href="http://pedromadias.wordpress.com/2013/01/27/backbone-js-and-i18n/">http://pedromadias.wordpress.com/2013/01/27/backbone-js-and-i18n/</a></li>
</ul>
<p>Cet article <a href="https://benjamin-balet.info/developpement/introduction-a-i18next-la-traduction-cote-client/">Introduction à i18next la traduction côté client</a> est apparu en premier sur <a href="https://benjamin-balet.info">Benjamin BALET</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Compiler un driver pour le Synology</title>
		<link>https://benjamin-balet.info/multimedia/synology/compiler-un-driver-pour-le-synology/</link>
		
		<dc:creator><![CDATA[Benjamin BALET]]></dc:creator>
		<pubDate>Sun, 14 Oct 2012 16:36:44 +0000</pubDate>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[Synology]]></category>
		<guid isPermaLink="false">http://benjamin-balet.info/?p=457</guid>

					<description><![CDATA[<p>Il peut être utile compiler un driver sur le Synology (par exemple pour installer une WebCam USB). Mais pour cela il faut savoir comment compiler le noyaux (ou kernel) du système Linux qui anime le Synology. Cet article s&#8217;adresse à des utilisateurs avancés ayant une bonne expérience avec Linux, mais ne pratiquant pas tous les [&#8230;]</p>
<p>Cet article <a href="https://benjamin-balet.info/multimedia/synology/compiler-un-driver-pour-le-synology/">Compiler un driver pour le Synology</a> est apparu en premier sur <a href="https://benjamin-balet.info">Benjamin BALET</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><a href="http://benjamin-balet.info/wp-content/uploads/2012/10/compilation_noyau_linux_synology.jpg" target="_blank"><img loading="lazy" decoding="async" class="alignleft size-full wp-image-1190" style="margin: 10px;" title="compilation_noyau_linux_synology" src="http://benjamin-balet.info/wp-content/uploads/2012/10/compilation_noyau_linux_synology.jpg" alt="" width="167" height="197" /></a>Il peut être utile compiler un driver sur le <strong>Synology</strong> (par exemple pour installer une WebCam USB). Mais pour cela il faut savoir comment compiler le noyaux (ou kernel) du système <em>Linux</em> qui anime le <em>Synology</em>.<span id="more-457"></span></p>
<p>Cet article s&rsquo;adresse à des utilisateurs avancés ayant une bonne expérience avec Linux, mais ne pratiquant pas tous les jours la compilation du kernel. Je ne suis pas responsable des mauvais traitements que vous infligerez à votre NAS et du mal de crâne causé par toute compilation d&rsquo;un noyau Linux.</p>
<p>En synthèse, la marche à suivre sera la suivante :</p>
<ol>
<li>Maîtriser certaines manipulations sur le NAS (Telnet, SSH).</li>
<li>Obtenir la <em>toolchain</em> (qui sert à compiler des logiciels pour le NAS, mais depuis un autre PC <em>Linux</em>).</li>
<li>Obtenir le code source des applications du <em>Synology</em>.</li>
<li>Configurer l&rsquo;environnement du PC <em>Linux</em> qui servira à la compilation. Régler les paramètres de compilation du noyau, compiler et installer.</li>
</ol>
<h1>Avant toute chose</h1>
<p><strong>Il faut posséder une machine qui tourne sous <em>Linux</em> pour effectuer les manipulations décrites dans ce post</strong>. Elle peut être virtuelle (avec <a title="VMWare Player" href="http://www.vmware.com/products/player/" target="_blank">VMWare</a> ou <a title="Virtual Box" href="http://www.virtualbox.org/" target="_blank">Virtual Box</a>), mais cela ne marchera pas avec <em><a title="CygWin est une émulation de l'environnement Linux sous Windows" href="http://www.cygwin.com/" target="_blank">CygWin</a></em>, ni avec <em>Visual Studio</em> ou <em>MinGW</em> qui ne savent pas produire des exécutables au format ELF.</p>
<h2>Les fondamentaux</h2>
<p>La lecture de ce billet nécessite en pré-requis fondamental :</p>
<ul>
<li><a title="Utiliser les fonctions SSH ou Telnet du Synology" href="http://benjamin-balet.info/multimedia/utiliser-les-fonctions-ssh-ou-telnet-du-synology/" target="_blank">Savoir utiliser SSH ou telnet avec son Synology</a>.</li>
<li><a title="Dopez votre Synology avec des centaines d’applications Linux" href="http://benjamin-balet.info/multimedia/dopez-votre-synology-avec-des-centaines-dapplications-linux/" target="_blank">Savoir installer des composants additionnel sur le Synology</a>.</li>
<li><a title="Cross-compilation pour Synology" href="http://benjamin-balet.info/multimedia/synology/cross-compilation-pour-synology/" target="_blank">Vous pouvez vous rafraîchir la mémoire sur la cross-compilation</a>.</li>
</ul>
<h2>Le modèle de CPU</h2>
<p>Il faut connaître la famille de processeur qui équipe votre Synology à l&rsquo;aide de cette page qui donne la correspondance. <strong>Ne pas confondre <em>famille de processeur</em> et <em>modèle</em></strong>. Par exemple, mon PC est équipé d&rsquo;un Intel Q6600 (le <em>modèle</em>), mais la <em>famille de CPU</em> est soit i686 ou x86_64 (selon si on souhaite faire une compilation 32bits ou 64bits). Il en va de même pour les Synology. Je suis l&rsquo;heureux possesseur d&rsquo;un DS211j équipé d&rsquo;un Feroceon (le <em>modèle</em>), mais la <em>famille du CPU</em> est <span style="text-decoration: underline;">Marvell Kirkwood mv6281</span>. Gardez cette info pour la partie compilation du Kernel, que l&rsquo;on verra en fin d&rsquo;article.</p>
<h2>La version de Linux du Syno</h2>
<p>Connectez-vous sur le <em>Synology</em> et utilisez la commande <tt>uname -r</tt> qui devrait renvoyer par exemple 2.6.32.12. Cela sera utile pour plus tard.</p>
<h1>Obtention de la <em>toolchain</em></h1>
<p>Rendez-vous sur <a title="Sources Synology" href="http://sourceforge.net/projects/dsgpl/files/" target="_blank">Sourceforge pour obtenir la <em>toolchain</em></a>. Il s&rsquo;agit d&rsquo;un ensemble d&rsquo;outils permettant de compiler un logiciel pour qu&rsquo;il soit exécuté sur le <em>Synology</em>. La toolchain ne s&rsquo;installe pas sur le NAS, mais sur un ordinateur <em>Linux</em>. Le fait de <strong>compiler des sources sur un système (<em>Linux</em>), alors que l&rsquo;exécutable produit tournera sur un autre système (le NAS Synology) s&rsquo;appelle la compilation croisée</strong> (ou cross-compilation an anglais).</p>
<p>Dans mon cas, comme la famille de CPU de mon NAS est de type Marvell 628x et que la version de Linux est la 2.6.32.12 ; j&rsquo;ai cliqué sur ce lien : <span style="text-decoration: underline;">http://sourceforge.net/projects/dsgpl/files/DSM%203.1%20Tool%20Chains/Marvell%2088F628x%20Linux%202.6.32/</span> pour obtenir l&rsquo;archive <em>gcc421_glibc25_88f628x.tgz</em>.</p>
<p>Toujours sur l&rsquo;ordinateur Linux, décompressez la toolchain dans <tt>/usr/local/</tt> avec, par exemple, la commande <tt>tar zxpf gcc421_glibc25_88f6281.tgz –C /usr/local/</tt></p>
<h1>Obtention des sources du Synology</h1>
<p>Le code source des applications open source composant le synology (noyau <em>Linux</em>, <em>php</em>, <em>Apache</em>, &#8230;) est disponible sur <a title="Source du Synology" href="http://sourceforge.net/projects/dsgpl/" target="_blank">Sourceforge</a> ainsi que la configuration utilisée par les développeurs pour compiler le noyau. Tout ceci va nous faciliter la tâche.</p>
<p>Dans mon cas, comme la famille de CPU de mon NAS est de type Marvell 628x et que la version de Linux est la 2.6.32.12 ; j&rsquo;ai cliqué sur ce lien :<span style="text-decoration: underline;"> http://sourceforge.net/projects/dsgpl/files/Synology%20NAS%20GPL%20Source/1594branch/synogpl-1594b-6281.tbz/download</span> pour obtenir l&rsquo;archive synogpl-1594b-6281.tbz que j&rsquo;ai décompressé dans un répertoire quelconque (rappel : tar zxpf).</p>
<h1>Compilation du noyau du Syno</h1>
<h2>Préparation de l&rsquo;environnement</h2>
<p>Sur la machine <em>Linux</em> sur laquelle vous allez compiler, ouvrez un terminal et préparez l&rsquo;environnement de compilation en positionnant différentes variables système  avec ces commandes (à adapter en fonction de la toolchain que vous avez téléchargé. Si votre NAS est basé sur une architecture PowerPC, il faudra remplacer <em>arm-none-linux-gnueabi</em> par <em>powerpc-linux-gnuspe</em>) :</p>
<div><tt>export CFLAGS="-I/usr/local/arm-none-linux-gnueabi/include"</tt></div>
<div><tt>export LDFLAGS="-I/usr/local/arm-none-linux-gnueabi/lib"</tt></div>
<div><tt>export RANLIB=/usr/local/arm-none-linux-gnueabi/bin/arm-none-linux-gnueabi-ranlib</tt></div>
<div><tt>export LD=/usr/local/arm-none-linux-gnueabi/bin/arm-none-linux-gnueabi-ld</tt></div>
<div><tt>export CC=/usr/local/arm-none-linux-gnueabi/bin/arm-none-linux-gnueabi-gcc</tt></div>
<div><tt>export LD_LIBRARY_PATH=/usr/local/arm-none-linux-gnueabi/lib</tt></div>
<div><tt>export ARCH=arm</tt></div>
<div><tt>export CROSS_COMPILE=/usr/local/arm-none-linux-gnueabi/bin/arm-none-linux-gnueabi-</tt></div>
<h2>Compilation du kernel</h2>
<p>Allez dans le répertoire dans lequel vous avez décompressé les fichiers sources du <em>Synology</em>, plus précisément dans le sous-répertoire <tt>source/linux-2.6.32</tt> (à adapter en fonction de ce que vous a donné la commande <tt>uname -r</tt>).</p>
<p>Il faut configurer les options de compilation du kernel (nom anglais pour noyau de l&rsquo;OS) en fonction du modèle de  processeur qui équipe votre NAS. il suffit de copier le fichier configuration du processeur à la racine de la source du kernel cela donne :</p>
<p><tt>cp /mon_rep/source/linux-2.6.32/synoconfigs/88f6281 /mon_rep/source/linux-2.6.32/.config</tt></p>
<p>Dans le répertoire source/linux-2.6.32, lancez les commandes suivantes :</p>
<p><tt>make oldconfig</tt></p>
<p><tt>make menuconfig</tt></p>
<p>Une interface graphique va s&rsquo;ouvrir dans laquelle vous choisissez différentes options de compilation et les drivers à compiler. Par exemple, pour une webcam, activez les options (la suite de la configuration dépend du modèle) :</p>
<ol>
<li>Devices Drivers</li>
<li>Multimedia Support</li>
<li>&#8230;</li>
</ol>
<p>Puis on lance la compilation avec les commandes :</p>
<p><tt>make dep</tt></p>
<p><tt>make modules</tt></p>
<p>La compilation peut être accéléré si vous avez un CPU multi-coeurs avec (pour 4 coeurs) la commande :</p>
<p><tt>make modules -j 4</tt></p>
<h2>Et après ?</h2>
<p>Vous trouverez les drivers sous la forme de fichiers .ko dans le sous-répertoire drivers. Par exemple, pour le dirver <em>Universal Video Class</em> (uvcvideo.ko) dans <span style="font-family: courier new,courier;">drivers/media/video/</span>.</p>
<p>Le module obtenu doit être chargé en mémoire afin de pouvoir piloter votre périphérique. on peut utiliser la commande <tt>insmod</tt> à cet effet. insmod fait partie du paquet module-init-tools (voir ce <a title="Dopez votre Synology avec des centaines d’applications Linux" href="http://benjamin-balet.info/multimedia/dopez-votre-synology-avec-des-centaines-dapplications-linux/" target="_blank">post pour l&rsquo;installer avec ipkg</a>) tout comme :</p>
<ul>
<li><tt>lsmod</tt> &#8211; pour lister les modules chargés</li>
<li><tt>rmmod</tt> &#8211; pour supprimer un module de la mémoire.</li>
</ul>
<p>On peut aussi le charger de manière permanente en copiant les fichiers ko dans /lib/modules/$(uname -r)/kernel/drivers/ et l&rsquo;ajouter au fichier /etc/modules</p>
<p>Un exemple concret de ces commandes est à suivre dans un billet qui explique comment compiler et installer un driver de webcam USB.</p>
<h2>Sources documentaires</h2>
<ul>
<li><a title="Guide d'intégration des applications tierces" href="http://www.synology.com/us/support/3rd-party_application_integration.php" target="_blank">Lien vers le fichier PDF officiel </a>(de Synology, anglais).</li>
</ul>
<p>Cet article <a href="https://benjamin-balet.info/multimedia/synology/compiler-un-driver-pour-le-synology/">Compiler un driver pour le Synology</a> est apparu en premier sur <a href="https://benjamin-balet.info">Benjamin BALET</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
