AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX Blogs
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 02.12.2018, 15:11   #1  
Blog bot is offline
Blog bot
Участник
 
25,475 / 846 (79) +++++++
Регистрация: 28.10.2006
gideonvos: R Web API from Dynamics 365 FinOps
Источник: https://gideonvos.wordpress.com/2018...cs-365-finops/
==============

Microsoft gives us a fair number of options to seamlessly connect machine learning models to our production code, and I honestly love using them all. AzureML is fantastic for many use cases, and with the Data Factory, Databricks and Data Lakes combo virtually every possible scenario can be covered really nicely.

Except of course if the model you need to use is hosted by a 3rd party which does not support any of these services. Then again, you might want to quickly test a few models first in a POC context before committing to “productizing” these into AzureML. Perhaps you just don’t want all your eggs in one vendor basket, or all your raindrops in one single cloud.

Worse, you might have a requirement to call an R API from D365 FinOps. In this blog post I’ll show you how.

First things first, let’s build a simple R model using the Prophet library from Facebook to do forecasting. This uses a data frame with two columns, y & ds to feed a time series set of values (y) based on time (ds). Prophet supports a lot of parameters for seasonality and such and I suggest reading up on it.

For our example I’ll keep things simple, and assume the R script won’t be doing any munging or wrangling as such. Clean data frame goes in, Prophet predicts, but instead of returning the y-hat values (Ŷ) we’ll make it interesting and return a set of base64 encoded PNG plots containing the forecast and seasonality trends instead.

So there are a number of challenges for us:
  • We need to host this R model as an API
  • We need to grab the resulting plot predictions created by Prophet
  • Encode the plots to base64 and return it from the API as JSON
  • Call and display this all in D365 from a form
The best way I’ve found to host R as an API is to use the Plumber library. So I’ve deployed a Linux environment in my cloud of choice and installed all the required R libraries, including Plumber, and set up NGINX to route incoming traffic on port 80 to Plumber which listens on port 8000. To call this API from D365 you’ll need to install a certificate as only HTTPS will do between D365 and our Linux box.

The R code is shown below, detailing how we grab the plots and encode it to base64. We also receive our data frame as part of the call so we need to URIDecode it. This will do for small data sets; if you want to tackle a large data set, use a different mechanism of passing a reference to the data, perhaps a POST call with the data in the body as JSON. In our case our API returns JSON containing three base64 encoded plots.

<div style="background:#ffffff;overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;">library(prophet)library(dplyr)library(ggplot2)library(png)library(plumber)library(urltools)encodeGraphic </p>[FormControlAttribute('Forecast','',classstr(ForecastControlBuild))]class ForecastControl extends FormTemplateControl{ FormProperty csv; public void new(FormBuildControl _build, FormRun _formRun) { super(_build, _formRun); this.setTemplateId('Forecast'); this.setResourceBundleName('/resources/html/Forecast'); csv = properties.addProperty(methodStr(ForecastControl, parmCSV), Types::String); } [FormPropertyAttribute(FormPropertyKind::Value, "CSV")] public str parmCSV(str _value = csv.parmValue()) { if (!prmIsDefault(_value)) { csv.setValueOrBinding(_value); } return csv.parmValue(); } public void applyBuild() { super(); ForecastControlBuild build = this.build(); if (build) { this.parmCSV(build.parmCSV()); } }}


We’ll add a minimal control HTML file to host our image placeholders. Three simple DIV controls with image controls with their ID’s set to forecastImage, trendImage and yearlyImage respectively, so we can get hold of them from our JavaScript code.

Finally the JavaScript for our control containing the actual Ajax call to our R API.

(function () { 'use strict'; $dyn.controls.Forecast = function (data, element) { $dyn.ui.Control.apply(this, arguments); $dyn.ui.applyDefaults(this, data, $dyn.ui.defaults.Forecast); }; $dyn.controls.Forecast.prototype = $dyn.ui.extendPrototype($dyn.ui.Control.prototype, { init: function (data, element) { var self = this; $dyn.ui.Control.prototype.init.apply(this, arguments); $dyn.observe(data.CSV, function (csv) { document.getElementById('forecastImage').style.display = "none"; document.getElementById('trendImage').style.display = "none"; document.getElementById('yearlyImage').style.display = "none"; if (csv.length>0) { var url = 'https://yourboxhere.australiaeast.cloudapp.azure.com/forecast?data=' + csv; $.ajax({ crossOrigin: true, url: url, success: function (data) { var obj = JSON.parse(data); var forecast = obj.forecast; var trend = obj.trend; var yearly = obj.yearly; document.getElementById('forecastImage').src = 'data:image/png;base64,' + forecast; document.getElementById('trendImage').src = 'data:image/png;base64,' + trend; document.getElementById('yearlyImage').src = 'data:image/png;base64,' + yearly; document.getElementById('forecastImage').style.display = "block"; document.getElementById('trendImage').style.display = "block"; document.getElementById('yearlyImage').style.display = "block"; } }); } }) } });})();


So far it’s all fairly simple, and we can add a demo form in X++ to use our extensible control. We’ll grab some sales orders from D365, URI encode it manually and then send it off to our extensible control to pass to our R API sitting somewhere outside the D365 cloud.

<div style="background:#ffffff;overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;">class ForecastFormClass{ /// /// ///

/// /// [FormControlEventHandler(formControlStr(ForecastForm, FormCommandButtonControl1), FormControlEventType::Clicked)] public static void FormCommandButtonControl1_OnClicked(FormControl sender, FormControlEventArgs e) { FormCommandButtonControl callerButton = sender as FormCommandButtonControl; FormRun form = callerButton.formRun(); ForecastControl forecastControl; forecastControl = form.control(form.controlId("ForecastControl1")); SalesLine SalesLine; date varStartPeriodDT = mkdate(1, 1, 2015); date varEndPeriodDT = mkDate(1,7,2016); str csv = "ordered%2Corderdate%0D%0A"; while select sum(QtyOrdered), ShippingDateRequested from SalesLine group by ShippingDateRequested where SalesLine.ShippingDateRequested >= varStartPeriodDT && SalesLine.ShippingDateRequested
__________________
Расскажите о новых и интересных блогах по Microsoft Dynamics, напишите личное сообщение администратору.
 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
survivingcrm: Trial & Error: Understanding Dynamics 365 CE Trials Blog bot Dynamics CRM: Blogs 0 26.02.2018 20:11
stoneridgesoftware: How to Integrate Power BI with Dynamics 365 for Financials Blog bot DAX Blogs 0 01.04.2017 02:17
crminthefield: Podcast and Overview: Microsoft Dynamics CRM 2011 Update Rollup 15 Blog bot Dynamics CRM: Blogs 1 10.02.2016 10:26
german_nav_developer: Buildnummern-Übersicht Microsoft Dynamics NAV 2009 Blog bot Dynamics CRM: Blogs 0 04.06.2010 13:21
wiki.dynamicsbook: Changes Made in Navision Attain 3.60 Blog bot Dynamics CRM: Blogs 0 02.09.2008 13:23

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 23:06.
Powered by vBulletin® v3.8.5. Перевод: zCarot
Контактная информация, Реклама.