PowerShell Core + イベント管理

これまで2002年より、Sakura-Conのゲストリレーションでスタッフをするにあたり、数々の技術を使用し、イベント計画などを立ててきました。過去には、Org-modeや小型のPythonスクリプトなどを使用してきましたが、今年は、PowerShell Coreを使用し、システムを構築しました。これまでに、PowerShellを使用して、多岐にわたるシステムを構築し、例えばカラオケ歌唱データの集計などがその一例で、他の領域での使用も検討していました。

目的

今回の目的としては、中核的なソリューションとして次を検討しました。

  • イベントスケジュールデータの集計と、可用性を高めたデータの作成。
  • カレンダーデータなどへの他の有用なデータ形式への変換の提供。

PowerShell Coreを選択した理由は以下のような理由からです。

  • LinuxとWindowsを使用している関係上、複数のプラットフォームで動作できる環境が重要でした。Androidで動けばなおプラスですが、この点に関してはUserLAndなどの環境を使用することで可能でした。(尚、Androidで使用する場合は、このようなキーボードの使用がおすすめです。
  • オフラインで使用可能なこと。イベント会場などではインターネット接続が劣悪になることがあり、インターネット接続なしでも使用できることが重要でした。

構成と内容

最初に作成したモジュールはスケジュールシステムより、データを取得するためのシステムでした。スケジュールシステムは、データを取得するためのAPIを持っておらず、この部分に関してはSeleniumを使用し、データを抽出しました。

Seleniumを使用したデータの抽出(守秘事項のため、使用しているソリューション名に関しては伏せてあります。)

Seleniumを使用してデータを取得するのには800件のエントリーで約30秒を要します。(実際に使用するデータはその8分の1程度。)SeleniumモジュールにおいてはUI上で必要な情報が存在している場所まで移動し、結果をDOMより抽出するようなものです。これはC#でプログラムしました。SeleniumはLinuxとWindowsでドライバが提供されているため、これはクロスプラットフォーム動作が可能です。(こちらは、Androidでは動きませんが、Androidで動作するドライバと対応するブラウザがあれば恐らく可能です。)

このシステムは以下のようなデータ構造を列として出力します。

   TypeName: SakuraCon.Relations.Data.Event
Name        MemberType     Definition
----        ----------     ----------
End Time    AliasProperty  End Time = EndTime
Event Title AliasProperty  Event Title = EventTitle
Start Time  AliasProperty  Start Time = StartTime
Equals      Method         bool Equals(System.Object obj)
GetHashCode Method         int GetHashCode()
GetType     Method         type GetType()
ToString    Method         string ToString()
EndTime     Property       datetime EndTime {get;set;}
EventId     Property       string EventId {get;set;}
EventTitle  Property       string EventTitle {get;set;}
Notes       Property       string Notes {get;set;}
Rating      Property       string Rating {get;set;}
StartTime   Property       datetime StartTime {get;set;}
Type        Property       string Type {get;set;}
Venue       Property       string Venue {get;set;}
Duration    ScriptProperty System.Object Duration {get=($this.EndTime - $this.StartTime);}

これに加え、以下のようなps1xmlを定義しました。

<Types>
    <Type>
        <Name>SakuraCon.Relations.Data.Event</Name>
        <Members>
            <MemberSet>
                <Name>PSStandardMembers</Name>
                <Members>
                    <PropertySet>
                        <Name>DefaultDisplayPropertySet</Name>
                        <ReferencedProperties>
                            <Name>Event Title</Name>
                            <Name>Venue</Name>
                            <Name>Type</Name>
                            <Name>Start Time</Name>
                            <Name>End Time</Name>
                            <Name>Duration</Name>
                            <Name>Notes</Name>
                        </ReferencedProperties>
                    </PropertySet>
                </Members>
            </MemberSet>
            <AliasProperty>
                <Name>Event Title</Name>
                <ReferencedMemberName>EventTitle</ReferencedMemberName>
            </AliasProperty>
            <AliasProperty>
                <Name>Start Time</Name>
                <ReferencedMemberName>StartTime</ReferencedMemberName>
            </AliasProperty>
            <AliasProperty>
                <Name>End Time</Name>
                <ReferencedMemberName>EndTime</ReferencedMemberName>
            </AliasProperty>
            <ScriptProperty>
                <Name>Duration</Name>
                <GetScriptBlock>($this.EndTime - $this.StartTime)</GetScriptBlock>
            </ScriptProperty>
        </Members>
    </Type>
</Types>

実際の運用では、このデータをClixmlとして出力し、後ほど読み込めるようにします。PowerShellではこれを可能にする便利なCmdletが用意されています。

$schedule | Export-Clixml schedule.xml

これを読み込むのも容易です。

$schedule = Import-Clixml schedule.xml

これにより、出力されたXMLは取得したデータのスナップショットになるので、オフラインでのアクセスが可能になります。

このデータをもとに、CSV (スケジュール情報等を集計するのに使用) する他、iCalendarなどのフォーマットを出力し、Google Calendarに入力することが可能になります。

このデータ構造により、Where-Objectを使用することにより、必要な情報を取り出すことができるようになります。例えば、「6C」で行われるイベントを知りたい場合は以下のようになります。

$schedule | ?{ $_.Venue -eq "6C" }

これに加え、以下のようなスクリプトを組み合わせました。

function Get-ScNextEvent {
    param(
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        $Schedule,
        [parameter(Mandatory = $false)]
        [uint] $Hour = 1
    )
    $result = $Schedule | Where-Object { ($_.StartTime -ge [DateTime]::Now) -and ($_.EndTime -le [DateTime]::Now.AddHours($Hour))}

    return ($result | Sort-Object StartTime)
}

function Get-ScCurrentEvent {
    param(
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        $Schedule
    )
    $result = $Schedule | Where-Object { ($_.StartTime -ge [DateTime]::Now) -and ($_.EndTime -le [DateTime]::Now)}

    return ($result | Sort-Object StartTime)
}

これにより、次のイベントや、現在進行中のイベントに関する情報を検索することができます。

他の分野でのPowerShell Coreの使用

PowerShell Core`はスケジュール管理以外の分野でも使用しました。他の部分で使用したのは定形文書を生成する管理システムです。ほとんどの定型文書はLaTeXソースとして生成されているため、コマンドラインより、パーソナライズされた情報を読み込む形式になっています。PowerShellはCSVをデータ構造に変換する仕組みを持っているため、必要な引数はCSVでまとめました。

Windowsでのコマンドラインの文字列の扱いには難があるため、これをWindowsで実行する場合にはWindows Subsystem for Linux (WSL)を使用する必要がありましたが、PowerShell CoreはLinuxでも動作するため、同じモジュールやスクリプトをそのまま使用することが可能でした。

これにより、20通を30秒程度で生成できます。

まとめ

PowerShell Coreにより、クロスプラットフォームな、一貫した環境をリレーション部内で使用する必要のある一部の情報の処理で使用しました。

今後はこのシステムを拡張し、スケジュールの競合探知、負荷確認や人員管理などにも適用できるようにしていく予定です。