0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GitBucket Flexible Gantt Plugin でマイルストーンやラベルでフィルタリングできるようにしました

Last updated at Posted at 2025-12-28

概要

GitBucket は、GitHub のようにネットワーク上で Git リポジトリを共有できる Web サービスです。

  • Apache-2.0 ライセンスで配布されている。
  • Scala で開発されており、Java 17 実行環境があれば、起動可能。
  • お試しであれば、内臓のH2データベースエンジンが利用できる。
    • 実運用には、PostgreSQL や MariaDB などのリレーショナルデータベースサーバーの利用を推奨
  • 非常に簡単にセルフホストが可能です。

現在、GitBucket の issue に以下の項目を設定可能にし、ガントチャートへ反映できる GitBucket Flexible Gantt Plugin を開発中です。

  • 開始日
  • 終了日
  • 進捗
  • 依存 issue (issue 番号をカンマ区切りで複数指定可能)

今回は、0.2.0 でマイルストーンやラベルでフィルタリングを可能にした話です。

前回の記事

メニューからマイルストーンを選択するとタスクが絞り込まれます

ガントチャートの上に Filter メニューを配置しました。

image.png

Milestone から絞り込みたいマイルストーンを選択するとタスクが絞り込まれます。

image.png

以下、ソースの修正について、説明します。

まずはマイルストーンによる絞り込みを行うデータベースアクセスを

IssuePeriodServicegetIssuePeriodsByMilestone メソッドを追加しました。

src/main/scala/io/github/yasumichi/gfg/service/IssuePeriodService.scala
  def getIssuePeriodsByMilestone(
      userName: String,
      repositoryName: String,
      milestoneId: Int
  )(implicit session: Session): List[(gitbucket.core.model.Issue, io.github.yasumichi.gfg.model.IssuePeriod)] = {
    Issues
      .filter(_.byMilestone(userName, repositoryName, milestoneId))
      .join(IssuePeriods)
      .on { case (t1: Issues, t2: IssuePeriods) =>
        t1.userName === t2.userName && t1.repositoryName === t2.repositoryName && t1.issueId === t2.issueId
      }
      .list
  }

ISSUE テーブルをマイルストーンIDで絞り込み、ISSUE_PERIOD と結合し、リスト化しています。

絞り込み条件を渡すため twirl テンプレートを修正

絞り込み条件を渡すため src/main/twirl/flexiblegantt/flexiblegantt.scala.html の引数を追加します。

src/main/twirl/flexiblegantt/flexiblegantt.scala.html
@(
    repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
    milestoneId: Int,
    labelName: String,
    isManageable: Boolean
)(implicit context: gitbucket.core.controller.Context)

後で追加するラベル名もここで追加しておきます。

twirl テンプレートにマイルストーンIDを渡すメソッドをコントローラーに作成

FlexibleGanttController/:owner/:repository/flexible-gantt/milestones/:milestoneId という URL を処理するメソッドを追加します。

src/main/scala/io/github/yasumichi/gfg/controller/FlexibleGanttController.scala
  get("/:owner/:repository/flexible-gantt/milestones/:milestoneId") {
    referrersOnly { repository: RepositoryInfo =>
      {
        implicit val session: Session = Database.getSession(context.request)
        html.flexiblegantt(repository, params("milestoneId").toInt, "", isIssueManageable(repository))
      }
    }
  }

さらにマイルストーンIDで絞り込んだタスクのリストを返すメソッドを作成

さらに FlexibleGanttController/:owner/:repository/flexible-gantt/issues/milestones/:milestoneId という URL を処理するメソッドを追加します。

src/main/scala/io/github/yasumichi/gfg/controller/FlexibleGanttController.scala
  ajaxGet("/:owner/:repository/flexible-gantt/issues/milestones/:milestoneId")(readableUsersOnly { repository =>
    implicit val session: Session = Database.getSession(context.request)
    contentType = formats("json")
    org.json4s.jackson.Serialization.write(
      "list" ->
        getIssuePeriodsByMilestone(repository.owner, repository.name, params("milestoneId").toInt)
          .map { t =>
            Map(
              "id" -> t._1.issueId.toString(),
              "name" -> t._1.title,
              "start" -> t._2.startDate,
              "end" -> t._2.endDate,
              "progress" -> t._2.progress,
              "dependencies" -> t._2.dependencies
            )
          }
    )
  })

フィルタリング条件に応じてタスクのリストを取得する URL を変更する仕組みを作る

Twirl テンプレート src/main/twirl/flexiblegantt/flexiblegantt.scala.html をさらに修正します。

src/main/twirl/flexiblegantt/flexiblegantt.scala.html
        @if(milestoneId == 0 && labelName == "") {
            <script>
                var taskUrl = "@context.baseUrl/@repository.owner/@repository.name/flexible-gantt/issues";
            </script>
        } else if (milestoneId > 0) {
            <script>
                var taskUrl = "@context.baseUrl/@repository.owner/@repository.name/flexible-gantt/issues/milestones/@milestoneId";
            </script>
        } else if (labelName != "") {
            <script>
                var taskUrl = "@context.baseUrl/@repository.owner/@repository.name/flexible-gantt/issues/labels/@labelName";
            </script>
        }

コントローラーから渡された引数に応じて、取得URLを切り替えています。

ラベルによる絞り込みも同様に修正していますがデータベース操作の部分だけ紹介します

IssuePeriodServicegetIssuePeriodsByLabel メソッドを追加しました。

src/main/scala/io/github/yasumichi/gfg/service/IssuePeriodService.scala
  def getIssuePeriodsByLabel(
      userName: String,
      repositoryName: String,
      labelName: String
  )(implicit session: Session): List[(gitbucket.core.model.Issue, io.github.yasumichi.gfg.model.IssuePeriod)] = {
    val issueIds: Query[Rep[Int], Int, Seq] = Labels.filter(_.byLabel(userName, repositoryName, labelName)).join(IssueLabels).on {case (t1: Labels, t2: IssueLabels) => t1.labelId === t2.labelId && t1.userName === t2.userName && t1.repositoryName === t2.repositoryName}.map{case (t1: Labels, t2: IssueLabels) => t2.issueId}
    Issues
      .filter(_.byRepository(userName, repositoryName))
      .filter(_.issueId in issueIds)
      .join(IssuePeriods)
      .on { case (t1: Issues, t2: IssuePeriods) =>
        t1.userName === t2.userName && t1.repositoryName === t2.repositoryName && t1.issueId === t2.issueId
      }
      .list
  }

Slick の理解が不足しており、3つのテーブルを結合しようとするとなかなかコンパイルエラーが解消できなかったため、方向を変えました。

まずは、LABEL テーブルをラベル名で絞り込み、ISSUE_LABEL テーブルと結合し、ISSUE_ID を絞り込みます。

次に ISSUE テーブルをユーザー名とリポジトリ名で絞り込み、さらに絞り込んだ ISSUE_ID で絞り込み、ISSUE_PERIOD テーブルと結合し、リスト化して返します。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?