package repoService import ( "fmt" "slices" "git.ma-al.com/goc_marek/timetracker/app/db" "git.ma-al.com/goc_marek/timetracker/app/model" "git.ma-al.com/goc_marek/timetracker/app/utils/pagination" "git.ma-al.com/goc_marek/timetracker/app/utils/responseErrors" "gorm.io/gorm" ) // type type RepoService struct { db *gorm.DB } func New() *RepoService { return &RepoService{ db: db.Get(), } } func (s *RepoService) GetRepositoriesForUser(userID uint) ([]uint, error) { var repoIDs []uint err := s.db. Table("customer_repo_accesses"). Where("user_id = ?", userID). Pluck("repo_id", &repoIDs).Error if err != nil { return nil, fmt.Errorf("database error: %w", err) } return repoIDs, nil } func (s *RepoService) UserHasAccessToRepo(userID uint, repoID uint) (bool, error) { var repositories []uint var err error if repositories, err = s.GetRepositoriesForUser(userID); err != nil { return false, err } if !slices.Contains(repositories, repoID) { return false, responseErrors.ErrInvalidRepoID } return true, nil } // Extract all repositories assigned to user with specific id func (s *RepoService) GetYearsForUser(userID uint, repoID uint) ([]uint, error) { if ok, err := s.UserHasAccessToRepo(userID, repoID); !ok { return nil, err } years, err := s.GetYears(repoID) if err != nil { return nil, fmt.Errorf("database error: %w", err) } return years, nil } func (s *RepoService) GetYears(repo uint) ([]uint, error) { var years []uint query := ` WITH bounds AS ( SELECT MIN(to_timestamp(tt.created_unix)) AS min_ts, MAX(to_timestamp(tt.created_unix)) AS max_ts FROM tracked_time tt JOIN issue i ON i.id = tt.issue_id WHERE i.repo_id = ? AND tt.deleted = false ) SELECT EXTRACT(YEAR FROM y.year_start)::int AS year FROM bounds CROSS JOIN LATERAL generate_series( date_trunc('year', min_ts), date_trunc('year', max_ts), interval '1 year' ) AS y(year_start) ORDER BY year ` err := db.Get().Raw(query, repo).Find(&years).Error if err != nil { return nil, err } return years, nil } // Extract all repositories assigned to user with specific id func (s *RepoService) GetQuartersForUser(userID uint, repoID uint, year uint) ([]model.QuarterData, error) { if ok, err := s.UserHasAccessToRepo(userID, repoID); !ok { return nil, err } response, err := s.GetQuarters(repoID, year) if err != nil { return nil, fmt.Errorf("database error: %w", err) } return response, nil } func (s *RepoService) GetQuarters(repo uint, year uint) ([]model.QuarterData, error) { var quarters []model.QuarterData query := ` WITH quarters AS ( SELECT make_date(?::int, 1, 1) + (q * interval '3 months') AS quarter_start, q + 1 AS quarter FROM generate_series(0, 3) AS q ), data AS ( SELECT EXTRACT(QUARTER FROM to_timestamp(tt.created_unix)) AS quarter, SUM(tt.time) / 3600 AS time FROM tracked_time tt JOIN issue i ON i.id = tt.issue_id JOIN repository r ON i.repo_id = r.id WHERE EXTRACT(YEAR FROM to_timestamp(tt.created_unix)) = ? AND r.id = ? AND tt.deleted = false GROUP BY EXTRACT(QUARTER FROM to_timestamp(tt.created_unix)) ) SELECT COALESCE(d.time, 0) AS time, CONCAT(EXTRACT(YEAR FROM q.quarter_start)::int, '_Q', q.quarter) AS quarter FROM quarters q LEFT JOIN data d ON d.quarter = q.quarter ORDER BY q.quarter ` err := db.Get(). Raw(query, year, year, repo). Find(&quarters). Error if err != nil { return nil, err } return quarters, nil } func (s *RepoService) GetIssuesForUser( userID uint, repoID uint, year uint, quarter uint, p pagination.Paging, ) (*pagination.Found[model.IssueTimeSummary], error) { if ok, err := s.UserHasAccessToRepo(userID, repoID); !ok { return nil, err } return s.GetIssues(repoID, year, quarter, p) } func (s *RepoService) GetIssues( repoId uint, year uint, quarter uint, p pagination.Paging, ) (*pagination.Found[model.IssueTimeSummary], error) { query := db.Get(). Table("issue i"). Select(` i.id AS issue_id, i.name AS issue_name, to_timestamp(i.created_unix) AS issue_created_at, ROUND(SUM(tt.time) / 3600.0, 2) AS total_hours_spent `). Joins(`JOIN tracked_time tt ON tt.issue_id = i.id`). Joins(`JOIN "user" u ON u.id = tt.user_id`). Where("i.repo_id = ?", repoId). Where(` EXTRACT(YEAR FROM to_timestamp(tt.created_unix)) = ? AND EXTRACT(QUARTER FROM to_timestamp(tt.created_unix)) = ? `, year, quarter). Group(` i.id, i.name, u.id, u.full_name `). Order("i.created_unix") result, err := pagination.Paginate[model.IssueTimeSummary](p, query) if err != nil { return nil, err } return &result, nil }