デヂエのデータをPleasanterに移行してみた。

# はじめに
この記事は、サイボウズデヂエのライブラリを、プリザンターに移行した際の手順をまとめたものです。
添付ファイルを含むデヂエのデータを、添付ファイルと共にプリザンターに移行します。

#### プリザンターについて

オープンソースソフトウェアのローコード開発プラットフォームです。マウス操作で素早く簡単に業務アプリを作成でき、最小限のコードで機能を拡張できるので様々な要件に合わせた業務アプリを開発できます。
https://pleasanter.net



# 環境
- 移行元：サイボウズ デヂエ 8
- 移行先：Pleasanter バージョン 1.3.0.0

# 準備

#### デヂエ
最初から登録されている、商談報告書を利用します。
<img width="1250" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/922382/11037a9b-3b8b-a338-8ba7-eca084fd841f.png">

<!--![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/922382/ec9f4fb6-22db-e20d-0109-4be8fb2822fd.png)-->

Pleasanterへの登録バリエーションを確認したいので、添付ファイルと金額のフィールドを追加しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/922382/959c2b8e-aa07-83c9-b69f-1fe672dc4b7d.png)



#### Pleasanter
デヂエの項目に対応するように用意します。
![screenshot-localhost-2022.03.02-17_50_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/922382/13bf7c03-22c6-aba5-daee-86887625da2e.png)

# ポイント
いくつか、Pleasanterでは対応していない属性だったり、考慮が必要な項目があったので整理。

### 添付ファイル
以下フォルダに保存される模様。ただし、デヂエの仕様に従いフォルダ・ファイルが作られているようで考慮が必要。実現方法は後述します。
\cgi-bin\cbdb\dze\file\DB
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/922382/eeee2c3a-1ede-8100-1f03-827015d7c4c2.png)
フォルダ名、ファイル名がデヂエのライブラリ、レコード、フィールドと紐づいているようです。

### ラジオボタン
これは、Pleasanterには無い項目属性。ですが、分類項目の選択肢設定に置き換えます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/922382/1adc9f90-e130-b584-0635-78d9e7558a92.png)

# 方針
デヂエの機能で出力したxmlをインプットとし、Powershellを使ってAPI経由でPleasanterへ登録してみたいと思います。

