diff --git a/app/assets/javascripts/analytics/shared/constants.js b/app/assets/javascripts/analytics/shared/constants.js index 18160ecd7ae9e2c125601e3d9e79dc50dda3b17d..1b54725b1f1563f10897859489c39b842bbc5f0c 100644 --- a/app/assets/javascripts/analytics/shared/constants.js +++ b/app/assets/javascripts/analytics/shared/constants.js @@ -131,6 +131,7 @@ export const CONTRIBUTOR_METRICS = { export const AI_METRICS = { CODE_SUGGESTIONS_USAGE_RATE: 'code_suggestions_usage_rate', CODE_SUGGESTIONS_ACCEPTANCE_RATE: 'code_suggestions_acceptance_rate', + DUO_PRO_USAGE_RATE: 'duo_pro_usage_rate', }; export const METRIC_TOOLTIPS = { diff --git a/ee/app/assets/javascripts/analytics/analytics_dashboards/data_sources/ai_impact_over_time.js b/ee/app/assets/javascripts/analytics/analytics_dashboards/data_sources/ai_impact_over_time.js index 5e1b728f9103e5ed74835e99fa0528714e40bb70..262ecfbd52ef6ebbccccd92e7920a784dfe311db 100644 --- a/ee/app/assets/javascripts/analytics/analytics_dashboards/data_sources/ai_impact_over_time.js +++ b/ee/app/assets/javascripts/analytics/analytics_dashboards/data_sources/ai_impact_over_time.js @@ -40,6 +40,14 @@ const extractMetricRateValue = ({ metric, rawQueryResult: result }) => { }); } + case AI_METRICS.DUO_PRO_USAGE_RATE: { + const { duoChatContributorsCount, duoProAssignedUsersCount } = resp; + return calculateRate({ + numerator: duoChatContributorsCount, + denominator: duoProAssignedUsersCount, + }); + } + default: return null; } 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 f93a5f0b123b741ab3e09529309056d8a1f9c09a..91db089ab8b3fc4a93f18221f13aa9425a524822 100644 --- a/ee/app/assets/javascripts/analytics/dashboards/ai_impact/constants.js +++ b/ee/app/assets/javascripts/analytics/dashboards/ai_impact/constants.js @@ -31,6 +31,10 @@ export const AI_IMPACT_OVER_TIME_METRICS = { label: s__('AiImpactAnalytics|Code Suggestions acceptance usage'), units: UNITS.PERCENT, }, + [AI_METRICS.DUO_PRO_USAGE_RATE]: { + label: s__('AiImpactAnalytics|Duo Pro seats: Assigned and used'), + units: UNITS.PERCENT, + }, }; export const AI_IMPACT_TABLE_METRICS = { 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 fd837b86a5b3fa6b8321fa9bd918921b2479f5c8..fba4349e6f7465fa2e9100e9392068df1050e8fe 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 @@ -5,4 +5,6 @@ fragment AiMetricItem on AiMetrics { codeSuggestionsContributorsCount codeSuggestionsShownCount codeSuggestionsAcceptedCount + duoChatContributorsCount + duoProAssignedUsersCount } diff --git a/ee/app/models/product_analytics/visualization.rb b/ee/app/models/product_analytics/visualization.rb index 427390b348baf49b934560d07e6a4c2ccdcb24a2..b64b62fa1b6d01531ba03425cc71477a5e0513c6 100644 --- a/ee/app/models/product_analytics/visualization.rb +++ b/ee/app/models/product_analytics/visualization.rb @@ -39,8 +39,12 @@ class Visualization ].freeze AI_IMPACT_DASHBOARD_PATH = 'ee/lib/gitlab/analytics/ai_impact_dashboard/visualizations' - AI_IMPACT_DASHBOARD_VISUALIZATIONS = %w[ai_impact_table code_suggestions_usage_rate_over_time - code_suggestions_acceptance_rate_over_time].freeze + AI_IMPACT_DASHBOARD_VISUALIZATIONS = %w[ + ai_impact_table + code_suggestions_usage_rate_over_time + code_suggestions_acceptance_rate_over_time + duo_pro_usage_rate_over_time + ].freeze def self.for(container:, user:) config_project = diff --git a/ee/lib/gitlab/analytics/ai_impact_dashboard/dashboard.yaml b/ee/lib/gitlab/analytics/ai_impact_dashboard/dashboard.yaml index e43e6175e8e87f0abd042d2193f981860402c307..133c16432fa28e6c6e6d60975ec7c3f92795442c 100644 --- a/ee/lib/gitlab/analytics/ai_impact_dashboard/dashboard.yaml +++ b/ee/lib/gitlab/analytics/ai_impact_dashboard/dashboard.yaml @@ -3,11 +3,19 @@ title: AI impact analytics description: Visualize the relation between AI usage and SDLC trends. version: "2" panels: + - title: "Duo Pro seats: Assigned and used" + visualization: duo_pro_usage_rate_over_time + gridAttributes: + yPos: 0 + xPos: 0 + width: 3 + height: 1 + options: {} - title: "Code Suggestions: Unique users" visualization: code_suggestions_usage_rate_over_time gridAttributes: yPos: 0 - xPos: 0 + xPos: 3 width: 3 height: 1 options: {} @@ -15,7 +23,7 @@ panels: visualization: code_suggestions_acceptance_rate_over_time gridAttributes: yPos: 0 - xPos: 3 + xPos: 6 width: 3 height: 1 options: {} diff --git a/ee/lib/gitlab/analytics/ai_impact_dashboard/visualizations/duo_pro_usage_rate_over_time.yaml b/ee/lib/gitlab/analytics/ai_impact_dashboard/visualizations/duo_pro_usage_rate_over_time.yaml new file mode 100644 index 0000000000000000000000000000000000000000..5f70140d6f22d32a77d2d6fdb7be3896d003e61a --- /dev/null +++ b/ee/lib/gitlab/analytics/ai_impact_dashboard/visualizations/duo_pro_usage_rate_over_time.yaml @@ -0,0 +1,11 @@ +--- +version: 1 +type: SingleStat +data: + type: ai_impact_over_time + query: + metric: duo_pro_usage_rate + dateRange: last_30_days +options: + unit: percent + decimalPlaces: 1 diff --git a/ee/spec/frontend/analytics/analytics_dashboards/components/data_sources/ai_impact_over_time_spec.js b/ee/spec/frontend/analytics/analytics_dashboards/components/data_sources/ai_impact_over_time_spec.js index a040e2e796a386704750e97ce57f1ab9606d99f9..908e46d55d4bbd1b5acf669ce74d8a33c8d45f44 100644 --- a/ee/spec/frontend/analytics/analytics_dashboards/components/data_sources/ai_impact_over_time_spec.js +++ b/ee/spec/frontend/analytics/analytics_dashboards/components/data_sources/ai_impact_over_time_spec.js @@ -1,4 +1,5 @@ import { mockAiMetricsResponseData } from 'ee_jest/analytics/dashboards/ai_impact/mock_data'; +import { AI_METRICS } from '~/analytics/shared/constants'; import fetch from 'ee/analytics/analytics_dashboards/data_sources/ai_impact_over_time'; import { defaultClient } from 'ee/analytics/analytics_dashboards/graphql/client'; import { LAST_WEEK, LAST_30_DAYS, LAST_180_DAYS } from 'ee/dora/components/static_data/shared'; @@ -6,7 +7,7 @@ import { LAST_WEEK, LAST_30_DAYS, LAST_180_DAYS } from 'ee/dora/components/stati describe('AI Impact Over Time Data Source', () => { let res; - const query = { metric: 'code_suggestions_usage_rate', dateRange: LAST_30_DAYS }; + const query = { metric: AI_METRICS.CODE_SUGGESTIONS_USAGE_RATE, dateRange: LAST_30_DAYS }; const namespace = 'cool namespace'; const defaultParams = { namespace, @@ -72,7 +73,7 @@ describe('AI Impact Over Time Data Source', () => { dateRange, { namespace: namespaceParam = 'cool namespace', - metric = 'code_suggestions_usage_rate', + metric = AI_METRICS.CODE_SUGGESTIONS_USAGE_RATE, } = {}, ) => { mockResolvedQuery(); @@ -94,8 +95,13 @@ describe('AI Impact Over Time Data Source', () => { }); }); - it('can override the metric queried', async () => { - res = await mockQuery(LAST_WEEK, { metric: 'code_suggestions_acceptance_rate' }); + it.each` + metric | result + ${AI_METRICS.CODE_SUGGESTIONS_USAGE_RATE} | ${'62.5'} + ${AI_METRICS.CODE_SUGGESTIONS_ACCEPTANCE_RATE} | ${'40.0'} + ${AI_METRICS.DUO_PRO_USAGE_RATE} | ${'50.0'} + `('can override the metric with `$metric`', async ({ metric, result }) => { + res = await mockQuery(LAST_WEEK, { metric }); expectQueryWithVariables({ startDate: new Date('2020-06-30'), @@ -103,7 +109,7 @@ describe('AI Impact Over Time Data Source', () => { fullPath: namespace, }); - expect(res).toBe('40.0'); + expect(res).toBe(result); }); it('can override the namespace', async () => { 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 bb4c3d34b1eaf8f62e5dc848402a5ff0ad690079..657e1ee66cb9c37de0da51bdca49088a835da908 100644 --- a/ee/spec/frontend/analytics/dashboards/ai_impact/mock_data.js +++ b/ee/spec/frontend/analytics/dashboards/ai_impact/mock_data.js @@ -209,6 +209,8 @@ export const mockAiMetricsResponseData = { codeSuggestionsContributorsCount: 5, codeSuggestionsAcceptedCount: 2, codeSuggestionsShownCount: 5, + duoChatContributorsCount: 5, + duoProAssignedUsersCount: 10, __typename: 'AiMetrics', }, __typename: 'Group', diff --git a/ee/spec/requests/api/graphql/product_analytics/visualizations_spec.rb b/ee/spec/requests/api/graphql/product_analytics/visualizations_spec.rb index ca1f4cdec4977769c118fb5e83822b4ba6ff8d53..dc46906a47b2279578c64cb9288691b2bda0546b 100644 --- a/ee/spec/requests/api/graphql/product_analytics/visualizations_spec.rb +++ b/ee/spec/requests/api/graphql/product_analytics/visualizations_spec.rb @@ -61,9 +61,10 @@ end where(:node_idx, :panel_type, :panel_title) do - 0 | 'SingleStat' | 'Code Suggestions: Unique users' - 1 | 'SingleStat' | 'Code Suggestions: Acceptance rate' - 2 | 'AiImpactTable' | 'Metric trends for %{namespaceType}: %{namespaceName}' + 0 | 'SingleStat' | 'Duo Pro seats: Assigned and used' + 1 | 'SingleStat' | 'Code Suggestions: Unique users' + 2 | 'SingleStat' | 'Code Suggestions: Acceptance rate' + 3 | 'AiImpactTable' | 'Metric trends for %{namespaceType}: %{namespaceName}' end with_them do diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 7f81ec1b7fc6edafe33ba9d259eb0dc8aa329682..c3f2f622a5eb1afd82f41c22f3bc06b6af59d68f 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -4957,6 +4957,9 @@ msgstr "" msgid "AiImpactAnalytics|Code Suggestions usage" msgstr "" +msgid "AiImpactAnalytics|Duo Pro seats: Assigned and used" +msgstr "" + msgid "AiImpactAnalytics|Monthly user engagement with AI Code Suggestions. Percentage ratio calculated as monthly unique Code Suggestions users / total monthly unique code contributors." msgstr ""