#7 PowerApps アプリ で撮影した画像を FaceAPI で感情分析してみました（累積分析）


## 概要
PowerApps のカメラで撮影した画像を API Management 経由で FaceAPI に転送し、その画像の分析結果「感情、性別、年齢」を PoweApps に表示するアプリの実装手順を数回に分け記載しています。また、並行して、その分析結果を CosmosDB に保存しておき、PowerApps からの累積情報取得リクエストにより累積クエリ結果「感情分布、性別分布」を PowerApps に返し、円グラフ表示する機能の実装手順も複数回に分け記載します。なお、PowerApp の画面作成については省略し、APIコール部分とその戻り値の部分に焦点をあてて記載しています。
本アプリの全体構成は下図となります。 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544022/d2dc8229-00c8-6a00-d009-f4ac860de50a.png)

今回（第７回目）の構成箇所は下図の部分となります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544022/476b1203-d698-329d-3bfb-54b3f5bdb3cd.png)

第７回目は、[第６回](https://qiita.com/turupon/items/2475ae4e9e39e1faf07a) で作成した PowerAppsアプリケーションでカメラ撮影された画像データのFaceAPI分析結果「感情、感情確率、性別、年齢」を CosmosDB へ登録し、その蓄積された情報を取得するための 前段階のローカルサンプルプログラムを作成します。

---

## 実行環境
macOS Big Sur 11.3
Python 3.8.3

---

## Azure Cosmos DB 関連 
最初にデータを保存する CosmosDB の Conteiner を作成します。

### Cosmos DB アカウントの作成 
画像分析結果情報「感情、感情確率、性別、年齢」の保存先として Azure Cosmos DB を使用します。アカウント名を「iturucosmosdx」として CosmosDB のアカウントを以下の画面内容で作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544022/5f160342-0170-fc49-b751-41f07facdef0.png)

上記アカウントの画面左側項目から「キー」を選択し、表示されている「エンドポイント（URI）」と「キー（プライマリキー）」を取得します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544022/4d2ca485-0da4-c0f6-acbb-03d0128c33be.png)

---

### Cosmos DB の Container の作成
上記アカウントの画面左側項目から「データエクスプローラ」を選択し、画面上部の「New Container」タブから「New Container」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544022/68ce13db-5038-50d9-89b3-e3a85ebec459.png)

「Add Container」画面において、以下を定義し、画面下部の「OK」ボタンを押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544022/7209ab4b-6c3f-3d18-d334-03a97eab3f10.png)

作成後、以下のようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544022/73d3ce76-a564-593a-4220-29d264d14d17.png)

---

### Cosmos DB の Container への Item の登録
上記アカウントの「データエクスプローラ」画面から、先程作成した Conteiner を選択し、画面上部の「New Item」ボタンを押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544022/dee588d1-28e1-a361-bc21-c8d6dfa311ee.png)

表示されるエディターに、今回登録されるべき情報を追加してみます。

```
{
    "id": "1",
    "age": 27,
    "gender": "female",
    "emotion": "幸",
    "data": 100
}
```

画面上部の「Save」ボタンを押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544022/1a35db26-59f4-43c4-73a6-1c8de77dca8d.png)

以下のように問題なくデータが登録されていることから、CosmosDB の Conteiner が正常に作成されたことを確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544022/3566b4d1-1353-9aaf-8c92-24de9aa2e951.png)

---

## CosmosDB へのデータ操作 関連

### CosmosDB へのデータ登録
画像データの分析結果情報を CosmosDB に登録するローカルプログラムは以下となります。

```CosmosSampleCreate.py
import pprint
from datetime import datetime
from azure.cosmos import exceptions, CosmosClient, PartitionKey

endpoint = 'https://iturucosmosdx.documents.azure.com:443/'
key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
databaseName = 'GetFaceAPIData'
containerName = 'FaceContainer'

def main():

  # CosmosDBへの接続
  client = CosmosClient(endpoint, key)
  db = client.get_database_client(databaseName)
  container = db.get_container_client(containerName)

  # idの作成
  now_ts_str = str(datetime.now().timestamp())

  # 登録データ
  newItem = {
    "id": now_ts_str,
    "age": 36,
    "gender": "female",
    "emotion": "幸",
    "data": 84.7
  }

  # アイテムの追加
  item = container.create_item(newItem)
  print(' --- create_item --- ')
  pprint.pprint(item)
  print()

  # アイテムの取得
  items = container.read_all_items()
  print(' --- read_all_items --- ')
  pprint.pprint(list(items))
  print()

main()
```

実行結果は以下となります。

```
$ python CosmosSampleCreate.py
 --- create_item --- 
{'_attachments': 'attachments/',
 '_etag': '"b1017372-0000-2400-0000-60a451e00000"',
 '_rid': '-MxGAMsRyx4uAAAAAAAAAA==',
 '_self': 'dbs/-MxGAA==/colls/-MxGAMsRyx4=/docs/-MxGAMsRyx4uAAAAAAAAAA==/',
 '_ts': 1621381600,
 'age': 32,
 'data': 81.7,
 'emotion': '幸',
 'gender': 'female',
 'id': '1621381600.242014'}

 --- read_all_items --- 
 [{'_attachments': 'attachments/',
  '_etag': '"b1017372-0000-2400-0000-60a451e00000"',
  '_rid': '-MxGAMsRyx4uAAAAAAAAAA==',
  '_self': 'dbs/-MxGAA==/colls/-MxGAMsRyx4=/docs/-MxGAMsRyx4uAAAAAAAAAA==/',
  '_ts': 1621381600,
  'age': 32,
  'data': 81.7,
  'emotion': '幸',
  'gender': 'female',
  'id': '1621381600.242014'}]
```

