この**フォーメーション**は[私の GitHub](https://github.com/LucasEnard/formation-template-python) にあり、30 分で csv ファイルと txt ファイルの読み取りと書き込み方法、Postgres を使った**IRIS データベース**と**リモートデータベース**の挿入とアクセス方法、FLASK API の使用方法について説明します。これらすべてに、[PEP8 命名規則](https://peps.python.org/pep-0008/)に従った、Python のみのインターオペラビリティフレームワークを使用します。
このフォーメーションは、ほとんどをコピー&ペースト操作で実行でき、グローバル演習を行う前に、ステップごとの操作が説明されています。
記事のコメント欄、Teams、またはメール(lucas.enard@intersystems.com)でご質問にお答えします。
このフォーメーションに関するあらゆる点において、ご意見やご感想をお送りいただけると幸いです。
# 1. **Ensemble / Interoperability のフォーメーション**
このフォーメーションでは、Python および特に以下を使用した InterSystems のインターオペラビリティフレームワークを学習することを目標としています。
* 本番環境
* メッセージ
* ビジネスオペレーション
* アダプター
* ビジネスプロセス
* ビジネスサービス
* REST サービスと運用
**目次:**
- [1. **Ensemble / Interoperability のフォーメーション**](#1-ensemble--interoperability-formation)
- [2. フレームワーク](#2-framework)
- [3. フレームワークの適用](#3-adapting-the-framework)
- [4. 前提条件](#4-prerequisites)
- [5. セットアップ](#5-setting-up)
- [5.1. Docker コンテナ](#51-docker-containers)
- [5.2. 管理ポータルと VSCode](#52-management-portal-and-vscode)
- [5.3. コンテナ内でフォルダを開く](#53-having-the-folder-open-inside-the-container)
- [5.4. コンポーネントの登録](#54-register-components)
- [5.5. 完成ファイル](#55-the-solution)
- [6. 本番環境](#6-productions)
- [7. ビジネスオペレーション](#7-business-operations)
- [7.1. オブジェクトクラスの作成](#71-creating-our-object-classes)
- [7.2. メッセージクラスの作成](#72-creating-our-message-classes)
- [7.3. オペレーションの作成](#73-creating-our-operations)
- [7.4. 本番環境へのオペレーションの追加](#74-adding-the-operations-to-the-production)
- [7.5. テスト](#75-testing)
- [8. ビジネスプロセス](#8-business-processes)
- [8.1. 単純な BP](#81-simple-bp)
- [8.2. 本番環境へのプロセスの追加](#82-adding-the-process-to-the-production)
- [8.3. テスト](#83-testing)
- [9. ビジネスサービス](#9-business-service)
- [9.1. 単純な BS](#91-simple-bs)
- [9.2. 本番環境へのサービスの追加](#92-adding-the-service-to-the-production)
- [9.3. テスト](#93-testing)
- [10. db-api による外部データベースへのアクセス](#10-getting-access-to-an-extern-database-using-a-db-api)
- [10.1. 前提条件](#101-prerequisites)
- [10.2. 新しいオペレーションの作成](#102-creating-our-new-operation)
- [10.3. 本番環境の構成](#103-configuring-the-production)
- [10.4. テスト](#104-testing)
- [10.5. 演習](#105-exercise)
- [10.6. ソリューション](#106-solution)
- [11. REST サービス](#11-rest-service)
- [11.1. 前提条件](#111-prerequisites)
- [11.2. サービスの作成](#112-creating-the-service)
- [11.3. テスト](#113-testing)
- [12. グローバル演習](#12-global-exercise)
- [12.1. 指示](#121-instructions)
- [12.2. ヒント](#122-hints)
- [12.2.1. bs](#1221-bs)
- [12.2.1.1. 情報の取得](#12211-get-information)
- [12.2.1.2. リクエストによる情報の取得](#12212-get-information-with-requests)
- [12.2.1.3. リクエストによる情報の取得とその使用](#12213-get-information-with-requests-and-using-it)
- [12.2.1.4. 情報の取得のソリューション](#12214-get-information-solution)
- [12.2.2. bp](#1222-bp)
- [12.2.2.1. 平均歩数と dict](#12221-average-number-of-steps-and-dict)
- [12.2.2.2. 平均歩数と dict: ヒント](#12222-average-number-of-steps-and-dict--hint)
- [12.2.2.3. 平均歩数と dict: map を使用する](#12223-average-number-of-steps-and-dict--with-map)
- [12.2.2.4. 平均歩数と dict: 解答](#12224-average-number-of-steps-and-dict--the-answer)
- [12.2.3. bo](#1223-bo)
- [12.3. ソリューション](#123-solutions)
- [12.3.1. obj と msg](#1231-obj--msg)
- [12.3.2. bs](#1232-bs)
- [12.3.3. bp](#1233-bp)
- [12.3.4. bo](#1234-bo)
- [12.4. テスト](#124-testing)
- [12.5. グローバル演習のまとめ](#125-conclusion-of-the-global-exercise)
- [13. まとめ](#13-conclusion)
# 2. フレームワーク
以下は、IRIS フレームワークです。
![フレームワーク全体図](https://raw.githubusercontent.com/thewophile-beep/formation-template/master/misc/img/FrameworkFull.png)
IRIS 内部のコンポーネントは、本番環境を表します。 インバウンドアダプターとアウトバウンドアダプターは、様々な種類のフォーマットをデータベースの入力と出力として使用できるようにします。
複合アプリケーションにより、REST サービスなどの外部アプリケーションを通じて本番環境にアクセスできます。
これらのコンポーネントを繋ぐ矢印は**メッセージ**で、 リクエストかレスポンスを表します。
# 3. フレームワークの適用
ここでは、CSV ファイルから行を読み取り、IRIS データベースに .txt ファイルで保存します。
次に、外部データベースにオブジェクトを保存できるようにするオペレーションを db-api を使って追加します。 このデータベースは Docker コンテナに配置されており、Postgres を使用します。
最後に、複合アプリケーションを使用して、新しいオブジェクトをデータベースに挿入する方法またはこのデータベースを照会する方法を確認します(ここでは、REST サービスを使用します)。
この目的に合わせて構成されたフレームワークは、以下のようになります。
WIP ![適応したフレームワーク](https://raw.githubusercontent.com/thewophile-beep/formation-template/master/misc/img/FrameworkAdapted.png)
# 4. 前提条件
このフォーメーションでは、以下の項目が必要です。
* VSCode: https://code.visualstudio.com/
* VSCode 用の InterSystems アドオンスイート: https://intersystems-community.github.io/vscode-objectscript/installation/
* Docker: https://docs.docker.com/get-docker/
* VSCode 用の Docker アドオン
* 自動的に行われるもの: [Postgres の要件](#101-prerequisites)
* 自動的に行われるもの: [Flask の要件](#111-prerequisites)
# 5. セットアップ
## 5.1. Docker コンテナ
InterSystems イメージにアクセスするために、次の URL に移動してください: http://container.intersystems.com。 InterSystems の資格情報にリンクすると、レジストリに接続するためのパスワードを取得できます。 Docker の VSCode アドオンのイメージタブで、[レジストリを接続]を押し、汎用レジストリとして上記の URL(http://container.intersystems.com)を入力すると、資格情報の入力を求められます。 このログインは通常のログインですが、パスワードはウェブサイトから取得したものを使用します。
これが済むと、コンテナを構築・作成(指定された `docker-compose.yml` と `Dockerfile` を使用)できるようになります。
## 5.2. 管理ポータルと VSCode
このリポジトリは、[VS Code](https://code.visualstudio.com/) 対応です。
ローカルにクローンした `formation-template-python` を VS Code で開きます。
指示されたら(右下に表示)、推奨される拡張機能をインストールしてください。
## 5.3. コンテナ内でフォルダを開く
コーディングする前に、コンテナ*内部*にアクセスしていることが**非常に重要です**。
これには、docker が VSCode を開く前に有効である必要があります。
次に VSCode 内で指示されたら(右下に表示)、コンテナ内のフォルダを開き直すと、その中にある Python コンポーネントを使用できるようになります。
これを初めて行う場合、コンテナの準備が整うまで数分かかる場合があります。
[詳細情報はこちらをご覧ください。](https://code.visualstudio.com/docs/remote/containers)
![アーキテクチャ](https://code.visualstudio.com/assets/docs/remote/containers/architecture-containers.png)
リモートのフォルダを開くと、その中で開く VS Code とターミナルで、コンテナ内の Python コンポーネントを使用できます。 `/usr/irissys/bin/irispython` を使用するように構成してください。
## 5.4. コンポーネントの登録
本番環境に対して Python で作成しているコンポーネントを登録するには、`grongier.pex._utils` モジュールから `register_component` 関数を使用する必要があります。
**重要**: コンポーネントはすでに登録済みです([グローバル演習](#12-global-exercise)を除く)。
情報までに、またグローバル演習で使用できるように、以下の手順でコンポーネントを登録します。
これには、プロジェクトで作業している際に、最初に組み込み Python コンソールを使用して手動でコンポーネントを追加することをお勧めします。
これらのコマンドは、`misc/register.py` ファイルにあります。
このコマンドを使用するには、まずコンポーネントを作成してから、VSCode でターミナルを起動する必要があります([5.2](#52-management-portal-and-vscode) と [5.3](#53-having-the-folder-open-inside-the-container) の手順を実行している場合は、自動的にコンテナ内に移動しています)。
IrisPython コンソールを起動するには、以下のように入力します。
```
/usr/irissys/bin/irispython
```
次に、以下を入力します。
```
from grongier.pex._utils import register_component
```
そして、以下のようにして、コンポーネントを登録できます。
```
register_component("bo","FileOperation","/irisdev/app/src/python/",1,"Python.FileOperation")
```
この行は、`bo` モジュール内にコーディングされている `FileOperation` クラスを登録します。このファイルは `/irisdev/app/src/python/`(このコースに従って作業している場合のパス)にあり、管理ポータルでは `Python.FileOperation` という名前を使用しています。
コンポーネントが登録済みである際にこのファイルの名前、クラスまたはパスを変更しない場合、VSCode でそれらを変更することが可能です。登録し直す必要はありません。 管理ポータルでの再起動は、必ず行ってください。
## 5.5. 完成ファイル
フォーメーションのある時点でやり方がわからなくなったり、さらに説明が必要となった場合は、GitHub の `solution` ブランチにすべての正しい内容と動作する[本番環境](#6-productions)をご覧いただけます。
# 6. 本番環境
**本番環境**は、Iris 上のすべての作業の土台であり、**サービス**、**プロセス**、および**オペレーション**をまとめる[フレームワーク](#2-framework)の外殻として考える必要があります。
本番環境内のすべては関数を継承します。この関数は、このクラスのインスタンスの作成時に解決する `on_init` 関数と、インスタンスがキルされたときに解決する `on_tear_down` 関数です。 これは、書き込みを行うときに変数を設定する際、または使用された開いているファイルをクローンする際に役立ちます。
ほぼすべてのサービス、プロセス、およびオペレーションを持つ**本番環境**は、**すでに作成済み**であることに注意してください。
指示されたら、ユーザー名の SuperUser とパスワードの SYS を使用して接続してください。
**本番環境が開いていない**場合は、[Interoperability] > [Configure] メニューに移動して、[Production] をクリックし、 次に [Open] をクリックして `iris` / `Production` を選択します。
これを行ったら、[ビジネスオペレーション](#7-business-operations)に直接進むことができます。
ただし、本番環境の作成方法に興味がある場合は、以下のようにして作成することができます。
管理ポータルに移動し、ユーザー名: SuperUser、パスワード: SYS を使用して接続します。
次に、[Interoperability] と [Configure] メニューに進みます。
![本番環境メニュー](https://user-images.githubusercontent.com/77791586/164473827-ffa2b322-095a-46e3-8c8b-16d467a80485.png)
次に、[New] を押して [Formation] パッケージを選択し、本番環境の名前を設定します。
![本番環境の作成](https://user-images.githubusercontent.com/77791586/164473884-5c7aec69-c45d-4062-bedc-2933e215da22.png)
本番環境を作成した直後、[Operations] セクションの真上にある [Production Settings(本番環境の設定)] をクリックする必要があります。 右のサイドバーメニューで、[Settings] タブの [Development and Debugging(開発とデバッグ)] で [Testing Enabled(テストを有効)] をアクティブにします(忘れずに [Apply] を押してください)。
![本番環境のテスト](https://user-images.githubusercontent.com/77791586/164473965-47ab1ba4-85d5-46e3-9e15-64186b5a457e.png)
この最初の本番環境で、ビジネスオペレーションを追加していきます。
# 7. ビジネスオペレーション
**ビジネスオペレーション**(BO)は、IRIS から外部アプリケーション/システムにリクエストを送信できるようにする特定のオペレーションです。 必要なものを IRIS に直接保存するためにも使用できます。
BO には、このインスタンスがソースからメッセージを受信するたびに呼び出される `on_message` 関数もあるため、フレームワークで確認できるように、情報を外部クライアントと送受信することが可能です。
これらのオペレーションは、VSCode でローカルに作成します。つまり、`src/python/bo.py` ファイルです。
このファイルを保存すると、IRIS でコンパイルされます。
ここでの最初のオペレーションでは、メッセージのコンテンツをローカルデータベースに保存し、同じ情報をローカルの .txt ファイルに書き込みます。
まずは、このメッセージを保存する方法を作成する必要があります。
## 7.1. オブジェクトクラスの作成
`dataclass` を使用して、[メッセージ](#72-creating-our-message-classes)の情報を格納することにします。
すでに存在する `src/python/obj.py` ファイルを以下のようにします。
インポート:
```python
from dataclasses import dataclass
```
コード:
```python
@dataclass
class Formation:
id_formation:int = None
nom:str = None
salle:str = None
@dataclass
class Training:
name:str = None
room:str = None
```
Formation クラスは、csv の情報を格納して「[8. ビジネスプロセス](#8-business-processes)」に送信する Python オブジェクトとして使用し、Training クラスは、「[8. ビジネスプロセス](#8-business-processes)」から複数のオペレーションに情報を送信し、Iris データベースに保存するか .txt ファイルに書き込むために使用されます。
## 7.2. メッセージクラスの作成
これらのメッセージには、[7.1](#71-creating-our-object-classes) で作成された `obj.py` ファイルにある `Formation` オブジェクトまたは `Training` オブジェクトが含まれます。
メッセージ、リクエスト、およびレスポンスはすべて `grongier.pex.Message` クラスの継承であることに注意してください。
すでに存在する `src/python/msg.py` ファイルを以下のようにします。
インポート:
```python
from dataclasses import dataclass
from grongier.pex import Message
from obj import Formation,Training
```
コード:
```python
@dataclass
class FormationRequest(Message):
formation:Formation = None
@dataclass
class TrainingRequest(Message):
training:Training = None
```
繰り返しますが、`FormationRequest` クラスは、csv の情報を格納して「[8. ビジネスプロセス](#8-business-processes)」に送信するメッセージとして使用し、`TrainingRequest` クラスは、「[8. ビジネスプロセス](#8-business-processes)」から複数のオペレーションに情報を送信し、Iris データベースに保存するか .txt ファイルに書き込むために使用されます。
## 7.3. オペレーションの作成
必要な要素がすべて揃ったので、オペレーションを作成できるようになりました。
すべてのビジネスオペレーションは、`grongier.pex.BusinessOperation` クラスを継承していることに注意してください。
すべてのオペレーションは、`src/python/bo.py` に保存されます。これらを区別できるよう、ファイルに現時点で確認できるように複数のクラスを作成する必要があります。オペレーションのすべてのクラスはすでに存在していますが、現時点ではほぼ空の状態です。
オペレーションがメッセージ/リクエストを受信すると、各関数のシグネチャに指定されたメッセージ/リクエストの種類に応じて自動的に正しい関数にメッセージ/リクエストを送信します。 メッセージ/リクエストの種類が処理されない場合は、`on_message` 関数に転送されます。
すでに存在する `src/python/bo.py` ファイルを以下のようにします。
インポート:
```python
from grongier.pex import BusinessOperation
import os
import iris
from msg import TrainingRequest,FormationRequest
```
`FileOperation` クラスのコード:
```python
class FileOperation(BusinessOperation):
"""
トレーニングまたは患者をファイルに書き込むオペレーションです。
"""
def on_init(self):
"""
現在の作業ディレクトリを、オブジェクトの path 属性に指定されたディレクトリか、path 属性が指定されていない場合は /tmp ディレクトリに変更します。
また、filename 属性が設定されていない場合は、toto
.csv に設定します。
:return: None
"""
if hasattr(self,'path'):
os.chdir(self.path)
else:
os.chdir("/tmp")
return None
def write_training(self, request:TrainingRequest):
"""
ファイルにトレーニングを書き込みます。
:param request: The request message
:type request: TrainingRequest
:return: None
"""
romm = name = ""
if request.training is not None:
room = request.training.room
name = request.training.name
line = room+" : "+name+"\n"
filename = 'toto.csv'
self.put_line(filename, line)
return None
def on_message(self, request):
return None
def put_line(self,filename,string):
"""
ファイルを開き、文字列を追加し、ファイルを閉じます。
:param filename: The name of the file to write to
:param string: The string to be written to the file
"""
try:
with open(filename, "a",encoding="utf-8",newline="") as outfile:
outfile.write(string)
except Exception as error:
raise error
```
ご覧のとおり、`FileOperation` が `msg.TrainingRequest` タイプのメッセージを受信すると、`request` のシグネチャが `TrainingRequest` であるため、`write_training` 関数に送信されます。
この関数では、メッセージが格納する情報が `toto.csv` ファイルに書き込まれます。
`path` はオペレーションのパラメーターであるため、`filename` を管理ポータルで直接変更できる変数にし、基本値を `toto.csv` とすることができます。
このようにするには、`on_init` 関数を以下のように編集する必要があります。
```python
def on_init(self):
if hasattr(self,'path'):
os.chdir(self.path)
else:
os.chdir("/tmp")
if not hasattr(self,'filename'):
self.filename = 'toto.csv'
return None
```
次に、オペレーションに直接コーディングして `filename = 'toto.csv'` を使用する代わりに、`self.filename` を呼び出します。
すると、`write_training` 関数は、以下のようになります。
```python
def write_training(self, request:TrainingRequest):
romm = name = ""
if request.training is not None:
room = request.training.room
name = request.training.name
line = room+" : "+name+"\n"
self.put_line(self.filename, line)
return None
```
独自の `filename` の選択方法にいては、「7.5 テスト」をご覧ください。
情報を .txt ファイルに書き込めるようになりましたが、iris データベースはどうなっているでしょうか?
`src/python/bo.py` ファイルでは、`IrisOperation` クラスのコードは以下のようになっています。
```python
class IrisOperation(BusinessOperation):
"""
iris データベースにトレーニングを書き込むオペレーション
"""
def insert_training(self, request:TrainingRequest):
"""
`TrainingRequest` オブジェクトを取り、新しい行を `iris.training` テーブルに挿入し、
`TrainingResponse` オブジェクトを返します
:param request: 関数に渡されるリクエストオブジェクト
:type request: TrainingRequest
:return: TrainingResponse メッセージ
"""
sql = """
INSERT INTO iris.training
( name, room )
VALUES( ?, ? )
"""
iris.sql.exec(sql,request.training.name,request.training.room)
return None
def on_message(self, request):
return None
```
ご覧のとおり、`IrisOperation` が `msg.TrainingRequest` タイプのメッセージを受信すると、このメッセージが保有する情報は、`iris.sql.exec` IrisPython 関数によって SQL クエリに変換されて実行されます。 このメソッドによって、メッセージは IRIS ローカルデータベースに保存されます。
これらのコンポーネントは、事前に本番環境に**登録済み**です。
情報までに、コンポーネントを登録する手順は、[5.4.](#54-register-components) に従い、以下を使用します。
```
register_component("bo","FileOperation","/irisdev/app/src/python/",1,"Python.FileOperation")
```
および、以下を使用します。
```
register_component("bo","IrisOperation","/irisdev/app/src/python/",1,"Python.IrisOperation")
```
## 7.4. 本番環境へのオペレーションの追加
オペレーションは、こちらで事前に登録済みです。
ただし、オペレーションを新規作成する場合は、手動で追加する必要があります。
今後の参考までに、オペレーションの登録手順を説明します。
登録には、管理ポータルを使用します。 [Operations] の横にある [+] 記号を押すと、[Business Operation Wizard(ビジネスオペレーションウィザード)] が開きます。
そのウィザードで、スクロールメニューから作成したばかりのオペレーションクラスを選択します。
![オペレーションの作成](https://user-images.githubusercontent.com/77791586/164474068-49c7799c-c6a2-4e1e-8489-3788c50acb86.png)
新しく作成したすべてのオペレーションに対して必ず実行してください!
## 7.5. テスト
オペレーションをダブルクリックすると、オペレーションが有効化されるか、再起動して変更内容が保存されます。
**重要**: この無効化して有効化し直す手順は、変更内容を保存する上で非常に重要な手順です。
**重要**: その後で、`Python.IrisOperation` **オペレーション**を選択し、右のサイドバーメニューの [Actions] タブに移動すると、**オペレーション**を**テスト**することができます。
(うまくいかない場合は、[テストを有効化](#6-productions)し、本番環境が開始していることを確認してから、本番環境をダブルクリックして再起動をクリックし、オペレーションをリロードしてください)。
`IrisOperation` については、テーブルは自動的に作成済みです。 情報までに、これを作成するには、管理ポータルを使用します。[System Explorer(システムエクスプローラー)] > [SQL] > [Go] に移動して、Iris データベースにアクセスします。 次に、[Execute Query(クエリを実行)] を開始します。
```
CREATE TABLE iris.training (
name varchar(50) NULL,
room varchar(50) NULL
)
```
管理ポータルのテスト機能を使用して、前に宣言したタイプのメッセージをオペレーションに送信します。 すべてがうまくいけば、仮想トレースを表示すると、プロセスとサービスとオペレーション間で何が起きたかを確認できるようになります。
`Request Type` として使用した場合:
```
Grongier.PEX.Message
```
`%classname` として使用した場合:
```
msg.TrainingRequest
```
`%json` として使用した場合:
```
{
"training":{
"name": "name1",
"room": "room1"
}
}
```
ここでは、メッセージがプロセスによってオペレーションに送信され、オペレーションがレスポンス(空の文字列)を送り返すのを確認できます。
以下のような結果が得られます。 ![IrisOperation](https://user-images.githubusercontent.com/77791586/166424497-0267af70-1fd5-40aa-bc71-1b0b0954e67a.png)
`FileOperation` については、以下のように管理ポータルの `%settings` に `path` を入力できます([7.3.](#73-creating-our-operations) の `filename` に関するメモに従った場合は、設定に `filename` を追加できます)。
```
path=/tmp/
filename=tata.csv
```
結果は以下のようになります。 ![FileOperation の設定](https://user-images.githubusercontent.com/77791586/165781963-34027c47-0188-44df-aedc-20537fc0ee32.png)
繰り返しますが、`Python.FileOperation` **オペレーション**を選択して、右サイドバーメニューの [Actions] タブに移動すると、**オペレーション**を**テスト**できます。
(うまくいかない場合は、[テストを有効化](#6-productions)し、本番環境が起動していることを確認してください)。
`Request Type` として使用した場合:
```
Grongier.PEX.Message
```
`%classname` として使用した場合:
```
msg.TrainingRequest
```
`%json` として使用した場合:
```
{
"training":{
"name": "name1",
"room": "room1"
}
}
```
結果は以下のようになります。 ![FileOperation](https://user-images.githubusercontent.com/77791586/166424216-8c250477-4337-4fee-97c9-28e0cca2f406.png)
オペレーションが動作したかどうかを確認するには、toto.csv([7.3.](#73-creating-our-operations) の `filename` に関するメモに従った場合は tata.csv)ファイルと Iris データベースにアクセスして変更内容を確認する必要があります。
次の手順を実行するには、コンテナの中に移動する必要がありますが、[5.2.](#52-management-portal-and-vscode) と [5.3](#53-having-the-folder-open-inside-the-container) を実行した場合には、省略できます。
toto.csv にアクセスするには、ターミナルを開いて、以下を入力する必要があります。
```
bash
```
```
cd /tmp
```
```
cat toto.csv
```
または、必要であれば `"cat tata.csv"` を使用してください。
**重要**: ファイルが存在しない場合、管理ポータルでオペレーションを再起動していない可能性があります。
再起動するには、オペレーションをクリックして再起動を選択してください(または、無効化してからもう一度ダブルクリックして有効化してください)。
もう一度[テスト](#75-testing)する必要があります。
Iris データベースにアクセスするには、管理ポータルにアクセスし、[System Explorer(システムエクスプローラー)] > [SQL] > [Go] を選択する必要があります。 次に、[Execute Query(クエリを実行)] を開始します。
```
SELECT * FROM iris.training
```
# 8. ビジネスプロセス
**ビジネスプロセス**(BP)は、本番環境のビジネスロジックです。 リクエストをプロセスしたり、本番環境の他のコンポーネントにそのリクエストをリレーしたリするために使用されます。
BP には、インスタンスがソースからリクエストを受信するたびに呼び出される `on_request` 関数もあるため、情報を受信して処理し、正しい BO に送信することができます。
これらのプロセスは、VSCode でローカルに作成します。つまり、`src/python/bp.py` ファイルです。
このファイルを保存すると、IRIS でコンパイルされます。
## 8.1. 単純な BP
サービスから受信する情報を処理し、適切に配信する**ビジネスプロセス**を作成しましょう。 オペレーションを呼び出す単純な BP を作成することにします。
この BP は情報をリダイレクトするだけであるため、これを `Router` と呼び、`src/python/bp.py` に作成します。
インポート:
```python
from grongier.pex import BusinessProcess
from msg import FormationRequest, TrainingRequest
from obj import Training
```
コード:
```python
class Router(BusinessProcess):
def on_request(self, request):
"""
リクエストを受信し、フォーメーションリクエストであるかを確認し、そうである場合は
TrainingRequest リクエストを FileOperation と IrisOperation に送信します。IrisOperation が 1 を返すと、PostgresOperation に送信します。
:param request: 受信したリクエストオブジェクト
:return: None
"""
if isinstance(request,FormationRequest):
msg = TrainingRequest()
msg.training = Training()
msg.training.name = request.formation.nom
msg.training.room = request.formation.salle
self.send_request_sync('Python.FileOperation',msg)
self.send_request_sync('Python.IrisOperation',msg)
return None
```
Router は `FormationRequest` タイプのリクエストを受信し、`TrainingRequest` タイプのメッセージを作成して `IrisOperation` と `FileOperation` オペレーションに送信します。 メッセージ/リクエストが求めているタイプのインスタンスでない場合、何も行わず、配信しません。
これらのコンポーネントは、事前に本番環境に**登録済み**です。
情報までに、コンポーネントを登録する手順は、[5.4.](#54-register-components) に従い、以下を使用します。
```
register_component("bp","Router","/irisdev/app/src/python/",1,"Python.Router")
```
## 8.2. 本番環境へのプロセスの追加
プロセスは、こちらで事前に登録済みです。
ただし、プロセスを新規作成する場合は、手動で追加する必要があります。
今後の参考までに、プロセスの登録手順を説明します。
登録には、管理ポータルを使用します。 [Process] の横にある [+] 記号を押すと、[Business Process Wizard(ビジネスプロセスウィザード)] が開きます。
そのウィザードで、スクロールメニューから作成したばかりのプロセスクラスを選択します。
## 8.3. テスト
プロセスをダブルクリックすると、プロセスが有効化されるか、再起動して変更内容が保存されます。
**重要**: この無効化して有効化し直す手順は、変更内容を保存する上で非常に重要な手順です。
**重要**: その後で、**プロセス**を選択し、右のサイドバーメニューの [Actions] タブに移動すると、**プロセス**を**テスト**することができます。
(うまくいかない場合は、[テストを有効化](#6-productions)し、本番環境が開始していることを確認してから、本番環境をダブルクリックして再起動をクリックし、プロセスをリロードしてください)。
こうすることで、プロセスに `msg.FormationRequest` タイプのメッセージを送信します。 `Request Type` として使用した場合:
```
Grongier.PEX.Message
```
`%classname` として使用した場合:
```
msg.FormationRequest
```
`%json` として使用した場合:
```
{
"formation":{
"id_formation": 1,
"nom": "nom1",
"salle": "salle1"
}
}
```
![Router のテスト](https://user-images.githubusercontent.com/77791586/164474368-838fd740-0548-44e6-9bc0-4c6c056f0cd7.png)
すべてがうまくいけば、仮想トレースを表示すると、プロセスとサービスとプロセス間で何が起きたかを確認できるようになります。
ここでは、メッセージがプロセスによってオペレーションに送信され、オペレーションがレスポンスを送り返すのを確認できます。 ![Router の結果](https://user-images.githubusercontent.com/77791586/164474411-efdae647-5b8b-4790-8828-5e926c597fd1.png)
# 9. ビジネスサービス
**ビジネスサービス**(BS)は、本番環境の中核です。 情報を収集し、ルーターに送信するために使用されます。 BS には、フレームワークの情報を頻繁に収集する `on_process_input` 関数もあるため、REST API やその他のサービス、またはサービス自体などの複数の方法で呼び出してサービスのコードをもう一度実行することが可能です。 BS には、クラスにアダプターを割り当てられる `get_adapter_type` 関数もあります。たとえば、`Ens.InboundAdapter` は、サービスがその `on_process_input` を 5 秒おきに呼び出すようにすることができます。
これらのサービスは、VSCode でローカルに作成します。つまり、`python/bs.py` ファイルです。
このファイルを保存すると、IRIS でコンパイルされます。
## 9.1. 単純な BS
CSV を読み取って、`msg.FormationRequest` として各行をルーターに送信するビジネスサービスを作成しましょう。
この BS は csv を読み取るため、これを `ServiceCSV` と呼び、`src/python/bs.py` に作成します。
インポート:
```python
from grongier.pex import BusinessService
from dataclass_csv import DataclassReader
from obj import Formation
from msg import FormationRequest
```
コード:
```python
class ServiceCSV(BusinessService):
"""
csv ファイルを 5 秒おきに読み取り、メッセージとして各行を Python Router プロセスに送信します。
"""
def get_adapter_type():
"""
登録済みのアダプタ名
"""
return "Ens.InboundAdapter"
def on_init(self):
"""
現在のファイルパスをオブジェクトの path 属性に指定されたパスに変更します。
path 属性が指定されていない場合は '/irisdev/app/misc/' に変更します。
:return: None
"""
if not hasattr(self,'path'):
self.path = '/irisdev/app/misc/'
return None
def on_process_input(self,request):
"""
formation.csv ファイルを読み取り、各行のFormationRequest メッセージを作成し、
Python.Router プロセスに送信します。
:param request: リクエストオブジェクト
:return: None
"""
filename='formation.csv'
with open(self.path+filename,encoding="utf-8") as formation_csv:
reader = DataclassReader(formation_csv, Formation,delimiter=";")
for row in reader:
msg = FormationRequest()
msg.formation = row
self.send_request_sync('Python.Router',msg)
return None
```
`FlaskService` はそのままにし、`ServiceCSV` のみを入力することをお勧めします。
ご覧のとおり、ServiceCSV は、独立して機能し、5 秒おき(管理ポータルのサービスの設定にある基本設定で変更できるパラメーター)に on_process_input を呼び出せるようにする InboundAdapter を取得します。
サービスは、5 秒おきに `formation.csv` を開き、各行を読み取って、`Python.Router` に送信される `msg.FormationRequest` を作成します。
これらのコンポーネントは、事前に本番環境に**登録済み**です。
情報までに、コンポーネントを登録する手順は、[5.4.](#54-register-components) に従い、以下を使用します。
```
register_component("bs","ServiceCSV","/irisdev/app/src/python/",1,"Python.ServiceCSV")
```
## 9.2. 本番環境へのサービスの追加
サービスは、こちらで事前に登録済みです。
ただし、サービスを新規作成する場合は、手動で追加する必要があります。
今後の参考までに、サービスの登録手順を説明します。
登録には、管理ポータルを使用します。 [service] の横にある [+] 記号を押すと、[Business Service Wizard(ビジネスサービスウィザード)] が開きます。
そのウィザードで、スクロールメニューから作成したばかりのサービスクラスを選択します。
## 9.3. テスト
サービスをダブルクリックすると、サービスが有効化されるか、再起動して変更内容が保存されます。
**重要**: この無効化して有効化し直す手順は、変更内容を保存する上で非常に重要な手順です。
前に説明したように、サービスは 5 秒おきに自動的に開始するため、ここでは他に行うことはありません。
すべてがうまくいけば、視覚的トレースを表示すると、プロセスとサービスとプロセス間で何が起きたかを確認することができます。
ここでは、メッセージがサービスによってプロセスに送信され、プロセスによってオペレーションに送信され、オペレーションがレスポンスを送り返すのを確認できます。 ![サービス CSV の結果](https://user-images.githubusercontent.com/77791586/164474470-c77c4a06-0d8f-4ba9-972c-ce09b20fa54a.png)
# 10. db-api による外部データベースへのアクセス
このセクションでは、外部データベースにオブジェクトを保存するオペレーションを作成します。 db-api を使用し、その他の Docker コンテナをセットアップして Postgres を設定します。
## 10.1. 前提条件
Postgres を使用するには psycopg2 が必要です。これは、単純なコマンドで Postgres データベースに接続できるようにする Python モジュールです。
これは自動的に完了済みですが、情報までに説明すると、Docker コンテナにアクセスし、pip3 を使って psycopg2 をインストールします。
ターミナルを開始したら、以下を入力します。
```
pip3 install psycopg2-binary
```
または、requirements.txt にモジュールを追加して、コンテナを再構築できます。
## 10.2. 新しいオペレーションの作成
新しいオペレーションは、`src/python/bo.py` ファイルの他の 2 つのオペレーションの後に追加してください。 以下は、新しいオペレーションとインポートです。
インポート:
```python
import psycopg2
```
コード:
```python
class PostgresOperation(BusinessOperation):
"""
トレーニングを Postgres データベースに書き込むオペレーションです。
"""
def on_init(self):
"""
Postgres データベースに接続して接続オブジェクトを初期化する関数です。
:return: None
"""
self.conn = psycopg2.connect(
host="db",
database="DemoData",
user="DemoData",
password="DemoData",
port="5432")
self.conn.autocommit = True
return None
def on_tear_down(self):
"""
データベースへの接続を閉じます。
:return: None
"""
self.conn.close()
return None
def insert_training(self,request:TrainingRequest):
"""
トレーニングを Postgre データベースに挿入します。
:param request: 関数に渡されるリクエストオブジェクト
:type request: TrainingRequest
:return: None
"""
cursor = self.conn.cursor()
sql = "INSERT INTO public.formation ( name,room ) VALUES ( %s , %s )"
cursor.execute(sql,(request.training.name,request.training.room))
return None
def on_message(self,request):
return None
```
このオペレーションは、最初に作成したオペレーションに似ています。 `msg.TrainingRequest` タイプのメッセージを受信すると、psycopg モジュールを使用して、SQL リクエストを実行します。 これらのリクエストは、Postgres データベースに送信されます。
ご覧のとおり、接続はコードに直接書き込まれています。コードを改善するために、他のオペレーションで前に行ったようにし、`host`、`database`、およびその他の接続情報を変数にすることができます。基本値を `db` や `DemoData` にし、管理ポータルで直接変更できるようにします。
これを行うには、以下のようにして `on_init` を変更します。
```python
def on_init(self):
if not hasattr(self,'host'):
self.host = 'db'
if not hasattr(self,'database'):
self.database = 'DemoData'
if not hasattr(self,'user'):
self.user = 'DemoData'
if not hasattr(self,'password'):
self.password = 'DemoData'
if not hasattr(self,'port'):
self.port = '5432'
self.conn = psycopg2.connect(
host=self.host,
database=self.database,
user=self.user,
password=self.password,
port=self.port)
self.conn.autocommit = True
return None
```
これらのコンポーネントは、事前に本番環境に**登録済み**です。
情報までに、コンポーネントを登録する手順は、[5.4.](#54-register-components) に従い、以下を使用します。
```
register_component("bo","PostgresOperation","/irisdev/app/src/python/",1,"Python.PostgresOperation")
```
## 10.3. 本番環境の構成
オペレーションは、こちらで事前に登録済みです。
ただし、オペレーションを新規作成する場合は、手動で追加する必要があります。
今後の参考までに、オペレーションの登録手順を説明します。
登録には、管理ポータルを使用します。 [Operations] の横にある [+] 記号を押すと、[Business Operation Wizard(ビジネスオペレーションウィザード)] が開きます。
そのウィザードで、スクロールメニューから作成したばかりのオペレーションクラスを選択します。
その後、接続を変更する場合は、オペレーションの [parameter] ウィンドウで、[Python] の %settings に変更するパラメーターを追加すれば完了です。 詳細については、「[7.5 テスト](#75-testing)」の 2 つ目の画像をご覧ください。
## 10.4. テスト
オペレーションをダブルクリックすると、オペレーションが有効化されるか、再起動して変更内容が保存されます。
**重要**: この無効化して有効化し直す手順は、変更内容を保存する上で非常に重要な手順です。
**重要**: その後で、**オペレーション**を選択し、右のサイドバーメニューの [Actions] タブに移動すると、**オペレーション**を**テスト**することができます。
(うまくいかない場合は、[テストを有効化](#6-productions)し、本番環境が開始していることを確認してから、本番環境をダブルクリックして再起動をクリックし、オペレーションをリロードしてください)。
`PostGresOperation` については、テーブルは自動的に作成済みです。
こうすることで、オペレーションに `msg.TrainingRequest` タイプのメッセージを送信します。 `Request Type` として使用した場合:
```
Grongier.PEX.Message
```
`%classname` として使用した場合:
```
msg.TrainingRequest
```
`%json` として使用した場合:
```
{
"training":{
"name": "nom1",
"room": "salle1"
}
}
```
以下のとおりです。 ![Postgres のテスト](https://user-images.githubusercontent.com/77791586/166425212-de16bfa0-6b6a-48a8-b333-d4d5cb3770f2.png)
仮想トレースをテストすると、成功が表示されます。
これで、外部データベースに接続することができました。
ここまで、ここに記載の情報に従ってきた場合、プロセスやサービスは新しい `PostgresOperation` を呼び出さない、つまり、管理ポータルのテスト機能を使用しなければ呼び出されないことを理解していることでしょう。
## 10.5. 演習
演習として、`bo.IrisOperation` がブール値を返すように変更し、そのブール値に応じて `bp.Router` に `bo.PostgresOperation` を呼び出すようにしてみましょう。
そうすることで、ここで新しく作成したオペレーションが呼び出されるようになります。
**ヒント**: これは、bo.IrisOperation レスポンスの戻り値のタイプを変更し、新しいメッセージ/レスポンスタイプに新しいブール値プロパティを追加して、bp.Router に `if` 操作を使用すると実現できます。
## 10.6. ソリューション
まず、`bo.IrisOperation` からのレスポンスが必要です。 `src/python/msg.py` の他の 2 つのメッセージの後に、以下のようにして新しいメッセージを作成します。
コード:
```python
@dataclass
class TrainingResponse(Message):
decision:int = None
```
次に、そのレスポンスによって、bo.IrisOperation のレスポンスを変更し、`decision` の値を 1 または 0 にランダムに設定します。
`src/python/bo.py` で 2 つのインポートを追加し、IrisOperation クラスを変更する必要があります。
インポート:
```python
import random
from msg import TrainingResponse
```
コード:
```python
class IrisOperation(BusinessOperation):
"""
トレーニングを iris データベースに書き込むオペレーションです。
"""
def insert_training(self, request:TrainingRequest):
"""
`TrainingRequest` オブジェクトを取り、新しい行を`iris.training` テーブルに挿入し、
`TrainingResponse` オブジェクトを返します。
:param request: 関数に渡されるリクエストオブジェクト
:type request: TrainingRequest
:return: A TrainingResponse message
"""
resp = TrainingResponse()
resp.decision = round(random.random())
sql = """
INSERT INTO iris.training
( name, room )
VALUES( ?, ? )
"""
iris.sql.exec(sql,request.training.name,request.training.room)
return resp
def on_message(self, request):
return None
```
次に、`src/python/bp.py` で `bp.Router` プロセスを変更します。ここでは、IrisOperation からのレスポンスが 1 の場合に PostgresOperation を呼び出すようにします。 新しいコードは以下のようになります。
````python
class Router(BusinessProcess):
def on_request(self, request):
"""
リクエストを受け取り、フォーメーションリクエストであるかどうかをチェックします。
その場合は、TrainingRequest リクエストを FileOperation と IrisOperation に送信し、IrisOperation が 1 を返すと PostgresOperation に送信します。
:param request: 受信したリクエストオブジェクト
:return: None
"""
if isinstance(request,FormationRequest):
msg = TrainingRequest()
msg.training = Training()
msg.training.name = request.formation.nom
msg.training.room = request.formation.salle
self.send_request_sync('Python.FileOperation',msg)
form_iris_resp = self.send_request_sync('Python.IrisOperation',msg)
if form_iris_resp.decision == 1:
self.send_request_sync('Python.PostgresOperation',msg)
return None
````
非常に重要: オペレーションの呼び出しには、**send_request_async** ではなく、必ず **send_request_sync** を使用する必要があります。そうでない場合、この操作はブール値のレスポンスを受け取る前に実行されます。
テストする前に、必ず変更したすべてのサービス、プロセス、およびオペレーションをダブルクリックして再起動してください。これを行わない場合、変更内容は適用されません。
テスト後、視覚的トレースで、csv で読み取られたおよそ半数のオブジェクトがリモートデータベースにも保存されていることがわかります。
`bs.ServiceCSV` を開始するだけでテストできることに注意してください。リクエストは自動的にルーターに送信され、適切に配信されます。
また、サービス、オペレーション、またはプロセスをダブルクリックしてリロードを押すか、VSCode で保存した変更を適用するには再起動を**押す必要がある**ことにも注意してください。
# 11. REST サービス
ここでは、REST サービスを作成して使用します。
## 11.1. 前提条件
Flask を使用するには、flask のインストールが必要です。これは、REST サービスを簡単に作成できるようにする Python モジュールです。 **これは、自動的に実行済みです**が、今後の情報までに説明すると、Docker コンテナ内にアクセスして iris python に flask をインストールします。 ターミナルを開始したら、以下を入力します。
```
pip3 install flask
```
または、requirements.txt にモジュールを追加して、コンテナを再構築できます。
## 11.2. サービスの作成
REST サービスを作成するには、API を本番環境にリンクするサービスが必要です。このために、`src/python/bs.py` の `ServiceCSV` クラスの直後に新しい単純なサービスを作成します。
```python
class FlaskService(BusinessService):
def on_init(self):
"""
API の現在のターゲットをオブジェクトの target 属性に指定されたターゲットに変更します。
または、target 属性が指定されていない場合は 'Python.Router' に変更します。
:return: None
"""
if not hasattr(self,'target'):
self.target = "Python.Router"
return None
def on_process_input(self,request):
"""
API から直接 Python.Router プロセスに情報を送信するために呼び出されます。
:return: None
"""
return self.send_request_sync(self.target,request)
```
このサービスに on_process_input を行うと、リクエストが Router に転送されます。
これらのコンポーネントは、事前に本番環境に**登録済み**です。
情報までに、コンポーネントを登録する手順は、[5.4.](#54-register-components) に従い、以下を使用します。
```
register_component("bs","FlaskService","/irisdev/app/src/python/",1,"Python.FlaskService")
```
REST サービスを作成するには、Flask を使って `get` と `post` 関数を管理する API を作成する必要があります。 新しいファイルを `python/app.py` として作成してください。
```python
from flask import Flask, jsonify, request, make_response
from grongier.pex import Director
import iris
from obj import Formation
from msg import FormationRequest
app = Flask(__name__)
# GET Infos
@app.route("/", methods=["GET"])
def get_info():
info = {'version':'1.0.6'}
return jsonify(info)
# GET all the formations
@app.route("/training/", methods=["GET"])
def get_all_training():
payload = {}
return jsonify(payload)
# POST a formation
@app.route("/training/", methods=["POST"])
def post_formation():
payload = {}
formation = Formation()
formation.nom = request.get_json()['nom']
formation.salle = request.get_json()['salle']
msg = FormationRequest(formation=formation)
service = Director.CreateBusinessService("Python.FlaskService")
response = service.dispatchProcessInput(msg)
return jsonify(payload)
# GET formation with id
@app.route("/training/", methods=["GET"])
def get_formation(id):
payload = {}
return jsonify(payload)
# PUT to update formation with id
@app.route("/training/", methods=["PUT"])
def update_person(id):
payload = {}
return jsonify(payload)
# DELETE formation with id
@app.route("/training/", methods=["DELETE"])
def delete_person(id):
payload = {}
return jsonify(payload)
if __name__ == '__main__':
app.run('0.0.0.0', port = "8081")
```
Flask API は Director を使用して、前述のものから FlaskService のインスタンスを作成してから適切なリクエストを送信することに注意してください。
上記の子k-度では、POST フォーメンション関数を作成しました。希望するなら、これまでに学習したものすべてを使用して、適切な情報を get/post するように、他の関数を作成することが可能です。ただし、これに関するソリューションは提供されていません。
## 11.3. テスト
Python Flask を使用して flask アプリを開始しましょう。
![Flask app.py の開始方法 ](https://user-images.githubusercontent.com/77791586/165757717-d62131d7-039a-4ed5-835f-ffe32ebd2547.mov)
最後に、Router サービスをリロードしてから、任意の REST クライアントを使用してサービスをテストできます。
(Mozilla の RESTer として)REST サービスを使用するには、以下のようにヘッダーを入力する必要があります。
```
Content-Type : application/json
```
![REST のヘッダー](https://user-images.githubusercontent.com/77791586/165522396-154a4ef4-535b-44d7-bcdd-a4bfd2f574d3.png)
ボディは以下のようになります。
```
{
"nom":"testN",
"salle":"testS"
}
```
![REST のボディ](https://user-images.githubusercontent.com/77791586/166432001-0cca76a8-bd90-4d3b-9dcb-80b309786bc0.png)
認証は以下のとおりです。
ユーザー名:
```
SuperUser
```
パスワード:
```
SYS
```
![REST の認証](https://user-images.githubusercontent.com/77791586/165522730-bb89797a-0dd1-4691-b1e8-b7c491b53a6a.png)
最後に、結果は以下のようになります。 ![REST の結果](https://user-images.githubusercontent.com/77791586/165522839-feec14c0-07fa-4d3f-a435-c9a06a544785.png)
# 12. グローバル演習
Iris DataPlatform とその[フレームワーク](#2-framework)のすべての重要な概念について学習したので、グローバル演習で腕試しをしましょう。この演習では、新しい BS と BP を作成し、BO を大きく変更して、新しい概念を Python で探ります。
## 12.1. 指示
こちらの**エンドポイント** `https://lucasenard.github.io/Data/patients.json` を使用して、`患者と歩数`に関する情報を自動的に**取得**するようにします。 次に、ローカルの csv ファイルに書き込む前に、各患者の平均歩数を計算します。
必要であれば、フォーメーション全体または必要な箇所を読むか、以下の[ヒント](#122-hints)を使って、ガイダンスを得ることをお勧めします。
管理ポータルでコンポーネントにアクセスできるように、[コンポーネントの登録](#54-register-components)を忘れずに行いましょう。
すべてを完了し、テストしたら、または演習を完了するのに十分なヒントを得られなかった場合は、全過程をステップごとに説明した[ソリューション](#123-solutions)をご覧ください。
## 12.2. ヒント
ここでは、演習を行うためのヒントを紹介します。
読んだ分だけヒントが得られてしまうため、必要な箇所のみを読み、毎回すべてを読まないようにすることをお勧めします。
たとえば、[bs](#1232-bs) の「[情報の取得](#12211-get-information)」と「[リクエストによる情報の取得](#12211-get-information-with-request)」のみを読み、他は読まないようにしましょう。
### 12.2.1. bs
#### 12.2.1.1. 情報の取得
エンドポイントから情報を取得するには、Python の `requests` モジュールを検索し、`json` と `json.dumps` を使用して文字列に変換してから bp に送信します。
#### 12.2.1.2. リクエストによる情報の取得
オンライン Python Web サイトまたはローカルの Python ファイルを使用してリクエストを使用し、取得した内容をさらに深く理解するために、出力とそのタイプを出力します。
#### 12.2.1.3. リクエストによる情報の取得とその使用
新しいメッセージタイプと情報を保持するオブジェクトタイプを作成し、プロセスに送信して平均を計算します。
#### 12.2.1.4. 情報の取得のソリューション
リクエストを使用してデータを取得する方法と、この場合に部分的に、それをどう処理するかに関するソリューションです。
```python
r = requests.get(https://lucasenard.github.io/Data/patients.json)
data = r.json()
for key,val in data.items():
...
```
繰り返しますが、オンライン Python Web サイトまたはローカルの Python ファイルでは、key、val、およびタイプを出力し、それらを使用して何をできるかを理解することが可能です。
`json.dumps(val)` を使用して `val` を格納してから、SendRequest の後にプロセスにいるときに、`json.loads(request.patient.infos)` を使用してその val を取得します(`val` の情報を `patient.infos` に格納した場合)。
### 12.2.2. bp
#### 12.2.2.1. 平均歩数と dict
`statistics` は、算術演算を行うために使用できるネイティブライブラリです。
#### 12.2.2.2. 平均歩数と dict: ヒント
Python のネイティブ `map` 関数では、たとえばリスト内または辞書内の情報を分離することができます。
`list` ネイティブ関数を使用して、`map` の結果をリストに変換し直すことを忘れないようにしましょう。
#### 12.2.2.3. 平均歩数と dict: map を使用する
オンライン Python Web サイトまたはローカルの Python ファイルを使用して、以下のように、リストのリストまたは辞書のリストの平均を計算することができます。
```python
l1 = [[0,5],[8,9],[5,10],[3,25]]
l2 = [["info",12],["bidule",9],[3,3],["patient1",90]]
l3 = [{"info1":"7","info2":0},{"info1":"15","info2":0},{"info1":"27","info2":0},{"info1":"7","info2":0}]
#最初のリストの最初の列の平均(0/8/5/3)
avg_l1_0 = statistics.mean(list(map(lambda x: x[0]),l1))
#最初のリストの 2 つ目列の平均(5/9/10/25)
avg_l1_1 = statistics.mean(list(map(lambda x: x[1]),l1))
#12/9/3/90 の平均
avg_l2_1 = statistics.mean(list(map(lambda x: x[1]),l2))
#7/15/27/7 の平均
avg_l3_info1 = statistics.mean(list(map(lambda x: int(x["info1"])),l3))
print(avg_l1_0)
print(avg_l1_1)
print(avg_l2_1)
print(avg_l3_info1)
```
#### 12.2.2.4. 平均歩数と dict: 解答
リクエストに、日付と歩数の dict の json.dumps である infos 属性を持つ患者が保持されている場合、以下のようにして平均歩数を計算できます。
```python
statistics.mean(list(map(lambda x: int(x['steps']),json.loads(request.patient.infos))))
```
### 12.2.3. bo
`bo.FileOperation.WriteFormation` に非常に似たものを使用できます。
`bo.FileOperation.WritePatient` のようなものです。
## 12.3. ソリューション
### 12.3.1. obj と msg
`obj.py` に以下を追加できます。
```python
@dataclass
class Patient:
name:str = None
avg:int = None
infos:str = None
```
`msg.py` に以下を追加できます。
インポート:
```python
from obj import Formation,Training,Patient
```
コード:
```python
@dataclass
class PatientRequest(Message):
patient:Patient = None
```
この情報を単一の obj に保持し、get リクエストから得る dict の str を直接 `infos` 属性に入れます。 平均は、プロセスで計算されます。
### 12.3.2. bs
`bs.py` に以下を追加できます。 インポート:
```python
import requests
```
コード:
```python
class PatientService(BusinessService):
def get_adapter_type():
"""
登録されたアダプタの名前
"""
return "Ens.InboundAdapter"
def on_init(self):
"""
API の現在のターゲットをオブジェクトの target 属性に指定されたターゲットに変更します。
target 属性が指定されていない場合は、'Python.PatientProcess' に変更します。
API の現在の api_url をオブジェクトの taget 属性に指定された api_url に変更します。
api_url 属性が指定されていない場合は、'https://lucasenard.github.io/Data/patients.json' に変更します。
:return: None
"""
if not hasattr(self,'target'):
self.target = 'Python.PatientProcess'
if not hasattr(self,'api_url'):
self.api_url = "https://lucasenard.github.io/Data/patients.json"
return None
def on_process_input(self,request):
"""
API にリクエストを行い、検出される患者ごとに Patient オブジェクトを作成し、
ターゲットに送信します。
:param request: サービスに送信されたリクエスト
:return: None
"""
req = requests.get(self.api_url)
if req.status_code == 200:
dat = req.json()
for key,val in dat.items():
patient = Patient()
patient.name = key
patient.infos = json.dumps(val)
msg = PatientRequest()
msg.patient = patient
self.send_request_sync(self.target,msg)
return None
```
ターゲットと api url 変数を作成します(on_init を参照)。
`requests.get` が `req` 変数に情報を入れた後、json で情報を抽出する必要があります。これにより `dat` が dict になります。
dat.items を使用して、患者とその情報を直接イテレートできます。
次に、patient オブジェクトを作成し、json データを文字列に変換する `json.dumps` を使用して `val` を文字列に変換し、`patient.infos` 変数に入れます。
次に、プロセスを呼び出す `msg.PatientRequest` の `msg` リクエストを作成します。
コンポーネントの登録を忘れずに行いましょう。 [5.4.](#54-register-components) に従い、以下を使用します。
```
register_component("bs","PatientService","/irisdev/app/src/python/",1,"Python.PatientService")
```
### 12.3.3. bp
`bp.py` に以下を追加できます。 インポート:
```python
import statistic
```
コード:
```python
class PatientProcess(BusinessProcess):
def on_request(self, request):
"""
リクエストを取り、PatientRequest であるかをチェックします。その場合は、患者の平均歩数を計算し、
リクエストを Python.FileOperation サービスに送信します。
:param request: サービスに送信されたリクエストオブジェクト
:return: None
"""
if isinstance(request,PatientRequest):
request.patient.avg = statistics.mean(list(map(lambda x: int(x['steps']),json.loads(request.patient.infos))))
self.send_request_sync('Python.FileOperation',request)
return None
```
取得したばかりのリクエストを取り、`PatientRequest` である場合は、歩数の平均を計算して、FileOperation に送信します。 これは患者の `avg` 変数に正しい情報を挿入します(詳細は、bp のヒントを参照してください)。
コンポーネントの登録を忘れずに行いましょう。 [5.4.](#54-register-components) に従い、以下を使用します。
```
register_component("bp","PatientProcess","/irisdev/app/src/python/",1,"Python.PatientProcess")
```
### 12.3.4. bo
`bo.py` に以下を追加できます。`FileOperation` クラス内:
```python
def write_patient(self, request:PatientRequest):
"""
患者の名前と平均歩数をファイルに書き込みます。
:param request: リクエストメッセージ
:type request: PatientRequest
:return: None
"""
name = ""
avg = 0
if request.patient is not None:
name = request.patient.name
avg = request.patient.avg
line = name + " avg nb steps : " + str(avg) +"\n"
filename = 'Patients.csv'
self.put_line(filename, line)
return None
```
前に説明したとおり、`FileOperation` は以前に登録済みであるため、もう一度登録する必要はありません。
## 12.4. テスト
[7.4.](#74-adding-the-operations-to-the-production) を参照して、オペレーションを追加しましょう。
[9.2.](#92-adding-the-service-to-the-production) を参照して、サービスを追加しましょう。
次に、管理ポータルに移動し、前と同じように行います。 新しいサービスは、InboundAdapter を追加したため、自動的に実行されます。
`toto.csv` と同様にして、`Patients.csv` を確認してください。
## 12.5. グローバル演習のまとめ
この演習を通じて、メッセージ、サービス、プロセス、およびオペレーションの作成を学習し、理解することができました。
Python で情報をフェッチする方法とデータに対して単純なタスクを実行する方法を発見しました。
すべての完成ファイルは、GitHub の [`solution` ブランチ](https://github.com/LucasEnard/formation-template-python/tree/solution)にあります。
# 13. まとめ
このフォーメーションを通じ、csv ファイルから行を読み取り、読み取ったデータを db-api を使ってローカル txt、IRIS データベース、および外部データベースに保存できる IrisPython のみを使用して、フル機能の本番環境を作成しました。
また、POST 動詞を使用して新しいオブジェクトを保存する REST サービスも追加しました。
InterSystems のインターオペラビリティフレームワークの主要要素を発見しました。
これには、Docker、VSCode、および InterSystems の IRIS 管理ポータルを使用して実行しました。