モデレーションとフラグ付けのアクティビティレポートの分析

健全で包括的なコミュニティを維持するには、フラグ付き投稿のレビュー、モデレーターのパフォーマンス分析、ユーザーコンテンツの管理などを含む効果的なモデレーションが必要です。

このガイドには、モデレーション関連のアクティビティを分析するのに役立つ Discourse 用の多様な SQL レポートが含まれています。

このトピックでは、以下の詳細な Data Explorer クエリを見つけることができます。

  • フラグ解決統計。
  • レビュー対象の解決率。
  • モデレーター固有のパフォーマンス指標。
  • ユーザーのフラグ付けアクティビティに関する洞察。
  • フラグ付きユーザー、投稿、トピックのアクションに関する包括的なデータ

種類別の投稿フラグ解決率

SQL クエリの説明

このクエリは、フラグの種類別にグループ化されたフラグ付き投稿の解決(同意、不同意、保留、削除)の割合を計算します。

-- [params]
-- date :start_date = 2024-01-01
-- date :end_date = 2025-01-01

WITH period_actions AS (
    SELECT pa.id,
           pa.post_action_type_id,
           pa.created_at,
           pa.agreed_at,
           pa.disagreed_at,
           pa.deferred_at,
           pa.agreed_by_id,
           pa.disagreed_by_id,
           pa.deferred_by_id,
           pa.deleted_at,
           pa.post_id,
           pa.user_id,
           COALESCE(pa.disagreed_at, pa.agreed_at, pa.deferred_at) AS responded_at,
           EXTRACT(EPOCH FROM (COALESCE(pa.disagreed_at, pa.agreed_at, pa.deferred_at) - pa.created_at)) / 60 AS time_to_resolution_minutes -- 解決までの時間(分)
    FROM post_actions pa
    WHERE pa.post_action_type_id IN (3,4,6,7,8)
      AND pa.created_at >= :start_date
      AND pa.created_at <= :end_date
),
flag_types AS (
    SELECT pat.id,
           CASE 
               WHEN pat.id = 3 THEN 'off_topic'
               WHEN pat.id = 4 THEN 'inappropriate'
               WHEN pat.id = 6 THEN 'notify_user'
               WHEN pat.id = 7 THEN 'notify_moderators'
               WHEN pat.id = 8 THEN 'spam'
           END AS flag_type
    FROM post_action_types pat
),
flag_resolutions AS (
    SELECT
        pa.post_action_type_id,
        CASE 
            WHEN pa.agreed_at IS NOT NULL THEN 'agreed'
            WHEN pa.disagreed_at IS NOT NULL THEN 'disagreed'
            WHEN pa.deferred_at IS NOT NULL THEN 'deferred'
            WHEN pa.deleted_at IS NOT NULL THEN 'deleted'
        END AS resolution,
        COUNT(*) AS resolution_count
    FROM period_actions pa
    GROUP BY pa.post_action_type_id, resolution
),
flag_totals AS (
    SELECT
        pa.post_action_type_id,
        COUNT(*) AS total_flags
    FROM period_actions pa
    GROUP BY pa.post_action_type_id
),
resolution_percentages AS (
    SELECT
        fty.flag_type,
        fr.resolution,
        fr.resolution_count,
        ft.total_flags,
        ROUND((fr.resolution_count::decimal / ft.total_flags) * 100, 2) AS resolution_percentage
    FROM flag_resolutions fr
    JOIN flag_totals ft ON ft.post_action_type_id = fr.post_action_type_id
    JOIN flag_types fty ON fty.id = fr.post_action_type_id
),
pivoted_data AS (
    SELECT
        flag_type,
        MAX(CASE WHEN resolution = 'agreed' THEN resolution_percentage ELSE 0 END) AS agreed_percentage,
        MAX(CASE WHEN resolution = 'disagreed' THEN resolution_percentage ELSE 0 END) AS disagreed_percentage,
        MAX(CASE WHEN resolution = 'deferred' THEN resolution_percentage ELSE 0 END) AS deferred_percentage,
        MAX(CASE WHEN resolution = 'deleted' THEN resolution_percentage ELSE 0 END) AS deleted_percentage,
        MAX(CASE WHEN resolution = 'agreed' THEN resolution_count ELSE 0 END) AS agreed_count,
        MAX(CASE WHEN resolution = 'disagreed' THEN resolution_count ELSE 0 END) AS disagreed_count,
        MAX(CASE WHEN resolution = 'deferred' THEN resolution_count ELSE 0 END) AS deferred_count,
        MAX(CASE WHEN resolution = 'deleted' THEN resolution_count ELSE 0 END) AS deleted_count,
        MAX(total_flags) AS total_flags
    FROM resolution_percentages
    GROUP BY flag_type
)
SELECT
    flag_type,
    agreed_percentage,
    agreed_count,
    disagreed_percentage,
    disagreed_count,
    deferred_percentage,
    deferred_count,
    deleted_percentage,
    deleted_count,
    total_flags
FROM pivoted_data
ORDER BY flag_type

使用されるパラメータ

  • :start_date: フラグ付き投稿をフィルタリングするための開始日。
  • :end_date: フラグ付き投稿をフィルタリングするための終了日。

CTE(共通テーブル式)の説明

  1. period_actions: 指定された日付範囲内のフラグ付き投稿をフィルタリングし、各フラグの解決までの時間を計算します。
  2. flag_types: フラグ種類の ID を人間が読みやすい名前にマッピングします(例:オフトピック、不適切、スパム)。
  3. flag_resolutions: フラグを種類と解決状況(同意、不同意、保留、削除)でグループ化し、各解決状況の発生回数をカウントします。
  4. flag_totals: 各フラグ種類のフラグ総数を計算します。
  5. resolution_percentages: 解決数とフラグ総数を組み合わせて、各フラグ種類の各解決タイプの割合を計算します。
  6. pivoted_data: データをピボットして、各解決タイプの解決率とカウントを別々の列に表示します。

結果の説明

最終結果は、以下の内容を示すテーブルです。

  • フラグの種類(例:オフトピック、スパム)。
  • 各解決タイプ(同意、不同意、保留、削除)の割合とカウント。
  • 各フラグ種類のフラグ総数。

結果の例

フラグの種類 同意 % 同意数 不同意 % 不同意数 保留 % 保留数 削除 % 削除数 フラグ総数
off_topic 50.00 25 30.00 15 10.00 5 10.00 5 50
spam 70.00 35 20.00 10 5.00 2 5.00 3 50

レビュー対象の解決率

SQL クエリの説明

このクエリは、指定された日付範囲内のレビュー対象アイテム(例:フラグ付き投稿)の解決ステータスを分析します。各フラグ種類ごとに、各解決ステータスの割合とカウントを計算します。

-- [params]
-- date :start_date = 2024-01-01
-- date :end_date = 2025-01-01

WITH flag_data AS (
    SELECT
        r.type AS flag_type,
        r.status AS resolution_status,
        COUNT(*) AS flag_count
    FROM
        reviewables r
    WHERE
        r.created_at BETWEEN :start_date AND :end_date
    GROUP BY
        r.type,
        r.status
),
flag_totals AS (
    SELECT
        flag_type,
        SUM(flag_count) AS total_flags
    FROM
        flag_data
    GROUP BY
        flag_type
),
flag_percentages AS (
    SELECT
        fd.flag_type,
        fd.resolution_status,
        fd.flag_count,
        ROUND((fd.flag_count::decimal / ft.total_flags) * 100, 2) AS percentage
    FROM
        flag_data fd
    JOIN
        flag_totals ft
    ON
        fd.flag_type = ft.flag_type
)
SELECT
    fp.flag_type,
    -- 割合
    COALESCE(MAX(CASE WHEN fp.resolution_status = 1 THEN fp.percentage END), 0) AS pending_percentage,
    COALESCE(MAX(CASE WHEN fp.resolution_status = 2 THEN fp.percentage END), 0) AS approved_percentage,
    COALESCE(MAX(CASE WHEN fp.resolution_status = 3 THEN fp.percentage END), 0) AS rejected_percentage,
    COALESCE(MAX(CASE WHEN fp.resolution_status = 4 THEN fp.percentage END), 0) AS ignored_percentage,
    COALESCE(MAX(CASE WHEN fp.resolution_status = 5 THEN fp.percentage END), 0) AS deleted_percentage,
    -- カウント
    COALESCE(MAX(CASE WHEN fp.resolution_status = 1 THEN fp.flag_count END), 0) AS pending_count,
    COALESCE(MAX(CASE WHEN fp.resolution_status = 2 THEN fp.flag_count END), 0) AS approved_count,
    COALESCE(MAX(CASE WHEN fp.resolution_status = 3 THEN fp.flag_count END), 0) AS rejected_count,
    COALESCE(MAX(CASE WHEN fp.resolution_status = 4 THEN fp.flag_count END), 0) AS ignored_count,
    COALESCE(MAX(CASE WHEN fp.resolution_status = 5 THEN fp.flag_count END), 0) AS deleted_count
