[LNT] r264723 - [ui] Add a mechanism for selecting different compare_to and baseline runs

Chris Matthews via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 27 14:22:29 PDT 2016


Hi James, 

This does not work for me.  I get the search dialog, and can pick a run, but then nothing happens. Is the intent that it would refresh the page at that point?

> On Mar 29, 2016, at 5:11 AM, James Molloy via llvm-commits <llvm-commits at lists.llvm.org> wrote:
> 
> Author: jamesm
> Date: Tue Mar 29 07:11:28 2016
> New Revision: 264723
> 
> URL: http://llvm.org/viewvc/llvm-project?rev=264723&view=rev
> Log:
> [ui] Add a mechanism for selecting different compare_to and baseline runs
> 
> In the v4_runs page, we compute a run to compare to and a baseline run. It's possible for the user to pick a different run to compare to by choosing from the past 5 previous runs, but if the user wants to compare to a specific run, he has to go find that run, look up its run ID, and manually edit the URL, adding "?compare_to=ID".
> 
> A similar procedure is required for changing the baseline.
> 
> This patch adds an easier mechanism. Hovering over the table rows for "previous" or "baseline" reveals an edit icon - clicking on that launches a popover that contains a search box.
> 
> The search mechanism (runTypeahead) was taken from the profiling infrastructure and has been found a new home in lnt_run.js.
> 
> Added:
>    lnt/trunk/lnt/server/ui/static/lnt_run.css
>    lnt/trunk/lnt/server/ui/static/lnt_run.js
> Modified:
>    lnt/trunk/lnt/server/ui/profile_views.py
>    lnt/trunk/lnt/server/ui/static/lnt_profile.js
>    lnt/trunk/lnt/server/ui/templates/reporting/runs.html
>    lnt/trunk/lnt/server/ui/templates/v4_profile.html
>    lnt/trunk/lnt/server/ui/templates/v4_run.html
>    lnt/trunk/lnt/server/ui/views.py
> 
> Modified: lnt/trunk/lnt/server/ui/profile_views.py
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/ui/profile_views.py?rev=264723&r1=264722&r2=264723&view=diff
> ==============================================================================
> --- lnt/trunk/lnt/server/ui/profile_views.py (original)
> +++ lnt/trunk/lnt/server/ui/profile_views.py Tue Mar 29 07:11:28 2016
> @@ -173,7 +173,7 @@ def v4_profile(testid, run1_id, run2_id=
>     else:
>         json_run2 = {}
>     urls = {
> -        'search': v4_url_for('v4_profile_search'),
> +        'search': v4_url_for('v4_search'),
>         'singlerun_template': v4_url_for('v4_profile_fwd',
>                                           testid=1111,
>                                           run1_id=2222) \
> @@ -194,64 +194,3 @@ def v4_profile(testid, run1_id, run2_id=
>                            ts=ts, test=test,
>                            run1=json_run1, run2=json_run2,
>                            urls=urls)
> -
> - at v4_route("/profile/search")
> -def v4_profile_search():
> -    def _isint(i):
> -        try:
> -            int(i)
> -            return True
> -        except:
> -            return False
> -
> -    ts = request.get_testsuite()
> -    query = request.args.get('q')
> -    l = request.args.get('l', 8)
> -    #default_machine = request.args.get('m')
> -
> -    machine_queries = []
> -    order_query = None
> -    for q in query.split(' '):
> -        if not q:
> -            continue
> -        if q.startswith('#'):
> -            order_query = q[1:]
> -        elif _isint(q):
> -            order_query = q
> -        else:
> -            machine_queries.append(q)
> -
> -    if not machine_queries and order_query is None:
> -        return "{}"
> -
> -    if machine_queries:
> -        machines = []
> -        for m in ts.query(ts.Machine).all():
> -            if all(q in m.name for q in machine_queries):
> -                machines.append(m.id)
> -        if not machines:
> -            return "{}"
> -    else:
> -        # FIXME:
> -        return "{}"
> -
> -    q = ts.query(ts.Run).filter(ts.Run.machine_id.in_(machines))
> -    if order_query:
> -        # FIXME: Is this generating a million billion queries under my feet?
> -        # I hate ORMs :( I know this column exists, but because it's
> -        # dynamically generated SQLAlchemy can't filter on it.
> -        # Perhaps there's a way to provide it a manual column name?
> -        # I want to do this:
> -        # filter(ts.Run.order.llvm_project_revision.like('%' + order_query + '%'))
> -        oq = str(order_query)
> -        data = [i
> -                for i in q.all()
> -                if oq in str(i.order.llvm_project_revision)]
> -
> -    else:
> -        data = q.limit(l).all()
> -
> -    return json.dumps(
> -        [('%s #%s' % (r.machine.name, r.order.llvm_project_revision),
> -          r.id)
> -         for r in data])
> 
> Modified: lnt/trunk/lnt/server/ui/static/lnt_profile.js
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/ui/static/lnt_profile.js?rev=264723&r1=264722&r2=264723&view=diff
> ==============================================================================
> --- lnt/trunk/lnt/server/ui/static/lnt_profile.js (original)
> +++ lnt/trunk/lnt/server/ui/static/lnt_profile.js Tue Mar 29 07:11:28 2016
> @@ -348,101 +348,6 @@ ToolBar.prototype = {
>     }
> };
> 
> -function RunTypeahead(element, options) {
> -    this.element = element;
> -    this.options = options;
> -    this.id = null;
> -
> -    this_ = this;
> -    element.typeahead({
> -        source: function(query, process) {
> -            $.ajax(options.searchURL, {
> -                dataType: "json",
> -                data: {'q': query},
> -                success: function(data) {
> -                    process(data);
> -                },
> -                error: function(xhr, textStatus, errorThrown) {
> -                    pf_flash_error('accessing URL ' + options.searchURL +
> -                                   '; ' + errorThrown);
> -                }
> -            });
> -        },
> -        // These identity functions are required because the defaults
> -        // assume items are strings, whereas they're objects here.
> -        sorter: function(items) {
> -            // The results should be sorted on the server.
> -            return items;
> -        },
> -        matcher: function(item) {
> -            return item;
> -        },
> -        updater: function(item) {
> -            // FIXME: the item isn't passed in as json any more, it's
> -            // been rendered. Lame. To get around this, hack the
> -            // components of the 2-tuple back apart.
> -            name = item.split(',')[0];
> -            id = item.split(',')[1];
> -            this_.id = id;
> -            
> -            if (options.updated)
> -                options.updated(name, id);
> -            return name;
> -        },
> -        highlighter: function(item) {
> -            // item is a 2-tuple [name, obj].
> -            item = item[0];
> -
> -            // This loop highlights each search term (split by space)
> -            // individually. The content of the for loop is lifted from
> -            // bootstrap.js (it's the original implementation of
> -            // highlighter()). In particular I have no clue what that regex
> -            // is doing, so don't bother asking.
> -            var arr = this.query.split(' ');
> -            for (i in arr) {
> -                query = arr[i];
> -                if (!query)
> -                    continue;
> -                var q = query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,
> -                                          '\\$&')
> -                item = item.replace(new RegExp('(' + q + ')', 'ig'), function ($1, match) {
> -                    // We want to replace with <strong>match</strong here,
> -                    // but it's possible another search term will then
> -                    // match part of <strong>.
> -                    //
> -                    // Therefore, replace with two replaceable tokens that
> -                    // no search query is very likely to match...
> -                    return '%%%' + match + '£££'
> -                });
> -            }
> -            return item
> -                .replace(/%%%/g, '<strong>')
> -                .replace(/£££/g, '</strong>');
> -        }
> -    });
> -    // Bind an event disabling the function box and removing the profile
> -    // if the run box is emptied.
> -    element.change(function() {
> -        if (!element.val()) {
> -            this_.id = null;
> -            if (options.cleared)
> -                options.cleared();
> -        }
> -    });
> -}
> -
> -RunTypeahead.prototype = {
> -    update: function (name, id) {
> -        this.element.val(name);
> -        this.id = id;
> -        if (this.options.updated)
> -            this.options.updated(name, id);
> -    },
> -    getSelectedRunId: function() {
> -        return this.id;
> -    }
> -};
> -
> function FunctionTypeahead(element, options) {
>     this.element = element;
>     this.options = options;
> @@ -623,12 +528,6 @@ $(document).ready(function () {
> 
>             return this.data('toolBar');
>         },
> -        runTypeahead: function(options) {
> -            if (options)
> -                this.data('runTypeahead',
> -                          new RunTypeahead(this, options));
> -            return this.data('runTypeahead');
> -        },
>         functionTypeahead: function(options) {
>             if (options)
>                 this.data('functionTypeahead',
> 
> Added: lnt/trunk/lnt/server/ui/static/lnt_run.css
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/ui/static/lnt_run.css?rev=264723&view=auto
> ==============================================================================
> --- lnt/trunk/lnt/server/ui/static/lnt_run.css (added)
> +++ lnt/trunk/lnt/server/ui/static/lnt_run.css Tue Mar 29 07:11:28 2016
> @@ -0,0 +1,20 @@
> +.editable {
> +    position: absolute;
> +    background-color: black;
> +    right: 0px;
> +    top: 0px;
> +    bottom: 0px;
> +    width: 25px;
> +    opacity: 0.5;
> +    text-align: center;
> +    visibility: hidden;
> +}
> +
> +tr:hover > td > .editable {
> +    visibility: visible;
> +    cursor: pointer;
> +}
> +
> +.editable span {
> +    position: relative;
> +}
> \ No newline at end of file
> 
> Added: lnt/trunk/lnt/server/ui/static/lnt_run.js
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/ui/static/lnt_run.js?rev=264723&view=auto
> ==============================================================================
> --- lnt/trunk/lnt/server/ui/static/lnt_run.js (added)
> +++ lnt/trunk/lnt/server/ui/static/lnt_run.js Tue Mar 29 07:11:28 2016
> @@ -0,0 +1,190 @@
> +function RunTypeahead(element, options) {
> +    this.element = element;
> +    this.options = options;
> +    this.id = null;
> +
> +    this_ = this;
> +    element.typeahead({
> +        source: function(query, process) {
> +            $.ajax(options.searchURL, {
> +                dataType: "json",
> +                data: $.extend({}, this_.options.data, {'q': query}),
> +                success: function(data) {
> +                    process(data);
> +                },
> +                error: function(xhr, textStatus, errorThrown) {
> +                    pf_flash_error('accessing URL ' + options.searchURL +
> +                                   '; ' + errorThrown);
> +                }
> +            });
> +        },
> +        // These identity functions are required because the defaults
> +        // assume items are strings, whereas they're objects here.
> +        sorter: function(items) {
> +            // The results should be sorted on the server.
> +            return items;
> +        },
> +        matcher: function(item) {
> +            return item;
> +        },
> +        updater: function(item) {
> +            // FIXME: the item isn't passed in as json any more, it's
> +            // been rendered. Lame. To get around this, hack the
> +            // components of the 2-tuple back apart.
> +            name = item.split(',')[0];
> +            id = item.split(',')[1];
> +            this_.id = id;
> +            
> +            if (options.updated)
> +                options.updated(name, id);
> +            return name;
> +        },
> +        highlighter: function(item) {
> +            // item is a 2-tuple [name, obj].
> +            item = item[0];
> +
> +            // This loop highlights each search term (split by space)
> +            // individually. The content of the for loop is lifted from
> +            // bootstrap.js (it's the original implementation of
> +            // highlighter()). In particular I have no clue what that regex
> +            // is doing, so don't bother asking.
> +            var arr = this.query.split(' ');
> +            for (i in arr) {
> +                query = arr[i];
> +                if (!query)
> +                    continue;
> +                var q = query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,
> +                                          '\\$&')
> +                item = item.replace(new RegExp('(' + q + ')', 'ig'), function ($1, match) {
> +                    // We want to replace with <strong>match</strong here,
> +                    // but it's possible another search term will then
> +                    // match part of <strong>.
> +                    //
> +                    // Therefore, replace with two replaceable tokens that
> +                    // no search query is very likely to match...
> +                    return '%%%' + match + '£££'
> +                });
> +            }
> +            return item
> +                .replace(/%%%/g, '<strong>')
> +                .replace(/£££/g, '</strong>');
> +        }
> +    });
> +    // Bind an event disabling the function box and removing the profile
> +    // if the run box is emptied.
> +    element.change(function() {
> +        if (!element.val()) {
> +            this_.id = null;
> +            if (options.cleared)
> +                options.cleared();
> +        }
> +    });
> +}
> +
> +RunTypeahead.prototype = {
> +    update: function (name, id) {
> +        this.element.val(name);
> +        this.id = id;
> +        if (this.options.updated)
> +            this.options.updated(name, id);
> +    },
> +    getSelectedRunId: function() {
> +        return this.id;
> +    }
> +};
> +
> +function modifyURL(type, id) {
> +    var url = window.location.href;
> +
> +    if (type == 'Current') {
> +        return url.replace(/\d+($|\?)/, id + '?');
> +    }
> +    if (type == 'Previous') {
> +        if (url.indexOf('compare_to=') != -1)
> +            return url.replace(/compare_to=\d+/, 'compare_to=' + id);
> +        else if (url.indexOf('?') != -1)
> +            return url + '&compare_to=' + id;
> +        else
> +            return url + '?compare_to=' + id;
> +    }
> +    if (type == 'Baseline') {
> +        if (url.indexOf('baseline=') != -1)
> +            return url.replace(/baseline=\d+/, 'baseline=' + id);
> +        else if (url.indexOf('?') != -1)
> +            return url + '&baseline=' + id;
> +        else
> +            return url + '?baseline=' + id;
> +    }
> +}
> +
> +function makeEditable(type, td) {
> +    var exterior = $('<span></span>');
> +
> +    var content = $('<input type="text">')
> +        .addClass("input-large");
> +    
> +    var title = $('<b></b>')
> +        .text('Choose run...')
> +        .append($('<span>×</span>')
> +                .addClass('close'));
> +
> +    var enableTypeahead = function() {
> +        content.runTypeahead({
> +            searchURL: g_urls.search,
> +            data: {'m': g_machine},
> +            updated: function(name, id) {
> +                var url = modifyURL(type, id);
> +                document.location.href = url;
> +            },
> +            cleared: function()  {
> +                console.log("jjg");
> +            }
> +        });
> +        title.children('span').first().click(function() {
> +            exterior.popover('hide');
> +        });
> +    }
> +
> +    var h = td.height() / 2;
> +    
> +    var interior = $('<span></span>')
> +        .css({marginTop: (h - 6) + 'px'})
> +        .addClass('icon-pencil icon-white');
> +
> +    exterior.addClass('editable')
> +        .append(interior)
> +        .popover({
> +            title: title,
> +            content: content,
> +            html: true,
> +            trigger: 'click'
> +        }).on('shown.bs.popover', function(){
> +            enableTypeahead();
> +        });
> +    
> +    td.css({position: 'relative'})
> +        .append(exterior);
> +}
> +
> +$(function() {
> +
> +    jQuery.fn.extend({
> +        runTypeahead: function(options) {
> +            if (options)
> +                this.data('runTypeahead',
> +                          new RunTypeahead(this, options));
> +            return this.data('runTypeahead');
> +        }
> +    });
> +    
> +    makeEditable( 'Current', $('#run-table-Current')
> +                  .children('td')
> +                  .last());
> +    makeEditable( 'Previous', $('#run-table-Previous')
> +                  .children('td')
> +                  .last());
> +    makeEditable( 'Baseline', $('#run-table-Baseline')
> +                  .children('td')
> +                  .last());
> +
> +});
> 
> Modified: lnt/trunk/lnt/server/ui/templates/reporting/runs.html
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/ui/templates/reporting/runs.html?rev=264723&r1=264722&r2=264723&view=diff
> ==============================================================================
> --- lnt/trunk/lnt/server/ui/templates/reporting/runs.html (original)
> +++ lnt/trunk/lnt/server/ui/templates/reporting/runs.html Tue Mar 29 07:11:28 2016
> @@ -96,7 +96,7 @@
>   <tbody>
>   {% for title, r in (('Current', run), ('Previous', compare_to), ('Baseline', baseline)) %}
>     {% if r %}
> -    <tr>
> +    <tr id="run-table-{{ title }}">
>       <td style="{{ styles['td'] }}"><a href="{{ ts_url }}/{{ r.id }}">{{ title }}</a></td>
>       <td style="{{ styles['td'] }}"><a href="{{ ts_url }}/order/{{ r.order.id }}">{{ r.order.llvm_project_revision }}</a></td>
>       <td style="{{ styles['td'] }}"><span class="utctime">{{ r.start_time.isoformat() }}</span></td>
> @@ -106,7 +106,9 @@
>       {% endif %}
>     </tr>
>     {% else %}
> -      <tr><td style="{{ styles['td'] }}" colspan="5">No {{ title }} Run</td></tr>
> +    <tr id="run-table-{{ title }}">
> +      <td style="{{ styles['td'] }}" colspan="5">No {{ title }} Run</td>
> +    </tr>
>     {% endif %}
>   {% endfor %}
>  </tbody>
> 
> Modified: lnt/trunk/lnt/server/ui/templates/v4_profile.html
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/ui/templates/v4_profile.html?rev=264723&r1=264722&r2=264723&view=diff
> ==============================================================================
> --- lnt/trunk/lnt/server/ui/templates/v4_profile.html (original)
> +++ lnt/trunk/lnt/server/ui/templates/v4_profile.html Tue Mar 29 07:11:28 2016
> @@ -7,6 +7,9 @@
> {% block head %}
>   <script language="javascript" type="text/javascript"
>           src="{{ url_for('.static',
> +               filename='lnt_run.js') }}"></script>
> +  <script language="javascript" type="text/javascript"
> +          src="{{ url_for('.static',
>                filename='lnt_profile.js') }}"></script>
>   <script language="javascript" type="text/javascript"
>           src="{{ url_for('.static',
> 
> Modified: lnt/trunk/lnt/server/ui/templates/v4_run.html
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/ui/templates/v4_run.html?rev=264723&r1=264722&r2=264723&view=diff
> ==============================================================================
> --- lnt/trunk/lnt/server/ui/templates/v4_run.html (original)
> +++ lnt/trunk/lnt/server/ui/templates/v4_run.html Tue Mar 29 07:11:28 2016
> @@ -15,8 +15,16 @@
>                      v4_url_for("v4_machine", id=machine.id))] %}
> 
> {% block head %}
> +  <script>
> +    g_urls = {{urls|tojson|safe}};
> +    g_machine = {{run.machine.id}};
> +  </script>
> +
>   <script src="{{ url_for('.static', filename='popup.js') }}"></script>
>   <script src="{{ url_for('.static', filename='sorttable.js') }}"></script>
> +  <script src="{{ url_for('.static', filename='lnt_run.js') }}"></script>
> +  <link href="{{ url_for('.static', filename='lnt_run.css') }}" rel="stylesheet" media="screen"/>
> +
>   <script type="text/javascript">
>   function selectAll(source) {
>     $(source).closest("table").find("input:checkbox").prop("checked", source.checked);
> 
> Modified: lnt/trunk/lnt/server/ui/views.py
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/ui/views.py?rev=264723&r1=264722&r2=264723&view=diff
> ==============================================================================
> --- lnt/trunk/lnt/server/ui/views.py (original)
> +++ lnt/trunk/lnt/server/ui/views.py Tue Mar 29 07:11:28 2016
> @@ -4,6 +4,7 @@ import re
> import tempfile
> import time
> import copy
> +import json
> 
> import flask
> from flask import abort
> @@ -30,6 +31,7 @@ import lnt.server.ui.util
> import lnt.server.reporting.dailyreport
> import lnt.server.reporting.summaryreport
> import lnt.server.db.rules_manager
> +import lnt.server.db.search
> from collections import namedtuple
> from lnt.util import async_ops
> 
> @@ -417,12 +419,16 @@ def v4_run(id):
> 
>         return flask.jsonify(**json_obj)
> 
> +    urls = {
> +        'search': v4_url_for('v4_search')
> +    }
>     return render_template(
>         "v4_run.html", ts=ts, options=options,
>         metric_fields=list(ts.Sample.get_metric_fields()),
>         test_info=test_info, analysis=lnt.server.reporting.analysis,
>         test_min_value_filter=test_min_value_filter,
> -        request_info=info)
> +        request_info=info, urls=urls
> +)
> 
> @v4_route("/order/<int:id>")
> def v4_order(id):
> @@ -1233,3 +1239,26 @@ def health():
>     if explode:
>         return msg, 500
>     return msg, 200
> +
> + at v4_route("/search")
> +def v4_search():
> +    def _isint(i):
> +        try:
> +            int(i)
> +            return True
> +        except:
> +            return False
> +
> +    ts = request.get_testsuite()
> +    query = request.args.get('q')
> +    l = request.args.get('l', 8)
> +    default_machine = request.args.get('m', None)
> +
> +    assert query
> +    results = lnt.server.db.search.search(ts, query, num_results=l,
> +                                          default_machine=default_machine)
> +
> +    return json.dumps(
> +        [('%s #%s' % (r.machine.name, r.order.llvm_project_revision),
> +          r.id)
> +         for r in results])
> 
> 
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits



More information about the llvm-commits mailing list