OpenProject に Metabase をドッキングしたら最強のダッシュボードが作れるかも

# なぜ書いた？
* 趣味で調べたことを職場で参照したい
* もしかすると参考になる人がいるかもしれない
* OpenProject と Metabase の可能性に震えたから

# OpenProject の準備

> OpenProjectは、場所に依存しないチームコラボレーションのためのWebベースのプロジェクト管理システムです。

まず OpenProject のインストール方法は下記の３つであると公式にあります。
docker(-compose) を使うのが手軽そうなのでこれでやってみます。

* DEB/RPM packages
    * 既存のマシンなどへ直接インストール(推奨)
* docker
    * docker-compose で起動
* Installation with Univention Corporate Server
    * VMイメージを入手

>
Installing OpenProject | OpenProject Help & Documentation
https://docs.openproject.org/installation-and-operations/installation/

docker を使ったインストール方法は下記の２つであると公式にあります。
推奨されている前者の方法でやってみます。

* One container per process
    * WebやDBなどの機能ごとにコンテナを分けます(推奨)
* All-in-one container
    * １つのコンテナで全ての機能を賄います

>
Install OpenProject with Docker | OpenProject Help & Documentation
https://docs.openproject.org/installation-and-operations/installation/docker/

とりあえずローカルで試運転する分には上記のページに従って下記のコマンドを打つだけで起動します(簡単！)。ですが Metabase と組み合わせたいので起動の前に `docker-compose.yml` を編集します。

```shell
# docker-compose.yml などを取得する
git clone --depth=1 --branch=stable/10 https://github.com/opf/openproject
# docker-compose.yml のあるディレクトリへ移動する
cd openproject
# デタッチモードで起動
docker-compose up -d
```

`docker-compose.yml` を読み解くと主な要素は下記のように配置されると分かります。
ここへ Metabase を追加してみます。

![deployment1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81857/915db92e-16ef-e34c-b915-de3a62c753bf.png)

<!-- this is a comment. 
@startuml deployment
left to right direction

frame "凡例" {
    cloud networks
    database volumes
    component services
}

cloud localhost
cloud frontend
cloud backend

database pgdata

component db <<postgres>>
component proxy <<openproject/community>>
component web <<openproject/community>>

localhost -- proxy : 8080:80
proxy -- frontend
backend -- db
frontend -- web
web -- backend
db -- pgdata

cloud "metabase-backend" as mb_backend
database "metabase-pgdata" as mb_pgdata
component "metabase-app" as mb_app <<metabase/metabase>>
component "metabase-db" as mb_db <<postgres>>

mb_backend -- mb_db
mb_app -- mb_backend
mb_db -- mb_pgdata
mb_app -- backend
localhost ---- mb_app : 3000:3000
@enduml
-->


# Metabase の準備

> メタベースは、社内の誰もが質問したり、データから学んだりするための簡単でオープンソースな方法です。

Metabase を加えて下記の構成となるよう `docker-compose.yml` を編集します。

![deployment2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81857/d0474c23-b07a-4f8a-d523-641e796e4bbc.png)

そして出来上がった `docker-compose.yml` がこちら。
`docker-compose up -d` で起動させると http://localhost:8080/ に OpenProject が、 http://localhost:3000/ に Metabase が起動します。Metabase の起動が完了するまではしばらく待つかもしれません。

```yml
version: "3.7"

networks:
  frontend:
  backend:
  metabase-backend:

volumes:
  pgdata:
  opdata:
  metabase-pgdata:

x-op-restart-policy: &restart_policy
  restart: unless-stopped
x-op-image: &image
  image: openproject/community:${TAG:-10}
x-op-app: &app
  <<: *image
  <<: *restart_policy
  environment:
    - "RAILS_CACHE_STORE=memcache"
    - "OPENPROJECT_CACHE__MEMCACHE__SERVER=cache:11211"
    - "OPENPROJECT_RAILS__RELATIVE__URL__ROOT=${OPENPROJECT_RAILS__RELATIVE__URL__ROOT:-}"
    - "DATABASE_URL=postgres://postgres:p4ssw0rd@db/openproject"
    - "USE_PUMA=true"
    # set to true to enable the email receiving feature. See ./docker/cron for more options
    - "IMAP_ENABLED=false"
  volumes:
    - "opdata:/var/openproject/assets"
  depends_on:
    - db
    - cache
x-mb-app: &mb-app
  image: metabase/metabase
  <<: *restart_policy
  environment:
    - "MB_DB_TYPE=postgres"
    - "MB_DB_DBNAME=metabase"
    - "MB_DB_PORT=5432"
    - "MB_DB_USER=postgres"
    - "MB_DB_PASS=p4ssw0rd"
    - "MB_DB_HOST=metabase-db"
  depends_on:
    - db
    - metabase-db

services:
  db:
    image: postgres:10
    <<: *restart_policy
    stop_grace_period: "3s"
    volumes:
      - "pgdata:/var/lib/postgresql/data"
    environment:
      - POSTGRES_PASSWORD=p4ssw0rd
      - POSTGRES_DB=openproject
    networks:
      - backend

  cache:
    image: memcached
    <<: *restart_policy
    networks:
      - backend

  proxy:
    <<: *image
    <<: *restart_policy
    command: "./docker/proxy"
    ports:
      - "8080:80"
    environment:
      - APP_HOST=web
      - "OPENPROJECT_RAILS__RELATIVE__URL__ROOT=${OPENPROJECT_RAILS__RELATIVE__URL__ROOT:-}"
    depends_on:
      - web
    networks:
      - frontend

  web:
    <<: *app
    command: "./docker/web"
    networks:
      - frontend
      - backend

  worker:
    <<: *app
    command: "./docker/worker"
    networks:
      - backend

  cron:
    <<: *app
    command: "./docker/cron"
    networks:
      - backend

  seeder:
    <<: *app
    command: "./docker/seeder"
    restart: on-failure
    networks:
      - backend

  metabase-app:
    <<: *mb-app
    ports:
      - "3000:3000"
    networks:
      - backend
      - metabase-backend

  metabase-db:
    image: postgres:10
    <<: *restart_policy
    stop_grace_period: "3s"
    volumes:
      - "metabase-pgdata:/var/lib/postgresql/data"
    environment:
      - POSTGRES_PASSWORD=p4ssw0rd
      - POSTGRES_DB=metabase
    networks:
      - metabase-backend
```

Metabase が起動したら初期設定時に OpenProject の DB を登録してしまいましょう。
「名前」は任意、「データベースユーザー名」と「ポート」は postgres のデフォルトですが、「ホスト」から「データベースパスワード」のその他の項目は `docker-compose.yml` の db サービスの設定から読み取れる内容で埋めます。

![Screenshot_2020-04-26 Metabase.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81857/254b915d-34de-bf99-d1c0-c34c0d117a8c.png)

# OpenProject の DB スキーマを紐解く

※ まだあまり紐解けていないので随時更新予定

OpenProject の Work packages(Redmine におけるチケットに相当する概念) を格納しているテーブルは、Metabase からは Work Packages という名前で見えるようになります(生のSQLで参照する場合は work_packages)。このテーブルには多数の列がありますが下記の列に注目してみます。

* Story Points
    * 相対見積もり
* Derived Estimated Hours
    * 配下のタスクの見積もりの合計(時間単位)

まず一例としてプロダクトバックログ項目に対して割り当てた相対見積もりと、いざ項目を実行しようとしてタスクへブレークダウンした後の時間見積もりが相関しているかウォッチできる散布図を作ってみました(データが少ないので見栄えしない)。

![Screenshot_2020-04-26 Test · ダッシュボード · Metabase.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81857/aff2c044-dfd0-6384-5d23-b4baea93f8f8.png)

最終的には[99%の確率で納期を守るには](https://qiita.com/ping-pong/items/d39b4e3699fe2cd82372)の考え方を使ってプロジェクトの成功率を表示したいと思っています。OpenProject のタイムトラッキング機能を有効にするとタスクの実行にかかった時間を入力できるようになりますが Work Packages テーブルに直接は反映されないので器用に抽出する必要がありそうです。