1
0
mirror of https://github.com/muety/wakapi.git synced 2023-08-10 21:12:56 +03:00

refactor: split utility functions into utils and helpers

This commit is contained in:
Ferdinand Mütsch
2022-12-01 10:57:07 +01:00
parent c5fda02900
commit 21f6809f05
35 changed files with 259 additions and 236 deletions

49
helpers/date.go Normal file
View File

@@ -0,0 +1,49 @@
package helpers
import (
"fmt"
"github.com/muety/wakapi/config"
"time"
)
// ParseDateTimeTZ attempts to parse the given date string from multiple formats.
// First, a time-zoned date-time string (e.g. 2006-01-02T15:04:05+02:00) is tried
// Second, a non-time-zoned date-time string (e.g. 2006-01-02 15:04:05) is tried at the given zone
// Third, a non-time-zoned date string (e.g. 2006-01-02) is tried at the given zone
// Example:
// - Server runs in CEST (UTC+2), requesting user lives in PDT (UTC-7).
// - 2021-04-25T10:30:00Z, 2021-04-25T3:30:00-0100 and 2021-04-25T12:30:00+0200 are equivalent, they represent the same point in time
// - When user requests non-time-zoned range (e.g. 2021-04-25T00:00:00), but has their time zone properly configured, this will resolve to 2021-04-25T09:00:00
func ParseDateTimeTZ(date string, tz *time.Location) (time.Time, error) {
if t, err := time.Parse(time.RFC3339, date); err == nil {
return t, nil
}
if t, err := time.ParseInLocation(config.SimpleDateTimeFormat, date, tz); err == nil {
return t, nil
}
return time.ParseInLocation(config.SimpleDateFormat, date, tz)
}
func FormatDate(date time.Time) string {
return date.Format(config.SimpleDateFormat)
}
func FormatDateTime(date time.Time) string {
return date.Format(config.SimpleDateTimeFormat)
}
func FormatDateTimeHuman(date time.Time) string {
return date.Format("Mon, 02 Jan 2006 15:04")
}
func FormatDateHuman(date time.Time) string {
return date.Format("Mon, 02 Jan 2006")
}
func FmtWakatimeDuration(d time.Duration) string {
d = d.Round(time.Minute)
h := d / time.Hour
d -= h * time.Hour
m := d / time.Minute
return fmt.Sprintf("%d hrs %d mins", h, m)
}

4
helpers/helpers.go Normal file
View File

@@ -0,0 +1,4 @@
package helpers
// helpers are different from utils in that they contain wakapi-specific utility functions
// also, helpers may depend on the config package, while utils must be entirely static

20
helpers/http.go Normal file
View File

@@ -0,0 +1,20 @@
package helpers
import (
"encoding/json"
"github.com/muety/wakapi/config"
"github.com/muety/wakapi/utils"
"net/http"
)
func ExtractCookieAuth(r *http.Request) (username *string, err error) {
return utils.ExtractCookieAuth(r, config.Get().Security.SecureCookie)
}
func RespondJSON(w http.ResponseWriter, r *http.Request, status int, object interface{}) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
if err := json.NewEncoder(w).Encode(object); err != nil {
config.Log().Request(r).Error("error while writing json response: %v", err)
}
}

147
helpers/summary.go Normal file
View File

