This is an automated email from the ASF dual-hosted git repository.

klesh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git


The following commit(s) were added to refs/heads/main by this push:
     new affed66b7 feat(q_dev):delete user metrics (#8493)
affed66b7 is described below

commit affed66b7a02a206e65d9ef2a36e96505194bdb1
Author: Warren Chen <[email protected]>
AuthorDate: Wed Jul 9 17:47:51 2025 +0800

    feat(q_dev):delete user metrics (#8493)
    
    * feat: delete user metrics
    
    * feat: modify dashboard for qdev
---
 backend/plugins/q_dev/impl/impl.go                 |   2 -
 backend/plugins/q_dev/impl/impl_test.go            |  18 +-
 .../20250709_delete_user_metrics.go                |  46 ++++
 backend/plugins/q_dev/models/user_metrics.go       |  68 ------
 backend/plugins/q_dev/models/user_metrics_test.go  | 155 -------------
 .../plugins/q_dev/tasks/user_metrics_converter.go  | 240 ---------------------
 .../q_dev/tasks/user_metrics_converter_test.go     | 212 ------------------
 7 files changed, 55 insertions(+), 686 deletions(-)

diff --git a/backend/plugins/q_dev/impl/impl.go 
b/backend/plugins/q_dev/impl/impl.go
index 3a93a11ef..5cf8bf393 100644
--- a/backend/plugins/q_dev/impl/impl.go
+++ b/backend/plugins/q_dev/impl/impl.go
@@ -52,7 +52,6 @@ func (p QDev) GetTablesInfo() []dal.Tabler {
        return []dal.Tabler{
                &models.QDevConnection{},
                &models.QDevUserData{},
-               &models.QDevUserMetrics{},
                &models.QDevS3FileMeta{},
        }
 }
@@ -81,7 +80,6 @@ func (p QDev) SubTaskMetas() []plugin.SubTaskMeta {
        return []plugin.SubTaskMeta{
                tasks.CollectQDevS3FilesMeta,
                tasks.ExtractQDevS3DataMeta,
-               tasks.ConvertQDevUserMetricsMeta,
        }
 }
 
diff --git a/backend/plugins/q_dev/impl/impl_test.go 
b/backend/plugins/q_dev/impl/impl_test.go
index 568c5af26..7c02ef488 100644
--- a/backend/plugins/q_dev/impl/impl_test.go
+++ b/backend/plugins/q_dev/impl/impl_test.go
@@ -27,19 +27,19 @@ import (
 
 func TestQDev_BasicPluginMethods(t *testing.T) {
        plugin := &QDev{}
-       
+
        assert.Equal(t, "q_dev", plugin.Name())
        assert.Equal(t, "To collect and enrich data from AWS Q Developer usage 
metrics", plugin.Description())
        assert.Equal(t, "github.com/apache/incubator-devlake/plugins/q_dev", 
plugin.RootPkgPath())
-       
+
        // Test table info
        tables := plugin.GetTablesInfo()
-       assert.Len(t, tables, 4)
-       
+       assert.Len(t, tables, 3)
+
        // Test subtask metas
        subtasks := plugin.SubTaskMetas()
-       assert.Len(t, subtasks, 3)
-       
+       assert.Len(t, subtasks, 2)
+
        // Test API resources
        apiResources := plugin.ApiResources()
        assert.NotEmpty(t, apiResources)
@@ -62,11 +62,11 @@ func TestQDev_TaskDataStructure(t *testing.T) {
                        Region:  "us-west-2",
                },
        }
-       
+
        assert.NotNil(t, taskData.Options)
        assert.NotNil(t, taskData.S3Client)
        assert.NotNil(t, taskData.IdentityClient)
-       
+
        assert.Equal(t, uint64(1), taskData.Options.ConnectionId)
        assert.Equal(t, "test/", taskData.Options.S3Prefix)
        assert.Equal(t, "test-bucket", taskData.S3Client.Bucket)
@@ -85,7 +85,7 @@ func TestQDev_TaskDataWithoutIdentityClient(t *testing.T) {
                },
                IdentityClient: nil, // No identity client
        }
-       
+
        assert.NotNil(t, taskData.Options)
        assert.NotNil(t, taskData.S3Client)
        assert.Nil(t, taskData.IdentityClient)
diff --git 
a/backend/plugins/q_dev/models/migrationscripts/20250709_delete_user_metrics.go 
b/backend/plugins/q_dev/models/migrationscripts/20250709_delete_user_metrics.go
new file mode 100644
index 000000000..4908559e3
--- /dev/null
+++ 
b/backend/plugins/q_dev/models/migrationscripts/20250709_delete_user_metrics.go
@@ -0,0 +1,46 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package migrationscripts
+
+import (
+       "github.com/apache/incubator-devlake/core/context"
+       "github.com/apache/incubator-devlake/core/errors"
+       "github.com/apache/incubator-devlake/core/plugin"
+)
+
+var _ plugin.MigrationScript = (*deleteUserMetrics)(nil)
+
+type deleteUserMetrics struct{}
+
+func (*deleteUserMetrics) Up(basicRes context.BasicRes) errors.Error {
+       db := basicRes.GetDal()
+
+       // Drop the QDevUserMetrics table
+       // Ignore error if table doesn't exist
+       _ = db.Exec("DROP TABLE IF EXISTS _tool_q_dev_user_metrics")
+
+       return nil
+}
+
+func (*deleteUserMetrics) Version() uint64 {
+       return 20250709000001
+}
+
+func (*deleteUserMetrics) Name() string {
+       return "delete QDevUserMetrics table"
+}
diff --git a/backend/plugins/q_dev/models/user_metrics.go 
b/backend/plugins/q_dev/models/user_metrics.go
deleted file mode 100644
index 71987e92e..000000000
--- a/backend/plugins/q_dev/models/user_metrics.go
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
-Licensed to the Apache Software Foundation (ASF) under one or more
-contributor license agreements.  See the NOTICE file distributed with
-this work for additional information regarding copyright ownership.
-The ASF licenses this file to You under the Apache License, Version 2.0
-(the "License"); you may not use this file except in compliance with
-the License.  You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package models
-
-import (
-       "time"
-
-       "github.com/apache/incubator-devlake/core/models/common"
-)
-
-// QDevUserMetrics 存储按用户聚合的指标数据
-type QDevUserMetrics struct {
-       common.NoPKModel
-       ConnectionId uint64 `gorm:"primaryKey"`
-       UserId       string `gorm:"primaryKey"`
-       DisplayName  string `gorm:"type:varchar(255)" json:"displayName"` // 
New field for user display name
-       FirstDate    time.Time
-       LastDate     time.Time
-       TotalDays    int
-
-       // 聚合指标
-       TotalCodeReview_FindingsCount          int
-       TotalCodeReview_SucceededEventCount    int
-       TotalInlineChat_AcceptanceEventCount   int
-       TotalInlineChat_AcceptedLineAdditions  int
-       TotalInlineChat_AcceptedLineDeletions  int
-       TotalInlineChat_DismissalEventCount    int
-       TotalInlineChat_DismissedLineAdditions int
-       TotalInlineChat_DismissedLineDeletions int
-       TotalInlineChat_RejectedLineAdditions  int
-       TotalInlineChat_RejectedLineDeletions  int
-       TotalInlineChat_RejectionEventCount    int
-       TotalInlineChat_TotalEventCount        int
-       TotalInline_AICodeLines                int
-       TotalInline_AcceptanceCount            int
-       TotalInline_SuggestionsCount           int
-
-       // 平均指标
-       AvgCodeReview_FindingsCount        float64
-       AvgCodeReview_SucceededEventCount  float64
-       AvgInlineChat_AcceptanceEventCount float64
-       AvgInlineChat_TotalEventCount      float64
-       AvgInline_AICodeLines              float64
-       AvgInline_AcceptanceCount          float64
-       AvgInline_SuggestionsCount         float64
-
-       // 接受率指标
-       AcceptanceRate float64
-}
-
-func (QDevUserMetrics) TableName() string {
-       return "_tool_q_dev_user_metrics"
-}
diff --git a/backend/plugins/q_dev/models/user_metrics_test.go 
b/backend/plugins/q_dev/models/user_metrics_test.go
deleted file mode 100644
index 0a85d3263..000000000
--- a/backend/plugins/q_dev/models/user_metrics_test.go
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
-Licensed to the Apache Software Foundation (ASF) under one or more
-contributor license agreements.  See the NOTICE file distributed with
-this work for additional information regarding copyright ownership.
-The ASF licenses this file to You under the Apache License, Version 2.0
-(the "License"); you may not use this file except in compliance with
-the License.  You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package models
-
-import (
-       "testing"
-       "time"
-
-       "github.com/stretchr/testify/assert"
-)
-
-func TestQDevUserMetrics_WithDisplayName(t *testing.T) {
-       userMetrics := QDevUserMetrics{
-               ConnectionId: 1,
-               UserId:       "uuid-123",
-               DisplayName:  "John Doe",
-               FirstDate:    time.Now().AddDate(0, 0, -30),
-               LastDate:     time.Now(),
-               TotalDays:    30,
-               TotalCodeReview_FindingsCount: 50,
-               AcceptanceRate:                0.85,
-       }
-
-       assert.Equal(t, "John Doe", userMetrics.DisplayName)
-       assert.Equal(t, "uuid-123", userMetrics.UserId)
-       assert.Equal(t, uint64(1), userMetrics.ConnectionId)
-       assert.Equal(t, 50, userMetrics.TotalCodeReview_FindingsCount)
-       assert.Equal(t, 0.85, userMetrics.AcceptanceRate)
-}
-
-func TestQDevUserMetrics_WithFallbackDisplayName(t *testing.T) {
-       userMetrics := QDevUserMetrics{
-               ConnectionId: 1,
-               UserId:       "uuid-456",
-               DisplayName:  "uuid-456", // Fallback case when display name 
resolution fails
-               TotalDays:    15,
-       }
-
-       assert.Equal(t, "uuid-456", userMetrics.DisplayName)
-       assert.Equal(t, userMetrics.UserId, userMetrics.DisplayName) // Should 
match when fallback
-       assert.Equal(t, 15, userMetrics.TotalDays)
-}
-
-func TestQDevUserMetrics_EmptyDisplayName(t *testing.T) {
-       userMetrics := QDevUserMetrics{
-               ConnectionId: 1,
-               UserId:       "uuid-789",
-               DisplayName:  "", // Empty display name
-               TotalDays:    5,
-       }
-
-       assert.Equal(t, "", userMetrics.DisplayName)
-       assert.Equal(t, "uuid-789", userMetrics.UserId)
-       assert.NotEqual(t, userMetrics.UserId, userMetrics.DisplayName)
-}
-
-func TestQDevUserMetrics_TableName(t *testing.T) {
-       userMetrics := QDevUserMetrics{}
-       assert.Equal(t, "_tool_q_dev_user_metrics", userMetrics.TableName())
-}
-
-func TestQDevUserMetrics_AllFields(t *testing.T) {
-       firstDate := time.Now().AddDate(0, 0, -30)
-       lastDate := time.Now()
-       
-       userMetrics := QDevUserMetrics{
-               ConnectionId: 1,
-               UserId:       "test-user",
-               DisplayName:  "Test User",
-               FirstDate:    firstDate,
-               LastDate:     lastDate,
-               TotalDays:    30,
-
-               // 聚合指标
-               TotalCodeReview_FindingsCount:          100,
-               TotalCodeReview_SucceededEventCount:    90,
-               TotalInlineChat_AcceptanceEventCount:   80,
-               TotalInlineChat_AcceptedLineAdditions:  70,
-               TotalInlineChat_AcceptedLineDeletions:  60,
-               TotalInlineChat_DismissalEventCount:    50,
-               TotalInlineChat_DismissedLineAdditions: 40,
-               TotalInlineChat_DismissedLineDeletions: 30,
-               TotalInlineChat_RejectedLineAdditions:  20,
-               TotalInlineChat_RejectedLineDeletions:  10,
-               TotalInlineChat_RejectionEventCount:    5,
-               TotalInlineChat_TotalEventCount:        200,
-               TotalInline_AICodeLines:                1000,
-               TotalInline_AcceptanceCount:            150,
-               TotalInline_SuggestionsCount:           180,
-
-               // 平均指标
-               AvgCodeReview_FindingsCount:        3.33,
-               AvgCodeReview_SucceededEventCount:  3.0,
-               AvgInlineChat_AcceptanceEventCount: 2.67,
-               AvgInlineChat_TotalEventCount:      6.67,
-               AvgInline_AICodeLines:              33.33,
-               AvgInline_AcceptanceCount:          5.0,
-               AvgInline_SuggestionsCount:         6.0,
-
-               // 接受率指标
-               AcceptanceRate: 0.83,
-       }
-
-       // Verify all fields are properly set
-       assert.Equal(t, uint64(1), userMetrics.ConnectionId)
-       assert.Equal(t, "test-user", userMetrics.UserId)
-       assert.Equal(t, "Test User", userMetrics.DisplayName)
-       assert.Equal(t, firstDate, userMetrics.FirstDate)
-       assert.Equal(t, lastDate, userMetrics.LastDate)
-       assert.Equal(t, 30, userMetrics.TotalDays)
-
-       // Test aggregated metrics
-       assert.Equal(t, 100, userMetrics.TotalCodeReview_FindingsCount)
-       assert.Equal(t, 90, userMetrics.TotalCodeReview_SucceededEventCount)
-       assert.Equal(t, 80, userMetrics.TotalInlineChat_AcceptanceEventCount)
-       assert.Equal(t, 70, userMetrics.TotalInlineChat_AcceptedLineAdditions)
-       assert.Equal(t, 60, userMetrics.TotalInlineChat_AcceptedLineDeletions)
-       assert.Equal(t, 50, userMetrics.TotalInlineChat_DismissalEventCount)
-       assert.Equal(t, 40, userMetrics.TotalInlineChat_DismissedLineAdditions)
-       assert.Equal(t, 30, userMetrics.TotalInlineChat_DismissedLineDeletions)
-       assert.Equal(t, 20, userMetrics.TotalInlineChat_RejectedLineAdditions)
-       assert.Equal(t, 10, userMetrics.TotalInlineChat_RejectedLineDeletions)
-       assert.Equal(t, 5, userMetrics.TotalInlineChat_RejectionEventCount)
-       assert.Equal(t, 200, userMetrics.TotalInlineChat_TotalEventCount)
-       assert.Equal(t, 1000, userMetrics.TotalInline_AICodeLines)
-       assert.Equal(t, 150, userMetrics.TotalInline_AcceptanceCount)
-       assert.Equal(t, 180, userMetrics.TotalInline_SuggestionsCount)
-
-       // Test average metrics
-       assert.Equal(t, 3.33, userMetrics.AvgCodeReview_FindingsCount)
-       assert.Equal(t, 3.0, userMetrics.AvgCodeReview_SucceededEventCount)
-       assert.Equal(t, 2.67, userMetrics.AvgInlineChat_AcceptanceEventCount)
-       assert.Equal(t, 6.67, userMetrics.AvgInlineChat_TotalEventCount)
-       assert.Equal(t, 33.33, userMetrics.AvgInline_AICodeLines)
-       assert.Equal(t, 5.0, userMetrics.AvgInline_AcceptanceCount)
-       assert.Equal(t, 6.0, userMetrics.AvgInline_SuggestionsCount)
-
-       // Test acceptance rate
-       assert.Equal(t, 0.83, userMetrics.AcceptanceRate)
-}
diff --git a/backend/plugins/q_dev/tasks/user_metrics_converter.go 
b/backend/plugins/q_dev/tasks/user_metrics_converter.go
deleted file mode 100644
index c639da3e3..000000000
--- a/backend/plugins/q_dev/tasks/user_metrics_converter.go
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
-Licensed to the Apache Software Foundation (ASF) under one or more
-contributor license agreements.  See the NOTICE file distributed with
-this work for additional information regarding copyright ownership.
-The ASF licenses this file to You under the Apache License, Version 2.0
-(the "License"); you may not use this file except in compliance with
-the License.  You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package tasks
-
-import (
-       "fmt"
-       "github.com/apache/incubator-devlake/core/dal"
-       "github.com/apache/incubator-devlake/core/errors"
-       "github.com/apache/incubator-devlake/core/plugin"
-       "github.com/apache/incubator-devlake/plugins/q_dev/models"
-       "math"
-       "time"
-)
-
-var _ plugin.SubTaskEntryPoint = ConvertQDevUserMetrics
-
-// ConvertQDevUserMetrics 按用户聚合指标 (enhanced with display name support)
-func ConvertQDevUserMetrics(taskCtx plugin.SubTaskContext) errors.Error {
-       data := taskCtx.GetData().(*QDevTaskData)
-       db := taskCtx.GetDal()
-
-       // 清空之前聚合的数据
-       clauses := []dal.Clause{
-               dal.Where("connection_id = ?", data.Options.ConnectionId),
-       }
-       err := db.Delete(&models.QDevUserMetrics{}, clauses...)
-       if err != nil {
-               return errors.Default.Wrap(err, "failed to delete previous user 
metrics")
-       }
-
-       // 聚合数据 (updated to include display name)
-       userDataMap := make(map[string]*UserMetricsAggregationWithDisplayName)
-
-       cursor, err := db.Cursor(
-               dal.From(&models.QDevUserData{}),
-               dal.Where("connection_id = ?", data.Options.ConnectionId),
-       )
-       if err != nil {
-               return errors.Default.Wrap(err, "failed to get user data 
cursor")
-       }
-       defer cursor.Close()
-
-       taskCtx.SetProgress(0, -1)
-
-       // 汇总每个用户的数据
-       for cursor.Next() {
-               userData := &models.QDevUserData{}
-               err = db.Fetch(cursor, userData)
-               if err != nil {
-                       return errors.Default.Wrap(err, "failed to fetch user 
data")
-               }
-
-               // 获取或创建用户聚合
-               aggregation, ok := userDataMap[userData.UserId]
-               if !ok {
-                       // Resolve display name for new user (new functionality)
-                       displayName := 
resolveDisplayNameForAggregation(userData.UserId, data.IdentityClient)
-                       // If user data already has display name, use it; 
otherwise use resolved name
-                       if userData.DisplayName != "" {
-                               displayName = userData.DisplayName
-                       }
-
-                       aggregation = &UserMetricsAggregationWithDisplayName{
-                               ConnectionId: userData.ConnectionId,
-                               UserId:       userData.UserId,
-                               DisplayName:  displayName, // New field
-                               FirstDate:    userData.Date,
-                               LastDate:     userData.Date,
-                               DataCount:    0,
-                       }
-                       userDataMap[userData.UserId] = aggregation
-               }
-
-               // 更新日期范围
-               if userData.Date.Before(aggregation.FirstDate) {
-                       aggregation.FirstDate = userData.Date
-               }
-               if userData.Date.After(aggregation.LastDate) {
-                       aggregation.LastDate = userData.Date
-               }
-
-               // 累加指标
-               aggregation.DataCount++
-               aggregation.TotalCodeReview_FindingsCount += 
userData.CodeReview_FindingsCount
-               aggregation.TotalCodeReview_SucceededEventCount += 
userData.CodeReview_SucceededEventCount
-               aggregation.TotalInlineChat_AcceptanceEventCount += 
userData.InlineChat_AcceptanceEventCount
-               aggregation.TotalInlineChat_AcceptedLineAdditions += 
userData.InlineChat_AcceptedLineAdditions
-               aggregation.TotalInlineChat_AcceptedLineDeletions += 
userData.InlineChat_AcceptedLineDeletions
-               aggregation.TotalInlineChat_DismissalEventCount += 
userData.InlineChat_DismissalEventCount
-               aggregation.TotalInlineChat_DismissedLineAdditions += 
userData.InlineChat_DismissedLineAdditions
-               aggregation.TotalInlineChat_DismissedLineDeletions += 
userData.InlineChat_DismissedLineDeletions
-               aggregation.TotalInlineChat_RejectedLineAdditions += 
userData.InlineChat_RejectedLineAdditions
-               aggregation.TotalInlineChat_RejectedLineDeletions += 
userData.InlineChat_RejectedLineDeletions
-               aggregation.TotalInlineChat_RejectionEventCount += 
userData.InlineChat_RejectionEventCount
-               aggregation.TotalInlineChat_TotalEventCount += 
userData.InlineChat_TotalEventCount
-               aggregation.TotalInline_AICodeLines += 
userData.Inline_AICodeLines
-               aggregation.TotalInline_AcceptanceCount += 
userData.Inline_AcceptanceCount
-               aggregation.TotalInline_SuggestionsCount += 
userData.Inline_SuggestionsCount
-       }
-
-       // 计算每个用户的平均指标和总天数
-       for _, aggregation := range userDataMap {
-               // 创建指标记录 (updated to use new method)
-               metrics := aggregation.ToUserMetrics()
-
-               // 存储聚合指标
-               err = db.Create(metrics)
-               if err != nil {
-                       return errors.Default.Wrap(err, "failed to create user 
metrics")
-               }
-
-               taskCtx.IncProgress(1)
-       }
-
-       return nil
-}
-
-// UserMetricsAggregationWithDisplayName 聚合过程中用于保存用户指标的结构 (enhanced with 
display name)
-type UserMetricsAggregationWithDisplayName struct {
-       ConnectionId                           uint64
-       UserId                                 string
-       DisplayName                            string // New field for display 
name
-       FirstDate                              time.Time
-       LastDate                               time.Time
-       DataCount                              int
-       TotalCodeReview_FindingsCount          int
-       TotalCodeReview_SucceededEventCount    int
-       TotalInlineChat_AcceptanceEventCount   int
-       TotalInlineChat_AcceptedLineAdditions  int
-       TotalInlineChat_AcceptedLineDeletions  int
-       TotalInlineChat_DismissalEventCount    int
-       TotalInlineChat_DismissedLineAdditions int
-       TotalInlineChat_DismissedLineDeletions int
-       TotalInlineChat_RejectedLineAdditions  int
-       TotalInlineChat_RejectedLineDeletions  int
-       TotalInlineChat_RejectionEventCount    int
-       TotalInlineChat_TotalEventCount        int
-       TotalInline_AICodeLines                int
-       TotalInline_AcceptanceCount            int
-       TotalInline_SuggestionsCount           int
-}
-
-// ToUserMetrics converts aggregation data to QDevUserMetrics model
-func (aggregation *UserMetricsAggregationWithDisplayName) ToUserMetrics() 
*models.QDevUserMetrics {
-       metrics := &models.QDevUserMetrics{
-               ConnectionId: aggregation.ConnectionId,
-               UserId:       aggregation.UserId,
-               DisplayName:  aggregation.DisplayName, // New field
-               FirstDate:    aggregation.FirstDate,
-               LastDate:     aggregation.LastDate,
-       }
-
-       // 计算总天数
-       metrics.TotalDays = 
int(math.Round(aggregation.LastDate.Sub(aggregation.FirstDate).Hours()/24)) + 1
-
-       // 设置总计指标
-       metrics.TotalCodeReview_FindingsCount = 
aggregation.TotalCodeReview_FindingsCount
-       metrics.TotalCodeReview_SucceededEventCount = 
aggregation.TotalCodeReview_SucceededEventCount
-       metrics.TotalInlineChat_AcceptanceEventCount = 
aggregation.TotalInlineChat_AcceptanceEventCount
-       metrics.TotalInlineChat_AcceptedLineAdditions = 
aggregation.TotalInlineChat_AcceptedLineAdditions
-       metrics.TotalInlineChat_AcceptedLineDeletions = 
aggregation.TotalInlineChat_AcceptedLineDeletions
-       metrics.TotalInlineChat_DismissalEventCount = 
aggregation.TotalInlineChat_DismissalEventCount
-       metrics.TotalInlineChat_DismissedLineAdditions = 
aggregation.TotalInlineChat_DismissedLineAdditions
-       metrics.TotalInlineChat_DismissedLineDeletions = 
aggregation.TotalInlineChat_DismissedLineDeletions
-       metrics.TotalInlineChat_RejectedLineAdditions = 
aggregation.TotalInlineChat_RejectedLineAdditions
-       metrics.TotalInlineChat_RejectedLineDeletions = 
aggregation.TotalInlineChat_RejectedLineDeletions
-       metrics.TotalInlineChat_RejectionEventCount = 
aggregation.TotalInlineChat_RejectionEventCount
-       metrics.TotalInlineChat_TotalEventCount = 
aggregation.TotalInlineChat_TotalEventCount
-       metrics.TotalInline_AICodeLines = aggregation.TotalInline_AICodeLines
-       metrics.TotalInline_AcceptanceCount = 
aggregation.TotalInline_AcceptanceCount
-       metrics.TotalInline_SuggestionsCount = 
aggregation.TotalInline_SuggestionsCount
-
-       // 计算平均值指标
-       if metrics.TotalDays > 0 {
-               metrics.AvgCodeReview_FindingsCount = 
float64(aggregation.TotalCodeReview_FindingsCount) / float64(metrics.TotalDays)
-               metrics.AvgCodeReview_SucceededEventCount = 
float64(aggregation.TotalCodeReview_SucceededEventCount) / 
float64(metrics.TotalDays)
-               metrics.AvgInlineChat_AcceptanceEventCount = 
float64(aggregation.TotalInlineChat_AcceptanceEventCount) / 
float64(metrics.TotalDays)
-               metrics.AvgInlineChat_TotalEventCount = 
float64(aggregation.TotalInlineChat_TotalEventCount) / 
float64(metrics.TotalDays)
-               metrics.AvgInline_AICodeLines = 
float64(aggregation.TotalInline_AICodeLines) / float64(metrics.TotalDays)
-               metrics.AvgInline_AcceptanceCount = 
float64(aggregation.TotalInline_AcceptanceCount) / float64(metrics.TotalDays)
-               metrics.AvgInline_SuggestionsCount = 
float64(aggregation.TotalInline_SuggestionsCount) / float64(metrics.TotalDays)
-       }
-
-       // 计算接受率
-       totalEvents := aggregation.TotalInlineChat_AcceptanceEventCount +
-               aggregation.TotalInlineChat_DismissalEventCount +
-               aggregation.TotalInlineChat_RejectionEventCount
-
-       if totalEvents > 0 {
-               metrics.AcceptanceRate = 
float64(aggregation.TotalInlineChat_AcceptanceEventCount) / float64(totalEvents)
-       }
-
-       return metrics
-}
-
-// resolveDisplayNameForAggregation resolves display name for user metrics 
aggregation
-func resolveDisplayNameForAggregation(userId string, identityClient 
UserDisplayNameResolver) string {
-       // If no identity client available, use userId as fallback
-       if identityClient == nil {
-               return userId
-       }
-
-       // Try to resolve display name
-       displayName, err := identityClient.ResolveUserDisplayName(userId)
-       if err != nil {
-               // Log error but continue with userId as fallback
-               fmt.Printf("Failed to resolve display name for user %s during 
aggregation: %v\n", userId, err)
-               return userId
-       }
-
-       // If display name is empty, use userId as fallback
-       if displayName == "" {
-               return userId
-       }
-
-       return displayName
-}
-
-var ConvertQDevUserMetricsMeta = plugin.SubTaskMeta{
-       Name:             "convertQDevUserMetrics",
-       EntryPoint:       ConvertQDevUserMetrics,
-       EnabledByDefault: true,
-       Description:      "Convert user data to metrics by each user",
-       DomainTypes:      []string{plugin.DOMAIN_TYPE_CROSS},
-}
diff --git a/backend/plugins/q_dev/tasks/user_metrics_converter_test.go 
b/backend/plugins/q_dev/tasks/user_metrics_converter_test.go
deleted file mode 100644
index 15df329f7..000000000
--- a/backend/plugins/q_dev/tasks/user_metrics_converter_test.go
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
-Licensed to the Apache Software Foundation (ASF) under one or more
-contributor license agreements.  See the NOTICE file distributed with
-this work for additional information regarding copyright ownership.
-The ASF licenses this file to You under the Apache License, Version 2.0
-(the "License"); you may not use this file except in compliance with
-the License.  You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package tasks
-
-import (
-       "testing"
-       "time"
-
-       "github.com/stretchr/testify/assert"
-)
-
-func TestUserMetricsAggregationWithDisplayName_SingleUser(t *testing.T) {
-       aggregation := &UserMetricsAggregationWithDisplayName{
-               ConnectionId: 1,
-               UserId:       "user-123",
-               DisplayName:  "John Doe",
-               FirstDate:    time.Date(2025, 6, 20, 0, 0, 0, 0, time.UTC),
-               LastDate:     time.Date(2025, 6, 23, 0, 0, 0, 0, time.UTC),
-               DataCount:    4,
-               TotalCodeReview_FindingsCount:        20,
-               TotalInlineChat_AcceptanceEventCount: 40,
-               TotalInline_AcceptanceCount:          60,
-               TotalInline_SuggestionsCount:         80,
-       }
-
-       metrics := aggregation.ToUserMetrics()
-
-       assert.Equal(t, uint64(1), metrics.ConnectionId)
-       assert.Equal(t, "user-123", metrics.UserId)
-       assert.Equal(t, "John Doe", metrics.DisplayName)
-       assert.Equal(t, 4, metrics.TotalDays) // 6/20 to 6/23 = 4 days
-       assert.Equal(t, 20, metrics.TotalCodeReview_FindingsCount)
-       assert.Equal(t, 40, metrics.TotalInlineChat_AcceptanceEventCount)
-       assert.Equal(t, 60, metrics.TotalInline_AcceptanceCount)
-       assert.Equal(t, 80, metrics.TotalInline_SuggestionsCount)
-
-       // Test averages
-       assert.Equal(t, 5.0, metrics.AvgCodeReview_FindingsCount)        // 20/4
-       assert.Equal(t, 10.0, metrics.AvgInlineChat_AcceptanceEventCount) // 
40/4
-       assert.Equal(t, 15.0, metrics.AvgInline_AcceptanceCount)         // 60/4
-       assert.Equal(t, 20.0, metrics.AvgInline_SuggestionsCount)        // 80/4
-}
-
-func TestUserMetricsAggregationWithDisplayName_FallbackDisplayName(t 
*testing.T) {
-       aggregation := &UserMetricsAggregationWithDisplayName{
-               ConnectionId: 1,
-               UserId:       "user-456",
-               DisplayName:  "user-456", // Fallback case
-               FirstDate:    time.Date(2025, 6, 23, 0, 0, 0, 0, time.UTC),
-               LastDate:     time.Date(2025, 6, 23, 0, 0, 0, 0, time.UTC),
-               DataCount:    1,
-       }
-
-       metrics := aggregation.ToUserMetrics()
-
-       assert.Equal(t, "user-456", metrics.UserId)
-       assert.Equal(t, "user-456", metrics.DisplayName)
-       assert.Equal(t, metrics.UserId, metrics.DisplayName) // Should match 
when fallback
-       assert.Equal(t, 1, metrics.TotalDays) // Same day = 1 day
-}
-
-func TestUserMetricsAggregationWithDisplayName_AcceptanceRateCalculation(t 
*testing.T) {
-       aggregation := &UserMetricsAggregationWithDisplayName{
-               ConnectionId: 1,
-               UserId:       "user-789",
-               DisplayName:  "Jane Smith",
-               FirstDate:    time.Date(2025, 6, 23, 0, 0, 0, 0, time.UTC),
-               LastDate:     time.Date(2025, 6, 23, 0, 0, 0, 0, time.UTC),
-               DataCount:    1,
-               TotalInlineChat_AcceptanceEventCount: 80,  // Accepted
-               TotalInlineChat_DismissalEventCount:  15,  // Dismissed
-               TotalInlineChat_RejectionEventCount:  5,   // Rejected
-               // Total events = 80 + 15 + 5 = 100
-               // Acceptance rate = 80/100 = 0.8
-       }
-
-       metrics := aggregation.ToUserMetrics()
-
-       assert.Equal(t, "Jane Smith", metrics.DisplayName)
-       assert.Equal(t, 80, metrics.TotalInlineChat_AcceptanceEventCount)
-       assert.Equal(t, 15, metrics.TotalInlineChat_DismissalEventCount)
-       assert.Equal(t, 5, metrics.TotalInlineChat_RejectionEventCount)
-       assert.Equal(t, 0.8, metrics.AcceptanceRate)
-}
-
-func TestUserMetricsAggregationWithDisplayName_ZeroAcceptanceRate(t 
*testing.T) {
-       aggregation := &UserMetricsAggregationWithDisplayName{
-               ConnectionId: 1,
-               UserId:       "user-zero",
-               DisplayName:  "Zero User",
-               FirstDate:    time.Date(2025, 6, 23, 0, 0, 0, 0, time.UTC),
-               LastDate:     time.Date(2025, 6, 23, 0, 0, 0, 0, time.UTC),
-               DataCount:    1,
-               // No events = acceptance rate should be 0
-       }
-
-       metrics := aggregation.ToUserMetrics()
-
-       assert.Equal(t, "Zero User", metrics.DisplayName)
-       assert.Equal(t, 0.0, metrics.AcceptanceRate)
-}
-
-func TestUserMetricsAggregationWithDisplayName_AllFields(t *testing.T) {
-       firstDate := time.Date(2025, 6, 1, 0, 0, 0, 0, time.UTC)
-       lastDate := time.Date(2025, 6, 30, 0, 0, 0, 0, time.UTC)
-       
-       aggregation := &UserMetricsAggregationWithDisplayName{
-               ConnectionId: 123,
-               UserId:       "test-user",
-               DisplayName:  "Test User",
-               FirstDate:    firstDate,
-               LastDate:     lastDate,
-               DataCount:    30,
-
-               // Set all total fields
-               TotalCodeReview_FindingsCount:          300,
-               TotalCodeReview_SucceededEventCount:    270,
-               TotalInlineChat_AcceptanceEventCount:   240,
-               TotalInlineChat_AcceptedLineAdditions:  210,
-               TotalInlineChat_AcceptedLineDeletions:  180,
-               TotalInlineChat_DismissalEventCount:    150,
-               TotalInlineChat_DismissedLineAdditions: 120,
-               TotalInlineChat_DismissedLineDeletions: 90,
-               TotalInlineChat_RejectedLineAdditions:  60,
-               TotalInlineChat_RejectedLineDeletions:  30,
-               TotalInlineChat_RejectionEventCount:    15,
-               TotalInlineChat_TotalEventCount:        600,
-               TotalInline_AICodeLines:                3000,
-               TotalInline_AcceptanceCount:            450,
-               TotalInline_SuggestionsCount:           540,
-       }
-
-       metrics := aggregation.ToUserMetrics()
-
-       // Verify basic fields
-       assert.Equal(t, uint64(123), metrics.ConnectionId)
-       assert.Equal(t, "test-user", metrics.UserId)
-       assert.Equal(t, "Test User", metrics.DisplayName)
-       assert.Equal(t, firstDate, metrics.FirstDate)
-       assert.Equal(t, lastDate, metrics.LastDate)
-       assert.Equal(t, 30, metrics.TotalDays) // June 1-30 = 30 days
-
-       // Verify all total fields
-       assert.Equal(t, 300, metrics.TotalCodeReview_FindingsCount)
-       assert.Equal(t, 270, metrics.TotalCodeReview_SucceededEventCount)
-       assert.Equal(t, 240, metrics.TotalInlineChat_AcceptanceEventCount)
-       assert.Equal(t, 210, metrics.TotalInlineChat_AcceptedLineAdditions)
-       assert.Equal(t, 180, metrics.TotalInlineChat_AcceptedLineDeletions)
-       assert.Equal(t, 150, metrics.TotalInlineChat_DismissalEventCount)
-       assert.Equal(t, 120, metrics.TotalInlineChat_DismissedLineAdditions)
-       assert.Equal(t, 90, metrics.TotalInlineChat_DismissedLineDeletions)
-       assert.Equal(t, 60, metrics.TotalInlineChat_RejectedLineAdditions)
-       assert.Equal(t, 30, metrics.TotalInlineChat_RejectedLineDeletions)
-       assert.Equal(t, 15, metrics.TotalInlineChat_RejectionEventCount)
-       assert.Equal(t, 600, metrics.TotalInlineChat_TotalEventCount)
-       assert.Equal(t, 3000, metrics.TotalInline_AICodeLines)
-       assert.Equal(t, 450, metrics.TotalInline_AcceptanceCount)
-       assert.Equal(t, 540, metrics.TotalInline_SuggestionsCount)
-
-       // Verify average fields (all divided by 30 days)
-       assert.Equal(t, 10.0, metrics.AvgCodeReview_FindingsCount)        // 
300/30
-       assert.Equal(t, 9.0, metrics.AvgCodeReview_SucceededEventCount)   // 
270/30
-       assert.Equal(t, 8.0, metrics.AvgInlineChat_AcceptanceEventCount)  // 
240/30
-       assert.Equal(t, 20.0, metrics.AvgInlineChat_TotalEventCount)      // 
600/30
-       assert.Equal(t, 100.0, metrics.AvgInline_AICodeLines)            // 
3000/30
-       assert.Equal(t, 15.0, metrics.AvgInline_AcceptanceCount)         // 
450/30
-       assert.Equal(t, 18.0, metrics.AvgInline_SuggestionsCount)        // 
540/30
-
-       // Verify acceptance rate: 240 / (240 + 150 + 15) = 240/405 ≈ 0.593
-       expectedAcceptanceRate := 240.0 / (240.0 + 150.0 + 15.0)
-       assert.InDelta(t, expectedAcceptanceRate, metrics.AcceptanceRate, 0.001)
-}
-
-func TestResolveDisplayNameForAggregation_Success(t *testing.T) {
-       mockIdentityClient := &MockIdentityClient{}
-       mockIdentityClient.On("ResolveUserDisplayName", 
"user-123").Return("John Doe", nil)
-
-       displayName := resolveDisplayNameForAggregation("user-123", 
mockIdentityClient)
-       assert.Equal(t, "John Doe", displayName)
-
-       mockIdentityClient.AssertExpectations(t)
-}
-
-func TestResolveDisplayNameForAggregation_NoClient(t *testing.T) {
-       displayName := resolveDisplayNameForAggregation("user-456", nil)
-       assert.Equal(t, "user-456", displayName)
-}
-
-func TestResolveDisplayNameForAggregation_Error(t *testing.T) {
-       mockIdentityClient := &MockIdentityClient{}
-       mockIdentityClient.On("ResolveUserDisplayName", 
"user-error").Return("user-error", assert.AnError)
-
-       displayName := resolveDisplayNameForAggregation("user-error", 
mockIdentityClient)
-       assert.Equal(t, "user-error", displayName) // Should fallback to UUID
-
-       mockIdentityClient.AssertExpectations(t)
-}

Reply via email to