| OLD | NEW |
| 1 // Copyright 2015 The LUCI Authors. All rights reserved. | 1 // Copyright 2015 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
| 4 | 4 |
| 5 package ui | 5 package ui |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "bytes" | 8 "bytes" |
| 9 "fmt" | 9 "fmt" |
| 10 "sort" | 10 "sort" |
| 11 "strings" | |
| 12 "time" | 11 "time" |
| 13 | 12 |
| 14 "github.com/dustin/go-humanize" | 13 "github.com/dustin/go-humanize" |
| 15 "github.com/golang/protobuf/proto" | 14 "github.com/golang/protobuf/proto" |
| 16 "golang.org/x/net/context" | 15 "golang.org/x/net/context" |
| 17 | 16 |
| 18 "github.com/luci/luci-go/common/clock" | 17 "github.com/luci/luci-go/common/clock" |
| 19 "github.com/luci/luci-go/common/logging" | 18 "github.com/luci/luci-go/common/logging" |
| 20 | 19 |
| 21 "github.com/luci/luci-go/common/data/sortby" | 20 "github.com/luci/luci-go/common/data/sortby" |
| 22 "github.com/luci/luci-go/scheduler/appengine/catalog" | 21 "github.com/luci/luci-go/scheduler/appengine/catalog" |
| 23 "github.com/luci/luci-go/scheduler/appengine/engine" | 22 "github.com/luci/luci-go/scheduler/appengine/engine" |
| 24 "github.com/luci/luci-go/scheduler/appengine/messages" | 23 "github.com/luci/luci-go/scheduler/appengine/messages" |
| 25 "github.com/luci/luci-go/scheduler/appengine/presentation" | 24 "github.com/luci/luci-go/scheduler/appengine/presentation" |
| 26 "github.com/luci/luci-go/scheduler/appengine/schedule" | 25 "github.com/luci/luci-go/scheduler/appengine/schedule" |
| 27 "github.com/luci/luci-go/scheduler/appengine/task" | 26 "github.com/luci/luci-go/scheduler/appengine/task" |
| 28 ) | 27 ) |
| 29 | 28 |
| 30 // schedulerJob is UI representation of engine.Job entity. | 29 // schedulerJob is UI representation of engine.Job entity. |
| 31 type schedulerJob struct { | 30 type schedulerJob struct { |
| 32 ProjectID string | 31 ProjectID string |
| 33 » JobID string | 32 » JobName string |
| 34 Schedule string | 33 Schedule string |
| 35 Definition string | 34 Definition string |
| 36 Revision string | 35 Revision string |
| 37 RevisionURL string | 36 RevisionURL string |
| 38 State string | 37 State string |
| 39 Overruns int | 38 Overruns int |
| 40 NextRun string | 39 NextRun string |
| 41 Paused bool | 40 Paused bool |
| 42 LabelClass string | 41 LabelClass string |
| 43 JobFlavorIcon string | 42 JobFlavorIcon string |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 96 // Job invocation is still in the task queue, but new invocation
should be | 95 // Job invocation is still in the task queue, but new invocation
should be |
| 97 // starting now (so the queue is lagging for some reason). | 96 // starting now (so the queue is lagging for some reason). |
| 98 labelClass = "label-warning" | 97 labelClass = "label-warning" |
| 99 } | 98 } |
| 100 // Put triggers after regular jobs. | 99 // Put triggers after regular jobs. |
| 101 sortGroup := "A" | 100 sortGroup := "A" |
| 102 if j.Flavor == catalog.JobFlavorTrigger { | 101 if j.Flavor == catalog.JobFlavorTrigger { |
| 103 sortGroup = "B" | 102 sortGroup = "B" |
| 104 } | 103 } |
| 105 | 104 |
| 106 // JobID has form <project>/<id>. Split it into components. | |
| 107 chunks := strings.Split(j.JobID, "/") | |
| 108 | |
| 109 return &schedulerJob{ | 105 return &schedulerJob{ |
| 110 » » ProjectID: chunks[0], | 106 » » ProjectID: j.ProjectID, |
| 111 » » JobID: chunks[1], | 107 » » JobName: j.GetJobName(), |
| 112 Schedule: j.Schedule, | 108 Schedule: j.Schedule, |
| 113 Definition: taskToText(j.Task), | 109 Definition: taskToText(j.Task), |
| 114 Revision: j.Revision, | 110 Revision: j.Revision, |
| 115 RevisionURL: j.RevisionURL, | 111 RevisionURL: j.RevisionURL, |
| 116 State: string(state), | 112 State: string(state), |
| 117 Overruns: j.State.Overruns, | 113 Overruns: j.State.Overruns, |
| 118 NextRun: nextRun, | 114 NextRun: nextRun, |
| 119 Paused: j.Paused, | 115 Paused: j.Paused, |
| 120 LabelClass: labelClass, | 116 LabelClass: labelClass, |
| 121 JobFlavorIcon: flavorToIconClass[j.Flavor], | 117 JobFlavorIcon: flavorToIconClass[j.Flavor], |
| (...skipping 21 matching lines...) Expand all Loading... |
| 143 } | 139 } |
| 144 | 140 |
| 145 type sortedJobs []*schedulerJob | 141 type sortedJobs []*schedulerJob |
| 146 | 142 |
| 147 func (s sortedJobs) Len() int { return len(s) } | 143 func (s sortedJobs) Len() int { return len(s) } |
| 148 func (s sortedJobs) Swap(i, j int) { s[i], s[j] = s[j], s[i] } | 144 func (s sortedJobs) Swap(i, j int) { s[i], s[j] = s[j], s[i] } |
| 149 func (s sortedJobs) Less(i, j int) bool { | 145 func (s sortedJobs) Less(i, j int) bool { |
| 150 return sortby.Chain{ | 146 return sortby.Chain{ |
| 151 func(i, j int) bool { return s[i].ProjectID < s[j].ProjectID }, | 147 func(i, j int) bool { return s[i].ProjectID < s[j].ProjectID }, |
| 152 func(i, j int) bool { return s[i].sortGroup < s[j].sortGroup }, | 148 func(i, j int) bool { return s[i].sortGroup < s[j].sortGroup }, |
| 153 » » func(i, j int) bool { return s[i].JobID < s[j].JobID }, | 149 » » func(i, j int) bool { return s[i].JobName < s[j].JobName }, |
| 154 }.Use(i, j) | 150 }.Use(i, j) |
| 155 } | 151 } |
| 156 | 152 |
| 157 // sortJobs instantiate a bunch of schedulerJob objects and sorts them in | 153 // sortJobs instantiate a bunch of schedulerJob objects and sorts them in |
| 158 // display order. | 154 // display order. |
| 159 func sortJobs(c context.Context, jobs []*engine.Job) sortedJobs { | 155 func sortJobs(c context.Context, jobs []*engine.Job) sortedJobs { |
| 160 out := make(sortedJobs, len(jobs)) | 156 out := make(sortedJobs, len(jobs)) |
| 161 for i, job := range jobs { | 157 for i, job := range jobs { |
| 162 out[i] = makeJob(c, job) | 158 out[i] = makeJob(c, job) |
| 163 } | 159 } |
| 164 sort.Sort(out) | 160 sort.Sort(out) |
| 165 return out | 161 return out |
| 166 } | 162 } |
| 167 | 163 |
| 168 // invocation is UI representation of engine.Invocation entity. | 164 // invocation is UI representation of engine.Invocation entity. |
| 169 type invocation struct { | 165 type invocation struct { |
| 170 ProjectID string | 166 ProjectID string |
| 171 » JobID string | 167 » JobName string |
| 172 InvID int64 | 168 InvID int64 |
| 173 Attempt int64 | 169 Attempt int64 |
| 174 Revision string | 170 Revision string |
| 175 RevisionURL string | 171 RevisionURL string |
| 176 Definition string | 172 Definition string |
| 177 TriggeredBy string | 173 TriggeredBy string |
| 178 Started string | 174 Started string |
| 179 Duration string | 175 Duration string |
| 180 Status string | 176 Status string |
| 181 DebugLog string | 177 DebugLog string |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 225 if finished.IsZero() { | 221 if finished.IsZero() { |
| 226 finished = j.now | 222 finished = j.now |
| 227 } | 223 } |
| 228 duration := humanize.RelTime(i.Started, finished, "", "") | 224 duration := humanize.RelTime(i.Started, finished, "", "") |
| 229 if duration == "now" { | 225 if duration == "now" { |
| 230 duration = "1 second" // "now" looks weird for durations | 226 duration = "1 second" // "now" looks weird for durations |
| 231 } | 227 } |
| 232 | 228 |
| 233 return &invocation{ | 229 return &invocation{ |
| 234 ProjectID: j.ProjectID, | 230 ProjectID: j.ProjectID, |
| 235 » » JobID: j.JobID, | 231 » » JobName: j.JobName, |
| 236 InvID: i.ID, | 232 InvID: i.ID, |
| 237 Attempt: i.RetryCount + 1, | 233 Attempt: i.RetryCount + 1, |
| 238 Revision: i.Revision, | 234 Revision: i.Revision, |
| 239 RevisionURL: i.RevisionURL, | 235 RevisionURL: i.RevisionURL, |
| 240 Definition: taskToText(i.Task), | 236 Definition: taskToText(i.Task), |
| 241 TriggeredBy: triggeredBy, | 237 TriggeredBy: triggeredBy, |
| 242 Started: humanize.RelTime(i.Started, j.now, "ago", "from now
"), | 238 Started: humanize.RelTime(i.Started, j.now, "ago", "from now
"), |
| 243 Duration: duration, | 239 Duration: duration, |
| 244 Status: string(status), | 240 Status: string(status), |
| 245 DebugLog: i.DebugLog, | 241 DebugLog: i.DebugLog, |
| 246 RowClass: statusToRowClass[status], | 242 RowClass: statusToRowClass[status], |
| 247 LabelClass: statusToLabelClass[status], | 243 LabelClass: statusToLabelClass[status], |
| 248 ViewURL: i.ViewURL, | 244 ViewURL: i.ViewURL, |
| 249 } | 245 } |
| 250 } | 246 } |
| OLD | NEW |