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

Add optimise timeline feature #2180

Merged
merged 49 commits into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
ec60570
add optimise timeline feature
jonasongg Apr 1, 2024
b814317
change v if for date-indicators
jonasongg Apr 1, 2024
8e943b7
add null checks for optimised date
jonasongg Apr 1, 2024
7c6ed99
add tests for optimise timeline feature
jonasongg Apr 6, 2024
f6d754c
fix test failing on github
jonasongg Apr 8, 2024
e1d9a06
make date indicators grey
jonasongg Apr 8, 2024
8b5fc11
Merge remote-tracking branch 'upstream/master' into add-optimise-time…
jonasongg Apr 8, 2024
755fde6
reduce complexity of total calculation for optimise timeline
jonasongg Apr 8, 2024
b1b64ff
add change url when optimise timeline is checked
jonasongg Apr 15, 2024
50a0ce9
fix lint
jonasongg Apr 15, 2024
fe97bf4
Merge remote-tracking branch 'upstream/master' into add-optimise-time…
jonasongg Apr 15, 2024
30dbef1
add optimiseTimeline to data return type
jonasongg Apr 15, 2024
0d4f5bb
Merge branch 'master' into add-optimise-timeline
jonasongg Apr 15, 2024
b7cdf85
fix data return type for c-ramp
jonasongg Apr 15, 2024
e47e8b0
Merge branch 'master' into add-optimise-timeline
jonasongg Apr 18, 2024
f3c19d4
Merge branch 'master' into add-optimise-timeline
ckcherry23 Apr 20, 2024
15c6bfa
fix optimise timeline when zoom subrange
jonasongg Apr 20, 2024
cb7faef
fix typo
jonasongg Apr 20, 2024
67acd87
fix optimised timeline not showing on zoom panel
jonasongg Apr 20, 2024
0fa2043
simplify logic
jonasongg Apr 20, 2024
14adcad
remove whitespace
jonasongg Apr 20, 2024
0d158b6
Merge branch 'master' into add-optimise-timeline
jonasongg Apr 24, 2024
c96ea51
fix zoom chart not working with optimise timeline
jonasongg Apr 25, 2024
90345e6
Merge remote-tracking branch 'refs/remotes/origin/add-optimise-timeli…
jonasongg Apr 25, 2024
500d8c9
refactor getSlicePos function
jonasongg Apr 25, 2024
6f429c9
fix getCommitPos for optimise timeline
jonasongg Apr 25, 2024
850a4d9
fix lint
jonasongg Apr 25, 2024
2f39383
fix lint again
jonasongg Apr 25, 2024
c080688
reduce code dupe in openTabZoomSubrange
jonasongg Apr 25, 2024
595883f
update ug for trim timeline
jonasongg Apr 25, 2024
4f8de1f
fix zoom subrange optimise timeline
jonasongg Apr 25, 2024
3b65d51
fix failing cypress
jonasongg Apr 25, 2024
5c8411c
fix cypress
jonasongg Apr 25, 2024
27d62ae
remove unnecessary prop passing
jonasongg Apr 25, 2024
6ab4714
fix tab zoom for optimise timeline
jonasongg Apr 26, 2024
4fe7575
refactor optimise min max date type
jonasongg Apr 26, 2024
45a2a6d
fix lint
jonasongg Apr 26, 2024
dacab6a
fix double braces
jonasongg Apr 27, 2024
819ab1d
fix optimisetimeline cypress test
jonasongg Apr 27, 2024
dbfcadb
Merge remote-tracking branch 'upstream/master' into add-optimise-time…
jonasongg Apr 30, 2024
cf282bd
Merge remote-tracking branch 'refs/remotes/upstream/add-optimise-time…
jonasongg Apr 30, 2024
cc52381
fix lint
jonasongg Apr 30, 2024
4fe8177
remove whitespace
jonasongg Apr 30, 2024
62a7854
add cypress for zoom and optimise timeline
jonasongg Apr 30, 2024
6324e9c
fix cypress
jonasongg Apr 30, 2024
d5125c3
fix lint
jonasongg Apr 30, 2024
77a3792
uncomment cypress test
jonasongg Apr 30, 2024
a97aa60
add comments
jonasongg May 1, 2024
faad1cc
add comments
ckcherry23 May 6, 2024
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
1 change: 1 addition & 0 deletions docs/ug/usingReports.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ The `Tool Bar` at the top of the Chart panel provides a set of configuration opt
* `Merge group`: merges all the ramp charts of each group into a single ramp chart; aggregates the contribution of each group.
* viewing of authored code of the group as a whole is available when `group by repos`.
* `Show tags`: shows the tags of all the repos under a group
* `Trim timeline`: trims the starting and ending portion of each ramp where no commits were made; only the part of each ramp where commits were made will be shown.

