Rune Audio

Published on 2016-12-16 00:00:00+01:00

Qu'est-ce qu'on peut faire avec un vieux Raspberry Pi qui traine au fond d'un tiroir? A priori, plein de choses: un serveur YunoHost, une sonde de température, une dashcam, ... Ou un serveur MPD :)

Si cela vous tente, il existe plusieurs distributions qui vous aideront à atteindre cet objectif:

J'ai pris la première option. Après avoir copié le contenu sur la carte SD en utilisant la commande dd (comme d'hab'), on plug le tout dans le Raspberry, on lui enfiche un câble RJ45, la prise d'alimentation et c'est parti!

Si sa détection n'est pas automatique, regardez les logs du routeur pour trouver son adresse IP; sinon, rendez-vous sur l'adresse http://runeaudio.local et ajoutez simplement une source (NAS, disque USB, Jamendo, Drible, Spotify ou une Webradio).

Deux-trois remarques:

  • Je n'ai pas réussi à configurer le partage par NFS (alors qu'il fonctionne parfaitement sur Kodi, sur le second Raspberry)
  • Pour contrôler le bouzin, passez soit par le client Web sur le réseau local, soit par une application. Sur Android, l'appli MPDroid est top et très simple: on parcourt les sources (avec récupération automatique des métadonnées et des jaquettes) et on lance (soit directement, soit à la suite). Sur iOS, je n'ai pas encore trouvé mon bonheur; je me limite donc à un raccourci du serveur sur la page d'accueil :)

Pour aller plus loin et si vous possédez un bon ampli, pensez à passer sur un HifiBerry ou un autre DAC.

Six lois pour la gestion du temps

Published on 2016-11-24 00:00:00+01:00

LOI DE PARKINSON

Plus on a le temps pour réaliser une tâche, plus cette tâche prends du temps.

LOI DE MURPHY

Toute chose prend plus de temps qu’on ne l’avait prévu.

LOI D’ILLICH

Au-delà d’un certain seuil de travail l’efficacité décroit.

LOI DE CARSON

Faire un travail de façon continue prend moins de temps que le faire en plusieurs fois.

LOI DE FRAISSE

Une heure n’est pas toujours égale à une heure.

LOI DE PARETO

20% de nos activités produisent 80% de nos résultats.

Une semaine avec Solus

Published on 2016-11-07 00:00:00+01:00

Je m'étais promis que ma dernière installation de Fedora (22?) serait la dernière, qu'à présent j'avais grandis et que je pouvais finalement, passé 30 ans, me poser un peu sur le système à utiliser. Bah non. L'appel de la nouveauté et le goût d'un système instable m'ont de nouveau fait plonger dans les méandres des distributions Linux à installer sur mon matériel...

Après avoir un peu fouillé distrowatch, je pensais repasser sur Arch grâce à Arch-Anywhere, pour une installation d'ArchLinux en 30 secondes-montre-en-main (en vrai, c'est plus proche de 45-50 minutes, surtout quand on a explosé son quota de téléchargement chez son FAI adoré et qu'on se retrouve limité à 300kB/s).

Bon. En fait, je n'ai plus l'âge de tripatouiller une distribution de barbu: ArchLinux, c'est passé. Après l'installation, Slim m'annonçait une disposition clavier en en-US avec un KDE derrière en fr-BE. Pas envie de chipoter, je voulais un truc qui "juste marche".

Bref, Solus. En fait, c'est 'achement rafraîchissant: le système est hyper réactif (mais vraiment hein: ça boot en 2 secondes après que Grub se soit chargé, sur du matériel relativement récent).

Outils et programmation

Si vous devez installer un environnement de développement, pensez toujours à ajouter *-devel à l'installation; par exemple python-devel ou ruby-devel, sans quoi vous n'aurez pas les librairies, et a priori, il y aura peu de choses qui fonctionneront... :)

Pour Ruby, on doit aller un chouia plus loin, puisque l'installation des Development Tools se fait grâce à la commande sudo gem update --system. Après cela, aucun soucis pour installer compass avec gem install compass.

