Skip to Content
Previous

Change Point Detection ML service with SAPUI5

By Abdel DADOUCHE

Discover how to implement SAP Leonardo Machine Learning Functional Service in a SAPUI5 application

You will learn

In this tutorial, you will learn how to quickly integrate the Time Series Change Point Detection SAP Leonardo Machine Learning Functional Services published from the SAP API Business Hub sandbox in a SAPUI5 application.

Details

You will then be able to substitute the Time Series Change Point Detection services with any other SAP Leonardo Machine Learning Functional Services that consumes text content.


Step 1: Get Your Sandbox URL

In order to consume the Time Series Change Point Detection Machine Learning Functional Services, you will first need to get the service URI, your API Key and the request and response parameters.

Go to https://api.sap.com/ and click on the Browse tile.

SAP API Business Hub

Then you will be able to search for the SAP Leonardo Machine Learning - Functional Services, then click on the package found.

SAP API Business Hub

Click on Artifacts, then click on the Time Series Change Point Detection API.

SAP API Business Hub

As you can notice the Time Series Change Point Detection API has only one resource (or service): /inference_sync.

Now click on the Overview tab.

Note: the term inference refers to the application phase (scoring) an existing model (as opposed to the training or inception phase) and sync for synchronous.

SAP API Business Hub

As displayed on the screen, the sandbox URL for the Time Series Change Point Detection API where we need to append the API resource:

https://sandbox.api.sap.com/ml/changepointdetection/inference_sync
Step 2: Get Your API key

When using any of the APIs outside of the SAP API Business Hub, an application key will be needed in every request header of your APIs calls.

To get to your API key, click on the key icon in the top right corner of the page. Click on the key icon.

The following pop-up should appear. Click on the Copy API Key button and save it in a text editor.

SAP API Business Hub

Now, let’s build a SAPUI5 application! But before doing so let’s first add the destination to connect to the SAP API Business Hub.

Step 3: Access the SAP Cloud Platform Cockpit

Go to your SAP Cloud Platform Cockpit account and access “Your Personal Developer Account”.

SAP HANA Cloud Platform Cockpit
Step 4: Configure your destination

You will need to create a destination in your SAP Cloud Platform account that allow will your applications to connect to external APIs such as the SAP API Business Hub.

On the left side bar, you can navigate in Connectivity > Destinations.

Your Personal Developer Account

On the Destinations overview page, click on New Destination

Destinations

Enter the following information:

Field Name Value
Name sapui5ml-api
Type HTTP
Description SAP Leonardo Machine Learning APIs
URL https://sandbox.api.sap.com/ml
Proxy Type Internet
Authentication NoAuthentication

Then you will need to add the following properties to the destination:

Property Name Value
WebIDEEnabled true

Click on Save

New Destinations

You can use the Check Connectivity button HTML5 Applications next to the new Destination to validate that the URL can be accessed.

Step 5: Open the Web IDE

On the left side bar, you can navigate in Services, then using the search box enter Web IDE.

Web IDE

Click on the tile, then click on Open SAP Web IDE.

Web IDE

You will get access to the SAP Web IDE main page:

Web IDE
Step 6: Create your application using the SAPUI5 template

Click on New Project from Template in the Create Project section or use the File > New > Project from Template.

Project

Select the SAPUI5 Application tile, then click on Next

Project

Enter the following information, then click on Next

Field Name Value
Project Name sapui5ml-changepointdetection
Namespace demo
Project

Enter the following information, then click on Finish

Field Name Value
View Type XML
View Name demo
Project
Step 7: Extend the application resource roots

In order to ease the use of the provided code, we will add a new SAPUI5 resource roots. The main reason for this is that the rule used to generate the initial resource root by the project template has change many time over the time.

Edit the index.html file located under Workspace > sapui5ml > webapp and add the below element to the existing data-sap-ui-resourceroots property around line 15 (don’t forget the comma in between the existing element and the new one).

"sapui5ml": ""

It should eventually look something like this:

data-sap-ui-resourceroots='{"demosapui5ml-changepointdetection": "", "sapui5ml": ""}'

Click on the Save Button button (or press CTRL+S).

Step 8: Configure your SAPUI5 application

In order to use the previously configured destination, we need to add its declaration into the neo-app.json file along with the header white list configuration that will prevent HTTP header parameters to be filtered out.

