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  }