207 lines
4.7 KiB
Go
207 lines
4.7 KiB
Go
package repoService
|
|
|
|
import (
|
|
"fmt"
|
|
"slices"
|
|
|
|
"git.ma-al.com/goc_daniel/b2b/app/db"
|
|
"git.ma-al.com/goc_daniel/b2b/app/model"
|
|
"git.ma-al.com/goc_daniel/b2b/app/utils/pagination"
|
|
"git.ma-al.com/goc_daniel/b2b/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
|
|
}
|