-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #67 from mansona/stats
Add stats to the lttf dashboard
- Loading branch information
Showing
10 changed files
with
382 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
<details data-test-completed-rules> | ||
<summary>Stats</summary> | ||
<div class="stats-wrapper"> | ||
{{#if this.removedToday.size}} | ||
<div class="stat-card"> | ||
🎉🌅 Rules removed today | ||
|
||
<ul> | ||
{{#each this.removedToday as |rule|}} | ||
<li> | ||
<LinkTo @route="rule" @model={{rule}}>{{rule}}</LinkTo> | ||
</li> | ||
{{/each}} | ||
</ul> | ||
</div> | ||
{{/if}} | ||
|
||
{{#if this.removedThisWeek.size}} | ||
<div class="stat-card"> | ||
🎉🗓️ Rules removed this week | ||
|
||
<ul> | ||
{{#each this.removedThisWeek as |rule|}} | ||
<li> | ||
<LinkTo @route="rule" @model={{rule}}>{{rule}}</LinkTo> | ||
</li> | ||
{{/each}} | ||
</ul> | ||
</div> | ||
{{/if}} | ||
|
||
{{#if this.improvedToday}} | ||
<div class="stat-card"> | ||
📉🌅 Biggest improvement since yesterday: | ||
<div class="my-2"> | ||
<LinkTo | ||
@route="rule" | ||
@model={{this.improvedToday.rule}} | ||
>{{this.improvedToday.rule}}</LinkTo></div> | ||
which has removed | ||
{{absolute this.improvedToday.value}} | ||
files | ||
</div> | ||
{{/if}} | ||
|
||
{{#if this.improvedThisWeek}} | ||
<div class="stat-card"> | ||
📉🗓️ Biggest improvment this week: | ||
|
||
<div class="my-2"> | ||
<LinkTo @route="rule" @model={{this.improvedThisWeek.rule}}> | ||
{{this.improvedThisWeek.rule}}</LinkTo> | ||
</div> | ||
which has removed | ||
{{absolute this.improvedThisWeek.value}} | ||
files | ||
|
||
</div> | ||
{{/if}} | ||
</div> | ||
|
||
<div class="stats-wrapper"> | ||
{{#if this.newToday.size}} | ||
<div class="stat-card"> | ||
🆕🌅 New rules added since yesterday: | ||
|
||
<ul> | ||
{{#each this.newToday as |rule|}} | ||
<li><LinkTo @route="rule" @model={{rule}}>{{rule}}</LinkTo></li> | ||
{{/each}} | ||
</ul> | ||
</div> | ||
{{/if}} | ||
|
||
{{#if this.newThisWeek.size}} | ||
<div class="stat-card"> | ||
🆕🗓️ New rules added this week: | ||
|
||
<ul> | ||
{{#each this.newThisWeek as |rule|}} | ||
<li><LinkTo @route="rule" @model={{rule}}>{{rule}}</LinkTo></li> | ||
{{/each}} | ||
</ul> | ||
</div> | ||
{{/if}} | ||
|
||
{{#if this.mostAddedToday}} | ||
<div class="stat-card"> | ||
📈🌅 Most new files since yesterday: | ||
|
||
<div class="my-2"> | ||
<LinkTo | ||
@route="rule" | ||
@model={{this.mostAddedToday.rule}} | ||
>{{this.mostAddedToday.rule}}</LinkTo> | ||
</div> | ||
|
||
which added | ||
{{this.mostAddedToday.value}} | ||
files | ||
|
||
</div> | ||
{{/if}} | ||
|
||
{{#if this.mostAddedThisWeek}} | ||
<div class="stat-card"> | ||
📈🗓️ Most new files this week: | ||
|
||
<div class="my-2"> | ||
<LinkTo | ||
@route="rule" | ||
@model={{this.mostAddedThisWeek.rule}} | ||
>{{this.mostAddedThisWeek.rule}}</LinkTo> | ||
</div> | ||
|
||
which added | ||
{{this.mostAddedThisWeek.value}} | ||
files | ||
|
||
</div> | ||
{{/if}} | ||
</div> | ||
</details> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import Component from '@glimmer/component'; | ||
|
||
export default class Stats extends Component { | ||
get improvedToday() { | ||
let biggest; | ||
|
||
for (let [rule, value] of Object.entries(this.args.data?.today?.changed)) { | ||
if (value > 0) { | ||
continue; | ||
} | ||
// removing trumps improvement | ||
if (this.removedToday.has(rule)) { | ||
continue; | ||
} | ||
// remember smaller numbers are bigger "improvmeents"; | ||
if (!biggest || value < biggest.value) { | ||
biggest = { rule, value }; | ||
} | ||
} | ||
return biggest; | ||
} | ||
|
||
get improvedThisWeek() { | ||
let biggest; | ||
|
||
for (let [rule, value] of Object.entries( | ||
this.args.data?.thisWeek?.changed, | ||
)) { | ||
if ( | ||
value > 0 || | ||
rule === this.improvedToday?.rule || | ||
value >= this.improvedToday?.value | ||
) { | ||
continue; | ||
} | ||
|
||
// removing trumps improvement | ||
if (this.removedThisWeek.has(rule)) { | ||
continue; | ||
} | ||
|
||
// remember smaller numbers are bigger "improvmeents"; | ||
if (!biggest || value < biggest.value) { | ||
biggest = { rule, value }; | ||
} | ||
} | ||
|
||
return biggest; | ||
} | ||
|
||
get mostAddedToday() { | ||
let biggest; | ||
|
||
for (let [rule, value] of Object.entries(this.args.data?.today?.changed)) { | ||
if (value < 0) { | ||
continue; | ||
} | ||
// new trumps added | ||
if (this.newToday.has(rule)) { | ||
continue; | ||
} | ||
|
||
if (!biggest || value > biggest.value) { | ||
biggest = { rule, value }; | ||
} | ||
} | ||
return biggest; | ||
} | ||
|
||
get mostAddedThisWeek() { | ||
let biggest; | ||
|
||
for (let [rule, value] of Object.entries( | ||
this.args.data?.thisWeek?.changed, | ||
)) { | ||
if ( | ||
value < 0 || | ||
rule === this.mostAddedToday?.rule || | ||
value <= this.mostAddedToday?.value | ||
) { | ||
continue; | ||
} | ||
|
||
// new trumps added | ||
if (this.newToday.has(rule) || this.newThisWeek.has(rule)) { | ||
continue; | ||
} | ||
|
||
if (!biggest || value > biggest.value) { | ||
biggest = { rule, value }; | ||
} | ||
} | ||
|
||
return biggest; | ||
} | ||
|
||
get newToday() { | ||
return new Set(this.args.data?.today?.added); | ||
} | ||
|
||
get newThisWeek() { | ||
return new Set(this.args.data.thisWeek.added).difference(this.newToday); | ||
} | ||
|
||
get removedToday() { | ||
return new Set(this.args.data?.today?.removed); | ||
} | ||
|
||
get removedThisWeek() { | ||
return new Set(this.args.data.thisWeek.removed).difference( | ||
this.removedToday, | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { helper } from '@ember/component/helper'; | ||
|
||
export default helper(function absolute(positional /*, named*/) { | ||
return Math.abs(positional); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,98 @@ | ||
/* eslint-disable prettier/prettier */ | ||
import Route from '@ember/routing/route'; | ||
import fetch from 'fetch'; | ||
import { Temporal } from 'temporal-polyfill' | ||
|
||
import env from 'lint-to-the-future/config/environment'; | ||
|
||
import timeSeries from 'lint-to-the-future/utils/time-series'; | ||
|
||
function lengthOrValuOrZero(data) { | ||
if(!data) { | ||
return 0; | ||
} | ||
return data.length ?? data; | ||
} | ||
|
||
function compareData(data, today, past) { | ||
const timeSeriesData = timeSeries(data); | ||
|
||
let changed = {} | ||
let removed = []; | ||
let added = []; | ||
|
||
for(let rule in timeSeriesData) { | ||
let diff = lengthOrValuOrZero(timeSeriesData[rule][today]) - lengthOrValuOrZero(timeSeriesData[rule][past]); | ||
|
||
if (diff !== 0 ) { | ||
changed[rule] = diff; | ||
} | ||
|
||
if (!timeSeriesData[rule][today] && timeSeriesData[rule][past]) { | ||
removed.push(rule); | ||
} | ||
|
||
if (timeSeriesData[rule][today] && !timeSeriesData[rule][past]) { | ||
added.push(rule); | ||
} | ||
} | ||
return { | ||
changed, | ||
removed, | ||
added, | ||
} | ||
} | ||
|
||
export default class ApplicationRoute extends Route { | ||
async model() { | ||
let data = await (await fetch(`${env.rootURL}data.json`)).json(); | ||
|
||
let allDates = Object.keys(data).sort((a, b) => b.localeCompare(a)) | ||
|
||
let timeSeriesData = timeSeries(data); | ||
|
||
let highestDate; | ||
const globalHighestDate = allDates[0]; | ||
const stats = {} | ||
|
||
const today = Temporal.PlainDate.from(globalHighestDate); | ||
|
||
// there is at least another date in the data | ||
if (allDates[1]) { | ||
const yesterday = Temporal.PlainDate.from(allDates[1]); | ||
if (yesterday.until(today).days === 1) { | ||
// there was a yesterday | ||
stats.today = compareData({ | ||
[globalHighestDate]: data[globalHighestDate], | ||
[allDates[1]]: data[allDates[1]] | ||
}, globalHighestDate, allDates[1]) | ||
} | ||
|
||
let lastWeek = yesterday; | ||
|
||
for (let i = 2; i < allDates.length; i++) { | ||
const currentDate = Temporal.PlainDate.from(allDates[i]); | ||
if (currentDate.until(today).days > 7) { | ||
break; | ||
} | ||
|
||
for (const rule in timeSeriesData) { | ||
for (const date in timeSeriesData[rule]) { | ||
if(!highestDate || highestDate < date) { | ||
highestDate = date; | ||
if (currentDate.until(lastWeek).days > 0) { | ||
lastWeek = Temporal.PlainDate.from(currentDate) | ||
} | ||
} | ||
|
||
// if we have a date that is bigger than yesterday but not bigger than 7 days ago | ||
if (lastWeek !== yesterday) { | ||
stats.thisWeek = compareData({ | ||
[globalHighestDate]: data[globalHighestDate], | ||
[lastWeek.toString()]: data[lastWeek.toString()] | ||
}, globalHighestDate, lastWeek.toString()) | ||
} | ||
} | ||
|
||
return { | ||
data: timeSeriesData, | ||
highestDate, | ||
highestDate: globalHighestDate, | ||
stats, | ||
} | ||
} | ||
} |
Oops, something went wrong.