kettlebell-tracker/internal/services/training.go

355 lines
9.4 KiB
Go

// package services
//
// import (
//
// "log"
// "time"
//
// "git.patanix.de/git/kettlebell-app/internal/data"
//
// )
//
// // TrainingState hält den aktuellen Zustand einer laufenden Trainingseinheit.
//
// type TrainingState struct {
// IsTrainingRunning bool
// RemainingSeconds int
// InitialDurationSeconds int
// SetsDone int
// GoalSets int
// RepsPerSet int
// SetTimes []time.Time
// Progress float64
// SecondsSinceLastSet int
// LastSetTimestamp *time.Time
// CurrentProgram string
// CurrentBlockDay int
// CurrentReps int
// TotalTrainingDays int
// }
//
// func NewTrainingState() *TrainingState {
// return &TrainingState{
// IsTrainingRunning: false,
// RemainingSeconds: 0,
// InitialDurationSeconds: 0,
// SetsDone: 0,
// GoalSets: 5,
// RepsPerSet: 5,
// Progress: 0.0,
// SecondsSinceLastSet: 0,
// LastSetTimestamp: nil,
// CurrentProgram: "giant_1.0",
// CurrentBlockDay: 1,
// CurrentReps: 5,
// TotalTrainingDays: 0,
// SetTimes: []time.Time{},
// }
// }
//
// type TrainingService struct {
// State *TrainingState
// dbService *data.DatabaseService
// settingsService *SettingsService
// }
//
// func NewTrainingService(db *data.DatabaseService, settings *SettingsService) *TrainingService {
// initialState := NewTrainingState()
// trainingCount, err := db.GetTrainingCount()
// if err != nil {
// log.Printf("Fehler beim Abrufen der Trainingsanzahl, setze auf 0: %v", err)
// initialState.TotalTrainingDays = 0
// } else {
// initialState.TotalTrainingDays = trainingCount
// }
// return &TrainingService{
// State: initialState,
// dbService: db,
// settingsService: settings,
// }
// }
//
// func (s *TrainingService) updateProgram() {
// st := s.State
// newTotalDays := st.TotalTrainingDays + 1
// newProgram := st.CurrentProgram
// newDay := (st.CurrentBlockDay % 3) + 1
// newReps := st.CurrentReps
//
// if newTotalDays > 0 && newTotalDays%12 == 0 {
// switch st.CurrentProgram {
// case "giant_1.0":
// newProgram = "ksk_1.0"
// case "giant_1.1":
// newProgram = "ksk_1.1"
// case "giant_1.2":
// newProgram = "ksk_1.2"
// case "ksk_1.0":
// newProgram = "giant_1.1"
// case "ksk_1.1":
// newProgram = "giant_1.2"
// case "ksk_1.2":
// newProgram = "giant_1.0"
// default:
// newProgram = "giant_1.0"
// }
// newDay = 1
// }
//
// repsMap := map[string][]int{
// "giant_1.0": {5, 6, 4},
// "giant_1.1": {6, 8, 7},
// "giant_1.2": {7, 9, 8},
// "ksk_1.0": {5, 6, 4},
// "ksk_1.1": {6, 8, 7},
// "ksk_1.2": {7, 9, 8},
// }
//
// if reps, ok := repsMap[newProgram]; ok && len(reps) >= newDay {
// newReps = reps[newDay-1]
// } else {
// newReps = 5
// }
//
// st.CurrentProgram = newProgram
// st.CurrentBlockDay = newDay
// st.CurrentReps = newReps
// st.TotalTrainingDays = newTotalDays
// }
//
// func (s *TrainingService) StartTraining(minutes, goal int) {
// s.updateProgram()
// duration := minutes * 60
// s.State = &TrainingState{
// IsTrainingRunning: true,
// InitialDurationSeconds: duration,
// RemainingSeconds: duration,
// GoalSets: goal,
// RepsPerSet: s.State.CurrentReps,
// CurrentProgram: s.State.CurrentProgram,
// CurrentBlockDay: s.State.CurrentBlockDay,
// TotalTrainingDays: s.State.TotalTrainingDays,
// SetTimes: []time.Time{},
// }
// }
//
// func (s *TrainingService) Tick() {
// if s.State.RemainingSeconds > 0 {
// s.State.RemainingSeconds--
// }
// }
//
// func (s *TrainingService) TickLastSetTimer() {
// if s.State.IsTrainingRunning && s.State.LastSetTimestamp != nil {
// s.State.SecondsSinceLastSet = int(time.Since(*s.State.LastSetTimestamp).Seconds())
// }
// }
//
// func (s *TrainingService) CompleteSet() {
// st := s.State
// st.SetsDone++
// now := time.Now()
// st.SetTimes = append(st.SetTimes, now)
// if st.GoalSets > 0 {
// st.Progress = min(float64(st.SetsDone)/float64(st.GoalSets), 1.0)
// }
// st.LastSetTimestamp = &now
// st.SecondsSinceLastSet = 0
// }
//
// func (s *TrainingService) FinishTraining(session *data.TrainingSession) error {
// session.Program = s.State.CurrentProgram
// session.BlockDay = int64(s.State.CurrentBlockDay)
//
// err := s.dbService.SaveTraining(session)
// if err != nil {
// return err
// }
//
// // Platzhalter für den API-Aufruf (aus api_service.dart)
// s.sendToBackend(session)
//
// s.ResetTraining()
// return nil
// }
//
// func (s *TrainingService) ResetTraining() {
// // Diesen Teil nochmals pruefen
// s.State = NewTrainingState()
// trainingCount, err := s.dbService.GetTrainingCount()
// if err != nil {
// log.Print("Unable to get training count")
// }
// s.State.CurrentBlockDay = trainingCount
// // Hier müsste man TotalTrainingDays wieder korrekt laden.
// }
//
// // sendToBackend ist ein Platzhalter für deinen API-Aufruf.
//
// func (s *TrainingService) sendToBackend(session *data.TrainingSession) {
// // Hier würde die Logik aus deinem `api_service.dart` hinkommen.
// // z.B. ein HTTP POST Request mit den Trainingsdaten.
// // Da der Service nicht existiert, loggen wir es nur.
// log.Println("Sende Trainingsdaten an das Backend (Platzhalter)...")
// // rest := float64(session.Duration) / float64(session.Sets)
// // log.Printf("Reps: %d, Rest: %.2f, Sets: %d", session.RepsPerSet, rest, session.Sets)
// }
package services
import (
"log"
"time"
"git.patanix.de/git/kettlebell-app/internal/data"
)
type TrainingState struct {
IsTrainingRunning bool
RemainingSeconds int
InitialDurationSeconds int
SetsDone int
GoalSets int
RepsPerSet int
SetTimes []time.Time
Progress float64
SecondsSinceLastSet int
LastSetTimestamp *time.Time
CurrentProgram string
CurrentBlockDay int
CurrentReps int
TotalTrainingDays int
}
func calculateStateByDayCount(totalDays int) (program string, blockDay, reps int) {
program = "clean_1.0"
blockDay = 1
reps = 5
if totalDays > 0 {
cycleIndex := (totalDays / 12) % 6
programs := []string{"clean_1.0", "snatch_1.0", "clean_1.1", "snatch_1.1", "clean_1.2", "snatch_1.2"}
program = programs[cycleIndex]
blockDay = (totalDays % 3) + 1
}
repsMap := map[string][]int{
"clean_1.0": {5, 6, 4},
"clean_1.1": {6, 8, 7},
"clean_1.2": {7, 9, 8},
"snatch_1.0": {5, 6, 4},
"snatch_1.1": {6, 8, 7},
"snatch_1.2": {7, 9, 8},
}
if r, ok := repsMap[program]; ok && len(r) >= blockDay {
reps = r[blockDay-1]
}
return
}
func NewTrainingState(db *data.DatabaseService) *TrainingState {
trainingCount, err := db.GetTrainingCount()
if err != nil {
log.Printf("Fehler beim Abrufen der Trainingsanzahl, setze auf 0: %v", err)
trainingCount = 0
}
program, blockDay, reps := calculateStateByDayCount(trainingCount)
return &TrainingState{
IsTrainingRunning: false,
TotalTrainingDays: trainingCount,
CurrentProgram: program,
CurrentBlockDay: blockDay,
CurrentReps: reps,
SetTimes: []time.Time{},
GoalSets: 5,
RepsPerSet: reps,
}
}
type TrainingService struct {
State *TrainingState
dbService *data.DatabaseService
settingsService *SettingsService
apiService *ApiService
}
func NewTrainingService(db *data.DatabaseService, settings *SettingsService, api *ApiService) *TrainingService {
return &TrainingService{
State: NewTrainingState(db),
dbService: db,
settingsService: settings,
apiService: api,
}
}
func (s *TrainingService) StartTraining(minutes, goal int) {
program, blockDay, reps := calculateStateByDayCount(s.State.TotalTrainingDays)
st := s.State
st.IsTrainingRunning = true
st.InitialDurationSeconds = minutes * 60
st.RemainingSeconds = st.InitialDurationSeconds
st.GoalSets = goal
st.CurrentProgram = program
st.CurrentBlockDay = blockDay
st.CurrentReps = reps
st.RepsPerSet = reps
st.SetsDone = 0
st.Progress = 0.0
st.SetTimes = []time.Time{}
st.LastSetTimestamp = nil
}
func (s *TrainingService) Tick() {
if s.State.RemainingSeconds > 0 {
s.State.RemainingSeconds--
}
}
func (s *TrainingService) TickLastSetTimer() {
if s.State.IsTrainingRunning && s.State.LastSetTimestamp != nil {
s.State.SecondsSinceLastSet = int(time.Since(*s.State.LastSetTimestamp).Seconds())
}
}
func (s *TrainingService) CompleteSet() {
st := s.State
st.SetsDone++
now := time.Now()
st.SetTimes = append(st.SetTimes, now)
if st.GoalSets > 0 {
st.Progress = min(float64(st.SetsDone)/float64(st.GoalSets), 1.0)
}
st.LastSetTimestamp = &now
st.SecondsSinceLastSet = 0
}
func (s *TrainingService) FinishTraining(session *data.TrainingSession) error {
session.Program = s.State.CurrentProgram
session.BlockDay = int64(s.State.CurrentBlockDay)
err := s.dbService.SaveTraining(session)
if err != nil {
return err
}
go s.apiService.SendTrainingData(session)
s.ResetTraining()
return nil
}
func (s *TrainingService) ResetTraining() {
s.State = NewTrainingState(s.dbService)
}
// sendToBackend ist ein Platzhalter für deinen API-Aufruf.
func (s *TrainingService) sendToBackend(session *data.TrainingSession) {
log.Println("Sende Trainingsdaten an das Backend (Platzhalter)...")
}