Contexte et objectifs de l'application CuisineWeek
CuisineWeek est une application mobile Android développée en Kotlin dans le cadre du BTS SIO option SLAM. Elle permet à l'utilisateur de consulter des recettes de cuisine, de planifier ses repas à la semaine et de générer automatiquement une liste de courses à partir du menu planifié.
Fonctionnalités principales
Module
Fonctionnalité
Statut
Catalogue
Affichage de la liste des recettes avec RecyclerView
✅ Implémenté
Catalogue
Recherche et filtrage des recettes
🔄 Prévu Sprint 5
Menu semaine
Navigation jour par jour avec DatePicker
✅ Implémenté
Menu semaine
Ajout / suppression de repas (Midi, Soir, Matin)
✅ Implémenté
Liste courses
Scan code-barre via OpenFoodFacts
✅ Implémenté
Liste courses
Cases à cocher et partage
✅ Implémenté
Recettes perso
Création de recettes personnalisées
🔄 Prévu Sprint 5
2. Architecture MVVM
Model – View – ViewModel : séparation des responsabilités
L'application suit le pattern architectural MVVM (Model-View-ViewModel) recommandé par Google pour les applications Android. Ce pattern garantit une séparation claire entre la logique métier et l'interface utilisateur.
🖥️ VIEW — Couche présentation
MainActivity
RecettesFragment
SemaineFragment
CoursesFragment
SelectionRecetteDialog
RecetteAdapter
ArticleCoursesAdapter
⬆️ observe LiveData ⬇️ appelle fonctions
🧠 VIEWMODEL — Couche logique
RecetteViewModel
MenuViewModel
CoursesViewModel
⬆️ LiveData ⬇️ appelle fonctions suspend
📦 REPOSITORY — Couche d'accès aux données
RecetteRepository
RetrofitClient (OpenFoodFacts)
⬆️ entités ⬇️ requêtes SQL / HTTP
🗄️ DATA — Couche données
AppDatabase (Room)
RecetteDao
MenuDao
ArticleCoursesDao
OpenFoodFactsApi (Retrofit)
DatabaseSeeder
Principes appliqués
Principe
Application dans CuisineWeek
Séparation des responsabilités
Chaque couche a un rôle unique et ne connaît pas les détails des autres
Réactivité (LiveData)
L'UI se met à jour automatiquement quand les données changent en BDD
Asynchronisme (Coroutines)
Toutes les opérations BDD et réseau s'exécutent en arrière-plan (Dispatchers.IO)
Singleton (AppDatabase)
Une seule instance de la BDD dans toute l'application (pattern @Volatile + synchronized)
Injection de dépendances
Les DAOs sont injectés dans les ViewModels via AppDatabase.getDatabase()
3. Diagramme de classes
Représentation UML des classes principales et leurs relations
Entités (data/entity)
«Entity» Recette
PK id : Int
+ nom : String
+ description : String
+ tempsPrep : Int
+ tempsCuisson : Int
+ nbPersonnes : Int
+ difficulte : String
FK categorieId : Int?
+ imageUri : String?
+ estPersonnalisee : Boolean
«Entity» Categorie
PK id : Int
+ nom : String
+ icone : String
«Entity» Ingredient
PK id : Int
+ nom : String
+ uniteDefaut : String
+ categorieCourses : String
«Entity» RecetteIngredient
PK/FK recetteId : Int
PK/FK ingredientId : Int
+ quantite : Double
+ unite : String
«Entity» MenuSemaine
PK id : Int
+ semaineDu : String
+ nbPersonnes : Int
«Entity» MenuRecette
PK/FK menuId : Int
PK/FK recetteId : Int
PK jour : String
PK typeRepas : String
«Entity» ArticleCourses
PK id : Int
+ nom : String
+ quantite : String
+ codeBarres : String
+ coche : Boolean
DAOs (data/dao)
«interface» RecetteDao
getAllRecettes() : LiveData<List<Recette>>
searchRecettes(q) : LiveData<List<Recette>>
getRecetteById(id) : Recette?
getCount() : Int
insert(recette) : Long
update(recette)
delete(recette)
«interface» MenuDao
getMenuBySemaine(s) : MenuSemaine?
insertMenu(menu) : Long
insertMenuRecette(mr)
deleteMenuRecette(id,j,t)
getRecetteIdPourRepas() : Int?
getRecettesDuJour() : LiveData
«interface» ArticleCoursesDao
getAllArticles() : LiveData<List>
insert(article)
update(article)
delete(article)
deleteAll()
ViewModels (viewmodel)
RecetteViewModel
- repository : RecetteRepository
+ toutesLesRecettes : LiveData
rechercherRecettes(q)
inserer(recette)
supprimer(recette)
MenuViewModel
- menuDao : MenuDao
- recetteDao : RecetteDao
+ jourActuel : LiveData<LocalDate>
+ recetteMidi : LiveData<Recette?>
+ recetteSoir : LiveData<Recette?>
+ recetteMatin : LiveData<Recette?>
jourSuivant()
jourPrecedent()
allerALaDate(date)
ajouterRecette(id, type)
supprimerRepas(type)
chargerMenuDuJour()
CoursesViewModel
- dao : ArticleCoursesDao
+ articles : LiveData<List>
ajouterDepuisScan(barcode)
toggleCoche(article)
supprimer(article)
toutEffacer()
4. Modèle Conceptuel de Données (MCD)
Représentation Merise des entités et associations
📊 Schéma MCD disponible dans Excalidraw
Le MCD complet (entités, associations avec cardinalités, clés primaires/étrangères) a été réalisé avec Excalidraw pour un rendu propre et précis. Il représente les entités suivantes et leurs relations :
RECETTE ↔ INGREDIENT via l'association contient (0,N — 0,N) avec quantite et unite
RECETTE → CATEGORIE via l'association appartient (0,N — 1,N)
RECETTE ↔ MENU_SEMAINE via l'association planifie (0,N — 1,N) avec jour et typeRepas
ARTICLE_COURSES alimenté par l'API OpenFoodFacts (source externe)
Définition SQL des tables telle qu'implémentée avec Room
-- Générée automatiquement par Room à partir des @EntityCREATE TABLE categories (
id INTEGER PRIMARY KEY AUTOINCREMENT,
nom TEXT NOT NULL,
icone TEXT NOT NULL
);
CREATE TABLE recettes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
nom TEXT NOT NULL,
description TEXT NOT NULL,
tempsPrep INTEGER NOT NULL,
tempsCuisson INTEGER NOT NULL,
nbPersonnes INTEGER NOT NULL,
difficulte TEXT NOT NULL,
categorieId INTEGER,
imageUri TEXT,
estPersonnalisee INTEGER NOT NULL DEFAULT 0,
FOREIGN KEY (categorieId) REFERENCES categories(id) ON DELETE SET NULL
);
CREATE TABLE ingredients (
id INTEGER PRIMARY KEY AUTOINCREMENT,
nom TEXT NOT NULL,
uniteDefaut TEXT NOT NULL,
categorieCourses TEXT NOT NULL
);
CREATE TABLE recette_ingredients (
recetteId INTEGER NOT NULL,
ingredientId INTEGER NOT NULL,
quantite REAL NOT NULL,
unite TEXT NOT NULL,
PRIMARY KEY (recetteId, ingredientId),
FOREIGN KEY (recetteId) REFERENCES recettes(id) ON DELETE CASCADE,
FOREIGN KEY (ingredientId) REFERENCES ingredients(id)
);
CREATE TABLE menus_semaine (
id INTEGER PRIMARY KEY AUTOINCREMENT,
semaineDu TEXT NOT NULL,
nbPersonnes INTEGER NOT NULL DEFAULT 4
);
CREATE TABLE menu_recettes (
menuId INTEGER NOT NULL,
recetteId INTEGER NOT NULL,
jour TEXT NOT NULL,
typeRepas TEXT NOT NULL,
PRIMARY KEY (menuId, recetteId, jour, typeRepas),
FOREIGN KEY (menuId) REFERENCES menus_semaine(id) ON DELETE CASCADE,
FOREIGN KEY (recetteId) REFERENCES recettes(id)
);
CREATE TABLE articles_courses (
id INTEGER PRIMARY KEY AUTOINCREMENT,
nom TEXT NOT NULL,
quantite TEXT NOT NULL DEFAULT '',
codeBarres TEXT NOT NULL,
coche INTEGER NOT NULL DEFAULT 0
);
Requête clé — Génération liste de courses depuis le menu
-- Récupère et additionne tous les ingrédients du menu de la semaineSELECT
i.nom AS ingredient,
SUM(ri.quantite * (m.nbPersonnes / r.nbPersonnes)) AS quantite_totale,
ri.unite AS unite,
i.categorieCourses AS categorie
FROM menu_recettes mr
JOIN recettes r ON mr.recetteId = r.id
JOIN recette_ingredients ri ON r.id = ri.recetteId
JOIN ingredients i ON ri.ingredientId = i.id
JOIN menus_semaine m ON mr.menuId = m.id
WHERE m.id = :menuId
GROUP BY i.nom, ri.unite
ORDER BY i.categorieCourses, i.nom;
7. Diagramme de cas d'utilisation
Actions disponibles pour l'utilisateur (acteur unique — pas de gestion de comptes)
🍳 Module Recettes
Consulter la liste des recettes
Rechercher une recette par nom
Filtrer par catégorie
Consulter le détail d'une recette
Créer une recette personnalisée
Modifier une recette personnalisée
Supprimer une recette personnalisée
📅 Module Menu Semaine
Naviguer entre les jours
Sélectionner une date via DatePicker
Ajouter une recette au midi
Ajouter une recette au soir
Ajouter une recette au matin
Changer une recette planifiée
Supprimer un repas planifié
🛒 Module Courses
Scanner un code-barre produit
Ajouter un produit à la liste
Cocher un article acheté
Supprimer un article
Vider toute la liste
Partager la liste (Android Intent)
8. Flux de données — Scan code-barre
Séquence complète d'un scan jusqu'à l'affichage dans la liste
1. UIClic bouton Scanner (FAB)
→
2. ZXingCaméra ouverte, scan code-barre
→
3. ViewModelajouterDepuisScan(barcode)
→
4. RetrofitGET /product/{barcode}.json
→
5. APIOpenFoodFacts répond en JSON
→
6. GsonDésérialisation → OpenFoodFactsResponse
→
7. DAOinsert(ArticleCourses)
→
8. LiveDataMise à jour automatique → RecyclerView
Flux de données — Ajout d'une recette au menu
1. UIClic "+ Ajouter un repas"
→
2. DialogSelectionRecetteDialog s'ouvre
→
3. UIClic sur une recette
→
4. ViewModelajouterRecette(id, typeRepas)
→
5. DAOgetOuCreerMenuId()
→
6. DAOinsertMenuRecette()
→
7. LiveDatarecetteMidi/Soir/Matin mis à jour
→
8. UINom de la recette affiché
9. Intégration API OpenFoodFacts
Documentation de l'intégration de l'API externe
Endpoint utilisé
GET https://world.openfoodfacts.org/api/v0/product/{barcode}.json
-- Exemple :
GET https://world.openfoodfacts.org/api/v0/product/3017620422003.json