GitHub Actions commited on
Commit
efafb1e
·
0 Parent(s):

Deploy from GitHub Actions

Browse files
Files changed (3) hide show
  1. README.md +14 -0
  2. app.py +131 -0
  3. requirements.txt +2 -0
README.md ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Projet 10 - Content Recommandation
3
+ emoji: 📚
4
+ colorFrom: blue
5
+ colorTo: indigo
6
+ sdk: streamlit
7
+ sdk_version: "1.32.0"
8
+ app_file: app.py
9
+ pinned: false
10
+ ---
11
+
12
+ # Système de Recommandation d'Articles
13
+ Cette application présente les recommandations issues d'un filtrage collaboratif et d'un modèle content-based.
14
+ L'application communique avec une API Google Cloud Functions.
app.py ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ My Content — interface de démonstration du système de recommandation.
3
+
4
+ Front Streamlit hébergé sur Hugging Face Spaces. Il n'embarque aucune logique
5
+ de recommandation : il appelle la Cloud Function serverless et affiche le top 5.
6
+ """
7
+
8
+ import os
9
+ import time
10
+
11
+ import requests
12
+ import streamlit as st
13
+
14
+ # Par défaut, l'API déployée. La variable API_URL permet de viser l'API locale
15
+ # (functions-framework, voir le README) sans modifier le code.
16
+ CLOUD_FUNCTION_URL = os.environ.get(
17
+ "API_URL",
18
+ "https://us-central1-projet-10-498214.cloudfunctions.net/api-recommandation",
19
+ )
20
+
21
+ MODELS = {
22
+ "Filtrage collaboratif (ALS)": True,
23
+ "Content-based (embeddings)": False,
24
+ }
25
+
26
+ MODEL_LABELS = {
27
+ "collaborative_filtering": "Filtrage collaboratif (ALS)",
28
+ "content_based": "Content-based (embeddings)",
29
+ "popularity_fallback": "Popularité (repli cold start)",
30
+ }
31
+
32
+ st.set_page_config(
33
+ page_title="My Content — Recommandations",
34
+ page_icon="📚",
35
+ layout="centered",
36
+ )
37
+
38
+ st.title("📚 My Content")
39
+ st.caption("Système de recommandation d'articles — MVP serverless")
40
+ st.divider()
41
+
42
+ col1, col2 = st.columns(2)
43
+
44
+ with col1:
45
+ st.markdown("**👤 Utilisateur**")
46
+ selected_user = st.number_input(
47
+ "Identifiant utilisateur",
48
+ min_value=0,
49
+ max_value=99_999_999,
50
+ value=0,
51
+ step=1,
52
+ help="176 840 utilisateurs sont couverts par les matrices pré-calculées.",
53
+ )
54
+
55
+ with col2:
56
+ st.markdown("**⚙️ Modèle**")
57
+ model_choice = st.radio(
58
+ "Algorithme de recommandation",
59
+ options=list(MODELS.keys()),
60
+ index=0,
61
+ )
62
+
63
+ st.divider()
64
+
65
+ if st.button("🔍 Obtenir les 5 recommandations", type="primary", use_container_width=True):
66
+ params = {
67
+ "user_id": int(selected_user),
68
+ "is_collaborative": str(MODELS[model_choice]).lower(),
69
+ }
70
+
71
+ with st.spinner("Appel de la fonction serverless..."):
72
+ started = time.perf_counter()
73
+ try:
74
+ response = requests.get(CLOUD_FUNCTION_URL, params=params, timeout=20)
75
+ except requests.exceptions.RequestException as exc:
76
+ st.error(f"Impossible de joindre l'API : {exc}")
77
+ st.stop()
78
+ elapsed_ms = (time.perf_counter() - started) * 1000
79
+
80
+ if response.status_code != 200:
81
+ st.error(f"Erreur de l'API (code {response.status_code}) : {response.text}")
82
+ st.stop()
83
+
84
+ data = response.json()
85
+ model_used = data.get("model", "inconnu")
86
+ articles = data.get("articles") or [
87
+ {"article_id": a} for a in data.get("recommendations", [])
88
+ ]
89
+
90
+ if data.get("cold_start"):
91
+ st.warning(
92
+ f"Utilisateur **{selected_user}** inconnu du système : aucun historique "
93
+ "disponible. Repli automatique sur les articles les plus lus "
94
+ "(stratégie cold start)."
95
+ )
96
+ else:
97
+ st.success(f"Recommandations générées via **{MODEL_LABELS.get(model_used, model_used)}**")
98
+
99
+ st.caption(f"Réponse de l'API en {elapsed_ms:.0f} ms")
100
+
101
+ st.markdown(f"#### Top 5 pour l'utilisateur {selected_user}")
102
+
103
+ for rank, article in enumerate(articles, start=1):
104
+ left, right = st.columns([1, 4])
105
+ with left:
106
+ st.metric("Rang", f"#{rank}")
107
+ with right:
108
+ st.markdown(f"**Article `{article['article_id']}`**")
109
+ details = []
110
+ if "category_id" in article:
111
+ details.append(f"Catégorie `{article['category_id']}`")
112
+ if "words_count" in article:
113
+ details.append(f"{article['words_count']} mots")
114
+ if details:
115
+ st.caption(" · ".join(details))
116
+
117
+ categories = [a["category_id"] for a in articles if "category_id" in a]
118
+ if categories:
119
+ st.caption(
120
+ f"Diversité de la sélection : {len(set(categories))} catégorie(s) "
121
+ f"distincte(s) sur {len(categories)} articles."
122
+ )
123
+
124
+ with st.expander("Réponse brute de l'API"):
125
+ st.json(data)
126
+
127
+ st.divider()
128
+ st.caption(
129
+ "Front Streamlit sur Hugging Face Spaces · API Google Cloud Functions (gen2) · "
130
+ "Données Globo.com"
131
+ )
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ requests
2
+ streamlit