1package db
2
3import (
4 "context"
5 "fmt"
6 "time"
7
8 "go.opentelemetry.io/otel/attribute"
9 "go.opentelemetry.io/otel/codes"
10 "go.opentelemetry.io/otel/trace"
11)
12
13type RepoEvent struct {
14 Repo *Repo
15 Source *Repo
16}
17
18type ProfileTimeline struct {
19 ByMonth []ByMonth
20}
21
22type ByMonth struct {
23 RepoEvents []RepoEvent
24 IssueEvents IssueEvents
25 PullEvents PullEvents
26}
27
28func (b ByMonth) IsEmpty() bool {
29 return len(b.RepoEvents) == 0 &&
30 len(b.IssueEvents.Items) == 0 &&
31 len(b.PullEvents.Items) == 0
32}
33
34type IssueEvents struct {
35 Items []*Issue
36}
37
38type IssueEventStats struct {
39 Open int
40 Closed int
41}
42
43func (i IssueEvents) Stats() IssueEventStats {
44 var open, closed int
45 for _, issue := range i.Items {
46 if issue.Open {
47 open += 1
48 } else {
49 closed += 1
50 }
51 }
52
53 return IssueEventStats{
54 Open: open,
55 Closed: closed,
56 }
57}
58
59type PullEvents struct {
60 Items []*Pull
61}
62
63func (p PullEvents) Stats() PullEventStats {
64 var open, merged, closed int
65 for _, pull := range p.Items {
66 switch pull.State {
67 case PullOpen:
68 open += 1
69 case PullMerged:
70 merged += 1
71 case PullClosed:
72 closed += 1
73 }
74 }
75
76 return PullEventStats{
77 Open: open,
78 Merged: merged,
79 Closed: closed,
80 }
81}
82
83type PullEventStats struct {
84 Closed int
85 Open int
86 Merged int
87}
88
89const TimeframeMonths = 7
90
91func MakeProfileTimeline(ctx context.Context, e Execer, forDid string) (*ProfileTimeline, error) {
92 span := trace.SpanFromContext(ctx)
93 defer span.End()
94
95 span.SetAttributes(
96 attribute.String("forDid", forDid),
97 )
98
99 timeline := ProfileTimeline{
100 ByMonth: make([]ByMonth, TimeframeMonths),
101 }
102 currentMonth := time.Now().Month()
103 timeframe := fmt.Sprintf("-%d months", TimeframeMonths)
104
105 pulls, err := GetPullsByOwnerDid(e, forDid, timeframe)
106 if err != nil {
107 span.RecordError(err)
108 span.SetStatus(codes.Error, "error getting pulls by owner did")
109 return nil, fmt.Errorf("error getting pulls by owner did: %w", err)
110 }
111
112 span.SetAttributes(attribute.Int("pulls.count", len(pulls)))
113
114 // group pulls by month
115 for _, pull := range pulls {
116 pullMonth := pull.Created.Month()
117
118 if currentMonth-pullMonth >= TimeframeMonths {
119 // shouldn't happen; but times are weird
120 continue
121 }
122
123 idx := currentMonth - pullMonth
124 items := &timeline.ByMonth[idx].PullEvents.Items
125
126 *items = append(*items, &pull)
127 }
128
129 issues, err := GetIssuesByOwnerDid(e, forDid, timeframe)
130 if err != nil {
131 span.RecordError(err)
132 span.SetStatus(codes.Error, "error getting issues by owner did")
133 return nil, fmt.Errorf("error getting issues by owner did: %w", err)
134 }
135
136 span.SetAttributes(attribute.Int("issues.count", len(issues)))
137
138 for _, issue := range issues {
139 issueMonth := issue.Created.Month()
140
141 if currentMonth-issueMonth >= TimeframeMonths {
142 // shouldn't happen; but times are weird
143 continue
144 }
145
146 idx := currentMonth - issueMonth
147 items := &timeline.ByMonth[idx].IssueEvents.Items
148
149 *items = append(*items, &issue)
150 }
151
152 repos, err := GetAllReposByDid(ctx, e, forDid)
153 if err != nil {
154 span.RecordError(err)
155 span.SetStatus(codes.Error, "error getting all repos by did")
156 return nil, fmt.Errorf("error getting all repos by did: %w", err)
157 }
158
159 span.SetAttributes(attribute.Int("repos.count", len(repos)))
160
161 for _, repo := range repos {
162 // TODO: get this in the original query; requires COALESCE because nullable
163 var sourceRepo *Repo
164 if repo.Source != "" {
165 sourceRepo, err = GetRepoByAtUri(ctx, e, repo.Source)
166 if err != nil {
167 span.RecordError(err)
168 span.SetStatus(codes.Error, "error getting repo by at uri")
169 return nil, err
170 }
171 }
172
173 repoMonth := repo.Created.Month()
174
175 if currentMonth-repoMonth >= TimeframeMonths {
176 // shouldn't happen; but times are weird
177 continue
178 }
179
180 idx := currentMonth - repoMonth
181
182 items := &timeline.ByMonth[idx].RepoEvents
183 *items = append(*items, RepoEvent{
184 Repo: &repo,
185 Source: sourceRepo,
186 })
187 }
188
189 return &timeline, nil
190}