OUAREDAEK commited on
Commit
b825c84
·
verified ·
1 Parent(s): a0a9838

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +214 -0
app.py ADDED
@@ -0,0 +1,214 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py optimisé pour Hugging Face
2
+ from flask import Flask, render_template, request, jsonify, send_from_directory
3
+ from datetime import datetime, timedelta
4
+ import json
5
+ import os
6
+ import numpy as np
7
+ from pathlib import Path
8
+
9
+ app = Flask(__name__)
10
+
11
+ # ======================================================
12
+ # CONFIGURATION POUR HUGGING FACE
13
+ # ======================================================
14
+ BASE_DIR = Path(__file__).parent
15
+ DATA_DIR = BASE_DIR / "data"
16
+ DATA_DIR.mkdir(exist_ok=True)
17
+
18
+ # Chemins des fichiers de données
19
+ COMPETENCES_FILE = BASE_DIR / "competences.json"
20
+ JOURNAL_FILE = DATA_DIR / "journal.json"
21
+ PLANNER_FILE = DATA_DIR / "planner.json"
22
+ LEARNER_STATE_FILE = DATA_DIR / "learner_state.json"
23
+
24
+ # ======================================================
25
+ # INITIALISATION DES DONNÉES
26
+ # ======================================================
27
+
28
+ def load_json_file(file_path, default_data):
29
+ """Charge un fichier JSON ou retourne les données par défaut"""
30
+ try:
31
+ if file_path.exists():
32
+ with open(file_path, 'r', encoding='utf-8') as f:
33
+ return json.load(f)
34
+ except:
35
+ pass
36
+ return default_data
37
+
38
+ def save_json_file(file_path, data):
39
+ """Sauvegarde des données dans un fichier JSON"""
40
+ try:
41
+ with open(file_path, 'w', encoding='utf-8') as f:
42
+ json.dump(data, f, ensure_ascii=False, indent=2)
43
+ return True
44
+ except:
45
+ return False
46
+
47
+ # Charger les données
48
+ COMPETENCES = load_json_file(COMPETENCES_FILE, [
49
+ {"id": "D-MCONJ", "label": "Marques de la conjugaison", "category": "Discours"},
50
+ {"id": "M-COMP", "label": "Construction des phrases", "category": "Maîtrise"},
51
+ {"id": "P-GRAM", "label": "Orthographe grammaticale", "category": "Phrase"},
52
+ {"id": "T-ORG", "label": "Organisation textuelle", "category": "Texte"}
53
+ ])
54
+
55
+ JOURNAL = load_json_file(JOURNAL_FILE, [])
56
+ PLANNER = load_json_file(PLANNER_FILE, [])
57
+ LEARNER_STATE = load_json_file(LEARNER_STATE_FILE, {
58
+ "retention": {},
59
+ "engagement": "stable",
60
+ "risk": "low",
61
+ "days_inactive": 0,
62
+ "total_sessions": 0,
63
+ "success_rate": 0.0
64
+ })
65
+
66
+ # Intervalles SM-2 simplifiés
67
+ SM2_INTERVALS = [1, 3, 7, 14, 30, 60]
68
+
69
+ # ======================================================
70
+ # ROUTES PRINCIPALES
71
+ # ======================================================
72
+
73
+ @app.route('/')
74
+ def home():
75
+ """Page d'accueil"""
76
+ return render_template('index.html', competences=COMPETENCES)
77
+
78
+ @app.route('/api/competences')
79
+ def get_competences():
80
+ return jsonify(COMPETENCES)
81
+
82
+ @app.route('/api/planner/generate', methods=['POST'])
83
+ def generate_planner():
84
+ try:
85
+ data = request.json
86
+ competence = data.get('competence', 'Compétence')
87
+ start_date = datetime.strptime(data.get('start_date', '2026-01-01'), '%Y-%m-%d')
88
+ duration = int(data.get('session_duration', 20))
89
+ repetitions = int(data.get('repetitions', 6))
90
+
91
+ plan = []
92
+ current_date = start_date
93
+
94
+ for i in range(min(repetitions, len(SM2_INTERVALS))):
95
+ plan.append({
96
+ 'date': current_date.strftime('%Y-%m-%d'),
97
+ 'jour': current_date.strftime('%A'),
98
+ 'competence': competence,
99
+ 'repetition': i + 1,
100
+ 'duree': f'{duration} minutes',
101
+ 'intervalle': f'+{SM2_INTERVALS[i]} jours',
102
+ 'type': 'Apprentissage' if i == 0 else 'Révision espacée',
103
+ 'strategie': 'Active Recall + Élaboration'
104
+ })
105
+ current_date += timedelta(days=SM2_INTERVALS[i])
106
+
107
+ # Sauvegarder
108
+ PLANNER.extend(plan)
109
+ save_json_file(PLANNER_FILE, PLANNER)
110
+
111
+ return jsonify({'success': True, 'plan': plan})
112
+ except Exception as e:
113
+ return jsonify({'success': False, 'error': str(e)})
114
+
115
+ @app.route('/api/journal', methods=['POST'])
116
+ def save_journal():
117
+ try:
118
+ entry = request.json
119
+ entry['timestamp'] = datetime.now().isoformat()
120
+ entry['date'] = datetime.now().strftime('%Y-%m-%d %H:%M')
121
+
122
+ JOURNAL.append(entry)
123
+ save_json_file(JOURNAL_FILE, JOURNAL)
124
+
125
+ # Mettre à jour les statistiques
126
+ if 'auto_eval' in entry:
127
+ try:
128
+ eval_score = float(entry['auto_eval'])
129
+ LEARNER_STATE['total_sessions'] += 1
130
+ LEARNER_STATE['success_rate'] = (
131
+ (LEARNER_STATE['success_rate'] * (LEARNER_STATE['total_sessions'] - 1) + eval_score)
132
+ / LEARNER_STATE['total_sessions']
133
+ )
134
+ save_json_file(LEARNER_STATE_FILE, LEARNER_STATE)
135
+ except:
136
+ pass
137
+
138
+ return jsonify({'success': True, 'message': 'Journal sauvegardé'})
139
+ except Exception as e:
140
+ return jsonify({'success': False, 'error': str(e)})
141
+
142
+ @app.route('/api/notifications')
143
+ def get_notification():
144
+ messages = [
145
+ "🌱 5 minutes aujourd'hui renforcent durablement votre mémoire.",
146
+ "⏰ Une courte révision maintenant évite l'oubli.",
147
+ "💪 Votre régularité montre une vraie progression.",
148
+ "🤔 Quelle stratégie vous aide le plus aujourd'hui ?"
149
+ ]
150
+
151
+ message = np.random.choice(messages)
152
+
153
+ return jsonify({
154
+ 'message': message,
155
+ 'timestamp': datetime.now().isoformat(),
156
+ 'type': 'reminder'
157
+ })
158
+
159
+ @app.route('/api/stats')
160
+ def get_stats():
161
+ return jsonify({
162
+ 'total_sessions': LEARNER_STATE.get('total_sessions', 0),
163
+ 'success_rate': round(LEARNER_STATE.get('success_rate', 0), 2),
164
+ 'journal_entries': len(JOURNAL),
165
+ 'planned_sessions': len(PLANNER),
166
+ 'engagement': LEARNER_STATE.get('engagement', 'stable')
167
+ })
168
+
169
+ @app.route('/api/sm2/explain')
170
+ def explain_sm2():
171
+ return jsonify({
172
+ 'title': 'Algorithme SM-2',
173
+ 'description': 'Répétition espacée optimisée pour la rétention mémoire',
174
+ 'intervals': SM2_INTERVALS,
175
+ 'principles': [
176
+ 'Réviser juste avant d\'oublier',
177
+ 'Augmenter l\'intervalle progressivement',
178
+ 'Adapter selon la difficulté',
179
+ 'Être régulier dans la pratique'
180
+ ]
181
+ })
182
+
183
+ # ======================================================
184
+ # ROUTES STATIQUES (pour Hugging Face)
185
+ # ======================================================
186
+
187
+ @app.route('/static/<path:filename>')
188
+ def serve_static(filename):
189
+ return send_from_directory('static', filename)
190
+
191
+ @app.route('/favicon.ico')
192
+ def favicon():
193
+ return '', 204
194
+
195
+ # ======================================================
196
+ # LANCEMENT
197
+ # ======================================================
198
+
199
+ if __name__ == '__main__':
200
+ # Configuration pour Hugging Face Spaces
201
+ port = int(os.environ.get('PORT', 7860))
202
+ debug = os.environ.get('FLASK_DEBUG', 'False').lower() == 'true'
203
+
204
+ print(f"🚀 Démarrage de l'application SRL sur le port {port}")
205
+ print(f"📚 Compétences chargées: {len(COMPETENCES)}")
206
+ print(f"📔 Entrées journal: {len(JOURNAL)}")
207
+ print(f"📅 Sessions planifiées: {len(PLANNER)}")
208
+
209
+ app.run(
210
+ host='0.0.0.0',
211
+ port=port,
212
+ debug=debug,
213
+ use_reloader=False
214
+ )