An Angular 2+ .Net Core Application Consuming an Azure Machine Learning Model

Mar 19

Written by:
3/19/2017 7:18 AM  RssIcon

image

You can create an Angular 2+ application that consumes a web service created from an Azure Machine Learning experiment.

This article is part of a series of articles where we create an Azure Machine Learning experiment that predicts the price of a vehicle given parameters such as make, horsepower, and body style. It does that by creating a Model based on prices of previous vehicles. We then operationalize the model by creating a web service.

In this installment, we we will create an Angular 2+ application that consumes the web service. Finally we will create a programmatic method to update the model with new data gathered from our Angular 2+ application.

The series of articles are as follows:

  • An End-To-End Azure Machine Learning Studio Application  – Create an Azure Machine Learning experiment and operationalize it by creating a web service and consuming it using Microsoft Excel.
  • (This article) An Angular 2+ Application Consuming an Azure Machine Learning Model – Create an Angular 2+ application that consumes a web service created from an Azure Machine Learning experiment.
  • Retraining an Azure Machine Learning Application – This article covers the steps needed to update the Azure Machine Learning model with new data to improve its predictions.

The Application

image

The Angular 2+ application, that will be covered in this article, allows users to set the values that will be passed to the Azure Machine Learning web service (that was created in An End-To-End Azure Machine Learning Studio Application).

image

The user can click the Price Vehicle button to call the web service.

The Predicted Price, returned by the web service, will then display.

image

The user can also record the actual sale price (based on the currently selected values) by entering the value and clicking the Record Price button.

The data will be stored in the local database file.

image

Clicking the Export Data button will produce a .csv file that can then be used to retrain the Azure Machine Learning Model, thereby improving the future predictions.

Creating The Application

If you do not already have them, install the following prerequisites:

image

Log into the Azure Machine Learning Studio at: https://studio.azureml.net/.

image

Select Web Services, then the predictive web service that we created in the previous article.

image

1) Click the button to copy the API Key. Save it. We will need it later. This will be what we will use as the PrimaryKey value in the appsettings.json file.

2) Click the REQUEST/RESPONSE link that will take us to the sample code page.

image

When the Request Response API Documentation page appears, copy the Request POST web address. Save it. We will need it later.

This will be what we will use as the BaseAddress value in the appsettings.json file.

 image

Next, scroll down to the bottom of the page to see sample code that calls the web service.

This is what we will use at the base code for our application.

Create The .Net Core Application

image

Create a folder on your Microsoft Windows computer (this tutorial was created using Windows 10).

Note: Do not have any special characters in the folder name. For example, and exclamation mark (!) will break the JavaScript code.

image

You can type CMD and press Enter to switch to the command line (and still be in that directory).

image

If you have not already installed JavaScriptServices, install them by entering (and pressing Enter):

dotnet new --install Microsoft.AspNetCore.SpaTemplates::*
 
image
 
The screen will display indicating the templates now available.
 
image

Create the ASP.NET Core JavaScriptServices application by entering (and pressing Enter):

dotnet new angular

image

The application will be created.

Double-click on the *.csproj file to open it in Visual Studio 2017.

image

It will start restoring .Net dependencies and the node_modules (you can view the ../node_modules directory to see the items populated).

(Note: This can take 3-10 minutes or more)

image

Visual Studio will indicate when everything is complete.

image

Press Ctrl+F5 to Start Without Debugging.

image

The application will show.

Close the web browser for now.

Add PrimeNG

image

We will now install the free open source PrimeNG Angular 2 components.

image

Open the package.json file and add:

   "font-awesome": "^4.7.0",
   "primeng": "^2.0.0"

Save the file.

image

Open the webpack.config.vendor.js file and add:

    'font-awesome/css/font-awesome.css',
    'primeng/primeng',
    'primeng/resources/themes/omega/theme.css',
    'primeng/resources/primeng.min.css',

 

image

Also, in rules/test, add:

    |gif

Save the file.

image

At this time, PrimeNg does not support pre-rendering so in ..\Views\Home\Index.cshtml, change:

<app asp-prerender-module="ClientApp/dist/main-server">Loading...</app>

to:

<app>Loading...</app>

Save the file.

image

Open ..\Views\Shared\_Layout.cshtml and replace all the code with the following code:

 

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - AzureMLCore</title>
    <base href="/" />
    <link rel="stylesheet" href="~/dist/vendor.css" asp-append-version="true" />