Notes:<br>
[1] **`Sort groups by`**: each main group has its own index and percentile according to its ranking position after sorting (e.g., if the groups are sorted by contribution in descending order, a 25% percentile indicates that the group is in the top 25% of the whole cohort in terms of contribution)<br>.
Expand Down
148 changes: 148 additions & 0 deletions frontend/cypress/tests/chartView/chartView_optimiseTimeline.cy.js
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a Cypress test for whether the optimised timeline feature works with the chart zoom feature? This can be done in chartView_zoomFeature.cy.js.

Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
describe('optimise timeline', () => {
it('ramp padding should only exist when optimise timeline is checked', () => {
cy.get('#summary label.optimise-timeline > input:visible')
.should('be.visible')
.uncheck()
.should('be.not.checked');

cy.get('#summary-charts .summary-chart')
.first()
.find('.summary-chart__ramp .ramp .ramp-padding')
.should('have.css', 'left', '0px');

cy.get('#summary label.optimise-timeline > input:visible')
.should('be.visible')
.check()
.should('be.checked');

cy.get('#summary-charts .summary-chart')
.first()
.find('.summary-chart__ramp .ramp .ramp-padding')
.should('have.not.css', 'left', '0px');
});

it('should retain the same number of ramp slices', () => {
cy.get('#summary-charts .summary-chart')
.first()
.find('.summary-chart__ramp .ramp .ramp-padding a')
.then(($el) => {
const rampSlices = $el.length;

cy.get('#summary label.optimise-timeline > input:visible')
.should('be.visible')
.check()
.should('be.checked');

cy.get('#summary-charts .summary-chart')
.first()
.find('.summary-chart__ramp .ramp .ramp-padding a')
.should('have.length', rampSlices);
});
});

it('start and end date indicators should exist', () => {
cy.get('#summary label.optimise-timeline > input:visible')
.should('be.visible')
.check()
.should('be.checked');

cy.get('#summary-charts .summary-chart')
.first()
.find('.summary-chart__ramp .date-indicators span')
.first()
.should('have.text', '2018-05-03');

cy.get('#summary-charts .summary-chart')
.first()
.find('.summary-chart__ramp .date-indicators span')
.last()

// 3/3 on GitHub CI, 3/4 on local
.should('have.text', '2023-03-03');
});

it('no commits in range should not have date indicators', () => {
cy.get('#summary label.optimise-timeline > input:visible')
.should('be.visible')
.check()
.should('be.checked');

// change since date
cy.get('input[name="since"]')
.type('2018-12-31');

// change until date
cy.get('input[name="until"]')
.type('2019-01-01');

cy.get('#summary-charts .summary-chart')
.first()
.find('.summary-chart__ramp .date-indicators')
.should('not.exist');
});

it('zoom panel range should work correctly when timeline is optimised', () => {
cy.get('.icon-button.fa-list-ul')
.should('exist')
.first()
.click();

cy.get('#tab-zoom')
.should('be.visible');

cy.get('#tab-zoom .ramp a')
.first()
.invoke('css', 'right')
.then((val) => parseFloat(val))
.should('gt', 0);
jonasongg marked this conversation as resolved.
Show resolved Hide resolved

cy.get('#summary label.optimise-timeline > input')
.check()
.should('be.checked');

cy.get('.icon-button.fa-list-ul')
.should('exist')
.first()
.click();

cy.get('#tab-zoom')
.should('be.visible');

cy.get('#tab-zoom .period')
.should('contain', '2018-05-03 to 2023-03-03');

cy.get('#tab-zoom .ramp a')
.first()
.invoke('css', 'right')
.then((val) => parseFloat(val))
.should('lt', 1);
});

it('subzoom panel range should work correctly when timeline is optimised', () => {
const zoomKey = Cypress.platform === 'darwin' ? '{meta}' : '{ctrl}';

cy.get('#summary label.optimise-timeline > input:visible')
.should('be.visible')
.check()
.should('be.checked');

// clicking from the 10th px to the 50th px in the ramp
cy.get('body').type(zoomKey, { release: false })
.get('#summary-charts .summary-chart__ramp .ramp')
.first()
.click(110, 20)
.click(120, 20);

cy.get('#tab-zoom')
.should('be.visible');

cy.get('#tab-zoom .ramp .ramp__slice')
.should('have.length', 1);

cy.get('#tab-zoom .ramp .ramp__slice')
.invoke('attr', 'title')
.then((title) => {
cy.wrap(title).should('eq', '[2019-08-18] AboutUs: update team members (#867): +94 -12 lines ');
});
});
});
14 changes: 14 additions & 0 deletions frontend/cypress/tests/codeView/codeView_renderFilterHash.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,20 @@ describe('render filter hash', () => {
.should('contain', 'viewRepoTags=true');
});

