異動するけど後任なし。持ってる仕事はPADで負担最小化して引き継ぎたい！

## :dove: あと半年で異動する私、立つ鳥跡を濁したくない！

ちょっとした事情につき、私は現在1年間の期間限定で今の部署に配属されています。半年前今の部署に異動してきて、同僚から仕事を引き継ぎつつ、それまでやれていなかった新たな取り組みを始めました。

しかし、人員+1になったからやれていた業務、人員数がもとにもどったらそのままは続けていけないですよね。私が半年間持っていた業務を0にはできませんが、できる限り**負担を軽減**して同僚に渡したい！

ということで、手持ち業務の負担軽減第一弾「デジタルサイネージへの画像アップロード作業を自動化しよう！」初めてPower Automate Desktop(PAD)を使って、作業を自動化してみました。

各ステップのPADフローの設定だけを知りたいよっていう方は以下から、必要箇所にジャンプして読んでくださいね。

[1. ウェブサイトにログインする](#1-ウェブサイトにログインする)
[2. 特定のフォルダ内の画像をウェブサイトにアップロードする](#2-特定のフォルダ内の画像をウェブサイトにアップロードする)
[3. 特定のフォルダ内の画像の情報を取得する](#3-特定のフォルダ内の画像の情報を取得する)
[4. ファイル名をウェブサイト内の検索窓に入力して探す](#4-ファイル名をウェブサイト内の検索窓に入力して探す)
[5. ファイルをドラッグ＆ドロップしてプレイリストに追加する](#5-ファイルをドラッグドロップしてプレイリストに追加する)
[6. 情報を送信してサイネージに反映し、ブラウザを閉じる](#6-情報を送信してサイネージに反映しブラウザを閉じる)
[7. 作業完了した画像を別フォルダに移動する](#7-作業完了した画像を別フォルダに移動する)

<details><summary>ソースコードはこちら</summary>

ステップごとにコメントを入れているので、各ステップで必要なものを切り取って使ってみてください。
※ソースコード内コメントの「ステップ」は本記事内の手順やPADフロー内のアクション番号とは一致していませんのでご注意ください。

```ruby:フローのソースコード（Robin）
# ステップ1: Chromeを起動し、指定されたURLにアクセス
WebAutomation.LaunchChrome.LaunchChrome 
    Url: $'''任意のURL''' # ログインページのURL
    WindowState: WebAutomation.BrowserWindowState.Maximized # ブラウザウィンドウを最大化
    ClearCache: False # キャッシュをクリアしない
    ClearCookies: False # クッキーをクリアしない
    WaitForPageToLoadTimeout: 60 # ページ読み込みを待つ時間（秒）
    Timeout: 60 # タイムアウト時間（秒）
    PiPUserDataFolderMode: WebAutomation.PiPUserDataFolderModeEnum.AutomaticProfile # ユーザーデータフォルダのモード
    BrowserInstance=> Browser # ブラウザインスタンスを変数"Browser"に格納

# ステップ2: ログイン情報の入力 (ユーザー名)
WebAutomation.PopulateTextField.PopulateTextFieldUsePhysicalKeyboard 
    BrowserInstance: Browser 
    Control: appmask['Web Page \'h . r/#!/login\'']['Input text \'login\''] 
    Text: '任意のユーザー名' # ユーザー名
    UnfocusAfterPopulate: False 
    Mode: WebAutomation.PopulateTextMode.Replace # 入力モード

# ステップ3: パスワードの入力
WebAutomation.PopulateTextField.PopulateTextFieldUsePhysicalKeyboard 
    BrowserInstance: Browser 
    Control: appmask['Web Page \'h . r/#!/login\'']['Input password \'password\''] 
    Text: P'AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAApPMiUDj0mEKysJo3m1syLgQAAAACAAAAAAADZgAAwAAAABAAAABtibSmi/n2ndVXymyQ/ABiAAAAAASAAACgAAAAEAAAAEEMKNHTEKDGQbQJLXNqjbAYAAAAhXoCUa3o2va6RSPEd55tnSdgpC2FPlHnFAAAAI/2F4nTqSYPJmORjhv4mgGnKCGp' # 暗号化されたパスワード
    UnfocusAfterPopulate: False 
    Mode: WebAutomation.PopulateTextMode.Replace

# ステップ4: 「接続」ボタンをクリック
WebAutomation.PressButton.PressButton 
    BrowserInstance: Browser 
    Control: appmask['Web Page \'h . r/#!/login\'']['Button \'Connection\''] 

# ステップ5: メイン画面の「Monitoring」ボタンをクリック
WebAutomation.PressButton.PressButton 
    BrowserInstance: Browser 
    Control: appmask['Web Page \'h . er/#!/main\'']['Button \'Monitoring\'']

# ステップ6: 右クリックして「Media & Templates」オプションを選択
WebAutomation.Click.Click 
    BrowserInstance: Browser 
    Control: appmask['Web Page \'h . er/#!/main\'']['Div \'Media & Templates\''] 
    ClickType: WebAutomation.ClickType.RightClick 
    MouseClick: True

# ステップ7: 「Add」要素をホバー
WebAutomation.HoverOverElement 
    BrowserInstance: Browser 
    Control: appmask['Web Page \'h . er/#!/main\'']['Div \'Add\'']

# ステップ8: 「Media」オプションをクリック
WebAutomation.Click.Click 
    BrowserInstance: Browser 
    Control: appmask['Web Page \'h . er/#!/main\'']['Div \'Media\''] 
    ClickType: WebAutomation.ClickType.LeftClick 
    MouseClick: True

# ステップ9: 「Select Files」ボタンを押してファイルを選択
WebAutomation.PressButton.PressButton 
    BrowserInstance: Browser 
    Control: appmask['Web Page \'h . er/#!/main\'']['Button \'Select Files\'']

# ステップ10: ファイルを右クリックで選択
UIAutomation.Click.Click 
    Element: appmask['Pane \'ページ情報 - Google Chrome – Googleアカウント名\'']['Split Button \'Documents\''] 
    ClickType: UIAutomation.ClickType.RightClick 
    OffsetX: 9 
    OffsetY: 3 
    MousePositionRelativeToElement: UIAutomation.RectangleEdgePoint.MiddleCenter

# ステップ11: 選択したファイルのアドレスを編集
UIAutomation.PopulateTextField.PopulateTextField 
    TextField: appmask['Pane \'ページ情報 - Google Chrome – Googleアカウント名\'']['Edit \'Address\''] 
    Text: 'C:\\Users\\397504\\Documents\\DTV\\All Stores' 
    Mode: UIAutomation.PopulateTextMode.Replace 
    ClickType: UIAutomation.PopulateMouseClickType.SingleClick

# ステップ12: 「Enter」キーを押して選択確定
MouseAndKeyboard.SendKeys.FocusAndSendKeys 
    TextToSend: '{Return}'

# ステップ13: 選択されたアイテムをクリック
UIAutomation.Click.Click 
    Element: appmask['Pane \'ページ情報 - Google Chrome – Googleアカウント名\'']['List \'Items View\''] 
    ClickType: UIAutomation.ClickType.LeftClick 
    OffsetX: 369 
    OffsetY: 100 
    MousePositionRelativeToElement: UIAutomation.RectangleEdgePoint.TopLeft

# ステップ14: 全選択 (Ctrl + A)
MouseAndKeyboard.SendKeys.FocusAndSendKeys 
    TextToSend: '{LControlKey}({A})'

# ステップ15: 「Open」ボタンをクリックしてファイルを開く
UIAutomation.Click.Click 
    Element: appmask['Pane \'ページ情報 - Google Chrome – Googleアカウント名\'']['Split Button \'Open\''] 
    ClickType: UIAutomation.ClickType.LeftClick 
    MousePositionRelativeToElement: UIAutomation.RectangleEdgePoint.MiddleCenter 
    OffsetX: 9 
    OffsetY: 3

# ステップ16: 「OK」ボタンを押して操作完了
WebAutomation.PressButton.PressButton 
    BrowserInstance: Browser 
    Control: appmask['Web Page \'h . er/#!/main\'']['Button \'OK\'']

# ステップ17: ファイルを指定フォルダに移動
File.Move 
    Files: Files 
    Destination: $'''C:\\Users\\397504\\Documents\\DTV\\Uploaded''' # ファイルの移動先
    IfFileExists: File.IfExists.DoNothing 
    MovedFiles=> MovedFiles # 移動されたファイルを格納
```
</details>

## :dividers: デジタルサイネージへの画像アップロード作業を自動化しよう！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3819429/a1ef970d-49c6-9e20-a8e3-10315a45fe48.png)


この業務にはざっくりと分けて工程が4つあります。
作業の自動化以前に、作業を見直しより効率的なやり方を突き詰めたところ、既に一部改善ができてしまいました。

今回は、初めてのチャレンジということで、発生頻度が高くて自動化の難易度が比較的低い「２．保存した画像を専用ウェブサイトでサイネージに反映」の作業を自動化していきたいと思います！

まずは完成形のおおまかな流れはこんな感じ。

```ruby:完成版概要
1. サイネージ用ウェブサイトにログインする
2. アップロードする画像の情報を取得する
3. 特定のフォルダに保存しておいた画像をウェブサイトにアップロードする
4. アップロードした画像を、ウェブサイト内で検索する
5. プレイリストに追加する
6. 情報を送信してサイネージに反映し、ブラウザを閉じる
7. アップロードが完了した画像をアップロード済フォルダに移動する
```
<details><summary>全体のフローはこちら</summary>

PADのフローはこんな感じです。42アクションにもなってしまいました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3819429/862b7462-e6d5-a1d3-870d-bac8a672abfb.png)

</details>

## :writing_hand_tone4: 試作版を見せて同僚から意見をもらう
先ほどの完成版にいたるまでに、試作版を作って同僚からのフィードバックをもらいました。

<details><summary>読みたい人はこちらから！</summary>

```ruby:試作版概要
1. サイネージ用ウェブサイトにログインする
2. 特定のフォルダに保存しておいた画像をウェブサイトにアップロードする
3. アップロードした画像を、プレイリストに追加する
4. 情報を送信してサイネージに反映する
```
PADの実行ボタンを押すと、ウェブサイトが立ち上がり自動で画面が動く様子を同僚に見てもらいました。

:boy_tone5:（同僚） : え、どうやってやるの？！
:woman_tone1:（私） : パソコンを開いて、このボタンを押すだけだよ！アップロードしたい画像はここに先に保存しておくんだけどね。

という感じで、終始感動してくれました。
現在使用しているサイネージ用ウェブサイト、かなり複雑で正直不便なところがあります。今回、PADでの自動化以前に、ウェブサイト内の機能を活用して業務の簡略化を行っているので試作段階でもかなり負担が軽減されています。

:woman_tone1: : 何かほかにもっと欲しい機能とかあるかな？
:boy_tone5: : そうだなぁ、画像１枚１枚じゃなくて一気に何枚もできたらいいかも。
　　あと、アップロードされた画像はフォルダにそのまま残っちゃうの？
:woman_tone1: : なるほど、一気にできると思う！
　　それから、アップロードできた画像は他のフォルダに移すとわかりやすいからそうしよう！

こうして話し合って、試作版に機能を追加した完成版ができたのです。
</details>

## :hammer_pick: 完成版のPADアクション解説
私は基本的にPADのレコーダー機能を使用して手動操作を読み込み、細かな点は後から修正する方法をとっています。参考にする方は、フローのスクリーンショットを見ながらアクションを手動で調整してみてください。

### 1. ウェブサイトにログインする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3819429/3914dd36-9ef1-9756-c42d-a818f9241165.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3819429/b072e18f-107b-12c7-407f-34ab1d80fde4.png)

こちらがログイン画面とログインするまでのアクションです。

私は、まずPADのレコーダー機能で操作を記録して、必要なところを修正ながら進めています。`Login ID`入力欄、`Password`入力欄、Connectionボタン、それから、これ以外の空白部分もUI要素に追加しています。

パスワードの入力内容は暗号化されたテキスト入力に変更できます↓↓
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3819429/68273e75-2993-e963-49c7-05d317e1a576.png" width="80%">

:::note info
ログインID入力とパスワード入力のアクションを連続させてしまうと、両方にログインIDが入力されてしまうという事態が発生しました。どこにもリンクされていない空白部分のクリックを１回挟むことで、これを解決できました。
:::

### 2. 特定のフォルダ内の画像をウェブサイトにアップロードする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3819429/8209fa7f-3079-6bc9-7d85-fe5f7dd75563.png)
まずは、ウェブサイト内の画像アップロードボタンを選択するアクションを追加します。するとファイル選択画面が表示されます。

#### アクション11～15: ファイル選択画面でフォルダを指定する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3819429/5f143827-ef4d-9c78-6a51-acd73bb7bb64.png)

指定のフォルダに飛ぶために、この赤枠の部分に指定のフォルダのパス（C:\Users\XXXXXX\Documents\XXXXXXXの形式）をテキストで入力します。

:::note warn
通常、枠内右寄りの空間をクリックするとテキストが入力できる状態になります。しかし、ファイル選択画面で最初に表示されるフォルダは毎回同じとは限らない（同PC上での前回使用したフォルダなどが表示される）ため、表示される要素の横幅がそのときによって異なります。そのため自動操作のでUI要素をクリックすると、テキスト入力可能な状態に上手く移行しないことがあります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3819429/6304e49e-0f17-e59d-3d03-eabe63175502.png" width="80%">

そこで採用したのが、右クリックで以下のようにコマンドを呼び出す方法です。この中から`Edit address`を選択すればOKです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3819429/ca1ce5cc-e94e-d447-aa9c-f8b28498da59.png" width="80%">
:::

この次のアクションのテキスト入力でファイルパスを入力して、Enter(return)キーで確定させてください。

#### アクション16～21: すべてのファイルを選択して開く

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3819429/bfbd17fa-e63d-b012-1473-52464f0b6baa.png" width="80%">

こちらの赤枠の部分(UI要素名: `List Items View` )を一度クリックし、`Ctrl+A`ですべてのファイルを選択、最後に右下のOpen(開く)ボタンをクリックするかEnter(return)キーでファイルを開き、ブラウザ画面に戻ります。

私の使用しているサイトの場合は、この後ブラウザのOKボタンをクリックすることでファイルのアップロードが完了します。


### 3. 特定のフォルダ内の画像の情報を取得する

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3819429/407e21f9-5f5e-27c5-76bc-487cb9756e6f.png" width="80%">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3819429/24032ef8-aa99-2005-72c9-8dd803fbadd3.png" width="80%">

先ほど、指定したフォルダ内のファイルをすべてアップロードしたので、このフォルダにあるすべてのファイルの情報を取得します。なぜここでファイルの情報を取得するかというと、あとでファイル名を使いたいからです！

:::note info
すべてのファイルについてアクションを行いたいときには、ファイルフィルターに`*`と入力します。
:::

### 4. ファイル名をウェブサイト内の検索窓に入力して探す

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3819429/56dcc1e0-d1b3-9e5c-0abe-9624b03f261c.png)

4.と5.のステップは、複数のファイルに対して行いたいので、`For Each`(繰り返し)の設定をしています。今回のデモ操作では、[こちら](#アクション1621-すべてのファイルを選択して開く)で３つのファイルの情報を取得しているので、`For Each`の中のフローを３回繰り返します。

#### アクション27: ファイルパスの取得
アクション26は後から設定するので一度飛ばして、アクション27から設定してください。また、この後のアクション28～29は次のステップにかかわるアクションなので、こちらも一旦飛ばします。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3819429/ed83e934-2fa9-a337-b89c-363faa9f73ec.png" width="80%">

ファイルパス取得のアクションをドラッグ＆ドロップすると、自動で入力されますが、もし入力されない場合にはこのように手動で入力してください。今回私が使いたいのは`FileNameNoExtension`(拡張子を除いたファイル名)です。

#### アクション30～34: 検索窓にファイル名入力して特定のファイルを探す
![video-output-F942B0A9-8E7D-42DA-901A-8CBB8588DCB4 3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3819429/20203cae-4e77-02c9-69d5-01ce6a7973b1.gif)
このように、虫メガネマークにマウスポインタを合わせるとテキストフィールドが表れます。そこに検索したいファイル名を入力すると、一番上に該当のファイルが表示されます。

これを自動で行うためのアクションを設定していきます。虫メガネマークをクリックせずに、マウスポインタを合わせるには`Webページの要素にマウスをポイント`のアクションを使っていきます。念のため、テキストフィールドが展開しきるまで`待機`のアクションで１秒待ってから、テキストフィールドをクリックします。

次に`テキストフィールドに入力するアクション`で、先ほど取得した変数`FileNameNoExtension`を入力するよう設定します。すると、拡張子を除いたファイル名がここに入力されるようになります。その後、Enter(return)キーで検索を実行し、検索結果に当てはまるファイル名の画像があればリストの一番上に表示されます。

#### アクション26: 変数のリセット

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3819429/3e63070d-8332-9b3d-76dc-a12be70f9d11.png" width="80%">

この繰り返しのパートの中で使いたい関数をリセットするために、値を`""`として設定します。今回の私の場合、使用するのは`FileNameNoExtension`(拡張子を除いたファイル名)のみなのでこの関数だけリセットしています。

:::note warn
`For Each`の一番最初に変数のリセットをしないと、作業を繰り返しても１周目の変数の設定が残ってしまいます。そのため、アクション33（拡張子なしのファイル名を入力する）のときに１周目のファイル名が毎回入力されてしまいます。これを避けるために、使用する変数はこの方法ですべてリセットしてください。
:::

### 5. ファイルをドラッグ＆ドロップしてプレイリストに追加する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3819429/b30f96d3-3a13-2c12-3085-acabff3e10e0.png)
このステップで解説するアクションはこちらです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3819429/13135d40-217e-2135-bb42-b942c6f1dd45.png)

こちらが作業したいブラウザ画面です。ステップ４で検索して出てきた画像を、画面上の黄枠にドラッグ＆ドロップすると、対象のプレイリストに表示されるようになります。

ドラッグ＆ドロップは2種類のアクション設定方法を試しました。

#### ➀`UI要素をウィンドウ内にドラッグ アンド ドロップする`を使う
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3819429/066938bf-6039-8f05-345f-c63108c6ae3a.png)

こちらは１つのアクションで、ドラッグするUI要素とドロップ先のUI要素の両方を設定することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3819429/e51de3bf-1ff1-a4af-2f3d-d4d08fae794f.png)

ドラッグするUI要素は`List Item`です。ドロップ先のUI要素は`Vlist`(または`Unordered List`と表示される）を選びたいのですが…`UI要素をウィンドウ内にドラッグ アンド ドロップする`アクションではこのUI要素を選択することができません！

:::note warn
ドロップ先のUI要素が選択できる場合には、`UI要素をウィンドウ内にドラッグ アンド ドロップする`が便利です。ただし、選択できない場合には次の➁の方法を試してみましょう。
:::

#### ➁`Webページのリンクをクリック`を2回使う
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3819429/ee1e9f90-7359-510d-15cf-4ec3c6994c23.png)

`Webページのリンクをクリック`のアクションだと、このようにボタンを押すと離すを選択することができます。こちらの方法だと`Vlist`(または`Unordered List`）のUI要素も選ぶことができました！

➀はウィンドウでのアクションで、➁はWebページでのアクションなので、それぞれ対象となるUI要素が異なるのでしょう。うまくできる方法があってよかった！

### 6. 情報を送信してサイネージに反映し、ブラウザを閉じる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3819429/99adc3db-6eb3-3a97-e33b-2e39e4eb8edc.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3819429/97160e1e-2d44-4434-fa54-48e8a805a97b.png)

ブラウザの作業用ページで画面を切り替えてPUBLISHボタンを押せば、各地のデジタルサイネージに画像がアップロードされます。アップロードのアクションが完了した時点でブラウザを閉じます。

### 7. 作業完了した画像を別フォルダに移動する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3819429/cb6f7f63-a74e-04a1-d991-25bc217c491b.png)

ついに最後のアクションです！同僚から要望のあったアップロード完了ファイルのフォルダ移動を行います。こちらのようにフォルダのパスを貼り付ければOK！

## :clap: できた
わーい、できました！今まで8分かかっていた作業が1クリックでできるようになりました。ただし、今回できたのは全デジタルサイネージへの同時アップロード作業のみなので、今後いくつかのグループごとにアップロードできるようなフォルダ分けをしていきたいと思います！

そして、再度同僚に見せてみました。
:boy_tone5:(同僚) : おー！動作が増えてるね。
:woman_tone1:(私) : どうかな、使えそうかな？
:boy_tone5:: こんなに簡単にしてくれて助かったよ！
:woman_tone1:: ほかにも何かあったら教えてね。
:boy_tone5:: スケジュール機能を自動で使えるようにしてくれたら助かるんだけど…
:woman_tone1:: ！！！

スケジュール機能は、動作が多くて複雑で1つの画像を設定するのに7分はかかる作業。PADのアクション数はいったいいくつになるのか。
私のPAD修行はまだまだ続きそうです。