Skip to content

Commit b091846

Browse files
authored
Merge pull request #3 from sassoftware/scr-scorer
Scr scorer
2 parents b504f9e + e28a568 commit b091846

File tree

12 files changed

+234
-3
lines changed

12 files changed

+234
-3
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## SAS Portal Framework for SAS Viya v1.0.4
4+
5+
- Add: The SCR Module Scorer object has been added
6+
- Add: New utility function getSCRMetadata, retrieves the inputs and outputs of a SCR endpoint
7+
- Add: New utility function scoreSCR, which enables you to score a SCR endpoint
8+
39
## SAS Portal Framework for SAS Viya v1.0.3
410

511
- Add: The MAS Module Scorer object now provides the ability to delete MAS Modules

index.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@
109109
<script src="./js/utility/get-model-variables.js"></script>
110110
<script src="./js/utility/delete-model-variable.js"></script>
111111
<script src="./js/utility/validate-ds2-variable-name.js"></script>
112+
<script src="./js/utility/get-SCR-metadata.js"></script>
113+
<script src="./js/utility/score-scr.js"></script>
112114

113115
<!-- Import Object Builder -->
114116
<script src="./js/objects/add-text-objects.js"></script>
@@ -119,6 +121,7 @@
119121
<script src="./js/objects/add-client-administrator.js"></script>
120122
<script src="./js/objects/add-vaReport-object.js"></script>
121123
<script src="./js/objects/add-run-custom-code-objects.js"></script>
124+
<script src="./js/objects/add-scrScore-object.js"></script>
122125

123126
<!-- Import Page Builing -->
124127
<script src="./js/generate-tabs.js"></script>

js/generate-pages.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,13 @@ async function generatePages(VIYAHOST, layout, paneContainer, interfaceText) {
125125
layout?.general?.shorthand
126126
);
127127
break;
128+
case 'scrScore':
129+
content = await addSCRScoreObject(
130+
currentObjectDefinition,
131+
layout?.general?.shorthand,
132+
interfaceText?.scrScore
133+
);
134+
break;
128135
default:
129136
content = document.createElement('p');
130137
content.innerText = interfaceText?.undefinedObjectText;