FROM
    flag_percentages fp
GROUP BY
    fp.flag_type
ORDER BY
    fp.flag_type

使用されるパラメータ

  • :start_date: レビュー対象をフィルタリングするための開始日。
  • :end_date: レビュー対象をフィルタリングするための終了日。

CTE(共通テーブル式)の説明

  1. flag_data: レビュー対象をフラグ種類と解決ステータスでグループ化し、各組み合わせの発生回数をカウントします。
  2. flag_totals: 各フラグ種類のフラグ総数を計算します。
  3. flag_percentages: フラグ数と総数を組み合わせて、各フラグ種類の各解決ステータスの割合を計算します。

結果の説明

最終結果は、以下の内容を示すテーブルです。

  • フラグの種類。
  • 各解決ステータス(保留中、承認、却下、無視、削除)の割合とカウント。

結果の例

フラグの種類 保留中 % 保留中数 承認 % 承認数 却下 % 却下数 無視 % 無視数 削除 % 削除数
off_topic 20.00 10 50.00 25 10.00 5 10.00 5 10.00 5
spam 10.00 5 70.00 35 10.00 5 5.00 2 5.00 3

モデレーターのフラグ解決

SQL クエリの説明

このクエリは、どのモデレーターがフラグ付き投稿を解決し、処理したフラグの種類、適用した解決内容を示すことで、モデレーターのアクティビティに関する洞察を提供します。各解決タイプの割合とカウントを計算します。

-- [params]
-- date :start_date = 2024-01-01
-- date :end_date = 2025-01-01
-- boolean :only_staff = false

WITH period_actions AS (
    SELECT 
        pa.id,
        pa.post_action_type_id,
        pa.created_at,
        pa.agreed_at,
        pa.disagreed_at,
        pa.deferred_at,
        pa.agreed_by_id,
        pa.disagreed_by_id,
        pa.deferred_by_id,
        pa.deleted_at,
        pa.post_id,
        pa.user_id,
        COALESCE(pa.disagreed_at, pa.agreed_at, pa.deferred_at) AS responded_at,
        EXTRACT(EPOCH FROM (COALESCE(pa.disagreed_at, pa.agreed_at, pa.deferred_at) - pa.created_at)) / 60 AS time_to_resolution_minutes -- 解決までの時間(分)
    FROM post_actions pa
    WHERE pa.post_action_type_id IN (3,4,6,7,8)
      AND pa.created_at >= :start_date
      AND pa.created_at <= :end_date
),
flag_types AS (
    SELECT 
        pat.id,
        CASE 
            WHEN pat.id = 3 THEN 'off_topic'
            WHEN pat.id = 4 THEN 'inappropriate'
            WHEN pat.id = 6 THEN 'notify_user'
            WHEN pat.id = 7 THEN 'notify_moderators'
            WHEN pat.id = 8 THEN 'spam'
        END AS flag_type
    FROM post_action_types pat
),
flag_resolutions AS (
    SELECT 
        pa.user_id,
        pa.post_action_type_id,
        CASE 
            WHEN pa.agreed_at IS NOT NULL THEN 'agreed'
            WHEN pa.disagreed_at IS NOT NULL THEN 'disagreed'
            WHEN pa.deferred_at IS NOT NULL THEN 'deferred'
            WHEN pa.deleted_at IS NOT NULL THEN 'deleted'
        END AS resolution,
        COUNT(*) AS resolution_count
    FROM period_actions pa
    GROUP BY pa.user_id, pa.post_action_type_id, resolution
),
flag_totals AS (
    SELECT 
        pa.user_id,
        pa.post_action_type_id,
        COUNT(*) AS total_flags
    FROM period_actions pa
    GROUP BY pa.user_id, pa.post_action_type_id
),
resolution_percentages AS (
    SELECT 
        fr.user_id,
        fty.flag_type,
        fr.resolution,
        fr.resolution_count,
        ft.total_flags,
        ROUND((fr.resolution_count::decimal / ft.total_flags) * 100, 2) AS resolution_percentage
    FROM flag_resolutions fr
    JOIN flag_totals ft ON ft.user_id = fr.user_id AND ft.post_action_type_id = fr.post_action_type_id
    JOIN flag_types fty ON fty.id = fr.post_action_type_id
),
pivoted_data AS (
    SELECT 
        rp.user_id,
        rp.flag_type,
        MAX(CASE WHEN rp.resolution = 'agreed' THEN rp.resolution_percentage ELSE 0 END) AS agreed_percentage,
        MAX(CASE WHEN rp.resolution = 'disagreed' THEN rp.resolution_percentage ELSE 0 END) AS disagreed_percentage,
        MAX(CASE WHEN rp.resolution = 'deferred' THEN rp.resolution_percentage ELSE 0 END) AS deferred_percentage,
        MAX(CASE WHEN rp.resolution = 'deleted' THEN rp.resolution_percentage ELSE 0 END) AS deleted_percentage,
        MAX(CASE WHEN rp.resolution = 'agreed' THEN rp.resolution_count ELSE 0 END) AS agreed_count,
        MAX(CASE WHEN rp.resolution = 'disagreed' THEN rp.resolution_count ELSE 0 END) AS disagreed_count,
        MAX(CASE WHEN rp.resolution = 'deferred' THEN rp.resolution_count ELSE 0 END) AS deferred_count,
        MAX(CASE WHEN rp.resolution = 'deleted' THEN rp.resolution_count ELSE 0 END) AS deleted_count,
        MAX(rp.total_flags) AS total_flags
    FROM resolution_percentages rp
    GROUP BY rp.user_id, rp.flag_type
)
SELECT 
    u.id AS user_id,
    u.username,
    p.flag_type,
    p.agreed_percentage,
    p.agreed_count,
    p.disagreed_percentage,
    p.disagreed_count,
    p.deferred_percentage,
    p.deferred_count,
    p.deleted_percentage,
    p.deleted_count,
    p.total_flags
FROM pivoted_data p
JOIN users u ON u.id = p.user_id
WHERE (:only_staff = false OR (u.admin = true OR u.moderator = true))
ORDER BY u.username, p.flag_type, p.total_flags

使用されるパラメータ

  • :start_date: フラグ付き投稿をフィルタリングするための開始日。
  • :end_date: フラグ付き投稿をフィルタリングするための終了日。
  • :only_staff: 結果をスタッフメンバーのみにフィルタリングするためのブール値パラメータ。

CTE(共通テーブル式)の説明

  1. period_actions: 指定された日付範囲内のフラグ付き投稿をフィルタリングし、各フラグの解決までの時間を計算します。
  2. flag_types: フラグ種類の ID を人間が読みやすい名前にマッピングします。
  3. flag_resolutions: フラグをユーザー、フラグ種類、解決状況でグループ化し、各組み合わせの発生回数をカウントします。
  4. flag_totals: 各ユーザーおよびフラグ種類のフラグ総数を計算します。
  5. resolution_percentages: 解決数と総数を組み合わせて、各解決タイプの割合を計算します。
  6. pivoted_data: データをピボットして、各解決タイプの解決率とカウントを別々の列に表示します。

