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:
|
||||
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 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)
|
||||
1 change necessary in database:
|
||||
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 ***********************************/
|
||||
/*****************************************************************************/
|
||||
|
||||
#define _GNU_SOURCE // For asprintf
|
||||
#include <stdio.h> // For asprintf
|
||||
#include <string.h> // For string functions
|
||||
|
||||
#include "swad_action.h"
|
||||
#include "swad_database.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 */
|
||||
DB_QueryINSERT ("can not log exam access",
|
||||
"INSERT INTO exa_log "
|
||||
"(LogCod,PrnCod,ActCod,QstInd,UserCanAnswer,ClickTime,IP,SessionId)"
|
||||
"(LogCod,PrnCod,ActCod,QstInd,CanAnswer,ClickTime,IP,SessionId)"
|
||||
" VALUES "
|
||||
"(%ld,%ld,%ld,%d,'%c',NOW(),'%s','%s')",
|
||||
LogCod,
|
||||
|
@ -156,3 +160,191 @@ void ExaLog_LogAccess (long LogCod)
|
|||
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 **********************************/
|
||||
/*****************************************************************************/
|
||||
|
||||
#include "swad_exam_print.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
/************************* Public types and constants ************************/
|
||||
/*****************************************************************************/
|
||||
|
@ -57,4 +59,6 @@ bool ExaLog_GetIfCanAnswer (void);
|
|||
|
||||
void ExaLog_LogAccess (long LogCod);
|
||||
|
||||
void ExaLog_ShowExamLog (const struct ExaPrn_Print *Print);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1138,10 +1138,11 @@ void ExaRes_ShowOneExaResult (void)
|
|||
ExaRes_ShowExamResult (&Exam,&Session,&Print,UsrDat);
|
||||
|
||||
/***** Show exam log *****/
|
||||
ExaLog_ShowExamLog (&Print);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/*************************** Show one exam result ****************************/
|
||||
/***************************** Show exam result ******************************/
|
||||
/*****************************************************************************/
|
||||
|
||||
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)
|
||||
{
|
||||
extern const char *Txt_Do_you_really_want_to_remove_the_event_X;
|
||||
extern const char *Txt_Remove_event;
|
||||
extern const char *Txt_Do_you_really_want_to_remove_the_session_X;
|
||||
extern const char *Txt_Remove_session;
|
||||
struct Exa_Exams Exams;
|
||||
struct Exa_Exam Exam;
|
||||
struct ExaSes_Session Session;
|
||||
|
@ -907,8 +907,8 @@ void ExaSes_RequestRemoveSession (void)
|
|||
Exams.SesCod = Session.SesCod;
|
||||
Ale_ShowAlertAndButton (ActRemExaSes,NULL,NULL,
|
||||
ExaSes_PutParamsEdit,&Exams,
|
||||
Btn_REMOVE_BUTTON,Txt_Remove_event,
|
||||
Ale_QUESTION,Txt_Do_you_really_want_to_remove_the_event_X,
|
||||
Btn_REMOVE_BUTTON,Txt_Remove_session,
|
||||
Ale_QUESTION,Txt_Do_you_really_want_to_remove_the_session_X,
|
||||
Session.Title);
|
||||
|
||||
/***** Show current exam *****/
|
||||
|
|
86
swad_text.c
86
swad_text.c
|
@ -7203,6 +7203,27 @@ const char *Txt_Date =
|
|||
"Data";
|
||||
#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 =
|
||||
#if L==1 // ca
|
||||
"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?";
|
||||
#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
|
||||
#if L==1 // ca
|
||||
"¿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";
|
||||
#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
|
||||
#if L==1 // ca
|
||||
"Examen <strong>%s</strong> eliminat.";
|
||||
|
@ -24470,7 +24533,7 @@ const char *Txt_MSG_Not_replied =
|
|||
|
||||
const char *Txt_MSG_Open =
|
||||
#if L==1 // ca
|
||||
"Abierto"; // Necessita traduccio
|
||||
"Obert";
|
||||
#elif L==2 // de
|
||||
"Geöffneten";
|
||||
#elif L==3 // en
|
||||
|
@ -34415,6 +34478,27 @@ const char *Txt_Remove_record_field =
|
|||
"Remover campo de cartão";
|
||||
#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 =
|
||||
#if L==1 // ca
|
||||
"Eliminar conjunt de preguntes";
|
||||
|
|
Loading…
Reference in New Issue