mirror of
https://github.com/TracksApp/tracks.git
synced 2025-12-16 15:20:13 +01:00
This commit fixes the tag loading issue and adds comprehensive testing. ## Bug Fix: Polymorphic Tag Loading Fixed issue with many-to-many tag relationships not working correctly with polymorphic associations. The problem was that GORM doesn't support using `many2many` with polymorphic relationships directly. **Changes:** - Modified `internal/models/todo.go`: Changed Tags field to use `gorm:"-"` to skip GORM handling - Modified `internal/models/recurring_todo.go`: Same fix for recurring todos - Modified `internal/services/todo_service.go`: Added `loadTodoTags()` helper function to manually load tags through the taggings join table **How it works now:** 1. Tags are no longer automatically loaded by GORM 2. Manual loading via JOIN query: `tags JOIN taggings ON tag_id WHERE taggable_id AND taggable_type` 3. Called after loading todos in both `GetTodo()` and `GetTodos()` ## Testing Added `test_api.sh` - comprehensive integration test script that tests: 1. Health check 2. User registration 3. Authentication 4. Context creation 5. Project creation 6. Todo creation with tags 7. Listing todos with filters 8. Completing todos 9. Project statistics All tests pass successfully! ## Files Changed - `internal/models/todo.go`: Fix tag relationship - `internal/models/recurring_todo.go`: Fix tag relationship - `internal/services/todo_service.go`: Add manual tag loading - `test_api.sh`: New integration test script - `go.sum`: Updated with exact dependency versions
125 lines
4.5 KiB
Go
125 lines
4.5 KiB
Go
package models
|
|
|
|
import (
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// RecurrenceType represents the type of recurrence pattern
|
|
type RecurrenceType string
|
|
|
|
const (
|
|
RecurrenceTypeDaily RecurrenceType = "daily"
|
|
RecurrenceTypeWeekly RecurrenceType = "weekly"
|
|
RecurrenceTypeMonthly RecurrenceType = "monthly"
|
|
RecurrenceTypeYearly RecurrenceType = "yearly"
|
|
)
|
|
|
|
// RecurringTodoState represents the state of a recurring todo
|
|
type RecurringTodoState string
|
|
|
|
const (
|
|
RecurringTodoStateActive RecurringTodoState = "active"
|
|
RecurringTodoStateCompleted RecurringTodoState = "completed"
|
|
)
|
|
|
|
// RecurrenceSelector represents how monthly/yearly recurrence is calculated
|
|
type RecurrenceSelector string
|
|
|
|
const (
|
|
RecurrenceSelectorDate RecurrenceSelector = "date" // e.g., 15th of month
|
|
RecurrenceSelectorDayOfWeek RecurrenceSelector = "day_of_week" // e.g., 3rd Monday
|
|
)
|
|
|
|
// RecurringTodo represents a template for recurring tasks
|
|
type RecurringTodo struct {
|
|
ID uint `gorm:"primaryKey" json:"id"`
|
|
UserID uint `gorm:"not null;index" json:"user_id"`
|
|
ContextID uint `gorm:"not null;index" json:"context_id"`
|
|
ProjectID *uint `gorm:"index" json:"project_id"`
|
|
Description string `gorm:"not null;size:300" json:"description"`
|
|
Notes string `gorm:"type:text" json:"notes"`
|
|
State RecurringTodoState `gorm:"type:varchar(20);default:'active'" json:"state"`
|
|
RecurrenceType RecurrenceType `gorm:"type:varchar(20);not null" json:"recurrence_type"`
|
|
RecurrenceSelector RecurrenceSelector `gorm:"type:varchar(20)" json:"recurrence_selector"`
|
|
EveryN int `gorm:"default:1" json:"every_n"` // Every N days/weeks/months/years
|
|
StartFrom time.Time `json:"start_from"`
|
|
EndsOn *time.Time `json:"ends_on"`
|
|
OccurrencesCount *int `json:"occurrences_count"` // Total occurrences to create
|
|
NumberOfOccurrences int `gorm:"default:0" json:"number_of_occurrences"` // Created so far
|
|
Target string `gorm:"type:varchar(20)" json:"target"` // 'due_date' or 'show_from'
|
|
|
|
// Weekly recurrence
|
|
RecursOnMonday bool `gorm:"default:false" json:"recurs_on_monday"`
|
|
RecursOnTuesday bool `gorm:"default:false" json:"recurs_on_tuesday"`
|
|
RecursOnWednesday bool `gorm:"default:false" json:"recurs_on_wednesday"`
|
|
RecursOnThursday bool `gorm:"default:false" json:"recurs_on_thursday"`
|
|
RecursOnFriday bool `gorm:"default:false" json:"recurs_on_friday"`
|
|
RecursOnSaturday bool `gorm:"default:false" json:"recurs_on_saturday"`
|
|
RecursOnSunday bool `gorm:"default:false" json:"recurs_on_sunday"`
|
|
|
|
// Daily recurrence
|
|
OnlyWorkdays bool `gorm:"default:false" json:"only_workdays"`
|
|
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
|
|
|
// Associations
|
|
User User `gorm:"foreignKey:UserID" json:"-"`
|
|
Context Context `gorm:"foreignKey:ContextID" json:"context,omitempty"`
|
|
Project *Project `gorm:"foreignKey:ProjectID" json:"project,omitempty"`
|
|
Todos []Todo `gorm:"foreignKey:RecurringTodoID" json:"todos,omitempty"`
|
|
Taggings []Tagging `gorm:"polymorphic:Taggable" json:"-"`
|
|
Tags []Tag `gorm:"-" json:"tags,omitempty"`
|
|
}
|
|
|
|
// BeforeCreate sets default values
|
|
func (rt *RecurringTodo) BeforeCreate(tx *gorm.DB) error {
|
|
if rt.State == "" {
|
|
rt.State = RecurringTodoStateActive
|
|
}
|
|
if rt.EveryN == 0 {
|
|
rt.EveryN = 1
|
|
}
|
|
if rt.Target == "" {
|
|
rt.Target = "due_date"
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// IsActive returns true if the recurring todo is active
|
|
func (rt *RecurringTodo) IsActive() bool {
|
|
return rt.State == RecurringTodoStateActive
|
|
}
|
|
|
|
// IsCompleted returns true if the recurring todo is completed
|
|
func (rt *RecurringTodo) IsCompleted() bool {
|
|
return rt.State == RecurringTodoStateCompleted
|
|
}
|
|
|
|
// Complete marks the recurring todo as completed
|
|
func (rt *RecurringTodo) Complete() {
|
|
rt.State = RecurringTodoStateCompleted
|
|
}
|
|
|
|
// ShouldComplete returns true if the recurring todo has reached its end condition
|
|
func (rt *RecurringTodo) ShouldComplete() bool {
|
|
// Check if ended by date
|
|
if rt.EndsOn != nil && time.Now().After(*rt.EndsOn) {
|
|
return true
|
|
}
|
|
|
|
// Check if ended by occurrence count
|
|
if rt.OccurrencesCount != nil && rt.NumberOfOccurrences >= *rt.OccurrencesCount {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// IncrementOccurrences increments the occurrence counter
|
|
func (rt *RecurringTodo) IncrementOccurrences() {
|
|
rt.NumberOfOccurrences++
|
|
}
|