ダッシュボードレポート - モデレーター活動

これはモデレーターアクティビティのダッシュボードレポートのSQLバージョンです。

このレポートは、指定された期間内にDiscourseサイトでモデレーターによって実行されたアクティビティの包括的な概要を提供します。レポートは、モデレーターアクティビティの複数の側面を組み合わせています:プラットフォームでの滞在時間、レビューされたフラグの数、作成された投稿、作成された個人メッセージ(PM)、作成されたトピック、および作成された投稿の改訂。

このダッシュボードレポートは、モデレーションチームの効果とエンゲージメントを測定したい管理者にとって貴重なツールであり、彼らのアクティビティと貢献の詳細な分析を提供します。レポートによって提供される洞察は、モデレーターのトレーニング、表彰、および採用に関する意思決定に情報を提供し、コミュニティ基準を維持するためにモデレーションチームが適切にバランスが取れて効果的であることを保証するのに役立ちます。

--[params]
-- date :start_date = 2023-12-01
-- date :end_date = 2024-12-01
-- boolean :include_admins = false


WITH mods AS (
    SELECT
        id AS user_id,
        username_lower AS username,
        uploaded_avatar_id
    FROM users u
    WHERE ((u.moderator = true) OR (:include_admins AND u.admin = true))
    AND u.id > 0

),

time_read AS (
    SELECT SUM(uv.time_read) AS time_read,
        uv.user_id
    FROM mods m
    JOIN user_visits uv ON m.user_id = uv.user_id
    WHERE uv.visited_at >= :start_date
        AND uv.visited_at <= :end_date
    GROUP BY uv.user_id
),

flag_count AS (
    WITH period_actions AS (
        SELECT agreed_by_id,
        disagreed_by_id
        FROM post_actions
        WHERE post_action_type_id IN (3,4,8,6,7)
        AND created_at >= :start_date
        AND created_at <= :end_date
    ),

agreed_flags AS (
        SELECT pa.agreed_by_id AS user_id,
        COUNT(*) AS flag_count
        FROM mods m
        JOIN period_actions pa
        ON pa.agreed_by_id = m.user_id
        GROUP BY agreed_by_id
    ),

disagreed_flags AS (
        SELECT pa.disagreed_by_id AS user_id,
        COUNT(*) AS flag_count
        FROM mods m
        JOIN period_actions pa
        ON pa.disagreed_by_id = m.user_id
        GROUP BY disagreed_by_id
    )
    SELECT
    COALESCE(af.user_id, df.user_id) AS user_id,
    COALESCE(af.flag_count, 0) + COALESCE(df.flag_count, 0) AS flag_count
    FROM agreed_flags af
    FULL OUTER JOIN disagreed_flags df
    ON df.user_id = af.user_id
      ),

revision_count AS (
      SELECT pr.user_id,
      COUNT(*) AS revision_count
      FROM mods m
      JOIN post_revisions pr
      ON pr.user_id = m.user_id
      JOIN posts p
      ON p.id = pr.post_id
      WHERE pr.created_at >= :start_date
      AND pr.created_at <= :end_date
      AND p.user_id <> pr.user_id
      GROUP BY pr.user_id
),

topic_count AS (
      SELECT t.user_id,
        COUNT(*) AS topic_count
      FROM mods m
      JOIN topics t ON t.user_id = m.user_id
      WHERE t.archetype = 'regular'
          AND t.created_at >= :start_date
          AND t.created_at <= :end_date
      GROUP BY t.user_id
),

post_count AS (
      SELECT p.user_id,
         COUNT(*) AS post_count
      FROM mods m
      JOIN posts p ON p.user_id = m.user_id
      JOIN topics t ON t.id = p.topic_id
      WHERE t.archetype = 'regular'
          AND p.created_at >= :start_date
          AND p.created_at <= :end_date
      GROUP BY p.user_id
),

pm_count AS (
      SELECT p.user_id,
        COUNT(*) AS pm_count
      FROM mods m
      JOIN posts p ON p.user_id = m.user_id
      JOIN topics t ON t.id = p.topic_id
      WHERE t.archetype = 'private_message'
          AND p.created_at >= :start_date
          AND p.created_at <= :end_date
      GROUP BY p.user_id
      )

