all repos — go-lift @ 092e7440440b8459d82fb90c05c005f9f38e3c51

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
 14type Database struct {
 15	*gorm.DB
 16}
 17
 18type User struct {
 19	ID        uint           `gorm:"primaryKey" json:"id"`
 20	Name      string         `gorm:"size:50" json:"name"`
 21	IsFemale  bool           `gorm:"default:false" json:"isFemale"`
 22	Height    float64        `json:"height"` // In cm
 23	Weight    float64        `json:"weight"` // In kg
 24	BirthDate time.Time      `json:"birthDate"`
 25	CreatedAt time.Time      `json:"createdAt"`
 26	UpdatedAt time.Time      `json:"updatedAt"`
 27	DeletedAt gorm.DeletedAt `gorm:"index" json:"deletedAt"`
 28}
 29
 30type Exercise struct {
 31	ID           uint    `gorm:"primaryKey;autoIncrement"`
 32	Name         string  `gorm:"not null;uniqueIndex"`
 33	Level        string  `gorm:"size:50;not null"`
 34	Category     string  `gorm:"size:50;not null"`
 35	Force        *string `gorm:"size:50"`
 36	Mechanic     *string `gorm:"size:50"`
 37	Equipment    *string `gorm:"size:50"`
 38	Instructions *string
 39
 40	PrimaryMuscles   []Muscle `gorm:"many2many:exercise_primary_muscles;constraint:OnDelete:CASCADE"`
 41	SecondaryMuscles []Muscle `gorm:"many2many:exercise_secondary_muscles;constraint:OnDelete:CASCADE"`
 42
 43	CreatedAt time.Time
 44	UpdatedAt time.Time
 45}
 46
 47type Muscle struct {
 48	ID   uint   `gorm:"primaryKey"`
 49	Name string `gorm:"uniqueIndex;size:50;not null"`
 50}
 51
 52type Set struct {
 53	ID         uint           `gorm:"primaryKey" json:"id"`
 54	ExerciseID uint           `gorm:"index" json:"exerciseId"`
 55	Reps       int            `json:"reps"`
 56	Weight     float64        `json:"weight"`
 57	Duration   int            `json:"duration"` // In seconds, for timed exercises
 58	OrderIndex int            `gorm:"not null" json:"orderIndex"`
 59	CreatedAt  time.Time      `json:"createdAt"`
 60	UpdatedAt  time.Time      `json:"updatedAt"`
 61	DeletedAt  gorm.DeletedAt `gorm:"index" json:"deletedAt"`
 62
 63	Exercise Exercise `json:"-"`
 64}
 65
 66// SuperSet to handle two exercises with single rest time
 67type SuperSet struct {
 68	ID                  uint           `gorm:"primaryKey" json:"id"`
 69	Name                string         `gorm:"size:100" json:"name"`
 70	PrimaryExerciseID   uint           `gorm:"index" json:"primaryExerciseId"`
 71	SecondaryExerciseID uint           `gorm:"index" json:"secondaryExerciseId"`
 72	RestTime            int            `gorm:"default:0" json:"restTime"` // In seconds
 73	CreatedAt           time.Time      `json:"createdAt"`
 74	UpdatedAt           time.Time      `json:"updatedAt"`
 75	DeletedAt           gorm.DeletedAt `gorm:"index" json:"deletedAt"`
 76
 77	PrimaryExercise   Exercise `json:"primaryExercise"`
 78	SecondaryExercise Exercise `json:"secondaryExercise"`
 79}
 80
 81// RoutineItem represents either an Exercise or a SuperSet in a Routine
 82type RoutineItem struct {
 83	ID         uint           `gorm:"primaryKey" json:"id"`
 84	RoutineID  uint           `gorm:"index" json:"routineId"`
 85	ExerciseID *uint          `gorm:"index" json:"exerciseId"`
 86	SuperSetID *uint          `gorm:"index" json:"superSetId"`
 87	RestTime   int            `gorm:"default:0" json:"restTime"` // In seconds
 88	OrderIndex int            `gorm:"not null" json:"orderIndex"`
 89	CreatedAt  time.Time      `json:"createdAt"`
 90	UpdatedAt  time.Time      `json:"updatedAt"`
 91	DeletedAt  gorm.DeletedAt `gorm:"index" json:"deletedAt"`
 92
 93	Routine  Routine   `json:"-"`
 94	SuperSet *SuperSet `json:"superSet,omitempty"`
 95	Exercise *Exercise `json:"exercise,omitempty"`
 96}
 97
 98type Routine struct {
 99	ID          uint   `gorm:"primaryKey" json:"id"`
100	Name        string `gorm:"size:100;not null" json:"name"`
101	Description string `gorm:"size:500" json:"description"`
102	//UserID      uint           `gorm:"index" json:"userId"`
103	//IsPublic  bool           `gorm:"default:false" json:"isPublic"`
104	CreatedAt time.Time      `json:"createdAt"`
105	UpdatedAt time.Time      `json:"updatedAt"`
106	DeletedAt gorm.DeletedAt `gorm:"index" json:"deletedAt"`
107
108	//User        User           `json:"-"`
109	RoutineItems []RoutineItem `json:"routineItems,omitempty"`
110}
111
112/*
113type User struct {
114	ID        uint           `gorm:"primaryKey" json:"id"`
115	Username  string         `gorm:"size:50;not null;uniqueIndex" json:"username"`
116	Email     string         `gorm:"size:100;not null;uniqueIndex" json:"email"`
117	Password  string         `gorm:"size:100;not null" json:"-"`
118	Name      string         `gorm:"size:100" json:"name"`
119	CreatedAt time.Time      `json:"createdAt"`
120	UpdatedAt time.Time      `json:"updatedAt"`
121	DeletedAt gorm.DeletedAt `gorm:"index" json:"deletedAt"`
122
123	Exercises      []Exercise      `json:"exercises,omitempty"`
124	Routines       []Routine       `json:"routines,omitempty"`
125	RecordRoutines []RecordRoutine `json:"recordRoutines,omitempty"`
126}
127*/
128
129type RecordRoutine struct {
130	ID uint `gorm:"primaryKey" json:"id"`
131	//UserID    uint           `gorm:"index" json:"userId"`
132	RoutineID uint           `gorm:"index" json:"routineId"`
133	StartedAt time.Time      `gorm:"not null" json:"startedAt"`
134	EndedAt   *time.Time     `json:"endedAt"`
135	CreatedAt time.Time      `json:"createdAt"`
136	UpdatedAt time.Time      `json:"updatedAt"`
137	DeletedAt gorm.DeletedAt `gorm:"index" json:"deletedAt"`
138
139	//User            User             `json:"-"`
140	Routine            Routine             `json:"routine"`
141	RecordRoutineItems []RecordRoutineItem `json:"recordRoutineItems,omitempty"`
142}
143
144// RecordRoutineItem represents either a RecordExercise or a RecordSuperSet in a completed routine
145type RecordRoutineItem struct {
146	ID               uint           `gorm:"primaryKey" json:"id"`
147	RecordRoutineID  uint           `gorm:"index" json:"recordRoutineId"`
148	RecordExerciseID *uint          `gorm:"index" json:"recordExerciseId"`
149	RecordSuperSetID *uint          `gorm:"index" json:"recordSuperSetId"`
150	ActualRestTime   int            `json:"actualRestTime"` // In seconds
151	OrderIndex       int            `gorm:"not null" json:"orderIndex"`
152	CreatedAt        time.Time      `json:"createdAt"`
153	UpdatedAt        time.Time      `json:"updatedAt"`
154	DeletedAt        gorm.DeletedAt `gorm:"index" json:"deletedAt"`
155
156	RecordRoutine  RecordRoutine   `json:"-"`
157	RecordSuperSet *RecordSuperSet `json:"recordSuperSet,omitempty"`
158	RecordExercise *RecordExercise `json:"recordExercise,omitempty"`
159}
160
161// RecordSuperSet records a completed superset
162type RecordSuperSet struct {
163	ID              uint           `gorm:"primaryKey" json:"id"`
164	RecordRoutineID uint           `gorm:"index" json:"recordRoutineId"`
165	SuperSetID      uint           `gorm:"index" json:"superSetId"`
166	StartedAt       time.Time      `gorm:"not null" json:"startedAt"`
167	EndedAt         time.Time      `gorm:"not null" json:"endedAt"`
168	ActualRestTime  int            `json:"actualRestTime"` // In seconds
169	OrderIndex      int            `gorm:"not null" json:"orderIndex"`
170	CreatedAt       time.Time      `json:"createdAt"`
171	UpdatedAt       time.Time      `json:"updatedAt"`
172	DeletedAt       gorm.DeletedAt `gorm:"index" json:"deletedAt"`
173
174	RecordRoutine RecordRoutine `json:"-"`
175	SuperSet      SuperSet      `json:"superSet"`
176}
177
178type RecordExercise struct {
179	ID               uint            `gorm:"primaryKey" json:"id"`
180	RecordRoutineID  uint            `gorm:"index" json:"recordRoutineId"`
181	RecordRoutine    RecordRoutine   `json:"-"`
182	ExerciseID       uint            `gorm:"index" json:"exerciseId"`
183	Exercise         Exercise        `json:"exercise"`
184	StartedAt        time.Time       `gorm:"not null" json:"startedAt"`
185	EndedAt          time.Time       `gorm:"not null" json:"endedAt"`
186	ActualRestTime   int             `json:"actualRestTime"` // In seconds
187	RecordSets       []RecordSet     `json:"recordSets,omitempty"`
188	OrderIndex       int             `gorm:"not null" json:"orderIndex"`
189	RecordSuperSetID *uint           `gorm:"index" json:"recordSuperSetId"`
190	RecordSuperSet   *RecordSuperSet `json:"-"`
191	CreatedAt        time.Time       `json:"createdAt"`
192	UpdatedAt        time.Time       `json:"updatedAt"`
193	DeletedAt        gorm.DeletedAt  `gorm:"index" json:"deletedAt"`
194}
195
196type RecordSet struct {
197	ID               uint           `gorm:"primaryKey" json:"id"`
198	RecordExerciseID uint           `gorm:"index" json:"recordExerciseId"`
199	SetID            uint           `gorm:"index" json:"setId"`
200	ActualReps       int            `json:"actualReps"`
201	ActualWeight     float64        `json:"actualWeight"`
202	ActualDuration   int            `json:"actualDuration"` // In seconds
203	CompletedAt      time.Time      `gorm:"not null" json:"completedAt"`
204	OrderIndex       int            `gorm:"not null" json:"orderIndex"`
205	CreatedAt        time.Time      `json:"createdAt"`
206	UpdatedAt        time.Time      `json:"updatedAt"`
207	DeletedAt        gorm.DeletedAt `gorm:"index" json:"deletedAt"`
208
209	RecordExercise RecordExercise `json:"-"`
210	Set            Set            `json:"set"`
211}
212
213type Localization struct {
214	ID         uint           `gorm:"primaryKey" json:"id"`
215	LanguageID uint           `gorm:"not null;uniqueIndex:idxLangKeyword" json:"languageId"`
216	Keyword    string         `gorm:"size:255;not null;uniqueIndex:idxLangKeyword" json:"keyword"`
217	Text       string         `gorm:"size:1000;not null" json:"text"`
218	CreatedAt  time.Time      `json:"createdAt"`
219	UpdatedAt  time.Time      `json:"updatedAt"`
220	DeletedAt  gorm.DeletedAt `gorm:"index" json:"deletedAt"`
221}
222
223type Language struct {
224	ID        uint           `gorm:"primaryKey" json:"id"`
225	Name      string         `gorm:"size:100;not null;uniqueIndex" json:"name"`
226	Code      string         `gorm:"size:8;not null;uniqueIndex" json:"code"`
227	Flag      string         `gorm:"size:50" json:"flag"`
228	CreatedAt time.Time      `json:"createdAt"`
229	UpdatedAt time.Time      `json:"updatedAt"`
230	DeletedAt gorm.DeletedAt `gorm:"index" json:"deletedAt"`
231}
232
233// InitializeDB creates and initializes the SQLite database with all models
234func InitializeDB() (db *Database, err error) {
235	// Create the data directory if it doesn't exist
236	if _, err = os.Stat(dbDir); os.IsNotExist(err) {
237		err = os.MkdirAll(dbDir, 0755)
238		if err != nil {
239			return
240		}
241	}
242
243	dbPath := filepath.Join(dbDir, dbName)
244
245	// Set up logger for GORM
246	newLogger := logger.New(
247		log.New(os.Stdout, "\r\n", log.LstdFlags),
248		logger.Config{
249			SlowThreshold: time.Second,
250			LogLevel:      logger.Info,
251			Colorful:      true,
252		},
253	)
254
255	dialector := sqlite.Open(dbPath + "?Pragma=foreignKeys(1)")
256	config := &gorm.Config{Logger: newLogger}
257
258	// Open connection to the database
259	conn, err := gorm.Open(dialector, config)
260	if err != nil {
261		return
262	}
263
264	// Get the underlying SQL database to set connection parameters
265	sqlDB, err := conn.DB()
266	if err != nil {
267		return nil, err
268	}
269
270	// Set connection pool settings
271	sqlDB.SetMaxIdleConns(10)
272	sqlDB.SetMaxOpenConns(100)
273	sqlDB.SetConnMaxLifetime(time.Hour)
274
275	// Auto migrate the models
276	err = conn.AutoMigrate(
277		Muscle{},
278		Exercise{},
279		Set{},
280		SuperSet{},
281		RoutineItem{},
282		Routine{},
283		User{},
284		RecordRoutine{},
285		RecordExercise{},
286		RecordSuperSet{},
287		RecordSet{},
288		Localization{},
289		Language{},
290	)
291	if err != nil {
292		return
293	}
294
295	db = &Database{conn}
296
297	// Ensure initial data is present
298	err = db.CheckInitialData()
299	if err != nil {
300		return nil, err
301	}
302
303	log.Println("Database initialized successfully")
304	return db, nil
305}