mirror of https://github.com/acanas/swad-core.git
Version19.243
This commit is contained in:
parent
1981c0b605
commit
5326b0cd03
|
@ -557,11 +557,11 @@ enscript -2 --landscape --color --file-align=2 --highlight --line-numbers -o - *
|
||||||
En OpenSWAD:
|
En OpenSWAD:
|
||||||
ps2pdf source.ps destination.pdf
|
ps2pdf source.ps destination.pdf
|
||||||
*/
|
*/
|
||||||
#define Log_PLATFORM_VERSION "SWAD 19.242.1 (2020-05-23)"
|
#define Log_PLATFORM_VERSION "SWAD 19.243 (2020-05-23)"
|
||||||
#define CSS_FILE "swad19.238.2.css"
|
#define CSS_FILE "swad19.238.2.css"
|
||||||
#define JS_FILE "swad19.239.6.js"
|
#define JS_FILE "swad19.239.6.js"
|
||||||
/*
|
/*
|
||||||
Version 19.243: May 23, 2020 List exam log. (? lines)
|
Version 19.243: May 23, 2020 List exam log. (302180 lines)
|
||||||
Version 19.242.1: May 23, 2020 Bug fixing and code refactoring in exam log. (301932 lines)
|
Version 19.242.1: May 23, 2020 Bug fixing and code refactoring in exam log. (301932 lines)
|
||||||
1 change necessary in database:
|
1 change necessary in database:
|
||||||
ALTER TABLE exa_log CHANGE COLUMN Open CanAnswer ENUM('N','Y') NOT NULL DEFAULT 'N';
|
ALTER TABLE exa_log CHANGE COLUMN Open CanAnswer ENUM('N','Y') NOT NULL DEFAULT 'N';
|
||||||
|
|
194
swad_exam_log.c
194
swad_exam_log.c
|
@ -25,6 +25,10 @@
|
||||||
/********************************* Headers ***********************************/
|
/********************************* Headers ***********************************/
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE // For asprintf
|
||||||
|
#include <stdio.h> // For asprintf
|
||||||
|
#include <string.h> // For string functions
|
||||||
|
|
||||||
#include "swad_action.h"
|
#include "swad_action.h"
|
||||||
#include "swad_database.h"
|
#include "swad_database.h"
|
||||||
#include "swad_exam_log.h"
|
#include "swad_exam_log.h"
|
||||||
|
@ -142,7 +146,7 @@ void ExaLog_LogAccess (long LogCod)
|
||||||
Redundant data (also present in log table) are stored for speed */
|
Redundant data (also present in log table) are stored for speed */
|
||||||
DB_QueryINSERT ("can not log exam access",
|
DB_QueryINSERT ("can not log exam access",
|
||||||
"INSERT INTO exa_log "
|
"INSERT INTO exa_log "
|
||||||
"(LogCod,PrnCod,ActCod,QstInd,UserCanAnswer,ClickTime,IP,SessionId)"
|
"(LogCod,PrnCod,ActCod,QstInd,CanAnswer,ClickTime,IP,SessionId)"
|
||||||
" VALUES "
|
" VALUES "
|
||||||
"(%ld,%ld,%ld,%d,'%c',NOW(),'%s','%s')",
|
"(%ld,%ld,%ld,%d,'%c',NOW(),'%s','%s')",
|
||||||
LogCod,
|
LogCod,
|
||||||
|
@ -156,3 +160,191 @@ void ExaLog_LogAccess (long LogCod)
|
||||||
Gbl.Session.Id);
|
Gbl.Session.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/****************************** Show exam log ********************************/
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
void ExaLog_ShowExamLog (const struct ExaPrn_Print *Print)
|
||||||
|
{
|
||||||
|
extern const char *Txt_Hits;
|
||||||
|
extern const char *Txt_Date_and_time;
|
||||||
|
extern const char *Txt_Action;
|
||||||
|
extern const char *Txt_Question;
|
||||||
|
extern const char *Txt_EXAM_Open;
|
||||||
|
extern const char *Txt_IP;
|
||||||
|
extern const char *Txt_Session;
|
||||||
|
extern const char *Txt_EXAM_LOG_ACTIONS[ExaLog_NUM_ACTIONS];
|
||||||
|
MYSQL_RES *mysql_res;
|
||||||
|
MYSQL_ROW row;
|
||||||
|
unsigned NumClicks;
|
||||||
|
unsigned NumClick;
|
||||||
|
unsigned ActCod;
|
||||||
|
int QstInd;
|
||||||
|
bool UsrCouldAnswer;
|
||||||
|
time_t ClickTimeUTC;
|
||||||
|
char IP[Cns_MAX_BYTES_IP + 1];
|
||||||
|
char SessionId[Cns_BYTES_SESSION_ID + 1];
|
||||||
|
char *Id;
|
||||||
|
size_t Length;
|
||||||
|
char Anonymized[14 + 1]; // XXX…XXX
|
||||||
|
// 12345678901234
|
||||||
|
|
||||||
|
/***** Check if I can view this print result *****/
|
||||||
|
switch (Gbl.Usrs.Me.Role.Logged)
|
||||||
|
{
|
||||||
|
case Rol_NET:
|
||||||
|
case Rol_TCH:
|
||||||
|
case Rol_DEG_ADM:
|
||||||
|
case Rol_CTR_ADM:
|
||||||
|
case Rol_INS_ADM:
|
||||||
|
case Rol_SYS_ADM:
|
||||||
|
break;
|
||||||
|
default: // Other users can not see log
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***** Get print log from database *****/
|
||||||
|
NumClicks = (unsigned)
|
||||||
|
DB_QuerySELECT (&mysql_res,"can not get exam print log",
|
||||||
|
"SELECT ActCod," // row[0]
|
||||||
|
"QstInd," // row[1]
|
||||||
|
"CanAnswer," // row[2]
|
||||||
|
"UNIX_TIMESTAMP(ClickTime)," // row[3]
|
||||||
|
"IP," // row[4]
|
||||||
|
"SessionId" // row[5]
|
||||||
|
" FROM exa_log"
|
||||||
|
" WHERE PrnCod=%ld"
|
||||||
|
" ORDER BY LogCod",
|
||||||
|
Print->PrnCod);
|
||||||
|
|
||||||
|
if (NumClicks)
|
||||||
|
{
|
||||||
|
/***** Begin box *****/
|
||||||
|
Box_BoxTableBegin (NULL,Txt_Hits,
|
||||||
|
NULL,NULL,
|
||||||
|
NULL,Box_CLOSABLE,2);
|
||||||
|
|
||||||
|
/***** Begin table *****/
|
||||||
|
HTM_TABLE_BeginWideMarginPadding (2);
|
||||||
|
|
||||||
|
/***** Write heading *****/
|
||||||
|
HTM_TR_Begin (NULL);
|
||||||
|
|
||||||
|
HTM_TH (1,1,"LB",Txt_Date_and_time);
|
||||||
|
HTM_TH (1,1,"LB",Txt_Action);
|
||||||
|
HTM_TH (1,1,"RB",Txt_Question);
|
||||||
|
HTM_TH (1,1,"CB",Txt_EXAM_Open);
|
||||||
|
HTM_TH (1,1,"LB",Txt_IP);
|
||||||
|
HTM_TH (1,1,"LB",Txt_Session);
|
||||||
|
|
||||||
|
HTM_TR_End ();
|
||||||
|
|
||||||
|
/***** Write clicks *****/
|
||||||
|
for (NumClick = 0;
|
||||||
|
NumClick < NumClicks;
|
||||||
|
NumClick++)
|
||||||
|
{
|
||||||
|
/***** Get row *****/
|
||||||
|
row = mysql_fetch_row (mysql_res);
|
||||||
|
|
||||||
|
/* Get code of action (row[0]) */
|
||||||
|
ActCod = Str_ConvertStrToUnsigned (row[0]);
|
||||||
|
if (ActCod >= ExaLog_NUM_ACTIONS)
|
||||||
|
ActCod = ExaLog_UNKNOWN_ACTION;
|
||||||
|
|
||||||
|
/* Get question index (row[1]) */
|
||||||
|
QstInd = (int) Str_ConvertStrCodToLongCod (row[1]);
|
||||||
|
|
||||||
|
/* Get if the user could answer (row[2]) */
|
||||||
|
UsrCouldAnswer = (row[2][0] == 'Y');
|
||||||
|
|
||||||
|
/* Get click time (row[3] holds the UTC time) */
|
||||||
|
ClickTimeUTC = Dat_GetUNIXTimeFromStr (row[3]);
|
||||||
|
|
||||||
|
/* Get IP (row[4]) */
|
||||||
|
Str_Copy (IP,row[4],
|
||||||
|
Cns_MAX_BYTES_IP);
|
||||||
|
|
||||||
|
/* Get session id (row[5]) */
|
||||||
|
Str_Copy (SessionId,row[5],
|
||||||
|
Cns_BYTES_SESSION_ID);
|
||||||
|
|
||||||
|
/***** Write row *****/
|
||||||
|
HTM_TR_Begin (NULL);
|
||||||
|
|
||||||
|
/* Write click time */
|
||||||
|
if (asprintf (&Id,"click_date_%u",NumClick) < 0)
|
||||||
|
Lay_NotEnoughMemoryExit ();
|
||||||
|
HTM_TD_Begin ("id=\"%s\" class=\"LM DAT\"",Id);
|
||||||
|
Dat_WriteLocalDateHMSFromUTC (Id,ClickTimeUTC,
|
||||||
|
Gbl.Prefs.DateFormat,Dat_SEPARATOR_COMMA,
|
||||||
|
true,true,true,0x7);
|
||||||
|
free (Id);
|
||||||
|
HTM_TD_End ();
|
||||||
|
|
||||||
|
/* Write action */
|
||||||
|
HTM_TD_Begin ("class=\"LM DAT\"");
|
||||||
|
HTM_Txt (Txt_EXAM_LOG_ACTIONS[ActCod]);
|
||||||
|
HTM_TD_End ();
|
||||||
|
|
||||||
|
/* Write number of question */
|
||||||
|
HTM_TD_Begin ("class=\"RM DAT\"");
|
||||||
|
if (QstInd >= 0)
|
||||||
|
HTM_Unsigned ((unsigned) QstInd + 1);
|
||||||
|
HTM_TD_End ();
|
||||||
|
|
||||||
|
/* Write if exam print was open and accesible to answer */
|
||||||
|
HTM_TD_Begin ("class=\"CM %s\"",UsrCouldAnswer ? "DAT_GREEN" :
|
||||||
|
"DAT_RED");
|
||||||
|
HTM_Txt (UsrCouldAnswer ? "✓" :
|
||||||
|
"✗");
|
||||||
|
HTM_TD_End ();
|
||||||
|
|
||||||
|
/* Write IP */
|
||||||
|
HTM_TD_Begin ("class=\"CM DAT\"");
|
||||||
|
Length = strlen (IP);
|
||||||
|
if (Length > 6)
|
||||||
|
{
|
||||||
|
sprintf (Anonymized,"%c%c%c…%c%c%c",
|
||||||
|
IP[0],
|
||||||
|
IP[1],
|
||||||
|
IP[2],
|
||||||
|
IP[Length - 3],
|
||||||
|
IP[Length - 2],
|
||||||
|
IP[Length - 1]);
|
||||||
|
HTM_Txt (Anonymized);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
HTM_Txt ("…");
|
||||||
|
HTM_TD_End ();
|
||||||
|
|
||||||
|
/* Write session id */
|
||||||
|
HTM_TD_Begin ("class=\"CM DAT\"");
|
||||||
|
Length = strlen (SessionId);
|
||||||
|
Length = strlen (IP);
|
||||||
|
if (Length > 6)
|
||||||
|
{
|
||||||
|
sprintf (Anonymized,"%c%c%c…%c%c%c",
|
||||||
|
SessionId[0],
|
||||||
|
SessionId[1],
|
||||||
|
SessionId[2],
|
||||||
|
SessionId[Length - 3],
|
||||||
|
SessionId[Length - 2],
|
||||||
|
SessionId[Length - 1]);
|
||||||
|
HTM_Txt (Anonymized);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
HTM_Txt ("…");
|
||||||
|
HTM_TD_End ();
|
||||||
|
|
||||||
|
HTM_TR_End ();
|
||||||
|
}
|
||||||
|
|
||||||
|
/***** End table and box *****/
|
||||||
|
Box_BoxTableEnd ();
|
||||||
|
}
|
||||||
|
|
||||||
|
/***** Free structure that stores the query result *****/
|
||||||
|
DB_FreeMySQLResult (&mysql_res);
|
||||||
|
}
|
||||||
|
|
|
@ -27,6 +27,8 @@
|
||||||
/********************************** Headers **********************************/
|
/********************************** Headers **********************************/
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
#include "swad_exam_print.h"
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/************************* Public types and constants ************************/
|
/************************* Public types and constants ************************/
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
@ -57,4 +59,6 @@ bool ExaLog_GetIfCanAnswer (void);
|
||||||
|
|
||||||
void ExaLog_LogAccess (long LogCod);
|
void ExaLog_LogAccess (long LogCod);
|
||||||
|
|
||||||
|
void ExaLog_ShowExamLog (const struct ExaPrn_Print *Print);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1138,10 +1138,11 @@ void ExaRes_ShowOneExaResult (void)
|
||||||
ExaRes_ShowExamResult (&Exam,&Session,&Print,UsrDat);
|
ExaRes_ShowExamResult (&Exam,&Session,&Print,UsrDat);
|
||||||
|
|
||||||
/***** Show exam log *****/
|
/***** Show exam log *****/
|
||||||
|
ExaLog_ShowExamLog (&Print);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/*************************** Show one exam result ****************************/
|
/***************************** Show exam result ******************************/
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
static void ExaRes_ShowExamResult (const struct Exa_Exam *Exam,
|
static void ExaRes_ShowExamResult (const struct Exa_Exam *Exam,
|
||||||
|
|
|
@ -888,8 +888,8 @@ static void ExaSes_GetSessionDataFromRow (MYSQL_RES *mysql_res,
|
||||||
|
|
||||||
void ExaSes_RequestRemoveSession (void)
|
void ExaSes_RequestRemoveSession (void)
|
||||||
{
|
{
|
||||||
extern const char *Txt_Do_you_really_want_to_remove_the_event_X;
|
extern const char *Txt_Do_you_really_want_to_remove_the_session_X;
|
||||||
extern const char *Txt_Remove_event;
|
extern const char *Txt_Remove_session;
|
||||||
struct Exa_Exams Exams;
|
struct Exa_Exams Exams;
|
||||||
struct Exa_Exam Exam;
|
struct Exa_Exam Exam;
|
||||||
struct ExaSes_Session Session;
|
struct ExaSes_Session Session;
|
||||||
|
@ -907,8 +907,8 @@ void ExaSes_RequestRemoveSession (void)
|
||||||
Exams.SesCod = Session.SesCod;
|
Exams.SesCod = Session.SesCod;
|
||||||
Ale_ShowAlertAndButton (ActRemExaSes,NULL,NULL,
|
Ale_ShowAlertAndButton (ActRemExaSes,NULL,NULL,
|
||||||
ExaSes_PutParamsEdit,&Exams,
|
ExaSes_PutParamsEdit,&Exams,
|
||||||
Btn_REMOVE_BUTTON,Txt_Remove_event,
|
Btn_REMOVE_BUTTON,Txt_Remove_session,
|
||||||
Ale_QUESTION,Txt_Do_you_really_want_to_remove_the_event_X,
|
Ale_QUESTION,Txt_Do_you_really_want_to_remove_the_session_X,
|
||||||
Session.Title);
|
Session.Title);
|
||||||
|
|
||||||
/***** Show current exam *****/
|
/***** Show current exam *****/
|
||||||
|
|
86
swad_text.c
86
swad_text.c
|
@ -7203,6 +7203,27 @@ const char *Txt_Date =
|
||||||
"Data";
|
"Data";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
const char *Txt_Date_and_time =
|
||||||
|
#if L==1 // ca
|
||||||
|
"Data i hora";
|
||||||
|
#elif L==2 // de
|
||||||
|
"Datum und Uhrzeit";
|
||||||
|
#elif L==3 // en
|
||||||
|
"Date and time";
|
||||||
|
#elif L==4 // es
|
||||||
|
"Fecha y hora";
|
||||||
|
#elif L==5 // fr
|
||||||
|
"Date et heure";
|
||||||
|
#elif L==6 // gn
|
||||||
|
"Fecha y hora"; // Okoteve traducción
|
||||||
|
#elif L==7 // it
|
||||||
|
"Data e ora";
|
||||||
|
#elif L==8 // pl
|
||||||
|
"Data i godzina";
|
||||||
|
#elif L==9 // pt
|
||||||
|
"Data e hora";
|
||||||
|
#endif
|
||||||
|
|
||||||
const char *Txt_Date_of_birth =
|
const char *Txt_Date_of_birth =
|
||||||
#if L==1 // ca
|
#if L==1 // ca
|
||||||
"Data naixement";
|
"Data naixement";
|
||||||
|
@ -9523,6 +9544,27 @@ const char *Txt_Do_you_really_want_to_remove_the_selected_questions =
|
||||||
"Você realmente deseja remover as perguntas selecionadas?";
|
"Você realmente deseja remover as perguntas selecionadas?";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
const char *Txt_Do_you_really_want_to_remove_the_session_X = // Warning: it is very important to include %s in the following sentences
|
||||||
|
#if L==1 // ca
|
||||||
|
"De veres voleu eliminar la sessió <strong>%s</strong>?";
|
||||||
|
#elif L==2 // de
|
||||||
|
"Wollen Sie der Sitzung <strong>%s</strong> wirklich entfernen?";
|
||||||
|
#elif L==3 // en
|
||||||
|
"Do you really want to remove the session <strong>%s</strong>?";
|
||||||
|
#elif L==4 // es
|
||||||
|
"¿Realmente desea eliminar la sesión <strong>%s</strong>?";
|
||||||
|
#elif L==5 // fr
|
||||||
|
"Voulez-vous vraiment supprimer la session <strong>%s</strong>?";
|
||||||
|
#elif L==6 // gn
|
||||||
|
"¿Realmente desea eliminar la sesión <strong>%s</strong>?"; // Okoteve traducción
|
||||||
|
#elif L==7 // it
|
||||||
|
"Vuoi realmente rimuovere la sessione <strong>%s</strong>?";
|
||||||
|
#elif L==8 // pl
|
||||||
|
"Czy na pewno chcesz usunac sesji <strong>%s</strong>?";
|
||||||
|
#elif L==9 // pt
|
||||||
|
"Você realmente deseja remover a sessão <strong>%s</strong>?";
|
||||||
|
#endif
|
||||||
|
|
||||||
const char *Txt_Do_you_really_want_to_remove_the_set_of_questions_X = // Warning: it is very important to include %s in the following sentences
|
const char *Txt_Do_you_really_want_to_remove_the_set_of_questions_X = // Warning: it is very important to include %s in the following sentences
|
||||||
#if L==1 // ca
|
#if L==1 // ca
|
||||||
"¿De veres voleu eliminar el conjunt de preguntes <strong>%s</strong>?";
|
"¿De veres voleu eliminar el conjunt de preguntes <strong>%s</strong>?";
|
||||||
|
@ -12006,6 +12048,27 @@ const char *Txt_Exam_of_X = // Warning: it is very important to include %s in th
|
||||||
"Exame de %s";
|
"Exame de %s";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
const char *Txt_EXAM_Open =
|
||||||
|
#if L==1 // ca
|
||||||
|
"Obert";
|
||||||
|
#elif L==2 // de
|
||||||
|
"Geöffneten";
|
||||||
|
#elif L==3 // en
|
||||||
|
"Open";
|
||||||
|
#elif L==4 // es
|
||||||
|
"Abierto";
|
||||||
|
#elif L==5 // fr
|
||||||
|
"Ouvert";
|
||||||
|
#elif L==6 // gn
|
||||||
|
"Abierto"; // Okoteve traducción
|
||||||
|
#elif L==7 // it
|
||||||
|
"Aperto";
|
||||||
|
#elif L==8 // pl
|
||||||
|
"otwarte";
|
||||||
|
#elif L==9 // pt
|
||||||
|
"Aberta";
|
||||||
|
#endif
|
||||||
|
|
||||||
const char *Txt_Exam_X_removed = // Warning: it is very important to include %s in the following sentences
|
const char *Txt_Exam_X_removed = // Warning: it is very important to include %s in the following sentences
|
||||||
#if L==1 // ca
|
#if L==1 // ca
|
||||||
"Examen <strong>%s</strong> eliminat.";
|
"Examen <strong>%s</strong> eliminat.";
|
||||||
|
@ -24470,7 +24533,7 @@ const char *Txt_MSG_Not_replied =
|
||||||
|
|
||||||
const char *Txt_MSG_Open =
|
const char *Txt_MSG_Open =
|
||||||
#if L==1 // ca
|
#if L==1 // ca
|
||||||
"Abierto"; // Necessita traduccio
|
"Obert";
|
||||||
#elif L==2 // de
|
#elif L==2 // de
|
||||||
"Geöffneten";
|
"Geöffneten";
|
||||||
#elif L==3 // en
|
#elif L==3 // en
|
||||||
|
@ -34415,6 +34478,27 @@ const char *Txt_Remove_record_field =
|
||||||
"Remover campo de cartão";
|
"Remover campo de cartão";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
const char *Txt_Remove_session =
|
||||||
|
#if L==1 // ca
|
||||||
|
"Eliminar sessió";
|
||||||
|
#elif L==2 // de
|
||||||
|
"Entfernen Sitzung";
|
||||||
|
#elif L==3 // en
|
||||||
|
"Remove session";
|
||||||
|
#elif L==4 // es
|
||||||
|
"Eliminar sesión";
|
||||||
|
#elif L==5 // fr
|
||||||
|
"Supprimer session";
|
||||||
|
#elif L==6 // gn
|
||||||
|
"Eliminar sesión"; // Okoteve traducción
|
||||||
|
#elif L==7 // it
|
||||||
|
"Rimuovere sessione";
|
||||||
|
#elif L==8 // pl
|
||||||
|
"Usuń sesji";
|
||||||
|
#elif L==9 // pt
|
||||||
|
"Remover sessão";
|
||||||
|
#endif
|
||||||
|
|
||||||
const char *Txt_Remove_set_of_questions =
|
const char *Txt_Remove_set_of_questions =
|
||||||
#if L==1 // ca
|
#if L==1 // ca
|
||||||
"Eliminar conjunt de preguntes";
|
"Eliminar conjunt de preguntes";
|
||||||
|
|
Loading…
Reference in New Issue