SELECT
    m.user_id,
    m.username,
    fc.flag_count as flags_reviewed,
    ROUND(((tr.time_read) / 3600.00),2) as time_reading_hours,
    tc.topic_count as topics_created,
    pmc.pm_count as PMs_created,
    pc.post_count as posts_created,
    rc.revision_count as revisions
FROM mods m
LEFT JOIN time_read tr ON tr.user_id = m.user_id
LEFT JOIN flag_count fc ON fc.user_id = m.user_id
LEFT JOIN revision_count rc ON rc.user_id = m.user_id
LEFT JOIN topic_count tc ON tc.user_id = m.user_id
LEFT JOIN post_count pc ON pc.user_id = m.user_id
LEFT JOIN pm_count pmc ON pmc.user_id = m.user_id
ORDER BY m.username ASC

パラメータ

  • :start_date および :end_date - レポートの期間を定義するパラメータです。両方の日付パラメータは YYYY-MM-DD の日付形式を受け入れます。
  • :include_admins - モデレーターと一緒に管理者をレポートに含めるかどうかを決定するパラメータです。

SQLクエリの説明

レポートは、共通テーブル式(CTE)を使用して、データ処理を管理可能で論理的なセクションに分割するように構造化されています。各CTEで実行されることは次のとおりです。

  1. mods: モデレーター権限を持つすべてのユーザー、または(:include_admins パラメータによって含まれる場合)管理者権限を持つユーザーを特定します。後続のクエリに必要なユーザー列のみを選択します。
  2. time_read: 提供された開始日と終了日の間に、モデレーターがプラットフォームでコンテンツを読んだ合計時間(秒単位)を計算します。
  3. flag_count: 指定された期間中にモデレーターが同意または不同意したフラグの数をカウントします。それぞれの投稿アクションタイプIDで表される複数のフラグタイプを考慮に入れます。
  4. revision_count: 指定された期間内にモデレーターが他のユーザーの投稿に対して行った投稿の改訂回数をカウントします。
  5. topic_count: モデレーターによって作成された通常のトピックの数をカウントします。
  6. post_count: 通常のトピック内でモデレーターによって作成された投稿の数をカウントします。
  7. pm_count: モデレーターによって開始された個人メッセージの数をカウントします。

CTEでデータを収集した後、メインクエリはユーザーIDに基づいてそれらを結合し、各モデレーターのユーザー名、読書に費やした合計時間(時間に変換)、レビューされたフラグの数、作成されたトピック、作成された個人メッセージ、作成された投稿、および作成された改訂を表示する最終レポートをコンパイルします。結果は、モデレーターのユーザー名のアルファベット順に並べ替えられます。

結果例

user username flags_reviewed time_reading_hours topics_created pms_created posts_created revisions
1 moderator1 NULL 36.11 NULL 344 8 15
2 moderator2 46 104.52 2 271 466 363
3 moderator3 NULL 72.15 NULL 418 64 16
「いいね!」 7

@SaraDev様

これは非常に役立ちます。「モデレーターアクティビティ」レポートに「承認済み投稿」と「却下済み投稿」の列を追加することは可能でしょうか?

よろしくお願いします。
T_Disco

「いいね!」 3

こんにちは @T_Disco

はい、「承認済み投稿」と「却下済み投稿」の結果をモデレーターアクティビティレポートに追加するには、reviewables および reviewable_scores テーブルを利用できます。具体的には、reviewable_scoresstatus フィールドを使用して、投稿が承認された(status = 1)か却下された(status = 2)かを判断できます。

レポートにこれを追加すると、次のようになります。

承認済み投稿と却下済み投稿を含むモデレーターアクティビティ

-- [params]
-- date :start_date = 2023-12-01
-- date :end_date = 2024-12-01
-- boolean :include_admins = false

WITH mods AS (
    SELECT
        id AS user_id,
        username_lower AS username,
        uploaded_avatar_id
    FROM users u
    WHERE ((u.moderator = true) OR (:include_admins AND u.admin = true))
    AND u.id > 0
),

time_read AS (
    SELECT SUM(uv.time_read) AS time_read,
        uv.user_id
    FROM mods m
    JOIN user_visits uv ON m.user_id = uv.user_id
    WHERE uv.visited_at >= :start_date
        AND uv.visited_at <= :end_date
    GROUP BY uv.user_id
),

flag_count AS (
    WITH period_actions AS (
        SELECT agreed_by_id,
        disagreed_by_id
        FROM post_actions
        WHERE post_action_type_id IN (3,4,8,6,7)
        AND created_at >= :start_date
        AND created_at <= :end_date
    ),

agreed_flags AS (
        SELECT pa.agreed_by_id AS user_id,
        COUNT(*) AS flag_count
        FROM mods m
        JOIN period_actions pa
        ON pa.agreed_by_id = m.user_id
        GROUP BY agreed_by_id
    ),

disagreed_flags AS (
        SELECT pa.disagreed_by_id AS user_id,
        COUNT(*) AS flag_count
        FROM mods m
        JOIN period_actions pa
        ON pa.disagreed_by_id = m.user_id
        GROUP BY disagreed_by_id
    )
    SELECT
    COALESCE(af.user_id, df.user_id) AS user_id,
    COALESCE(af.flag_count, 0) + COALESCE(df.flag_count, 0) AS flag_count
    FROM agreed_flags af
    FULL OUTER JOIN disagreed_flags df
    ON df.user_id = af.user_id
      ),

revision_count AS (
      SELECT pr.user_id,
      COUNT(*) AS revision_count
      FROM mods m
      JOIN post_revisions pr
      ON pr.user_id = m.user_id
      JOIN posts p
      ON p.id = pr.post_id
      WHERE pr.created_at >= :start_date
      AND pr.created_at <= :end_date
      AND p.user_id <> pr.user_id
      GROUP BY pr.user_id
),

topic_count AS (
      SELECT t.user_id,
        COUNT(*) AS topic_count
      FROM mods m
      JOIN topics t ON t.user_id = m.user_id
      WHERE t.archetype = 'regular'
          AND t.created_at >= :start_date
          AND t.created_at <= :end_date
      GROUP BY t.user_id
),

post_count AS (
      SELECT p.user_id,
         COUNT(*) AS post_count
      FROM mods m
      JOIN posts p ON p.user_id = m.user_id
      JOIN topics t ON t.id = p.topic_id
      WHERE t.archetype = 'regular'
          AND p.created_at >= :start_date
          AND p.created_at <= :end_date
      GROUP BY p.user_id
),

pm_count AS (
      SELECT p.user_id,
        COUNT(*) AS pm_count
      FROM mods m
      JOIN posts p ON p.user_id = m.user_id
      JOIN topics t ON t.id = p.topic_id
      WHERE t.archetype = 'private_message'
          AND p.created_at >= :start_date
          AND p.created_at <= :end_date
      GROUP BY p.user_id
),

reviewable_actions AS (
    SELECT
        rs.reviewed_by_id AS user_id,
        SUM(CASE WHEN rs.status = 1 THEN 1 ELSE 0 END) AS posts_approved,
        SUM(CASE WHEN rs.status = 2 THEN 1 ELSE 0 END) AS posts_rejected
    FROM mods m
    JOIN reviewable_scores rs ON rs.reviewed_by_id = m.user_id
    JOIN reviewables r ON r.id = rs.reviewable_id
    WHERE rs.reviewed_at >= :start_date
      AND rs.reviewed_at <= :end_date
    GROUP BY rs.reviewed_by_id
)

SELECT
    m.user_id,
    m.username,
    fc.flag_count as flags_reviewed,
    ROUND(((tr.time_read) / 3600.00),2) as time_reading_hours,
    tc.topic_count as topics_created,
    pmc.pm_count as PMs_created,
    pc.post_count as posts_created,
    rc.revision_count as revisions,
    ra.posts_approved,
    ra.posts_rejected
