Skip to content

Instantly share code, notes, and snippets.

@takahashim
Created June 30, 2025 08:38
Show Gist options
  • Save takahashim/6f9967019594a476920cb99b88a47b2e to your computer and use it in GitHub Desktop.
Save takahashim/6f9967019594a476920cb99b88a47b2e to your computer and use it in GitHub Desktop.

Decidim Meeting コンポーネントの投票機能

概要

Decidim の Meeting コンポーネントには、ミーティング中にリアルタイムで投票(Poll)を実施する機能が組み込まれています。この機能により、参加者は質問に対してライブで回答することができ、管理者はその結果をリアルタイムで確認できます。

データ構造

主要なモデル

1. Poll(投票)

ファイル: app/models/decidim/meetings/poll.rb

class Poll < Meetings::ApplicationRecord
  has_one :questionnaire, as: :questionnaire_for
  belongs_to :meeting
  
  QUESTION_TYPES = %w(single_option multiple_option).freeze
end
  • 各ミーティングに1つの投票(Poll)を設定可能
  • アンケート形式で質問を管理
  • 単一選択・複数選択の質問タイプをサポート

2. Questionnaire(アンケート)

ファイル: app/models/decidim/meetings/questionnaire.rb

class Questionnaire < Meetings::ApplicationRecord
  belongs_to :questionnaire_for, polymorphic: true
  has_many :questions, -> { order(:position) }
  has_many :answers
end
  • 投票の質問群を管理
  • 位置順で質問を並び替え
  • 全ての質問・回答を追跡

3. Question(質問)

ファイル: app/models/decidim/meetings/question.rb

class Question < Meetings::ApplicationRecord
  include Decidim::TranslatableResource
  
  QUESTION_TYPES = %w(single_option multiple_option).freeze
  enum status: { unpublished: 0, published: 1, closed: 2 }
  
  belongs_to :questionnaire
  has_many :answers
  has_many :answer_options
end

質問の状態:

  • unpublished: 未公開(管理者のみ表示)
  • published: 公開中(参加者が回答可能)
  • closed: 終了(結果表示のみ)

質問タイプ:

  • single_option: 単一選択(ラジオボタン)
  • multiple_option: 複数選択(チェックボックス)

4. Answer(回答)

ファイル: app/models/decidim/meetings/answer.rb

class Answer < Meetings::ApplicationRecord
  belongs_to :user, optional: true
  belongs_to :questionnaire
  belongs_to :question
  has_many :choices
end
  • ユーザーの回答を記録
  • 複数の選択肢(choices)を持てる

5. AnswerOption(選択肢)

ファイル: app/models/decidim/meetings/answer_option.rb

class AnswerOption < Meetings::ApplicationRecord
  include Decidim::TranslatableResource
  
  belongs_to :question
  has_many :choices
end
  • 質問の選択肢を定義
  • 多言語対応

データベース構造

-- 投票テーブル
CREATE TABLE decidim_meetings_polls (
  id bigint PRIMARY KEY,
  decidim_meeting_id bigint, -- ミーティングとの関連
  created_at timestamp,
  updated_at timestamp
);

-- その他のテーブルは decidim-forms モジュールの構造を使用
-- - decidim_meetings_questionnaires
-- - decidim_meetings_questions  
-- - decidim_meetings_answer_options
-- - decidim_meetings_answers
-- - decidim_meetings_answer_choices

コントローラー構造

1. 管理画面コントローラー

MeetingsPollController(管理者用)

ファイル: app/controllers/decidim/meetings/admin/meetings_poll_controller.rb

主要メソッド:

  • edit: 投票の質問を編集
  • update: 投票の質問を更新
  • answer_options: JSON形式で選択肢を返す
def edit
  enforce_permission_to(:update, :poll, meeting:, poll:)
  @form = form(Admin::QuestionnaireForm).from_model(questionnaire)
end

def update
  Admin::UpdateQuestionnaire.call(@form, questionnaire) do
    on(:ok) { redirect_to after_update_url }
    on(:invalid) { render template: "decidim/meetings/admin/poll/edit" }
  end
end

2. 公開画面コントローラー

QuestionsController(質問管理)

ファイル: app/controllers/decidim/meetings/polls/questions_controller.rb

主要メソッド:

  • index: 質問一覧表示(AJAX)
  • update: 質問の状態更新(公開/終了)
def update
  enforce_permission_to(:update, :question, question:)
  
  Decidim::Meetings::Admin::UpdateQuestionStatus.call(question, current_user) do
    respond_to do |format|
      format.js { render template: admin_index_template }
    end
  end
end

AnswersController(回答処理)

