Skip to Content
fields forms

Profiling Qweb View

Odoo 19 fields — Profiling Qweb View (webclient)

Live preview Interactive
Source excerpt web/static/src/webclient/debug/profiling/profiling_qweb.js
import { registry } from "@web/core/registry";
import { useService } from "@web/core/utils/hooks";
import { loadBundle } from "@web/core/assets";
import { renderToString } from "@web/core/utils/render";
import { useDebounced } from "@web/core/utils/timing";
import { standardFieldProps } from "@web/views/fields/standard_field_props";

import { Component, useState, useRef, onWillStart, onMounted, onWillUnmount } from "@odoo/owl";

class MenuItem extends Component {
    static template = "web.ProfilingQwebView.menuitem";
    static props = {
        view: Object,
    };
}

function processValue(value) {
    const data = JSON.parse(value);
    for (const line of data[0].results.data) {
        line.xpath = line.xpath.replace(/([^\]])\//g, "$1[1]/").replace(/([^\]])$/g, "$1[1]");
    }
    return data;
}

/**
 * This widget is intended to be used on Text fields. It will provide Ace Editor
 * for display XML and Python profiling.
 */
export class ProfilingQwebView extends Component {
    static template = "web.ProfilingQwebView";
    static components = { MenuItem };
    static props = { ...standardFieldProps };

    setup() {
        super.setup();

        this.orm = useService("orm");
        this.ace = useRef("ace");
        this.selector = useRef("selector");

        this.value = processValue(this.props.record.data[this.props.name]);
        this.state = useState({
            viewID: this.profile.data.length ? this.profile.data[0].view_id : 0,
            view: null,
        });

        this.renderProfilingInformation = useDebounced(this.renderProfilingInformation, 100);

        onWillStart(async () => {
            await loadBundle("web.ace_lib");
            await this._fetchViewData();
            this.state.view = this.viewObjects.find((view) => view.id === this.state.viewID);
        });
        onMounted(() => {
            this._startAce(this.ace.el);
            this._renderView();
        });
        onWillUnmount(() => {
            if (this.aceEditor) {
                this.aceEditor.destroy();
            }
            this._unmoutInfo();
        });
    }

    /**
     * Return JSON values to render the view
     *
     * @returns {archs, data: {template, xpath, directive, time, duration, query }[]}
     */
    get profile() {
        return this.value ? this.value[0].results : { archs: {}, data: [] };
    }

    //--------------------------------------------------------------------------
    // Public
    //--------------------------------------------------------------------------

    /**
     * Return association of view key, view name, query number and total delay
     *
     * @private
     * @returns {Promise<viewObjects>}
     */
    async _fetchViewData() {
        const viewIDs = Array.from(new Set(this.profile.data.map((line) => line.view_id)));
        const viewObjects = await this.orm.call("ir.ui.view", "search_read", [], {
            fields: ["id", "display_name", "key"],
            domain: [["id", "in", viewIDs]],
        });
        for (const view of viewObjects) {
            view.delay = 0;
            view.query = 0;
            const lines = this.profile.data.filter((l) => l.view_id === view.id);
            const root = lines.find((l) => l.xpath === "");
            if (root) {
                view.delay += root.delay;
                view.query += root.query;
            } else {
                view.delay = lines.map((l) => l.delay).reduce((a, b) => a + b);
                view.query = lines.map((l) => l.query).reduce((a, b) => a + b);
            }
            view.delay = Math.ceil(view.delay * 10) / 10;
        }
        this.viewObjects = viewObjects;
    }

    /**
     * Format delay to readable.
     *
     * @private
     * @param {number} delay
     * @returns {string}
     */
    _formatDelay(delay) {
        return delay ? (Math.ceil(delay * 10) / 10).toFixed(1) : ".";
    }

    /**
     * Starts the ace library on the given DOM element. This initializes the
Registry / API
Registry name
profiling_qweb_view
Category
fields
Module
web
Slug
profiling-qweb-view
Nav group
forms