Analyse des rapports d'activité de modération et de signalement

Le maintien d’une communauté saine et inclusive nécessite une modération efficace, qui peut inclure la révision des publications signalées, l’analyse des performances des modérateurs et la gestion du contenu des utilisateurs.

Ce guide contient une variété de rapports SQL pour Discourse conçus pour aider à analyser les activités liées à la modération.

Dans ce sujet, vous trouverez des requêtes Data Explorer détaillées pour :

  • Statistiques de résolution des signalements.
  • Pourcentages de résolution des éléments à examiner.
  • Métriques de performance spécifiques aux modérateurs.
  • Informations sur l’activité de signalement des utilisateurs.
  • Données complètes de toutes les actions signalées sur les utilisateurs, les publications et les sujets

Pourcentage de résolution des signalements de publication par type

Explication de la requête SQL

Cette requête calcule le pourcentage de résolutions (acceptées, rejetées, différées, supprimées) pour les publications signalées, regroupées par type de signalement.

-- [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 -- temps de résolution en 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 'hors_sujet'
               WHEN pat.id = 4 THEN 'inapproprié'
               WHEN pat.id = 6 THEN 'notifier_utilisateur'
               WHEN pat.id = 7 THEN 'notifier_modérateurs'
               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 'accepté'
            WHEN pa.disagreed_at IS NOT NULL THEN 'rejeté'
            WHEN pa.deferred_at IS NOT NULL THEN 'différé'
            WHEN pa.deleted_at IS NOT NULL THEN 'supprimé'
        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 = 'accepté' THEN resolution_percentage ELSE 0 END) AS agreed_percentage,
        MAX(CASE WHEN resolution = 'rejeté' THEN resolution_percentage ELSE 0 END) AS disagreed_percentage,
        MAX(CASE WHEN resolution = 'différé' THEN resolution_percentage ELSE 0 END) AS deferred_percentage,
        MAX(CASE WHEN resolution = 'supprimé' THEN resolution_percentage ELSE 0 END) AS deleted_percentage,
        MAX(CASE WHEN resolution = 'accepté' THEN resolution_count ELSE 0 END) AS agreed_count,
        MAX(CASE WHEN resolution = 'rejeté' THEN resolution_count ELSE 0 END) AS disagreed_count,
        MAX(CASE WHEN resolution = 'différé' THEN resolution_count ELSE 0 END) AS deferred_count,
        MAX(CASE WHEN resolution = 'supprimé' 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

Paramètres utilisés

  • :start_date : La date de début pour filtrer les publications signalées.
  • :end_date : La date de fin pour filtrer les publications signalées.

Explication des CTE (Common Table Expressions)

  1. period_actions : Filtre les publications signalées dans la plage de dates spécifiée et calcule le temps de résolution pour chaque signalement.
  2. flag_types : Associe les identifiants de type de signalement à des noms lisibles (par exemple, hors-sujet, inapproprié, spam).
  3. flag_resolutions : Regroupe les signalements par type et résolution (accepté, rejeté, différé, supprimé) et compte les occurrences de chaque résolution.
  4. flag_totals : Calcule le nombre total de signalements pour chaque type de signalement.
  5. resolution_percentages : Combine les comptes de résolution et les totaux pour calculer le pourcentage de chaque type de résolution pour chaque type de signalement.
  6. pivoted_data : Pivote les données pour afficher les pourcentages et les comptes de résolution dans des colonnes séparées pour chaque type de résolution.

Explication des résultats

Le résultat final est un tableau montrant :

  • Type de signalement (par exemple, hors-sujet, spam).
  • Pourcentages et comptes pour chaque type de résolution (accepté, rejeté, différé, supprimé).
  • Total des signalements pour chaque type de signalement.

Exemples de résultats

Type de signalement % Accepté Nb Accepté % Rejeté Nb Rejeté % Différé Nb Différé % Supprimé Nb Supprimé Total Signalements
hors_sujet 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

Pourcentages de résolution des éléments à examiner

Explication de la requête SQL

Cette requête analyse les statuts de résolution des éléments à examiner (par exemple, publications signalées) dans une plage de dates donnée. Elle calcule le pourcentage et le compte de chaque statut de résolution pour chaque type de signalement.

-- [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,
    -- Pourcentages
    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,
    -- Comptes
    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

Paramètres utilisés

  • :start_date : La date de début pour filtrer les éléments à examiner.
  • :end_date : La date de fin pour filtrer les éléments à examiner.

Explication des CTE

  1. flag_data : Regroupe les éléments à examiner par type de signalement et statut de résolution, en comptant les occurrences de chaque combinaison.
  2. flag_totals : Calcule le nombre total de signalements pour chaque type de signalement.
  3. flag_percentages : Combine les comptes de signalements et les totaux pour calculer le pourcentage de chaque statut de résolution pour chaque type de signalement.

Explication des résultats

Le résultat final est un tableau montrant :

  • Type de signalement.
  • Pourcentages et comptes pour chaque statut de résolution (en attente, approuvé, rejeté, ignoré, supprimé).

Exemples de résultats

Type de signalement % En attente Nb En attente % Approuvé Nb Approuvé % Rejeté Nb Rejeté % Ignoré Nb Ignoré % Supprimé Nb Supprimé
hors_sujet 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

Résolutions des signalements par les modérateurs

Explication de la requête SQL

Cette requête fournit des informations sur l’activité des modérateurs en montrant quels modérateurs ont résolu les publications signalées, les types de signalements qu’ils ont traités et les résolutions qu’ils ont appliquées. Elle calcule les pourcentages et les comptes pour chaque type de résolution.

-- [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 -- temps de résolution en 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 'hors_sujet'
            WHEN pat.id = 4 THEN 'inapproprié'
            WHEN pat.id = 6 THEN 'notifier_utilisateur'
            WHEN pat.id = 7 THEN 'notifier_modérateurs'
            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 'accepté'
            WHEN pa.disagreed_at IS NOT NULL THEN 'rejeté'
            WHEN pa.deferred_at IS NOT NULL THEN 'différé'
            WHEN pa.deleted_at IS NOT NULL THEN 'supprimé'
        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 = 'accepté' THEN rp.resolution_percentage ELSE 0 END) AS agreed_percentage,
        MAX(CASE WHEN rp.resolution = 'rejeté' THEN rp.resolution_percentage ELSE 0 END) AS disagreed_percentage,
        MAX(CASE WHEN rp.resolution = 'différé' THEN rp.resolution_percentage ELSE 0 END) AS deferred_percentage,
        MAX(CASE WHEN rp.resolution = 'supprimé' THEN rp.resolution_percentage ELSE 0 END) AS deleted_percentage,
        MAX(CASE WHEN rp.resolution = 'accepté' THEN rp.resolution_count ELSE 0 END) AS agreed_count,
        MAX(CASE WHEN rp.resolution = 'rejeté' THEN rp.resolution_count ELSE 0 END) AS disagreed_count,
        MAX(CASE WHEN rp.resolution = 'différé' THEN rp.resolution_count ELSE 0 END) AS deferred_count,
        MAX(CASE WHEN rp.resolution = 'supprimé' 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

Paramètres utilisés

  • :start_date : La date de début pour filtrer les publications signalées.
  • :end_date : La date de fin pour filtrer les publications signalées.
  • :only_staff : Un paramètre booléen pour filtrer les résultats afin d’inclure uniquement les membres du personnel.

Explication des CTE

  1. period_actions : Filtre les publications signalées dans la plage de dates spécifiée et calcule le temps de résolution pour chaque signalement.
  2. flag_types : Associe les identifiants de type de signalement à des noms lisibles.
  3. flag_resolutions : Regroupe les signalements par utilisateur, type de signalement et résolution, en comptant les occurrences de chaque combinaison.
  4. flag_totals : Calcule le nombre total de signalements pour chaque utilisateur et type de signalement.
  5. resolution_percentages : Combine les comptes de résolution et les totaux pour calculer les pourcentages pour chaque type de résolution.
  6. pivoted_data : Pivote les données pour afficher les pourcentages et les comptes de résolution dans des colonnes séparées pour chaque type de résolution.

Explication des résultats

Le résultat final est un tableau montrant :

  • Nom d’utilisateur du modérateur.
  • Pourcentages et comptes pour chaque type de résolution (accepté, rejeté, différé, supprimé).
  • Total des signalements traités par chaque modérateur.

Exemples de résultats

Modérateur Type de signalement % Accepté Nb Accepté % Rejeté Nb Rejeté % Différé Nb Différé % Supprimé Nb Supprimé Total Signalements
mod1 hors_sujet 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

Qui signale les publications

Explication de la requête SQL

Cette requête identifie les utilisateurs qui ont signalé des publications dans une plage de dates spécifiée et calcule le nombre total de signalements soumis par chaque utilisateur.

-- [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) -- types de signalements
  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

Paramètres utilisés

  • :start_date : La date de début pour filtrer les publications signalées.
  • :end_date : La date de fin pour filtrer les publications signalées.
  • :only_staff : Un paramètre booléen pour filtrer les résultats afin d’inclure uniquement les membres du personnel.

Explication des résultats

Le résultat final est une liste classée d’utilisateurs avec leurs comptes de signalements.

Exemples de résultats

ID Utilisateur Nom d’utilisateur Nb Signalements
1 user1 50
2 user2 30
3 user3 20

Notes sur les utilisateurs

Explication de la requête SQL

Cette requête récupère les notes sur les utilisateurs stockées dans la table plugin_store_rows. Elle extrait des détails tels que l’ID de l’utilisateur, la date de création, le contenu de la note et l’ID du créateur.

-- [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

Paramètres utilisés

  • :start_date : La date de début pour filtrer les notes sur les utilisateurs.
  • :end_date : La date de fin pour filtrer les notes sur les utilisateurs.

Explication des résultats

Le résultat final est une liste détaillée de notes sur les utilisateurs avec les métadonnées pertinentes.

Exemples de résultats

ID Utilisateur Créé le Note Utilisateur ID Utilisateur Créé par
1 2025-01-01 Cet utilisateur est utile. 2
2 2025-02-01 Cet utilisateur est associé à deux autres comptes utilisateurs. 3

KPI des modérateurs - Signalements et temps moyen de résolution

Explication de la requête SQL

Cette requête évalue la performance des modérateurs en calculant le nombre de signalements traités et le temps moyen de résolution (en minutes) pour chaque modérateur.

-- [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 -- temps de résolution en minutes
    FROM post_actions pa
    WHERE pa.post_action_type_id IN (3,4,6,7,8) -- Types de signalements
      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

Paramètres utilisés

  • :start_date : La date de début pour filtrer les publications signalées.
  • :end_date : La date de fin pour filtrer les publications signalées.

Explication des CTE

  1. period_actions : Filtre les publications signalées dans la plage de dates spécifiée et calcule le temps de résolution pour chaque signalement.
  2. moderator_actions : Identifie les signalements résolus par les modérateurs et calcule le temps de résolution pour chaque signalement.
  3. moderator_stats : Regroupe les signalements par modérateur et calcule le nombre total de signalements traités et le temps moyen de résolution.

Explication des résultats

Le résultat final est une liste classée de modérateurs avec leurs comptes de signalements traités et leurs temps moyens de résolution.

Exemples de résultats

Nom d’utilisateur du modérateur Signalements traités Temps moyen de résolution (minutes)
mod1 50 15.00
mod2 30 20.00

Toutes les données de signalement

Explication de la requête SQL

Cette requête fournit un jeu de données complet de toutes les données signalées sur les utilisateurs, les publications et les sujets dans une plage de dates spécifiée. Elle combine des données provenant de plusieurs tables pour inclure des détails tels que le type de signalement, l’élément signalé, la raison du signalement, la source du signalement, la décision de résolution et les messages associés.

-- [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 -- Inclure uniquement les signalements acceptés avec action entreprise
),
review_decisions AS (
    SELECT
        0 AS status_code, 'en attente' AS decision_name
    UNION ALL
    SELECT
        1 AS status_code, 'accepté' AS decision_name
    UNION ALL
    SELECT
        2 AS status_code, 'rejeté' AS decision_name
    UNION ALL
    SELECT
        3 AS status_code, 'ignoré' AS decision_name
),
flag_types AS (
    SELECT
        3 AS post_action_type_id, 'hors_sujet' AS flag_type_name
    UNION ALL
    SELECT
        4 AS post_action_type_id, 'inapproprié' AS flag_type_name
    UNION ALL
    SELECT
        6 AS post_action_type_id, 'notifier_utilisateur' AS flag_type_name
    UNION ALL
    SELECT
        7 AS post_action_type_id, 'notifier_modérateurs' 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, 'illégal' 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, -- Supprime uniquement les 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 'Utilisateur muet'
        WHEN fd.user_suspended_till IS NOT NULL THEN 'Utilisateur suspendu'
        WHEN fd.post_deleted_at IS NOT NULL THEN 'Publication supprimée'
        WHEN fd.post_hidden_at IS NOT NULL THEN 'Publication masquée'
        ELSE 'Aucune action entreprise'
    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 -- Différence de temps en 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

Paramètres utilisés

  • :start_date : La date de début pour filtrer les publications signalées.
  • :end_date : La date de fin pour filtrer les publications signalées.

Explication des CTE

  1. flag_data : Récupère des informations détaillées sur les publications signalées, y compris l’ID du signalement, l’élément signalé, le type de signalement, la raison du signalement, la source du signalement et les détails de l’examen.
  2. review_decisions : Associe les codes de statut d’examen à des noms de décision lisibles (par exemple, en attente, accepté, rejeté, ignoré).
  3. flag_types : Associe les identifiants de type d’action de publication à des noms de type de signalement lisibles (par exemple, hors-sujet, inapproprié, spam).

Explication des résultats

  • ID Signalement : L’identifiant unique du signalement.
  • Élément signalé : L’ID de la publication signalée.
  • Nom d’utilisateur ayant signalé : Le nom d’utilisateur de l’utilisateur qui a signalé la publication.
  • Date de signalement : La date de création du signalement.
  • Type de signalement : Le type numérique du signalement.
  • Nom du type de signalement : Le nom lisible du type de signalement (par exemple, hors-sujet, spam).
  • Examinable par modérateur : Indique si le signalement a été soumis par un utilisateur ou par le système.
  • Raison du signalement : La raison fournie pour le signalement.
  • Texte de l’élément signalé : Le contenu de la publication signalée.
  • ID du message associé : L’ID de tout message associé (le cas échéant).
  • Texte du message associé : Le contenu du message associé, avec les URL supprimées pour plus de clarté.
  • Nom d’utilisateur ayant examiné : Le nom d’utilisateur du modérateur qui a examiné le signalement.
  • Décision d’examen : La décision prise par l’examinateur (par exemple, accepté, rejeté, ignoré, supprimé).
  • Action entreprise : L’action entreprise à la suite du signalement, telle que le mutage ou la suspension de l’utilisateur, la suppression ou le masquage de la publication, ou aucune action.
  • Temps d’examen (minutes) : Le temps pris pour examiner le signalement, calculé comme la différence entre le temps de création du signalement et le temps d’examen, en minutes.

Exemples de résultats (anonymisés)

Exemples de résultats (anonymisés)

ID Signalement Élément signalé Nom d’utilisateur ayant signalé Date de signalement Type de signalement Nom du type de signalement Examinable par modérateur Raison du signalement Texte de l’élément signalé ID du message associé Texte du message associé Nom d’utilisateur ayant examiné Décision d’examen Action entreprise Temps d’examen (minutes)
12345 67890 user123 2025-04-01 12:00 8 Spam true Contenu spam « Achetez maintenant sur spam.com » 98765 « Regardez ça ! » mod456 Accepté Publication supprimée 15.25
12346 67891 user124 2025-04-02 14:30 4 Inapproprié false Offensif « C’est inapproprié ! » NULL NULL mod457 Rejeté Aucune action entreprise 30.50

Rapports de signalement soumis

Ce rapport fournit une vue d’ensemble des signalements soumis dans la plage de dates spécifiée. Il catégorise les signalements par leur type (par exemple, Spam, Inapproprié) et distingue entre les signalements rapportés par les utilisateurs et les signalements générés par le système. Le rapport inclut le nombre total de signalements pour chaque type, aidant à identifier les problèmes les plus couramment signalés sur la plateforme.

-- [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, 'Hors-sujet' AS flag_type_name
    UNION ALL
    SELECT
        4 AS post_action_type_id, 'Inapproprié' AS flag_type_name
    UNION ALL
    SELECT
        6 AS post_action_type_id, 'Notifier_utilisateur' AS flag_type_name
    UNION ALL
    SELECT
        7 AS post_action_type_id, 'Notifier_modérateurs' 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, 'Illégal' AS flag_type_name
    UNION ALL
    SELECT
        NULL AS post_action_type_id, 'Autre chose' 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 Rapporté,
    COUNT(CASE WHEN fd.flagged_by_username IN ('spam_scanner_bot', 'system') THEN 1 END) AS Automatisé,
    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

Paramètres utilisés

  • :start_date : La date de début pour filtrer les publications signalées.
  • :end_date : La date de fin pour filtrer les publications signalées.

Explication des CTE

  1. flag_data :
    Cette CTE récupère des informations détaillées sur les publications signalées, notamment :
  • L’identifiant unique du signalement (flag_id), l’identifiant de la publication signalée (post_id) et le sujet auquel elle appartient (topic_id).
  • Des informations sur le signalement lui-même, telles que l’utilisateur qui l’a effectué (flagged_by_username), la date du signalement (flagged_date), le type de signalement (flag_type) et la raison du signalement (flag_reason).
  • Des détails sur le processus de révision, incluant le modérateur qui a examiné le signalement (reviewed_by_username), la décision de révision (review_status) et l’heure de la révision (reviewed_at).
  1. flag_types :
    Cette CTE associe les identifiants numériques de type d’action sur publication à des noms de type de signalement lisibles par l’humain :
  • 3 : Hors-sujet
  • 4 : Inapproprié
  • 6 :Notifier l’utilisateur
  • 7 :Notifier les modérateurs
  • 8 : Spam
  • 10 : Illégal
  • NULL : Autre chose

Explication des résultats

La requête finale agrège les données de signalement par type de signalement et fournit les métriques suivantes :

  • Type : Le nom lisible par l’humain du type de signalement (par ex. Hors-sujet, Spam).
  • Signalé : Le nombre de signalements soumis par les utilisateurs (excluant les signalements générés par le système).
  • Automatisé : Le nombre de signalements générés par le système ou les bots (par ex. spam_scanner_bot, system).
  • Total : Le nombre total de signalements pour chaque type.

Les résultats sont triés par ordre décroissant du nombre total de signalements.

Exemple de résultats

Type Signalé Automatisé Total
Spam 120 80 200
Inapproprié 90 10 100
Hors-sujet 60 5 65
Notifier_modérateurs 30 0 30
Illégal 10 2 12
Autre chose 5 0 5

Bannissements et suspensions :

Ce rapport liste les utilisateurs qui ont été suspendus ou réduits au silence dans la plage de dates spécifiée. Il inclut des détails tels que les dates de suspension ou de réduction au silence, la durée de la mesure, ainsi que les dates de création du compte et de dernière activité de l’utilisateur. Ce rapport est utile pour surveiller les actions de modération et identifier les schémas de comportement utilisateur menant à des bannissements ou réductions au silence.

-- [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

Paramètres utilisés

  • :start_date : La date de début pour filtrer les utilisateurs suspendus ou réduits au silence.
  • :end_date : La date de fin pour filtrer les utilisateurs suspendus ou réduits au silence.

Explication des résultats

Cette requête récupère une liste d’utilisateurs qui ont été suspendus ou réduits au silence dans la plage de dates spécifiée. Les colonnes clés incluent :

  • ID utilisateur : L’identifiant unique de l’utilisateur.
  • Nom d’utilisateur : Le nom d’utilisateur de l’utilisateur.
  • Nom : Le nom complet de l’utilisateur (si disponible).
  • Suspendu le : La date à laquelle l’utilisateur a été suspendu.
  • Suspendu jusqu’au : La date jusqu’à laquelle l’utilisateur est suspendu.
  • Réduit au silence jusqu’au : La date jusqu’à laquelle l’utilisateur est réduit au silence.
  • Compte créé le : La date de création du compte de l’utilisateur.
  • Vu pour la dernière fois le : La dernière fois que l’utilisateur a été actif sur la plateforme.
  • Niveau de signalement : Le niveau de signalement actuel de l’utilisateur.
  • Admin : Si l’utilisateur est un administrateur (vrai/faux).
  • Modérateur : Si l’utilisateur est un modérateur (vrai/faux).

Les résultats sont triés par ordre décroissant de la date de suspension (suspended_at) et de la date de réduction au silence (silenced_till), les valeurs nulles apparaissant en dernier.

Exemple de résultats

ID utilisateur Nom d’utilisateur Nom Suspendu le Suspendu jusqu’au Réduit au silence jusqu’au Compte créé le Vu pour la dernière fois le Niveau de signalement Admin Modérateur
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

Actions de signalement validées

Ce rapport se concentre sur les signalements qui ont été validés par les modérateurs et ont entraîné des actions. Il catégorise les signalements par type et fournit des métriques telles que le nombre total de signalements, le temps médian nécessaire pour agir sur ceux-ci, et les résultats (par ex. utilisateurs réduits au silence, publications supprimées).

-- [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 -- Inclure uniquement les signalements validés et pour lesquels une action a été entreprise
),
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 -- Inclure les types de signalement mappés
            OR fd.flag_type IN (
                'ReviewableAkismetPost',
                'ReviewableUser',
                'ReviewableFlaggedPost',
                'ReviewableChatMessage',
                'ReviewablePost',
                'ReviewableQueuedPost'
            ) -- Inclure les types de signalement spécifiques pour les résultats NULL
        )
    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 "Temps médian d'action (minutes)",
    COUNT(CASE WHEN fd.user_silenced_till IS NOT NULL THEN 1 END) AS "Utilisateur réduit au silence",
    COUNT(CASE WHEN fd.user_suspended_till IS NOT NULL THEN 1 END) AS "Utilisateur supprimé",
    COUNT(CASE WHEN fd.post_deleted_at IS NOT NULL THEN 1 END) AS "Publication supprimée",
    COUNT(CASE WHEN fd.post_hidden_at IS NOT NULL THEN 1 END) AS "Publication masquée"

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 -- Inclure les types de signalement mappés
    OR fd.flag_type IN (
        'ReviewableAkismetPost',
        'ReviewableUser',
        'ReviewableFlaggedPost',
        'ReviewableChatMessage',
        'ReviewablePost',
        'ReviewableQueuedPost'
    ) -- Inclure les types de signalement spécifiques pour les résultats NULL
GROUP BY
    COALESCE(ft.flag_type_name, fd.flag_type), mta.median_time_minutes
ORDER BY
    Total DESC

Paramètres utilisés

  • :start_date : La date de début pour filtrer les signalements validés.
  • :end_date : La date de fin pour filtrer les signalements validés.

Explication des CTE

  1. flag_data :
    Cette CTE récupère des informations détaillées sur les signalements qui ont été validés et pour lesquels des actions ont été entreprises. Elle inclut :
  • L’identifiant unique du signalement (flag_id), l’identifiant de la publication signalée (post_id) et le sujet auquel elle appartient (topic_id).
  • Des informations sur le signalement lui-même, telles que l’utilisateur qui l’a effectué (flagged_by_username), la date du signalement (flagged_date), le type de signalement (flag_type) et la raison du signalement (flag_reason).
  • Des détails sur le processus de révision, incluant le modérateur qui a examiné le signalement (reviewed_by_username), la décision de révision (review_status) et l’heure de la révision (reviewed_at).
  • Des informations supplémentaires sur la publication signalée, telles que si elle a été supprimée, masquée, ou si l’auteur a été réduit au silence ou suspendu.
  1. flag_types :
    Cette CTE associe les identifiants numériques de type d’action sur publication à des noms de type de signalement lisibles par l’humain :
  • 3 : Hors-sujet
  • 4 : Inapproprié
  • 6 :Notifier l’utilisateur
  • 7 :Notifier les modérateurs
  • 8 : Spam
  • 10 : Illégal
  1. median_time_to_act :
    Cette CTE calcule le temps médian (en minutes) nécessaire pour agir sur chaque type de signalement. Le temps est calculé comme la différence entre l’heure de création du signalement (flagged_date) et l’heure de révision (reviewed_at).

Explication des résultats

La requête finale agrège les données de signalement validé par type de signalement et fournit les métriques suivantes :

  • Type : Le nom lisible par l’humain du type de signalement (par ex. Hors-sujet, Spam).
  • Signalé : Le nombre de signalements soumis par les utilisateurs (excluant les signalements générés par le système).
  • Automatisé : Le nombre de signalements générés par le système ou les bots (par ex. spam_scanner_bot, system).
  • Total : Le nombre total de signalements pour chaque type.
  • Temps médian d’action (minutes) : Le temps médian nécessaire pour agir sur les signalements de ce type, en minutes.
  • Utilisateur réduit au silence : Le nombre de signalements ayant entraîné la réduction au silence de l’utilisateur.
  • Utilisateur supprimé : Le nombre de signalements ayant entraîné la suspension de l’utilisateur.
  • Publication supprimée : Le nombre de signalements ayant entraîné la suppression de la publication.
  • Publication masquée : Le nombre de signalements ayant entraîné le masquage de la publication.

Les résultats sont triés par ordre décroissant du nombre total de signalements.

Exemple de résultats

Type Signalé Automatisé Total Temps médian d’action (minutes) Utilisateur réduit au silence Utilisateur supprimé Publication supprimée Publication masquée
Spam 100 50 150 30 20 10 50 30
Inapproprié 80 5 85 45 15 5 30 20
Hors-sujet 40 2 42 25 5 0 10 15
Notifier_modérateurs 20 0 20 60 0 0 5 10
Illégal 5 1 6 120 1 1 3 2

Actions de modération entreprises

Ce rapport fournit un résumé des actions de modération entreprises dans la plage de dates spécifiée. Il agrège divers types d’actions validées, incluant le contenu signalé par les utilisateurs ou l’automatisation, les publications supprimées ou masquées, les avertissements émis, les comptes supprimés ou suspendus, et les utilisateurs réduits au silence pour des périodes prolongées. Chaque catégorie est présentée avec le nombre total de cas, offrant une vue d’ensemble de l’activité de modération.

-- [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 -- Inclure uniquement les signalements validés et pour lesquels une action a été entreprise
),
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 -- Inclure les types de signalement mappés
        OR fd.flag_type IN (
            'ReviewableAkismetPost',
            'ReviewableUser',
            'ReviewableFlaggedPost',
            'ReviewableChatMessage',
            'ReviewablePost',
            'ReviewableQueuedPost'
        ) -- Inclure les types de signalement spécifiques pour les résultats NULL
),
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
    'Contenu signalé par les utilisateurs' AS category,
    fc.user_flagged AS "Nombre de cas"
FROM flagged_content fc

UNION ALL

SELECT
    'Contenu signalé par l'automatisation' AS category,
    fc.automation_flagged AS "Nombre de cas"
FROM flagged_content fc

UNION ALL

SELECT
    'Publications supprimées pour violation des conditions' AS category,
    pdh.posts_deleted AS "Nombre de cas"
FROM posts_deleted_and_hidden pdh

UNION ALL

SELECT
    'Publications masquées' AS category,
    pdh.posts_hidden AS "Nombre de cas"
FROM posts_deleted_and_hidden pdh

UNION ALL

SELECT
    'Avertissements émis' AS category,
    wi.warnings_count AS "Nombre de cas"
FROM warnings_issued wi

UNION ALL

SELECT
    'Comptes supprimés' AS category,
    vs.accounts_deleted AS "Nombre de cas"
FROM violations_and_suspensions vs

UNION ALL

SELECT
    'Comptes suspendus' AS category,
    vs.accounts_suspended AS "Nombre de cas"
FROM violations_and_suspensions vs

UNION ALL

SELECT
    'Utilisateurs réduits au silence pendant 10+ ans' AS category,
    si.silences_count AS "Nombre de cas"
FROM silences_issued si

Paramètres utilisés

  • :start_date : La date de début pour filtrer les actions de modération.
  • :end_date : La date de fin pour filtrer les actions de modération.

Explication des résultats

La requête agrège les actions de modération en catégories et fournit le nombre total de cas pour chaque catégorie. Les catégories incluent :

  1. Contenu signalé par les utilisateurs : Le nombre de publications signalées par les utilisateurs réguliers (excluant les signalements générés par le système).
  2. Contenu signalé par l’automatisation : Le nombre de publications signalées par des systèmes automatisés ou des bots (par ex. spam_scanner_bot, system).
  3. Publications supprimées pour violation des conditions : Le nombre de publications qui ont été supprimées en raison de violations des règles de la communauté ou des conditions d’utilisation.
  4. Publications masquées : Le nombre de publications qui ont été masquées (mais pas supprimées) pour diverses raisons.
  5. Avertissements émis : Le nombre d’avertissements émis aux utilisateurs pour comportement ou contenu inapproprié.
  6. Comptes supprimés : Le nombre de comptes utilisateur supprimés en raison de violations, telles que le signalement en tant que spammers ou le rejet dans les files d’attente de révision.
  7. Comptes suspendus : Le nombre de comptes utilisateur suspendus pour une période spécifique en raison de violations.
  8. Utilisateurs réduits au silence pendant 10+ ans : Le nombre d’utilisateurs réduits au silence indéfiniment ou pour des périodes prolongées (10+ ans).

Exemple de résultats

Catégorie Nombre de cas
Contenu signalé par les utilisateurs 150
Contenu signalé par l’automatisation 100
Publications supprimées pour violation des conditions 50
Publications masquées 30
Avertissements émis 20
Comptes supprimés 10
Comptes suspendus 15
Utilisateurs réduits au silence pendant 10+ ans 5

Actions de modération individuelles entreprises

Ce rapport fournit un journal détaillé des actions de modération individuelles validées entreprises dans la plage de dates spécifiée. Il inclut des informations sur l’utilisateur qui a effectué l’action, l’utilisateur cible, la date de l’action, la catégorie de l’action (par ex. contenu signalé, publications supprimées, avertissements émis), et le contexte ou la raison de l’action. Ce rapport est utile pour auditer des décisions de modération spécifiques et comprendre le contexte derrière chaque action.

-- [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 -- Inclure uniquement les signalements validés et pour lesquels une action a été entreprise
),
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,
        'Contenu signalé par les utilisateurs' 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,
        'Contenu signalé par l'automatisation' 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,
        'Publications supprimées pour violation des conditions' 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,
        'Publications masquées' 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,
        'Avertissements émis' 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,
        'Comptes supprimés' 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,
        'Comptes suspendus' 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,
        'Utilisateurs réduits au silence pendant 10+ ans' 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

Paramètres utilisés

  • :start_date : La date de début pour filtrer les actions de modération individuelles.
  • :end_date : La date de fin pour filtrer les actions de modération individuelles.

Explication des résultats

La requête fournit un journal détaillé des actions de modération individuelles, incluant :

  1. Utilisateur agissant : Le nom d’utilisateur du modérateur, du système ou de l’utilisateur qui a effectué l’action.
  2. Utilisateur cible : Le nom d’utilisateur ou l’ID de l’utilisateur qui était l’objet de l’action (par ex. l’auteur d’une publication signalée ou le destinataire d’un avertissement).
  3. Date de l’action : La date et l’heure à laquelle l’action a eu lieu.
  4. Catégorie : Le type d’action de modération, tel que :
  • Contenu signalé par les utilisateurs
  • Contenu signalé par l’automatisation
  • Publications supprimées pour violation des conditions
  • Publications masquées
  • Avertissements émis
  • Comptes supprimés
  • Comptes suspendus
  • Utilisateurs réduits au silence pendant 10+ ans
  1. Contexte : Informations supplémentaires ou contenu lié à l’action, telles que le texte d’une publication signalée ou la raison d’une suspension.

Exemple de résultats

Utilisateur agissant Utilisateur cible Date de l’action Catégorie Contexte
user123 user456 2024-02-01 10:00 Contenu signalé par les utilisateurs « Cette publication contient du contenu spam. »
spam_scanner user789 2024-02-02 12:00 Contenu signalé par l’automatisation « Détecté comme spam par le système. »
mod001 user456 2024-02-03 14:00 Publications supprimées pour violation des conditions « Exemple de contenu de publication
mod002 user123 2024-02-04 16:00 Publications masquées « Publication jugée inappropriée. »
admin001 user789 2024-02-05 18:00 Avertissements émis NULL
admin002 user456 2024-02-06 20:00 Comptes supprimés « Compte supprimé via la file de révision. »
mod003 user123 2024-02-07 22:00 Comptes suspendus « Utilisateur suspendu pour spam répété. »
admin003 user789 2024-02-08 08:00 Utilisateurs réduits au silence pendant 10+ ans « Utilisateur réduit au silence pour violations extrêmes. »
6 « J'aime »