ファイル: app/controllers/decidim/meetings/polls/answers_controller.rb

主要メソッド:

  • admin: 管理者向け結果表示
  • index: 参加者向け投票画面
  • create: 回答の作成
def create
  enforce_permission_to(:create, :answer, question:)
  @form = form(AnswerForm).from_params(params.merge(question:, current_user:))
  
  CreateAnswer.call(@form, questionnaire) do
    respond_to do |format|
      format.js # AJAX レスポンス
    end
  end
end

権限システム

投票関連の権限

ファイル: app/permissions/decidim/meetings/permissions.rb

一般ユーザー権限

  • reply_poll: 投票への回答
    • 条件: ミーティングが存在 && 投票が存在 && 認証済み
def can_reply_poll?
  meeting.present? &&
    meeting.poll.present? &&
    authorized?(:reply_poll, resource: meeting)
end

管理者権限

  • update (poll): 投票の更新
    • 条件: ユーザーが管理者 && ミーティング・投票が存在
def can_update_poll?
  user.present? &&
    user.admin? &&
    meeting.present? &&
    meeting.poll.present?
end

質問関連権限

  • create (answer): 回答の作成
    • 条件: 質問が存在 && ユーザー存在 && 未回答
def can_answer_question?
  question.present? && user.present? && !question.answered_by?(user)
end
  • update (question): 質問の状態更新
    • 条件: ユーザーが管理者 && 質問が存在

投票機能の使用フロー

1. 管理者のワークフロー

  1. 投票作成

    • ミーティング詳細画面で「Manage poll」をクリック
    • /admin/meetings/{meeting_id}/poll/edit にアクセス
  2. 質問設定

    • 質問タイプ選択(単一選択/複数選択)
    • 質問文の入力(多言語対応)
    • 選択肢の設定
  3. 質問公開

    • 質問を published 状態に変更
    • 参加者が回答可能になる
  4. 結果確認

    • リアルタイムで回答状況を確認
    • 質問を closed 状態に変更して結果公開

2. 参加者のワークフロー

  1. 投票参加

    • ミーティングページで「Reply poll」をクリック
    • 権限チェック(認証・参加権限)
  2. 質問回答

    • 公開中の質問に回答
    • AJAX で即座に送信
    • 1つの質問につき1回のみ回答可能
  3. 結果確認

    • 管理者が結果を公開した質問の結果を表示

設定とカスタマイズ

コンポーネント設定

ファイル: config/locales/en.yml

decidim:
  components:
    meetings:
      actions:
        reply_poll: Reply poll
      settings:
        global:
          # 投票関連の設定は現在特別な設定項目なし
          # ミーティング全体の設定を継承

質問タイプ

現在サポートされている質問タイプ:

QUESTION_TYPES = %w(single_option multiple_option).freeze
  • single_option: ラジオボタン(1つのみ選択)
  • multiple_option: チェックボックス(複数選択可能)

技術的な特徴

1. リアルタイム更新

  • JavaScript/AJAX による動的更新
  • ページリロード不要で質問の状態変更
  • 即座の回答反映

2. 多言語対応

  • 質問文・選択肢の多言語対応
  • TranslatableResource を使用

3. 権限管理

  • きめ細かい権限制御
  • 認証・認可の分離
  • ロールベースアクセス制御

4. データ整合性

  • 外部キー制約
  • バリデーション
  • トランザクション処理

制限事項

1. 投票数の制限

  • 1つのミーティングにつき1つの投票のみ
  • 複数の投票を並行実施することは不可

2. 回答の制限

  • 1つの質問につき1ユーザー1回のみ回答可能
  • 回答の修正・削除は不可

3. 質問タイプの制限

  • 現在は選択式のみサポート
  • 自由記述式の質問は未対応

4. 匿名投票

  • 回答はユーザーと紐づけて保存
  • 完全匿名での投票は不可

拡張可能性

1. 新しい質問タイプの追加

# Question モデルを拡張
QUESTION_TYPES = %w(single_option multiple_option text_input rating_scale).freeze

2. 投票結果の可視化

  • グラフ・チャート表示機能
  • エクスポート機能
  • 統計分析機能

3. 高度な権限制御

  • 参加者グループ別の投票権限
  • 段階的な結果公開
  • 投票期間の設定

まとめ

Decidim の Meeting Poll 機能は、リアルタイムでの参加型投票を実現する強力な機能です。シンプルでありながら拡張性を持ち、様々な会議や意思決定プロセスで活用できます。多言語対応や権限管理など、Decidim の基本的な思想を踏襲しており、民主的なプロセスを支援するツールとして設計されています。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment