Rapport du tableau de bord - Activité du modérateur

Ceci est une version SQL du rapport de tableau de bord pour l’activité des modérateurs.

Ce rapport fournit un aperçu complet des activités effectuées par les modérateurs dans un laps de temps spécifié sur un site Discourse. Le rapport combine plusieurs aspects de l’activité des modérateurs : le temps passé sur la plateforme, le nombre de drapeaux examinés, les publications créées, les messages personnels (MP) créés, les sujets créés et les révisions de publications effectuées.

Ce rapport de tableau de bord est un outil précieux pour les administrateurs cherchant à mesurer l’efficacité et l’engagement de leur équipe de modération, en offrant un aperçu détaillé de leur activité et de leurs contributions. Les informations fournies par le rapport peuvent éclairer les décisions concernant la formation, la reconnaissance et le recrutement des modérateurs, et garantir que l’équipe de modération est bien équilibrée et efficace dans le maintien des normes de la communauté.

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


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

),

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

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

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

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

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

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

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

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

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

Paramètres

  • :start_date et :end_date - Ces paramètres définissent la plage de dates pour le rapport. Les deux paramètres de date acceptent le format de date AAAA-MM-JJ.
  • :include_admins - Ce paramètre détermine s’il faut inclure les administrateurs dans le rapport aux côtés des modérateurs.

Explication de la requête SQL

Le rapport est structuré à l’aide d’expressions de table communes (CTE) pour segmenter le traitement des données en sections gérables et logiques. Voici ce qui se passe dans chaque CTE :

  1. mods : Identifie tous les utilisateurs ayant le statut de modérateur ou d’administrateur (si inclus par le paramètre :include_admins). Il ne sélectionne que les colonnes utilisateur pertinentes pour les requêtes ultérieures.
  2. time_read : Calcule le temps total (en secondes) que chaque modérateur a passé à lire du contenu sur la plateforme entre les dates de début et de fin fournies.
  3. flag_count : Compte le nombre de drapeaux que les modérateurs ont approuvés ou désapprouvés pendant la période spécifiée. Il prend en compte plusieurs types de drapeaux représentés par leurs identifiants d’action de publication respectifs.
  4. revision_count : Compte le nombre de révisions de publications effectuées par les modérateurs sur les publications d’autres utilisateurs dans le délai imparti.
  5. topic_count : Compte le nombre de sujets réguliers créés par les modérateurs.
  6. post_count : Compte le nombre de publications créées par les modérateurs dans des sujets réguliers.
  7. pm_count : Compte le nombre de messages privés initiés par les modérateurs.

Après avoir collecté les données dans les CTE, la requête principale les joint sur l’ID utilisateur et compile le rapport final affichant le nom d’utilisateur de chaque modérateur, le temps total passé à lire (converti en heures), le nombre de drapeaux examinés, les sujets créés, les messages personnels créés, les publications créées et les révisions effectuées. Les résultats sont triés par ordre alphabétique par le nom d’utilisateur du modérateur.

Résultats d’exemple

user username flags_reviewed time_reading_hours topics_created pms_created posts_created revisions
1 moderator1 NULL 36.11 NULL 344 8 15
2 moderator2 46 104.52 2 271 466 363
3 moderator3 NULL 72.15 NULL 418 64 16
7 « J'aime »

Salut @SaraDev,

C’est vraiment utile. Serait-il possible d’ajouter des colonnes « Posts approuvés » et « Posts rejetés » au rapport d’activité du modérateur ?

Merci,
T_Disco

3 « J'aime »

Salut @T_Disco,

Oui, pour ajouter les résultats « Posts approuvés » et « Posts rejetés » au rapport d’activité du modérateur, nous pouvons utiliser les tables reviewables et reviewable_scores. Plus précisément, le champ status dans reviewable_scores peut être utilisé pour déterminer si un message a été approuvé (status = 1) ou rejeté (status = 2).

Voici à quoi cela ressemblerait ajouté au rapport :

Activité du modérateur avec les posts approuvés et les posts rejetés

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

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

time_read AS (
    SELECT SUM(uv.time_read) AS time_read,
        uv.user_id
    FROM mods m
    JOIN user_visits uv ON m.user_id = uv.user_id
    WHERE uv.visited_at >= :start_date
        AND uv.visited_at <= :end_date
    GROUP BY uv.user_id
),
      
flag_count AS (
    WITH period_actions AS (
        SELECT agreed_by_id,
        disagreed_by_id
        FROM post_actions
        WHERE post_action_type_id IN (3,4,8,6,7)
        AND created_at >= :start_date
        AND created_at <= :end_date
    ),

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

disagreed_flags AS (
        SELECT pa.disagreed_by_id AS user_id,
        COUNT(*) AS flag_count
        FROM mods m
        JOIN period_actions pa
        ON pa.disagreed_by_id = m.user_id
        GROUP BY disagreed_by_id
    )
    SELECT
    COALESCE(af.user_id, df.user_id) AS user_id,
    COALESCE(af.flag_count, 0) + COALESCE(df.flag_count, 0) AS flag_count
    FROM agreed_flags af
    FULL OUTER JOIN disagreed_flags df
    ON df.user_id = af.user_id
      ),
      
