|
| 1 | +/* |
| 2 | +Licensed to the Apache Software Foundation (ASF) under one or more |
| 3 | +contributor license agreements. See the NOTICE file distributed with |
| 4 | +this work for additional information regarding copyright ownership. |
| 5 | +The ASF licenses this file to You under the Apache License, Version 2.0 |
| 6 | +(the "License"); you may not use this file except in compliance with |
| 7 | +the License. You may obtain a copy of the License at |
| 8 | +
|
| 9 | + http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | +
|
| 11 | +Unless required by applicable law or agreed to in writing, software |
| 12 | +distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | +See the License for the specific language governing permissions and |
| 15 | +limitations under the License. |
| 16 | +*/ |
| 17 | + |
| 18 | +package tasks |
| 19 | + |
| 20 | +import ( |
| 21 | + "github.com/apache/incubator-devlake/core/dal" |
| 22 | + "github.com/apache/incubator-devlake/core/errors" |
| 23 | + "github.com/apache/incubator-devlake/core/plugin" |
| 24 | + "github.com/apache/incubator-devlake/plugins/q_dev/models" |
| 25 | + "math" |
| 26 | + "time" |
| 27 | +) |
| 28 | + |
| 29 | +var _ plugin.SubTaskEntryPoint = ConvertQDevUserMetrics |
| 30 | + |
| 31 | +// ConvertQDevUserMetrics 按用户聚合指标 |
| 32 | +func ConvertQDevUserMetrics(taskCtx plugin.SubTaskContext) errors.Error { |
| 33 | + data := taskCtx.GetData().(*QDevTaskData) |
| 34 | + db := taskCtx.GetDal() |
| 35 | + |
| 36 | + // 清空之前聚合的数据 |
| 37 | + clauses := []dal.Clause{ |
| 38 | + dal.Where("connection_id = ?", data.Options.ConnectionId), |
| 39 | + } |
| 40 | + err := db.Delete(&models.QDevUserMetrics{}, clauses...) |
| 41 | + if err != nil { |
| 42 | + return errors.Default.Wrap(err, "failed to delete previous user metrics") |
| 43 | + } |
| 44 | + |
| 45 | + // 聚合数据 |
| 46 | + userDataMap := make(map[string]*UserMetricsAggregation) |
| 47 | + |
| 48 | + cursor, err := db.Cursor( |
| 49 | + dal.From(&models.QDevUserData{}), |
| 50 | + dal.Where("connection_id = ?", data.Options.ConnectionId), |
| 51 | + ) |
| 52 | + if err != nil { |
| 53 | + return errors.Default.Wrap(err, "failed to get user data cursor") |
| 54 | + } |
| 55 | + defer cursor.Close() |
| 56 | + |
| 57 | + taskCtx.SetProgress(0, -1) |
| 58 | + |
| 59 | + // 汇总每个用户的数据 |
| 60 | + for cursor.Next() { |
| 61 | + userData := &models.QDevUserData{} |
| 62 | + err = db.Fetch(cursor, userData) |
| 63 | + if err != nil { |
| 64 | + return errors.Default.Wrap(err, "failed to fetch user data") |
| 65 | + } |
| 66 | + |
| 67 | + // 获取或创建用户聚合 |
| 68 | + aggregation, ok := userDataMap[userData.UserId] |
| 69 | + if !ok { |
| 70 | + aggregation = &UserMetricsAggregation{ |
| 71 | + ConnectionId: userData.ConnectionId, |
| 72 | + UserId: userData.UserId, |
| 73 | + FirstDate: userData.Date, |
| 74 | + LastDate: userData.Date, |
| 75 | + DataCount: 0, |
| 76 | + } |
| 77 | + userDataMap[userData.UserId] = aggregation |
| 78 | + } |
| 79 | + |
| 80 | + // 更新日期范围 |
| 81 | + if userData.Date.Before(aggregation.FirstDate) { |
| 82 | + aggregation.FirstDate = userData.Date |
| 83 | + } |
| 84 | + if userData.Date.After(aggregation.LastDate) { |
| 85 | + aggregation.LastDate = userData.Date |
| 86 | + } |
| 87 | + |
| 88 | + // 累加指标 |
| 89 | + aggregation.DataCount++ |
| 90 | + aggregation.TotalCodeReview_FindingsCount += userData.CodeReview_FindingsCount |
| 91 | + aggregation.TotalCodeReview_SucceededEventCount += userData.CodeReview_SucceededEventCount |
| 92 | + aggregation.TotalInlineChat_AcceptanceEventCount += userData.InlineChat_AcceptanceEventCount |
| 93 | + aggregation.TotalInlineChat_AcceptedLineAdditions += userData.InlineChat_AcceptedLineAdditions |
| 94 | + aggregation.TotalInlineChat_AcceptedLineDeletions += userData.InlineChat_AcceptedLineDeletions |
| 95 | + aggregation.TotalInlineChat_DismissalEventCount += userData.InlineChat_DismissalEventCount |
| 96 | + aggregation.TotalInlineChat_DismissedLineAdditions += userData.InlineChat_DismissedLineAdditions |
| 97 | + aggregation.TotalInlineChat_DismissedLineDeletions += userData.InlineChat_DismissedLineDeletions |
| 98 | + aggregation.TotalInlineChat_RejectedLineAdditions += userData.InlineChat_RejectedLineAdditions |
| 99 | + aggregation.TotalInlineChat_RejectedLineDeletions += userData.InlineChat_RejectedLineDeletions |
| 100 | + aggregation.TotalInlineChat_RejectionEventCount += userData.InlineChat_RejectionEventCount |
| 101 | + aggregation.TotalInlineChat_TotalEventCount += userData.InlineChat_TotalEventCount |
| 102 | + aggregation.TotalInline_AICodeLines += userData.Inline_AICodeLines |
| 103 | + aggregation.TotalInline_AcceptanceCount += userData.Inline_AcceptanceCount |
| 104 | + aggregation.TotalInline_SuggestionsCount += userData.Inline_SuggestionsCount |
| 105 | + } |
| 106 | + |
| 107 | + // 计算每个用户的平均指标和总天数 |
| 108 | + for _, aggregation := range userDataMap { |
| 109 | + // 创建指标记录 |
| 110 | + metrics := &models.QDevUserMetrics{ |
| 111 | + ConnectionId: aggregation.ConnectionId, |
| 112 | + UserId: aggregation.UserId, |
| 113 | + FirstDate: aggregation.FirstDate, |
| 114 | + LastDate: aggregation.LastDate, |
| 115 | + } |
| 116 | + |
| 117 | + // 计算总天数 |
| 118 | + metrics.TotalDays = int(math.Round(aggregation.LastDate.Sub(aggregation.FirstDate).Hours()/24)) + 1 |
| 119 | + |
| 120 | + // 设置总计指标 |
| 121 | + metrics.TotalCodeReview_FindingsCount = aggregation.TotalCodeReview_FindingsCount |
| 122 | + metrics.TotalCodeReview_SucceededEventCount = aggregation.TotalCodeReview_SucceededEventCount |
| 123 | + metrics.TotalInlineChat_AcceptanceEventCount = aggregation.TotalInlineChat_AcceptanceEventCount |
| 124 | + metrics.TotalInlineChat_AcceptedLineAdditions = aggregation.TotalInlineChat_AcceptedLineAdditions |
| 125 | + metrics.TotalInlineChat_AcceptedLineDeletions = aggregation.TotalInlineChat_AcceptedLineDeletions |
| 126 | + metrics.TotalInlineChat_DismissalEventCount = aggregation.TotalInlineChat_DismissalEventCount |
| 127 | + metrics.TotalInlineChat_DismissedLineAdditions = aggregation.TotalInlineChat_DismissedLineAdditions |
| 128 | + metrics.TotalInlineChat_DismissedLineDeletions = aggregation.TotalInlineChat_DismissedLineDeletions |
| 129 | + metrics.TotalInlineChat_RejectedLineAdditions = aggregation.TotalInlineChat_RejectedLineAdditions |
| 130 | + metrics.TotalInlineChat_RejectedLineDeletions = aggregation.TotalInlineChat_RejectedLineDeletions |
| 131 | + metrics.TotalInlineChat_RejectionEventCount = aggregation.TotalInlineChat_RejectionEventCount |
| 132 | + metrics.TotalInlineChat_TotalEventCount = aggregation.TotalInlineChat_TotalEventCount |
| 133 | + metrics.TotalInline_AICodeLines = aggregation.TotalInline_AICodeLines |
| 134 | + metrics.TotalInline_AcceptanceCount = aggregation.TotalInline_AcceptanceCount |
| 135 | + metrics.TotalInline_SuggestionsCount = aggregation.TotalInline_SuggestionsCount |
| 136 | + |
| 137 | + // 计算平均值指标 |
| 138 | + if metrics.TotalDays > 0 { |
| 139 | + metrics.AvgCodeReview_FindingsCount = float64(aggregation.TotalCodeReview_FindingsCount) / float64(metrics.TotalDays) |
| 140 | + metrics.AvgCodeReview_SucceededEventCount = float64(aggregation.TotalCodeReview_SucceededEventCount) / float64(metrics.TotalDays) |
| 141 | + metrics.AvgInlineChat_AcceptanceEventCount = float64(aggregation.TotalInlineChat_AcceptanceEventCount) / float64(metrics.TotalDays) |
| 142 | + metrics.AvgInlineChat_TotalEventCount = float64(aggregation.TotalInlineChat_TotalEventCount) / float64(metrics.TotalDays) |
| 143 | + metrics.AvgInline_AICodeLines = float64(aggregation.TotalInline_AICodeLines) / float64(metrics.TotalDays) |
| 144 | + metrics.AvgInline_AcceptanceCount = float64(aggregation.TotalInline_AcceptanceCount) / float64(metrics.TotalDays) |
| 145 | + metrics.AvgInline_SuggestionsCount = float64(aggregation.TotalInline_SuggestionsCount) / float64(metrics.TotalDays) |
| 146 | + } |
| 147 | + |
| 148 | + // 计算接受率 |
| 149 | + totalEvents := aggregation.TotalInlineChat_AcceptanceEventCount + |
| 150 | + aggregation.TotalInlineChat_DismissalEventCount + |
| 151 | + aggregation.TotalInlineChat_RejectionEventCount |
| 152 | + |
| 153 | + if totalEvents > 0 { |
| 154 | + metrics.AcceptanceRate = float64(aggregation.TotalInlineChat_AcceptanceEventCount) / float64(totalEvents) |
| 155 | + } |
| 156 | + |
| 157 | + // 存储聚合指标 |
| 158 | + err = db.Create(metrics) |
| 159 | + if err != nil { |
| 160 | + return errors.Default.Wrap(err, "failed to create user metrics") |
| 161 | + } |
| 162 | + |
| 163 | + taskCtx.IncProgress(1) |
| 164 | + } |
| 165 | + |
| 166 | + return nil |
| 167 | +} |
| 168 | + |
| 169 | +// UserMetricsAggregation 聚合过程中用于保存用户指标的结构 |
| 170 | +type UserMetricsAggregation struct { |
| 171 | + ConnectionId uint64 |
| 172 | + UserId string |
| 173 | + FirstDate time.Time |
| 174 | + LastDate time.Time |
| 175 | + DataCount int |
| 176 | + TotalCodeReview_FindingsCount int |
| 177 | + TotalCodeReview_SucceededEventCount int |
| 178 | + TotalInlineChat_AcceptanceEventCount int |
| 179 | + TotalInlineChat_AcceptedLineAdditions int |
| 180 | + TotalInlineChat_AcceptedLineDeletions int |
| 181 | + TotalInlineChat_DismissalEventCount int |
| 182 | + TotalInlineChat_DismissedLineAdditions int |
| 183 | + TotalInlineChat_DismissedLineDeletions int |
| 184 | + TotalInlineChat_RejectedLineAdditions int |
| 185 | + TotalInlineChat_RejectedLineDeletions int |
| 186 | + TotalInlineChat_RejectionEventCount int |
| 187 | + TotalInlineChat_TotalEventCount int |
| 188 | + TotalInline_AICodeLines int |
| 189 | + TotalInline_AcceptanceCount int |
| 190 | + TotalInline_SuggestionsCount int |
| 191 | +} |
| 192 | + |
| 193 | +var ConvertQDevUserMetricsMeta = plugin.SubTaskMeta{ |
| 194 | + Name: "convertQDevUserMetrics", |
| 195 | + EntryPoint: ConvertQDevUserMetrics, |
| 196 | + EnabledByDefault: true, |
| 197 | + Description: "Convert user data to metrics by each user", |
| 198 | + DomainTypes: []string{plugin.DOMAIN_TYPE_CROSS}, |
| 199 | +} |
0 commit comments