Chapitre 2 : Architecture Technique
2.1. Vue d'ensemble de l'architecture
L'architecture du projet "Augmented Analyst" a été conçue pour garantir la robustesse, la fiabilité et l'automatisation. Elle repose sur une séparation claire des responsabilités entre trois environnements distincts : l'environnement de développement local, la plateforme d'intégration continue (CI/CD) et l'environnement de production.
Le flux de travail standard est le suivant :
- Le développement et les tests initiaux sont effectués dans l'environnement local.
- Chaque modification validée et "pushée" vers le dépôt Git déclenche le processus dans l'environnement de CI/CD, qui agit comme un portail de contrôle qualité.
- Si, et seulement si, la qualité est validée, le code est automatiquement déployé dans l'environnement de production, où il est accessible à l'utilisateur final.
Cette structure garantit qu'aucune modification ne peut atteindre la production sans avoir été testée et validée, assurant ainsi une stabilité maximale du service.
2.2. L'application principale (Backend)
Le cœur logique de l'application est un backend développé en Python, orchestré par le micro-framework Flask.
2.2.1. Framework
L'application est construite avec Flask. Le code suit le design pattern de l'Application Factory (via la fonction create_app). Cette approche consiste à encapsuler la création et la configuration de l'application dans une fonction, ce qui offre plusieurs avantages :
- Testabilité : permet de créer différentes instances de l'application pour différents contextes (production, test) avec des configurations distinctes.
- Organisation : évite les variables d'application globales et favorise une structure de code plus propre et plus modulaire.
2.2.2. Modèles et base de données
L'interaction avec la base de données est gérée par l'ORM SQLAlchemy. Le schéma est composé de trois tables principales :
user: stocke les informations d'authentification.-
titres: table de référence contenant la liste des actifs. Elle inclut les colonnes de seuils sur 52 semaines, qui suivent une stratégie de dénormalisation pour la performance :an_haut,an_bas: stockent la valeur brute du seuil, dans la devise d'origine du titre (USD, CAD, etc.).an_haut_cad,an_bas_cad: stockent la valeur du seuil, systématiquement convertie en CAD. Ces colonnes sont précalculées par le pipeline pour optimiser les requêtes du dashboard.
-
historique: enregistre la "photographie" de chaque titre pour chaque jour. Elle contient :valeur,devise: le prix brut du titre et sa devise d'origine.cad_value: une colonne précalculéee contenant la valeur du prix, systématiquement convertie en CAD.quantite: la quantité détenue.
2.2.3. Gestion de l'Authentification
La sécurité de l'accès est assurée par deux extensions Flask clés : * Flask-Login : gère le cycle de vie de la session utilisateur (connexion, déconnexion, protection des routes "privées"). * Flask-Bcrypt : assure le stockage sécurisé des mots de passe en ne sauvegardant que leur "hash" cryptographique, jamais le mot de passe en clair.
2.3. Le pipeline de données (ETL)
Le système est alimenté par un pipeline ETL (Extract, Transform, Load) robuste, orchestré par le script pipeline.py. Ce pipeline est conçu pour être indépendant de son heure d'exécution.
2.3.1. Logique Temporelle : la Robustesse par le J-1
Le principe directeur du pipeline est de ne jamais dépendre de l'heure exacte de son exécution. Pour garantir des données de clôture fiables et définitives, le pipeline traite systématiquement les données de la veille (jour J-1).
get_current_montreal_date(): le script détermine d'abord la date actuelle dans le fuseau horaire de Montréal (America/Montreal).date_a_traiter = date_actuelle - 1 jour: cette date (J-1) devient la source de vérité pour toutes les opérations du pipeline (appels API, insertions en base de données).- Gestion des week-ends : le pipeline est conçu pour ne pas s'exécuter s'il est lancé un dimanche ou un lundi, car les marchés de la veille (samedi et dimanche) étaient fermés. Il s'arrête alors proprement.
2.3.2. Orchestration via run_full_pipeline
Le point d'entrée du processus est la fonction run_full_pipeline, qui accepte un argument booléen fetch_market_data.
-
Mode
fetch_market_data=True(comportement pour le cronjob quotidien) :- Extraction : Le pipeline lit le CSV de référence et appelle l'API Marketstack pour récupérer les prix de clôture pour la
date_a_traiter(J-1). - Objectif : Assurer la mise à jour officielle des prix avec les données de clôture définitives de la veille.
- Extraction : Le pipeline lit le CSV de référence et appelle l'API Marketstack pour récupérer les prix de clôture pour la
-
Mode
fetch_market_data=False(pour la synchronisation de portefeuille) :- Extraction : Le pipeline lit uniquement le fichier CSV. Aucun appel à Marketstack n'est effectué.
- Objectif : Permet de synchroniser immédiatement la structure de la base de données après une modification du portefeuille (ajout/suppression de titres). Il insère un enregistrement pour la
date_a_traiter(J-1) en utilisant le prix temporaire présent dans le CSV.
2.3.3. Transformation (Transform)
- La fonction
read_and_clean_csvstandardise les noms de colonnes, gère les fichiers vides ou corrompus, et parse les informations complexes (comme la colonne "52 Week Range"). - La logique de conversion de devises est appliquée en se basant sur la colonne
Marketstack_Currencyet le tauxusd_to_cad_ratedu fichierconfig.ini.
2.3.4. Chargement (Load)
Les données transformées sont insérées dans la base de données MariaDB via des requêtes UPSERT (INSERT ... ON DUPLICATE KEY UPDATE).
- La clé unique
(titre_id, date_releve)de la tablehistoriqueest fondamentale. Elle garantit qu'il ne peut y avoir qu'un seul enregistrement par titre et par jour. - Scénario de mise à jour : si une exécution en mode
fetch_market_data=Falsea créé une entrée pour le 2025-11-04 avec un prix temporaire, et que le cronjob du lendemain s'exécute (ciblant lui aussi le 2025-11-04), la commandeUPSERTne créera pas de doublon. Elle mettra à jour l'entrée existante avec le prix officiel de Marketstack.
2.4. L'infrastructure de production (VPS)
L'application est hébergée sur un serveur privé virtuel (VPS) sous Ubuntu, avec une architecture moderne basée sur la conteneurisation.
2.4.1. Conteneurisation (Docker)
- Service
app: un conteneur Docker, construit sur mesure via unDockerfile, qui encapsule l'application Python. L'application est servie par un serveur de production WSGI, Gunicorn, optimisé pour gérer plusieurs requêtes simultanées. - Service
db: un conteneur officiel MariaDB 10.6, assurant un environnement de base de données stable et isolé. - Orchestration : l'ensemble des services est défini, configuré et lié par Docker Compose (via le fichier
docker-compose.yml), qui agit comme le chef d'orchestre de l'infrastructure. - Persistance des données : pour garantir qu'aucune donnée de la base de données ne soit perdue lors des mises à jour ou des redémarrages, les fichiers de MariaDB sont stockés dans un volume Docker nommé, qui est indépendant du cycle de vie du conteneur.
2.4.2. Serveur Web
Nginx est utilisé comme serveur web principal. Il joue le rôle de reverse proxy : * Il reçoit toutes les requêtes web entrantes sur les ports 80 (HTTP) et 443 (HTTPS). * Il transmet les requêtes à l'application tournant dans le conteneur Docker sur le port 8000. * Il gère la terminaison SSL/TLS, en servant les certificats gérés par Certbot pour assurer une connexion sécurisée (HTTPS).
2.5. Le système de qualité et de déploiement (CI/CD)
L'automatisation est au cœur du projet, gérée par une pipeline de CI/CD hébergée sur GitHub Actions.
2.5.1. Workflow (deploy.yml)
- Déclencheur : le workflow est automatiquement lancé à chaque
pushsur la branchemain. - Job
test(intégration continue) : avant tout déploiement, le code est récupéré dans un environnement Linux éphémère. Une suite de tests complète est exécutée avec Pytest. Pour garantir une validation réaliste, ce job lance son propre service de base de données MariaDB temporaire. Si un seul test échoue, toute la pipeline s'arrête. -
Job
deploy(déploiement continu) : uniquement si le jobtestréussit, le workflow se connecte au VPS de production via SSH. Il exécute ensuite un script qui orchestre le déploiement :git pullpour récupérer le code validé.docker compose up -d --buildpour reconstruire l'image de l'application avec le nouveau code et redémarrer les services sans interruption majeure.
2.5.2. Stratégie de Tests
- Framework : Pytest, pour sa simplicité et sa puissance, ainsi que
pytest-mockpour simuler les appels externes. - Environnements de Test : Utilisation d'une base de données SQLite en mémoire pour les tests locaux rapides, et d'une vraie base de données MariaDB en CI pour une fiabilité maximale.
- Philosophie TDD : Le développement suit une approche de Test-Driven Development.
-
Gestion et Traçabilité des Tests : Le projet inclut un système de gestion des tests "docs-as-code" :
- Chaque cas de test est un fichier Markdown stocké dans le répertoire
test_cases/. - Un script Python (
scripts/sync_tests.py) assure la traçabilité en liant chaque cas de test à son implémentationpytestvia un marqueur@pytest.mark.test_id. - Un site web de rapport de couverture fonctionnelle est généré automatiquement avec MkDocs, fournissant une vue claire du statut de validation de chaque fonctionnalité.
- Chaque cas de test est un fichier Markdown stocké dans le répertoire