Windows
PowerShell
ピボットテーブル
工数

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

※追記:本稿で紹介したツールの改良版を以下記事で紹介しました。

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

本稿を読んでご興味が出た方は上記記事もご参照ください。
(長い記事ですみません...)

最近PowerShellが楽しくて色々いじってます。
ループ処理とInputBoxを利用して工数集計やWindowsのリソース取得を楽にできないかなと思っていたので、作成してみました。リソース取得ツールはまた次の記事で記載します。

作成しながら構文作成時に気を付けた点や課題点を記載します。
課題点はもし解決法を知ってらっしゃる方いたら教えてほしいです。

【工数集計用のツール】

私の現部署は工数集計していませんが、
なにかの時に「お前今までなににどれだけ工数使ったんだ?」と聞かれるとなにもデータがないので、勝手に工数をまとめた表を作成してくれるツールが欲しいなあと思っていました。

今回まだまだ完全自動ではないですが、
表作成はほぼ自動でやってくれるツールを作成してみました。

以下の機能は入れています。

 ・何分間分の工数を記載するかを設定可能(分単位)
 ・何分間隔で工数記録を行うかを設定可能(分単位)
 ・業務カテゴリを入力可能(手動入力)
 ・業務詳細を入力可能(手動入力)
 ・担当者を入力可能(手動入力)
 ・以上の情報をカンマ区切り形式の表として出力
 ・ファイル出力と同時に画面出力も行う

以下構文です。

PeformanceCounter.ps1
### InputBoxで設定時間と時間間隔を入力指示(InputBoxで取得した値は型がStringであることは要注意)
[void][System.Reflection.Assembly]::Load("Microsoft.VisualBasic, Version=8.0.0.0, Culture=Neutral, PublicKeyToken=b03f5f7f11d50a3a")
$WATCH_TIME=[Microsoft.VisualBasic.Interaction]::InputBox("設定時間を入力してください(分単位)`n`n`n例) 90", "設定時間画面")
$DELAY_TIME=[Microsoft.VisualBasic.Interaction]::InputBox("時間間隔を入力してください(分単位)`n`n`n例) 15", "間隔設定画面")

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

### 出力ファイルの設定
$FILE_DATE=Get-Date -UFormat "%Y%m%d"
$OUT_FILE="C:\Tools\logs\WorkTime_$FILE_DATE.log"

### 本処理(今回は工数集計用の表を作成します)

# 各項目のInputBox用意
$WORK=[Microsoft.VisualBasic.Interaction]::InputBox("業務カテゴリを入れてください。`n`n`n例) アラート,定例...etc", "業務入力画面")
$DETAIL=[Microsoft.VisualBasic.Interaction]::InputBox("業務の詳細を入れてください。`n`n`n例) Apache再起動対応...etc", "業務詳細入力画面")
$PERSON=[Microsoft.VisualBasic.Interaction]::InputBox("担当者名を入れてください。`n`n`n例) 川野、相川...etc", "担当者入力画面")

# 工数用意(単位は「時間」で合わせる)
$WORK_TIME=$DELAY_TIME / 60 #割り算なら先頭がStringでも計算してくれる
$WORK_TIME=[Math]::ROund($WORK_TIME, 2 , [MidpointRounding]::AwayFromZero); #小数点第3位切り捨て
#Add-Content -Value "タイムスタンプ処理を開始します(Time: $WATCH_TIME Min DELAY: $DELAY_TIME Min)" -Pass $OUT_FILE -Encoding String
$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)

以上のPowerShellファイルをバッチファイルで実行します。

TimeLoop.bat
@echo off
powershell -ExecutionPolicy RemoteSigned -File "C:\Tools\TimeLoop\TimeLoop.ps1"
pause

【出力結果】

InputBoxに入力すると以下のプロンプト画面が設定時間が来るまでひたすら出てきます。

キャプチャ.PNG

左から、日付、時刻、業務項目、業務詳細、担当者名、工数(÷60にした値で出してます)で出力されています。さらに同じ形式でログファイルも同時に出力されています。

キャプチャ.PNG

私はこの表をEXCELのピボットでまとめて上司に提出する用で作成しました。
日次、週次、月次、年次でピボット化するための元データとして使います。
以下はテストデータなので値がかなりおかしいですが、
右側へ日付や月が伸び続けるようなイメージで表が蓄積されていきます。

キャプチャ.PNG

ピボットは表さえあれば簡単かつスピーディにデータをまとめられるので重宝してます。
ただ本ツールは実行する時だけどうしても手動でやらないとだめですが。。。

ピボットの基本は以下記事をご参照ください。
【Excel】ピボットテーブルって何に使うの?エクセルで大量のデータを効率よく集計・分析するテク

【注意点や課題】

1.InputBoxで取得した値は型がかならずString
VBSと同じ仕様です。入力した文字が例え整数や少数であろうが、データ型はStringになります。