Edit the neo-app.json file located under Workspace > sapui5ml-changepointdetection and replace the current content with the below code.

Then click on the Save Button button (or press CTRL+S).

{
	"welcomeFile": "/webapp/index.html",
	"routes": [{
		"path": "/resources",
		"target": {
			"type": "service",
			"name": "sapui5",
			"entryPath": "/resources"
		},
		"description": "SAPUI5 Resources"
	}, {
		"path": "/test-resources",
		"target": {
			"type": "service",
			"name": "sapui5",
			"entryPath": "/test-resources"
		},
		"description": "SAPUI5 Test Resources"
	}, {
		"path": "/ml",
		"target": {
			"type": "destination",
			"name": "sapui5ml-api"
		},
		"description": "ML API destination"
	}],
	"sendWelcomeFileRedirect": true,
	"headerWhiteList": [
		"APIKey"
	]
}
Step 9: Store your API setting in a JSON model

There are multiple options to achieve this goal. Here we will use a pre-loaded JSON model configured in the manifest.json file.

Create a new file named demo.json under Workspace > sapui5ml-changepointdetection > webapp > model, copy the below code and make sure you replace <<<<< COPY YOUR API KEY >>>>> by your the API key we retrieved in step 2.

Then click on the Save Button button (or press CTRL+S).

{
	"url": "/ml/changepointdetection/inference_sync",
	"APIKey": "<<<<< COPY YOUR API KEY >>>>>",
	"options": {
		"separator": "|",
		"series_separator": "#"
	}
}

Edit the manifest.json file located under Workspace > sapui5ml-changepointdetection > webapp and locate the models section (around line 55), and update the section like this:

Then click on the Save Button button (or press CTRL+S).

"models": {
  "i18n": {
    "type": "sap.ui.model.resource.ResourceModel",
    "settings": {
      "bundleName": "demosapui5ml-changepointdetection.i18n.i18n"
    }
  },
  "demo": {
    "type": "sap.ui.model.json.JSONModel",
    "preload": true,
    "uri": "model/demo.json"
  }
}
Step 10: Extend the main SAPUI5 view

The view will contain a table to display the results along with a canvas to display the selected image (if a single one is selected) and 2 buttons, one to import a snapshot and the other one to take snapshot using the webcam (if any, this button won’t be visible on mobile device because it is not supported).

Edit the demo.view.xml file located under Workspace > sapui5ml-changepointdetection > webapp > view and replace the existing code with the below code.

Then click on the Save Button button (or press CTRL+S).