結果の説明

最終結果は、以下の内容を示すテーブルです。

  • モデレーターのユーザー名。
  • 各解決タイプ(同意、不同意、保留、削除)の割合とカウント。
  • 各モデレーターが処理したフラグ総数。

結果の例

モデレーター フラグの種類 同意 % 同意数 不同意 % 不同意数 保留 % 保留数 削除 % 削除数 フラグ総数
mod1 off_topic 60.00 30 20.00 10 10.00 5 10.00 5 50
mod2 spam 70.00 35 20.00 10 5.00 2 5.00 3 50

投稿をフラグ付けしているのは誰か

SQL クエリの説明

このクエリは、指定された日付範囲内で投稿をフラグ付けしたユーザーを特定し、各ユーザーが提出したフラグの総数を計算します。

-- [params]
-- date :start_date = 2024-01-01
-- date :end_date = 2025-01-01
-- boolean :only_staff = false

SELECT 
    u.id AS user_id,
    u.username,
    COUNT(pa.id) AS flag_count
FROM post_actions pa
JOIN users u ON u.id = pa.user_id
WHERE pa.post_action_type_id IN (3, 4, 6, 7, 8) -- フラグの種類
  AND pa.created_at >= :start_date
  AND pa.created_at <= :end_date
  AND (:only_staff = false OR (u.admin = true OR u.moderator = true))
GROUP BY u.id, u.username
ORDER BY flag_count DESC, u.username
LIMIT 10

使用されるパラメータ

  • :start_date: フラグ付き投稿をフィルタリングするための開始日。
  • :end_date: フラグ付き投稿をフィルタリングするための終了日。
  • :only_staff: 結果をスタッフメンバーのみにフィルタリングするためのブール値パラメータ。

結果の説明

最終結果は、フラグ数ごとのユーザーのランキングリストです。

結果の例

ユーザー ID ユーザー名 フラグ数
1 user1 50
2 user2 30
3 user3 20

ユーザーノート

SQL クエリの説明

このクエリは、plugin_store_rows テーブルに格納されているユーザーノートを取得します。ユーザー ID、作成日、ノート内容、作成者の ID などの詳細を抽出します。

-- [params]
-- date :start_date = 2025-01-01
-- date :end_date = 2026-01-01

WITH user_notes AS (

    SELECT 
        REPLACE(key, 'notes:', '')::int AS user_id,
        notes.value->>'created_at' AS created_at,
        notes.value->>'raw' AS user_note,
        notes.value->>'created_by' AS created_by
    FROM plugin_store_rows,
    LATERAL json_array_elements(value::json) notes
    WHERE plugin_name = 'user_notes'
    ORDER BY 2 DESC 
)

SELECT 
    un.user_id,
    un.created_at::date,
    un.user_note,
    un.created_by AS created_by_user_id
FROM user_notes un
JOIN users u ON u.id = un.user_id
WHERE un.created_at::date BETWEEN :start_date AND :end_date
ORDER BY created_at DESC

使用されるパラメータ

  • :start_date: ユーザーノートをフィルタリングするための開始日。
  • :end_date: ユーザーノートをフィルタリングするための終了日。

結果の説明

最終結果は、関連するメタデータを含むユーザーノートの詳細リストです。

結果の例

ユーザー ID 作成日 ユーザーノート 作成者ユーザー ID
1 2025-01-01 このユーザーは親切です。 2
2 2025-02-01 このユーザーは他の 2 つのユーザーアカウントと関連しています。 3

モデレーターの KPI - フラグ数と平均フラグ解決時間

SQL クエリの説明

このクエリは、各モデレーターが処理したフラグの数と平均解決時間(分)を計算することで、モデレーターのパフォーマンスを評価します。

-- [params]
-- date :start_date = 2025-01-01
-- date :end_date = 2026-01-01

WITH period_actions AS (
    SELECT pa.id,
           pa.post_action_type_id,
           pa.created_at,
           pa.agreed_at,
           pa.disagreed_at,
           pa.deferred_at,
           pa.agreed_by_id,
           pa.disagreed_by_id,
           pa.deferred_by_id,
           pa.post_id,
           pa.user_id,
           COALESCE(pa.disagreed_at, pa.agreed_at, pa.deferred_at) AS responded_at,
           EXTRACT(EPOCH FROM (COALESCE(pa.disagreed_at, pa.agreed_at, pa.deferred_at) - pa.created_at)) / 60 AS time_to_resolution_minutes -- 解決までの時間(分)
    FROM post_actions pa
    WHERE pa.post_action_type_id IN (3,4,6,7,8) -- フラグの種類
      AND pa.created_at >= :start_date
      AND pa.created_at <= :end_date
),
moderator_actions AS (
    SELECT pa.id,
           pa.post_id,
           pa.created_at,
           pa.responded_at,
           pa.time_to_resolution_minutes,
           COALESCE(pa.agreed_by_id, pa.disagreed_by_id, pa.deferred_by_id) AS moderator_id
    FROM period_actions pa
    WHERE COALESCE(pa.agreed_by_id, pa.disagreed_by_id, pa.deferred_by_id) IS NOT NULL
),
moderator_stats AS (
    SELECT
        m.moderator_id,
        u.username AS moderator_username,
        COUNT(m.id) AS handled_flags,
        AVG(m.time_to_resolution_minutes) AS avg_resolution_time_minutes
    FROM moderator_actions m
    JOIN users u ON u.id = m.moderator_id
    GROUP BY m.moderator_id, u.username
)
SELECT
    ms.moderator_username,
    ms.handled_flags,
    ROUND(ms.avg_resolution_time_minutes::numeric, 2) AS avg_resolution_time_minutes
FROM moderator_stats ms
ORDER BY ms.handled_flags DESC, ms.avg_resolution_time_minutes ASC

使用されるパラメータ

  • :start_date: フラグ付き投稿をフィルタリングするための開始日。
  • :end_date: フラグ付き投稿をフィルタリングするための終了日。

CTE(共通テーブル式)の説明

  1. period_actions: 指定された日付範囲内のフラグ付き投稿をフィルタリングし、各フラグの解決までの時間を計算します。
  2. moderator_actions: モデレーターが解決したフラグを特定し、各フラグの解決までの時間を計算します。
  3. moderator_stats: フラグをモデレーターごとにグループ化し、処理されたフラグの総数と平均解決時間を計算します。

結果の説明

最終結果は、処理されたフラグ数と平均解決時間ごとのモデレーターのランキングリストです。

結果の例

モデレーターユーザー名 処理されたフラグ数 平均解決時間(分)
mod1 50 15.00
mod2 30 20.00

すべてのフラグデータ

SQL クエリの説明

このクエリは、指定された日付範囲内のすべてのフラグ付きユーザー、投稿、トピックの包括的なデータセットを提供します。複数のテーブルからデータを組み合わせ、フラグの種類、フラグ対象、フラグ理由、フラグソース、解決決定、関連メッセージなどの詳細を含みます。

-- [params]
-- date :start_date = 2020-01-01
-- date :end_date = 2026-01-01

