github.com/jancarloviray/community@v0.41.1-0.20170124221257-33a66c87cf2f/core/section/github/issues.go (about) 1 // Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved. 2 // 3 // This software (Documize Community Edition) is licensed under 4 // GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html 5 // 6 // You can operate outside the AGPL restrictions by purchasing 7 // Documize Enterprise Edition and obtaining a commercial license 8 // by contacting <sales@documize.com>. 9 // 10 // https://documize.com 11 12 package github 13 14 import ( 15 "html/template" 16 "sort" 17 "time" 18 19 "github.com/documize/community/core/log" 20 21 gogithub "github.com/google/go-github/github" 22 ) 23 24 type githubIssue struct { 25 ID int `json:"id"` 26 Date string `json:"date"` 27 Updated string `json:"dated"` 28 Message string `json:"message"` 29 URL template.URL `json:"url"` 30 Name string `json:"name"` 31 Creator string `json:"creator"` 32 Avatar string `json:"avatar"` 33 Labels template.HTML `json:"labels"` 34 LabelNames []string `json:"labelNames"` 35 LabelColors []string `json:"labelColors"` 36 IsOpen bool `json:"isopen"` 37 Repo string `json:"repo"` 38 Private bool `json:"private"` 39 Milestone string `json:"milestone"` 40 } 41 42 type githubSharedLabel struct { 43 Name string `json:"name"` 44 Count int `json:"count"` 45 Color string `json:"color"` 46 Repos template.HTML `json:"Repos"` 47 } 48 49 // sort issues in order that that should be presented - by date updated. 50 type issuesToSort []githubIssue 51 52 func (s issuesToSort) Len() int { return len(s) } 53 func (s issuesToSort) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 54 func (s issuesToSort) Less(i, j int) bool { 55 if s[i].Milestone != noMilestone && s[j].Milestone == noMilestone { 56 return true 57 } 58 if s[i].Milestone == noMilestone && s[j].Milestone != noMilestone { 59 return false 60 } 61 if s[i].Milestone != s[j].Milestone { 62 // TODO should this order be by milestone completion? 63 return s[i].Milestone < s[j].Milestone 64 } 65 if !s[i].IsOpen && s[j].IsOpen { 66 return true 67 } 68 if s[i].IsOpen && !s[j].IsOpen { 69 return false 70 } 71 // TODO this seems a very slow approach 72 iDate, iErr := time.Parse(issuesTimeFormat, s[i].Updated) 73 log.IfErr(iErr) 74 jDate, jErr := time.Parse(issuesTimeFormat, s[j].Updated) 75 log.IfErr(jErr) 76 return iDate.Before(jDate) 77 } 78 79 // sort shared labels alphabetically 80 type sharedLabelsSort []githubSharedLabel 81 82 func (s sharedLabelsSort) Len() int { return len(s) } 83 func (s sharedLabelsSort) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 84 func (s sharedLabelsSort) Less(i, j int) bool { return s[i].Name < s[j].Name } 85 86 const ( 87 tagIssuesData = "issuesData" 88 issuesTimeFormat = "January 2 2006, 15:04" 89 unassignedIssue = "(unassigned)" 90 ) 91 92 func init() { 93 reports[tagIssuesData] = report{refreshIssues, renderIssues, issuesTemplate} 94 } 95 96 func wrapLabels(labels []gogithub.Label) (l string, labelNames []string, labelColors []string) { 97 labelNames = make([]string, 0, len(labels)) 98 labelColors = make([]string, 0, len(labels)) 99 for _, ll := range labels { 100 labelNames = append(labelNames, *ll.Name) 101 labelColors = append(labelColors, *ll.Color) 102 l += `<span class="issue-label" style="background-color:#` + *ll.Color + `">` + *ll.Name + `</span> ` 103 } 104 return l, labelNames, labelColors 105 } 106 107 func getIssues(client *gogithub.Client, config *githubConfig) ([]githubIssue, error) { 108 109 if !config.ShowIssues { 110 return nil, nil 111 } 112 113 ret := []githubIssue{} 114 115 hadRepo := make(map[string]bool) 116 117 for _, orb := range config.Lists { 118 if orb.Included { 119 120 rName := orb.Owner + "/" + orb.Repo 121 122 if !hadRepo[rName] { 123 124 for _, state := range []string{"open", "closed"} { 125 126 opts := &gogithub.IssueListByRepoOptions{ 127 Sort: "updated", 128 State: state, 129 ListOptions: gogithub.ListOptions{PerPage: config.BranchLines}} 130 131 if config.SincePtr != nil && state == "closed" /* we want all the open ones */ { 132 opts.Since = *config.SincePtr 133 } 134 135 guff, _, err := client.Issues.ListByRepo(orb.Owner, orb.Repo, opts) 136 137 if err != nil { 138 return ret, err 139 } 140 141 for _, v := range guff { 142 n := unassignedIssue 143 av := githubGravatar 144 ptr := v.Assignee 145 if ptr != nil { 146 if ptr.Login != nil { 147 n = *ptr.Login 148 av = *ptr.AvatarURL 149 } 150 } 151 ms := noMilestone 152 if v.Milestone != nil { 153 if v.Milestone.Title != nil { 154 ms = *v.Milestone.Title 155 } 156 } 157 l, ln, lc := wrapLabels(v.Labels) 158 ret = append(ret, githubIssue{ 159 Name: n, 160 Creator: getUserName(client, config, *v.User.Login), 161 Avatar: av, 162 Message: *v.Title, 163 Date: v.CreatedAt.Format(issuesTimeFormat), 164 Updated: v.UpdatedAt.Format(issuesTimeFormat), 165 URL: template.URL(*v.HTMLURL), 166 Labels: template.HTML(l), 167 LabelNames: ln, 168 LabelColors: lc, 169 ID: *v.Number, 170 IsOpen: *v.State == "open", 171 Repo: repoName(rName), 172 Private: orb.Private, 173 Milestone: ms, 174 }) 175 } 176 } 177 } 178 hadRepo[rName] = true 179 } 180 181 } 182 183 sort.Sort(issuesToSort(ret)) 184 185 return ret, nil 186 187 } 188 189 func refreshIssues(gr *githubRender, config *githubConfig, client *gogithub.Client) (err error) { 190 191 if !config.ShowIssues { 192 return nil 193 } 194 195 gr.Issues, err = getIssues(client, config) 196 if err != nil { 197 log.Error("unable to get github issues (cmd)", err) 198 return err 199 } 200 201 gr.OpenIssues = 0 202 gr.ClosedIssues = 0 203 sharedLabels := make(map[string][]string) 204 sharedLabelColors := make(map[string]string) 205 for _, v := range gr.Issues { 206 if v.IsOpen { 207 gr.OpenIssues++ 208 } else { 209 gr.ClosedIssues++ 210 } 211 for i, lab := range v.LabelNames { 212 sharedLabels[lab] = append(sharedLabels[lab], v.Repo) 213 if _, exists := sharedLabelColors[lab]; !exists { // use the first one we see 214 sharedLabelColors[lab] = v.LabelColors[i] 215 } 216 } 217 } 218 gr.HasIssues = (gr.OpenIssues + gr.ClosedIssues) > 0 219 220 gr.SharedLabels = make([]githubSharedLabel, 0, len(sharedLabels)) // will usually be too big 221 for name, repos := range sharedLabels { 222 if len(repos) > 1 { 223 thisLab := githubSharedLabel{Name: name, Count: len(repos), Color: sharedLabelColors[name]} 224 show := "" 225 for i, r := range repos { 226 if i > 0 { 227 show += ", " 228 } 229 show += "<a href='https://github.com/" + config.Owner + "/" + r + 230 "/issues?q=is%3Aissue+label%3A" + name + "'>" + r + "</a>" 231 } 232 thisLab.Repos = template.HTML(show) 233 gr.SharedLabels = append(gr.SharedLabels, thisLab) 234 } 235 } 236 sort.Sort(sharedLabelsSort(gr.SharedLabels)) 237 gr.HasSharedLabels = len(gr.SharedLabels) > 0 238 239 return nil 240 } 241 242 func renderIssues(payload *githubRender, c *githubConfig) error { 243 return nil 244 }