mirror of
https://gitea.com/gitea/tea.git
synced 2026-10-26 11:01:25 +01:00
tea time create 1 1h25m printed 2026-10-11T18:57:49.588823137Z while time list printed whole seconds, and "1h 30m" or 2d failed with "duration must be nonzero whole seconds". Durations may now contain spaces, and errors name the problem: "invalid duration "2d": use h, m and s units such as 1h25m". Tracked times are whole seconds everywhere. Signed-off-by: bircni <bircni@icloud.com>
111 lines
3.7 KiB
Go
111 lines
3.7 KiB
Go
// Copyright 2026 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package services
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
gitea "gitea.dev/sdk"
|
|
"gitea.dev/tea/internal/transport"
|
|
)
|
|
|
|
// TrackedTime is a stable record of seconds spent on an issue or pull request.
|
|
type TrackedTime struct {
|
|
ID int64 `json:"id"`
|
|
Seconds int64 `json:"seconds"`
|
|
CreatedAt string `json:"created_at"`
|
|
User string `json:"user"`
|
|
Repository string `json:"repository"`
|
|
IssueNumber int64 `json:"issue_number"`
|
|
IssueURL string `json:"issue_url"`
|
|
}
|
|
|
|
// trackedTimeFrom falls back to repository when the record does not name its issue's repository.
|
|
func trackedTimeFrom(record *gitea.TrackedTime, repository string) TrackedTime {
|
|
result := TrackedTime{ID: record.ID, Seconds: record.Time, User: record.UserName, Repository: repository}
|
|
if !record.Created.IsZero() {
|
|
// Gitea returns the creation time of a new record with nanoseconds, but whole seconds in listings.
|
|
result.CreatedAt = record.Created.Format(time.RFC3339)
|
|
}
|
|
if record.Issue != nil {
|
|
result.IssueNumber = record.Issue.Index
|
|
result.IssueURL = record.Issue.HTMLURL
|
|
if record.Issue.Repository != nil {
|
|
result.Repository = record.Issue.Repository.FullName
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// TimeOptions supplies an explicit scope, pagination and date filters.
|
|
type TimeOptions struct {
|
|
Mine bool
|
|
Issue int64
|
|
Username string
|
|
Limit, Page int
|
|
Since, Before *time.Time
|
|
}
|
|
|
|
// Times implements repository and account time tracking.
|
|
type Times struct {
|
|
Client *transport.Client
|
|
Repository string
|
|
}
|
|
|
|
// List returns a non-nil page of tracked time records.
|
|
func (s Times) List(ctx context.Context, opts TimeOptions) ([]TrackedTime, error) {
|
|
query := gitea.ListTrackedTimesOptions{ListOptions: listOptions(opts.Limit, opts.Page), User: opts.Username}
|
|
if opts.Since != nil {
|
|
query.Since = *opts.Since
|
|
}
|
|
if opts.Before != nil {
|
|
query.Before = *opts.Before
|
|
}
|
|
records, _, err := call(ctx, s.Client, func(ctx context.Context, c *gitea.Client) ([]*gitea.TrackedTime, *gitea.Response, error) {
|
|
if opts.Mine {
|
|
return c.Issues.ListMyTrackedTimes(ctx, query)
|
|
}
|
|
owner, repo, err := splitRepository(s.Repository)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
if opts.Issue != 0 {
|
|
return c.Issues.ListIssueTrackedTimes(ctx, owner, repo, opts.Issue, query)
|
|
}
|
|
return c.Issues.ListRepoTrackedTimes(ctx, owner, repo, query)
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return convertAll(records, func(record *gitea.TrackedTime) TrackedTime { return trackedTimeFrom(record, s.Repository) }), nil
|
|
}
|
|
|
|
// Create records a nonzero number of seconds on one issue.
|
|
func (s Times) Create(ctx context.Context, n, seconds int64) (TrackedTime, error) {
|
|
record, err := repoCall(ctx, s.Client, s.Repository, func(ctx context.Context, c *gitea.Client, owner, repo string) (*gitea.TrackedTime, *gitea.Response, error) {
|
|
return c.Issues.AddTime(ctx, owner, repo, n, gitea.AddTimeOption{Time: seconds})
|
|
})
|
|
if err != nil {
|
|
return TrackedTime{}, err
|
|
}
|
|
result := trackedTimeFrom(record, s.Repository)
|
|
result.IssueNumber = n
|
|
return result, nil
|
|
}
|
|
|
|
// Delete removes one tracked time record from the selected issue.
|
|
func (s Times) Delete(ctx context.Context, n, id int64) error {
|
|
return repoDo(ctx, s.Client, s.Repository, func(ctx context.Context, c *gitea.Client, owner, repo string) (*gitea.Response, error) {
|
|
return c.Issues.DeleteTime(ctx, owner, repo, n, id)
|
|
})
|
|
}
|
|
|
|
// Reset removes all tracked time records on the selected issue.
|
|
func (s Times) Reset(ctx context.Context, n int64) error {
|
|
return repoDo(ctx, s.Client, s.Repository, func(ctx context.Context, c *gitea.Client, owner, repo string) (*gitea.Response, error) {
|
|
return c.Issues.ResetIssueTime(ctx, owner, repo, n)
|
|
})
|
|
}
|