WITH flag_data AS (
    SELECT
        r.id AS flag_id,
        p.id AS post_id,
        p.topic_id,
        p.raw AS flagged_item_text,
        p.user_id AS post_author_id,
        fu.username AS flagged_by_username,
        r.created_at AS flagged_date,
        r.type AS flag_type,
        r.reviewable_by_moderator AS flag_source,
        r.payload AS flag_reason,
        r.status AS review_status,
        r.potentially_illegal AS potentially_illegal, 
        rs.reviewed_by_id,
        rs.reviewed_at,
        rs.score AS review_score,
        ru.username AS reviewed_by_username,
        p.deleted_at AS post_deleted_at,
        p.hidden_at AS post_hidden_at,
        u.silenced_till AS user_silenced_till,
        u.suspended_till AS user_suspended_till
    FROM
        reviewables r
    LEFT JOIN posts p ON r.target_id = p.id AND r.target_type = 'Post'
    LEFT JOIN users fu ON r.created_by_id = fu.id
    LEFT JOIN reviewable_scores rs ON rs.reviewable_id = r.id
    LEFT JOIN users ru ON rs.reviewed_by_id = ru.id
    LEFT JOIN users u ON p.user_id = u.id
    WHERE
        r.created_at BETWEEN :start_date AND :end_date
        --AND r.status = 1 -- 同意され、アクションが実行されたフラグのみを含める
),
review_decisions AS (
    SELECT
        0 AS status_code, 'pending' AS decision_name
    UNION ALL
    SELECT
        1 AS status_code, 'agreed' AS decision_name
    UNION ALL
    SELECT
        2 AS status_code, 'disagreed' AS decision_name
    UNION ALL
    SELECT
        3 AS status_code, 'ignored' AS decision_name
),
flag_types AS (
    SELECT
        3 AS post_action_type_id, 'off_topic' AS flag_type_name
    UNION ALL
    SELECT
        4 AS post_action_type_id, 'inappropriate' AS flag_type_name
    UNION ALL
    SELECT
        6 AS post_action_type_id, 'notify_user' AS flag_type_name
    UNION ALL
    SELECT
        7 AS post_action_type_id, 'notify_moderators' AS flag_type_name
    UNION ALL
    SELECT
        8 AS post_action_type_id, 'spam' AS flag_type_name
    UNION ALL
    SELECT
        10 AS post_action_type_id, 'illegal' AS flag_type_name
)
SELECT
    fd.flag_id,
    fd.post_id AS flagged_item,
    fd.flagged_by_username,
    fd.flagged_date,
    fd.flag_type,
    ft.flag_type_name AS flag_type_name,
    fd.flag_source AS reviewable_by_moderator,
    fd.flag_reason,
    fd.flagged_item_text,
    pa.related_post_id AS related_message_id_post_id,
    regexp_replace(rp.raw, '(https?://[^\s]+)', '', 'g') AS related_message_text, -- URL のみ削除
    fd.reviewed_at,
    fd.reviewed_by_username AS reviewed_by,
    rd.decision_name AS review_decision,
    CASE 
        WHEN fd.user_silenced_till IS NOT NULL THEN 'User silenced'
        WHEN fd.user_suspended_till IS NOT NULL THEN 'User suspended'
        WHEN fd.post_deleted_at IS NOT NULL THEN 'Post deleted'
        WHEN fd.post_hidden_at IS NOT NULL THEN 'Post hidden'
        ELSE 'No action taken'
    END AS action_taken,
    CASE 
        WHEN fd.reviewed_at IS NOT NULL THEN ROUND(EXTRACT(EPOCH FROM (fd.reviewed_at - fd.flagged_date)) / 60, 2)
        ELSE NULL
    END AS review_time_minutes -- 時間差(分)
FROM
    flag_data fd
LEFT JOIN post_actions pa 
    ON pa.post_id = fd.post_id
LEFT JOIN posts rp 
    ON pa.related_post_id = rp.id
LEFT JOIN flag_types ft 
    ON pa.post_action_type_id = ft.post_action_type_id
LEFT JOIN review_decisions rd 
    ON fd.review_status = rd.status_code
ORDER BY
    fd.flagged_date DESC

使用されるパラメータ

  • :start_date: フラグ付き投稿をフィルタリングするための開始日。
  • :end_date: フラグ付き投稿をフィルタリングするための終了日。

CTE(共通テーブル式)の説明

  1. flag_data: フラグ ID、フラグ対象、フラグ種類、フラグ理由、フラグソース、レビュー詳細など、フラグ付き投稿に関する詳細情報を取得します。
  2. review_decisions: レビューステータスコードを人間が読みやすい決定名にマッピングします(例:保留中、同意、不同意、無視)。
  3. flag_types: 投稿アクションタイプの ID を人間が読みやすいフラグ種類名にマッピングします(例:オフトピック、不適切、スパム)。

結果の説明

  • フラグ ID: フラグの一意の識別子。
  • フラグ対象: フラグ付き投稿の ID。
  • フラグ付けしたユーザー名: 投稿をフラグ付けしたユーザーのユーザー名。
  • フラグ付け日: フラグが作成された日付。
  • フラグの種類: フラグの数値タイプ。
  • フラグ種類名: フラグ種類の人間が読みやすい名前(例:オフトピック、スパム)。
  • モデレーターによるレビュー対象: フラグがユーザーによって、それともシステムによって提起されたかを示します。
  • フラグ理由: フラグの理由。
  • フラグ対象テキスト: フラグ付き投稿の内容。
  • 関連メッセージ ID: 関連するメッセージの ID(該当する場合)。
  • 関連メッセージテキスト: 関連メッセージの内容。明確化のために URL が削除されています。
  • レビューしたユーザー名: フラグをレビューしたモデレーターのユーザー名。
  • レビュー決定: レビュアーが行った決定(例:同意、不同意、無視、削除)。
  • 実行されたアクション: フラグの結果として実行されたアクション。ユーザーのサイレンシングまたは停止、投稿の削除または非表示、またはアクションなし。
  • レビュー時間(分): フラグのレビューに要した時間。フラグの作成時間とレビュー時間の差を分で計算。

結果の例(匿名化)

結果の例(匿名化)

フラグ ID フラグ対象 フラグ付けしたユーザー名 フラグ付け日 フラグの種類 フラグ種類名 モデレーターによるレビュー対象 フラグ理由 フラグ対象テキスト 関連メッセージ ID 関連メッセージテキスト レビューしたユーザー名 レビュー決定 実行されたアクション レビュー時間(分)
12345 67890 user123 2025-04-01 12:00 8 Spam true Spam content “Buy now at spam.com 98765 “Check this out!” mod456 Agreed Post deleted 15.25
12346 67891 user124 2025-04-02 14:30 4 Inappropriate false Offensive “This is inappropriate!” NULL NULL mod457 Disagreed No action taken 30.50

提出されたフラグレポート

このレポートは、指定された日付範囲内で提出されたフラグの概要を提供します。フラグを種類(例:スパム、不適切)で分類し、ユーザー報告のフラグとシステム生成のフラグを区別します。レポートには、各タイプのフラグ総数が含まれており、プラットフォームで最もよくフラグ付けされる問題を特定するのに役立ちます。

-- [params]
-- date :start_date = 2020-01-01
-- date :end_date = 2026-01-01

WITH flag_data AS (
    SELECT
        r.id AS flag_id,
        p.id AS post_id,
        p.topic_id,
        p.raw AS flagged_item_text,
        p.user_id AS post_author_id,
        fu.username AS flagged_by_username,
        r.created_at AS flagged_date,
        r.type AS flag_type,
        r.reviewable_by_moderator AS flag_source,
        r.payload AS flag_reason,
        r.status AS review_status,
        rs.reviewed_by_id,
        rs.reviewed_at,
        rs.score AS review_score,
        ru.username AS reviewed_by_username
    FROM
        reviewables r
    LEFT JOIN posts p ON r.target_id = p.id AND r.target_type = 'Post'
    LEFT JOIN users fu ON r.created_by_id = fu.id
    LEFT JOIN reviewable_scores rs ON rs.reviewable_id = r.id
    LEFT JOIN users ru ON rs.reviewed_by_id = ru.id
    WHERE
        r.created_at BETWEEN :start_date AND :end_date
),
flag_types AS (
    SELECT
        3 AS post_action_type_id, 'Off-topic' AS flag_type_name
    UNION ALL
    SELECT
        4 AS post_action_type_id, 'Inappropriate' AS flag_type_name
    UNION ALL
    SELECT
        6 AS post_action_type_id, 'Notify_user' AS flag_type_name
    UNION ALL
    SELECT
        7 AS post_action_type_id, 'Notify_moderators' AS flag_type_name
    UNION ALL
    SELECT
        8 AS post_action_type_id, 'Spam' AS flag_type_name
    UNION ALL
    SELECT
        10 AS post_action_type_id, 'Illegal' AS flag_type_name
    UNION ALL
    SELECT
        NULL AS post_action_type_id, 'Something else' AS flag_type_name
)
SELECT
    ft.flag_type_name AS Type,
    COUNT(CASE WHEN fd.flagged_by_username NOT IN ('spam_scanner_bot', 'system') THEN 1 END) AS Reported,
    COUNT(CASE WHEN fd.flagged_by_username IN ('spam_scanner_bot', 'system') THEN 1 END) AS Automated,
    COUNT(*) AS Total