問題なく CosmosDB にデータが登録でき、その登録データを確認することができました。

---

### CosmosDB からのデータ取得
CosmosDB の分析結果情報から、感情毎の件数と性別毎の件数を取得するローカルプログラムは以下となります。戻り値としては、PowerAppsアプリケーション上でグラフ表示させるためにArray型のJSON形式にしておきます。

```CosmosSampleSummary.py
import pprint
from datetime import datetime
from azure.cosmos import exceptions, CosmosClient, PartitionKey
from collections import OrderedDict

endpoint = 'https://iturucosmosdx.documents.azure.com:443/'
key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
databaseName = 'GetFaceAPIData'
containerName = 'FaceContainer'

# emotion定義
emobase = ['anger', 'contempt', 'disgust', 'fear', 'happiness', 'neutral', 'sadness', 'surprise']
emolist = ['怒', '侮', '嫌', '恐', '幸', '無', '悲', '驚']

# gender定義
genlist = ['male', 'female']

def main():

  # CosmosDBへの接続
  client = CosmosClient(endpoint, key)
  db = client.get_database_client(databaseName)
  container = db.get_container_client(containerName)
  
  # 感情毎にデータ件数を取得する
  print("------ emotion -----")
  emo_list = []
  for el in emolist:
    query = ("SELECT VALUE COUNT(1) FROM items i WHERE i.emotion = '{}'").format(el)
    items = container.query_items(query, enable_cross_partition_query=True)
    emo_list.append(OrderedDict(感=el, 値=list(items)[0]))
  pprint.pprint(emo_list)

  # 性別毎にデータ件数を取得する
  print("------ genger -----")
  gen_list = []
  for gl in genlist:
    query = ("SELECT VALUE COUNT(1) FROM items i WHERE i.gender = '{}'").format(gl)
    items = container.query_items(query, enable_cross_partition_query=True)
    gen_list.append(OrderedDict(性=gl, 値=list(items)[0]))
  pprint.pprint(gen_list)

  # 戻り値の生成
  print("------ return -----")
  return_dict = {'emo_graph': emo_list, 'gen_graph': gen_list}   # 辞書データの作成
  pprint.pprint(return_dict)
  print(type(return_dict))
  
main()
```

実行結果は以下となります。

```
$ python CosmosSampleSummary.py
------ emotion -----
[OrderedDict([('感', '怒'), ('値', 1)]),
 OrderedDict([('感', '侮'), ('値', 1)]),
 OrderedDict([('感', '嫌'), ('値', 0)]),
 OrderedDict([('感', '恐'), ('値', 0)]),
 OrderedDict([('感', '幸'), ('値', 20)]),
 OrderedDict([('感', '無'), ('値', 13)]),
 OrderedDict([('感', '悲'), ('値', 5)]),
 OrderedDict([('感', '驚'), ('値', 3)])]
------ genger -----
[OrderedDict([('性', 'male'), ('値', 25)]),
 OrderedDict([('性', 'female'), ('値', 18)])]
------ return -----
{'emo_graph': [OrderedDict([('感', '怒'), ('値', 1)]),
               OrderedDict([('感', '侮'), ('値', 1)]),
               OrderedDict([('感', '嫌'), ('値', 0)]),
               OrderedDict([('感', '恐'), ('値', 0)]),
               OrderedDict([('感', '幸'), ('値', 20)]),
               OrderedDict([('感', '無'), ('値', 13)]),
               OrderedDict([('感', '悲'), ('値', 5)]),
               OrderedDict([('感', '驚'), ('値', 3)])],
 'gen_graph': [OrderedDict([('性', 'male'), ('値', 25)]),
               OrderedDict([('性', 'female'), ('値', 18)])]}
<class 'dict'>
```

問題なく CosmosDB にあるデータから感情毎の件数と性別毎の件数をArray型の辞書型で取得できることを確認できました。

---

## 次回について
次回[（第８回）](https://qiita.com/turupon/items/cc61092729a696b30ef1)はCosmosDBへのデータ登録ローカルプログラムを [第２回](https://qiita.com/turupon/items/6f771a14aa011052ac23) で作成した Azure Functions に追加します。

---

## 参考情報
以下の情報を参考にさせていただきました。感謝申し上げます。
[Azure Cosmos DB - 基本操作（Python）](https://qiita.com/takmot/items/f8cfe499b9b98bec4031)
[PythonからAzure Comos DBのデータ操作](https://qiita.com/tamo_breaker/items/38eae9e5a24fade35a36)
[Azure Functions App × Python で Azure CosmosDB にアクセスしてみる](https://qiita.com/annie/items/2245dd548b9d8c427f77)
[Azure cosmosDB のアラート設定について](https://qiita.com/kewpie134134/items/2fd28f9e31d1888fdc96)