Ajoute/modifie tests Gherkin pour couvrir les 7 sections clarifiées : 1. Algorithme recommandation (scoring intérêts nuls) : - Ajout scénarios scoring-recommandation.feature - Cas contenu géo-ancré proche avec intérêts nuls = recommandable - Comparaison scores géo vs intérêts 2. Audio-guides mode voiture (système double clic) : - Nouveau fichier systeme-double-clic-sortie.feature - Premier clic : passage mode manuel + séquence suivante - Deuxième clic <10s : sortie audio-guide - Détection hors itinéraire + reprise 3. Monétisation créateurs (soldes dormants + DAS2) : - Nouveau fichier soldes-dormants-inactifs.feature - Conservation indéfinie si actif - Emails 12/18 mois + versement forcé 18 mois + 30j - Exception soldes <10€ avec proposition don - Modification obligations-fiscales.feature - DAS2 systématique tous montants (même <1200€) 4. Skip et abonnement (neutralisation pénalités) : - Nouveau fichier skip-abonnes-neutralisation.feature - Skip <10s non-abonné : -0.5% - Skip <10s abonné : 0% (neutre) - Métriques engagement : abonnés ne pénalisent pas - Anti-raid naturel (sources non pertinentes) 5. Premium multi-devices (KISS) : - Nouveau fichier multi-devices-dernier-priorite.feature - Règle simple : dernier device prend toujours priorité - Offline connecté vs déconnecté - Détection abus post-MVP (pas automatique) 6. Mode offline (contenus supprimés) : - Nouveau fichier contenus-supprimes-pendant-offline.feature - Suppression immédiate à reconnexion - Modal si contenu en cours d'écoute - Popup récapitulative si 2+ contenus supprimés 7. Publicités (ciblage horaire + fuseaux horaires) : - Nouveau fichier ciblage-horaire-fuseaux-horaires.feature - Ciblage horaire = heure locale utilisateur - France entière = Métropole + DOM - Détection fuseau GPS/device/IP - Cas d'usage restaurant Guadeloupe, assureur national Couverture complète de toutes les règles métier clarifiées.
222 lines
10 KiB
Gherkin
222 lines
10 KiB
Gherkin
# 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
|
||
|
||
# Règle: Score géo excellent + intérêts nuls = recommandation possible (MVP)
|
||
Scénario: Contenu géo-ancré proche avec intérêts nuls reste recommandable
|
||
Étant donné qu'un contenu géo-ancré "Info trafic local" est à 100m de l'utilisateur
|
||
Et que le contenu est tagué "Actualités" et "Trafic"
|
||
Et que l'utilisateur a des jauges à 0% pour ces tags (aucun intérêt marqué)
|
||
Et que le score_geo = 1.0 (distance 100m, excellent)
|
||
Et que le score_interets = 0.0 (jauges nulles)
|
||
Et que le score_engagement = 0.6 (contenu récent, peu d'historique)
|
||
Quand l'algorithme calcule le score_final pour un contenu géo-ancré
|
||
Alors score_final = (1.0 × 0.7) + (0.0 × 0.1) + (0.6 × 0.2)
|
||
Et score_final = 0.7 + 0.0 + 0.12 = 0.82
|
||
Et le contenu peut être recommandé malgré l'intérêt nul
|
||
Et ce comportement est accepté pour MVP car:
|
||
| justification |
|
||
| Le quota 6 contenus géolocalisés/h protège du spam |
|
||
| L'info peut être utile contextuellement |
|
||
| La distinction info/divertissement est reportée post-MVP|
|
||
|
||
Scénario: Contenu géo-neutre loin avec intérêts élevés recommandé
|
||
Étant donné qu'un contenu géo-neutre "Podcast philosophie" est à 150 km
|
||
Et que le contenu est tagué "Philosophie" et "Culture"
|
||
Et que l'utilisateur a des jauges à 90% pour ces tags
|
||
Et que le score_geo = 0.25 (150 km de distance)
|
||
Et que le score_interets = 0.9 (jauges élevées)
|
||
Et que le score_engagement = 0.7
|
||
Quand l'algorithme calcule le score_final pour un contenu géo-neutre
|
||
Alors score_final = (0.25 × 0.2) + (0.9 × 0.6) + (0.7 × 0.2)
|
||
Et score_final = 0.05 + 0.54 + 0.14 = 0.73
|
||
Et le contenu est bien recommandé grâce aux intérêts élevés
|
||
|
||
Scénario: Comparaison scores - géo proche vs intérêts élevés
|
||
Étant donné deux contenus:
|
||
| contenu | type | distance | score_geo | tags | jauges_user | score_interets | score_engagement |
|
||
| Info trafic locale | Géo-ancré | 100m | 1.0 | Trafic | 0% | 0.0 | 0.6 |
|
||
| Podcast philosophie | Géo-neutre | 150 km | 0.25 | Philosophie | 90% | 0.9 | 0.7 |
|
||
Quand l'algorithme calcule les scores finaux
|
||
Alors score_final("Info trafic locale") = 0.82
|
||
Et score_final("Podcast philosophie") = 0.73
|
||
Et "Info trafic locale" sera proposé avant "Podcast philosophie"
|
||
Et les deux contenus sont recommandables selon leurs critères différents
|