Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
  • 12 commits
  • 21 files changed
  • 0 commit comments
  • 2 contributors
Showing with 1,203 additions and 385 deletions.
  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
View
5 crontab.txt
@@ -2,11 +2,6 @@
PYTHON_ROOT=/usr/bin/
DATAZILLA_HOME=/usr/local/datazilla
-*/2 * * * * $PYTHON_ROOT/python $DATAZILLA_HOME/manage.py populate_summary_cache --build --cache --project stoneridge
-*/2 * * * * $PYTHON_ROOT/python $DATAZILLA_HOME/manage.py populate_summary_cache --build --cache --project b2g
-*/2 * * * * $PYTHON_ROOT/python $DATAZILLA_HOME/manage.py populate_summary_cache --build --cache --project jetperf
-*/2 * * * * $PYTHON_ROOT/python $DATAZILLA_HOME/manage.py populate_summary_cache --build --cache --project test
-
*/2 * * * * $PYTHON_ROOT/python $DATAZILLA_HOME/manage.py populate_test_collections --load --project stoneridge
*/2 * * * * $PYTHON_ROOT/python $DATAZILLA_HOME/manage.py populate_test_collections --load --project b2g
*/2 * * * * $PYTHON_ROOT/python $DATAZILLA_HOME/manage.py populate_test_collections --load --project jetperf
View
2  datazilla/controller/admin/collection.py
@@ -39,6 +39,8 @@ def load_test_collection(project):
id = ptm.set_test_collection(new_name, "")
ptm.set_test_collection_map(id, product_names[ new_name ])
+ ptm.cache_ref_data()
+
ptm.disconnect()
def get_test_collection_name(product, version, branch):
View
2  datazilla/controller/admin/metrics/perftest_metrics.py
@@ -139,7 +139,7 @@ def check_run_conditions(test_name, rep_count, push_node, branch, debug):
println(u"Cannot run {0}".format(test_name), debug)
return False
- if rep_count < 3:
+ if rep_count < 5:
#If we don't have more than one replicate we cannot
#run any of the existing metric tests
println(
View
8 datazilla/controller/admin/refdata/pushlog_refdata.py
@@ -62,6 +62,14 @@ def get_all_branches():
plm.disconnect()
return branches
+def get_branch_uri(branch):
+ """Return the requested branch uri. If no branch is requested return all
+ branch uris."""
+ plm = factory.get_plm()
+ branches = plm.get_branch_uri(branch)
+ plm.disconnect()
+ return branches
+
def get_db_size():
"""Return the database size, on disk in MB"""
View
124 datazilla/controller/admin/testdata.py
@@ -5,11 +5,12 @@
import json
from datazilla.model import factory
+from datazilla.model import utils
def get_testdata(
- project, branch, revision, os_name=None, os_version=None,
- branch_version=None, processor=None, build_type=None, test_name=None,
- page_name=None):
+ project, branch, revision, product_name=None, os_name=None,
+ os_version=None, branch_version=None, processor=None,
+ build_type=None, test_name=None, page_name=None):
"""Return test data based on the parameters and optional filters."""
ptm = factory.get_ptm(project)
@@ -17,8 +18,8 @@ def get_testdata(
# get the testrun ids from perftest
test_run_ids = ptm.get_test_run_ids(
- branch, revision, os_name, os_version, branch_version, processor,
- build_type, test_name
+ branch, [revision], product_name, os_name, os_version,
+ branch_version, processor, build_type, test_name
)
blobs = ptrdm.get_object_json_blob_for_test_run(test_run_ids)
@@ -56,9 +57,9 @@ def get_testdata(
def get_metrics_data(
- project, branch, revision, os_name=None, os_version=None,
- branch_version=None, processor=None, build_type=None, test_name=None,
- page_name=None
+ project, branch, revision, product_name=None, os_name=None,
+ os_version=None, branch_version=None, processor=None, build_type=None,
+ test_name=None, page_name=None
):
"""Return metrics data based on the parameters and optional filters."""
@@ -67,8 +68,8 @@ def get_metrics_data(
# get the testrun ids from perftest
test_run_ids = ptm.get_test_run_ids(
- branch, revision, os_name, os_version, branch_version, processor,
- build_type, test_name
+ branch, [revision], product_name, os_name, os_version,
+ branch_version, processor, build_type, test_name
)
#test page metric
@@ -82,33 +83,44 @@ def get_metrics_data(
return metrics_data
def get_metrics_summary(
- project, branch, revision, os_name=None, os_version=None,
- branch_version=None, processor=None, build_type=None, test_name=None
+ project, branch, revision, product_name=None, os_name=None,
+ os_version=None, branch_version=None, processor=None, build_type=None,
+ test_name=None, pushlog_project=None
):
"""Return a metrics summary based on the parameters and optional filters."""
+ plm = factory.get_plm(pushlog_project)
ptm = factory.get_ptm(project)
mtm = factory.get_mtm(project)
# get the testrun ids from perftest
test_run_ids = ptm.get_test_run_ids(
- branch, revision, os_name, os_version, branch_version, processor,
- build_type, test_name
+ branch, [revision], product_name, os_name, os_version,
+ branch_version, processor, build_type, test_name
)
#test page metric
metrics_data = mtm.get_metrics_summary(test_run_ids)
+ #get push info
+ push_data = plm.get_node_from_revision(revision, branch)
+ metrics_data['push_data'] = push_data
+
+ #get the products associated with this revision/branch combination
+ products = ptm.get_revision_products(revision, branch)
+ metrics_data['products'] = products
+
+ plm.disconnect()
ptm.disconnect()
mtm.disconnect()
return metrics_data
def get_metrics_pushlog(
- project, branch, revision, os_name=None, os_version=None, branch_version=None,
- processor=None, build_type=None, test_name=None, page_name=None,
- days_ago=None, pushes_before=None, pushes_after=None, numdays=None,
- pushlog_project=None
+ project, branch, revision, product_name=None, os_name=None,
+ os_version=None, branch_version=None, processor=None, build_type=None,
+ test_name=None, page_name=None, days_ago=None, pushes_before=None,
+ pushes_after=None, numdays=None, pushlog_project=None
):
"""Return a metrics summary based on the parameters and optional filters."""
@@ -116,53 +128,59 @@ def get_metrics_pushlog(
ptm = factory.get_ptm(project)
mtm = factory.get_mtm(project)
- pushlog = {}
- if days_ago > 0:
- pushlog = plm.get_branch_pushlog(None, days_ago, numdays, branch)
- else:
- pushlog = plm.get_branch_pushlog_by_revision(
- revision, branch, pushes_before, pushes_after
+ aggregate_pushlog, changeset_lookup = plm.get_branch_pushlog_by_revision(
+ revision, branch, pushes_before, pushes_after
)
- aggregate_pushlog = []
pushlog_id_index_map = {}
+ all_revisions = []
+
+ for index, node in enumerate(aggregate_pushlog):
+
+ pushlog_id_index_map[node['pushlog_id']] = index
+
+ aggregate_pushlog[index]['metrics_data'] = []
+ aggregate_pushlog[index]['dz_revision'] = ""
+ aggregate_pushlog[index]['branch_name'] = branch
+
+ changesets = changeset_lookup[ node['pushlog_id'] ]
+
+ #The revisions associated with a push are returned in reverse order
+ #from the pushlog web service. This orders them the same way tbpl
+ #does.
+ changesets['revisions'].reverse()
+
+ #truncate the revision strings and collect them
+ for cset_index, revision_data in enumerate(changesets['revisions']):
+
+ full_revision = revision_data['revision']
- for node in pushlog:
- if node['pushlog_id'] not in pushlog_id_index_map:
- node_struct = {
- 'revisions':[],
- 'dz_revision':"",
- 'branch_name':node['name'],
- 'date':node['date'],
- 'push_id':node['push_id'],
- 'pushlog_id':node['pushlog_id'],
- 'metrics_data':[],
- }
+ revision = mtm.truncate_revision(full_revision)
+ changesets['revisions'][cset_index]['revision'] = revision
- aggregate_pushlog.append(node_struct)
- index = len(aggregate_pushlog) - 1
+ all_revisions.append(revision)
- pushlog_id_index_map[node['pushlog_id']] = index
+ aggregate_pushlog[index]['revisions'] = changesets['revisions']
- pushlog_index = pushlog_id_index_map[ node['pushlog_id'] ]
- revision = mtm.truncate_revision(node['node'])
- aggregate_pushlog[pushlog_index]['revisions'].append(revision)
pushlog_id_list = pushlog_id_index_map.keys()
# get the testrun ids from perftest
filtered_test_run_ids = ptm.get_test_run_ids(
- branch, None, os_name, os_version, branch_version, processor,
- build_type, test_name, page_name
+ branch, all_revisions, product_name, os_name, os_version,
+ branch_version, processor, build_type, test_name
)
+ # get the test run ids associated with the pushlog ids
pushlog_test_run_ids = mtm.get_test_run_ids_from_pushlog_ids(
pushlog_ids=pushlog_id_list
)
+ # get intersection
test_run_ids = list( set(filtered_test_run_ids).intersection(
set(pushlog_test_run_ids)) )
+ # get the metrics data for the intersection
metrics_data = mtm.get_metrics_data_from_test_run_ids(
test_run_ids, page_name
)
@@ -192,3 +210,21 @@ def get_application_log(project, revision):
log = mtm.get_application_log(revision)
return log
+def get_default_version(project, branch, product_name):
+
+ ptm = factory.get_ptm(project)
+
+ default_version = ptm.get_default_branch_version(
+ branch, product_name
+ )
+
+ ptm.disconnect()
+
+ version = ""
+ if 'version' in default_version:
+ version = default_version['version']
+
+ return version
+
+
+
View
172 datazilla/model/base.py
@@ -85,8 +85,6 @@ def create(cls, host=None, type=None, project=None):
use "MySQL-Engine", where "Engine" could be "InnoDB", "Aria", etc. Not
all contenttypes need to be represented; any that aren't will use the
default (``MySQL-InnoDB``).
-
-
"""
project = project or cls.DEFAULT_PROJECT
@@ -137,6 +135,28 @@ def get_branch_list(self, branch=None):
return branch_list
+ def get_branch_uri(self, branch=None):
+
+ if branch:
+
+ proc = 'hgmozilla.selects.get_branch_uri'
+
+ data = self.hg_ds.dhub.execute(
+ proc=proc,
+ debug_show=self.DEBUG,
+ placeholders=[branch],
+ return_type='tuple',
+ )
+ else:
+ proc = 'hgmozilla.selects.get_all_branch_uris'
+
+ data = self.hg_ds.dhub.execute(
+ proc=proc,
+ debug_show=self.DEBUG,
+ return_type='tuple',
+ )
+
+ return data
def get_all_pushlogs(self):
@@ -246,25 +266,73 @@ def get_branch_pushlog_by_revision(
node = push_data[0]['node']
push_id = push_data[0]['push_id']
+ branch_id = push_data[0]['branch_id']
pushes_before_proc = 'hgmozilla.selects.get_push_ids_before_node'
pushes_after_proc = 'hgmozilla.selects.get_push_ids_after_node'
+ before_boundary = push_id - pushes_before
+
pushes_before_data = self.hg_ds.dhub.execute(
proc=pushes_before_proc,
debug_show=self.DEBUG,
return_type='tuple',
- placeholders=[push_id, branch_name, pushes_before]
+ placeholders=[ push_id, before_boundary, branch_id ]
)
+ after_boundary = push_id + pushes_after
+
pushes_after_data = self.hg_ds.dhub.execute(
proc=pushes_after_proc,
debug_show=self.DEBUG,
return_type='tuple',
- placeholders=[push_id, branch_name, pushes_after]
+ placeholders=[ push_id, after_boundary, branch_id ]
+ )
+
+ #Combine all of the requested push data
+ pushlog = pushes_before_data + push_data + pushes_after_data
+
+ #Retrieve a complete list of all of the pushlog ids
+ pushlog_ids = []
+
+ map(
+ lambda n: pushlog_ids.append(n['pushlog_id']),
+ pushlog
)
- return pushes_before_data + push_data + pushes_after_data
+ #Use a separate query to retrieve associated revisions so
+ #we can control the number of pushes by using a LIMIT clause
+ changeset_data_proc = 'hgmozilla.selects.get_changeset_data_for_pushes'
+
+ #Build the sql WHERE IN clause
+ where_in_clause = ','.join( map( lambda v:'%s', pushlog_ids ) )
+
+ changeset_data = self.hg_ds.dhub.execute(
+ proc=changeset_data_proc,
+ debug_show=self.DEBUG,
+ return_type='tuple',
+ placeholders=pushlog_ids,
+ replace=[where_in_clause]
+ )
+
+ #Aggregate changesets
+ changeset_lookup = {}
+ for changeset in changeset_data:
+ if changeset['pushlog_id'] not in changeset_lookup:
+ changeset_struct = {
+ 'revisions':[],
+ 'pushlog_id':changeset['pushlog_id']
+ }
+
+ changeset_lookup[ changeset['pushlog_id'] ] = changeset_struct
+
+ changeset_lookup[ changeset['pushlog_id'] ]['revisions'].append(
+ { 'revision':changeset['node'],
+ 'desc':changeset['desc'],
+ 'author':changeset['author'] }
+ )
+
+ return pushlog, changeset_lookup
def get_params(self, numdays, enddate=None):
"""
@@ -288,6 +356,7 @@ def get_params(self, numdays, enddate=None):
"full": 1,
"startdate": _startdate.strftime("%m/%d/%Y"),
}
+
# enddate is optional. the endpoint will just presume today,
# if not given.
if enddate:
@@ -476,6 +545,10 @@ class PerformanceTestModel(DatazillaModelBase):
# content types that every project will have
CONTENT_TYPES = ["perftest", "objectstore"]
+ # Total number of replicates allowed for a
+ # single test suite associated with a JSON object
+ REPLICATE_LIMIT = 5000
+
@classmethod
def create(cls, project, hosts=None, types=None, cron_batch=None):
"""
@@ -618,6 +691,19 @@ def get_products(self, key_column=None):
return products
+ def get_revision_products(self, revision, branch):
+
+ proc = 'perftest.selects.get_revision_products'
+
+ products = self.sources["perftest"].dhub.execute(
+ proc=proc,
+ debug_show=self.DEBUG,
+ return_type='tuple',
+ placeholders=[revision, branch]
+ )
+
+ return products
+
def get_default_products(self):
@@ -635,6 +721,24 @@ def get_default_products(self):
return default_products
+ def get_default_branch_version(self, branch, product_name):
+
+ proc = 'perftest.selects.get_default_products'
+
+ products = self.sources["perftest"].dhub.execute(
+ proc=proc,
+ debug_show=self.DEBUG,
+ return_type='tuple'
+ )
+
+ target_product = {}
+
+ for product in products:
+ if (product['branch'] == branch) and (product['product'] == product_name):
+ target_product = product
+ break
+
+ return target_product
def get_machines(self):
@@ -852,22 +956,22 @@ def get_test_run_value_summary(self, test_run_id):
def get_test_run_ids(
- self, branch, revision, os_name=None, os_version=None,
- branch_version=None, processor=None, build_type=None,
- test_name=None, page_name=None):
+ self, branch, revisions, product_name=None, os_name=None,
+ os_version=None, branch_version=None, processor=None,
+ build_type=None, test_name=None, page_name=None):
proc = 'perftest.selects.get_test_run_ids'
placeholders = [branch]
rep = []
- if not revision:
- proc = 'perftest.selects.get_test_run_ids_no_revision'
- else:
- placeholders.append(revision)
-
- if page_name:
+ if revisions:
+ revision_string = ','.join( map( lambda v:str(v), revisions ) )
+ self.get_replace_and_placeholders(
+ rep, placeholders, 'tr.revision', revision_string
+ )
+ if product_name:
self.get_replace_and_placeholders(
- rep, placeholders, 'pg.url', page_name
+ rep, placeholders, 'p.product', product_name
)
if os_name:
self.get_replace_and_placeholders(
@@ -1012,8 +1116,7 @@ def set_test_collection(self, name, description):
id = self._insert_data_and_get_id('set_test_collection',
[ name,
- description,
- name ])
+ description ])
return id
@@ -1225,25 +1328,38 @@ def _set_test_aux_data(self, data, test_id, test_run_id):
def _set_test_values(self, data, test_id, test_run_id):
"""Insert test values to database for given test_id and test_run_id."""
+
+ total_replicates = 0
+
for page, values in data['results'].items():
page_id = self._get_or_create_page_id(page, test_id)
placeholders = []
for index, value in enumerate(values, 1):
- placeholders.append(
- (
- test_run_id,
- index,
- page_id,
- # TODO: Need to get the value id into the json
- 1,
- value,
+
+ total_replicates += 1
+
+ if total_replicates <= self.REPLICATE_LIMIT:
+
+ placeholders.append(
+ (
+ test_run_id,
+ index,
+ page_id,
+ # TODO: Need to get the value id into the json
+ 1,
+ value,
+ )
)
- )
- self._insert_data(
- 'set_test_values', placeholders, executemany=True)
+ else:
+ #Replicate limit reached
+ break
+
+ if placeholders:
+ self._insert_data(
+ 'set_test_values', placeholders, executemany=True)
def _get_or_create_aux_id(self, aux_data, test_id):
View
54 datazilla/model/sql/hgmozilla.json
@@ -65,6 +65,7 @@
"sql":"SELECT p.id AS 'pushlog_id',
p.push_id,
p.date,
+ p.user,
c.node,
b.id AS 'branch_id',
b.name
@@ -76,19 +77,32 @@
"host":"read_host"
},
+ "get_branch_uri":{
+
+ "sql":"SELECT b.name, b.uri
+ FROM branches AS b
+ WHERE b.name = ?",
+
+ "host":"read_host"
+ },
+
+ "get_all_branch_uris":{
+
+ "sql":"SELECT b.name, b.uri
+ FROM branches AS b",
+
+ "host":"read_host"
+ },
+
"get_push_ids_before_node":{
"sql":"SELECT p.id AS 'pushlog_id',
p.push_id,
p.date,
- c.node,
- b.id AS 'branch_id',
- b.name
+ p.user
FROM pushlogs AS p
- LEFT JOIN changesets AS c ON p.id = c.pushlog_id
- LEFT JOIN branches AS b ON p.branch_id = b.id
- WHERE p.push_id < ? AND b.name = ?
- LIMIT ?",
+ WHERE p.push_id < ? AND p.push_id >= ? AND p.branch_id = ?
+ ORDER BY p.push_id",
"host":"read_host"
},
@@ -98,18 +112,26 @@
"sql":"SELECT p.id AS 'pushlog_id',
p.push_id,
p.date,
- c.node,
- b.id AS 'branch_id',
- b.name
+ p.user
FROM pushlogs AS p
- LEFT JOIN changesets AS c ON p.id = c.pushlog_id
- LEFT JOIN branches AS b ON p.branch_id = b.id
- WHERE p.push_id > ? AND b.name = ?
- LIMIT ?",
+ WHERE p.push_id > ? AND p.push_id <= ? AND p.branch_id = ?
+ ORDER BY p.push_id",
"host":"read_host"
},
+ "get_changeset_data_for_pushes":{
+ "sql":"SELECT c.id,
+ c.pushlog_id,
+ c.node,
+ c.author,
+ c.desc
+ FROM changesets AS c
+ WHERE c.pushlog_id IN (REP0)",
+
+ "host":"read_host"
+
+ },
"get_all_branch_pushlogs":{
"sql":"SELECT p.id AS 'pushlog_id',
p.push_id,
@@ -129,9 +151,11 @@
"sql":"SELECT p.id AS 'pushlog_id',
p.push_id,
p.date,
+ p.user,
b.id AS 'branch_id',
b.name,
- c.node
+ c.node,
+ c.desc
FROM pushlogs AS p
LEFT JOIN changesets AS c ON p.id = c.pushlog_id
LEFT JOIN branches AS b ON p.branch_id = b.id
View
36 datazilla/model/sql/perftest.json
@@ -271,8 +271,7 @@
"set_test_collection":{
"sql":"INSERT INTO `test_collection` (`name`, `description`)
- VALUES (?, ?)
- ON DUPLICATE KEY UPDATE `name`=?",
+ VALUES (?, ?)",
"host":"master_host"
},
@@ -411,8 +410,8 @@
"get_test_run_counts":{
"sql":"SELECT COUNT(tr.id) as count, p.branch
- FROM test_run as tr
- LEFT JOIN build as b ON tr.build_id = b.id
+ FROM test_run AS tr
+ LEFT JOIN build AS b ON tr.build_id = b.id
LEFT JOIN product as p ON b.product_id = p.id
WHERE tr.date_run BETWEEN ? AND ?
GROUP BY p.branch",
@@ -426,22 +425,8 @@
LEFT JOIN build AS b ON b.id = tr.build_id
LEFT JOIN product AS p ON p.id = b.product_id
LEFT JOIN machine AS m ON m.id = tr.machine_id
- LEFT JOIN operating_system as os on os.id = m.operating_system_id
- LEFT JOIN test as t on t.id = tr.test_id
- WHERE p.branch = ? AND tr.revision = ? REP0",
-
- "host":"read_host"
- },
- "get_test_run_ids_no_revision":{
- "sql":"SELECT DISTINCT(tr.id) AS test_run_id
- FROM test_run AS tr
- LEFT JOIN test_value AS tv ON tr.id = tv.test_run_id
- LEFT JOIN pages AS pg ON tv.page_id = pg.id
- LEFT JOIN build AS b ON b.id = tr.build_id
- LEFT JOIN product AS p ON p.id = b.product_id
- LEFT JOIN machine AS m ON m.id = tr.machine_id
- LEFT JOIN operating_system as os on os.id = m.operating_system_id
- LEFT JOIN test as t on t.id = tr.test_id
+ LEFT JOIN operating_system AS os ON os.id = m.operating_system_id
+ LEFT JOIN test AS t ON t.id = tr.test_id
WHERE p.branch = ? REP0",
"host":"read_host"
@@ -461,6 +446,17 @@
"host":"read_host"
},
+ "get_revision_products":{
+
+ "sql":"SELECT p.product, p.branch, p.version
+ FROM test_run AS tr
+ LEFT JOIN build AS b ON tr.build_id = b.id
+ LEFT JOIN product AS p ON b.product_id = p.id
+ WHERE tr.revision = ? AND p.branch = ?
+ GROUP BY p.product, p.branch, p.version",
+
+ "host":"read_host"
+ },
"get_test_run_values":{
"sql":"SELECT tv.test_run_id,
View
2  datazilla/model/utils.py
@@ -11,8 +11,6 @@
import datetime
import sys
-
-
def is_number(s):
try:
float(s)
View
6 datazilla/webapp/apps/datazilla/refdata/pushlog_views.py
@@ -64,6 +64,12 @@ def get_all_branches(request):
branches = pushlog_refdata.get_all_branches()
return HttpResponse(json.dumps(branches), content_type=API_CONTENT_TYPE)
+def get_branch_uri(request):
+ """Get the uri associated with branch"""
+ branch = request.GET.get("branch", None)
+ branches = pushlog_refdata.get_branch_uri(branch)
+ return HttpResponse(json.dumps(branches), content_type=API_CONTENT_TYPE)
+
def get_db_size(request):
"""Return the size of the DB on disk in MB."""
View
2  datazilla/webapp/apps/datazilla/refdata/urls_no_project.py
@@ -8,6 +8,8 @@
(r"^pushlog/branches/?$", "pushlog_views.get_all_branches"),
+ (r"^pushlog/branch_uri/?$", "pushlog_views.get_branch_uri"),
+
(r"^pushlog/db_size/?$", "pushlog_views.get_db_size"),
)
View
43 datazilla/webapp/apps/datazilla/testdata/views.py
@@ -16,7 +16,6 @@
API_CONTENT_TYPE = 'application/json; charset=utf-8'
-
def get_testdata(request, project, branch, revision):
"""
Apply data filters and return all test data objects associated with the
@@ -51,9 +50,18 @@ def get_metrics_data(request, project, branch, revision):
"""
Apply filters and return all metrics data associated with the revision.
"""
+
+ #Default to most current version of Firefox
+ product_name = request.GET.get("product", "Firefox")
os_name = request.GET.get("os_name", None)
os_version = request.GET.get("os_version", None)
+
branch_version = request.GET.get("branch_version", None)
+ if not branch_version:
+ branch_version = testdata.get_default_version(
+ project, branch, product_name
+ )
+
processor = request.GET.get("processor", None)
build_type = request.GET.get("build_type", None)
test_name = request.GET.get("test_name", None)
@@ -64,6 +72,7 @@ def get_metrics_data(request, project, branch, revision):
project,
branch,
revision,
+ product_name=product_name,
os_name=os_name,
os_version=os_version,
branch_version=branch_version,
@@ -80,24 +89,35 @@ def get_metrics_summary(request, project, branch, revision):
Apply filters and build a summary of all metric test evaluations.
"""
+ #Default to most current version of Firefox
+ product_name = request.GET.get("product", "Firefox")
os_name = request.GET.get("os_name", None)
os_version = request.GET.get("os_version", None)
+
branch_version = request.GET.get("branch_version", None)
+ if not branch_version:
+ branch_version = testdata.get_default_version(
+ project, branch, product_name
+ )
+
processor = request.GET.get("processor", None)
build_type = request.GET.get("build_type", None)
test_name = request.GET.get("test_name", None)
+ pushlog_project = request.GET.get("pushlog_project", None)
return HttpResponse(
json.dumps(testdata.get_metrics_summary(
project,
branch,
revision,
+ product_name=product_name,
os_name=os_name,
os_version=os_version,
branch_version=branch_version,
processor=processor,
build_type=build_type,
test_name=test_name,
+ pushlog_project=pushlog_project
)),
content_type=API_CONTENT_TYPE,
)
@@ -106,9 +126,18 @@ def get_metrics_pushlog(request, project, branch, revision):
"""
Apply filters and return trend line data for the time period requested.
"""
+
+ #Default to most current version of Firefox
+ product_name = request.GET.get("product", "Firefox")
os_name = request.GET.get("os_name", None)
os_version = request.GET.get("os_version", None)
+
branch_version = request.GET.get("branch_version", None)
+ if not branch_version:
+ branch_version = testdata.get_default_version(
+ project, branch, product_name
+ )
+
processor = request.GET.get("processor", None)
build_type = request.GET.get("build_type", None)
test_name = request.GET.get("test_name", None)
@@ -121,6 +150,10 @@ def get_metrics_pushlog(request, project, branch, revision):
except ValueError:
pass
+ #applies to both before/after, so total of 2*maximum_pushes
+ #are allowed
+ maximum_pushes = 1000
+
pushes_before = 10
try:
pushes_before = int(request.GET.get("pushes_before", 10))
@@ -133,6 +166,12 @@ def get_metrics_pushlog(request, project, branch, revision):
except ValueError:
pass
+ #Set maximum limit for pushes before/after
+ if pushes_before > maximum_pushes:
+ pushes_before = maximum_pushes
+ if pushes_after > maximum_pushes:
+ pushes_after = maximum_pushes
+
numdays = 0
try:
numdays = int(request.GET.get("numdays", 0))
@@ -152,6 +191,7 @@ def get_metrics_pushlog(request, project, branch, revision):
project,
branch,
revision,
+ product_name=product_name,
os_name=os_name,
os_version=os_version,
branch_version=branch_version,
@@ -181,3 +221,4 @@ def get_application_log(request, project, revision):
)
+
View
35 datazilla/webapp/apps/datazilla/views.py
@@ -238,41 +238,6 @@ def _get_test_run_summary(project, method, request, dm):
json_data = '{}'
- #Commenting the use of memcache out for now, the shared memcache
- #in production is failing to return the expected data. This works
- #in development so it's likely a configuration issue of some sort.
- """
- if product_ids and (not test_ids) and (not platform_ids):
-
- if len(product_ids) > 1:
- extend_list = { 'data':[], 'columns':[] }
- for id in product_ids:
- key = utils.get_summary_cache_key(project, str(id), time_key)
-
- compressed_json_data = cache.get(key)
-
- if compressed_json_data:
- json_data = zlib.decompress( compressed_json_data )
- data = json.loads( json_data )
- extend_list['data'].extend( data['data'] )
- extend_list['columns'] = data['columns']
-
- json_data = json.dumps(extend_list)
-
-
- else:
- key = utils.get_summary_cache_key(
- project,
- str(product_ids[0]),
- time_key,
- )
- compressed_json_data = cache.get(key)
-
- if compressed_json_data:
- json_data = zlib.decompress( compressed_json_data )
-
- else:
- """
table = dm.get_test_run_summary(time_ranges[time_key]['start'],
time_ranges[time_key]['stop'],
product_ids,
View
72 datazilla/webapp/static/css/summary.css
@@ -4,7 +4,11 @@ body {
font-size: 14px;
padding: 0;
border-top: 2px solid #676767;
- min-width: 1024px;
+ min-width: 1065px;
+}
+a {
+ text-decoration: underline;
+ font-weight: bold;
}
div.su-large-text {
font-size: 14px;
@@ -34,7 +38,7 @@ div.su-panel-base {
}
div.su-main-gauge {
float: left;
- width: 325px;
+ width: 375px;
margin-left:10px;
}
div.su-reference-info {
@@ -49,6 +53,11 @@ div.su-spinner {
background: transparent url(/static/images/spinner.gif) no-repeat center center;
height:280px;
}
+div.su-small-spinner {
+ background: transparent url(/static/images/spinner.gif) no-repeat center center;
+ height:35px;
+ width:35px;
+}
.su-scroll-panel {
overflow-y: auto;
height:200px;
@@ -140,7 +149,6 @@ div.su-grid-column {
white-space:nowrap;
text-align:center;
float: left;
-
width:30px;
height:25px;
}
@@ -199,8 +207,7 @@ div.su-grid-value {
background: #ffffff;
border: 3px solid #e8e8e8;
}
-.arrow_box:after,
-.arrow_box:before {
+.arrow_box:after, .arrow_box:before {
top: 100%;
border: solid transparent;
content: " ";
@@ -222,11 +229,66 @@ div.su-grid-value {
left: 50%;
margin-left: -24px;
}
+
+.arrow_box_up {
+ position: relative;
+ background: #ffffff;
+ border: 3px solid #e8e8e8;
+}
+.arrow_box_up:after, .arrow_box_up:before {
+ bottom: 100%;
+ border: solid transparent;
+ content: " ";
+ height: 0;
+ width: 0;
+ position: absolute;
+ pointer-events: none;
+}
+.arrow_box_up:after {
+ border-color: rgba(255, 255, 255, 0);
+ border-bottom-color: #ffffff;
+ border-width: 20px;
+ left: 50%;
+ margin-left: -20px;
+}
+.arrow_box_up:before {
+ border-color: rgba(232, 232, 232, 0);
+ border-bottom-color: #e8e8e8;
+ border-width: 24px;
+ left: 50%;
+ margin-left: -24px;
+}
div.su-data-series-panel {
margin-top: 5px;
padding: 0.5em;
overflow: auto;
}
+div.su-datum-info-panel {
+ margin-top: 5px;
+ margin-left: 5px;
+ padding: 0.5em;
+ float:right;
+ width:190px;
+}
+.su-datum-value {
+ text-align:right;
+ float:right;
+}
+/*style for the description field of a patch*/
+div.su-datum-desc-value {
+ text-align:right;
+ margin-top:5px;
+ margin-bottom:15px;
+}
.ui-button-text{
font-size: 11px !important;
}
+.su-vertical-text {
+ -webkit-transform:rotate(270deg);
+ -moz-transform:rotate(270deg);
+ -o-transform: rotate(270deg);
+ white-space:nowrap;
+ position:absolute;
+ left:135px;
+ margin-top:150px;
+}
View
57 datazilla/webapp/static/js/metric_summary/MetricDashboardComponent.js
@@ -79,8 +79,10 @@ var MetricDashboardView = new Class({
this.progressBarTitleClassName = 'su-progressbar-title';
this.progressBarClassName = 'su-progressbar';
- this.productTestsSel = '#su_product_tested';
+ this.revisionProductsSel = '#su_revision_products';
this.revisionTestedSel = '#su_revision_tested';
+ this.pushDescSel = '#su_push_desc';
+ this.authorSel = '#su_push_author';
this.totalCountSel = '#su_total_count';
this.noTrendCountSel = '#su_no_trend_count';
this.passCountSel = '#su_pass_count';
@@ -191,13 +193,49 @@ var MetricDashboardView = new Class({
},
setReferenceInfo: function(data){
- var productInfo = data.product_info.name + ' ' +
- data.product_info.branch + ' ' + data.product_info.version;
+ for(var i=0; i<data.products.length; i++){
- $(this.productTestsSel).text(productInfo);
+ var product = data.products[i].product + ' ' +
+ data.products[i].branch + ' ' + data.products[i].version;
- $(this.revisionTestedSel).text(
- data.product_info.revision
+ var option = $(document.createElement('option'));
+
+ $(option).text(product);
+
+ $(option).val(
+ JSON.stringify(
+ { 'product':data.products[i].product,
+ 'version':data.products[i].version }
+ )
+ );
+
+ if((data.product_info.name === data.products[i].product) &&
+ (data.product_info.branch === data.products[i].branch) &&
+ (data.product_info.version === data.products[i].version)){
+ //This is the option the page is loaded on
+ //mark as selected
+ $(option).attr('selected', true);
+ }
+ $(this.revisionProductsSel).append(option);
+
+ }
+
+ $(this.revisionProductsSel).change(
+ _.bind( function(event){
+ var selectedOption = $(event.target).find(':selected');
+
+ var value = $(selectedOption).val();
+ var productData = JSON.parse(value);
+
+ console.log(productData);
+
+ }, this)
+ );
+
+ $(this.authorSel).text(data.push_data.user);
+
+ $(this.revisionTestedSel).html(
+ MS_PAGE.getHgUrlATag('rev', data.product_info.revision)
);
$(this.noTrendCountSel).text(data.summary.keys_without_trend);
@@ -206,6 +244,9 @@ var MetricDashboardView = new Class({
$(this.passCountSel).text(data.summary.pass.value + ' passed');
$(this.failCountSel).text(data.summary.fail.value + ' failed');
+ var descHtml = MS_PAGE.addBugzillaATagsToDesc(data.push_data.desc);
+ $(this.pushDescSel).html(descHtml);
+
},
loadProgressBars: function(targetContainer, data, order){
@@ -280,7 +321,9 @@ var MetricDashboardModel = new Class({
uri = '/' + MS_PAGE.refData.project +
'/testdata/metrics/' + MS_PAGE.refData.branch +
- '/' + MS_PAGE.refData.revision + '/summary';
+ '/' + MS_PAGE.refData.revision + '/summary?' +
+ 'product=' + MS_PAGE.refData.product + '&branch_version=' +
+ MS_PAGE.refData.branch_version;
jQuery.ajax( uri, {
accepts:'application/json',
View
32 datazilla/webapp/static/js/metric_summary/MetricGridComponent.js
@@ -166,9 +166,35 @@ var MetricGridView = new Class({
//first time the page loads.
if(this.triggerDefaultEvent){
- this._triggerEvent(
- this.gridMouseoverEvent, cell
- );
+ var platform = MS_PAGE.refData.platform;
+ var test = MS_PAGE.refData.test;
+ var initializeValue = "";
+ if(data.tests[test] && data.tests[test][platform]){
+ initializeValue = data.tests[test][platform]['pass']['percent'];
+ }
+
+ if(initializeValue){
+ //URL has test/platform specified, initialize
+ //table to requested data target
+ var initializeCell = this.getValueCell(
+ platform,
+ test,
+ initializeValue
+ );
+
+ this._triggerEvent(
+ this.gridMouseoverEvent, initializeCell
+ );
+
+ //Lock the table to the data requested
+ MS_PAGE.testPagesComponent.view.lockTable();
+
+ }else {
+ //No target table use first defined cell
+ this._triggerEvent(
+ this.gridMouseoverEvent, cell
+ );
+ }
this.triggerDefaultEvent = false;
}
View
63 datazilla/webapp/static/js/metric_summary/MetricSummaryPage.js
@@ -18,15 +18,56 @@ var MetricSummaryPage = new Class( {
this.failColor = '#FF7700';
this.passColor = '#44AA00';
+ this.branchUri = '/refdata/pushlog/branch_uri?branch=';
+ this.revisionUrl = 'https://hg.mozilla.org/URI/pushloghtml?TYPE=';
+ this.bugzillaUrl = 'https://bugzilla.mozilla.org/show_bug.cgi?id=';
+
},
+ getHgUrlATag: function(type, revision){
+ /****
+ * Get an html <a> tag for the given type and revision provided.
+ * Type can be 'rev' or 'changeset'.
+ ****/
+ var revisionUrl = this.revisionUrl.replace(
+ 'URI', MS_PAGE.refData.branch_uri
+ );
+
+ revisionUrl = revisionUrl.replace(
+ 'TYPE', type
+ ) + revision;
+
+ var a = $(document.createElement('a'));
+ $(a).attr('href', revisionUrl);
+ $(a).attr('target', '_blank');
+ $(a).text(revision);
+
+ return a;
+ },
+ addBugzillaATagsToDesc: function(desc){
+ /****
+ * Replace bugzilla bug number strings (Bug 123456) with
+ * an html <a> tag.
+ ****/
+ return desc.replace(
+ /(bug).*?(\d+)/ig,
+ '<a target="_blank" href="' + this.bugzillaUrl + "$2" +
+ '">$1 $2</a>');
+ },
setRefData: function(){
MS_PAGE.refData = {};
- MS_PAGE.refData.project = MS_PAGE.urlObj.data.seg.path[0];
- MS_PAGE.refData.branch = MS_PAGE.urlObj.data.seg.path[2];
- MS_PAGE.refData.revision = MS_PAGE.urlObj.data.seg.path[3];
+ var urlObj = MS_PAGE.urlObj.data;
+
+ MS_PAGE.refData.project = urlObj.seg.path[0];
+ MS_PAGE.refData.branch = urlObj.seg.path[2];
+ MS_PAGE.refData.revision = urlObj.seg.path[3];
+ MS_PAGE.refData.product = urlObj.param.query.product;
+ MS_PAGE.refData.branch_version = urlObj.param.query.branch_version;
+ MS_PAGE.refData.test = urlObj.param.query.test;
+ MS_PAGE.refData.platform = urlObj.param.query.platform;
+
},
getDatumKey: function(data){
/***
@@ -42,6 +83,21 @@ var MetricSummaryPage = new Class( {
key = key.hashCode();
return key;
+ },
+ getBranchUri: function(){
+
+ var url = this.branchUri + MS_PAGE.refData.branch;
+ jQuery.ajax( url, {
+ accepts:'application/json',
+ dataType:'json',
+ cache:false,
+ type:'GET',
+ context:this,
+ success: function(data, textStatus, jqXHR){
+ MS_PAGE.refData.branch_uri = data[0].uri;
+ console.log(MS_PAGE.refData.branch_uri);
+ }
+ });
}
});
@@ -51,6 +107,7 @@ $(document).ready(function() {
MS_PAGE = new MetricSummaryPage();
MS_PAGE.setRefData();
+ MS_PAGE.getBranchUri();
MS_PAGE.trendLineComponent = new TrendLineComponent();
MS_PAGE.testPagesComponent = new TestPagesComponent();
View
48 datazilla/webapp/static/js/metric_summary/TestPagesComponent.js
@@ -52,6 +52,12 @@ var TestPagesView = new Class({
this.failBackgroundColor = 'su-fail-background-color';
this.passBackgroundColor = 'su-pass-background-color';
+ //This is used to store the row to initialize the
+ //push log with. It's set by _adaptData to the first
+ //row in the first table loaded.
+ this.defaultRowCbSel = "";
+ this.defaultRowSelectionSent = false;
+
this.cbIdPrefix = 'su_cb_';
this.scrollHeight = parseInt($(this.tableContainerSel).css('height')) - 125;
@@ -64,6 +70,7 @@ var TestPagesView = new Class({
this.gridClickEvent = 'GRID_CLICK_EVENT';
this.gridMouseoverEvent = 'GRID_MOUSEOVER_EVENT';
this.closeDataSeriesEvent = 'CLOSE_DATA_SERIES_EVENT';
+ this.defaultRowSelectionEvent = 'DEFAULT_ROW_SELECTION_EVENT';
$(this.eventContainerSel).bind(
this.gridMouseoverEvent,
@@ -80,8 +87,8 @@ var TestPagesView = new Class({
_.bind(this.uncheckCb, this)
);
- $(this.tableSel).live(
- 'click mouseover', _.bind(this.tableEventHandler, this)
+ $(this.tableSel).bind(
+ 'click', _.bind(this.tableEventHandler, this)
);
},
@@ -130,42 +137,53 @@ var TestPagesView = new Class({
'aaSorting':[ [2, 'asc'] ]
};
+
this._adaptData(datatableOptions, eventData.data);
this.dataTable = $(this.tableSel).dataTable( datatableOptions );
+ //Send default row event out
+ if(this.defaultRowSelectionSent === false){
+
+ $(this.eventContainerSel).trigger(
+ this.defaultRowSelectionEvent, this.defaultRowCbSel
+ );
+
+ this.defaultRowSelectionSent = true;
+ }
+
},
lockTable: function(event, eventData){
$(this.lockTableSel).click();
},
uncheckCb: function(event, datumKey){
var id = this.cbIdPrefix + datumKey;
- $('#' + id).prop("checked", false);
+ $('#' + id).attr("checked", false);
},
tableEventHandler: function(event){
- if(event.type == 'mouseover'){
-
- var target = $(event.target);
- var elParent = $(target).parent();
-
- var highlightClass = "";
-
- }else if(event.type == 'click'){
+ if(event.type == 'click'){
if( $(event.target).is('input') ){
- var checked = $(event.target).attr('checked');
+ //Retrieve the associated mean
+ var row = $(event.target).closest('tr');
+ var cells = $(row).find('td');
+ var meanValue = $(cells[3]).text();
+
var pagename = $(event.target).attr(this.pagenameDataAttr);
var testSuite = $(this.testSuiteSel).text();
var platform = $(this.platformSel).text();
+ var checked = $(event.target).attr('checked');
+
var eventData = {
'checked':checked,
'pagename':pagename,
'testsuite':testSuite,
'platform':platform,
- 'platform_info':this.platformInfo
+ 'platform_info':this.platformInfo,
+ 'mean':meanValue
};
$(this.eventContainerSel).trigger(
@@ -192,6 +210,10 @@ var TestPagesView = new Class({
var key = this.cbIdPrefix + MS_PAGE.getDatumKey(keyData);
+ if( (i === 0) && (this.defaultRowCbSel === "")){
+ this.defaultRowCbSel = '#' + key;
+ }
+
//page name
var row = {};
row['0'] = '<input id="' + key +
View
768 datazilla/webapp/static/js/metric_summary/TrendLineComponent.js
@@ -18,16 +18,45 @@ var TrendLineComponent = new Class({
this.view = new TrendLineView('#TrendLineView',{});
this.model = new TrendLineModel('#TrendLineModel',{});
+ //Holds all trend line data loaded
this.trendLines = {};
+ //Maintains the trend line load order according
+ //to the order the user loads them
this.trendLineOrder = [];
- //[series index][datapoint index]
+ //Maps the flot series index to the trendline keys
+ //and allows flot callback functions to map to the
+ //corresponding reference data in this.trendLines
this.seriesIndexToKey = [];
- this.seriesIndexToKey.push([]); //pass series
- this.seriesIndexToKey.push([]); //fail series
+
+ //pass series
+ this.passSeriesIndex = this.seriesIndexToKey.push([]) - 1;
+ //fail series
+ this.failSeriesIndex = this.seriesIndexToKey.push([]) - 1;
+ //no data series
+ this.noMetricsDataSeriesIndex = this.seriesIndexToKey.push([]) - 1;
this.tickDisplayDates = {};
+ //Holds all event data received. It's used for
+ //reloading all series data with updated pushes
+ //before and after values.
+ this.eventData = [];
+
+ this.pushesBefore = 20;
+ this.pushesAfter = 5;
+
+ //true indicates push retrieval in progress
+ //false indicates more pushes can be retrieved
+ this.getPushState = false;
+
+ //The first simulated table checkbox click will
+ //not have the eventData.checked attribute set. This
+ //switch allows initializeTrend to fully process the
+ //event anyway. Not sure why the .click() function
+ //does not take care of this.
+ this.simulatedTableCBClick = false;
+
this.plot = undefined;
this.chartOptions = {
@@ -46,6 +75,18 @@ var TrendLineComponent = new Class({
'autoscaleMargin':0.1
},
+ 'series': {
+
+ 'points': {
+ 'radius': 2.5
+ }
+ },
+
+ 'selection':{
+ 'mode':'x',
+ 'color':'#BDBDBD'
+ },
+
'hooks': {
'draw':[this.draw]
} };
@@ -53,31 +94,75 @@ var TrendLineComponent = new Class({
this.tableInputClickEvent = 'TABLE_CLICK_EVENT';
this.closeDataSeriesEvent = 'CLOSE_DATA_SERIES_EVENT';
+ this.defaultRowSelectionEvent = 'DEFAULT_ROW_SELECTION_EVENT';
+
+ $(this.view.chartContainerSel).bind(
+ 'plotclick', _.bind(this._clickPlot, this)
+ );
$(this.view.chartContainerSel).bind(
- 'plotclick', _.bind(this._clickPlot, this));
+ 'plothover', _.bind(this._hoverPlot, this)
+ );
$(this.view.chartContainerSel).bind(
- 'plothover', _.bind(this._hoverPlot, this));
+ 'plotselected', _.bind(this._selectPlot, this)
+ );
$(this.view.eventContainerSel).bind(
this.tableInputClickEvent,
_.bind(this.initializeTrend, this)
);
+ $(this.view.getPushesSel).bind(
+ 'click', _.bind(this.getPushes, this)
+ );
+
$(this.view.detailContainerOneSel).live(
'click mouseover mouseout', _.bind(this.closeDataSeries, this)
);
+ $(this.view.pushesBeforeSel).bind(
+ 'keyup', _.bind(this._handlePushAroundInput, this)
+ );
+
+ $(this.view.pushesAfterSel).bind(
+ 'keyup', _.bind(this._handlePushAroundInput, this)
+ );
+
+ //Set the push counts in the HTML
+ this.view.setPushCounts(this.pushesBefore, this.pushesAfter);
+
+ //Simulate click on the default test suite table row
+ $(this.view.eventContainerSel).bind(
+ this.defaultRowSelectionEvent,
+ _.bind(this.clickTableCB, this)
+ );
+
+ },
+ clickTableCB: function(event, eventData){
+ this.simulatedTableCBClick = true;
+ $(eventData).click();
},
initializeTrend: function(event, eventData){
- this.eventDataReceived = eventData;
+ if(this.simulatedTableCBClick === true){
+ eventData.checked = 'checked';
+ this.simulatedTableCBClick = false;
+ }
if( eventData.checked ){
//Load trend line
+ var key = MS_PAGE.getDatumKey(eventData);
+
+ this.trendLineOrder.push(key);
+
+ var pushCounts = this.view.getPushCounts();
+
+ this.setGetPushState();
+
this.model.getTrendLine(
- this, this.loadTrendData, this.dataLoadError, eventData
+ this, this.loadTrendData, this.dataLoadError, eventData,
+ pushCounts.before, pushCounts.after
);
}else {
@@ -86,11 +171,42 @@ var TrendLineComponent = new Class({
this.deleteDataSeries(key);
}
},
- loadTrendData: function(data, response){
+ getPushes: function(event){
+
+ var pushCounts = this.view.getPushCounts();
+
- data = this._loadMockData(data);
+ for(var i=0; i<this.trendLineOrder.length; i++){
+
+ var key = this.trendLineOrder[i];
+
+ this.setGetPushState();
+
+ //Build the eventData data structure
+ //for each trend line
+ var trendLineEventData = {};
+ trendLineEventData.pagename = this.trendLines[key].pagename;
+ trendLineEventData.testsuite = this.trendLines[key].testsuite;
+ trendLineEventData.platform = this.trendLines[key].platform;
+ trendLineEventData.platform_info = this.trendLines[key].platform_info;
+
+ this.model.getTrendLine(
+ this, this.loadTrendData, this.dataLoadError,
+ trendLineEventData, pushCounts.before, pushCounts.after
+ );
+ }
+ },
+ setGetPushState: function(){
+ this.getPushState = true;
+ this.view.setGetPushState();
+ },
+ unsetGetPushState: function(){
+ this.getPushState = false;
+ this.view.unsetGetPushState();
+ },
+ loadTrendData: function(data, response, eventData){
- this._loadTrendLineData(data);
+ this._loadTrendLineData(data, eventData);
var chartData = [];
@@ -100,14 +216,16 @@ var TrendLineComponent = new Class({
this.tickDisplayDates = {};
- var passSeriesIndex = 0;
- var failSeriesIndex = 1;
+ //flot data item to be passed to hoverPlot
+ var flotItem = {};
var failDataset = this.getFailDataset();
var passDataset= this.getPassDataset();
+ var noDataDataset= this.getNoDataDataset();
chartData.push(passDataset);
chartData.push(failDataset);
+ chartData.push(noDataDataset);
for(var i=0; i<this.trendLineOrder.length; i++){
@@ -117,70 +235,126 @@ var TrendLineComponent = new Class({
var failData = [];
var trendData = [];
- var seriesColor = MS_PAGE.passColor;
+ var seriesColor = '';
var targetRevisionType = 'pass';
var trendDataset = this.getTrendLineDataset();
- var trendIndex = chartData.push(trendDataset);
+ var trendIndex = chartData.push(trendDataset) - 1;
- this.seriesIndexToKey[trendIndex - 1] = [];
+ this.seriesIndexToKey[trendIndex] = [];
var passIndex = 0;
var failIndex = 0;
+ var noDataIndex = 0;
+
+ var metricsData = [];
for(var j=0; j<this.trendLines[key]['data'].length; j++){
+ metricsData = this.trendLines[key]['data'][j]['metrics_data'];
+
+ //Store x axis tick labels
if(!this.tickDisplayDates[j]){
- var unixTimestamp = this.trendLines[key]['data'][j]['date'];
- var tickLabel = this.convertTimestampToDate(unixTimestamp);
+
+ var tickLabel = "";
+
+ //Exclude first and last tick labels on the x-axis
+ //to keep things unclutered
+ if((j > 0) && (j <this.trendLines[key]['data'].length - 1)){
+ var unixTimestamp = this.trendLines[key]['data'][j]['date'];
+ tickLabel = this.convertTimestampToDate(
+ unixTimestamp, false
+ );
+ }
+
this.tickDisplayDates[j] = tickLabel;
}
- var pageData = this.trendLines[key]['data'][j][
- 'metrics_data'][0]['pages'][
- this.trendLines[key]['pagename'] ];
+ if(metricsData.length > 0){
+ var pageData =
+ metricsData[0]['pages'][this.trendLines[key]['pagename'] ];
- if( pageData.test_evaluation === true ){
- passIndex = chartData[0].data.push(
- [ j, parseInt(pageData.mean) ]
- );
- this.seriesIndexToKey[0][passIndex -1] = key;
- } else {
- seriesColor = MS_PAGE.failColor;
+ //Metrics data is available set trend datum
+ var trendDatumIndex = chartData[trendIndex].data.push(
+ [ j, parseInt(pageData.trend_mean) ]
+ ) - 1;
- failIndex = chartData[1].data.push(
- [ j, parseInt(pageData.mean) ]
- );
+ this.seriesIndexToKey[trendIndex][trendDatumIndex] = key;
- this.seriesIndexToKey[1][failIndex - 1] = key;
- }
+ if( pageData.test_evaluation === true ){
- var trendDatumIndex = chartData[trendIndex - 1].data.push(
- [ j, parseInt(pageData.trend_mean) ]
- );
+ //Test passed
+ passIndex = chartData[this.passSeriesIndex].data.push(
+ [ j, parseInt(pageData.mean) ]
+ ) - 1;
- this.seriesIndexToKey[trendIndex - 1][trendDatumIndex - 1] = key;
+ this.seriesIndexToKey[this.passSeriesIndex][passIndex] = key;
- //revision that is loaded in the page
- if( this.trendLines[key]['data'][j]['dz_revision'] === MS_PAGE.refData.revision ){
+ } else if(pageData.test_evaluation === false) {
- targetRevisions.push(
- {'x':j, 'y':parseInt(pageData.mean) }
- );
+ //Test failed
- if( pageData.test_evaluation === true ){
- this.trendLines[key].datapoint_plot_location.point_index = passIndex - 1;
- } else {
- targetRevisionType = 'fail';
- this.trendLines[key].datapoint_plot_location.point_index = failIndex - 1;
+ failIndex = chartData[this.failSeriesIndex].data.push(
+ [ j, parseInt(pageData.mean) ]
+ ) - 1;
+
+ this.seriesIndexToKey[this.failSeriesIndex][failIndex] = key;
+
+ }
+
+ //revision that is loaded in the page
+ if( this.trendLines[key]['data'][j]['dz_revision'] ===
+ MS_PAGE.refData.revision ){
+
+ targetRevisions.push(
+ {'x':j, 'y':parseInt(pageData.mean) }
+ );
+
+ flotItem.datapoint = [
+ j, pageData.mean
+ ];
+
+ if( pageData.test_evaluation === true ){
+
+ seriesColor = MS_PAGE.passColor;
+
+ this.trendLines[key].datapoint_plot_location.point_index = passIndex;
+
+ flotItem.seriesIndex = this.passSeriesIndex;
+ flotItem.dataIndex =
+ this.trendLines[key].datapoint_plot_location.point_index;
+
+ } else {
+
+ targetRevisionType = 'fail';
+
+ seriesColor = MS_PAGE.failColor;
+
+ this.trendLines[key].datapoint_plot_location.point_index = failIndex;
+
+ flotItem.seriesIndex = this.failSeriesIndex;
+ flotItem.dataIndex =
+ this.trendLines[key].datapoint_plot_location.point_index;
+
+ }
}
+
+ } else {
+ //No metrics data is available, color gray and place
+ //on trend line
+ noDataIndex = chartData[this.noMetricsDataSeriesIndex].data.push(
+ [ j, parseInt(this.trendLines[key]['mean']) ]
+ ) - 1;
+
+ this.seriesIndexToKey[this.noMetricsDataSeriesIndex][noDataIndex] = key;
}
+
}
if(targetRevisionType === 'pass'){
- this.trendLines[key].datapoint_plot_location.series_index = passSeriesIndex;
+ this.trendLines[key].datapoint_plot_location.series_index = this.passSeriesIndex;
}else{
- this.trendLines[key].datapoint_plot_location.series_index = failSeriesIndex;
+ this.trendLines[key].datapoint_plot_location.series_index = this.failSeriesIndex;
}
var rgbAlpha = this.view.loadSeriesLabelContainer(
@@ -198,6 +372,22 @@ var TrendLineComponent = new Class({
this.view.drawCircleAroundDataPoints(targetRevisions, this.plot);
+ $(window).resize(
+ //Redraw the circle around target revision
+ //when the chart is resized
+ _.bind(
+ function(){
+ //this.view.drawCircleAroundDataPoints(
+ // targetRevisions, this.plot
+ // );
+ }, this)
+ );
+ this.unsetGetPushState();
+ this.view.displayDashboard();
+
+ //Set hover datum to revision of interest
+ this._hoverPlot({}, {}, flotItem);
+
},
getFailDataset: function(){
return {
@@ -213,6 +403,13 @@ var TrendLineComponent = new Class({
'color': MS_PAGE.passColor
};
},
+ getNoDataDataset: function(){
+ return {
+ 'data':[],
+ 'points': { 'show': true },
+ 'color': this.view.trendLineColor
+ };
+ },
getTrendLineDataset: function(){
return {
'data':[],
@@ -220,18 +417,33 @@ var TrendLineComponent = new Class({
'color': this.view.trendLineColor
};
},
- convertTimestampToDate: function(unixTimestamp){
+ convertTimestampToDate: function(unixTimestamp, getHMS){
var dateObj = new Date(unixTimestamp * 1000);
var dateString = dateObj.getFullYear() + '-' +
- (dateObj.getMonth() + 1) + '-' +
- dateObj.getDate() + ' ' +
- dateObj.getHours() + ':' +
- dateObj.getMinutes() + ':' +
- dateObj.getSeconds();
+ this.padNumber((dateObj.getMonth() + 1), 10, '0') + '-' +
+ dateObj.getDate();
+
+ if(getHMS){
+ dateString += ' ' +
+ dateObj.getHours() + ':' +
+ dateObj.getMinutes() + ':' +
+ this.padNumber(dateObj.getSeconds(), 10, '0');
+ }
+
return dateString;
},
+ padNumber: function(n, max, pad){
+
+ n = parseInt(n);
+
+ if( n < max ){
+ return pad + n;
+ }
+
+ return n;
+ },
formatLabel: function(label, series){
- return this.tickDisplayDates[label];
+ return this.tickDisplayDates[label] || "";
},
closeDataSeries: function(event){
@@ -277,6 +489,7 @@ var TrendLineComponent = new Class({
deleteDataSeries: function(key){
var intKey = parseInt(key);
+
for(var i=0; i<this.trendLineOrder.length; i++){
//Data series found, delete it
if( intKey === this.trendLineOrder[i] ){
@@ -291,8 +504,21 @@ var TrendLineComponent = new Class({
}
}
+ //Redraw trend lines
this.loadTrendData([], {});
},
+ _handlePushAroundInput: function(event){
+ if(event.keyCode === 13){
+ if(this.getPushState === false){
+ this.getPushes();
+ }
+ } else {
+ //Prevent user from entering anything other than an integer
+ var v = $(event.target).val();
+ var integersEntered = parseInt(v);
+ $(event.target).val(integersEntered || "");
+ }
+ },
_getKeyFromEventTarget: function(etarget){
var sel = '[id*="' + this.view.legendIdPrefix + '"]';
@@ -306,10 +532,45 @@ var TrendLineComponent = new Class({
return key;
},
+ _selectPlot: function(event, ranges){
+
+ var begin = Math.ceil(ranges.xaxis.from);
+ var end = parseInt(ranges.xaxis.to);
+
+ //Don't care what the series is, just need the data index
+ //equivalent
+ var keys = _.keys(this.trendLines);
+ var rangeBegin = this.trendLines[keys[0]]['data'][begin]['revisions'][0]['revision'];
+ var rangeEnd = this.trendLines[keys[0]]['data'][end]['revisions'][0]['revision'];
+
+ //Retrieve the url for the get range href
+ var url = this.model.getPushRangeUrl(rangeBegin, rangeEnd);
+
+ //Set the inputs and href
+ this.view.setPushRange(rangeBegin, rangeEnd, url);
+
+ },
_hoverPlot: function(event, pos, item){
if(item){
+ //Check if the datum display is locked
+ var checked = $(this.view.datumLockSel).attr('checked');
+ if(checked){
+ //Datum locked, do nothing
+ return;
+ }
+
+ //Highlight the datapoint that the datum info is initialized to
+ this.plot.unhighlight();
+
+ this.plot.highlight(item.seriesIndex, item.datapoint);
+
+ if(item.dataIndex === undefined){
+ //dataIndex must be defined to proceed
+ return;
+ }
+
var key = this.seriesIndexToKey[item.seriesIndex][item.dataIndex];
if(key != this.hoverLegendKey){
@@ -317,15 +578,18 @@ var TrendLineComponent = new Class({
//User has hovered directly from a point on series a
//to a point on series b, we need to set the point on
//series a back to the appropriate background color
- this.view.unhighlightLegend(
- this.trendLines[lastKey].rgb_alpha,
- this.hoverLegendEl
- );
+ if(this.trendLines[lastKey] != undefined){
+ this.view.unhighlightLegend(
+ this.trendLines[lastKey].rgb_alpha,
+ this.hoverLegendEl
+ );
+ }
}
this.hoverLegendKey = key;
var datum = this.trendLines[key]['data'][ item.datapoint[0] ];
+ var dataSeries = this.trendLines[key];
var keyValueArray = [];
var platform = this.trendLines[key]['platform'];
@@ -339,133 +603,156 @@ var TrendLineComponent = new Class({
this.view.highlightLegend(legendEl);
var metricKeys = [
- 'mean', 'trend_mean', 'stddev', 'trend_stddev',
- 'p', 'fdr', 'h0_rejected', 'n_replicates' ];
+ 'test_evaluation', 'mean', 'trend_mean', 'stddev',
+ 'trend_stddev', 'p', 'fdr', 'h0_rejected', 'n_replicates' ];
+
+ //If the dz_revision is not defined, data metrics datum has not
+ //been received in datazilla, use the first revision associated
+ //with the push.
+ var revision = datum.dz_revision || datum.revisions[0]['revision'];
+ var revisionHtml = MS_PAGE.getHgUrlATag('rev', revision);
keyValueArray.push(
{'label':'dz_revision',
- 'value':[datum.dz_revision]});
+ 'value':revisionHtml });
+
+ keyValueArray.push(
+ {'label':'author',
+ 'value':datum.user});
keyValueArray.push(
{'label':'date',
- 'value':[this.convertTimestampToDate(datum.date)]});
+ 'value':this.convertTimestampToDate(datum.date, true)});
keyValueArray.push(
{'label':'branch',
- 'value':[datum.branch_name]});
+ 'value':datum.branch_name});
keyValueArray.push(
{'label':'test suite',
- 'value':[datum.metrics_data[0]['testrun']['suite'] ]});
+ 'value':dataSeries.testsuite});
keyValueArray.push(
{'label':'page',
- 'value':[pagename]});
+ 'value':pagename});
- for(var i=0; i<metricKeys.length; i++){
+ var color = MS_PAGE.passColor;
- var mk = metricKeys[i];
- keyValueArray.push(
- {'label':mk,
- 'value':[datum.metrics_data[0]['pages'][pagename][mk] ] });
- }
+ if(datum.metrics_data.length > 0){
+ //Load metrics specific data
+ for(var i=0; i<metricKeys.length; i++){
+
+ var mk = metricKeys[i];
+ keyValueArray.push(
+ {'label':mk.replace('_', ' '),
+ 'value':datum.metrics_data[0]['pages'][pagename][mk] });
+ }
- if(datum.revisions.length > 1){
- keyValueArray.push(
- {'label':'revisions',
- 'value':datum.revisions});
+ if( datum.metrics_data[0]['pages'][pagename]['test_evaluation'] === false ){
+ color = MS_PAGE.failColor;
+ }
+
+ } else {
+ color = this.view.trendLineColor;
}
- var url = this.model.getRawDataUrl(this.trendLines[key]);
+ var revisionKeyValues = [];
+ for(var i=0; i<datum.revisions.length; i++){
- var color = MS_PAGE.passColor;
- if( datum.metrics_data[0]['pages'][pagename]['test_evaluation'] === false ){
- color = MS_PAGE.failColor;
+ var revisionDatum = datum.revisions[i];
+
+ var changesetHtml = MS_PAGE.getHgUrlATag(
+ 'changeset', revisionDatum.revision
+ );
+
+ revisionKeyValues.push(
+ {'label':'revision',
+ 'value':changesetHtml});
+
+ var contactInfo = revisionDatum.author.match(/(.*?)\<(\S+)\>/);
+
+ var email = "";
+ var author = "";
+
+ if(contactInfo.length === 3){
+ revisionKeyValues.push(
+ {'label':'author',
+ 'value':contactInfo[1]});
+ revisionKeyValues.push(
+ {'label':'email',
+ 'value':contactInfo[2]});
+
+ }else{
+ revisionKeyValues.push(
+ {'label':'author',
+ 'value':revisionDatum.author});
+ }
+
+ var desc = MS_PAGE.addBugzillaATagsToDesc(revisionDatum.desc);
+
+ revisionKeyValues.push(
+ {'label':'desc',
+ 'value':desc});
}
+ var url = this.model.getRawDataUrl(
+ this.trendLines[key], datum.revisions[0].revision);
+
var rgbAlpha = this.view.hexToRgb(color);
this.view.loadDatumLabelContainer(
- keyValueArray, url, rgbAlpha, color
+ keyValueArray, revisionKeyValues, url, rgbAlpha, color
);
+
} else {
if(this.hoverLegendKey && this.hoverLegendEl){
- this.view.unhighlightLegend(
- this.trendLines[this.hoverLegendKey].rgb_alpha,
- this.hoverLegendEl
- );
-
- this.hoverLegendKey = undefined;
- this.hoverLegendEl = undefined;
+ if(this.trendLines[this.hoverLegendKey] != undefined){
+ this.view.unhighlightLegend(
+ this.trendLines[this.hoverLegendKey].rgb_alpha,
+ this.hoverLegendEl
+ );
+ this.hoverLegendKey = undefined;
+ this.hoverLegendEl = undefined;
+ }
}
}
},
_clickPlot: function(event, pos, item){
if(item){
- var key = this.seriesIndexToKey[item.seriesIndex][item.dataIndex];
+ $(this.view.datumLockSel).click();
}
},
- _loadTrendLineData: function(data){
+ _loadTrendLineData: function(data, eventData){
if(data.length > 0){
- var key = MS_PAGE.getDatumKey(this.eventDataReceived);
-
- if(!this.trendLines[key]){
- this.trendLineOrder.push(key);
-
- this.trendLines[key] = {
- pagename:this.eventDataReceived.pagename,
- testsuite:this.eventDataReceived.testsuite,
- platform:this.eventDataReceived.platform,
- platform_info:this.eventDataReceived.platform_info,
- data:data,
- rgb_alpha:"",
- datapoint_plot_location:{
- 'series_index':0, 'point_index':0
- }
- };
- }
- }
- },
- _loadMockData: function(data){
+ var key = MS_PAGE.getDatumKey(eventData);
- var datum = {};
- var datumIndex = 0;
+ var mean = eventData.mean;
- for(var i=0; i<data.length; i++){
- if( data[i]['metrics_data'].length > 0 ){
- datum = data[i];
- datumIndex = i;
+ if(this.trendLines[key]){
+ mean = this.trendLines[key].mean;
}
- }
- for(var i=0; i<data.length; i++){
- if( datumIndex != i ){
-
- var datumClone = jQuery.extend(true, {}, datum);
- var mean = parseInt(datumClone['metrics_data'][0].pages[ this.eventDataReceived.pagename ].mean);
- var trendMean = parseInt(datumClone['metrics_data'][0].pages[ this.eventDataReceived.pagename ].trend_mean);
-
- //mean = Math.floor(Math.random() * (mean - (mean - 5) + 1)) + (mean - 5);
- //trendMean = Math.floor(Math.random() * (trendMean - (trendMean - 5) + 1)) + (trendMean - 5);
-
- datumClone['metrics_data'][0].pages[ this.eventDataReceived.pagename ].mean = parseInt(mean);
- datumClone['metrics_data'][0].pages[ this.eventDataReceived.pagename ].trend_mean = parseInt(trendMean);
- datumClone['metrics_data'][0].pages[ this.eventDataReceived.pagename ].test_evaluation = true;
- datumClone.dz_revision = 'SomeOtherRevision';
-
- data[i] = datumClone;
- }
- var pushlogId = parseInt(datumClone['metrics_data'][0].pages[ this.eventDataReceived.pagename ]['pushlog_id']);
- datumClone['metrics_data'][0].pages[ this.eventDataReceived.pagename ]['pushlog_id'] = pushlogId + i;
+ this.trendLines[key] = {
+ 'pagename':eventData.pagename,
+ 'testsuite':eventData.testsuite,
+ 'platform':eventData.platform,
+ 'platform_info':eventData.platform_info,
+ 'data':data,
+ 'rgb_alpha':"",
+ //This is the mean for the revision of interest. It's
+ //used by pushes with no metrics data to place them along
+ //side the revision of interest.
+ 'mean': mean,
+ 'datapoint_plot_location':{
+ 'series_index':0, 'point_index':0
+ }
+ };
}
-
- return data;
},
-
dataLoadError: function(data, textStatus, jqXHR){
var messageText = 'Ohhh no, something has gone horribly wrong! ';
@@ -487,18 +774,36 @@ var TrendLineView = new Class({
this.parent(options);
+ this.pushlogSpinnerSel = '#su_pushlog_spinner';
+ this.pushlogDashboardSel = '#su_pushlog_dashboard