【PowerShell】個人だけで工数集計をしたい時に使う簡易ツール②

前記事の簡易ツールを現場で使いながら改良していたところ、だいぶ中身が変わって別ツールみたいになったので、本記事で②として記事にします。前記事は以下です。

[■【PowerShell】個人だけで工数集計をしたい時に使う簡易ツール①](https://qiita.com/HOBO0222/items/f5205a5176278d38b0c6)

本記事では前記事で記載済みの機能紹介や注意点などは割愛します。よろしくお願い致します。

###【前ツールから追加した機能】
新しく追加した機能は語尾に「 → New!!」を入れました。

　・何分間分の工数を記載するかを選択可能(分単位)  ***→ New!!***
　・何分間隔で工数記録を行うかを設定可能(15分固定) ***→ New!!***
　・業務カテゴリを選択可能 ***→ New!!***
　・業務詳細を選択可能(手動入力)
　・担当者を選択可能 ***→ New!!***
　・以上の情報をカンマ区切り形式の表として出力
　・ファイル出力と同時に画面出力も行う
　・ユーザ入力フォームを作成 ***→ New!!***
　・工数集計ボタンを設置(logを残していれば年次、月次、日時の集計を即確認可能) ***→ New!!***
　・Cancelボタンを押すとツールを終了させる ***→ New!!***
　・各項目のいずれかに空白があった場合はツールを終了させる ***→ New!!***

個人的に追加したいと思っていた機能はほぼ追加できたので満足です。

特に任意の日付の工数をすぐに確認できる工数集計ボタンは欲しかったのでありがたい。
あとで詳細は説明しますが小数チームで同じフォルダにWorkTime~~.logを格納しておくと、
知りたいときにすぐに確認できるようにしています。

###【TimeLoop.ps1】
以下が構文です。

```POWERSHELL:TimeLoop.ps1
do { #工数集計時に再度業務工数入力画面へ戻るためのループ処理
### 出力ファイルの設定
$FILE_DATE=Get-Date -UFormat "%Y%m%d"
$OUT_PATH="C:\Tools\logs"
$OUT_FILE="C:\Tools\logs\WorkTime_$FILE_DATE.log"

### コンボボックス画面の作成
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

# フォントの設定
$Font = New-Object System.Drawing.Font("MS ゴシック",8)

# フォームの作成
$form = New-Object System.Windows.Forms.Form 
$form.Text = "業務工数入力画面"
$form.Size = New-Object System.Drawing.Size(480,460)
$form.StartPosition = "CenterScreen"
$form.font = $Font

# OKボタン作成
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Point(56,380)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
$OKButton.DialogResult = "OK"
$form.AcceptButton = $OKButton
$form.Controls.Add($OKButton)

# Cancelボタン作成
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Point(190,380)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Cancel"
$CancelButton.DialogResult = "Cancel"
$form.CancelButton = $CancelButton
$form.Controls.Add($CancelButton)

# 工数集計ボタン作成
$SumButton = New-Object System.Windows.Forms.Button
$SumButton.Location = New-Object System.Drawing.Point(330,380)
$SumButton.Size = New-Object System.Drawing.Size(75,23)
$SumButton.Text = "工数集計"
$SumButton.DialogResult = "Retry"
$form.Controls.Add($SumButton)

# ラベル5つ分の設定
$label1 = New-Object System.Windows.Forms.Label
$label1.Location = New-Object System.Drawing.Point(10,20)
$label1.Size = New-Object System.Drawing.Size(240,20)
$label1.Text = "1.設定時間(分単位:任意入力可能)"
$form.Controls.Add($label1) 

$label2 = New-Object System.Windows.Forms.Label
$label2.Location = New-Object System.Drawing.Point(10,90)
$label2.Size = New-Object System.Drawing.Size(240,20)
$label2.Text = "2.間隔時間(15分固定で設定中)"
$form.Controls.Add($label2) 

$label3 = New-Object System.Windows.Forms.Label
$label3.Location = New-Object System.Drawing.Point(10,160)
$label3.Size = New-Object System.Drawing.Size(240,20)
$label3.Text = "3.業務項目(任意入力可能)"
$form.Controls.Add($label3) 

$label4 = New-Object System.Windows.Forms.Label
$label4.Location = New-Object System.Drawing.Point(10,230)
$label4.Size = New-Object System.Drawing.Size(240,20)
$label4.Text = "4.業務詳細(任意の業務名を入れてください)"
$form.Controls.Add($label4) 

$label5 = New-Object System.Windows.Forms.Label
$label5.Location = New-Object System.Drawing.Point(10,300)
$label5.Size = New-Object System.Drawing.Size(240,20)
$label5.Text = "5.担当者(任意入力可能)"
$form.Controls.Add($label5)

### 各種ボックスの作成
# $WATCH_TIME用の変数$Box1を作成
$Box1 = New-Object System.Windows.Forms.Combobox
$Box1.Location = New-Object System.Drawing.Point(50,50)
$Box1.size = New-Object System.Drawing.Size(150,30)
$Box1.DropDownStyle = "DropDown" # もし入力を許さない場合はDropDownList
$Box1.FlatStyle = "Standard" # Flat,Popup,Standard,Systemから選択可能
$Box1.font = $Font
$Box1.Height = 80
[void] $Box1.Items.Add("15")
[void] $Box1.Items.Add("30")
[void] $Box1.Items.Add("45")
[void] $Box1.Items.Add("60")
[void] $Box1.Items.Add("75")
[void] $Box1.Items.Add("90")
$form.Controls.Add($Box1) 

# $DELAY_TIME用の変数$Box2を作成
$Box2 = New-Object System.Windows.Forms.Combobox
$Box2.Location = New-Object System.Drawing.Point(50,120)
$Box2.size = New-Object System.Drawing.Size(150,30)
$Box2.DropDownStyle = "DropDownList"
$Box2.FlatStyle = "Standard" # Flat,Popup,Standard,Systemから選択可能
$Box2.font = $Font
$Box2.Height = 80
#[void] $Box2.Items.Add("1")
#[void] $Box1.Items.Add("5")
#[void] $Box1.Items.Add("10")
[void] $Box2.Items.Add("15")
#[void] $Box1.Items.Add("30")
$form.Controls.Add($Box2) 

# $WORK用の変数$Box3を作成
$Box3 = New-Object System.Windows.Forms.Combobox
$Box3.Location = New-Object System.Drawing.Point(50,190)
$Box3.size = New-Object System.Drawing.Size(150,30)
$Box3.DropDownStyle = "DropDown" # もし入力を許さない場合はDropDownList
$Box3.FlatStyle = "Standard" # Flat,Popup,Standard,Systemから選択可能
$Box3.font = $Font
$Box3.Height = 80
[void] $Box3.Items.Add("案件")
[void] $Box3.Items.Add("定例")
[void] $Box3.Items.Add("MTG")
[void] $Box3.Items.Add("資料作成")
[void] $Box3.Items.Add("改善")
[void] $Box3.Items.Add("アラート")
[void] $Box3.Items.Add("障害")
$form.Controls.Add($Box3) 

# $DETAIL用の変数$Box4を作成(DETAILだけは完全任意入力)
$Box4 = New-Object System.Windows.Forms.TextBox #任意入力させるためTextBoxメソッドを使用
$Box4.Location = New-Object System.Drawing.Point(50,260)
$Box4.size = New-Object System.Drawing.Size(150,30)
$Box4.font = $Font
$Box4.Height = 80
$form.Controls.Add($Box4) 

# $PERSON用の変数$Box5を作成
$Box5 = New-Object System.Windows.Forms.Combobox
$Box5.Location = New-Object System.Drawing.Point(50,330)
$Box5.size = New-Object System.Drawing.Size(150,30)
$Box5.DropDownStyle = "DropDown" # もし入力を許さない場合はDropDownList
$Box5.FlatStyle = "Standard" # Flat,Popup,Standard,Systemから選択可能
$Box5.font = $Font
$Box5.Height = 80
[void] $Box5.Items.Add("HOBO")
[void] $Box5.Items.Add("上原")
[void] $Box5.Items.Add("菅野")
[void] $Box5.Items.Add("桑田")
[void] $Box5.Items.Add("マルティネス")
[void] $Box5.Items.Add("カーショー")
$form.Controls.Add($Box5) 

# フォームを最前面に表示
$form.Topmost = $True

# フォームを表示＋選択結果を変数に格納
$result = $form.ShowDialog()

### 選択ボタンごとの分岐処理
 if ($result -eq "OK") {
     $WATCH_TIME= $Box1.Text 
     $DELAY_TIME= $Box2.Text
     $WORK= $Box3.Text
     $DETAIL= $Box4.Text
     $PERSON= $Box5.Text
 } elseif ($result -eq "Cancel") {
     Write-Host "画面を閉じます。"
     Start-Sleep -m 1000
     exit
 } else { #工数集計ボタン用のInputBoxを用意
     [void][System.Reflection.Assembly]::Load("Microsoft.VisualBasic, Version=8.0.0.0, Culture=Neutral, PublicKeyToken=b03f5f7f11d50a3a")
     $SUM=[Microsoft.VisualBasic.Interaction]::InputBox("集計対象月もしくは日付を入力してください。`n`n`n例) 2018、201804、20180411", "集計対象入力画面",$FILE_DATE)
     [System.Double]$WORK_SUM=(Get-Content $OUT_PATH\WorkTime_$SUM*.log).Count * 0.25 #一行15分単位で計算しています。改良の余地あり。
     Write-Host "$SUM`の集計工数は$WORK_SUM`時間です。"
     Write-Host "業務工数入力画面に戻ります。"
     Start-Sleep -m 1000
 }
} while ($result -eq "Retry") # 工数集計ボタン(戻り値Retry)を選択していると元に戻る

