Skip to content

Commit

Permalink
[REF] runbot: rewrite stat page layout
Browse files Browse the repository at this point in the history
Rewrite the page layout to have good separation within components.
  • Loading branch information
Williambraecky committed Jan 10, 2025
1 parent cd70f5f commit 4eaf55d
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 103 deletions.
29 changes: 24 additions & 5 deletions runbot/static/src/stats/stats_chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import { Component, useEffect, useRef, useState } from '@odoo/owl';

import { debounce, filterKeys, randomColor } from '@runbot/utils';
import { useBus } from '@runbot/stats/use_bus';
import { useConfig, onConfigChange } from '@runbot/stats/use_config';
import { Chart } from '@runbot/chartjs';

Expand Down Expand Up @@ -70,8 +69,6 @@ export class StatsChart extends Component {
});

onConfigChange(() => this.fetchStats(), true);
useBus(this.env.bus, 'click-previous', () => this.selectPrevious());
useBus(this.env.bus, 'click-next', () => this.selectNext());
useEffect(() => {
this.updateChart();
}, () => [
Expand All @@ -80,6 +77,28 @@ export class StatsChart extends Component {
]);
}

/**
* Whether to display the next button
*/
get shouldDisplayNext() {
const builds = Object.keys(this.state.data);
if (!builds.length) {
return false;
}
return this.config.center_build_id !== '0' && this.config.center_build_id !== builds[builds.length - 1];
}

/**
* Whether to display the previous button
*/
get shouldDisplayPrevious() {
const builds = Object.keys(this.state.data);
if (!builds.length) {
return false;
}
return this.config.center_build_id !== builds[0];
}

/**
* Called before actually fetching stat, this triggers the spinner while waiting
* on the debounced _fetchStat.
Expand Down Expand Up @@ -281,7 +300,7 @@ export class StatsChart extends Component {
/**
* Selects the first build as the center build for the next fetch.
*/
selectPrevious() {
onClickPrevious() {
const builds = Object.keys(this.state.data);
if (!builds || !builds.length) {
return
Expand All @@ -292,7 +311,7 @@ export class StatsChart extends Component {
/**
* Selects the last build as the center build for the next fetch.
*/
selectNext() {
onClickNext() {
const builds = Object.keys(this.state.data);
if (!builds || !builds.length) {
return
Expand Down
75 changes: 51 additions & 24 deletions runbot/static/src/stats/stats_chart.xml
Original file line number Diff line number Diff line change
@@ -1,39 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<t t-name="runbot.StatsChart">
<div class="container">
<!-- Controls strictly related to dataset or chart behavior -->
<div class="row g-1">
<div class="col-4">
<label for="mode" class="form-label fw-bold mb-0">Mode</label>
<select class="form-select" id="mode" aria-label="Display Mode" t-model="config.mode">
<option title="Real Values ordered by value" value="normal">Value</option>
<option title="Real Values ordered by name" value="alpha">Alphabetical</option>
<option title="Delta With Reference Build Values" value="difference">Difference</option>
<option title="Bigger # of datapoint varying from previous one" value="change_count">Noisy</option>
</select>
</div>
<div class="col-4">
<label for="nb_dataset" class="form-label fw-bold mb-0">Display</label>
<select class="form-select" id="nb_dataset" aria-label="Number Of Builds" t-on-change="(ev) => this.onChangeNbDataset(ev)">
<option value="-1">Custom</option>
<option value="0">0</option>
<option value="10">Top 10</option>
<option value="20">Top 20</option>
<option value="50">Top 50</option>
</select>
</div>
<div class="col-4">
<label for="display_aggregate" class="form-label fw-bold mb-0">Display Aggregate</label>
<select class="form-select" id="display_aggregate" aria-label="Display sum" t-model="config.display_aggregate">
<option value="none">No</option>
<option value="sum">Sum</option>
<option value="average">Average</option>
</select>
</div>
<div class="col-12">
<div class="d-flex justify-content-between gap-4">
<div t-if="!state.loading &amp;&amp; (shouldDisplayPrevious || shouldDisplayNext || config.center_build_id !== '0')" class="d-flex justify-content-between flex-grow-1">
<button t-if="shouldDisplayPrevious" class="btn btn-default" title="Previous Builds" aria-label="Previous Builds" t-on-click="() => this.onClickPrevious()">
<i t-attf-class="fa fa-backward"/>
</button>
<button t-if="shouldDisplayNext" class="btn btn-default" title="Next Builds" aria-label="Next Builds" t-on-click="() => this.onClickNext()">
<i t-attf-class="fa fa-forward"/>
</button>
<button t-if="config.center_build_id !== '0'" class="btn btn-default" title="Latest Builds" aria-label="Latest Builds" t-on-click="() => this.config.center_build_id = '0'">
<i t-attf-class="fa fa-fast-forward"/>
</button>
</div>
<div class="align-content-center text-center flex-grow-1">
Tips: click a bullet to see corresponding build stats, shift+click to center the graph on this build
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-9 col-md-10">
<div class="position-relative">
<div class="position-relative w-100 h-100">
<div t-if="state.loading" class="o_runbot_stat_chart_backdrop position-absolute h-100 w-100 text-center align-content-center">
<i class="fa fa-2x fa-circle-o-notch fa-spin"/>
</div>
<canvas t-ref="canvas"/>
</div>
</div>
<div class="col-xs-3 col-md-2">
<b>Mode:</b>
<select class="form-select" aria-label="Display Mode" t-model="config.mode">
<option title="Real Values ordered by value" value="normal">Value</option>
<option title="Real Values ordered by name" value="alpha">Alphabetical</option>
<option title="Delta With Reference Build Values" value="difference">Difference</option>
<option title="Bigger # of datapoint varying from previous one" value="change_count">Noisy</option>
</select>
<br/>
<b>Display:</b>
<select class="form-select" aria-label="Number Of Builds" t-on-change="(ev) => this.onChangeNbDataset(ev)">
<option value="-1">Custom</option>
<option value="0">0</option>
<option value="10">Top 10</option>
<option value="20">Top 20</option>
<option value="50">Top 50</option>
</select>
<br/>
<b>Display aggregate:</b>
<select class="form-select" aria-label="Display sum" t-model="config.display_aggregate">
<option value="none">No</option>
<option value="sum">Sum</option>
<option value="average">Average</option>
</select><br/>
<!-- legend -->
<div class="chart-legend" t-if="chartConfig.data?.datasets">
<ul>
Expand Down
11 changes: 1 addition & 10 deletions runbot/static/src/stats/stats_config.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
/** @odoo-module **/

import { Component, useState } from '@odoo/owl';
import { Component } from '@odoo/owl';

import { useBus } from '@runbot/stats/use_bus';
import { useConfig } from '@runbot/stats/use_config';


Expand Down Expand Up @@ -44,14 +43,6 @@ export class StatsConfig extends Component {
this.config = useConfig();
}

onClickPrevious() {
this.env.bus.trigger('click-previous', {});
}

onClickNext() {
this.env.bus.trigger('click-next', {});
}

/**
* Called when the trigger selection is changed.
* This changes the config to remove trigger specific keys and redirects
Expand Down
67 changes: 33 additions & 34 deletions runbot/static/src/stats/stats_config.xml
Original file line number Diff line number Diff line change
@@ -1,40 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<t t-name="runbot.StatsConfig">
<nav class="navbar navbar-light">
<div class="container">
<b>Bundle:</b><t t-out="props.bundle.name"/>
<b>Trigger:</b>
<select class="form-select text-capitalize" aria-label="Select Trigger" t-on-change="(ev) => this.onChangeTrigger(ev)">
<optgroup t-foreach="Object.entries(props.triggers_by_category)" t-as="entry" t-key="entry[0]" t-att-label="entry[0]">
<option t-foreach="entry[1]" t-as="trigger" t-key="trigger.slug" t-out="trigger.name" t-att-value="trigger.slug" t-att-selected="trigger.id === props.trigger.id ? 'selected' : undefined"/>
</optgroup>
</select>
<b>Stat Category:</b>
<select class="form-select text-capitalize" aria-label="Stat Category" t-model="config.key_category">
<option t-foreach="props.stats_categories" t-as="category" t-key="category" t-attf-value="{{category}}">
<t t-out="category.replaceAll('_', ' ')"/>
</option>
</select>
<b>Nb of builds:</b>
<select class="form-select" aria-label="Number Of Builds" t-model.number="config.limit">
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
<option value="250">250</option>
</select>
<button class="btn btn-default" title="Previous Builds" aria-label="Previous Builds" t-on-click="() => this.onClickPrevious()">
<i t-attf-class="fa fa-backward"/>
</button>
<button class="btn btn-default" title="Next Builds" aria-label="Next Builds" t-on-click="() => this.onClickNext()">
<i t-attf-class="fa fa-forward"/>
</button>
<button t-if="config.center_build_id != 0" class="btn btn-default" title="Latest Builds" aria-label="Latest Builds" t-on-click="() => this.config.center_build_id = 0">
<i t-attf-class="fa fa-fast-forward"/>
</button>
<p>Tips: click a bullet to see corresponding build stats, shift+click to center the graph on this build</p>
<div class="container mb-4">
<div class="row mt-2 g-1">
<div class="col-12">
<label for="bundle" class="form-label fw-bold mb-0">Bundle</label>
<input id="bundle" class="form-control" disabled="" t-att-value="props.bundle.name" aria-label="Bundle"/>
</div>
<div class="col-12">
<label for="trigger" class="form-label fw-bold mb-0">Trigger</label>
<select class="form-select text-capitalize" id="trigger" aria-label="Select Trigger" t-on-change="(ev) => this.onChangeTrigger(ev)">
<optgroup t-foreach="Object.entries(props.triggers_by_category)" t-as="entry" t-key="entry[0]" t-att-label="entry[0]">
<option t-foreach="entry[1]" t-as="trigger" t-key="trigger.slug" t-out="trigger.name" t-att-value="trigger.slug" t-att-selected="trigger.id === props.trigger.id ? 'selected' : undefined"/>
</optgroup>
</select>
</div>
<div class="col-12">
<label for="key_category" class="form-label fw-bold mb-0">Stat Category</label>
<select class="form-select text-capitalize" id="key_category" aria-label="Stat Category" t-model="config.key_category">
<option t-foreach="props.stats_categories" t-as="category" t-key="category" t-attf-value="{{category}}">
<t t-out="category.replaceAll('_', ' ')"/>
</option>
</select>
</div>
<div class="col-12">
<label for="limit" class="form-label fw-bold mb-0">Number of builds:</label>
<select class="form-select" id="limit" aria-label="Number Of Builds" t-model.number="config.limit">
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
<option value="250">250</option>
</select>
</div>
</div>
</nav>
</div>
</t>
</odoo>
7 changes: 1 addition & 6 deletions runbot/static/src/stats/stats_root.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/** @odoo-module **/

import { Component, whenReady, App, EventBus, useSubEnv } from '@odoo/owl';
import { Component, whenReady, App } from '@odoo/owl';
import { getTemplate } from '@web/core/templates';

import { StatsConfig } from '@runbot/stats/stats_config';
Expand Down Expand Up @@ -47,11 +47,6 @@ export class StatsRoot extends Component {
setup() {
// Initialize shared configuration for children components.
useConfig(false);

// Bus for communicating between children
useSubEnv({
bus: new EventBus(),
});
}
}

Expand Down
22 changes: 0 additions & 22 deletions runbot/static/src/stats/use_bus.js

This file was deleted.

4 changes: 2 additions & 2 deletions runbot/static/src/stats/use_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { reactive, useEffect, useState, useEnv, useSubEnv } from '@odoo/owl';
*/
export class Config {
constructor({
limit = 25, center_build_id = 0, key_category = 'module_loading_queries',
limit = 25, center_build_id = '0', key_category = 'module_loading_queries',
mode = 'normal', nb_dataset = 20, display_aggregate = 'none', visible_keys = '',
}) {
this.limit = limit;
Expand All @@ -27,7 +27,7 @@ export class Config {
*/
static fromSearchParams() {
const config = Object.fromEntries(new URLSearchParams(window.location.hash.substring(1)));
const numberKeys = ['limit', 'center_build_id', 'nb_dataset'];
const numberKeys = ['limit', 'nb_dataset'];
numberKeys.forEach((key) => {
if (!(key in config)) {
return;
Expand Down

0 comments on commit 4eaf55d

Please sign in to comment.