Skip to content
Snippets Groups Projects
Commit 7515ec41 authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent a77db6bc
No related branches found
No related tags found
No related merge requests found
Showing
with 412 additions and 770 deletions
<script>
import { __ } from '~/locale';
import _ from 'underscore';
import { mapActions, mapState, mapGetters } from 'vuex';
import { GlLoadingIcon } from '@gitlab/ui';
import { GlAreaChart } from '@gitlab/ui/dist/charts';
import { getSvgIconPathContent } from '~/lib/utils/icon_utils';
import { getDatesInRange } from '~/lib/utils/datetime_utility';
import { xAxisLabelFormatter, dateFormatter } from '../utils';
export default {
components: {
GlAreaChart,
GlLoadingIcon,
},
props: {
endpoint: {
type: String,
required: true,
},
branch: {
type: String,
required: true,
},
},
data() {
return {
masterChart: null,
individualCharts: [],
svgs: {},
masterChartHeight: 264,
individualChartHeight: 216,
};
},
computed: {
...mapState(['chartData', 'loading']),
...mapGetters(['showChart', 'parsedData']),
masterChartData() {
const data = {};
this.xAxisRange.forEach(date => {
data[date] = this.parsedData.total[date] || 0;
});
return [
{
name: __('Commits'),
data: Object.entries(data),
},
];
},
masterChartOptions() {
return {
...this.getCommonChartOptions(true),
yAxis: {
name: __('Number of commits'),
},
grid: {
bottom: 64,
left: 64,
right: 20,
top: 20,
},
};
},
individualChartsData() {
const maxNumberOfIndividualContributorsCharts = 100;
return Object.keys(this.parsedData.byAuthor)
.map(name => {
const author = this.parsedData.byAuthor[name];
return {
name,
email: author.email,
commits: author.commits,
dates: [
{
name: __('Commits'),
data: this.xAxisRange.map(date => [date, author.dates[date] || 0]),
},
],
};
})
.sort((a, b) => b.commits - a.commits)
.slice(0, maxNumberOfIndividualContributorsCharts);
},
individualChartOptions() {
return {
...this.getCommonChartOptions(false),
yAxis: {
name: __('Commits'),
max: this.individualChartYAxisMax,
},
grid: {
bottom: 27,
left: 64,
right: 20,
top: 8,
},
};
},
individualChartYAxisMax() {
return this.individualChartsData.reduce((acc, item) => {
const values = item.dates[0].data.map(value => value[1]);
return Math.max(acc, ...values);
}, 0);
},
xAxisRange() {
const dates = Object.keys(this.parsedData.total).sort((a, b) => new Date(a) - new Date(b));
const firstContributionDate = new Date(dates[0]);
const lastContributionDate = new Date(dates[dates.length - 1]);
return getDatesInRange(firstContributionDate, lastContributionDate, dateFormatter);
},
firstContributionDate() {
return this.xAxisRange[0];
},
lastContributionDate() {
return this.xAxisRange[this.xAxisRange.length - 1];
},
charts() {
return _.uniq(this.individualCharts);
},
},
mounted() {
this.fetchChartData(this.endpoint);
},
methods: {
...mapActions(['fetchChartData']),
getCommonChartOptions(isMasterChart) {
return {
xAxis: {
type: 'time',
name: '',
data: this.xAxisRange,
axisLabel: {
formatter: xAxisLabelFormatter,
showMaxLabel: false,
showMinLabel: false,
},
boundaryGap: false,
splitNumber: isMasterChart ? 24 : 18,
// 28 days
minInterval: 28 * 86400 * 1000,
min: this.firstContributionDate,
max: this.lastContributionDate,
},
};
},
setSvg(name) {
return getSvgIconPathContent(name)
.then(path => {
if (path) {
this.$set(this.svgs, name, `path://${path}`);
}
})
.catch(() => {});
},
onMasterChartCreated(chart) {
this.masterChart = chart;
this.setSvg('scroll-handle')
.then(() => {
this.masterChart.setOption({
dataZoom: [
{
type: 'slider',
handleIcon: this.svgs['scroll-handle'],
},
],
});
})
.catch(() => {});
this.masterChart.on('datazoom', _.debounce(this.setIndividualChartsZoom, 200));
},
onIndividualChartCreated(chart) {
this.individualCharts.push(chart);
},
setIndividualChartsZoom(options) {
this.charts.forEach(chart =>
chart.setOption(
{
dataZoom: {
start: options.start,
end: options.end,
show: false,
},
},
{ lazyUpdate: true },
),
);
},
},
};
</script>
<template>
<div>
<div v-if="loading" class="contributors-loader text-center">
<gl-loading-icon :inline="true" :size="4" />
</div>
<div v-else-if="showChart" class="contributors-charts">
<h4>{{ __('Commits to') }} {{ branch }}</h4>
<span>{{ __('Excluding merge commits. Limited to 6,000 commits.') }}</span>
<div>
<gl-area-chart
:data="masterChartData"
:option="masterChartOptions"
:height="masterChartHeight"
@created="onMasterChartCreated"
/>
</div>
<div class="row">
<div v-for="contributor in individualChartsData" :key="contributor.name" class="col-6">
<h4>{{ contributor.name }}</h4>
<p>{{ n__('%d commit', '%d commits', contributor.commits) }} ({{ contributor.email }})</p>
<gl-area-chart
:data="contributor.dates"
:option="individualChartOptions"
:height="individualChartHeight"
@created="onIndividualChartCreated"
/>
</div>
</div>
</div>
</div>
</template>
import Vue from 'vue';
import ContributorsGraphs from './components/contributors.vue';
import store from './stores';
export default () => {
const el = document.querySelector('.js-contributors-graph');
if (!el) return null;
return new Vue({
el,
store,
render(createElement) {
return createElement(ContributorsGraphs, {
props: {
endpoint: el.dataset.projectGraphPath,
branch: el.dataset.projectBranch,
},
});
},
});
};
import axios from '~/lib/utils/axios_utils';
export default {
fetchChartData(endpoint) {
return axios.get(endpoint);
},
};
import flash from '~/flash';
import { __ } from '~/locale';
import service from '../services/contributors_service';
import * as types from './mutation_types';
export const fetchChartData = ({ commit }, endpoint) => {
commit(types.SET_LOADING_STATE, true);
return service
.fetchChartData(endpoint)
.then(res => res.data)
.then(data => {
commit(types.SET_CHART_DATA, data);
commit(types.SET_LOADING_STATE, false);
})
.catch(() => flash(__('An error occurred while loading chart data')));
};
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
export const showChart = state => Boolean(!state.loading && state.chartData);
export const parsedData = state => {
const byAuthor = {};
const total = {};
state.chartData.forEach(({ date, author_name, author_email }) => {
total[date] = total[date] ? total[date] + 1 : 1;
const authorData = byAuthor[author_name];
if (!authorData) {
byAuthor[author_name] = {
email: author_email.toLowerCase(),
commits: 1,
dates: {
[date]: 1,
},
};
} else {
authorData.commits += 1;
authorData.dates[date] = authorData.dates[date] ? authorData.dates[date] + 1 : 1;
}
});
return {
total,
byAuthor,
};
};
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
import Vue from 'vue';
import Vuex from 'vuex';
import state from './state';
import mutations from './mutations';
import * as getters from './getters';
import * as actions from './actions';
Vue.use(Vuex);
export const createStore = () =>
new Vuex.Store({
actions,
mutations,
getters,
state: state(),
});
export default createStore();
export const SET_CHART_DATA = 'SET_CHART_DATA';
export const SET_LOADING_STATE = 'SET_LOADING_STATE';
export const SET_ACTIVE_BRANCH = 'SET_ACTIVE_BRANCH';
import * as types from './mutation_types';
export default {
[types.SET_LOADING_STATE](state, value) {
state.loading = value;
},
[types.SET_CHART_DATA](state, chartData) {
Object.assign(state, {
chartData,
});
},
[types.SET_ACTIVE_BRANCH](state, branch) {
Object.assign(state, {
branch,
});
},
};
export default () => ({
loading: false,
chartData: null,
branch: 'master',
});
import { getMonthNames } from '~/lib/utils/datetime_utility';
/**
* Converts provided string to date and returns formatted value as a year for date in January and month name for the rest
* @param {String}
* @returns {String} - formatted value
*
* xAxisLabelFormatter('01-12-2019') will return '2019'
* xAxisLabelFormatter('02-12-2019') will return 'Feb'
* xAxisLabelFormatter('07-12-2019') will return 'Jul'
*/
export const xAxisLabelFormatter = val => {
const date = new Date(val);
const month = date.getUTCMonth();
const year = date.getUTCFullYear();
return month === 0 ? `${year}` : getMonthNames(true)[month];
};
/**
* Formats provided date to YYYY-MM-DD format
* @param {Date}
* @returns {String} - formatted value
*/
export const dateFormatter = date => {
const year = date.getUTCFullYear();
const month = date.getUTCMonth();
const day = date.getUTCDate();
return `${year}-${`0${month + 1}`.slice(-2)}-${`0${day}`.slice(-2)}`;
};
Loading
Loading
@@ -564,3 +564,26 @@ export const getDateInPast = (date, daysInPast) => {
 
export const beginOfDayTime = 'T00:00:00Z';
export const endOfDayTime = 'T23:59:59Z';
/**
* @param {Date} d1
* @param {Date} d2
* @param {Function} formatter
* @return {Any[]} an array of formatted dates between 2 given dates (including start&end date)
*/
export const getDatesInRange = (d1, d2, formatter = x => x) => {
if (!(d1 instanceof Date) || !(d2 instanceof Date)) {
return [];
}
let startDate = d1.getTime();
const endDate = d2.getTime();
const oneDay = 24 * 3600 * 1000;
const range = [d1];
while (startDate < endDate) {
startDate += oneDay;
range.push(new Date(startDate));
}
return range.map(formatter);
};
import $ from 'jquery';
import flash from '~/flash';
import { __ } from '~/locale';
import axios from '~/lib/utils/axios_utils';
import ContributorsStatGraph from './stat_graph_contributors';
import initContributorsGraphs from '~/contributors';
 
document.addEventListener('DOMContentLoaded', () => {
const url = document.querySelector('.js-graphs-show').dataset.projectGraphPath;
axios
.get(url)
.then(({ data }) => {
const graph = new ContributorsStatGraph();
graph.init(data);
$('#brush_change').change(() => {
graph.change_date_header();
graph.redraw_authors();
});
$('.stat-graph').fadeIn();
$('.loading-graph').hide();
})
.catch(() => flash(__('Error fetching contributors data.')));
});
document.addEventListener('DOMContentLoaded', initContributorsGraphs);
/* eslint-disable func-names, no-var, one-var, camelcase, no-param-reassign, no-return-assign */
import $ from 'jquery';
import _ from 'underscore';
import { n__, s__, createDateTimeFormat, sprintf } from '~/locale';
import {
ContributorsGraph,
ContributorsAuthorGraph,
ContributorsMasterGraph,
} from './stat_graph_contributors_graph';
import ContributorsStatGraphUtil from './stat_graph_contributors_util';
export default (function() {
function ContributorsStatGraph() {
this.dateFormat = createDateTimeFormat({ year: 'numeric', month: 'long', day: 'numeric' });
}
ContributorsStatGraph.prototype.init = function(log) {
var author_commits, total_commits;
this.parsed_log = ContributorsStatGraphUtil.parse_log(log);
this.set_current_field('commits');
total_commits = ContributorsStatGraphUtil.get_total_data(this.parsed_log, this.field);
author_commits = ContributorsStatGraphUtil.get_author_data(this.parsed_log, this.field);
this.add_master_graph(total_commits);
this.add_authors_graph(author_commits);
return this.change_date_header();
};
ContributorsStatGraph.prototype.add_master_graph = function(total_data) {
this.master_graph = new ContributorsMasterGraph(total_data);
return this.master_graph.draw();
};
ContributorsStatGraph.prototype.add_authors_graph = function(author_data) {
var limited_author_data;
this.authors = [];
limited_author_data = author_data.slice(0, 100);
return _.each(
limited_author_data,
(function(_this) {
return function(d) {
var author_graph, author_header;
author_header = _this.create_author_header(d);
$('.contributors-list').append(author_header);
author_graph = new ContributorsAuthorGraph(d.dates);
_this.authors[d.author_name] = author_graph;
return author_graph.draw();
};
})(this),
);
};
ContributorsStatGraph.prototype.format_author_commit_info = function(author) {
var commits;
commits = $('<span/>', {
class: 'graph-author-commits-count',
});
commits.text(n__('%d commit', '%d commits', author.commits));
return $('<span/>').append(commits);
};
ContributorsStatGraph.prototype.create_author_header = function(author) {
var author_commit_info, author_commit_info_span, author_email, author_name, list_item;
list_item = $('<li/>', {
class: 'person',
style: 'display: block;',
});
author_name = $(`<h4>${author.author_name}</h4>`);
author_email = $(`<p class="graph-author-email">${author.author_email}</p>`);
author_commit_info_span = $('<span/>', {
class: 'commits',
});
author_commit_info = this.format_author_commit_info(author);
author_commit_info_span.html(author_commit_info);
list_item.append(author_name);
list_item.append(author_email);
list_item.append(author_commit_info_span);
return list_item;
};
ContributorsStatGraph.prototype.redraw_master = function() {
var total_data;
total_data = ContributorsStatGraphUtil.get_total_data(this.parsed_log, this.field);
this.master_graph.set_data(total_data);
return this.master_graph.redraw();
};
ContributorsStatGraph.prototype.redraw_authors = function() {
$('ol').html('');
const { x_domain } = ContributorsGraph.prototype;
const author_commits = ContributorsStatGraphUtil.get_author_data(
this.parsed_log,
this.field,
x_domain,
);
return _.each(
author_commits,
(function(_this) {
return function(d) {
_this.redraw_author_commit_info(d);
if (_this.authors[d.author_name] != null) {
$(_this.authors[d.author_name].list_item).appendTo('ol');
_this.authors[d.author_name].set_data(d.dates);
return _this.authors[d.author_name].redraw();
}
return '';
};
})(this),
);
};
ContributorsStatGraph.prototype.set_current_field = function(field) {
return (this.field = field);
};
ContributorsStatGraph.prototype.change_date_header = function() {
const { x_domain } = ContributorsGraph.prototype;
const formattedDateRange = sprintf(s__('ContributorsPage|%{startDate} – %{endDate}'), {
startDate: this.dateFormat.format(new Date(x_domain[0])),
endDate: this.dateFormat.format(new Date(x_domain[1])),
});
return $('#date_header').text(formattedDateRange);
};
ContributorsStatGraph.prototype.redraw_author_commit_info = function(author) {
var author_commit_info, author_list_item, $author;
$author = this.authors[author.author_name];
if ($author != null) {
author_list_item = $(this.authors[author.author_name].list_item);
author_commit_info = this.format_author_commit_info(author);
return author_list_item.find('span').html(author_commit_info);
}
return '';
};
return ContributorsStatGraph;
})();
/* eslint-disable func-names, no-restricted-syntax, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, no-return-assign, no-else-return, no-shadow */
import $ from 'jquery';
import _ from 'underscore';
import { extent, max } from 'd3-array';
import { select, event as d3Event } from 'd3-selection';
import { scaleTime, scaleLinear } from 'd3-scale';
import { axisLeft, axisBottom } from 'd3-axis';
import { area } from 'd3-shape';
import { brushX } from 'd3-brush';
import { timeParse } from 'd3-time-format';
import { dateTickFormat } from '~/lib/utils/tick_formats';
const d3 = {
extent,
max,
select,
scaleTime,
scaleLinear,
axisLeft,
axisBottom,
area,
brushX,
timeParse,
};
const hasProp = {}.hasOwnProperty;
const extend = function(child, parent) {
for (const key in parent) {
if (hasProp.call(parent, key)) child[key] = parent[key];
}
function ctor() {
this.constructor = child;
}
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype;
return child;
};
export const ContributorsGraph = (function() {
function ContributorsGraph() {}
ContributorsGraph.prototype.MARGIN = {
top: 20,
right: 10,
bottom: 30,
left: 40,
};
ContributorsGraph.prototype.x_domain = null;
ContributorsGraph.prototype.y_domain = null;
ContributorsGraph.prototype.dates = [];
ContributorsGraph.prototype.determine_width = function(baseWidth, $parentElement) {
const parentPaddingWidth =
parseFloat($parentElement.css('padding-left')) +
parseFloat($parentElement.css('padding-right'));
const marginWidth = this.MARGIN.left + this.MARGIN.right;
return baseWidth - parentPaddingWidth - marginWidth;
};
ContributorsGraph.set_x_domain = function(data) {
return (ContributorsGraph.prototype.x_domain = data);
};
ContributorsGraph.set_y_domain = function(data) {
return (ContributorsGraph.prototype.y_domain = [
0,
d3.max(data, d => (d.commits = d.commits || d.additions || d.deletions)),
]);
};
ContributorsGraph.init_x_domain = function(data) {
return (ContributorsGraph.prototype.x_domain = d3.extent(data, d => d.date));
};
ContributorsGraph.init_y_domain = function(data) {
return (ContributorsGraph.prototype.y_domain = [
0,
d3.max(data, d => (d.commits = d.commits || d.additions || d.deletions)),
]);
};
ContributorsGraph.init_domain = function(data) {
ContributorsGraph.init_x_domain(data);
return ContributorsGraph.init_y_domain(data);
};
ContributorsGraph.set_dates = function(data) {
return (ContributorsGraph.prototype.dates = data);
};
ContributorsGraph.prototype.set_x_domain = function() {
return this.x.domain(this.x_domain);
};
ContributorsGraph.prototype.set_y_domain = function() {
return this.y.domain(this.y_domain);
};
ContributorsGraph.prototype.set_domain = function() {
this.set_x_domain();
return this.set_y_domain();
};
ContributorsGraph.prototype.create_scale = function(width, height) {
this.x = d3
.scaleTime()
.range([0, width])
.clamp(true);
return (this.y = d3
.scaleLinear()
.range([height, 0])
.nice());
};
ContributorsGraph.prototype.draw_x_axis = function() {
return this.svg
.append('g')
.attr('class', 'x axis')
.attr('transform', `translate(0, ${this.height})`)
.call(this.x_axis);
};
ContributorsGraph.prototype.draw_y_axis = function() {
return this.svg
.append('g')
.attr('class', 'y axis')
.call(this.y_axis);
};
ContributorsGraph.prototype.set_data = function(data) {
return (this.data = data);
};
return ContributorsGraph;
})();
export const ContributorsMasterGraph = (function(superClass) {
extend(ContributorsMasterGraph, superClass);
function ContributorsMasterGraph(data1) {
const $parentElement = $('#contributors-master');
this.data = data1;
this.update_content = this.update_content.bind(this);
this.width = this.determine_width($('.js-graphs-show').width(), $parentElement);
this.height = 200;
this.x = null;
this.y = null;
this.x_axis = null;
this.y_axis = null;
this.area = null;
this.svg = null;
this.brush = null;
this.x_max_domain = null;
}
ContributorsMasterGraph.prototype.process_dates = function(data) {
const dates = this.get_dates(data);
this.parse_dates(data);
return ContributorsGraph.set_dates(dates);
};
ContributorsMasterGraph.prototype.get_dates = function(data) {
return _.pluck(data, 'date');
};
ContributorsMasterGraph.prototype.parse_dates = function(data) {
const parseDate = d3.timeParse('%Y-%m-%d');
return data.forEach(d => (d.date = parseDate(d.date)));
};
ContributorsMasterGraph.prototype.create_scale = function() {
return ContributorsMasterGraph.__super__.create_scale.call(this, this.width, this.height);
};
ContributorsMasterGraph.prototype.create_axes = function() {
this.x_axis = d3
.axisBottom()
.scale(this.x)
.tickFormat(dateTickFormat);
return (this.y_axis = d3
.axisLeft()
.scale(this.y)
.ticks(5));
};
ContributorsMasterGraph.prototype.create_svg = function() {
this.svg = d3
.select('#contributors-master')
.append('svg')
.attr('width', this.width + this.MARGIN.left + this.MARGIN.right)
.attr('height', this.height + this.MARGIN.top + this.MARGIN.bottom)
.attr('class', 'tint-box')
.append('g')
.attr('transform', `translate(${this.MARGIN.left},${this.MARGIN.top})`);
return this.svg;
};
ContributorsMasterGraph.prototype.create_area = function(x, y) {
return (this.area = d3
.area()
.x(d => x(d.date))
.y0(this.height)
.y1(d => {
d.commits = d.commits || d.additions || d.deletions;
return y(d.commits);
}));
};
ContributorsMasterGraph.prototype.create_brush = function() {
return (this.brush = d3
.brushX(this.x)
.extent([[this.x.range()[0], 0], [this.x.range()[1], this.height]])
.on('end', this.update_content));
};
ContributorsMasterGraph.prototype.draw_path = function(data) {
return this.svg
.append('path')
.datum(data)
.attr('class', 'area')
.attr('d', this.area);
};
ContributorsMasterGraph.prototype.add_brush = function() {
return this.svg
.append('g')
.attr('class', 'selection')
.call(this.brush)
.selectAll('rect')
.attr('height', this.height);
};
ContributorsMasterGraph.prototype.update_content = function() {
// d3Event.selection replaces the function brush.empty() calls
if (d3Event.selection != null) {
ContributorsGraph.set_x_domain(d3Event.selection.map(this.x.invert));
} else {
ContributorsGraph.set_x_domain(this.x_max_domain);
}
return $('#brush_change').trigger('change');
};
ContributorsMasterGraph.prototype.draw = function() {
this.process_dates(this.data);
this.create_scale();
this.create_axes();
ContributorsGraph.init_domain(this.data);
this.x_max_domain = this.x_domain;
this.set_domain();
this.create_area(this.x, this.y);
this.create_svg();
this.create_brush();
this.draw_path(this.data);
this.draw_x_axis();
this.draw_y_axis();
return this.add_brush();
};
ContributorsMasterGraph.prototype.redraw = function() {
this.process_dates(this.data);
ContributorsGraph.set_y_domain(this.data);
this.set_y_domain();
this.svg.select('path').datum(this.data);
this.svg.select('path').attr('d', this.area);
return this.svg.select('.y.axis').call(this.y_axis);
};
return ContributorsMasterGraph;
})(ContributorsGraph);
export const ContributorsAuthorGraph = (function(superClass) {
extend(ContributorsAuthorGraph, superClass);
function ContributorsAuthorGraph(data1) {
const $parentElements = $('.person');
this.data = data1;
// Don't split graph size in half for mobile devices.
if ($(window).width() < 790) {
this.width = this.determine_width($('.js-graphs-show').width(), $parentElements);
} else {
this.width = this.determine_width($('.js-graphs-show').width() / 2, $parentElements);
}
this.height = 200;
this.x = null;
this.y = null;
this.x_axis = null;
this.y_axis = null;
this.area = null;
this.svg = null;
this.list_item = null;
}
ContributorsAuthorGraph.prototype.create_scale = function() {
return ContributorsAuthorGraph.__super__.create_scale.call(this, this.width, this.height);
};
ContributorsAuthorGraph.prototype.create_axes = function() {
this.x_axis = d3
.axisBottom()
.scale(this.x)
.ticks(8)
.tickFormat(dateTickFormat);
return (this.y_axis = d3
.axisLeft()
.scale(this.y)
.ticks(5));
};
ContributorsAuthorGraph.prototype.create_area = function(x, y) {
return (this.area = d3
.area()
.x(d => {
const parseDate = d3.timeParse('%Y-%m-%d');
return x(parseDate(d));
})
.y0(this.height)
.y1(
(function(_this) {
return function(d) {
if (_this.data[d] != null) {
return y(_this.data[d]);
} else {
return y(0);
}
};
})(this),
));
};
ContributorsAuthorGraph.prototype.create_svg = function() {
const persons = document.querySelectorAll('.person');
this.list_item = persons[persons.length - 1];
this.svg = d3
.select(this.list_item)
.append('svg')
.attr('width', this.width + this.MARGIN.left + this.MARGIN.right)
.attr('height', this.height + this.MARGIN.top + this.MARGIN.bottom)
.attr('class', 'spark')
.append('g')
.attr('transform', `translate(${this.MARGIN.left},${this.MARGIN.top})`);
return this.svg;
};
ContributorsAuthorGraph.prototype.draw_path = function(data) {
return this.svg
.append('path')
.datum(data)
.attr('class', 'area-contributor')
.attr('d', this.area);
};
ContributorsAuthorGraph.prototype.draw = function() {
this.create_scale();
this.create_axes();
this.set_domain();
this.create_area(this.x, this.y);
this.create_svg();
this.draw_path(this.dates);
this.draw_x_axis();
return this.draw_y_axis();
};
ContributorsAuthorGraph.prototype.redraw = function() {
this.set_domain();
this.svg.select('path').datum(this.dates);
this.svg.select('path').attr('d', this.area);
this.svg.select('.x.axis').call(this.x_axis);
return this.svg.select('.y.axis').call(this.y_axis);
};
return ContributorsAuthorGraph;
})(ContributorsGraph);
/* eslint-disable func-names, no-var, one-var, camelcase, no-param-reassign, no-return-assign, consistent-return, no-cond-assign, no-else-return */
import _ from 'underscore';
export default {
parse_log(log) {
var by_author, by_email, data, entry, i, len, total, normalized_email;
total = {};
by_author = {};
by_email = {};
for (i = 0, len = log.length; i < len; i += 1) {
entry = log[i];
if (total[entry.date] == null) {
this.add_date(entry.date, total);
}
normalized_email = entry.author_email.toLowerCase();
data = by_author[entry.author_name] || by_email[normalized_email];
if (data == null) {
data = this.add_author(entry, by_author, by_email);
}
if (!data[entry.date]) {
this.add_date(entry.date, data);
}
this.store_data(entry, total[entry.date], data[entry.date]);
}
total = _.toArray(total);
by_author = _.toArray(by_author);
return {
total,
by_author,
};
},
add_date(date, collection) {
collection[date] = {};
return (collection[date].date = date);
},
add_author(author, by_author, by_email) {
var data, normalized_email;
data = {};
data.author_name = author.author_name;
data.author_email = author.author_email;
normalized_email = author.author_email.toLowerCase();
by_author[author.author_name] = data;
by_email[normalized_email] = data;
return data;
},
store_data(entry, total, by_author) {
this.store_commits(total, by_author);
this.store_additions(entry, total, by_author);
return this.store_deletions(entry, total, by_author);
},
store_commits(total, by_author) {
this.add(total, 'commits', 1);
return this.add(by_author, 'commits', 1);
},
add(collection, field, value) {
if (collection[field] == null) {
collection[field] = 0;
}
return (collection[field] += value);
},
store_additions(entry, total, by_author) {
if (entry.additions == null) {
entry.additions = 0;
}
this.add(total, 'additions', entry.additions);
return this.add(by_author, 'additions', entry.additions);
},
store_deletions(entry, total, by_author) {
if (entry.deletions == null) {
entry.deletions = 0;
}
this.add(total, 'deletions', entry.deletions);
return this.add(by_author, 'deletions', entry.deletions);
},
get_total_data(parsed_log, field) {
var log, total_data;
log = parsed_log.total;
total_data = this.pick_field(log, field);
return _.sortBy(total_data, d => d.date);
},
pick_field(log, field) {
var total_data;
total_data = [];
_.each(log, d => total_data.push(_.pick(d, [field, 'date'])));
return total_data;
},
get_author_data(parsed_log, field, date_range) {
var author_data, log;
if (date_range == null) {
date_range = null;
}
log = parsed_log.by_author;
author_data = [];
_.each(
log,
(function(_this) {
return function(log_entry) {
var parsed_log_entry;
parsed_log_entry = _this.parse_log_entry(log_entry, field, date_range);
if (!_.isEmpty(parsed_log_entry.dates)) {
return author_data.push(parsed_log_entry);
}
};
})(this),
);
return _.sortBy(author_data, d => d[field]).reverse();
},
parse_log_entry(log_entry, field, date_range) {
var parsed_entry;
parsed_entry = {};
parsed_entry.author_name = log_entry.author_name;
parsed_entry.author_email = log_entry.author_email;
parsed_entry.dates = {};
parsed_entry.commits = 0;
parsed_entry.additions = 0;
parsed_entry.deletions = 0;
_.each(
_.omit(log_entry, 'author_name', 'author_email'),
(function(_this) {
return function(value) {
if (_this.in_range(value.date, date_range)) {
parsed_entry.dates[value.date] = value[field];
parsed_entry.commits += value.commits;
parsed_entry.additions += value.additions;
return (parsed_entry.deletions += value.deletions);
}
};
})(this),
);
return parsed_entry;
},
in_range(date, date_range) {
var ref;
if (date_range === null || (date_range[0] <= (ref = new Date(date)) && ref <= date_range[1])) {
return true;
} else {
return false;
}
},
};
Loading
Loading
@@ -345,8 +345,8 @@ export default {
<project-setting-row
v-if="pagesAvailable && pagesAccessControlEnabled"
:help-path="pagesHelpPath"
label="Pages access control"
help-text="Access control for the project's static website"
:label="s__('ProjectSettings|Pages')"
:help-text="__('With GitLab Pages you can host your static websites on GitLab')"
>
<project-feature-setting
v-model="pagesAccessLevel"
Loading
Loading
Loading
Loading
@@ -17,21 +17,6 @@
}
}
 
