diff --git a/bin/copy-dashboard.py b/bin/copy-dashboard.py index af32409..6e78cf7 100755 --- a/bin/copy-dashboard.py +++ b/bin/copy-dashboard.py @@ -134,37 +134,40 @@ r = requests.get(baseurl + 'devices.json') if not validate_json_response(r): print "Can't download device list, exiting" sys.exit(1) +devicedict = r.json() save_file(os.path.join(outputdir, 'devices.json'), r.content) if options.device_id: if options.device_id in r.json()['devices'].keys(): - device_names = [ options.device_id ] + deviceids = [ options.device_id ] else: print "WARNING: Device id '%s' specified but unavailable. Skipping." % \ options.device_id - device_names = [] + deviceids = [] else: - device_names = r.json()['devices'].keys() + deviceids = devicedict['devices'].keys() with concurrent.futures.ThreadPoolExecutor(MAX_WORKERS) as executor: - for device_name in device_names: - r = requests.get(baseurl + '%s/tests.json' % device_name) - if not validate_json_response(r): - print "Skipping tests for %s" % device_name - continue + for deviceid in deviceids: + for branchid in devicedict['devices'][deviceid]['branches']: + r = requests.get(baseurl + '%s/%s/tests.json' % (deviceid, branchid)) + if not validate_json_response(r): + print "Skipping tests for device: %s, branch: %s" % ( + deviceid, branchid) + continue - devicedir = os.path.join(outputdir, device_name) - create_dir(devicedir) - save_file(os.path.join(devicedir, 'tests.json'), r.content) - testnames = r.json()['tests'].keys() - for testname in testnames: - executor.submit(download_testdata, - baseurl + '%s/%s.json' % (device_name, testname), - baseurl, - os.path.join(outputdir, device_name, - '%s.json' % testname), - options, - metadatadir, videodir, profiledir) + testdir = os.path.join(outputdir, deviceid, branchid) + create_dir(testdir) + save_file(os.path.join(testdir, 'tests.json'), r.content) + testnames = r.json()['tests'].keys() + for testname in testnames: + executor.submit(download_testdata, + baseurl + '%s/%s/%s.json' % (deviceid, branchid, testname), + baseurl, + os.path.join(outputdir, deviceid, branchid, + '%s.json' % testname), + options, + metadatadir, videodir, profiledir) sys.exit(exit_status) diff --git a/bin/update-dashboard.py b/bin/update-dashboard.py index 79dad82..c046c3f 100755 --- a/bin/update-dashboard.py +++ b/bin/update-dashboard.py @@ -117,8 +117,10 @@ def runtest(dm, device_prefs, options, product, appinfo, testinfo, log_actions=(testtype == 'web' or testtype == 'b2g'))) # Write testdata - eideticker.update_dashboard_testdata(options.dashboard_dir, options.device_id, - testinfo, productname, appdate, + eideticker.update_dashboard_testdata(options.dashboard_dir, + options.device_id, + options.branch_id, testinfo, + productname, appdate, datapoint, metadata) def main(args=sys.argv[1:]): @@ -132,6 +134,9 @@ def main(args=sys.argv[1:]): parser.add_option("--device-id", action="store", dest="device_id", help="id of device (used in output json)", default=os.environ.get('DEVICE_ID')) + parser.add_option("--branch", action="store", dest="branch_id", + help="branch under test (used in output json)", + default=os.environ.get('BRANCH')) parser.add_option("--device-name", action="store", dest="device_name", help="name of device to display in dashboard (if not " "specified, display model name)", @@ -155,17 +160,19 @@ def main(args=sys.argv[1:]): default="nightly", help="product name (android-specific, default: " "%default)") - options, args = parser.parse_args() if not args: # need to specify at least one test to run! parser.print_usage() sys.exit(1) - device_id = options.device_id - if not device_id: - print "ERROR: Must specify device id (either with --device-id or with " - "DEVICE_ID environment variable)" + if not options.device_id: + print "ERROR: Must specify device id (either with --device-id or with " \ + "DEVICE_ID environment variable)" + sys.exit(1) + if not options.branch_id: + print "ERROR: Must specify branch (either with --branch or with " \ + "BRANCH environment variable)" sys.exit(1) # get device info @@ -189,8 +196,8 @@ def main(args=sys.argv[1:]): print "ERROR: Unknown device type '%s'" % options.devicetype # update device index - eideticker.update_dashboard_device_list(options.dashboard_dir, device_id, - device_info) + eideticker.update_dashboard_device_list(options.dashboard_dir, options.device_id, + options.branch_id, device_info) # get application/build info if options.devicetype == "android": @@ -229,7 +236,8 @@ def main(args=sys.argv[1:]): for testkey in args: testinfo = eideticker.get_testinfo(testkey) - eideticker.update_dashboard_test_list(options.dashboard_dir, device_id, + eideticker.update_dashboard_test_list(options.dashboard_dir, options.device_id, + options.branch_id, testinfo) current_date = time.strftime("%Y-%m-%d") diff --git a/src/dashboard/index.html b/src/dashboard/index.html index cbaf4ca..e72a365 100644 --- a/src/dashboard/index.html +++ b/src/dashboard/index.html @@ -25,6 +25,9 @@ #device-chooser::before { content: "Device"; } + #branch-chooser::before { + content: "Branch"; + } #test-chooser::before { content: "Test"; } @@ -93,6 +96,7 @@
No data for that device/test combination. :(
"); return; @@ -43,7 +43,7 @@ function updateContent(testInfo, deviceId, testId, measureId) { $('#measure-'+measureId).attr("selected", "true"); $('#measure').change(function() { var newMeasureId = $(this).val(); - window.location.hash = '/' + [ deviceId, testId, newMeasureId ].join('/'); + window.location.hash = '/' + [ deviceId, branchId, testId, newMeasureId ].join('/'); }); }); @@ -286,75 +286,135 @@ $(function() { var devices = deviceData['devices']; var deviceIds = Object.keys(devices).sort(); - $.when.apply($, deviceIds.map(function(deviceId) { - return $.getJSON(getResourceURL([deviceId, 'tests.json'].join('/')), + var deviceBranchPairs = []; + deviceIds.forEach(function(deviceId) { + devices[deviceId].branches.forEach(function(branchId) { + deviceBranchPairs.push([deviceId, branchId]); + }); + }); + $.when.apply($, deviceBranchPairs.map(function(pair) { + var deviceId = pair[0]; + var branchId = pair[1]; + + return $.getJSON(getResourceURL([deviceId, branchId, 'tests.json'].join('/')), function(testData) { var tests = testData['tests']; - devices[deviceId]['tests'] = tests; + if (!devices[deviceId][branchId]) { + devices[deviceId][branchId] = {}; + } + devices[deviceId][branchId]['tests'] = tests; }); })).done(function() { + var defaultDeviceId = deviceIds[0]; - // initialize device chooser - deviceIds.forEach(function(deviceId) { - var tests = devices[deviceId].tests; - var firstTestKey = Object.keys(tests).sort()[0]; - var defaultMeasureId = tests[firstTestKey].defaultMeasureId; + function getTestIdForDeviceAndBranch(deviceId, branchId, preferredTest) { + var device = devices[deviceId]; + var tests = device[branchId].tests; + var testIds = Object.keys(tests).sort(); + var testId; + if (preferredTest && testIds.indexOf(preferredTest) >= 0) { + // this deviceid/branch combo has our preferred test, return it + return preferredTest; + } else { + // this deviceid/branch combo *doesn't* have our preferred test, + // fall back to the first one + return testIds[0]; + } + } - var deviceURL = "#/" + [ deviceId, firstTestKey, defaultMeasureId ].join('/'); - $('' + devices[deviceId].name+'').appendTo( + function updateDeviceChooser(preferredBranchId, preferredTest) { + $('#device-chooser').empty(); + + deviceIds.forEach(function(deviceId) { + var device = devices[deviceId]; + + var branchId; + if (preferredBranchId && device.branches.indexOf(preferredBranchId) >= 0) { + branchId = preferredBranchId; + } else { + branchId = device.branches.sort()[0]; + } + + var testId = getTestIdForDeviceAndBranch(deviceId, branchId, preferredTest); + var defaultMeasureId = device[branchId].tests[testId].defaultMeasureId; + + var deviceURL = "#/" + [ deviceId, branchId, testId, defaultMeasureId ].join('/'); + $('' + devices[deviceId].name+'').appendTo( $('#device-chooser')); - }); + }); + } + + function updateBranchChooser(deviceId, preferredTest) { + $('#branch-chooser').empty(); + + var device = devices[deviceId]; + device.branches.forEach(function(branchId) { + var testId = getTestIdForDeviceAndBranch(deviceId, branchId, preferredTest); + var defaultMeasureId = device[branchId].tests[testId].defaultMeasureId; + + var url = "#/" + [ deviceId, branchId, testId, defaultMeasureId ].join('/'); + $('' + branchId +'').appendTo( + $('#branch-chooser')); + }); + } + + var currentBranchId = null; + var currentDeviceId = null; + var currentTestId = null; var routes = { - '/:deviceId/:testId/:measureId': { - on: function(deviceId, testId, measureId) { - if (!devices[deviceId] || !devices[deviceId]['tests'][testId]) { - $('#data-view').html("That device/test/measure combination does not seem to exist. Maybe you're using an expired link? Reload page?
"); + '/:deviceId/:branchId/:testId/:measureId': { + on: function(deviceId, branchId, testId, measureId) { + if (!devices[deviceId] || !devices[deviceId][branchId] || !devices[deviceId][branchId]['tests'][testId]) { + $('#data-view').html("That device/branch/test/measure combination does not seem to exist. Maybe you're using an expired link? Reload page?
"); return; } - // update device chooser - $('#device-chooser').children('a').removeClass("active"); - $('#device-chooser').children('#device-'+deviceId).addClass("active"); + updateDeviceChooser(branchId, testId); + updateBranchChooser(deviceId, testId); // update list of tests to be consistent with those of this // particular device (in case it changed) $('#test-chooser').empty(); - var tests = devices[deviceId].tests; + var tests = devices[deviceId][branchId].tests; var testKeys = Object.keys(tests).sort(); testKeys.forEach(function(testKey) { $('' + testKey + '').appendTo($('#test-chooser')); }); // update all test links to be relative to the new test or device - $('#test-chooser').children('a').removeClass("active"); - $('#test-chooser').children('#'+testId).addClass("active"); - $('#test-chooser').children('a').each(function() { var testIdAttr = $(this).attr('id'); if (testIdAttr) { var defaultMeasureId = tests[testIdAttr].defaultMeasureId; $(this).attr('href', '#/' + - [ deviceId, testIdAttr, + [ deviceId, branchId, testIdAttr, defaultMeasureId ].join('/')); } }); + // highlight chosen selections in choosers + $('#device-chooser').children('#device-'+deviceId).addClass("active"); + $('#branch-chooser').children('#branch-'+branchId.replace('.', '\\.')).addClass("active"); + $('#test-chooser').children('#'+testId).addClass("active"); + var testInfo = tests[testId]; updateFooter(); - updateContent(testInfo, deviceId, testId, measureId); + updateContent(testInfo, deviceId, branchId, testId, measureId); } } }; var defaultDeviceId = deviceIds[0]; - var initialTestKey = Object.keys(devices[defaultDeviceId]['tests'])[0]; - var initialTest = devices[defaultDeviceId]['tests'][initialTestKey] + var defaultBranchId = devices[defaultDeviceId].branches[0]; + var defaultTestId = Object.keys(devices[defaultDeviceId][defaultBranchId].tests)[0]; + var defaultMeasureId = devices[defaultDeviceId][defaultBranchId].tests[defaultTestId].defaultMeasureId; var router = Router(routes).init('/' + [ defaultDeviceId, - initialTestKey, - initialTest.defaultMeasureId ].join('/')); + defaultBranchId, + defaultTestId, + defaultMeasureId ].join('/')); }); }); }); diff --git a/src/eideticker/eideticker/dashboard.py b/src/eideticker/eideticker/dashboard.py index fb3af03..3dae5a8 100644 --- a/src/eideticker/eideticker/dashboard.py +++ b/src/eideticker/eideticker/dashboard.py @@ -66,19 +66,27 @@ def copy_dashboard_files(dashboard_dir, indexfile='index.html'): os.remove(dest) shutil.copyfile(source, dest) -def update_dashboard_device_list(dashboard_dir, device_id, device_info): +def update_dashboard_device_list(dashboard_dir, device_id, branch_id, device_info): devices = {} device_filename = os.path.join(dashboard_dir, 'devices.json') if os.path.isfile(device_filename): devices = json.loads(open(device_filename).read())['devices'] + + branches = [] + if devices.get(device_id): + branches = devices[device_id].get('branches', []) + if branch_id not in branches: + branches.append(branch_id) + devices[device_id] = device_info + devices[device_id]['branches'] = branches with open(device_filename, 'w') as f: f.write(json.dumps({'devices': devices})) -def update_dashboard_test_list(dashboard_dir, device_id, testinfo): - testsdirname = os.path.join(dashboard_dir, device_id) +def update_dashboard_test_list(dashboard_dir, device_id, branch_id, testinfo): + testsdirname = os.path.join(dashboard_dir, device_id, branch_id) if not os.path.exists(testsdirname): - os.mkdir(testsdirname) + os.makedirs(testsdirname) tests = {} testsfilename = os.path.join(testsdirname, 'tests.json') @@ -91,10 +99,11 @@ def update_dashboard_test_list(dashboard_dir, device_id, testinfo): with open(testsfilename, 'w') as f: f.write(json.dumps({'tests': tests})) -def update_dashboard_testdata(dashboard_dir, device_id, testinfo, productname, - productdate, datapoint, metadata): +def update_dashboard_testdata(dashboard_dir, device_id, branch_id, testinfo, + productname, productdate, datapoint, metadata): # get existing data - fname = os.path.join(dashboard_dir, device_id, '%s.json' % testinfo['key']) + fname = os.path.join(dashboard_dir, device_id, branch_id, + '%s.json' % testinfo['key']) testdata = NestedDict() if os.path.isfile(fname): testdata.update(json.loads(open(fname).read()))