FROM
    flag_data fd
LEFT JOIN post_actions pa 
    ON pa.post_id = fd.post_id
LEFT JOIN flag_types ft 
    ON pa.post_action_type_id = ft.post_action_type_id
GROUP BY
    ft.flag_type_name
ORDER BY
    Total DESC

使用されたパラメータ

  • :start_date: フラグ付き投稿をフィルタリングする開始日。
  • :end_date: フラグ付き投稿をフィルタリングする終了日。

CTE(共通テーブル式)の説明

  1. flag_data:
    このCTEは、フラグ付き投稿に関する詳細情報を取得します。含まれる情報には以下があります:
  • フラグの一意のID(flag_id)、フラグ付き投稿のID(post_id)、および所属するトピック(topic_id)。
  • フラグ自体に関する情報、例えばフラグを立てたユーザー(flagged_by_username)、フラグが立てられた日付(flagged_date)、フラグの種類(flag_type)、およびフラグの理由(flag_reason)。
  • レビュープロセスに関する詳細、例えばフラグをレビューしたモデレーター(reviewed_by_username)、レビューの判断(review_status)、およびレビュー日時(reviewed_at)。
  1. flag_types:
    このCTEは、数値の投稿アクションタイプIDを人間が読みやすいフラグタイプ名にマッピングします:
  • 3: 無関係(Off-topic)
  • 4: 不適切(Inappropriate)
  • 6: ユーザーに通知(Notify user)
  • 7: モデレーターに通知(Notify moderators)
  • 8: スパム(Spam)
  • 10: 違法(Illegal)
  • NULL: その他(Something else)

結果の説明

最終クエリはフラグタイプごとにフラグデータを集約し、以下のメトリクスを提供します:

  • Type(タイプ): フラグタイプの人間が読みやすい名前(例:無関係、スパム)。
  • Reported(報告数): ユーザーによって提出されたフラグの件数(システム生成フラグを除く)。
  • Automated(自動検知数): システムやボットによって生成されたフラグの件数(例:spam_scanner_botsystem)。
  • Total(合計): 各タイプごとのフラグの総数。

結果はフラグの総数の降順でソートされます。

結果の例

Type Reported Automated Total
Spam 120 80 200
Inappropriate 90 10 100
Off-topic 60 5 65
Notify_moderators 30 0 30
Illegal 10 2 12
Something else 5 0 5

BANおよびサスペンション(一時停止):

このレポートは、指定された日付範囲内でサスペンションまたはサイレンス(発言禁止)されたユーザーを一覧表示します。サスペンションまたはサイレンスの日付、アクションの期間、およびユーザーのアカウント作成日と最終アクティビティ日などの詳細が含まれます。このレポートは、モデレーションアクションの監視や、BANやサイレンスにつながるユーザー行動のパターンを特定するのに役立ちます。

-- [params]
-- date :start_date = 2024-01-01
-- date :end_date = 2026-01-01
SELECT 
    u.id AS user_id,
    u.username,
    u.name,
    u.suspended_at,
    u.suspended_till,
    u.silenced_till,
    u.created_at AS account_created_at,
    u.last_seen_at,
    u.flag_level,
    u.admin,
    u.moderator
FROM 
    users u
WHERE 
    (
        u.suspended_at BETWEEN :start_date AND :end_date
        OR u.silenced_till BETWEEN :start_date AND :end_date
    )
ORDER BY 
    u.suspended_at DESC NULLS LAST,
    u.silenced_till DESC NULLS LAST

使用されたパラメータ

  • :start_date: サスペンションまたはサイレンスされたユーザーをフィルタリングする開始日。
  • :end_date: サスペンションまたはサイレンスされたユーザーをフィルタリングする終了日。

結果の説明

このクエリは、指定された日付範囲内でサスペンションまたはサイレンスされたユーザーの一覧を取得します。主要なカラムには以下が含まれます:

  • User ID(ユーザーID): ユーザーの一意の識別子。
  • Username(ユーザー名): ユーザーのユーザー名。
  • Name(名前): ユーザーのフルネーム(利用可能な場合)。
  • Suspended At(サスペンション日時): ユーザーがサスペンションされた日付。
  • Suspended Till(サスペンション終了日): ユーザーがサスペンションされている期間の終了日。
  • Silenced Till(サイレンス終了日): ユーザーがサイレンスされている期間の終了日。
  • Account Created At(アカウント作成日): ユーザーのアカウントが作成された日付。
  • Last Seen At(最終アクティビティ日時): ユーザーがプラットフォーム上で最後にアクティブだった時間。
  • Flag Level(フラグレベル): ユーザーの現在のフラグレベル。
  • Admin(管理者): ユーザーが管理者かどうか(true/false)。
  • Moderator(モデレーター): ユーザーがモデレーターかどうか(true/false)。

結果はサスペンション日(suspended_at)とサイレンス日(silenced_till)の降順でソートされ、NULL値は最後に配置されます。

結果の例

User ID Username Name Suspended At Suspended Till Silenced Till Account Created At Last Seen At Flag Level Admin Moderator
101 user123 John Doe 2025-03-15 10:00 2025-04-15 10:00 NULL 2020-01-01 12:00 2025-03-14 18:00 2 false false
102 user456 Jane Smith NULL NULL 2025-03-20 18:00 2021-06-10 15:00 2025-03-19 20:00 1 false false
103 mod789 Moderator1 2025-02-01 08:00 2025-03-01 08:00 NULL 2019-05-05 10:00 2025-01-31 22:00 3 false true
104 admin001 AdminUser NULL NULL 2025-03-25 12:00 2018-12-25 09:00 2025-03-24 16:00 0 true false

合意されたフラグアクション

このレポートは、モデレーターによって合意され、アクションが実行されたフラグに焦点を当てています。フラグをタイプ別に分類し、フラグの総数、アクション実行までの中央値時間、および結果(例:ユーザーのサイレンス、投稿の削除)などのメトリクスを提供します。

-- [params]
-- date :start_date = 2024-01-01
-- date :end_date = 2025-01-01

