Merge "Commit View: multiple commits, search dialog"
diff --git a/app/commit_view.html b/app/commit_view.html
index 5ecb306..800e185 100644
--- a/app/commit_view.html
+++ b/app/commit_view.html
@@ -24,7 +24,6 @@
<!-- The majority of our javascript -->
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.js"></script>
-<script type="text/javascript" src="/js/jstree/jquery.jstree.js"></script>
<link rel="stylesheet" type="text/css" href="/js/jquery-ui/css/custom-theme/jquery-ui-1.8.23.custom.css" />
<script type="text/javascript" src="/js/jquery-ui/js/jquery-ui-1.8.21.custom.min.js"></script>
<script type="text/javascript" src="/js/json2.js"></script>
@@ -39,26 +38,7 @@
<script type="text/javascript">
google.load('visualization', '1.0', {'packages':['corechart', 'table']});
-function ChartFillerCaller(input) {
- input = input.split(',');
- var metric = input[0];
- var config = input[1];
- var filename = input[2];
- var commit = input[3];
- var baseline = input[4];
- $("#chartdialog").dialog('option', 'title', input.slice(0, 3).join(', '));
- ChartFiller_Commit(metric, config, filename, commit, baseline, 'chartdiv',
- 'chartdialog', null, 'configInfo', 'status');
-}
-
-function linkToBaseline(baseline) {
- // Note: with js, there is no way to force this to open in a new tab, rather
- // than a window.
- var url = "/commit_viewer/" + baseline;
- newWindow = window.open(url, '_blank');
- newWindow.focus();
-}
// These apply to all charts in commit_view.html
var chartOptions = {width: 720,
@@ -67,7 +47,6 @@
$(function() {
$("#chartdialog").dialog({ autoOpen: false});
$("#configInfo").dialog({ autoOpen: false, width: 800, position: ['right', 60] });
- google.load('visualization', '1.0', {'packages':['corechart', 'table']});
$("#chartdialog").bind("dialogclose", function(event, ui) {
$('#status').html("");
diff --git a/app/commit_view.py b/app/commit_view.py
index bd05634..b8b1ce2 100644
--- a/app/commit_view.py
+++ b/app/commit_view.py
@@ -26,15 +26,14 @@
import main
import drilldown
import logging
+import urllib
# A global variable to determine how important a test run is (percent improvement)
THRESHOLD_HIGH = 1.0
THRESHOLD_LOW = 0.1
-
-class CommitQueryHandler(webapp.RequestHandler):
- def get(self):
- self.response.out.write(template.render("commit_viewer.html", {}))
+# ------------------------------------------------------------------------------
+# Helpers for both handlers
@cache_result()
def get_adhoc_improvement(metrics, config, filenames, commit):
@@ -65,26 +64,144 @@
'filename': f, 'value': composite})
return response
+def run_formatter(commit, resps):
+ '''A helper function to format the run data of a commit'''
+ formatted_resps = []
+ for row in resps:
+ if row['metric'] == 'Time(us)' or row['metric'] == 'Bitrate' or row['metric'] == 'target_bitrate':
+ continue
+ if row['filename'][0] == '~':
+ continue
+
+ if not row['baseline']:
+ row['class'] = 'unknown'
+ elif abs(row['value']) > THRESHOLD_HIGH:
+ if row['value'] > 0:
+ row['class'] = 'good major'
+ else:
+ row['class'] = 'bad major'
+
+ elif abs(row['value']) > THRESHOLD_LOW:
+ if row['value'] > 0:
+ row['class'] = 'good minor'
+ else:
+ row['class'] = 'bad minor'
+
+ else: # We are right in the middle
+ row['class'] = "unchanged"
+
+ # This is a bit messy, but it works (mixing django and
+ # javascript doesn't work like you would hope)
+ if row['baseline']:
+ row['clickcommand'] = str("javascript: ChartFillerCaller(" + "\'" +
+ row['metric'].encode('ascii', 'ignore') + "," +
+ row['config'].encode('ascii', 'ignore') + "," +
+ row['filename'].encode('ascii', 'ignore') + ',' +
+ commit['commitid'].encode('ascii', 'ignore') + "," +
+ row['baseline'].encode('ascii', 'ignore') + "\'"+ ')')
+ formatted_resps.append(row)
+
+ resp_rows = {}
+ for resp in formatted_resps:
+ key = (resp['metric'], resp['config'])
+ row = resp_rows.setdefault(key, [])
+ row.append(resp)
+ formatted_resps=[]
+ for key in sorted(resp_rows.keys()):
+ formatted_resps.append({
+ 'metric': key[0],
+ 'config': key[1],
+ 'runs': sorted(resp_rows[key], key=lambda x: x['filename']),
+ })
+
+ return formatted_resps
+
+
+# ------------------------------------------------------------------------------
+
+class CommitQueryHandler(webapp.RequestHandler):
+ def get(self):
+ # We get the 5 most recent commits
+ query = model.Commit.all()
+
+ # We use this if we just want the newest 5, regardless of run data
+ #current_commits = query.order("-commit_time").fetch(limit=5)
+
+ # test data
+ current_commits = ['0030303b6949ba2d3391f3ae400213acc0e80db7',
+ '062864f4cc2179b6f222ae337538c18bfd08037a',
+ '05bde9d4a4b575aaadd9b6f5d0f82826b1cb4900',
+ '0c483d6b683fa4313cf7dadf448a707fe32714a4']
+
+
+ formatted_commits = [] # These are commit_dict, formatted_resps pairs
+
+ for commit in current_commits:
+ # We get all the data about the commit we need
+ #commit_data = commit
+
+ # only for test data
+ commit_data = model.commits()[commit]
+
+ message = commit_data.message.split("\n")
+ commit = {'commit': commit_data.key().name()[:9],
+ 'commitid': commit_data.key().name(),
+ 'author': commit_data.author,
+ 'subject': message[0],
+ 'body': message[1:],
+ 'date': commit_data.author_time,
+ 'branches': commit_data.branches}
+ commitid = commit_data.key().name()
+
+ # We need (metric, config, fileset) tuples
+ resps = []
+ query = model.CodecMetricIndex.all()
+ query = query.filter('commit =', commitid)
+ for item in query:
+ resps.extend(get_adhoc_improvement(item.metrics, item.config_name,
+ item.files, commitid))
+
+ # Now that we have our responses, we can format them by seeing if
+ # the value crosses our threshold
+ formatted_resps = run_formatter(commit, resps)
+
+ formatted_commits.append((commit, formatted_resps))
+
+ values = {
+ "user": users.get_current_user(),
+ "login_url": users.create_login_url("/"),
+ "logout_url": users.create_logout_url("/"),
+ "formatted_commits" : formatted_commits,
+ }
+ self.response.out.write(template.render("commit_viewer.html", values))
+
class CommitDisplayHandler(webapp.RequestHandler):
- def get(self, commit, optThreshold):
+ def get(self, commit):
+ commit = urllib.unquote(commit)
- # TODO: will the threshold ever be adjustable?
- if not optThreshold:
- threshold = THRESHOLD_HIGH # Go back to default if none given
- else:
- threshold = float(optThreshold)
-
- # We start by seeing if its a valid commit
+ # We start by seeing if its a valid commit (or email address)
indexes = model.CodecMetricIndex.all(keys_only = True)
indexes = indexes.filter('commit =', commit)
keys = [k.parent() for k in indexes]
if len(keys) == 0:
- self.error(404)
+
+ values = {
+ "user": users.get_current_user(),
+ "login_url": users.create_login_url("/"),
+ "logout_url": users.create_logout_url("/"),
+ 'commit': commit,
+ 'error': True,
+ 'errormessage': "There are no matching results for this search.",
+ }
+
+ html = template.render("commit_view.html", values)
+ self.response.out.write(html)
+
return
+ # We get all the data about the commit we need
commit_data = model.commits()[commit]
message = commit_data.message.split("\n")
- nonempty_lines = sum(map(bool, message))
commit = {'commit': commit_data.key().name()[:9],
'commitid': commit_data.key().name(),
'author': commit_data.author,
@@ -104,64 +221,23 @@
# Now that we have our responses, we can format them by seeing if
# the value crosses our threshold
- formatted_resps = []
- for row in resps:
- if row['metric'] == 'Time(us)' or row['metric'] == 'Bitrate' or row['metric'] == 'target_bitrate':
- continue
- if row['filename'][0] == '~':
- continue
+ formatted_resps = run_formatter(commit, resps)
- if not row['baseline']:
- row['class'] = 'unknown'
- elif abs(row['value']) > THRESHOLD_HIGH:
- if row['value'] > 0:
- row['class'] = 'good major'
- else:
- row['class'] = 'bad major'
+ values = {
+ "user": users.get_current_user(),
+ "login_url": users.create_login_url("/"),
+ "logout_url": users.create_logout_url("/"),
+ 'commit': commit,
+ 'runs': formatted_resps
+ }
- elif abs(row['value']) > THRESHOLD_LOW:
- if row['value'] > 0:
- row['class'] = 'good minor'
- else:
- row['class'] = 'bad minor'
-
- else: # We are right in the middle
- row['class'] = "unchanged"
-
- # This is a bit messy, but it works (mixing django and
- # javascript doesn't work like you would hope)
- if row['baseline']:
- row['clickcommand'] = str("javascript: ChartFillerCaller(" + "\'" +
- row['metric'].encode('ascii', 'ignore') + "," +
- row['config'].encode('ascii', 'ignore') + "," +
- row['filename'].encode('ascii', 'ignore') + ',' +
- commit['commitid'].encode('ascii', 'ignore') + "," +
- row['baseline'].encode('ascii', 'ignore') + "\'"+ ')')
- formatted_resps.append(row)
-
- # TODO: How do we want to sort this?
- resp_rows = {}
- for resp in formatted_resps:
- key = (resp['metric'], resp['config'])
- row = resp_rows.setdefault(key, [])
- row.append(resp)
- formatted_resps=[]
- for key in sorted(resp_rows.keys()):
- formatted_resps.append({
- 'metric': key[0],
- 'config': key[1],
- 'runs': sorted(resp_rows[key], key=lambda x: x['filename']),
- })
-
- html = template.render("commit_view.html", {'commit': commit,
- 'runs': formatted_resps,
- 'threshold': threshold})
+ html = template.render("commit_view.html", values)
self.response.out.write(html)
def main_func():
application = webapp.WSGIApplication([
('/commit_viewer/', CommitQueryHandler),
- ('/commit_viewer/(.*)/(.*)', CommitDisplayHandler),
+ ('/commit_viewer/(.*)', CommitDisplayHandler),
], debug=True)
webapp_util.run_wsgi_app(application)
diff --git a/app/commit_viewer.html b/app/commit_viewer.html
index a577e09..f2bb98a 100644
--- a/app/commit_viewer.html
+++ b/app/commit_viewer.html
@@ -26,35 +26,35 @@
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
+<!-- The majority of our javascript -->
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.js"></script>
+ <link rel="stylesheet" type="text/css" href="/js/jquery-ui/css/custom-theme/jquery-ui-1.8.23.custom.css" />
+<script type="text/javascript" src="/js/jquery-ui/js/jquery-ui-1.8.21.custom.min.js"></script>
+<script type="text/javascript" src="/js/json2.js"></script>
+
+<!-- our js utils file, shared with index.html -->
+<script type="text/javascript" src="/js/utils.js"></script>
+<script type="text/javascript" src="/js/chartutils.js"></script>
+<script type="text/javascript" src="/js/commit_search.js"></script>
+
+<!-- the AJAX API for google charts -->
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
-
<script type="text/javascript">
-
-// We load this here as well as in commit_view.html to avoid errors
google.load('visualization', '1.0', {'packages':['corechart', 'table']});
+// These apply to all charts in commit_view.html
+var chartOptions = {width: 720,
+ height: 500};
-function searchcommits(form){
- var commitid = form.query.value;
+$(function() {
+ $("#chartdialog").dialog({ autoOpen: false});
+ $("#configInfo").dialog({ autoOpen: false, width: 800, position: ['right', 60] });
- // We send a request to the backend to see if it is a valid commit
- var url = "/commit_viewer/" + commitid + '/';
- $.ajax({
- type: "GET",
- url: url,
- success: function(response){
- $('#searchResults').html(response);
- //$("#accordion").accordion({collapsible: true});
- form.query.value = '';
- },
- error: function(xhr, ajaxOptions, thrownError) {
- $("#searchResults").html("Not a valid commit. Try again");
- form.query.value = '';
- },
+ $("#chartdialog").bind("dialogclose", function(event, ui) {
+ $('#status').html("");
});
+});
-}
</script>
<link rel="shortcut icon" href="/images/webm-48px.png">
@@ -110,32 +110,141 @@
</header>
- <div class="container-fluid">
+<div class="container-fluid">
+<table class="multicommits" width="100%">
+<tr><td>
<div class="row-fluid" id="">
<div class="span6">
+ <h3>Most Recent Commits</h3>
</div>
<div class="span6">
- <a href="/" id="mainpbutton" class="btn pull-right">Back to Dashboard</a>
+ <a href="#searchModal" data-toggle="modal" id="searchbutton" class="btn pull-right">Search Commits</a>
</div>
</div>
+</tr></td>
+
+{% for commit, runs in formatted_commits %}
+<tr>
+<td>
<div class="row-fluid" id="">
<div class="span12">
-
- <div id="search">
- <div id="searchCommand"></div>
- <form name="myform" action="" class="form-inline">
- <input type='text' name='query' class="input-xxlarge" placeholder="Full commit hash">
- <input type='button' id="submitButton" value="Search" onClick="javascript: searchcommits(this.form)" class="btn">
- <span class="help-block">Example block-level help text here.</span>
- </form>
- </div>
-
- <div id="searchResults"></div>
+ <h4>Commit Summary: {{ commit.commit }}</h4>
</div>
</div>
+
+ <div class="row-fluid" id="">
+ <div class="span6">
+
+ <table class="commitlog">
+ <tr id="{{commit.commit}}_row">
+ <td>{{ commit.commit }}</td>
+ <td>{{ commit.date }}</td>
+ </tr>
+ <tr id="{{commit.commit}}_row2">
+ <td></td>
+ <td>{{ commit.author }}</td>
+ </tr>
+ <tr id="{{commit.commit}}_row3">
+ <td></td>
+ <td>
+ <div><strong>{{ commit.subject }}</strong></div>
+ <div id="{{commit.commit}}_body1">
+ {% for line in commit.body %}
+ {{ line }}<br>
+ {% endfor %}
+ </div>
+ </td>
+ </tr>
+ <tr id="{{commit.commit}}_row4">
+ <td></td>
+ <td>
+ <div> Branches:</div>
+ <div id="{{commit.commit}}_body2">
+ {% if commit.branches %}
+ {% for br in commit.branches %}
+ {{ br }}<br>
+ {% endfor %}
+ {% else %}
+ None
+ {% endif %}
+ </div>
+ </td>
+ </tr>
+ </table>
+ </div>
+ <div class="span6" style="overflow: auto;">
+
+ <!-- Hide the threshold for now
+ Threshold: {{threshold}}% <input type="button" value="Change" onclick="openThresholdDialog()"/>
+ <br>
+
+ <div id="thresholdDialog"> Please choose a new threshold:
+ <form id="thform" name="thform" onsubmit="submitThreshold()" style="float:left">
+ <input type='text' name='newThreshold' />
+ <input type='button' value="Submit" onclick="submitThreshold()">
+ </form>
+ </div>
+ -->
+
+ <!-- Elif statement not supported in Django 1.2 -->
+ <table class="resultgrid">
+ {% for run in runs %}
+ <tr>
+ <th>{{ run.metric }}</th>
+ <th>{{ run.config }}</th>
+ <td>
+ {% for e in run.runs %}
+ {% if e.class == 'good major' or e.class == 'good minor' %}
+ <dt class="{{e.class}}" onclick='{{e.clickcommand}}' title="{{ e.filename }}, {{e.value}}" >▲</dt>
+ {% else %}
+ {% if e.class == 'bad major' or e.class == 'bad minor' %}
+ <dt class="{{e.class}}" onclick='{{e.clickcommand}}' title="{{ e.filename }}, {{e.value}}">▼</dt>
+ {% else %}
+ <dt class="unchanged" onclick='{{e.clickcommand}}' title="{{ e.filename }}, {{e.value}}">◼</dt>
+ {% endif %}
+ {% endif %}
+ {% endfor %}
+ {% endfor %}
+ </td>
+ </tr>
+ </table>
+ </div>
+
+ </div>
</div>
+</td>
+</tr>
+{% endfor %}
+</table>
+<div> <!-- end of container fluid -->
+
+<!-- The search dialog -->
+ <div class="modal hide fade" id="searchModal" tabindex="-1" role="dialog" aria-labelledby="searchModalLabel" aria-hidden="true">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
+ <h3 id="searchModalLabel">Search for Commits</h3>
+ </div>
+ <div class="modal-body">
+
+ <div id="searchCommand" style="float:left">Please enter a commit to display, or an email to look for:</div>
+ <form name="myform">
+ <input type='text' name='query' />
+ <input type='button' id="submitButton" value="Search" onClick="javascript: searchcommits(this.form)">
+ </form>
+
+ </div>
+ </div>
+
+
+<div id="chartdialog">
+ <div id="chartdiv"></div>
+ <div id="status"></div>
+</div>
+<div id="configInfo"></div>
+<script src="/js/bootstrap.min.js"></script>
+
</body>
</html>
diff --git a/app/js/commit_search.js b/app/js/commit_search.js
index d665d62..b00edfc 100644
--- a/app/js/commit_search.js
+++ b/app/js/commit_search.js
@@ -1,6 +1,27 @@
// search.js
// Contains utilities to search for a commit
+function ChartFillerCaller(input) {
+ input = input.split(',');
+ var metric = input[0];
+ var config = input[1];
+ var filename = input[2];
+ var commit = input[3];
+ var baseline = input[4];
+
+ $("#chartdialog").dialog('option', 'title', input.slice(0, 3).join(', '));
+ ChartFiller_Commit(metric, config, filename, commit, baseline, 'chartdiv',
+ 'chartdialog', null, 'configInfo', 'status');
+}
+
+function linkToBaseline(baseline) {
+ // Note: with js, there is no way to force this to open in a new tab, rather
+ // than a window.
+ var url = "/commit_viewer/" + baseline;
+ newWindow = window.open(url, '_blank');
+ newWindow.focus();
+}
+
function searchcommits(form){