Version 20.95.1: Jul 15, 2021 Queries moved to module swad_exam_database.

This commit is contained in:
acanas 2021-07-15 19:34:41 +02:00
parent 0866392160
commit c44e6be88a
6 changed files with 507 additions and 384 deletions

View File

@ -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. TODO: En las encuestas, que los estudiantes no puedan ver los resultados hasta que no finalice el plazo.
*/ */
#define Log_PLATFORM_VERSION "SWAD 20.95 (2021-07-08)" #define Log_PLATFORM_VERSION "SWAD 20.95.1 (2021-07-15)"
#define CSS_FILE "swad20.45.css" #define CSS_FILE "swad20.45.css"
#define JS_FILE "swad20.69.1.js" #define JS_FILE "swad20.69.1.js"
/* /*
TODO: Rename CENTRE to CENTER in help wiki. TODO: Rename CENTRE to CENTER in help wiki.
TODO: Rename ASSESSMENT.Announcements to ASSESSMENT.Calls_for_exams TODO: Rename ASSESSMENT.Announcements to ASSESSMENT.Calls_for_exams
Version 20.95.1: Jul 15, 2021 Queries moved to module swad_exam_database. (314082 lines)
Version 20.95: Jul 08, 2021 New module swad_exam_database for database queries related to exams. (313981 lines) Version 20.95: Jul 08, 2021 New module swad_exam_database for database queries related to exams. (313981 lines)
Version 20.94.10: Jun 29, 2021 Code refactoring related to HTML output. (313860 lines) Version 20.94.10: Jun 29, 2021 Code refactoring related to HTML output. (313860 lines)
Version 20.94.9: Jun 29, 2021 Query moved from module swad_menu to module swad_setting. (313848 lines) Version 20.94.9: Jun 29, 2021 Query moved from module swad_menu to module swad_setting. (313848 lines)

View File

@ -3944,7 +3944,7 @@ Rol_Role_t DB_QuerySELECTRole (const char *MsgError,
} }
/*****************************************************************************/ /*****************************************************************************/
/**** Make a SELECT query for a unique row with one double from database *****/ /**** Make a SELECT query for a unique row with one string from database *****/
/*****************************************************************************/ /*****************************************************************************/
// StrSize does not include the ending byte '\0' // StrSize does not include the ending byte '\0'

View File

