-
Notifications
You must be signed in to change notification settings - Fork 23
/
Copy pathlabels.go
149 lines (134 loc) · 3.65 KB
/
labels.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"errors"
"fmt"
"net/http"
"slices"
"strconv"
"strings"
"github.com/google/safehtml"
"github.com/google/safehtml/template"
"golang.org/x/oscar/internal/github"
"golang.org/x/oscar/internal/htmlutil"
"golang.org/x/oscar/internal/labels"
)
// labelsPage holds the fields needed to display the results
// of an issue categorization.
type labelsPage struct {
CommonPage
Params labelsParams // the raw parameters
Results []*labelsResult
Error error // if non-nil, the error to display instead of the result
}
type labelsResult struct {
*github.Issue // the issue we're reporting on
Problem string
Category labels.Category
Explanation string
BodyHTML safehtml.HTML
}
// labelsParams holds the raw inputs to the labels form.
type labelsParams struct {
Query string // the issue ID to lookup
}
func (g *Gaby) handleLabels(w http.ResponseWriter, r *http.Request) {
handlePage(w, g.populateLabelsPage(r), labelsPageTmpl)
}
var labelsPageTmpl = newTemplate(labelsPageTmplFile, template.FuncMap{})
// populateLabelsPage returns the contents of the labels page.
func (g *Gaby) populateLabelsPage(r *http.Request) *labelsPage {
pm := labelsParams{
Query: r.FormValue(paramQuery),
}
p := &labelsPage{
Params: pm,
}
p.setCommonPage()
if pm.Query == "" {
return p
}
var project string
if len(g.githubProjects) > 0 {
project = g.githubProjects[0] // default to first project
}
var issueMin, issueMax int64
smin, smax, ok := strings.Cut(pm.Query, ",")
if ok {
var err1, err2 error
issueMin, err1 = strconv.ParseInt(smin, 10, 64)
issueMax, err2 = strconv.ParseInt(smax, 10, 64)
if err := errors.Join(err1, err2); err != nil {
p.Error = err
return p
}
} else {
proj, issue, err := parseIssueNumber(pm.Query)
if err != nil {
p.Error = fmt.Errorf("invalid form value %q: %w", pm.Query, err)
return p
}
if proj != "" {
if !slices.Contains(g.githubProjects, proj) {
p.Error = fmt.Errorf("invalid form value (unrecognized project): %q", pm.Query)
return p
}
project = proj
}
issueMin = issue
issueMax = issue
}
// Find issues in database.
for i := range github.LookupIssues(g.db, project, issueMin, issueMax) {
lr := &labelsResult{Issue: i}
if i.PullRequest != nil {
lr.Problem = "skipping: pull request"
} else if isBot(i.User.Login) {
lr.Problem = "skipping: author is a bot"
} else {
cat, exp, err := labels.IssueCategory(r.Context(), g.db, g.llm, i)
if err != nil {
p.Error = err
return p
}
lr.Category = cat
lr.Explanation = exp
// Should probably use GH-flavored markdown here, but it's only for display.
lr.BodyHTML = htmlutil.MarkdownToSafeHTML(i.Body)
}
p.Results = append(p.Results, lr)
}
return p
}
func (p *labelsPage) setCommonPage() {
p.CommonPage = CommonPage{
ID: labelsID,
Description: "Categorize issues.",
Styles: []safeURL{labelsID.CSS()},
Form: Form{
Inputs: p.Params.inputs(),
SubmitText: "categorize",
},
}
}
func (pm *labelsParams) inputs() []FormInput {
return []FormInput{
{
Label: "issue",
Type: "int, int,int or string",
Description: "the issue(s) to check, as a number, two numbers, or URL (e.g. 1234, golang/go#1234, or https://github.com/golang/go/issues/1234)",
Name: safeQuery,
Required: true,
Typed: TextInput{
ID: safeQuery,
Value: pm.Query,
},
},
}
}
func isBot(author string) bool {
// TODO: generalize.
return author == "gopherbot" || author == "gabyhelp"
}