diff --git a/Makefile b/Makefile
index acc4cdf6..7f62626a 100644
--- a/Makefile
+++ b/Makefile
@@ -79,8 +79,9 @@ OBJS = swad_account.o swad_account_database.o swad_action.o swad_admin.o \
swad_report_database.o swad_role.o swad_role_database.o swad_room.o \
swad_room_database.o swad_RSS.o \
swad_scope.o swad_search.o swad_session.o swad_session_database.o \
- swad_setting.o swad_setting_database.o swad_statistic.o swad_string.o \
- swad_survey.o swad_syllabus.o swad_system_config.o \
+ swad_setting.o swad_setting_database.o swad_statistic.o \
+ swad_statistic_database.o swad_string.o swad_survey.o swad_syllabus.o \
+ swad_system_config.o \
swad_tab.o swad_tag.o swad_test.o swad_test_config.o \
swad_test_import.o swad_test_print.o swad_test_visibility.o \
swad_theme.o \
diff --git a/swad_attendance.c b/swad_attendance.c
index 14891f17..7af39f20 100644
--- a/swad_attendance.c
+++ b/swad_attendance.c
@@ -131,8 +131,8 @@ static void Att_PutLinkAttEvent (struct Att_Event *AttEvent,
const char *Class);
static void Att_PutParamsCodGrps (long AttCod);
static unsigned Att_GetNumUsrsFromAListWhoAreInAttEvent (long AttCod,
- long LstSelectedUsrCods[],
- unsigned NumUsrsInList);
+ long LstSelectedUsrCods[],
+ unsigned NumUsrsInList);
static bool Att_CheckIfUsrIsPresentInAttEvent (long AttCod,long UsrCod);
static bool Att_CheckIfUsrIsPresentInAttEventAndGetComments (long AttCod,long UsrCod,
char CommentStd[Cns_MAX_BYTES_TEXT + 1],
@@ -2085,8 +2085,8 @@ void Att_RegisterStudentsInAttEvent (void)
/*****************************************************************************/
static unsigned Att_GetNumUsrsFromAListWhoAreInAttEvent (long AttCod,
- long LstSelectedUsrCods[],
- unsigned NumUsrsInList)
+ long LstSelectedUsrCods[],
+ unsigned NumUsrsInList)
{
char *SubQueryUsrs;
unsigned NumUsrsInAttEvent;
diff --git a/swad_changelog.h b/swad_changelog.h
index 9aeb2efa..e37bb170 100644
--- a/swad_changelog.h
+++ b/swad_changelog.h
@@ -602,13 +602,14 @@ TODO: FIX BUG, URGENT! En las fechas como par
TODO: En las encuestas, que los estudiantes no puedan ver los resultados hasta que no finalice el plazo.
*/
-#define Log_PLATFORM_VERSION "SWAD 21.36 (2021-10-18)"
+#define Log_PLATFORM_VERSION "SWAD 21.37 (2021-10-19)"
#define CSS_FILE "swad20.45.css"
#define JS_FILE "swad20.69.1.js"
/*
TODO: Rename CENTRE to CENTER in help wiki.
TODO: Rename ASSESSMENT.Announcements to ASSESSMENT.Calls_for_exams
+ Version 21.37: Oct 19, 2021 New module swad_statistic_database for database queries related to statistics of accesses to platform. (320363 lines)
Version 21.36: Oct 18, 2021 New module swad_setting_database for database queries related to user's settings / preferences. (320253 lines)
Version 21.35.1: Oct 18, 2021 Queries moved to module swad_file_database. (320175 lines)
Version 21.35: Oct 18, 2021 New module swad_file_database for database queries related to files. (320171 lines)
diff --git a/swad_log_database.c b/swad_log_database.c
index 4df8e668..cd928b55 100644
--- a/swad_log_database.c
+++ b/swad_log_database.c
@@ -380,6 +380,20 @@ unsigned Log_DB_GetMyHitsPerYear (MYSQL_RES **mysql_res,
SubQueryRol);
}
+/*****************************************************************************/
+/********************* Get the comments of a hit from log ********************/
+/*****************************************************************************/
+
+void Log_DB_GetLogComments (long LogCod,char Comments[Cns_MAX_BYTES_TEXT + 1])
+ {
+ DB_QuerySELECTString (Comments,Cns_MAX_BYTES_TEXT,
+ "can not get log comments",
+ "SELECT Comments"
+ " FROM log_comments"
+ " WHERE LogCod=%ld",
+ LogCod);
+ }
+
/*****************************************************************************/
/************ Sometimes, we delete old entries in recent log table ***********/
/*****************************************************************************/
diff --git a/swad_log_database.h b/swad_log_database.h
index 63d79099..4e02d6bf 100644
--- a/swad_log_database.h
+++ b/swad_log_database.h
@@ -60,6 +60,7 @@ unsigned Log_DB_GetMyHistoricCrss (MYSQL_RES **mysql_res,
unsigned Log_DB_GetMyHitsPerYear (MYSQL_RES **mysql_res,
bool AnyCourse,long CrsCod,Rol_Role_t Role,
time_t FirstClickTimeUTC);
+void Log_DB_GetLogComments (long LogCod,char Comments[Cns_MAX_BYTES_TEXT + 1]);
void Log_DB_RemoveOldEntriesRecentLog (void);
diff --git a/swad_statistic.c b/swad_statistic.c
index d116b50a..11b2e539 100644
--- a/swad_statistic.c
+++ b/swad_statistic.c
@@ -42,9 +42,11 @@
#include "swad_HTML.h"
#include "swad_ID.h"
#include "swad_log.h"
+#include "swad_log_database.h"
#include "swad_profile.h"
#include "swad_role.h"
#include "swad_statistic.h"
+#include "swad_statistic_database.h"
/*****************************************************************************/
/************** External global variables from others modules ****************/
@@ -94,12 +96,6 @@ static const unsigned Sta_CellPadding[Sta_NUM_CLICKS_GROUPED_BY] =
/******************************* Private types *******************************/
/*****************************************************************************/
-typedef enum
- {
- Sta_SHOW_GLOBAL_ACCESSES,
- Sta_SHOW_COURSE_ACCESSES,
- } Sta_GlobalOrCourseAccesses_t;
-
/*****************************************************************************/
/***************************** Private prototypes ****************************/
/*****************************************************************************/
@@ -262,8 +258,8 @@ static void Sta_PutFormCrsHits (struct Sta_Stats *Stats)
/***** Contextual menu *****/
Mnu_ContextMenuBegin ();
- Sta_PutLinkToGlobalHits (); // Global hits
- Log_PutLinkToLastClicks (); // Last clicks in real time
+ Sta_PutLinkToGlobalHits (); // Global hits
+ Log_PutLinkToLastClicks (); // Last clicks in real time
Mnu_ContextMenuEnd ();
/***** Get and update type of list,
@@ -743,10 +739,6 @@ void Sta_SeeCrsAccesses (void)
/******************** Compute and show access statistics ********************/
/*****************************************************************************/
-#define Sta_MAX_BYTES_QUERY_ACCESS (1024 + (10 + ID_MAX_BYTES_USR_ID) * 5000 - 1)
-
-#define Sta_MAX_BYTES_COUNT_TYPE (256 - 1)
-
static void Sta_ShowHits (Sta_GlobalOrCourseAccesses_t GlobalOrCourse)
{
extern const char *Txt_You_must_select_one_ore_more_users;
@@ -757,24 +749,19 @@ static void Sta_ShowHits (Sta_GlobalOrCourseAccesses_t GlobalOrCourse)
extern const char *Txt_STAT_TYPE_COUNT_CAPS[Sta_NUM_COUNT_TYPES];
extern const char *Txt_Time_zone_used_in_the_calculation_of_these_statistics;
struct Sta_Stats Stats;
- char *Query = NULL;
- char QueryAux[512];
- long LengthQuery;
MYSQL_RES *mysql_res;
unsigned NumHits;
const char *LogTable;
Sta_ClicksDetailedOrGrouped_t DetailedOrGrouped = Sta_CLICKS_GROUPED;
- struct UsrData UsrDat;
char BrowserTimeZone[Dat_MAX_BYTES_TIME_ZONE + 1];
- unsigned NumUsr = 0;
- const char *Ptr;
- char StrRole[256];
- char StrQueryCountType[Sta_MAX_BYTES_COUNT_TYPE + 1];
unsigned NumDays;
bool ICanQueryWholeRange;
+ unsigned NumUsrsInList = 0;
+ long *LstSelectedUsrCods = NULL;
/***** Reset stats context *****/
Sta_ResetStats (&Stats);
+ Stats.GlobalOrCourse = GlobalOrCourse;
/***** Get initial and ending dates *****/
Dat_GetIniEndDatesFromForm ();
@@ -820,7 +807,7 @@ static void Sta_ShowHits (Sta_GlobalOrCourseAccesses_t GlobalOrCourse)
Act_NUM_ACTIONS - 1,
(unsigned long) Sta_NUM_ACTION_DEFAULT);
- switch (GlobalOrCourse)
+ switch (Stats.GlobalOrCourse)
{
case Sta_SHOW_GLOBAL_ACCESSES:
/***** Get the type of user of clicks *****/
@@ -883,14 +870,20 @@ static void Sta_ShowHits (Sta_GlobalOrCourseAccesses_t GlobalOrCourse)
/****** Get lists of selected users ******/
Usr_GetListsSelectedEncryptedUsrsCods (&Gbl.Usrs.Selected);
+ /***** Count number of valid users in list of encrypted user codes *****/
+ NumUsrsInList = Usr_CountNumUsrsInListOfSelectedEncryptedUsrCods (&Gbl.Usrs.Selected);
+
/***** Show the form again *****/
Sta_PutFormCrsHits (&Stats);
/***** Begin results section *****/
HTM_SECTION_Begin (Sta_STAT_RESULTS_SECTION_ID);
- /***** Check selection *****/
- if (!Usr_CheckIfThereAreUsrsInListOfSelectedEncryptedUsrCods (&Gbl.Usrs.Selected)) // Error: there are no users selected
+ /***** Check users' selection *****/
+ if (NumUsrsInList)
+ /* Get list of user codes from encrypted user codes */
+ Usr_GetListSelectedUsrCods (&Gbl.Usrs.Selected,NumUsrsInList,&LstSelectedUsrCods);
+ else // There are no users selected
{
/* Write warning message, clean and abort */
Ale_ShowAlert (Ale_WARNING,Txt_You_must_select_one_ore_more_users);
@@ -902,7 +895,7 @@ static void Sta_ShowHits (Sta_GlobalOrCourseAccesses_t GlobalOrCourse)
/***** Check if range of dates is forbidden for me *****/
NumDays = Dat_GetNumDaysBetweenDates (&Gbl.DateRange.DateIni.Date,&Gbl.DateRange.DateEnd.Date);
- ICanQueryWholeRange = (Gbl.Usrs.Me.Role.Logged >= Rol_TCH && GlobalOrCourse == Sta_SHOW_COURSE_ACCESSES) ||
+ ICanQueryWholeRange = (Gbl.Usrs.Me.Role.Logged >= Rol_TCH && Stats.GlobalOrCourse == Sta_SHOW_COURSE_ACCESSES) ||
(Gbl.Usrs.Me.Role.Logged == Rol_TCH && Gbl.Scope.Current == HieLvl_CRS) ||
(Gbl.Usrs.Me.Role.Logged == Rol_DEG_ADM && (Gbl.Scope.Current == HieLvl_DEG ||
Gbl.Scope.Current == HieLvl_CRS)) ||
@@ -922,516 +915,9 @@ static void Sta_ShowHits (Sta_GlobalOrCourseAccesses_t GlobalOrCourse)
return;
}
- /***** Query depending on the type of count *****/
- switch (Stats.CountType)
- {
- case Sta_TOTAL_CLICKS:
- Str_Copy (StrQueryCountType,"COUNT(*)",sizeof (StrQueryCountType) - 1);
- break;
- case Sta_DISTINCT_USRS:
- sprintf (StrQueryCountType,"COUNT(DISTINCT(%s.UsrCod))",LogTable);
- break;
- case Sta_CLICKS_PER_USR:
- sprintf (StrQueryCountType,"COUNT(*)/GREATEST(COUNT(DISTINCT(%s.UsrCod)),1)+0.000000",LogTable);
- break;
- case Sta_GENERATION_TIME:
- sprintf (StrQueryCountType,"(AVG(%s.TimeToGenerate)/1E6)+0.000000",LogTable);
- break;
- case Sta_SEND_TIME:
- sprintf (StrQueryCountType,"(AVG(%s.TimeToSend)/1E6)+0.000000",LogTable);
- break;
- }
-
- /***** Select clicks from the table of log *****/
- /* Allocate memory for the query */
- if ((Query = malloc (Sta_MAX_BYTES_QUERY_ACCESS + 1)) == NULL)
- Err_NotEnoughMemoryExit ();
-
- /* Start the query */
- switch (Stats.ClicksGroupedBy)
- {
- case Sta_CLICKS_CRS_DETAILED_LIST:
- snprintf (Query,Sta_MAX_BYTES_QUERY_ACCESS + 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_MAX_BYTES_QUERY_ACCESS + 1,
- "SELECT SQL_NO_CACHE UsrCod,"
- "%s AS Num"
- " FROM %s",
- StrQueryCountType,
- LogTable);
- break;
- case Sta_CLICKS_CRS_PER_DAY:
- case Sta_CLICKS_GBL_PER_DAY:
- snprintf (Query,Sta_MAX_BYTES_QUERY_ACCESS + 1,
- "SELECT SQL_NO_CACHE DATE_FORMAT(CONVERT_TZ(ClickTime,@@session.time_zone,'%s'),'%%Y%%m%%d') AS Day,"
- "%s"
- " FROM %s",
- BrowserTimeZone,
- StrQueryCountType,
- LogTable);
- break;
- case Sta_CLICKS_CRS_PER_DAY_AND_HOUR:
- case Sta_CLICKS_GBL_PER_DAY_AND_HOUR:
- snprintf (Query,Sta_MAX_BYTES_QUERY_ACCESS + 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,
- StrQueryCountType,
- 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_MAX_BYTES_QUERY_ACCESS + 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,
- StrQueryCountType,
- LogTable);
- break;
- case Sta_CLICKS_CRS_PER_MONTH:
- case Sta_CLICKS_GBL_PER_MONTH:
- snprintf (Query,Sta_MAX_BYTES_QUERY_ACCESS + 1,
- "SELECT SQL_NO_CACHE DATE_FORMAT(CONVERT_TZ(ClickTime,@@session.time_zone,'%s'),'%%Y%%m') AS Month,"
- "%s"
- " FROM %s",
- BrowserTimeZone,
- StrQueryCountType,
- LogTable);
- break;
- case Sta_CLICKS_CRS_PER_YEAR:
- case Sta_CLICKS_GBL_PER_YEAR:
- snprintf (Query,Sta_MAX_BYTES_QUERY_ACCESS + 1,
- "SELECT SQL_NO_CACHE DATE_FORMAT(CONVERT_TZ(ClickTime,@@session.time_zone,'%s'),'%%Y') AS Year,"
- "%s"
- " FROM %s",
- BrowserTimeZone,
- StrQueryCountType,
- LogTable);
- break;
- case Sta_CLICKS_CRS_PER_HOUR:
- case Sta_CLICKS_GBL_PER_HOUR:
- snprintf (Query,Sta_MAX_BYTES_QUERY_ACCESS + 1,
- "SELECT SQL_NO_CACHE DATE_FORMAT(CONVERT_TZ(ClickTime,@@session.time_zone,'%s'),'%%H') AS Hour,"
- "%s"
- " FROM %s",
- BrowserTimeZone,
- StrQueryCountType,
- LogTable);
- break;
- case Sta_CLICKS_CRS_PER_MINUTE:
- case Sta_CLICKS_GBL_PER_MINUTE:
- snprintf (Query,Sta_MAX_BYTES_QUERY_ACCESS + 1,
- "SELECT SQL_NO_CACHE DATE_FORMAT(CONVERT_TZ(ClickTime,@@session.time_zone,'%s'),'%%H%%i') AS Minute,"
- "%s"
- " FROM %s",
- BrowserTimeZone,
- StrQueryCountType,
- LogTable);
- break;
- case Sta_CLICKS_CRS_PER_ACTION:
- case Sta_CLICKS_GBL_PER_ACTION:
- snprintf (Query,Sta_MAX_BYTES_QUERY_ACCESS + 1,
- "SELECT SQL_NO_CACHE ActCod,"
- "%s AS Num"
- " FROM %s",
- StrQueryCountType,
- LogTable);
- break;
- case Sta_CLICKS_GBL_PER_PLUGIN:
- snprintf (Query,Sta_MAX_BYTES_QUERY_ACCESS + 1,
- "SELECT SQL_NO_CACHE log_api.PlgCod,"
- "%s AS Num"
- " FROM %s,"
- "log_api",
- StrQueryCountType,
- LogTable);
- break;
- case Sta_CLICKS_GBL_PER_API_FUNCTION:
- snprintf (Query,Sta_MAX_BYTES_QUERY_ACCESS + 1,
- "SELECT SQL_NO_CACHE log_api.FunCod,"
- "%s AS Num"
- " FROM %s,"
- "log_api",
- StrQueryCountType,
- LogTable);
- break;
- case Sta_CLICKS_GBL_PER_BANNER:
- snprintf (Query,Sta_MAX_BYTES_QUERY_ACCESS + 1,
- "SELECT SQL_NO_CACHE log_banners.BanCod,"
- "%s AS Num"
- " FROM %s,"
- "log_banners",
- StrQueryCountType,
- LogTable);
- break;
- case Sta_CLICKS_GBL_PER_COUNTRY:
- snprintf (Query,Sta_MAX_BYTES_QUERY_ACCESS + 1,
- "SELECT SQL_NO_CACHE CtyCod,"
- "%s AS Num"
- " FROM %s",
- StrQueryCountType,
- LogTable);
- break;
- case Sta_CLICKS_GBL_PER_INSTITUTION:
- snprintf (Query,Sta_MAX_BYTES_QUERY_ACCESS + 1,
- "SELECT SQL_NO_CACHE InsCod,"
- "%s AS Num"
- " FROM %s",
- StrQueryCountType,
- LogTable);
- break;
- case Sta_CLICKS_GBL_PER_CENTER:
- snprintf (Query,Sta_MAX_BYTES_QUERY_ACCESS + 1,
- "SELECT SQL_NO_CACHE CtrCod,"
- "%s AS Num"
- " FROM %s",
- StrQueryCountType,
- LogTable);
- break;
- case Sta_CLICKS_GBL_PER_DEGREE:
- snprintf (Query,Sta_MAX_BYTES_QUERY_ACCESS + 1,
- "SELECT SQL_NO_CACHE DegCod,"
- "%s AS Num"
- " FROM %s",
- StrQueryCountType,
- LogTable);
- break;
- case Sta_CLICKS_GBL_PER_COURSE:
- snprintf (Query,Sta_MAX_BYTES_QUERY_ACCESS + 1,
- "SELECT SQL_NO_CACHE CrsCod,"
- "%s AS Num"
- " FROM %s",
- StrQueryCountType,
- LogTable);
- break;
- }
- sprintf (QueryAux," WHERE %s.ClickTime"
- " BETWEEN FROM_UNIXTIME(%ld)"
- " AND FROM_UNIXTIME(%ld)",
- LogTable,
- (long) Gbl.DateRange.TimeUTC[Dat_STR_TIME],
- (long) Gbl.DateRange.TimeUTC[Dat_END_TIME]);
- Str_Concat (Query,QueryAux,Sta_MAX_BYTES_QUERY_ACCESS);
-
- switch (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 (QueryAux," AND %s.CtyCod=%ld",
- LogTable,Gbl.Hierarchy.Cty.CtyCod);
- Str_Concat (Query,QueryAux,Sta_MAX_BYTES_QUERY_ACCESS);
- }
- break;
- case HieLvl_INS:
- if (Gbl.Hierarchy.Ins.InsCod > 0)
- {
- sprintf (QueryAux," AND %s.InsCod=%ld",
- LogTable,Gbl.Hierarchy.Ins.InsCod);
- Str_Concat (Query,QueryAux,Sta_MAX_BYTES_QUERY_ACCESS);
- }
- break;
- case HieLvl_CTR:
- if (Gbl.Hierarchy.Ctr.CtrCod > 0)
- {
- sprintf (QueryAux," AND %s.CtrCod=%ld",
- LogTable,Gbl.Hierarchy.Ctr.CtrCod);
- Str_Concat (Query,QueryAux,Sta_MAX_BYTES_QUERY_ACCESS);
- }
- break;
- case HieLvl_DEG:
- if (Gbl.Hierarchy.Deg.DegCod > 0)
- {
- sprintf (QueryAux," AND %s.DegCod=%ld",
- LogTable,Gbl.Hierarchy.Deg.DegCod);
- Str_Concat (Query,QueryAux,Sta_MAX_BYTES_QUERY_ACCESS);
- }
- break;
- case HieLvl_CRS:
- if (Gbl.Hierarchy.Level == HieLvl_CRS)
- {
- sprintf (QueryAux," AND %s.CrsCod=%ld",
- LogTable,Gbl.Hierarchy.Crs.CrsCod);
- Str_Concat (Query,QueryAux,Sta_MAX_BYTES_QUERY_ACCESS);
- }
- break;
- }
-
- /* Type of users */
- switch (Stats.Role)
- {
- case Sta_ROLE_IDENTIFIED_USRS:
- sprintf (StrRole," 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:
- StrRole[0] = '\0';
- break;
- case Sta_DISTINCT_USRS:
- case Sta_CLICKS_PER_USR:
- sprintf (StrRole," AND %s.Role<>%u",
- LogTable,(unsigned) Rol_UNK);
- break;
- }
- break;
- case Sta_ROLE_INS_ADMINS:
- sprintf (StrRole," AND %s.Role=%u",
- LogTable,(unsigned) Rol_INS_ADM);
- break;
- case Sta_ROLE_CTR_ADMINS:
- sprintf (StrRole," AND %s.Role=%u",
- LogTable,(unsigned) Rol_CTR_ADM);
- break;
- case Sta_ROLE_DEG_ADMINS:
- sprintf (StrRole," AND %s.Role=%u",
- LogTable,(unsigned) Rol_DEG_ADM);
- break;
- case Sta_ROLE_TEACHERS:
- sprintf (StrRole," AND %s.Role=%u",
- LogTable,(unsigned) Rol_TCH);
- break;
- case Sta_ROLE_NON_EDITING_TEACHERS:
- sprintf (StrRole," AND %s.Role=%u",
- LogTable,(unsigned) Rol_NET);
- break;
- case Sta_ROLE_STUDENTS:
- sprintf (StrRole," AND %s.Role=%u",
- LogTable,(unsigned) Rol_STD);
- break;
- case Sta_ROLE_USERS:
- sprintf (StrRole," AND %s.Role=%u",
- LogTable,(unsigned) Rol_USR);
- break;
- case Sta_ROLE_GUESTS:
- sprintf (StrRole," AND %s.Role=%u",
- LogTable,(unsigned) Rol_GST);
- break;
- case Sta_ROLE_UNKNOWN_USRS:
- sprintf (StrRole," AND %s.Role=%u",
- LogTable,(unsigned) Rol_UNK);
- break;
- case Sta_ROLE_ME:
- sprintf (StrRole," AND %s.UsrCod=%ld",
- LogTable,Gbl.Usrs.Me.UsrDat.UsrCod);
- break;
- }
- Str_Concat (Query,StrRole,Sta_MAX_BYTES_QUERY_ACCESS);
-
- switch (Stats.ClicksGroupedBy)
- {
- case Sta_CLICKS_GBL_PER_PLUGIN:
- case Sta_CLICKS_GBL_PER_API_FUNCTION:
- sprintf (QueryAux," AND %s.LogCod=log_api.LogCod",
- LogTable);
- Str_Concat (Query,QueryAux,Sta_MAX_BYTES_QUERY_ACCESS);
- break;
- case Sta_CLICKS_GBL_PER_BANNER:
- sprintf (QueryAux," AND %s.LogCod=log_banners.LogCod",
- LogTable);
- Str_Concat (Query,QueryAux,Sta_MAX_BYTES_QUERY_ACCESS);
- break;
- default:
- break;
- }
- break;
- case Sta_SHOW_COURSE_ACCESSES:
- sprintf (QueryAux," AND %s.CrsCod=%ld",
- LogTable,Gbl.Hierarchy.Crs.CrsCod);
- Str_Concat (Query,QueryAux,Sta_MAX_BYTES_QUERY_ACCESS);
-
- /***** Initialize data structure of the user *****/
- Usr_UsrDataConstructor (&UsrDat);
-
- LengthQuery = strlen (Query);
- NumUsr = 0;
- Ptr = Gbl.Usrs.Selected.List[Rol_UNK];
- while (*Ptr)
- {
- Par_GetNextStrUntilSeparParamMult (&Ptr,UsrDat.EnUsrCod,
- Cry_BYTES_ENCRYPTED_STR_SHA256_BASE64);
- Usr_GetUsrCodFromEncryptedUsrCod (&UsrDat);
- if (UsrDat.UsrCod > 0)
- {
- LengthQuery = LengthQuery + 25 + 10 + 1;
- if (LengthQuery > Sta_MAX_BYTES_QUERY_ACCESS - 128)
- Err_ShowErrorAndExit ("Query is too large.");
- sprintf (QueryAux,
- NumUsr ? " OR %s.UsrCod=%ld" :
- " AND (%s.UsrCod=%ld",
- LogTable,UsrDat.UsrCod);
- Str_Concat (Query,QueryAux,Sta_MAX_BYTES_QUERY_ACCESS);
- NumUsr++;
- }
- }
- Str_Concat (Query,")",Sta_MAX_BYTES_QUERY_ACCESS);
-
- /***** Free memory used by the data of the user *****/
- Usr_UsrDataDestructor (&UsrDat);
- break;
- }
-
- /* Select action */
- if (Stats.NumAction != ActAll)
- {
- sprintf (QueryAux," AND %s.ActCod=%ld",
- LogTable,Act_GetActCod (Stats.NumAction));
- Str_Concat (Query,QueryAux,Sta_MAX_BYTES_QUERY_ACCESS);
- }
-
- /* End the query */
- switch (Stats.ClicksGroupedBy)
- {
- case Sta_CLICKS_CRS_DETAILED_LIST:
- Str_Concat (Query," ORDER BY F",
- Sta_MAX_BYTES_QUERY_ACCESS);
- break;
- case Sta_CLICKS_CRS_PER_USR:
- sprintf (QueryAux," GROUP BY %s.UsrCod"
- " ORDER BY Num DESC",
- LogTable);
- Str_Concat (Query,QueryAux,Sta_MAX_BYTES_QUERY_ACCESS);
- break;
- case Sta_CLICKS_CRS_PER_DAY:
- case Sta_CLICKS_GBL_PER_DAY:
- Str_Concat (Query," GROUP BY Day"
- " ORDER BY Day DESC",
- Sta_MAX_BYTES_QUERY_ACCESS);
- 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_MAX_BYTES_QUERY_ACCESS);
- break;
- case Sta_CLICKS_CRS_PER_WEEK:
- case Sta_CLICKS_GBL_PER_WEEK:
- Str_Concat (Query," GROUP BY Week"
- " ORDER BY Week DESC",
- Sta_MAX_BYTES_QUERY_ACCESS);
- break;
- case Sta_CLICKS_CRS_PER_MONTH:
- case Sta_CLICKS_GBL_PER_MONTH:
- Str_Concat (Query," GROUP BY Month"
- " ORDER BY Month DESC",
- Sta_MAX_BYTES_QUERY_ACCESS);
- break;
- case Sta_CLICKS_CRS_PER_YEAR:
- case Sta_CLICKS_GBL_PER_YEAR:
- Str_Concat (Query," GROUP BY Year"
- " ORDER BY Year DESC",
- Sta_MAX_BYTES_QUERY_ACCESS);
- break;
- case Sta_CLICKS_CRS_PER_HOUR:
- case Sta_CLICKS_GBL_PER_HOUR:
- Str_Concat (Query," GROUP BY Hour"
- " ORDER BY Hour",
- Sta_MAX_BYTES_QUERY_ACCESS);
- break;
- case Sta_CLICKS_CRS_PER_MINUTE:
- case Sta_CLICKS_GBL_PER_MINUTE:
- Str_Concat (Query," GROUP BY Minute"
- " ORDER BY Minute",
- Sta_MAX_BYTES_QUERY_ACCESS);
- break;
- case Sta_CLICKS_CRS_PER_ACTION:
- case Sta_CLICKS_GBL_PER_ACTION:
- sprintf (QueryAux," GROUP BY %s.ActCod"
- " ORDER BY Num DESC",
- LogTable);
- Str_Concat (Query,QueryAux,Sta_MAX_BYTES_QUERY_ACCESS);
- break;
- case Sta_CLICKS_GBL_PER_PLUGIN:
- Str_Concat (Query," GROUP BY log_api.PlgCod"
- " ORDER BY Num DESC",
- Sta_MAX_BYTES_QUERY_ACCESS);
- break;
- case Sta_CLICKS_GBL_PER_API_FUNCTION:
- Str_Concat (Query," GROUP BY log_api.FunCod"
- " ORDER BY Num DESC",
- Sta_MAX_BYTES_QUERY_ACCESS);
- break;
- case Sta_CLICKS_GBL_PER_BANNER:
- Str_Concat (Query," GROUP BY log_banners.BanCod"
- " ORDER BY Num DESC",
- Sta_MAX_BYTES_QUERY_ACCESS);
- break;
- case Sta_CLICKS_GBL_PER_COUNTRY:
- sprintf (QueryAux," GROUP BY %s.CtyCod"
- " ORDER BY Num DESC",
- LogTable);
- Str_Concat (Query,QueryAux,Sta_MAX_BYTES_QUERY_ACCESS);
- break;
- case Sta_CLICKS_GBL_PER_INSTITUTION:
- sprintf (QueryAux," GROUP BY %s.InsCod"
- " ORDER BY Num DESC",
- LogTable);
- Str_Concat (Query,QueryAux,Sta_MAX_BYTES_QUERY_ACCESS);
- break;
- case Sta_CLICKS_GBL_PER_CENTER:
- sprintf (QueryAux," GROUP BY %s.CtrCod"
- " ORDER BY Num DESC",
- LogTable);
- Str_Concat (Query,QueryAux,Sta_MAX_BYTES_QUERY_ACCESS);
- break;
- case Sta_CLICKS_GBL_PER_DEGREE:
- sprintf (QueryAux," GROUP BY %s.DegCod"
- " ORDER BY Num DESC",
- LogTable);
- Str_Concat (Query,QueryAux,Sta_MAX_BYTES_QUERY_ACCESS);
- break;
- case Sta_CLICKS_GBL_PER_COURSE:
- sprintf (QueryAux," GROUP BY %s.CrsCod"
- " ORDER BY Num DESC",
- LogTable);
- Str_Concat (Query,QueryAux,Sta_MAX_BYTES_QUERY_ACCESS);
- 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);
-
- /***** Count the number of rows in result *****/
- if (NumHits == 0)
- Ale_ShowAlert (Ale_INFO,Txt_There_are_no_accesses_with_the_selected_search_criteria);
- else
+ if ((NumHits = Sta_DB_GetHits (&mysql_res,&Stats,LogTable,BrowserTimeZone,
+ NumUsrsInList,LstSelectedUsrCods)))
{
/***** Put the table with the clicks *****/
if (Stats.ClicksGroupedBy == Sta_CLICKS_CRS_DETAILED_LIST)
@@ -1509,19 +995,26 @@ static void Sta_ShowHits (Sta_GlobalOrCourseAccesses_t GlobalOrCourse)
Sta_ShowNumHitsPerCourse (Stats.CountType,NumHits,mysql_res);
break;
}
+
HTM_TABLE_End ();
/* End box and section */
Box_BoxEnd ();
HTM_SECTION_End ();
}
+ else // No hits retrieved
+ Ale_ShowAlert (Ale_INFO,Txt_There_are_no_accesses_with_the_selected_search_criteria);
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
/***** Free memory used by list of selected users' codes *****/
- if (Gbl.Action.Act == ActSeeAccCrs)
+ if (Stats.GlobalOrCourse == Sta_SHOW_COURSE_ACCESSES)
+ {
+ if (NumUsrsInList)
+ Usr_FreeListSelectedUsrCods (LstSelectedUsrCods);
Usr_FreeListsSelectedEncryptedUsrsCods (&Gbl.Usrs.Selected);
+ }
/***** Write time zone used in the calculation of these statistics *****/
switch (Stats.ClicksGroupedBy)
@@ -1787,7 +1280,7 @@ static void Sta_ShowDetailedAccessesList (const struct Sta_Stats *Stats,
}
/*****************************************************************************/
-/******** Show a listing of with the number of clicks of each user ***********/
+/*************** Get and write the comments of a hit from log ****************/
/*****************************************************************************/
static void Sta_WriteLogComments (long LogCod)
@@ -1795,12 +1288,7 @@ static void Sta_WriteLogComments (long LogCod)
char Comments[Cns_MAX_BYTES_TEXT + 1];
/***** Get log comments from database *****/
- DB_QuerySELECTString (Comments,sizeof (Comments) - 1,
- "can not get log comments",
- "SELECT Comments"
- " FROM log_comments"
- " WHERE LogCod=%ld",
- LogCod);
+ Log_DB_GetLogComments (LogCod,Comments);
/***** Write comments *****/
if (Comments[0])
@@ -2351,7 +1839,7 @@ static void Sta_DrawBarColors (Sta_ColorType_t ColorType,double HitsMax)
HTM_Unsigned (0);
HTM_TD_End ();
- for (Interval = 1;
+ for (Interval = 1;
Interval <= 4;
Interval++)
{
@@ -2985,34 +2473,34 @@ static void Sta_ShowAverageAccessesPerMinute (unsigned NumHits,MYSQL_RES *mysql_
/***** X axis *****/
HTM_TR_Begin (NULL);
- /* First division (left) */
- HTM_TD_Begin ("class=\"LM\" style=\"width:%upx;\"",
- Sta_WIDTH_SEMIDIVISION_GRAPHIC);
- HTM_IMG (Cfg_URL_ICON_PUBLIC,"ejexizq24x1.gif",NULL,
- "style=\"display:block;width:%upx;height:1px;\"",
- Sta_WIDTH_SEMIDIVISION_GRAPHIC);
- HTM_TD_End ();
-
- /* All the intermediate divisions */
- for (i = 0;
- i < Sta_NUM_DIVISIONS_X * 2;
- i++)
- {
+ /* First division (left) */
HTM_TD_Begin ("class=\"LM\" style=\"width:%upx;\"",
Sta_WIDTH_SEMIDIVISION_GRAPHIC);
- HTM_IMG (Cfg_URL_ICON_PUBLIC,"ejex24x1.gif",NULL,
- "style=\"display:block;width:%upx;height:1px;\"",
- Sta_WIDTH_SEMIDIVISION_GRAPHIC);
+ HTM_IMG (Cfg_URL_ICON_PUBLIC,"ejexizq24x1.gif",NULL,
+ "style=\"display:block;width:%upx;height:1px;\"",
+ Sta_WIDTH_SEMIDIVISION_GRAPHIC);
HTM_TD_End ();
- }
- /* Last division (right) */
- HTM_TD_Begin ("class=\"LM\" style=\"width:%upx;\"",
- Sta_WIDTH_SEMIDIVISION_GRAPHIC);
- HTM_IMG (Cfg_URL_ICON_PUBLIC,"tr24x1.gif",NULL,
- "style=\"display:block;width:%upx;height:1px;\"",
- Sta_WIDTH_SEMIDIVISION_GRAPHIC);
- HTM_TD_End ();
+ /* All the intermediate divisions */
+ for (i = 0;
+ i < Sta_NUM_DIVISIONS_X * 2;
+ i++)
+ {
+ HTM_TD_Begin ("class=\"LM\" style=\"width:%upx;\"",
+ Sta_WIDTH_SEMIDIVISION_GRAPHIC);
+ HTM_IMG (Cfg_URL_ICON_PUBLIC,"ejex24x1.gif",NULL,
+ "style=\"display:block;width:%upx;height:1px;\"",
+ Sta_WIDTH_SEMIDIVISION_GRAPHIC);
+ HTM_TD_End ();
+ }
+
+ /* Last division (right) */
+ HTM_TD_Begin ("class=\"LM\" style=\"width:%upx;\"",
+ Sta_WIDTH_SEMIDIVISION_GRAPHIC);
+ HTM_IMG (Cfg_URL_ICON_PUBLIC,"tr24x1.gif",NULL,
+ "style=\"display:block;width:%upx;height:1px;\"",
+ Sta_WIDTH_SEMIDIVISION_GRAPHIC);
+ HTM_TD_End ();
HTM_TR_End ();
diff --git a/swad_statistic.h b/swad_statistic.h
index 22a6e3f8..062bc5d6 100644
--- a/swad_statistic.h
+++ b/swad_statistic.h
@@ -128,8 +128,15 @@ struct Sta_Hits
double Total;
};
+typedef enum
+ {
+ Sta_SHOW_GLOBAL_ACCESSES,
+ Sta_SHOW_COURSE_ACCESSES,
+ } Sta_GlobalOrCourseAccesses_t;
+
struct Sta_Stats
{
+ Sta_GlobalOrCourseAccesses_t GlobalOrCourse;
Sta_ClicksGroupedBy_t ClicksGroupedBy;
Sta_Role_t Role;
Sta_CountType_t CountType;
diff --git a/swad_statistic_database.c b/swad_statistic_database.c
new file mode 100644
index 00000000..dfdaf609
--- /dev/null
+++ b/swad_statistic_database.c
@@ -0,0 +1,576 @@
+// 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-2021 Antonio Caņ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 .
+*/
+/*****************************************************************************/
+/********************************* Headers ***********************************/
+/*****************************************************************************/
+
+#include // For NULL
+#include // For string functions
+
+#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;
+
+/*****************************************************************************/
+/***************************** Private constants *****************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/******************************* Private types *******************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/***************************** Private prototypes ****************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/******************** 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) Gbl.DateRange.TimeUTC[Dat_STR_TIME],
+ (long) Gbl.DateRange.TimeUTC[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_ShowErrorAndExit ("Query is too large.");
+ 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;
+ }
diff --git a/swad_statistic_database.h b/swad_statistic_database.h
new file mode 100644
index 00000000..97c8a173
--- /dev/null
+++ b/swad_statistic_database.h
@@ -0,0 +1,52 @@
+// swad_statistic_database.h: statistics, operations with database
+
+#ifndef _SWAD_STA_DB
+#define _SWAD_STA_DB
+/*
+ SWAD (Shared Workspace At a Distance in Spanish),
+ 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-2021 Antonio Caņ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 .
+*/
+/*****************************************************************************/
+/********************************* Headers ***********************************/
+/*****************************************************************************/
+
+#include // To access MySQL databases
+
+// #include "swad_indicator.h"
+// #include "swad_photo.h"
+// #include "swad_user.h"
+#include "swad_statistic.h"
+
+/*****************************************************************************/
+/************************** Public types and constants ***********************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/***************************** Public prototypes *****************************/
+/*****************************************************************************/
+
+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[]);
+
+#endif
diff --git a/swad_user.c b/swad_user.c
index 01b4c88d..b027217e 100644
--- a/swad_user.c
+++ b/swad_user.c
@@ -6343,7 +6343,7 @@ void Usr_CreateSubqueryUsrCods (long LstSelectedUsrCods[],
size_t MaxLength;
/***** Allocate space for subquery *****/
- MaxLength = NumUsrsInList * (1 + Cns_MAX_DECIMAL_DIGITS_LONG);
+ MaxLength = (size_t) NumUsrsInList * (size_t) (1 + Cns_MAX_DECIMAL_DIGITS_LONG);
if ((*UsrsSubQuery = malloc (MaxLength + 1)) == NULL)
Err_NotEnoughMemoryExit ();
(*UsrsSubQuery)[0] = '\0';
@@ -7221,7 +7221,7 @@ void Usr_ListAllDataStds (void)
/***** Allocate memory for the string with the list of group names where student belongs to *****/
if (Gbl.Scope.Current == HieLvl_CRS)
{
- Length = (Grp_MAX_BYTES_GROUP_NAME + 2) * Gbl.Crs.Grps.GrpTypes.NumGrpsTotal;
+ Length = (size_t) (Grp_MAX_BYTES_GROUP_NAME + 2) * (size_t) Gbl.Crs.Grps.GrpTypes.NumGrpsTotal;
if ((GroupNames = malloc (Length + 1)) == NULL)
Err_NotEnoughMemoryExit ();
}