diff --git a/swad_changelog.h b/swad_changelog.h index fec9dacf2..d8ba80353 100644 --- a/swad_changelog.h +++ b/swad_changelog.h @@ -143,13 +143,14 @@ /****************************** Public constants *****************************/ /*****************************************************************************/ -#define Log_PLATFORM_VERSION "SWAD 16.5 (2016-10-02)" +#define Log_PLATFORM_VERSION "SWAD 16.6 (2016-10-02)" #define CSS_FILE "swad15.229.css" #define JS_FILE "swad15.238.1.js" // Number of lines (includes comments but not blank lines) has been got with the following command: // nl swad*.c swad*.h css/swad*.css py/swad*.py js/swad*.js soap/swad*.h sql/swad*.sql | tail -1 /* + Version 16.6: Oct 02, 2016 Hits in historic courses in user's usage report. (205432 lines) Version 16.5: Oct 02, 2016 Changes in hits in current courses in user's usage report. (205364 lines) Version 16.4.1: Sep 29, 2016 Changes in hits in current courses in user's usage report. (205316 lines) Version 16.4: Sep 29, 2016 Hits in current courses in user's usage report. (205299 lines) diff --git a/swad_course.c b/swad_course.c index a59ea0b4a..05cd05a92 100644 --- a/swad_course.c +++ b/swad_course.c @@ -1999,7 +1999,7 @@ bool Crs_GetDataOfCourseByCod (struct Course *Crs) unsigned long NumRows; bool CrsFound = false; - if (Crs->CrsCod < 0) + if (Crs->CrsCod <= 0) { Crs->CrsCod = -1L; Crs->DegCod = -1L; diff --git a/swad_report.c b/swad_report.c index 1d231d657..f4c9f5d2b 100644 --- a/swad_report.c +++ b/swad_report.c @@ -77,20 +77,25 @@ extern struct Globals Gbl; static void Rep_ShowOrPrintMyUsageReport (Rep_SeeOrPrint_t SeeOrPrint); static void Rep_PutIconToPrintMyUsageReport (void); +static unsigned long Rep_GetMaxHitsPerYear (const char *BrowserTimeZone, + time_t FirstClickTimeUTC); static void Rep_GetAndWriteCurrentCrssOfAUsr (const struct UsrData *UsrDat,Rol_Role_t Role, const char *BrowserTimeZone, time_t FirstClickTimeUTC, struct tm *tm_FirstClickTime, unsigned long MaxHitsPerYear); -static void Rep_WriteRowCrsData (MYSQL_ROW row, +static void Rep_GetAndWriteHistoricCrssOfAUsr (const struct UsrData *UsrDat,Rol_Role_t Role, + const char *BrowserTimeZone, + time_t FirstClickTimeUTC, + struct tm *tm_FirstClickTime, + unsigned long MaxHitsPerYear); +static void Rep_WriteRowCrsData (long CrsCod,Rol_Role_t Role, const char *BrowserTimeZone, time_t FirstClickTimeUTC, struct tm *tm_FirstClickTime, unsigned long MaxHitsPerYear); -static unsigned long Rep_GetMaxHitsPerYear (const char *BrowserTimeZone, - time_t FirstClickTimeUTC); -static void Rep_ShowMyHitsPerYear (long CrsCod, +static void Rep_ShowMyHitsPerYear (bool AnyCourse,long CrsCod,Rol_Role_t Role, const char *BrowserTimeZone, time_t FirstClickTimeUTC, struct tm *tm_FirstClickTime, @@ -141,12 +146,6 @@ static void Rep_ShowOrPrintMyUsageReport (Rep_SeeOrPrint_t SeeOrPrint) extern const char *Txt_message; extern const char *Txt_messages; extern const char *Txt_Courses; - extern const char *Txt_USER_in_COURSE; - extern const char *Txt_ROLES_SINGUL_Abc[Rol_NUM_ROLES][Usr_NUM_SEXS]; - extern const char *Txt_course; - extern const char *Txt_courses; - extern const char *Txt_teachers_ABBREVIATION; - extern const char *Txt_students_ABBREVIATION; extern const char *Txt_Hits; char BrowserTimeZone[Dat_MAX_BYTES_TIME_ZONE+1]; unsigned NumID; @@ -157,7 +156,6 @@ static void Rep_ShowOrPrintMyUsageReport (Rep_SeeOrPrint_t SeeOrPrint) unsigned NumFiles; unsigned NumPublicFiles; Rol_Role_t Role; - unsigned NumCrss; unsigned long MaxHitsPerYear; /***** Get client time zone *****/ @@ -337,47 +335,44 @@ static void Rep_ShowOrPrintMyUsageReport (Rep_SeeOrPrint_t SeeOrPrint) fprintf (Gbl.F.Out,""); - /***** Courses *****/ - fprintf (Gbl.F.Out,"