WITH flag_data AS (
    SELECT
        r.id AS flag_id,
        p.id AS post_id,
        p.topic_id,
        p.raw AS flagged_item_text,
        p.user_id AS post_author_id,
        fu.username AS flagged_by_username,
        r.created_at AS flagged_date,
        r.type AS flag_type,
        r.reviewable_by_moderator AS flag_source,
        r.payload AS flag_reason,
        r.status AS review_status,
        rs.reviewed_by_id,
        rs.reviewed_at,
        rs.score AS review_score,
        ru.username AS reviewed_by_username,
        p.deleted_at AS post_deleted_at,
        u.silenced_till AS user_silenced_till,
        u.suspended_till AS user_suspended_till,
        p.hidden_at AS post_hidden_at,
        pa.post_action_type_id
    FROM
        reviewables r
    LEFT JOIN posts p ON r.target_id = p.id AND r.target_type = 'Post'
    LEFT JOIN users fu ON r.created_by_id = fu.id
    LEFT JOIN reviewable_scores rs ON rs.reviewable_id = r.id
    LEFT JOIN users ru ON rs.reviewed_by_id = ru.id
    LEFT JOIN users u ON p.user_id = u.id
    LEFT JOIN post_actions pa ON pa.post_id = p.id
    WHERE
        r.created_at BETWEEN :start_date AND :end_date
        AND r.status = 1 -- Only include flags that were agreed with and action was taken
),
flag_types AS (
    SELECT
        3 AS post_action_type_id, 'Off-topic' AS flag_type_name
    UNION ALL
    SELECT
        4 AS post_action_type_id, 'Inappropriate' AS flag_type_name
    UNION ALL
    SELECT
        6 AS post_action_type_id, 'Notify_user' AS flag_type_name
    UNION ALL
    SELECT
        7 AS post_action_type_id, 'Notify_moderators' AS flag_type_name
    UNION ALL
    SELECT
        8 AS post_action_type_id, 'Spam' AS flag_type_name
    UNION ALL
    SELECT
        10 AS post_action_type_id, 'Illegal' AS flag_type_name
),
median_time_to_act AS (
    SELECT
        COALESCE(ft.flag_type_name, fd.flag_type) AS flag_type,
        ROUND(PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY EXTRACT(EPOCH FROM (fd.reviewed_at - fd.flagged_date))) / 60) AS median_time_minutes
    FROM
        flag_data fd
    LEFT JOIN flag_types ft 
        ON fd.post_action_type_id = ft.post_action_type_id
    WHERE
        fd.reviewed_at IS NOT NULL
        AND (
            ft.flag_type_name IS NOT NULL -- Include mapped flag types
            OR fd.flag_type IN (
                'ReviewableAkismetPost',
                'ReviewableUser',
                'ReviewableFlaggedPost',
                'ReviewableChatMessage',
                'ReviewablePost',
                'ReviewableQueuedPost'
            ) -- Include specific flag types for NULL results
        )
    GROUP BY
        COALESCE(ft.flag_type_name, fd.flag_type)
)
SELECT
    COALESCE(ft.flag_type_name, fd.flag_type) AS Type,
    COUNT(CASE WHEN fd.flagged_by_username NOT IN ('spam_scanner_bot', 'system') THEN 1 END) AS Reported,
    COUNT(CASE WHEN fd.flagged_by_username IN ('spam_scanner_bot', 'system') THEN 1 END) AS Automated,
    COUNT(*) AS Total,
    COALESCE(mta.median_time_minutes, 0) AS "Median time to act (minutes)",
    COUNT(CASE WHEN fd.user_silenced_till IS NOT NULL THEN 1 END) AS "User silenced",
    COUNT(CASE WHEN fd.user_suspended_till IS NOT NULL THEN 1 END) AS "User deleted",
    COUNT(CASE WHEN fd.post_deleted_at IS NOT NULL THEN 1 END) AS "Post deleted",
    COUNT(CASE WHEN fd.post_hidden_at IS NOT NULL THEN 1 END) AS "Post hidden"

FROM
    flag_data fd
LEFT JOIN flag_types ft 
    ON fd.post_action_type_id = ft.post_action_type_id
LEFT JOIN median_time_to_act mta 
    ON COALESCE(ft.flag_type_name, fd.flag_type) = mta.flag_type
WHERE
    ft.flag_type_name IS NOT NULL -- Include mapped flag types
    OR fd.flag_type IN (
        'ReviewableAkismetPost',
        'ReviewableUser',
        'ReviewableFlaggedPost',
        'ReviewableChatMessage',
        'ReviewablePost',
        'ReviewableQueuedPost'
    ) -- Include specific flag types for NULL results
GROUP BY
    COALESCE(ft.flag_type_name, fd.flag_type), mta.median_time_minutes
ORDER BY
    Total DESC

使用されたパラメータ

  • :start_date: 合意されたフラグをフィルタリングする開始日。
  • :end_date: 合意されたフラグをフィルタリングする終了日。

CTE(共通テーブル式)の説明

  1. flag_data:
    このCTEは、合意され、アクションが実行されたフラグに関する詳細情報を取得します。含まれる情報には以下があります:
  • フラグの一意のID(flag_id)、フラグ付き投稿のID(post_id)、および所属するトピック(topic_id)。
  • フラグ自体に関する情報、例えばフラグを立てたユーザー(flagged_by_username)、フラグが立てられた日付(flagged_date)、フラグの種類(flag_type)、およびフラグの理由(flag_reason)。
  • レビュープロセスに関する詳細、例えばフラグをレビューしたモデレーター(reviewed_by_username)、レビューの判断(review_status)、およびレビュー日時(reviewed_at)。
  • フラグ付き投稿に関する追加情報、例えば投稿が削除または非表示にされたかどうか、または著者がサイレンスまたはサスペンションされたかどうか。
  1. flag_types:
    このCTEは、数値の投稿アクションタイプIDを人間が読みやすいフラグタイプ名にマッピングします:
  • 3: 無関係(Off-topic)
  • 4: 不適切(Inappropriate)
  • 6: ユーザーに通知(Notify user)
  • 7: モデレーターに通知(Notify moderators)
  • 8: スパム(Spam)
  • 10: 違法(Illegal)
  1. median_time_to_act:
    このCTEは、各フラグタイプに対してアクションを実行するまでの中央値時間(分)を計算します。時間は、フラグ作成時間(flagged_date)とレビュー時間(reviewed_at)の差として計算されます。

結果の説明

最終クエリは、合意されたフラグデータをフラグタイプごとに集約し、以下のメトリクスを提供します:

  • Type(タイプ): フラグタイプの人間が読みやすい名前(例:無関係、スパム)。
  • Reported(報告数): ユーザーによって提出されたフラグの件数(システム生成フラグを除く)。
  • Automated(自動検知数): システムやボットによって生成されたフラグの件数(例:spam_scanner_botsystem)。
  • Total(合計): 各タイプごとのフラグの総数。
  • Median Time to Act (Minutes)(アクション実行までの中央値時間(分)): このタイプのフラグに対してアクションを実行するまでの中央値時間(分)。
  • User Silenced(ユーザーサイレンス数): ユーザーがサイレンスされたフラグの件数。
  • User Deleted(ユーザー削除数): ユーザーがサスペンションされたフラグの件数。
  • Post Deleted(投稿削除数): 投稿が削除されたフラグの件数。
  • Post Hidden(投稿非表示数): 投稿が非表示にされたフラグの件数。

結果はフラグの総数の降順でソートされます。

結果の例

Type Reported Automated Total Median Time to Act (Minutes) User Silenced User Deleted Post Deleted Post Hidden
Spam 100 50 150 30 20 10 50 30
Inappropriate 80 5 85 45 15 5 30 20
Off-topic 40 2 42 25 5 0 10 15
Notify_moderators 20 0 20 60 0 0 5 10
Illegal 5 1 6 120 1 1 3 2

実行されたモデレーションアクション

このレポートは、指定された日付範囲内で実行されたモデレーションアクションの概要を提供します。ユーザーまたは自動化によってフラグが立てられたコンテンツ、削除または非表示にされた投稿、発行された警告、削除またはサスペンションされたアカウント、および長期間サイレンスされたユーザーなど、さまざまなタイプの合意アクションを集約します。各カテゴリにはケースの総数が表示され、モデレーション活動の概要を高いレベルで提供します。

-- [params]
-- date :start_date = 2024-01-01
-- date :end_date = 2025-01-01

WITH flag_data AS (
    SELECT
        r.id AS flag_id,
        p.id AS post_id,
        p.topic_id,
        p.raw AS flagged_item_text,
        p.user_id AS post_author_id,
        fu.username AS flagged_by_username,
        r.created_at AS flagged_date,
        r.type AS flag_type,
        r.reviewable_by_moderator AS flag_source,
        r.payload AS flag_reason,
        r.status AS review_status,
        rs.reviewed_by_id,
        rs.reviewed_at,
        rs.score AS review_score,
        ru.username AS reviewed_by_username,
        p.deleted_at AS post_deleted_at,
        u.silenced_till AS user_silenced_till,
        u.suspended_till AS user_suspended_till,
        p.hidden_at AS post_hidden_at,
        pa.post_action_type_id
    FROM
        reviewables r
    LEFT JOIN posts p ON r.target_id = p.id AND r.target_type = 'Post'
    LEFT JOIN users fu ON r.created_by_id = fu.id
    LEFT JOIN reviewable_scores rs ON rs.reviewable_id = r.id
    LEFT JOIN users ru ON rs.reviewed_by_id = ru.id
    LEFT JOIN users u ON p.user_id = u.id
    LEFT JOIN post_actions pa ON pa.post_id = p.id
    WHERE
        r.created_at BETWEEN :start_date AND :end_date
        AND r.status = 1 -- Only include flags that were agreed with and action was taken
),
flag_types AS (
    SELECT
        3 AS post_action_type_id, 'Off-topic' AS flag_type_name
    UNION ALL
    SELECT
        4 AS post_action_type_id, 'Inappropriate' AS flag_type_name
    UNION ALL
    SELECT
        6 AS post_action_type_id, 'Notify_user' AS flag_type_name
    UNION ALL
    SELECT
        7 AS post_action_type_id, 'Notify_moderators' AS flag_type_name
    UNION ALL
    SELECT
        8 AS post_action_type_id, 'Spam' AS flag_type_name
    UNION ALL
    SELECT
        10 AS post_action_type_id, 'Illegal' AS flag_type_name
),
flagged_content AS (
    SELECT
        COUNT(CASE WHEN fd.flagged_by_username NOT IN ('spam_scanner_bot', 'system') THEN 1 END) AS user_flagged,
        COUNT(CASE WHEN fd.flagged_by_username IN ('spam_scanner_bot', 'system') THEN 1 END) AS automation_flagged
    FROM
        flag_data fd
    LEFT JOIN flag_types ft 
        ON fd.post_action_type_id = ft.post_action_type_id
    WHERE
        ft.flag_type_name IS NOT NULL -- Include mapped flag types
        OR fd.flag_type IN (
            'ReviewableAkismetPost',
            'ReviewableUser',
            'ReviewableFlaggedPost',
            'ReviewableChatMessage',
            'ReviewablePost',
            'ReviewableQueuedPost'
        ) -- Include specific flag types for NULL results
),
warnings_issued AS (
    SELECT
        COUNT(*) AS warnings_count
    FROM
        user_warnings
    WHERE
        created_at BETWEEN :start_date AND :end_date
),
violations_and_suspensions AS (
    SELECT
        COUNT(CASE 
            WHEN uh.action = 1 
                AND (
                    LOWER(uh.context) LIKE '%deleted via review queue%' OR
                    LOWER(uh.context) LIKE '%to be a spammer%' OR
                    LOWER(uh.context) LIKE '%review%' OR
                    LOWER(uh.context) LIKE '%reviewable user rejected%'
                ) 
            THEN 1 
        END) AS accounts_deleted,
        COUNT(CASE WHEN uh.action = 10 THEN 1 END) AS accounts_suspended
    FROM
        user_histories uh
    WHERE
        uh.created_at BETWEEN :start_date AND :end_date
),
posts_deleted_and_hidden AS (
    SELECT
        COUNT(CASE WHEN fd.post_deleted_at IS NOT NULL THEN 1 END) AS posts_deleted,
        COUNT(CASE WHEN fd.post_hidden_at IS NOT NULL THEN 1 END) AS posts_hidden
    FROM
        flag_data fd
),
silences_issued AS (
    SELECT
        COUNT(*) AS silences_count
    FROM
        user_histories uh
    WHERE
        uh.action = 30 -- silence_user
        AND uh.created_at BETWEEN :start_date AND :end_date
        AND EXISTS (
            SELECT 1
            FROM users u
            WHERE u.id = uh.target_user_id 
              AND u.silenced_till > (CAST(:start_date AS TIMESTAMP) + INTERVAL '10 years')
        )
)
SELECT
    'Content flagged by users' AS category,
    fc.user_flagged AS "Number of Cases"
FROM flagged_content fc

UNION ALL

SELECT
    'Content flagged by automation' AS category,
    fc.automation_flagged AS "Number of Cases"
FROM flagged_content fc

UNION ALL

SELECT
    'Posts deleted for violating terms' AS category,
    pdh.posts_deleted AS "Number of Cases"
FROM posts_deleted_and_hidden pdh

UNION ALL

SELECT
    'Posts hidden' AS category,
    pdh.posts_hidden AS "Number of Cases"
FROM posts_deleted_and_hidden pdh

UNION ALL

SELECT
    'Warnings Issued' AS category,
    wi.warnings_count AS "Number of Cases"
FROM warnings_issued wi

UNION ALL

SELECT
    'Accounts deleted' AS category,
    vs.accounts_deleted AS "Number of Cases"
FROM violations_and_suspensions vs

UNION ALL

SELECT
    'Accounts suspended' AS category,
    vs.accounts_suspended AS "Number of Cases"
FROM violations_and_suspensions vs

UNION ALL

SELECT
    'Users silenced for 10+ years' AS category,
    si.silences_count AS "Number of Cases"
FROM silences_issued si

使用されたパラメータ

  • :start_date: モデレーションアクションをフィルタリングする開始日。
  • :end_date: モデレーションアクションをフィルタリングする終了日。

結果の説明

クエリはモデレーションアクションをカテゴリに集約し、各カテゴリのケースの総数を提供します。カテゴリには以下が含まれます:

  1. ユーザーによってフラグが立てられたコンテンツ: 一般ユーザーによってフラグが立てられた投稿の数(システム生成フラグを除く)。
  2. 自動化によってフラグが立てられたコンテンツ: 自動化システムやボットによってフラグが立てられた投稿の数(例:spam_scanner_botsystem)。
  3. 規約違反により削除された投稿: コミュニティガイドラインまたは利用規約の違反により削除された投稿の数。
  4. 非表示にされた投稿: さまざまな理由で非表示(ただし削除はされていない)投稿の数。
  5. 発行された警告: 不適切な行動またはコンテンツに対してユーザーに発行された警告の数。
  6. 削除されたアカウント: スパマーとしてフラグが立てられたり、レビューキューで拒否されたりするなど、違反により削除されたユーザーアカウントの数。
  7. サスペンションされたアカウント: 違反により特定の期間サスペンションされたユーザーアカウントの数。
  8. 10年以上サイレンスされたユーザー: 無期限または長期(10年以上)にわたってサイレンスされたユーザーの数。

結果の例

Category Number of Cases
Content flagged by users 150
Content flagged by automation 100
Posts deleted for violating terms 50
Posts hidden 30
Warnings Issued 20
Accounts deleted 10
Accounts suspended 15
Users silenced for 10+ years 5

実行された個別のモデレーションアクション

このレポートは、指定された日付範囲内で実行された合意された個別のモデレーションアクションの詳細なログを提供します。アクションを実行したユーザー、対象ユーザー、アクションの日付、アクションのカテゴリ(例:フラグ付きコンテンツ、削除された投稿、発行された警告)、およびアクションの文脈または理由に関する情報が含まれます。このレポートは、特定のモデレーション決定の監査や、各アクションの背景にある文脈を理解するのに役立ちます。

-- [params]
-- date :start_date = 2024-01-01
-- date :end_date = 2025-01-01

