github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/service/stats.go (about) 1 package service 2 3 import ( 4 "net/http" 5 "time" 6 7 "github.com/evergreen-ci/evergreen" 8 "github.com/evergreen-ci/evergreen/model/build" 9 "github.com/evergreen-ci/evergreen/model/patch" 10 "github.com/evergreen-ci/evergreen/model/task" 11 "github.com/evergreen-ci/evergreen/model/user" 12 "github.com/evergreen-ci/evergreen/model/version" 13 "github.com/gorilla/mux" 14 "github.com/pkg/errors" 15 ) 16 17 // UIBuildVariant contains the name of the build variant and the tasks associated with that build variant. 18 type UIBuildVariant struct { 19 Name string `json:"name"` 20 TaskNames []string `json:"task_names"` 21 } 22 23 // UIProject has all the tasks that are in a project and all the BuildVariants. 24 type UIProject struct { 25 Name string `json:"name"` 26 BuildVariants []UIBuildVariant `json:"build_variants"` 27 TaskNames []string `json:"task_names"` 28 } 29 30 //UITask has the fields that are necessary to send over the wire for tasks 31 type UITask struct { 32 Id string `json:"id"` 33 CreateTime time.Time `json:"create_time"` 34 DispatchTime time.Time `json:"dispatch_time"` 35 PushTime time.Time `json:"push_time"` 36 ScheduledTime time.Time `json:"scheduled_time"` 37 StartTime time.Time `json:"start_time"` 38 FinishTime time.Time `json:"finish_time"` 39 Version string `json:"version"` 40 Status string `json:"status"` 41 Host string `json:"host"` 42 Distro string `json:"distro"` 43 } 44 45 //UIBuild has the fields that are necessary to send over the wire for builds 46 type UIBuild struct { 47 Id string `json:"id"` 48 CreateTime time.Time `json:"create_time"` 49 StartTime time.Time `json:"start_time"` 50 FinishTime time.Time `json:"finish_time"` 51 Version string `json:"version"` 52 Status string `json:"status"` 53 Tasks []build.TaskCache `json:"tasks"` 54 TimeTaken int64 `json:"time_taken"` 55 } 56 57 // UIStats is all of the data that the stats page might need. 58 type UIStats struct { 59 Tasks []*UITask `json:"tasks"` 60 Builds []*UIBuild `json:"builds"` 61 Versions []version.Version `json:"versions"` 62 Patches []patch.Patch `json:"patches"` 63 } 64 65 func (uis *UIServer) taskTimingPage(w http.ResponseWriter, r *http.Request) { 66 projCtx := MustHaveProjectContext(r) 67 68 if projCtx.Project == nil { 69 uis.ProjectNotFound(projCtx, w, r) 70 return 71 } 72 73 currentProject := UIProject{projCtx.Project.Identifier, []UIBuildVariant{}, []string{}} 74 75 // populate buildVariants by iterating over the build variants tasks 76 for _, bv := range projCtx.Project.BuildVariants { 77 newBv := UIBuildVariant{bv.Name, []string{}} 78 for _, task := range bv.Tasks { 79 newBv.TaskNames = append(newBv.TaskNames, task.Name) 80 } 81 currentProject.BuildVariants = append(currentProject.BuildVariants, newBv) 82 } 83 for _, task := range projCtx.Project.Tasks { 84 currentProject.TaskNames = append(currentProject.TaskNames, task.Name) 85 } 86 87 data := struct { 88 ProjectData projectContext 89 User *user.DBUser 90 Project UIProject 91 }{projCtx, GetUser(r), currentProject} 92 93 uis.WriteHTML(w, http.StatusOK, data, "base", "task_timing.html", "base_angular.html", "menu.html") 94 } 95 96 // taskTimingJSON sends over the task data for a certain task of a certain build variant 97 func (uis *UIServer) taskTimingJSON(w http.ResponseWriter, r *http.Request) { 98 projCtx := MustHaveProjectContext(r) 99 beforeTaskId := r.FormValue("before") 100 101 limit, err := getIntValue(r, "limit", 50) 102 if err != nil { 103 uis.LoggedError(w, r, http.StatusBadRequest, err) 104 return 105 } 106 107 buildVariant := mux.Vars(r)["build_variant"] 108 taskName := mux.Vars(r)["task_name"] 109 request := mux.Vars(r)["request"] 110 111 if projCtx.Project == nil { 112 uis.LoggedError(w, r, http.StatusNotFound, errors.New("not found")) 113 return 114 } 115 116 bv := projCtx.Project.FindBuildVariant(buildVariant) 117 if bv == nil { 118 uis.LoggedError(w, r, http.StatusNotFound, errors.Errorf("build variant %v not found", buildVariant)) 119 return 120 } 121 var versionIds []string 122 data := UIStats{} 123 124 // if its all tasks find the build 125 if taskName == "" || taskName == "All Tasks" { 126 // TODO: switch this to be a query on the builds TaskCache 127 builds, err := build.Find(build.ByProjectAndVariant(projCtx.Project.Identifier, buildVariant, request). 128 WithFields(build.IdKey, build.CreateTimeKey, build.VersionKey, 129 build.TimeTakenKey, build.TasksKey, build.FinishTimeKey, build.StartTimeKey, build.StatusKey). 130 Sort([]string{"-" + build.RevisionOrderNumberKey}). 131 Limit(limit)) 132 if err != nil { 133 uis.LoggedError(w, r, http.StatusBadRequest, err) 134 return 135 } 136 versionIds = make([]string, 0, len(builds)) 137 uiBuilds := []*UIBuild{} 138 // get the versions for every single task that was returned 139 for _, build := range builds { 140 141 // create a UITask 142 b := &UIBuild{ 143 Id: build.Id, 144 CreateTime: build.CreateTime, 145 StartTime: build.StartTime, 146 FinishTime: build.FinishTime, 147 Version: build.Version, 148 Status: build.Status, 149 TimeTaken: int64(build.TimeTaken), 150 Tasks: build.Tasks, 151 } 152 uiBuilds = append(uiBuilds, b) 153 versionIds = append(versionIds, b.Version) 154 } 155 156 data.Builds = uiBuilds 157 158 } else { 159 foundTask := false 160 161 for _, t := range bv.Tasks { 162 if t.Name == taskName { 163 foundTask = true 164 break 165 } 166 } 167 168 if !foundTask { 169 uis.LoggedError(w, r, http.StatusNotFound, errors.Errorf("no task named '%v'", taskName)) 170 return 171 } 172 var tasks []task.Task 173 174 fields := []string{task.CreateTimeKey, task.DispatchTimeKey, task.PushTimeKey, 175 task.ScheduledTimeKey, task.StartTimeKey, task.FinishTimeKey, 176 task.VersionKey, task.HostIdKey, task.StatusKey, task.HostIdKey, 177 task.DistroIdKey} 178 179 if beforeTaskId != "" { 180 t, err := task.FindOne(task.ById(beforeTaskId)) 181 if err != nil { 182 uis.LoggedError(w, r, http.StatusNotFound, err) 183 return 184 } 185 if t == nil { 186 uis.LoggedError(w, r, http.StatusNotFound, errors.Errorf("Task %v not found", beforeTaskId)) 187 return 188 } 189 190 tasks, err = task.Find(task.ByBeforeRevisionWithStatusesAndRequester(t.RevisionOrderNumber, evergreen.CompletedStatuses, 191 buildVariant, taskName, projCtx.Project.Identifier, request).Limit(limit).WithFields(fields...)) 192 if err != nil { 193 uis.LoggedError(w, r, http.StatusNotFound, err) 194 return 195 } 196 197 } else { 198 tasks, err = task.Find(task.ByStatuses(evergreen.CompletedStatuses, 199 buildVariant, taskName, projCtx.Project.Identifier, request).Limit(limit).WithFields(fields...).Sort([]string{"-" + task.RevisionOrderNumberKey})) 200 if err != nil { 201 uis.LoggedError(w, r, http.StatusNotFound, err) 202 return 203 } 204 } 205 206 uiTasks := []*UITask{} 207 versionIds = make([]string, 0, len(tasks)) 208 // get the versions for every single task that was returned 209 for _, t := range tasks { 210 // create a UITask 211 uiTask := &UITask{ 212 Id: t.Id, 213 CreateTime: t.CreateTime, 214 DispatchTime: t.DispatchTime, 215 PushTime: t.PushTime, 216 ScheduledTime: t.ScheduledTime, 217 StartTime: t.StartTime, 218 FinishTime: t.FinishTime, 219 Version: t.Version, 220 Status: t.Status, 221 Host: t.HostId, 222 Distro: t.DistroId, 223 } 224 uiTasks = append(uiTasks, uiTask) 225 versionIds = append(versionIds, t.Version) 226 } 227 data.Tasks = uiTasks 228 } 229 230 // Populate the versions field if with commits, otherwise patches field 231 if request == evergreen.RepotrackerVersionRequester { 232 versions, err := version.Find(version.ByIds(versionIds). 233 WithFields(version.IdKey, version.CreateTimeKey, version.MessageKey, 234 version.AuthorKey, version.RevisionKey, version.RevisionOrderNumberKey). 235 Sort([]string{"-" + version.RevisionOrderNumberKey})) 236 if err != nil { 237 uis.LoggedError(w, r, http.StatusBadRequest, err) 238 return 239 } 240 data.Versions = versions 241 } else { 242 // patches 243 patches, err := patch.Find(patch.ByVersions(versionIds). 244 WithFields(patch.IdKey, patch.CreateTimeKey, patch.DescriptionKey, patch.AuthorKey)) 245 if err != nil { 246 uis.LoggedError(w, r, http.StatusBadRequest, err) 247 return 248 } 249 data.Patches = patches 250 } 251 252 uis.WriteJSON(w, http.StatusOK, data) 253 }