Autres bonnes nouvelles: vscode et atom sont intégrés directement dans les dépôts de la distribution. Parmi les third parties, on trouve également Android Studio, Idea, SublimeText3 et PyCharm. Le choix est là, et c'est le top.

Carte d'identité électronique

En Belgique, on a une carte d'identité électronique, avec une puce, un code PIN, et on peut faire plein plein de choses super utiles avec (comme remplir sa déclaration d'impôts). Il y a deux paquets à prendre en compte pour cela (le middleware et le viewer); tout est expliqué sur la page d'accueil du projet pour les principales distributions. Pour les autres, il y a les sources :) Solus faisant partie "des autres", il y a quelques dépendances à résoudre avant d'arriver à compiler le nécessaire:

sudo pisi install pcsc-lite-devel libusb-devel libgtk-3-devel openjdk-8
./configure
make
sudo make install

On a aussi besoin des drivers ccid, qui ne sont pas dispos dans les dépôts. On peut les télécharger ici, et les installer en suivant la manière classique (./configure; make; sudo make install). L'installation nous informe qu'il ne faut surtout pas oublier de copier le fichier src/92_pcscd_ccid.rules dans le répertoire /etc/udev/rules.d/. A l'occasion, il faudra que je creuse pourquoi cette étape n'est pas effectuée en même temps que l'installation...

Démarrez ensuite pcscd en mode foreground avec les traces de debug avec les options -f -d. Cela vous permettra de vérifier et valider que votre lecteur USB est correctement détecté. Si cela coince, vous devrez vous débrouillez pour trouver les pilotes ccid compatible avec votre matériel. Par exemple avec un lecteur ACR38en USB: dmesg le détecte bien, mais pcscd ne comprend que dalle. Il suffit alors de télécharger le pilote ccid sur le site du constructeur, de l'installer, et de redémarrer les démons:

$ dmesg

[  710.951271] usb 2-3: new full-speed USB device number 8 using xhci_hcd
[  711.122192] usb 2-3: New USB device found, idVendor=072f, idProduct=9000
[  711.122194] usb 2-3: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[  711.122196] usb 2-3: Product: ACR38 USB Reader
[  711.122197] usb 2-3: Manufacturer: ACS
$ sudo pcscd -f -d

ccid_usb.c:313:OpenUSBByName() Using: /usr/lib64/pcsc/drivers/ifd-acsccid.bundle/Contents/Info.plist
ccid_usb.c:331:OpenUSBByName() ifdManufacturerString: Advanced Card Systems Ltd.
ccid_usb.c:332:OpenUSBByName() ifdProductString: ACS CCID driver
ccid_usb.c:333:OpenUSBByName() Copyright: This driver is protected by terms of the GNU Lesser General Public License version 2.1, or (at your option) any later
ccid_usb.c:706:OpenUSBByName() Found Vendor/Product: 072F/9000 (ACS ACR38U)
ccid_usb.c:708:OpenUSBByName() Using USB bus/device: 2/9
acr38cmd.c:519:ACR38_SetCardVoltage() cardVoltage: 0
acr38cmd.c:600:ACR38_SetCardType() cardType: 0
ccid.c:728:ccid_open_hack_post() Firmware: ACR38-1100

Et voilà! Un peu plus compliqué que sur une autre distribution, mais cela fonctionne :)

