Power Automate Desktop「俺のデスクトップはきたない」を何とかする

## 概要  

Power Automate Desktop（以下PAD) と自然言語処理APIプラットフォーム「[COTOHA API](https://api.ce-cotoha.com/contents/index.html)」の連携を検証しました。
俺のデスクトップはきたないです。興味を持ったことをデスクトップにポイポイして気づくとこんな感じです。完全にダメな人です。そこで、身近なことからコツコツということでデスクトップの掃除＋分析を題材にしてみようと思います。
![2021-03-06-12-33-55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/5eecf321-b4da-0008-b65e-3b2c3c036867.png)

散らかったブックマークのデータを収集してタグ付けを行います。
![2021-03-06-12-37-16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/c346a805-06ee-f233-1150-ca289904daa3.png)

データとして収集整理してしまえばお好みでいろいろできちゃいます。
![2021-03-06-12-37-55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/51c3bc02-a79f-adc3-ea16-f4718ef3a5ae.png)


## 今回やること

PADをつかってファイル操作、変数を使ったリストやデータテーブル作成、なるべく速いExcel操作、JSON、カスタムオブジェクトによるCOTOHA APIとの連携からキーワード抽出を行います。

![ezgif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/245e62e3-18b2-9aa9-8ff9-e2c0a8767bb4.gif)


## 準備

- [Power Automate Desktop](https://flow.microsoft.com/ja-jp/desktop/)のインストール。

https://flow.microsoft.com/ja-jp/desktop/

  [@Maekawaさん記事](https://qiita.com/maekawawawa/items/9edbd4c33b780fb2b8ec)と[@K2_kzyさんの記事](https://qiita.com/K2_kzy/items/109fccae160fbb874fad)が参考になります。

- [COTOHA API](https://api.ce-cotoha.com/contents/index.html) for Developersアカウントの取得

https://api.ce-cotoha.com/contents/index.html

  [スタートガイド](https://api.ce-cotoha.com/contents/gettingStarted.html)を参考に認証情報を準備しておきます。

## 注意事項等

- 長めのフローになるため要点のアクションのみ解説します。
- ファイル操作がフローに含まれるためサンプルコードの利用は十分理解した上でお願いいたします。
- 自己責任でお願いいたします。
- API利用は規約にしたがってください。詳しくは[FAQ](https://api.ce-cotoha.com/contents/faq.html参照。
- 2021年3月の情報です。

## フロー概要

1. パスを取得や保存フォルダーの作成等の準備作業
2. ブックマークファイルの各種データを読み取ってデーターテーブルを生成およファイルのを移動、Excelに転写
3. 作成したデータテーブルからCOTOHA APIでキーワード抽出を行いExcelに追記
4. 終了

## フロー作成  

シンプルな構成ですが、より分かりやすくするためにMainではコントロールのみ行い、処理はサブフローで行います。Mainの「サブフローの実行」アクションはサブフローを作るまで選択肢に出てこないため作りながら追加します。

![2021-03-09-00-06-34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/c095f206-3b94-d5ca-1fe3-1c02726f8743.png)


### Get_readyサブフローの作成

![2021-03-09-00-07-27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/2fb956c1-5c41-2d0e-952a-00a6f954e077.png)


####2「フォルダー内のファイルを取得」

ファイルフィルターに*.urlを指定することでブックマークファイルのパスリスト変数Filesを作成できます。
Filesは各種プロパティをもっているのでこの情報を利用します。
Steamゲームのショートカットも「.url」であるためご注意ください。
![2021-03-06-21-32-26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/93b2ed80-1a1d-d38c-e0ed-c89a1da398b6.png)

![2021-03-06-21-45-05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/0d431f9d-8640-9d66-79fa-e0af1bca94ce.png)

![2021-03-06-21-48-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/193631d5-d0b7-bc77-4f54-839e4576fbcc.png)


####5「フォルダーの作成」

ブックマークファイルを移動するフォルダーを```Bookmarks%FormattedDateTime%```としておきます。

![](2021-03-06-21-51-51.png)![2021-03-06-21-51-51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/805bd34e-66d3-2a19-3021-52f74749b53d.png)


####6「変数の設定」

変数でデータテーブルを作成し列名をハードコートします。```%{['Title', 'Created', 'URL', 'TAG1', 'TAG2', 'TAG3'] }%```「^」とせず1行目にしているのはデータテーブル内で列名にしてしまうとExcelに転写できなくなるためです。
また今後取得したデータをこのデータテーブルに結合します。
![](2021-03-06-21-56-57.png)![2021-03-06-21-56-57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/47addbd7-48cb-9693-1c19-ffdbf9d23ef3.png)

![](2021-03-06-21-59-31.png)![2021-03-06-21-59-31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/2a18bc41-5fa0-426d-18a3-a308e06849a1.png)


####　```Get_ready```

```
Folder.GetSpecialFolder SpecialFolder: Folder.SpecialFolder.DesktopDirectory SpecialFolderPath=> SpecialFolderPath
Folder.GetFiles Folder: SpecialFolderPath FileFilter: $'''*.url''' IncludeSubfolders: False FailOnAccessDenied: True SortBy1: Folder.SortBy.NoSort SortDescending1: False SortBy2: Folder.SortBy.NoSort SortDescending2: False SortBy3: Folder.SortBy.NoSort SortDescending3: False Files=> Files
DateTime.Local DateTimeFormat: DateTime.DateTimeFormat.DateAndTime CurrentDateTime=> CurrentDateTime
Text.FromCustomDateTime DateTime: CurrentDateTime CustomFormat: $'''yyyyMMdd''' Result=> FormattedDateTime
Folder.Create FolderPath: SpecialFolderPath FolderName: $'''Bookmarks%FormattedDateTime%''' Folder=> NewFolder
SET Table TO {['Title', 'Created', 'URL', 'TAG1', 'TAG2', 'TAG3'] }
```

## Make_excel_and_move_filesサブフローの作成

このサブフローでは変数の記事に書いたことをフルに使っていきます。
データテーブルを変数に作成することで、一括で転写することができ、セル毎でexcelに書き込むよりフローもすっきりします。

![](2021-03-09-00-14-18.png)![2021-03-09-00-14-18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/8ad8e3d2-a204-b471-0e73-1100c6b6dd90.png)


####2「ファイルからテキストを読み取ります」

このアクションでブックマークファイルの中のURLを読み取ることができます。
![](2021-03-06-22-09-01.png)![2021-03-06-22-09-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/5ff11343-1b46-db65-bbc8-0bbeaac27ae8.png)

![](2021-03-06-22-10-49.png)![2021-03-06-22-10-49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/8a188215-a8ca-690e-ba16-048673ce5133.png)


####5「変数の設定」

FilesContentsのURLはURL=となっているためスライスを使ってURL部分を抜き出します。URLを抜き出す場合、正規表現を使うことが多いかもしれませんがPADは%が特別な意味を持つためURL抜き出しではうまく機能しないケースがあります。このケースではスライスが簡単です。
![](2021-03-06-22-18-18.png)![2021-03-06-22-18-18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/4a20aa37-4110-79f6-22e6-176d17076abe.png)

FilesContentsリストのインデックス番号1、文字列のインデックス番号4より後を指定しています。
####6「変数の設定」
この段階ではTAGをAPIから取得していないためnullを作成します。```%''%```
![](2021-03-06-22-43-59.png)![2021-03-06-22-43-59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/015f114c-19dc-974d-c46b-b98d3615465c.png)


####7「変数の設定」

行リストを作成します。
3～6で取得した値をリスト化しています。
![](2021-03-06-22-45-56.png)![2021-03-06-22-45-56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/05718248-6b46-a73e-c20d-abf24a7e1ac6.png)

![](2021-03-06-22-46-31.png)![2021-03-06-22-46-31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/e29cd7ee-962b-c9d2-f6a2-c51ae2a60b56.png)


####8「変数の設定」
Get_Readyサブフローで準備した変数Tableに結合します。
![](2021-03-06-22-49-22.png)![2021-03-06-22-49-22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/bf8f1915-589d-e2b5-f2e8-30173621f5a4.png)

####9「ファイルの移動」
Get_Readyサブフローで準備したフォルダーに移動します。
![](2021-03-06-22-50-31.png)![2021-03-06-22-50-31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/f5e00607-aaa2-a998-e3fd-3a81a4d44a47.png)


####15 キーの送信

Excelをショートカットで素早く操作できます。
昇竜拳はでません。
いろいろ試すと楽しいです。
![](2021-03-06-23-06-32.png)![2021-03-06-23-06-32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/41f39bf0-1f35-6e7e-6fc7-392faca70724.png)

#### ```Make_excel_and_move_files```

```
File.ReadTextAsList File: CurrentItem Encoding: File.TextFileEncoding.UTF8 Contents=> FileContents
SET Title TO CurrentItem.NameWithoutExtension
SET Created TO CurrentItem.CreationTime
SET URL TO FileContents[1][4:]
SET null TO $'''%''%'''
SET list TO [Title, Created, URL, null, null, null]
SET Table TO Table + list
File.Move Files: CurrentItem Destination: $'''%SpecialFolderPath%/Bookmarks%FormattedDateTime%''' IfFileExists: File.IfExists.DoNothing MovedFiles=> MovedFiles
END
Excel.Launch Visible: True LoadAddInsAndMacros: False Instance=> ExcelInstance
Excel.WriteCell Instance: ExcelInstance Value: Table Column: 1 Row: 1
UIAutomation.Windows.FocusByInstanceOrHandle WindowInstance: ExcelInstance
UIAutomation.Windows.SetStateByInstanceOrHandle WindowInstance: ExcelInstance State: UIAutomation.WindowState.Maximized
MouseAndKeyboard.SendKeys TextToSend: $'''{Control}({A})
{ALT}({O}{C}{A})
{ALT}(hba)
{Control}{Home}{Right}
''' DelayBetweenKeystrokes: 10 SendTextAsHardwareKeys: False
```

## Cotoha_APIサブフローの作成

![2021-03-09.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/2fc04a43-687d-5554-ab08-a152b54e1d27.jpeg)

クライアントID、クライアントシークレット、アクセストークン発行URL、APIベースURLはマイページで発行されます。
これらの情報についてはフローの共有をことを考えてハードコードせずテキストとして外にだしてあります。

### 1～9　アクセストークンの取得

COTOHA APIのリファレンスを参考にフローを作成します。
アクセストークン取得
https://api.ce-cotoha.com/contents/reference/accesstoken.html

#### 1 「ファイルからテキストを読み取ります」

![2021-03-09-00-48-49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/1afa29ef-ccbe-6c57-678f-abe9fe970797.png)


今回は認証情報Secret.txtをデスクトップ作成しています。デスクトップを片づけるフローのなのにデスクトップにファイルを置くとはけしからんと思いますが、検証記事なので勘弁してください。

![2021-03-092.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/b08ded2f-60fb-2ad0-e77c-fcb3cf74f92e.jpeg)

#### 2～5　「変数の設定」

テキストから読み込んだSecretKeyはリスト形式なのでそれぞれの値を変数に設定しておきます。

#### 6 「変数の設定」

認証情報をJSON形式のテキストをとし記述します。リファレンス通りに書き、2～4で設定した変数を使います。

``` {"grantType": "client_credentials","clientId": "%ClientId%","clientSecret": "%ClientSecret%"}```

![2021-03-09-01-06-50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/eb7a0709-ac0d-a221-7404-a5cc434646e9.png)

#### 7「WEBサービスを呼び出します」

認証情報をPOSTしてアクセストークンを取得します。
![2021-03-09-01-10-26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/a76b5e91-cae2-4001-785c-058d54cb0827.png)


#### 8「JSONをカスタムオブジェクトに変換」

返却されたJSONをカスタムオブジェクトに格納することで簡単に値にアクセスできます。

#### 9「変数の設定」

カスタムオブジェクトからアクセストークンを変数に格納しておきます。

![2021-03-09-01-14-45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/0139db4c-9cd0-a497-312b-a4c9f6e43446.png)


### 10～21ブックマークのタイトルからキーワード抽出を行います

このフローではCOTOHA APIを使って「キーワード抽出」を行いタグとしてExcelに記録します。APIの仕様についてはリファレンスを参考にしてください。

https://api.ce-cotoha.com/contents/reference/apireference.html

#### 10 Loop

すでに前のサブフローでデータテーブルは作成済みなので、%Table%変数に格納されているデータを使用します。ここでは1行目を列名にしていたので行数から　-1します。```%Table.RowsCount - 1%``` また開始値が1であることにも注意が必要です。

![2021-03-09-20-43-25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/8b693f59-621f-1cf9-91bb-1f355adc4ab1.png)


#### 11「変数の設定」

データテーブルの[0]]列からAPIに送信するテキストを設定します。
![2021-03-09-20-54-24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/26755d03-c777-9e4e-5bd3-2f9e3d3daff7.png)

#### 12「WEBサービスを呼び出します」

%で囲んでいる文字列はこれまでに設定してある変数名です。あとはリファレンスにしたがって書いています。
![2021-03-09-20-59-39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/7396ab7b-ecfe-18e4-0865-39f79de957f7.png)

URL ```%APIBaseURL%/nlp/v1/keyword```
受け入れる: ```application/json```
コンテンツタイプ:```application/json;charset=UTF-8```
カスタムヘッダー:``` Authorization: Bearer %Token%```
要求本文 : ```{"document":"%SendDoc%","type": "default","do_segment":true,"max_keyword_num":3}```

成功するとこのようにJSONが返ってきます
![2021-03-09-21-48-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/13ccafa1-b1fc-aa71-bc49-bc683ef35b6e.png)


#### 13「JSONをカスタムオブジェクトに変換」

![2021-03-09-21-50-26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/590a1e67-5d1d-a30f-9246-d7df26647235.png)


#### 14「変数の設定」

13のカスタムオブジェクト内のresultはリストとして格納されています。

入れ子になっているカスタムオブジェクトのリストを数えます。

![2021-03-09-22-00-42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/e0fe5e36-d5f2-e831-4fc9-f496a2758a2c.png)

*```%count(JsonAsCustomObject2['result'])%```*

PADで唯一使うことができる関数count関数を使うことでカスタムオブジェクト内のリストを数えることできます。（他にあったらすいません）


#### 15~19リストを作成してカスタムオブジェクトのformから値をループを使って取得します

![2021-03-09-21-58-43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/44c86ae0-71dd-5bf6-2f70-2794a12d65f3.png)

Resultの変数設定は次のようにしています```%JsonAsCustomObject2['result'][LoopIndex]['form']%```
LoopIndexは変数なのでシングルクオーテーションで囲いません。
![](2021-03-09-22-00-42.png)![2021-03-09-22-00-42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/fec93bb5-d9ba-628e-5042-f75ee3801fa7.png)


#### 20「Excelワークシートに書き込み」

前のアクションで作成したFormListはリスト形式です。波括弧で囲い行データに変換します。
これを指定セルに書き込むことで一度に書き込みことができます。

![2021-03-09-22-02-15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/8aebd6d0-1499-8848-d5d9-6f1181ca1b16.png)


![2021-03-09-22-07-49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/25042252-bd66-7287-e041-21aebb4a152f.png)


*```Cotoha_API```*


```
File.ReadTextAsList File: $'''%SpecialFolderPath%\\Secret.txt''' Encoding: File.TextFileEncoding.UTF8 Contents=> SecretKey
SET ClientId TO SecretKey[0]
SET ClientSecret TO SecretKey[1]
SET AccessTokenPublishURL TO SecretKey[2]
SET APIBaseURL TO SecretKey[3]
SET Auth TO $'''{\"grantType\": \"client_credentials\",\"clientId\": \"%ClientId%\",\"clientSecret\": \"%ClientSecret%\"}'''
Web.InvokeWebService Url: AccessTokenPublishURL Method: Web.Method.Post Accept: $'''application/json''' ContentType: $'''application/json''' RequestBody: Auth ConnectionTimeout: 30 FollowRedirection: True ClearCookies: False FailOnErrorStatus: False EncodeRequestBody: False UserAgent: $'''Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.21) Gecko/20100312 Firefox/3.6''' Encoding: Web.Encoding.AutoDetect AcceptUntrustedCertificates: False ResponseHeaders=> WebServiceResponseHeaders Response=> WebServiceResponse StatusCode=> StatusCode
Variables.ConvertJsonToCustomObject Json: WebServiceResponse CustomObject=> JsonAsCustomObject
SET Token TO JsonAsCustomObject['access_token']
LOOP LoopIndex2 FROM 1 TO Table.RowsCount - 1 STEP 1
    SET SendDoc TO Table[LoopIndex2][0]
    Web.InvokeWebService Url: $'''%APIBaseURL%/nlp/v1/keyword''' Method: Web.Method.Post Accept: $'''application/json''' ContentType: $'''application/json;charset=UTF-8''' CustomHeaders: $'''Authorization: Bearer %Token%''' RequestBody: $'''{\"document\":\"%SendDoc%\",\"type\": \"default\",\"do_segment\":true,\"max_keyword_num\":3}''' ConnectionTimeout: 30 FollowRedirection: True ClearCookies: False FailOnErrorStatus: False EncodeRequestBody: False UserAgent: $'''Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.21) Gecko/20100312 Firefox/3.6''' Encoding: Web.Encoding.AutoDetect AcceptUntrustedCertificates: False ResponseHeaders=> WebServiceResponseHeaders2 Response=> WebServiceResponse2 StatusCode=> StatusCode2
    Variables.ConvertJsonToCustomObject Json: WebServiceResponse2 CustomObject=> JsonAsCustomObject2
    SET ResultCount TO count(JsonAsCustomObject2['result'])
    Variables.CreateNewList List=> FormList
    LOOP LoopIndex FROM 0 TO ResultCount - 1 STEP 1
        SET Result TO JsonAsCustomObject2['result'][LoopIndex]['form']
        Variables.AddItemToList Item: Result List: FormList NewList=> FormList
    END
    Excel.WriteCell Instance: ExcelInstance Value: {FormList } Column: 4 Row: LoopIndex2 + 1
END
```

Cotoha API for Developersまたはfor EnterPriseの認証情報をSecret.txtをデスクトップに作成する必要があります。

## Exit_flowサブフロー

Excelの体裁を適当に整えて名前を付けて保存しています。

![](2021-03-09-22-21-00.png)

ビジュアル化はPower BIをつかうといろいろできますのでこの稿では省略いたします。

*```Exit_flow```*

```
MouseAndKeyboard.SendKeys TextToSend: $'''{Control}({A})
{ALT}({O}{C}{A})
{Up}{Up}
{Control}{Home}
''' DelayBetweenKeystrokes: 10 SendTextAsHardwareKeys: False
Excel.CloseAndSaveAs Instance: ExcelInstance DocumentFormat: Excel.ExcelFormat.FromExtension DocumentPath: $'''%SpecialFolderPath%\\Bookmarks%FormattedDateTime%\\Bookmarks%FormattedDateTime%.xlsx'''
```

## まとめ

PADとCOTOHA APIの連携することでデスクトップに散らかったファイルを片づけるだけでなく、内容をデータとして整理する作業も比較的簡単に行えることが検証出来ました。

Windows 10のライセンスがあればPADが使えるようになりました！
あれもこれもできるわけではないですが適材を上手くつなげることで、身の回りのちょっとメンドクサイを少し進めたカタチで解決することが身近になるかもしれません。
今回はこれまで変数の記事に書いた要素たくさんを使いました。
詳細は[以前記事](https://qiita.com/sys_zero/items/13e06a41834cf40988ea)に書いております。興味がある方はご覧いただければと思います。