it('optimise timeline: url params should persist after change and reload', () => {
cy.get('#summary label.optimise-timeline input:visible')
.should('be.visible')
.check();

cy.url()
.should('contain', 'optimiseTimeline=true');

cy.reload();

cy.url()
.should('contain', 'optimiseTimeline=true');
});

it('checked file types: url params should persist after change and reload', () => {
cy.get('#summary label.filter-breakdown input:visible')
.should('not.be.checked');
Expand Down
83 changes: 62 additions & 21 deletions frontend/src/components/c-ramp.vue
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
<template lang="pug">
.ramp
template(v-if="tframe === 'commit'")
template(v-for="(slice, j) in user.commits")
template(v-for="(commit, k) in slice.commitResults")
a.ramp__slice(
draggable="false",
v-on:click="rampClick",
v-bind:href="getLink(commit)", target="_blank",
v-bind:title="getContributionMessageByCommit(slice, commit)",
v-bind:class="`ramp__slice--color${getRampColor(commit, slice)}`,\
!isBrokenLink(getLink(commit)) ? '' : 'broken-link'",
v-bind:style="{\
zIndex: user.commits.length - j,\
borderLeftWidth: `${getWidth(commit)}em`,\
right: `${((getSlicePos(slice.date)\
+ (getCommitPos(k, slice.commitResults.length))) * 100)}%`\
}"
)
.ramp-padding(
v-bind:style="optimiseTimeline ? {width: `${100 - optimisedPadding * 2}%`, left: `${optimisedPadding}%`} : ''"
)
template(v-for="(slice, j) in user.commits")
template(v-for="(commit, k) in slice.commitResults")
a.ramp__slice(
draggable="false",
v-on:click="rampClick",
v-bind:href="getLink(commit)", target="_blank",
v-bind:title="getContributionMessageByCommit(slice, commit)",
v-bind:class="`ramp__slice--color${getRampColor(commit, slice)}`,\
!isBrokenLink(getLink(commit)) ? '' : 'broken-link'",
v-bind:style="{\
zIndex: user.commits.length - j,\
borderLeftWidth: `${getWidth(commit)}em`,\
right: `${((getSlicePos(slice.date)\
+ (getCommitPos(k, slice.commitResults.length))) * 100)}%`\
}"
)

template(v-else)
a(v-bind:href="getReportLink()", target="_blank")
Expand All @@ -30,8 +33,11 @@
zIndex: user.commits.length - j,\
borderLeftWidth: `${getWidth(slice)}em`,\
right: `${(getSlicePos(tframe === 'day' ? slice.date : slice.endDate) * 100)}%` \
}"
}"
)
.date-indicators(v-if="optimiseTimeline")
span {{optimisedMinimumDate}}
span {{optimisedMaximumDate}}
</template>