Le pilote Firefox fonctionnera (pour peu que vous l'ayez installé, forcément). Par contre, avec les traces de pcscd, on a vraiment l'impression que ce module consomme énormément. Quand vous ne l'utilisez pas, il peut être intéressant de le désactiver complètement. On perdrait en confort ce qu'on gagnerait en performances.

Django et ses contexts processors

Published on 2016-10-25 00:00:00+02:00

De manière très grossière, les contexts processors permettent de définir du contenu accessible globalement dans l'application, par exemple pour définir un menu de navigation ou le titre du site. On parle donc d'éléments qui ne sont pas spécifiques au contexte (vue, fonction, ...) actuel, mais plutôt définis globalement par rapport à l'application.

Au niveau de la vue, on a pour habitude de passer un dictionnaire au template; celui-ci sera complété par un ensemble d'autres informations, définies dans les contexts processors.

Techniquement, il s'agit simplement de fonctions référencées au niveau de la configuration de l'application. Si on souhaite générer un menu global, il suffit dès lors de définir une fonction dans un module particulier:

# my_module/navigation.py

def main_menu(request):
    return { 'links': ['Accueil', 'Se restaurer', 'Informations pratiques' ] }

On peut dès lors y accéder dans le template de base:

<!-- templates/base.html -->
<!DOCTYPE HTML>
<html>
    {% for link in links %}
        <a href="#">{{ link }}</a>
    {% endfor %}
</html>

Pour éviter que ces variables ne soient écrasées par la construction de la vue, la transmission du dictionnaire vers la vue doit être construite en utilisant la fonction render(request, template, context), de la manière suivante. Evitez la fonction render_to_response:

def events(request):
    context = { 'events': events = Event.objects.all() }

    return render(request, 'events.html', context)

Depuis Django 1.10, la variable TEMPLATE_CONTEXT_PROCESSORS est dépréciée et doit être remplacée par l'option context_processors:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': ['templates', ],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                '...',
            ],
        },
    },
]

Recherche full-text dans toute la db... avec grep

Published on 2016-09-30 00:00:00+02:00

Je n'en suis franchement pas fier parce que c'est tout sauf propre et réutilisable, mais cela pourrait peut-être à nouveau servir à l'occasion... Le script ci-dessous lit l'ensemble d'une base de données en utilisant pyodbc et écrit leur contenu dans un fichier texte, nommé d'après la table dont elles sont issues. En mode bourrin, en somme, puisqu'on liste toutes les tables, et qu'on fait ensuite un select sur chacune d'entre elles, avant d'en dumper le contenu dans un fichier texte. Intérêt? Exécuter un grep -rin par la suite sur les fichiers écrits.

import pyodbc

CXSTR = 'DRIVER={SQL Server};SERVER=...;DATABASE=...;UID=...;PWD=...;'

if __name__ == '__main__':

    cnxn = pyodbc.connect(CXSTR)
    cursor = cnxn.cursor()

    cursor.execute("SELECT * FROM information_schema.tables WHERE TABLE_TYPE='BASE TABLE'")
    for row in cursor.fetchall():
        cursor2 = cnxn.cursor()
        cursor2.execute("Select * From " + row[2])

        print('using {}'.format(row[2] + '.txt'))
        with open(row[2] + '.txt', 'wt', encoding='utf-8') as f:
            for row2 in cursor2.fetchall():
                f.write(str(row2))

Quand je disais qu'il y avait moyen de faire plus propre... :-) Allez, pour le fun: le script s'en sort avec un score PyLint de 2.67/10 (missing docstring, invalid name, lines too long, no member et anomalous backslash in string).

Mémo pour clôturer des issues au travers de commits dans Gitlab

Published on 2016-09-26 00:00:00+02:00

Juste un mémo pour les mots-clés gérés par GitLab et Github permettant de clôturer une issue au moment du commit:

close
closes
closed
fix
fixes
fixed
resolve
resolves
resolved

Au moment du commit, il suffit de référencer l'issue grâce à #<issue_number>, en plus d'utiliser l'un des mots-clés ci-dessus.

Source: https://help.github.com/articles/closing-issues-via-commit-messages/

Générateurs de sites statiques

Published on 2016-09-17 00:00:00+02:00

A nouveau un petit tour des générateurs de sites statiques. Il y a quelques temps, j'avais essayé Jekyll et j'avais un peu galéré sur sa configuration (principalement sous Windows...), ce qui m'a fait arriver sur Pelican, avec lequel j'ai pu construire quelques blocs d'informations (blogs, deux-trois sites sites de contenus, ...).

Parmi les (nombreuses) autres applications de génération, il y en a quelques unes qui me font de l'oeil:

  • Hugo. Il suffit de télécharger un exécutable et de démarrer. Les thèmes disponibles sont pour la plupart sympas, quoique souvent tirés d'une autre plateforme (Jekyll en tête).
  • Lektor, parce qu'il y a un oeuf dans la bannière principale.
  • Hexo, pour un blog fonctionnel rapidement sous NodeJS.
  • Et le petit dernier qui a l'air bien sympa, Grow, quoique certains commentaires le laissent en version alpha pour un petit temps encore.