</head>
<body>
    <div class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle"
                        data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                @Html.ActionLink(".Net Core Angular 2 Azure Machine Learning App",
               "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
            </div>
        </div>
    </div>
    <div class="container body-content">
        @RenderBody()
    </div>
    <hr />
    @RenderSection("scripts", required: false)
</body>
</html>

image

We altered the webpack.config.vendor.js file (to add PrimeNg and Font Awesome) but the compiled files that it creates (that are used at runtime) are not updated by the normal build process. We have to run its configuration manually whenever we alter it.

In a command prompt, at the project root, run:

webpack --config webpack.config.vendor.js

(Note: If you don’t already have the webpack tool installed (for example you get an error when you run the code above), you’ll need to run: npm install -g webpack first)

Add The Database

image

Right-click on the project node, select Add then New Scaffolded Item…

image

Select Full Dependencies then click Add.

image

The Scaffolding will run.

image

Close the ScaffoldingReadMe.txt file that opens.

We will perform the needed changes it describes in the next steps.

image

Right-click on the project node and select Edit AzureMLCore.csproj.

image

Add:

<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="1.0.0" />

Save and close the file.

image

From the menu in Visual Studio, select Tools then Connect to Database…

image

  • Ensure that Microsoft SQL Server Database File (SqlClient) is selected for Data source (use the Change button if not)
  • Enter AzureMLData.mdf for the database name and indicate that it is in a folder called Data that is under your project root (the file does not exist, it will be created by Visual Studio)
  • Click OK

image

Click Yes to create the database.

image

In the Server Explorer window in Visual Studio (you can get to it from the Visual Studio menu using View then Server Explorer), the database will show.

Expand it, right-click on the Tables node and select Add New Table.

image

Enter the following script and click the Update button:

 

CREATE TABLE [dbo].[Vehicles] (
    [Id]                INT           IDENTITY (1, 1) NOT NULL,
    [symboling]         NVARCHAR (50) NULL,
    [normalized-losses] NVARCHAR (50) NULL,
    [make]              NVARCHAR (50) NULL,
    [fuel-type]         NVARCHAR (50) NULL,
    [aspiration]        NVARCHAR (50) NULL,
    [num-of-doors]      NVARCHAR (50) NULL,
    [body-style]        NVARCHAR (50) NULL,
    [drive-wheels]      NVARCHAR (50) NULL,
    [engine-location]   NVARCHAR (50) NULL,
    [wheel-base]        NVARCHAR (50) NULL,
    [length]            NVARCHAR (50) NULL,
    [width]             NVARCHAR (50) NULL,
    [height]            NVARCHAR (50) NULL,
    [curb-weight]       NVARCHAR (50) NULL,
    [engine-type]       NVARCHAR (50) NULL,
    [num-of-cylinders]  NVARCHAR (50) NULL,
    [engine-size]       NVARCHAR (50) NULL,
    [fuel-system]       NVARCHAR (50) NULL,
    [bore]              NVARCHAR (50) NULL,
    [stroke]            NVARCHAR (50) NULL,
    [compression-ratio] NVARCHAR (50) NULL,
    [horsepower]        NVARCHAR (50) NULL,
    [peak-rpm]          NVARCHAR (50) NULL,
    [city-mpg]          NVARCHAR (50) NULL,
    [highway-mpg]       NVARCHAR (50) NULL,
    [price]             NVARCHAR (50) NULL,
    PRIMARY KEY CLUSTERED ([Id] ASC)
);

 

image

Click Update Database.

image

The Data Tools Operations window will indicate when the update is complete.

image

In the Server Explorer window, right-click on the database and select Refresh.

You will see the Vehicles table.

Always Close the database connection when done working with it to prevent locking.

image

In the Solution Explorer, right-click on the project node and select Manage NuGet Packages.

image

Search for and install: Microsoft.EntityFrameworkCore.Tools.

image

Open the NuGet Package Manager Console.

image

Enter:

Scaffold-DbContext "Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=C:\TEMP\AzureMLCore\Data\AzureMLData.mdf;Integrated Security=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models

and press Enter.

(update the connection string above to point to the location of the .mdf file in the project)

image

The scaffolded files will appear in the Models directory.

Note: If you get DBContext cannot be found errors (red squiggly lines in the Visual Studio text editor, simply close Visual Studio and re-open it.

image

Rename the DataContext file and the class to AzureMLDataContext.

image

Next, we follow the directions at this link: ASP.NET Core - Existing Database.

We remove the OnConfiguring method.

We add the following constructor to the class:

 

        public AzureMLDataContext(DbContextOptions<AzureMLDataContext> options) : 
        base(options) { }

 

image

We add the following using statements to Startup.cs:

using AzureMLCore.Models;
using Microsoft.EntityFrameworkCore;

 

image

We add the following code to the ConfigureServices section to configure the database setting:

 

    services.AddDbContext<AzureMLDataContext>(
        options => options.UseSqlServer(
            Configuration.GetConnectionString("AzureMLDataDatabase")));

 

image

Add the following database setting to the appsettings.json file:

 

"ConnectionStrings": {  
 "AzureMLDataDatabase": "Data Source=(LocalDB)\\MSSQLLocalDB;AttachDbFilename=
  C:\\Temp\\AzureMLCore\\Data\\AzureMLData.mdf;Integrated Security=True;" 
},

 

(Note: The AzureMLDataDatabase value needs to be on a single line – see the source code for the exact setting)

Create Code To Call Azure Machine Learning Web Service

We will now create the code that will call the Azure Machine Learning web service (that was created in An End-To-End Azure Machine Learning Studio Application), passing the parameters (such as such as make, horsepower, and body style) and retrieve a response by the underlying Model in the form of a predicted price.

image

First, we need to create a class that will pass the settings (PrimaryKey and BaseAddress) from the app settings file to the code we will create.

Create a file called AzureMLSettings.cs using the following code:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace AzureMLCore.Models
{
    public class AzureMLSettings
    {
        public string PrimaryKey { get; set; }
        public string BaseAddress { get; set; }
    }
}

 

image

Add  the following code to the ConfigureServices method in the Startup.cs file:

 

   // Get the AzureMLSettings
            services.Configure<AzureMLSettings>(
                Configuration.GetSection("AzureMLSettings"));

 

image

Next, add the settings to the appsettings.json file, replacing {{ API Key }} and {{ Base Address }} with the values you saved earlier:

 

  "AzureMLSettings": {
    "PrimaryKey": "{{ API Key }}",
    "BaseAddress": "{{ Base Address }}"
  },

 

The values will be retrieved and passed to the web service using the code we will create in the following two steps.

image

Create a file called AzureMLParameter.cs using the following code:

 

using System.ComponentModel.DataAnnotations;
namespace AzureMLCore.Models
{
    public class AzureMLParameter
    {
        [Key]
        public int Id { get; set; }
        public string symboling { get; set; }
        public string normalizedlosses { get; set; }
        public string make { get; set; }
        public string fueltype { get; set; }
        public string aspiration { get; set; }
        public string numofdoors { get; set; }
        public string bodystyle { get; set; }
        public string drivewheels { get; set; }
        public string enginelocation { get; set; }
        public string wheelbase { get; set; }
        public string length { get; set; }
        public string width { get; set; }
        public string height { get; set; }
        public string curbweight { get; set; }
        public string enginetype { get; set; }
        public string numofcylinders { get; set; }
        public string enginesize { get; set; }
        public string fuelsystem { get; set; }
        public string bore { get; set; }
        public string stroke { get; set; }
        public string compressionratio { get; set; }
        public string horsepower { get; set; }
        public string peakrpm { get; set; }
        public string citympg { get; set; }
        public string highwaympg { get; set; }
        public string price { get; set; }
        public string Scoredlabels { get; set; }
    }
}

 

image

Finally, to we will create the controller class, that will be called by the Angular code, that will call the web service.

Create a file called AzureMLParameterController.cs using the following code:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using AzureMLCore.Models;
using Newtonsoft.Json;
using System.Net.Http.Headers;
using System.Net.Http;
using Microsoft.Extensions.Options;
using System.Text;
namespace AngularAzureMLApp.Controllers
{
    [Produces("application/json")]
    [Route("api/AzureMLParameter")]
    public class AzureMLParameterController : Controller
    {
        // Global values to hold the PrimaryKey and BaseAddress
        private string _PrimaryKey;
        private string _BaseAddress;
        // This is the default contructor for the class
        // We will inject the AzureMLSettings when the class
        // is instantiated
        public AzureMLParameterController(
            IOptions<AzureMLSettings> AzureMLSettings)
        {
            // Set the values for PrimaryKey and BaseAddress
            _PrimaryKey = AzureMLSettings.Value.PrimaryKey;
            _BaseAddress = AzureMLSettings.Value.BaseAddress;
        }
        #region public IHttpActionResult Post(AzureMLParameter paramAzureMLParameter)
        // POST: odata/AzureMLParameter
        [HttpPost]
        public async Task<IActionResult> Post([FromBody] AzureMLParameter paramAzureMLParameter)
        {
            // Call the web service
            string ScoredLabels = await InvokeRequestResponseService(paramAzureMLParameter);
            paramAzureMLParameter.Scoredlabels = ScoredLabels;
            // Return the result
            return Ok(paramAzureMLParameter);
        }
        #endregion
        // Helpers
        #region async Task<string> InvokeRequestResponseService(AzureMLParameter objAzureMLParameter)
        async Task<string> InvokeRequestResponseService(AzureMLParameter objAzureMLParameter)
        {
            string strResponse = "";
            try
            {
                // Get the Global values for PrimaryKey and BaseAddress
                string PrimaryKey = _PrimaryKey;
                string BaseAddress = _BaseAddress;
                using (var client = new HttpClient())
                {
                    // We will create a request to the web service
                    // Note: The only columns that are actually being used by the Model and that need to be set:
                    // highwaympg, citympg, horsepower, fuelsystem, enginesize, numofcylinders
                    // enginetype, bodystyle, make, curbweight, wheelbase, numofdoors, fueltype
                    
                    var scoreRequest = new
                    {
                        Inputs = new Dictionary<string, StringTable>() {
                        {
                            "input1",
                            new StringTable()
                            {
                                ColumnNames = new string[] {
                                    "symboling", "normalized-losses","make","fuel-type","aspiration",
                                    "num-of-doors","body-style","drive-wheels","engine-location",
                                    "wheel-base","length","width", "height","curb-weight","engine-type",
                                    "num-of-cylinders","engine-size","fuel-system","bore","stroke",
                                    "compression-ratio","horsepower","peak-rpm","city-mpg","highway-mpg","price"},
                                Values = new string[,] {
                                    {
                                        objAzureMLParameter.symboling,
                                        objAzureMLParameter.normalizedlosses,
                                        objAzureMLParameter.make,
                                        objAzureMLParameter.fueltype,
                                        objAzureMLParameter.aspiration,
                                        objAzureMLParameter.numofdoors,
                                        objAzureMLParameter.bodystyle,
                                        objAzureMLParameter.drivewheels,
                                        objAzureMLParameter.enginelocation,
                                        objAzureMLParameter.wheelbase,
                                        objAzureMLParameter.length,
                                        objAzureMLParameter.width,
                                        objAzureMLParameter.height,
                                        objAzureMLParameter.curbweight,
                                        objAzureMLParameter.enginetype,
                                        objAzureMLParameter.numofcylinders,
                                        objAzureMLParameter.enginesize,
                                        objAzureMLParameter.fuelsystem,
                                        objAzureMLParameter.bore,
                                        objAzureMLParameter.stroke,
                                        objAzureMLParameter.compressionratio,
                                        objAzureMLParameter.horsepower,
                                        objAzureMLParameter.peakrpm,
                                        objAzureMLParameter.citympg,
                                        objAzureMLParameter.highwaympg,
                                        objAzureMLParameter.price
                                    }
                                }
                            }
                        },
                    },
                        GlobalParameters = new Dictionary<string, string>()
                        {
                        }
                    };
                    // Create an authorization value
                    client.DefaultRequestHeaders.Authorization = 
                        new AuthenticationHeaderValue("Bearer", PrimaryKey);
                    // Call the web service
                    HttpResponseMessage response = 
                        await client.PostAsync(new Uri(BaseAddress), 
                        new StringContent(JsonConvert.SerializeObject(scoreRequest), 
                        Encoding.UTF8, "application/json"));
                    if (response.IsSuccessStatusCode)
                    {
                        // The call was a success -- get the response
                        string result = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
                        Rootobject obj = JsonConvert.DeserializeObject<Rootobject>(result);
                        // The predicted price is in the last element
                        int intLastElement = (obj.Results.output1.value.Values[0].Count() - 1);
                        var Price = obj.Results.output1.value.Values[0][intLastElement];
                        // Convert the value to a currency string
                        strResponse = Convert.ToDecimal(Price).ToString("C");
                    }
                    else
                    {
                        // The call returned an error
                        var strContent = await response.Content.ReadAsStringAsync();
                        strResponse = string.Format("The request failed with status code: {0}", 
                            response.StatusCode);
                        // Add the headers - they include the request ID and the timestamp, 
                        // which are useful for debugging the failure
                        strResponse = strResponse + " " + response.Headers.ToString();
                        strResponse = strResponse + " " + strContent;
                    }
                }
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
            return strResponse;
        }
        #endregion
        #region public class StringTable
        public class StringTable
        {
            public string[] ColumnNames { get; set; }
            public string[,] Values { get; set; }
        }
        #endregion
        #region JSON Result Classes
        public class Rootobject
        {
            public Results Results { get; set; }
        }
        public class Results
        {
            public Output1 output1 { get; set; }
        }
        public class Output1
        {
            public string type { get; set; }
            public Value value { get; set; }
        }
        public class Value
        {
            public string[] ColumnNames { get; set; }
            public string[] ColumnTypes { get; set; }
            public string[][] Values { get; set; }
        }
        #endregion
    }
}

 

Create The Angular Application

Let’s build the entire Angular application from scratch.

Highlight all the files under: …\ClientApp\app\

image

…and delete them.

image

The entry point for the custom code for the application is in app.module.ts.

Create the file using the following code:

 

import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { UniversalModule } from 'angular2-universal';
import { AppComponent } from './app.component'
import { BrowserModule } from '@angular/platform-browser';
import { HttpModule } from '@angular/http';
import { FormsModule } from '@angular/forms';
import {
    InputTextModule,
    DropdownModule,
    ButtonModule,
    FieldsetModule,
    TreeModule,
    TreeNode,
    SelectItem,
    TabMenuModule,
    MenuItem,
    TabViewModule,
    PanelModule,
    InputSwitchModule,
    PasswordModule
} from 'primeng/primeng';
import { PriceVehicleComponent } from './priceVehicle/priceVehicle.component';
import { PriceVehicleService } from './priceVehicle/priceVehicle.service';
@NgModule({
    bootstrap: [ AppComponent ],
    declarations: [
        AppComponent,
        PriceVehicleComponent,
    ],
    imports: [
        UniversalModule, // Must be first import. 
        BrowserModule,
        HttpModule,
        FormsModule,
        InputTextModule,
        TreeModule,
        DropdownModule,
        ButtonModule,
        FieldsetModule,
        TabMenuModule,
        TabViewModule,
        PanelModule,
        InputSwitchModule,
        PasswordModule
    ],
    providers: [
        PriceVehicleService
    ]
})
export class AppModule {
}

(note: you will see red swiggly lines in the Visual Studio editor because there are files being referenced that we have not created yet)

image

app.module, in it’s bootstrap setting, it indicates that AppComponent is the first component to be loaded.

To implement it, create the file: app.component.ts using the following code:

 

import { Component } from '@angular/core';
@Component({
    selector: 'app',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
}

image

app.component.ts specifies app.component.html for templateUrl and app.component.css for styleUrls.

Create those files and implement them using the following code:

app.component.html:

 

<priceVehicle-form>Loading...</priceVehicle-form>

 

app.component.css:

 

@media (max-width: 767px) {
    /* On small screens, the nav menu spans the full width of the screen. 
    Leave a space for it. */
    .body-content {
        padding-top: 50px;
    }
}

image

The primary code for the application will be placed in the priceVehicle folder.

Create It.

image

We will create an interface that will allow us to communicate with the AzureMLParameterController.cs file using a strongly typed class.

Create a file: azureMLParameter.ts using the following code:

 

/* Defines the AzureMLParameter entity */
export interface IAzureMLParameter {
    id: number;
    symboling: string;
    normalizedlosses: string;
    make: string;
    fueltype: string;
    aspiration: string;
    numofdoors: string;
    bodystyle: string;
    drivewheels: string;
    enginelocation: string;
    wheelbase: string;
    length: string;
    width: string;
    height: string;
    curbweight: string;
    enginetype: string;
    numofcylinders: string;
    enginesize: string;
    fuelsystem: string;
    bore: string;
    stroke: string;
    compressionratio: string;
    horsepower: string;
    peakrpm: string;
    citympg: string;
    highwaympg: string;
    price: string; // price we submit
    scoredlabels: string; // prediction we get back    
}

image

We will create a service that will communicate with the C# controller code, both to get price predictions and to save actual prices to the database.

Create a file: priceVehicle.service.ts using the following code:

 

import { Injectable } from '@angular/core';
import { Http, Response, RequestOptions, Request, RequestMethod, Headers } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import { IAzureMLParameter } from './azureMLParameter';
@Injectable()
export class PriceVehicleService {
    constructor(private _http: Http) { }
    priceVehicle(AzureMLParameter: IAzureMLParameter): Observable<IAzureMLParameter> {
        var _Url = 'api/AzureMLParameter';
        AzureMLParameter.price = "0";
        // This is a Post so we have to pass Headers
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });
        // Make the Angular 2 Post
        // passing the AzureMLParameter
        return this._http.post(_Url, JSON.stringify(AzureMLParameter), options)
            .map((response: Response) => <IAzureMLParameter>response.json())
            .catch(this.handleError);
    }
    recordPrice(AzureMLParameter: IAzureMLParameter): Observable<IAzureMLParameter> {
        var _Url = 'api/Vehicles';
        // This is a Post so we have to pass Headers
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });
        // Make the Angular 2 Post
        // passing the AzureMLParameter
        return this._http.post(_Url, JSON.stringify(AzureMLParameter), options)
            .map((response: Response) => <IAzureMLParameter>response.json())
            .catch(this.handleError);
    }
    // Utility
    private handleError(error: Response) {
        // in a real world app, we may send the server to some remote logging infrastructure
        // instead of just logging it to the console
        console.error(error);
        return Observable.throw(error.json().error || 'Server error');
    }
}

 

(Note: we have implemented code to save recorded prices, but we have not added the server-side C# code yet)

 

image

Next we will add the actual markup for the application.

Create a file: priceVehicle.component.html using the following code:

 

<div style="width:800px">
    <br /><br /><br />
    <div>
        <span style="background-color: #FFFF00">{{errorMessage}}</span>
    </div>    
    <h3 class="first">Price A Vehicle</h3>
    <button pButton type="button" label="Export Data" (click)="getVehicles()" class="ui-button-secondary"></button>
    <div class="ui-grid ui-grid-responsive ui-grid-pad">
        <div class="ui-grid-row">
            <div class="ui-grid-col-6">
                <div class="ui-grid-col-4">
                    Make:
                </div>
                <div class="ui-grid-col-8">
                    <p-dropdown id="Make"
                                [options]="makeDropdown"
                                [(ngModel)]="AzureMLParameter.make"
                                [style]="{'width':'150px'}"></p-dropdown>
                </div>
            </div>
            <div class="ui-grid-col-6">
                <div class="ui-grid-col-4">
                    Fuel type:
                </div>
                <div class="ui-grid-col-8">
                    <p-dropdown id="FuelType"
                                [options]="fueltypeDropdown"
                                [(ngModel)]="AzureMLParameter.fueltype"
                                [style]="{'width':'150px'}"></p-dropdown>
                </div>
            </div>
        </div>
        <div class="ui-grid-row">
            <div class="ui-grid-col-6">
                <div class="ui-grid-col-4">
                    Num of doors:
                </div>
                <div class="ui-grid-col-8">
                    <p-dropdown id="NumOfDoors"
                                [options]="numofdoorsDropdown"
                                [(ngModel)]="AzureMLParameter.numofdoors"
                                [style]="{'width':'150px'}"></p-dropdown>
                </div>
            </div>
            <div class="ui-grid-col-6">
                <div class="ui-grid-col-4">
                    Body style:
                </div>
                <div class="ui-grid-col-8">
                    <p-dropdown id="BodyStyle"
                                [options]="bodystyleDropdown"
                                [(ngModel)]="AzureMLParameter.bodystyle"
                                [style]="{'width':'150px'}"></p-dropdown>
                </div>
            </div>
        </div>
        <div class="ui-grid-row">
            <div class="ui-grid-col-6">
                <div class="ui-grid-col-4">
                    Wheel base:
                </div>
                <div class="ui-grid-col-8">
                    <input type="text" id="WheelBase" pInputText [(ngModel)]="AzureMLParameter.wheelbase" />
                </div>
            </div>
            <div class="ui-grid-col-6">
                <div class="ui-grid-col-4">
                    Curb weight:
                </div>
                <div class="ui-grid-col-8">
                    <input type="text" id="CurbWeight" pInputText [(ngModel)]="AzureMLParameter.curbweight" />
                </div>
            </div>
        </div>
        <div class="ui-grid-row">
            <div class="ui-grid-col-6">
                <div class="ui-grid-col-4">
                    Engine type:
                </div>
                <div class="ui-grid-col-8">
                    <p-dropdown id="EngineType"
                                [options]="enginetypeDropdown"
                                [(ngModel)]="AzureMLParameter.enginetype"
                                [style]="{'width':'150px'}"></p-dropdown>
                </div>
            </div>
            <div class="ui-grid-col-6">
                <div class="ui-grid-col-4">
                    Num of cylinders:
                </div>
                <div class="ui-grid-col-8">
                    <p-dropdown id="NumOfCylinders"
                                [options]="numofcylindersDropdown"
                                [(ngModel)]="AzureMLParameter.numofcylinders"
                                [style]="{'width':'150px'}"></p-dropdown>
                </div>
            </div>
        </div>
        <div class="ui-grid-row">
            <div class="ui-grid-col-6">
                <div class="ui-grid-col-4">
                    Engine size:
                </div>
                <div class="ui-grid-col-8">
                    <input type="text" id="EngineSize" pInputText [(ngModel)]="AzureMLParameter.enginesize" />
                </div>
            </div>
            <div class="ui-grid-col-6">
                <div class="ui-grid-col-4">
                    Fuel system:
                </div>
                <div class="ui-grid-col-8">
                    <p-dropdown id="FuelSystem"
                                [options]="fuelsystemDropdown"
                                [(ngModel)]="AzureMLParameter.fuelsystem"
                                [style]="{'width':'150px'}"></p-dropdown>
                </div>
            </div>
        </div>
        <div class="ui-grid-row">
            <div class="ui-grid-col-6">
                <div class="ui-grid-col-4">
                    Horsepower:
                </div>
                <div class="ui-grid-col-8">
                    <input type="text" id="Horsepower" pInputText [(ngModel)]="AzureMLParameter.horsepower" />
                </div>
            </div>
            <div class="ui-grid-col-6">
                <div class="ui-grid-col-4">
                    City mpg:
                </div>
                <div class="ui-grid-col-8">
                    <input type="text" id="CityMpg" pInputText [(ngModel)]="AzureMLParameter.citympg" />
                </div>
            </div>
        </div>
        <div class="ui-grid-row">
            <div class="ui-grid-col-6">
                <div class="ui-grid-col-4">
                    Highway mpg:
                </div>
                <div class="ui-grid-col-8">
                    <input type="text" id="HighwayMpg" pInputText [(ngModel)]="AzureMLParameter.highwaympg" />
                </div>
            </div>
        </div>
    </div>
    <div class="ui-grid ui-grid-responsive ui-grid-pad">
        <div class="ui-grid-row">
            <div class="ui-grid-col-6">
                <div class="ui-grid-col-4">
                    <button pButton type="button" label="Price Vehicle" (click)="priceVehicle()"></button>
                </div>
                <div class="ui-grid-col-8">
                    <p>Predicted Price: <b>{{AzureMLParameter.scoredlabels}}</b></p>
                </div>
            </div>
            <div class="ui-grid-col-6">
                <div class="ui-grid-col-4">
                    <button pButton type="button" label="Record Price" (click)="recordPrice()"></button>
                </div>
                <div class="ui-grid-col-8">
                    <input type="text" id="Price" pInputText [(ngModel)]="AzureMLParameter.price" />
                </div>
            </div>
        </div>
    </div>
</div>

 

image

Finally, add the primary custom code for the application by creating a file called priceVehicle.component.ts using the following code:

 

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs/Subscription';
import {
    SelectItem,
    InputTextModule,
    ButtonModule,
    DropdownModule
}
    from 'primeng/primeng';
import { IAzureMLParameter } from './azureMLParameter';
import { PriceVehicleService } from './priceVehicle.service';
@Component({
    selector: 'priceVehicle-form',
    templateUrl: './priceVehicle.component.html'
})
export class PriceVehicleComponent implements OnInit {
    errorMessage: string;
    AzureMLParameter: IAzureMLParameter;
    makeDropdown: SelectItem[] = [];
    fueltypeDropdown: SelectItem[] = [];
    numofdoorsDropdown: SelectItem[] = [];
    bodystyleDropdown: SelectItem[] = [];
    enginetypeDropdown: SelectItem[] = [];
    numofcylindersDropdown: SelectItem[] = [];
    fuelsystemDropdown: SelectItem[] = [];
    // Register the service
    constructor(private _PriceVehicleService: PriceVehicleService) { }
    ngOnInit(): void {
        // Set default values for vehicle parameters
        this.setDefaultValues();
        // Fill the dropdowns
        this.fillDropdowns();
    }
    priceVehicle() {
        this.errorMessage = "";
        // Get the predicted price for a vehicle
        // Call the service
        this._PriceVehicleService.priceVehicle(this.AzureMLParameter)
            .subscribe(
            AzureMLParameter => {
                this.AzureMLParameter = AzureMLParameter
            },
            error => this.errorMessage = <any>error);
    }
    recordPrice() {
        this.errorMessage = "";
        // Save the price of a vehicle to the database
        // Call the service
        this._PriceVehicleService.recordPrice(this.AzureMLParameter)
            .subscribe(
            AzureMLParameter => {
                alert('Recorded: ' + AzureMLParameter.id);
            },
            error => this.errorMessage = <any>error);
    }
    getVehicles() {
        window.location.href = 'Home/DownloadCSV';
    }
    // Utility
    setDefaultValues() {
        // Note: The only columns that are actually being used by the Model and that need to be set:
        // highwaympg, citympg, horsepower, fuelsystem, enginesize, numofcylinders
        // enginetype, bodystyle, make, curbweight, wheelbase, numofdoors, fueltype
        let NewAzureMLParameter: IAzureMLParameter = {
            id: 0,
            symboling: "0",
            normalizedlosses: "1",
            make: "toyota",
            fueltype: "gas",
            aspiration: " ",
            numofdoors: "four",
            bodystyle: "sedan",
            drivewheels: " ",
            enginelocation: " ",
            wheelbase: "99.4",
            length: "0",
            width: "0",
            height: "0",
            curbweight: "2824",
            enginetype: "ohc",
            numofcylinders: "five",
            enginesize: "136",
            fuelsystem: "mpfi",
            bore: "0",
            stroke: "0",
            compressionratio: "0",
            horsepower: "115",
            peakrpm: "0",
            citympg: "18",
            highwaympg: "22",
            price: "0",
            scoredlabels: " "
        }
        this.AzureMLParameter = NewAzureMLParameter;
    }
    fillDropdowns() {
        // makeDropdown
        this.makeDropdown.push({ label: 'toyota', value: 'toyota' });
        this.makeDropdown.push({ label: 'nissan', value: 'nissan' });
        this.makeDropdown.push({ label: 'mazda', value: 'mazda' });
        this.makeDropdown.push({ label: 'mitsubishi', value: 'mitsubishi' });
        this.makeDropdown.push({ label: 'honda', value: 'honda' });
        this.makeDropdown.push({ label: 'volkswagen', value: 'volkswagen' });
        this.makeDropdown.push({ label: 'subaru', value: 'subaru' });
        this.makeDropdown.push({ label: 'volvo', value: 'volvo' });
        this.makeDropdown.push({ label: 'peugot', value: 'peugot' });
        this.makeDropdown.push({ label: 'dodge', value: 'dodge' });
        // fueltypeDropdown
        this.fueltypeDropdown.push({ label: 'gas', value: 'gas' });
        this.fueltypeDropdown.push({ label: 'diesel', value: 'diesel' });
        // numofdoorsDropdown
        this.numofdoorsDropdown.push({ label: 'four', value: 'four' });
        this.numofdoorsDropdown.push({ label: 'two', value: 'two' });
        // bodystyleDropdown
        this.bodystyleDropdown.push({ label: 'sedan', value: 'sedan' });
        this.bodystyleDropdown.push({ label: 'hatchback', value: 'hatchback' });
        this.bodystyleDropdown.push({ label: 'wagon', value: 'wagon' });
        this.bodystyleDropdown.push({ label: 'hardtop', value: 'hardtop' });
        this.bodystyleDropdown.push({ label: 'convertible', value: 'convertible' });
        // enginetypeDropdown
        this.enginetypeDropdown.push({ label: 'ohc', value: 'ohc' });
        this.enginetypeDropdown.push({ label: 'ohcf', value: 'ohcf' });
        this.enginetypeDropdown.push({ label: 'ohcv', value: 'ohcv' });
        this.enginetypeDropdown.push({ label: 'dohc', value: 'dohc' });
        this.enginetypeDropdown.push({ label: 'l', value: 'l' });
        this.enginetypeDropdown.push({ label: 'rotor', value: 'rotor' });
        this.enginetypeDropdown.push({ label: 'dohcv', value: 'dohcv' });
        // numofcylindersDropdown
        this.numofcylindersDropdown.push({ label: 'four', value: 'four' });
        this.numofcylindersDropdown.push({ label: 'six', value: 'six' });
        this.numofcylindersDropdown.push({ label: 'five', value: 'five' });
        this.numofcylindersDropdown.push({ label: 'eight', value: 'eight' });
        this.numofcylindersDropdown.push({ label: 'two', value: 'two' });
        this.numofcylindersDropdown.push({ label: 'three', value: 'three' });
        this.numofcylindersDropdown.push({ label: 'twelve', value: 'twelve' });
        // fuelsystemDropdown
        this.fuelsystemDropdown.push({ label: 'mpfi', value: 'mpfi' });
        this.fuelsystemDropdown.push({ label: '2bbl', value: '2bbl' });
        this.fuelsystemDropdown.push({ label: 'idi', value: 'idi' });
        this.fuelsystemDropdown.push({ label: '1bbl', value: '1bbl' });
        this.fuelsystemDropdown.push({ label: 'spdi', value: 'spdi' });
        this.fuelsystemDropdown.push({ label: '4bbl', value: '4bbl' });
        this.fuelsystemDropdown.push({ label: 'mfi', value: 'mfi' });
        this.fuelsystemDropdown.push({ label: 'spfi', value: 'spfi' });
    }
}

 

image

Run the application.

image

The application will show, and we can click the Price Vehicle button to see the predicted price for the options selected on the form.

Saving Data

image

To save data, we need to add a server-side method.

Create a file at: ..\Controllers\VehiclesController.cs using the following code:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using AzureMLCore.Models;
namespace AzureMLCore.Controllers
{
    [Produces("application/json")]
    [Route("api/Vehicles")]
    public class VehiclesController : Controller
    {
        private readonly AzureMLDataContext _context;
        public VehiclesController(AzureMLDataContext context)
        {
            _context = context;
        }       
        // POST: api/Vehicles
        [HttpPost]
        public IActionResult PostVehicles([FromBody] Vehicles vehicles)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
            _context.Vehicles.Add(vehicles);
            _context.SaveChanges();
            return CreatedAtAction("GetVehicles", new { id = vehicles.Id }, vehicles);
        }
    }
}

 