<script lang='ts'>
Expand Down Expand Up @@ -83,10 +89,26 @@ export default defineComponent({
type: Boolean,
default: false,
},
optimiseTimeline: {
type: Boolean,
default: false,
},
optimisedMinimumDate: {
type: String,
default: '',
},
optimisedMaximumDate: {
type: String,
default: '',
},
},
data(): {rampSize: number} {
data(): {
rampSize: number,
optimisedPadding: number,
} {
return {
rampSize: 0.01 as number,
optimisedPadding: 3, // as % of total timeline,
};
},
Expand Down Expand Up @@ -172,13 +194,20 @@ export default defineComponent({
// position for commit granularity
getCommitPos(i: number, total: number): number {
const totalTime = this.optimiseTimeline
? this.getTotalForPos(this.optimisedMinimumDate, this.optimisedMaximumDate)
: this.getTotalForPos(this.sdate, this.udate);
return (((total - i - 1) * window.DAY_IN_MS) / total)
/ (this.getTotalForPos(this.sdate, this.udate) + window.DAY_IN_MS);
/ (totalTime + window.DAY_IN_MS);
},
// position for day granularity
getSlicePos(date: string): number {
const total = this.getTotalForPos(this.sdate, this.udate);
return (new Date(this.udate).valueOf() - new Date(date).valueOf()) / (total + window.DAY_IN_MS);
const toDate = this.optimiseTimeline ? this.optimisedMaximumDate : this.udate;
const total = this.optimiseTimeline
? this.getTotalForPos(this.optimisedMinimumDate, this.optimisedMaximumDate)
: this.getTotalForPos(this.sdate, this.udate);
return (new Date(toDate).valueOf() - new Date(date).valueOf()) / (total + window.DAY_IN_MS);
},
// get duration in miliseconds between 2 date
Expand Down Expand Up @@ -267,4 +296,16 @@ export default defineComponent({
}
}
}
.ramp-padding {
height: 100%;
position: relative;
}
.date-indicators {
color: mui-color('grey', '700');
display: flex;
justify-content: space-between;
padding-top: 1px;
}
</style>
54 changes: 46 additions & 8 deletions frontend/src/components/c-summary-charts.vue
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,12 @@
a(
v-if="!isChartGroupWidgetMode",
onclick="deactivateAllOverlays()",
v-on:click="openTabZoom(repo[0], filterSinceDate, filterUntilDate, isGroupMerged(getGroupName(repo)))"
v-on:click="openTabZoom(\
repo[0],\
getIsOptimising(repo[0]) ? getOptimisedMinimumDate(repo[0]) : filterSinceDate,\
getIsOptimising(repo[0]) ? getOptimisedMaximumDate(repo[0]) : filterUntilDate,\
isGroupMerged(getGroupName(repo))\
)"
)
.tooltip(
v-on:mouseover="onTooltipHover(`summary-charts-${i}-commit-breakdown`)",
Expand Down Expand Up @@ -230,7 +235,12 @@
a(
v-if="!isChartGroupWidgetMode",
onclick="deactivateAllOverlays()",
v-on:click="openTabZoom(user, filterSinceDate, filterUntilDate, isGroupMerged(getGroupName(repo)))"
v-on:click="openTabZoom(\
user,\
getIsOptimising(user) ? getOptimisedMinimumDate(user) : filterSinceDate,\
getIsOptimising(user) ? getOptimisedMaximumDate(user) : filterUntilDate,\
isGroupMerged(getGroupName(repo))\
)"
)
.tooltip(
v-on:mouseover="onTooltipHover(`repo-${i}-author-${j}-commit-breakdown`)",
Expand Down Expand Up @@ -296,7 +306,11 @@
v-bind:avgsize="avgCommitSize",
v-bind:mergegroup="isGroupMerged(getGroupName(repo))",
v-bind:filtersearch="filterSearch",
v-bind:is-widget-mode="isChartGroupWidgetMode")
v-bind:is-widget-mode="isChartGroupWidgetMode",
v-bind:optimise-timeline="getIsOptimising(user)",
v-bind:optimised-minimum-date="getOptimisedMinimumDate(user)",
v-bind:optimised-maximum-date="getOptimisedMaximumDate(user)"
)
.overlay

.summary-chart__contribution
Expand Down Expand Up @@ -400,6 +414,10 @@ export default defineComponent({
type: Boolean,
default: false,
},
optimiseTimeline: {
type: Boolean,
default: false,
},
},
data(): {
drags: Array<number>,
Expand Down Expand Up @@ -609,6 +627,21 @@ export default defineComponent({
return ['fas', 'database'];
}
},
getOptimisedMinimumDate(user: User): string {
return user.commits.length === 0
? this.filterSinceDate
: user.commits.reduce((prev, curr) => (new Date(prev.date) < new Date(curr.date) ? prev : curr))
.date;
},
getOptimisedMaximumDate(user: User): string {
return user.commits.length === 0
? this.filterUntilDate
: user.commits.reduce((prev, curr) => (new Date(prev.date) > new Date(curr.date) ? prev : curr))
.date;
},
getIsOptimising(user: User): boolean {
return user.commits.length !== 0 && this.optimiseTimeline;
},

getTagLink(repo: User, tag: string): string | undefined {
return window.filterUnsupported(`${window.getRepoLinkUnfiltered(repo.repoId)}releases/tag/${tag}`);
Expand Down Expand Up @@ -648,12 +681,17 @@ export default defineComponent({

// skip if accidentally clicked on ramp chart
if (this.drags.length === 2 && this.drags[1] - this.drags[0]) {
// additional day was added to include the date represented by filterUntilDate
const tdiff = (new Date(this.filterUntilDate)).valueOf() - (new Date(this.filterSinceDate)).valueOf()
+ window.DAY_IN_MS;
const fromDate = (new Date(this.getIsOptimising(user)
? this.getOptimisedMinimumDate(user)
: this.filterSinceDate)).valueOf();
const toDate = (new Date(this.getIsOptimising(user)
? this.getOptimisedMaximumDate(user)
: this.filterUntilDate)).valueOf();

const tdiff = toDate - fromDate + window.DAY_IN_MS;
const idxs = this.drags.map((x) => (x * tdiff) / 100);
const tsince = window.getDateStr(new Date(this.filterSinceDate).getTime() + idxs[0]);
const tuntil = window.getDateStr(new Date(this.filterSinceDate).getTime() + idxs[1]);
const tsince = window.getDateStr(fromDate + idxs[0]);
const tuntil = window.getDateStr(fromDate + idxs[1]);
this.drags = [];
this.openTabZoom(user, tsince, tuntil, isMerged);
}
Expand Down
Loading
Loading