diff --git a/app/assets/javascripts/analytics/shared/constants.js b/app/assets/javascripts/analytics/shared/constants.js index d699f54db1486d4ace469821d5f887d844a53062..58cc80ea2e9bd9681e3b3ba26cac9c68343348bb 100644 --- a/app/assets/javascripts/analytics/shared/constants.js +++ b/app/assets/javascripts/analytics/shared/constants.js @@ -201,6 +201,8 @@ export const AI_METRICS = { DUO_USAGE_RATE: 'duo_usage_rate', DUO_RCA_USAGE_RATE: 'duo_rca_usage_rate', DUO_USED_COUNT: 'duo_used_count', + DUO_REVIEW_REQUESTS_COUNT: 'duo_review_requests_count', + DUO_REVIEW_COMMENT_COUNT: 'duo_review_comment_count', }; export const VALUE_STREAM_METRIC_DISPLAY_UNITS = { @@ -416,6 +418,18 @@ export const VALUE_STREAM_METRIC_METADATA = { projectLink: '', docsLink: helpPagePath('user/gitlab_duo/feature_summary'), }, + [AI_METRICS.DUO_REVIEW_REQUESTS_COUNT]: { + description: s__('AiImpactAnalytics|Number of Duo Code Review requests.'), + groupLink: '', + projectLink: '', + docsLink: helpPagePath('user/gitlab_duo/feature_summary'), + }, + [AI_METRICS.DUO_REVIEW_COMMENT_COUNT]: { + description: s__('AiImpactAnalytics|Number of Duo Code Review comments.'), + groupLink: '', + projectLink: '', + docsLink: helpPagePath('user/gitlab_duo/feature_summary'), + }, ...PIPELINE_ANALYTICS_METRIC_METADATA, }; diff --git a/doc/user/analytics/duo_and_sdlc_trends.md b/doc/user/analytics/duo_and_sdlc_trends.md index d72927e84a4289b899c3101b23e8e9242c85c9bb..beef9929e1d2e69154007ccc2b6b01bd6ba0002e 100644 --- a/doc/user/analytics/duo_and_sdlc_trends.md +++ b/doc/user/analytics/duo_and_sdlc_trends.md @@ -74,6 +74,7 @@ The **Metric trends** table displays metrics for the last six months, with month - Duo RCA usage [enabled on GitLab.com, GitLab Self-Managed, and GitLab Dedicated](https://gitlab.com/gitlab-org/gitlab/-/issues/543987) in GitLab 18.3. - Duo RCA usage [generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/556726) in GitLab 18.4. Feature flag `duo_rca_usage_rate` removed. - Duo features usage [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/207562) in GitLab 18.6. +- Duo Code Review requests and Duo Code Review comments [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/573979) in GitLab 18.7. {{< /history >}} @@ -105,6 +106,11 @@ The **Metric trends** table displays metrics for the last six months, with month - **Duo features usage**: Number of contributors who used any GitLab Duo feature. +- **Duo Code Review requests**: Number of Duo Code Review requests made on merge requests. + This includes requests initiated by both merge request authors and non-authors. + +- **Duo Code Review comments**: Number of comments posted by Duo Code Review on merge request diffs. + ### Development metrics - [**Lead time**](../group/value_stream_analytics/_index.md#lifecycle-metrics) diff --git a/ee/app/assets/javascripts/analytics/dashboards/ai_impact/api.js b/ee/app/assets/javascripts/analytics/dashboards/ai_impact/api.js index 9ad53ac340b21dca09d28f4e5041d9119056ffc7..e1d25c679bfed3ce298837eab77ea6f193c2f1df 100644 --- a/ee/app/assets/javascripts/analytics/dashboards/ai_impact/api.js +++ b/ee/app/assets/javascripts/analytics/dashboards/ai_impact/api.js @@ -45,6 +45,11 @@ export const extractGraphqlAiData = ({ rootCauseAnalysisUsersCount = null, duoAssignedUsersCount = null, duoUsedCount = null, + codeReview: { + requestReviewDuoCodeReviewOnMrByAuthorEventCount = null, + requestReviewDuoCodeReviewOnMrByNonAuthorEventCount = null, + postCommentDuoCodeReviewOnDiffEventCount = null, + } = {}, } = {}) => { const codeSuggestionsUsageRate = calculateRate({ numerator: codeSuggestionsContributorsCount, @@ -66,6 +71,13 @@ export const extractGraphqlAiData = ({ denominator: duoAssignedUsersCount, }); + const duoReviewCount = + requestReviewDuoCodeReviewOnMrByAuthorEventCount === null && + requestReviewDuoCodeReviewOnMrByNonAuthorEventCount === null + ? '-' + : (requestReviewDuoCodeReviewOnMrByAuthorEventCount ?? 0) + + (requestReviewDuoCodeReviewOnMrByNonAuthorEventCount ?? 0); + return { [AI_METRICS.CODE_SUGGESTIONS_USAGE_RATE]: { identifier: AI_METRICS.CODE_SUGGESTIONS_USAGE_RATE, @@ -103,5 +115,13 @@ export const extractGraphqlAiData = ({ identifier: AI_METRICS.DUO_USED_COUNT, value: duoUsedCount ?? '-', }, + [AI_METRICS.DUO_REVIEW_REQUESTS_COUNT]: { + identifier: AI_METRICS.DUO_REVIEW_REQUESTS_COUNT, + value: duoReviewCount ?? '-', + }, + [AI_METRICS.DUO_REVIEW_COMMENT_COUNT]: { + identifier: AI_METRICS.DUO_REVIEW_COMMENT_COUNT, + value: postCommentDuoCodeReviewOnDiffEventCount ?? '-', + }, }; }; diff --git a/ee/app/assets/javascripts/analytics/dashboards/ai_impact/constants.js b/ee/app/assets/javascripts/analytics/dashboards/ai_impact/constants.js index 2896b89bde85c9a3ee926f2db35ddb06996ca19d..8c5dd92fa7c9f735b12fe6da6c522cc42ae8c94b 100644 --- a/ee/app/assets/javascripts/analytics/dashboards/ai_impact/constants.js +++ b/ee/app/assets/javascripts/analytics/dashboards/ai_impact/constants.js @@ -11,6 +11,8 @@ export const SUPPORTED_AI_METRICS = [ AI_METRICS.CODE_SUGGESTIONS_ACCEPTANCE_RATE, AI_METRICS.DUO_CHAT_USAGE_RATE, AI_METRICS.DUO_RCA_USAGE_RATE, + AI_METRICS.DUO_REVIEW_REQUESTS_COUNT, + AI_METRICS.DUO_REVIEW_COMMENT_COUNT, ]; export const HIDE_METRIC_DRILL_DOWN = [ AI_METRICS.CODE_SUGGESTIONS_USAGE_RATE, @@ -18,6 +20,8 @@ export const HIDE_METRIC_DRILL_DOWN = [ AI_METRICS.DUO_CHAT_USAGE_RATE, AI_METRICS.DUO_RCA_USAGE_RATE, AI_METRICS.DUO_USED_COUNT, + AI_METRICS.DUO_REVIEW_REQUESTS_COUNT, + AI_METRICS.DUO_REVIEW_COMMENT_COUNT, ]; // The AI impact metrics supported for over time tiles @@ -46,6 +50,14 @@ export const AI_IMPACT_USAGE_METRICS = { label: s__('AiImpactAnalytics|Duo RCA usage'), units: UNITS.PERCENT, }, + [AI_METRICS.DUO_REVIEW_REQUESTS_COUNT]: { + label: s__('AiImpactAnalytics|Duo Code Review requests'), + units: UNITS.COUNT, + }, + [AI_METRICS.DUO_REVIEW_COMMENT_COUNT]: { + label: s__('AiImpactAnalytics|Duo Code Review comments'), + units: UNITS.COUNT, + }, [AI_METRICS.DUO_USED_COUNT]: { label: s__('AiImpactAnalytics|Duo features usage'), units: UNITS.COUNT, diff --git a/ee/app/assets/javascripts/analytics/dashboards/ai_impact/graphql/ai_metric_item.fragment.graphql b/ee/app/assets/javascripts/analytics/dashboards/ai_impact/graphql/ai_metric_item.fragment.graphql index a95c993eb263fb78fbe60033742f3d9ec484e2b4..ebd483d0d595a522d2751dbee9228d2727775432 100644 --- a/ee/app/assets/javascripts/analytics/dashboards/ai_impact/graphql/ai_metric_item.fragment.graphql +++ b/ee/app/assets/javascripts/analytics/dashboards/ai_impact/graphql/ai_metric_item.fragment.graphql @@ -6,6 +6,11 @@ fragment AiMetricItem on AiMetrics { duoAssignedUsersCount duoUsedCount rootCauseAnalysisUsersCount + codeReview { + requestReviewDuoCodeReviewOnMrByAuthorEventCount + requestReviewDuoCodeReviewOnMrByNonAuthorEventCount + postCommentDuoCodeReviewOnDiffEventCount + } codeSuggestions(languages: $languages) { contributorsCount shownCount diff --git a/ee/lib/gitlab/analytics/ai_impact_dashboard/dashboard.yaml b/ee/lib/gitlab/analytics/ai_impact_dashboard/dashboard.yaml index 53a7f59a0574a246d3231aa427ab7fd0f0d5f401..70fbdfd45f41651dc2f5e3b82c585e133d347f01 100644 --- a/ee/lib/gitlab/analytics/ai_impact_dashboard/dashboard.yaml +++ b/ee/lib/gitlab/analytics/ai_impact_dashboard/dashboard.yaml @@ -56,12 +56,12 @@ panels: yPos: 5 xPos: 0 width: 12 - height: 3 + height: 4 options: {} - title: 'Development metrics for the %{namespaceName} %{namespaceType}' visualization: ai_impact_lifecycle_metrics_table gridAttributes: - yPos: 8 + yPos: 9 xPos: 0 width: 12 height: 3 @@ -69,7 +69,7 @@ panels: - title: 'Pipeline metrics for the %{namespaceName} %{namespaceType}' visualization: pipeline_metrics_table gridAttributes: - yPos: 11 + yPos: 12 xPos: 0 width: 12 height: 3 @@ -77,7 +77,7 @@ panels: - title: 'Code Suggestions accepted by user' visualization: user_metrics_table gridAttributes: - yPos: 13 + yPos: 14 xPos: 0 width: 12 height: 8 diff --git a/ee/lib/gitlab/analytics/ai_impact_dashboard/visualizations/ai_impact_ai_metrics_table.yaml b/ee/lib/gitlab/analytics/ai_impact_dashboard/visualizations/ai_impact_ai_metrics_table.yaml index 6ba36b62cccc6d2140f52eee883978b3c113885c..e26e49ec004564d6619ab74e048a88e1bfbefab0 100644 --- a/ee/lib/gitlab/analytics/ai_impact_dashboard/visualizations/ai_impact_ai_metrics_table.yaml +++ b/ee/lib/gitlab/analytics/ai_impact_dashboard/visualizations/ai_impact_ai_metrics_table.yaml @@ -11,4 +11,6 @@ data: - code_suggestions_acceptance_rate - duo_chat_usage_rate - duo_rca_usage_rate + - duo_review_requests_count + - duo_review_comment_count options: {} diff --git a/ee/spec/frontend/analytics/dashboards/ai_impact/__snapshots__/api_spec.js.snap b/ee/spec/frontend/analytics/dashboards/ai_impact/__snapshots__/api_spec.js.snap index c7cff7ec909d4c5cc62ba288f353c0c14518a95b..ea5af9f5b3f584996fc6a487a33f493c2ae796e7 100644 --- a/ee/spec/frontend/analytics/dashboards/ai_impact/__snapshots__/api_spec.js.snap +++ b/ee/spec/frontend/analytics/dashboards/ai_impact/__snapshots__/api_spec.js.snap @@ -1,14 +1,14 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`AI impact dashboard api extractGraphqlAiData returns code_suggestions_acceptance_rate given {"codeSuggestions": [Object]} 1`] = ` +exports[`AI impact dashboard api extractGraphqlAiData returns code_suggestions_acceptance_rate given { codeSuggestions: { acceptedCount: 0, shownCount: 20 } } 1`] = ` { "identifier": "code_suggestions_acceptance_rate", - "tooltip": "No data", - "value": "-", + "tooltip": "0/20", + "value": 0, } `; -exports[`AI impact dashboard api extractGraphqlAiData returns code_suggestions_acceptance_rate given {"codeSuggestions": [Object]} 2`] = ` +exports[`AI impact dashboard api extractGraphqlAiData returns code_suggestions_acceptance_rate given { codeSuggestions: { acceptedCount: 3 } } 1`] = ` { "identifier": "code_suggestions_acceptance_rate", "tooltip": "No data", @@ -16,7 +16,7 @@ exports[`AI impact dashboard api extractGraphqlAiData returns code_suggestions_a } `; -exports[`AI impact dashboard api extractGraphqlAiData returns code_suggestions_acceptance_rate given {"codeSuggestions": [Object]} 3`] = ` +exports[`AI impact dashboard api extractGraphqlAiData returns code_suggestions_acceptance_rate given { codeSuggestions: { acceptedCount: 3, shownCount: 4 } } 1`] = ` { "identifier": "code_suggestions_acceptance_rate", "tooltip": "3/4", @@ -24,23 +24,29 @@ exports[`AI impact dashboard api extractGraphqlAiData returns code_suggestions_a } `; -exports[`AI impact dashboard api extractGraphqlAiData returns code_suggestions_acceptance_rate given {"codeSuggestions": [Object]} 4`] = ` +exports[`AI impact dashboard api extractGraphqlAiData returns code_suggestions_acceptance_rate given { codeSuggestions: { shownCount: 4 } } 1`] = ` { "identifier": "code_suggestions_acceptance_rate", - "tooltip": "0/20", - "value": 0, + "tooltip": "No data", + "value": "-", } `; -exports[`AI impact dashboard api extractGraphqlAiData returns code_suggestions_usage_rate given {"codeContributorsCount": 5} 1`] = ` +exports[`AI impact dashboard api extractGraphqlAiData returns code_suggestions_usage_rate given { + codeSuggestions: { contributorsCount: 0 }, + codeContributorsCount: 10 +} 1`] = ` { "identifier": "code_suggestions_usage_rate", - "tooltip": "No data", - "value": "-", + "tooltip": "0/10", + "value": 0, } `; -exports[`AI impact dashboard api extractGraphqlAiData returns code_suggestions_usage_rate given {"codeContributorsCount": 10, "codeSuggestions": [Object]} 1`] = ` +exports[`AI impact dashboard api extractGraphqlAiData returns code_suggestions_usage_rate given { + codeSuggestions: { contributorsCount: 5 }, + codeContributorsCount: 10 +} 1`] = ` { "identifier": "code_suggestions_usage_rate", "tooltip": "5/10", @@ -48,15 +54,15 @@ exports[`AI impact dashboard api extractGraphqlAiData returns code_suggestions_u } `; -exports[`AI impact dashboard api extractGraphqlAiData returns code_suggestions_usage_rate given {"codeContributorsCount": 10, "codeSuggestions": [Object]} 2`] = ` +exports[`AI impact dashboard api extractGraphqlAiData returns code_suggestions_usage_rate given { codeContributorsCount: 5 } 1`] = ` { "identifier": "code_suggestions_usage_rate", - "tooltip": "0/10", - "value": 0, + "tooltip": "No data", + "value": "-", } `; -exports[`AI impact dashboard api extractGraphqlAiData returns code_suggestions_usage_rate given {"codeSuggestions": [Object]} 1`] = ` +exports[`AI impact dashboard api extractGraphqlAiData returns code_suggestions_usage_rate given { codeSuggestions: { contributorsCount: 2 } } 1`] = ` { "identifier": "code_suggestions_usage_rate", "tooltip": "No data", @@ -64,7 +70,7 @@ exports[`AI impact dashboard api extractGraphqlAiData returns code_suggestions_u } `; -exports[`AI impact dashboard api extractGraphqlAiData returns duo_chat_usage_rate given {"duoAssignedUsersCount": 3} 1`] = ` +exports[`AI impact dashboard api extractGraphqlAiData returns duo_chat_usage_rate given { duoAssignedUsersCount: 3 } 1`] = ` { "identifier": "duo_chat_usage_rate", "tooltip": "No data", @@ -72,31 +78,31 @@ exports[`AI impact dashboard api extractGraphqlAiData returns duo_chat_usage_rat } `; -exports[`AI impact dashboard api extractGraphqlAiData returns duo_chat_usage_rate given {"duoAssignedUsersCount": 8, "duoChatContributorsCount": 7} 1`] = ` +exports[`AI impact dashboard api extractGraphqlAiData returns duo_chat_usage_rate given { duoChatContributorsCount: 0, duoAssignedUsersCount: 50 } 1`] = ` { "identifier": "duo_chat_usage_rate", - "tooltip": "7/8", - "value": 87.5, + "tooltip": "0/50", + "value": 0, } `; -exports[`AI impact dashboard api extractGraphqlAiData returns duo_chat_usage_rate given {"duoAssignedUsersCount": 50, "duoChatContributorsCount": 0} 1`] = ` +exports[`AI impact dashboard api extractGraphqlAiData returns duo_chat_usage_rate given { duoChatContributorsCount: 4 } 1`] = ` { "identifier": "duo_chat_usage_rate", - "tooltip": "0/50", - "value": 0, + "tooltip": "No data", + "value": "-", } `; -exports[`AI impact dashboard api extractGraphqlAiData returns duo_chat_usage_rate given {"duoChatContributorsCount": 4} 1`] = ` +exports[`AI impact dashboard api extractGraphqlAiData returns duo_chat_usage_rate given { duoChatContributorsCount: 7, duoAssignedUsersCount: 8 } 1`] = ` { "identifier": "duo_chat_usage_rate", - "tooltip": "No data", - "value": "-", + "tooltip": "7/8", + "value": 87.5, } `; -exports[`AI impact dashboard api extractGraphqlAiData returns duo_rca_usage_rate given {"duoAssignedUsersCount": 3} 1`] = ` +exports[`AI impact dashboard api extractGraphqlAiData returns duo_rca_usage_rate given { duoAssignedUsersCount: 3 } 1`] = ` { "identifier": "duo_rca_usage_rate", "tooltip": "No data", @@ -104,7 +110,15 @@ exports[`AI impact dashboard api extractGraphqlAiData returns duo_rca_usage_rate } `; -exports[`AI impact dashboard api extractGraphqlAiData returns duo_rca_usage_rate given {"duoAssignedUsersCount": 8, "rootCauseAnalysisUsersCount": 5} 1`] = ` +exports[`AI impact dashboard api extractGraphqlAiData returns duo_rca_usage_rate given { rootCauseAnalysisUsersCount: 0, duoAssignedUsersCount: 50 } 1`] = ` +{ + "identifier": "duo_rca_usage_rate", + "tooltip": "0/50", + "value": 0, +} +`; + +exports[`AI impact dashboard api extractGraphqlAiData returns duo_rca_usage_rate given { rootCauseAnalysisUsersCount: 5, duoAssignedUsersCount: 8 } 1`] = ` { "identifier": "duo_rca_usage_rate", "tooltip": "5/8", @@ -112,18 +126,80 @@ exports[`AI impact dashboard api extractGraphqlAiData returns duo_rca_usage_rate } `; -exports[`AI impact dashboard api extractGraphqlAiData returns duo_rca_usage_rate given {"duoAssignedUsersCount": 50, "rootCauseAnalysisUsersCount": 0} 1`] = ` +exports[`AI impact dashboard api extractGraphqlAiData returns duo_rca_usage_rate given { rootCauseAnalysisUsersCount: 10 } 1`] = ` { "identifier": "duo_rca_usage_rate", - "tooltip": "0/50", + "tooltip": "No data", + "value": "-", +} +`; + +exports[`AI impact dashboard api extractGraphqlAiData returns duo_review_comment_count given { codeReview: { postCommentDuoCodeReviewOnDiffEventCount: 0 } } 1`] = ` +{ + "identifier": "duo_review_comment_count", "value": 0, } `; -exports[`AI impact dashboard api extractGraphqlAiData returns duo_rca_usage_rate given {"rootCauseAnalysisUsersCount": 10} 1`] = ` +exports[`AI impact dashboard api extractGraphqlAiData returns duo_review_comment_count given { codeReview: { postCommentDuoCodeReviewOnDiffEventCount: 25 } } 1`] = ` { - "identifier": "duo_rca_usage_rate", - "tooltip": "No data", + "identifier": "duo_review_comment_count", + "value": 25, +} +`; + +exports[`AI impact dashboard api extractGraphqlAiData returns duo_review_comment_count given {} 1`] = ` +{ + "identifier": "duo_review_comment_count", + "value": "-", +} +`; + +exports[`AI impact dashboard api extractGraphqlAiData returns duo_review_requests_count given { + codeReview: { + requestReviewDuoCodeReviewOnMrByAuthorEventCount: 0, + requestReviewDuoCodeReviewOnMrByNonAuthorEventCount: 0 + } +} 1`] = ` +{ + "identifier": "duo_review_requests_count", + "value": 0, +} +`; + +exports[`AI impact dashboard api extractGraphqlAiData returns duo_review_requests_count given { + codeReview: { + requestReviewDuoCodeReviewOnMrByAuthorEventCount: 5, + requestReviewDuoCodeReviewOnMrByNonAuthorEventCount: 10 + } +} 1`] = ` +{ + "identifier": "duo_review_requests_count", + "value": 15, +} +`; + +exports[`AI impact dashboard api extractGraphqlAiData returns duo_review_requests_count given { + codeReview: { requestReviewDuoCodeReviewOnMrByAuthorEventCount: 10 } +} 1`] = ` +{ + "identifier": "duo_review_requests_count", + "value": 10, +} +`; + +exports[`AI impact dashboard api extractGraphqlAiData returns duo_review_requests_count given { + codeReview: { requestReviewDuoCodeReviewOnMrByNonAuthorEventCount: 5 } +} 1`] = ` +{ + "identifier": "duo_review_requests_count", + "value": 5, +} +`; + +exports[`AI impact dashboard api extractGraphqlAiData returns duo_review_requests_count given {} 1`] = ` +{ + "identifier": "duo_review_requests_count", "value": "-", } `; diff --git a/ee/spec/frontend/analytics/dashboards/ai_impact/__snapshots__/utils_spec.js.snap b/ee/spec/frontend/analytics/dashboards/ai_impact/__snapshots__/utils_spec.js.snap index f2200f981d888dc5f0b929cd0add6dc8675c8885..7eb6aeb48a2645ff2a2417880f3748ebd6bf8a11 100644 --- a/ee/spec/frontend/analytics/dashboards/ai_impact/__snapshots__/utils_spec.js.snap +++ b/ee/spec/frontend/analytics/dashboards/ai_impact/__snapshots__/utils_spec.js.snap @@ -304,6 +304,20 @@ exports[`AI impact Dashboard utils generateSkeletonTableData returns the skeleto }, "trendStyle": "ASC", }, + { + "metric": { + "identifier": "duo_review_requests_count", + "value": "Duo Code Review requests", + }, + "trendStyle": "ASC", + }, + { + "metric": { + "identifier": "duo_review_comment_count", + "value": "Duo Code Review comments", + }, + "trendStyle": "ASC", + }, ] `; diff --git a/ee/spec/frontend/analytics/dashboards/ai_impact/api_spec.js b/ee/spec/frontend/analytics/dashboards/ai_impact/api_spec.js index cc5b2d7a0a408a099e4a170acdde65ce0bef42d0..634e7a6cdebd65887a399005c5afcef77f3f6e36 100644 --- a/ee/spec/frontend/analytics/dashboards/ai_impact/api_spec.js +++ b/ee/spec/frontend/analytics/dashboards/ai_impact/api_spec.js @@ -2,25 +2,69 @@ import { extractGraphqlAiData } from 'ee/analytics/dashboards/ai_impact/api'; describe('AI impact dashboard api', () => { describe('extractGraphqlAiData', () => { - it.each` - identifier | data - ${'code_suggestions_acceptance_rate'} | ${{ codeSuggestions: { shownCount: 4 } }} - ${'code_suggestions_acceptance_rate'} | ${{ codeSuggestions: { acceptedCount: 3 } }} - ${'code_suggestions_acceptance_rate'} | ${{ codeSuggestions: { acceptedCount: 3, shownCount: 4 } }} - ${'code_suggestions_acceptance_rate'} | ${{ codeSuggestions: { acceptedCount: 0, shownCount: 20 } }} - ${'code_suggestions_usage_rate'} | ${{ codeContributorsCount: 5 }} - ${'code_suggestions_usage_rate'} | ${{ codeSuggestions: { contributorsCount: 2 } }} - ${'code_suggestions_usage_rate'} | ${{ codeSuggestions: { contributorsCount: 5 }, codeContributorsCount: 10 }} - ${'code_suggestions_usage_rate'} | ${{ codeSuggestions: { contributorsCount: 0 }, codeContributorsCount: 10 }} - ${'duo_chat_usage_rate'} | ${{ duoAssignedUsersCount: 3 }} - ${'duo_chat_usage_rate'} | ${{ duoChatContributorsCount: 4 }} - ${'duo_chat_usage_rate'} | ${{ duoChatContributorsCount: 7, duoAssignedUsersCount: 8 }} - ${'duo_chat_usage_rate'} | ${{ duoChatContributorsCount: 0, duoAssignedUsersCount: 50 }} - ${'duo_rca_usage_rate'} | ${{ duoAssignedUsersCount: 3 }} - ${'duo_rca_usage_rate'} | ${{ rootCauseAnalysisUsersCount: 10 }} - ${'duo_rca_usage_rate'} | ${{ rootCauseAnalysisUsersCount: 5, duoAssignedUsersCount: 8 }} - ${'duo_rca_usage_rate'} | ${{ rootCauseAnalysisUsersCount: 0, duoAssignedUsersCount: 50 }} - `('returns $identifier given $data', ({ identifier, data }) => { + it.each([ + ['code_suggestions_acceptance_rate', { codeSuggestions: { shownCount: 4 } }], + ['code_suggestions_acceptance_rate', { codeSuggestions: { acceptedCount: 3 } }], + [ + 'code_suggestions_acceptance_rate', + { codeSuggestions: { acceptedCount: 3, shownCount: 4 } }, + ], + [ + 'code_suggestions_acceptance_rate', + { codeSuggestions: { acceptedCount: 0, shownCount: 20 } }, + ], + ['code_suggestions_usage_rate', { codeContributorsCount: 5 }], + ['code_suggestions_usage_rate', { codeSuggestions: { contributorsCount: 2 } }], + [ + 'code_suggestions_usage_rate', + { codeSuggestions: { contributorsCount: 5 }, codeContributorsCount: 10 }, + ], + [ + 'code_suggestions_usage_rate', + { codeSuggestions: { contributorsCount: 0 }, codeContributorsCount: 10 }, + ], + ['duo_chat_usage_rate', { duoAssignedUsersCount: 3 }], + ['duo_chat_usage_rate', { duoChatContributorsCount: 4 }], + ['duo_chat_usage_rate', { duoChatContributorsCount: 7, duoAssignedUsersCount: 8 }], + ['duo_chat_usage_rate', { duoChatContributorsCount: 0, duoAssignedUsersCount: 50 }], + ['duo_rca_usage_rate', { duoAssignedUsersCount: 3 }], + ['duo_rca_usage_rate', { rootCauseAnalysisUsersCount: 10 }], + ['duo_rca_usage_rate', { rootCauseAnalysisUsersCount: 5, duoAssignedUsersCount: 8 }], + ['duo_rca_usage_rate', { rootCauseAnalysisUsersCount: 0, duoAssignedUsersCount: 50 }], + ['duo_review_requests_count', {}], + [ + 'duo_review_requests_count', + { codeReview: { requestReviewDuoCodeReviewOnMrByAuthorEventCount: 10 } }, + ], + [ + 'duo_review_requests_count', + { codeReview: { requestReviewDuoCodeReviewOnMrByNonAuthorEventCount: 5 } }, + ], + [ + 'duo_review_requests_count', + { + codeReview: { + requestReviewDuoCodeReviewOnMrByAuthorEventCount: 0, + requestReviewDuoCodeReviewOnMrByNonAuthorEventCount: 0, + }, + }, + ], + [ + 'duo_review_requests_count', + { + codeReview: { + requestReviewDuoCodeReviewOnMrByAuthorEventCount: 5, + requestReviewDuoCodeReviewOnMrByNonAuthorEventCount: 10, + }, + }, + ], + ['duo_review_comment_count', {}], + [ + 'duo_review_comment_count', + { codeReview: { postCommentDuoCodeReviewOnDiffEventCount: 25 } }, + ], + ['duo_review_comment_count', { codeReview: { postCommentDuoCodeReviewOnDiffEventCount: 0 } }], + ])('returns %s given %o', (identifier, data) => { expect(extractGraphqlAiData(data)[identifier]).toMatchSnapshot(); }); }); diff --git a/ee/spec/frontend/analytics/dashboards/ai_impact/components/__snapshots__/metric_table_spec.js.snap b/ee/spec/frontend/analytics/dashboards/ai_impact/components/__snapshots__/metric_table_spec.js.snap index 357b4f6b2ce72539007cec41b21f2cfcf473cc69..27b8ee9be5acd0c34d13c21f2dfe0da546e3c460 100644 --- a/ee/spec/frontend/analytics/dashboards/ai_impact/components/__snapshots__/metric_table_spec.js.snap +++ b/ee/spec/frontend/analytics/dashboards/ai_impact/components/__snapshots__/metric_table_spec.js.snap @@ -382,6 +382,86 @@ exports[`Metric table for the duo_rca_usage_rate table row when the data is load } `; +exports[`Metric table for the duo_review_comment_count table row when the data is loaded renders the metric values 1`] = ` +[ + "45", + "22", + "67", + "34", + "28", + "53", +] +`; + +exports[`Metric table for the duo_review_comment_count table row when the data is loaded renders the sparkline chart with expected props 1`] = ` +{ + "data": [ + [ + "Jul 1 – 31", + 45, + ], + [ + "Aug 1 – 31", + 22, + ], + [ + "Sep 1 – 30", + 67, + ], + [ + "Oct 1 – 31", + 34, + ], + [ + "Nov 1 – 30", + 28, + ], + ], + "tooltipLabel": "", + "trendStyle": "ASC", +} +`; + +exports[`Metric table for the duo_review_requests_count table row when the data is loaded renders the metric values 1`] = ` +[ + "20", + "8", + "32", + "15", + "11", + "26", +] +`; + +exports[`Metric table for the duo_review_requests_count table row when the data is loaded renders the sparkline chart with expected props 1`] = ` +{ + "data": [ + [ + "Jul 1 – 31", + 20, + ], + [ + "Aug 1 – 31", + 8, + ], + [ + "Sep 1 – 30", + 32, + ], + [ + "Oct 1 – 31", + 15, + ], + [ + "Nov 1 – 30", + 11, + ], + ], + "tooltipLabel": "", + "trendStyle": "ASC", +} +`; + exports[`Metric table for the issues table row when the data is loaded renders the metric values 1`] = ` [ "1", diff --git a/ee/spec/frontend/analytics/dashboards/ai_impact/components/metric_table_spec.js b/ee/spec/frontend/analytics/dashboards/ai_impact/components/metric_table_spec.js index a7229d78231da22259e2d92d7262f7b96ca1def0..b21c95690246d08b2ac84a6a6362dcac8fc23e59 100644 --- a/ee/spec/frontend/analytics/dashboards/ai_impact/components/metric_table_spec.js +++ b/ee/spec/frontend/analytics/dashboards/ai_impact/components/metric_table_spec.js @@ -221,6 +221,8 @@ describe('Metric table', () => { ${AI_METRICS.CODE_SUGGESTIONS_ACCEPTANCE_RATE} | ${''} | ${''} ${AI_METRICS.DUO_CHAT_USAGE_RATE} | ${''} | ${''} ${AI_METRICS.DUO_RCA_USAGE_RATE} | ${''} | ${''} + ${AI_METRICS.DUO_REVIEW_REQUESTS_COUNT} | ${''} | ${''} + ${AI_METRICS.DUO_REVIEW_COMMENT_COUNT} | ${''} | ${''} ${PIPELINE_ANALYTICS_METRICS.COUNT} | ${namespace} | ${AI_IMPACT_TABLE_TRACKING_PROPERTY} ${PIPELINE_ANALYTICS_METRICS.MEDIAN} | ${namespace} | ${AI_IMPACT_TABLE_TRACKING_PROPERTY} ${PIPELINE_ANALYTICS_METRICS.SUCCESS_RATE} | ${namespace} | ${AI_IMPACT_TABLE_TRACKING_PROPERTY} @@ -257,6 +259,8 @@ describe('Metric table', () => { ${AI_METRICS.CODE_SUGGESTIONS_ACCEPTANCE_RATE} | ${'Code Suggestions acceptance rate'} ${AI_METRICS.DUO_CHAT_USAGE_RATE} | ${'Duo Chat usage'} ${AI_METRICS.DUO_RCA_USAGE_RATE} | ${'Duo RCA usage'} + ${AI_METRICS.DUO_REVIEW_REQUESTS_COUNT} | ${'Duo Code Review requests'} + ${AI_METRICS.DUO_REVIEW_COMMENT_COUNT} | ${'Duo Code Review comments'} ${PIPELINE_ANALYTICS_METRICS.COUNT} | ${'Total pipeline runs'} ${PIPELINE_ANALYTICS_METRICS.MEDIAN} | ${'Median duration'} ${PIPELINE_ANALYTICS_METRICS.SUCCESS_RATE} | ${'Success rate'} diff --git a/ee/spec/frontend/analytics/dashboards/ai_impact/helpers.js b/ee/spec/frontend/analytics/dashboards/ai_impact/helpers.js index 830c3e50c8c898358921caeaf405d1aa441c9cb5..e0556a3bfb5b24a9ec5031639739cda9be42ebb9 100644 --- a/ee/spec/frontend/analytics/dashboards/ai_impact/helpers.js +++ b/ee/spec/frontend/analytics/dashboards/ai_impact/helpers.js @@ -156,6 +156,9 @@ export const mockAiMetricsResponse = (values = []) => rootCauseAnalysisUsersCount, duoAssignedUsersCount, duoUsedCount, + requestReviewDuoCodeReviewOnMrByAuthorEventCount, + requestReviewDuoCodeReviewOnMrByNonAuthorEventCount, + postCommentDuoCodeReviewOnDiffEventCount, languages, acceptedLinesOfCode, shownLinesOfCode, @@ -180,6 +183,11 @@ export const mockAiMetricsResponse = (values = []) => rootCauseAnalysisUsersCount, duoAssignedUsersCount, duoUsedCount, + codeReview: { + requestReviewDuoCodeReviewOnMrByAuthorEventCount, + requestReviewDuoCodeReviewOnMrByNonAuthorEventCount, + postCommentDuoCodeReviewOnDiffEventCount, + }, __typename: 'AiMetrics', }, }, diff --git a/ee/spec/frontend/analytics/dashboards/ai_impact/mock_data.js b/ee/spec/frontend/analytics/dashboards/ai_impact/mock_data.js index 3ae9d6beb915e15e28b3d7cec9df2839cedce2fb..0731fa0dfd58da3700ce56fd9a4707a19f4419b8 100644 --- a/ee/spec/frontend/analytics/dashboards/ai_impact/mock_data.js +++ b/ee/spec/frontend/analytics/dashboards/ai_impact/mock_data.js @@ -150,6 +150,9 @@ export const mockTableValues = [ rootCauseAnalysisUsersCount: 5, duoAssignedUsersCount: 15, duoUsedCount: 100, + requestReviewDuoCodeReviewOnMrByAuthorEventCount: 12, + requestReviewDuoCodeReviewOnMrByNonAuthorEventCount: 8, + postCommentDuoCodeReviewOnDiffEventCount: 45, pipelineCount: 387, pipelineSuccessCount: 149, pipelineFailedCount: 175, @@ -179,6 +182,9 @@ export const mockTableValues = [ rootCauseAnalysisUsersCount: 6, duoAssignedUsersCount: 7, duoUsedCount: 100, + requestReviewDuoCodeReviewOnMrByAuthorEventCount: 5, + requestReviewDuoCodeReviewOnMrByNonAuthorEventCount: 3, + postCommentDuoCodeReviewOnDiffEventCount: 22, pipelineCount: 37, pipelineSuccessCount: 49, pipelineFailedCount: 15, @@ -208,6 +214,9 @@ export const mockTableValues = [ rootCauseAnalysisUsersCount: 11, duoAssignedUsersCount: 15, duoUsedCount: 100, + requestReviewDuoCodeReviewOnMrByAuthorEventCount: 18, + requestReviewDuoCodeReviewOnMrByNonAuthorEventCount: 14, + postCommentDuoCodeReviewOnDiffEventCount: 67, pipelineCount: 27, pipelineSuccessCount: 10, pipelineFailedCount: 5, @@ -237,6 +246,9 @@ export const mockTableValues = [ rootCauseAnalysisUsersCount: 12, duoAssignedUsersCount: 18, duoUsedCount: 100, + requestReviewDuoCodeReviewOnMrByAuthorEventCount: 9, + requestReviewDuoCodeReviewOnMrByNonAuthorEventCount: 6, + postCommentDuoCodeReviewOnDiffEventCount: 34, pipelineCount: 95, pipelineSuccessCount: 60, pipelineFailedCount: 10, @@ -266,6 +278,9 @@ export const mockTableValues = [ rootCauseAnalysisUsersCount: 7, duoAssignedUsersCount: 17, duoUsedCount: 100, + requestReviewDuoCodeReviewOnMrByAuthorEventCount: 7, + requestReviewDuoCodeReviewOnMrByNonAuthorEventCount: 4, + postCommentDuoCodeReviewOnDiffEventCount: 28, pipelineCount: 75, pipelineSuccessCount: 18, pipelineFailedCount: 15, @@ -295,6 +310,9 @@ export const mockTableValues = [ rootCauseAnalysisUsersCount: 6, duoAssignedUsersCount: 12, duoUsedCount: 100, + requestReviewDuoCodeReviewOnMrByAuthorEventCount: 15, + requestReviewDuoCodeReviewOnMrByNonAuthorEventCount: 11, + postCommentDuoCodeReviewOnDiffEventCount: 53, pipelineCount: 100, pipelineSuccessCount: 50, pipelineFailedCount: 25, @@ -326,6 +344,9 @@ export const mockTableLargeValues = [ rootCauseAnalysisUsersCount: 1000, duoAssignedUsersCount: 2000, duoUsedCount: 10000, + requestReviewDuoCodeReviewOnMrByAuthorEventCount: 1250, + requestReviewDuoCodeReviewOnMrByNonAuthorEventCount: 850, + postCommentDuoCodeReviewOnDiffEventCount: 4500, pipelineCount: 37000, pipelineSuccessCount: 49000, pipelineFailedCount: 15000, @@ -352,6 +373,9 @@ export const mockTableLargeValues = [ rootCauseAnalysisUsersCount: 1200, duoAssignedUsersCount: 1500, duoUsedCount: 10000, + requestReviewDuoCodeReviewOnMrByAuthorEventCount: 980, + requestReviewDuoCodeReviewOnMrByNonAuthorEventCount: 620, + postCommentDuoCodeReviewOnDiffEventCount: 3200, pipelineCount: 27000, pipelineSuccessCount: 29000, pipelineFailedCount: 1500, @@ -378,6 +402,9 @@ export const mockTableLargeValues = [ rootCauseAnalysisUsersCount: 2000, duoAssignedUsersCount: 2400, duoUsedCount: 10000, + requestReviewDuoCodeReviewOnMrByAuthorEventCount: 1800, + requestReviewDuoCodeReviewOnMrByNonAuthorEventCount: 1400, + postCommentDuoCodeReviewOnDiffEventCount: 6700, pipelineCount: 45000, pipelineSuccessCount: 1000, pipelineFailedCount: 100, @@ -404,6 +431,9 @@ export const mockTableLargeValues = [ rootCauseAnalysisUsersCount: 4000, duoAssignedUsersCount: 6000, duoUsedCount: 10000, + requestReviewDuoCodeReviewOnMrByAuthorEventCount: 2100, + requestReviewDuoCodeReviewOnMrByNonAuthorEventCount: 1650, + postCommentDuoCodeReviewOnDiffEventCount: 8900, pipelineCount: 217000, pipelineSuccessCount: 129000, pipelineFailedCount: 15400, @@ -430,6 +460,9 @@ export const mockTableLargeValues = [ rootCauseAnalysisUsersCount: 7000, duoAssignedUsersCount: 9000, duoUsedCount: 10000, + requestReviewDuoCodeReviewOnMrByAuthorEventCount: 3400, + requestReviewDuoCodeReviewOnMrByNonAuthorEventCount: 2800, + postCommentDuoCodeReviewOnDiffEventCount: 12500, pipelineCount: 52000, pipelineSuccessCount: 41000, pipelineFailedCount: 1400, @@ -456,6 +489,9 @@ export const mockTableLargeValues = [ rootCauseAnalysisUsersCount: 8000, duoAssignedUsersCount: 8500, duoUsedCount: 10000, + requestReviewDuoCodeReviewOnMrByAuthorEventCount: 4200, + requestReviewDuoCodeReviewOnMrByNonAuthorEventCount: 3300, + postCommentDuoCodeReviewOnDiffEventCount: 15800, pipelineCount: 720, pipelineSuccessCount: 240, pipelineFailedCount: 70, @@ -533,6 +569,9 @@ export const mockAiMetricsResponseData = { duoChatContributorsCount: 5, duoAssignedUsersCount: 10, duoUsedCount: 3, + requestReviewDuoCodeReviewOnMrByAuthorEventCount: 100, + requestReviewDuoCodeReviewOnMrByNonAuthorEventCount: 100, + postCommentDuoCodeReviewOnDiffEventCount: 100, __typename: 'AiMetrics', }, __typename: 'Group', @@ -551,6 +590,9 @@ export const mockAiMetricsZeroResponseData = { duoChatContributorsCount: 0, duoAssignedUsersCount: 0, duoUsedCount: 0, + requestReviewDuoCodeReviewOnMrByAuthorEventCount: 0, + requestReviewDuoCodeReviewOnMrByNonAuthorEventCount: 0, + postCommentDuoCodeReviewOnDiffEventCount: 0, __typename: 'AiMetrics', }, __typename: 'Group', @@ -567,6 +609,9 @@ export const mockAiMetricsNullResponseData = { duoChatContributorsCount: null, duoAssignedUsersCount: null, duoUsedCount: null, + requestReviewDuoCodeReviewOnMrByAuthorEventCount: null, + requestReviewDuoCodeReviewOnMrByNonAuthorEventCount: null, + postCommentDuoCodeReviewOnDiffEventCount: null, __typename: 'AiMetrics', }, __typename: 'Group', diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 1a85177ef324b1f5c7e1b8c9e9f83a272cd71079..84bd2cb80ea4add728a84f8eac7372f5de9769be 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -6723,12 +6723,24 @@ msgstr "" msgid "AiImpactAnalytics|Duo Chat usage" msgstr "" +msgid "AiImpactAnalytics|Duo Code Review comments" +msgstr "" + +msgid "AiImpactAnalytics|Duo Code Review requests" +msgstr "" + msgid "AiImpactAnalytics|Duo RCA usage" msgstr "" msgid "AiImpactAnalytics|Duo features usage" msgstr "" +msgid "AiImpactAnalytics|Number of Duo Code Review comments." +msgstr "" + +msgid "AiImpactAnalytics|Number of Duo Code Review requests." +msgstr "" + msgid "AiImpactAnalytics|Number of contributors who used any GitLab Duo feature." msgstr ""