K2MAR commited on
Commit
e1d3c92
·
1 Parent(s): 0f7dfed

feat: Notifications complètes - rappels examens + détection nouveautés

Browse files

- Rappels examens: 7j, 5j, 3j, 2j, 1j, 1h avant
- Détection nouveaux projets/groupes automatique
- Route check-exam-reminders pour rappels multi-jours
- Route check-new-items pour polling des nouveautés
- Timestamp lastCheck pour éviter doublons

Files changed (1) hide show
  1. app.py +202 -1
app.py CHANGED
@@ -194,7 +194,9 @@ def home():
194
  'new-groups': 'POST /api/notifications/new-groups (token requis)',
195
  'group-assignment': 'POST /api/notifications/group-assignment (token requis)',
196
  'check-deadlines': 'POST /api/notifications/check-deadlines (token requis)',
197
- 'check-examens': 'POST /api/notifications/check-examens (token requis)'
 
 
198
  }
199
  }
200
  })
@@ -1362,6 +1364,205 @@ def check_upcoming_examens():
1362
  'message': str(e)
1363
  }), 500
1364
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1365
  # ==================== DÉMARRAGE DU SERVEUR ====================
1366
 
1367
  if __name__ == '__main__':
 
194
  'new-groups': 'POST /api/notifications/new-groups (token requis)',
195
  'group-assignment': 'POST /api/notifications/group-assignment (token requis)',
196
  'check-deadlines': 'POST /api/notifications/check-deadlines (token requis)',
197
+ 'check-examens': 'POST /api/notifications/check-examens (token requis)',
198
+ 'check-exam-reminders': 'POST /api/notifications/check-exam-reminders (token requis)',
199
+ 'check-new-items': 'POST /api/notifications/check-new-items (token requis)'
200
  }
201
  }
202
  })
 
1364
  'message': str(e)
1365
  }), 500
1366
 
