L'API GitLab, Flask et HTMX pour développer une application web

Développer une application web, sujet en perpétuel renouvellement, mais qui reste tout de même complexe à aborder, surtout quand ce n'est pas l'objectif principal.

Lors d'ateliers d'initiation au monde de l'IA, en compagnie du talentueux Stéphane Philippart, nous nous étions posé la question de la mise en valeur de tout ce qui est exploré pendant ces sessions.
Une application web écrite en utilisant Streamlit permet de mettre en pratique le modèle développé pendant l'atelier, illustrant la détection de signes du jeu Pierre/Feuille/Ciseau.

Ce framework est assez connu des data scientists parce qu'il permet d'exposer sous forme d'application web le résultat de travaux souvent arides dans leur forme première. Le résultat est assez facile à obtenir car il s'appuie sur Python qui reste le langage le plus utilisé dans ce domaine, et la partie front end est générée depuis le même code backend.
Mais cela comporte bien des limites et on s’éloigne rapidement de possibilités de modifications plus avancées par la suite.

Alors que faire ? C'est une des questions à laquelle je m'intéresse pendant ma veille technologique.
La suite de cet article est donc un des résultats que j'ai noté : il ne reflète que ma propre approche et n'est pas une solution absolue et définitive, je cherche encore.

Pour ne pas me retrouver trop orienté dans mes choix, je me suis un peu éloigné du propos de départ, à savoir l'exposition d'un modèle de machine learning.
Néanmoins, j'ai décidé de rester dans l'exploitation d'une API, ce qui reste très proche des problématiques initiales. Un prérequis important est néanmoins de se baser sur l'écosystème Python, déjà connu de la population manipulant les modèles de machine learning.

J'ai donc rapidement choisi d'utiliser l'API d'un outil que j'aime particulièrement : GitLab. Parmi les fonctionnalités peut être moins connues, il y a une To-Do list recensant tout ce qui vous a été assigné.
Plutôt qu'un long discours, une illustration par l'image de cette fonctionnalité, sur un miroir du code du projet de cet article.

L'idée est donc de proposer ces éléments sous la forme d'un site web, écrit le plus simplement possible. L'objectif sera d'obtenir le résultat suivant :

Pour concrétiser tout ça, il va nous falloir les éléments suivants :

  • un accès aux données de cette To-Do list

  • un moyen de consommer ces données et les exposer sous forme de site web

Si vous souhaitez découvrir par vous même sans lire la suite, le code issu de cet article est disponible sur un repository publique.

Commençons par l'accès aux données. Comme je l'ai indiqué, GitLab expose déjà le nécessaire sous forme d'une API.
Pour rester dans le périmètre de l'écosystème Python, l'usage de cette API se fera par le biais d'un paquet Python appelé python-gitlab.

Pour obtenir la liste des éléments de To-Do, seulement 2 appels à ce paquet sont nécessaire :

import gitlab

# Initialize GitLab
gl = gitlab.Gitlab(url="https://gitlab.com", private_token="glpat-xx_your_token")

# Get todos
todos = gl.todos.list(project_id=123456, all=True)

Parmi les particularités, vous noterez :

  • la nécessité de générer un token d'accès à votre projet. La documentation de GitLab vous aidera pour cette étape.

  • l'ID de projet qui permettra de filtrer les issues de ce projet uniquement. Cet ID est visible sur la page Settings - General (ou Paramètres - Général en VF)

Avec ce code, nous avons les données que nous souhaitons afficher.
Passons maintenant à l'exposition par un site web.
Pour cela, je vous propose d'utiliser Flask, un micro-framework web Python très populaire.

Flask, est souvent choisi en raison de sa simplicité et de sa flexibilité. Contrairement aux frameworks plus lourds, Flask fournit les outils de base nécessaires pour démarrer un projet, sans imposer une structure rigide. Cette légèreté permet aux développeurs de construire une application sur mesure, en ajoutant uniquement les extensions et les bibliothèques nécessaires.
Bref, pour des projets web, si je ne choisis pas FastAPI, je pars avec Flask.

Pour le rendu lui même, j'ai choisi de m'appuyer sur la capacité de Flask à utiliser des templates. Plus spécifiquement, il est possible d'utiliser des templates HTMX.

HTMX, est une extension récente pour HTML. En s'appuyant sur HTMX, on peut facilement ajouter des fonctionnalités AJAX à une application Flask sans écrire forcément de JavaScript. Cela signifie qu'il est possible de créer des pages web interactives et réactives tout en conservant la simplicité et la lisibilité du code côté serveur en Python.