%s

" + /***** Current courses *****/ + fprintf (Gbl.F.Out,"

%s (actuales)

" // TODO: Need translation!!! ""); + /***** Historic courses *****/ + fprintf (Gbl.F.Out,"

%s (histórico)

" // TODO: Need translation!!! + ""); /***** Global hits *****/ fprintf (Gbl.F.Out,"

%s

",Txt_Hits); - Rep_ShowMyHitsPerYear (-1L, + Rep_ShowMyHitsPerYear (true,-1L,Rol_UNKNOWN, BrowserTimeZone, UsrFigures.FirstClickTimeUTC, &tm_FirstClickTime, @@ -405,133 +400,6 @@ static void Rep_PutIconToPrintMyUsageReport (void) NULL); } -/*****************************************************************************/ -/************************** Write courses of a user **************************/ -/*****************************************************************************/ - -static void Rep_GetAndWriteCurrentCrssOfAUsr (const struct UsrData *UsrDat,Rol_Role_t Role, - const char *BrowserTimeZone, - time_t FirstClickTimeUTC, - struct tm *tm_FirstClickTime, - unsigned long MaxHitsPerYear) - { - char Query[1024]; - MYSQL_RES *mysql_res; - MYSQL_ROW row; - unsigned NumCrss; - unsigned NumCrs; - - /***** Get courses of a user from database *****/ - /* - SELECT degrees.DegCod 0 - courses.CrsCod 1 - courses.FullName 2 - courses.Year 3 - degrees.FullName 4 - */ - sprintf (Query,"SELECT degrees.DegCod,courses.CrsCod,courses.FullName,courses.Year,degrees.FullName" - " FROM crs_usr,courses,degrees" - " WHERE crs_usr.UsrCod='%ld'" - " AND crs_usr.Role='%u'" - " AND crs_usr.CrsCod=courses.CrsCod" - " AND courses.DegCod=degrees.DegCod" - " ORDER BY degrees.FullName,courses.Year,courses.FullName", - UsrDat->UsrCod,(unsigned) Role); - - /***** List the courses (one row per course) *****/ - if ((NumCrss = (unsigned) DB_QuerySELECT (Query,&mysql_res,"can not get courses of a user"))) - { - /* Heading row */ - fprintf (Gbl.F.Out,"
    "); - - /* Write courses */ - for (NumCrs = 1; - NumCrs <= NumCrss; - NumCrs++) - { - /* Get next course */ - row = mysql_fetch_row (mysql_res); - - /* Write data of this course */ - Rep_WriteRowCrsData (row, - BrowserTimeZone,FirstClickTimeUTC,tm_FirstClickTime, - MaxHitsPerYear); - } - - /* End table */ - fprintf (Gbl.F.Out,"
