Monorepo for Tangled
tangled.org
1package state
2
3import (
4 "net/http"
5 "sort"
6
7 "tangled.org/core/appview/db"
8 "tangled.org/core/appview/models"
9 "tangled.org/core/appview/pages"
10 "tangled.org/core/appview/pagination"
11 "tangled.org/core/consts"
12 "tangled.org/core/orm"
13)
14
15func (s *State) GoodFirstIssues(w http.ResponseWriter, r *http.Request) {
16 l := s.logger.With("handler", "GoodFirstIssues")
17
18 page := pagination.FromContext(r.Context())
19
20 goodFirstIssueLabel := s.config.Label.GoodFirstIssue
21
22 gfiLabelDef, err := db.GetLabelDefinition(s.db, orm.FilterEq("at_uri", goodFirstIssueLabel))
23 if err != nil {
24 l.Error("failed to get gfi label def", "err", err)
25 s.pages.Error500(w)
26 return
27 }
28
29 repoLabels, err := db.GetRepoLabels(s.db, orm.FilterEq("label_at", goodFirstIssueLabel))
30 if err != nil {
31 l.Error("failed to get repo labels", "err", err)
32 s.pages.Error503(w)
33 return
34 }
35
36 if len(repoLabels) == 0 {
37 s.pages.GoodFirstIssues(w, pages.GoodFirstIssuesParams{
38 BaseParams: pages.BaseParamsFromContext(r.Context()),
39 RepoGroups: []*models.RepoGroup{},
40 LabelDefs: make(map[string]*models.LabelDefinition),
41 Page: page,
42 GfiLabel: gfiLabelDef,
43 })
44 return
45 }
46
47 repoUris := make([]string, 0, len(repoLabels))
48 for _, rl := range repoLabels {
49 repoUris = append(repoUris, string(rl.RepoDid))
50 }
51
52 allIssues, err := db.GetIssuesPaginated(
53 s.db,
54 pagination.Page{
55 Limit: 500,
56 },
57 orm.FilterIn("repo_did", repoUris),
58 orm.FilterEq("open", 1),
59 )
60 if err != nil {
61 l.Error("failed to get issues", "err", err)
62 s.pages.Error503(w)
63 return
64 }
65
66 var goodFirstIssues []models.Issue
67 for _, issue := range allIssues {
68 if issue.Labels.ContainsLabel(goodFirstIssueLabel) {
69 goodFirstIssues = append(goodFirstIssues, issue)
70 }
71 }
72
73 repoGroups := make(map[string]*models.RepoGroup)
74 for _, issue := range goodFirstIssues {
75 if group, exists := repoGroups[issue.Repo.RepoDid]; exists {
76 group.Issues = append(group.Issues, issue)
77 } else {
78 repoGroups[issue.Repo.RepoDid] = &models.RepoGroup{
79 Repo: issue.Repo,
80 Issues: []models.Issue{issue},
81 }
82 }
83 }
84
85 var sortedGroups []*models.RepoGroup
86 for _, group := range repoGroups {
87 sortedGroups = append(sortedGroups, group)
88 }
89
90 sort.Slice(sortedGroups, func(i, j int) bool {
91 iIsTangled := sortedGroups[i].Repo.Did == consts.TangledDid
92 jIsTangled := sortedGroups[j].Repo.Did == consts.TangledDid
93
94 // If one is tangled and the other isn't, non-tangled comes first
95 if iIsTangled != jIsTangled {
96 return jIsTangled // true if j is tangled (i should come first)
97 }
98
99 // Both tangled or both not tangled: sort by name
100 return sortedGroups[i].Repo.Name < sortedGroups[j].Repo.Name
101 })
102
103 groupStart := page.Offset
104 groupEnd := page.Offset + page.Limit
105 if groupStart > len(sortedGroups) {
106 groupStart = len(sortedGroups)
107 }
108 if groupEnd > len(sortedGroups) {
109 groupEnd = len(sortedGroups)
110 }
111
112 paginatedGroups := sortedGroups[groupStart:groupEnd]
113
114 var allIssuesFromGroups []models.Issue
115 for _, group := range paginatedGroups {
116 allIssuesFromGroups = append(allIssuesFromGroups, group.Issues...)
117 }
118
119 var allLabelDefs []models.LabelDefinition
120 if len(allIssuesFromGroups) > 0 {
121 labelDefUris := make(map[string]bool)
122 for _, issue := range allIssuesFromGroups {
123 for labelDefUri := range issue.Labels.Inner() {
124 labelDefUris[labelDefUri] = true
125 }
126 }
127
128 uriList := make([]string, 0, len(labelDefUris))
129 for uri := range labelDefUris {
130 uriList = append(uriList, uri)
131 }
132
133 if len(uriList) > 0 {
134 allLabelDefs, err = db.GetLabelDefinitions(s.db, orm.FilterIn("at_uri", uriList))
135 if err != nil {
136 l.Error("failed to fetch labels", "err", err)
137 }
138 }
139 }
140
141 labelDefsMap := make(map[string]*models.LabelDefinition)
142 for i := range allLabelDefs {
143 labelDefsMap[allLabelDefs[i].AtUri().String()] = &allLabelDefs[i]
144 }
145
146 s.pages.GoodFirstIssues(w, pages.GoodFirstIssuesParams{
147 BaseParams: pages.BaseParamsFromContext(r.Context()),
148 RepoGroups: paginatedGroups,
149 LabelDefs: labelDefsMap,
150 Page: page,
151 GfiLabel: gfiLabelDef,
152 })
153}