// swad_statistic.c: statistics
/*
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-2019 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 log10, floor, ceil, modf, sqrt...
#include // For getenv, malloc
#include // For string functions
#include "swad_box.h"
#include "swad_database.h"
#include "swad_form.h"
#include "swad_global.h"
#include "swad_HTML.h"
#include "swad_ID.h"
#include "swad_profile.h"
#include "swad_role.h"
#include "swad_statistic.h"
/*****************************************************************************/
/************** External global variables from others modules ****************/
/*****************************************************************************/
extern struct Globals Gbl;
/*****************************************************************************/
/***************************** Private constants *****************************/
/*****************************************************************************/
#define Sta_SECONDS_IN_RECENT_LOG ((time_t) (Cfg_DAYS_IN_RECENT_LOG * 24UL * 60UL * 60UL)) // Remove entries in recent log oldest than this time
const unsigned Sta_CellPadding[Sta_NUM_CLICKS_GROUPED_BY] =
{
2, // Sta_CLICKS_CRS_DETAILED_LIST
1, // Sta_CLICKS_CRS_PER_USR
1, // Sta_CLICKS_CRS_PER_DAY
0, // Sta_CLICKS_CRS_PER_DAY_AND_HOUR
1, // Sta_CLICKS_CRS_PER_WEEK
1, // Sta_CLICKS_CRS_PER_MONTH
1, // Sta_CLICKS_CRS_PER_YEAR
1, // Sta_CLICKS_CRS_PER_HOUR
0, // Sta_CLICKS_CRS_PER_MINUTE
1, // Sta_CLICKS_CRS_PER_ACTION
1, // Sta_CLICKS_GBL_PER_DAY
0, // Sta_CLICKS_GBL_PER_DAY_AND_HOUR
1, // Sta_CLICKS_GBL_PER_WEEK
1, // Sta_CLICKS_GBL_PER_MONTH
1, // Sta_CLICKS_GBL_PER_YEAR
1, // Sta_CLICKS_GBL_PER_HOUR
0, // Sta_CLICKS_GBL_PER_MINUTE
1, // Sta_CLICKS_GBL_PER_ACTION
1, // Sta_CLICKS_GBL_PER_PLUGIN
1, // Sta_CLICKS_GBL_PER_API_FUNCTION
1, // Sta_CLICKS_GBL_PER_BANNER
1, // Sta_CLICKS_GBL_PER_COUNTRY
1, // Sta_CLICKS_GBL_PER_INSTITUTION
1, // Sta_CLICKS_GBL_PER_CENTRE
1, // Sta_CLICKS_GBL_PER_DEGREE
1, // Sta_CLICKS_GBL_PER_COURSE
};
#define Sta_STAT_RESULTS_SECTION_ID "stat_results"
/*****************************************************************************/
/******************************* Private types *******************************/
/*****************************************************************************/
typedef enum
{
Sta_SHOW_GLOBAL_ACCESSES,
Sta_SHOW_COURSE_ACCESSES,
} Sta_GlobalOrCourseAccesses_t;
/*****************************************************************************/
/***************************** Internal prototypes ***************************/
/*****************************************************************************/
static void Sta_PutLinkToCourseHits (void);
static void Sta_PutLinkToGlobalHits (void);
static void Sta_WriteSelectorCountType (void);
static void Sta_WriteSelectorAction (void);
static void Sta_ShowHits (Sta_GlobalOrCourseAccesses_t GlobalOrCourse);
static void Sta_ShowDetailedAccessesList (unsigned long NumRows,MYSQL_RES *mysql_res);
static void Sta_WriteLogComments (long LogCod);
static void Sta_ShowNumHitsPerUsr (unsigned long NumRows,MYSQL_RES *mysql_res);
static void Sta_ShowNumHitsPerDay (unsigned long NumRows,MYSQL_RES *mysql_res);
static void Sta_ShowDistrAccessesPerDayAndHour (unsigned long NumRows,MYSQL_RES *mysql_res);
static void Sta_PutHiddenParamScopeSta (void);
static Sta_ColorType_t Sta_GetStatColorType (void);
static void Sta_DrawBarColors (Sta_ColorType_t ColorType,float HitsMax);
static void Sta_DrawAccessesPerHourForADay (Sta_ColorType_t ColorType,float HitsNum[24],float HitsMax);
static void Sta_SetColor (Sta_ColorType_t ColorType,float HitsNum,float HitsMax,
unsigned *R,unsigned *G,unsigned *B);
static void Sta_ShowNumHitsPerWeek (unsigned long NumRows,
MYSQL_RES *mysql_res);
static void Sta_ShowNumHitsPerMonth (unsigned long NumRows,
MYSQL_RES *mysql_res);
static void Sta_ShowNumHitsPerYear (unsigned long NumRows,
MYSQL_RES *mysql_res);
static void Sta_ShowNumHitsPerHour (unsigned long NumRows,
MYSQL_RES *mysql_res);
static void Sta_WriteAccessHour (unsigned Hour,struct Sta_Hits *Hits,unsigned ColumnWidth);
static void Sta_ShowAverageAccessesPerMinute (unsigned long NumRows,MYSQL_RES *mysql_res);
static void Sta_WriteLabelsXAxisAccMin (float IncX,const char *Format);
static void Sta_WriteAccessMinute (unsigned Minute,float HitsNum,float MaxX);
static void Sta_ShowNumHitsPerAction (unsigned long NumRows,
MYSQL_RES *mysql_res);
static void Sta_ShowNumHitsPerPlugin (unsigned long NumRows,
MYSQL_RES *mysql_res);
static void Sta_ShowNumHitsPerWSFunction (unsigned long NumRows,
MYSQL_RES *mysql_res);
static void Sta_ShowNumHitsPerBanner (unsigned long NumRows,
MYSQL_RES *mysql_res);
static void Sta_ShowNumHitsPerCountry (unsigned long NumRows,
MYSQL_RES *mysql_res);
static void Sta_WriteCountry (long CtyCod);
static void Sta_ShowNumHitsPerInstitution (unsigned long NumRows,
MYSQL_RES *mysql_res);
static void Sta_WriteInstitution (long InsCod);
static void Sta_ShowNumHitsPerCentre (unsigned long NumRows,
MYSQL_RES *mysql_res);
static void Sta_WriteCentre (long CtrCod);
static void Sta_ShowNumHitsPerDegree (unsigned long NumRows,
MYSQL_RES *mysql_res);
static void Sta_WriteDegree (long DegCod);
static void Sta_ShowNumHitsPerCourse (unsigned long NumRows,
MYSQL_RES *mysql_res);
static void Sta_DrawBarNumHits (char Color,
float HitsNum,float HitsMax,float HitsTotal,
unsigned MaxBarWidth);
/*****************************************************************************/
/*************** Read CGI environment variable REMOTE_ADDR *******************/
/*****************************************************************************/
/*
CGI Environment Variables:
REMOTE_ADDR
The IP address of the remote host making the request.
*/
void Sta_GetRemoteAddr (void)
{
if (getenv ("REMOTE_ADDR"))
Str_Copy (Gbl.IP,getenv ("REMOTE_ADDR"),
Cns_MAX_BYTES_IP);
else
Gbl.IP[0] = '\0';
}
/*****************************************************************************/
/**************************** Log access in database *************************/
/*****************************************************************************/
void Sta_LogAccess (const char *Comments)
{
long LogCod;
long ActCod = Act_GetActCod (Gbl.Action.Act);
Rol_Role_t RoleToStore = (Gbl.Action.Act == ActLogOut) ? Gbl.Usrs.Me.Role.LoggedBeforeCloseSession :
Gbl.Usrs.Me.Role.Logged;
/***** Insert access into database *****/
/* Log access in historical log (log_full) */
LogCod =
DB_QueryINSERTandReturnCode ("can not log access (full)",
"INSERT INTO log_full "
"(ActCod,CtyCod,InsCod,CtrCod,DegCod,CrsCod,UsrCod,"
"Role,ClickTime,TimeToGenerate,TimeToSend,IP)"
" VALUES "
"(%ld,%ld,%ld,%ld,%ld,%ld,%ld,"
"%u,NOW(),%ld,%ld,'%s')",
ActCod,
Gbl.Hierarchy.Cty.CtyCod,
Gbl.Hierarchy.Ins.InsCod,
Gbl.Hierarchy.Ctr.CtrCod,
Gbl.Hierarchy.Deg.DegCod,
Gbl.Hierarchy.Crs.CrsCod,
Gbl.Usrs.Me.UsrDat.UsrCod,
(unsigned) RoleToStore,
Gbl.TimeGenerationInMicroseconds,
Gbl.TimeSendInMicroseconds,
Gbl.IP);
/* Log access in recent log (log_recent) */
DB_QueryINSERT ("can not log access (recent)",
"INSERT INTO log_recent "
"(LogCod,ActCod,CtyCod,InsCod,CtrCod,DegCod,CrsCod,UsrCod,"
"Role,ClickTime,TimeToGenerate,TimeToSend,IP)"
" VALUES "
"(%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,"
"%u,NOW(),%ld,%ld,'%s')",
LogCod,ActCod,
Gbl.Hierarchy.Cty.CtyCod,
Gbl.Hierarchy.Ins.InsCod,
Gbl.Hierarchy.Ctr.CtrCod,
Gbl.Hierarchy.Deg.DegCod,
Gbl.Hierarchy.Crs.CrsCod,
Gbl.Usrs.Me.UsrDat.UsrCod,
(unsigned) RoleToStore,
Gbl.TimeGenerationInMicroseconds,
Gbl.TimeSendInMicroseconds,
Gbl.IP);
/* Log comments */
if (Comments)
DB_QueryINSERT ("can not log access (comments)",
"INSERT INTO log_comments"
" (LogCod,Comments)"
" VALUES"
" (%ld,'%s')",
LogCod,Comments);
/* Log search string */
if (Gbl.Search.LogSearch && Gbl.Search.Str[0])
DB_QueryINSERT ("can not log access (search)",
"INSERT INTO log_search"
" (LogCod,SearchStr)"
" VALUES"
" (%ld,'%s')",
LogCod,Gbl.Search.Str);
if (Gbl.WebService.IsWebService)
/* Log web service plugin and function */
DB_QueryINSERT ("can not log access (comments)",
"INSERT INTO log_ws"
" (LogCod,PlgCod,FunCod)"
" VALUES"
" (%ld,%ld,%u)",
LogCod,Gbl.WebService.PlgCod,
(unsigned) Gbl.WebService.Function);
else if (Gbl.Banners.BanCodClicked > 0)
/* Log banner clicked */
DB_QueryINSERT ("can not log banner clicked",
"INSERT INTO log_banners"
" (LogCod,BanCod)"
" VALUES"
" (%ld,%ld)",
LogCod,Gbl.Banners.BanCodClicked);
/***** Increment my number of clicks *****/
if (Gbl.Usrs.Me.Logged)
Prf_IncrementNumClicksUsr (Gbl.Usrs.Me.UsrDat.UsrCod);
}
/*****************************************************************************/
/************ Sometimes, we delete old entries in recent log table ***********/
/*****************************************************************************/
void Sta_RemoveOldEntriesRecentLog (void)
{
/***** Remove all expired clipboards *****/
DB_QueryDELETE ("can not remove old entries from recent log",
"DELETE LOW_PRIORITY FROM log_recent"
" WHERE ClickTime Sta_CLICKS_CRS_PER_ACTION) &&
Gbl.Stat.ClicksGroupedBy != Sta_CLICKS_CRS_DETAILED_LIST)
Gbl.Stat.ClicksGroupedBy = Sta_CLICKS_GROUPED_BY_DEFAULT;
fprintf (Gbl.F.Out,"");
/* Selection of count type (number of pages generated, accesses per user, etc.) */
Sta_WriteSelectorCountType ();
fprintf (Gbl.F.Out," ");
/***** Option b) Listing of detailed clicks to this course *****/
fprintf (Gbl.F.Out,"",
Txt_STAT_CLICKS_GROUPED_BY[Sta_CLICKS_CRS_DETAILED_LIST]);
/* Number of rows per page */
// To use getElementById in Firefox, it's necessary to have the id attribute
fprintf (Gbl.F.Out," "
"");
HTM_TD_End ();
HTM_TR_End ();
HTM_TABLE_End ();
/***** Hidden param used to get client time zone *****/
Dat_PutHiddenParBrowserTZDiff ();
/***** Send button *****/
Btn_PutConfirmButton (Txt_Show_hits);
/***** End form *****/
Frm_EndForm ();
}
}
else // No teachers nor students found
Ale_ShowAlert (Ale_WARNING,Txt_No_teachers_or_students_found);
/***** End section with user list *****/
HTM_SECTION_End ();
/***** End box *****/
Box_BoxEnd ();
/***** Free memory used by the lists *****/
Usr_FreeUsrsList (Rol_TCH);
Usr_FreeUsrsList (Rol_NET);
Usr_FreeUsrsList (Rol_STD);
/***** Free memory for list of selected groups *****/
Grp_FreeListCodSelectedGrps ();
}
/*****************************************************************************/
/********** Show a form to select the type of global stat of clics ***********/
/*****************************************************************************/
void Sta_AskShowGblHits (void)
{
extern const char *Hlp_ANALYTICS_Visits_global_visits;
extern const char *The_ClassFormInBox[The_NUM_THEMES];
extern const char *Txt_Statistics_of_all_visits;
extern const char *Txt_Users;
extern const char *Txt_ROLE_STATS[Sta_NUM_ROLES_STAT];
extern const char *Txt_Scope;
extern const char *Txt_Show;
extern const char *Txt_distributed_by;
extern const char *Txt_STAT_CLICKS_GROUPED_BY[Sta_NUM_CLICKS_GROUPED_BY];
extern const char *Txt_Show_hits;
Sta_Role_t RoleStat;
Sta_ClicksGroupedBy_t ClicksGroupedBy;
/***** Contextual menu *****/
Mnu_ContextMenuBegin ();
Sta_PutLinkToCourseHits (); // Course hits
Sta_PutLinkToLastClicks (); // Last clicks in real time
Mnu_ContextMenuEnd ();
/***** Begin form *****/
Frm_StartFormAnchor (ActSeeAccGbl,Sta_STAT_RESULTS_SECTION_ID);
/***** Begin box and table *****/
Box_StartBoxTable (NULL,Txt_Statistics_of_all_visits,NULL,
Hlp_ANALYTICS_Visits_global_visits,Box_NOT_CLOSABLE,2);
/***** Start and end dates for the search *****/
Dat_PutFormStartEndClientLocalDateTimesWithYesterdayToday (Gbl.Action.Act == ActReqAccGbl);
/***** Users' roles whose accesses we want to see *****/
HTM_TR_Begin (NULL);
HTM_TD_Begin ("class=\"RM\"");
fprintf (Gbl.F.Out,"",
The_ClassFormInBox[Gbl.Prefs.Theme],Txt_Users);
HTM_TD_End ();
HTM_TD_Begin ("colspan=\"2\" class=\"LM\"");
fprintf (Gbl.F.Out,"");
HTM_TD_End ();
HTM_TR_End ();
/***** Selection of action *****/
Sta_WriteSelectorAction ();
/***** Clicks made from anywhere, current centre, current degree or current course *****/
HTM_TR_Begin (NULL);
HTM_TD_Begin ("class=\"RM\"");
fprintf (Gbl.F.Out,"",
The_ClassFormInBox[Gbl.Prefs.Theme],Txt_Scope);
HTM_TD_End ();
HTM_TD_Begin ("colspan=\"2\" class=\"LM\"");
Gbl.Scope.Allowed = 1 << Hie_SYS |
1 << Hie_CTY |
1 << Hie_INS |
1 << Hie_CTR |
1 << Hie_DEG |
1 << Hie_CRS;
Gbl.Scope.Default = Hie_SYS;
Sco_GetScope ("ScopeSta");
Sco_PutSelectorScope ("ScopeSta",false);
HTM_TD_End ();
HTM_TR_End ();
/***** Count type for the statistic *****/
HTM_TR_Begin (NULL);
HTM_TD_Begin ("class=\"RM\"");
fprintf (Gbl.F.Out,"",
The_ClassFormInBox[Gbl.Prefs.Theme],Txt_Show);
HTM_TD_End ();
HTM_TD_Begin ("colspan=\"2\" class=\"LM\"");
Sta_WriteSelectorCountType ();
/***** Type of statistic *****/
fprintf (Gbl.F.Out,"");
HTM_TD_End ();
HTM_TR_End ();
/***** End table *****/
HTM_TABLE_End ();
/***** Hidden param used to get client time zone *****/
Dat_PutHiddenParBrowserTZDiff ();
/***** Send button and end box *****/
Box_EndBoxWithButton (Btn_CONFIRM_BUTTON,Txt_Show_hits);
/***** End form *****/
Frm_EndForm ();
}
/*****************************************************************************/
/*************** Put a link to show visits to current course *****************/
/*****************************************************************************/
static void Sta_PutLinkToCourseHits (void)
{
extern const char *Txt_Visits_to_course;
if (Gbl.Hierarchy.Level == Hie_CRS) // Course selected
switch (Gbl.Usrs.Me.Role.Logged)
{
case Rol_NET:
case Rol_TCH:
case Rol_SYS_ADM:
Lay_PutContextualLinkIconText (ActReqAccCrs,NULL,NULL,
"chart-line.svg",
Txt_Visits_to_course);
break;
default:
break;
}
}
/*****************************************************************************/
/********************* Put a link to show global visits **********************/
/*****************************************************************************/
static void Sta_PutLinkToGlobalHits (void)
{
extern const char *Txt_Global_visits;
Lay_PutContextualLinkIconText (ActReqAccGbl,NULL,NULL,
"chart-line.svg",
Txt_Global_visits);
}
/*****************************************************************************/
/****** Put selectors for type of access count and for degree or course ******/
/*****************************************************************************/
static void Sta_WriteSelectorCountType (void)
{
extern const char *Txt_STAT_TYPE_COUNT_SMALL[Sta_NUM_COUNT_TYPES];
Sta_CountType_t StatCountType;
/**** Count type *****/
fprintf (Gbl.F.Out,"");
}
/*****************************************************************************/
/****** Put selectors for type of access count and for degree or course ******/
/*****************************************************************************/
static void Sta_WriteSelectorAction (void)
{
extern const char *The_ClassFormInBox[The_NUM_THEMES];
extern const char *Txt_Action;
extern const char *Txt_TABS_TXT[Tab_NUM_TABS];
Act_Action_t Action;
Tab_Tab_t Tab;
char ActTxt[Act_MAX_BYTES_ACTION_TXT + 1];
HTM_TR_Begin (NULL);
HTM_TD_Begin ("class=\"RM\"");
fprintf (Gbl.F.Out,"",
The_ClassFormInBox[Gbl.Prefs.Theme],Txt_Action);
HTM_TD_End ();
HTM_TD_Begin ("colspan=\"2\" class=\"LM\"");
fprintf (Gbl.F.Out,"");
HTM_TD_End ();
HTM_TR_End ();
}
/*****************************************************************************/
/************ Set end date to current date ************/
/************ and set initial date to end date minus several days ************/
/*****************************************************************************/
void Sta_SetIniEndDates (void)
{
Gbl.DateRange.TimeUTC[0] = Gbl.StartExecutionTimeUTC - ((Cfg_DAYS_IN_RECENT_LOG - 1) * 24 * 60 * 60);
Gbl.DateRange.TimeUTC[1] = Gbl.StartExecutionTimeUTC;
}
/*****************************************************************************/
/******************** Compute and show access statistics *********************/
/*****************************************************************************/
void Sta_SeeGblAccesses (void)
{
Sta_ShowHits (Sta_SHOW_GLOBAL_ACCESSES);
}
void Sta_SeeCrsAccesses (void)
{
Sta_ShowHits (Sta_SHOW_COURSE_ACCESSES);
}
/*****************************************************************************/
/******************** 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;
extern const char *Txt_There_is_no_knowing_how_many_users_not_logged_have_accessed;
extern const char *Txt_The_date_range_must_be_less_than_or_equal_to_X_days;
extern const char *Txt_There_are_no_accesses_with_the_selected_search_criteria;
extern const char *Txt_List_of_detailed_clicks;
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;
char *Query = NULL;
char QueryAux[512];
long LengthQuery;
MYSQL_RES *mysql_res;
unsigned long NumRows;
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;
/***** Get initial and ending dates *****/
Dat_GetIniEndDatesFromForm ();
/***** Get client time zone *****/
Dat_GetBrowserTimeZone (BrowserTimeZone);
/***** Set table where to find depending on initial date *****/
/* If initial day is older than current day minus Cfg_DAYS_IN_RECENT_LOG,
then use recent log table, else use historic log table */
LogTable = (Dat_GetNumDaysBetweenDates (&Gbl.DateRange.DateIni.Date,&Gbl.Now.Date)
<= Cfg_DAYS_IN_RECENT_LOG) ? "log_recent" :
"log_full";
/***** Get the type of stat of clicks ******/
DetailedOrGrouped = (Sta_ClicksDetailedOrGrouped_t)
Par_GetParToUnsignedLong ("GroupedOrDetailed",
0,
Sta_NUM_CLICKS_DETAILED_OR_GROUPED - 1,
(unsigned long) Sta_CLICKS_DETAILED_OR_GROUPED_DEFAULT);
if (DetailedOrGrouped == Sta_CLICKS_DETAILED)
Gbl.Stat.ClicksGroupedBy = Sta_CLICKS_CRS_DETAILED_LIST;
else // DetailedOrGrouped == Sta_CLICKS_GROUPED
Gbl.Stat.ClicksGroupedBy = (Sta_ClicksGroupedBy_t)
Par_GetParToUnsignedLong ("GroupedBy",
0,
Sta_NUM_CLICKS_GROUPED_BY - 1,
(unsigned long) Sta_CLICKS_GROUPED_BY_DEFAULT);
/***** Get the type of count of clicks *****/
if (Gbl.Stat.ClicksGroupedBy != Sta_CLICKS_CRS_DETAILED_LIST)
Gbl.Stat.CountType = (Sta_CountType_t)
Par_GetParToUnsignedLong ("CountType",
0,
Sta_NUM_COUNT_TYPES - 1,
(unsigned long) Sta_COUNT_TYPE_DEFAULT);
/***** Get action *****/
Gbl.Stat.NumAction = (Act_Action_t)
Par_GetParToUnsignedLong ("StatAct",
0,
Act_NUM_ACTIONS - 1,
(unsigned long) Sta_NUM_ACTION_DEFAULT);
switch (GlobalOrCourse)
{
case Sta_SHOW_GLOBAL_ACCESSES:
/***** Get the type of user of clicks *****/
Gbl.Stat.Role = (Sta_Role_t)
Par_GetParToUnsignedLong ("Role",
0,
Sta_NUM_ROLES_STAT - 1,
(unsigned long) Sta_ROLE_DEFAULT);
/***** Get users range for access statistics *****/
Gbl.Scope.Allowed = 1 << Hie_SYS |
1 << Hie_CTY |
1 << Hie_INS |
1 << Hie_CTR |
1 << Hie_DEG |
1 << Hie_CRS;
Gbl.Scope.Default = Hie_SYS;
Sco_GetScope ("ScopeSta");
/***** Show form again *****/
Sta_AskShowGblHits ();
/***** Start results section *****/
HTM_SECTION_Begin (Sta_STAT_RESULTS_SECTION_ID);
/***** Check selection *****/
if ((Gbl.Stat.Role == Sta_ROLE_ALL_USRS ||
Gbl.Stat.Role == Sta_ROLE_UNKNOWN_USRS) &&
(Gbl.Stat.CountType == Sta_DISTINCT_USRS ||
Gbl.Stat.CountType == Sta_CLICKS_PER_USR)) // These types of query will never give a valid result
{
/* Write warning message and abort */
Ale_ShowAlert (Ale_WARNING,Txt_There_is_no_knowing_how_many_users_not_logged_have_accessed);
return;
}
break;
case Sta_SHOW_COURSE_ACCESSES:
if (Gbl.Stat.ClicksGroupedBy == Sta_CLICKS_CRS_DETAILED_LIST)
{
/****** Get the number of the first row to show ******/
Gbl.Stat.FirstRow = Par_GetParToUnsignedLong ("FirstRow",
1,
ULONG_MAX,
0);
/****** Get the number of the last row to show ******/
Gbl.Stat.LastRow = Par_GetParToUnsignedLong ("LastRow",
1,
ULONG_MAX,
0);
/****** Get the number of rows per page ******/
Gbl.Stat.RowsPerPage = Par_GetParToUnsignedLong ("RowsPage",
Sta_MIN_ROWS_PER_PAGE,
Sta_MAX_ROWS_PER_PAGE,
Sta_DEF_ROWS_PER_PAGE);
}
/****** Get lists of selected users ******/
Usr_GetListsSelectedUsrsCods ();
/***** Show the form again *****/
Sta_AskShowCrsHits ();
/***** Start results section *****/
HTM_SECTION_Begin (Sta_STAT_RESULTS_SECTION_ID);
/***** Check selection *****/
if (!Usr_CountNumUsrsInListOfSelectedUsrs ()) // Error: there are no users selected
{
/* Write warning message, clean and abort */
Ale_ShowAlert (Ale_WARNING,Txt_You_must_select_one_ore_more_users);
Usr_FreeListsSelectedUsrsCods ();
return;
}
break;
}
/***** 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) ||
(Gbl.Usrs.Me.Role.Logged == Rol_TCH && Gbl.Scope.Current == Hie_CRS) ||
(Gbl.Usrs.Me.Role.Logged == Rol_DEG_ADM && (Gbl.Scope.Current == Hie_DEG ||
Gbl.Scope.Current == Hie_CRS)) ||
(Gbl.Usrs.Me.Role.Logged == Rol_CTR_ADM && (Gbl.Scope.Current == Hie_CTR ||
Gbl.Scope.Current == Hie_DEG ||
Gbl.Scope.Current == Hie_CRS)) ||
(Gbl.Usrs.Me.Role.Logged == Rol_INS_ADM && (Gbl.Scope.Current == Hie_INS ||
Gbl.Scope.Current == Hie_CTR ||
Gbl.Scope.Current == Hie_DEG ||
Gbl.Scope.Current == Hie_CRS)) ||
Gbl.Usrs.Me.Role.Logged == Rol_SYS_ADM;
if (!ICanQueryWholeRange && NumDays > Cfg_DAYS_IN_RECENT_LOG)
{
/* ...write warning message and show the form again */
Ale_ShowAlert (Ale_WARNING,Txt_The_date_range_must_be_less_than_or_equal_to_X_days,
Cfg_DAYS_IN_RECENT_LOG);
return;
}
/***** Query depending on the type of count *****/
switch (Gbl.Stat.CountType)
{
case Sta_TOTAL_CLICKS:
Str_Copy (StrQueryCountType,"COUNT(*)",
Sta_MAX_BYTES_COUNT_TYPE);
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 = (char *) malloc (Sta_MAX_BYTES_QUERY_ACCESS + 1)) == NULL)
Lay_NotEnoughMemoryExit ();
/* Start the query */
switch (Gbl.Stat.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 " // Weeks start on monday
"DATE_FORMAT(CONVERT_TZ(ClickTime,@@session.time_zone,'%s'),'%%x%%v') AS Week,"
"%s FROM %s" :
"SELECT SQL_NO_CACHE " // Weeks start on sunday
"DATE_FORMAT(CONVERT_TZ(ClickTime,@@session.time_zone,'%s'),'%%X%%V') AS Week,"
"%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_ws.PlgCod,%s AS Num FROM %s,log_ws",
StrQueryCountType,LogTable);
break;
case Sta_CLICKS_GBL_PER_API_FUNCTION:
snprintf (Query,Sta_MAX_BYTES_QUERY_ACCESS + 1,
"SELECT SQL_NO_CACHE log_ws.FunCod,%s AS Num FROM %s,log_ws",
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_CENTRE:
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[0],
(long) Gbl.DateRange.TimeUTC[1]);
Str_Concat (Query,QueryAux,
Sta_MAX_BYTES_QUERY_ACCESS);
switch (GlobalOrCourse)
{
case Sta_SHOW_GLOBAL_ACCESSES:
/* Scope */
switch (Gbl.Scope.Current)
{
case Hie_UNK:
case Hie_SYS:
break;
case Hie_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 Hie_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 Hie_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 Hie_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 Hie_CRS:
if (Gbl.Hierarchy.Level == Hie_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 (Gbl.Stat.Role)
{
case Sta_ROLE_IDENTIFIED_USRS:
sprintf (StrRole," AND %s.Role<>%u",
LogTable,(unsigned) Rol_UNK);
break;
case Sta_ROLE_ALL_USRS:
switch (Gbl.Stat.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 (Gbl.Stat.ClicksGroupedBy)
{
case Sta_CLICKS_GBL_PER_PLUGIN:
case Sta_CLICKS_GBL_PER_API_FUNCTION:
sprintf (QueryAux," AND %s.LogCod=log_ws.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.EncryptedUsrCod,
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)
Lay_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 (Gbl.Stat.NumAction != ActAll)
{
sprintf (QueryAux," AND %s.ActCod=%ld",
LogTable,Act_GetActCod (Gbl.Stat.NumAction));
Str_Concat (Query,QueryAux,
Sta_MAX_BYTES_QUERY_ACCESS);
}
/* End the query */
switch (Gbl.Stat.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 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 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 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 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 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",
Sta_MAX_BYTES_QUERY_ACCESS);
break;
case Sta_CLICKS_CRS_PER_MINUTE:
case Sta_CLICKS_GBL_PER_MINUTE:
Str_Concat (Query," GROUP 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_ws.PlgCod ORDER BY Num DESC",
Sta_MAX_BYTES_QUERY_ACCESS);
break;
case Sta_CLICKS_GBL_PER_API_FUNCTION:
Str_Concat (Query," GROUP BY log_ws.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_CENTRE:
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_ShowFixedAlert (Ale_INFO,Query);
*/
/***** Make the query *****/
NumRows = DB_QuerySELECT (&mysql_res,"can not get clicks",
"%s",
Query);
/***** Count the number of rows in result *****/
if (NumRows == 0)
Ale_ShowAlert (Ale_INFO,Txt_There_are_no_accesses_with_the_selected_search_criteria);
else
{
/***** Put the table with the clicks *****/
if (Gbl.Stat.ClicksGroupedBy == Sta_CLICKS_CRS_DETAILED_LIST)
Box_BoxBegin ("100%",Txt_List_of_detailed_clicks,NULL,
NULL,Box_NOT_CLOSABLE);
else
Box_BoxBegin (NULL,Txt_STAT_TYPE_COUNT_CAPS[Gbl.Stat.CountType],NULL,
NULL,Box_NOT_CLOSABLE);
HTM_TABLE_BeginPadding (Sta_CellPadding[Gbl.Stat.ClicksGroupedBy]);
switch (Gbl.Stat.ClicksGroupedBy)
{
case Sta_CLICKS_CRS_DETAILED_LIST:
Sta_ShowDetailedAccessesList (NumRows,mysql_res);
break;
case Sta_CLICKS_CRS_PER_USR:
Sta_ShowNumHitsPerUsr (NumRows,mysql_res);
break;
case Sta_CLICKS_CRS_PER_DAY:
case Sta_CLICKS_GBL_PER_DAY:
Sta_ShowNumHitsPerDay (NumRows,mysql_res);
break;
case Sta_CLICKS_CRS_PER_DAY_AND_HOUR:
case Sta_CLICKS_GBL_PER_DAY_AND_HOUR:
Sta_ShowDistrAccessesPerDayAndHour (NumRows,mysql_res);
break;
case Sta_CLICKS_CRS_PER_WEEK:
case Sta_CLICKS_GBL_PER_WEEK:
Sta_ShowNumHitsPerWeek (NumRows,mysql_res);
break;
case Sta_CLICKS_CRS_PER_MONTH:
case Sta_CLICKS_GBL_PER_MONTH:
Sta_ShowNumHitsPerMonth (NumRows,mysql_res);
break;
case Sta_CLICKS_CRS_PER_YEAR:
case Sta_CLICKS_GBL_PER_YEAR:
Sta_ShowNumHitsPerYear (NumRows,mysql_res);
break;
case Sta_CLICKS_CRS_PER_HOUR:
case Sta_CLICKS_GBL_PER_HOUR:
Sta_ShowNumHitsPerHour (NumRows,mysql_res);
break;
case Sta_CLICKS_CRS_PER_MINUTE:
case Sta_CLICKS_GBL_PER_MINUTE:
Sta_ShowAverageAccessesPerMinute (NumRows,mysql_res);
break;
case Sta_CLICKS_CRS_PER_ACTION:
case Sta_CLICKS_GBL_PER_ACTION:
Sta_ShowNumHitsPerAction (NumRows,mysql_res);
break;
case Sta_CLICKS_GBL_PER_PLUGIN:
Sta_ShowNumHitsPerPlugin (NumRows,mysql_res);
break;
case Sta_CLICKS_GBL_PER_API_FUNCTION:
Sta_ShowNumHitsPerWSFunction (NumRows,mysql_res);
break;
case Sta_CLICKS_GBL_PER_BANNER:
Sta_ShowNumHitsPerBanner (NumRows,mysql_res);
break;
case Sta_CLICKS_GBL_PER_COUNTRY:
Sta_ShowNumHitsPerCountry (NumRows,mysql_res);
break;
case Sta_CLICKS_GBL_PER_INSTITUTION:
Sta_ShowNumHitsPerInstitution (NumRows,mysql_res);
break;
case Sta_CLICKS_GBL_PER_CENTRE:
Sta_ShowNumHitsPerCentre (NumRows,mysql_res);
break;
case Sta_CLICKS_GBL_PER_DEGREE:
Sta_ShowNumHitsPerDegree (NumRows,mysql_res);
break;
case Sta_CLICKS_GBL_PER_COURSE:
Sta_ShowNumHitsPerCourse (NumRows,mysql_res);
break;
}
HTM_TABLE_End ();
/* End box and section */
Box_BoxEnd ();
HTM_SECTION_End ();
}
/***** 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)
Usr_FreeListsSelectedUsrsCods ();
/***** Write time zone used in the calculation of these statistics *****/
switch (Gbl.Stat.ClicksGroupedBy)
{
case Sta_CLICKS_CRS_PER_DAY:
case Sta_CLICKS_GBL_PER_DAY:
case Sta_CLICKS_CRS_PER_DAY_AND_HOUR:
case Sta_CLICKS_GBL_PER_DAY_AND_HOUR:
case Sta_CLICKS_CRS_PER_WEEK:
case Sta_CLICKS_GBL_PER_WEEK:
case Sta_CLICKS_CRS_PER_MONTH:
case Sta_CLICKS_GBL_PER_MONTH:
case Sta_CLICKS_CRS_PER_YEAR:
case Sta_CLICKS_GBL_PER_YEAR:
case Sta_CLICKS_CRS_PER_HOUR:
case Sta_CLICKS_GBL_PER_HOUR:
case Sta_CLICKS_CRS_PER_MINUTE:
case Sta_CLICKS_GBL_PER_MINUTE:
fprintf (Gbl.F.Out,"