@ -35,6 +35,7 @@
//#include "swad_error.h" //#include "swad_error.h"
#include "swad_exam_database.h" #include "swad_exam_database.h"
#include "swad_exam_log.h" #include "swad_exam_log.h"
#include "swad_exam_print.h"
#include "swad_global.h" #include "swad_global.h"
/*****************************************************************************/ /*****************************************************************************/
@ -59,11 +60,359 @@ extern struct Globals Gbl;
/***************************** Private prototypes ****************************/ /***************************** Private prototypes ****************************/
/*****************************************************************************/ /*****************************************************************************/
/*****************************************************************************/
/***************** Get sets of questions in a given exam *********************/
/*****************************************************************************/
unsigned Exa_DB_GetExamSets (MYSQL_RES **mysql_res,long ExaCod)
{
return (unsigned)
DB_QuerySELECT (mysql_res,"can not get sets of questions",
"SELECT SetCod," // row[0]
"NumQstsToPrint," // row[1]
"Title" // row[2]
" FROM exa_sets"
" WHERE ExaCod=%ld"
" ORDER BY SetInd",
ExaCod);
}
/*****************************************************************************/
/******************* Get some random questions from a set ********************/
/*****************************************************************************/
unsigned Exa_DB_GetSomeQstsFromSetToPrint (MYSQL_RES **mysql_res,
long SetCod,unsigned NumQstsToPrint)
{
return (unsigned)
DB_QuerySELECT (mysql_res,"can not get questions from set",
"SELECT QstCod," // row[0]
"AnsType," // row[1]
"Shuffle" // row[2]
" FROM exa_set_questions"
" WHERE SetCod=%ld"
" ORDER BY RAND()" // Don't use RAND(NOW()) because the same ordering will be repeated across sets
" LIMIT %u",
SetCod,
NumQstsToPrint);
}
/*****************************************************************************/
/************** Get answers text for a question in an exam set ***************/
/*****************************************************************************/
unsigned Exa_DB_GetQstAnswersTextFromSet (MYSQL_RES **mysql_res,long QstCod)
{
return (unsigned)
DB_QuerySELECT (mysql_res,"can not get text of answers of a question",
"SELECT Answer" // row[0]
" FROM exa_set_answers"
" WHERE QstCod=%ld",
QstCod);
}
/*****************************************************************************/
/********** Get answers correctness for a question in an exam set ************/
/*****************************************************************************/
unsigned Exa_DB_GetQstAnswersCorrFromSet (MYSQL_RES **mysql_res,long QstCod)
{
return (unsigned)
DB_QuerySELECT (mysql_res,"can not get correctness of answers of a question",
"SELECT Correct" // row[0]
" FROM exa_set_answers"
" WHERE QstCod=%ld"
" ORDER BY AnsInd",
QstCod);
}
/*****************************************************************************/
/***************** Create new blank exam print in database *******************/
/*****************************************************************************/
long Exa_DB_CreatePrint (const struct ExaPrn_Print *Print)
{
return
DB_QueryINSERTandReturnCode ("can not create new exam print",
"INSERT INTO exa_prints"
" (SesCod,UsrCod,StartTime,EndTime,"
"NumQsts,NumQstsNotBlank,Sent,Score)"
" VALUES"
" (%ld,%ld,NOW(),NOW(),"
"%u,0,'N',0)",
Print->SesCod,
Print->UsrCod,
Print->NumQsts.All);
}
/*****************************************************************************/
/********************** Update exam print in database ************************/
/*****************************************************************************/
void Exa_DB_UpdatePrint (const struct ExaPrn_Print *Print)
{
/***** Update exam print in database *****/
Str_SetDecimalPointToUS (); // To print the floating point as a dot
DB_QueryUPDATE ("can not update exam print",
"UPDATE exa_prints"
" SET EndTime=NOW(),"
"NumQstsNotBlank=%u,"
"Sent='%c',"
"Score='%.15lg'"
" WHERE PrnCod=%ld"
" AND SesCod=%ld"
" AND UsrCod=%ld", // Extra checks
Print->NumQsts.NotBlank,
Print->Sent ? 'Y' :
'N',
Print->Score,
Print->PrnCod,
Print->SesCod,
Gbl.Usrs.Me.UsrDat.UsrCod);
Str_SetDecimalPointToLocal (); // Return to local system
}
/*****************************************************************************/
/**************** Get data of an exam print using print code *****************/
/*****************************************************************************/
unsigned Exa_DB_GetDataOfPrintByPrnCod (MYSQL_RES **mysql_res,long PrnCod)
{
return (unsigned)
DB_QuerySELECT (mysql_res,"can not get data of an exam print",
"SELECT PrnCod," // row[0]
"SesCod," // row[1]
"UsrCod," // row[2]
"UNIX_TIMESTAMP(StartTime)," // row[3]
"UNIX_TIMESTAMP(EndTime)," // row[4]
"NumQsts," // row[5]
"NumQstsNotBlank," // row[6]
"Sent," // row[7]
"Score" // row[8]
" FROM exa_prints"
" WHERE PrnCod=%ld",
PrnCod);
}
/*****************************************************************************/
/******** Get data of an exam print using session code and user code *********/
/*****************************************************************************/
unsigned Exa_DB_GetDataOfPrintBySesCodAndUsrCod (MYSQL_RES **mysql_res,
long SesCod,long UsrCod)
{
return (unsigned)
DB_QuerySELECT (mysql_res,"can not get data of an exam print",
"SELECT PrnCod," // row[0]
"SesCod," // row[1]
"UsrCod," // row[2]
"UNIX_TIMESTAMP(StartTime)," // row[3]
"UNIX_TIMESTAMP(EndTime)," // row[4]
"NumQsts," // row[5]
"NumQstsNotBlank," // row[6]
"Sent," // row[7]
"Score" // row[8]
" FROM exa_prints"
" WHERE SesCod=%ld"
" AND UsrCod=%ld",
SesCod,
UsrCod);
}
/*****************************************************************************/
/******************* Remove exam prints for a given user *********************/
/*****************************************************************************/
void Exa_DB_RemovePrintsMadeByUsrInAllCrss (long UsrCod)
{
DB_QueryDELETE ("can not remove exam prints made by a user",
"DELETE FROM exa_prints"
" WHERE UsrCod=%ld",
UsrCod);
}
/*****************************************************************************/
/*************** Remove exam prints made by a user in a course ***************/
/*****************************************************************************/
void Exa_DB_RemovePrintsMadeByUsrInCrs (long UsrCod,long CrsCod)
{
DB_QueryDELETE ("can not remove exams prints made by a user in a course",
"DELETE FROM exa_prints"
" USING exa_exams,"
"exa_sessions,"
"exa_prints"
" WHERE exa_exams.CrsCod=%ld"
" AND exa_exams.ExaCod=exa_sessions.ExaCod"
" AND exa_sessions.SesCod=exa_prints.SesCod"
" AND exa_prints.UsrCod=%ld",
CrsCod,
UsrCod);
}
/*****************************************************************************/
/******* Remove exams prints made by the given user in the given course ******/
/*****************************************************************************/
void Exa_DB_RemovePrintsInCrs (long CrsCod)
{
DB_QueryDELETE ("can not remove exams prints in a course",
"DELETE FROM exa_prints"
" USING exa_exams,"
"exa_sessions,"
"exa_prints"
" WHERE exa_exams.CrsCod=%ld"
" AND exa_exams.ExaCod=exa_sessions.ExaCod"
" AND exa_sessions.SesCod=exa_prints.SesCod",
CrsCod);
}
/*****************************************************************************/
/************* Store user's answers of an test exam into database ************/
/*****************************************************************************/
void Exa_DB_StoreOneQstOfPrint (const struct ExaPrn_Print *Print,
unsigned QstInd)
{
Str_SetDecimalPointToUS (); // To print the floating point as a dot
DB_QueryREPLACE ("can not update a question in an exam print",
"REPLACE INTO exa_print_questions"
" (PrnCod,QstCod,QstInd,SetCod,Score,Indexes,Answers)"
" VALUES"
" (%ld,%ld,%u,%ld,'%.15lg','%s','%s')",
Print->PrnCod,
Print->PrintedQuestions[QstInd].QstCod,
QstInd, // 0, 1, 2, 3...
Print->PrintedQuestions[QstInd].SetCod,
Print->PrintedQuestions[QstInd].Score,
Print->PrintedQuestions[QstInd].StrIndexes,
Print->PrintedQuestions[QstInd].StrAnswers);
Str_SetDecimalPointToLocal (); // Return to local system
}
/*****************************************************************************/
/************* Get the questions of an exam print from database **************/
/*****************************************************************************/
unsigned Exa_DB_GetPrintQuestions (MYSQL_RES **mysql_res,long PrnCod)
{
return (unsigned)
DB_QuerySELECT (mysql_res,"can not get questions of an exam print",
"SELECT QstCod," // row[0]
"SetCod," // row[1]
"Score," // row[2]
"Indexes," // row[3]
"Answers" // row[4]
" FROM exa_print_questions"
" WHERE PrnCod=%ld"
" ORDER BY QstInd",
PrnCod);
}
/*****************************************************************************/
/************** Get the answers of an exam print from database ***************/
/*****************************************************************************/
void Exa_DB_GetAnswersFromQstInPrint (long PrnCod,long QstCod,
char StrAnswers[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1])
{
DB_QuerySELECTString (StrAnswers,Tst_MAX_BYTES_ANSWERS_ONE_QST,
"can not get answer in an exam print",
"SELECT Answers"
" FROM exa_print_questions"
" WHERE PrnCod=%ld"
" AND QstCod=%ld",
PrnCod,QstCod);
}
/*****************************************************************************/
/************ Get number of questions not blank in an exam print *************/
/*****************************************************************************/
unsigned Exa_DB_GetNumQstsNotBlankInPrint (long PrnCod)
{
return (unsigned)
DB_QueryCOUNT ("can not get number of questions not blank",
"SELECT COUNT(*)"
" FROM exa_print_questions"
" WHERE PrnCod=%ld"
" AND Answers<>''",
PrnCod);
}
/*****************************************************************************/
/************* Compute total score of questions of an exam print *************/
/*****************************************************************************/
double Exa_DB_ComputeTotalScoreOfPrint (long PrnCod)
{
return DB_QuerySELECTDouble ("can not get score of exam print",
"SELECT SUM(Score)"
" FROM exa_print_questions"
" WHERE PrnCod=%ld",
PrnCod);
}
/*****************************************************************************/
/*************** Remove exam prints questions for a given user ***************/
/*****************************************************************************/
void Exa_DB_RemovePrintQuestionsMadeByUsrInAllCrss (long UsrCod)
{
DB_QueryDELETE ("can not remove exam prints made by a user",
"DELETE FROM exa_print_questions"
" USING exa_prints,"
"exa_print_questions"
" WHERE exa_prints.UsrCod=%ld"
" AND exa_prints.PrnCod=exa_print_questions.PrnCod",
UsrCod);
}
/*****************************************************************************/
/* Remove questions of exams prints made by the given user in a given course */
/*****************************************************************************/
void Exa_DB_RemovePrintsQuestionsMadeByUsrInCrs (long UsrCod,long CrsCod)
{
DB_QueryDELETE ("can not remove exams prints made by a user in a course",
"DELETE FROM exa_print_questions"
" USING exa_exams,"
"exa_sessions,"
"exa_prints,"
"exa_print_questions"
" WHERE exa_exams.CrsCod=%ld"
" AND exa_exams.ExaCod=exa_sessions.ExaCod"
" AND exa_sessions.SesCod=exa_prints.SesCod"
" AND exa_prints.UsrCod=%ld"
" AND exa_prints.PrnCod=exa_print_questions.PrnCod",
CrsCod,
UsrCod);
}
/*****************************************************************************/
/* Remove questions of exams prints made by the given user in a given course */
/*****************************************************************************/
void Exa_DB_RemovePrintQuestionsInCrs (long CrsCod)
{
DB_QueryDELETE ("can not remove exams prints in a course",
"DELETE FROM exa_print_questions"
" USING exa_exams,"
"exa_sessions,"
"exa_prints,"
"exa_print_questions"
" WHERE exa_exams.CrsCod=%ld"
" AND exa_exams.ExaCod=exa_sessions.ExaCod"
" AND exa_sessions.SesCod=exa_prints.SesCod"
" AND exa_prints.PrnCod=exa_print_questions.PrnCod",
CrsCod);
}
/*****************************************************************************/ /*****************************************************************************/
/******** Check if the current session id is the same as the last one ********/ /******** Check if the current session id is the same as the last one ********/
/*****************************************************************************/ /*****************************************************************************/
bool ExaLog_DB_CheckIfSessionIsTheSameAsTheLast (long PrnCod) bool Exa_DB_CheckIfSessionIsTheSameAsTheLast (long PrnCod)
{ {
/***** Check if the current session id /***** Check if the current session id
is the same as the last one stored in database *****/ is the same as the last one stored in database *****/
@ -83,7 +432,7 @@ bool ExaLog_DB_CheckIfSessionIsTheSameAsTheLast (long PrnCod)
/******** Check if the current user agent is the same as the last one ********/ /******** Check if the current user agent is the same as the last one ********/
/*****************************************************************************/ /*****************************************************************************/
bool ExaLog_DB_CheckIfUserAgentIsTheSameAsTheLast (long PrnCod,const char *UserAgentDB) bool Exa_DB_CheckIfUserAgentIsTheSameAsTheLast (long PrnCod,const char *UserAgentDB)
{ {
/***** Get if the current user agent /***** Get if the current user agent
is the same as the last stored in database *****/ is the same as the last stored in database *****/
@ -103,7 +452,7 @@ bool ExaLog_DB_CheckIfUserAgentIsTheSameAsTheLast (long PrnCod,const char *UserA
/******************************** Log access *********************************/ /******************************** Log access *********************************/
/*****************************************************************************/ /*****************************************************************************/
void ExaLog_DB_LogAccess (long LogCod,long PrnCod,ExaLog_Action_t Action) void Exa_DB_LogAccess (long LogCod,long PrnCod,ExaLog_Action_t Action)
{ {
/* Log access in exam log. /* Log access in exam log.
Redundant data (also present in log table) are stored for speed */ Redundant data (also present in log table) are stored for speed */
@ -126,7 +475,7 @@ void ExaLog_DB_LogAccess (long LogCod,long PrnCod,ExaLog_Action_t Action)
/*************************** Log session in database *************************/ /*************************** Log session in database *************************/
/*****************************************************************************/ /*****************************************************************************/
void ExaLog_DB_LogSession (long LogCod,long PrnCod) void Exa_DB_LogSession (long LogCod,long PrnCod)
{ {
DB_QueryINSERT ("can not log session", DB_QueryINSERT ("can not log session",
"INSERT INTO exa_log_sessions " "INSERT INTO exa_log_sessions "
@ -142,7 +491,7 @@ void ExaLog_DB_LogSession (long LogCod,long PrnCod)
/************************* Log user agent in database ************************/ /************************* Log user agent in database ************************/
/*****************************************************************************/ /*****************************************************************************/
void ExaLog_DB_LogUserAgent (long LogCod,long PrnCod,const char *UserAgentDB) void Exa_DB_LogUserAgent (long LogCod,long PrnCod,const char *UserAgentDB)
{ {
DB_QueryINSERT ("can not log user agent", DB_QueryINSERT ("can not log user agent",
"INSERT INTO exa_log_user_agents " "INSERT INTO exa_log_user_agents "
@ -153,3 +502,28 @@ void ExaLog_DB_LogUserAgent (long LogCod,long PrnCod,const char *UserAgentDB)
PrnCod, PrnCod,
UserAgentDB); UserAgentDB);
} }
/*****************************************************************************/
/********************* Get exam print log from database **********************/
/*****************************************************************************/
unsigned Exa_DB_GetExamLog (MYSQL_RES **mysql_res,long PrnCod)
{
return (unsigned)
DB_QuerySELECT (mysql_res,"can not get exam print log",
"SELECT exa_log.ActCod," // row[0]
"exa_log.QstInd," // row[1]
"exa_log.CanAnswer," // row[2]
"UNIX_TIMESTAMP(exa_log.ClickTime)," // row[3]
"exa_log.IP," // row[4]
"exa_log_sessions.SessionId," // row[5]
"exa_log_user_agents.UserAgent" // row[6]
" FROM exa_log"
" LEFT JOIN exa_log_sessions"
" ON exa_log.LogCod=exa_log_sessions.LogCod"
" LEFT JOIN exa_log_user_agents"
" ON exa_log.LogCod=exa_log_user_agents.LogCod"
" WHERE exa_log.PrnCod=%ld"
" ORDER BY exa_log.LogCod",
PrnCod);
}

View File

@ -28,7 +28,8 @@
/*****************************************************************************/ /*****************************************************************************/
#include "swad_exam_log.h" #include "swad_exam_log.h"
// #include "swad_exam_print.h" #include "swad_exam_print.h"
#include "swad_test_type.h"
/*****************************************************************************/ /*****************************************************************************/
/************************* Public types and constants ************************/ /************************* Public types and constants ************************/
@ -38,11 +39,39 @@
/***************************** Public prototypes *****************************/ /***************************** Public prototypes *****************************/
/*****************************************************************************/ /*****************************************************************************/
bool ExaLog_DB_CheckIfSessionIsTheSameAsTheLast (long PrnCod); unsigned Exa_DB_GetExamSets (MYSQL_RES **mysql_res,long ExaCod);
bool ExaLog_DB_CheckIfUserAgentIsTheSameAsTheLast (long PrnCod,const char *UserAgentDB); unsigned Exa_DB_GetSomeQstsFromSetToPrint (MYSQL_RES **mysql_res,
long SetCod,unsigned NumQstsToPrint);
unsigned Exa_DB_GetQstAnswersTextFromSet (MYSQL_RES **mysql_res,long QstCod);
unsigned Exa_DB_GetQstAnswersCorrFromSet (MYSQL_RES **mysql_res,long QstCod);
void ExaLog_DB_LogAccess (long LogCod,long PrnCod,ExaLog_Action_t Action); long Exa_DB_CreatePrint (const struct ExaPrn_Print *Print);
void ExaLog_DB_LogSession (long LogCod,long PrnCod); void Exa_DB_UpdatePrint (const struct ExaPrn_Print *Print);
void ExaLog_DB_LogUserAgent (long LogCod,long PrnCod,const char *UserAgentDB); unsigned Exa_DB_GetDataOfPrintByPrnCod (MYSQL_RES **mysql_res,long PrnCod);
unsigned Exa_DB_GetDataOfPrintBySesCodAndUsrCod (MYSQL_RES **mysql_res,
long SesCod,long UsrCod);
void Exa_DB_RemovePrintsMadeByUsrInAllCrss (long UsrCod);
void Exa_DB_RemovePrintsMadeByUsrInCrs (long UsrCod,long CrsCod);
void Exa_DB_RemovePrintsInCrs (long CrsCod);
void Exa_DB_StoreOneQstOfPrint (const struct ExaPrn_Print *Print,
unsigned QstInd);
unsigned Exa_DB_GetPrintQuestions (MYSQL_RES **mysql_res,long PrnCod);
void Exa_DB_GetAnswersFromQstInPrint (long PrnCod,long QstCod,
char StrAnswers[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1]);
unsigned Exa_DB_GetNumQstsNotBlankInPrint (long PrnCod);
double Exa_DB_ComputeTotalScoreOfPrint (long PrnCod);
void Exa_DB_RemovePrintQuestionsMadeByUsrInAllCrss (long UsrCod);
void Exa_DB_RemovePrintsQuestionsMadeByUsrInCrs (long UsrCod,long CrsCod);
void Exa_DB_RemovePrintQuestionsInCrs (long CrsCod);
bool Exa_DB_CheckIfSessionIsTheSameAsTheLast (long PrnCod);
bool Exa_DB_CheckIfUserAgentIsTheSameAsTheLast (long PrnCod,const char *UserAgentDB);
void Exa_DB_LogAccess (long LogCod,long PrnCod,ExaLog_Action_t Action);
void Exa_DB_LogSession (long LogCod,long PrnCod);
void Exa_DB_LogUserAgent (long LogCod,long PrnCod,const char *UserAgentDB);
unsigned Exa_DB_GetExamLog (MYSQL_RES **mysql_res,long PrnCod);
#endif #endif

View File

@ -151,7 +151,7 @@ void ExaLog_LogAccess (long LogCod)
/***** Insert access into database *****/ /***** Insert access into database *****/
/* Log access in exam log. /* Log access in exam log.
Redundant data (also present in log table) are stored for speed */ Redundant data (also present in log table) are stored for speed */
ExaLog_DB_LogAccess (LogCod,PrnCod,Action); Exa_DB_LogAccess (LogCod,PrnCod,Action);
/***** Log session and user agent *****/ /***** Log session and user agent *****/
ExaLog_LogSession (LogCod,PrnCod); ExaLog_LogSession (LogCod,PrnCod);
@ -168,8 +168,8 @@ static void ExaLog_LogSession (long LogCod,long PrnCod)
{ {
/***** Insert session id into database /***** Insert session id into database
only if it's not the same as the last one stored *****/ only if it's not the same as the last one stored *****/
if (!ExaLog_DB_CheckIfSessionIsTheSameAsTheLast (PrnCod)) if (!Exa_DB_CheckIfSessionIsTheSameAsTheLast (PrnCod))
ExaLog_DB_LogSession (LogCod,PrnCod); Exa_DB_LogSession (LogCod,PrnCod);
} }
/*****************************************************************************/ /*****************************************************************************/
@ -209,8 +209,8 @@ static void ExaLog_LogUsrAgent (long LogCod,long PrnCod)
/***** Insert user agent into database /***** Insert user agent into database
only if it's not the same as the last one stored *****/ only if it's not the same as the last one stored *****/
if (!ExaLog_DB_CheckIfUserAgentIsTheSameAsTheLast (PrnCod,UserAgentDB)) if (!Exa_DB_CheckIfUserAgentIsTheSameAsTheLast (PrnCod,UserAgentDB))
ExaLog_DB_LogUserAgent (LogCod,PrnCod,UserAgentDB); Exa_DB_LogUserAgent (LogCod,PrnCod,UserAgentDB);
/***** Free user agent *****/ /***** Free user agent *****/
free (UserAgentDB); free (UserAgentDB);
@ -251,25 +251,7 @@ void ExaLog_ShowExamLog (const struct ExaPrn_Print *Print)
const char *Class; const char *Class;
/***** Get print log from database *****/ /***** Get print log from database *****/
NumClicks = (unsigned) if ((NumClicks = Exa_DB_GetExamLog (&mysql_res,Print->PrnCod)))
DB_QuerySELECT (&mysql_res,"can not get exam print log",
"SELECT exa_log.ActCod," // row[0]
"exa_log.QstInd," // row[1]
"exa_log.CanAnswer," // row[2]
"UNIX_TIMESTAMP(exa_log.ClickTime)," // row[3]
"exa_log.IP," // row[4]
"exa_log_sessions.SessionId," // row[5]
"exa_log_user_agents.UserAgent" // row[6]
" FROM exa_log"
" LEFT JOIN exa_log_sessions"
" ON exa_log.LogCod=exa_log_sessions.LogCod"
" LEFT JOIN exa_log_user_agents"
" ON exa_log.LogCod=exa_log_user_agents.LogCod"
" WHERE exa_log.PrnCod=%ld"
" ORDER BY exa_log.LogCod",
Print->PrnCod);
if (NumClicks)
{ {
/***** Initialize last session id and last user agent ******/ /***** Initialize last session id and last user agent ******/
SessionId[0] = '\0'; SessionId[0] = '\0';

View File

@ -35,6 +35,7 @@
#include "swad_database.h" #include "swad_database.h"
#include "swad_error.h" #include "swad_error.h"
#include "swad_exam.h" #include "swad_exam.h"
#include "swad_exam_database.h"
#include "swad_exam_log.h" #include "swad_exam_log.h"
#include "swad_exam_print.h" #include "swad_exam_print.h"
#include "swad_exam_result.h" #include "swad_exam_result.h"
@ -75,10 +76,10 @@ static void ExaPrn_GetDataOfPrint (struct ExaPrn_Print *Print,
static void ExaPrn_GetQuestionsForNewPrintFromDB (struct ExaPrn_Print *Print,long ExaCod); static void ExaPrn_GetQuestionsForNewPrintFromDB (struct ExaPrn_Print *Print,long ExaCod);
static unsigned ExaPrn_GetSomeQstsFromSetToPrint (struct ExaPrn_Print *Print, static unsigned ExaPrn_GetSomeQstsFromSetToPrint (struct ExaPrn_Print *Print,
struct ExaSet_Set *Set, struct ExaSet_Set *Set,
unsigned *NumQstInPrint); unsigned *NumQstsInPrint);
static void ExaPrn_GenerateChoiceIndexes (struct TstPrn_PrintedQuestion *PrintedQuestion, static void ExaPrn_GenerateChoiceIndexes (struct TstPrn_PrintedQuestion *PrintedQuestion,
bool Shuffle); bool Shuffle);
static void ExaPrn_CreatePrintInDB (struct ExaPrn_Print *Print); static void ExaPrn_CreatePrint (struct ExaPrn_Print *Print);
static void ExaPrn_ShowExamPrintToFillIt (struct Exa_Exams *Exams, static void ExaPrn_ShowExamPrintToFillIt (struct Exa_Exams *Exams,
const struct Exa_Exam *Exam, const struct Exa_Exam *Exam,
@ -141,15 +142,6 @@ static void ExaPrn_GetCorrectChoAnswerFromDB (struct Tst_Question *Question);
static void ExaPrn_GetCorrectTxtAnswerFromDB (struct Tst_Question *Question); static void ExaPrn_GetCorrectTxtAnswerFromDB (struct Tst_Question *Question);
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
static void ExaPrn_GetAnswerFromDB (struct ExaPrn_Print *Print,long QstCod,
char StrAnswers[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1]);
static void ExaPrn_StoreOneQstOfPrintInDB (const struct ExaPrn_Print *Print,
unsigned QstInd);
static unsigned Exa_DB_GetNumQstsNotBlankInPrint (long PrnCod);
static double Exa_DB_ComputeTotalScoreOfPrint (long PrnCod);
static void Exa_DB_UpdatePrint (const struct ExaPrn_Print *Print);
/*****************************************************************************/ /*****************************************************************************/
/**************************** Reset exam print *******************************/ /**************************** Reset exam print *******************************/
/*****************************************************************************/ /*****************************************************************************/
@ -216,7 +208,7 @@ void ExaPrn_ShowExamPrint (void)
if (Print.NumQsts.All) if (Print.NumQsts.All)
{ {
/***** Create new exam print in database *****/ /***** Create new exam print in database *****/
ExaPrn_CreatePrintInDB (&Print); ExaPrn_CreatePrint (&Print);
/***** Set log print code and action *****/ /***** Set log print code and action *****/
ExaLog_SetPrnCod (Print.PrnCod); ExaLog_SetPrnCod (Print.PrnCod);
@ -256,20 +248,7 @@ void ExaPrn_GetDataOfPrintByPrnCod (struct ExaPrn_Print *Print)
unsigned NumPrints; unsigned NumPrints;
/***** Make database query *****/ /***** Make database query *****/
NumPrints = (unsigned) NumPrints = Exa_DB_GetDataOfPrintByPrnCod (&mysql_res,Print->PrnCod);
DB_QuerySELECT (&mysql_res,"can not get data of an exam print",
"SELECT PrnCod," // row[0]
"SesCod," // row[1]
"UsrCod," // row[2]
"UNIX_TIMESTAMP(StartTime)," // row[3]
"UNIX_TIMESTAMP(EndTime)," // row[4]
"NumQsts," // row[5]
"NumQstsNotBlank," // row[6]
"Sent," // row[7]
"Score" // row[8]
" FROM exa_prints"
" WHERE PrnCod=%ld",
Print->PrnCod);
/***** Get data of print *****/ /***** Get data of print *****/
ExaPrn_GetDataOfPrint (Print,&mysql_res,NumPrints); ExaPrn_GetDataOfPrint (Print,&mysql_res,NumPrints);
@ -285,22 +264,9 @@ void ExaPrn_GetDataOfPrintBySesCodAndUsrCod (struct ExaPrn_Print *Print)
unsigned NumPrints; unsigned NumPrints;
/***** Make database query *****/ /***** Make database query *****/
NumPrints = (unsigned) NumPrints = Exa_DB_GetDataOfPrintBySesCodAndUsrCod (&mysql_res,
DB_QuerySELECT (&mysql_res,"can not get data of an exam print", Print->SesCod,
"SELECT PrnCod," // row[0] Print->UsrCod);
"SesCod," // row[1]
"UsrCod," // row[2]
"UNIX_TIMESTAMP(StartTime)," // row[3]
"UNIX_TIMESTAMP(EndTime)," // row[4]
"NumQsts," // row[5]
"NumQstsNotBlank," // row[6]
"Sent," // row[7]
"Score" // row[8]
" FROM exa_prints"
" WHERE SesCod=%ld"
" AND UsrCod=%ld",
Print->SesCod,
Print->UsrCod);
/***** Get data of print *****/ /***** Get data of print *****/
ExaPrn_GetDataOfPrint (Print,&mysql_res,NumPrints); ExaPrn_GetDataOfPrint (Print,&mysql_res,NumPrints);
@ -369,22 +335,11 @@ static void ExaPrn_GetQuestionsForNewPrintFromDB (struct ExaPrn_Print *Print,lon
unsigned NumSet; unsigned NumSet;
struct ExaSet_Set Set; struct ExaSet_Set Set;
unsigned NumQstsFromSet; unsigned NumQstsFromSet;
unsigned NumQstInPrint = 0; unsigned NumQstsInPrint = 0;
/***** Get data of set of questions from database *****/
NumSets = (unsigned)
DB_QuerySELECT (&mysql_res,"can not get sets of questions",
"SELECT SetCod," // row[0]
"NumQstsToPrint," // row[1]
"Title" // row[2]
" FROM exa_sets"
" WHERE ExaCod=%ld"
" ORDER BY SetInd",
ExaCod);
/***** Get questions from all sets *****/ /***** Get questions from all sets *****/
Print->NumQsts.All = 0; Print->NumQsts.All = 0;
if (NumSets) if ((NumSets = Exa_DB_GetExamSets (&mysql_res,ExaCod)))
/***** For each set in exam... *****/ /***** For each set in exam... *****/
for (NumSet = 0; for (NumSet = 0;
NumSet < NumSets; NumSet < NumSets;
@ -400,22 +355,21 @@ static void ExaPrn_GetQuestionsForNewPrintFromDB (struct ExaPrn_Print *Print,lon
row[1] NumQstsToPrint row[1] NumQstsToPrint
row[2] Title row[2] Title
*/ */
/* Get set code (row[0]) */ /* Get set code (row[0])
Set.SetCod = Str_ConvertStrCodToLongCod (row[0]); and set index (row[1]) */
Set.SetCod = Str_ConvertStrCodToLongCod (row[0]);
/* Get set index (row[1]) */
Set.NumQstsToPrint = Str_ConvertStrToUnsigned (row[1]); Set.NumQstsToPrint = Str_ConvertStrToUnsigned (row[1]);
/* Get the title of the set (row[2]) */ /* Get the title of the set (row[2]) */
Str_Copy (Set.Title,row[2],sizeof (Set.Title) - 1); Str_Copy (Set.Title,row[2],sizeof (Set.Title) - 1);
/***** Questions in this set *****/ /***** Questions in this set *****/
NumQstsFromSet = ExaPrn_GetSomeQstsFromSetToPrint (Print,&Set,&NumQstInPrint); NumQstsFromSet = ExaPrn_GetSomeQstsFromSetToPrint (Print,&Set,&NumQstsInPrint);
Print->NumQsts.All += NumQstsFromSet; Print->NumQsts.All += NumQstsFromSet;
} }
/***** Check *****/ /***** Check *****/
if (Print->NumQsts.All != NumQstInPrint) if (Print->NumQsts.All != NumQstsInPrint)
Err_ShowErrorAndExit ("Wrong number of questions."); Err_ShowErrorAndExit ("Wrong number of questions.");
/***** Free structure that stores the query result *****/ /***** Free structure that stores the query result *****/
@ -423,12 +377,12 @@ static void ExaPrn_GetQuestionsForNewPrintFromDB (struct ExaPrn_Print *Print,lon
} }
/*****************************************************************************/ /*****************************************************************************/
/************************ Show questions from a set **************************/ /********************** Get some questions from a set ************************/
/*****************************************************************************/ /*****************************************************************************/
static unsigned ExaPrn_GetSomeQstsFromSetToPrint (struct ExaPrn_Print *Print, static unsigned ExaPrn_GetSomeQstsFromSetToPrint (struct ExaPrn_Print *Print,
struct ExaSet_Set *Set, struct ExaSet_Set *Set,
unsigned *NumQstInPrint) unsigned *NumQstsInPrint)
{ {
MYSQL_RES *mysql_res; MYSQL_RES *mysql_res;
MYSQL_ROW row; MYSQL_ROW row;
@ -438,22 +392,14 @@ static unsigned ExaPrn_GetSomeQstsFromSetToPrint (struct ExaPrn_Print *Print,
bool Shuffle; bool Shuffle;
/***** Get questions from database *****/ /***** Get questions from database *****/
NumQstsInSet = (unsigned) NumQstsInSet = Exa_DB_GetSomeQstsFromSetToPrint (&mysql_res,
DB_QuerySELECT (&mysql_res,"can not get questions from set", Set->SetCod,
"SELECT QstCod," // row[0] Set->NumQstsToPrint);
"AnsType," // row[1]
"Shuffle" // row[2]
" FROM exa_set_questions"
" WHERE SetCod=%ld"
" ORDER BY RAND()" // Don't use RAND(NOW()) because the same ordering will be repeated across sets
" LIMIT %u",
Set->SetCod,
Set->NumQstsToPrint);
/***** Questions in this set *****/ /***** Questions in this set *****/
for (NumQstInSet = 0; for (NumQstInSet = 0;
NumQstInSet < NumQstsInSet; NumQstInSet < NumQstsInSet;
NumQstInSet++, (*NumQstInPrint)++) NumQstInSet++, (*NumQstsInPrint)++)
{ {
Gbl.RowEvenOdd = 1 - Gbl.RowEvenOdd; Gbl.RowEvenOdd = 1 - Gbl.RowEvenOdd;
@ -466,10 +412,10 @@ static unsigned ExaPrn_GetSomeQstsFromSetToPrint (struct ExaPrn_Print *Print,
*/ */
/* Get question code (row[0]) */ /* Get question code (row[0]) */
Print->PrintedQuestions[*NumQstInPrint].QstCod = Str_ConvertStrCodToLongCod (row[0]); Print->PrintedQuestions[*NumQstsInPrint].QstCod = Str_ConvertStrCodToLongCod (row[0]);
/* Set set of questions */ /* Set set of questions */
Print->PrintedQuestions[*NumQstInPrint].SetCod = Set->SetCod; Print->PrintedQuestions[*NumQstsInPrint].SetCod = Set->SetCod;
/* Get answer type (row[1]) */ /* Get answer type (row[1]) */
AnswerType = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[1]); AnswerType = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[1]);
@ -484,13 +430,13 @@ static unsigned ExaPrn_GetSomeQstsFromSetToPrint (struct ExaPrn_Print *Print,
case Tst_ANS_FLOAT: case Tst_ANS_FLOAT:
case Tst_ANS_TRUE_FALSE: case Tst_ANS_TRUE_FALSE:
case Tst_ANS_TEXT: case Tst_ANS_TEXT:
Print->PrintedQuestions[*NumQstInPrint].StrIndexes[0] = '\0'; Print->PrintedQuestions[*NumQstsInPrint].StrIndexes[0] = '\0';
break; break;
case Tst_ANS_UNIQUE_CHOICE: case Tst_ANS_UNIQUE_CHOICE:
case Tst_ANS_MULTIPLE_CHOICE: case Tst_ANS_MULTIPLE_CHOICE:
/* If answer type is unique or multiple option, /* If answer type is unique or multiple option,
generate indexes of answers depending on shuffle */ generate indexes of answers depending on shuffle */
ExaPrn_GenerateChoiceIndexes (&Print->PrintedQuestions[*NumQstInPrint],Shuffle); ExaPrn_GenerateChoiceIndexes (&Print->PrintedQuestions[*NumQstsInPrint],Shuffle);
break; break;
default: default:
break; break;
@ -500,10 +446,10 @@ static unsigned ExaPrn_GetSomeQstsFromSetToPrint (struct ExaPrn_Print *Print,
Initially user has not answered the question ==> initially all the answers will be blank. Initially user has not answered the question ==> initially all the answers will be blank.
If the user does not confirm the submission of their exam ==> If the user does not confirm the submission of their exam ==>
==> the exam may be half filled ==> the answers displayed will be those selected by the user. */ ==> the exam may be half filled ==> the answers displayed will be those selected by the user. */
Print->PrintedQuestions[*NumQstInPrint].StrAnswers[0] = '\0'; Print->PrintedQuestions[*NumQstsInPrint].StrAnswers[0] = '\0';
/* Reset score of this question in print */ /* Reset score of this question in print */
Print->PrintedQuestions[*NumQstInPrint].Score = 0.0; Print->PrintedQuestions[*NumQstsInPrint].Score = 0.0;
} }
return NumQstsInSet; return NumQstsInSet;
@ -578,29 +524,19 @@ static void ExaPrn_GenerateChoiceIndexes (struct TstPrn_PrintedQuestion *Printed
/***************** Create new blank exam print in database *******************/ /***************** Create new blank exam print in database *******************/
/*****************************************************************************/ /*****************************************************************************/
static void ExaPrn_CreatePrintInDB (struct ExaPrn_Print *Print) static void ExaPrn_CreatePrint (struct ExaPrn_Print *Print)
{ {
unsigned QstInd; unsigned QstInd;
/***** Insert new exam print into table *****/ /***** Insert new exam print into database *****/
Print->PrnCod = Print->PrnCod = Exa_DB_CreatePrint (Print);
DB_QueryINSERTandReturnCode ("can not create new exam print",
"INSERT INTO exa_prints"
" (SesCod,UsrCod,StartTime,EndTime,"
"NumQsts,NumQstsNotBlank,Sent,Score)"
" VALUES"
" (%ld,%ld,NOW(),NOW(),"
"%u,0,'N',0)",
Print->SesCod,
Print->UsrCod,
Print->NumQsts.All);
/***** Store all questions (with blank answers) /***** Store all questions (with blank answers)
of this exam print just generated in database *****/ of this exam print just generated in database *****/
for (QstInd = 0; for (QstInd = 0;
QstInd < Print->NumQsts.All; QstInd < Print->NumQsts.All;
QstInd++) QstInd++)
ExaPrn_StoreOneQstOfPrintInDB (Print,QstInd); Exa_DB_StoreOneQstOfPrint (Print,QstInd);
} }
/*****************************************************************************/ /*****************************************************************************/
@ -614,31 +550,18 @@ void ExaPrn_GetPrintQuestionsFromDB (struct ExaPrn_Print *Print)
unsigned QstInd; unsigned QstInd;
/***** Get questions of an exam print from database *****/ /***** Get questions of an exam print from database *****/
Print->NumQsts.All = (unsigned) if ((Print->NumQsts.All = Exa_DB_GetPrintQuestions (&mysql_res,Print->PrnCod))
DB_QuerySELECT (&mysql_res,"can not get questions of an exam print", <= ExaPrn_MAX_QUESTIONS_PER_EXAM_PRINT)
"SELECT QstCod," // row[0]
"SetCod," // row[1]
"Score," // row[2]
"Indexes," // row[3]
"Answers" // row[4]
" FROM exa_print_questions"
" WHERE PrnCod=%ld"
" ORDER BY QstInd",
Print->PrnCod);
/***** Get questions *****/
if (Print->NumQsts.All <= ExaPrn_MAX_QUESTIONS_PER_EXAM_PRINT)
for (QstInd = 0; for (QstInd = 0;
QstInd < Print->NumQsts.All; QstInd < Print->NumQsts.All;
QstInd++) QstInd++)
{ {
row = mysql_fetch_row (mysql_res); row = mysql_fetch_row (mysql_res);
/* Get question code (row[0]) */ /* Get question code (row[0])
and set code (row[1]) */
if ((Print->PrintedQuestions[QstInd].QstCod = Str_ConvertStrCodToLongCod (row[0])) <= 0) if ((Print->PrintedQuestions[QstInd].QstCod = Str_ConvertStrCodToLongCod (row[0])) <= 0)
Err_WrongQuestionExit (); Err_WrongQuestionExit ();
/* Get set code (row[1]) */
if ((Print->PrintedQuestions[QstInd].SetCod = Str_ConvertStrCodToLongCod (row[1])) <= 0) if ((Print->PrintedQuestions[QstInd].SetCod = Str_ConvertStrCodToLongCod (row[1])) <= 0)
Err_WrongSetExit (); Err_WrongSetExit ();
@ -648,11 +571,10 @@ void ExaPrn_GetPrintQuestionsFromDB (struct ExaPrn_Print *Print)
Err_ShowErrorAndExit ("Wrong question score."); Err_ShowErrorAndExit ("Wrong question score.");
Str_SetDecimalPointToLocal (); // Return to local system Str_SetDecimalPointToLocal (); // Return to local system
/* Get indexes for this question (row[3]) */ /* Get indexes for this question (row[3])
and answers selected by user for this question (row[4]) */
Str_Copy (Print->PrintedQuestions[QstInd].StrIndexes,row[3], Str_Copy (Print->PrintedQuestions[QstInd].StrIndexes,row[3],
sizeof (Print->PrintedQuestions[QstInd].StrIndexes) - 1); sizeof (Print->PrintedQuestions[QstInd].StrIndexes) - 1);
/* Get answers selected by user for this question (row[4]) */
Str_Copy (Print->PrintedQuestions[QstInd].StrAnswers,row[4], Str_Copy (Print->PrintedQuestions[QstInd].StrAnswers,row[4],
sizeof (Print->PrintedQuestions[QstInd].StrAnswers) - 1); sizeof (Print->PrintedQuestions[QstInd].StrAnswers) - 1);
} }
@ -914,9 +836,9 @@ static void ExaPrn_WriteTF_AnsToFill (const struct ExaPrn_Print *Print,
HTM_TxtF ("<select id=\"%s\" name=\"Ans\"",Id); HTM_TxtF ("<select id=\"%s\" name=\"Ans\"",Id);
ExaPrn_WriteJSToUpdateExamPrint (Print,QstInd,Id,-1); ExaPrn_WriteJSToUpdateExamPrint (Print,QstInd,Id,-1);
HTM_Txt (" />"); HTM_Txt (" />");
HTM_OPTION (HTM_Type_STRING,"" ,Print->PrintedQuestions[QstInd].StrAnswers[0] == '\0',false,"&nbsp;"); HTM_OPTION (HTM_Type_STRING,"" ,Print->PrintedQuestions[QstInd].StrAnswers[0] == '\0',false,"&nbsp;");
HTM_OPTION (HTM_Type_STRING,"T",Print->PrintedQuestions[QstInd].StrAnswers[0] == 'T' ,false,"%s",Txt_TF_QST[0]); HTM_OPTION (HTM_Type_STRING,"T",Print->PrintedQuestions[QstInd].StrAnswers[0] == 'T' ,false,"%s",Txt_TF_QST[0]);
HTM_OPTION (HTM_Type_STRING,"F",Print->PrintedQuestions[QstInd].StrAnswers[0] == 'F' ,false,"%s",Txt_TF_QST[1]); HTM_OPTION (HTM_Type_STRING,"F",Print->PrintedQuestions[QstInd].StrAnswers[0] == 'F' ,false,"%s",Txt_TF_QST[1]);
HTM_Txt ("</select>"); HTM_Txt ("</select>");
} }
@ -953,37 +875,37 @@ static void ExaPrn_WriteChoAnsToFill (const struct ExaPrn_Print *Print,
or 3 1 0 2... (example) if shuffle *****/ or 3 1 0 2... (example) if shuffle *****/
HTM_TR_Begin (NULL); HTM_TR_Begin (NULL);
/***** Write selectors and letter of this option *****/ /***** Write selectors and letter of this option *****/
/* Initially user has not answered the question ==> initially all the answers will be blank. /* Initially user has not answered the question ==> initially all the answers will be blank.
If the user does not confirm the submission of their exam ==> If the user does not confirm the submission of their exam ==>
==> the exam may be half filled ==> the answers displayed will be those selected by the user. */ ==> the exam may be half filled ==> the answers displayed will be those selected by the user. */
HTM_TD_Begin ("class=\"LT\""); HTM_TD_Begin ("class=\"LT\"");
snprintf (Id,sizeof (Id),"Ans%010u",QstInd); snprintf (Id,sizeof (Id),"Ans%010u",QstInd);
HTM_TxtF ("<input type=\"%s\" id=\"%s_%u\" name=\"Ans\" value=\"%u\"%s", HTM_TxtF ("<input type=\"%s\" id=\"%s_%u\" name=\"Ans\" value=\"%u\"%s",
Question->Answer.Type == Tst_ANS_UNIQUE_CHOICE ? "radio" : Question->Answer.Type == Tst_ANS_UNIQUE_CHOICE ? "radio" :
"checkbox", "checkbox",
Id,NumOpt,Indexes[NumOpt], Id,NumOpt,Indexes[NumOpt],
UsrAnswers[Indexes[NumOpt]] ? " checked=\"checked\"" : UsrAnswers[Indexes[NumOpt]] ? " checked=\"checked\"" :
""); "");
ExaPrn_WriteJSToUpdateExamPrint (Print,QstInd,Id,(int) NumOpt); ExaPrn_WriteJSToUpdateExamPrint (Print,QstInd,Id,(int) NumOpt);
HTM_Txt (" />"); HTM_Txt (" />");
HTM_TD_End (); HTM_TD_End ();
HTM_TD_Begin ("class=\"LT\""); HTM_TD_Begin ("class=\"LT\"");
HTM_LABEL_Begin ("for=\"Ans%010u_%u\" class=\"TEST_TXT\"",QstInd,NumOpt); HTM_LABEL_Begin ("for=\"Ans%010u_%u\" class=\"TEST_TXT\"",QstInd,NumOpt);
HTM_TxtF ("%c)&nbsp;",'a' + (char) NumOpt); HTM_TxtF ("%c)&nbsp;",'a' + (char) NumOpt);
HTM_LABEL_End (); HTM_LABEL_End ();
HTM_TD_End (); HTM_TD_End ();
/***** Write the option text *****/ /***** Write the option text *****/
HTM_TD_Begin ("class=\"LT\""); HTM_TD_Begin ("class=\"LT\"");
HTM_LABEL_Begin ("for=\"Ans%010u_%u\" class=\"TEST_TXT\"",QstInd,NumOpt); HTM_LABEL_Begin ("for=\"Ans%010u_%u\" class=\"TEST_TXT\"",QstInd,NumOpt);
HTM_Txt (Question->Answer.Options[Indexes[NumOpt]].Text); HTM_Txt (Question->Answer.Options[Indexes[NumOpt]].Text);
HTM_LABEL_End (); HTM_LABEL_End ();
Med_ShowMedia (&Question->Answer.Options[Indexes[NumOpt]].Media, Med_ShowMedia (&Question->Answer.Options[Indexes[NumOpt]].Media,
"TEST_MED_SHOW_CONT", "TEST_MED_SHOW_CONT",
"TEST_MED_SHOW"); "TEST_MED_SHOW");
HTM_TD_End (); HTM_TD_End ();
HTM_TR_End (); HTM_TR_End ();
} }
@ -1009,7 +931,6 @@ static void ExaPrn_WriteTxtAnsToFill (const struct ExaPrn_Print *Print,
Id,Tst_MAX_CHARS_ANSWERS_ONE_QST, Id,Tst_MAX_CHARS_ANSWERS_ONE_QST,
Print->PrintedQuestions[QstInd].StrAnswers); Print->PrintedQuestions[QstInd].StrAnswers);
ExaPrn_WriteJSToUpdateExamPrint (Print,QstInd,Id,-1); ExaPrn_WriteJSToUpdateExamPrint (Print,QstInd,Id,-1);
HTM_Txt (" />"); HTM_Txt (" />");
} }
@ -1174,8 +1095,8 @@ static void ExaPrn_ComputeScoreAndStoreQuestionOfPrint (struct ExaPrn_Print *Pri
==> uncheck it by deleting answer *****/ ==> uncheck it by deleting answer *****/
if (Question.Answer.Type == Tst_ANS_UNIQUE_CHOICE) if (Question.Answer.Type == Tst_ANS_UNIQUE_CHOICE)
{ {
ExaPrn_GetAnswerFromDB (Print,Print->PrintedQuestions[QstInd].QstCod, Exa_DB_GetAnswersFromQstInPrint (Print->PrnCod,Print->PrintedQuestions[QstInd].QstCod,
CurrentStrAnswersInDB); CurrentStrAnswersInDB);
if (!strcmp (Print->PrintedQuestions[QstInd].StrAnswers,CurrentStrAnswersInDB)) if (!strcmp (Print->PrintedQuestions[QstInd].StrAnswers,CurrentStrAnswersInDB))
{ {
/* The answer just clicked by user /* The answer just clicked by user
@ -1186,8 +1107,8 @@ static void ExaPrn_ComputeScoreAndStoreQuestionOfPrint (struct ExaPrn_Print *Pri
} }
/***** Store test exam question in database *****/ /***** Store test exam question in database *****/
ExaPrn_StoreOneQstOfPrintInDB (Print, Exa_DB_StoreOneQstOfPrint (Print,
QstInd); // 0, 1, 2, 3... QstInd); // 0, 1, 2, 3...
} }
/*****************************************************************************/ /*****************************************************************************/
@ -1271,12 +1192,7 @@ static void ExaPrn_GetCorrectIntAnswerFromDB (struct Tst_Question *Question)
MYSQL_ROW row; MYSQL_ROW row;
/***** Query database *****/ /***** Query database *****/
Question->Answer.NumOptions = (unsigned) Question->Answer.NumOptions = Exa_DB_GetQstAnswersTextFromSet (&mysql_res,Question->QstCod);
DB_QuerySELECT (&mysql_res,"can not get answers of a question",
"SELECT Answer" // row[0]
" FROM exa_set_answers"
" WHERE QstCod=%ld",
Question->QstCod);
/***** Check if number of rows is correct *****/ /***** Check if number of rows is correct *****/
Tst_CheckIfNumberOfAnswersIsOne (Question); Tst_CheckIfNumberOfAnswersIsOne (Question);
@ -1298,12 +1214,7 @@ static void ExaPrn_GetCorrectFltAnswerFromDB (struct Tst_Question *Question)
double Tmp; double Tmp;
/***** Query database *****/ /***** Query database *****/
Question->Answer.NumOptions = (unsigned) Question->Answer.NumOptions = Exa_DB_GetQstAnswersTextFromSet (&mysql_res,Question->QstCod);
DB_QuerySELECT (&mysql_res,"can not get answers of a question",
"SELECT Answer" // row[0]
" FROM exa_set_answers"
" WHERE QstCod=%ld",
Question->QstCod);
/***** Check if number of rows is correct *****/ /***** Check if number of rows is correct *****/
if (Question->Answer.NumOptions != 2) if (Question->Answer.NumOptions != 2)
@ -1336,12 +1247,7 @@ static void ExaPrn_GetCorrectTF_AnswerFromDB (struct Tst_Question *Question)
MYSQL_ROW row; MYSQL_ROW row;
/***** Query database *****/ /***** Query database *****/
Question->Answer.NumOptions = (unsigned) Question->Answer.NumOptions = Exa_DB_GetQstAnswersTextFromSet (&mysql_res,Question->QstCod);
DB_QuerySELECT (&mysql_res,"can not get answers of a question",
"SELECT Answer" // row[0]
" FROM exa_set_answers"
" WHERE QstCod=%ld",
Question->QstCod);
/***** Check if number of rows is correct *****/ /***** Check if number of rows is correct *****/
Tst_CheckIfNumberOfAnswersIsOne (Question); Tst_CheckIfNumberOfAnswersIsOne (Question);
@ -1361,13 +1267,7 @@ static void ExaPrn_GetCorrectChoAnswerFromDB (struct Tst_Question *Question)
unsigned NumOpt; unsigned NumOpt;
/***** Query database *****/ /***** Query database *****/
Question->Answer.NumOptions = (unsigned) Question->Answer.NumOptions = Exa_DB_GetQstAnswersCorrFromSet (&mysql_res,Question->QstCod);
DB_QuerySELECT (&mysql_res,"can not get answers of a question",
"SELECT Correct" // row[0]
" FROM exa_set_answers"
" WHERE QstCod=%ld"
" ORDER BY AnsInd",
Question->QstCod);
for (NumOpt = 0; for (NumOpt = 0;
NumOpt < Question->Answer.NumOptions; NumOpt < Question->Answer.NumOptions;
NumOpt++) NumOpt++)
@ -1390,12 +1290,7 @@ static void ExaPrn_GetCorrectTxtAnswerFromDB (struct Tst_Question *Question)
unsigned NumOpt; unsigned NumOpt;
/***** Query database *****/ /***** Query database *****/
Question->Answer.NumOptions = (unsigned) Question->Answer.NumOptions = Exa_DB_GetQstAnswersTextFromSet (&mysql_res,Question->QstCod);
DB_QuerySELECT (&mysql_res,"can not get answers of a question",
"SELECT Answer" // row[0]
" FROM exa_set_answers"
" WHERE QstCod=%ld",
Question->QstCod);
/***** Get text and correctness of answers for this question from database (one row per answer) *****/ /***** Get text and correctness of answers for this question from database (one row per answer) *****/
for (NumOpt = 0; for (NumOpt = 0;
@ -1422,115 +1317,6 @@ static void ExaPrn_GetCorrectTxtAnswerFromDB (struct Tst_Question *Question)
DB_FreeMySQLResult (&mysql_res); DB_FreeMySQLResult (&mysql_res);
} }
/*****************************************************************************/
/************* Get the questions of an exam print from database **************/
/*****************************************************************************/
static void ExaPrn_GetAnswerFromDB (struct ExaPrn_Print *Print,long QstCod,
char StrAnswers[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1])
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
/***** Get questions of an exam print from database *****/
if (DB_QuerySELECT (&mysql_res,"can not get answer in an exam print",
"SELECT Answers" // row[0]
" FROM exa_print_questions"
" WHERE PrnCod=%ld"
" AND QstCod=%ld",
Print->PrnCod,QstCod))
{
row = mysql_fetch_row (mysql_res);
/* Get answers selected by user for this question (row[0]) */
Str_Copy (StrAnswers,row[0],strlen (StrAnswers) - 1);
}
else
StrAnswers[0] = '\0';
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
/*****************************************************************************/
/************* Store user's answers of an test exam into database ************/
/*****************************************************************************/
static void ExaPrn_StoreOneQstOfPrintInDB (const struct ExaPrn_Print *Print,
unsigned QstInd)
{
/***** Insert question and user's answers into database *****/
Str_SetDecimalPointToUS (); // To print the floating point as a dot
DB_QueryREPLACE ("can not update a question in an exam print",
"REPLACE INTO exa_print_questions"
" (PrnCod,QstCod,QstInd,SetCod,Score,Indexes,Answers)"
" VALUES"
" (%ld,%ld,%u,%ld,'%.15lg','%s','%s')",
Print->PrnCod,
Print->PrintedQuestions[QstInd].QstCod,
QstInd, // 0, 1, 2, 3...
Print->PrintedQuestions[QstInd].SetCod,
Print->PrintedQuestions[QstInd].Score,
Print->PrintedQuestions[QstInd].StrIndexes,
Print->PrintedQuestions[QstInd].StrAnswers);
Str_SetDecimalPointToLocal (); // Return to local system
}
/*****************************************************************************/
/************ Get number of questions not blank in an exam print *************/
/*****************************************************************************/
static unsigned Exa_DB_GetNumQstsNotBlankInPrint (long PrnCod)
{
return (unsigned)
DB_QueryCOUNT ("can not get number of questions not blank",
"SELECT COUNT(*)"
" FROM exa_print_questions"
" WHERE PrnCod=%ld"
" AND Answers<>''",
PrnCod);
}
/*****************************************************************************/
/************* Compute total score of questions of an exam print *************/
/*****************************************************************************/
static double Exa_DB_ComputeTotalScoreOfPrint (long PrnCod)
{
return DB_QuerySELECTDouble ("can not get score of exam print",
"SELECT SUM(Score)"
" FROM exa_print_questions"
" WHERE PrnCod=%ld",
PrnCod);
}
/*****************************************************************************/
/********************** Update exam print in database ************************/
/*****************************************************************************/
static void Exa_DB_UpdatePrint (const struct ExaPrn_Print *Print)
{
/***** Update exam print in database *****/
Str_SetDecimalPointToUS (); // To print the floating point as a dot
DB_QueryUPDATE ("can not update exam print",
"UPDATE exa_prints"
" SET EndTime=NOW(),"
"NumQstsNotBlank=%u,"
"Sent='%c',"
"Score='%.15lg'"
" WHERE PrnCod=%ld"
" AND SesCod=%ld"
" AND UsrCod=%ld", // Extra checks
Print->NumQsts.NotBlank,
Print->Sent ? 'Y' :
'N',
Print->Score,
Print->PrnCod,
Print->SesCod,
Gbl.Usrs.Me.UsrDat.UsrCod);
Str_SetDecimalPointToLocal (); // Return to local system
}
/*****************************************************************************/ /*****************************************************************************/
/********************** Remove exam prints made by a user ********************/ /********************** Remove exam prints made by a user ********************/
/*****************************************************************************/ /*****************************************************************************/
@ -1538,19 +1324,10 @@ static void Exa_DB_UpdatePrint (const struct ExaPrn_Print *Print)
void ExaPrn_RemovePrintsMadeByUsrInAllCrss (long UsrCod) void ExaPrn_RemovePrintsMadeByUsrInAllCrss (long UsrCod)
{ {
/***** Remove exam prints questions for the given user *****/ /***** Remove exam prints questions for the given user *****/
DB_QueryDELETE ("can not remove exam prints made by a user", Exa_DB_RemovePrintQuestionsMadeByUsrInAllCrss (UsrCod);
"DELETE FROM exa_print_questions"
" USING exa_prints,"
"exa_print_questions"
" WHERE exa_prints.UsrCod=%ld"
" AND exa_prints.PrnCod=exa_print_questions.PrnCod",
UsrCod);
/***** Remove exam prints made by the given user *****/ /***** Remove exam prints made by the given user *****/
DB_QueryDELETE ("can not remove exam prints made by a user", Exa_DB_RemovePrintsMadeByUsrInAllCrss (UsrCod);
"DELETE FROM exa_prints"
" WHERE UsrCod=%ld",
UsrCod);
} }
/*****************************************************************************/ /*****************************************************************************/
@ -1560,32 +1337,10 @@ void ExaPrn_RemovePrintsMadeByUsrInAllCrss (long UsrCod)
void ExaPrn_RemovePrintsMadeByUsrInCrs (long UsrCod,long CrsCod) void ExaPrn_RemovePrintsMadeByUsrInCrs (long UsrCod,long CrsCod)
{ {
/***** Remove questions of exams prints made by the given user in the given course *****/ /***** Remove questions of exams prints made by the given user in the given course *****/
DB_QueryDELETE ("can not remove exams prints made by a user in a course", Exa_DB_RemovePrintsQuestionsMadeByUsrInCrs (UsrCod,CrsCod);
"DELETE FROM exa_print_questions"
" USING exa_exams,"
"exa_sessions,"
"exa_prints,"
"exa_print_questions"
" WHERE exa_exams.CrsCod=%ld"
" AND exa_exams.ExaCod=exa_sessions.ExaCod"
" AND exa_sessions.SesCod=exa_prints.SesCod"
" AND exa_prints.UsrCod=%ld"
" AND exa_prints.PrnCod=exa_print_questions.PrnCod",
CrsCod,
UsrCod);
/***** Remove exams prints made by the given user in the given course *****/ /***** Remove exams prints made by the given user in the given course *****/
DB_QueryDELETE ("can not remove exams prints made by a user in a course", Exa_DB_RemovePrintsMadeByUsrInCrs (UsrCod,CrsCod);
"DELETE FROM exa_prints"
" USING exa_exams,"
"exa_sessions,"
"exa_prints"
" WHERE exa_exams.CrsCod=%ld"
" AND exa_exams.ExaCod=exa_sessions.ExaCod"
" AND exa_sessions.SesCod=exa_prints.SesCod"
" AND exa_prints.UsrCod=%ld",
CrsCod,
UsrCod);
} }
/*****************************************************************************/ /*****************************************************************************/
@ -1595,26 +1350,8 @@ void ExaPrn_RemovePrintsMadeByUsrInCrs (long UsrCod,long CrsCod)
void ExaPrn_RemoveCrsPrints (long CrsCod) void ExaPrn_RemoveCrsPrints (long CrsCod)
{ {
/***** Remove questions of exams prints made by the given user in the given course *****/ /***** Remove questions of exams prints made by the given user in the given course *****/
DB_QueryDELETE ("can not remove exams prints in a course", Exa_DB_RemovePrintQuestionsInCrs (CrsCod);
"DELETE FROM exa_print_questions"
" USING exa_exams,"
"exa_sessions,"
"exa_prints,"
"exa_print_questions"
" WHERE exa_exams.CrsCod=%ld"
" AND exa_exams.ExaCod=exa_sessions.ExaCod"
" AND exa_sessions.SesCod=exa_prints.SesCod"
" AND exa_prints.PrnCod=exa_print_questions.PrnCod",
CrsCod);
/***** Remove exams prints made by the given user in the given course *****/ /***** Remove exams prints made by the given user in the given course *****/
DB_QueryDELETE ("can not remove exams prints in a course", Exa_DB_RemovePrintsInCrs (CrsCod);
"DELETE FROM exa_prints"
" USING exa_exams,"
"exa_sessions,"
"exa_prints"
" WHERE exa_exams.CrsCod=%ld"
" AND exa_exams.ExaCod=exa_sessions.ExaCod"
" AND exa_sessions.SesCod=exa_prints.SesCod",
CrsCod);
} }