Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
  • 12 commits
  • 21 files changed
  • 0 comments
  • 2 contributors

Showing 21 changed files with 1,203 additions and 385 deletions. Show diff stats Hide diff stats

  1. +0 5 crontab.txt
  2. +2 0  datazilla/controller/admin/collection.py
  3. +1 1  datazilla/controller/admin/metrics/perftest_metrics.py
  4. +8 0 datazilla/controller/admin/refdata/pushlog_refdata.py
  5. +80 44 datazilla/controller/admin/testdata.py
  6. +144 28 datazilla/model/base.py
  7. +39 15 datazilla/model/sql/hgmozilla.json
  8. +16 20 datazilla/model/sql/perftest.json
  9. +0 2  datazilla/model/utils.py
  10. +6 0 datazilla/webapp/apps/datazilla/refdata/pushlog_views.py
  11. +2 0  datazilla/webapp/apps/datazilla/refdata/urls_no_project.py
  12. +42 1 datazilla/webapp/apps/datazilla/testdata/views.py
  13. +0 35 datazilla/webapp/apps/datazilla/views.py
  14. +67 5 datazilla/webapp/static/css/summary.css
  15. +50 7 datazilla/webapp/static/js/metric_summary/MetricDashboardComponent.js
  16. +29 3 datazilla/webapp/static/js/metric_summary/MetricGridComponent.js
  17. +60 3 datazilla/webapp/static/js/metric_summary/MetricSummaryPage.js
  18. +35 13 datazilla/webapp/static/js/metric_summary/TestPagesComponent.js
  19. +581 187 datazilla/webapp/static/js/metric_summary/TrendLineComponent.js
  20. +41 15 datazilla/webapp/templates/metrics.summary.html
  21. +0 1  datazilla/webapp/urls.py
5 crontab.txt
@@ -2,11 +2,6 @@
2 2 PYTHON_ROOT=/usr/bin/
3 3 DATAZILLA_HOME=/usr/local/datazilla
4 4
5   -*/2 * * * * $PYTHON_ROOT/python $DATAZILLA_HOME/manage.py populate_summary_cache --build --cache --project stoneridge
6   -*/2 * * * * $PYTHON_ROOT/python $DATAZILLA_HOME/manage.py populate_summary_cache --build --cache --project b2g
7   -*/2 * * * * $PYTHON_ROOT/python $DATAZILLA_HOME/manage.py populate_summary_cache --build --cache --project jetperf
8   -*/2 * * * * $PYTHON_ROOT/python $DATAZILLA_HOME/manage.py populate_summary_cache --build --cache --project test
9   -
10 5 */2 * * * * $PYTHON_ROOT/python $DATAZILLA_HOME/manage.py populate_test_collections --load --project stoneridge
11 6 */2 * * * * $PYTHON_ROOT/python $DATAZILLA_HOME/manage.py populate_test_collections --load --project b2g
12 7 */2 * * * * $PYTHON_ROOT/python $DATAZILLA_HOME/manage.py populate_test_collections --load --project jetperf
2  datazilla/controller/admin/collection.py
@@ -39,6 +39,8 @@ def load_test_collection(project):
39 39 id = ptm.set_test_collection(new_name, "")
40 40 ptm.set_test_collection_map(id, product_names[ new_name ])
41 41
  42 + ptm.cache_ref_data()
  43 +
42 44 ptm.disconnect()
43 45
44 46 def get_test_collection_name(product, version, branch):
2  datazilla/controller/admin/metrics/perftest_metrics.py
@@ -139,7 +139,7 @@ def check_run_conditions(test_name, rep_count, push_node, branch, debug):
139 139 println(u"Cannot run {0}".format(test_name), debug)
140 140 return False
141 141
142   - if rep_count < 3:
  142 + if rep_count < 5:
143 143 #If we don't have more than one replicate we cannot
144 144 #run any of the existing metric tests
145 145 println(
8 datazilla/controller/admin/refdata/pushlog_refdata.py
@@ -62,6 +62,14 @@ def get_all_branches():
62 62 plm.disconnect()
63 63 return branches
64 64
  65 +def get_branch_uri(branch):
  66 + """Return the requested branch uri. If no branch is requested return all
  67 + branch uris."""
  68 + plm = factory.get_plm()
  69 + branches = plm.get_branch_uri(branch)
  70 + plm.disconnect()
  71 + return branches
  72 +
65 73
66 74 def get_db_size():
67 75 """Return the database size, on disk in MB"""
124 datazilla/controller/admin/testdata.py
@@ -5,11 +5,12 @@
5 5 import json
6 6
7 7 from datazilla.model import factory
  8 +from datazilla.model import utils
8 9
9 10 def get_testdata(
10   - project, branch, revision, os_name=None, os_version=None,
11   - branch_version=None, processor=None, build_type=None, test_name=None,
12   - page_name=None):
  11 + project, branch, revision, product_name=None, os_name=None,
  12 + os_version=None, branch_version=None, processor=None,
  13 + build_type=None, test_name=None, page_name=None):