1367
+ @app.route('/api/notifications/check-exam-reminders', methods=['POST'])
1368
+ @token_required
1369
+ def check_exam_reminders():
1370
+ """Vérifier les examens à venir et envoyer rappels (7j, 5j, 3j, 2j, 1j)"""
1371
+ try:
1372
+ # Récupérer l'emploi du temps depuis Firebase
1373
+ emploi_response = requests.get(f'{FIREBASE_DB_URL}/emploi_du_temps.json')
1374
+
1375
+ if emploi_response.status_code != 200:
1376
+ return jsonify({
1377
+ 'success': False,
1378
+ 'message': 'Erreur récupération emploi du temps'
1379
+ }), 500
1380
+
1381
+ emploi_data = emploi_response.json()
1382
+
1383
+ if not emploi_data or not emploi_data.get('semaines'):
1384
+ return jsonify({
1385
+ 'success': True,
1386
+ 'message': 'Aucun emploi du temps',
1387
+ 'reminders': []
1388
+ }), 200
1389
+
1390
+ now = datetime.now()
1391
+ reminders = []
1392
+
1393
+ # Jours à checker : 7, 5, 3, 2, 1
1394
+ days_to_check = [7, 5, 3, 2, 1]
1395
+
1396
+ # Parcourir toutes les semaines
1397
+ for semaine in emploi_data.get('semaines', []):
1398
+ for jour_id, cours_list in semaine.get('cours_par_jour', {}).items():
1399
+ for cours in cours_list:
1400
+ # Vérifier si c'est un examen
1401
+ cours_type = cours.get('details', ['', '', '', '', ''])[4] if cours.get('details') else ''
1402
+
1403
+ if cours_type.lower() not in ['examen', 'evaluation', 'évaluation', 'test', 'contrôle']:
1404
+ continue
1405
+
1406
+ # Extraire la date (format: "lun 03 mars")
1407
+ date_str = cours.get('date', '')
1408
+
1409
+ try:
1410
+ # Parser la date complète avec l'année actuelle
1411
+ # Format: "lun 03 mars"
1412
+ parts = date_str.split()
1413
+ if len(parts) >= 3:
1414
+ day_num = int(parts[1])
1415
+ month_name = parts[2].lower()
1416
+
1417
+ # Map mois français
1418
+ mois = {
1419
+ 'janvier': 1, 'février': 2, 'mars': 3, 'avril': 4,
1420
+ 'mai': 5, 'juin': 6, 'juillet': 7, 'août': 8,
1421
+ 'septembre': 9, 'octobre': 10, 'novembre': 11, 'décembre': 12
1422
+ }
1423
+
1424
+ month_num = mois.get(month_name)
1425
+ if not month_num:
1426
+ continue
1427
+
1428
+ # Créer la date de l'examen
1429
+ year = now.year
1430
+ examen_date = datetime(year, month_num, day_num)
1431
+
1432
+ # Si la date est passée, essayer l'année prochaine
1433
+ if examen_date < now:
1434
+ examen_date = datetime(year + 1, month_num, day_num)
1435
+
1436
+ # Calculer jours restants
1437
+ days_until = (examen_date - now).days
1438
+
1439
+ # Vérifier si on doit envoyer un rappel
1440
+ if days_until in days_to_check:
1441
+ cours_nom = cours.get('details', [''])[0] if cours.get('details') else 'Examen'
1442
+ salle = cours.get('details', ['', '', ''])[2] if cours.get('details') and len(cours.get('details', [])) > 2 else ''
1443
+ horaire = cours.get('horaire', '')
1444
+
1445
+ reminders.append({
1446
+ 'type': cours_type,
1447
+ 'nom': cours_nom,
1448
+ 'date': date_str,
1449
+ 'horaire': horaire,
1450
+ 'salle': salle,
1451
+ 'daysRemaining': days_until
1452
+ })
1453
+
1454
+ except Exception as e:
1455
+ print(f"⚠️ Erreur parsing date {date_str}: {e}")
1456
+ continue
1457
+
1458
+ return jsonify({
1459
+ 'success': True,
1460
+ 'reminders': reminders
1461
+ }), 200
1462
+
1463
+ except Exception as e:
1464
+ print(f"❌ Erreur vérification rappels examens: {e}")
1465
+ return jsonify({
1466
+ 'success': False,
1467
+ 'message': str(e)
1468
+ }), 500
1469
+
1470
+ @app.route('/api/notifications/check-new-items', methods=['POST'])
1471
+ @token_required
1472
+ def check_new_items():
1473
+ """Détecter les nouveaux projets et groupes créés depuis dernière vérification"""
1474
+ try:
1475
+ data = request.get_json()
1476
+ last_check = data.get('lastCheck') # Timestamp ISO
1477
+
1478
+ if not last_check:
1479
+ return jsonify({
1480
+ 'success': False,
1481
+ 'message': 'lastCheck requis'
1482
+ }), 400
1483
+
1484
+ last_check_time = datetime.fromisoformat(last_check.replace('Z', '+00:00'))
1485
+
1486
+ new_projets = []
1487
+ new_groupes = []
1488
+
1489
+ # 1. Vérifier nouveaux projets
1490
+ projets_response = requests.get(f'{FIREBASE_DB_URL}/projets.json')
1491
+ if projets_response.status_code == 200:
1492
+ all_projets = projets_response.json()
1493
+
1494
+ if all_projets:
1495
+ for projet_id, projet_data in all_projets.items():
1496
+ if not projet_data or not projet_data.get('createdAt'):
1497
+ continue
1498
+
1499
+ try:
1500
+ created_at = datetime.fromisoformat(projet_data['createdAt'].replace('Z', '+00:00'))
1501
+
1502
+ if created_at > last_check_time:
1503
+ new_projets.append({
1504
+ 'projetId': projet_id,
1505
+ 'subjectName': projet_data.get('subjectName', 'Projet'),
1506
+ 'deadline': projet_data.get('deadline', ''),
1507
+ 'profName': projet_data.get('profName', ''),
1508
+ 'createdAt': projet_data.get('createdAt')
1509
+ })
1510
+ except Exception as e:
1511
+ print(f"⚠️ Erreur parsing projet {projet_id}: {e}")
1512
+ continue
1513
+
1514
+ # 2. Vérifier nouveaux groupes
1515
+ groupes_response = requests.get(f'{FIREBASE_DB_URL}/groupes.json')
1516
+ if groupes_response.status_code == 200:
1517
+ all_groupes = groupes_response.json()
1518
+
1519
+ if all_groupes:
1520
+ for batch_id, batch_data in all_groupes.items():
1521
+ if not batch_data or not batch_data.get('createdAt'):
1522
+ continue
1523
+
1524
+ try:
1525
+ created_at = datetime.fromisoformat(batch_data['createdAt'].replace('Z', '+00:00'))
1526
+
1527
+ if created_at > last_check_time:
1528
+ # Vérifier si l'utilisateur actuel est dans un des groupes
1529
+ user_email = request.current_user.get('email')
1530
+ user_groups = []
1531
+
1532
+ for groupe in batch_data.get('groups', []):
1533
+ for member in groupe.get('members', []):
1534
+ if member.get('email') == user_email:
1535
+ user_groups.append({
1536
+ 'groupName': groupe.get('name', 'Groupe'),
1537
+ 'members': [f"{m.get('firstName', '')} {m.get('lastName', '')}" for m in groupe.get('members', [])]
1538
+ })
1539
+ break
1540
+
1541
+ if user_groups:
1542
+ new_groupes.append({
1543
+ 'batchId': batch_id,
1544
+ 'subjectName': batch_data.get('subjectName', 'Projet'),
1545
+ 'deadline': batch_data.get('deadline', ''),
1546
+ 'userGroups': user_groups,
1547
+ 'createdAt': batch_data.get('createdAt')
1548
+ })
1549
+ except Exception as e:
1550
+ print(f"⚠️ Erreur parsing groupe {batch_id}: {e}")
1551
+ continue
1552
+
1553
+ return jsonify({
1554
+ 'success': True,
1555
+ 'newProjets': new_projets,
1556
+ 'newGroupes': new_groupes
1557
+ }), 200
1558
+
1559
+ except Exception as e:
1560
+ print(f"❌ Erreur vérification nouveautés: {e}")
1561
+ return jsonify({
1562
+ 'success': False,
1563
+ 'message': str(e)
1564
+ }), 500
1565
+
1566
  # ==================== DÉMARRAGE DU SERVEUR ====================
1567
 
1568
  if __name__ == '__main__':