.graphs {
.graph-author-email {
float: right;
color: $gl-gray-500;
}
.graph-additions {
color: $green-600;
}
.graph-deletions {
color: $red-500;
}
}
.svg-graph-container {
width: 100%;
 
Loading
Loading
.tint-box {
background: $stat-graph-common-bg;
position: relative;
margin-bottom: 10px;
}
.area {
fill: $green-500;
fill-opacity: 0.5;
}
.axis {
font-size: 10px;
}
#contributors-master {
@include media-breakpoint-up(md) {
@include make-col-ready();
@include make-col(12);
}
}
#contributors {
flex: 1;
.contributors-list {
margin: 0 0 10px;
list-style: none;
padding: 0;
}
.person {
@include media-breakpoint-up(md) {
@include make-col-ready();
@include make-col(6);
}
margin-top: 10px;
@include media-breakpoint-down(xs) {
width: 100%;
}
.spark {
display: block;
background: $stat-graph-common-bg;
width: 100%;
}
.area-contributor {
fill: $orange-500;
}
}
}
.selection rect {
fill-opacity: 0.1;
stroke-width: 1px;
stroke-opacity: 0.4;
shape-rendering: crispedges;
stroke-dasharray: 3 3;
}
Loading
Loading
@@ -281,10 +281,7 @@ module IssuablesHelper
}
 
data[:hasClosingMergeRequest] = issuable.merge_requests_count(current_user) != 0 if issuable.is_a?(Issue)
zoom_links = Gitlab::ZoomLinkExtractor.new(issuable.description).links
data[:zoomMeetingUrl] = zoom_links.last if zoom_links.any?
data[:zoomMeetingUrl] = ZoomMeeting.canonical_meeting_url(issuable) if issuable.is_a?(Issue)
 
if parent.is_a?(Group)
data[:groupPath] = parent.path
Loading
Loading
Loading
Loading
@@ -273,7 +273,7 @@ module SearchHelper
sanitize(html, tags: %w(a p ol ul li pre code))
end
 
def search_tabs?(tab)
def show_user_search_tab?
return false if Feature.disabled?(:users_search, default_enabled: true)
 
if @project
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment