ShinyとVisTimeでガントチャートを作成する

# はじめに
いろんなプロジェクトのスケジュールを管理するとき，ガントチャートが作れればいいなあ，と思うことがあるかと思います．ガントチャートが作成できるサービスは数多く存在しますが，多くは有料であったり（AsanaやWrikeなど），導入が難しかったり（Redmineなど）します．

2022年10月から，plotlyベースのガントチャートを作成するRのライブラリである，[VisTime](https://cran.r-project.org/web/packages/vistime/vignettes/vistime-vignette.html)が提供されています．手元にはRやShinyがあるので，VisTimeを使用してガントチャートを作成するツールを構築してみました．ツールはshinyapps.ioで公開しています．このアプリでは，ShinyApp内でデータフレームを作成し，そのデータフレームにしたがってガントチャートをVisTimeで表示できる形になっています．

https://xjorv.shinyapps.io/visTimeLine_trial/

コードは以下に保存しています．
https://github.com/sb8001at/visTimeLine

このアプリをShinyで構築する際に用いたノウハウをココに残しておきます．

# VisTimeの使い方
VisTimeの使い方についてはこのリンク（[VisTime](https://cran.r-project.org/web/packages/vistime/vignettes/vistime-vignette.html)）や以下のページに詳しく記載されています．
https://www.karada-good.net/analyticsr/r-558/

vistime関数の引数としてデータフレームを指定し，さらにデータフレーム上のイベント名の列，イベントグループの列，イベントの開始日，イベントの終了日，イベントの色を引数で指定するだけで[plotly](https://plotly.com/r/)ベースのガントチャートを作成できます．この際，イベント名やイベントグループは文字列，イベント開始・終了日は日付，色は16進数（#000000など）の文字列で指定します．

```r
library(vistime)

# schedule_dはデータフレーム，以下イベント名，イベントグループ，開始日，終了日，イベントの色
vistime(schedule_d,
    col.event="event_name_v",
    col.group="event_group_v",
    col.start="start_date_v",
    col.end="end_date_v",
    col.color="selectedColor_v"
)
```
# Shiny上でデータフレームを更新する
Shinyでは，変数のスコープが厳密にコントロールされており，observeEvent関数内でデータフレームを設定しても，関数外にそのデータフレームを持ち出すことができません．ですので，UI側で入力した値をデータフレームとして保持し，編集するのが比較的難しいです．

スコープを乗り越えてデータフレームをobserveEvent関数から持ち出す方法については，以下のstack overflowが参考になります．
[Using Shiny to update dataframe values and access it in the local environment after Shiny session ends](https://stackoverflow.com/questions/65341002/using-shiny-to-update-dataframe-values-and-access-it-in-the-local-environment-af)

ただ，データフレームをこの方法で持ち出してみるとなかなか上手くいかない上，VisTimeで求められるデータの型（文字列と日付）が異なっているので，データフレームを1行ずつ更新するのが難しいという問題があります．ですので，VisTimeの各引数をベクトルとして更新し，VisTimeで表示する直前にデータフレームを生成する形としています．

予定を1つ追加する場合にはベクトルに1項目追加，1つ削る場合にはベクトルの最後の項目を削除，予定を全削除する場合にはベクトルを空にする，という形で構成しています．

ベクトルの更新では，reactive関数でInputを受けておきます．別途，入力を受け取るベクトルをreactiveVal関数の引数として変数としておきます．observeEvent関数内では，この変数に数値を追加する形で新しいベクトルを作成し，このベクトルでreactiveVal関数の変数を更新する，という謎の作業をすることでベクトルを持ち出せます．

…何を言っているのかわからないとは思いますが，具体的には下のコードのようなことをしています．

```server.R
# dataframe (schedule_v)を更新するために，各列の要素のInputをreactive関数でとらえる（変数1つ分だけ例示）
event_name <- reactive({input$eventName})

# 要素の更新にはreactiveVal関数が必要なので，各列の要素を保存するベクターはreactiveVal関数で形を変えておく
en_rv <- reactiveVal(event_name_v)

observeEvent(input$scheduleFileAddButton, {
    # 各列のベクターに入力項目を追加
    new_en_v <- c(en_rv(), event_name())
    
    # reactiveValの出力を更新
    en_rv(new_en_v)

    # reactiveValの出力をベクターとして再入力
    event_name_v <<- en_rv()
})
```

この謎プロセスでベクトルをobserveEvent関数から持ち出すことができます．ただし，普通は持ち出せていいことは無いようにも思います．

# 色を選択するUIを準備する
UI上で色を選んでガントチャート上での表示ができるよう，色選択するためのUIを作成しています．色選択のUI作成は以下のページを参考にて作成しています．
[画像から素敵なカラーパレットをつくりたい](https://qiita.com/sasaki_K_sasaki/items/bc46b58c9d140b4c3e83)

UI側のselectInputで色コードを選択，入力し，server側で入力された色の棒グラフをggplot2で作成，UI側（sidebarPanel上）のplotOutputでこの棒グラフを受け取って表示しています．簡単かつ視覚的にも分かりやすい形で色を選択することができます．

```ui.R
selectInput(
    "colorEvent",
    "イベントの表示色",
    choices = c(
    "青" = "#0d6efd",
    "インディゴ" = "#6610f2",
    "紫" = "#6f42c1",
    "ピンク" = "#d63384",
    "赤" = "#dc3545",
    "オレンジ" = "#df7e14",
    "黄" = "#ffc107",
    "緑" = "#198754",
    "緑茶" = "#20c997",
    "シアン" = "#0dcaf0",
    "灰" = "#adb5bd"
)
# カラーバーの出力
plotOutput("colorbar", height = "20px"),
```

```server.R
# カラーバーの作成
output$colorbar <- renderPlot({
ggplot(
    data.frame(x=1, y=1),
    aes(x=x, y=y, fill=selectedColor())) +
    geom_bar(stat="identity") +
    scale_fill_manual(values = selectedColor()) +
    theme_void() + 
    theme(legend.position="none")
})
```
# 感想
他に工夫した点はほとんどありません．Shiny上で作成したデータフレームのダウンロード・アップロードはExcelの比較ツールを作成した際に用いたものを参考としており，日付データの編集にはlubridateを，Shinyのデザイン変更にはbslibを用いています．この程度のツールであれば，（データフレームのスコープ外持ち出しを除くと）簡単に作れるのがShinyのいいところだと思います．

ただし，使い勝手がいいツールができたかというと，微妙なところです．いちいち日付を編集してデータを書き換えるのが面倒くさい…．やはりGUIでマウスを触っているだけでガリガリ日付や予定を変更・追加できるツールの方がいいですね．[Frappe](https://frappe.io/)などを使えば無料でもっと良いものが作れるのかもしれません（日本語では https://zenn.dev/phi/articles/how-to-use-frappe-gantt-js に詳しい解説があります）．

できたものはイマイチですが，作成することで勉強にはなりました．