Alors comment est ce que ça se concrétise ?
Je vous rappelle que le code est disponible sur GitHub.
Il va vous falloir essentiellement 2 fichiers : whats-next.py pour le code python et, dans le répertoire templates, un fichier todos.html.

Nous reviendrons sur les autres fichier par la suite.
Jetons un œil à whats-next.py :

import gitlab
import os
from flask import Flask, render_template
import config

# Initialize Flask app
app = Flask(__name__)


# Function to get todos from GitLab
def get_gitlab_todos():
    # Initialize GitLab
    gl = gitlab.Gitlab(url=config.gitlab_server, private_token=config.gitlab_token)

    # Get todos
    todos = gl.todos.list(project_id=config.gitlab_project_id, all=True)
    return [todo.attributes for todo in todos]


# Route to display todos
@app.route("/")
def index():
    todos = get_gitlab_todos()
    return render_template("todos.html", todos=todos)


if __name__ == "__main__":
    app.run()

Le cœur de la mécanique de Flask peut se réduire aux éléments :

app = Flask(__name__)
@app.route("/")
def index():
    return <YOUR HTML>
app.run()

L'application Flask que l'on initialise et que l'on lance va se charger de faire correspondre des fonctions à toutes les routes que votre application va utiliser.

Vous aurez ensuite reconnu le contenu de la fonction get_gitlab_todos qui va retourner une liste des données de To-Do.
Outre les appels à l'API que j'ai détaillé précédemment, notez le code de la ligne de return :

return [todo.attributes for todo in todos]

Il s'agit d'une mécanique Python très utile appelée "list comprehension".
Cela permet de retourner une liste en effectuant une opération sur les éléments de la liste de départ. Ici, nous allons retourner une liste dont chaque élément sera le contenu de la propriété attributes de la liste todos retournée par l'API GitLab.
Si vous souhaitez plus d'information, je vous conseille un excellent article de Real Python.

L'interaction entre le code de votre serveur et le rendu se produit dans l'usage de render_template :

return render_template("todos.html", todos=todos)

Flask va grâce à cet appel utiliser le template du fichier todos.html et lui fourni la variable todos.
Regardons donc ce qui se passe dans le fichier de template.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>GitLab Todos</title>
    <link rel="stylesheet" href="https://unpkg.com/nes.css/css/nes.min.css">
    <link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet">
    <style>
        body {
            font-family: 'Press Start 2P', cursive;
        }
    </style>
</head>

<body>
    <h2 class="nes-text is-primary">GitLab Todos</h2>
    <table class="nes-table is-bordered">
        {% for todo in todos %}
        <tr>
            <td># {{ todo['target']['iid'] }}</td>
            <td><a href="{{ todo['target_url'] }}" class="nes-text is-primary">{{ todo['target']['title'] }}</a></td>
        </tr>
        {% endfor %}
    </table>
</body>

</html>

Concentrons nous sur la portion qui utilise cette interaction avec Flask, dans le code de la table.

<table class="nes-table is-bordered">
    {% for todo in todos %}
    <tr>
        <td># {{ todo['target']['iid'] }}</td>
        <td><a href="{{ todo['target_url'] }}" class="nes-text is-primary">{{ todo['target']['title'] }}</a></td>
    </tr>
    {% endfor %}
</table>

Les portions de code situés entre les balises {% et %} sont du code python que Flask exécute pour effectuer le rendu.
Ici, vous apercevez une boucle qui parcourt les éléments de la liste todos.
Dans les cellules td il est alors possible d'utiliser le contenu de variables à l'aide des balises {{ et }} comme par exemple {{ todo['target_url'] }} qui utilise alors l'URL de l'issue à afficher qui est dans l'attribut target_url de l’élément courant.

Afin d'obtenir un rendu un peu particulier, j'ai utilisé des éléments CSS issus du projet Nes.css ainsi que la fonte Press Start préconisée par le projet pour un visuel très retro gaming comme je les aime.

<link rel="stylesheet" href="https://unpkg.com/nes.css/css/nes.min.css">
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet">

Nous voilà avec un petit site web qui expose cette To-Do list à l'aide d'un serveur qui est dorénavant le votre 👏

Dans les derniers détails, vous noterez dans le code python l'usage d'un fichier config.py que j'utilise pour définir les variables que je ne souhaite pas exposer comme mon token GitLab ou l'ID du projet.

J'espère que cet article vous permettra d'utiliser les technologies web pour exposer des données que vous auriez besoin de partager.

Pour ma part, je sais que ce projet nécessiterai encore quelques améliorations, que ce soit dans les données affichées ou bien dans la séparation plus propre du CSS.

N'hésitez pas à me donnez votre opinion et posez vos questions que ce soit ici où en me contactant.

Le projet sur GitHub : https://github.com/titimoby/whats-next