Rapporto Dashboard - Attività dei Moderator

Questo è un SQL del Report Dashboard per l’Attività del Moderatore.

Questo report fornisce una panoramica completa delle attività svolte dai moderatori in un determinato periodo di tempo su un sito Discourse. Il report combina molteplici aspetti dell’attività dei moderatori: tempo trascorso sulla piattaforma, numero di flag revisionati, post creati, messaggi personali (PM) creati, argomenti creati e revisioni dei post effettuate.

Questo report dashboard è uno strumento prezioso per gli amministratori che desiderano misurare l’efficacia e il coinvolgimento del loro team di moderazione, fornendo uno sguardo dettagliato sulla loro attività e sui loro contributi. Le informazioni fornite dal report possono informare le decisioni sulla formazione, il riconoscimento e il reclutamento dei moderatori, garantendo che il team di moderazione sia ben equilibrato ed efficace nel mantenere gli standard della community.

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

Parametri

  • :start_date e :end_date - Questi parametri definiscono l’intervallo di date per il report. Entrambi i parametri di data accettano il formato data AAAA-MM-GG.
  • :include_admins - Questo parametro determina se includere gli amministratori nel report insieme ai moderatori.

Spiegazione della Query SQL

Il report è strutturato utilizzando espressioni di tabella comuni (CTE) per segmentare l’elaborazione dei dati in sezioni gestibili e logiche. Ecco cosa succede in ogni CTE:

  1. mods: Identifica tutti gli utenti con stato di moderatore o stato di amministratore (se incluso dal parametro :include_admins). Seleziona solo le colonne utente pertinenti per le query successive.
  2. time_read: Calcola il tempo totale (in secondi) che ciascun moderatore ha trascorso leggendo contenuti sulla piattaforma tra le date di inizio e fine fornite.
  3. flag_count: Conta il numero di flag che i moderatori hanno approvato o respinto durante il periodo specificato. Tiene conto di più tipi di flag rappresentati dai rispettivi ID di tipo di azione sul post.
  4. revision_count: Conta il numero di revisioni dei post effettuate dai moderatori sui post di altri utenti nell’intervallo di tempo specificato.
  5. topic_count: Conta il numero di argomenti regolari creati dai moderatori.
  6. post_count: Conta il numero di post creati dai moderatori in argomenti regolari.
  7. pm_count: Conta il numero di messaggi privati avviati dai moderatori.

Dopo aver raccolto i dati nelle CTE, la query principale li unisce in base all’ID utente e compila il report finale mostrando il nome utente di ciascun moderatore, il tempo totale trascorso a leggere (convertito in ore), il numero di flag revisionati, argomenti creati, messaggi personali creati, post creati e revisioni effettuate. I risultati sono ordinati alfabeticamente per nome utente del moderatore.

Risultati di esempio

utente 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 Mi Piace

Ciao @SaraDev,

Questo è davvero utile. Sarebbe possibile aggiungere le colonne “Post approvati” e “Post rifiutati” al report Attività moderatore?

Grazie,
T_Disco

3 Mi Piace

Ciao @T_Disco,

Sì, per aggiungere i risultati di Post approvati e Post rifiutati al report Attività moderatore, possiamo utilizzare le tabelle reviewables e reviewable_scores. Nello specifico, il campo status in reviewable_scores può essere utilizzato per determinare se un post è stato approvato (status = 1) o rifiutato (status = 2).

Ecco come apparirebbe aggiunto al report:

Attività moderatore con post approvati e post rifiutati

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

Dove i risultati per questo report apparirebbero così:

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 Mi Piace

È fantastico, grazie @SaraDev!

Quelle colonne verranno aggiunte al report sull’attività del moderatore nella scheda /admin/dashboard/moderation a un certo punto?

Grazie ancora :slight_smile:
T_Disco

1 Mi Piace

@SaraDev
È possibile una query per il numero di unioni di argomenti effettuate in un dato periodo di tempo?

2 Mi Piace

Attualmente non abbiamo in programma di aggiungere queste colonne alla versione dashboard del report, ma posso discuterne con il nostro team per vedere se possiamo apportare alcuni miglioramenti in futuro. :slightly_smiling_face:

Sì, per creare una query che mostri quando i post sono stati spostati in altri argomenti, puoi filtrare la tabella posts per le voci in cui action_code indica uno spostamento con una voce split_topic.

Ad esempio:

Azioni di spostamento di singoli post

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

Mostrerebbe risultati come:

post user topic post_number created_at updated_at action_code
Un post è stato unito a un argomento esistente:: [Titolo argomento unito] USERNAME Titolo argomento originale 3 2024-10-30 2024-10-30 split_topic
2 post sono stati uniti a un argomento esistente:: [Titolo argomento unito] USERNAME Titolo argomento originale 5 2024-10-30 2024-10-30 split_topic
Un post è stato diviso in un nuovo argomento: [Titolo argomento diviso] USERNAME Titolo argomento originale 2 2024-10-30 2024-10-30 split_topic

Se volessi mostrare un conteggio totale degli spostamenti di post per ciascun utente su un sito e aggiungere un paio di parametri per filtrare le azioni di spostamento per data, potresti anche utilizzare una query come la seguente:

Numero di azioni di spostamento di post per utente

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

Dove i risultati di esempio sarebbero simili a:

user move_count
Username_1 5
Username_2 2

Nota che con entrambe queste query, lo spostamento di un qualsiasi numero di post da un argomento a un altro viene conteggiato come una sola azione, indipendentemente dal numero di post spostati. Anche lo spostamento del contenuto di un intero argomento in un altro argomento viene conteggiato come una sola azione.

Il numero di post spostati durante ciascuna azione può essere visualizzato nella query Azioni di spostamento di singoli post sotto la colonna post con il testo X posts were merged into an existing topic..., tuttavia, queste informazioni non sono presenti nella seconda query.

Nella query Azioni di spostamento di singoli post, potresti anche vedere post con il testo: A post was split to a new topic ..., che indicano che il post è stato diviso in un nuovo argomento anziché essere spostato in un argomento esistente, poiché Discourse considera entrambe queste azioni come azioni split_topic, dato che i post vengono spostati da un argomento a un altro.

2 Mi Piace

5 post sono stati spostati in un nuovo argomento: Metriche della pagina utente