Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#1899] Add graphic for commit diffstat #2010

Merged
merged 32 commits into from
Jul 28, 2023
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
2963a8f
Add diffstat component
nseah21 Jun 23, 2023
cc4c913
Update zoom view to use diffstat graphic
nseah21 Jun 23, 2023
eef1ea0
Modify threshold in c-diffstat
nseah21 Jun 24, 2023
b7d47ea
Rename c-diffstat to c-histogram
nseah21 Jun 25, 2023
168dac9
Add contribution bar for commit diffstat
nseah21 Jun 25, 2023
e78c45e
Add c-stacked-bar-chart component for summary charts
nseah21 Jun 29, 2023
02a87a6
Remove references to old diffstat in zoom view
nseah21 Jun 29, 2023
7783db8
Add support for rendering diffstat
nseah21 Jun 29, 2023
4bcd5a8
Add diffstat component to zoom view
nseah21 Jun 29, 2023
2cc49e7
Use average contribution for diffstat calculations
nseah21 Jun 29, 2023
56ff61e
Add support for toggling diffstat view
nseah21 Jun 29, 2023
afe8c4c
Remove unused components
nseah21 Jun 29, 2023
d0fe7b0
Remove old calculation for average contribution
nseah21 Jun 29, 2023
9e3e48b
Refactor c-stacked-bar-chart component
nseah21 Jul 3, 2023
b53af8b
Use latest stacked bar chart for diffstat
nseah21 Jul 3, 2023
b8d718e
Merge branch 'refactor-diffstat-graphic' into add-diffstat-graphic
nseah21 Jul 3, 2023
8b8a1ff
Add fix for overflowing contribution bars
nseah21 Jul 4, 2023
5168a60
Refactor stacked-bar-chart to use new selectors
nseah21 Jul 10, 2023
9d063b5
Refactor bar generation methods
nseah21 Jul 10, 2023
2f5acbb
Fix broken frontend tests
nseah21 Jul 10, 2023
70a69f4
Add initial tests for stacked-bar-chart component
nseah21 Jul 10, 2023
06d2f55
Revert changes to chartView_mergeCommits.cy.js
nseah21 Jul 11, 2023
8b592e3
Fix bug in bar generation methods
nseah21 Jul 11, 2023
eb9b0d6
Add target attribute for lines changed
nseah21 Jul 11, 2023
2698ade
Add more tests for diffstat component
nseah21 Jul 11, 2023
40962d2
Fix CI issues
nseah21 Jul 11, 2023
e6f2cb8
Fix environmental checks
nseah21 Jul 14, 2023
2860caa
Merge branch 'master' into add-diffstat-graphic
ckcherry23 Jul 15, 2023
59ce334
Fix linting checks
nseah21 Jul 15, 2023
2366ad1
Merge branch 'add-diffstat-graphic' of github.com:nseah21/RepoSense i…
nseah21 Jul 15, 2023
0ae39bd
Remove old implementation for bar generation
nseah21 Jul 15, 2023
70183cf
Add comments for tests in zoomView_diffstat.cy.js
nseah21 Jul 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
describe('contribution bar', () => {
it('same length when breakdown selected', () => {
let expectedWidthSum = 0;
cy.get('.summary-chart__contrib--bar').then((ele) => {
cy.get('.stacked-bar__contrib--bar').then((ele) => {
let i;
for (i = 0; i < ele.length; i += 1) {
expectedWidthSum += parseFloat(ele[i].style.width.split('%')[0]);
Expand All @@ -12,7 +12,7 @@ describe('contribution bar', () => {
.check();

let actualWidthSum = 0;
cy.get('.summary-chart__contrib--bar').then((ele) => {
cy.get('.stacked-bar__contrib--bar').then((ele) => {
let i;
for (i = 0; i < ele.length; i += 1) {
actualWidthSum += parseFloat(ele[i].style.width.split('%')[0]);
Expand All @@ -35,7 +35,7 @@ describe('contribution bar', () => {
let expectedWidthSum = 0;
cy.get('#summary-wrapper label').contains('breakdown by file type').siblings().filter('input')
.check();
cy.get('.summary-chart__contrib--bar').then((ele) => {
cy.get('.stacked-bar__contrib--bar').then((ele) => {
expectedWidthSum += parseFloat(ele[0].style.width.split('%')[0]);
});

Expand All @@ -46,7 +46,7 @@ describe('contribution bar', () => {
.check();

let actualWidthSum = 0;
cy.get('.summary-chart__contrib--bar').then((ele) => {
cy.get('.stacked-bar__contrib--bar').then((ele) => {
let i;
for (i = 0; i < ele.length; i += 1) {
actualWidthSum += parseFloat(ele[i].style.width.split('%')[0]);
Expand Down
6 changes: 3 additions & 3 deletions frontend/cypress/tests/chartView/chartView_mergeGroup.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ describe('merge group', () => {
.should('be.checked');

// get the three chart bars and assert they have the correct initial widths
cy.get('.summary-chart__contrib--bar')
cy.get('.stacked-bar__contrib--bar')
.should('have.length', 3)
.then(($bars) => {
// calculate the percentage of the width relative to the parent container
Expand All @@ -88,7 +88,7 @@ describe('merge group', () => {
const initialWidths = [];

// Store the initial widths of the contribution bars
cy.get('.summary-chart__contrib--bar')
cy.get('.stacked-bar__contrib--bar')
.each(($bar) => {
const width = window.getComputedStyle($bar[0]).width;
initialWidths.push(width);
Expand All @@ -99,7 +99,7 @@ describe('merge group', () => {
cy.get('.overlay-loader').should('not.be.visible');

// Get the contribution bars again and compare their widths with the initial widths
cy.get('.summary-chart__contrib--bar')
cy.get('.stacked-bar__contrib--bar')
.should('have.length', initialWidths.length)
.each(($bar, index) => {
const width = window.getComputedStyle($bar[0]).width;
Expand Down
89 changes: 89 additions & 0 deletions frontend/cypress/tests/zoomView/zoomView_diffstat.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
describe('diffstat', () => {
it('should render container for contribution bars', () => {
cy.get('.icon-button.fa-list-ul')
.should('be.visible')
.first()
.click();

cy.get('#tab-zoom .commit-message .stacked-bar-container')
.should('be.visible');
});

it('should render non-empty contribution bars for commits with changes', () => {
cy.get('.icon-button.fa-list-ul')
.should('be.visible')
.first()
.click();

cy.get('#tab-zoom .commit-message')
.first()
.within(() => {
cy.get('.stacked-bar__contrib--bar')
.then((element) => {
expect(element.length).to.be.equal(2);
expect(element[0].style['background-color']).to.be.equal('limegreen');
expect(element[0].style.width).to.be.equal('0.1%');
expect(element[1].style['background-color']).to.be.equal('red');
expect(element[1].style.width).to.be.equal('0.1%');
vvidday marked this conversation as resolved.
Show resolved Hide resolved
});
});
});

it('should render empty contribution bars for commits with no changes', () => {
cy.get('.icon-button.fa-list-ul')
.should('be.visible')
.first()
.click();

cy.get('#tab-zoom .commit-message')
.eq(1)
vvidday marked this conversation as resolved.
Show resolved Hide resolved
.within(() => {
cy.get('.stacked-bar__contrib--bar')
.then((element) => {
expect(element.length).to.be.equal(2);
expect(element[0].style['background-color']).to.be.equal('limegreen');
expect(element[0].style.width).to.be.equal('0%');
expect(element[1].style['background-color']).to.be.equal('red');
expect(element[1].style.width).to.be.equal('0%');
});
});
});

it('should render contribution bars in proportion', () => {
cy.get('.icon-button.fa-list-ul')
.should('be.visible')
.first()
.click();

let insertionWidthSum = 0;
let deletionWidthSum = 0;
let widthProportion = 0;
cy.get('#tab-zoom .commit-message .stacked-bar__contrib--bar')
.then((element) => {
for (let i = 0; i < element.length; i += 1) {
const val = parseFloat(element[i].style.width.split('%')[0]);
if (element[i].style['background-color'] === 'limegreen') {
insertionWidthSum += val;
} else {
deletionWidthSum += val;
}
}
widthProportion = insertionWidthSum / deletionWidthSum;
});

let insertions = 0;
let deletions = 0;
let actualProportion = 0;
cy.get('[data-cy="changes"]')
.invoke('text')
.then((text) => {
const temp = text.split('lines');
for (let i = 0; i < temp.length - 1; i += 1) {
insertions += parseFloat(temp[i].split('-')[0].split('+')[1].trim());
deletions += parseFloat(temp[i].split('-')[1].trim());
}
actualProportion = insertions / deletions;
expect(widthProportion.toFixed(3)).to.be.equal(actualProportion.toFixed(3));
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ describe('hide all commit messages ', () => {
.should('have.length', 1);

cy.get('#tab-zoom .toolbar--multiline > a')
.should('have.text', 'hide all commit messages');
.should('have.text', 'hide all commit details');
});

it('should only display show all commit messages when all are hidden', () => {
Expand All @@ -93,7 +93,7 @@ describe('hide all commit messages ', () => {
.should('have.length', 1);

cy.get('#tab-zoom .toolbar--multiline > a')
.should('have.text', 'show all commit messages');
.should('have.text', 'show all commit details');
});

it('should display both show and hide all commit messages when some are hidden', () => {
Expand All @@ -119,11 +119,11 @@ describe('hide all commit messages ', () => {

cy.get('#tab-zoom .toolbar--multiline > a')
.eq(0)
.should('have.text', 'show all commit messages');
.should('have.text', 'show all commit details');

cy.get('#tab-zoom .toolbar--multiline > a')
.eq(1)
.should('have.text', 'hide all commit messages');
.should('have.text', 'hide all commit details');
});

it('check show all and hide all commit messages only toggle current commits', () => {
Expand Down Expand Up @@ -152,7 +152,7 @@ describe('hide all commit messages ', () => {
.should('have.length', 1);

cy.get('#tab-zoom .toolbar--multiline > a')
.should('have.text', 'show all commit messages');
.should('have.text', 'show all commit details');

// check java file type
cy.get('#tab-zoom .fileTypes input[value="java"]')
Expand Down Expand Up @@ -182,11 +182,11 @@ describe('hide all commit messages ', () => {

cy.get('#tab-zoom .toolbar--multiline > a')
.eq(0)
.should('have.text', 'show all commit messages');
.should('have.text', 'show all commit details');

cy.get('#tab-zoom .toolbar--multiline > a')
.eq(1)
.should('have.text', 'hide all commit messages');
.should('have.text', 'hide all commit details');
});

it('check hidden commit message persists after sort', () => {
Expand Down
39 changes: 39 additions & 0 deletions frontend/src/components/c-stacked-bar-chart.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<template lang="pug">
.stacked-bar-container
.stacked-bar__contrib--bar(
v-for="bar in bars",
v-bind:title="bar.tooltipText",
v-bind:style="{ width: `${bar.width}%`,\
'background-color': bar.color }"
)
</template>

<script lang="ts">
import { PropType, defineComponent } from 'vue';
import { Bar } from '../types/types';

export default defineComponent({
props: {
bars: {
type: Array as PropType<Bar[]>,
required: true,
},
},
});
</script>

<style scoped lang="scss">
@import '../styles/_colors.scss';

.stacked-bar-container {
display: flex;
flex-wrap: wrap;
}

.stacked-bar__contrib--bar {
background-color: mui-color('blue');
height: 4px;
margin-top: 2px;
}

</style>
63 changes: 42 additions & 21 deletions frontend/src/components/c-summary-charts.vue
Original file line number Diff line number Diff line change
Expand Up @@ -220,25 +220,17 @@

.summary-chart__contribution
template(v-if="filterBreakdown")
.summary-chart__contrib(
v-for="(widths, fileType) in getFileTypeContributionBars(user.fileTypeContribution)"
)
.summary-chart__contrib--bar(
v-for="width in widths",
v-bind:style="{ width: `${width}%`,\
'background-color': fileTypeColors[fileType] }",
v-bind:title="`${fileType}: ${user.fileTypeContribution[fileType]} lines, \
total: ${user.checkedFileTypeContribution} lines (contribution from ${minDate} to \
${maxDate})`"
.summary-chart__contrib
c-stacked-bar-chart(
v-bind:bars="getFileTypeContributionBars(user.fileTypeContribution, user.checkedFileTypeContribution)"
)
template(v-else)
.summary-chart__contrib(
v-bind:title="`Total contribution from ${minDate} to ${maxDate}: \
${user.checkedFileTypeContribution} lines`"
)
.summary-chart__contrib--bar(
v-for="width in getContributionBars(user.checkedFileTypeContribution)",
v-bind:style="{ width: `${width}%` }"
c-stacked-bar-chart(
v-bind:bars="getContributionBars(user.checkedFileTypeContribution)"
)
</template>

Expand All @@ -248,7 +240,8 @@

import brokenLinkDisabler from '../mixin/brokenLinkMixin';
import cRamp from './c-ramp.vue';
import { Repo, User } from '../types/types';
import cStackedBarChart from './c-stacked-bar-chart.vue';
import { Bar, Repo, User } from '../types/types';
import { FilterGroupSelection, FilterTimeFrame, SortGroupSelection } from '../types/summary';
import { StoreState, ZoomInfo } from '../types/vuex.d';
import { AuthorFileTypeContributions } from '../types/zod/commits-type';
Expand All @@ -257,6 +250,7 @@
name: 'c-summary-charts',
components: {
cRamp,
cStackedBarChart,
},
mixins: [brokenLinkDisabler],
props: {
Expand Down Expand Up @@ -376,12 +370,15 @@
this.retrieveSelectedTabHash();
},
methods: {
getFileTypeContributionBars(fileTypeContribution: AuthorFileTypeContributions): { [key: string]: number[] } {
getFileTypeContributionBars(
fileTypeContribution: AuthorFileTypeContributions,
checkedFileTypeContribution: number | undefined,
): Bar[] {
let currentBarWidth = 0;
const fullBarWidth = 100;
const contributionPerFullBar = (this.avgContributionSize * 2);

const allFileTypesContributionBars: { [key: string]: number[] } = {};
const allFileTypesContributionBars: Bar[] = [];
if (contributionPerFullBar === 0) {
return allFileTypesContributionBars;
}
Expand Down Expand Up @@ -413,7 +410,15 @@
currentBarWidth = remainingBarWidth;
}

allFileTypesContributionBars[fileType] = contributionBars;
contributionBars.forEach((width) => {
allFileTypesContributionBars.push({
width,
color: this.fileTypeColors[fileType],
tooltipText: `${fileType}: ${fileTypeContribution[fileType]} lines, \
total: ${checkedFileTypeContribution} lines (contribution from ${this.minDate} to
${this.maxDate})`,
});
});
});

return allFileTypesContributionBars;
Expand All @@ -435,20 +440,20 @@
return group.reduce((acc, ele) => acc + (ele.checkedFileTypeContribution ?? 0), 0);
},

getContributionBars(totalContribution: number): number[] {
const res: number[] = [];
getContributionBars(totalContribution: number): Bar[] {
const res: Bar[] = [];
const contributionLimit = (this.avgContributionSize * 2);
if (contributionLimit === 0) {
return res;
}
const cnt = Math.floor(totalContribution / contributionLimit);
for (let cntId = 0; cntId < cnt; cntId += 1) {
res.push(100);
res.push({ width: 100 });
}

const last = (totalContribution % contributionLimit) / contributionLimit;
if (last !== 0) {
res.push(last * 100);
res.push({ width: last * 100 });
}

return res;
Expand Down Expand Up @@ -559,6 +564,7 @@
zFileTypeColors: this.fileTypeColors,
zFromRamp: false,
zFilterSearch: filterSearch,
zAvgContributionSize: this.getAvgContributionSize(),
};
this.addSelectedTab(user.name, user.repoName, 'zoom', isMerged);
this.$store.commit('updateTabZoomInfo', info);
Expand Down Expand Up @@ -627,7 +633,7 @@
getBaseTarget(target: HTMLElement | null): HTMLElement | null {
if (!target) {
// Should never reach here - function assumes that target is a child of the div with class 'summary-chart__ramp'
console.error('Error: The getBaseTarget function in c-summary-charts.vue has been called on an element that is '

Check warning on line 636 in frontend/src/components/c-summary-charts.vue

View workflow job for this annotation

GitHub Actions / Cypress frontend tests

Unexpected console statement

Check warning on line 636 in frontend/src/components/c-summary-charts.vue

View workflow job for this annotation

GitHub Actions / ubuntu-20.04 JDK 8

Unexpected console statement

Check warning on line 636 in frontend/src/components/c-summary-charts.vue

View workflow job for this annotation

GitHub Actions / ubuntu-20.04 JDK 8

Unexpected console statement

Check warning on line 636 in frontend/src/components/c-summary-charts.vue

View workflow job for this annotation

GitHub Actions / ubuntu-22.04 JDK 8

Unexpected console statement

Check warning on line 636 in frontend/src/components/c-summary-charts.vue

View workflow job for this annotation

GitHub Actions / macos-11 JDK 8

Unexpected console statement

Check warning on line 636 in frontend/src/components/c-summary-charts.vue

View workflow job for this annotation

GitHub Actions / macos-12 JDK 8

Unexpected console statement

Check warning on line 636 in frontend/src/components/c-summary-charts.vue

View workflow job for this annotation

GitHub Actions / windows-2019 JDK 8

Unexpected console statement

Check warning on line 636 in frontend/src/components/c-summary-charts.vue

View workflow job for this annotation

GitHub Actions / windows-2022 JDK 8

Unexpected console statement
+ 'not a child of the div with class summary-chart__ramp. This might affect the drag view functionality.');
return null;
}
Expand Down Expand Up @@ -794,6 +800,21 @@
return explanation;
},

getAvgContributionSize(): number {
let totalContribution = 0;
let totalCommits = 0;

this.filteredRepos.forEach((repo) => {
repo.forEach((user) => {
user.commits?.forEach((commit) => {
totalCommits += 1;
totalContribution += (commit.insertions + commit.deletions);
});
});
});

return totalContribution / totalCommits;
},
},
});
</script>
Loading