補足：PleasanterのAPI仕様は[マニュアル](https://pleasanter.org/manual/api)を参照ください。

# XMLを見てみる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/922382/b7253100-3859-066f-2ad4-a622dadc29d9.png)

field-listタグの要素をPleasanterの項目と紐づけます。
|項目名|デヂエのfield ID|Pleasanterへ登録する項目ID|補足|
|:----|:----|:----|:----|
|日付|4|DateB| |
|部署名|5|ClassA| |
|名前|6|ClassB| |
|お客様コード|117|ClassC| |
|お客様名|7|ClassD| |
|商談項目|8|ClassF| |
|商談内容|9|DescriptionA| |
|見込みランク|10|ClassE|ラジオ項目はPleasanterには分類項目として設定|
|次回訪問日|11|DateA| |
|上司のチェック|12|CheckA| |
|添付ファイル|136|AttachmentsA|添付ファイルは別途考慮|
|金額|142|NumA| |

ラジオボタン項目は以下のように設定してみました。
![screenshot-localhost-2022.03.03-11_32_57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/922382/e9d21418-270e-52a2-fe0a-53a7b3fe2b67.png)
xmlには、”A”、”B”、”C”　と設定されているので、これで行けるはず。


# 実装

### 定義宣言等

```powershell
# pleasanter登録用のUrlを指定します。
$url = 'http://localhost/api/items/99999/create'

# pleasanter登録用のApiKeyを指定します。
$ApiKey = 'xxxxxxxxxxxxxxxxxxxxxxx'

# デヂエから出力したxmlファイルのPATHを指定します。
$source = 'C:\temp\db65.xml'

# デヂエ資源のPATHを指定します。（添付ファイルを登録するため）
$deziePatch = 'C:\Program Files (x86)\Apache Software Foundation\Apache2.2\cgi-bin\cbdb\dze'

# AttachmentsのcontentType判定用
Add-Type -AssemblyName System.Web
```

連想配列のKeyにデヂエのフィールドID、ValueにPleasanterの項目IDを設定、お互いを紐づけます。

```powershell
# デヂエのフィールドIDとpleasanterの項目IDの対応を指定します。
$ConvItemsId = @{

    '4' = 'DateB'
    '5' = 'ClassA'
    '6' = 'ClassB'
    '117' = 'ClassC'
    '7' = 'ClassD'
    '8' = 'ClassF'
    '9' = 'DescriptionA'
    '10' = 'ClassE'
    '11' = 'DateA'
    '12' = 'CheckA'
    '136' = 'AttachmentsA'
    '142' = 'NumA'

}
```

### メイン処理

```powershell

function main {
    write-host 'main start'

    # xmlオブジェクト作成
    $xmlObj = [System.Xml.XmlDocument](Get-Content $source)

    # ライブラリIDを取得
    $libraryId = $xmlObj.dezie.library.GetAttribute('id')

    # record単位でループ
    foreach ($record in $xmlObj.dezie.library.'record-list'.record) {

        # レコードIDを取得
        $recordId = $record.GetAttribute('id')    

        $Hash = @{}
        $AttachmentsHash = @{}
        $ClassHash = @{}
        $DateHash = @{}
        $CheckHash = @{}
        $NumHash = @{}
        $DescriptionHash = @{}

        Write-Host 'recordId = ' $recordId
        
        # xmlのvalue要素からPleasanterへ登録する情報を編集
        foreach($valueItem in $record.value){

            # フィールドIDを取得
            $fieldId = $valueItem.GetAttribute('id')

            # フィールドIDからPleasanterの項目IDを判断し、各項目分類ごとに編集
            switch -Wildcard ($ConvItemsId[$fieldId]) {

                'Attachments*' {

                    # ファイル名からcontentTypeを取得
                    $contentType = [System.Web.MimeMapping]::GetMimeMapping($valueItem.file.innerText)
                    
                    # 添付ファイルはライブラリID、フィールドID、レコードIDより対象ファイルを判断
                    $attachFile = EditAttach-Path $libraryId $fieldId $recordId                    

                    # base64に変換
                    $b64 = ConvertTo-Base64 $attachFile
                    
                    # リクエストjson編集
                    $attachments = New-Object System.Collections.ArrayList
                    $attachments.Add(@{
                        Name = $valueItem.file.innerText
                        ContentType =  $contentType
                        Base64 = $b64
                    })
                    $AttachmentsHash.Add($ConvItemsId[$fieldId],$attachments)

                }
                
                'Class*' {
                    
                    # リクエストjson編集
                    $ClassHash.Add($ConvItemsId[$fieldId],$valueItem.innerText)
                
                }

                'Num*' {
                    
                    # リクエストjson編集
                    $NumHash.Add($ConvItemsId[$fieldId],[int]$valueItem.innerText)
                
                }

                'Check*' {
                    
                    # リクエストjson用にbool値に変換
                    if($valueItem.innerText -eq '0'){

                        $CheckHash.Add($ConvItemsId[$fieldId],$false)

                    }else{

                        $CheckHash.Add($ConvItemsId[$fieldId],$true)

                    }
                }

                'Date*' {
                    
                    # リクエストjson用にdatetimeに変換
                    $date = [DateTime]::ParseExact($valueItem.date,'yyyy-MM-dd', $null);
                    $DateHash.Add($ConvItemsId[$fieldId],$date)
                }

                'Description*' {
                    $DescriptionHash.Add($ConvItemsId[$fieldId],$valueItem.innerText)

                }
            }
        }


        # リクエストjson編集
        $Hash.Add('ApiVersion',1.1)
        $Hash.Add('ApiKey',$ApiKey)
    
        if($AttachmentsHash.Count -gt 0) {
            $Hash.Add('AttachmentsHash',$AttachmentsHash)    
        }

        if($ClassHash.Count -gt 0) {
            $Hash.Add('ClassHash',$ClassHash)
        }

        if($NumHash.Count -gt 0) {
            $Hash.Add('NumHash',$NumHash)    
        }

        if($CheckHash.Count -gt 0) {
            $Hash.Add('CheckHash',$CheckHash)    
        }

        if($DateHash.Count -gt 0) {
            $Hash.Add('DateHash',$DateHash)    
        }

        if($DescriptionHash.Count -gt 0) {
            $Hash.Add('DescriptionHash',$DescriptionHash)    
        }

        $requestBody = $Hash | ConvertTo-Json -Depth 3

        #write-host $requestBody

        $Byte = [System.Text.Encoding]::UTF8.GetBytes($requestBody)
        
        # APIリクエスト発行
        $res = Invoke-RestMethod -Uri $url -ContentType 'application/json' -Method POST -Body ${Byte}

        Write-Host 'Reg to Pleasanter Comp' 
    }

    write-host 'main end'
}
```

### 添付ファイル用処理

ライブラリID＋フィールドID＋レコードIDで対象の添付ファイルを判断します。

```powershell
function EditAttach-Path($libraryId, $fieldId, $recordId) {

    $path = $deziePatch + '\file\DB\' + $libraryId +'\' + $fieldId + '\' + $recordId
    return $path

}

```

API登録用にBase64に変換します。
```powershell
function ConvertTo-Base64($filePath) {

    $file = Get-Item $filePath
    $b64 = [Convert]::ToBase64String([System.IO.File]::ReadAllBytes($file.FullName))
    return $b64

}
```

# 実行結果

登録できました。添付ファイルもちゃんと取得できます。

![screenshot-localhost-2022.03.03-11_41_13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/922382/dfa92a2b-814e-2c83-641d-d8a41ccbf41c.png)


# まとめ
デヂエから出力されるxmlが意外とわかりやすく、内容を確認してどういう移行仕様にするかを整理できれば、取り込みは割と簡単にできそうです。

# サンプル
参考に、[[こちら]](https://github.com/m-ishikawa1980/transitionDezie)に実装したpowershellのサンプルスクリプトと、pleasanterのサイトパッケージがあります。

- transitionDezie.ps1
- 商談報告書_2022_03_03 17_28_48.json