js/objects/add-scrScore-object.js

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/**
2+
* Create a SCR Scoring Object
3+
*
4+
* @param {Object} scrObject - Contains the definition of the SCR Score Object
5+
* @param {String} paneID - The shorthand of the page which will contain the object
6+
* @param {Object} scrInterfaceText - Contains all of the SCR relevant language interface
7+
* @returns a SCR Score object
8+
*/
9+
async function addSCRScoreObject(scrObject, paneID, scrInterfaceText) {
10+
// Create SCR Score Container
11+
let scrContainer = document.createElement('div');
12+
scrContainer.setAttribute('id', `${paneID}-obj-${scrObject?.id}`);
13+
14+
// Create the Input Heading
15+
let inputHeader = document.createElement('h3');
16+
inputHeader.innerText = scrInterfaceText?.inputHeader;
17+
18+
// Create the Input Container
19+
let scrInputContainer = document.createElement('div');
20+
scrInputContainer.id = `${paneID}-obj-${scrObject?.id}-inputs`;
21+
22+
// Create the Output Heading
23+
let outputHeader = document.createElement('h3');
24+
outputHeader.innerText = scrInterfaceText?.outputHeader;
25+
26+
// Create the Output Container
27+
let scrOutputContainer = document.createElement('div');
28+
scrOutputContainer.id = `${paneID}-obj-${scrObject?.id}-outputs`;
29+
30+
// Create the Score Button
31+
let scrScoreButton = document.createElement('button');
32+
scrScoreButton.type = 'button';
33+
scrScoreButton.id = `${paneID}-obj-${scrObject?.id}-score`;
34+
scrScoreButton.setAttribute('class', 'btn btn-primary');
35+
scrScoreButton.innerText = scrInterfaceText?.scoreButton;
36+
scrScoreButton.onclick = async function () {
37+
this.disabled = true;
38+
this.innerHTML = `<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> ${scrInterfaceText?.runStatus}`;
39+
let submitForm = document.getElementById(
40+
`${paneID}-obj-${scrObject?.id}-inputs-form`
41+
);
42+
const values = Array.from(submitForm.elements).map((x) => {
43+
return {
44+
name: x.id,
45+
value: isNaN(parseFloat(x.value))
46+
? x.value
47+
: parseFloat(x.value),
48+
};
49+
});
50+
51+
let currentSCR = document.getElementById(`${paneID}-obj-${scrObject?.id}-endpoint`).value;
52+
let scrResponse = await scoreSCR(currentSCR, values);
53+
let scrResultTable = document.getElementById(`${paneID}-obj-${scrObject?.id}-outputs-table-table-root`);
54+
addRowToTable(scrResultTable, [Object.values(scrResponse?.data)]);
55+
this.disabled = false;
56+
this.innerText = scrInterfaceText?.scoreButton;
57+
}
58+
59+
// Add the SCR Module Input Selector
60+
let scrEndpointInput = document.createElement('input');
61+
scrEndpointInput.type = 'text';
62+
scrEndpointInput.className = 'form-control';
63+
scrEndpointInput.placeholder = 'https://example.com/SCR';
64+
scrEndpointInput.id = `${paneID}-obj-${scrObject?.id}-endpoint`;
65+
scrEndpointInput.onblur = async function () {
66+
let scrDefinition = await getSCRMetadata(this.value);
67+
// Reset containers
68+
let scrInContainer = document.getElementById(`${paneID}-obj-${scrObject?.id}-inputs`);
69+
scrInContainer.innerHTML = '';
70+
let scrOutContainer = document.getElementById(`${paneID}-obj-${scrObject?.id}-outputs`)
71+
scrOutContainer.innerHTML = '';
72+
if(scrDefinition.length === 1) {
73+
let scrError = document.createElement('p');
74+
scrError.style.color = 'red';
75+
scrError.innerText = scrDefinition[0];
76+
scrInContainer.appendChild(scrError);
77+
} else {
78+
// Create the inputs
79+
addAccordionBody(`${paneID}-obj-${scrObject?.id}`, 'inputs', 'input', scrDefinition[0]);
80+
// Create the outputs
81+
createTable(scrOutContainer, `${paneID}-obj-${scrObject?.id}-outputs-table`, Object.values(scrDefinition[1]).map(field => field.name), []);
82+
}
83+
}
84+
85+
// Add Label for input
86+
let scrEndpointInputLabel = document.createElement('label');
87+
scrEndpointInputLabel.for = `${paneID}-obj-${scrObject?.id}-endpoint`;
88+
scrEndpointInputLabel.innerText = `${scrInterfaceText?.endpoint}:`;
89+
scrEndpointInputLabel.style.textTransform = 'capitalize';
90+
91+
// Create result
92+
scrContainer.appendChild(scrEndpointInputLabel);
93+
scrContainer.appendChild(scrEndpointInput);
94+
scrContainer.appendChild(document.createElement('br'));
95+
scrContainer.appendChild(inputHeader);
96+
scrContainer.appendChild(document.createElement('br'));
97+
scrContainer.appendChild(scrInputContainer);
98+
scrContainer.appendChild(document.createElement('br'));
99+
scrContainer.appendChild(scrScoreButton);
100+
scrContainer.appendChild(document.createElement('br'));
101+
scrContainer.appendChild(document.createElement('br'));
102+
scrContainer.appendChild(outputHeader);
103+
scrContainer.appendChild(document.createElement('br'));
104+
scrContainer.appendChild(scrOutputContainer);
105+
106+
return scrContainer;
107+
}

js/utility/get-SCR-metadata.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* Returns the In- and Outputs of a SCR endpoint
3+
* @param {String} endpoint - The SCR endpoint
4+
* @returns {Array} - The first entry is the inputs and the second the outputs
5+
*/
6+
async function getSCRMetadata(endpoint) {
7+
let variableDefinitions = [];
8+
9+
// Call the SCR metadata endpoint
10+
const SCRMETADATARESPONSE = await fetch(`${endpoint}/apiMeta/api`);
11+
12+
// Parse the response
13+
if(SCRMETADATARESPONSE.status === 200) {
14+
const SCRMETADATA = await SCRMETADATARESPONSE.json();
15+
variableDefinitions.push(SCRMETADATA?.definitions?.PCRInput?.properties?.data?.properties);
16+
variableDefinitions.push(SCRMETADATA?.definitions?.PCROutput?.properties?.data?.properties);
17+
return variableDefinitions;
18+
} else {
19+
return [`Request for ${endpoint} failed with the HTTP code: ${SCRMETADATARESPONSE.status} - please check the SCR endpoint.`]
20+
}
21+
}

