Files
roadwave/features/api/recommendation/scoring-recommandation.feature
jpgiannetti 37c62206ad feat(bdd): réorganiser features en catégories api/ui/e2e et créer ADR-024
Résolution des incohérences #10, #11, et #12 de l'analyse d'architecture.

## Phase 1 : Réorganisation Features BDD (Point #10 - RÉSOLU)

- Créer structure features/{api,ui,e2e}
- Déplacer 83 features en 3 catégories via git mv (historique préservé)
  - features/api/ : 53 features (tests API backend)
  - features/ui/ : 22 features (tests UI mobile)
  - features/e2e/ : 8 features (tests end-to-end)

Domaines déplacés :
- API : authentication, recommendation, rgpd-compliance, content-creation,
  moderation, monetisation, premium, radio-live, publicites
- UI : audio-guides, navigation, interest-gauges, mode-offline,
  partage, profil, recherche
- E2E : abonnements, error-handling

## Phase 2 : Mise à jour Documentation

### ADR-007 - Tests BDD
- Ajouter section "Convention de Catégorisation des Features"
- Documenter règles api/ui/e2e avec exemples concrets
- Spécifier step definitions (backend Go, mobile Dart)

### ADR-024 - Stratégie CI/CD Monorepo (NOUVEAU)
- Créer ADR dédié pour stratégie CI/CD avec path filters
- Architecture workflows séparés (backend.yml, mobile.yml, shared.yml)
- Configuration path filters détaillée avec exemples YAML
- Matrice de déclenchement et optimisations (~70% gain temps CI)
- Plan d'implémentation (~2h, reporté jusqu'au développement)

### ADR-016 - Organisation Monorepo
- Simplifier en retirant section CI/CD détaillée
- Ajouter référence vers ADR-024 pour stratégie CI/CD

### INCONSISTENCIES-ANALYSIS.md
- Point #10 (Tests BDD synchronisés) :  RÉSOLU
  - Catégorisation features implémentée
  - ADR-007 mis à jour avec convention complète
- Point #11 (70/30 Split paiements) :  ANNULÉ (faux problème)
  - ADR-009 et Règle 18 parfaitement cohérents
  - Documentation exhaustive existante (formule, SQL, comparaisons)
- Point #12 (Monorepo path filters) : ⏸️ DOCUMENTÉ
  - Architecture CI/CD complète dans ADR-024
  - Implémentation reportée (projet en phase documentation)
- Métriques mises à jour :
  - MODERATE : 6/9 traités (4 résolus + 1 annulé + 1 documenté)
  - ADR à jour : 100% (19/19 avec ADR-024)

## Phase 3 : Validation

- Structure features validée (api/ui/e2e, aucun répertoire restant)
- Historique Git préservé (git mv, renommages détectés)
- 83 features total (API: 53, UI: 22, E2E: 8)

Closes: Point #10 (résolu), Point #11 (annulé), Point #12 (documenté)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-01 11:31:41 +01:00

