Initial commit

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Christoph K.
2026-03-21 15:03:55 +01:00
commit dfd66e43c6
78 changed files with 6219 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
package model
import (
"errors"
"strings"
"time"
)
// Exercise repräsentiert eine Kraftübung.
type Exercise struct {
ID int64 `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
MuscleGroup string `json:"muscle_group"`
WeightStepKg float64 `json:"weight_step_kg"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt *time.Time `json:"deleted_at,omitempty"`
}
// CreateExerciseRequest enthält die Felder zum Anlegen einer Übung.
type CreateExerciseRequest struct {
Name string `json:"name"`
Description string `json:"description"`
MuscleGroup string `json:"muscle_group"`
WeightStepKg *float64 `json:"weight_step_kg"`
}
// Validate prüft und normalisiert den Request. Setzt Default für WeightStepKg.
func (r *CreateExerciseRequest) Validate() error {
r.Name = strings.TrimSpace(r.Name)
if len(r.Name) == 0 || len(r.Name) > 100 {
return errors.New("Name muss 1100 Zeichen lang sein")
}
if !ValidMuscleGroup(r.MuscleGroup) {
return errors.New("Ungültige Muskelgruppe")
}
if r.WeightStepKg != nil {
if *r.WeightStepKg <= 0 {
return errors.New("Gewichtsschritt muss > 0 sein")
}
} else {
def := 2.5
r.WeightStepKg = &def
}
return nil
}

View File

@@ -0,0 +1,19 @@
package model
import "time"
// Session repräsentiert eine Trainingseinheit.
type Session struct {
ID int64 `json:"id"`
SetID int64 `json:"set_id"`
SetName string `json:"set_name"`
StartedAt time.Time `json:"started_at"`
EndedAt *time.Time `json:"ended_at,omitempty"`
Note string `json:"note"`
Logs []SessionLog `json:"logs,omitempty"`
}
// CreateSessionRequest enthält die Felder zum Starten einer Session.
type CreateSessionRequest struct {
SetID int64 `json:"set_id"`
}

View File

@@ -0,0 +1,76 @@
package model
import (
"errors"
"time"
)
// SessionLog repräsentiert einen einzelnen Satz innerhalb einer Session.
type SessionLog struct {
ID int64 `json:"id"`
SessionID int64 `json:"session_id"`
ExerciseID int64 `json:"exercise_id"`
ExerciseName string `json:"exercise_name"`
SetNumber int `json:"set_number"`
WeightKg float64 `json:"weight_kg"`
Reps int `json:"reps"`
Note string `json:"note"`
LoggedAt time.Time `json:"logged_at"`
}
// CreateLogRequest enthält die Felder zum Loggen eines Satzes.
type CreateLogRequest struct {
ExerciseID int64 `json:"exercise_id"`
SetNumber int `json:"set_number"`
WeightKg float64 `json:"weight_kg"`
Reps int `json:"reps"`
Note string `json:"note"`
}
// Validate prüft den Request.
func (r *CreateLogRequest) Validate() error {
if r.SetNumber < 1 {
return errors.New("Satznummer muss ≥ 1 sein")
}
if r.WeightKg < 0 || r.WeightKg > 999 {
return errors.New("Gewicht muss zwischen 0 und 999 kg liegen")
}
if r.Reps < 0 || r.Reps > 999 {
return errors.New("Wiederholungen müssen zwischen 0 und 999 liegen")
}
return nil
}
// UpdateLogRequest enthält die Felder zum Korrigieren eines Satzes.
type UpdateLogRequest struct {
WeightKg *float64 `json:"weight_kg"`
Reps *int `json:"reps"`
Note *string `json:"note"`
}
// Validate prüft den Request.
func (r *UpdateLogRequest) Validate() error {
if r.WeightKg != nil && (*r.WeightKg < 0 || *r.WeightKg > 999) {
return errors.New("Gewicht muss zwischen 0 und 999 kg liegen")
}
if r.Reps != nil && (*r.Reps < 0 || *r.Reps > 999) {
return errors.New("Wiederholungen müssen zwischen 0 und 999 liegen")
}
return nil
}
// LastLogResponse enthält die letzten Werte einer Übung.
type LastLogResponse struct {
WeightKg float64 `json:"weight_kg"`
Reps int `json:"reps"`
}
// ExerciseStats enthält aggregierte Statistiken einer Übung.
type ExerciseStats struct {
ExerciseID int64 `json:"exercise_id"`
ExerciseName string `json:"exercise_name"`
MaxWeightKg float64 `json:"max_weight_kg"`
TotalVolumeKg float64 `json:"total_volume_kg"`
TotalSets int `json:"total_sets"`
LastTrained string `json:"last_trained"`
}

View File

@@ -0,0 +1,52 @@
package model
import (
"errors"
"strings"
"time"
)
// TrainingSet ist eine benannte Zusammenstellung von Übungen.
type TrainingSet struct {
ID int64 `json:"id"`
Name string `json:"name"`
Exercises []Exercise `json:"exercises"`
CreatedAt time.Time `json:"created_at"`
DeletedAt *time.Time `json:"deleted_at,omitempty"`
}
// CreateSetRequest enthält die Felder zum Anlegen eines Sets.
type CreateSetRequest struct {
Name string `json:"name"`
ExerciseIDs []int64 `json:"exercise_ids"`
}
// Validate prüft und normalisiert den Request.
func (r *CreateSetRequest) Validate() error {
r.Name = strings.TrimSpace(r.Name)
if len(r.Name) == 0 || len(r.Name) > 100 {
return errors.New("Name muss 1100 Zeichen lang sein")
}
if len(r.ExerciseIDs) == 0 {
return errors.New("Mindestens eine Übung erforderlich")
}
return nil
}
// UpdateSetRequest enthält die Felder zum Aktualisieren eines Sets.
type UpdateSetRequest struct {
Name string `json:"name"`
ExerciseIDs []int64 `json:"exercise_ids"`
}
// Validate prüft und normalisiert den Request.
func (r *UpdateSetRequest) Validate() error {
r.Name = strings.TrimSpace(r.Name)
if len(r.Name) == 0 || len(r.Name) > 100 {
return errors.New("Name muss 1100 Zeichen lang sein")
}
if len(r.ExerciseIDs) == 0 {
return errors.New("Mindestens eine Übung erforderlich")
}
return nil
}

View File

@@ -0,0 +1,28 @@
package model
// Gültige Muskelgruppen für Übungen.
var muscleGroups = map[string]bool{
"brust": true,
"ruecken": true,
"schultern": true,
"bizeps": true,
"trizeps": true,
"beine": true,
"bauch": true,
"ganzkoerper": true,
"sonstiges": true,
}
// ValidMuscleGroup prüft ob die übergebene Muskelgruppe gültig ist.
func ValidMuscleGroup(mg string) bool {
return muscleGroups[mg]
}
// MuscleGroups gibt alle gültigen Muskelgruppen zurück.
func MuscleGroups() []string {
groups := make([]string, 0, len(muscleGroups))
for g := range muscleGroups {
groups = append(groups, g)
}
return groups
}