js/utility/score-scr.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
* Returns the Scored Results of a SCR Endpoint
3+
*
4+
* @param {String} scrEndpoint - The endpoint of the SCR that is to be scored
5+
* @param {Array of Objects} scrInput - An array of input objects for the SCR
6+
* @returns {Promise/Object of SCR} - Returns a Promise that should resolve into the scored results
7+
*/
8+
async function scoreSCR(scrEndpoint, scrInput) {
9+
let SCRSCORE = await fetch(
10+
scrEndpoint,
11+
{
12+
// mode: 'no-cors',
13+
method: 'post',
14+
headers: {
15+
Accept: 'application/json',
16+
'Content-Type': 'application/json',
17+
},
18+
body: JSON.stringify({
19+
inputs: scrInput,
20+
}),
21+
redirect: 'follow',
22+
}
23+
);
24+
if (!SCRSCORE.ok) {
25+
if (
26+
SCRSCORE.status === 403 &&
27+
SCRSCORE.headers.get('x-forbidden-reason') === 'CSRF'
28+
) {
29+
let h = SCRSCORE.headers.get('x-csrf-header');
30+
let t = SCRSCORE.headers.get('x-csrf-token');
31+
document.csrfToken = t;
32+
SCRSCORE = await fetch(
33+
scrEndpoint,
34+
{
35+
// mode: 'no-cors',
36+
method: 'post',
37+
headers: {
38+
Accept: 'application/json',
39+
'Content-Type': 'application/json',
40+
'X-CSRF-TOKEN': t,
41+
},
42+
body: JSON.stringify({
43+
inputs: scrInput,
44+
}),
45+
redirect: 'follow',
46+
}
47+
);
48+
} else if (SCRSCORE.status === 400) {
49+
window.alert(await SCRSCORE.json());
50+
}
51+
}
52+
53+
const SCRSCORECONTENT = await SCRSCORE.json();
54+
return SCRSCORECONTENT;
55+
}

language/de.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,5 +97,12 @@
9797
"oauthClientAuthenticatorText1": "Für die Bearbeitung/Erstellung der OAuth Clients muss ein spezieller Token generiert werden, öffne hierzu den folgenden Link:",
9898
"oauthClientAuthenticatorText2": ",dann die administrative Gruppe annehmen und kopiere den Wert in der URL Leiste komplett in das Eingabefeld:",
9999
"oauthClientNoTokenError": "Bitte gebe einen Token an, wie oben beschrieben."
100+
},
101+
"scrScore": {
102+
"endpoint": "Bitte gib den SCR Endpunkt hier an (rausklicken aus dem Feld startet das Metadaten sammeln):",
103+
"scoreButton": "Score",
104+
"inputHeader": "SCR Eingaben",
105+
"outputHeader": "SCR Ergebnisse",
106+
"runStatus": "Scoring"
100107
}
101108
}

language/en.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,5 +97,12 @@
9797
"oauthClientAuthenticatorText1": "For editing/creating the OAuth clients a special token must be generated, click on the following link:",
9898
"oauthClientAuthenticatorText2": ", assume the administrative group and copy the full URL value into the input field:",
9999
"oauthClientNoTokenError": "Please enter an authentication token as described above."
100+
},
101+
"scrScore": {
102+
"endpoint": "Please enter the SCR endpoint (click outside of the field to trigger metadata fetching):",
103+
"scoreButton": "Score",
104+
"inputHeader": "SCR Inputs",
105+
"outputHeader": "SCR Outputs",
106+
"runStatus": "Scoring"
100107
}
101108
}

website/docs/Objects/client-administrator-object.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
sidebar_position: 7
2+
sidebar_position: 8
33
---
44

55
# Client Administrator

website/docs/Objects/portal-builder-object.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
sidebar_position: 9
2+
sidebar_position: 10
33
---
44

55
# Portal Builder

0 commit comments

Comments
 (0)