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.site_id }} | ' +
+ '{{ 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 @@
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-