Progress boxes constructed separately

(Still need to fix numbering)
This commit is contained in:
Elliptic Ellipsis
2021-12-15 16:58:12 +00:00
parent cf2ed58706
commit 9d20482fa7
10 changed files with 183 additions and 150 deletions
@@ -34,13 +34,13 @@ export class GameSummaryComponent implements OnChanges {
ngOnChanges(): void {
// Matching is second
this.gamesService.getGameCSV(this.data.csv_info[1].data).subscribe(
this.gamesService.getGameCSV(this.data.csvData[1]).subscribe(
res => {
const points = res.split("\n").filter((line) => line != "");
const latestPoint = points[points.length - 1];
const column = latestPoint.split(",");
let i = this.data.charts[0].index;
let i = this.data.progressMeta[0].index;
this.total = +column[i] / +column[i + 1];
},
err => console.error(err)
@@ -15,14 +15,10 @@ export class GameChartComponent implements OnChanges {
constructor() { }
/**
* The names of the lines to use in this chart.
*/
// @Input() line_names: string[] = null;
/**
* The CSV data to use for this chart.
*/
@Input() datasets: string[] = null;
@Input() csvData: string[] = null;
/**
* The chart metadata stored in the game settings.
*/
@@ -34,24 +30,28 @@ export class GameChartComponent implements OnChanges {
ngOnChanges(): void {
// don't do anything until all inputs are provided
if (typeof this.datasets !== "object" || typeof this.metadata !== "object") {
if (typeof this.csvData !== "object" || typeof this.metadata !== "object") {
return;
}
// set up chart data
const metadata = this.metadata;
let nonmatchingData = this.parseData(this.datasets[0]);
let matchingData = this.parseData(this.datasets[1]);
let nonmatchingData = this.parseData(this.csvData[0]);
let matchingData = this.parseData(this.csvData[1]);
// Names for lines
let nonmatchingName = this.metadata.line_names[0];
let matchingName = this.metadata.line_names[1];
let nonmatchingName = this.metadata.series[0];
let matchingName = this.metadata.series[1];
let joined = Array.prototype.concat(matchingData, nonmatchingData);
joined.sort((a, b) => a["y"] - b["y"]);
const interval = 1 / 5;
const max = Math.max(interval, Math.ceil(joined.slice(-1)[0].y / interval) * interval);
let max = Math.max(interval, Math.ceil(joined.slice(-1)[0].y / interval) * interval);
if (max < 0.9) {
max = null;
}
const options: Options = {
chart: { type: "line" },
@@ -64,11 +64,11 @@ export class GameChartComponent implements OnChanges {
tooltip += `Date: ${new Date(data.x).toLocaleString()}<br/>\n`;
tooltip += `Commit: ${data["commit"]}<br/>\n`
tooltip += `Total ${metadata.series[0].metric}: ${(+data.y * 100).toFixed(2)}%<br/>\n`
tooltip += `Total ${metadata.subdivisions[0].metric}: ${(+data.y * 100).toFixed(2)}%<br/>\n`
tooltip += `-------------------------------------------<br/>\n`;
for (let i = 0; i < metadata.series.length - 1; i += 1) {
const text = metadata.series[i + 1].description
for (let i = 0; i < metadata.subdivisions.length - 1; i += 1) {
const text = metadata.subdivisions[i + 1].description
.replace("{0}", data["metrics"][i][0])
.replace("{1}", data["metrics"][i][1])
.replace("{2}", (+data["metrics"][i][2] * 100).toFixed(2));
@@ -92,7 +92,8 @@ export class GameChartComponent implements OnChanges {
type: "line",
name: nonmatchingName,
data: nonmatchingData,
color: "#ffc107"
color: "#ffc107",
visible: false
},
{
type: "line",
@@ -1,6 +1,42 @@
<n64-box [number]="1" heading="{{ meta.subtitle }}">
<strong class="key">Last updated</strong>
<strong class="value date">{{ lastUpdate }}</strong>
<br>
<hr>
<table>
<thead>
<th scope="col"></th>
<ng-container *ngFor="let name of meta.series; let j = index">
<th scope="col" [ngClass]="{nonmatching: j === 0, matching: j === 1}">{{ name }}</th>
</ng-container>
</thead>
<tbody>
<tr>
<th scope="row">
<strong class="key">Total</strong>
</th>
<td *ngFor="let total of totals; let j = index">
<strong [ngClass]="{nonmatching: j === 0, matching: j === 1}">{{ (total * 100).toFixed(2) }}%</strong>
</td>
</tr>
<tr *ngFor="let metric of metrics | keyvalue: originalOrder">
<th scope="row" class="key">{{ metric.key }}</th>
<td *ngFor="let val of metric.value; let j = index">
<div [ngClass]="{nonmatching: j === 0, matching: j === 1}">{{ (val * 100).toFixed(2) }}%</div>
</td>
</tr>
</tbody>
</table>
</n64-box>
<n64-box [simple]="true">
<game-chart [csvData]="csvData" [metadata]="meta"></game-chart>
</n64-box>
<!--
<ng-container *ngFor="let chart of metrics; let i = index">
<div class="box-wrapper">
<n64-box [number]="i" heading="{{ game.charts[i].subtitle }}">
<n64-box [number]="i" heading="{{ game.progressMeta[i].subtitle }}">
<strong class="key">Last updated</strong>
<strong class="value date">{{ lastUpdate }}</strong>
<br/>
@@ -8,7 +44,7 @@
<table>
<thead>
<th scope="col"> {{ (totals[i] | keyvalue)[0].key }}</th>
<ng-container *ngFor="let name of game.charts[i].line_names; let j = index">
<ng-container *ngFor="let name of game.progressMeta[i].series; let j = index">
<th scope="col" [ngClass]="{nonmatching: j === 0, matching: j === 1}">{{ name }}</th>
</ng-container>
</thead>
@@ -31,7 +67,7 @@
</table>
</n64-box>
<n64-box [simple]="true">
<game-chart [datasets]="datasets" [metadata]="game.charts[i]"></game-chart>
<game-chart [csvData]="csvData" [metadata]="game.progressMeta[i]"></game-chart>
</n64-box>
</div>
</ng-container>
</ng-container> -->
@@ -1,9 +1,9 @@
import { KeyValue } from '@angular/common';
import { ThrowStmt } from '@angular/compiler';
import { Component, Input, OnChanges, OnInit } from '@angular/core';
import { forkJoin } from 'rxjs';
import { concat, forkJoin } from 'rxjs';
import { GamesService } from 'src/app/games/games.service';
import { IGame } from 'src/app/games/games.service.interface';
import { IChart, IGame } from 'src/app/games/games.service.interface';
/**
* A single project displayed in a box, alongside one or more graphs.
@@ -20,83 +20,70 @@ export class GameProgressComponent implements OnChanges {
/**
* Game object which holds info on how to parse the CSV data.
*/
@Input() game: IGame = null;
@Input() meta: IChart = null;
/**
* CSV data.
*/
@Input() csvData: string[] = [];
/**
* The date time string for when this project was last updated
*/
lastUpdate: string = "";
lastUpdate: string = "";
/**
* The simple numbers showing the total progress on respective charts.
*/
totals: {[key: string]: number[]}[] = [];
totals: number[] = [];
/**
* Separate project metrics parsed from the CSV.
*/
metrics: {[key: string]: number[]}[] = [];
/**
* CSV data for use with the chart.
*/
datasets: string[];
metrics: { [key: string]: number[] } = {};
/**
* Names for lines for use with the chart.
*/
line_names: string[];
series: string[];
ngOnChanges(): void {
this.line_names = this.game.csv_info.map((info) => info.name);
// don't do anything until all inputs are provided
console.log(this.meta);
if (typeof this.csvData !== "object" || typeof this.meta !== "object") {
return;
}
forkJoin(
this.game.csv_info.map((info) => this.gamesService.getGameCSV(info.data), this)
).subscribe(
res => {
this.datasets = res;
// reset on page load
this.totals = [];
this.metrics = [];
this.lastUpdate = null;
// reset on page load
this.totals = [];
this.metrics = {};
this.lastUpdate = null;
for (const data of this.csvData) {
const points = data.split("\n").filter((line) => line != "");
const latestPoint = points[points.length - 1];
const column = latestPoint.split(",");
if (this.lastUpdate === null) {
this.lastUpdate = new Date(+column[1] * 1000).toLocaleString();
}
for (const _ of this.game.charts) {
this.metrics.push({});
// this.totals.push({[this.meta.subdivisions[0].metric]: []});
// this.totals[this.meta.subdivisions[0].metric].push([this.meta.subdivisions[0].metric]);
let i = this.meta.index;
this.totals.push(+column[i] / +column[i + 1]);
i += 2;
for (const subdivision of this.meta.subdivisions.slice(1)) {
if (!(subdivision.metric in this.metrics)) {
this.metrics[subdivision.metric] = [];
}
for (const data of res) {
const points = data.split("\n").filter((line) => line != "");
const latestPoint = points[points.length - 1];
const column = latestPoint.split(",");
if (this.lastUpdate === null) {
this.lastUpdate = new Date(+column[1] * 1000).toLocaleString();
}
for (const chart of this.game.charts) {
this.totals.push({ [chart.series[0].metric]: [] });
}
let c = 0;
for (const chart of this.game.charts) {
let i = chart.index;
this.totals[c][chart.series[0].metric].push(+column[i] / +column[i+1]);
i += 2;
for (const serie of chart.series.slice(1)) {
if (!(serie.metric in this.metrics[c])) {
this.metrics[c][serie.metric] = [];
}
this.metrics[c][serie.metric].push(+column[i] / +column[i+1]);
i += 2;
}
c += 1;
}
}
},
err => console.error(err)
);
this.metrics[subdivision.metric].push(+column[i] / +column[i + 1]);
i += 2;
}
}
console.log(this.metrics)
}
originalOrder(a: KeyValue<any, any>, b: KeyValue<any, any>): number {
return 0;
}
+6 -3
View File
@@ -1,7 +1,10 @@
<h1>{{ game.title }}</h1>
<article>
<game-progress [game]="game"></game-progress>
</article>
<ng-container *ngFor="let meta of game.progressMeta">
<article>
<game-progress [csvData]="csvData" [meta]="meta"></game-progress>
</article>
</ng-container>
<article>
<game-faq [game]="game"></game-faq>
+22 -10
View File
@@ -1,10 +1,11 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnChanges, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { forkJoin } from 'rxjs';
import { GamesService } from '../games.service';
import { IGame } from '../games.service.interface';
import { IChart, IGame } from '../games.service.interface';
/**
* Container component for the tabs and relevant content.
* Container component for relevant content on a game.
*/
@Component({
selector: 'game',
@@ -18,8 +19,13 @@ export class GameComponent implements OnInit {
/**
* The summary to use to display basic game info.
*/
game: IGame;
game: IGame;
/**
* Data obtained from the CSV files.
*/
csvData: string[];
// Get the CSVs if the navigated slug is correct, otherwise navigate to 404
ngOnInit(): void {
this.route.params.subscribe(
res => {
@@ -27,12 +33,19 @@ export class GameComponent implements OnInit {
this.gamesService.getGames().subscribe(
res => {
// redirect if an unknown slug is provided
const index = res.findIndex((game) => game.slug === slug)
if (index !== -1) {
this.game = res[index];
const index = res.findIndex((game) => game.slug === slug);
if (index === -1) {
this.router.navigateByUrl("/404", { skipLocationChange: true });
}
else {
this.router.navigateByUrl("/404", { skipLocationChange: true });
this.game = res[index];
forkJoin([
this.gamesService.getGameCSV(this.game.csvs.nonmatching),
this.gamesService.getGameCSV(this.game.csvs.matching)
]).subscribe(
res => this.csvData = res,
err => console.error(err)
);
}
},
err => {
@@ -43,5 +56,4 @@ export class GameComponent implements OnInit {
}
);
}
}
}
+20 -24
View File
@@ -20,22 +20,18 @@ export interface IGame {
* Links to display as an icon. The key defines the icon, and the value is the link href.
*/
links: {[type: string]: string};
// /**
// * The name of the "matching" CSV to read.
// */
// matching: string;
// /**
// * The name of the "nonmatching" CSV to read.
// */
// nonmatching: string;
/**
* Information on which csvs to use and what to call them.
* Struct with names of CSVs to use
*/
csv_info: ICSVInfo[];
csvs: ICSVs;
/**
* The charts to display for this game.
* Data from CSVs
*/
charts: IChart[];
csvData: string[];
/**
* The charts and data for each game.
*/
progressMeta: IChart[];
}
/**
@@ -83,35 +79,35 @@ export interface IChart {
/**
* The index to start counting pairs from. If none provided, will assume 5.
*/
index?: number;
/**
* The series to have for each chart.
*/
series: ISeries[];
index: number;
/**
* Names to use for the lines
*/
line_names: string[];
series: string[];
/**
* The series to have for each chart.
*/
subdivisions: ISubdivision[];
}
/**
* Describes a csv of chart data and its name.
*/
export interface ICSVInfo {
export interface ICSVs {
/**
* The name of the csv type to display with the line.
* CSV file to get the matching data from.
*/
name?: string;
matching: string;
/**
* CSV file to get the data from.
* CSV file to get the nonmatching data from.
*/
data: string;
nonmatching: string;
}
/**
* Describes a singles series within a chart.
*/
export interface ISeries {
export interface ISubdivision {
/**
* The name of the metric to display at the top.
*/
-1
View File
@@ -1,4 +1,3 @@
csv_version,timestamp,git_hash,src,total,boot,boot_total,code,code_total,ovl,ovl_total,asm,nonMatchingFunctions,assets,assets_total,archive,archive_total,audio,audio_total,interface,interface_total,misc,misc total,objects,objects_total,scenes,scenes_total,text,text_total
2,1615435438,e788bfecbfb10afd4182332db99bb562ea75b1de,103860,4747680,31572,86160,58624,1065936,13664,3595584,4597948,49,0,40817824,0,434608,0,6029280,0,749728,0,3953872,0,13518320,0,15695712,0,436304
2,1615511099,72f2b6aa4d154514008f61cc35025875a0528e02,103860,4747680,31572,86160,58624,1065936,13664,3595584,4597948,49,0,40817824,0,434608,0,6029280,0,749728,0,3953872,0,13518320,0,15695712,0,436304
2,1615512459,a983d14081b0e0daee4428769e7968071a1e9401,103860,4747680,31572,86160,58624,1065936,13664,3595584,4597948,49,0,40817824,0,434608,0,6029280,0,749728,0,3953872,0,13518320,0,15695712,0,436304
1 csv_version 2 timestamp 1615435438 git_hash e788bfecbfb10afd4182332db99bb562ea75b1de src 103860 total 4747680 boot 31572 boot_total 86160 code 58624 code_total 1065936 ovl 13664 ovl_total 3595584 asm 4597948 nonMatchingFunctions 49 assets 0 assets_total 40817824 archive 0 archive_total 434608 audio 0 audio_total 6029280 interface 0 interface_total 749728 misc 0 misc total 3953872 objects 0 objects_total 13518320 scenes 0 scenes_total 15695712 text 0 text_total 436304
csv_version timestamp git_hash src total boot boot_total code code_total ovl ovl_total asm nonMatchingFunctions assets assets_total archive archive_total audio audio_total interface interface_total misc misc total objects objects_total scenes scenes_total text text_total
1 2 1615435438 1615435438 e788bfecbfb10afd4182332db99bb562ea75b1de e788bfecbfb10afd4182332db99bb562ea75b1de 103860 103860 4747680 31572 86160 58624 1065936 13664 13664 3595584 4597948 4597948 49 0 40817824 0 434608 0 6029280 0 749728 0 3953872 0 13518320 0 15695712 0 436304
2 2 1615511099 1615511099 72f2b6aa4d154514008f61cc35025875a0528e02 72f2b6aa4d154514008f61cc35025875a0528e02 103860 103860 4747680 31572 86160 58624 1065936 13664 13664 3595584 4597948 4597948 49 0 40817824 0 434608 0 6029280 0 749728 0 3953872 0 13518320 0 15695712 0 436304
3 2 1615512459 1615512459 a983d14081b0e0daee4428769e7968071a1e9401 a983d14081b0e0daee4428769e7968071a1e9401 103860 103860 4747680 31572 86160 58624 1065936 13664 13664 3595584 4597948 4597948 49 0 40817824 0 434608 0 6029280 0 749728 0 3953872 0 13518320 0 15695712 0 436304
@@ -1,4 +1,3 @@
csv_version,timestamp,git_hash,src,total,boot,boot_total,code,code_total,ovl,ovl_total,asm,nonMatchingFunctions,assets,assets_total,archive,archive_total,audio,audio_total,interface,interface_total,misc,misc total,objects,objects_total,scenes,scenes_total,text,text_total
2,1615435438,e788bfecbfb10afd4182332db99bb562ea75b1de,120152,4747680,36352,86160,70136,1065936,13664,3595584,4582152,0,0,40817824,0,434608,0,6029280,0,749728,0,3953872,0,13518320,0,15695712,0,436304
2,1615511099,72f2b6aa4d154514008f61cc35025875a0528e02,120152,4747680,36352,86160,70136,1065936,13664,3595584,4582152,0,0,40817824,0,434608,0,6029280,0,749728,0,3953872,0,13518320,0,15695712,0,436304
2,1615512459,a983d14081b0e0daee4428769e7968071a1e9401,120152,4747680,36352,86160,70136,1065936,13664,3595584,4582152,0,0,40817824,0,434608,0,6029280,0,749728,0,3953872,0,13518320,0,15695712,0,436304
1 csv_version 2 timestamp 1615435438 git_hash e788bfecbfb10afd4182332db99bb562ea75b1de src 120152 total 4747680 boot 36352 boot_total 86160 code 70136 code_total 1065936 ovl 13664 ovl_total 3595584 asm 4582152 nonMatchingFunctions 0 assets 0 assets_total 40817824 archive 0 archive_total 434608 audio 0 audio_total 6029280 interface 0 interface_total 749728 misc 0 misc total 3953872 objects 0 objects_total 13518320 scenes 0 scenes_total 15695712 text 0 text_total 436304
csv_version timestamp git_hash src total boot boot_total code code_total ovl ovl_total asm nonMatchingFunctions assets assets_total archive archive_total audio audio_total interface interface_total misc misc total objects objects_total scenes scenes_total text text_total
1 2 1615435438 1615435438 e788bfecbfb10afd4182332db99bb562ea75b1de e788bfecbfb10afd4182332db99bb562ea75b1de 120152 120152 4747680 36352 86160 70136 1065936 13664 13664 3595584 4582152 4582152 0 0 40817824 0 434608 0 6029280 0 749728 0 3953872 0 13518320 0 15695712 0 436304
2 2 1615511099 1615511099 72f2b6aa4d154514008f61cc35025875a0528e02 72f2b6aa4d154514008f61cc35025875a0528e02 120152 120152 4747680 36352 86160 70136 1065936 13664 13664 3595584 4582152 4582152 0 0 40817824 0 434608 0 6029280 0 749728 0 3953872 0 13518320 0 15695712 0 436304
3 2 1615512459 1615512459 a983d14081b0e0daee4428769e7968071a1e9401 a983d14081b0e0daee4428769e7968071a1e9401 120152 120152 4747680 36352 86160 70136 1065936 13664 13664 3595584 4582152 4582152 0 0 40817824 0 434608 0 6029280 0 749728 0 3953872 0 13518320 0 15695712 0 436304
+33 -33
View File
@@ -6,23 +6,21 @@
"github": "https://www.github.com/zeldaret/oot"
},
"faq": "oot_faq",
"csv_info": [
{
"name": "nonmatching",
"data": "progress-oot-nonmatching"
},
{
"name": "matching",
"data": "progress-oot-matching"
}
],
"charts": [
"csvs": {
"matching": "progress-oot-matching",
"nonmatching": "progress-oot-nonmatching"
},
"progressMeta": [
{
"type": "code",
"title": "Ocarina of Time MQ Debug Rom",
"subtitle": "Code decompilation progress",
"index": 3,
"line_names": ["Nonmatching", "Matching"],
"series": [
"Nonmatching",
"Matching"
],
"subdivisions": [
{
"metric": "Code",
"description": "{0} / {1} bytes of all code decompiled ({2}%)"
@@ -50,34 +48,32 @@
"github": "https://www.github.com/zeldaret/mm"
},
"faq": "mm_faq",
"csv_info": [
{
"name": "nonmatching",
"data": "progress-mm-nonmatching"
},
{
"name": "matching",
"data": "progress-mm-matching"
}
],
"charts": [
"csvs": {
"matching": "progress-mm-matching",
"nonmatching": "progress-mm-nonmatching"
},
"progressMeta": [
{
"type": "code",
"title": "Majora's Mask",
"subtitle": "Code decompilation progress",
"line_names": ["Nonmatching", "Matching"],
"index": 3,
"series": [
"Nonmatching",
"Matching"
],
"index": 3,
"subdivisions": [
{
"metric": "Code",
"description": "{0} / {1} bytes of all code decompiled ({2}%)"
},
{
"metric": "boot",
"description": "{0} / {1} bytes of boot decompiled ({2}%)"
"metric": "Code",
"description": "{0} / {1} bytes of code decompiled ({2}%)"
},
{
"metric": "code",
"description": "{0} / {1} bytes of code decompiled ({2}%)"
"metric": "Boot",
"description": "{0} / {1} bytes of boot decompiled ({2}%)"
},
{
"metric": "Overlays",
@@ -86,11 +82,15 @@
]
},
{
"type": "assets",
"title": "Majora's Mask",
"subtitle": "Asset decompilation progress",
"line_names": ["Debinarised", "Identified"],
"index": 13,
"subtitle": "Asset analysis progress",
"series": [
"Debinarised",
"Identified"
],
"index": 13,
"subdivisions": [
{
"metric": "Assets",
"description": "{0} / {1} bytes of all assets decompiled ({2}%)"
@@ -127,4 +127,4 @@
}
]
}
]
]