WITH flag_data AS (
    SELECT
        r.id AS flag_id,
        p.id AS post_id,
        p.topic_id,
        p.raw AS flagged_item_text,
        p.user_id AS post_author_id,
        fu.username AS flagged_by_username,
        r.created_at AS flagged_date,
        r.type AS flag_type,
        r.reviewable_by_moderator AS flag_source,
        r.payload AS flag_reason,
        r.status AS review_status,
        rs.reviewed_by_id,
        rs.reviewed_at,
        rs.score AS review_score,
        ru.username AS reviewed_by_username,
        p.deleted_at AS post_deleted_at,
        u.silenced_till AS user_silenced_till,
        u.suspended_till AS user_suspended_till,
        p.hidden_at AS post_hidden_at,
        pa.post_action_type_id
    FROM
        reviewables r
    LEFT JOIN posts p ON r.target_id = p.id AND r.target_type = 'Post'
    LEFT JOIN users fu ON r.created_by_id = fu.id
    LEFT JOIN reviewable_scores rs ON rs.reviewable_id = r.id
    LEFT JOIN users ru ON rs.reviewed_by_id = ru.id
    LEFT JOIN users u ON p.user_id = u.id
    LEFT JOIN post_actions pa ON pa.post_id = p.id
    WHERE
        r.created_at BETWEEN :start_date AND :end_date
        AND r.status = 1 -- Only include flags that were agreed with and action was taken
),
flag_types AS (
    SELECT
        3 AS post_action_type_id, 'Off-topic' AS flag_type_name
    UNION ALL
    SELECT
        4 AS post_action_type_id, 'Inappropriate' AS flag_type_name
    UNION ALL
    SELECT
        6 AS post_action_type_id, 'Notify_user' AS flag_type_name
    UNION ALL
    SELECT
        7 AS post_action_type_id, 'Notify_moderators' AS flag_type_name
    UNION ALL
    SELECT
        8 AS post_action_type_id, 'Spam' AS flag_type_name
    UNION ALL
    SELECT
        10 AS post_action_type_id, 'Illegal' AS flag_type_name
),
flagged_content AS (
    SELECT
        fd.flagged_by_username AS acting_user,
        CAST(fd.post_author_id AS TEXT) AS target_user,
        fd.flagged_date AS action_date,
        'Content flagged by users' AS category,
        fd.flagged_item_text AS context
    FROM
        flag_data fd
    LEFT JOIN flag_types ft 
        ON fd.post_action_type_id = ft.post_action_type_id
    WHERE
        ft.flag_type_name IS NOT NULL
        AND fd.flagged_by_username NOT IN ('spam_scanner_bot', 'system')
    
    UNION ALL

    SELECT
        fd.flagged_by_username AS acting_user,
        CAST(fd.post_author_id AS TEXT) AS target_user,
        fd.flagged_date AS action_date,
        'Content flagged by automation' AS category,
        fd.flagged_item_text AS context
    FROM
        flag_data fd
    LEFT JOIN flag_types ft 
        ON fd.post_action_type_id = ft.post_action_type_id
    WHERE
        ft.flag_type_name IS NOT NULL
        AND fd.flagged_by_username IN ('spam_scanner_bot', 'system')
),
posts_deleted_and_hidden AS (
    SELECT
        fd.reviewed_by_username AS acting_user,
        CAST(fd.post_author_id AS TEXT) AS target_user,
        fd.post_deleted_at AS action_date,
        'Posts deleted for violating terms' AS category,
        fd.flagged_item_text AS context
    FROM
        flag_data fd
    WHERE
        fd.post_deleted_at IS NOT NULL
    
    UNION ALL

    SELECT
        fd.reviewed_by_username AS acting_user,
        CAST(fd.post_author_id AS TEXT) AS target_user,
        fd.post_hidden_at AS action_date,
        'Posts hidden' AS category,
        fd.flagged_item_text AS context
    FROM
        flag_data fd
    WHERE
        fd.post_hidden_at IS NOT NULL
),
warnings_issued AS (
    SELECT
        CAST(uw.created_by_id AS TEXT) AS acting_user,
        CAST(uw.user_id AS TEXT) AS target_user,
        uw.created_at AS action_date,
        'Warnings Issued' AS category,
        NULL AS context
    FROM
        user_warnings uw
    WHERE
        uw.created_at BETWEEN :start_date AND :end_date
),
violations_and_suspensions AS (
    SELECT
        CAST(uh.acting_user_id AS TEXT) AS acting_user,
        CAST(uh.target_user_id AS TEXT) AS target_user,
        uh.created_at AS action_date,
        'Accounts deleted' AS category,
        uh.context AS context
    FROM
        user_histories uh
    WHERE
        uh.created_at BETWEEN :start_date AND :end_date
        AND uh.action = 1
        AND (
            LOWER(uh.context) LIKE '%deleted via review queue%' OR
            LOWER(uh.context) LIKE '%to be a spammer%' OR
            LOWER(uh.context) LIKE '%review%' OR
            LOWER(uh.context) LIKE '%reviewable user rejected%'
        )
    
    UNION ALL

    SELECT
        CAST(uh.acting_user_id AS TEXT) AS acting_user,
        CAST(uh.target_user_id AS TEXT) AS target_user,
        uh.created_at AS action_date,
        'Accounts suspended' AS category,
        uh.context AS context
    FROM
        user_histories uh
    WHERE
        uh.created_at BETWEEN :start_date AND :end_date
        AND uh.action = 10
),
silences_issued AS (
    SELECT
        CAST(uh.acting_user_id AS TEXT) AS acting_user,
        CAST(uh.target_user_id AS TEXT) AS target_user,
        uh.created_at AS action_date,
        'Users silenced for 10+ years' AS category,
        uh.context AS context
    FROM
        user_histories uh
    WHERE
        uh.action = 30 -- silence_user
        AND uh.created_at BETWEEN :start_date AND :end_date
        AND EXISTS (
            SELECT 1
            FROM users u
            WHERE u.id = uh.target_user_id 
              AND u.silenced_till > (CAST(:start_date AS TIMESTAMP) + INTERVAL '10 years')
        )
)
SELECT
    acting_user,
    target_user,
    action_date,
    category,
    context
FROM flagged_content

UNION ALL

SELECT
    acting_user,
    target_user,
    action_date,
    category,
    context
FROM posts_deleted_and_hidden

UNION ALL

SELECT
    acting_user,
    target_user,
    action_date,
    category,
    context
FROM warnings_issued

UNION ALL

SELECT
    acting_user,
    target_user,
    action_date,
    category,
    context
FROM violations_and_suspensions

UNION ALL

SELECT
    acting_user,
    target_user,
    action_date,
    category,
    context
FROM silences_issued

使用されたパラメータ

  • :start_date: 個別のモデレーションアクションをフィルタリングする開始日。
  • :end_date: 個別のモデレーションアクションをフィルタリングする終了日。

結果の説明

クエリは個別のモデレーションアクションの詳細なログを提供し、以下が含まれます:

  1. Acting User(アクション実行者): アクションを実行したモデレーター、システム、またはユーザーのユーザー名。
  2. Target User(対象ユーザー): アクションの対象となったユーザーのユーザー名またはID(例:フラグ付き投稿の著者、または警告の受信者)。
  3. Action Date(アクション日時): アクションが発生した日付と時刻。
  4. Category(カテゴリ): モデレーションアクションの種類、例えば:
  • ユーザーによってフラグが立てられたコンテンツ
  • 自動化によってフラグが立てられたコンテンツ
  • 規約違反により削除された投稿
  • 非表示にされた投稿
  • 発行された警告
  • 削除されたアカウント
  • サスペンションされたアカウント
  • 10年以上サイレンスされたユーザー
  1. Context(文脈): アクションに関連する追加情報またはコンテンツ、例えばフラグ付き投稿のテキストやサスペンションの理由。

結果の例

Acting User Target User Action Date Category Context
user123 user456 2024-02-01 10:00 Content flagged by users “この投稿にはスパムコンテンツが含まれています。”
spam_scanner user789 2024-02-02 12:00 Content flagged by automation “システムによりスパムと検出されました。”
mod001 user456 2024-02-03 14:00 Posts deleted for violating terms “例の投稿コンテンツ
mod002 user123 2024-02-04 16:00 Posts hidden “投稿は不適切と判断されました。”
admin001 user789 2024-02-05 18:00 Warnings Issued NULL
admin002 user456 2024-02-06 20:00 Accounts deleted “アカウントはレビューキューを介して削除されました。”
mod003 user123 2024-02-07 22:00 Accounts suspended “ユーザーは繰り返しのスパムによりサスペンションされました。”
admin003 user789 2024-02-08 08:00 Users silenced for 10+ years “ユーザーは重大な違反によりサイレンスされました。”
「いいね!」 6