revision_count AS (
      SELECT pr.user_id,
      COUNT(*) AS revision_count
      FROM mods m
      JOIN post_revisions pr
      ON pr.user_id = m.user_id
      JOIN posts p
      ON p.id = pr.post_id
      WHERE pr.created_at >= :start_date
      AND pr.created_at <= :end_date
      AND p.user_id <> pr.user_id
      GROUP BY pr.user_id
),
      
topic_count AS (
      SELECT t.user_id,
        COUNT(*) AS topic_count
      FROM mods m
      JOIN topics t ON t.user_id = m.user_id
      WHERE t.archetype = 'regular'
          AND t.created_at >= :start_date
          AND t.created_at <= :end_date
      GROUP BY t.user_id
),
      
post_count AS (
      SELECT p.user_id,
         COUNT(*) AS post_count
      FROM mods m
      JOIN posts p ON p.user_id = m.user_id
      JOIN topics t ON t.id = p.topic_id
      WHERE t.archetype = 'regular'
          AND p.created_at >= :start_date
          AND p.created_at <= :end_date
      GROUP BY p.user_id
),
      
pm_count AS (
      SELECT p.user_id,
        COUNT(*) AS pm_count
      FROM mods m
      JOIN posts p ON p.user_id = m.user_id
      JOIN topics t ON t.id = p.topic_id
      WHERE t.archetype = 'private_message'
          AND p.created_at >= :start_date
          AND p.created_at <= :end_date
      GROUP BY p.user_id
),

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

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

Où les résultats de ce rapport ressembleraient à ceci :

user username flags_reviewed time_reading_hours topics_created pms_created posts_created revisions posts_approved posts_rejected
1 moderator1 NULL 36.11 NULL 344 8 15 10 5
2 moderator2 46 104.52 2 271 466 363 7 3
3 moderator3 NULL 72.15 NULL 418 64 16 NULL NULL
1 « J'aime »

C’est génial, merci @SaraDev !

Ces colonnes seront-elles ajoutées au rapport d’activité du modérateur dans l’onglet /admin/dashboard/moderation à un moment donné ?

Merci encore :slight_smile:
T_Disco

1 « J'aime »

@SaraDev
Est-il possible d’interroger le nombre de fusions de sujets effectuées sur une période donnée ?

2 « J'aime »

Nous n’avons actuellement aucun plan pour ajouter ces colonnes à la version du rapport du tableau de bord, mais je peux en discuter avec notre équipe pour voir si nous pouvons apporter des améliorations à l’avenir. :slightly_smiling_face:

Oui, pour créer une requête qui montre quand des messages ont été déplacés vers d’autres sujets, vous pouvez filtrer la table posts pour les entrées où le action_code indique un déplacement avec une entrée split_topic.

Par exemple :

Actions individuelles de déplacement de messages

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

Afficherait des résultats comme :

message utilisateur sujet numéro de message créé à mis à jour à code d’action
Un message a été fusionné dans un sujet existant:: [Titre du sujet fusionné] NOM D’UTILISATEUR Titre du sujet d’origine 3 2024-10-30 2024-10-30 split_topic
2 messages ont été fusionnés dans un sujet existant:: [Titre du sujet fusionné] NOM D’UTILISATEUR Titre du sujet d’origine 5 2024-10-30 2024-10-30 split_topic
Un message a été divisé dans un nouveau sujet : [Titre du sujet divisé] NOM D’UTILISATEUR Titre du sujet d’origine 2 2024-10-30 2024-10-30 split_topic

Si vous souhaitiez afficher un décompte total des déplacements de messages par utilisateur sur un site, et ajouter quelques paramètres pour filtrer les actions de déplacement par date, vous pourriez également utiliser une requête comme la suivante :

Nombre d’actions de déplacement de messages par utilisateur

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

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

Où des résultats d’exemple ressembleraient à :

utilisateur nombre de déplacements
Nom d’utilisateur_1 5
Nom d’utilisateur_2 2

Notez qu’avec ces deux requêtes, le déplacement d’un nombre quelconque de messages d’un sujet à un autre n’est compté que comme une action, quel que soit le nombre de messages déplacés. Le déplacement du contenu d’un sujet entier vers un autre sujet n’est également compté que comme une action.

Le nombre de messages déplacés lors de chaque action peut être vu dans la requête Actions individuelles de déplacement de messages sous la colonne message avec le texte X messages ont été fusionnés dans un sujet existant..., cependant, ces informations ne sont pas présentes dans la deuxième requête.

Dans la requête Actions individuelles de déplacement de messages, vous pourriez également voir des messages avec le texte : Un message a été divisé dans un nouveau sujet ..., qui indiquent que le message a été divisé en un nouveau sujet au lieu d’être déplacé dans un sujet existant, car Discourse considère ces deux actions comme des actions split_topic, puisque des messages sont déplacés d’un sujet à un autre.

2 « J'aime »

5 messages ont été déplacées vers un nouveau sujet : Métriques de la page utilisateur