diff --git a/README.md b/README.md index dfe056f..b60a29b 100644 --- a/README.md +++ b/README.md @@ -1 +1,7 @@ -# psij-testing-service \ No newline at end of file +# psij-testing-service + +# Helpful aliases: +# for starting the Back end. +alias start='cd ~/psi-j-testing-service;python3 src/psij/testing/service.py' + +# set up sym links like this: ln -s customization-psij.js customization.js diff --git a/src/psij/testing/service.py b/src/psij/testing/service.py index fa433f6..9198148 100644 --- a/src/psij/testing/service.py +++ b/src/psij/testing/service.py @@ -406,9 +406,11 @@ def tests(self, sites_to_get, tests_to_match) -> object: resp[test.site_id] = {} # only store the more recent test if test.test_name not in resp[test.site_id]: - resp[test.site_id][test.test_name] = test.to_mongo().to_dict() + test_dict = test.to_mongo().to_dict() + resp[test.site_id][test.test_name] = test_dict for key in ['_id', "stdout", "stderr", "log"]: - del resp[test.site_id][test.test_name][key] + if key in test_dict: + del test_dict[key] add_cors_headers() return resp @@ -437,11 +439,6 @@ def json_handler(*args, **kwargs): 'tools.staticdir.on': True, 'tools.staticdir.dir': '', 'tools.json_out.handler': json_handler - }, - '/instance': { - 'tools.staticdir.root': '/var/www/html', - 'tools.staticdir.on': True, - 'tools.staticdir.dir': '', } }) diff --git a/src/psij/web/style.css b/src/psij/web/css/style.css similarity index 76% rename from src/psij/web/style.css rename to src/psij/web/css/style.css index abf27f2..16cab4f 100644 --- a/src/psij/web/style.css +++ b/src/psij/web/css/style.css @@ -23,7 +23,7 @@ body { background-color: #e0e0e0; border: 1pt solid #a0a0a0; border-radius: 4pt; - width: 190pt; + width: 220pt; height: 80pt; z-index: 20; } @@ -45,7 +45,7 @@ body { .branch-tile { position: relative; display: inline-block; - width: 95pt; + width: 110pt; height: 16pt; font-size: 8.5pt; padding: 2pt; @@ -461,9 +461,7 @@ td.run-id { color: rgb(180, 0, 0); } -#status-calendar table { - position: relative; -} + #status-calendar table tr:first-child th { position: sticky; @@ -482,7 +480,6 @@ td.run-id { font-weight: normal; font-size: 11pt; padding-bottom: 4pt; - border-bottom: 2pt solid #e0e0e0; margin-bottom: 2pt; } @@ -540,6 +537,19 @@ td.run-id { background-color: #e0e0e0; } +.calendar-bubble.state-good { + background-color: rgb(106, 255, 77); +} + +.calendar-bubble.state-somewhat-bad { + background-color: rgb(242, 219, 73); +} + +.calendar-bubble.state-really-bad { + background-color: #ffb0a0; +} + + .bg { background-color: #f0f0f0; } @@ -547,3 +557,153 @@ td.run-id { .calendar-cell { text-align: center; } + +.RowAccordion { + border-top: 1px solid #e0e0e1; +} + +.RowAccordion td { + padding: 5px; + position:relative; +} + +.RowAccordion .status-cell { + text-align: center; +} + +.RowAccordion td:first-child { + cursor: pointer; + color: #333333; +} + +.RowAccordion .logo_name { + font-weight: bold; +} + +.RowAccordion .ArrowDown { + width: 0; + height: 0; + display: inline-block; + border-left: 8px solid transparent; + border-right: 8px solid transparent; + border-top: 8px solid #333333; + margin-right: 11px; + margin-left: 3px; +} + +.RowAccordion .ArrowRight { + display: inline-block; + width: 0; + height: 0; + border-top: 8px solid transparent; + border-bottom: 8px solid transparent; + border-left: 8px solid #333334; + padding: 0 6px 0 0; + margin-left: 8px; + margin-right: 8px; +} + +.RowAccordion .machineLink { + padding: 5px 5px 5px 35px; +} + +.RowAccordion .rowIsClosed { + display: none; +} + +.status-table .status-header-cell { + width: 60pt; +} + +#status-calendar .RowAccordion td { + height: 32px; +} + +#status-calendar td.logo_name { + width: 100%; +} + +#status-calendar .schedulerCol { + width: 0; +} + +.status-table th.status-header-cell { + text-align: center; +} + +.status-table .status-cell { + padding: 0; + width: 60pt; + min-width: 60pt; +} + +.status-table th, .status-table td { + font-size: 11pt; +} + +.status-table th { + font-weight: normal; +} + +#status-calendar.status-table .status-table-header, #status-calendar .RowAccordion { + width: 700pt; + max-width: 700pt; +} + +#curated-sites-home.status-table .status-table-header, #curated-sites-home .RowAccordion { + width: 600pt; + max-width: 600pt; +} + +.status-table .status-table-header { + border-bottom: 2pt solid #e0e0e0; +} + +#status-calendar > div:first-of-type > table.RowAccordion { + border-top: none; +} + +#status { + margin-top: 16pt; +} + +.status-table .scheduler-header-cell, .status-table .scheduler-cell { + width: 100pt; +} + +.status-table .scheduler-header-cell { + text-align: left; +} + +.open_close_arrow { + border-left: 8px solid transparent; + border-right: 8px solid transparent; + border-top: 8px solid #444444; + margin-left: 8px; + cursor: pointer; +} + +.open_close_arrow[position="otherPos"] { + display: none; +} + +.open_close_arrow[position="oneRowOnly"] { + display: none; +} + +.open_close_arrow.open { + position: relative; + left: 5px; + border-top: 8px solid transparent; + border-bottom: 8px solid transparent; + border-left: 8px solid #444444; +} + +.container-for-mini-branches { + display: inline-flex; + flex-direction: column; + flex-wrap: wrap; + vertical-align: middle; + margin-right: auto; + height: 10pt; +} \ No newline at end of file diff --git a/src/psij/web/checkmark.png b/src/psij/web/imgs/checkmark.png similarity index 100% rename from src/psij/web/checkmark.png rename to src/psij/web/imgs/checkmark.png diff --git a/src/psij/web/imgs/exaworks-name.png b/src/psij/web/imgs/exaworks-name.png new file mode 100644 index 0000000..df7f0f1 Binary files /dev/null and b/src/psij/web/imgs/exaworks-name.png differ diff --git a/src/psij/web/instance/customization-psij.js b/src/psij/web/instance/customization-psij.js new file mode 100644 index 0000000..b559323 --- /dev/null +++ b/src/psij/web/instance/customization-psij.js @@ -0,0 +1,75 @@ +CUSTOMIZATION = { + projectName: "PSI/J" +}; + +var CONF = { + backendURL: "https://psij.testing.exaworks.org/", + STR: { + "dashboardTitle": "Exascale dashboard testing service" + }, + "curated_sites": { + xaxis: [ + "Tests Suite", + "Basic test", + "Parallel jobs" + ], + xaxisTests: [ + "test_simple_job[local:single]", + "test_parallel_jobs[local:single]" + ], + yaxis: [ + { + logo_src: "", + logo_name: "University of Oregon", + machines: { + "axis1.hidden.uoregon.edu": { + scheduler: "SLURM" + }, + "illyad.hidden.uoregon.edu": { + scheduler: "" + }, + "jupiter.hidden.uoregon.edu": { + scheduler: "" + }, + "reptar.hidden.uoregon.edu": { + scheduler: "" + }, + "saturn.hidden.uoregon.edu": { + scheduler: "" + }, + "orthus.hidden.uoregon.edu": { + scheduler: "" + } + } + }, + { + logo_src: "", + logo_name: "Lawrence Livermore National Lab", + machines: { + "doc.llnl.gov": { + scheduler: "SLURM" + } + } + }, + { + logo_src: "", + logo_name: "Nersc National Lab", + machines: { + "cori08.nersc.gov": { + scheduler: "SLURM" + }, + "perlmutter.nersc": { + schedulor: "SLURM" + } + } + }, + { + logo_src: "", + logo_name: "Oakridge National Lab", + machines: { + } + } + ] + } +}; + diff --git a/src/psij/web/instance/customization-sdk.js b/src/psij/web/instance/customization-sdk.js new file mode 100644 index 0000000..4baceb5 --- /dev/null +++ b/src/psij/web/instance/customization-sdk.js @@ -0,0 +1,54 @@ +CUSTOMIZATION = { + projectName: "SDK" +}; + +var CONF = { + backendURL: "https://sdk.testing.exaworks.org/", + STR: { + "dashboardTitle": "Exascale dashboard testing service" + }, + "curated_sites": { + xaxis: [ + "Tests Suites", + "Flux", + "Parsl", + "Parsl-flux", + "RP", + "Swift-t" + ], + xaxisTests: [ + "flux", + "parsl", + "parsl-flux", + "rp", + "swift-t" + ], + yaxis: [ + { + logo_src: "", + logo_name: "Lawrence Livermore National Lab", + machines: { + "llnl-lassen": { + scheduler: "SLURM" + }, + "llnl-quartz": { + scheduler: "SLURM" + }, + "llnl-ruby": { + scheduler: "SLURM" + } + } + }, + { + logo_src: "", + logo_name: "GitHub Actions", + machines: { + "Github Actions": { + scheduler: "none" + } + } + } + ] + } +}; + diff --git a/src/psij/web/js/BuildAccordionModelFromSites.js b/src/psij/web/js/BuildAccordionModelFromSites.js new file mode 100644 index 0000000..80ca983 --- /dev/null +++ b/src/psij/web/js/BuildAccordionModelFromSites.js @@ -0,0 +1,101 @@ +/* + This builds the DataModel for the Calendar View. + It preps the Data for consumption. + */ +PS.BuildAccordionModelFromSites = function() { + + var get_states_ = function( months ) { + + var sts = []; + var objStates = []; + + for( var month in months ) { + + var days = months[month]; + + for( var day in days ) { + + var res = days[day]; + + var sobj = PS.SitesModel.getMainBranch( res.branches ); + + if( sobj === null) { + // case where there are no branches or no main branch. + sobj = { + completed_count:0, + failed_count:0, + name: 'none' + } + } + + var bc = globalMethods.badnessClass(sobj); + sts.push(bc); + + var branches = PS.SitesModel.getBranchesForSmallBubbles( res.branches ); + + objStates.push({ + mainState: bc, + branches: branches + }); + } + } + + return objStates; + }; + + + var get_ = function( accordionModelRef ) { + + // Accordion Model. + var am = []; + var machObjs = {}; + + for( var x=0; x < PS.sites.length; x++ ) { + + var site = PS.sites[x]; + var domain = globalMethods.getDomain(site.site_id); + + machObjs[domain] = machObjs[domain] || []; + machObjs[domain].push({ + site_id: site.site_id, + states: get_states_(site.months ) //["state-good","state-good","state-really-bad","state-good","state-good","state-bad", "state-good", "state-good"] + }); + } + + + for( var domain in machObjs ) { + + am.push({ + logo_name: domain, + machinesFromModel: machObjs[domain] + }); + } + + for( var i=0; i < am.length; i++ ) { + accordionModelRef[i] = am[i]; + } + + accordionModelRef.splice(am.length); + + /* EXAMPLE STUB: + return [{ + logo_name: "uregon.edu", + machinesFromModel: [{ + machine: "axis833334.uregon.edu", + states: ["state-good","state-good","state-really-bad","state-good","state-good"] + }, + { + machine: "axis8690003.uregon.edu", + states: ["state-good","state-good","state-really-bad","state-good","state-good"] + }, + { + machine: "axis2222283.uregon.edu", + states: ["state-good","state-good","state-really-bad","state-good","state-good"] + }] + }];*/ + }; + + return { + get: get_ + } +}(); \ No newline at end of file diff --git a/src/psij/web/js/RowAccordion.js b/src/psij/web/js/RowAccordion.js new file mode 100644 index 0000000..6d294f3 --- /dev/null +++ b/src/psij/web/js/RowAccordion.js @@ -0,0 +1,66 @@ +var VC = VC || {}; + +VC.goHere = function(ev) { + + var x = $(event.target); + var site_id = x.html(); + + window.location="site.html?site_id=" + site_id; +}; + + +VC.RowAccordion = Vue.component("row-accordion", { + props: [ + 'show_scheduler', + 'yaxe_prop' // one yaxis property: logo_name, logo source and machines list. + ], + data: function () { + + return { + open: 0 + } + }, + template: '' + + '' + + '' + + '' + + ''+ + ' \ + \ + ' + + '' + + ''+ + '' + + '
' + + '
' + + '
' + + '{{ this.yaxe_prop.logo_name }}
' + + '
' + + '
{{ machineObj.schedulerShow }}' + + '
' + + '
' + + '
' + + '  ' + + '
' + + '
' + + '
', + mounted: function( event ) { + + PS.showOrHideBranchBubbles(); + + }, + methods: { + toggle: function( event ) { + + this.open = this.open ? 0 : 1; + console.log( this.open ); + }, + nice_bubble: function( state ) { + console.dir(state); + return '
\n' + + '  \n' + + '
'; + } + } +}); diff --git a/src/psij/web/js/SitesModel.js b/src/psij/web/js/SitesModel.js new file mode 100644 index 0000000..6daae32 --- /dev/null +++ b/src/psij/web/js/SitesModel.js @@ -0,0 +1,315 @@ +var PS = PS || {}; +/* + This builds the Data model for the curated sites view. + */ +PS.SitesModel = function() { + + var callback_; + + var getModel_ = function( callback ) { + + callback_ = callback; + getLatestResultForTestOnAllSites_(); + }; + + + var makeModel_ = function() { + + var curSite = makeModelInstance_( CONF.curated_sites ); + + callback_( curSite ); + }; + + + var makeModelInstance_ = function( cur_site ) { + + var curSite = $.extend({}, cur_site ); + + for( var yIndex=0; yIndex < curSite.yaxis.length; yIndex++ ) { + + var machinesInAccordion = []; + + curSite.yaxis[yIndex].horizontalSummary = makeGoodStatesArray_(curSite.xaxis.length); + + loadMachines_( curSite, machinesInAccordion, yIndex); + + curSite.yaxis[yIndex].machinesFromModel = machinesInAccordion; + + + } + + return curSite; + }; + + + var makeGoodStatesArray_ = function( entryCount ) { + + var x = new Array(entryCount).fill(PS.STATES.GOOD); + console.log(x); + return x; + }; + + var getMainBranch_ = function( branches ) { + + var branchToSearchFor = location.href.indexOf("/sdk.") > -1 ? "master" : "main"; + + for( var i=0; i < branches.length; i++ ) { + + var branch = branches[i]; + if( branch.name === branchToSearchFor) { + return branch; + } + } + + return null; + }; + + + var loadMachines_ = function( curSite, machinesInAccordion, yIndex ) { + + var yobj = curSite.yaxis[yIndex]; + + for( var siteId in yobj.machines ) { + + var machinesObj = yobj.machines[siteId]; + var site = getSiteBySiteId_(siteId); + + if (site) { + + var keys = Object.keys( site.months ); + var lastKeyIdx = keys.length - 1; + var lastMon = keys[ lastKeyIdx ]; + + var days = site.months[lastMon]; + var daysKeys = Object.keys( days ); + var lastDayKeyIdx = daysKeys.length - 1; + var lastDay = daysKeys[ lastDayKeyIdx ]; + + var sobj_outer = site.months[lastMon][lastDay]; + var sobj = getMainBranch_( sobj_outer.branches ); + + if( sobj === null) { + // case where there are no branches or no main branch. + sobj = { + completed_count:0, + failed_count:0, + name: 'none' + } + } + + var state = globalMethods.badnessClass(sobj); + var states = [state]; + + for( var cIndex = 0; cIndex < 6; cIndex++ ) { + + var s = getMachineStateByTestingColumn_(siteId, cIndex ); + + if( s !== PS.STATES.GOOD && s !== "" && curSite.xaxis.length > (cIndex+1) ) { + curSite.yaxis[yIndex].horizontalSummary[cIndex+1] = PS.STATES.REALLY_BAD; + } + + if (curSite.xaxis.length > (cIndex+1)) { + states.push(s); + } + } + + var objStates = []; + + for( var y=0; y < states.length; y++ ) { + objStates.push({ + mainState: states[y], + branches: [] + }); + } + + machinesInAccordion.push({ + site_id: siteId, + schedulerShow: machinesObj.scheduler, + states: objStates + }); + } + } + }; + + + /* + * curated sites: + * columnIndex - 0 is the first column, "Test Suite", + * 1 is Tests, etc. + */ + var getMachineStateByTestingColumn_ = function( siteId, columnIndex) { + + var xTests = CONF.curated_sites.xaxisTests; + var testName = xTests[columnIndex]; + + var resultSetupAndCall = resultsBySiteIdAndCol_[ siteId ] ? resultsBySiteIdAndCol_[ siteId ][ testName ] : {}; + + var bl = getStatus_(resultSetupAndCall, testName); + return bl; + }; + + + var getMachineArray_ = function() { + + var yaxis = CONF.curated_sites.yaxis; + var machine_arr = []; + + for( var x in yaxis ) { + + var lm_obj = yaxis[x]; + var machineKeys = Object.keys(lm_obj.machines); + machine_arr = machine_arr.concat( machineKeys ) + } + + return machine_arr; + }; + + + var getLatestResultForTestOnAllSites_ = function() { + + var t_arr = CONF.curated_sites.xaxisTests; + var js = JSON.stringify( t_arr ); + var path = "tests"; + var ma = getMachineArray_(); + var params = "sites_to_get=" + JSON.stringify( ma ) + + "&tests_to_match=" + js; + + var url = PS.getURL(path); + + $.get(url, params, handleTests_); + }; + + + var resultsBySiteIdAndCol_ = {}; + + var handleTests_ = function(results) { + + resultsBySiteIdAndCol_ = results; + + makeModel_(); + callback_(); + }; + + + var handleColumn_ = function( zCol, testsBySites ) { + + var xTests = CONF.curated_sites.xaxisTests; + var testName = xTests[zCol]; + + for( var siteId in testsBySites ) { + + var result = getResult_( testsBySites[siteId], testName ); + console.log('siteId: ' + siteId); + var boolRes = getStatus_( result ); + + resultsBySiteIdAndCol_[ siteId ] = resultsBySiteIdAndCol_[ siteId ] || []; + resultsBySiteIdAndCol_[ siteId ][ zCol ] = boolRes; + + //console.log( siteId ); + //console.dir( result ); + } + }; + + + var getStatus_ = function( resultContainer, testName ) { + + if( !resultContainer || !resultContainer.results ) { + return PS.STATES.EMPTY; + } + + var result = resultContainer.results; + + // SDK page send back results in a slightly different format. + if( result && result[testName] ) { + + return result[testName].passed === true ? PS.STATES.GOOD : PS.STATES.REALLY_BAD; + } + + // PSIJ + + if( result && result.call && result.call.passed && + result.setup && result.setup.passed ) { + + return PS.STATES.GOOD; + } + + return PS.STATES.REALLY_BAD; + }; + + + var getResult_ = function( allTestsForSite, testName ) { + + for( var i=0; i < allTestsForSite.length; i++ ) { + + var test = allTestsForSite[i]; + + if( test.test_name === testName ) { + console.log('branch: ' + test.branch); + return test.results; + } + } + + return false; + }; + + + + var getSiteBySiteId_ = function( siteId ) { + + for( var y=0; y < PS.sites.length; y++ ) { + + var site = PS.sites[y]; + + if( siteId === site.site_id ) { + return site; + } + } + + return null; + }; + + + var calendarBubbleClass_ = function(d) { + + var size = function(name) { + return name == "main" || name == "master" ? "bubble-large" : "bubble-small"; + }; + + var bc = globalMethods.badnessClass( d ); + + if (d == null || (d.completed_count == 0 && d.failed_count == 0)) { + return "state-unknown "; + } + else { + return globalMethods.badnessClass(d) + " " + size(d.name); + } + }; + + var getBranchesForSmallBubbles_ = function( branches ) { + + var br = []; + + for( var x=0; x < branches.length; x++ ) { + + var branch = branches[x]; + var bc = calendarBubbleClass_( branch ); + var bs = globalMethods.calendarBubbleStyleObj( branch ); + + if( branch.name !== "main" && branch.name !== "master") { + br.push({ + calendarBubbleClass: bc, + calendarBubbleStyle: bs + }); + } + } + + return br; + }; + + return { + getBranchesForSmallBubbles: getBranchesForSmallBubbles_, + getMainBranch: getMainBranch_, + getLatestResultForTestOnAllSites: getLatestResultForTestOnAllSites_, + getModel: getModel_ + } +}(); \ No newline at end of file diff --git a/src/psij/web/js/common.js b/src/psij/web/js/common.js new file mode 100644 index 0000000..101684b --- /dev/null +++ b/src/psij/web/js/common.js @@ -0,0 +1,10 @@ +var PS = PS || {}; + +PS.sites = [ +]; + +settingsDefaults({"viewMode": "calendar", "inactiveTimeout": 10, "showBranchBubbles": false}); + +PS.getURL = function( path ) { + return CONF.backendURL + path; +}; diff --git a/src/psij/web/lib.js b/src/psij/web/js/lib.js similarity index 78% rename from src/psij/web/lib.js rename to src/psij/web/js/lib.js index ac809a5..5f37cd4 100644 --- a/src/psij/web/lib.js +++ b/src/psij/web/js/lib.js @@ -1,16 +1,29 @@ +// lib.js +var PS = PS || {}; +PS.STATES = { + "GOOD": "state-good", + "SOMEWHAT_BAD": "state-somewhat-bad", + "REALLY_BAD": "state-really-bad", + "EMPTY": "empty" +}; var badness = function(obj) { - if (obj['completed_count'] <= 2) { - return 2; + if( obj['completed_count'] === 0 ) { + // empty. + return 3; } - if (obj['failed_count'] > 0) { - if (obj['completed_count'] < obj['failed_count']) { - return 2; + if( obj['completed_count'] <= 2 ) { + return 2; // red. + } + + if (obj['failed_count'] > 0 ) { + if (obj['completed_count'] < obj['failed_count'] ) { + return 2; // red } else { - return 1; + return 1; // orange } } else { @@ -30,7 +43,7 @@ var getDayData = function(site, day) { return month[day.day]; } } - return null; + return {branches:[]}; } var getBranchData = function(branch, dayData) { @@ -39,6 +52,7 @@ var getBranchData = function(branch, dayData) { } var name = branch.name; if (dayData != null) { + for (var i = 0; i < dayData.branches.length; i++) { if (dayData.branches[i].name == name) { return dayData.branches[i]; @@ -60,19 +74,40 @@ var settingsDefaults = function(dict) { var makeSetting = function(name, callback, type) { return { get: function() { - var val = Cookies.get(name); - if (type == "bool") { - return val == "true"; - } - else { - return val; + + var sbb = localStorage.getItem(name); + + if( name === 'showBranchBubbles' ) { + sbb = localStorage.getItem('showBranchBubbles') === 'true' ? 'true' : false; + } else if( name === 'inactiveTimeout' ) { + sbb = localStorage.getItem('inactiveTimeout') || 10; } + + return sbb; }, set: function(val) { - Cookies.set(name, val); + + localStorage.setItem( name, val ); callback(); - } + + if( name === "showBranchBubbles" ) { + console.log('showBranchbubbles:'); + console.dir(val); + } + } + } +}; + + +PS.showOrHideBranchBubbles = function() { + + var show = localStorage.getItem('showBranchBubbles') === 'true'; + + if( show ) { + $('.container-for-mini-branches').show(); + } else { + $('.container-for-mini-branches').hide(); } }; @@ -96,7 +131,6 @@ ansi_up.use_classes = true; var globalMethods = { sort: function(key, lst) { - console.log(lst); if (!lst) { return lst; } @@ -111,9 +145,9 @@ var globalMethods = { for (var i = 0; i < copy.length; i++) { var site = copy[i]; site.key = site.site_id.split('.').reverse().join('.'); - console.log(site.key) } strComp = function(a, b) {if (a > b) return 1; else if (a == b) return 0; else return -1;}; + return copy.sort((a, b) => strComp(a.key, b.key)); }, limit: function(array, n) { @@ -127,11 +161,13 @@ var globalMethods = { badnessClass: function(obj) { switch(badness(obj)) { case 0: - return "state-good"; + return PS.STATES.GOOD; case 1: - return "state-somewhat-bad"; + return PS.STATES.SOMEWHAT_BAD; case 2: - return "state-really-bad"; + return PS.STATES.REALLY_BAD; + case 3: + return PS.STATES.EMPTY; } }, badnessSort: function(lst) { @@ -140,7 +176,8 @@ var globalMethods = { }); }, branchSort: function(lst) { - if (setting("showBranchBubbles") != "true") { + + if (localStorage.getItem("showBranchBubbles") != "true") { return lst.filter(branch => (branch.name == "main") || (branch.name == "master")); } var sorted = lst.slice().sort((a, b) => { @@ -155,8 +192,55 @@ var globalMethods = { }); return sorted; }, + // could be more optimized + isSingleUrl: function( url ) { + + var count = 0; + var test_domain = this.getDomain( url ); + + for( var x in this.sites ) { + + var site = this.sites[x]; + var loop_domain = this.getDomain( site.site_id ); + + if( test_domain === loop_domain ) { + count++; + } + } + + console.dir( this.sitesSort(this.sites) ); + console.log( test_domain + count ); + return count < 2; + }, + getPosition: function( url ) { + + if( this.isSingleUrl(url) ) { + return 'oneRowOnly'; + } + + var domain = globalMethods.getDomain( url ); + PS.urls = PS.urls || {}; + + PS.urls[domain] = PS.urls[domain] || 0; + PS.urls[domain]++; + + if( PS.urls[domain] <= 2 ) { + return "firstPos"; + } + + return "otherPos"; + }, + getDomain: function( url ) { + + var uparts = url.split('.'); + var len = uparts.length; + var first = uparts[len - 2]; + var first_dot = first ? (first + '.') : ""; + + return first_dot + uparts[len - 1]; + }, navigate: function(loc) { - console.log(loc); + //console.log(loc); return "event.stopPropagation(); window.location=\"" + loc + "\""; }, shortenId: function(id) { @@ -325,14 +409,18 @@ var globalMethods = { } var d = getBranchData(branch, getDayData(site, day)); if (d == null || (d.completed_count == 0 && d.failed_count == 0)) { - return "state-unknown, " + size(branch); + return "state-unknown " + size(branch); } else { - return this.badnessClass(d) + ", " + size(branch); + return this.badnessClass(d); // + ", " + size(branch); } }, calendarBubbleStyle: function(site, day, branch) { + var d = getBranchData(branch, getDayData(site, day)); + return globalMethods.calendarBubbleStyleObj(d); + }, + calendarBubbleStyleObj: function( d ) { var color; if (d != null) { var completed = d.completed_count; @@ -385,7 +473,10 @@ var globalMethods = { }, getDayData: getDayData, breadcrumbs: function() { - var b = [{text: CUSTOMIZATION.projectName + " Tests", href: "summary.html"}]; + + var projName = CUSTOMIZATION.projectName; + + var b = [{text: projName + " Tests", href: "summary.html"}]; var level = 0; if (window.location.pathname.startsWith("/site.html")) { level = 1; @@ -404,11 +495,16 @@ var globalMethods = { } return b; }, + updateRender: function() { + this.$forceUpdate(); + }, setting: setting } $(document).ready(function() { + setTimeout(function() { + $('.clickable').each(function() { if ($(this).attr('onclick') !== undefined) { var v = $(this).attr('onclick'); diff --git a/src/psij/web/js/summary.js b/src/psij/web/js/summary.js new file mode 100644 index 0000000..3c4f530 --- /dev/null +++ b/src/psij/web/js/summary.js @@ -0,0 +1,188 @@ + +var setViewMode = function (viewMode) { + + if (viewMode === undefined) { + viewMode = Cookies.get('viewMode'); + } + if (viewMode === undefined) { + viewMode = 'calendar'; + } + + Cookies.set('viewMode', viewMode); + + $('.view-mode-group').removeClass('selected'); + $('#view-mode-' + viewMode).addClass('selected'); + + siteStatus.mode = viewMode; + + if( viewMode === 'calendar' ) { + setTimeout( PS.bindRowOpenClose, 0 ); + } + + if( viewMode === 'home' ) { + PS.SitesModel.getLatestResultForTestOnAllSites(); + } +}; + + +PS.SummaryModel = function() { + + var set = function(dst, src) { + dst.length = 0; + for (var i = 0; i < src.length; i++) { + dst.push(src[i]); + } + }; + + var updateSummary = function() { + + var context = PS.getURL('summary'); + var inact = localStorage.getItem('inactiveTimeout'); + inact = inact ? inact : 10; + + var obj = { + inactiveTimeout: inact + }; + + + $.get(context, obj, function(data) { + + set(PS.sites, data); + + PS.accordionModel = PS.accordionModel || []; + PS.BuildAccordionModelFromSites.get(PS.accordionModel); + + console.log('after summary get:'); + console.dir(PS.accordionModel); + + PS.makeSites(); + + // Vue wants to rerender this section immediately after this function + // That would wipe out showOrHideBranchBubbles. Give Vue the chance + // to do it rerender, then run showOrHideBranchBubbles. + setTimeout(PS.showOrHideBranchBubbles,0); + }); + }; + + $(document).ready( updateSummary ); + + return { + updateSummary: updateSummary + } +}(); + + + + +var siteStatus; + +PS.bindRowOpenClose = function() { + + PS.urls = {}; + console.log($('.open_close_arrow').length); + + $('.open_close_arrow').unbind('click').bind('click', function() { + + var parent = $(this).closest('tr'); + var site_el = parent.find('[site_id]'); + var siteIdClicked = site_el.attr('site_id'); + + console.log( siteIdClicked ); + $(this).toggleClass('open'); + + $('[site_id="' + siteIdClicked + '"][position="otherPosOnc"]').parent().toggle(); + }); + + PS.showOrHideBranchBubbles(); +}; + + + +PS.makeSites = function() { + + var init_ = function( sites_model ) { + + if( !sites_model ) { + return false; + } + + PS.BuildAccordionModelFromSites.get(PS.accordionModel); + + siteStatus = new Vue({ + el: "#container", + components: { + }, + vuetify: new Vuetify(), + data: { + sites: PS.sites, + accordionModel: PS.accordionModel, + mode: '', + now: moment().utc().startOf("day"), + STR: CONF.STR, + curated_sites: sites_model, + settingsDialog: false + }, + methods: globalMethods, + computed: { + inactiveTimeout: makeSetting("inactiveTimeout", PS.SummaryModel.updateSummary), + showBranchBubbles: makeSetting("showBranchBubbles", PS.SummaryModel.updateSummary, "bool"), + allBranches: function () { + + console.log('allBranches:'); + console.dir(this.sites); + + var unique = {}; + for (var i = 0; i < this.sites.length; i++) { + + var site = this.sites[i]; + var branches = site.branches; + for (var j = 0; j < branches.length; j++) { + var branch = branches[j]; + if (branch.name in unique) { + var existing = unique[branch.name]; + existing['failed_count'] += branch['failed_count']; + existing['completed_count'] += branch['completed_count']; + } else { + unique[branch.name] = {...branch}; + unique[branch.name]['sites'] = {}; + } + unique[branch.name]['sites'][site.site_id] = branch; + } + } + + values = []; + for (var k in unique) { + values.push(unique[k]); + } + return this.sort('name', values); + }, + branchIndexMap: function () { + var all = this.allBranches; + var map = {}; + for (var i = 0; i < all.length; i++) { + map[all[i].name] = i; + } + return map; + } + } + }); + + setViewMode(); + PS.bindRowOpenClose(); + }; + + + + $(document).ready( PS.bindRowOpenClose ); + + if( !PS.already_made_site ) { + PS.already_made_site = 1; + + PS.SitesModel.getModel( init_ ); + } + + window.reget = function() { + PS.SitesModel.getModel( init_ ); + }; + +}; \ No newline at end of file diff --git a/src/psij/web/run.html b/src/psij/web/run.html index 36e5be2..415e34b 100644 --- a/src/psij/web/run.html +++ b/src/psij/web/run.html @@ -8,12 +8,13 @@ crossorigin="anonymous"> - - + + + - +
@@ -33,7 +34,7 @@ {{ branch.name }}
{{ branch.failed_count }} - +
@@ -122,6 +123,9 @@

+ + + - - + + - +
@@ -48,7 +48,7 @@
{{ branch.failed_count }} - +
@@ -57,6 +57,8 @@ + + - - - - - - - - - - - - - - -
- -
-
- calendar_today -
-
- grid_view -
-
- table_view + + Summary + + + + + + + + + + + + + + + + + + + + + +
+ -
 
- + + +
+ +
+ home +
+ +
+ calendar_today +
+
+ grid_view +
+ + +
 
+ +