型の確認
PS C:\Users\TASK-PC> [void][System.Reflection.Assembly]::Load("Microsoft.VisualBasic, Version=8.0.0.0, Culture=Neutral,
PublicKeyToken=b03f5f7f11d50a3a")
PS C:\Users\TASK-PC> $WATCH_TIME=[Microsoft.VisualBasic.Interaction]::InputBox("設定時間を入力してください(分単位)`n`n`n例) 90", "設定時間画面")
PS C:\Users\TASK-PC> $WATCH_TIME
60
PS C:\Users\TASK-PC> $WATCH_TIME.GetType().FullName
System.String

型がSystem.Stringになっているのがわかると思います。
これは後ほどの計算時に影響を与える仕様です。

2.計算時は先頭の値のデータ型を前提に行われる
VBSを習ったときにハマりました。
以下が普通の計算式です。

INT型
PS C:\Users\TASK-PC> $INT1=15
PS C:\Users\TASK-PC> $INT2=30
PS C:\Users\TASK-PC> $INT1 + $INT2
45
PS C:\Users\TASK-PC> $INT1.GetType().FullName
System.Int32
PS C:\Users\TASK-PC> $INT2.GetType().FullName
System.Int32

INT型同士の計算ですね。普通に変数に格納すればこうなります。

しかし今回のようにInputBOX利用でString型として生成された値で
計算する場合は注意しないと以下のようになります。

String型
PS C:\Users\TASK-PC> [void][System.Reflection.Assembly]::Load("Microsoft.VisualBasic, Version=8.0.0.0, Culture=Neutral, PublicKeyToken=b03f5f7f11d50a3a")
PS C:\Users\TASK-PC> $DELAY_TIME=[Microsoft.VisualBasic.Interaction]::InputBox("時間間隔を入力してください(分単位)`n`n`n例) 15", "間隔設定画面")
PS C:\Users\TASK-PC> $DELAY_TIME
15
PS C:\Users\TASK-PC> $SLEEP_TIME=$DELAY_TIME * 60000
PS C:\Users\TASK-PC> $SLEEP_TIME
15151515151515151515151515151515151515151515151515151515151515151515...
15151515151515151515151515151515151515151515151515151515151515151515
PS C:\Users\TASK-PC> $SLEEP_TIME.GetType().FullName
System.String

この場合、「15」という文字列を60000回結合する出力になります。
計算式の先頭である$DELAY_TIMEのデータ型がStringであるため、こういう結果になってしまいます。

よって、先頭の値をINT型の60000にすれば、格納される値もINT型になります。

これが正解
PS C:\Users\TASK-PC> $DELAY_TIME=[Microsoft.VisualBasic.Interaction]::InputBox("時間間隔を入力してください(分単位)`n`n`n例) 15", "間隔設定画面")
PS C:\Users\TASK-PC> $SLEEP_TIME=60000 * $DELAY_TIME
PS C:\Users\TASK-PC> $SLEEP_TIME
900000
PS C:\Users\TASK-PC> $DELAY_TIME
15
PS C:\Users\TASK-PC> $SLEEP_TIME.GetType().FullName
System.Int32

入力フォームで値を指定したあとに計算する場合は気を付けてください。
ちなみに引き算や割り算であれば意図した通りに計算してくれるようです。

3.二方向出力の方法
これは少しハマりました。今回はAdd-Contentに-Passオプションをつけると何故か意図した通りにプロンプト画面とファイル出力を同時にできるようになりました。しかし、この-Passオプションというのがどこのネット記事や参考書を読んでも記載されてないんです。ちなみに-Pathオプションだとファイル出力のみとなります。

無難に二方向出力する場合はTee-Objectに-Appendオプションを付与すればできますのでそちらで組まれたほうが良いかもしれません。私は見つけてしまったのでこれでいきます。もし-Passのことでなにか情報を知ってる方がいらしたら教えて頂けるとありがたいです。バグなのか仕様なのかも現状わかっていません。(追記:この件についてはコメント欄をご確認ください。)

4.課題:リスト形式で選択させることは可能かどうか
まだちゃんと調べていないのですが、ユーザ入力フォーム画面であらかじめこちらで用意されたリストの内容だけを選択させるように制御したいなと思っています。それができれば入力ミスを防げるのでどうしても入れたいですがInputBoxではできそうにないので、他に方法がないかまた今度調べてみます。

【工数を記録する意義について】

工数集計ツールの話しはこれで終わりです。
あくまで個人的に工数を入力して集計することをやってみたいと思っていたので作成しました。

私はどの現場に行っても表を元とするピボット工数集計表を作成するのですが、長期になればなるほど自身の使用工数の偏りが可視化されていくので面白がって確認してます。

実用性としての一つ実体験を話しますが、以前の現場では運用SEとしてメンバー入りしたはずがシステム更改案件まっさかりで案件対応に工数を費やし運用業務の引継ぎすら行われない状況でした。更改も一段落した4カ月後に「HOBOさん、運用引継ぎ全部終わりましたよね?」とリーダたちに確認されたので、ピボット表をお見せしました。全体約720時間の内、「案件」項目に約80%、「運用」項目に約5%しか使っていなかったため、「全く引継ぎは受けていないです」と言えました。

また振り返りを行う上でもこのピボット表は有用です。
自身が担当したい業務にどれほど工数が割けているのか、どれに工数を奪われているのか、確認しようと思えば柔軟にピボット表を組み替えれば分析できますので、便利です。

ちなみに少数チームであれば本ツールを使って担当者ごとの簡易な工数集計はできます。
 ※が、上述したリスト機能は入力ミス防止のため必須なのでやめた方がいいと思います。
  チームで使うのであればユーザ入力フォームの作り込みは必須だと感じます。

どんな形であれ自身の業務工数を確認し、振り返りや可視化を行うのはいいことなので実施してみることをオススメします。