diff --git a/internal/adapters/db/db.go b/internal/adapters/db/db.go index 7bb3372..51b59f2 100644 --- a/internal/adapters/db/db.go +++ b/internal/adapters/db/db.go @@ -26,6 +26,7 @@ type Piece struct { PieceComplexity models.PieceComplexity State models.PieceState Practices []Practice + Users []*User `gorm:"many2many:user_practices;"` } type Lesson struct { @@ -35,6 +36,7 @@ type Lesson struct { StartDate time.Time EndDate time.Time Comment string + UserID uint } type Practice struct { @@ -47,6 +49,7 @@ type Practice struct { Piece Piece LessonID uint Lesson Lesson + UserID uint } type Warmup struct { @@ -57,6 +60,16 @@ type Warmup struct { State models.WarmupState LessonID uint Lesson Lesson + UserID uint +} + +type User struct { + gorm.Model + ID uint `gorm:"primary_key"` + Name string `gorm:"primary_key"` + Pieces []Piece `gorm:"many2many:user_pieces;"` + Practices []Practice `gorm:"many2many:user_practices;"` + Warmups []Warmup `gorm:"many2many:user_warmups;"` } type Adapter struct { @@ -71,7 +84,7 @@ func NewAdapter(dbUrl string) (*Adapter, error) { return nil, fmt.Errorf("Db connection error: %v", openErr) } if err := db.AutoMigrate( - &Composer{}, &Piece{}, &Practice{}, &Lesson{}, &Warmup{}, + &Composer{}, &Piece{}, &Practice{}, &Lesson{}, &Warmup{}, &User{}, ); err != nil { return nil, fmt.Errorf("Db migration error: %v", err) } @@ -304,3 +317,37 @@ func (a *Adapter) UpdatePractice(practice *models.Practice) error { res := a.db.Save(practice) return res.Error } + +func (a *Adapter) AddUser(user *models.User) (*models.User, error) { + userModel := User{ + Name: user.Name, + } + res := a.db.Create(&userModel) + if res.Error == nil { + user.ID = int64(userModel.ID) + } + return user, res.Error +} +func (a *Adapter) GetUser(id int64) (*models.User, error) { + var u User + res := a.db.First(&u, id) + user := models.User{ + ID: int64(u.ID), + Name: u.Name, + } + return &user, res.Error +} + +func (a *Adapter) GetUsers() ([]*models.User, error) { + var urs []User + var users []*models.User + a.db.Find(&urs) + for _, u := range urs { + user := models.User{ + ID: int64(u.ID), + Name: u.Name, + } + users = append(users, &user) + } + return users, nil +} diff --git a/internal/ports/db.go b/internal/ports/db.go index 0edac47..3b80d7a 100644 --- a/internal/ports/db.go +++ b/internal/ports/db.go @@ -22,4 +22,8 @@ type DBPort interface { AddWarmup(warmup *models.Warmup) error UpdateWarmup(warmup *models.Warmup) error GetActiveWarmup() (*models.Warmup, error) + // User + AddUser(user *models.User) (*models.User, error) + GetUser(id int64) (*models.User, error) + GetUsers() ([]*models.User, error) } diff --git a/models/user.go b/models/user.go new file mode 100644 index 0000000..63be04e --- /dev/null +++ b/models/user.go @@ -0,0 +1,26 @@ +package models + +import "slices" + +type User struct { + ID int64 `json:"id"` + Name string `json:"name"` + Pieces []*Piece `json:"pieces"` + Practices []*Practice `json:"practices"` +} + +func NewUser(name string) *User { + return &User{Name: name} +} + +func (u *User) AssignPiece(piece *Piece) { + if !slices.Contains(u.Pieces, piece) { + u.Pieces = append(u.Pieces, piece) + } +} + +func (u *User) PracticePiece(practice *Practice, lesson *Lesson) { + if !slices.Contains(u.Practices, practice) { + u.Practices = append(u.Practices, practice) + } +} diff --git a/models/user_test.go b/models/user_test.go new file mode 100644 index 0000000..3660b7b --- /dev/null +++ b/models/user_test.go @@ -0,0 +1,17 @@ +package models + +import ( + "slices" + "testing" +) + +func TestAssignPiece(t *testing.T) { + piece := NewPiece("test piece", "test composer", PieceComplexityEasy) + user := NewUser("test") + + user.AssignPiece(piece) + if !slices.Contains(user.Pieces, piece) { + t.Fatal("Piece wasn't added") + } + +} diff --git a/musgit.go b/musgit.go index 4ede0ea..f3bb7d8 100644 --- a/musgit.go +++ b/musgit.go @@ -13,6 +13,7 @@ type Musgit struct { Practice services.PracticeService Lesson services.LessonService Piece services.PieceService + User services.UserService } func New(dbUri string) *Musgit { @@ -23,10 +24,12 @@ func New(dbUri string) *Musgit { practiceService := services.NewPracticeService(dbAdapter) lessonService := services.NewLessonService(dbAdapter) pieceService := services.NewPieceService(dbAdapter) + userService := services.NewUserService(dbAdapter) return &Musgit{ db: dbAdapter, Practice: *practiceService, Lesson: *lessonService, Piece: *pieceService, + User: *userService, } } diff --git a/services/user.go b/services/user.go new file mode 100644 index 0000000..6c8ebcb --- /dev/null +++ b/services/user.go @@ -0,0 +1,69 @@ +package services + +import ( + "errors" + + "github.com/musgit-dev/musgit/internal/ports" + "github.com/musgit-dev/musgit/models" +) + +var ( + ErrUserNotFound = errors.New("User not found") +) + +type UserService struct { + db ports.DBPort +} + +func NewUserService(db ports.DBPort) *UserService { + return &UserService{db: db} +} + +func (s *UserService) GetAll() ([]*models.User, error) { + users, err := s.db.GetUsers() + if err != nil { + return []*models.User{}, err + } + return users, nil +} + +func (s *UserService) Get(id int64) (*models.User, error) { + user, err := s.db.GetUser(id) + if err != nil { + return &models.User{}, err + } + return user, nil +} + +func (s *UserService) Add(name string) (*models.User, error) { + u := models.NewUser(name) + u, err := s.db.AddUser(u) + if err != nil { + return u, err + } + return u, nil +} + +func (s *UserService) AssignPiece(userId int64, piece models.Piece) error { + user, err := s.Get(userId) + if err != nil { + return ErrUserNotFound + } + user.AssignPiece(&piece) + return nil +} + +func (s *UserService) StartPractice( + userId, pieceId, lessonId int64, +) (*models.Practice, error) { + var practice *models.Practice + user, err := s.Get(userId) + if err != nil { + return practice, ErrUserNotFound + } + piece, err := s.db.GetPiece(pieceId) + lesson, err := s.db.GetLesson(lessonId) + practice, err = piece.StartPractice() + user.PracticePiece(practice, &lesson) + return practice, err +} diff --git a/services/user_test.go b/services/user_test.go new file mode 100644 index 0000000..82ea459 --- /dev/null +++ b/services/user_test.go @@ -0,0 +1,60 @@ +package services + +import ( + "fmt" + "os" + "testing" + + "github.com/musgit-dev/musgit/internal/adapters/db" + "github.com/musgit-dev/musgit/models" +) + +var service *UserService +var pieceService *PieceService + +func TestMain(m *testing.M) { + dbPort, err := db.NewAdapter(":memory:") + if err != nil { + os.Exit(1) + } + pieceService = NewPieceService(dbPort) + + piece, err := pieceService.Add( + "test_piece", + "test_composer", + models.PieceComplexityUnknown, + ) + if err != nil { + os.Exit(1) + } + fmt.Println("Added piece", piece.ID) + service = NewUserService(dbPort) + exitVal := m.Run() + os.Exit(exitVal) +} + +func TestAdd(t *testing.T) { + user, err := service.Add("test_user") + if err != nil { + t.Fatal(err) + } + if user.Name != "test_user" { + t.Fatalf("Unexpected user name, got %s, expected test user", user.Name) + } +} + +func TestAssignPiece(t *testing.T) { + user, err := service.Add("test_user") + if err != nil { + t.Fatal(err) + } + piece, _ := pieceService.Get(1) + if piece.ID != 1 { + t.Fatal("Unknown piece") + } + user.AssignPiece(&piece) + if len(user.Pieces) != 1 { + t.Fatalf("Piece wasn't added") + } + +}