"); - } - - /***** Free structure that stores the query result *****/ - DB_FreeMySQLResult (&mysql_res); - } - -/*****************************************************************************/ -/************** Write the data of a course (result of a query) ***************/ -/*****************************************************************************/ - -static void Rep_WriteRowCrsData (MYSQL_ROW row, - const char *BrowserTimeZone, - time_t FirstClickTimeUTC, - struct tm *tm_FirstClickTime, - unsigned long MaxHitsPerYear) - { - extern const char *Txt_YEAR_OF_DEGREE[1+Deg_MAX_YEARS_PER_DEGREE]; - extern const char *Txt_teachers_ABBREVIATION; - extern const char *Txt_students_ABBREVIATION; - struct Degree Deg; - long CrsCod; - unsigned Year; - unsigned NumTchs; - unsigned NumStds; - - /* - SELECT degrees.DegCod 0 - courses.CrsCod 1 - courses.FullName 2 - courses.Year 3 - degrees.FullName 4 - */ - - /***** Get degree code (row[0]) *****/ - if ((Deg.DegCod = Str_ConvertStrCodToLongCod (row[0])) < 0) - Lay_ShowErrorAndExit ("Wrong code of degree."); - if (!Deg_GetDataOfDegreeByCod (&Deg)) - Lay_ShowErrorAndExit ("Degree not found."); - - /***** Get course code (row[1]) *****/ - if ((CrsCod = Str_ConvertStrCodToLongCod (row[1])) < 0) - Lay_ShowErrorAndExit ("Wrong code of course."); - - /***** Get number of teachers and students in this course *****/ - NumTchs = Usr_GetNumUsrsInCrs (Rol_TEACHER,CrsCod); - NumStds = Usr_GetNumUsrsInCrs (Rol_STUDENT,CrsCod); - - /***** Start row *****/ - fprintf (Gbl.F.Out,"
  • "); - - /***** Write course full name (row[2]) *****/ - fprintf (Gbl.F.Out,"%s -",row[2]); - - /***** Write year (row[3]) *****/ - if ((Year = Deg_ConvStrToYear (row[3]))) - fprintf (Gbl.F.Out," %s",Txt_YEAR_OF_DEGREE[Year]); - - /***** Write degree full name (row[4]) *****/ - fprintf (Gbl.F.Out," %s",row[4]); - - /***** Write number of teachers / students in course *****/ - fprintf (Gbl.F.Out," (%u %s / %u %s)
    ", - NumTchs,Txt_teachers_ABBREVIATION, - NumStds,Txt_students_ABBREVIATION); - - /***** Write hits per year for this course *****/ - Rep_ShowMyHitsPerYear (CrsCod, - BrowserTimeZone,FirstClickTimeUTC,tm_FirstClickTime, - MaxHitsPerYear); - - fprintf (Gbl.F.Out,"
  • "); - } /*****************************************************************************/ /************ Get the maximum number of hits per course-year-role ************/ @@ -549,12 +417,12 @@ static unsigned long Rep_GetMaxHitsPerYear (const char *BrowserTimeZone, sprintf (Query,"SELECT MAX(N) FROM (SELECT " "CrsCod," "YEAR(CONVERT_TZ(ClickTime,@@session.time_zone,'%s')) AS Year," + "Role," "COUNT(*) AS N" " FROM log_full" " WHERE ClickTime>=FROM_UNIXTIME('%ld')" " AND UsrCod='%ld'" - " AND CrsCod>'0'" - " GROUP BY CrsCod,Year)" + " GROUP BY CrsCod,Year,Role)" " AS hits_per_crs_year", BrowserTimeZone, (long) FirstClickTimeUTC, @@ -570,11 +438,216 @@ static unsigned long Rep_GetMaxHitsPerYear (const char *BrowserTimeZone, return MaxHitsPerYear; } +/*****************************************************************************/ +/************************** Write courses of a user **************************/ +/*****************************************************************************/ + +static void Rep_GetAndWriteCurrentCrssOfAUsr (const struct UsrData *UsrDat,Rol_Role_t Role, + const char *BrowserTimeZone, + time_t FirstClickTimeUTC, + struct tm *tm_FirstClickTime, + unsigned long MaxHitsPerYear) + { + extern const char *Txt_USER_in_COURSE; + extern const char *Txt_ROLES_SINGUL_Abc[Rol_NUM_ROLES][Usr_NUM_SEXS]; + extern const char *Txt_course; + extern const char *Txt_courses; + extern const char *Txt_teachers_ABBREVIATION; + extern const char *Txt_students_ABBREVIATION; + char Query[1024]; + MYSQL_RES *mysql_res; + MYSQL_ROW row; + unsigned NumCrss; + unsigned NumCrs; + long CrsCod; + + NumCrss = Usr_GetNumCrssOfUsrWithARole (UsrDat->UsrCod,Role); + sprintf (Gbl.Title,Txt_USER_in_COURSE,Txt_ROLES_SINGUL_Abc[Role][Gbl.Usrs.Me.UsrDat.Sex]); + fprintf (Gbl.F.Out,"
  • %s %u %s", + Gbl.Title, + NumCrss, + NumCrss == 1 ? Txt_course : + Txt_courses); + if (NumCrss) + { + fprintf (Gbl.F.Out," (%u %s / %u %s)", + Usr_GetNumUsrsInCrssOfAUsr (Gbl.Usrs.Me.UsrDat.UsrCod,Role,Rol_TEACHER), + Txt_teachers_ABBREVIATION, + Usr_GetNumUsrsInCrssOfAUsr (Gbl.Usrs.Me.UsrDat.UsrCod,Role,Rol_STUDENT), + Txt_students_ABBREVIATION); + + /***** Get courses of a user from database *****/ + sprintf (Query,"SELECT courses.CrsCod,degrees.FullName,courses.Year,courses.FullName" + " FROM crs_usr,courses,degrees" + " WHERE crs_usr.UsrCod='%ld'" + " AND crs_usr.Role='%u'" + " AND crs_usr.CrsCod=courses.CrsCod" + " AND courses.DegCod=degrees.DegCod" + " ORDER BY degrees.FullName,courses.Year,courses.FullName", + UsrDat->UsrCod,(unsigned) Role); + + /***** List the courses (one row per course) *****/ + if ((NumCrss = (unsigned) DB_QuerySELECT (Query,&mysql_res,"can not get courses of a user"))) + { + /* Heading row */ + fprintf (Gbl.F.Out,"
      "); + + /* Write courses */ + for (NumCrs = 1; + NumCrs <= NumCrss; + NumCrs++) + { + /* Get next course */ + row = mysql_fetch_row (mysql_res); + + /* Get course code (row[0]) */ + CrsCod = Str_ConvertStrCodToLongCod (row[0]); + + /* Write data of this course */ + Rep_WriteRowCrsData (CrsCod,Role, + BrowserTimeZone,FirstClickTimeUTC,tm_FirstClickTime, + MaxHitsPerYear); + } + + /* End table */ + fprintf (Gbl.F.Out,"
    "); + } + + /***** Free structure that stores the query result *****/ + DB_FreeMySQLResult (&mysql_res); + } + + fprintf (Gbl.F.Out,"
  • "); + } + +/*****************************************************************************/ +/************************** Write courses of a user **************************/ +/*****************************************************************************/ + +static void Rep_GetAndWriteHistoricCrssOfAUsr (const struct UsrData *UsrDat,Rol_Role_t Role, + const char *BrowserTimeZone, + time_t FirstClickTimeUTC, + struct tm *tm_FirstClickTime, + unsigned long MaxHitsPerYear) + { + extern const char *Txt_ROLES_SINGUL_abc[Rol_NUM_ROLES][Usr_NUM_SEXS]; + char Query[1024]; + MYSQL_RES *mysql_res; + MYSQL_ROW row; + unsigned NumCrss; + unsigned NumCrs; + long CrsCod; + + /***** Get courses of a user from database *****/ + sprintf (Query,"SELECT CrsCod," + "COUNT(*) AS N" + " FROM log_full" + " WHERE UsrCod='%ld' AND Role='%u'" + " GROUP BY CrsCod ORDER BY N DESC", + UsrDat->UsrCod,(unsigned) Role); + + /***** List the courses (one row per course) *****/ + if ((NumCrss = (unsigned) DB_QuerySELECT (Query,&mysql_res,"can not get courses of a user"))) + { + /* Heading row */ + sprintf (Gbl.Title,"Accesos como %s", // TODO: Need translation + Txt_ROLES_SINGUL_abc[Role][Gbl.Usrs.Me.UsrDat.Sex]); + fprintf (Gbl.F.Out,"
  • %s:" + "
      ", + Gbl.Title); + + /* Write courses */ + for (NumCrs = 1; + NumCrs <= NumCrss; + NumCrs++) + { + /* Get next course */ + row = mysql_fetch_row (mysql_res); + + /* Get course code (row[0]) */ + CrsCod = Str_ConvertStrCodToLongCod (row[0]); + + /* Write data of this course */ + Rep_WriteRowCrsData (CrsCod,Role, + BrowserTimeZone,FirstClickTimeUTC,tm_FirstClickTime, + MaxHitsPerYear); + } + + /* End of list */ + fprintf (Gbl.F.Out,"
    " + "
  • "); + } + + /***** Free structure that stores the query result *****/ + DB_FreeMySQLResult (&mysql_res); + } + +/*****************************************************************************/ +/************** Write the data of a course (result of a query) ***************/ +/*****************************************************************************/ + +static void Rep_WriteRowCrsData (long CrsCod,Rol_Role_t Role, + const char *BrowserTimeZone, + time_t FirstClickTimeUTC, + struct tm *tm_FirstClickTime, + unsigned long MaxHitsPerYear) + { + extern const char *Txt_YEAR_OF_DEGREE[1+Deg_MAX_YEARS_PER_DEGREE]; + extern const char *Txt_teachers_ABBREVIATION; + extern const char *Txt_students_ABBREVIATION; + struct Course Crs; + struct Degree Deg; + + /***** Get course data *****/ + Crs.CrsCod = CrsCod; + Crs_GetDataOfCourseByCod (&Crs); + + /***** Get degree data *****/ + Deg.DegCod = Crs.DegCod; + Deg_GetDataOfDegreeByCod (&Deg); + + /***** Start row *****/ + fprintf (Gbl.F.Out,"
  • "); + + if (CrsCod > 0) // CrsCod > 0 in log ==> course selected + { + if (Crs.CrsCod > 0) // Course exists + { + /***** Write course full name *****/ + fprintf (Gbl.F.Out,"%s -",Crs.FullName); + + /***** Write year *****/ + if (Crs.Year) + fprintf (Gbl.F.Out," %s",Txt_YEAR_OF_DEGREE[Crs.Year]); + + /***** Write degree full name *****/ + fprintf (Gbl.F.Out," %s",Deg.FullName); + + /***** Write number of teachers / students in course *****/ + fprintf (Gbl.F.Out," (%u %s / %u %s)", + Usr_GetNumUsrsInCrs (Rol_TEACHER,Crs.CrsCod),Txt_teachers_ABBREVIATION, + Usr_GetNumUsrsInCrs (Rol_STUDENT,Crs.CrsCod),Txt_students_ABBREVIATION); + } + else + fprintf (Gbl.F.Out,"(asignatura desconocida/eliminada)"); // TODO: Need translation + } + else // CrsCod <= 0 in log ==> no course selected + fprintf (Gbl.F.Out,"(asignatura no seleccionada)"); // TODO: Need translation + + /***** Write hits per year for this course *****/ + fprintf (Gbl.F.Out,"
    "); + Rep_ShowMyHitsPerYear (false,CrsCod,Role, + BrowserTimeZone,FirstClickTimeUTC,tm_FirstClickTime, + MaxHitsPerYear); + + fprintf (Gbl.F.Out,"
  • "); + } + /*****************************************************************************/ /********************** Write my hits grouped by years ***********************/ /*****************************************************************************/ -static void Rep_ShowMyHitsPerYear (long CrsCod, +static void Rep_ShowMyHitsPerYear (bool AnyCourse,long CrsCod,Rol_Role_t Role, const char *BrowserTimeZone, time_t FirstClickTimeUTC, struct tm *tm_FirstClickTime, @@ -582,6 +655,7 @@ static void Rep_ShowMyHitsPerYear (long CrsCod, { char Query[1024]; char SubQueryCrs[128]; + char SubQueryRol[128]; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned long NumRows; @@ -592,21 +666,27 @@ static void Rep_ShowMyHitsPerYear (long CrsCod, struct Sta_Hits Hits; /***** Make the query *****/ - if (CrsCod > 0) - sprintf (SubQueryCrs," AND CrsCod='%ld'",CrsCod); - else + if (AnyCourse) SubQueryCrs[0] = '\0'; + else + sprintf (SubQueryCrs," AND CrsCod='%ld'",CrsCod); + + if (Role == Rol_UNKNOWN) // Here Rol_UNKNOWN means any role + SubQueryRol[0] = '\0'; + else + sprintf (SubQueryRol," AND Role='%u'",(unsigned) Role); sprintf (Query,"SELECT SQL_NO_CACHE " "YEAR(CONVERT_TZ(ClickTime,@@session.time_zone,'%s')) AS Year," "COUNT(*) FROM log_full" " WHERE ClickTime>=FROM_UNIXTIME('%ld')" - " AND UsrCod='%ld'%s" + " AND UsrCod='%ld'%s%s" " GROUP BY Year DESC", BrowserTimeZone, (long) FirstClickTimeUTC, Gbl.Usrs.Me.UsrDat.UsrCod, - SubQueryCrs); + SubQueryCrs, + SubQueryRol); NumRows = DB_QuerySELECT (Query,&mysql_res,"can not get clicks"); /***** Initialize first year *****/