swad-core/swad_statistic_database.c

567 lines
20 KiB
C
Raw Normal View History

// swad_statistic_database.c: statistics, operations with database
/*
SWAD (Shared Workspace At a Distance),
is a web platform developed at the University of Granada (Spain),
and used to support university teaching.
This file is part of SWAD core.
Copyright (C) 1999-2023 Antonio Ca<EFBFBD>as Vargas
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************/
/********************************* Headers ***********************************/
/*****************************************************************************/
#include <stddef.h> // For NULL
#include <stdlib.h> // For free
#include <string.h> // For string functions
#include "swad_action_list.h"
#include "swad_database.h"
#include "swad_error.h"
#include "swad_global.h"
#include "swad_ID.h"
#include "swad_statistic.h"
/*****************************************************************************/
/************** External global variables from others modules ****************/
/*****************************************************************************/
extern struct Globals Gbl;
/*****************************************************************************/
/******************** Compute and show access statistics ********************/
/*****************************************************************************/
#define Sta_DB_MAX_BYTES_QUERY (1024 + (10 + ID_MAX_BYTES_USR_ID) * 5000 - 1)
unsigned Sta_DB_GetHits (MYSQL_RES **mysql_res,
const struct Sta_Stats *Stats,
const char *LogTable,
const char BrowserTimeZone[Dat_MAX_BYTES_TIME_ZONE + 1],
unsigned NumUsrsInList,
const long LstSelectedUsrCods[])
{
char SubQueryCountType[256];
char SubQueryRole[256];
char SubQuery[512];
char *Query = NULL;
long LengthQuery;
unsigned NumUsr;
unsigned NumHits;
/***** Build subquery depending on the type of count *****/
switch (Stats->CountType)
{
case Sta_TOTAL_CLICKS:
Str_Copy (SubQueryCountType,"COUNT(*)",sizeof (SubQueryCountType) - 1);
break;
case Sta_DISTINCT_USRS:
sprintf (SubQueryCountType,"COUNT(DISTINCT %s.UsrCod)",LogTable);
break;
case Sta_CLICKS_PER_USR:
sprintf (SubQueryCountType,"COUNT(*)/GREATEST(COUNT(DISTINCT %s.UsrCod),1)+0.000000",LogTable);
break;
case Sta_GENERATION_TIME:
sprintf (SubQueryCountType,"(AVG(%s.TimeToGenerate)/1E6)+0.000000",LogTable);
break;
case Sta_SEND_TIME:
sprintf (SubQueryCountType,"(AVG(%s.TimeToSend)/1E6)+0.000000",LogTable);
break;
}
/***** Allocate memory for the query *****/
if ((Query = malloc (Sta_DB_MAX_BYTES_QUERY + 1)) == NULL)
Err_NotEnoughMemoryExit ();
/***** Begin building the query *****/
switch (Stats->ClicksGroupedBy)
{
case Sta_CLICKS_CRS_DETAILED_LIST:
snprintf (Query,Sta_DB_MAX_BYTES_QUERY + 1,
"SELECT SQL_NO_CACHE LogCod,"
"UsrCod,"
"Role,"
"UNIX_TIMESTAMP(ClickTime) AS F,"
"ActCod"
" FROM %s",
LogTable);
break;
case Sta_CLICKS_CRS_PER_USR:
snprintf (Query,Sta_DB_MAX_BYTES_QUERY + 1,
"SELECT SQL_NO_CACHE UsrCod,"
"%s AS Num"
" FROM %s",
SubQueryCountType,
LogTable);
break;
case Sta_CLICKS_CRS_PER_DAY:
case Sta_CLICKS_GBL_PER_DAY:
snprintf (Query,Sta_DB_MAX_BYTES_QUERY + 1,
"SELECT SQL_NO_CACHE DATE_FORMAT(CONVERT_TZ(ClickTime,@@session.time_zone,'%s'),'%%Y%%m%%d') AS Day,"
"%s"
" FROM %s",
BrowserTimeZone,
SubQueryCountType,
LogTable);
break;
case Sta_CLICKS_CRS_PER_DAY_AND_HOUR:
case Sta_CLICKS_GBL_PER_DAY_AND_HOUR:
snprintf (Query,Sta_DB_MAX_BYTES_QUERY + 1,
"SELECT SQL_NO_CACHE DATE_FORMAT(CONVERT_TZ(ClickTime,@@session.time_zone,'%s'),'%%Y%%m%%d') AS Day,"
"DATE_FORMAT(CONVERT_TZ(ClickTime,@@session.time_zone,'%s'),'%%H') AS Hour,"
"%s"
" FROM %s",
BrowserTimeZone,
BrowserTimeZone,
SubQueryCountType,
LogTable);
break;
case Sta_CLICKS_CRS_PER_WEEK:
case Sta_CLICKS_GBL_PER_WEEK:
/* With %x%v the weeks are counted from monday to sunday.
With %X%V the weeks are counted from sunday to saturday. */
snprintf (Query,Sta_DB_MAX_BYTES_QUERY + 1,
(Gbl.Prefs.FirstDayOfWeek == 0) ?
"SELECT SQL_NO_CACHE DATE_FORMAT(CONVERT_TZ(ClickTime,@@session.time_zone,'%s'),'%%x%%v') AS Week,"// Weeks start on monday
"%s"
" FROM %s" :
"SELECT SQL_NO_CACHE DATE_FORMAT(CONVERT_TZ(ClickTime,@@session.time_zone,'%s'),'%%X%%V') AS Week,"// Weeks start on sunday
"%s"
" FROM %s",
BrowserTimeZone,
SubQueryCountType,
LogTable);
break;
case Sta_CLICKS_CRS_PER_MONTH:
case Sta_CLICKS_GBL_PER_MONTH:
snprintf (Query,Sta_DB_MAX_BYTES_QUERY + 1,
"SELECT SQL_NO_CACHE DATE_FORMAT(CONVERT_TZ(ClickTime,@@session.time_zone,'%s'),'%%Y%%m') AS Month,"
"%s"
" FROM %s",
BrowserTimeZone,
SubQueryCountType,
LogTable);
break;
case Sta_CLICKS_CRS_PER_YEAR:
case Sta_CLICKS_GBL_PER_YEAR:
snprintf (Query,Sta_DB_MAX_BYTES_QUERY + 1,
"SELECT SQL_NO_CACHE DATE_FORMAT(CONVERT_TZ(ClickTime,@@session.time_zone,'%s'),'%%Y') AS Year,"
"%s"
" FROM %s",
BrowserTimeZone,
SubQueryCountType,
LogTable);
break;
case Sta_CLICKS_CRS_PER_HOUR:
case Sta_CLICKS_GBL_PER_HOUR:
snprintf (Query,Sta_DB_MAX_BYTES_QUERY + 1,
"SELECT SQL_NO_CACHE DATE_FORMAT(CONVERT_TZ(ClickTime,@@session.time_zone,'%s'),'%%H') AS Hour,"
"%s"
" FROM %s",
BrowserTimeZone,
SubQueryCountType,
LogTable);
break;
case Sta_CLICKS_CRS_PER_MINUTE:
case Sta_CLICKS_GBL_PER_MINUTE:
snprintf (Query,Sta_DB_MAX_BYTES_QUERY + 1,
"SELECT SQL_NO_CACHE DATE_FORMAT(CONVERT_TZ(ClickTime,@@session.time_zone,'%s'),'%%H%%i') AS Minute,"
"%s"
" FROM %s",
BrowserTimeZone,
SubQueryCountType,
LogTable);
break;
case Sta_CLICKS_CRS_PER_ACTION:
case Sta_CLICKS_GBL_PER_ACTION:
snprintf (Query,Sta_DB_MAX_BYTES_QUERY + 1,
"SELECT SQL_NO_CACHE ActCod,"
"%s AS Num"
" FROM %s",
SubQueryCountType,
LogTable);
break;
case Sta_CLICKS_GBL_PER_PLUGIN:
snprintf (Query,Sta_DB_MAX_BYTES_QUERY + 1,
"SELECT SQL_NO_CACHE log_api.PlgCod,"
"%s AS Num"
" FROM %s,"
"log_api",
SubQueryCountType,
LogTable);
break;
case Sta_CLICKS_GBL_PER_API_FUNCTION:
snprintf (Query,Sta_DB_MAX_BYTES_QUERY + 1,
"SELECT SQL_NO_CACHE log_api.FunCod,"
"%s AS Num"
" FROM %s,"
"log_api",
SubQueryCountType,
LogTable);
break;
case Sta_CLICKS_GBL_PER_BANNER:
snprintf (Query,Sta_DB_MAX_BYTES_QUERY + 1,
"SELECT SQL_NO_CACHE log_banners.BanCod,"
"%s AS Num"
" FROM %s,"
"log_banners",
SubQueryCountType,
LogTable);
break;
case Sta_CLICKS_GBL_PER_COUNTRY:
snprintf (Query,Sta_DB_MAX_BYTES_QUERY + 1,
"SELECT SQL_NO_CACHE CtyCod,"
"%s AS Num"
" FROM %s",
SubQueryCountType,
LogTable);
break;
case Sta_CLICKS_GBL_PER_INSTITUTION:
snprintf (Query,Sta_DB_MAX_BYTES_QUERY + 1,
"SELECT SQL_NO_CACHE InsCod,"
"%s AS Num"
" FROM %s",
SubQueryCountType,
LogTable);
break;
case Sta_CLICKS_GBL_PER_CENTER:
snprintf (Query,Sta_DB_MAX_BYTES_QUERY + 1,
"SELECT SQL_NO_CACHE CtrCod,"
"%s AS Num"
" FROM %s",
SubQueryCountType,
LogTable);
break;
case Sta_CLICKS_GBL_PER_DEGREE:
snprintf (Query,Sta_DB_MAX_BYTES_QUERY + 1,
"SELECT SQL_NO_CACHE DegCod,"
"%s AS Num"
" FROM %s",
SubQueryCountType,
LogTable);
break;
case Sta_CLICKS_GBL_PER_COURSE:
snprintf (Query,Sta_DB_MAX_BYTES_QUERY + 1,
"SELECT SQL_NO_CACHE CrsCod,"
"%s AS Num"
" FROM %s",
SubQueryCountType,
LogTable);
break;
}
sprintf (SubQuery," WHERE %s.ClickTime"
" BETWEEN FROM_UNIXTIME(%ld)"
" AND FROM_UNIXTIME(%ld)",
LogTable,
(long) Dat_GetRangeTimeUTC (Dat_STR_TIME),
(long) Dat_GetRangeTimeUTC (Dat_END_TIME));
Str_Concat (Query,SubQuery,Sta_DB_MAX_BYTES_QUERY);
switch (Stats->GlobalOrCourse)
{
case Sta_SHOW_GLOBAL_ACCESSES:
/* Scope */
switch (Gbl.Scope.Current)
{
case HieLvl_UNK:
case HieLvl_SYS:
break;
case HieLvl_CTY:
if (Gbl.Hierarchy.Cty.CtyCod > 0)
{
sprintf (SubQuery," AND %s.CtyCod=%ld",
LogTable,Gbl.Hierarchy.Cty.CtyCod);
Str_Concat (Query,SubQuery,Sta_DB_MAX_BYTES_QUERY);
}
break;
case HieLvl_INS:
if (Gbl.Hierarchy.Ins.InsCod > 0)
{
sprintf (SubQuery," AND %s.InsCod=%ld",
LogTable,Gbl.Hierarchy.Ins.InsCod);
Str_Concat (Query,SubQuery,Sta_DB_MAX_BYTES_QUERY);
}
break;
case HieLvl_CTR:
if (Gbl.Hierarchy.Ctr.CtrCod > 0)
{
sprintf (SubQuery," AND %s.CtrCod=%ld",
LogTable,Gbl.Hierarchy.Ctr.CtrCod);
Str_Concat (Query,SubQuery,Sta_DB_MAX_BYTES_QUERY);
}
break;
case HieLvl_DEG:
if (Gbl.Hierarchy.Deg.DegCod > 0)
{
sprintf (SubQuery," AND %s.DegCod=%ld",
LogTable,Gbl.Hierarchy.Deg.DegCod);
Str_Concat (Query,SubQuery,Sta_DB_MAX_BYTES_QUERY);
}
break;
case HieLvl_CRS:
if (Gbl.Hierarchy.Level == HieLvl_CRS)
{
sprintf (SubQuery," AND %s.CrsCod=%ld",
LogTable,Gbl.Hierarchy.Crs.CrsCod);
Str_Concat (Query,SubQuery,Sta_DB_MAX_BYTES_QUERY);
}
break;
}
/* Type of users */
switch (Stats->Role)
{
case Sta_ROLE_IDENTIFIED_USRS:
sprintf (SubQueryRole," AND %s.Role<>%u",
LogTable,(unsigned) Rol_UNK);
break;
case Sta_ROLE_ALL_USRS:
switch (Stats->CountType)
{
case Sta_TOTAL_CLICKS:
case Sta_GENERATION_TIME:
case Sta_SEND_TIME:
SubQueryRole[0] = '\0';
break;
case Sta_DISTINCT_USRS:
case Sta_CLICKS_PER_USR:
sprintf (SubQueryRole," AND %s.Role<>%u",
LogTable,(unsigned) Rol_UNK);
break;
}
break;
case Sta_ROLE_INS_ADMINS:
sprintf (SubQueryRole," AND %s.Role=%u",
LogTable,(unsigned) Rol_INS_ADM);
break;
case Sta_ROLE_CTR_ADMINS:
sprintf (SubQueryRole," AND %s.Role=%u",
LogTable,(unsigned) Rol_CTR_ADM);
break;
case Sta_ROLE_DEG_ADMINS:
sprintf (SubQueryRole," AND %s.Role=%u",
LogTable,(unsigned) Rol_DEG_ADM);
break;
case Sta_ROLE_TEACHERS:
sprintf (SubQueryRole," AND %s.Role=%u",
LogTable,(unsigned) Rol_TCH);
break;
case Sta_ROLE_NON_EDITING_TEACHERS:
sprintf (SubQueryRole," AND %s.Role=%u",
LogTable,(unsigned) Rol_NET);
break;
case Sta_ROLE_STUDENTS:
sprintf (SubQueryRole," AND %s.Role=%u",
LogTable,(unsigned) Rol_STD);
break;
case Sta_ROLE_USERS:
sprintf (SubQueryRole," AND %s.Role=%u",
LogTable,(unsigned) Rol_USR);
break;
case Sta_ROLE_GUESTS:
sprintf (SubQueryRole," AND %s.Role=%u",
LogTable,(unsigned) Rol_GST);
break;
case Sta_ROLE_UNKNOWN_USRS:
sprintf (SubQueryRole," AND %s.Role=%u",
LogTable,(unsigned) Rol_UNK);
break;
case Sta_ROLE_ME:
sprintf (SubQueryRole," AND %s.UsrCod=%ld",
LogTable,Gbl.Usrs.Me.UsrDat.UsrCod);
break;
}
Str_Concat (Query,SubQueryRole,Sta_DB_MAX_BYTES_QUERY);
switch (Stats->ClicksGroupedBy)
{
case Sta_CLICKS_GBL_PER_PLUGIN:
case Sta_CLICKS_GBL_PER_API_FUNCTION:
sprintf (SubQuery," AND %s.LogCod=log_api.LogCod",
LogTable);
Str_Concat (Query,SubQuery,Sta_DB_MAX_BYTES_QUERY);
break;
case Sta_CLICKS_GBL_PER_BANNER:
sprintf (SubQuery," AND %s.LogCod=log_banners.LogCod",
LogTable);
Str_Concat (Query,SubQuery,Sta_DB_MAX_BYTES_QUERY);
break;
default:
break;
}
break;
case Sta_SHOW_COURSE_ACCESSES:
sprintf (SubQuery," AND %s.CrsCod=%ld",
LogTable,Gbl.Hierarchy.Crs.CrsCod);
Str_Concat (Query,SubQuery,Sta_DB_MAX_BYTES_QUERY);
if (NumUsrsInList)
{
LengthQuery = strlen (Query);
for (NumUsr = 0;
NumUsr < NumUsrsInList;
NumUsr++)
{
LengthQuery = LengthQuery + 25 + 10 + 1;
if (LengthQuery > Sta_DB_MAX_BYTES_QUERY - 128)
Err_QuerySizeExceededExit ();
sprintf (SubQuery,
NumUsr ? " OR %s.UsrCod=%ld" :
" AND (%s.UsrCod=%ld",
LogTable,LstSelectedUsrCods[NumUsr]);
Str_Concat (Query,SubQuery,Sta_DB_MAX_BYTES_QUERY);
}
Str_Concat (Query,")",Sta_DB_MAX_BYTES_QUERY);
}
break;
}
/* Select action */
if (Stats->NumAction != ActAll)
{
sprintf (SubQuery," AND %s.ActCod=%ld",
LogTable,Act_GetActCod (Stats->NumAction));
Str_Concat (Query,SubQuery,Sta_DB_MAX_BYTES_QUERY);
}
/* End the query */
switch (Stats->ClicksGroupedBy)
{
case Sta_CLICKS_CRS_DETAILED_LIST:
Str_Concat (Query," ORDER BY F",
Sta_DB_MAX_BYTES_QUERY);
break;
case Sta_CLICKS_CRS_PER_USR:
sprintf (SubQuery," GROUP BY %s.UsrCod"
" ORDER BY Num DESC",
LogTable);
Str_Concat (Query,SubQuery,Sta_DB_MAX_BYTES_QUERY);
break;
case Sta_CLICKS_CRS_PER_DAY:
case Sta_CLICKS_GBL_PER_DAY:
Str_Concat (Query," GROUP BY Day"
" ORDER BY Day DESC",
Sta_DB_MAX_BYTES_QUERY);
break;
case Sta_CLICKS_CRS_PER_DAY_AND_HOUR:
case Sta_CLICKS_GBL_PER_DAY_AND_HOUR:
Str_Concat (Query," GROUP BY Day,Hour"
" ORDER BY Day DESC,Hour",
Sta_DB_MAX_BYTES_QUERY);
break;
case Sta_CLICKS_CRS_PER_WEEK:
case Sta_CLICKS_GBL_PER_WEEK:
Str_Concat (Query," GROUP BY Week"
" ORDER BY Week DESC",
Sta_DB_MAX_BYTES_QUERY);
break;
case Sta_CLICKS_CRS_PER_MONTH:
case Sta_CLICKS_GBL_PER_MONTH:
Str_Concat (Query," GROUP BY Month"
" ORDER BY Month DESC",
Sta_DB_MAX_BYTES_QUERY);
break;
case Sta_CLICKS_CRS_PER_YEAR:
case Sta_CLICKS_GBL_PER_YEAR:
Str_Concat (Query," GROUP BY Year"
" ORDER BY Year DESC",
Sta_DB_MAX_BYTES_QUERY);
break;
case Sta_CLICKS_CRS_PER_HOUR:
case Sta_CLICKS_GBL_PER_HOUR:
Str_Concat (Query," GROUP BY Hour"
" ORDER BY Hour",
Sta_DB_MAX_BYTES_QUERY);
break;
case Sta_CLICKS_CRS_PER_MINUTE:
case Sta_CLICKS_GBL_PER_MINUTE:
Str_Concat (Query," GROUP BY Minute"
" ORDER BY Minute",
Sta_DB_MAX_BYTES_QUERY);
break;
case Sta_CLICKS_CRS_PER_ACTION:
case Sta_CLICKS_GBL_PER_ACTION:
sprintf (SubQuery," GROUP BY %s.ActCod"
" ORDER BY Num DESC",
LogTable);
Str_Concat (Query,SubQuery,Sta_DB_MAX_BYTES_QUERY);
break;
case Sta_CLICKS_GBL_PER_PLUGIN:
Str_Concat (Query," GROUP BY log_api.PlgCod"
" ORDER BY Num DESC",
Sta_DB_MAX_BYTES_QUERY);
break;
case Sta_CLICKS_GBL_PER_API_FUNCTION:
Str_Concat (Query," GROUP BY log_api.FunCod"
" ORDER BY Num DESC",
Sta_DB_MAX_BYTES_QUERY);
break;
case Sta_CLICKS_GBL_PER_BANNER:
Str_Concat (Query," GROUP BY log_banners.BanCod"
" ORDER BY Num DESC",
Sta_DB_MAX_BYTES_QUERY);
break;
case Sta_CLICKS_GBL_PER_COUNTRY:
sprintf (SubQuery," GROUP BY %s.CtyCod"
" ORDER BY Num DESC",
LogTable);
Str_Concat (Query,SubQuery,Sta_DB_MAX_BYTES_QUERY);
break;
case Sta_CLICKS_GBL_PER_INSTITUTION:
sprintf (SubQuery," GROUP BY %s.InsCod"
" ORDER BY Num DESC",
LogTable);
Str_Concat (Query,SubQuery,Sta_DB_MAX_BYTES_QUERY);
break;
case Sta_CLICKS_GBL_PER_CENTER:
sprintf (SubQuery," GROUP BY %s.CtrCod"
" ORDER BY Num DESC",
LogTable);
Str_Concat (Query,SubQuery,Sta_DB_MAX_BYTES_QUERY);
break;
case Sta_CLICKS_GBL_PER_DEGREE:
sprintf (SubQuery," GROUP BY %s.DegCod"
" ORDER BY Num DESC",
LogTable);
Str_Concat (Query,SubQuery,Sta_DB_MAX_BYTES_QUERY);
break;
case Sta_CLICKS_GBL_PER_COURSE:
sprintf (SubQuery," GROUP BY %s.CrsCod"
" ORDER BY Num DESC",
LogTable);
Str_Concat (Query,SubQuery,Sta_DB_MAX_BYTES_QUERY);
break;
}
/***** Write query for debug *****/
/*
if (Gbl.Usrs.Me.Role.Logged == Rol_SYS_ADM)
Ale_ShowAlert (Ale_INFO,Query);
*/
/***** Make the query *****/
NumHits = (unsigned)
DB_QuerySELECT (mysql_res,"can not get clicks",
"%s",
Query);
/* Free memory for the query */
free (Query);
return NumHits;
}