13 14 """Return test data based on the parameters and optional filters."""
14 15
15 16 ptm = factory.get_ptm(project)
@@ -17,8 +18,8 @@ def get_testdata(
17 18
18 19 # get the testrun ids from perftest
19 20 test_run_ids = ptm.get_test_run_ids(
20   - branch, revision, os_name, os_version, branch_version, processor,
21   - build_type, test_name
  21 + branch, [revision], product_name, os_name, os_version,
  22 + branch_version, processor, build_type, test_name
22 23 )
23 24
24 25 blobs = ptrdm.get_object_json_blob_for_test_run(test_run_ids)
@@ -56,9 +57,9 @@ def get_testdata(
56 57
57 58
58 59 def get_metrics_data(
59   - project, branch, revision, os_name=None, os_version=None,
60   - branch_version=None, processor=None, build_type=None, test_name=None,
61   - page_name=None
  60 + project, branch, revision, product_name=None, os_name=None,
  61 + os_version=None, branch_version=None, processor=None, build_type=None,
  62 + test_name=None, page_name=None
62 63 ):
63 64 """Return metrics data based on the parameters and optional filters."""
64 65
@@ -67,8 +68,8 @@ def get_metrics_data(
67 68
68 69 # get the testrun ids from perftest
69 70 test_run_ids = ptm.get_test_run_ids(
70   - branch, revision, os_name, os_version, branch_version, processor,
71   - build_type, test_name
  71 + branch, [revision], product_name, os_name, os_version,
  72 + branch_version, processor, build_type, test_name
72 73 )
73 74
74 75 #test page metric
@@ -82,33 +83,44 @@ def get_metrics_data(
82 83 return metrics_data
83 84
84 85 def get_metrics_summary(
85   - project, branch, revision, os_name=None, os_version=None,
86   - branch_version=None, processor=None, build_type=None, test_name=None
  86 + project, branch, revision, product_name=None, os_name=None,
  87 + os_version=None, branch_version=None, processor=None, build_type=None,
  88 + test_name=None, pushlog_project=None
87 89 ):
88 90 """Return a metrics summary based on the parameters and optional filters."""
89 91
  92 + plm = factory.get_plm(pushlog_project)
90 93 ptm = factory.get_ptm(project)
91 94 mtm = factory.get_mtm(project)
92 95
93 96 # get the testrun ids from perftest
94 97 test_run_ids = ptm.get_test_run_ids(
95   - branch, revision, os_name, os_version, branch_version, processor,
96   - build_type, test_name
  98 + branch, [revision], product_name, os_name, os_version,
  99 + branch_version, processor, build_type, test_name
97 100 )
98 101
99 102 #test page metric
100 103 metrics_data = mtm.get_metrics_summary(test_run_ids)
101 104
  105 + #get push info
  106 + push_data = plm.get_node_from_revision(revision, branch)
  107 + metrics_data['push_data'] = push_data
  108 +
  109 + #get the products associated with this revision/branch combination
  110 + products = ptm.get_revision_products(revision, branch)
  111 + metrics_data['products'] = products
  112 +
  113 + plm.disconnect()
102 114 ptm.disconnect()
103 115 mtm.disconnect()
104 116
105 117 return metrics_data
106 118
107 119 def get_metrics_pushlog(
108   - project, branch, revision, os_name=None, os_version=None, branch_version=None,
109   - processor=None, build_type=None, test_name=None, page_name=None,
110   - days_ago=None, pushes_before=None, pushes_after=None, numdays=None,
111   - pushlog_project=None
  120 + project, branch, revision, product_name=None, os_name=None,
  121 + os_version=None, branch_version=None, processor=None, build_type=None,
  122 + test_name=None, page_name=None, days_ago=None, pushes_before=None,
  123 + pushes_after=None, numdays=None, pushlog_project=None
112 124 ):
113 125 """Return a metrics summary based on the parameters and optional filters."""
114 126
@@ -116,53 +128,59 @@ def get_metrics_pushlog(
116 128 ptm = factory.get_ptm(project)
117 129 mtm = factory.get_mtm(project)
118 130
119   - pushlog = {}
120   - if days_ago > 0:
121   - pushlog = plm.get_branch_pushlog(None, days_ago, numdays, branch)
122   - else:
123   - pushlog = plm.get_branch_pushlog_by_revision(
124   - revision, branch, pushes_before, pushes_after
  131 + aggregate_pushlog, changeset_lookup = plm.get_branch_pushlog_by_revision(
  132 + revision, branch, pushes_before, pushes_after
125 133 )
126 134
127   - aggregate_pushlog = []
128 135 pushlog_id_index_map = {}
  136 + all_revisions = []
  137 +
  138 + for index, node in enumerate(aggregate_pushlog):
  139 +
  140 + pushlog_id_index_map[node['pushlog_id']] = index
  141 +
  142 + aggregate_pushlog[index]['metrics_data'] = []
  143 + aggregate_pushlog[index]['dz_revision'] = ""
  144 + aggregate_pushlog[index]['branch_name'] = branch
  145 +
  146 + changesets = changeset_lookup[ node['pushlog_id'] ]
  147 +
  148 + #The revisions associated with a push are returned in reverse order
  149 + #from the pushlog web service. This orders them the same way tbpl
  150 + #does.
  151 + changesets['revisions'].reverse()
  152 +
  153 + #truncate the revision strings and collect them
  154 + for cset_index, revision_data in enumerate(changesets['revisions']):
  155 +
  156 + full_revision = revision_data['revision']
129 157
130   - for node in pushlog:
131   - if node['pushlog_id'] not in pushlog_id_index_map:
132   - node_struct = {
133   - 'revisions':[],
134   - 'dz_revision':"",
135   - 'branch_name':node['name'],
136   - 'date':node['date'],
137   - 'push_id':node['push_id'],
138   - 'pushlog_id':node['pushlog_id'],
139   - 'metrics_data':[],
140   - }
  158 + revision = mtm.truncate_revision(full_revision)
  159 + changesets['revisions'][cset_index]['revision'] = revision
141 160
142   - aggregate_pushlog.append(node_struct)
143   - index = len(aggregate_pushlog) - 1
  161 + all_revisions.append(revision)
144 162
145   - pushlog_id_index_map[node['pushlog_id']] = index
  163 + aggregate_pushlog[index]['revisions'] = changesets['revisions']
146 164
147   - pushlog_index = pushlog_id_index_map[ node['pushlog_id'] ]
148   - revision = mtm.truncate_revision(node['node'])
149   - aggregate_pushlog[pushlog_index]['revisions'].append(revision)
150 165
151 166 pushlog_id_list = pushlog_id_index_map.keys()
152 167
153 168 # get the testrun ids from perftest
154 169 filtered_test_run_ids = ptm.get_test_run_ids(
155   - branch, None, os_name, os_version, branch_version, processor,
156   - build_type, test_name, page_name
  170 + branch, all_revisions, product_name, os_name, os_version,
  171 + branch_version, processor, build_type, test_name
157 172 )
158 173
  174 + # get the test run ids associated with the pushlog ids
159 175 pushlog_test_run_ids = mtm.get_test_run_ids_from_pushlog_ids(
160 176 pushlog_ids=pushlog_id_list
161 177 )
162 178
  179 + # get intersection
163 180 test_run_ids = list( set(filtered_test_run_ids).intersection(
164 181 set(pushlog_test_run_ids)) )
165 182
  183 + # get the metrics data for the intersection
166 184 metrics_data = mtm.get_metrics_data_from_test_run_ids(
167 185 test_run_ids, page_name
168 186 )
@@ -192,3 +210,21 @@ def get_application_log(project, revision):
192 210 log = mtm.get_application_log(revision)
193 211 return log
194 212
  213 +def get_default_version(project, branch, product_name):
  214 +
  215 + ptm = factory.get_ptm(project)
  216 +
  217 + default_version = ptm.get_default_branch_version(
  218 + branch, product_name
  219 + )
  220 +
  221 + ptm.disconnect()
  222 +
  223 + version = ""
  224 + if 'version' in default_version:
  225 + version = default_version['version']
  226 +
  227 + return version
  228 +
  229 +
  230 +
172 datazilla/model/base.py
@@ -85,8 +85,6 @@ def create(cls, host=None, type=None, project=None):
85 85 use "MySQL-Engine", where "Engine" could be "InnoDB", "Aria", etc. Not
86 86 all contenttypes need to be represented; any that aren't will use the
87 87 default (``MySQL-InnoDB``).
88   -
89   -
90 88 """
91 89
92 90 project = project or cls.DEFAULT_PROJECT
@@ -137,6 +135,28 @@ def get_branch_list(self, branch=None):
137 135
138 136 return branch_list
139 137
  138 + def get_branch_uri(self, branch=None):
  139 +
  140 + if branch:
  141 +
  142 + proc = 'hgmozilla.selects.get_branch_uri'
  143 +
  144 + data = self.hg_ds.dhub.execute(
  145 + proc=proc,
  146 + debug_show=self.DEBUG,
  147 + placeholders=[branch],
  148 + return_type='tuple',
  149 + )
  150 + else:
  151 + proc = 'hgmozilla.selects.get_all_branch_uris'
  152 +
  153 + data = self.hg_ds.dhub.execute(
  154 + proc=proc,
  155 + debug_show=self.DEBUG,
  156 + return_type='tuple',
  157 + )
  158 +
  159 + return data
140 160
141 161 def get_all_pushlogs(self):
142 162
@@ -246,25 +266,73 @@ def get_branch_pushlog_by_revision(
246 266
247 267 node = push_data[0]['node']
248 268 push_id = push_data[0]['push_id']
  269 + branch_id = push_data[0]['branch_id']
249 270
250 271 pushes_before_proc = 'hgmozilla.selects.get_push_ids_before_node'
251 272 pushes_after_proc = 'hgmozilla.selects.get_push_ids_after_node'
252 273
  274 + before_boundary = push_id - pushes_before
  275 +
253 276 pushes_before_data = self.hg_ds.dhub.execute(
254 277 proc=pushes_before_proc,
255 278 debug_show=self.DEBUG,
256 279 return_type='tuple',
257   - placeholders=[push_id, branch_name, pushes_before]
  280 + placeholders=[ push_id, before_boundary, branch_id ]
258 281 )
259 282
  283 + after_boundary = push_id + pushes_after
  284 +
260 285 pushes_after_data = self.hg_ds.dhub.execute(
261 286 proc=pushes_after_proc,
262 287 debug_show=self.DEBUG,
263 288 return_type='tuple',
264   - placeholders=[push_id, branch_name, pushes_after]
  289 + placeholders=[ push_id, after_boundary, branch_id ]
  290 + )
  291 +
  292 + #Combine all of the requested push data
  293 + pushlog = pushes_before_data + push_data + pushes_after_data
  294 +
  295 + #Retrieve a complete list of all of the pushlog ids
  296 + pushlog_ids = []
  297 +
  298 + map(
  299 + lambda n: pushlog_ids.append(n['pushlog_id']),
  300 + pushlog
265 301 )
266 302
267   - return pushes_before_data + push_data + pushes_after_data
  303 + #Use a separate query to retrieve associated revisions so
  304 + #we can control the number of pushes by using a LIMIT clause
  305 + changeset_data_proc = 'hgmozilla.selects.get_changeset_data_for_pushes'
  306 +
  307 + #Build the sql WHERE IN clause
  308 + where_in_clause = ','.join( map( lambda v:'%s', pushlog_ids ) )
  309 +
  310 + changeset_data = self.hg_ds.dhub.execute(
  311 + proc=changeset_data_proc,
  312 + debug_show=self.DEBUG,
  313 + return_type='tuple',
  314 + placeholders=pushlog_ids,
  315 + replace=[where_in_clause]
  316 + )
  317 +
  318 + #Aggregate changesets
  319 + changeset_lookup = {}
  320 + for changeset in changeset_data:
  321 + if changeset['pushlog_id'] not in changeset_lookup:
  322 + changeset_struct = {
  323 + 'revisions':[],
  324 + 'pushlog_id':changeset['pushlog_id']
  325 + }
  326 +
  327 + changeset_lookup[ changeset['pushlog_id'] ] = changeset_struct
  328 +
  329 + changeset_lookup[ changeset['pushlog_id'] ]['revisions'].append(
  330 + { 'revision':changeset['node'],
  331 + 'desc':changeset['desc'],
  332 + 'author':changeset['author'] }
  333 + )
  334 +
  335 + return pushlog, changeset_lookup
268 336
269 337 def get_params(self, numdays, enddate=None):
270 338 """
@@ -288,6 +356,7 @@ def get_params(self, numdays, enddate=None):
288 356 "full": 1,
289 357 "startdate": _startdate.strftime("%m/%d/%Y"),
290 358 }
  359 +
291 360 # enddate is optional. the endpoint will just presume today,
292 361 # if not given.
293 362 if enddate:
@@ -476,6 +545,10 @@ class PerformanceTestModel(DatazillaModelBase):
476 545 # content types that every project will have
477 546 CONTENT_TYPES = ["perftest", "objectstore"]
478 547
  548 + # Total number of replicates allowed for a
  549 + # single test suite associated with a JSON object
  550 + REPLICATE_LIMIT = 5000
  551 +
479 552 @classmethod
480 553 def create(cls, project, hosts=None, types=None, cron_batch=None):
481 554 """
@@ -618,6 +691,19 @@ def get_products(self, key_column=None):
618 691
619 692 return products
620 693
  694 + def get_revision_products(self, revision, branch):
  695 +
  696 + proc = 'perftest.selects.get_revision_products'
  697 +
  698 + products = self.sources["perftest"].dhub.execute(
  699 + proc=proc,
  700 + debug_show=self.DEBUG,
  701 + return_type='tuple',
  702 + placeholders=[revision, branch]
  703 + )
  704 +
  705 + return products
  706 +
621 707
622 708 def get_default_products(self):
623 709
@@ -635,6 +721,24 @@ def get_default_products(self):
635 721
636 722 return default_products
637 723
  724 + def get_default_branch_version(self, branch, product_name):
  725 +
  726 + proc = 'perftest.selects.get_default_products'
  727 +
  728 + products = self.sources["perftest"].dhub.execute(
  729 + proc=proc,
  730 + debug_show=self.DEBUG,
  731 + return_type='tuple'
  732 + )
  733 +
  734 + target_product = {}
  735 +
  736 + for product in products:
  737 + if (product['branch'] == branch) and (product['product'] == product_name):
  738 + target_product = product
  739 + break
  740 +
  741 + return target_product
638 742
639 743 def get_machines(self):
640 744
@@ -852,22 +956,22 @@ def get_test_run_value_summary(self, test_run_id):
852 956
853 957
854 958 def get_test_run_ids(
855   - self, branch, revision, os_name=None, os_version=None,
856   - branch_version=None, processor=None, build_type=None,
857   - test_name=None, page_name=None):
  959 + self, branch, revisions, product_name=None, os_name=None,
  960 + os_version=None, branch_version=None, processor=None,
  961 + build_type=None, test_name=None, page_name=None):
858 962
859 963 proc = 'perftest.selects.get_test_run_ids'
860 964 placeholders = [branch]
861 965 rep = []
862 966
863   - if not revision:
864   - proc = 'perftest.selects.get_test_run_ids_no_revision'
865   - else:
866   - placeholders.append(revision)
867   -
868   - if page_name:
  967 + if revisions:
  968 + revision_string = ','.join( map( lambda v:str(v), revisions ) )
  969 + self.get_replace_and_placeholders(
  970 + rep, placeholders, 'tr.revision', revision_string
  971 + )
  972 + if product_name:
869 973 self.get_replace_and_placeholders(
870   - rep, placeholders, 'pg.url', page_name
  974 + rep, placeholders, 'p.product', product_name
871 975 )
872 976 if os_name:
873 977 self.get_replace_and_placeholders(
@@ -1012,8 +1116,7 @@ def set_test_collection(self, name, description):
1012 1116
1013 1117 id = self._insert_data_and_get_id('set_test_collection',
1014 1118 [ name,
1015   - description,
1016   - name ])
  1119 + description ])
1017 1120
1018 1121 return id
1019 1122
@@ -1225,25 +1328,38 @@ def _set_test_aux_data(self, data, test_id, test_run_id):
1225 1328
1226 1329 def _set_test_values(self, data, test_id, test_run_id):
1227 1330 """Insert test values to database for given test_id and test_run_id."""
  1331 +
  1332 + total_replicates = 0
  1333 +
1228 1334 for page, values in data['results'].items():
1229 1335
1230 1336 page_id = self._get_or_create_page_id(page, test_id)
1231 1337
1232 1338 placeholders = []
1233 1339 for index, value in enumerate(values, 1):
1234   - placeholders.append(
1235   - (
1236   - test_run_id,
1237   - index,
1238   - page_id,
1239   - # TODO: Need to get the value id into the json
1240   - 1,
1241   - value,
  1340 +
  1341 + total_replicates += 1
  1342 +
  1343 + if total_replicates <= self.REPLICATE_LIMIT:
  1344 +
  1345 + placeholders.append(
  1346 + (
  1347 + test_run_id,
  1348 + index,
  1349 + page_id,
  1350 + # TODO: Need to get the value id into the json
  1351 + 1,
  1352 + value,
  1353 + )
1242 1354 )
1243   - )
1244 1355
1245   - self._insert_data(
1246   - 'set_test_values', placeholders, executemany=True)
  1356 + else:
  1357 + #Replicate limit reached
  1358 + break
  1359 +
  1360 + if placeholders:
  1361 + self._insert_data(
  1362 + 'set_test_values', placeholders, executemany=True)
1247 1363
1248 1364
1249 1365 def _get_or_create_aux_id(self, aux_data, test_id):
54 datazilla/model/sql/hgmozilla.json
@@ -65,6 +65,7 @@
65 65 "sql":"SELECT p.id AS 'pushlog_id',
66 66 p.push_id,
67 67 p.date,
  68 + p.user,
68 69 c.node,
69 70 b.id AS 'branch_id',
70 71 b.name
@@ -76,19 +77,32 @@
76 77 "host":"read_host"
77 78 },
78 79
  80 + "get_branch_uri":{
  81 +
  82 + "sql":"SELECT b.name, b.uri
  83 + FROM branches AS b
  84 + WHERE b.name = ?",
  85 +
  86 + "host":"read_host"
  87 + },
  88 +
  89 + "get_all_branch_uris":{
  90 +
  91 + "sql":"SELECT b.name, b.uri
  92 + FROM branches AS b",
  93 +
  94 + "host":"read_host"
  95 + },
  96 +
79 97 "get_push_ids_before_node":{
80 98
81 99 "sql":"SELECT p.id AS 'pushlog_id',
82 100 p.push_id,
83 101 p.date,
84   - c.node,
85   - b.id AS 'branch_id',
86   - b.name
  102 + p.user
87 103 FROM pushlogs AS p
88   - LEFT JOIN changesets AS c ON p.id = c.pushlog_id
89   - LEFT JOIN branches AS b ON p.branch_id = b.id
90   - WHERE p.push_id < ? AND b.name = ?
91   - LIMIT ?",
  104 + WHERE p.push_id < ? AND p.push_id >= ? AND p.branch_id = ?
  105 + ORDER BY p.push_id",
92 106
93 107 "host":"read_host"
94 108 },
@@ -98,18 +112,26 @@
98 112 "sql":"SELECT p.id AS 'pushlog_id',
99 113 p.push_id,
100 114 p.date,
101   - c.node,
102   - b.id AS 'branch_id',
103   - b.name
  115 + p.user
104 116 FROM pushlogs AS p
105   - LEFT JOIN changesets AS c ON p.id = c.pushlog_id
106   - LEFT JOIN branches AS b ON p.branch_id = b.id
107   - WHERE p.push_id > ? AND b.name = ?
108   - LIMIT ?",
  117 + WHERE p.push_id > ? AND p.push_id <= ? AND p.branch_id = ?
  118 + ORDER BY p.push_id",
109 119
110 120 "host":"read_host"
111 121 },
  122 + "get_changeset_data_for_pushes":{
112 123
  124 + "sql":"SELECT c.id,
  125 + c.pushlog_id,
  126 + c.node,
  127 + c.author,
  128 + c.desc
  129 + FROM changesets AS c
  130 + WHERE c.pushlog_id IN (REP0)",
  131 +
  132 + "host":"read_host"
  133 +
  134 + },
113 135 "get_all_branch_pushlogs":{
114 136 "sql":"SELECT p.id AS 'pushlog_id',
115 137 p.push_id,
@@ -129,9 +151,11 @@
129 151 "sql":"SELECT p.id AS 'pushlog_id',
130 152 p.push_id,
131 153 p.date,
  154 + p.user,
132 155 b.id AS 'branch_id',
133 156 b.name,
134   - c.node
  157 + c.node,
  158 + c.desc
135 159 FROM pushlogs AS p
136 160 LEFT JOIN changesets AS c ON p.id = c.pushlog_id
137 161 LEFT JOIN branches AS b ON p.branch_id = b.id
36 datazilla/model/sql/perftest.json
@@ -271,8 +271,7 @@
271 271 "set_test_collection":{
272 272
273 273 "sql":"INSERT INTO `test_collection` (`name`, `description`)
274   - VALUES (?, ?)
275   - ON DUPLICATE KEY UPDATE `name`=?",
  274 + VALUES (?, ?)",
276 275
277 276 "host":"master_host"
278 277 },
@@ -411,8 +410,8 @@
411 410 "get_test_run_counts":{
412 411
413 412 "sql":"SELECT COUNT(tr.id) as count, p.branch
414   - FROM test_run as tr
415   - LEFT JOIN build as b ON tr.build_id = b.id
  413 + FROM test_run AS tr
  414 + LEFT JOIN build AS b ON tr.build_id = b.id
416 415 LEFT JOIN product as p ON b.product_id = p.id
417 416 WHERE tr.date_run BETWEEN ? AND ?
418 417 GROUP BY p.branch",
@@ -426,22 +425,8 @@
426 425 LEFT JOIN build AS b ON b.id = tr.build_id
427 426 LEFT JOIN product AS p ON p.id = b.product_id
428 427 LEFT JOIN machine AS m ON m.id = tr.machine_id
429   - LEFT JOIN operating_system as os on os.id = m.operating_system_id
430   - LEFT JOIN test as t on t.id = tr.test_id
431   - WHERE p.branch = ? AND tr.revision = ? REP0",
432   -
433   - "host":"read_host"
434   - },
435   - "get_test_run_ids_no_revision":{
436   - "sql":"SELECT DISTINCT(tr.id) AS test_run_id
437   - FROM test_run AS tr
438   - LEFT JOIN test_value AS tv ON tr.id = tv.test_run_id
439   - LEFT JOIN pages AS pg ON tv.page_id = pg.id
440   - LEFT JOIN build AS b ON b.id = tr.build_id
441   - LEFT JOIN product AS p ON p.id = b.product_id
442   - LEFT JOIN machine AS m ON m.id = tr.machine_id
443   - LEFT JOIN operating_system as os on os.id = m.operating_system_id
444   - LEFT JOIN test as t on t.id = tr.test_id
  428 + LEFT JOIN operating_system AS os ON os.id = m.operating_system_id
  429 + LEFT JOIN test AS t ON t.id = tr.test_id
445 430 WHERE p.branch = ? REP0",
446 431
447 432 "host":"read_host"
@@ -461,6 +446,17 @@
461 446 "host":"read_host"
462 447
463 448 },
  449 + "get_revision_products":{
  450 +
  451 + "sql":"SELECT p.product, p.branch, p.version
  452 + FROM test_run AS tr
  453 + LEFT JOIN build AS b ON tr.build_id = b.id
  454 + LEFT JOIN product AS p ON b.product_id = p.id
  455 + WHERE tr.revision = ? AND p.branch = ?
  456 + GROUP BY p.product, p.branch, p.version",
  457 +
  458 + "host":"read_host"
  459 + },
464 460 "get_test_run_values":{
465 461
466 462 "sql":"SELECT tv.test_run_id,
2  datazilla/model/utils.py
@@ -11,8 +11,6 @@
11 11 import datetime
12 12 import sys
13 13
14   -
15   -
16 14 def is_number(s):
17 15 try:
18 16 float(s)
6 datazilla/webapp/apps/datazilla/refdata/pushlog_views.py
@@ -64,6 +64,12 @@ def get_all_branches(request):
64 64 branches = pushlog_refdata.get_all_branches()
65 65 return HttpResponse(json.dumps(branches), content_type=API_CONTENT_TYPE)
66 66
  67 +def get_branch_uri(request):
  68 + """Get the uri associated with branch"""
  69 + branch = request.GET.get("branch", None)
  70 + branches = pushlog_refdata.get_branch_uri(branch)
  71 + return HttpResponse(json.dumps(branches), content_type=API_CONTENT_TYPE)
  72 +
67 73
68 74 def get_db_size(request):
69 75 """Return the size of the DB on disk in MB."""
2  datazilla/webapp/apps/datazilla/refdata/urls_no_project.py
@@ -8,6 +8,8 @@
8 8
9 9 (r"^pushlog/branches/?$", "pushlog_views.get_all_branches"),
10 10
  11 + (r"^pushlog/branch_uri/?$", "pushlog_views.get_branch_uri"),
  12 +
11 13 (r"^pushlog/db_size/?$", "pushlog_views.get_db_size"),
12 14
13 15 )
43 datazilla/webapp/apps/datazilla/testdata/views.py
@@ -16,7 +16,6 @@
16 16
17 17 API_CONTENT_TYPE = 'application/json; charset=utf-8'
18 18
19   -
20 19 def get_testdata(request, project, branch, revision):
21 20 """
22 21 Apply data filters and return all test data objects associated with the
@@ -51,9 +50,18 @@ def get_metrics_data(request, project, branch, revision):
51 50 """
52 51 Apply filters and return all metrics data associated with the revision.
53 52 """
  53 +
  54 + #Default to most current version of Firefox
  55 + product_name = request.GET.get("product", "Firefox")
54 56 os_name = request.GET.get("os_name", None)
55 57 os_version = request.GET.get("os_version", None)
  58 +
56 59 branch_version = request.GET.get("branch_version", None)
  60 + if not branch_version:
  61 + branch_version = testdata.get_default_version(
  62 + project, branch, product_name
  63 + )
  64 +
57 65 processor = request.GET.get("processor", None)
58 66 build_type = request.GET.get("build_type", None)
59 67 test_name = request.GET.get("test_name", None)
@@ -64,6 +72,7 @@ def get_metrics_data(request, project, branch, revision):
64 72 project,
65 73 branch,
66 74 revision,
  75 + product_name=product_name,
67 76 os_name=os_name,
68 77 os_version=os_version,
69 78 branch_version=branch_version,
@@ -80,24 +89,35 @@ def get_metrics_summary(request, project, branch, revision):
80 89 Apply filters and build a summary of all metric test evaluations.
81 90 """
82 91
  92 + #Default to most current version of Firefox
  93 + product_name = request.GET.get("product", "Firefox")
83 94 os_name = request.GET.get("os_name", None)
84 95 os_version = request.GET.get("os_version", None)
  96 +
85 97 branch_version = request.GET.get("branch_version", None)
  98 + if not branch_version:
  99 + branch_version = testdata.get_default_version(
  100 + project, branch, product_name
  101 + )
  102 +
86 103 processor = request.GET.get("processor", None)
87 104 build_type = request.GET.get("build_type", None)
88 105 test_name = request.GET.get("test_name", None)
  106 + pushlog_project = request.GET.get("pushlog_project", None)
89 107
90 108 return HttpResponse(
91 109 json.dumps(testdata.get_metrics_summary(
92 110 project,
93 111 branch,
94 112 revision,
  113 + product_name=product_name,
95 114 os_name=os_name,
96 115 os_version=os_version,
97 116 branch_version=branch_version,
98 117 processor=processor,
99 118 build_type=build_type,
100 119 test_name=test_name,
  120 + pushlog_project=pushlog_project
101 121 )),
102 122 content_type=API_CONTENT_TYPE,
103 123 )
@@ -106,9 +126,18 @@ def get_metrics_pushlog(request, project, branch, revision):
106 126 """
107 127 Apply filters and return trend line data for the time period requested.
108 128 """
  129 +
  130 + #Default to most current version of Firefox
  131 + product_name = request.GET.get("product", "Firefox")
109 132 os_name = request.GET.get("os_name", None)
110 133 os_version = request.GET.get("os_version", None)
  134 +
111 135 branch_version = request.GET.get("branch_version", None)
  136 + if not branch_version:
  137 + branch_version = testdata.get_default_version(
  138 + project, branch, product_name
  139 + )
  140 +
112 141 processor = request.GET.get("processor", None)
113 142 build_type = request.GET.get("build_type", None)
114 143 test_name = request.GET.get("test_name", None)
@@ -121,6 +150,10 @@ def get_metrics_pushlog(request, project, branch, revision):
121 150 except ValueError:
122 151 pass
123 152
  153 + #applies to both before/after, so total of 2*maximum_pushes
  154 + #are allowed
  155 + maximum_pushes = 1000
  156 +
124 157 pushes_before = 10
125 158 try:
126 159 pushes_before = int(request.GET.get("pushes_before", 10))
@@ -133,6 +166,12 @@ def get_metrics_pushlog(request, project, branch, revision):
133 166 except ValueError:
134 167 pass
135 168
  169 + #Set maximum limit for pushes before/after
  170 + if pushes_before > maximum_pushes:
  171 + pushes_before = maximum_pushes
  172 + if pushes_after > maximum_pushes:
  173 + pushes_after = maximum_pushes
  174 +
136 175 numdays = 0
137 176 try:
138 177 numdays = int(request.GET.get("numdays", 0))
@@ -152,6 +191,7 @@ def get_metrics_pushlog(request, project, branch, revision):
152 191 project,
153 192 branch,
154 193 revision,
  194 + product_name=product_name,
155 195 os_name=os_name,
156 196 os_version=os_version,
157 197 branch_version=branch_version,
@@ -181,3 +221,4 @@ def get_application_log(request, project, revision):
181 221 )
182 222
183 223
  224 +
35 datazilla/webapp/apps/datazilla/views.py
@@ -238,41 +238,6 @@ def _get_test_run_summary(project, method, request, dm):
238 238
239 239 json_data = '{}'
240 240
241   - #Commenting the use of memcache out for now, the shared memcache
242   - #in production is failing to return the expected data. This works
243   - #in development so it's likely a configuration issue of some sort.
244   - """
245   - if product_ids and (not test_ids) and (not platform_ids):
246   -
247   - if len(product_ids) > 1:
248   - extend_list = { 'data':[], 'columns':[] }
249   - for id in product_ids:
250   - key = utils.get_summary_cache_key(project, str(id), time_key)
251   -
252   - compressed_json_data = cache.get(key)
253   -
254   - if compressed_json_data:
255   - json_data = zlib.decompress( compressed_json_data )
256   - data = json.loads( json_data )
257   - extend_list['data'].extend( data['data'] )
258   - extend_list['columns'] = data['columns']
259   -
260   - json_data = json.dumps(extend_list)
261   -
262   -
263   - else:
264   - key = utils.get_summary_cache_key(
265   - project,
266   - str(product_ids[0]),
267   - time_key,
268   - )
269   - compressed_json_data = cache.get(key)
270   -
271   - if compressed_json_data:
272   - json_data = zlib.decompress( compressed_json_data )
273   -
274   - else:
275   - """
276 241 table = dm.get_test_run_summary(time_ranges[time_key]['start'],
277 242 time_ranges[time_key]['stop'],
278 243 product_ids,
72 datazilla/webapp/static/css/summary.css
@@ -4,7 +4,11 @@ body {
4 4 font-size: 14px;
5 5 padding: 0;
6 6 border-top: 2px solid #676767;
7   - min-width: 1024px;
  7 + min-width: 1065px;
  8 +}
  9 +a {
  10 + text-decoration: underline;
  11 + font-weight: bold;
8 12 }
9 13 div.su-large-text {
10 14 font-size: 14px;
@@ -34,7 +38,7 @@ div.su-panel-base {
34 38 }
35 39 div.su-main-gauge {
36 40 float: left;
37   - width: 325px;
  41 + width: 375px;
38 42 margin-left:10px;
39 43 }
40 44 div.su-reference-info {
@@ -49,6 +53,11 @@ div.su-spinner {
49 53 background: transparent url(/static/images/spinner.gif) no-repeat center center;
50 54 height:280px;
51 55 }
  56 +div.su-small-spinner {
  57 + background: transparent url(/static/images/spinner.gif) no-repeat center center;
  58 + height:35px;
  59 + width:35px;
  60 +}
52 61 .su-scroll-panel {
53 62 overflow-y: auto;
54 63 height:200px;
@@ -140,7 +149,6 @@ div.su-grid-column {
140 149 white-space:nowrap;
141 150 text-align:center;
142 151 float: left;
143   -
144 152 width:30px;
145 153 height:25px;
146 154 }
@@ -199,8 +207,7 @@ div.su-grid-value {
199 207 background: #ffffff;
200 208 border: 3px solid #e8e8e8;
201 209 }
202   -.arrow_box:after,
203   -.arrow_box:before {
  210 +.arrow_box:after, .arrow_box:before {
204 211 top: 100%;
205 212 border: solid transparent;
206 213 content: " ";
@@ -222,11 +229,66 @@ div.su-grid-value {
222 229 left: 50%;
223 230 margin-left: -24px;
224 231 }
  232 +
  233 +.arrow_box_up {
  234 + position: relative;
  235 + background: #ffffff;
  236 + border: 3px solid #e8e8e8;
  237 +}
  238 +.arrow_box_up:after, .arrow_box_up:before {
  239 + bottom: 100%;
  240 + border: solid transparent;
  241 + content: " ";
  242 + height: 0;
  243 + width: 0;
  244 + position: absolute;
  245 + pointer-events: none;
  246 +}
  247 +.arrow_box_up:after {
  248 + border-color: rgba(255, 255, 255, 0);
  249 + border-bottom-color: #ffffff;
  250 + border-width: 20px;
  251 + left: 50%;
  252 + margin-left: -20px;
  253 +}
  254 +.arrow_box_up:before {
  255 + border-color: rgba(232, 232, 232, 0);
  256 + border-bottom-color: #e8e8e8;
  257 + border-width: 24px;
  258 + left: 50%;
  259 + margin-left: -24px;
  260 +}
225 261 div.su-data-series-panel {
226 262 margin-top: 5px;
227 263 padding: 0.5em;
228 264 overflow: auto;
229 265 }
  266 +div.su-datum-info-panel {
  267 + margin-top: 5px;
  268 + margin-left: 5px;
  269 + padding: 0.5em;
  270 + float:right;
  271 + width:190px;
  272 +}
  273 +.su-datum-value {
  274 + text-align:right;
  275 + float:right;
  276 +}
  277 +/*style for the description field of a patch*/
  278 +div.su-datum-desc-value {
  279 + text-align:right;
  280 + margin-top:5px;
  281 + margin-bottom:15px;
  282 +}
230 283 .ui-button-text{
231 284 font-size: 11px !important;
232 285 }
  286 +.su-vertical-text {
  287 + -webkit-transform:rotate(270deg);
  288 + -moz-transform:rotate(270deg);
  289 + -o-transform: rotate(270deg);
  290 + white-space:nowrap;
  291 + position:absolute;
  292 + left:135px;
  293 + margin-top:150px;
  294 +}
57 datazilla/webapp/static/js/metric_summary/MetricDashboardComponent.js
@@ -79,8 +79,10 @@ var MetricDashboardView = new Class({
79 79 this.progressBarTitleClassName = 'su-progressbar-title';
80 80 this.progressBarClassName = 'su-progressbar';
81 81
82   - this.productTestsSel = '#su_product_tested';
  82 + this.revisionProductsSel = '#su_revision_products';
83 83 this.revisionTestedSel = '#su_revision_tested';
  84 + this.pushDescSel = '#su_push_desc';
  85 + this.authorSel = '#su_push_author';
84 86 this.totalCountSel = '#su_total_count';
85 87 this.noTrendCountSel = '#su_no_trend_count';
86 88 this.passCountSel = '#su_pass_count';
@@ -191,13 +193,49 @@ var MetricDashboardView = new Class({
191 193 },
192 194 setReferenceInfo: function(data){
193 195
194   - var productInfo = data.product_info.name + ' ' +
195   - data.product_info.branch + ' ' + data.product_info.version;
  196 + for(var i=0; i<data.products.length; i++){
196 197
197   - $(this.productTestsSel).text(productInfo);
  198 + var product = data.products[i].product + ' ' +
  199 + data.products[i].branch + ' ' + data.products[i].version;
198 200
199   - $(this.revisionTestedSel).text(
200   - data.product_info.revision
  201 + var option = $(document.createElement('option'));
  202 +
  203 + $(option).text(product);
  204 +
  205 + $(option).val(
  206 + JSON.stringify(
  207 + { 'product':data.products[i].product,
  208 + 'version':data.products[i].version }
  209 + )
  210 + );
  211 +
  212 + if((data.product_info.name === data.products[i].product) &&
  213 + (data.product_info.branch === data.products[i].branch) &&
  214 + (data.product_info.version === data.products[i].version)){
  215 + //This is the option the page is loaded on
  216 + //mark as selected
  217 + $(option).attr('selected', true);
  218 + }
  219 + $(this.revisionProductsSel).append(option);
  220 +
  221 + }
  222 +
  223 + $(this.revisionProductsSel).change(
  224 + _.bind( function(event){
  225 + var selectedOption = $(event.target).find(':selected');
  226 +
  227 + var value = $(selectedOption).val();
  228 + var productData = JSON.parse(value);
  229 +
  230 + console.log(productData);
  231 +
  232 + }, this)
  233 + );
  234 +
  235 + $(this.authorSel).text(data.push_data.user);
  236 +
  237 + $(this.revisionTestedSel).html(
  238 + MS_PAGE.getHgUrlATag('rev', data.product_info.revision)
201 239 );
202 240
203 241 $(this.noTrendCountSel).text(data.summary.keys_without_trend);
@@ -206,6 +244,9 @@ var MetricDashboardView = new Class({
206 244 $(this.passCountSel).text(data.summary.pass.value + ' passed');
207 245 $(this.failCountSel).text(data.summary.fail.value + ' failed');
208 246
  247 + var descHtml = MS_PAGE.addBugzillaATagsToDesc(data.push_data.desc);
  248 + $(this.pushDescSel).html(descHtml);
  249 +
209 250 },
210 251 loadProgressBars: function(targetContainer, data, order){
211 252
@@ -280,7 +321,9 @@ var MetricDashboardModel = new Class({
280 321
281 322 uri = '/' + MS_PAGE.refData.project +
282 323 '/testdata/metrics/' + MS_PAGE.refData.branch +
283   - '/' + MS_PAGE.refData.revision + '/summary';
  324 + '/' + MS_PAGE.refData.revision + '/summary?' +
  325 + 'product=' + MS_PAGE.refData.product + '&branch_version=' +
  326 + MS_PAGE.refData.branch_version;
284 327
285 328 jQuery.ajax( uri, {
286 329 accepts:'application/json',
32 datazilla/webapp/static/js/metric_summary/MetricGridComponent.js
@@ -166,9 +166,35 @@ var MetricGridView = new Class({
166 166 //first time the page loads.
167 167 if(this.triggerDefaultEvent){
168 168
169   - this._triggerEvent(
170   - this.gridMouseoverEvent, cell
171   - );
  169 + var platform = MS_PAGE.refData.platform;
  170 + var test = MS_PAGE.refData.test;
  171 + var initializeValue = "";
  172 + if(data.tests[test] && data.tests[test][platform]){
  173 + initializeValue = data.tests[test][platform]['pass']['percent'];
  174 + }
  175 +
  176 + if(initializeValue){
  177 + //URL has test/platform specified, initialize
  178 + //table to requested data target
  179 + var initializeCell = this.getValueCell(
  180 + platform,
  181 + test,
  182 + initializeValue
  183 + );
  184 +
  185 + this._triggerEvent(
  186 + this.gridMouseoverEvent, initializeCell
  187 + );
  188 +
  189 + //Lock the table to the data requested
  190 + MS_PAGE.testPagesComponent.view.lockTable();
  191 +
  192 + }else {
  193 + //No target table use first defined cell
  194 + this._triggerEvent(
  195 + this.gridMouseoverEvent, cell
  196 + );
  197 + }
172 198
173 199 this.triggerDefaultEvent = false;
174 200 }
63 datazilla/webapp/static/js/metric_summary/MetricSummaryPage.js
@@ -18,15 +18,56 @@ var MetricSummaryPage = new Class( {
18 18
19 19 this.failColor = '#FF7700';
20 20 this.passColor = '#44AA00';
  21 + this.branchUri = '/refdata/pushlog/branch_uri?branch=';
  22 + this.revisionUrl = 'https://hg.mozilla.org/URI/pushloghtml?TYPE=';
  23 + this.bugzillaUrl = 'https://bugzilla.mozilla.org/show_bug.cgi?id=';
  24 +
21 25 },
22 26
  27 + getHgUrlATag: function(type, revision){
  28 + /****
  29 + * Get an html <a> tag for the given type and revision provided.
  30 + * Type can be 'rev' or 'changeset'.
  31 + ****/
  32 + var revisionUrl = this.revisionUrl.replace(
  33 + 'URI', MS_PAGE.refData.branch_uri
  34 + );
  35 +
  36 + revisionUrl = revisionUrl.replace(
  37 + 'TYPE', type
  38 + ) + revision;
  39 +
  40 + var a = $(document.createElement('a'));
  41 + $(a).attr('href', revisionUrl);
  42 + $(a).attr('target', '_blank');
  43 + $(a).text(revision);
  44 +
  45 + return a;
  46 + },
  47 + addBugzillaATagsToDesc: function(desc){
  48 + /****
  49 + * Replace bugzilla bug number strings (Bug 123456) with
  50 + * an html <a> tag.
  51 + ****/
  52 + return desc.replace(
  53 + /(bug).*?(\d+)/ig,
  54 + '<a target="_blank" href="' + this.bugzillaUrl + "$2" +
  55 + '">$1 $2</a>');
  56 + },
23 57 setRefData: function(){
24 58
25 59 MS_PAGE.refData = {};
26 60
27   - MS_PAGE.refData.project = MS_PAGE.urlObj.data.seg.path[0];
28   - MS_PAGE.refData.branch = MS_PAGE.urlObj.data.seg.path[2];
29   - MS_PAGE.refData.revision = MS_PAGE.urlObj.data.seg.path[3];
  61 + var urlObj = MS_PAGE.urlObj.data;
  62 +
  63 + MS_PAGE.refData.project = urlObj.seg.path[0];
  64 + MS_PAGE.refData.branch = urlObj.seg.path[2];
  65 + MS_PAGE.refData.revision = urlObj.seg.path[3];
  66 + MS_PAGE.refData.product = urlObj.param.query.product;
  67 + MS_PAGE.refData.branch_version = urlObj.param.query.branch_version;
  68 + MS_PAGE.refData.test = urlObj.param.query.test;
  69 + MS_PAGE.refData.platform = urlObj.param.query.platform;
  70 +
30 71 },
31 72 getDatumKey: function(data){
32 73 /***
@@ -42,6 +83,21 @@ var MetricSummaryPage = new Class( {
42 83 key = key.hashCode();
43 84
44 85 return key;
  86 + },
  87 + getBranchUri: function(){
  88 +
  89 + var url = this.branchUri + MS_PAGE.refData.branch;
  90 + jQuery.ajax( url, {
  91 + accepts:'application/json',
  92 + dataType:'json',
  93 + cache:false,
  94 + type:'GET',
  95 + context:this,
  96 + success: function(data, textStatus, jqXHR){
  97 + MS_PAGE.refData.branch_uri = data[0].uri;
  98 + console.log(MS_PAGE.refData.branch_uri);
  99 + }
  100 + });
45 101 }
46 102
47 103 });
@@ -51,6 +107,7 @@ $(document).ready(function() {
51 107 MS_PAGE = new MetricSummaryPage();
52 108
53 109 MS_PAGE.setRefData();
  110 + MS_PAGE.getBranchUri();
54 111
55 112 MS_PAGE.trendLineComponent = new TrendLineComponent();
56 113 MS_PAGE.testPagesComponent = new TestPagesComponent();
48 datazilla/webapp/static/js/metric_summary/TestPagesComponent.js
@@ -52,6 +52,12 @@ var TestPagesView = new Class({
52 52 this.failBackgroundColor = 'su-fail-background-color';
53 53 this.passBackgroundColor = 'su-pass-background-color';
54 54
  55 + //This is used to store the row to initialize the
  56 + //push log with. It's set by _adaptData to the first
  57 + //row in the first table loaded.
  58 + this.defaultRowCbSel = "";
  59 + this.defaultRowSelectionSent = false;
  60 +
55 61 this.cbIdPrefix = 'su_cb_';
56 62
57 63 this.scrollHeight = parseInt($(this.tableContainerSel).css('height')) - 125;
@@ -64,6 +70,7 @@ var TestPagesView = new Class({
64 70 this.gridClickEvent = 'GRID_CLICK_EVENT';
65 71 this.gridMouseoverEvent = 'GRID_MOUSEOVER_EVENT';
66 72 this.closeDataSeriesEvent = 'CLOSE_DATA_SERIES_EVENT';
  73 + this.defaultRowSelectionEvent = 'DEFAULT_ROW_SELECTION_EVENT';
67 74
68 75 $(this.eventContainerSel).bind(
69 76 this.gridMouseoverEvent,
@@ -80,8 +87,8 @@ var TestPagesView = new Class({
80 87 _.bind(this.uncheckCb, this)
81 88 );
82 89
83   - $(this.tableSel).live(
84   - 'click mouseover', _.bind(this.tableEventHandler, this)
  90 + $(this.tableSel).bind(
  91 + 'click', _.bind(this.tableEventHandler, this)
85 92 );
86 93 },
87 94
@@ -130,42 +137,53 @@ var TestPagesView = new Class({
130 137 'aaSorting':[ [2, 'asc'] ]
131 138 };
132 139
  140 +
133 141 this._adaptData(datatableOptions, eventData.data);
134 142
135 143 this.dataTable = $(this.tableSel).dataTable( datatableOptions );
136 144
  145 + //Send default row event out
  146 + if(this.defaultRowSelectionSent === false){
  147 +
  148 + $(this.eventContainerSel).trigger(
  149 + this.defaultRowSelectionEvent, this.defaultRowCbSel
  150 + );
  151 +
  152 + this.defaultRowSelectionSent = true;
  153 + }
  154 +
137 155 },
138 156 lockTable: function(event, eventData){
139 157 $(this.lockTableSel).click();
140 158 },
141 159 uncheckCb: function(event, datumKey){
142 160 var id = this.cbIdPrefix + datumKey;
143   - $('#' + id).prop("checked", false);
  161 + $('#' + id).attr("checked", false);
144 162 },
145 163 tableEventHandler: function(event){
146 164
147   - if(event.type == 'mouseover'){
148   -
149   - var target = $(event.target);
150   - var elParent = $(target).parent();
151   -
152   - var highlightClass = "";
153   -
154   - }else if(event.type == 'click'){
  165 + if(event.type == 'click'){
155 166
156 167 if( $(event.target).is('input') ){
157 168
158   - var checked = $(event.target).attr('checked');
  169 + //Retrieve the associated mean
  170 + var row = $(event.target).closest('tr');
  171 + var cells = $(row).find('td');
  172 + var meanValue = $(cells[3]).text();
  173 +
159 174 var pagename = $(event.target).attr(this.pagenameDataAttr);
160 175 var testSuite = $(this.testSuiteSel).text();
161 176 var platform = $(this.platformSel).text();
162 177
  178 + var checked = $(event.target).attr('checked');
  179 +
163 180 var eventData = {
164 181 'checked':checked,
165 182 'pagename':pagename,
166 183 'testsuite':testSuite,
167 184 'platform':platform,
168   - 'platform_info':this.platformInfo
  185 + 'platform_info':this.platformInfo,
  186 + 'mean':meanValue
169 187 };
170 188
171 189 $(this.eventContainerSel).trigger(
@@ -192,6 +210,10 @@ var TestPagesView = new Class({
192 210
193 211 var key = this.cbIdPrefix + MS_PAGE.getDatumKey(keyData);
194 212
  213 + if( (i === 0) && (this.defaultRowCbSel === "")){
  214 + this.defaultRowCbSel = '#' + key;
  215 + }
  216 +
195 217 //page name
196 218 var row = {};
197 219 row['0'] = '<input id="' + key +
768 datazilla/webapp/static/js/metric_summary/TrendLineComponent.js
@@ -18,16 +18,45 @@ var TrendLineComponent = new Class({
18 18 this.view = new TrendLineView('#TrendLineView',{});
19 19 this.model = new TrendLineModel('#TrendLineModel',{});
20 20
  21 + //Holds all trend line data loaded
21 22 this.trendLines = {};
  23 + //Maintains the trend line load order according
  24 + //to the order the user loads them
22 25 this.trendLineOrder = [];
23 26
24   - //[series index][datapoint index]
  27 + //Maps the flot series index to the trendline keys
  28 + //and allows flot callback functions to map to the
  29 + //corresponding reference data in this.trendLines
25 30 this.seriesIndexToKey = [];
26   - this.seriesIndexToKey.push([]); //pass series
27   - this.seriesIndexToKey.push([]); //fail series
  31 +
  32 + //pass series
  33 + this.passSeriesIndex = this.seriesIndexToKey.push([]) - 1;
  34 + //fail series
  35 + this.failSeriesIndex = this.seriesIndexToKey.push([]) - 1;
  36 + //no data series
  37 + this.noMetricsDataSeriesIndex = this.seriesIndexToKey.push([]) - 1;
28 38
29 39 this.tickDisplayDates = {};
30 40
  41 + //Holds all event data received. It's used for
  42 + //reloading all series data with updated pushes
  43 + //before and after values.
  44 + this.eventData = [];
  45 +
  46 + this.pushesBefore = 20;
  47 + this.pushesAfter = 5;
  48 +
  49 + //true indicates push retrieval in progress
  50 + //false indicates more pushes can be retrieved
  51 + this.getPushState = false;
  52 +
  53 + //The first simulated table checkbox click will
  54 + //not have the eventData.checked attribute set. This
  55 + //switch allows initializeTrend to fully process the
  56 + //event anyway. Not sure why the .click() function
  57 + //does not take care of this.
  58 + this.simulatedTableCBClick = false;
  59 +
31 60 this.plot = undefined;
32 61
33 62 this.chartOptions = {
@@ -46,6 +75,18 @@ var TrendLineComponent = new Class({
46 75 'autoscaleMargin':0.1
47 76 },
48 77
  78 + 'series': {
  79 +
  80 + 'points': {