<mvc:View xmlns:html="http://www.w3.org/1999/xhtml" xmlns:mvc="sap.ui.core.mvc" xmlns:form="sap.ui.layout.form" xmlns="sap.m"
	controllerName="sapui5ml.controller.demo" displayBlock="true">
	<App>
		<pages>
			<Page title="Change Point Detection">
				<content>
					<HBox width="100%">
						<items>
							<VBox width="100%" direction="Column">
								<items>
									<HBox width="100%">
										<items>
											<VBox alignItems="End" width="50%">
												<items>
													<Text textAlign="End" class="sapUiSmallMargin" text="Number of data point"/>
													<Text textAlign="End" class="sapUiSmallMargin" text="Number of series"/>
												</items>
											</VBox>
											<VBox alignItems="Start" width="50%">
												<items>
													<StepInput value="{demo>/options/numDataPoints}" required="true"/>
													<StepInput value="{demo>/options/numDataSeries}" required="true"/>
												</items>
											</VBox>
										</items>
									</HBox>
									<VBox width="100%" alignItems="Center">
										<items>
											<Button text="Generate Sample Data" press="onGenerate"/>
										</items>
									</VBox>
								</items>
							</VBox>
							<VBox width="100%" alignContent="Start" alignItems="Start" justifyContent="Start">
								<items>
									<HBox width="100%" visible="{= typeof ${demo>/hasData} !== 'undefined'}">
										<items>
											<VBox alignItems="End" width="50%">
												<items>
													<Text textAlign="End" class="sapUiSmallMargin" text="Values separator"/>
													<Text textAlign="End" class="sapUiSmallMargin" text="Series separator for multivariate time series"/>
												</items>
											</VBox>
											<VBox alignItems="Start" width="50%">
												<items>
													<Input type="Text"
														value="{ path : 'demo>/options/separator', type : 'sap.ui.model.type.String', constraints : { minLength: 1, maxLength: 1 } }"/>
													<Input type="Text"
														value="{ path : 'demo>/options/series_separator', type : 'sap.ui.model.type.String', constraints : { minLength: 1, maxLength: 1 } }"/>
												</items>
											</VBox>
										</items>
									</HBox>
									<VBox width="100%" alignItems="Center" visible="{= typeof ${demo>/hasData} !== 'undefined'}">
										<items>
											<Button text="Execute Service" press="onExecute"/>
										</items>
									</VBox>
								</items>
							</VBox>
						</items>
					</HBox>
					<HBox width="100%" alignItems="Start" justifyContent="Start" visible="{= typeof ${demo>/hasData} !== 'undefined'}">
						<items>
							<VBox width="50%">
								<items>
									<Table columns="{demo>/cols}" items="{path: 'demo>/rows'}">
										<items>
											<ColumnListItem cells="{demo>cols}">
												<cells>
													<Input value="{demo>}"/>
												</cells>
											</ColumnListItem>
										</items>
										<columns>
											<Column>
												<header>
													<Text text="{demo>colid}"/>
												</header>
											</Column>
										</columns>
									</Table>
								</items>
							</VBox>
							<VBox width="50%" visible="{= typeof ${demo>/hasResult} !== 'undefined'}">
								<items>
									<Table columns="{demo>/resultCols}" items="{path: 'demo>/resultRows'}" visible="{= typeof ${demo>/hasResult} !== 'undefined'}">
										<items>
											<ColumnListItem cells="{demo>cols}">
												<cells>
													<Input value="{demo>}" editable="false"/>
												</cells>
											</ColumnListItem>
										</items>
										<columns>
											<Column>
												<header>
													<Text text="Result series: {demo>colid}"/>
												</header>
											</Column>
										</columns>
									</Table>
								</items>
							</VBox>
						</items>
					</HBox>
				</content>
			</Page>
		</pages>
	</App>
</mvc:View>
Step 11: Extend the main SAPUI5 controller

Edit the demo.controller.js file located under Workspace > sapui5ml-changepointdetection > webapp > controller and replace the existing code with the below code.

Then click on the Save Button button (or press CTRL+S).

