Relatorios personalizados desenvolvidos pela comunidade: Difference between revisions

From MoodleDocs
 
(22 intermediate revisions by the same user not shown)
Line 1: Line 1:
== Introdução ==
== Introdução ==
Os modelos de relatórios disponibilizados aqui foram desenvolvidos pela comunidade. Todos são livres para contribuir. Se você tiver dúvidas de trabalho que acha que seriam úteis para os membros da comunidade, compartilhe-as aqui.
==== Plugins de relatório ====
Há dois plugins de relatório comumente utilizados no Moodle para criar e exibir relatórios SQL:
* [https://moodle.org/plugins/report_customsql Ad-hoc database queries ]
* [https://moodle.org/plugins/block_configurable_reports Configurable Reports]
Você pode instalar e usar qualquer um desses plugins de relatório em seu site Moodle. Se seu foro é mais no envio de relatórios por e-mail, então verifique primeiro o ”''Ad-hoc database queries''”. Se você deseja compartilhar relatórios com professores e estudantes em cursos, então veja o plugin “''Configurable Reports''”.
==== Algumas notas técnicas para usuários de relatórios ====
===== Estrutura de tabelas e schema =====
Se você está procurando alguma ajuda para entender as tabelas e estrutura do banco de dados, você pode encontrar nesta página um [https://www.examulator.com/er/ esquema de banco de dados atualizado], graças ao [https://moodle.org/user/profile.php?id=2246 Marcus Green].
===== Nomes das tabelas e prefix_ =====
Você verá que muitas das consultas abaixo usam "prefix_” para iniciar nomes de tabelas, por exemplo “prefix_user”. Este “prefix_” é usado antes dos nomes das tabelas quando você está usando um dos dois plugins, relatórios “''Ad-hoc''” ou “''Configurable Reports''”. Esses dois plugins substituem “prefix_” pelo prefixo real definido em seu arquivo de configuração do Moodle “config.php”. Isso ocorre porque, embora o prefixo padrão da tabela seja “mdl_”, ao instalar o Moodle, você pode definir qualquer prefixo que desejar, portanto, é personalizável.
As consultas escritas para serem usadas com qualquer um desses dois plugins devem sempre ter “prefix_” para iniciar todos os nomes de tabelas em vez do prefixo real.
Quando você usa ferramentas como PhpMyAdmin, Workbench, etc., você deve usar os nomes das tabelas reais como estão no banco de dados, incluindo o prefixo real definido para seu próprio site.
Portanto:
* Se uma consulta abaixo tiver "prefix_” em seus nomes de tabela e você quiser usar essa consulta em uma ferramenta como phpMyAdmin, será necessário substituí-los pelo prefixo real das tabelas do seu site;
* Se uma consulta abaixo tiver "mdl_” ou algum outro prefixo em seus nomes de tabela e você estiver usando isso em qualquer um dos dois plugins do Moodle, será necessário substituir esses prefixos explícitos por “prefix_”.
Você receberá um erro '''"Sem prefixo explícito”''' em qualquer um desses plugins se usar código com o prefixo da tabela real nele.
===== Sobre %%FILTER_ e variáveis similares =====
Você verá várias consultas com variáveis que começam e terminam com dois “%%” sinais de porcentagem, por exemplo “%%WWWROOT%%” e vários deles que começam com “%%FILTER_”. Essas variáveis são específicas para um ou ambos os plugins do Moodle. Eles não são códigos SQL padrão e não funcionarão em código feito em outras ferramentas fora desses dois plugins.
Além disso, embora esses dois plugins compartilhem parte da variável, alguns funcionam apenas em um ou outro plugin. Por exemplo, “%%WWWROOT%%” funciona em ambos; as variáveis “%%C%%”, “%%S%%” e “%%Q%%” só funcionam no plugin de consultas de banco de dados Ad-hoc; e aqueles que iniciam “%%FILTER_” funcionam apenas no plugin Relatórios Configuráveis (''Configurable Reports''). Você receberá um erro de sintaxe se usar uma variável que o plugin não suporta.
Encontre mais detalhes na seção sobre Variáveis no final da página.
===== Sobre formatação de datas =====
As datas e horas no Moodle são armazenadas internamente no banco de dados como o [https://pt.wikipedia.org/wiki/Era_Unix timestamp Unix Epoch], sendo o número de segundos desde 1º de janeiro de 1970 às 00:00 UTC, que no banco de dados será representado como 0.
Para convertê-los em formatos “legíveis por humanos” que usamos diariamente, você precisará fazer isso em sua consulta com código. Existem vários exemplos nos relatórios nesta página: uma pesquisa nesta página por “FROM_UNIXTIME” mostrará como usar e converter as datas. Observe que quando os horários são convertidos em data padrão e os horários dos carimbos de data/hora estão no [https://pt.wikipedia.org/wiki/Tempo_Universal_Coordenado formato UTC] e você pode querer [https://dev.mysql.com/doc/refman/8.0/en/time-zone-support.html convertê-los em um fuso horário local].
As funções “[https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_from-unixtime FROM_UNIXTIME]” e “[https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_date-format DATE_FORMAT]” comuns nos relatórios abaixo são funções específicas do MySQL, que você pode usar em sites MySQL e MariaDB.
Para outra versão do SQL, você precisará editar essas consultas para funcionarem corretamente. Para PostgreSQL, consulte a página de manual em [https://www.postgresql.org/docs/current/functions-formatting.html funções de formatação de tipo de dados]. Há vários exemplos específicos do PostgreSQL entre as consultas abaixo.
Existem muitos [https://en.wikipedia.org/wiki/Date_format_by_country formatos de data e hora usados em vários países], mas todos podem ser formatados com base no valor do registro de data e hora original. Para obter uma amostra em MySQL, você pode usar a seguinte consulta para mostrar a data de instalação do seu site Moodle em vários formatos:
<syntaxhighlight lang="sql">
SELECT
    l.timecreated AS "Timestamp",
    DATE_FORMAT(FROM_UNIXTIME(l.timecreated), '%d/%m/%Y') AS "Formato padrão no mundo",
    DATE_FORMAT(FROM_UNIXTIME(l.timecreated), '%m/%d/%Y') AS "Formato EUA",
    DATE_FORMAT(FROM_UNIXTIME(l.timecreated), '%Y-%m-%d') AS "Formato padrão ISO",
    DATE_FORMAT(FROM_UNIXTIME(l.timecreated), '%Y-%m-%d %H:%i') AS "Formato padrão ISO com tempo em 24 horas"
FROM
    prefix_logstore_standard_log l
WHERE
    l.id = 1
</syntaxhighlight>
Veja a [https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_date-format documentação do MySQL para todas as opções possíveis de formatação de data].
'''Nota''': para “timestamps” que são 0, isso às vezes representa "Nunca” ou “Sem data” para alguns campos no Moodle. Por exemplo, se o primeiro login de um usuário for 0, isso significa que ele nunca fez login, não que seu primeiro login tenha sido em 1º de janeiro de 1970 (já que o Moodle ainda não existia). Se você deseja distinguir essas datas em sua saída, pode usar uma condicional para testar o 0, por exemplo:
<syntaxhighlight lang="sql">
IF(
    u.firstaccess = 0,
    "Nunca",
    DATE_FORMAT(FROM_UNIXTIME(u.firstaccess), '%d-%m-%Y %H:%i')
) AS "Primeiro acesso",
</syntaxhighlight>
Isso mostrará “Nunca” quando o “timestamp” for igual a “0” ou a data formatada corretamente, permitindo que você classifique a coluna facilmente.
====Sugestões para Colaboradores====
As sugestões a seguir tornariam suas contribuições mais úteis.
===== Controle de versão =====
* Apenas poste consultas verificadas que funcionem em [https://moodledev.io/general/releases versões suportadas pelo Moodle];
* Por favor, não poste versões de consultas para versões antigas e fora de suporte;
* Identifique no início de cada consulta o banco de dados compatível (PostgreSQL, MariaDB, etc.); e
* Por favor, remova qualquer consulta que você escreveu que não funcione mais na versão atual do Moodle: especialmente em casos de alterações estruturais em tabelas que tornam consultas antigas quebradas (por exemplo, alterações nos Logs em 2.7, Mensagens em 3.6, etc.)
===== Adicionando anotações para outros =====
* Se você estiver usando uma linguagem SQL diferente do MySQL, sinalize quaisquer funções específicas ou outras peculiaridades presentes;
* Considere colocar seu nome e/ou um link como “Contribuído por”;
* Considere adicionar uma breve descrição do que seu relatório faz e sua saída esperada;
* Se o código incluir plugins de terceiros, informe especificamente qual em sua descrição;
* Se o seu código incluir qualquer nome de domínio, curso ou nome de usuário, ou outras referências específicas ao seu site, considere torná-los mais genéricos;
* Por favor, use o português, pois esta é a versão em português da documentação do Moodle; e
* Se houver um debate ou outras informações sobre a consulta de uma discussão no fórum, sinta-se à vontade para adicionar um link referenciando a discussão.
===== Aliases de tabela comumente usados =====
Ao longo dos anos, surgiram algumas convenções comuns para ''aliases'' de tabelas. Você os verá usados frequentemente nesses relatórios.
Se você usar esses ''aliases'', suas consultas serão mais fáceis de serem lidas por outras pessoas. Mas é claro, qualquer pseudônimo legal pode ser usado.
* c = prefix_course
* cat ou cc = prefix_course_categories
* cm = prefix_course_modules
* ctx or con = prefix_context
* e = prefix_enrol
* f = prefix_files
* l ou log = prefix_logstore_standard_log
* m = prefix_module
* r = prefix_role
* ra = prefix_role_assignments
* u = prefix_user
* ue = prefix_user_enrolments
== Relatórios ==
== Relatórios ==
=== Relatórios Administrativos ===
=== Relatórios Administrativos ===
==== Acessos únicos ao Moodle ====
Contribuído por: [https://moodle.org/user/profile.php?id=1404379 Anderson Blaine]
Este relatório considera acesso únicos como qualquer ação de “visualização” de usuários distintos (contagem de "username”). Limitado ao período de 30 dias.
'''Observação''': O Moodle registra a ação de "loggedin”, mas esse parâmetro pode gerar inconsistências no relatório de acessos únicos por dia, uma vez que dependendo da configuração do Moodle o usuário pode permanecer autenticado por mais de um dia (no caso do Moodle App o padrão é 12 semanas) e por isso foi considerado a ação de visualização e não “login”.
PostgreSQL
<syntaxhighlight lang="sql">
SELECT
    TO_CHAR(TO_TIMESTAMP(l.timecreated), 'DD/MM/YYYY') AS "Data",
    COUNT (DISTINCT u.username) AS "Acessos"
FROM
    prefix_logstore_standard_log AS l
    JOIN prefix_user AS u ON l.userid = u.id
WHERE
    l.action = 'viewed'
    AND TO_TIMESTAMP(l.timecreated) >= NOW() - interval '30 days'
GROUP BY
    "Data"
</syntaxhighlight>
==== Acessos únicos ao Moodle com dados de campo de perfil do usuário ====
Contribuído por: [https://moodle.org/user/profile.php?id=1404379 Anderson Blaine]
Este relatório considera acesso únicos como qualquer ação de “visualização” de usuários distintos (contagem de "username”). Limitado ao período de 30 dias.
No exemplo abaixo foi adicionado uma "subquery” para adicionar um campo de perfil personalizado de nome “Polo”. Você pode alterar para qualquer valor de “shortname” (nome curto do campo).
PostgreSQL
<syntaxhighlight lang="sql">
SELECT
    TO_CHAR(TO_TIMESTAMP(l.timecreated), 'DD/MM/YYYY') AS "Data",
    (
        SELECT
            d1.data
        FROM
            prefix_user_info_data AS d1
            JOIN prefix_user_info_field AS f1 ON d1.fieldid = f1.id
            AND f1.shortname = 'polo'
        WHERE
            d1.userid = u.id
    ) AS "Polo",
    COUNT (DISTINCT u.username) AS "Acessos"
FROM
    prefix_logstore_standard_log AS l
    JOIN prefix_user AS u ON l.userid = u.id
WHERE
    l.action = 'viewed'
    AND TO_TIMESTAMP(l.timecreated) >= NOW() - interval '30 days'
GROUP BY
    "Data",
    "Pólo"
</syntaxhighlight>
'''Dica''': A pesquisa por texto é sempre mais lenta que a pesquisa por “chave primária”, logo, recomendo trocar o “''shortname''” pelo ”''id''” da tabela “''prefix_user_info_field''”. No exemplo acima, a variável F1.SHORTNAME = 'polo' uma vez otimizada seria 'F1.ID = '1'. O número “1” corresponde a chave primária do campo de perfil “Polo”, para saber qual chave utilizar você deve checar a tabela "''prefix_user_info_field''”.
==== Erros de acesso, motivo: usuário inexistente ====
==== Erros de acesso, motivo: usuário inexistente ====
O Moodle registra em seu log as tentativas de acesso malsucedidas e este relatório lista, dentro do intervalo de 30 dias, os acessos não concluídos devido a inexistência de cadastro ou erro de preenchimento dos dados de login.
Contribuído por: [https://moodle.org/user/profile.php?id=1404379 Anderson Blaine]
 
O Moodle registra em seu log as tentativas de acesso malsucedidas e este relatório lista, no intervalo de 30 dias, os acessos não concluídos devido à inexistência de cadastro ou erro de preenchimento dos dados de login.


PostgreSQL
PostgreSQL
<syntaxhighlight lang="sql">
<syntaxhighlight lang="sql">
SELECT JSON_EXTRACT_PATH_TEXT(OTHER :: JSON,'username') AS USERNAME,
SELECT
TO_CHAR(TO_TIMESTAMP(L.TIMECREATED),'DD/MM/YYYY HH:MM') AS DATA
    JSON_EXTRACT_PATH_TEXT(OTHER :: JSON, 'username') AS "username",
FROM PREFIX_LOGSTORE_STANDARD_LOG AS L
    TO_CHAR(TO_TIMESTAMP(l.timecreated), 'DD/MM/YYYY HH:MM') AS "Data"
WHERE L.ACTION = 'failed'
FROM
AND L.TARGET = 'user_login'
    prefix_logstore_standard_log AS l
AND L.OTHER like '%reason":1%'
WHERE
AND TO_TIMESTAMP(L.TIMECREATED) >= NOW() - interval '30 days'
    l.action = 'failed'
ORDER BY TIMECREATED DESC
    AND l.target = 'user_login'
    AND l.other like '%reason":1%'
    AND TO_TIMESTAMP(l.timecreated) >= NOW() - interval '30 days'
ORDER BY
    l.timecreated DESC
</syntaxhighlight>
</syntaxhighlight>
==== Erros de acesso, motivo: senha inválida ====
==== Erros de acesso, motivo: senha inválida ====
Este relatório lista os usuários válidos, que nunca acessaram a plataforma devido a senha inválida. No início de semestre ou cadastro recém realizado, para ações proativas, o suporte poderá agir entrando em contato com o usuário.
Contribuído por: [https://moodle.org/user/profile.php?id=1404379 Anderson Blaine]
 
Este relatório lista os usuários válidos, que nunca acessaram a plataforma devido à senha inválida. No início de semestre ou cadastro recém-realizado, para ações proativas, o suporte poderá agir entrando em contato com o usuário.


PostgreSQL
PostgreSQL
<syntaxhighlight lang="sql">
<syntaxhighlight lang="sql">
SELECT DISTINCT U.USERNAME AS "Usuário", CONCAT (U.FIRSTNAME,' ',U.LASTNAME) AS "Nome completo",
SELECT
CONCAT('<a target="_new" href="%%WWWROOT%%/user/profile.php?id=',L.USERID,'">Acessar Perfil</a>') AS "Perfil",
    DISTINCT u.username AS "Usuário",
L.TARGET,
    CONCAT (u.firstname, ' ', u.lastname) AS "Nome completo",
L.ACTION
    CONCAT(
FROM PREFIX_LOGSTORE_STANDARD_LOG AS L
        '<a target="_new" href="%%WWWROOT%%/user/profile.php?id=',
JOIN PREFIX_USER AS U ON U.ID = L.USERID
        l.userid,
WHERE L.ACTION LIKE 'failed'
        '">Acessar Perfil</a>'
AND L.TARGET LIKE 'user_login'
    ) AS "Perfil",
AND TO_TIMESTAMP(L.TIMECREATED) >= NOW() - interval '7 days'
    l.target,
AND U.LASTACCESS = 0
    l.action
FROM
    prefix_logstore_standard_log AS l
    JOIN prefix_user AS u ON u.id = l.userid
WHERE
    l.action LIKE 'failed'
    AND l.target LIKE 'user_login'
    AND TO_TIMESTAMP(l.timecreated) >= NOW() - interval '7 days'
    AND u.lastaccess = 0
</syntaxhighlight>
 
==== Inscrições vs. Conclusões mensais por curso ====
Contribuído por: [https://moodle.org/user/profile.php?id=1404379 Anderson Blaine]
 
Este relatório apresenta o total de '''estudantes''' inscritos e concluintes por mês, agrupados por curso.
 
'''Observações:''' Cursos com duração superior a 30 dias podem ter o número de conclusões superior ao número de inscritos no mês. Exemplo: Janeiro ocorreram 100 inscrições, em fevereiro outras 20 inscrições. Mas o número de conclusões de curso em fevereiro foi de 60 (soma das conclusões, independente da data de inscrição). Portanto, os dados podem flutuar conforme a movimentação natural de inscrições e conclusões.
 
PostgreSQL<syntaxhighlight lang="sql">
SELECT
CONCAT('<a target="_new" href="%%WWWROOT%%/course/view.php?id=', c.id, '">', c.shortname, '</a>') AS "Curso",
count(DISTINCT ra.id) AS "Inscrições",
count(DISTINCT cc.timecompleted) AS "Conclusões",
to_char(date_trunc('month', to_timestamp(ra.timemodified)), 'MM/YYYY') AS "Data"
FROM
prefix_course c
JOIN prefix_context cx ON
c.id = cx.instanceid
JOIN prefix_role_assignments ra ON
cx.id = ra.contextid
JOIN prefix_user u ON
ra.userid = u.id
JOIN prefix_role r ON
ra.roleid = r.id
LEFT JOIN prefix_course_completions cc ON
c.id = cc.course
AND ra.userid = cc.userid
WHERE
cx.contextlevel = 50
AND r.shortname = 'student'
GROUP BY
"Curso",
"Data"
ORDER BY
"Curso",
"Data"
</syntaxhighlight>
</syntaxhighlight>


==== Usuários criados: Contagem por autor do cadastro ====
==== Usuários criados: Contagem por autor do cadastro ====
Contribuído por: [https://moodle.org/user/profile.php?id=1404379 Anderson Blaine]
Este relatório lista todos os autores de novos cadastros de usuários no site e realiza a contagem de quantos foram criados por autor.
PostgreSQL/MariaDB
<syntaxhighlight lang="sql">
SELECT
    CONCAT(u2.firstname, ' ', u2.lastname) AS "Autor dos cadastros",
    COUNT(DISTINCT u.username) AS "Usuários criados"
FROM
    prefix_logstore_standard_log AS l
    JOIN prefix_user AS u ON u.id = l.relateduserid
    JOIN prefix_user AS u2 ON u2.id = l.userid
WHERE
    l.action = 'created'
    AND l.target = 'user'
GROUP BY
    "Autor dos cadastros"
ORDER BY
    "Usuários criados" DESC
</syntaxhighlight>
==== Usuários criados: Últimos 30 dias ====
Contribuído por: [https://moodle.org/user/profile.php?id=1404379 Anderson Blaine]
Lista os usuários criados nos últimos 30 dias, o autor do cadastro e a origem da solicitação. A “origem” é uma coluna útil para verificar de onde partiu o cadastro, por exemplo, pela “web” quando um usuário administrador cadastrou pela administração do site ou “ws” para ''webservice'', sinalizando que o cadastro pode ter sido realizado pela integração de um software de terceiros ao Moodle.
PostgreSQL<syntaxhighlight lang="sql">
SELECT
    u.username AS "Usuário",
    CONCAT(u.firstname, ' ', u.lastname) AS "Nome completo",
    CONCAT(u2.firstname, ' ', u2.lastname) AS "Autor do cadastro",
    l.origin AS "Origem",
    TO_CHAR(TO_TIMESTAMP(l.timecreated), 'DD/MM/YYYY HH:MM') AS "Data de criação"
FROM
    prefix_logstore_standard_log AS L
    JOIN prefix_user AS u ON u.id = l.relateduserid
    JOIN prefix_user AS u2 ON u2.id = l.userid
WHERE
    l.action = 'created'
    AND l.target = 'user'
    AND TO_TIMESTAMP(l.timecreated) >= NOW() - interval '30 days'
ORDER BY
    l.timecreated DESC
</syntaxhighlight>
=== Relatórios de Curso ===
==== Funil de conclusão de curso / Relatório de evasão ====
Contribuído por: [https://moodle.org/user/profile.php?id=1404379 Anderson Blaine]
Este relatório de curso apresenta um funil de ações esperadas até a conclusão efetiva do curso pelo aluno, respondendo as seguintes questões:
* O aluno acessou o curso?
* A quanto tempo ele não acessa o curso?
* Quantas atividades com acompanhamento de conclusão foram finalizadas?
* Qual a nota atual no curso?
* Qual a data de conclusão?
'''Observação:''' Outros papéis como professor, moderador, etc. foram intencionalmente adicionados neste relatório. Também foram incluídos os grupos para facilitar a gestão.
PostgreSQL<syntaxhighlight lang="sql">
SELECT
    CONCAT('<a target="_new" href="%%WWWROOT%%/course/view.php?id=', c.id,'">',c.shortname,'</a>') AS "Curso",
    CONCAT('<a target="_new" href="%%WWWROOT%%/user/profile.php?id=', u.id,'&course=',c.id,'">',u.firstname, ' ', u.lastname,'</a>') AS "Perfil",
    u.username AS "Identificação de usuário",
    u.email AS "E-mail",
(
  SELECT
  ARRAY_TO_STRING(array_agg(r.shortname), ' | ')
  FROM prefix_course AS c
  JOIN prefix_context AS ctx ON c.id = ctx.instanceid
  JOIN prefix_role_assignments AS ra ON ra.contextid = ctx.id
  JOIN prefix_role AS r ON r.id = ra.roleid
  JOIN prefix_user AS u ON u.id = ra.userid
  WHERE c.id = e.courseid AND u.id = ue.userid
) AS "Papel(eis)",
(
    SELECT
        ARRAY_TO_STRING(array_agg(g.name), ' | ')
    FROM
      prefix_course AS c
  JOIN prefix_groups AS g ON g.courseid = c.id
  JOIN prefix_groups_members AS m ON g.id = m.groupid
  JOIN prefix_user AS u ON m.userid = u.id
  WHERE c.id = e.courseid AND u.id = ue.userid
) AS "Grupo(s)",
    CASE ue.status
    WHEN 0 THEN 'Ativo'
    WHEN 1 THEN 'Suspenso'
    END AS "Estado da inscrição",
    CASE ue.timestart
        WHEN 0 THEN 'N/I'
        ELSE to_char(to_timestamp(ue.timestart), 'DD/MM/YYYY')
    END AS "Inscrição começa",
    CASE
    WHEN ul.timeaccess IS NULL THEN 'Nunca acessou'
    ELSE to_char(to_timestamp(ul.timeaccess), 'DD/MM/YYYY')
    END AS "Data do último acesso",
    DATE_PART('day', CURRENT_TIMESTAMP - to_timestamp(ul.timeaccess)::TIMESTAMP) AS "Dias sem acessar",
(
SELECT
COUNT(cm.course)
FROM prefix_course_modules AS cm
WHERE cm.course = c.id AND cm.completion <> 0
) AS "Atividades c/ conclusão ativa",
(
SELECT
COUNT(cmc.coursemoduleid)
FROM prefix_course_modules AS cm
LEFT JOIN prefix_course_modules_completion AS cmc ON cmc.coursemoduleid = cm.id
WHERE cm.course = c.id AND cm.completion <> 0 AND cmc.userid = ue.userid
) AS "Atividades concluídas",
(
SELECT
    ROUND(gg.finalgrade, 2)
    FROM prefix_grade_grades AS gg
    JOIN prefix_grade_items AS gi ON gi.id = gg.itemid
    WHERE gg.userid = u.id AND gi.itemtype = 'course' AND gi.courseid = c.id
    ) AS "Nota no curso",
(
SELECT
ROUND(gi.grademax, 2)
    FROM prefix_grade_grades AS gg
    JOIN prefix_grade_items AS gi ON gi.id = gg.itemid
    WHERE gg.userid = u.id AND gi.itemtype = 'course' AND gi.courseid = c.id
) AS "Total do curso",
    CASE
    WHEN cc.timecompleted IS NULL THEN 'Não concluído'
    ELSE to_char(to_timestamp(cc.timecompleted), 'DD/MM/YYYY HH24:MI')
    END AS "Conclusão do curso"
FROM
    prefix_user_enrolments AS ue
    JOIN prefix_enrol AS e ON e.id = ue.enrolid
    JOIN prefix_course AS c ON c.id = e.courseid
    JOIN prefix_user AS u ON u.id = ue.userid
    LEFT JOIN prefix_user_lastaccess as ul ON u.id = ul.userid AND c.id = ul.courseid
    LEFT JOIN prefix_course_completions AS cc ON cc.course = c.id AND cc.userid = u.id
WHERE
    c.id = %%COURSEID%%
</syntaxhighlight>
== Módulos de Atividades ==
=== Questionário ===
==== Sobreposições de usuários e grupos em questionários ====
Contribuído por: [https://moodle.org/user/profile.php?id=1404379 Anderson Blaine] e Leandro Engler Bocon
Lista todas as sobreposições de data de início, de encerramento e tentativas permitidas na atividade Questionário para usuários e grupos.
'''Observações:''' Para determinar a ID do módulo “''Quiz''” crie um relatório simples com a ''query'' “''select * from prefix_modules''”.
MariaDB
<syntaxhighlight lang="sql">
SELECT
CONCAT('<a target="_new" href="%%WWWROOT%%/course/view.php?id=',c.id,'">',c.shortname,'</a>') AS "Curso",
    CONCAT('<a target="_new" href="%%WWWROOT%%/mod/quiz/view.php?id=',cm.id,'">',q.name,'</a>') AS "Questionário",
    IF (q.attempts = 0, "Ilimitado", q.attempts) AS "Tentativas permitidas",
    ROUND(q.grade,0) AS "Nota máxima",
    IF (q.timeopen = 0, "Não definida", DATE_FORMAT(FROM_UNIXTIME(q.timeopen), '%d/%m/%Y %H:%i')) AS "Data de abertura",
    IF (q.timeclose = 0,"Não definida", DATE_FORMAT(FROM_UNIXTIME(q.timeclose), '%d/%m/%Y %H:%i')) AS "Data de encerramento",
CASE
    WHEN (SELECT qo.groupid IS NOT NULL) THEN (SELECT CONCAT('Grupo: ','<a target="_new" href="%%WWWROOT%%/mod/quiz/overrides.php?cmid=',cm.id,'&mode=group','">',g.name, '</a>'))
    ELSE CONCAT('Aluno: ','<a target="_new" href="%%WWWROOT%%/mod/quiz/overrides.php?cmid=',cm.id,'&mode=user','">', u.firstname, ' ',u.lastname, '</a>')
    END AS "Sobreposições",
    IF (qo.attempts IS NULL, "Sem sobreposição", qo.attempts) AS "Sobreposição: Tentativas permitidas",
    IF (qo.timeopen IS NULL, "Sem sobreposição", DATE_FORMAT(FROM_UNIXTIME(qo.timeopen), '%d/%m/%Y %H:%i')) AS "Sobreposição: Data de abertura",
    IF (qo.timeclose IS NULL, "Sem sobreposição", DATE_FORMAT(FROM_UNIXTIME(qo.timeclose), '%d/%m/%Y %H:%i')) AS "Sobreposição: Data de encerramento"
FROM
  prefix_quiz AS q
  JOIN prefix_course_modules AS cm ON cm.instance = q.id AND cm.module = 32 /* id do módulo quiz na tabela prefix_modules. */
  JOIN prefix_course AS c ON q.course = c.id
  JOIN prefix_course_categories AS cat on c.category = cat.id
  JOIN prefix_quiz_overrides AS qo ON q.id = qo.quiz
  LEFT JOIN prefix_user as u ON u.id = qo.userid
  LEFT JOIN prefix_groups AS g ON g.id = qo.groupid
</syntaxhighlight>
PostgreSQL
PostgreSQL
<syntaxhighlight lang="sql">
<syntaxhighlight lang="sql">
SELECT CONCAT(U2.FIRSTNAME, ' ', U2.LASTNAME) AS "Autor dos cadastros",
SELECT
COUNT(DISTINCT U.USERNAME) AS "Usuários criados"
    CONCAT('<a target="_new" href="%%WWWROOT%%/course/view.php?id=', c.id, '">', c.shortname, '</a>') AS "Curso",
FROM PREFIX_LOGSTORE_STANDARD_LOG AS L
    CONCAT('<a target="_new" href="%%WWWROOT%%/mod/quiz/view.php?id=', cm.id, '">', q.name, '</a>') AS "Questionário",
JOIN PREFIX_USER AS U ON U.ID = L.RELATEDUSERID
    CASE
JOIN PREFIX_USER AS U2 ON U2.ID = L.USERID
        WHEN q.attempts = 0 THEN 'Ilimitado'
WHERE L.ACTION = 'created'
        ELSE q.attempts::text
AND L.TARGET = 'user'
    END AS "Tentativas permitidas",
GROUP BY "Autor dos cadastros"
    CASE
ORDER BY "Usuários criados" DESC
        WHEN q.timeopen = 0 THEN 'Não definida'
        ELSE to_char(to_timestamp(q.timeopen), 'DD/MM/YYYY HH24:MI')
    END AS "Data de abertura",
    CASE
        WHEN q.timeclose = 0 THEN 'Não definida'
        ELSE to_char(to_timestamp(q.timeclose), 'DD/MM/YYYY HH24:MI')
    END AS "Data de encerramento",
    CASE
        WHEN qo.groupid IS NOT NULL THEN CONCAT('Grupo: ', '<a target="_new" href="%%WWWROOT%%/mod/quiz/overrides.php?cmid=', cm.id, '&mode=group">', g.name, '</a>')
        WHEN qo.userid IS NOT NULL THEN CONCAT('Aluno: ', '<a target="_new" href="%%WWWROOT%%/mod/quiz/overrides.php?cmid=', cm.id, '&mode=user">', u.firstname, ' ', u.lastname, '</a>')
        ELSE 'Outro método'
    END AS "Sobreposições",
    CASE
        WHEN qo.attempts IS NULL THEN 'Sem sobreposição'
        ELSE qo.attempts::text
    END AS "Sobreposição: Tentativas permitidas",
    CASE
        WHEN qo.timeopen IS NULL THEN 'Sem sobreposição'
        ELSE to_char(to_timestamp(qo.timeopen), 'DD/MM/YYYY HH24:MI')
    END AS "Sobreposição: Data de abertura",
    CASE
        WHEN qo.timeclose IS NULL THEN 'Sem sobreposição'
        ELSE to_char(to_timestamp(qo.timeclose), 'DD/MM/YYYY HH24:MI')
    END AS "Sobreposição: Data de encerramento"
FROM
    prefix_quiz AS q
    JOIN prefix_course_modules AS cm ON cm.instance = q.id AND cm.module = 17  /* id do módulo quiz na tabela prefix_modules. */
    JOIN prefix_course AS c ON q.course = c.id
    JOIN prefix_course_categories AS cat ON c.category = cat.id
    LEFT JOIN prefix_quiz_overrides AS qo ON q.id = qo.quiz
    LEFT JOIN prefix_user AS u ON u.id = qo.userid
    LEFT JOIN prefix_groups AS g ON g.id = qo.groupid;
 
</syntaxhighlight>
</syntaxhighlight>
==== Usuários criados: Últimos 30 dias ====
 
=== Tarefa ===
 
==== Sobreposições de usuários e grupos em tarefas ====
Contribuído por: [https://moodle.org/user/profile.php?id=1404379 Anderson Blaine]
 
Lista todas as sobreposições de “Permite envios a partir de”, “Data de entrega”, “Data limite”, etc. na atividade Tarefa para usuários e grupos.
 
'''Observações:''' Para determinar a ID do módulo "''Assign''” (Tarefa) crie um relatório simples com a ''query'' “''select * from prefix_modules''”.
 
PostgreSQL<syntaxhighlight lang="sql">
PostgreSQL<syntaxhighlight lang="sql">
SELECT U.USERNAME,CONCAT(U.FIRSTNAME,' ',U.LASTNAME) AS "Usuário criado",
SELECT
CONCAT(U2.FIRSTNAME,' ',U2.LASTNAME) AS "Autor do cadastro",
    CONCAT('<a target="_new" href="%%WWWROOT%%/course/view.php?id=', c.id, '">', c.shortname, '</a>') AS "Curso",
L.ORIGIN,
    CONCAT('<a target="_new" href="%%WWWROOT%%/mod/assign/view.php?id=', cm.id, '">', asg.name, '</a>') AS "Tarefa",
TO_CHAR(TO_TIMESTAMP(L.TIMECREATED),'DD/MM/YYYY HH:MM') AS "Data de criação"
    CASE
FROM PREFIX_LOGSTORE_STANDARD_LOG AS L
        WHEN asg.allowsubmissionsfromdate = 0 THEN 'Não definida'
JOIN PREFIX_USER AS U ON U.ID = L.RELATEDUSERID
        ELSE to_char(to_timestamp(asg.allowsubmissionsfromdate), 'DD/MM/YYYY HH24:MI')
JOIN PREFIX_USER AS U2 ON U2.ID = L.USERID
    END AS "Data de abertura",
WHERE L.ACTION = 'created'
    CASE
AND L.TARGET = 'user'
        WHEN asg.duedate = 0 THEN 'Não definida'
AND TO_TIMESTAMP(L.TIMECREATED) >= NOW() - interval '30 days'
        ELSE to_char(to_timestamp(asg.duedate), 'DD/MM/YYYY HH24:MI')
ORDER BY L.TIMECREATED DESC
    END AS "Data de entrega",
    CASE
        WHEN asg.cutoffdate = 0 THEN 'Não definida'
        ELSE to_char(to_timestamp(asg.cutoffdate), 'DD/MM/YYYY HH24:MI')
    END AS "Data limite",
    CASE
        WHEN asg.submissiondrafts = 1 THEN 'Sim'
        ELSE 'Não'
    END AS "Possui trava de envio",
    CASE
        WHEN asg.attemptreopenmethod = 'none' THEN 'Nunca'
        WHEN asg.attemptreopenmethod = 'manual' THEN 'Manualmente pelo professor'
        WHEN asg.attemptreopenmethod = 'untilpass' THEN 'Automaticamente até que passe'
        ELSE 'Outro método'
    END AS "Envios adicionais",
    CASE
        WHEN asgo.groupid IS NOT NULL THEN CONCAT('Grupo: ', '<a target="_new" href="%%WWWROOT%%/mod/assign/overrides.php?cmid=', cm.id, '&mode=group">', g.name, '</a>')
        WHEN asgo.userid IS NOT NULL THEN CONCAT('Aluno: ', '<a target="_new" href="%%WWWROOT%%/mod/assign/overrides.php?cmid=', cm.id, '&mode=user">', u.firstname, ' ', u.lastname, '</a>')
        ELSE 'Sem sobreposição'
    END AS "Sobreposições",
    CASE
        WHEN asgo.allowsubmissionsfromdate IS NULL THEN 'Sem sobreposição'
        ELSE to_char(to_timestamp(asgo.allowsubmissionsfromdate), 'DD/MM/YYYY HH24:MI')
    END AS "Permite envios a partir de",
    CASE
        WHEN asgo.duedate IS NULL THEN 'Sem sobreposição'
        ELSE to_char(to_timestamp(asgo.duedate), 'DD/MM/YYYY HH24:MI')
    END AS "Data de entrega",
    CASE
        WHEN asgo.cutoffdate IS NULL THEN 'Sem sobreposição'
        ELSE to_char(to_timestamp(asgo.cutoffdate), 'DD/MM/YYYY HH24:MI')
    END AS "Data limite",
    to_char(to_timestamp(cm.added), 'DD/MM/YYYY HH24:MI') as "Atividade criada em"
FROM
    prefix_assign AS asg
    JOIN prefix_course_modules AS cm ON cm.instance = asg.id AND cm.module = 1 /* id do módulo assign na tabela prefix_modules. */
    JOIN prefix_course AS c ON asg.course = c.id
    JOIN prefix_course_categories AS cat ON c.category = cat.id
    LEFT JOIN prefix_assign_overrides AS asgo ON asg.id = asgo.assignid
    LEFT JOIN prefix_user AS u ON u.id = asgo.userid
    LEFT JOIN prefix_groups AS g ON g.id = asgo.groupid
</syntaxhighlight>
</syntaxhighlight>
== Anexos: Dicas úteis ==
== Anexos: Dicas úteis ==
[[en:ad-hoc contributed reports]]
[[es:Reportes específicos hechos por usuarios]]

Latest revision as of 21:37, 21 May 2024

Introdução

Os modelos de relatórios disponibilizados aqui foram desenvolvidos pela comunidade. Todos são livres para contribuir. Se você tiver dúvidas de trabalho que acha que seriam úteis para os membros da comunidade, compartilhe-as aqui.

Plugins de relatório

Há dois plugins de relatório comumente utilizados no Moodle para criar e exibir relatórios SQL:

Você pode instalar e usar qualquer um desses plugins de relatório em seu site Moodle. Se seu foro é mais no envio de relatórios por e-mail, então verifique primeiro o ”Ad-hoc database queries”. Se você deseja compartilhar relatórios com professores e estudantes em cursos, então veja o plugin “Configurable Reports”.

Algumas notas técnicas para usuários de relatórios

Estrutura de tabelas e schema

Se você está procurando alguma ajuda para entender as tabelas e estrutura do banco de dados, você pode encontrar nesta página um esquema de banco de dados atualizado, graças ao Marcus Green.

Nomes das tabelas e prefix_

Você verá que muitas das consultas abaixo usam "prefix_” para iniciar nomes de tabelas, por exemplo “prefix_user”. Este “prefix_” é usado antes dos nomes das tabelas quando você está usando um dos dois plugins, relatórios “Ad-hoc” ou “Configurable Reports”. Esses dois plugins substituem “prefix_” pelo prefixo real definido em seu arquivo de configuração do Moodle “config.php”. Isso ocorre porque, embora o prefixo padrão da tabela seja “mdl_”, ao instalar o Moodle, você pode definir qualquer prefixo que desejar, portanto, é personalizável.

As consultas escritas para serem usadas com qualquer um desses dois plugins devem sempre ter “prefix_” para iniciar todos os nomes de tabelas em vez do prefixo real.

Quando você usa ferramentas como PhpMyAdmin, Workbench, etc., você deve usar os nomes das tabelas reais como estão no banco de dados, incluindo o prefixo real definido para seu próprio site.

Portanto:

  • Se uma consulta abaixo tiver "prefix_” em seus nomes de tabela e você quiser usar essa consulta em uma ferramenta como phpMyAdmin, será necessário substituí-los pelo prefixo real das tabelas do seu site;
  • Se uma consulta abaixo tiver "mdl_” ou algum outro prefixo em seus nomes de tabela e você estiver usando isso em qualquer um dos dois plugins do Moodle, será necessário substituir esses prefixos explícitos por “prefix_”.

Você receberá um erro "Sem prefixo explícito” em qualquer um desses plugins se usar código com o prefixo da tabela real nele.

Sobre %%FILTER_ e variáveis similares

Você verá várias consultas com variáveis que começam e terminam com dois “%%” sinais de porcentagem, por exemplo “%%WWWROOT%%” e vários deles que começam com “%%FILTER_”. Essas variáveis são específicas para um ou ambos os plugins do Moodle. Eles não são códigos SQL padrão e não funcionarão em código feito em outras ferramentas fora desses dois plugins.

Além disso, embora esses dois plugins compartilhem parte da variável, alguns funcionam apenas em um ou outro plugin. Por exemplo, “%%WWWROOT%%” funciona em ambos; as variáveis “%%C%%”, “%%S%%” e “%%Q%%” só funcionam no plugin de consultas de banco de dados Ad-hoc; e aqueles que iniciam “%%FILTER_” funcionam apenas no plugin Relatórios Configuráveis (Configurable Reports). Você receberá um erro de sintaxe se usar uma variável que o plugin não suporta.

Encontre mais detalhes na seção sobre Variáveis no final da página.

Sobre formatação de datas

As datas e horas no Moodle são armazenadas internamente no banco de dados como o timestamp Unix Epoch, sendo o número de segundos desde 1º de janeiro de 1970 às 00:00 UTC, que no banco de dados será representado como 0.

Para convertê-los em formatos “legíveis por humanos” que usamos diariamente, você precisará fazer isso em sua consulta com código. Existem vários exemplos nos relatórios nesta página: uma pesquisa nesta página por “FROM_UNIXTIME” mostrará como usar e converter as datas. Observe que quando os horários são convertidos em data padrão e os horários dos carimbos de data/hora estão no formato UTC e você pode querer convertê-los em um fuso horário local.

As funções “FROM_UNIXTIME” e “DATE_FORMAT” comuns nos relatórios abaixo são funções específicas do MySQL, que você pode usar em sites MySQL e MariaDB.

Para outra versão do SQL, você precisará editar essas consultas para funcionarem corretamente. Para PostgreSQL, consulte a página de manual em funções de formatação de tipo de dados. Há vários exemplos específicos do PostgreSQL entre as consultas abaixo.

Existem muitos formatos de data e hora usados em vários países, mas todos podem ser formatados com base no valor do registro de data e hora original. Para obter uma amostra em MySQL, você pode usar a seguinte consulta para mostrar a data de instalação do seu site Moodle em vários formatos:

SELECT
    l.timecreated AS "Timestamp",
    DATE_FORMAT(FROM_UNIXTIME(l.timecreated), '%d/%m/%Y') AS "Formato padrão no mundo",
    DATE_FORMAT(FROM_UNIXTIME(l.timecreated), '%m/%d/%Y') AS "Formato EUA",
    DATE_FORMAT(FROM_UNIXTIME(l.timecreated), '%Y-%m-%d') AS "Formato padrão ISO",
    DATE_FORMAT(FROM_UNIXTIME(l.timecreated), '%Y-%m-%d %H:%i') AS "Formato padrão ISO com tempo em 24 horas"
FROM
    prefix_logstore_standard_log l
WHERE
    l.id = 1

Veja a documentação do MySQL para todas as opções possíveis de formatação de data.

Nota: para “timestamps” que são 0, isso às vezes representa "Nunca” ou “Sem data” para alguns campos no Moodle. Por exemplo, se o primeiro login de um usuário for 0, isso significa que ele nunca fez login, não que seu primeiro login tenha sido em 1º de janeiro de 1970 (já que o Moodle ainda não existia). Se você deseja distinguir essas datas em sua saída, pode usar uma condicional para testar o 0, por exemplo:

IF(
    u.firstaccess = 0,
    "Nunca",
    DATE_FORMAT(FROM_UNIXTIME(u.firstaccess), '%d-%m-%Y %H:%i')
) AS "Primeiro acesso",

Isso mostrará “Nunca” quando o “timestamp” for igual a “0” ou a data formatada corretamente, permitindo que você classifique a coluna facilmente.

Sugestões para Colaboradores

As sugestões a seguir tornariam suas contribuições mais úteis.

Controle de versão
  • Apenas poste consultas verificadas que funcionem em versões suportadas pelo Moodle;
  • Por favor, não poste versões de consultas para versões antigas e fora de suporte;
  • Identifique no início de cada consulta o banco de dados compatível (PostgreSQL, MariaDB, etc.); e
  • Por favor, remova qualquer consulta que você escreveu que não funcione mais na versão atual do Moodle: especialmente em casos de alterações estruturais em tabelas que tornam consultas antigas quebradas (por exemplo, alterações nos Logs em 2.7, Mensagens em 3.6, etc.)
Adicionando anotações para outros
  • Se você estiver usando uma linguagem SQL diferente do MySQL, sinalize quaisquer funções específicas ou outras peculiaridades presentes;
  • Considere colocar seu nome e/ou um link como “Contribuído por”;
  • Considere adicionar uma breve descrição do que seu relatório faz e sua saída esperada;
  • Se o código incluir plugins de terceiros, informe especificamente qual em sua descrição;
  • Se o seu código incluir qualquer nome de domínio, curso ou nome de usuário, ou outras referências específicas ao seu site, considere torná-los mais genéricos;
  • Por favor, use o português, pois esta é a versão em português da documentação do Moodle; e
  • Se houver um debate ou outras informações sobre a consulta de uma discussão no fórum, sinta-se à vontade para adicionar um link referenciando a discussão.
Aliases de tabela comumente usados

Ao longo dos anos, surgiram algumas convenções comuns para aliases de tabelas. Você os verá usados frequentemente nesses relatórios.

Se você usar esses aliases, suas consultas serão mais fáceis de serem lidas por outras pessoas. Mas é claro, qualquer pseudônimo legal pode ser usado.

  • c = prefix_course
  • cat ou cc = prefix_course_categories
  • cm = prefix_course_modules
  • ctx or con = prefix_context
  • e = prefix_enrol
  • f = prefix_files
  • l ou log = prefix_logstore_standard_log
  • m = prefix_module
  • r = prefix_role
  • ra = prefix_role_assignments
  • u = prefix_user
  • ue = prefix_user_enrolments

Relatórios

Relatórios Administrativos

Acessos únicos ao Moodle

Contribuído por: Anderson Blaine

Este relatório considera acesso únicos como qualquer ação de “visualização” de usuários distintos (contagem de "username”). Limitado ao período de 30 dias.

Observação: O Moodle registra a ação de "loggedin”, mas esse parâmetro pode gerar inconsistências no relatório de acessos únicos por dia, uma vez que dependendo da configuração do Moodle o usuário pode permanecer autenticado por mais de um dia (no caso do Moodle App o padrão é 12 semanas) e por isso foi considerado a ação de visualização e não “login”.

PostgreSQL

SELECT
    TO_CHAR(TO_TIMESTAMP(l.timecreated), 'DD/MM/YYYY') AS "Data",
    COUNT (DISTINCT u.username) AS "Acessos"
FROM
    prefix_logstore_standard_log AS l
    JOIN prefix_user AS u ON l.userid = u.id
WHERE
    l.action = 'viewed'
    AND TO_TIMESTAMP(l.timecreated) >= NOW() - interval '30 days'
GROUP BY
    "Data"

Acessos únicos ao Moodle com dados de campo de perfil do usuário

Contribuído por: Anderson Blaine

Este relatório considera acesso únicos como qualquer ação de “visualização” de usuários distintos (contagem de "username”). Limitado ao período de 30 dias.

No exemplo abaixo foi adicionado uma "subquery” para adicionar um campo de perfil personalizado de nome “Polo”. Você pode alterar para qualquer valor de “shortname” (nome curto do campo).

PostgreSQL

SELECT
    TO_CHAR(TO_TIMESTAMP(l.timecreated), 'DD/MM/YYYY') AS "Data",
    (
        SELECT
            d1.data
        FROM
            prefix_user_info_data AS d1
            JOIN prefix_user_info_field AS f1 ON d1.fieldid = f1.id
            AND f1.shortname = 'polo'
        WHERE
            d1.userid = u.id
    ) AS "Polo",
    COUNT (DISTINCT u.username) AS "Acessos"
FROM
    prefix_logstore_standard_log AS l
    JOIN prefix_user AS u ON l.userid = u.id
WHERE
    l.action = 'viewed'
    AND TO_TIMESTAMP(l.timecreated) >= NOW() - interval '30 days'
GROUP BY
    "Data",
    "Pólo"

Dica: A pesquisa por texto é sempre mais lenta que a pesquisa por “chave primária”, logo, recomendo trocar o “shortname” pelo ”id” da tabela “prefix_user_info_field”. No exemplo acima, a variável F1.SHORTNAME = 'polo' uma vez otimizada seria 'F1.ID = '1'. O número “1” corresponde a chave primária do campo de perfil “Polo”, para saber qual chave utilizar você deve checar a tabela "prefix_user_info_field”.

Erros de acesso, motivo: usuário inexistente

Contribuído por: Anderson Blaine

O Moodle registra em seu log as tentativas de acesso malsucedidas e este relatório lista, no intervalo de 30 dias, os acessos não concluídos devido à inexistência de cadastro ou erro de preenchimento dos dados de login.

PostgreSQL

SELECT
    JSON_EXTRACT_PATH_TEXT(OTHER :: JSON, 'username') AS "username",
    TO_CHAR(TO_TIMESTAMP(l.timecreated), 'DD/MM/YYYY HH:MM') AS "Data"
FROM
    prefix_logstore_standard_log AS l
WHERE
    l.action = 'failed'
    AND l.target = 'user_login'
    AND l.other like '%reason":1%'
    AND TO_TIMESTAMP(l.timecreated) >= NOW() - interval '30 days'
ORDER BY
    l.timecreated DESC

Erros de acesso, motivo: senha inválida

Contribuído por: Anderson Blaine

Este relatório lista os usuários válidos, que nunca acessaram a plataforma devido à senha inválida. No início de semestre ou cadastro recém-realizado, para ações proativas, o suporte poderá agir entrando em contato com o usuário.

PostgreSQL

SELECT
    DISTINCT u.username AS "Usuário",
    CONCAT (u.firstname, ' ', u.lastname) AS "Nome completo",
    CONCAT(
        '<a target="_new" href="%%WWWROOT%%/user/profile.php?id=',
        l.userid,
        '">Acessar Perfil</a>'
    ) AS "Perfil",
    l.target,
    l.action
FROM
    prefix_logstore_standard_log AS l
    JOIN prefix_user AS u ON u.id = l.userid
WHERE
    l.action LIKE 'failed'
    AND l.target LIKE 'user_login'
    AND TO_TIMESTAMP(l.timecreated) >= NOW() - interval '7 days'
    AND u.lastaccess = 0

Inscrições vs. Conclusões mensais por curso

Contribuído por: Anderson Blaine

Este relatório apresenta o total de estudantes inscritos e concluintes por mês, agrupados por curso.

Observações: Cursos com duração superior a 30 dias podem ter o número de conclusões superior ao número de inscritos no mês. Exemplo: Janeiro ocorreram 100 inscrições, em fevereiro outras 20 inscrições. Mas o número de conclusões de curso em fevereiro foi de 60 (soma das conclusões, independente da data de inscrição). Portanto, os dados podem flutuar conforme a movimentação natural de inscrições e conclusões.

PostgreSQL

SELECT
	CONCAT('<a target="_new" href="%%WWWROOT%%/course/view.php?id=', c.id, '">', c.shortname, '</a>') AS "Curso",
	count(DISTINCT ra.id) AS "Inscrições",
	count(DISTINCT cc.timecompleted) AS "Conclusões",
	to_char(date_trunc('month', to_timestamp(ra.timemodified)), 'MM/YYYY') AS "Data"
FROM
	prefix_course c
JOIN prefix_context cx ON
	c.id = cx.instanceid
JOIN prefix_role_assignments ra ON
	cx.id = ra.contextid
JOIN prefix_user u ON
	ra.userid = u.id
JOIN prefix_role r ON
	ra.roleid = r.id
LEFT JOIN prefix_course_completions cc ON
	c.id = cc.course
	AND ra.userid = cc.userid
WHERE
	cx.contextlevel = 50
	AND r.shortname = 'student'
GROUP BY
	"Curso",
	"Data"
ORDER BY
	"Curso",
	"Data"

Usuários criados: Contagem por autor do cadastro

Contribuído por: Anderson Blaine

Este relatório lista todos os autores de novos cadastros de usuários no site e realiza a contagem de quantos foram criados por autor.

PostgreSQL/MariaDB

SELECT
    CONCAT(u2.firstname, ' ', u2.lastname) AS "Autor dos cadastros",
    COUNT(DISTINCT u.username) AS "Usuários criados"
FROM
    prefix_logstore_standard_log AS l
    JOIN prefix_user AS u ON u.id = l.relateduserid
    JOIN prefix_user AS u2 ON u2.id = l.userid
WHERE
    l.action = 'created'
    AND l.target = 'user'
GROUP BY
    "Autor dos cadastros"
ORDER BY
    "Usuários criados" DESC

Usuários criados: Últimos 30 dias

Contribuído por: Anderson Blaine

Lista os usuários criados nos últimos 30 dias, o autor do cadastro e a origem da solicitação. A “origem” é uma coluna útil para verificar de onde partiu o cadastro, por exemplo, pela “web” quando um usuário administrador cadastrou pela administração do site ou “ws” para webservice, sinalizando que o cadastro pode ter sido realizado pela integração de um software de terceiros ao Moodle.

PostgreSQL

SELECT
    u.username AS "Usuário",
    CONCAT(u.firstname, ' ', u.lastname) AS "Nome completo",
    CONCAT(u2.firstname, ' ', u2.lastname) AS "Autor do cadastro",
    l.origin AS "Origem",
    TO_CHAR(TO_TIMESTAMP(l.timecreated), 'DD/MM/YYYY HH:MM') AS "Data de criação"
FROM
    prefix_logstore_standard_log AS L
    JOIN prefix_user AS u ON u.id = l.relateduserid
    JOIN prefix_user AS u2 ON u2.id = l.userid
WHERE
    l.action = 'created'
    AND l.target = 'user'
    AND TO_TIMESTAMP(l.timecreated) >= NOW() - interval '30 days'
ORDER BY
    l.timecreated DESC

Relatórios de Curso

Funil de conclusão de curso / Relatório de evasão

Contribuído por: Anderson Blaine

Este relatório de curso apresenta um funil de ações esperadas até a conclusão efetiva do curso pelo aluno, respondendo as seguintes questões:

  • O aluno acessou o curso?
  • A quanto tempo ele não acessa o curso?
  • Quantas atividades com acompanhamento de conclusão foram finalizadas?
  • Qual a nota atual no curso?
  • Qual a data de conclusão?


Observação: Outros papéis como professor, moderador, etc. foram intencionalmente adicionados neste relatório. Também foram incluídos os grupos para facilitar a gestão.

PostgreSQL

SELECT
    CONCAT('<a target="_new" href="%%WWWROOT%%/course/view.php?id=', c.id,'">',c.shortname,'</a>') AS "Curso",
    CONCAT('<a target="_new" href="%%WWWROOT%%/user/profile.php?id=', u.id,'&course=',c.id,'">',u.firstname, ' ', u.lastname,'</a>') AS "Perfil",
    u.username AS "Identificação de usuário",
    u.email AS "E-mail",
(
   	 SELECT
   		 ARRAY_TO_STRING(array_agg(r.shortname), ' | ')
   	 	FROM prefix_course AS c
   	 	JOIN prefix_context AS ctx ON c.id = ctx.instanceid
   	 	JOIN prefix_role_assignments AS ra ON ra.contextid = ctx.id
   	 	JOIN prefix_role AS r ON r.id = ra.roleid
   	 	JOIN prefix_user AS u ON u.id = ra.userid
   	 	WHERE c.id = e.courseid AND u.id = ue.userid
) AS "Papel(eis)",
(
    SELECT
        ARRAY_TO_STRING(array_agg(g.name), ' | ')
    	FROM
       	prefix_course AS c
   	 	JOIN prefix_groups AS g ON g.courseid = c.id
   	 	JOIN prefix_groups_members AS m ON g.id = m.groupid
   	 	JOIN prefix_user AS u ON m.userid = u.id
   	 	WHERE c.id = e.courseid AND u.id = ue.userid
	) AS "Grupo(s)",
    CASE ue.status
     	WHEN 0 THEN 'Ativo'
     	WHEN 1 THEN 'Suspenso'
    END AS "Estado da inscrição",
    CASE ue.timestart
        WHEN 0 THEN 'N/I'
        ELSE to_char(to_timestamp(ue.timestart), 'DD/MM/YYYY')
    END AS "Inscrição começa",
    CASE
    	WHEN ul.timeaccess IS NULL THEN 'Nunca acessou'
    	ELSE to_char(to_timestamp(ul.timeaccess), 'DD/MM/YYYY')
    END AS "Data do último acesso",
    DATE_PART('day', CURRENT_TIMESTAMP - to_timestamp(ul.timeaccess)::TIMESTAMP) AS "Dias sem acessar",
(
	SELECT
		COUNT(cm.course)
		FROM prefix_course_modules AS cm
		WHERE cm.course = c.id AND cm.completion <> 0
	) AS "Atividades c/ conclusão ativa",
(
	SELECT
		COUNT(cmc.coursemoduleid)
		FROM prefix_course_modules AS cm
		LEFT JOIN prefix_course_modules_completion AS cmc ON cmc.coursemoduleid = cm.id
		WHERE cm.course = c.id AND cm.completion <> 0 AND cmc.userid = ue.userid
	) AS "Atividades concluídas",
(
	SELECT 
    	ROUND(gg.finalgrade, 2) 
    	FROM prefix_grade_grades AS gg
    	JOIN prefix_grade_items AS gi ON gi.id = gg.itemid
    	WHERE gg.userid = u.id AND gi.itemtype = 'course' AND gi.courseid = c.id
    ) AS "Nota no curso",
(
	SELECT
		ROUND(gi.grademax, 2)
    	FROM prefix_grade_grades AS gg
    	JOIN prefix_grade_items AS gi ON gi.id = gg.itemid
    	WHERE gg.userid = u.id AND gi.itemtype = 'course' AND gi.courseid = c.id
	 ) AS "Total do curso",
     CASE
     	WHEN cc.timecompleted IS NULL THEN 'Não concluído'
     	ELSE to_char(to_timestamp(cc.timecompleted), 'DD/MM/YYYY HH24:MI')
     END AS "Conclusão do curso"
FROM
    prefix_user_enrolments AS ue
    JOIN prefix_enrol AS e ON e.id = ue.enrolid
    JOIN prefix_course AS c ON c.id = e.courseid
    JOIN prefix_user AS u ON u.id = ue.userid
    LEFT JOIN prefix_user_lastaccess as ul ON u.id = ul.userid AND c.id = ul.courseid
    LEFT JOIN prefix_course_completions AS cc ON cc.course = c.id AND cc.userid = u.id
WHERE
    c.id = %%COURSEID%%

Módulos de Atividades

Questionário

Sobreposições de usuários e grupos em questionários

Contribuído por: Anderson Blaine e Leandro Engler Bocon

Lista todas as sobreposições de data de início, de encerramento e tentativas permitidas na atividade Questionário para usuários e grupos.

Observações: Para determinar a ID do módulo “Quiz” crie um relatório simples com a queryselect * from prefix_modules”.

MariaDB

SELECT
	CONCAT('<a target="_new" href="%%WWWROOT%%/course/view.php?id=',c.id,'">',c.shortname,'</a>') AS "Curso",
    CONCAT('<a target="_new" href="%%WWWROOT%%/mod/quiz/view.php?id=',cm.id,'">',q.name,'</a>') AS "Questionário",
    IF (q.attempts = 0, "Ilimitado", q.attempts) AS "Tentativas permitidas",
    ROUND(q.grade,0) AS "Nota máxima",
    IF (q.timeopen = 0, "Não definida", DATE_FORMAT(FROM_UNIXTIME(q.timeopen), '%d/%m/%Y %H:%i')) AS "Data de abertura",
    IF (q.timeclose = 0,"Não definida", DATE_FORMAT(FROM_UNIXTIME(q.timeclose), '%d/%m/%Y %H:%i')) AS "Data de encerramento",
	CASE
    	WHEN (SELECT qo.groupid IS NOT NULL) THEN (SELECT CONCAT('Grupo: ','<a target="_new" href="%%WWWROOT%%/mod/quiz/overrides.php?cmid=',cm.id,'&mode=group','">',g.name, '</a>'))
    	ELSE CONCAT('Aluno: ','<a target="_new" href="%%WWWROOT%%/mod/quiz/overrides.php?cmid=',cm.id,'&mode=user','">', u.firstname, ' ',u.lastname, '</a>')
    END AS "Sobreposições",
    IF (qo.attempts IS NULL, "Sem sobreposição", qo.attempts) AS "Sobreposição: Tentativas permitidas",
    IF (qo.timeopen IS NULL, "Sem sobreposição", DATE_FORMAT(FROM_UNIXTIME(qo.timeopen), '%d/%m/%Y %H:%i')) AS "Sobreposição: Data de abertura",
    IF (qo.timeclose IS NULL, "Sem sobreposição", DATE_FORMAT(FROM_UNIXTIME(qo.timeclose), '%d/%m/%Y %H:%i')) AS "Sobreposição: Data de encerramento"
FROM
   prefix_quiz AS q
   JOIN prefix_course_modules AS cm ON cm.instance = q.id AND cm.module = 32 /* id do módulo quiz na tabela prefix_modules. */
   JOIN prefix_course AS c ON q.course = c.id
   JOIN prefix_course_categories AS cat on c.category = cat.id
   JOIN prefix_quiz_overrides AS qo ON q.id = qo.quiz
   LEFT JOIN prefix_user as u ON u.id = qo.userid
   LEFT JOIN prefix_groups AS g ON g.id = qo.groupid

PostgreSQL

SELECT
    CONCAT('<a target="_new" href="%%WWWROOT%%/course/view.php?id=', c.id, '">', c.shortname, '</a>') AS "Curso",
    CONCAT('<a target="_new" href="%%WWWROOT%%/mod/quiz/view.php?id=', cm.id, '">', q.name, '</a>') AS "Questionário",
    CASE
        WHEN q.attempts = 0 THEN 'Ilimitado'
        ELSE q.attempts::text
    END AS "Tentativas permitidas",
    CASE
        WHEN q.timeopen = 0 THEN 'Não definida'
        ELSE to_char(to_timestamp(q.timeopen), 'DD/MM/YYYY HH24:MI')
    END AS "Data de abertura",
    CASE
        WHEN q.timeclose = 0 THEN 'Não definida'
        ELSE to_char(to_timestamp(q.timeclose), 'DD/MM/YYYY HH24:MI')
    END AS "Data de encerramento",
    CASE
        WHEN qo.groupid IS NOT NULL THEN CONCAT('Grupo: ', '<a target="_new" href="%%WWWROOT%%/mod/quiz/overrides.php?cmid=', cm.id, '&mode=group">', g.name, '</a>')
        WHEN qo.userid IS NOT NULL THEN CONCAT('Aluno: ', '<a target="_new" href="%%WWWROOT%%/mod/quiz/overrides.php?cmid=', cm.id, '&mode=user">', u.firstname, ' ', u.lastname, '</a>')
        ELSE 'Outro método'
    END AS "Sobreposições",
    CASE
        WHEN qo.attempts IS NULL THEN 'Sem sobreposição'
        ELSE qo.attempts::text
    END AS "Sobreposição: Tentativas permitidas",
    CASE
        WHEN qo.timeopen IS NULL THEN 'Sem sobreposição'
        ELSE to_char(to_timestamp(qo.timeopen), 'DD/MM/YYYY HH24:MI')
    END AS "Sobreposição: Data de abertura",
    CASE
        WHEN qo.timeclose IS NULL THEN 'Sem sobreposição'
        ELSE to_char(to_timestamp(qo.timeclose), 'DD/MM/YYYY HH24:MI')
    END AS "Sobreposição: Data de encerramento"
FROM
    prefix_quiz AS q
    JOIN prefix_course_modules AS cm ON cm.instance = q.id AND cm.module = 17  /* id do módulo quiz na tabela prefix_modules. */
    JOIN prefix_course AS c ON q.course = c.id
    JOIN prefix_course_categories AS cat ON c.category = cat.id
    LEFT JOIN prefix_quiz_overrides AS qo ON q.id = qo.quiz
    LEFT JOIN prefix_user AS u ON u.id = qo.userid
    LEFT JOIN prefix_groups AS g ON g.id = qo.groupid;

Tarefa

Sobreposições de usuários e grupos em tarefas

Contribuído por: Anderson Blaine

Lista todas as sobreposições de “Permite envios a partir de”, “Data de entrega”, “Data limite”, etc. na atividade Tarefa para usuários e grupos.

Observações: Para determinar a ID do módulo "Assign” (Tarefa) crie um relatório simples com a queryselect * from prefix_modules”.

PostgreSQL

SELECT
    CONCAT('<a target="_new" href="%%WWWROOT%%/course/view.php?id=', c.id, '">', c.shortname, '</a>') AS "Curso",
    CONCAT('<a target="_new" href="%%WWWROOT%%/mod/assign/view.php?id=', cm.id, '">', asg.name, '</a>') AS "Tarefa",
    CASE
        WHEN asg.allowsubmissionsfromdate = 0 THEN 'Não definida'
        ELSE to_char(to_timestamp(asg.allowsubmissionsfromdate), 'DD/MM/YYYY HH24:MI')
    END AS "Data de abertura",
    CASE
        WHEN asg.duedate = 0 THEN 'Não definida'
        ELSE to_char(to_timestamp(asg.duedate), 'DD/MM/YYYY HH24:MI')
    END AS "Data de entrega",
    CASE
        WHEN asg.cutoffdate = 0 THEN 'Não definida'
        ELSE to_char(to_timestamp(asg.cutoffdate), 'DD/MM/YYYY HH24:MI')
    END AS "Data limite",
    CASE
        WHEN asg.submissiondrafts = 1 THEN 'Sim'
        ELSE 'Não'
    END AS "Possui trava de envio",
    CASE
        WHEN asg.attemptreopenmethod = 'none' THEN 'Nunca'
        WHEN asg.attemptreopenmethod = 'manual' THEN 'Manualmente pelo professor'
        WHEN asg.attemptreopenmethod = 'untilpass' THEN 'Automaticamente até que passe'
        ELSE 'Outro método'
    END AS "Envios adicionais",
    CASE
        WHEN asgo.groupid IS NOT NULL THEN CONCAT('Grupo: ', '<a target="_new" href="%%WWWROOT%%/mod/assign/overrides.php?cmid=', cm.id, '&mode=group">', g.name, '</a>')
        WHEN asgo.userid IS NOT NULL THEN CONCAT('Aluno: ', '<a target="_new" href="%%WWWROOT%%/mod/assign/overrides.php?cmid=', cm.id, '&mode=user">', u.firstname, ' ', u.lastname, '</a>')
        ELSE 'Sem sobreposição'
    END AS "Sobreposições",
    CASE
        WHEN asgo.allowsubmissionsfromdate IS NULL THEN 'Sem sobreposição'
        ELSE to_char(to_timestamp(asgo.allowsubmissionsfromdate), 'DD/MM/YYYY HH24:MI')
    END AS "Permite envios a partir de",
    CASE
        WHEN asgo.duedate IS NULL THEN 'Sem sobreposição'
        ELSE to_char(to_timestamp(asgo.duedate), 'DD/MM/YYYY HH24:MI')
    END AS "Data de entrega",
    CASE
        WHEN asgo.cutoffdate IS NULL THEN 'Sem sobreposição'
        ELSE to_char(to_timestamp(asgo.cutoffdate), 'DD/MM/YYYY HH24:MI')
    END AS "Data limite",
    to_char(to_timestamp(cm.added), 'DD/MM/YYYY HH24:MI') as "Atividade criada em"
FROM
    prefix_assign AS asg
    JOIN prefix_course_modules AS cm ON cm.instance = asg.id AND cm.module = 1 /* id do módulo assign na tabela prefix_modules. */
    JOIN prefix_course AS c ON asg.course = c.id
    JOIN prefix_course_categories AS cat ON c.category = cat.id
    LEFT JOIN prefix_assign_overrides AS asgo ON asg.id = asgo.assignid
    LEFT JOIN prefix_user AS u ON u.id = asgo.userid
    LEFT JOIN prefix_groups AS g ON g.id = asgo.groupid

Anexos: Dicas úteis