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

James Molloy via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 27 23:13:30 PDT 2016


Hi Chris,

Hmm, that’s interesting. Yes, the intent is that when a run is picked the page immediately refreshes. I’ve tested with safari and chrome (both on OS X) and it appears to work here - what environment are you using? is a javascript error being thrown?

Cheers,

James

> On 27 Apr 2016, at 22:22, Chris Matthews <chris.matthews at apple.com> wrote:
>
> 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
>

IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.


More information about the llvm-commits mailing list