sap.ui.define([
	"sap/ui/core/mvc/Controller",
	"sap/m/MessageToast",
	"sap/m/Image"
], function(Controller, MessageToast, Image) {
	"use strict";
	return Controller.extend("sapui5ml.controller.demo", {

		onGenerate: function(oControlEvent) {
			// get the current view
			var oView = this.getView();

			// start the busy indicator
			var oBusyIndicator = new sap.m.BusyDialog();
			// keep a reference in the view to close it later
			oBusyIndicator.open();

			// get a random number
			var rnd = Math.random();

			// get the expected data points count
			var iRowCount = oView.getModel("demo").getProperty("/options/numDataPoints");
			var iColCount = oView.getModel("demo").getProperty("/options/numDataSeries");

			// build the array to store the generated data
			var oSeriesCol = new Array(iColCount);
			var oSeriesRow = new Array(iRowCount);
			for (var iCol = 0; iCol < iColCount; iCol++) {
				var oCol = {
					"colid": iCol
				};
				oCol.rows = new Array(iRowCount);
				oSeriesCol[iCol] = (oCol);
			}
			for (var iRow = 0; iRow < iRowCount; iRow++) {
				var oRow = {
					"rowid": iRow
				};
				oRow.cols = new Array(iColCount);
				oSeriesRow[iRow] = (oRow);
			}

			// generate the data
			for (var iColValue = 0; iColValue < iColCount; iColValue++) {
				for (var iRowValue = 0; iRowValue < iRowCount; iRowValue++) {
					var value = (Math.cos((1 + iRowValue) * rnd * (1 + iColValue) * 20) * 100).toFixed(2);
					oSeriesCol[iColValue].rows[iRowValue] = (value);
					oSeriesRow[iRowValue].cols[iColValue] = (value);
				}
			}

			// save it in the model
			oView.getModel("demo").setProperty("/cols", oSeriesCol);
			oView.getModel("demo").setProperty("/rows", oSeriesRow);
			oView.getModel("demo").setProperty("/hasData", true);
			oBusyIndicator.close();
		},

		onExecute: function(oControlEvent) {
			// start the busy indicator
			var oBusyIndicator = new sap.m.BusyDialog();
			// keep a reference in the view to close it later
			oBusyIndicator.open();

			// get the current view
			var oView = this.getView();

			// get service settings and options
			var url = oView.getModel("demo").getProperty("/url");
			var APIKey = oView.getModel("demo").getProperty("/APIKey");
			var separator = oView.getModel("demo").getProperty("/options/separator");
			var seriesSeparator = oView.getModel("demo").getProperty("/options/series_separator");

			// generate the options as JSON stringify
			var options = {
				"separator": oView.getModel("demo").getProperty("/options/separator"),
				"series_separator": oView.getModel("demo").getProperty("/options/series_separator")
			};
			oView.getModel("demo").setProperty("/optionsJs", JSON.stringify(options));

			// convert the input data in the proper format using the separator & seriesSeparator options
			var text = "";
			var series = oView.getModel("demo").getProperty("/cols");
			for (var i = 0; i < series.length; i++) {
				text += series[i].rows.join(separator);
				if (i < series.length - 1) {
					text += seriesSeparator;
				}
			}

			// call the service and define call back methods
			$.ajax({
				headers: {
					'Accept': 'application/json',
					'APIKey': APIKey
				},
				url: url,
				type: "POST",
				data: $.param({
					"options": "{\"separator\":\"" + oView.getModel("demo").getProperty("/options/separator") + "\", \"series_separator\":\"" +
						oView.getModel("demo").getProperty("/options/series_separator") + "\"}",
					"texts": text
				}),
				async: false,
				success: function(data) {
					try {
						//get the result size
						var iRowCount = data.response[0].split(",").length;
						var iColCount = data.response.length;

						var oSeriesCol = new Array(iColCount);
						var oSeriesRow = new Array(iRowCount);

						// build the array to store the result data
						for (var iCol = 0; iCol < data.response.length; iCol++) {
							var oCol = {
								"colid": iCol
							};
							oCol.rows = new Array(iRowCount);
							oSeriesCol[iCol] = (oCol);
						}
						for (var iRow = 0; iRow < iRowCount; iRow++) {
							var oRow = {
								"rowid": iRow
							};
							oRow.cols = new Array(iColCount);
							oSeriesRow[iRow] = (oRow);
						}

						// get the reslt data
						for (var iColValue = 0; iColValue < data.response.length; iColValue++) {
							var row = data.response[0].split(",");
							for (var iRowValue = 0; iRowValue < row.length; iRowValue++) {
								oSeriesCol[iColValue].rows[iRowValue] = row[iRowValue];
								oSeriesRow[iRowValue].cols[iColValue] = row[iRowValue];
							}
						}

						// save it in the model
						oView.getModel("demo").setProperty("/resultCols", oSeriesCol);
						oView.getModel("demo").setProperty("/resultRows", oSeriesRow);
						oView.getModel("demo").setProperty("/hasResult", true);
						oBusyIndicator.close();
					} catch (err) {
						MessageToast.show("Caught - [ajax error] :" + err.message);
					}
					oBusyIndicator.close();
				},
				error: function(request, status, error) {
					MessageToast.show("Caught - [ajax error] :" + request.responseText);
					oBusyIndicator.close();
				}
			});
			oBusyIndicator.close();
		}
	});
});
Step 12: Test the application

Click on the Run icon Run Applications or press ALT+F5.

Click on the Generate Sample Data button to get a cosine data series generated, then click on Execute Service.

The service will be called, and the result displayed in a table.

You will notice that the longer the series is, the lower the probability will be. This is because the algorithm had learned the patter in place within our data.

Result
Solution: Project files

In case you are having problems when running the application, the complete project code can be found on the SAP Tutorial public GitHub repository.

However, this is not a repository you can clone and run the code.

You have to import the sapui5ml-imageclassifier directory content into your existing project directory.

Make sure you check the LICENSE before starting using its content.

Next Steps

Updated 11/30/2017

Time to Complete

20 Min

Beginner

Next Steps

Next
Back to top