blob: 7ca2ff9fbda1e71129bf3b3f212a9458f2b95344 [file] [log] [blame]
package handlers
import (
"bytes"
"encoding/json"
"chromium.googlesource.com/infra/rotang"
"io"
"net/http"
"strconv"
"strings"
"time"
"go.chromium.org/luci/common/clock"
"go.chromium.org/luci/common/logging"
"go.chromium.org/luci/server/router"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
const elementTimeFormat = "2006-01-02"
// HandleGenerate generates rota schedules.
// Used by the `shiftgenerate-element`
func (h *State) HandleGenerate(ctx *router.Context) {
rota, err := h.rota(ctx)
if err != nil {
http.Error(ctx.Writer, err.Error(), http.StatusInternalServerError)
return
}
var start time.Time
var shifts []rotang.ShiftEntry
switch ctx.Request.FormValue("startTime") {
case "":
var err error
if shifts, err = h.shiftStore(ctx.Context).AllShifts(ctx.Context, rota.Config.Name); err != nil {
if status.Code(err) != codes.NotFound {
http.Error(ctx.Writer, err.Error(), http.StatusInternalServerError)
return
}
start = clock.Now(ctx.Context)
}
default:
var err error
if start, err = time.ParseInLocation(elementTimeFormat, ctx.Request.FormValue("startTime"), mtvTime); err != nil {
http.Error(ctx.Writer, err.Error(), http.StatusBadRequest)
return
}
}
memberStore := h.memberStore(ctx.Context)
var members []rotang.Member
for _, m := range rota.Members {
m, err := memberStore.Member(ctx.Context, m.Email)
if err != nil {
http.Error(ctx.Writer, err.Error(), http.StatusInternalServerError)
return
}
members = append(members, *m)
}
nrSchedStr := ctx.Request.FormValue("nrShifts")
if nrSchedStr == "" {
nrSchedStr = strconv.Itoa(rota.Config.ShiftsToSchedule)
}
nrSched, err := strconv.Atoi(nrSchedStr)
if err != nil {
http.Error(ctx.Writer, err.Error(), http.StatusInternalServerError)
return
}
generator := ctx.Request.FormValue("generator")
if generator == "" {
generator = rota.Config.Shifts.Generator
}
g, err := h.generators.Fetch(generator)
if err != nil {
http.Error(ctx.Writer, err.Error(), http.StatusInternalServerError)
return
}
ss, err := g.Generate(rota, start, shifts, members, nrSched)
if err != nil {
http.Error(ctx.Writer, err.Error(), http.StatusInternalServerError)
return
}
// TODO(olakar): Remove this when rotation TZ is implemented.
rota.Config.Shifts.TZ = *mtvTime
modifiers := strings.Split(ctx.Request.FormValue("modifiers"), ",")
for _, m := range modifiers {
if m == "" {
continue
}
mod, err := h.generators.FetchModifier(m)
if err != nil {
http.Error(ctx.Writer, err.Error(), http.StatusInternalServerError)
return
}
if ss, err = mod.Modify(&rota.Config.Shifts, ss); err != nil {
http.Error(ctx.Writer, err.Error(), http.StatusInternalServerError)
return
}
logging.Infof(ctx.Context, "modifier: %q applied to generated shifts for rota: %q", m, rota.Config.Name)
}
res := RotaShifts{
Rota: rota.Config.Name,
SplitShifts: makeSplitShifts(ss, rota.Members),
}
var resBuf bytes.Buffer
if err := json.NewEncoder(&resBuf).Encode(res); err != nil {
http.Error(ctx.Writer, err.Error(), http.StatusInternalServerError)
return
}
io.Copy(ctx.Writer, &resBuf)
}