A priori, la manière de présenter les choses est plus ou moins similaire pour tous ces outils. Hugo a l'air de proposer pas mal de bonnes idées, notamment au niveau des taxonomies. Il est ainsi possible d'associer pratiquement n'importe quel terme à du texte y faire référence par la suite. Lektor ressemble à un générateur de CMS statique plus qu'à un générateur de blog: l'interface est dynamique, le contenu généré statique. Il est évidemment possible d'en faire une plateforme simple, mais cela demande déjà un petit travail à la base, tout comme Nanoc, Middleman ou Wintersmith. Installez ces quatre derniers et vous n'aurez pratiquement rien sans un peu de sueur. Au final par contre, ils feront sans doute gagner un temps dingue et iront probablement plus loin que n'importe quel autre générateur de site.

Pour ma part, je tente l'aventure sur Hugo: les idées ont l'air reltivement intéressantes, la structure du front matter peut être écrite en YAML (et donc relativement réutilisable avec d'autres générateurs). Seul désavantage par rapport à Pelican, les articles sont à écrire en markdown (mais un plugin RestructuredText existe).

C'est parti!

Hugo

Après quelques jours à tripatouiller Hugo, voici un petit retour sur la plateforme. Il y a pas mal de choses très intéressantes: sa facilité de configuration, sa flexibilité et sa structure. En plus, le projet avance super bien (il suffit de voir les nouveautés sur la v0.16 pour s'en rendre compte). Un des gros points noirs a été corrigé avec l'implémentation de l'héritage de templates. Il suffit de créer une page baseof.html placée dans le répertoire layouts/_default/ du thème utilisé, et dans laquelle on trouvera le squelette global agrémenté de bloc {{ block "bidule" . }} ... {{ end }}. Un template peut alors en hériter, en redéfinissant ces blocs par un {{ define "bidule" }} ... {{ end }}.

Si on reste sur des templates plus basiques, il est du coup très facile de définir un flux RSS ou d'inclure une sitemap. On crée un fichier sitemap.xml dans les layouts, qu'on rempli avec quelques paramètres du fichier config.toml:

<!-- le fichier /layouts/sitemap.xml -->
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
{{ range .Data.Pages }}
<url>
    <loc>{{ .Permalink }}</loc>
    <lastmod>{{ safeHTML ( .Date.Format "2006-01-02T15:04:05-07:00" ) }}</lastmod>{{ with .Sitemap.ChangeFreq }}
    <changefreq>{{ . }}</changefreq>{{ end }}{{ if ge .Sitemap.Priority 0.0 }}
    <priority>{{ .Sitemap.Priority }}</priority>{{ end }}
</url>
{{ end }}
</urlset>

Et pour les paramètres, c'est ceci:

[sitemap]
    changefreq = "monthly"
    priority = 0.5
    filename = "sitemap.xml"

Deux autres points sympas, ce sont les taxonomies et les types (ou sections): le contenu du site est structuré selon plusieurs sous-dossiers, chacun représentant une section (publications, posts, projets, ...). Hugo permet également de récupérer facilement chacun de ces types séparément, grâce à un filtre type {{ range where .Data.Pages "Section" "post" }} (que l'on traduira par "prend tout le contenu que tu trouves et qui s'apparente à quelque chose de la section post"). Et comme le contenu est classé parmi ces sections, chacune d'entre elle peut avoir un modèle de contenu, (archetype), qui vient avec ses propres métadonnées.

L'exemple ci-dessus est repris du thème Academic, où on voit clairement que chaque type (associé à une section, donc) est représenté différement, et que la plateforme permet de facilement sélectionner tel ou tel type pour le placer à un endroit particulier de la page.

En résumé, la plateforme est rafraîchissante. Parmi les points qui tachent: le manque d'intermédiaires pour la construction des fichiers (minification, thumbnailer, ... - quoique...). Parmi les points plaisants: les idées implémentées, le suivi, la facilité de déploiement et le résultat général.

Adopté.

Building Maintainable Software

Published on 2016-09-12 00:00:00+02:00

En profitant d'une petite promo de -50% chez O'Reilly, je me suis offert l'ebook Building Maintainable Software, édition C-Tartine. Le livre propose de suivre une série de préceptes permettant d'améliorer la qualité du code produit. Bien que le contenu ressemble parfois plus à une grosse page marketing pour le Software Improvment Group, il reste intéressant et vaut le coup d'être lu. Deux petits problèmes par contre:

  1. On part parfois d'un cas concret (qui ressemble parfois à ce que j'ai pu commettre de code foireux), en prenant une méthode dans laquelle on trouve plusieurs concepts différents (appel à la db, cast vers une classe type DTO, validation d'input). Juste après, on passe directement sur un autre exemple, beaucoup plus simple à résoudre.
  2. J'ai l'impression que cela parle plus de théorie, sans proposer de solution automatique, ce qui rejoint un peu ce que je disais au niveau marketing: "si vous voulez vous améliorer, signez chez nous!".

Les principaux conseils sont les suivants (pour les détails, il vous faudra acheter le bouquin :) ):

Au niveau des méthodes

  • Gardez vos méthodes/fonctions courtes. Pas plus de 15 lignes, en comptant les commentaires. Des exceptions sont possibles, mais dans une certaine mesure uniquement (pas plus de 6.9% de plus de 60 lignes; pas plus de 22.3% de plus de 30 lignes, au plus 43.7% de plus de 15 lignes et au moins 56.3% en dessous de 15 lignes). Oui, c'est dur à tenir, mais faisable.
  • Conserver une complexité de McCabe en dessous de 5, c'est-à-dire avec quatre branches au maximum. A nouveau, si on a une méthode avec une complexité cyclomatique de 15, la séparer en 3 fonctions avec une complexité de 5 conservera globalement le nombre 15, mais rendra le code de chacune de ces méthodes plus lisible, plus maintenable.
  • N'écrivez votre code qu'une seule fois: évitez les duplications, copie, etc., c'est juste mal: imaginez qu'un bug soit découvert dans une fonction; il devra alors être corrigé dans toutes les fonctions qui auront été copiées/collées. C'est aussi une forme de régression.
  • Conservez de petites interfaces. Quatre paramètres, pas plus. Au besoin, refactorisez certains paramètres dans une classe, plus facile à tester.

Au niveau des classes

  • Privilégiez un couplage faible entre vos classes. Ceci n'est pas toujours possible, mais dans la mesure du possible, éclatez vos classes en fonction de leur domaine de compétences. L'implémentation du service UserNotificationsService ne doit pas forcément se trouver embarqué dans une classe UserService. De même, pensez à passer par une interface (commune à plusieurs classes), afin d'ajouter une couche d'abstraction. La classe appellante n'aura alors que les méthodes offertes par l'interface comme points d'entrée.

Au niveau des composants

  • Tout comme pour les classes, il faut conserver un couplage faible au niveau des composants également. Une manière d'arriver à ce résultat est de conserver un nombre de points d'entrée restreint, et d'éviter qu'on ne puisse contacter trop facilement des couches séparées de l'architecture. Pour une architecture n-tiers par exemple, la couche d'abstraction à la base de données ne peut être connue que des services; sans cela, au bout de quelques semaines, n'importe quelle couche de présentation risque de contacter directement la base de données, "juste parce qu'elle en a la possibilité". Vous pourrez également passer par des interfaces, afin de réduire le nombre de points d'entrée connus par un composant externe (qui ne connaîtra par exemple que IFileTransfer avec ses méthodes put et get, et non pas les détails d'implémentation complet d'une classe FtpFileTransfer ou SshFileTransfer).
  • Conserver un bon balancement au niveau des composants: évitez qu'un composant A ne soit un énorme mastodonte, alors que le composant juste à côté n'est capable que d'une action. De cette manière, les nouvelles fonctionnalités seront mieux réparties parmi les différents systèmes, et les responsabilités plus faciles à gérer. Un conseil est d'avoir un nombre de composants compris entre 6 et 12 (idéalement, 12), et que ces composants soit approximativement de même taille.

Et de manière plus générale

  • Conserver une densité de code faible: il n'est évidemment pas possible d'implémenter n'importe quelle nouvelle fonctionnalité en moins de 20 lignes de code; l'idée ici est que la réécriture du projet ne prenne pas plus de 20 hommes/mois. Pour cela, il faut (activement) passer du temps à réduire la taille du code existant: soit en faisant du refactoring (intensif?), soit en utilisant des librairies existantes, soit en explosant un système existant en plusieurs sous-systèmes communiquant entre eux. Mais surtout en évitant de copier/coller bêtement du code existant.
  • Automatiser les tests, ajouter un environnement d'intégration continue dès le début du projet et vérifier par des outils les points ci-dessus.

Ceci est sans doute un des points les plus ennuyants de ce livre: il n'y a finalement que très peu d'exemples concrets, notamment pour la mise en place d'un tel environnement. Ok, ça parle de Jenkins du début à la fin, mais plus comme un exemple à suivre (ou parfois à ne pas suivre) que comme un outil à utiliser. De manière plus générale, j'ai l'impression que le code .Net reste extrêmement fermé à des outils open source permettant d'augmenter la qualité du code. Il existe des linters pratiquement pour tous les langages, mais si vous voulez quelque chose de fonctionnel pour C#, il va falloir passer par la caisse. Une stratégie MS classique en sommme: "on vous offre les outils pour pas grand chose, et pour aller plus loin, vous douillez".

En regardant un peu à droite-à gauche, pour du code .Net, les outils suivants ont l'air sympa, comme:

  • SonarLint (en cli, intégré dans un IDE ou en mode CI) (dont je ne peux plus me passer :-) )
  • StyleCop
  • Refactoring Essentials
  • CodeMaid, pour un code propre et soyeux. En zieutant la description, on trouve que CodeMaid is an open source Visual Studio extension to cleanup, dig through and simplify our C#, C++, F#, VB, PHP, JSON, XAML, XML, ASP, HTML, CSS, LESS, SCSS, JavaScript and TypeScript coding.

Gravity

Published on 2016-09-04 00:00:00+02:00

Je pense que la meilleure manière de résumer ce film peut être attribuée à M. François Pérusse pour son "Et galère, et merde, oh merde!".

En soi, le film est une expérience cinématographique à vivre en 3D plus qu'une histoire à suivre (bien qu'ils aient fait des films comme Titanic et la Passion du Christ, mais bon...). On a une action continue pendant près de 90 minutes, sans aucun temps mort. Vu la quantité de tuiles qui leur tombe dessus, on en viendrait presque à être déçu de la fin: si Hollywood m'avait demandé mon avis, j'aurais fait un crossover avec Jaws pour qu'un requin les bouffe à leur arrivée sur Terre. BIzarrement, ils ne l'ont pas fait.

Ne pas afficher certains fichiers/dossiers avec Visual Studio Code

Published on 2016-08-30 00:00:00+02:00


Par défaut, Visual Studio Code affiche pratiquement tous les fichiers et dossiers, à l'exception des répertoires de contrôle de sources (.git, .svn, .hg) et des bidules spécifiques à OSX qui ennuient tout le monde (.DS_Store).

Pour peu que vous développiez en Python (et ce sera a priori le cas pour tous les autres langages...), vous pourriez avoir envie de dégager l'affichage des dossiers __pycache__ et des fichiers .pyc. Depuis le menu, ouvrez les paramètres utilisateurs dans le menu préférences. L'éditeur se scindera en deux: d'un côté, vous trouverez les paramètres par défaut, et de l'autre, vos paramètres personnalisés. Ajoutez-y les deux nouvelles entrées à ignorer:

// Place your settings in this file to overwrite the default settings
{
    "editor.fontSize": 13,
    "editor.wrappingColumn": 300,
    "files.exclude": {
        "**/.git": true,
        "**/.svn": true,
        "**/.hg": true,
        "**/.DS_Store": true,
        "**/__pycache__": true,
        "**.pyc": true
    }
}

Et redémarrez l'éditeur :)

Page 1 / 14 »