@@ -0,0 +1,147 @@
package helpers
import (
"errors"
"github.com/muety/wakapi/models"
"github.com/muety/wakapi/utils"
"net/http"
"time"
)
func ParseSummaryParams(r *http.Request) (*models.SummaryParams, error) {
user := extractUser(r)
params := r.URL.Query()
var err error
var from, to time.Time
if interval := params.Get("interval"); interval != "" {
err, from, to = ResolveIntervalRawTZ(interval, user.TZ())
} else if start := params.Get("start"); start != "" {
err, from, to = ResolveIntervalRawTZ(start, user.TZ())
} else {
from, err = ParseDateTimeTZ(params.Get("from"), user.TZ())
if err != nil {
return nil, errors.New("missing or invalid 'from' parameter")
}
to, err = ParseDateTimeTZ(params.Get("to"), user.TZ())
if err != nil {
return nil, errors.New("missing or invalid 'to' parameter")
}
}
recompute := params.Get("recompute") != "" && params.Get("recompute") != "false"
filters := ParseSummaryFilters(r)
return &models.SummaryParams{
From: from,
To: to,
User: user,
Recompute: recompute,
Filters: filters,
}, nil
}
func ParseSummaryFilters(r *http.Request) *models.Filters {
filters := &models.Filters{}
if q := r.URL.Query().Get("project"); q != "" {
filters.With(models.SummaryProject, q)
}
if q := r.URL.Query().Get("language"); q != "" {
filters.With(models.SummaryLanguage, q)
}
if q := r.URL.Query().Get("editor"); q != "" {
filters.With(models.SummaryEditor, q)
}
if q := r.URL.Query().Get("machine"); q != "" {
filters.With(models.SummaryMachine, q)
}
if q := r.URL.Query().Get("operating_system"); q != "" {
filters.With(models.SummaryOS, q)
}
if q := r.URL.Query().Get("label"); q != "" {
filters.With(models.SummaryLabel, q)
}
if q := r.URL.Query().Get("branch"); q != "" {
filters.With(models.SummaryBranch, q)
}
return filters
}
func extractUser(r *http.Request) *models.User {
type principalGetter interface {
GetPrincipal() *models.User
}
if p := r.Context().Value("principal"); p != nil {
return p.(principalGetter).GetPrincipal()
}
return nil
}
func ParseInterval(interval string) (*models.IntervalKey, error) {
for _, i := range models.AllIntervals {
if i.HasAlias(interval) {
return i, nil
}
}
return nil, errors.New("not a valid interval")
}
func MustResolveIntervalRawTZ(interval string, tz *time.Location) (from, to time.Time) {
_, from, to = ResolveIntervalRawTZ(interval, tz)
return from, to
}
func ResolveIntervalRawTZ(interval string, tz *time.Location) (err error, from, to time.Time) {
parsed, err := ParseInterval(interval)
if err != nil {
return err, time.Time{}, time.Time{}
}
return ResolveIntervalTZ(parsed, tz)
}
func ResolveIntervalTZ(interval *models.IntervalKey, tz *time.Location) (err error, from, to time.Time) {
now := time.Now().In(tz)
to = now
switch interval {
case models.IntervalToday:
from = utils.BeginOfToday(tz)
case models.IntervalYesterday:
from = utils.BeginOfToday(tz).Add(-24 * time.Hour)
to = utils.BeginOfToday(tz)
case models.IntervalThisWeek:
from = utils.BeginOfThisWeek(tz)
case models.IntervalLastWeek:
from = utils.BeginOfThisWeek(tz).AddDate(0, 0, -7)
to = utils.BeginOfThisWeek(tz)
case models.IntervalThisMonth:
from = utils.BeginOfThisMonth(tz)
case models.IntervalLastMonth:
from = utils.BeginOfThisMonth(tz).AddDate(0, -1, 0)
to = utils.BeginOfThisMonth(tz)
case models.IntervalThisYear:
from = utils.BeginOfThisYear(tz)
case models.IntervalPast7Days:
from = now.AddDate(0, 0, -7)
case models.IntervalPast7DaysYesterday:
from = utils.BeginOfToday(tz).AddDate(0, 0, -1).AddDate(0, 0, -7)
to = utils.BeginOfToday(tz).AddDate(0, 0, -1)
case models.IntervalPast14Days:
from = now.AddDate(0, 0, -14)
case models.IntervalPast30Days:
from = now.AddDate(0, 0, -30)
case models.IntervalPast6Months:
from = now.AddDate(0, -6, 0)
case models.IntervalPast12Months:
from = now.AddDate(0, -12, 0)
case models.IntervalAny:
from = time.Time{}
default:
err = errors.New("invalid interval")
}
return err, from, to
}