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

warren 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 ce89adebe fix(q_dev): include more metrics, prevent data duplication 
(#8498)
ce89adebe is described below

commit ce89adebe28c1beb245a2d205f8ef84b10b3464f
Author: DiscreteTom <[email protected]>
AuthorDate: Thu Jul 10 19:51:07 2025 +0800

    fix(q_dev): include more metrics, prevent data duplication (#8498)
---
 .../20250710_add_missing_metrics.go                |  78 +++++++
 .../q_dev/models/migrationscripts/register.go      |   1 +
 backend/plugins/q_dev/models/user_data.go          |  68 ++++--
 backend/plugins/q_dev/models/user_data_test.go     | 193 ++++++++--------
 backend/plugins/q_dev/tasks/s3_data_extractor.go   |  67 +++++-
 .../plugins/q_dev/tasks/s3_data_extractor_test.go  | 180 ++++++++++++++-
 backend/plugins/q_dev/tasks/s3_file_collector.go   |  32 ++-
 grafana/dashboards/qdev_user_data.json             | 243 +++------------------
 8 files changed, 515 insertions(+), 347 deletions(-)

diff --git 
a/backend/plugins/q_dev/models/migrationscripts/20250710_add_missing_metrics.go 
b/backend/plugins/q_dev/models/migrationscripts/20250710_add_missing_metrics.go
new file mode 100644
index 000000000..d2c6ebe39
--- /dev/null
+++ 
b/backend/plugins/q_dev/models/migrationscripts/20250710_add_missing_metrics.go
@@ -0,0 +1,78 @@
+/*
+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 = (*addMissingMetrics)(nil)
+
+type addMissingMetrics struct{}
+
+func (*addMissingMetrics) Up(basicRes context.BasicRes) errors.Error {
+       db := basicRes.GetDal()
+
+       // Add all missing metrics columns to _tool_q_dev_user_data table
+       // All columns are integer type with default value 0
+       // Using snake_case column names to match GORM's default naming 
convention
+       _ = db.Exec(`
+        ALTER TABLE _tool_q_dev_user_data 
+        ADD COLUMN chat_ai_code_lines INT DEFAULT 0,
+        ADD COLUMN chat_messages_interacted INT DEFAULT 0,
+        ADD COLUMN chat_messages_sent INT DEFAULT 0,
+        ADD COLUMN code_fix_acceptance_event_count INT DEFAULT 0,
+        ADD COLUMN code_fix_accepted_lines INT DEFAULT 0,
+        ADD COLUMN code_fix_generated_lines INT DEFAULT 0,
+        ADD COLUMN code_fix_generation_event_count INT DEFAULT 0,
+        ADD COLUMN code_review_failed_event_count INT DEFAULT 0,
+        ADD COLUMN dev_acceptance_event_count INT DEFAULT 0,
+        ADD COLUMN dev_accepted_lines INT DEFAULT 0,
+        ADD COLUMN dev_generated_lines INT DEFAULT 0,
+        ADD COLUMN dev_generation_event_count INT DEFAULT 0,
+        ADD COLUMN doc_generation_accepted_file_updates INT DEFAULT 0,
+        ADD COLUMN doc_generation_accepted_files_creations INT DEFAULT 0,
+        ADD COLUMN doc_generation_accepted_line_additions INT DEFAULT 0,
+        ADD COLUMN doc_generation_accepted_line_updates INT DEFAULT 0,
+        ADD COLUMN doc_generation_event_count INT DEFAULT 0,
+        ADD COLUMN doc_generation_rejected_file_creations INT DEFAULT 0,
+        ADD COLUMN doc_generation_rejected_file_updates INT DEFAULT 0,
+        ADD COLUMN doc_generation_rejected_line_additions INT DEFAULT 0,
+        ADD COLUMN doc_generation_rejected_line_updates INT DEFAULT 0,
+        ADD COLUMN test_generation_accepted_lines INT DEFAULT 0,
+        ADD COLUMN test_generation_accepted_tests INT DEFAULT 0,
+        ADD COLUMN test_generation_event_count INT DEFAULT 0,
+        ADD COLUMN test_generation_generated_lines INT DEFAULT 0,
+        ADD COLUMN test_generation_generated_tests INT DEFAULT 0,
+        ADD COLUMN transformation_event_count INT DEFAULT 0,
+        ADD COLUMN transformation_lines_generated INT DEFAULT 0,
+        ADD COLUMN transformation_lines_ingested INT DEFAULT 0
+    `)
+
+       return nil
+}
+
+func (*addMissingMetrics) Version() uint64 {
+       return 20250710000001
+}
+
+func (*addMissingMetrics) Name() string {
+       return "add missing metrics columns to QDevUserData table"
+}
diff --git a/backend/plugins/q_dev/models/migrationscripts/register.go 
b/backend/plugins/q_dev/models/migrationscripts/register.go
index 4020753d5..85a74690e 100644
--- a/backend/plugins/q_dev/models/migrationscripts/register.go
+++ b/backend/plugins/q_dev/models/migrationscripts/register.go
@@ -27,5 +27,6 @@ func All() []plugin.MigrationScript {
                new(initTables),
                new(modifyFileMetaTable),
                new(addDisplayNameFields),
+               new(addMissingMetrics),
        }
 }
diff --git a/backend/plugins/q_dev/models/user_data.go 
b/backend/plugins/q_dev/models/user_data.go
index 107076742..75907121e 100644
--- a/backend/plugins/q_dev/models/user_data.go
+++ b/backend/plugins/q_dev/models/user_data.go
@@ -26,25 +26,55 @@ import (
 // QDevUserData 存储从CSV中提取的原始数据
 type QDevUserData struct {
        common.Model
-       ConnectionId                      uint64    `gorm:"primaryKey"`
-       UserId                            string    `gorm:"index" json:"userId"`
-       Date                              time.Time `gorm:"index" json:"date"`
-       DisplayName                       string    `gorm:"type:varchar(255)" 
json:"displayName"` // New field for user display name
-       CodeReview_FindingsCount          int
-       CodeReview_SucceededEventCount    int
-       InlineChat_AcceptanceEventCount   int
-       InlineChat_AcceptedLineAdditions  int
-       InlineChat_AcceptedLineDeletions  int
-       InlineChat_DismissalEventCount    int
-       InlineChat_DismissedLineAdditions int
-       InlineChat_DismissedLineDeletions int
-       InlineChat_RejectedLineAdditions  int
-       InlineChat_RejectedLineDeletions  int
-       InlineChat_RejectionEventCount    int
-       InlineChat_TotalEventCount        int
-       Inline_AICodeLines                int
-       Inline_AcceptanceCount            int
-       Inline_SuggestionsCount           int
+       ConnectionId uint64    `gorm:"primaryKey"`
+       UserId       string    `gorm:"index" json:"userId"`
+       Date         time.Time `gorm:"index" json:"date"`
+       DisplayName  string    `gorm:"type:varchar(255)" json:"displayName"` // 
New field for user display name
+
+       CodeReview_FindingsCount             int
+       CodeReview_SucceededEventCount       int
+       InlineChat_AcceptanceEventCount      int
+       InlineChat_AcceptedLineAdditions     int
+       InlineChat_AcceptedLineDeletions     int
+       InlineChat_DismissalEventCount       int
+       InlineChat_DismissedLineAdditions    int
+       InlineChat_DismissedLineDeletions    int
+       InlineChat_RejectedLineAdditions     int
+       InlineChat_RejectedLineDeletions     int
+       InlineChat_RejectionEventCount       int
+       InlineChat_TotalEventCount           int
+       Inline_AICodeLines                   int
+       Inline_AcceptanceCount               int
+       Inline_SuggestionsCount              int
+       Chat_AICodeLines                     int
+       Chat_MessagesInteracted              int
+       Chat_MessagesSent                    int
+       CodeFix_AcceptanceEventCount         int
+       CodeFix_AcceptedLines                int
+       CodeFix_GeneratedLines               int
+       CodeFix_GenerationEventCount         int
+       CodeReview_FailedEventCount          int
+       Dev_AcceptanceEventCount             int
+       Dev_AcceptedLines                    int
+       Dev_GeneratedLines                   int
+       Dev_GenerationEventCount             int
+       DocGeneration_AcceptedFileUpdates    int
+       DocGeneration_AcceptedFilesCreations int
+       DocGeneration_AcceptedLineAdditions  int
+       DocGeneration_AcceptedLineUpdates    int
+       DocGeneration_EventCount             int
+       DocGeneration_RejectedFileCreations  int
+       DocGeneration_RejectedFileUpdates    int
+       DocGeneration_RejectedLineAdditions  int
+       DocGeneration_RejectedLineUpdates    int
+       TestGeneration_AcceptedLines         int
+       TestGeneration_AcceptedTests         int
+       TestGeneration_EventCount            int
+       TestGeneration_GeneratedLines        int
+       TestGeneration_GeneratedTests        int
+       Transformation_EventCount            int
+       Transformation_LinesGenerated        int
+       Transformation_LinesIngested         int
 }
 
 func (QDevUserData) TableName() string {
diff --git a/backend/plugins/q_dev/models/user_data_test.go 
b/backend/plugins/q_dev/models/user_data_test.go
index 857fb6537..d45d1748f 100644
--- a/backend/plugins/q_dev/models/user_data_test.go
+++ b/backend/plugins/q_dev/models/user_data_test.go
@@ -20,99 +20,118 @@ package models
 import (
        "testing"
        "time"
-
+       
        "github.com/stretchr/testify/assert"
 )
 
-func TestQDevUserData_WithDisplayName(t *testing.T) {
-       userData := QDevUserData{
-               ConnectionId: 1,
-               UserId:       "uuid-123",
-               DisplayName:  "John Doe",
-               Date:         time.Now(),
-               CodeReview_FindingsCount: 5,
-               Inline_AcceptanceCount:   10,
-       }
-
-       assert.Equal(t, "John Doe", userData.DisplayName)
-       assert.Equal(t, "uuid-123", userData.UserId)
-       assert.Equal(t, uint64(1), userData.ConnectionId)
-       assert.Equal(t, 5, userData.CodeReview_FindingsCount)
-       assert.Equal(t, 10, userData.Inline_AcceptanceCount)
-}
-
-func TestQDevUserData_WithFallbackDisplayName(t *testing.T) {
-       userData := QDevUserData{
-               ConnectionId: 1,
-               UserId:       "uuid-456",
-               DisplayName:  "uuid-456", // Fallback case when display name 
resolution fails
-               Date:         time.Now(),
-       }
-
-       assert.Equal(t, "uuid-456", userData.DisplayName)
-       assert.Equal(t, userData.UserId, userData.DisplayName) // Should match 
when fallback
-}
-
-func TestQDevUserData_EmptyDisplayName(t *testing.T) {
-       userData := QDevUserData{
+func TestQDevUserDataAllMetrics(t *testing.T) {
+       // Create a test user data object with all metrics
+       userData := &QDevUserData{
                ConnectionId: 1,
-               UserId:       "uuid-789",
-               DisplayName:  "", // Empty display name
-               Date:         time.Now(),
+               UserId: "test-user-id",
+               Date: time.Now(),
+               DisplayName: "Test User",
+               
+               // Set values for existing metrics
+               CodeReview_FindingsCount: 10,
+               CodeReview_SucceededEventCount: 11,
+               InlineChat_AcceptanceEventCount: 12,
+               InlineChat_AcceptedLineAdditions: 13,
+               InlineChat_AcceptedLineDeletions: 14,
+               InlineChat_DismissalEventCount: 15,
+               InlineChat_DismissedLineAdditions: 16,
+               InlineChat_DismissedLineDeletions: 17,
+               InlineChat_RejectedLineAdditions: 18,
+               InlineChat_RejectedLineDeletions: 19,
+               InlineChat_RejectionEventCount: 20,
+               InlineChat_TotalEventCount: 21,
+               Inline_AICodeLines: 22,
+               Inline_AcceptanceCount: 23,
+               Inline_SuggestionsCount: 24,
+               
+               // Set values for new metrics
+               Chat_AICodeLines: 25,
+               Chat_MessagesInteracted: 26,
+               Chat_MessagesSent: 27,
+               CodeFix_AcceptanceEventCount: 28,
+               CodeFix_AcceptedLines: 29,
+               CodeFix_GeneratedLines: 30,
+               CodeFix_GenerationEventCount: 31,
+               CodeReview_FailedEventCount: 32,
+               Dev_AcceptanceEventCount: 33,
+               Dev_AcceptedLines: 34,
+               Dev_GeneratedLines: 35,
+               Dev_GenerationEventCount: 36,
+               DocGeneration_AcceptedFileUpdates: 37,
+               DocGeneration_AcceptedFilesCreations: 38,
+               DocGeneration_AcceptedLineAdditions: 39,
+               DocGeneration_AcceptedLineUpdates: 40,
+               DocGeneration_EventCount: 41,
+               DocGeneration_RejectedFileCreations: 42,
+               DocGeneration_RejectedFileUpdates: 43,
+               DocGeneration_RejectedLineAdditions: 44,
+               DocGeneration_RejectedLineUpdates: 45,
+               TestGeneration_AcceptedLines: 46,
+               TestGeneration_AcceptedTests: 47,
+               TestGeneration_EventCount: 48,
+               TestGeneration_GeneratedLines: 49,
+               TestGeneration_GeneratedTests: 50,
+               Transformation_EventCount: 51,
+               Transformation_LinesGenerated: 52,
+               Transformation_LinesIngested: 53,
        }
-
-       assert.Equal(t, "", userData.DisplayName)
-       assert.Equal(t, "uuid-789", userData.UserId)
-       assert.NotEqual(t, userData.UserId, userData.DisplayName)
+       
+       // Verify that all metrics are accessible
+       // Existing metrics
+       assert.Equal(t, 10, userData.CodeReview_FindingsCount)
+       assert.Equal(t, 11, userData.CodeReview_SucceededEventCount)
+       assert.Equal(t, 12, userData.InlineChat_AcceptanceEventCount)
+       assert.Equal(t, 13, userData.InlineChat_AcceptedLineAdditions)
+       assert.Equal(t, 14, userData.InlineChat_AcceptedLineDeletions)
+       assert.Equal(t, 15, userData.InlineChat_DismissalEventCount)
+       assert.Equal(t, 16, userData.InlineChat_DismissedLineAdditions)
+       assert.Equal(t, 17, userData.InlineChat_DismissedLineDeletions)
+       assert.Equal(t, 18, userData.InlineChat_RejectedLineAdditions)
+       assert.Equal(t, 19, userData.InlineChat_RejectedLineDeletions)
+       assert.Equal(t, 20, userData.InlineChat_RejectionEventCount)
+       assert.Equal(t, 21, userData.InlineChat_TotalEventCount)
+       assert.Equal(t, 22, userData.Inline_AICodeLines)
+       assert.Equal(t, 23, userData.Inline_AcceptanceCount)
+       assert.Equal(t, 24, userData.Inline_SuggestionsCount)
+       
+       // New metrics
+       assert.Equal(t, 25, userData.Chat_AICodeLines)
+       assert.Equal(t, 26, userData.Chat_MessagesInteracted)
+       assert.Equal(t, 27, userData.Chat_MessagesSent)
+       assert.Equal(t, 28, userData.CodeFix_AcceptanceEventCount)
+       assert.Equal(t, 29, userData.CodeFix_AcceptedLines)
+       assert.Equal(t, 30, userData.CodeFix_GeneratedLines)
+       assert.Equal(t, 31, userData.CodeFix_GenerationEventCount)
+       assert.Equal(t, 32, userData.CodeReview_FailedEventCount)
+       assert.Equal(t, 33, userData.Dev_AcceptanceEventCount)
+       assert.Equal(t, 34, userData.Dev_AcceptedLines)
+       assert.Equal(t, 35, userData.Dev_GeneratedLines)
+       assert.Equal(t, 36, userData.Dev_GenerationEventCount)
+       assert.Equal(t, 37, userData.DocGeneration_AcceptedFileUpdates)
+       assert.Equal(t, 38, userData.DocGeneration_AcceptedFilesCreations)
+       assert.Equal(t, 39, userData.DocGeneration_AcceptedLineAdditions)
+       assert.Equal(t, 40, userData.DocGeneration_AcceptedLineUpdates)
+       assert.Equal(t, 41, userData.DocGeneration_EventCount)
+       assert.Equal(t, 42, userData.DocGeneration_RejectedFileCreations)
+       assert.Equal(t, 43, userData.DocGeneration_RejectedFileUpdates)
+       assert.Equal(t, 44, userData.DocGeneration_RejectedLineAdditions)
+       assert.Equal(t, 45, userData.DocGeneration_RejectedLineUpdates)
+       assert.Equal(t, 46, userData.TestGeneration_AcceptedLines)
+       assert.Equal(t, 47, userData.TestGeneration_AcceptedTests)
+       assert.Equal(t, 48, userData.TestGeneration_EventCount)
+       assert.Equal(t, 49, userData.TestGeneration_GeneratedLines)
+       assert.Equal(t, 50, userData.TestGeneration_GeneratedTests)
+       assert.Equal(t, 51, userData.Transformation_EventCount)
+       assert.Equal(t, 52, userData.Transformation_LinesGenerated)
+       assert.Equal(t, 53, userData.Transformation_LinesIngested)
 }
 
-func TestQDevUserData_TableName(t *testing.T) {
-       userData := QDevUserData{}
+func TestQDevUserDataTableName(t *testing.T) {
+       userData := &QDevUserData{}
        assert.Equal(t, "_tool_q_dev_user_data", userData.TableName())
 }
-
-func TestQDevUserData_AllFields(t *testing.T) {
-       now := time.Now()
-       userData := QDevUserData{
-               ConnectionId:                      1,
-               UserId:                            "test-user",
-               DisplayName:                       "Test User",
-               Date:                              now,
-               CodeReview_FindingsCount:          1,
-               CodeReview_SucceededEventCount:    2,
-               InlineChat_AcceptanceEventCount:   3,
-               InlineChat_AcceptedLineAdditions:  4,
-               InlineChat_AcceptedLineDeletions:  5,
-               InlineChat_DismissalEventCount:    6,
-               InlineChat_DismissedLineAdditions: 7,
-               InlineChat_DismissedLineDeletions: 8,
-               InlineChat_RejectedLineAdditions:  9,
-               InlineChat_RejectedLineDeletions:  10,
-               InlineChat_RejectionEventCount:    11,
-               InlineChat_TotalEventCount:        12,
-               Inline_AICodeLines:                13,
-               Inline_AcceptanceCount:            14,
-               Inline_SuggestionsCount:           15,
-       }
-
-       // Verify all fields are properly set
-       assert.Equal(t, uint64(1), userData.ConnectionId)
-       assert.Equal(t, "test-user", userData.UserId)
-       assert.Equal(t, "Test User", userData.DisplayName)
-       assert.Equal(t, now, userData.Date)
-       assert.Equal(t, 1, userData.CodeReview_FindingsCount)
-       assert.Equal(t, 2, userData.CodeReview_SucceededEventCount)
-       assert.Equal(t, 3, userData.InlineChat_AcceptanceEventCount)
-       assert.Equal(t, 4, userData.InlineChat_AcceptedLineAdditions)
-       assert.Equal(t, 5, userData.InlineChat_AcceptedLineDeletions)
-       assert.Equal(t, 6, userData.InlineChat_DismissalEventCount)
-       assert.Equal(t, 7, userData.InlineChat_DismissedLineAdditions)
-       assert.Equal(t, 8, userData.InlineChat_DismissedLineDeletions)
-       assert.Equal(t, 9, userData.InlineChat_RejectedLineAdditions)
-       assert.Equal(t, 10, userData.InlineChat_RejectedLineDeletions)
-       assert.Equal(t, 11, userData.InlineChat_RejectionEventCount)
-       assert.Equal(t, 12, userData.InlineChat_TotalEventCount)
-       assert.Equal(t, 13, userData.Inline_AICodeLines)
-       assert.Equal(t, 14, userData.Inline_AcceptanceCount)
-       assert.Equal(t, 15, userData.Inline_SuggestionsCount)
-}
diff --git a/backend/plugins/q_dev/tasks/s3_data_extractor.go 
b/backend/plugins/q_dev/tasks/s3_data_extractor.go
index a4cbbcbdf..f091e15b9 100644
--- a/backend/plugins/q_dev/tasks/s3_data_extractor.go
+++ b/backend/plugins/q_dev/tasks/s3_data_extractor.go
@@ -20,16 +20,17 @@ package tasks
 import (
        "encoding/csv"
        "fmt"
+       "io"
+       "strconv"
+       "strings"
+       "time"
+
        "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"
        "github.com/aws/aws-sdk-go/aws"
        "github.com/aws/aws-sdk-go/service/s3"
-       "io"
-       "strconv"
-       "strings"
-       "time"
 )
 
 var _ plugin.SubTaskEntryPoint = ExtractQDevS3Data
@@ -70,21 +71,34 @@ func ExtractQDevS3Data(taskCtx plugin.SubTaskContext) 
errors.Error {
                        return errors.Convert(err)
                }
 
-               // 处理CSV文件
-               err = processCSVData(taskCtx, db, getResult.Body, fileMeta)
-               if err != nil {
-                       return errors.Default.Wrap(err, fmt.Sprintf("failed to 
process CSV file %s", fileMeta.FileName))
+               // Use a transaction to process the file and update its status
+               tx := db.Begin()
+               csvErr := processCSVData(taskCtx, tx, getResult.Body, fileMeta)
+               if csvErr != nil {
+                       if rollbackErr := tx.Rollback(); rollbackErr != nil {
+                               taskCtx.GetLogger().Error(rollbackErr, "failed 
to rollback transaction")
+                       }
+                       return errors.Default.Wrap(csvErr, fmt.Sprintf("failed 
to process CSV file %s", fileMeta.FileName))
                }
 
-               // 更新文件处理状态
+               // Update file processing status within the same transaction
                fileMeta.Processed = true
                now := time.Now()
                fileMeta.ProcessedTime = &now
-               err = db.Update(fileMeta)
+               err = tx.Update(fileMeta)
                if err != nil {
+                       if rollbackErr := tx.Rollback(); rollbackErr != nil {
+                               taskCtx.GetLogger().Error(rollbackErr, "failed 
to rollback transaction")
+                       }
                        return errors.Default.Wrap(err, "failed to update file 
metadata")
                }
 
+               // Commit the transaction
+               err = tx.Commit()
+               if err != nil {
+                       return errors.Default.Wrap(err, "failed to commit 
transaction")
+               }
+
                taskCtx.IncProgress(1)
        }
 
@@ -126,7 +140,7 @@ func processCSVData(taskCtx plugin.SubTaskContext, db 
dal.Dal, reader io.ReadClo
                        return errors.Default.Wrap(err, "failed to create user 
data")
                }
 
-               // 保存到数据库
+               // Save to database - no need to check for duplicates since 
we're processing each file only once
                err = db.Create(userData)
                if err != nil {
                        return errors.Default.Wrap(err, "failed to save user 
data")
@@ -187,7 +201,7 @@ func createUserDataWithDisplayName(headers []string, record 
[]string, fileMeta *
                return nil, errors.Default.Wrap(err, "failed to parse date")
        }
 
-       // 设置指标字段
+       // 设置所有指标字段
        userData.CodeReview_FindingsCount = parseInt(fieldMap, 
"CodeReview_FindingsCount")
        userData.CodeReview_SucceededEventCount = parseInt(fieldMap, 
"CodeReview_SucceededEventCount")
        userData.InlineChat_AcceptanceEventCount = parseInt(fieldMap, 
"InlineChat_AcceptanceEventCount")
@@ -203,6 +217,35 @@ func createUserDataWithDisplayName(headers []string, 
record []string, fileMeta *
        userData.Inline_AICodeLines = parseInt(fieldMap, "Inline_AICodeLines")
        userData.Inline_AcceptanceCount = parseInt(fieldMap, 
"Inline_AcceptanceCount")
        userData.Inline_SuggestionsCount = parseInt(fieldMap, 
"Inline_SuggestionsCount")
+       userData.Chat_AICodeLines = parseInt(fieldMap, "Chat_AICodeLines")
+       userData.Chat_MessagesInteracted = parseInt(fieldMap, 
"Chat_MessagesInteracted")
+       userData.Chat_MessagesSent = parseInt(fieldMap, "Chat_MessagesSent")
+       userData.CodeFix_AcceptanceEventCount = parseInt(fieldMap, 
"CodeFix_AcceptanceEventCount")
+       userData.CodeFix_AcceptedLines = parseInt(fieldMap, 
"CodeFix_AcceptedLines")
+       userData.CodeFix_GeneratedLines = parseInt(fieldMap, 
"CodeFix_GeneratedLines")
+       userData.CodeFix_GenerationEventCount = parseInt(fieldMap, 
"CodeFix_GenerationEventCount")
+       userData.CodeReview_FailedEventCount = parseInt(fieldMap, 
"CodeReview_FailedEventCount")
+       userData.Dev_AcceptanceEventCount = parseInt(fieldMap, 
"Dev_AcceptanceEventCount")
+       userData.Dev_AcceptedLines = parseInt(fieldMap, "Dev_AcceptedLines")
+       userData.Dev_GeneratedLines = parseInt(fieldMap, "Dev_GeneratedLines")
+       userData.Dev_GenerationEventCount = parseInt(fieldMap, 
"Dev_GenerationEventCount")
+       userData.DocGeneration_AcceptedFileUpdates = parseInt(fieldMap, 
"DocGeneration_AcceptedFileUpdates")
+       userData.DocGeneration_AcceptedFilesCreations = parseInt(fieldMap, 
"DocGeneration_AcceptedFilesCreations")
+       userData.DocGeneration_AcceptedLineAdditions = parseInt(fieldMap, 
"DocGeneration_AcceptedLineAdditions")
+       userData.DocGeneration_AcceptedLineUpdates = parseInt(fieldMap, 
"DocGeneration_AcceptedLineUpdates")
+       userData.DocGeneration_EventCount = parseInt(fieldMap, 
"DocGeneration_EventCount")
+       userData.DocGeneration_RejectedFileCreations = parseInt(fieldMap, 
"DocGeneration_RejectedFileCreations")
+       userData.DocGeneration_RejectedFileUpdates = parseInt(fieldMap, 
"DocGeneration_RejectedFileUpdates")
+       userData.DocGeneration_RejectedLineAdditions = parseInt(fieldMap, 
"DocGeneration_RejectedLineAdditions")
+       userData.DocGeneration_RejectedLineUpdates = parseInt(fieldMap, 
"DocGeneration_RejectedLineUpdates")
+       userData.TestGeneration_AcceptedLines = parseInt(fieldMap, 
"TestGeneration_AcceptedLines")
+       userData.TestGeneration_AcceptedTests = parseInt(fieldMap, 
"TestGeneration_AcceptedTests")
+       userData.TestGeneration_EventCount = parseInt(fieldMap, 
"TestGeneration_EventCount")
+       userData.TestGeneration_GeneratedLines = parseInt(fieldMap, 
"TestGeneration_GeneratedLines")
+       userData.TestGeneration_GeneratedTests = parseInt(fieldMap, 
"TestGeneration_GeneratedTests")
+       userData.Transformation_EventCount = parseInt(fieldMap, 
"Transformation_EventCount")
+       userData.Transformation_LinesGenerated = parseInt(fieldMap, 
"Transformation_LinesGenerated")
+       userData.Transformation_LinesIngested = parseInt(fieldMap, 
"Transformation_LinesIngested")
 
        return userData, nil
 }
diff --git a/backend/plugins/q_dev/tasks/s3_data_extractor_test.go 
b/backend/plugins/q_dev/tasks/s3_data_extractor_test.go
index 8c824f3e1..1e84e81ef 100644
--- a/backend/plugins/q_dev/tasks/s3_data_extractor_test.go
+++ b/backend/plugins/q_dev/tasks/s3_data_extractor_test.go
@@ -118,7 +118,7 @@ func TestCreateUserDataWithDisplayName_EmptyDisplayName(t 
*testing.T) {
        mockIdentityClient.AssertExpectations(t)
 }
 
-func TestCreateUserDataWithDisplayName_AllFields(t *testing.T) {
+func TestCreateUserDataWithDisplayName_AllExistingMetrics(t *testing.T) {
        headers := []string{
                "UserId", "Date", "CodeReview_FindingsCount", 
"CodeReview_SucceededEventCount",
                "InlineChat_AcceptanceEventCount", 
"InlineChat_AcceptedLineAdditions",
@@ -152,7 +152,7 @@ func TestCreateUserDataWithDisplayName_AllFields(t 
*testing.T) {
        expectedDate, _ := time.Parse("2006-01-02", "2025-06-23")
        assert.Equal(t, expectedDate, userData.Date)
        
-       // Verify all metric fields
+       // Verify all existing metric fields
        assert.Equal(t, 1, userData.CodeReview_FindingsCount)
        assert.Equal(t, 2, userData.CodeReview_SucceededEventCount)
        assert.Equal(t, 3, userData.InlineChat_AcceptanceEventCount)
@@ -172,6 +172,139 @@ func TestCreateUserDataWithDisplayName_AllFields(t 
*testing.T) {
        mockIdentityClient.AssertExpectations(t)
 }
 
+func TestCreateUserDataWithDisplayName_AllNewMetrics(t *testing.T) {
+       headers := []string{
+               "UserId", "Date",
+               "Chat_AICodeLines", "Chat_MessagesInteracted", 
"Chat_MessagesSent",
+               "CodeFix_AcceptanceEventCount", "CodeFix_AcceptedLines", 
"CodeFix_GeneratedLines", "CodeFix_GenerationEventCount",
+               "CodeReview_FailedEventCount",
+               "Dev_AcceptanceEventCount", "Dev_AcceptedLines", 
"Dev_GeneratedLines", "Dev_GenerationEventCount",
+               "DocGeneration_AcceptedFileUpdates", 
"DocGeneration_AcceptedFilesCreations", "DocGeneration_AcceptedLineAdditions",
+               "DocGeneration_AcceptedLineUpdates", 
"DocGeneration_EventCount", "DocGeneration_RejectedFileCreations",
+               "DocGeneration_RejectedFileUpdates", 
"DocGeneration_RejectedLineAdditions", "DocGeneration_RejectedLineUpdates",
+               "TestGeneration_AcceptedLines", "TestGeneration_AcceptedTests", 
"TestGeneration_EventCount",
+               "TestGeneration_GeneratedLines", 
"TestGeneration_GeneratedTests",
+               "Transformation_EventCount", "Transformation_LinesGenerated", 
"Transformation_LinesIngested",
+       }
+       
+       record := []string{
+               "test-user", "2025-06-23",
+               "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",
+       }
+       
+       fileMeta := &models.QDevS3FileMeta{
+               ConnectionId: 123,
+       }
+       
+       mockIdentityClient := &MockIdentityClient{}
+       mockIdentityClient.On("ResolveUserDisplayName", 
"test-user").Return("Test User", nil)
+       
+       userData, err := createUserDataWithDisplayName(headers, record, 
fileMeta, mockIdentityClient)
+       
+       assert.NoError(t, err)
+       assert.NotNil(t, userData)
+       
+       // Verify basic fields
+       assert.Equal(t, "test-user", userData.UserId)
+       assert.Equal(t, "Test User", userData.DisplayName)
+       
+       // Verify all new metric fields
+       assert.Equal(t, 101, userData.Chat_AICodeLines)
+       assert.Equal(t, 102, userData.Chat_MessagesInteracted)
+       assert.Equal(t, 103, userData.Chat_MessagesSent)
+       assert.Equal(t, 104, userData.CodeFix_AcceptanceEventCount)
+       assert.Equal(t, 105, userData.CodeFix_AcceptedLines)
+       assert.Equal(t, 106, userData.CodeFix_GeneratedLines)
+       assert.Equal(t, 107, userData.CodeFix_GenerationEventCount)
+       assert.Equal(t, 108, userData.CodeReview_FailedEventCount)
+       assert.Equal(t, 109, userData.Dev_AcceptanceEventCount)
+       assert.Equal(t, 110, userData.Dev_AcceptedLines)
+       assert.Equal(t, 111, userData.Dev_GeneratedLines)
+       assert.Equal(t, 112, userData.Dev_GenerationEventCount)
+       assert.Equal(t, 113, userData.DocGeneration_AcceptedFileUpdates)
+       assert.Equal(t, 114, userData.DocGeneration_AcceptedFilesCreations)
+       assert.Equal(t, 115, userData.DocGeneration_AcceptedLineAdditions)
+       assert.Equal(t, 116, userData.DocGeneration_AcceptedLineUpdates)
+       assert.Equal(t, 117, userData.DocGeneration_EventCount)
+       assert.Equal(t, 118, userData.DocGeneration_RejectedFileCreations)
+       assert.Equal(t, 119, userData.DocGeneration_RejectedFileUpdates)
+       assert.Equal(t, 120, userData.DocGeneration_RejectedLineAdditions)
+       assert.Equal(t, 121, userData.DocGeneration_RejectedLineUpdates)
+       assert.Equal(t, 122, userData.TestGeneration_AcceptedLines)
+       assert.Equal(t, 123, userData.TestGeneration_AcceptedTests)
+       assert.Equal(t, 124, userData.TestGeneration_EventCount)
+       assert.Equal(t, 125, userData.TestGeneration_GeneratedLines)
+       assert.Equal(t, 126, userData.TestGeneration_GeneratedTests)
+       assert.Equal(t, 127, userData.Transformation_EventCount)
+       assert.Equal(t, 128, userData.Transformation_LinesGenerated)
+       assert.Equal(t, 129, userData.Transformation_LinesIngested)
+       
+       mockIdentityClient.AssertExpectations(t)
+}
+
+func TestCreateUserDataWithDisplayName_MissingMetrics(t *testing.T) {
+       // Only provide a few metrics in the CSV
+       headers := []string{"UserId", "Date", "CodeReview_FindingsCount", 
"Chat_AICodeLines"}
+       record := []string{"test-user", "2025-06-23", "42", "99"}
+       
+       fileMeta := &models.QDevS3FileMeta{
+               ConnectionId: 123,
+       }
+       
+       mockIdentityClient := &MockIdentityClient{}
+       mockIdentityClient.On("ResolveUserDisplayName", 
"test-user").Return("Test User", nil)
+       
+       userData, err := createUserDataWithDisplayName(headers, record, 
fileMeta, mockIdentityClient)
+       
+       assert.NoError(t, err)
+       assert.NotNil(t, userData)
+       
+       // Verify provided metrics are set correctly
+       assert.Equal(t, 42, userData.CodeReview_FindingsCount)
+       assert.Equal(t, 99, userData.Chat_AICodeLines)
+       
+       // Verify missing metrics are set to 0
+       assert.Equal(t, 0, userData.CodeReview_SucceededEventCount)
+       assert.Equal(t, 0, userData.InlineChat_AcceptanceEventCount)
+       assert.Equal(t, 0, userData.Chat_MessagesInteracted)
+       assert.Equal(t, 0, userData.TestGeneration_AcceptedTests)
+       assert.Equal(t, 0, userData.Transformation_LinesIngested)
+       
+       mockIdentityClient.AssertExpectations(t)
+}
+
+func TestCreateUserDataWithDisplayName_InvalidMetricValues(t *testing.T) {
+       headers := []string{
+               "UserId", "Date", "CodeReview_FindingsCount", 
"Chat_AICodeLines", 
+               "InlineChat_AcceptanceEventCount", 
"TestGeneration_AcceptedTests",
+       }
+       record := []string{"test-user", "2025-06-23", "42", "not-a-number", 
"abc", ""}
+       
+       fileMeta := &models.QDevS3FileMeta{
+               ConnectionId: 123,
+       }
+       
+       mockIdentityClient := &MockIdentityClient{}
+       mockIdentityClient.On("ResolveUserDisplayName", 
"test-user").Return("Test User", nil)
+       
+       userData, err := createUserDataWithDisplayName(headers, record, 
fileMeta, mockIdentityClient)
+       
+       assert.NoError(t, err)
+       assert.NotNil(t, userData)
+       
+       // Verify valid metric is set correctly
+       assert.Equal(t, 42, userData.CodeReview_FindingsCount)
+       
+       // Verify invalid metrics are set to 0
+       assert.Equal(t, 0, userData.Chat_AICodeLines)
+       assert.Equal(t, 0, userData.InlineChat_AcceptanceEventCount)
+       assert.Equal(t, 0, userData.TestGeneration_AcceptedTests)
+       
+       mockIdentityClient.AssertExpectations(t)
+}
+
 func TestCreateUserDataWithDisplayName_MissingUserId(t *testing.T) {
        headers := []string{"Date", "CodeReview_FindingsCount"}
        record := []string{"2025-06-23", "5"}
@@ -199,3 +332,46 @@ func TestCreateUserDataWithDisplayName_MissingDate(t 
*testing.T) {
        assert.Nil(t, userData)
        assert.Contains(t, err.Error(), "Date not found")
 }
+
+func TestParseDate(t *testing.T) {
+       testCases := []struct {
+               dateStr      string
+               expectedDate time.Time
+               expectError  bool
+       }{
+               {"2025-07-10", time.Date(2025, 7, 10, 0, 0, 0, 0, time.UTC), 
false},
+               {"2025/07/10", time.Date(2025, 7, 10, 0, 0, 0, 0, time.UTC), 
false},
+               {"07/10/2025", time.Date(2025, 7, 10, 0, 0, 0, 0, time.UTC), 
false},
+               {"07-10-2025", time.Date(2025, 7, 10, 0, 0, 0, 0, time.UTC), 
false},
+               {"2025-07-10T15:04:05Z", time.Date(2025, 7, 10, 15, 4, 5, 0, 
time.UTC), false},
+               {"invalid-date", time.Time{}, true},
+       }
+       
+       for _, tc := range testCases {
+               date, err := parseDate(tc.dateStr)
+               
+               if tc.expectError {
+                       assert.Error(t, err)
+               } else {
+                       assert.NoError(t, err)
+                       assert.Equal(t, tc.expectedDate, date)
+               }
+       }
+}
+
+func TestParseInt(t *testing.T) {
+       fieldMap := map[string]string{
+               "ValidInt": "42",
+               "ZeroInt": "0",
+               "NegativeInt": "-10",
+               "InvalidInt": "not-a-number",
+               "EmptyString": "",
+       }
+       
+       assert.Equal(t, 42, parseInt(fieldMap, "ValidInt"))
+       assert.Equal(t, 0, parseInt(fieldMap, "ZeroInt"))
+       assert.Equal(t, -10, parseInt(fieldMap, "NegativeInt"))
+       assert.Equal(t, 0, parseInt(fieldMap, "InvalidInt"))
+       assert.Equal(t, 0, parseInt(fieldMap, "EmptyString"))
+       assert.Equal(t, 0, parseInt(fieldMap, "NonExistentField"))
+}
diff --git a/backend/plugins/q_dev/tasks/s3_file_collector.go 
b/backend/plugins/q_dev/tasks/s3_file_collector.go
index d06f066e1..37abf1845 100644
--- a/backend/plugins/q_dev/tasks/s3_file_collector.go
+++ b/backend/plugins/q_dev/tasks/s3_file_collector.go
@@ -18,13 +18,14 @@ limitations under the License.
 package tasks
 
 import (
+       "strings"
+
        "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"
        "github.com/aws/aws-sdk-go/aws"
        "github.com/aws/aws-sdk-go/service/s3"
-       "strings"
 )
 
 var _ plugin.SubTaskEntryPoint = CollectQDevS3Files
@@ -43,12 +44,6 @@ func CollectQDevS3Files(taskCtx plugin.SubTaskContext) 
errors.Error {
 
        taskCtx.SetProgress(0, -1)
 
-       // 清空以前的元数据记录
-       err := db.Delete(&models.QDevS3FileMeta{}, dal.Where("connection_id = 
?", data.Options.ConnectionId))
-       if err != nil {
-               return errors.Default.Wrap(err, "failed to clean previous file 
metadata")
-       }
-
        for {
                input := &s3.ListObjectsV2Input{
                        Bucket:            aws.String(data.S3Client.Bucket),
@@ -63,12 +58,31 @@ func CollectQDevS3Files(taskCtx plugin.SubTaskContext) 
errors.Error {
 
                // 处理每个CSV文件
                for _, object := range result.Contents {
-                       // 只处理CSV文件
+                       // Only process CSV files
                        if !strings.HasSuffix(*object.Key, ".csv") {
+                               taskCtx.GetLogger().Debug("Skipping non-CSV 
file: %s", *object.Key)
+                               continue
+                       }
+
+                       // Check if this file already exists in our database
+                       existingFile := &models.QDevS3FileMeta{}
+                       err = db.First(existingFile, dal.Where("connection_id = 
? AND s3_path = ?",
+                               data.Options.ConnectionId, *object.Key))
+
+                       if err == nil {
+                               // File already exists in database, skip it if 
it's already processed
+                               if existingFile.Processed {
+                                       taskCtx.GetLogger().Debug("Skipping 
already processed file: %s", *object.Key)
+                                       continue
+                               }
+                               // Otherwise, we'll keep the existing record 
(which is still marked as unprocessed)
+                               taskCtx.GetLogger().Debug("Found existing 
unprocessed file: %s", *object.Key)
                                continue
+                       } else if !db.IsErrorNotFound(err) {
+                               return errors.Default.Wrap(err, "failed to 
query existing file metadata")
                        }
 
-                       // 保存文件元数据
+                       // This is a new file, save its metadata
                        fileMeta := &models.QDevS3FileMeta{
                                ConnectionId: data.Options.ConnectionId,
                                FileName:     *object.Key,
diff --git a/grafana/dashboards/qdev_user_data.json 
b/grafana/dashboards/qdev_user_data.json
index 9a66d82b7..3bdf3c427 100644
--- a/grafana/dashboards/qdev_user_data.json
+++ b/grafana/dashboards/qdev_user_data.json
@@ -72,7 +72,7 @@
           "group": [],
           "metricColumn": "none",
           "rawQuery": true,
-          "rawSql": "SELECT\n  COUNT(DISTINCT user_id) as 'Active Users',\n  
SUM(inline_ai_code_lines) as 'Accepted Lines (Inline Suggestion)',\n  
SUM(inline_acceptance_count) as 'Accepted Count (Inline Suggestion)',\n  
SUM(inline_suggestions_count) as 'Total Count (Inline Suggestion)',\n  
SUM(inline_acceptance_count) / NULLIF(SUM(inline_suggestions_count), 0) as 
'Acceptance Rate (Inline Suggestion)',\n  SUM(code_review_findings_count) as 
'Findings (Code Review)',\n  SUM(inline_chat_acce [...]
+          "rawSql": "SELECT\n  COUNT(DISTINCT user_id) as 'Active Users',\n  
SUM(chat_ai_code_lines) as 'Accepted Lines (Chat)',\n  
SUM(inline_ai_code_lines) as 'Accepted Lines (Inline Suggestion)',\n  
SUM(inline_acceptance_count) / NULLIF(SUM(inline_suggestions_count), 0) as 
'Acceptance Rate (Inline Suggestion)',\n  SUM(code_review_findings_count) as 
'Findings (Code Review)',\n  SUM(code_fix_accepted_lines) as 'Accepted Lines 
(Code Fix)',\n  SUM(code_fix_generation_event_count) / NULLIF [...]
           "refId": "A",
           "select": [
             [
@@ -201,7 +201,7 @@
           "group": [],
           "metricColumn": "none",
           "rawQuery": true,
-          "rawSql": "SELECT\n  date as time,\n  SUM(inline_ai_code_lines) as 
'Inline Suggestion Accepted Lines',\n  SUM(inline_chat_accepted_line_additions) 
as 'Inline Chat Accepted Line Additions',\n  
SUM(inline_chat_accepted_line_deletions) as 'Inline Chat Accepted Line 
Deletions',\n  SUM(inline_chat_dismissed_line_additions) as 'Inline Chat 
Dismissed Line Additions',\n  SUM(inline_chat_dismissed_line_deletions) as 
'Inline Chat Dismissed Line Deletions',\n  SUM(inline_chat_rejected_lin [...]
+          "rawSql": "SELECT\n  date as time,\n  SUM(chat_ai_code_lines) as 
'Chat Accepted Lines',\n  SUM(code_fix_accepted_lines) as 'Code Fix Accepted 
Lines',\n  SUM(code_fix_generated_lines) as 'Code Fix Generated Lines',\n  
SUM(transformation_lines_ingested) as 'Java Transform Ingested Lines',\n  
SUM(transformation_lines_generated) as 'Java Transform Generated Lines',\n  
SUM(inline_ai_code_lines) as 'Inline Suggestion Accepted Lines',\n  
SUM(inline_chat_accepted_line_additions) as 'In [...]
           "refId": "A",
           "select": [
             [
@@ -245,7 +245,7 @@
     },
     {
       "datasource": "mysql",
-      "description": "Daily AI code interaction trends across all users",
+      "description": "Daily AI interaction trends across all users",
       "fieldConfig": {
         "defaults": {
           "color": {
@@ -330,7 +330,7 @@
           "group": [],
           "metricColumn": "none",
           "rawQuery": true,
-          "rawSql": "SELECT\n  date as time,\n  SUM(inline_acceptance_count) 
as 'Inline Suggestion Accepted Suggestions',\n  SUM(inline_suggestions_count) 
as 'Inline Suggestion Count',\n  SUM(inline_chat_total_event_count) as 'Inline 
Chat Total Suggestions',\n  SUM(inline_chat_acceptance_event_count) as 'Inline 
Chat Accepted Suggestions',\n  SUM(inline_chat_dismissal_event_count) as 
'Inline Chat Dismissed Suggestions',\n  SUM(inline_chat_rejection_event_count) 
as 'Inline Chat Rejected Su [...]
+          "rawSql": "SELECT\n  date as time,\n  SUM(chat_messages_sent) as 
'Chat Messages Sent',\n  SUM(code_fix_acceptance_event_count) as 'Code Fix 
Accepted Event Count',\n  SUM(code_fix_generation_event_count) as 'Code Fix 
Generated Event Count',\n  SUM(transformation_event_count) as 'Java Transform 
Event Count',\n  SUM(inline_acceptance_count) as 'Inline Suggestion Accepted 
Suggestions',\n  SUM(inline_suggestions_count) as 'Inline Suggestion Count',\n  
SUM(inline_chat_total_event_cou [...]
           "refId": "A",
           "select": [
             [
@@ -369,7 +369,7 @@
           ]
         }
       ],
-      "title": "Daily AI Suggestion Interactions",
+      "title": "Daily AI Interactions",
       "type": "timeseries"
     },
     {
@@ -458,7 +458,7 @@
           "group": [],
           "metricColumn": "none",
           "rawQuery": true,
-          "rawSql": "SELECT\n  date as time,\n  
SUM(code_review_findings_count) as 'Code Review Findings',\n  
SUM(code_review_succeeded_event_count) as 'Code Review Succeeded Events'\nFROM 
lake._tool_q_dev_user_data\nWHERE $__timeFilter(date)\nGROUP BY date\nORDER BY 
date",
+          "rawSql": "SELECT\n  date as time,\n  
SUM(code_fix_acceptance_event_count) as 'Code Fix Accepted Event Count',\n  
SUM(code_fix_generation_event_count) as 'Code Fix Generated Event Count',\n  
SUM(code_review_findings_count) as 'Total Findings'\nFROM 
lake._tool_q_dev_user_data\nWHERE $__timeFilter(date)\nGROUP BY date\nORDER BY 
date",
           "refId": "A",
           "select": [
             [
@@ -470,6 +470,23 @@
               }
             ]
           ],
+          "sql": {
+            "columns": [
+              {
+                "parameters": [],
+                "type": "function"
+              }
+            ],
+            "groupBy": [
+              {
+                "property": {
+                  "type": "string"
+                },
+                "type": "groupBy"
+              }
+            ],
+            "limit": 50
+          },
           "timeColumn": "time",
           "where": [
             {
@@ -570,7 +587,7 @@
           "group": [],
           "metricColumn": "none",
           "rawQuery": true,
-          "rawSql": "SELECT\n  date as time,\n  SUM(inline_acceptance_count) / 
NULLIF(SUM(inline_suggestions_count), 0) as 'Inline Suggestions Acceptance 
Rate',\n  SUM(inline_chat_acceptance_event_count) / 
NULLIF(SUM(inline_chat_total_event_count), 0) as 'Inline Chat Acceptance 
Rate'\nFROM lake._tool_q_dev_user_data\nWHERE $__timeFilter(date)\nGROUP BY 
date\nORDER BY date",
+          "rawSql": "SELECT\n  date as time,\n  
SUM(code_fix_acceptance_event_count) / 
NULLIF(SUM(code_fix_generation_event_count), 0) as 'Code Fix',\n  
SUM(inline_acceptance_count) / NULLIF(SUM(inline_suggestions_count), 0) as 
'Inline Suggestions',\n  SUM(inline_chat_acceptance_event_count) / 
NULLIF(SUM(inline_chat_total_event_count), 0) as 'Inline Chat'\nFROM 
lake._tool_q_dev_user_data\nWHERE $__timeFilter(date)\nGROUP BY date\nORDER BY 
date",
           "refId": "A",
           "select": [
             [
@@ -713,7 +730,7 @@
           "group": [],
           "metricColumn": "none",
           "rawQuery": true,
-          "rawSql": "SELECT\n  COALESCE(display_name, user_id) as 'User',\n  
SUM(inline_ai_code_lines) as 'Accepted Lines (Inline Suggestion)',\n  
SUM(inline_acceptance_count) as 'Accepted Count (Inline Suggestion)',\n  
SUM(inline_suggestions_count) as 'Total Count (Inline Suggestion)',\n  
CONCAT(ROUND(SUM(inline_acceptance_count) / 
NULLIF(SUM(inline_suggestions_count), 0) * 100, 2), '%') as 'Acceptance Rate 
(Inline Suggestion)',\n  SUM(code_review_findings_count) as 'Findings (Code 
Revi [...]
+          "rawSql": "SELECT\n  COALESCE(display_name, user_id) as 'User',\n  
SUM(chat_ai_code_lines) as 'Accepted Lines (Chat)',\n  
SUM(transformation_lines_ingested) as 'Lines Ingested (Java Transform)',\n  
SUM(transformation_lines_generated) as 'Lines Generated (Java Transform)',\n  
SUM(transformation_event_count) as 'Event Count (Java Transform)',\n  
SUM(code_review_findings_count) as 'Findings (Code Review)',\n  
SUM(code_fix_accepted_lines) as 'Accepted Lines (Code Fix)',\n  SUM(code [...]
           "refId": "A",
           "select": [
             [
@@ -754,216 +771,6 @@
       ],
       "title": "User Interactions",
       "type": "table"
-    },
-    {
-      "datasource": "mysql",
-      "description": "Distribution of AI suggestion types across users",
-      "fieldConfig": {
-        "defaults": {
-          "color": {
-            "mode": "palette-classic"
-          },
-          "custom": {
-            "hideFrom": {
-              "legend": false,
-              "tooltip": false,
-              "viz": false
-            }
-          },
-          "mappings": []
-        },
-        "overrides": []
-      },
-      "gridPos": {
-        "h": 8,
-        "w": 12,
-        "x": 0,
-        "y": 40
-      },
-      "id": 7,
-      "options": {
-        "displayLabels": [
-          "percent"
-        ],
-        "legend": {
-          "displayMode": "list",
-          "placement": "right",
-          "showLegend": true,
-          "values": [
-            "value"
-          ]
-        },
-        "pieType": "pie",
-        "reduceOptions": {
-          "calcs": [
-            "sum"
-          ],
-          "fields": "",
-          "values": false
-        },
-        "tooltip": {
-          "hideZeros": false,
-          "mode": "single",
-          "sort": "none"
-        }
-      },
-      "pluginVersion": "11.6.2",
-      "targets": [
-        {
-          "datasource": "mysql",
-          "format": "table",
-          "group": [],
-          "metricColumn": "none",
-          "rawQuery": true,
-          "rawSql": "SELECT\n  'Accepted' as category,\n  
SUM(inline_chat_acceptance_event_count) as value\nFROM 
lake._tool_q_dev_user_data\nWHERE $__timeFilter(date)\nUNION ALL\nSELECT\n  
'Dismissed' as category,\n  SUM(inline_chat_dismissal_event_count) as 
value\nFROM lake._tool_q_dev_user_data\nWHERE $__timeFilter(date)\nUNION 
ALL\nSELECT\n  'Rejected' as category,\n  
SUM(inline_chat_rejection_event_count) as value\nFROM 
lake._tool_q_dev_user_data\nWHERE $__timeFilter(date)",
-          "refId": "A",
-          "select": [
-            [
-              {
-                "params": [
-                  "value"
-                ],
-                "type": "column"
-              }
-            ]
-          ],
-          "timeColumn": "time",
-          "where": [
-            {
-              "name": "$__timeFilter",
-              "params": [],
-              "type": "macro"
-            }
-          ]
-        }
-      ],
-      "title": "Inline Chat Response Distribution",
-      "type": "piechart"
-    },
-    {
-      "datasource": "mysql",
-      "description": "Weekly trends in AI code interactions",
-      "fieldConfig": {
-        "defaults": {
-          "color": {
-            "mode": "palette-classic"
-          },
-          "custom": {
-            "axisBorderShow": false,
-            "axisCenteredZero": false,
-            "axisColorMode": "text",
-            "axisLabel": "",
-            "axisPlacement": "auto",
-            "axisSoftMin": 0,
-            "fillOpacity": 80,
-            "gradientMode": "none",
-            "hideFrom": {
-              "legend": false,
-              "tooltip": false,
-              "viz": false
-            },
-            "lineWidth": 1,
-            "scaleDistribution": {
-              "type": "linear"
-            },
-            "thresholdsStyle": {
-              "mode": "off"
-            }
-          },
-          "mappings": [],
-          "thresholds": {
-            "mode": "absolute",
-            "steps": [
-              {
-                "color": "green"
-              }
-            ]
-          }
-        },
-        "overrides": []
-      },
-      "gridPos": {
-        "h": 8,
-        "w": 12,
-        "x": 12,
-        "y": 40
-      },
-      "id": 8,
-      "options": {
-        "barRadius": 0,
-        "barWidth": 0.6,
-        "fullHighlight": false,
-        "groupWidth": 0.7,
-        "legend": {
-          "calcs": [],
-          "displayMode": "list",
-          "placement": "right",
-          "showLegend": true
-        },
-        "orientation": "auto",
-        "showValue": "auto",
-        "stacking": "none",
-        "text": {
-          "valueSize": 12
-        },
-        "tooltip": {
-          "hideZeros": false,
-          "mode": "single",
-          "sort": "none"
-        },
-        "xTickLabelRotation": 0,
-        "xTickLabelSpacing": 0
-      },
-      "pluginVersion": "11.6.2",
-      "targets": [
-        {
-          "datasource": "mysql",
-          "editorMode": "code",
-          "format": "time_series",
-          "group": [],
-          "metricColumn": "none",
-          "rawQuery": true,
-          "rawSql": "SELECT\n  DATE_FORMAT(date, '%Y-%U') as metric,\n  
MIN(date) as time,\n  SUM(inline_chat_acceptance_event_count) as 'Accepted',\n  
SUM(inline_chat_dismissal_event_count) as 'Dismissed',\n  
SUM(inline_chat_rejection_event_count) as 'Rejected'\nFROM 
lake._tool_q_dev_user_data\nWHERE $__timeFilter(date)\nGROUP BY 
DATE_FORMAT(date, '%Y-%U')\nORDER BY time",
-          "refId": "A",
-          "select": [
-            [
-              {
-                "params": [
-                  "value"
-                ],
-                "type": "column"
-              }
-            ]
-          ],
-          "sql": {
-            "columns": [
-              {
-                "parameters": [],
-                "type": "function"
-              }
-            ],
-            "groupBy": [
-              {
-                "property": {
-                  "type": "string"
-                },
-                "type": "groupBy"
-              }
-            ],
-            "limit": 50
-          },
-          "timeColumn": "time",
-          "where": [
-            {
-              "name": "$__timeFilter",
-              "params": [],
-              "type": "macro"
-            }
-          ]
-        }
-      ],
-      "title": "Weekly Inline Chat Responses",
-      "type": "barchart"
     }
   ],
   "preload": false,


Reply via email to