# 値のどれかが空白だった場合は一旦ツールを閉じさせたいので分岐処理
 if (($WATCH_TIME -eq "") -Or ($DELAY_TIME -eq "") -Or ($WORK -eq "") -Or ($DETAIL -eq "") -Or ($PERSON -eq "")) {
     Write-Host "入力されていない値があるため処理を中止します。"
     Write-Host "画面を閉じます。"
     Start-Sleep -m 1000
     exit
 }

### 待機時間の設定
$SLEEP_TIME=60000 * $DELAY_TIME #型を考慮して必ず計算時はint型の値を式の先頭に持ってくること

### 本処理(今回は工数集計用の表を作成します)
# 工数用意(単位は「時間」で合わせる)
$WORK_TIME=$DELAY_TIME / 60 #割り算なら先頭がStringでも計算してくれる
$WORK_TIME=[Math]::ROund($WORK_TIME, 2 , [MidpointRounding]::AwayFromZero); #小数点第3位切り捨て
Write-Host "タイムスタンプ処理を開始します(Time: $WATCH_TIME`分間 DELAY: $DELAY_TIME`分周期)"
$i=0
do {
Start-Sleep -m $SLEEP_TIME
$i=$i + $DELAY_TIME
[int]$p=$i / $WATCH_TIME * 100
$DATE=Get-Date -UFormat "%Y/%m/%d,%H:%M"
#$DATE + "," + $WORK + "," + $DETAIL + "," + $PERSON + "," + $WORK_TIME 2>&1 | Add-Content -Pass $OUT_FILE
Add-Content -Value "$DATE,$WORK,$DETAIL,$PERSON,$WORK_TIME" 2>&1 -Pass $OUT_FILE
} while ($i -lt $WATCH_TIME)
```

前記事ツールは32行でしたが、今回はユーザフォームを作った影響で202行まで伸びました。PowerShellでもユーザフォームや分岐処理、ループ処理を多用すると行数が増えてしまいますね。

細かい説明は構文内のコメントでしております。

起動は以下のBATCHを実行して使います。

```BATCH:TimeLoop.bat
@echo off
powershell -ExecutionPolicy RemoteSigned -File "C:\Tools\TimeLoop\TimeLoop.ps1"
pause
```
以下使い方の説明です。

###【OKボタンを選択した場合の挙動】
BATCHを実行するとプロンプト画面と一緒に以下の画面が表示されます。
![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/198785/0c36d26d-5434-8bdb-e5f9-5abf8683443d.png)
最前面で表示されるようにしていますが、嫌であれば以下の構文部分をコメントアウトしてください。

```Powershell:最前面表示
# フォームを最前面に表示
$form.Topmost = $True
```
1、3、5はリストから選択可能していますが任意の文字列での入力は可能にしています。もし任意文字列入力なしでリスト選択だけにする場合は、以下のDropDownをDropDownListにしてください。

```POWERSHELL:リスト選択のみ
$Box1.DropDownStyle = "DropDown" # もし入力を許さない場合はDropDownList
```

全てを入力し、OKボタンを押すと以下の通りにプロンプト画面で進捗が表示され、別途指定したファイルにも出力され始めます。ここは前記事と仕様は変わりません。
![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/198785/84566f26-e091-ad91-1eb4-acf4eb994edc.png)


終わるとプロンプト画面が閉じて終わりです。都度作業が発生する度に入力していきます。

またエラー処理として、各項目のいずれかで空白があった場合は値が空白だったと判定してツールを終了させる分岐処理を追加させてます。
![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/198785/19e2c0d3-c1c7-60db-4e27-3c463908ee4a.png)
↑でOKボタンを押すと↓のように出てきてツールが終了します。
![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/198785/f5c1b1f3-2589-c943-733f-3b6901cb8e06.png)


###【Cancelボタンを選択した場合の挙動】
これはOKボタンで空白があった場合と同じくCancelボタンを押したと判定してツール終了させる分岐処理を追加させました。

###【工数集計ボタンを選択した場合の挙動】
このツールを毎日使ってると指定したフォルダに以下のような感じでファイルが積み重なっていきます。
![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/198785/1491959d-a977-156d-9bd4-b69fac6ba3fe.png)

工数集計ボタンを押すとこのフォルダに入っているWorkTime_~~.logの該当ファイルの行数をカウントして工数を集計するようにしています。構文内では以下の部分が該当します。

```POWERSHELL:工数集計部分
[void][System.Reflection.Assembly]::Load("Microsoft.VisualBasic, Version=8.0.0.0, Culture=Neutral, PublicKeyToken=b03f5f7f11d50a3a")
$SUM=[Microsoft.VisualBasic.Interaction]::InputBox("集計対象月もしくは日付を入力してください。`n`n`n例) 2018、201804、20180411", "集計対象入力画面",$FILE_DATE)
[System.Double]$WORK_SUM=(Get-Content $OUT_PATH\WorkTime_$SUM*.log).Count * 0.25 #一行15分単位で計算しています。改良の余地あり。
```
(Get-Content <指定ファイル>).Countで指定ファイルの行数確認ができます。今回は*でワイルドカードで複数ファイルがヒットするようにしています。ちなみに1行で15分の前提でツールを組んでいますので、現場ルールが違う方はその辺りは修正してもらう必要があります。

例えば入力時に「20180411」と入力すると本日の工数を確認できます。「201804」なら4月度、「2018」なら2018年現時点での総工数確認ができます。以下の画像の感じでプロンプト画面に出てきます。ファイルが3つしかなくて申し訳ないですがイメージこんな感じですね。
![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/198785/3393e350-eafb-44d2-2664-67fbcb559deb.png)

集計工数が表示されるとまた業務工数入力画面が表示されるようにしているので連打で工数確認できます。あとはツール実行中にも工数集計ボタンとCancelボタンを選択するだけなら大丈夫です。

このボタンは個人的に気に入っています。
どうしても本日の確定後に実績工数とツールの記録がずれてる時があるので、最後にログファイルを確認しに行くのは日課(30秒で終わる日課)なんですが、めんどくさくて...。これ打てばすぐに実績工数とどれだけズレてるかわかってその分の行数をうまくちょろめかせばいいので助かってます。ファイルが溜まれば溜まるほど用途の幅は広がります。

それと、これは要改修ですが少人数チームで集計したい時なんかは、チーム内でWorkTime_YYYYMMDD_担当者名_グループ名.logと出力するようにしてチームみんなで格納しておくと、担当者ごとやグループごとの集計も簡単に出るようになります。必要であれば改修がんばってください...!!(必要であれば可能な限り質問にお答えします)

###【今後の課題】
だいたいやりたいことできましたが、あとは以下の二点実装できると楽しそうだなと思っています。

・OKボタンで正常出力後に「完了しました」画面表示と「続けて入力しますか？」の選択肢
→いちいちプロンプト画面閉じるのめんどくさいので。
　すぐに実装できそうなので、気が向いたらしれっと追加しておきます。

・工数集計機能の深堀り
→これやってみたいです。例えば「案件」ごとの工数集計とかですね。
　やれてしまうので、PowerShellの勉強がてらやってみようと思います。

でも、じっくり集計される場合はやはりピボット集計が一番良いので、
前記事で紹介したサイトでやり方を覚えて実施されるのをオススメします。

最後となりますが、ほぼコピペで本ツールは動いてしまいます。
OUT_PATHとOUT_FILE変数を変えたらおそらく動きます。あとは各項目のリストをお好みで編集してください。

作成してるの楽しかったです。
長文にお付き合い頂きありがとうございました。

なにかありましたらコメントください。