all repos — go-lift @ 2e1c54060dc6096e5772a13f5248451e86b03f6d

Lightweight workout tracker prototype..

src/database/models.go (view raw)

  1package database
  2
  3import (
  4	"log"
  5	"os"
  6	"path/filepath"
  7	"time"
  8
  9	"github.com/glebarez/sqlite"
 10	"gorm.io/gorm"
 11	"gorm.io/gorm/logger"
 12)
 13
 14const (
 15	dbDir  = "data"
 16	dbName = "fitness.db"
 17)
 18
 19type Equipment struct {
 20	ID          uint           `gorm:"primaryKey" json:"id"`
 21	Name        string         `gorm:"size:100;not null;uniqueIndex" json:"name"`
 22	Description string         `gorm:"size:500" json:"description"`
 23	CreatedAt   time.Time      `json:"created_at"`
 24	UpdatedAt   time.Time      `json:"updated_at"`
 25	DeletedAt   gorm.DeletedAt `gorm:"index" json:"deleted_at"`
 26
 27	Exercises []Exercise `gorm:"many2many:exercise_equipment" json:"exercises,omitempty"`
 28}
 29
 30type MuscleGroup struct {
 31	ID        uint           `gorm:"primaryKey" json:"id"`
 32	Name      string         `gorm:"size:100;not null;uniqueIndex" json:"name"`
 33	CreatedAt time.Time      `json:"created_at"`
 34	UpdatedAt time.Time      `json:"updated_at"`
 35	DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at"`
 36
 37	Exercises []Exercise `gorm:"many2many:exercise_muscle_groups" json:"exercises,omitempty"`
 38}
 39
 40type ExerciseMuscleGroup struct {
 41	ID            uint `gorm:"primaryKey" json:"id"`
 42	ExerciseID    uint `gorm:"uniqueIndex:idx_exercise_muscle_group" json:"exercise_id"`
 43	MuscleGroupID uint `gorm:"uniqueIndex:idx_exercise_muscle_group" json:"muscle_group_id"`
 44
 45	Exercise    Exercise    `json:"exercise"`
 46	MuscleGroup MuscleGroup `json:"muscle_group"`
 47}
 48
 49type Exercise struct {
 50	ID          uint   `gorm:"primaryKey" json:"id"`
 51	Name        string `gorm:"size:100;not null" json:"name"`
 52	Description string `gorm:"size:500" json:"description"`
 53	//UserID      uint           `gorm:"index" json:"user_id"`
 54	//IsPublic  bool           `gorm:"default:false" json:"is_public"`
 55	CreatedAt time.Time      `json:"created_at"`
 56	UpdatedAt time.Time      `json:"updated_at"`
 57	DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at"`
 58
 59	//User         User          `json:"-"`
 60	Equipment    []Equipment   `gorm:"many2many:exercise_equipment" json:"equipment"`
 61	MuscleGroups []MuscleGroup `gorm:"many2many:exercise_muscle_groups" json:"muscle_groups"`
 62	Sets         []Set         `json:"sets,omitempty"`
 63}
 64
 65type Set struct {
 66	ID         uint           `gorm:"primaryKey" json:"id"`
 67	ExerciseID uint           `gorm:"index" json:"exercise_id"`
 68	Reps       int            `json:"reps"`
 69	Weight     float64        `json:"weight"`
 70	Duration   int            `json:"duration"` // In seconds, for timed exercises
 71	OrderIndex int            `gorm:"not null" json:"order_index"`
 72	CreatedAt  time.Time      `json:"created_at"`
 73	UpdatedAt  time.Time      `json:"updated_at"`
 74	DeletedAt  gorm.DeletedAt `gorm:"index" json:"deleted_at"`
 75
 76	Exercise Exercise `json:"-"`
 77}
 78
 79// SuperSet to handle two exercises with single rest time
 80type SuperSet struct {
 81	ID                  uint           `gorm:"primaryKey" json:"id"`
 82	Name                string         `gorm:"size:100" json:"name"`
 83	PrimaryExerciseID   uint           `gorm:"index" json:"primary_exercise_id"`
 84	SecondaryExerciseID uint           `gorm:"index" json:"secondary_exercise_id"`
 85	RestTime            int            `gorm:"default:0" json:"rest_time"` // In seconds
 86	CreatedAt           time.Time      `json:"created_at"`
 87	UpdatedAt           time.Time      `json:"updated_at"`
 88	DeletedAt           gorm.DeletedAt `gorm:"index" json:"deleted_at"`
 89
 90	PrimaryExercise   Exercise `json:"primary_exercise"`
 91	SecondaryExercise Exercise `json:"secondary_exercise"`
 92}
 93
 94// RoutineItem represents either an Exercise or a SuperSet in a Routine
 95type RoutineItem struct {
 96	ID         uint           `gorm:"primaryKey" json:"id"`
 97	RoutineID  uint           `gorm:"index" json:"routine_id"`
 98	ExerciseID *uint          `gorm:"index" json:"exercise_id"`
 99	SuperSetID *uint          `gorm:"index" json:"super_set_id"`
100	RestTime   int            `gorm:"default:0" json:"rest_time"` // In seconds
101	OrderIndex int            `gorm:"not null" json:"order_index"`
102	CreatedAt  time.Time      `json:"created_at"`
103	UpdatedAt  time.Time      `json:"updated_at"`
104	DeletedAt  gorm.DeletedAt `gorm:"index" json:"deleted_at"`
105
106	Routine  Routine   `json:"-"`
107	SuperSet *SuperSet `json:"super_set,omitempty"`
108	Exercise *Exercise `json:"exercise,omitempty"`
109}
110
111type Routine struct {
112	ID          uint   `gorm:"primaryKey" json:"id"`
113	Name        string `gorm:"size:100;not null" json:"name"`
114	Description string `gorm:"size:500" json:"description"`
115	//UserID      uint           `gorm:"index" json:"user_id"`
116	//IsPublic  bool           `gorm:"default:false" json:"is_public"`
117	CreatedAt time.Time      `json:"created_at"`
118	UpdatedAt time.Time      `json:"updated_at"`
119	DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at"`
120
121	//User        User           `json:"-"`
122	RoutineItems []RoutineItem `json:"routine_items,omitempty"`
123}
124
125/*
126type User struct {
127	ID        uint           `gorm:"primaryKey" json:"id"`
128	Username  string         `gorm:"size:50;not null;uniqueIndex" json:"username"`
129	Email     string         `gorm:"size:100;not null;uniqueIndex" json:"email"`
130	Password  string         `gorm:"size:100;not null" json:"-"`
131	Name      string         `gorm:"size:100" json:"name"`
132	CreatedAt time.Time      `json:"created_at"`
133	UpdatedAt time.Time      `json:"updated_at"`
134	DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at"`
135
136	Exercises      []Exercise      `json:"exercises,omitempty"`
137	Routines       []Routine       `json:"routines,omitempty"`
138	RecordRoutines []RecordRoutine `json:"record_routines,omitempty"`
139}
140*/
141
142type RecordRoutine struct {
143	ID uint `gorm:"primaryKey" json:"id"`
144	//UserID    uint           `gorm:"index" json:"user_id"`
145	RoutineID uint           `gorm:"index" json:"routine_id"`
146	StartedAt time.Time      `gorm:"not null" json:"started_at"`
147	EndedAt   *time.Time     `json:"ended_at"`
148	CreatedAt time.Time      `json:"created_at"`
149	UpdatedAt time.Time      `json:"updated_at"`
150	DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at"`
151
152	//User            User             `json:"-"`
153	Routine            Routine             `json:"routine"`
154	RecordRoutineItems []RecordRoutineItem `json:"record_routine_items,omitempty"`
155}
156
157// RecordRoutineItem represents either a RecordExercise or a RecordSuperSet in a completed routine
158type RecordRoutineItem struct {
159	ID               uint           `gorm:"primaryKey" json:"id"`
160	RecordRoutineID  uint           `gorm:"index" json:"record_routine_id"`
161	RecordExerciseID *uint          `gorm:"index" json:"record_exercise_id"`
162	RecordSuperSetID *uint          `gorm:"index" json:"record_super_set_id"`
163	ActualRestTime   int            `json:"actual_rest_time"` // In seconds
164	OrderIndex       int            `gorm:"not null" json:"order_index"`
165	CreatedAt        time.Time      `json:"created_at"`
166	UpdatedAt        time.Time      `json:"updated_at"`
167	DeletedAt        gorm.DeletedAt `gorm:"index" json:"deleted_at"`
168
169	RecordRoutine  RecordRoutine   `json:"-"`
170	RecordSuperSet *RecordSuperSet `json:"record_super_set,omitempty"`
171	RecordExercise *RecordExercise `json:"record_exercise,omitempty"`
172}
173
174// RecordSuperSet records a completed superset
175type RecordSuperSet struct {
176	ID              uint           `gorm:"primaryKey" json:"id"`
177	RecordRoutineID uint           `gorm:"index" json:"record_routine_id"`
178	SuperSetID      uint           `gorm:"index" json:"super_set_id"`
179	StartedAt       time.Time      `gorm:"not null" json:"started_at"`
180	EndedAt         time.Time      `gorm:"not null" json:"ended_at"`
181	ActualRestTime  int            `json:"actual_rest_time"` // In seconds
182	OrderIndex      int            `gorm:"not null" json:"order_index"`
183	CreatedAt       time.Time      `json:"created_at"`
184	UpdatedAt       time.Time      `json:"updated_at"`
185	DeletedAt       gorm.DeletedAt `gorm:"index" json:"deleted_at"`
186
187	RecordRoutine RecordRoutine `json:"-"`
188	SuperSet      SuperSet      `json:"super_set"`
189}
190
191type RecordExercise struct {
192	ID               uint            `gorm:"primaryKey" json:"id"`
193	RecordRoutineID  uint            `gorm:"index" json:"record_routine_id"`
194	RecordRoutine    RecordRoutine   `json:"-"`
195	ExerciseID       uint            `gorm:"index" json:"exercise_id"`
196	Exercise         Exercise        `json:"exercise"`
197	StartedAt        time.Time       `gorm:"not null" json:"started_at"`
198	EndedAt          time.Time       `gorm:"not null" json:"ended_at"`
199	ActualRestTime   int             `json:"actual_rest_time"` // In seconds
200	RecordSets       []RecordSet     `json:"record_sets,omitempty"`
201	OrderIndex       int             `gorm:"not null" json:"order_index"`
202	RecordSuperSetID *uint           `gorm:"index" json:"record_super_set_id"`
203	RecordSuperSet   *RecordSuperSet `json:"-"`
204	CreatedAt        time.Time       `json:"created_at"`
205	UpdatedAt        time.Time       `json:"updated_at"`
206	DeletedAt        gorm.DeletedAt  `gorm:"index" json:"deleted_at"`
207}
208
209type RecordSet struct {
210	ID               uint           `gorm:"primaryKey" json:"id"`
211	RecordExerciseID uint           `gorm:"index" json:"record_exercise_id"`
212	SetID            uint           `gorm:"index" json:"set_id"`
213	ActualReps       int            `json:"actual_reps"`
214	ActualWeight     float64        `json:"actual_weight"`
215	ActualDuration   int            `json:"actual_duration"` // In seconds
216	CompletedAt      time.Time      `gorm:"not null" json:"completed_at"`
217	OrderIndex       int            `gorm:"not null" json:"order_index"`
218	CreatedAt        time.Time      `json:"created_at"`
219	UpdatedAt        time.Time      `json:"updated_at"`
220	DeletedAt        gorm.DeletedAt `gorm:"index" json:"deleted_at"`
221
222	RecordExercise RecordExercise `json:"-"`
223	Set            Set            `json:"set"`
224}
225
226type Localization struct {
227	ID         uint           `gorm:"primaryKey" json:"id"`
228	LanguageID uint           `gorm:"not null;uniqueIndex:idx_lang_keyword" json:"language_id"`
229	Keyword    string         `gorm:"size:255;not null;uniqueIndex:idx_lang_keyword" json:"keyword"`
230	Text       string         `gorm:"size:1000;not null" json:"text"`
231	CreatedAt  time.Time      `json:"created_at"`
232	UpdatedAt  time.Time      `json:"updated_at"`
233	DeletedAt  gorm.DeletedAt `gorm:"index" json:"deleted_at"`
234}
235
236type Language struct {
237	ID        uint           `gorm:"primaryKey" json:"id"`
238	Name      string         `gorm:"size:100;not null;uniqueIndex" json:"name"`
239	Code      string         `gorm:"size:8;not null;uniqueIndex" json:"code"`
240	Flag      string         `gorm:"size:50" json:"flag"`
241	CreatedAt time.Time      `json:"created_at"`
242	UpdatedAt time.Time      `json:"updated_at"`
243	DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at"`
244}
245
246// InitializeDB creates and initializes the SQLite database with all models
247func InitializeDB() (db *gorm.DB, err error) {
248	// Create the data directory if it doesn't exist
249	if _, err = os.Stat(dbDir); os.IsNotExist(err) {
250		err = os.MkdirAll(dbDir, 0755)
251		if err != nil {
252			return
253		}
254	}
255
256	dbPath := filepath.Join(dbDir, dbName)
257
258	// Set up logger for GORM
259	newLogger := logger.New(
260		log.New(os.Stdout, "\r\n", log.LstdFlags),
261		logger.Config{
262			SlowThreshold: time.Second,
263			LogLevel:      logger.Info,
264			Colorful:      true,
265		},
266	)
267
268	dialector := sqlite.Open(dbPath + "?_pragma=foreign_keys(1)")
269	config := &gorm.Config{Logger: newLogger}
270
271	// Open connection to the database
272	db, err = gorm.Open(dialector, config)
273	if err != nil {
274		return
275	}
276
277	// Get the underlying SQL database to set connection parameters
278	sqlDB, err := db.DB()
279	if err != nil {
280		return nil, err
281	}
282
283	// Set connection pool settings
284	sqlDB.SetMaxIdleConns(10)
285	sqlDB.SetMaxOpenConns(100)
286	sqlDB.SetConnMaxLifetime(time.Hour)
287
288	// Auto migrate the models
289	err = db.AutoMigrate(
290		Equipment{},
291		MuscleGroup{},
292		Exercise{},
293		ExerciseMuscleGroup{},
294		Set{},
295		SuperSet{},
296		RoutineItem{},
297		Routine{},
298		//User{},
299		RecordRoutine{},
300		RecordExercise{},
301		RecordSuperSet{},
302		RecordSet{},
303		Localization{},
304		Language{},
305	)
306	if err != nil {
307		return
308	}
309
310	// Ensure initial data is present
311	err = CheckInitialData(db)
312	if err != nil {
313		return nil, err
314	}
315
316	log.Println("Database initialized successfully")
317	return db, nil
318}