From beec506bf061a983fe6cf8722da3b5bb58a61d36 Mon Sep 17 00:00:00 2001 From: warren Date: Sat, 27 Dec 2025 00:46:17 +0800 Subject: [PATCH 1/2] feat: Collect Pull Request Reviewer Status --- backend/plugins/bitbucket/impl/impl.go | 2 + .../20251226_add_pr_reviewer_table.go | 64 + .../models/migrationscripts/register.go | 1 + .../plugins/bitbucket/models/pr_reviewer.go | 41 + .../plugins/bitbucket/tasks/pr_collector.go | 2 + .../plugins/bitbucket/tasks/pr_extractor.go | 41 +- .../bitbucket/tasks/pr_reviewer_convertor.go | 80 + grafana/dashboards/qdev_user_data.json | 1588 ++++++++--------- 8 files changed, 1023 insertions(+), 796 deletions(-) create mode 100644 backend/plugins/bitbucket/models/migrationscripts/20251226_add_pr_reviewer_table.go create mode 100644 backend/plugins/bitbucket/models/pr_reviewer.go create mode 100644 backend/plugins/bitbucket/tasks/pr_reviewer_convertor.go diff --git a/backend/plugins/bitbucket/impl/impl.go b/backend/plugins/bitbucket/impl/impl.go index 3fd1ec6f836..9586294ec22 100644 --- a/backend/plugins/bitbucket/impl/impl.go +++ b/backend/plugins/bitbucket/impl/impl.go @@ -80,6 +80,7 @@ func (p Bitbucket) GetTablesInfo() []dal.Tabler { &models.BitbucketDeployment{}, &models.BitbucketPipelineStep{}, &models.BitbucketPrCommit{}, + &models.BitbucketPrReviewer{}, &models.BitbucketScopeConfig{}, } } @@ -125,6 +126,7 @@ func (p Bitbucket) SubTaskMetas() []plugin.SubTaskMeta { tasks.ConvertRepoMeta, tasks.ConvertAccountsMeta, tasks.ConvertPullRequestsMeta, + tasks.ConvertPrReviewersMeta, tasks.ConvertPrCommentsMeta, tasks.ConvertPrCommitsMeta, tasks.ConvertCommitsMeta, diff --git a/backend/plugins/bitbucket/models/migrationscripts/20251226_add_pr_reviewer_table.go b/backend/plugins/bitbucket/models/migrationscripts/20251226_add_pr_reviewer_table.go new file mode 100644 index 00000000000..54a04cc243c --- /dev/null +++ b/backend/plugins/bitbucket/models/migrationscripts/20251226_add_pr_reviewer_table.go @@ -0,0 +1,64 @@ +/* +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 ( + "time" + + "github.com/apache/incubator-devlake/core/context" + "github.com/apache/incubator-devlake/core/errors" + "github.com/apache/incubator-devlake/core/plugin" +) + +var _ plugin.MigrationScript = (*addPrReviewerTable)(nil) + +type prReviewer20251226 struct { + ConnectionId uint64 `gorm:"primaryKey"` + RepoId string `gorm:"primaryKey;type:varchar(255)"` + PullRequestId int `gorm:"primaryKey"` + AccountId string `gorm:"primaryKey;type:varchar(255)"` + DisplayName string `gorm:"type:varchar(255)"` + Role string `gorm:"type:varchar(100)"` + State string `gorm:"type:varchar(100)"` + Approved bool + ParticipatedOn *time.Time + CreatedAt time.Time + UpdatedAt time.Time +} + +func (prReviewer20251226) TableName() string { + return "_tool_bitbucket_pr_reviewers" +} + +type addPrReviewerTable struct{} + +func (*addPrReviewerTable) Up(basicRes context.BasicRes) errors.Error { + db := basicRes.GetDal() + if err := db.AutoMigrate(&prReviewer20251226{}); err != nil { + return err + } + return nil +} + +func (*addPrReviewerTable) Version() uint64 { + return 20251226100000 +} + +func (*addPrReviewerTable) Name() string { + return "add _tool_bitbucket_pr_reviewers table" +} diff --git a/backend/plugins/bitbucket/models/migrationscripts/register.go b/backend/plugins/bitbucket/models/migrationscripts/register.go index c47f7db4f7d..e8a31d2c1c9 100644 --- a/backend/plugins/bitbucket/models/migrationscripts/register.go +++ b/backend/plugins/bitbucket/models/migrationscripts/register.go @@ -43,5 +43,6 @@ func All() []plugin.MigrationScript { new(addMergedByToPr), new(changeIssueComponentType), new(addApiTokenAuth), + new(addPrReviewerTable), } } diff --git a/backend/plugins/bitbucket/models/pr_reviewer.go b/backend/plugins/bitbucket/models/pr_reviewer.go new file mode 100644 index 00000000000..43df5273981 --- /dev/null +++ b/backend/plugins/bitbucket/models/pr_reviewer.go @@ -0,0 +1,41 @@ +/* +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" +) + +type BitbucketPrReviewer struct { + ConnectionId uint64 `gorm:"primaryKey"` + RepoId string `gorm:"primaryKey;type:varchar(255)"` + PullRequestId int `gorm:"primaryKey"` + AccountId string `gorm:"primaryKey;type:varchar(255)"` + DisplayName string `gorm:"type:varchar(255)"` + Role string `gorm:"type:varchar(100)"` // PARTICIPANT, REVIEWER + State string `gorm:"type:varchar(100)"` // approved, changes_requested, null + Approved bool + ParticipatedOn *time.Time + common.NoPKModel +} + +func (BitbucketPrReviewer) TableName() string { + return "_tool_bitbucket_pr_reviewers" +} diff --git a/backend/plugins/bitbucket/tasks/pr_collector.go b/backend/plugins/bitbucket/tasks/pr_collector.go index efa6890e1d9..4db0dd08fae 100644 --- a/backend/plugins/bitbucket/tasks/pr_collector.go +++ b/backend/plugins/bitbucket/tasks/pr_collector.go @@ -53,6 +53,8 @@ func CollectApiPullRequests(taskCtx plugin.SubTaskContext) errors.Error { `values.merge_commit.hash,values.merge_commit.date,values.links.html,values.author,values.created_on,values.updated_on,`+ `values.destination.branch.name,values.destination.commit.hash,values.destination.repository.full_name,`+ `values.source.branch.name,values.source.commit.hash,values.source.repository.full_name,`+ + `values.participants.user.account_id,values.participants.user.display_name,`+ + `values.participants.role,values.participants.state,values.participants.approved,values.participants.participated_on,`+ `page,pagelen,size`, collectorWithState), GetTotalPages: GetTotalPagesFromResponse, diff --git a/backend/plugins/bitbucket/tasks/pr_extractor.go b/backend/plugins/bitbucket/tasks/pr_extractor.go index 9cca3a0ce6b..5edb75e284b 100644 --- a/backend/plugins/bitbucket/tasks/pr_extractor.go +++ b/backend/plugins/bitbucket/tasks/pr_extractor.go @@ -77,8 +77,15 @@ type BitbucketApiPullRequest struct { } `json:"commit"` Repo *models.BitbucketApiRepo `json:"repository"` } `json:"source"` - //Reviewers []BitbucketAccountResponse `json:"reviewers"` - //Participants []BitbucketAccountResponse `json:"participants"` + Participants []BitbucketParticipant `json:"participants"` +} + +type BitbucketParticipant struct { + User *BitbucketAccountResponse `json:"user"` + Role string `json:"role"` + State *string `json:"state"` + Approved bool `json:"approved"` + ParticipatedOn *common.Iso8601Time `json:"participated_on"` } func ExtractApiPullRequests(taskCtx plugin.SubTaskContext) errors.Error { @@ -117,6 +124,36 @@ func ExtractApiPullRequests(taskCtx plugin.SubTaskContext) errors.Error { } results = append(results, bitbucketPr) + // Extract participants/reviewers + for _, participant := range rawL.Participants { + if participant.User == nil { + continue + } + reviewer := &models.BitbucketPrReviewer{ + ConnectionId: data.Options.ConnectionId, + RepoId: data.Options.FullName, + PullRequestId: rawL.BitbucketId, + AccountId: participant.User.AccountId, + DisplayName: participant.User.DisplayName, + Role: participant.Role, + Approved: participant.Approved, + } + if participant.State != nil { + reviewer.State = *participant.State + } + if participant.ParticipatedOn != nil { + reviewer.ParticipatedOn = participant.ParticipatedOn.ToNullableTime() + } + results = append(results, reviewer) + + // Also save the user account + bitbucketUser, err := convertAccount(participant.User, data.Options.ConnectionId) + if err != nil { + return nil, err + } + results = append(results, bitbucketUser) + } + return results, nil }, }) diff --git a/backend/plugins/bitbucket/tasks/pr_reviewer_convertor.go b/backend/plugins/bitbucket/tasks/pr_reviewer_convertor.go new file mode 100644 index 00000000000..c6de00e92a8 --- /dev/null +++ b/backend/plugins/bitbucket/tasks/pr_reviewer_convertor.go @@ -0,0 +1,80 @@ +/* +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 ( + "reflect" + + "github.com/apache/incubator-devlake/core/dal" + "github.com/apache/incubator-devlake/core/errors" + "github.com/apache/incubator-devlake/core/models/domainlayer/code" + "github.com/apache/incubator-devlake/core/models/domainlayer/didgen" + plugin "github.com/apache/incubator-devlake/core/plugin" + "github.com/apache/incubator-devlake/helpers/pluginhelper/api" + "github.com/apache/incubator-devlake/plugins/bitbucket/models" +) + +var ConvertPrReviewersMeta = plugin.SubTaskMeta{ + Name: "Convert PR Reviewers", + EntryPoint: ConvertPrReviewers, + EnabledByDefault: true, + Required: false, + Description: "Convert tool layer PR reviewers to domain layer", + DomainTypes: []string{plugin.DOMAIN_TYPE_CODE_REVIEW}, +} + +func ConvertPrReviewers(taskCtx plugin.SubTaskContext) errors.Error { + rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_PULL_REQUEST_TABLE) + db := taskCtx.GetDal() + repoId := data.Options.FullName + + cursor, err := db.Cursor( + dal.From(&models.BitbucketPrReviewer{}), + dal.Where("repo_id = ? and connection_id = ?", repoId, data.Options.ConnectionId), + ) + if err != nil { + return err + } + defer cursor.Close() + + prIdGen := didgen.NewDomainIdGenerator(&models.BitbucketPullRequest{}) + accountIdGen := didgen.NewDomainIdGenerator(&models.BitbucketAccount{}) + + converter, err := api.NewDataConverter(api.DataConverterArgs{ + InputRowType: reflect.TypeOf(models.BitbucketPrReviewer{}), + Input: cursor, + RawDataSubTaskArgs: *rawDataSubTaskArgs, + Convert: func(inputRow interface{}) ([]interface{}, errors.Error) { + reviewer := inputRow.(*models.BitbucketPrReviewer) + domainReviewer := &code.PullRequestReviewer{ + PullRequestId: prIdGen.Generate(data.Options.ConnectionId, reviewer.RepoId, reviewer.PullRequestId), + ReviewerId: accountIdGen.Generate(data.Options.ConnectionId, reviewer.AccountId), + Name: reviewer.DisplayName, + UserName: reviewer.DisplayName, + } + return []interface{}{ + domainReviewer, + }, nil + }, + }) + if err != nil { + return err + } + + return converter.Execute() +} diff --git a/grafana/dashboards/qdev_user_data.json b/grafana/dashboards/qdev_user_data.json index 8ce8d11b37a..ff55b9ff81d 100644 --- a/grafana/dashboards/qdev_user_data.json +++ b/grafana/dashboards/qdev_user_data.json @@ -1,795 +1,795 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 100, - "links": [], - "panels": [ - { - "datasource": "mysql", - "description": "Overview of key user metrics", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 10, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "sum" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "text": {}, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.6.2", - "targets": [ - { - "datasource": "mysql", - "editorMode": "code", - "format": "table", - "group": [], - "metricColumn": "none", - "rawQuery": true, - "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_acceptance_event_count) / NULLIF(SUM(code_fix_generation_event_count), 0) as 'Acceptance Rate (Code Fix)',\n SUM(transformation_lines_ingested) as 'Ingested Lines (Java Transform)',\n SUM(transformation_lines_generated) as 'Generated Lines (Java Transform)',\n SUM(inline_chat_accepted_line_additions) as 'Accepted Lines (Inline Chat)',\n SUM(inline_chat_acceptance_event_count) / NULLIF(SUM(inline_chat_total_event_count), 0) as 'Acceptance Rate (Inline Chat)'\nFROM lake._tool_q_dev_user_data\nWHERE $__timeFilter(date)", - "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": "Overall Usage Statistics", - "type": "stat" - }, - { - "datasource": "mysql", - "description": "Daily AI code line changes across all users", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "barWidthFactor": 0.6, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "smooth", - "lineWidth": 2, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 8 - }, - "id": 3, - "options": { - "legend": { - "calcs": [ - "mean", - "max", - "sum" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true - }, - "tooltip": { - "hideZeros": false, - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "11.6.2", - "targets": [ - { - "datasource": "mysql", - "editorMode": "code", - "format": "time_series", - "group": [], - "metricColumn": "none", - "rawQuery": true, - "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 '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_line_additions) as 'Inline Chat Rejected Line Additions',\n SUM(inline_chat_rejected_line_deletions) as 'Inline Chat Rejected Line Deletions'\nFROM lake._tool_q_dev_user_data\nWHERE $__timeFilter(date)\nGROUP BY date\nORDER BY date", - "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": "Daily AI Code Line Changes", - "type": "timeseries" - }, - { - "datasource": "mysql", - "description": "Daily AI interaction trends across all users", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "barWidthFactor": 0.6, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "smooth", - "lineWidth": 2, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 16 - }, - "id": 9, - "options": { - "legend": { - "calcs": [ - "mean", - "max", - "sum" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true - }, - "tooltip": { - "hideZeros": false, - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "11.6.2", - "targets": [ - { - "datasource": "mysql", - "editorMode": "code", - "format": "time_series", - "group": [], - "metricColumn": "none", - "rawQuery": true, - "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_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 Suggestions'\nFROM lake._tool_q_dev_user_data\nWHERE $__timeFilter(date)\nGROUP BY date\nORDER BY date", - "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": "Daily AI Interactions", - "type": "timeseries" - }, - { - "datasource": "mysql", - "description": "Code review metrics over time", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "barWidthFactor": 0.6, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "smooth", - "lineWidth": 2, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 24 - }, - "id": 4, - "options": { - "legend": { - "calcs": [ - "mean", - "max", - "sum" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "hideZeros": false, - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "11.6.2", - "targets": [ - { - "datasource": "mysql", - "format": "time_series", - "group": [], - "metricColumn": "none", - "rawQuery": true, - "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": [ - [ - { - "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": "Code Review Metrics", - "type": "timeseries" - }, - { - "datasource": "mysql", - "description": "Daily acceptance rate of AI suggestions", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "barWidthFactor": 0.6, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "smooth", - "lineWidth": 2, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "percentunit" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 24 - }, - "id": 5, - "options": { - "legend": { - "calcs": [ - "mean", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "hideZeros": false, - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "11.6.2", - "targets": [ - { - "datasource": "mysql", - "editorMode": "code", - "format": "time_series", - "group": [], - "metricColumn": "none", - "rawQuery": true, - "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": [ - [ - { - "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": "Daily AI Suggestion Acceptance Rate", - "type": "timeseries" - }, - { - "datasource": "mysql", - "description": "User AI interaction metrics", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "filterable": true, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Acceptance Rate" - }, - "properties": [ - { - "id": "unit", - "value": "percentunit" - }, - { - "id": "custom.cellOptions", - "value": { - "mode": "gradient", - "type": "gauge" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Inline Chat Accepted Events" - }, - "properties": [ - { - "id": "custom.width", - "value": 239 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Acceptance Rate (Inline Suggestion)" - }, - "properties": [ - { - "id": "custom.width", - "value": 172 - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 32 - }, - "id": 6, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true, - "sortBy": [] - }, - "pluginVersion": "11.6.2", - "targets": [ - { - "datasource": "mysql", - "editorMode": "code", - "format": "table", - "group": [], - "metricColumn": "none", - "rawQuery": true, - "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_fix_generated_lines) as 'Generated Lines (Code Fix)',\n SUM(code_fix_acceptance_event_count) as 'Accepted Count (Code Fix)',\n SUM(code_fix_generation_event_count) as 'Generated Count (Code Fix)',\n CONCAT(ROUND(SUM(code_fix_acceptance_event_count) / NULLIF(SUM(code_fix_generation_event_count), 0) * 100, 2), '%') as 'Acceptance Rate (Code Fix)',\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(inline_chat_accepted_line_additions) as 'Accepted Line Additions (Inline Chat)',\n SUM(inline_chat_accepted_line_deletions) as 'Accepted Line Deletions (Inline Chat)',\n SUM(inline_chat_acceptance_event_count) as 'Accepted Events (Inline Chat)',\n SUM(inline_chat_total_event_count) as 'Total Events (Inline Chat)',\n CONCAT(ROUND(SUM(inline_chat_acceptance_event_count) / NULLIF(SUM(inline_chat_total_event_count), 0) * 100, 2), '%') as 'Acceptance Rate (Inline Chat)',\n MIN(date) as 'First Activity',\n MAX(date) as 'Last Activity'\nFROM lake._tool_q_dev_user_data\nWHERE $__timeFilter(date)\nGROUP BY user_id, display_name\nORDER BY SUM(inline_ai_code_lines) DESC", - "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": "User Interactions", - "type": "table" - } - ], - "preload": false, - "refresh": "5m", - "schemaVersion": 41, - "tags": [ - "q_dev", - "user_data" - ], - "templating": { - "list": [] - }, - "time": { - "from": "now-30d", - "to": "now" - }, - "timepicker": {}, - "timezone": "utc", - "title": "Q Dev User Data Dashboard", - "uid": "qdev_user_data", - "version": 1 +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 100, + "links": [], + "panels": [ + { + "datasource": "mysql", + "description": "Overview of key user metrics", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 10, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "sum" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "text": {}, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.6.2", + "targets": [ + { + "datasource": "mysql", + "editorMode": "code", + "format": "table", + "group": [], + "metricColumn": "none", + "rawQuery": true, + "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_acceptance_event_count) / NULLIF(SUM(code_fix_generation_event_count), 0) as 'Acceptance Rate (Code Fix)',\n SUM(transformation_lines_ingested) as 'Ingested Lines (Java Transform)',\n SUM(transformation_lines_generated) as 'Generated Lines (Java Transform)',\n SUM(inline_chat_accepted_line_additions) as 'Accepted Lines (Inline Chat)',\n SUM(inline_chat_acceptance_event_count) / NULLIF(SUM(inline_chat_total_event_count), 0) as 'Acceptance Rate (Inline Chat)'\nFROM lake._tool_q_dev_user_data\nWHERE $__timeFilter(date)", + "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": "Overall Usage Statistics", + "type": "stat" + }, + { + "datasource": "mysql", + "description": "Daily AI code line changes across all users", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "smooth", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 3, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "sum" + ], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.6.2", + "targets": [ + { + "datasource": "mysql", + "editorMode": "code", + "format": "time_series", + "group": [], + "metricColumn": "none", + "rawQuery": true, + "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 '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_line_additions) as 'Inline Chat Rejected Line Additions',\n SUM(inline_chat_rejected_line_deletions) as 'Inline Chat Rejected Line Deletions'\nFROM lake._tool_q_dev_user_data\nWHERE $__timeFilter(date)\nGROUP BY date\nORDER BY date", + "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": "Daily AI Code Line Changes", + "type": "timeseries" + }, + { + "datasource": "mysql", + "description": "Daily AI interaction trends across all users", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "smooth", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 16 + }, + "id": 9, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "sum" + ], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.6.2", + "targets": [ + { + "datasource": "mysql", + "editorMode": "code", + "format": "time_series", + "group": [], + "metricColumn": "none", + "rawQuery": true, + "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_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 Suggestions'\nFROM lake._tool_q_dev_user_data\nWHERE $__timeFilter(date)\nGROUP BY date\nORDER BY date", + "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": "Daily AI Interactions", + "type": "timeseries" + }, + { + "datasource": "mysql", + "description": "Code review metrics over time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "smooth", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 4, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "sum" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.6.2", + "targets": [ + { + "datasource": "mysql", + "format": "time_series", + "group": [], + "metricColumn": "none", + "rawQuery": true, + "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": [ + [ + { + "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": "Code Review Metrics", + "type": "timeseries" + }, + { + "datasource": "mysql", + "description": "Daily acceptance rate of AI suggestions", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "smooth", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 24 + }, + "id": 5, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "min" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.6.2", + "targets": [ + { + "datasource": "mysql", + "editorMode": "code", + "format": "time_series", + "group": [], + "metricColumn": "none", + "rawQuery": true, + "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": [ + [ + { + "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": "Daily AI Suggestion Acceptance Rate", + "type": "timeseries" + }, + { + "datasource": "mysql", + "description": "User AI interaction metrics", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Acceptance Rate" + }, + "properties": [ + { + "id": "unit", + "value": "percentunit" + }, + { + "id": "custom.cellOptions", + "value": { + "mode": "gradient", + "type": "gauge" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Inline Chat Accepted Events" + }, + "properties": [ + { + "id": "custom.width", + "value": 239 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Acceptance Rate (Inline Suggestion)" + }, + "properties": [ + { + "id": "custom.width", + "value": 172 + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 32 + }, + "id": 6, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "11.6.2", + "targets": [ + { + "datasource": "mysql", + "editorMode": "code", + "format": "table", + "group": [], + "metricColumn": "none", + "rawQuery": true, + "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_fix_generated_lines) as 'Generated Lines (Code Fix)',\n SUM(code_fix_acceptance_event_count) as 'Accepted Count (Code Fix)',\n SUM(code_fix_generation_event_count) as 'Generated Count (Code Fix)',\n CONCAT(ROUND(SUM(code_fix_acceptance_event_count) / NULLIF(SUM(code_fix_generation_event_count), 0) * 100, 2), '%') as 'Acceptance Rate (Code Fix)',\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(inline_chat_accepted_line_additions) as 'Accepted Line Additions (Inline Chat)',\n SUM(inline_chat_accepted_line_deletions) as 'Accepted Line Deletions (Inline Chat)',\n SUM(inline_chat_acceptance_event_count) as 'Accepted Events (Inline Chat)',\n SUM(inline_chat_total_event_count) as 'Total Events (Inline Chat)',\n CONCAT(ROUND(SUM(inline_chat_acceptance_event_count) / NULLIF(SUM(inline_chat_total_event_count), 0) * 100, 2), '%') as 'Acceptance Rate (Inline Chat)',\n MIN(date) as 'First Activity',\n MAX(date) as 'Last Activity'\nFROM lake._tool_q_dev_user_data\nWHERE $__timeFilter(date)\nGROUP BY user_id, display_name\nORDER BY SUM(inline_ai_code_lines) DESC", + "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": "User Interactions", + "type": "table" + } + ], + "preload": false, + "refresh": "5m", + "schemaVersion": 41, + "tags": [ + "q_dev", + "user_data" + ], + "templating": { + "list": [] + }, + "time": { + "from": "now-30d", + "to": "now" + }, + "timepicker": {}, + "timezone": "utc", + "title": "Q Dev User Data Dashboard", + "uid": "qdev_user_data", + "version": 1 } \ No newline at end of file From becfb9046a27c9ff64f40b4982d65cf183289b2e Mon Sep 17 00:00:00 2001 From: warren Date: Sat, 27 Dec 2025 00:46:20 +0800 Subject: [PATCH 2/2] feat: Collect Pull Request Reviewer Status --- .../20251226_add_pr_reviewer_table.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/plugins/bitbucket/models/migrationscripts/20251226_add_pr_reviewer_table.go b/backend/plugins/bitbucket/models/migrationscripts/20251226_add_pr_reviewer_table.go index 54a04cc243c..c5da61075be 100644 --- a/backend/plugins/bitbucket/models/migrationscripts/20251226_add_pr_reviewer_table.go +++ b/backend/plugins/bitbucket/models/migrationscripts/20251226_add_pr_reviewer_table.go @@ -22,23 +22,23 @@ import ( "github.com/apache/incubator-devlake/core/context" "github.com/apache/incubator-devlake/core/errors" + "github.com/apache/incubator-devlake/core/models/migrationscripts/archived" "github.com/apache/incubator-devlake/core/plugin" ) var _ plugin.MigrationScript = (*addPrReviewerTable)(nil) type prReviewer20251226 struct { - ConnectionId uint64 `gorm:"primaryKey"` - RepoId string `gorm:"primaryKey;type:varchar(255)"` - PullRequestId int `gorm:"primaryKey"` - AccountId string `gorm:"primaryKey;type:varchar(255)"` - DisplayName string `gorm:"type:varchar(255)"` - Role string `gorm:"type:varchar(100)"` - State string `gorm:"type:varchar(100)"` + ConnectionId uint64 `gorm:"primaryKey"` + RepoId string `gorm:"primaryKey;type:varchar(255)"` + PullRequestId int `gorm:"primaryKey"` + AccountId string `gorm:"primaryKey;type:varchar(255)"` + DisplayName string `gorm:"type:varchar(255)"` + Role string `gorm:"type:varchar(100)"` + State string `gorm:"type:varchar(100)"` Approved bool ParticipatedOn *time.Time - CreatedAt time.Time - UpdatedAt time.Time + archived.NoPKModel } func (prReviewer20251226) TableName() string {