mirror of
https://github.com/Dvorinka/Trackeep.git
synced 2026-06-03 20:12:58 +00:00
174 lines
6.3 KiB
Go
174 lines
6.3 KiB
Go
package models
|
|
|
|
import (
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// Habit represents a habit that can be tracked
|
|
type Habit struct {
|
|
ID uint `json:"id" gorm:"primaryKey"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
|
|
|
UserID uint `json:"user_id" gorm:"not null;index"`
|
|
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
|
|
|
// Basic habit information
|
|
Name string `json:"name" gorm:"not null"`
|
|
Description string `json:"description" gorm:"type:text"`
|
|
Category string `json:"category" gorm:"default:personal"` // health, productivity, learning, personal
|
|
|
|
// Habit tracking
|
|
TargetFrequency int `json:"target_frequency"` // e.g., 7 times per week
|
|
FrequencyUnit string `json:"frequency_unit"` // daily, weekly, monthly
|
|
TargetValue float64 `json:"target_value"` // e.g., 30 minutes, 8 glasses
|
|
Unit string `json:"unit"` // minutes, glasses, pages, etc.
|
|
|
|
// Schedule
|
|
TimeOfDay string `json:"time_of_day"` // morning, afternoon, evening, night
|
|
DaysOfWeek []string `json:"days_of_week" gorm:"serializer:json"` // ["monday", "tuesday", etc.]
|
|
|
|
// Status and settings
|
|
IsActive bool `json:"is_active" gorm:"default:true"`
|
|
IsPublic bool `json:"is_public" gorm:"default:false"`
|
|
Streak int `json:"streak" gorm:"default:0"`
|
|
LongestStreak int `json:"longest_streak" gorm:"default:0"`
|
|
CompletionRate float64 `json:"completion_rate" gorm:"default:0"` // percentage
|
|
|
|
// Relationships
|
|
GoalID *uint `json:"goal_id,omitempty"`
|
|
Goal *Goal `json:"goal,omitempty" gorm:"foreignKey:GoalID"`
|
|
HabitEntries []HabitEntry `json:"habit_entries,omitempty" gorm:"foreignKey:HabitID"`
|
|
HabitTags []HabitTag `json:"habit_tags,omitempty" gorm:"foreignKey:HabitID"`
|
|
}
|
|
|
|
// HabitEntry represents a single completion of a habit
|
|
type HabitEntry struct {
|
|
ID uint `json:"id" gorm:"primaryKey"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
|
|
|
HabitID uint `json:"habit_id" gorm:"not null;index"`
|
|
Habit Habit `json:"habit,omitempty" gorm:"foreignKey:HabitID"`
|
|
|
|
EntryDate time.Time `json:"entry_date" gorm:"not null;index"`
|
|
Value float64 `json:"value"` // actual value completed
|
|
TargetValue float64 `json:"target_value"` // target value for this entry
|
|
Unit string `json:"unit"`
|
|
Notes string `json:"notes" gorm:"type:text"`
|
|
IsCompleted bool `json:"is_completed" gorm:"default:false"`
|
|
Quality int `json:"quality" gorm:"default:3"` // 1-5 rating
|
|
TimeSpent int `json:"time_spent"` // minutes spent
|
|
Location string `json:"location"`
|
|
Mood string `json:"mood"` // happy, neutral, stressed, etc.
|
|
}
|
|
|
|
// GoalTag represents tags for goals
|
|
type GoalTag struct {
|
|
ID uint `json:"id" gorm:"primaryKey"`
|
|
GoalID uint `json:"goal_id" gorm:"not null;index"`
|
|
TagID uint `json:"tag_id" gorm:"not null;index"`
|
|
Goal Goal `json:"goal,omitempty" gorm:"foreignKey:GoalID"`
|
|
Tag Tag `json:"tag,omitempty" gorm:"foreignKey:TagID"`
|
|
}
|
|
|
|
// HabitTag represents tags for habits
|
|
type HabitTag struct {
|
|
ID uint `json:"id" gorm:"primaryKey"`
|
|
HabitID uint `json:"habit_id" gorm:"not null;index"`
|
|
TagID uint `json:"tag_id" gorm:"not null;index"`
|
|
Habit Habit `json:"habit,omitempty" gorm:"foreignKey:HabitID"`
|
|
Tag Tag `json:"tag,omitempty" gorm:"foreignKey:TagID"`
|
|
}
|
|
|
|
// GoalTemplate represents templates for creating goals
|
|
type GoalTemplate struct {
|
|
ID uint `json:"id" gorm:"primaryKey"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
|
|
|
Name string `json:"name" gorm:"not null"`
|
|
Description string `json:"description" gorm:"type:text"`
|
|
Category string `json:"category"`
|
|
Unit string `json:"unit"`
|
|
TargetValue float64 `json:"target_value"`
|
|
Duration int `json:"duration"` // suggested duration in days
|
|
IsPublic bool `json:"is_public" gorm:"default:false"`
|
|
UsageCount int `json:"usage_count" gorm:"default:0"`
|
|
|
|
// Template milestones
|
|
Milestones []GoalTemplateMilestone `json:"milestones,omitempty" gorm:"foreignKey:GoalTemplateID"`
|
|
}
|
|
|
|
// GoalTemplateMilestone represents milestones in goal templates
|
|
type GoalTemplateMilestone struct {
|
|
ID uint `json:"id" gorm:"primaryKey"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
|
|
|
GoalTemplateID uint `json:"goal_template_id" gorm:"not null;index"`
|
|
GoalTemplate GoalTemplate `json:"goal_template,omitempty" gorm:"foreignKey:GoalTemplateID"`
|
|
|
|
Title string `json:"title" gorm:"not null"`
|
|
Description string `json:"description" gorm:"type:text"`
|
|
TargetValue float64 `json:"target_value"`
|
|
Unit string `json:"unit"`
|
|
DayOffset int `json:"day_offset"` // days from start date
|
|
SortOrder int `json:"sort_order" gorm:"default:0"`
|
|
}
|
|
|
|
// BeforeCreate hooks
|
|
func (h *Habit) BeforeCreate(tx *gorm.DB) error {
|
|
if h.Category == "" {
|
|
h.Category = "personal"
|
|
}
|
|
if h.FrequencyUnit == "" {
|
|
h.FrequencyUnit = "daily"
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (he *HabitEntry) BeforeCreate(tx *gorm.DB) error {
|
|
// Set completion based on value vs target
|
|
if he.Value >= he.TargetValue {
|
|
he.IsCompleted = true
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// BeforeUpdate hooks
|
|
func (h *Habit) BeforeUpdate(tx *gorm.DB) error {
|
|
// Update completion rate and streak
|
|
h.updateStreakAndRate()
|
|
return nil
|
|
}
|
|
|
|
// Helper methods
|
|
|
|
// updateStreakAndRate calculates current streak and completion rate
|
|
func (h *Habit) updateStreakAndRate() {
|
|
// This would typically involve querying habit entries
|
|
// For now, we'll keep the existing values
|
|
// In a real implementation, you'd calculate based on recent entries
|
|
}
|
|
|
|
// GetTodayEntry gets today's habit entry if it exists
|
|
func (h *Habit) GetTodayEntry() *HabitEntry {
|
|
// This would typically query the database
|
|
// For now, return nil
|
|
return nil
|
|
}
|
|
|
|
// GetWeeklyStreak gets the current weekly streak
|
|
func (h *Habit) GetWeeklyStreak() int {
|
|
// This would calculate based on habit entries
|
|
// For now, return the stored streak
|
|
return h.Streak
|
|
}
|