feat(watch): remonte les séances de renfo pour la complétion auto de la routine

Pendant natif de coach_sportif c12caef. À la détection d'un HKWorkout de type
renforcement, POST /api/routine/auto-complete avec les faits bruts.

Volontairement bête côté natif : aucune règle de correspondance ici. Fenêtre de
12 h depuis l'envoi explicite, tolérance de durée, choix du bloc — tout vit
côté serveur et reste donc ajustable sans rebuild iOS.

NON COMPILÉ — à inclure au prochain build Xcode.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Sylvain Bettinelli
2026-08-03 16:13:02 +00:00
parent d5e42bf662
commit 21d819a52b

View File

@@ -335,7 +335,48 @@ public class CoachWorkoutObserverPlugin: CAPPlugin, CAPBridgedPlugin {
}
}
/// Remonte au serveur la fin d'une séance de renfo, qui décide seul si elle
/// correspond à une routine poussée dans l'app Exercice.
///
/// Volontairement bête : on n'envoie que des faits bruts (type, durée). Les
/// règles de correspondance fenêtre de 12 h depuis l'envoi explicite,
/// tolérance de durée vivent dans `/api/routine/auto-complete`, donc
/// ajustables sans rebuild iOS.
private func reportToRoutineIfStrength(_ workout: HKWorkout) {
let activity = workout.workoutActivityType
guard activity == .functionalStrengthTraining
|| activity == .traditionalStrengthTraining else { return }
guard let url = URL(string: "https://coach.hypnotruck.ch/api/routine/auto-complete") else { return }
var req = URLRequest(url: url)
req.httpMethod = "POST"
req.setValue("application/json", forHTTPHeaderField: "Content-Type")
req.setValue("coach_web_token=\(CoachAuth.kCoachWebToken)", forHTTPHeaderField: "Cookie")
req.httpBody = try? JSONSerialization.data(withJSONObject: [
"activity": "functionalStrengthTraining",
"duration_min": workout.duration / 60,
])
req.timeoutInterval = 15
URLSession.shared.dataTask(with: req) { data, _, error in
if let error = error {
NSLog("[CoachWorkoutObserver] auto-complete routine: %@", error.localizedDescription)
return
}
guard let data = data,
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { return }
if (json["matched"] as? Bool) == true {
let n = (json["done"] as? [String])?.count ?? 0
NSLog("[CoachWorkoutObserver] routine '%@' cochée automatiquement (%d exercices)",
(json["moment"] as? String) ?? "?", n)
} else {
NSLog("[CoachWorkoutObserver] pas une routine (%@)", (json["reason"] as? String) ?? "?")
}
}.resume()
}
private func scheduleNotification(for workout: HKWorkout) {
reportToRoutineIfStrength(workout)
let durationMin = Int(workout.duration / 60)
var bodyParts: [String] = ["\(durationMin) min"]
if let kcal = workout.totalEnergyBurned?.doubleValue(for: .kilocalorie()), kcal > 0 {