FROM mods m
LEFT JOIN time_read tr ON tr.user_id = m.user_id
LEFT JOIN flag_count fc ON fc.user_id = m.user_id
LEFT JOIN revision_count rc ON rc.user_id = m.user_id
LEFT JOIN topic_count tc ON tc.user_id = m.user_id
LEFT JOIN post_count pc ON pc.user_id = m.user_id
LEFT JOIN pm_count pmc ON pmc.user_id = m.user_id
LEFT JOIN reviewable_actions ra ON ra.user_id = m.user_id
ORDER BY m.username ASC

このレポートの結果は次のようになります。

user username flags_reviewed time_reading_hours topics_created pms_created posts_created revisions posts_approved posts_rejected
1 moderator1 NULL 36.11 NULL 344 8 15 10 5
2 moderator2 46 104.52 2 271 466 363 7 3
3 moderator3 NULL 72.15 NULL 418 64 16 NULL NULL
「いいね!」 1

素晴らしい、ありがとうございます @SaraDev

これらの列は、/admin/dashboard/moderation タブのモデレーターアクティビティレポートにも追加されますか?

重ねてお礼申し上げます :slight_smile:
T_Disco

「いいね!」 1

@SaraDev
指定された期間に実行されたトピックマージの数を照会することは可能ですか?

「いいね!」 2

現在、レポートのダッシュボードバージョンにこれらの列を追加する計画はありませんが、将来的に改善できるかどうか、チームに提起することができます。:slightly_smiling_face:

はい、投稿が他のトピックに移動された日時を示すクエリを作成するには、posts テーブルを、移動を示す action_code エントリと split_topic エントリがあるレコードでフィルタリングできます。

例:

個々の投稿移動アクション

SELECT
    id AS post_id,
    user_id,
    topic_id,
    post_number,
    created_at::date,
    updated_at::date,
    action_code
FROM
    posts
WHERE
    action_code = 'split_topic'
ORDER BY
    created_at DESC

以下のような結果が表示されます:

post user topic post_number created_at updated_at action_code
投稿が既存のトピックにマージされました:: [マージされたトピックのタイトル] USERNAME 元のトピックのタイトル 3 2024-10-30 2024-10-30 split_topic
2件の投稿が既存のトピックにマージされました:: [マージされたトピックのタイトル] USERNAME 元のトピックのタイトル 5 2024-10-30 2024-10-30 split_topic
投稿が新しいトピックに分割されました: [分割されたトピックのタイトル] USERNAME 元のトピックのタイトル 2 2024-10-30 2024-10-30 split_topic

サイト上のユーザーごとの投稿移動の総数を表示し、日付で移動アクションをフィルタリングするパラメータをいくつか追加したい場合は、次のクエリも使用できます。

ユーザーごとの投稿移動アクション数

-- [params]
-- date :start_date
-- date :end_date

SELECT
    user_id,
    COUNT(*) AS move_count
FROM
    posts
WHERE
    action_code = 'split_topic'
    AND created_at BETWEEN :start_date AND :end_date
GROUP BY
    user_id
ORDER BY
    move_count DESC

例の結果は次のようになります:

user move_count
Username_1 5
Username_2 2

これらのクエリの両方で、1つのトピックから別のトピックに任意の数の投稿を移動することは、移動された投稿の数に関係なく、1回のアクションとしてカウントされることに注意してください。また、トピック全体のコンテンツを別のトピックに移動することも、1回のアクションとしてカウントされます。

各アクション中に移動された投稿の数は、Individual Post Move Actions クエリの post 列のテキスト X posts were merged into an existing topic... で確認できますが、この情報は2番目のクエリには存在しません。

Individual Post Move Actions クエリでは、A post was split to a new topic ... というテキストが表示される投稿もあります。これは、Discourseでは両方のアクションが split_topic アクションとして扱われるため(投稿が別のトピックから移動されているため)、投稿が既存のトピックに移動されるのではなく、新しいトピックに分割されたことを示しています。

「いいね!」 2

5件の投稿が新しいトピックに分割されました: User Page Metrics