image

Rebuild the application and re-launch or refresh it in the web browser.

We can now enter a recorded price and save it to the database.

Viewing Data

image

We can export the recorded prices.

We will use this data in the next article to retrain the Model used by the web service to make price predictions.

image

Open the HomeController.cs file and add the following code to the class:

 

private readonly Models.AzureMLDataContext _context;
        public HomeController(Models.AzureMLDataContext context)
        {
            _context = context;
        }
        public FileContentResult DownloadCSV()
        {
            // Break up array so the code formats properly for the blog post article
            string strArray1 = "{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},";
            string strArray2 = "{11},{12},{13},{14},{15},{16},{17},{18},{19},";
            string strArray3 = "{20},{21},{22},{23},{24},{25}\n";
            string strFullArray = strArray1 + strArray2 + strArray3;
            // This returns all vehicles in the database
            var result = (from vehicle in _context.Vehicles
                          select new Models.AzureMLParameter
                          {
                              Id = vehicle.Id,
                              symboling = vehicle.Symboling,
                              normalizedlosses = vehicle.NormalizedLosses,
                              make = vehicle.Make,
                              fueltype = vehicle.FuelType,
                              aspiration = vehicle.Aspiration,
                              numofdoors = vehicle.NumOfDoors,
                              bodystyle = vehicle.BodyStyle,
                              drivewheels = vehicle.DriveWheels,
                              enginelocation = vehicle.EngineLocation,
                              wheelbase = vehicle.WheelBase,
                              length = vehicle.Length,
                              width = vehicle.Width,
                              height = vehicle.Height,
                              curbweight = vehicle.CurbWeight,
                              enginetype = vehicle.EngineType,
                              numofcylinders = vehicle.NumOfCylinders,
                              enginesize = vehicle.EngineSize,
                              fuelsystem = vehicle.FuelSystem,
                              bore = vehicle.Bore,
                              stroke = vehicle.Stroke,
                              compressionratio = vehicle.CompressionRatio,
                              horsepower = vehicle.Horsepower,
                              peakrpm = vehicle.PeakRpm,
                              citympg = vehicle.CityMpg,
                              highwaympg = vehicle.HighwayMpg,
                              price = vehicle.Price
                          }).ToList();
            // Write headers
            string csv = string.Format(strFullArray,
                                        "symboling",
                                        "normalized-losses",
                                        "make",
                                        "fuel-type",
                                        "aspiration",
                                        "num-of-doors",
                                        "body-style",
                                        "drive-wheels",
                                        "engine-location",
                                        "wheel-base",
                                        "length",
                                        "width",
                                        "height",
                                        "curb-weight",
                                        "engine-type",
                                        "num-of-cylinders",
                                        "engine-size",
                                        "fuel-system",
                                        "bore",
                                        "stroke",
                                        "compression-ratio",
                                        "horsepower",
                                        "peak-rpm",
                                        "city-mpg",
                                        "highway-mpg",
                                        "price");
            // Write data
            csv = csv + string.Concat(from vehicle in result
                                      select string.Format(strFullArray,
                                      vehicle.symboling,
                                      vehicle.normalizedlosses,
                                      vehicle.make,
                                      vehicle.fueltype,
                                      vehicle.aspiration,
                                      vehicle.numofdoors,
                                      vehicle.bodystyle,
                                      vehicle.drivewheels,
                                      vehicle.enginelocation,
                                      vehicle.wheelbase,
                                      vehicle.length,
                                      vehicle.width,
                                      vehicle.height,
                                      vehicle.curbweight,
                                      vehicle.enginetype,
                                      vehicle.numofcylinders,
                                      vehicle.enginesize,
                                      vehicle.fuelsystem,
                                      vehicle.bore,
                                      vehicle.stroke,
                                      vehicle.compressionratio,
                                      vehicle.horsepower,
                                      vehicle.peakrpm,
                                      vehicle.citympg,
                                      vehicle.highwaympg,
                                      vehicle.price
                                      ));
            return File(new System.Text.UTF8Encoding().GetBytes(csv), "text/csv", "vehicles.csv");
        }

 

 

Links

 

Download

You can download the code from the Download page