181 lines
7.7 KiB
Gherkin
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# language: fr
Fonctionnalité: Formule de scoring et recommandation
En tant que système de recommandation
Je veux calculer un score combiné pour chaque contenu
Afin de proposer les contenus les plus pertinents à l'utilisateur
Contexte:
Étant donné que l'API RoadWave est disponible
Scénario: Calcul du score géographique linéaire
Étant donné qu'un contenu existe à Paris
Et que la distance_max_km est configurée à 200 km
Quand un utilisateur est à 50 km du contenu
Alors le score_geo = 1 - (50 / 200) = 0.75
Scénario: Score géo à distance nulle (sur place)
Étant donné qu'un contenu existe à un point GPS précis
Quand un utilisateur est exactement au même point (0 km)
Alors le score_geo = 1.0 (maximum)
Scénario: Score géo à distance_max (200 km)
Étant donné qu'un contenu existe à Paris
Quand un utilisateur est à 200 km du contenu
Alors le score_geo = 1 - (200 / 200) = 0.0
Scénario: Score géo au-delà de distance_max
Étant donné qu'un contenu existe à Paris
Quand un utilisateur est à 250 km du contenu (au-delà de 200 km max)
Alors le score_geo = 0.0 (minimum)
Et le contenu a peu de chances d'être recommandé sauf engagement très élevé
Scénario: Calcul du score d'intérêts avec jauges utilisateur
Étant donné qu'un utilisateur a les jauges suivantes:
| categorie | niveau |
| Automobile | 80% |
| Voyage | 60% |
| Musique | 40% |
Et qu'un contenu est tagué "Automobile" et "Voyage"
Quand l'algorithme calcule le score_interets
Alors score_interets = (0.8 + 0.6) / 2 = 0.7
Scénario: Score d'intérêts avec un seul tag
Étant donné qu'un utilisateur a la jauge "Économie" à 90%
Et qu'un contenu est tagué uniquement "Économie"
Quand l'algorithme calcule le score_interets
Alors score_interets = 0.9
Scénario: Score d'intérêts avec tags non matchés
Étant donné qu'un utilisateur a des jauges "Sport" et "Politique" élevées
Et qu'un contenu est tagué "Musique" et "Philosophie"
Et que l'utilisateur n'a pas ces catégories
Quand l'algorithme calcule le score_interets
Alors score_interets = 0.5 (neutre par défaut pour catégories inconnues)
Scénario: Calcul du score d'engagement avec métriques
Étant donné qu'un contenu a:
| metrique | valeur |
| ecoutes | 1000 |
| ecoutes_completes | 700 |
| likes | 300 |
| abonnements_apres | 50 |
Quand l'algorithme calcule le score_engagement
Alors taux_completion = 700 / 1000 = 0.7
Et ratio_likes = 300 / 1000 = 0.3
Et ratio_abonnements = 50 / 1000 = 0.05
Et score_engagement = (0.7 × 0.5) + (0.3 × 0.3) + (0.05 × 0.2) = 0.35 + 0.09 + 0.01 = 0.45
Scénario: Contenu avec moins de 50 écoutes - score neutre
Étant donné qu'un contenu a seulement 30 écoutes
Quand l'algorithme calcule le score_engagement
Alors score_engagement = 0.5 (neutre par défaut)
Et le contenu n'est pas pénalisé pour manque de données
Scénario: Contenu avec exactement 50 écoutes - calcul réel
Étant donné qu'un contenu a exactement 50 écoutes
Et des métriques d'engagement complètes
Quand l'algorithme calcule le score_engagement
Alors le score est calculé normalement (pas de seuil neutre)
Scénario: Bonus aléatoire - 10% des recommandations
Étant donné qu'un utilisateur demande 10 recommandations
Et que la part_aleatoire_global est à 10%
Quand l'algorithme génère les recommandations
Alors 1 contenu sur 10 est tiré aléatoirement
Et 9 contenus sont calculés avec le score combiné
Et le contenu aléatoire n'est pas dans l'historique déjà écouté
Scénario: Curseur utilisateur découverte à 0% - aucun aléatoire
Étant donné qu'un utilisateur configure le curseur découverte à 0%
Quand l'utilisateur demande 20 recommandations
Alors les 20 contenus sont calculés avec le score combiné
Et aucun contenu aléatoire n'est proposé
Scénario: Curseur utilisateur découverte à 50% - découverte max
Étant donné qu'un utilisateur configure le curseur découverte à 50%
Quand l'utilisateur demande 20 recommandations
Alors 10 contenus sont tirés aléatoirement
Et 10 contenus sont calculés avec le score combiné
Scénario: Score final combiné pour contenu géo-ancré
Étant donné qu'un contenu "Géo-ancré" a:
| parametre | valeur |
| score_geo | 0.9 |
| score_interets | 0.6 |
| score_engagement | 0.45 |
| poids_geo | 0.7 |
| poids_interets | 0.1 |
| poids_engagement | 0.2 |
Quand l'algorithme calcule le score_final
Alors score_final = (0.9 × 0.7) + (0.6 × 0.1) + (0.45 × 0.2)
Et score_final = 0.63 + 0.06 + 0.09 = 0.78
Scénario: Score final combiné pour contenu géo-neutre
Étant donné qu'un contenu "Géo-neutre" a:
| parametre | valeur |
| score_geo | 0.3 |
| score_interets | 0.9 |
| score_engagement | 0.6 |
| poids_geo | 0.2 |
| poids_interets | 0.6 |
| poids_engagement | 0.2 |
Quand l'algorithme calcule le score_final
Alors score_final = (0.3 × 0.2) + (0.9 × 0.6) + (0.6 × 0.2)
Et score_final = 0.06 + 0.54 + 0.12 = 0.72
Et le contenu peut être recommandé malgré la distance
Scénario: Contenu viral lointain peut être recommandé
Étant donné qu'un contenu viral existe à Paris
Et qu'il a un score_engagement très élevé de 0.95
Et qu'un utilisateur est à Marseille (score_geo = 0.1)
Quand l'algorithme calcule le score_final
Alors le score_engagement élevé compense le score_geo faible
Et le contenu peut apparaître dans les recommandations
Scénario: Ordre de recommandation par score décroissant
Étant donné 5 contenus avec les scores suivants:
| contenu | score_final |
| Contenu A | 0.85 |
| Contenu B | 0.72 |
| Contenu C | 0.90 |
| Contenu D | 0.65 |
| Contenu E | 0.78 |
Quand l'utilisateur demande des recommandations
Alors l'ordre de proposition est:
| position | contenu |
| 1 | Contenu C |
| 2 | Contenu A |
| 3 | Contenu E |
| 4 | Contenu B |
| 5 | Contenu D |
Scénario: Exclusion de l'historique déjà écouté >80%
Étant donné qu'un utilisateur a écouté les contenus suivants:
| contenu | completion |
| Contenu A | 85% |
| Contenu B | 95% |
| Contenu C | 30% |
Quand l'algorithme génère les recommandations
Alors "Contenu A" et "Contenu B" ne sont jamais proposés
Mais "Contenu C" peut être reproposé
Scénario: Pré-calcul de 5 contenus suivants
Étant donné qu'un utilisateur écoute un contenu
Quand l'algorithme prépare les contenus suivants
Alors 5 contenus sont pré-calculés selon le score
Et ces contenus sont mis en cache pour performance
Scénario: Recalcul si déplacement >10 km
Étant donné que 5 contenus suivants sont pré-calculés
Et que l'utilisateur se déplace de 12 km
Quand l'utilisateur demande le contenu suivant
Alors l'algorithme recalcule les scores avec la nouvelle position
Et propose de nouveaux contenus plus pertinents géographiquement
Scénario: Recalcul après 10 minutes d'inactivité
Étant donné que 5 contenus suivants sont pré-calculés
Et que 11 minutes se sont écoulées sans action
Quand l'utilisateur demande le contenu suivant
Alors l'algorithme recalcule les scores
Et prend en compte les nouveaux contenus publiés