diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-10-20 09:08:43 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-10-20 09:08:43 +0000 |
commit | bc12365ae0254332f97299138f019bea3ff12351 (patch) | |
tree | c3a65a5523b0b0455780b17d11e0d7039355baa7 /app/assets/javascripts/analytics | |
parent | 99551d44588b9c815df9691c8e619eb8beaa0045 (diff) | |
download | gitlab-ce-bc12365ae0254332f97299138f019bea3ff12351.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/analytics')
6 files changed, 180 insertions, 7 deletions
diff --git a/app/assets/javascripts/analytics/instance_statistics/components/app.vue b/app/assets/javascripts/analytics/instance_statistics/components/app.vue index 64c1a2565be..7aa5c98aa0b 100644 --- a/app/assets/javascripts/analytics/instance_statistics/components/app.vue +++ b/app/assets/javascripts/analytics/instance_statistics/components/app.vue @@ -1,19 +1,30 @@ <script> import InstanceCounts from './instance_counts.vue'; import PipelinesChart from './pipelines_chart.vue'; +import UsersChart from './users_chart.vue'; +import { TODAY, TOTAL_DAYS_TO_SHOW, START_DATE } from '../constants'; export default { name: 'InstanceStatisticsApp', components: { InstanceCounts, PipelinesChart, + UsersChart, }, + TOTAL_DAYS_TO_SHOW, + START_DATE, + TODAY, }; </script> <template> <div> <instance-counts /> + <users-chart + :start-date="$options.START_DATE" + :end-date="$options.TODAY" + :total-data-points="$options.TOTAL_DAYS_TO_SHOW" + /> <pipelines-chart /> </div> </template> diff --git a/app/assets/javascripts/analytics/instance_statistics/components/users_chart.vue b/app/assets/javascripts/analytics/instance_statistics/components/users_chart.vue new file mode 100644 index 00000000000..a4a1d40b70b --- /dev/null +++ b/app/assets/javascripts/analytics/instance_statistics/components/users_chart.vue @@ -0,0 +1,143 @@ +<script> +import { GlAlert } from '@gitlab/ui'; +import { GlAreaChart } from '@gitlab/ui/dist/charts'; +import produce from 'immer'; +import { sortBy } from 'lodash'; +import * as Sentry from '~/sentry/wrapper'; +import ChartSkeletonLoader from '~/vue_shared/components/resizable_chart/skeleton_loader.vue'; +import { __ } from '~/locale'; +import { formatDateAsMonth } from '~/lib/utils/datetime_utility'; +import usersQuery from '../graphql/queries/users.query.graphql'; +import { getAverageByMonth } from '../utils'; + +const sortByDate = data => sortBy(data, item => new Date(item[0]).getTime()); + +export default { + name: 'UsersChart', + components: { GlAlert, GlAreaChart, ChartSkeletonLoader }, + props: { + startDate: { + type: Date, + required: true, + }, + endDate: { + type: Date, + required: true, + }, + totalDataPoints: { + type: Number, + required: true, + }, + }, + data() { + return { + loadingError: null, + users: [], + pageInfo: null, + }; + }, + apollo: { + users: { + query: usersQuery, + variables() { + return { + first: this.totalDataPoints, + after: null, + }; + }, + update(data) { + return data.users?.nodes || []; + }, + result({ data }) { + const { + users: { pageInfo }, + } = data; + this.pageInfo = pageInfo; + this.fetchNextPage(); + }, + error(error) { + this.handleError(error); + }, + }, + }, + i18n: { + yAxisTitle: __('Total users'), + xAxisTitle: __('Month'), + loadUserChartError: __('Could not load the user chart. Please refresh the page to try again.'), + noDataMessage: __('There is no data available.'), + }, + computed: { + isLoading() { + return this.$apollo.queries.users.loading || this.pageInfo?.hasNextPage; + }, + chartUserData() { + const averaged = getAverageByMonth( + this.users.length > this.totalDataPoints + ? this.users.slice(0, this.totalDataPoints) + : this.users, + { shouldRound: true }, + ); + return sortByDate(averaged); + }, + options() { + return { + xAxis: { + name: this.$options.i18n.xAxisTitle, + type: 'category', + axisLabel: { + formatter: formatDateAsMonth, + }, + }, + yAxis: { + name: this.$options.i18n.yAxisTitle, + }, + }; + }, + }, + methods: { + handleError(error) { + this.loadingError = true; + this.users = []; + Sentry.captureException(error); + }, + fetchNextPage() { + if (this.pageInfo?.hasNextPage) { + this.$apollo.queries.users + .fetchMore({ + variables: { first: this.totalDataPoints, after: this.pageInfo.endCursor }, + updateQuery: (previousResult, { fetchMoreResult }) => { + return produce(fetchMoreResult, newUsers => { + // eslint-disable-next-line no-param-reassign + newUsers.users.nodes = [...previousResult.users.nodes, ...newUsers.users.nodes]; + }); + }, + }) + .catch(this.handleError); + } + }, + }, +}; +</script> +<template> + <div> + <h3>{{ $options.i18n.yAxisTitle }}</h3> + <gl-alert v-if="loadingError" variant="danger" :dismissible="false" class="gl-mt-3"> + {{ this.$options.i18n.loadUserChartError }} + </gl-alert> + <chart-skeleton-loader v-else-if="isLoading" /> + <gl-alert v-else-if="!chartUserData.length" variant="info" :dismissible="false" class="gl-mt-3"> + {{ $options.i18n.noDataMessage }} + </gl-alert> + <gl-area-chart + v-else + :option="options" + :include-legend-avg-max="true" + :data="[ + { + name: $options.i18n.yAxisTitle, + data: chartUserData, + }, + ]" + /> + </div> +</template> diff --git a/app/assets/javascripts/analytics/instance_statistics/constants.js b/app/assets/javascripts/analytics/instance_statistics/constants.js index 5ea5d17c974..846c0ef408b 100644 --- a/app/assets/javascripts/analytics/instance_statistics/constants.js +++ b/app/assets/javascripts/analytics/instance_statistics/constants.js @@ -1,5 +1,5 @@ import { getDateInPast } from '~/lib/utils/datetime_utility'; -const TOTAL_DAYS_TO_SHOW = 365; +export const TOTAL_DAYS_TO_SHOW = 365; export const TODAY = new Date(); export const START_DATE = getDateInPast(TODAY, TOTAL_DAYS_TO_SHOW); diff --git a/app/assets/javascripts/analytics/instance_statistics/graphql/fragments/count.fragment.graphql b/app/assets/javascripts/analytics/instance_statistics/graphql/fragments/count.fragment.graphql new file mode 100644 index 00000000000..40cef95c2e7 --- /dev/null +++ b/app/assets/javascripts/analytics/instance_statistics/graphql/fragments/count.fragment.graphql @@ -0,0 +1,4 @@ +fragment Count on InstanceStatisticsMeasurement { + count + recordedAt +} diff --git a/app/assets/javascripts/analytics/instance_statistics/graphql/queries/instance_statistics_count.query.graphql b/app/assets/javascripts/analytics/instance_statistics/graphql/queries/instance_statistics_count.query.graphql index fd8282683d9..f14c2658674 100644 --- a/app/assets/javascripts/analytics/instance_statistics/graphql/queries/instance_statistics_count.query.graphql +++ b/app/assets/javascripts/analytics/instance_statistics/graphql/queries/instance_statistics_count.query.graphql @@ -1,32 +1,34 @@ +#import "../fragments/count.fragment.graphql" + query getInstanceCounts { projects: instanceStatisticsMeasurements(identifier: PROJECTS, first: 1) { nodes { - count + ...Count } } groups: instanceStatisticsMeasurements(identifier: GROUPS, first: 1) { nodes { - count + ...Count } } users: instanceStatisticsMeasurements(identifier: USERS, first: 1) { nodes { - count + ...Count } } issues: instanceStatisticsMeasurements(identifier: ISSUES, first: 1) { nodes { - count + ...Count } } mergeRequests: instanceStatisticsMeasurements(identifier: MERGE_REQUESTS, first: 1) { nodes { - count + ...Count } } pipelines: instanceStatisticsMeasurements(identifier: PIPELINES, first: 1) { nodes { - count + ...Count } } } diff --git a/app/assets/javascripts/analytics/instance_statistics/graphql/queries/users.query.graphql b/app/assets/javascripts/analytics/instance_statistics/graphql/queries/users.query.graphql new file mode 100644 index 00000000000..6235e36eb89 --- /dev/null +++ b/app/assets/javascripts/analytics/instance_statistics/graphql/queries/users.query.graphql @@ -0,0 +1,13 @@ +#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" +#import "../fragments/count.fragment.graphql" + +query getUsersCount($first: Int, $after: String) { + users: instanceStatisticsMeasurements(identifier: USERS, first: $first, after: $after) { + nodes { + ...Count + } + pageInfo { + ...PageInfo + } + } +} |