Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Sv translation
languageen

Loading camera stream

Introduction

In this section, we will delve into the fundamental concepts of working with video stream services and prepare an HTML template for our example. To stream video from various sources or services, we need to obtain a link to the video feed, which we can later embed within an iframe. In our case, authentication is often required, which is typically achieved through a secure (HTTPS) POST request to the service's server, providing authentication data.

Let's begin with the HTML template required for displaying the video.

Code Block
languagexml
<section class="camera-player">
    <iframe id="camera-output" frameborder="0" style="width: 100%; height: 100%"></iframe>
    <div class="bottom-panel">
        <button class="icon-settings"></button>
        <button class="full-screen icon-enlarge"></button>
    </div>
    <div class="controls hidden">
       <!-- Structure of your controls if needed -->
    </div>
</section>

Configuring the View and Authentication Request

In this section, we will take a closer look at the authentication process. To send a request, you need to create a "Request configuration" by navigating to the "Administration" menu and selecting "Request configuration." If you have the option to create a configuration that suits all domains, you can do this outside of the domain and mark it as "Public" using the checkbox.

Here are the steps to create a new configuration:

  1. Specify a name for the configuration in the "Name" field.
  2. Choose the request type from the dropdown menu.
  3. In the configuration JSON, include the following:
    • "url": The URL to which the request will be sent.
    • "methodType": The request type (in our case, it's a POST request).
    • "headers": Request headers in the format of a JSON array of objects with "name" and "value" fields, e.g., [{"name": "Authorization", "value": "$token"}].

You can also include variables in the request path or JSON configuration by using the "$" sign, like "$variableName" or "$token." If the request body remains static without changes, you can add it as a "Payload Template" and click "Add." You can add multiple static templates if needed.

Once the configuration is ready, click "Save" to save the newly created configuration.

Moving forward, let's put our configuration to work. We will use it in an endpoint to conceal authentication data and return only a temporary access token in the response body. You can find more details on creating and configuring endpoints here.

To make use of the created request configuration, employ the "httpRequestAsync" function. This function takes three parameters and returns a request identifier for further processing. Here are the parameters:

  1. Request configuration ID (number 1 in the image).
  2. Payload (which can be null).
  3. An associative array of key-value pairs for substituting values in the configuration JSON.

Please note that when using the "payload" parameter, templates will not be applied ("Payload Template").

Image Modified


To handle the request, we will need the "serviceResponse" function. It takes three parameters and a callback function. The first parameter is the request ID, generated by the "httpRequestAsync" function. The second parameter specifies the frequency at which the request will be checked for a response, and the third parameter defines the timeout duration after which the request is considered unsuccessful.

Inside the function, you have access to response processing fields such as "responseRetrieved", "httpStatusCode" and "httpBody". Let's examine these fields in the code example below.

Code Block
languagegroovy
def response;
def requestId = httpRequestAsync(255, null, null);
serviceResponse(requestId, 250, 10000) {
  if (!responseRetrieved) {
    response = Response.ok('No response retrieved','text/plain').status(500).build();
    return;
  }

  if (httpStatusCode != 200) {
    response = Response.ok('Something went wrong during the authorization','text/plain').status(httpStatusCode as Integer).build();
    return;
  }

  try {
    response = Response.ok(httpBody as String, 'application/json').build()
  } catch (Exception e) {
    response = Response.ok('Internal server error: ' + e.message, 'text/plain').status(500).build();
  }
}

return response;


After validation, you can return the token separately or the entire response body, as shown above. Save this endpoint as a "BellaDati" endpoint. You can call it using JavaScript at the necessary point in your application.

Next, you should create and describe the logic for all UI components. At the appropriate moment to obtain an access token, invoke the endpoint with a URL structure like `${location.origin}/endpoint/endpointURL`, where "endpointURL" is the URL you specified during creation.

Upon acquiring the access token, we can proceed to interact with the streaming service provider and obtain the URL to embed into the previously prepared iframe. This can be achieved on the client side or by creating a new request configuration.

You can adapt the UI by adding CSS files to the extension.


Loading Data from API

Introduction

In this tutorial, we will explore methods for updating information within a data set using the API. It's worth mentioning that there are two primary ways to load information into a data set:

  1. Connecting over HTTP: This method involves making HTTP requests to load data into the data set. For a comprehensive guide on connecting over HTTP, please refer to this link.

  2. Using ML Studio: The focus of this tutorial will be on this method, where we load information into a data set based on existing data. We will delve into this option in more detail.

Configuring Data Retrieval

Before we dive into the implementation, it's crucial to define the requirements for the resource from which we need to fetch information. In our case, the following requirements apply: we need to send an HTTP GET request to the service's address with an API key and handle the response. The key can be dynamically configured in the domain parameters.

To meet these requirements, we need to create a request configuration that includes a variable for sending the API key.

Navigate to the "Administration" menu and select "Request configuration". Create a new configuration. The JSON configuration will appear as follows: 

Code Block
{"url": "https://api-service.com/v1/forecastPrefWeek?code=$code&key=$apiKey","methodType":"GET"}

Apply the "Public" checkbox to this configuration and save it. In this case, we don't need to send anything in the request body, so we'll leave the "Payload Template" field untouched.

Next, add a domain parameter. Click on your profile icon and select the domain name. On the right, you'll find a button for adding parameters. Add a new parameter with the name "weather_api_key" and the corresponding value.

Image Modified

Now, let's move on to creating an ML script for data processing. Based on existing data, we will send HTTP requests cyclically to retrieve weather information for each region, followed by processing and updating the data. Here's an example of the code.

Code Block
languagegroovy
import org.joda.time.*;

apiKey = currentDomain().getParameter('weather_api_key').getValue();
requestConfigId = 9;

weatherDs = [
  id: 'WEATHER_DATA_WEEKLY',
  col: [
    date: 'L_FORECAST_DATE',
    prefecture: 'L_PREFECTURE_CODE',
    weather: 'L_WEATHER',
    weatherCode: 'L_WEATHER_CODE',
    tempMax: 'M_TEMPERATURE_MAX',
    tempMin: 'M_TEMPERATURE_MIN',
    precipitation: 'M_PRECIPITATION',
  ],
];

siteDs = [
  id: 'CONSTRUCTION_SITES',
  col: [
    gps: 'L_GPS',
    prefectureCode: 'L_PREF_CODE',
    yumakeCode: 'L_YUMAKE_CODE',
    regionCode: 'L_REGION_CODE',
  ],
];

def hasGpsFilter = isNotNullFilter(siteDs.col.gps);
def hasPrefectureCodeFilter = isNotNullFilter(siteDs.col.prefectureCode);
def siteFilter = andFilter(hasGpsFilter, hasPrefectureCodeFilter);

def sites = readDataset(siteDs.id, -1, siteFilter, descSort('ROW_UID'));

if (sites.isEmpty()) {
  return warn('Site list does not contain prefecture codes. Skipping...');
}

def prefectures = sites.collect{ it[siteDs.col.prefectureCode] }.unique(false);

prefectures.each{ code ->
  if (!(code instanceof String)) return;
  
  def variables = [
    code: code,
   	apiKey: apiKey,
  ];
  
  def requestId = httpRequestAsync(requestConfigId, null, variables);
  serviceResponse(requestId, 500, 10000) {
    if (!responseRetrieved) {
      return warn("No response retrieved for the prefecture code ${code}");
    }
    
    if (httpStatusCode != 200) {
      warn("Status code: '${httpStatusCode}'");
      warn("Something went wrong during the weather data getting for the prefecture code: ${code}")
      return;
    }
    
    def response = jsonToMap(httpBody);
    
    if (response.errors?.size()) {
      response.errors.each{ warn("Request failed with error: ${it.message ?: 'message'}") }
      return;
    }
    
    handleResponse(response);
  }
}

void handleResponse(response) {
  response.area.each{ area ->
    def rows = [];
    def prefectureCode = area.areaCode;
    
    area.forecastDateTime.eachWithIndex{ date, idx ->
      rows.add([
        (weatherDs.col.date): new LocalDate(date.take(10)),
        (weatherDs.col.prefecture): prefectureCode,
        (weatherDs.col.weather): area.weather[idx],
        (weatherDs.col.weatherCode): area.weatherCode[idx],
        (weatherDs.col.tempMax): area.temperatureMax[idx],
        (weatherDs.col.tempMin): area.temperatureMin[idx],
        (weatherDs.col.precipitation): area.precipitation[idx],
      ]);  
    }
    
    def dateFrom = rows.collect{ it[weatherDs.col.date] }.min();
    def dateFromFilter = isGreaterOrEqualFilter(weatherDs.col.date, dateFrom);
    def prefectureFilter = isEqualFilter(weatherDs.col.prefecture, prefectureCode);
    def weatherFilter = andFilter(prefectureFilter, dateFromFilter);
    
    deleteData(weatherDs.id, weatherFilter);
    storeDataset(weatherDs.id, rows);
  }
}

void warn(String data) {
  println("[WARN] ${data}");
}

void dev(String data) {
  println("[DEV] ${data}");
}

Pay attention to how we retrieve parameters from the current domain at the beginning of the script.

Code Block
languagegroovy
apiKey = currentDomain().getParameter('weather_api_key').getValue();


It's also essential to note the parameter passing for substitution in the JSON request configuration and response processing.

Code Block
languagegroovy
// variables definition
def variables = [
    code: code,
    apiKey: apiKey,
  ];
   
def requestId = httpRequestAsync(requestConfigId, null, variables);  // passing the variables for substitution in the JSON request configuration
serviceResponse(requestId, 500, 10000) {
  if (!responseRetrieved) {
    return warn("No response retrieved for the prefecture code ${code}");
  }
  
  if (httpStatusCode != 200) {
    warn("Status code: '${httpStatusCode}'");
    warn("Something went wrong during the weather data getting for the prefecture code: ${code}")
    return;
  }
  
  def response = jsonToMap(httpBody);
  
  if (response.errors?.size()) {
    response.errors.each{ warn("Request failed with error: ${it.message ?: 'message'}") }
    return;
  }
  
  handleResponse(response);
}

Configure automatic data update

To ensure our script runs automatically on a specific schedule, we need to create a data set that will act as the trigger. To achieve the desired data refresh frequency, we'll use another data set from which we'll import data. In this data set, you can have just a single record and configure the import in such a way that you always get a random value.

Image Modified

To connect data sources, you can refer to the documentation here.

Once the data source is connected, you need to set up the import scheduler. Here's how you can do it:

  1. Go to the "Data set summary" page of the trigger data set and click the "Schedule import" button.
  2. Fill in all the necessary fields and confirm by clicking "Schedule." If you need to initiate multiple imports, you can create several processes by clicking the "Schedule import" button again.

An exmaple of the result:

Image Modified

Next, to trigger the ML project based on this data set, you need to wrap it in a pipeline. You can find instructions on creating a pipeline here.

Once the pipeline is set up, you need to add the trigger. After setting this up, you may need to refresh the page. In the pop-up window, select the data set that will act as the trigger and click the "Add" button, then close the window.

Image Modified

You can test the trigger's functionality by making changes to the trigger data set.

Sv translation
languageja

カメラストリームの読み込み

はじめに

このセクションでは、動画ストリーム・サービスを扱うための基本的な概念を掘り下げ、例として使用する HTML テンプレートを用意します。さまざまなソースやサービスから動画をストリーム配信するには、動画フィードへのリンクを取得する必要があります。この場合、認証が必要になることが多く、通常、サービスのサーバに安全な(HTTPS)POST リクエストを送信し、認証データを提供することで実現されます。

まずは、ビデオを表示するために必要なHTMLテンプレートから始めましょう。

Code Block
languagexml
<section class="camera-player">
    <iframe id="camera-output" frameborder="0" style="width: 100%; height: 100%"></iframe>
    <div class="bottom-panel">
        <button class="icon-settings"></button>
        <button class="full-screen icon-enlarge"></button>
    </div>
    <div class="controls hidden">
       <!-- Structure of your controls if needed -->
    </div>
</section>

ビューと認証リクエストの設定

このセクションでは、認証プロセスについて詳しく見ていきます。リクエストを送信するには、"Administration "メニューから "Request configuration "を選択して、"Request configuration "を作成する必要があります。すべてのドメインに適合するコンフィギュレーションを作成するオプションがある場合、ドメインの外でこれを行い、チェックボックスを使って "Public "としてマークすることができます。

以下は、新しいコンフィギュレーションを作成する手順です:

  1. Nameフィールドにコンフィギュレーションの名前を指定します。
  2. ドロップダウンメニューからリクエストタイプを選択します。
  3. コンフィギュレーションJSONには、以下を含めます:
    • "url": リクエストの送信先URL。
    • "methodType": リクエストタイプ(この場合はPOSTリクエスト)。
    • "headers": ”name" と "value" フィールドを持つオブジェクトのJSON配列形式のリクエストヘッダ。例 [{"name": "Authorization", "value": "$token"}].

また、"$variableName "や"$token "のように、"$"記号を使ってリクエストパスやJSON設定に変数を含めることもできます。リクエスト本体が変更されずに静的なままであれば、"Payload Template "として追加し、"Add "をクリックします。必要に応じて、複数の静的テンプレートを追加することができます。

コンフィギュレーションの準備ができたら、"Save "をクリックして新しく作成したコンフィギュレーションを保存します。

次に、この設定を実際に使ってみよう。認証データを隠蔽し、レスポンスボディに一時的なアクセストークンのみを返すエンドポイントにこの設定を使用します。エンドポイントの作成と設定の詳細については、こちらを参照してください。

作成されたリクエスト設定を利用するには、"httpRequestAsync "関数を使用します。この関数は3つのパラメーターを受け取り、さらなる処理のためにリクエスト識別子を返します。以下がそのパラメータです:

  1. リクエスト・コンフィギュレーションID(画像の1番)。
  2. ペイロード(NULLでも可)。
  3. 構成JSONの値を置換するための、キーと値のペアの連想配列。

payload "パラメーターを使用する場合、テンプレートは適用されませんのでご注意ください("Payload Template")。

Image Added


Tリクエストを処理するには、"serviceResponse "関数が必要です。この関数は3つのパラメータとコールバック関数を受け取る。最初のパラメータは、"httpRequestAsync "関数で生成されたリクエストIDです。2番目のパラメータは、リクエストが応答をチェックする頻度を指定し、3番目のパラメータは、リクエストが失敗したとみなされるタイムアウト時間を定義します。

関数内部では、"responseRetrieved"、"httpStatusCode"、"httpBody "といったレスポンス処理フィールドにアクセスできます。以下のコード例で、これらのフィールドを調べてみましょう。

Code Block
languagegroovy
def response;
def requestId = httpRequestAsync(255, null, null);
serviceResponse(requestId, 250, 10000) {
  if (!responseRetrieved) {
    response = Response.ok('No response retrieved','text/plain').status(500).build();
    return;
  }

  if (httpStatusCode != 200) {
    response = Response.ok('Something went wrong during the authorization','text/plain').status(httpStatusCode as Integer).build();
    return;
  }

  try {
    response = Response.ok(httpBody as String, 'application/json').build()
  } catch (Exception e) {
    response = Response.ok('Internal server error: ' + e.message, 'text/plain').status(500).build();
  }
}

return response;


バリデーションの後、上記のように、トークンを個別に返すことも、レスポンスボディ全体を返すこともできます。このエンドポイントを "BellaDati "エンドポイントとして保存します。アプリケーションの必要な箇所でJavaScriptを使って呼び出すことができます。

次に、すべてのUIコンポーネントのロジックを作成し、記述します。アクセストークンを取得する適切なタイミングで、`${location.origin}/endpoint/endpointURL`のようなURL構造でエンドポイントを呼び出します。

アクセストークンを取得したら、ストリーミングサービス・プロバイダーとやり取りを行い、事前に用意したiframeに埋め込むためのURLを取得します。これは、クライアント側で行うか、新しいリクエスト設定を作成することで実現できます。

拡張機能にCSSファイルを追加することで、UIを適応させることができます。


APIからデータをロード

はじめに

このチュートリアルでは、APIを使用してデータセット内の情報を更新する方法を探ります。データセットに情報をロードするには、主に2つの方法があることに触れておきましょう:

  1. HTTP経由での接続:この方法では、HTTPリクエストを行ってデータをデータセットにロードします。HTTP経由の接続に関する包括的なガイドについては、こちらのリンクを参照してください。

  2. ML Studioを使用: このチュートリアルの焦点はこの方法で、既存のデータに基づいてデータセットに情報をロードします。このオプションについてより詳しく説明します。

データ検索の設定

実装に入る前に、情報を取得するリソースの要件を定義することが重要です。この場合、以下の要件が当てはまります。サービスのアドレスにAPIキーでHTTP GETリクエストを送信し、レスポンスを処理する必要があります。キーはドメイン・パラメータで動的に設定できます。

これらの要件を満たすには、APIキーを送信するための変数を含むリクエスト・コンフィギュレーションを作成する必要があります。

Administration "メニューに移動し、"Request configuration "を選択します。新しいコンフィギュレーションを作成します。JSONコンフィギュレーションは以下のように表示されます:

Code Block
{"url": "https://api-service.com/v1/forecastPrefWeek?code=$code&key=$apiKey","methodType":"GET"}

この設定に "Public "チェックボックスを適用し、保存します。この場合、リクエストボディに何も送る必要はないので、"Payload Template "フィールドはそのままにしておきます。

次に、ドメインパラメータを追加します。プロフィールアイコンをクリックし、ドメイン名を選択します。右側に、パラメータを追加するためのボタンがあります。weather_api_key "という名前と対応する値で新しいパラメータを追加します。

Image Added

次に、データ処理のためのMLスクリプトの作成に移りましょう。既存のデータを基に、周期的にHTTPリクエストを送り、各地域の気象情報を取得し、その後、データを処理し更新します。以下はコードの例です。

Code Block
languagegroovy
import org.joda.time.*;

apiKey = currentDomain().getParameter('weather_api_key').getValue();
requestConfigId = 9;

weatherDs = [
  id: 'WEATHER_DATA_WEEKLY',
  col: [
    date: 'L_FORECAST_DATE',
    prefecture: 'L_PREFECTURE_CODE',
    weather: 'L_WEATHER',
    weatherCode: 'L_WEATHER_CODE',
    tempMax: 'M_TEMPERATURE_MAX',
    tempMin: 'M_TEMPERATURE_MIN',
    precipitation: 'M_PRECIPITATION',
  ],
];

siteDs = [
  id: 'CONSTRUCTION_SITES',
  col: [
    gps: 'L_GPS',
    prefectureCode: 'L_PREF_CODE',
    yumakeCode: 'L_YUMAKE_CODE',
    regionCode: 'L_REGION_CODE',
  ],
];

def hasGpsFilter = isNotNullFilter(siteDs.col.gps);
def hasPrefectureCodeFilter = isNotNullFilter(siteDs.col.prefectureCode);
def siteFilter = andFilter(hasGpsFilter, hasPrefectureCodeFilter);

def sites = readDataset(siteDs.id, -1, siteFilter, descSort('ROW_UID'));

if (sites.isEmpty()) {
  return warn('Site list does not contain prefecture codes. Skipping...');
}

def prefectures = sites.collect{ it[siteDs.col.prefectureCode] }.unique(false);

prefectures.each{ code ->
  if (!(code instanceof String)) return;
  
  def variables = [
    code: code,
   	apiKey: apiKey,
  ];
  
  def requestId = httpRequestAsync(requestConfigId, null, variables);
  serviceResponse(requestId, 500, 10000) {
    if (!responseRetrieved) {
      return warn("No response retrieved for the prefecture code ${code}");
    }
    
    if (httpStatusCode != 200) {
      warn("Status code: '${httpStatusCode}'");
      warn("Something went wrong during the weather data getting for the prefecture code: ${code}")
      return;
    }
    
    def response = jsonToMap(httpBody);
    
    if (response.errors?.size()) {
      response.errors.each{ warn("Request failed with error: ${it.message ?: 'message'}") }
      return;
    }
    
    handleResponse(response);
  }
}

void handleResponse(response) {
  response.area.each{ area ->
    def rows = [];
    def prefectureCode = area.areaCode;
    
    area.forecastDateTime.eachWithIndex{ date, idx ->
      rows.add([
        (weatherDs.col.date): new LocalDate(date.take(10)),
        (weatherDs.col.prefecture): prefectureCode,
        (weatherDs.col.weather): area.weather[idx],
        (weatherDs.col.weatherCode): area.weatherCode[idx],
        (weatherDs.col.tempMax): area.temperatureMax[idx],
        (weatherDs.col.tempMin): area.temperatureMin[idx],
        (weatherDs.col.precipitation): area.precipitation[idx],
      ]);  
    }
    
    def dateFrom = rows.collect{ it[weatherDs.col.date] }.min();
    def dateFromFilter = isGreaterOrEqualFilter(weatherDs.col.date, dateFrom);
    def prefectureFilter = isEqualFilter(weatherDs.col.prefecture, prefectureCode);
    def weatherFilter = andFilter(prefectureFilter, dateFromFilter);
    
    deleteData(weatherDs.id, weatherFilter);
    storeDataset(weatherDs.id, rows);
  }
}

void warn(String data) {
  println("[WARN] ${data}");
}

void dev(String data) {
  println("[DEV] ${data}");
}

スクリプトの冒頭で、現在のドメインからどのようにパラメーターを取得しているかに注目してください。

Code Block
languagegroovy
apiKey = currentDomain().getParameter('weather_api_key').getValue();


JSONリクエストのコンフィギュレーションとレスポンス処理における、置換のためのパラメータ・パッシングにも注意する必要があります。

Code Block
languagegroovy
// variables definition
def variables = [
    code: code,
    apiKey: apiKey,
  ];
   
def requestId = httpRequestAsync(requestConfigId, null, variables);  // passing the variables for substitution in the JSON request configuration
serviceResponse(requestId, 500, 10000) {
  if (!responseRetrieved) {
    return warn("No response retrieved for the prefecture code ${code}");
  }
  
  if (httpStatusCode != 200) {
    warn("Status code: '${httpStatusCode}'");
    warn("Something went wrong during the weather data getting for the prefecture code: ${code}")
    return;
  }
  
  def response = jsonToMap(httpBody);
  
  if (response.errors?.size()) {
    response.errors.each{ warn("Request failed with error: ${it.message ?: 'message'}") }
    return;
  }
  
  handleResponse(response);
}

自動データ更新の設定

スクリプトが特定のスケジュールで自動的に実行されるようにするには、トリガーとなるデータセットを作成する必要があります。目的のデータ更新頻度を達成するために、データをインポートする別のデータセットを使用します。このデータセットでは、レコードを1つだけ持ち、常にランダムな値を取得するようにインポートを設定することができます。Image Added

データソースを接続するには、こちらのドキュメントを参照してください。 

データソースを接続したら、インポート・スケジューラーを設定する必要があります。ここではその方法を説明します:

  1. トリガーデータセットの "Data set summary "ページに行き、"Schedule import "ボタンをクリックします。
  2. 必要なフィールドをすべて入力し、"Schedule "をクリックして確認します。複数のインポートを開始する必要がある場合は、"Schedule import "ボタンを再度クリックして、複数のプロセスを作成することができます。

結果の一例:

Image Added

次に、このデータセットに基づいてMLプロジェクトを起動するために、パイプラインでラップする必要があります。パイプラインの作り方はこちらをご参照ください。

パイプラインを設定したら、トリガーを追加する必要があります。設定後、ページを更新する必要があるかもしれません。ポップアップ・ウィンドウで、トリガーとなるデータ・セットを選択し、"Add "ボタンをクリックし、ウィンドウを閉じます。

Image Added

トリガーのデータセットに変更を加えることで、トリガーの機能をテストすることができます。