mirror of https://github.com/acanas/swad-core.git
Version19.160
This commit is contained in:
parent
f690d1ed72
commit
7db18f8aa9
|
@ -497,7 +497,7 @@ enscript -2 --landscape --color --file-align=2 --highlight --line-numbers -o - *
|
|||
En OpenSWAD:
|
||||
ps2pdf source.ps destination.pdf
|
||||
*/
|
||||
#define Log_PLATFORM_VERSION "SWAD 19.159 (2020-04-03)"
|
||||
#define Log_PLATFORM_VERSION "SWAD 19.160 (2020-04-03)"
|
||||
#define CSS_FILE "swad19.146.css"
|
||||
#define JS_FILE "swad19.153.js"
|
||||
/*
|
||||
|
@ -523,8 +523,14 @@ Param
|
|||
// TODO: Miguel Damas: al principio de los exámenes tendría que poner cuánto resta cada pregunta
|
||||
// TODO: Oresti Baños: cambiar ojos por candados en descriptores para prohibir/permitir y dejar los ojos para poder elegir descriptores
|
||||
// TODO: Integrar pull requests con traducciones del alemán del usuario eruedin en GitHub
|
||||
// TODO: Comprobar si la puntuación de cada pregunta de un examen se recalcula al mostrarlo o se saca de la base de datos
|
||||
y qué pasa cuando se edita una pregunta
|
||||
// TODO: Integrar Stem y Feedback en question, creando espacio con malloc como en las respuestas
|
||||
// TODO: Intentar cambiar Tst_WriteChoiceAnsSeeing usando Tst_GetQstDataFromDB y quitar Tst_GetChoiceAns
|
||||
// TODO: Intentar quitar Tst_GetOneQuestionByCod usando Tst_GetQstDataFromDB
|
||||
|
||||
Version 19.160: Apr 03, 2020 The score for each test question displayed in an exam is the one stored in the database instead of being calculated.
|
||||
New file extension, suggested by Rosa Medina Doménech. (284933 lines)
|
||||
Copy the following icon to icon public directory:
|
||||
sudo cp icon/filext32x32/m4a32x32.gif /var/www/html/swad/icon/filext32x32/
|
||||
|
||||
Version 19.159: Apr 03, 2020 Code refactoring and bug fixing in tests. (285052 lines)
|
||||
Version 19.158: Apr 02, 2020 Lot of code refactoring in tests. (285031 lines)
|
||||
|
|
|
@ -75,6 +75,7 @@ const char *Ext_FileExtensionsAllowed[] =
|
|||
"jpeg" ,
|
||||
"latex",
|
||||
"m" ,
|
||||
"m4a" , // MPEG-4 de audio
|
||||
"mdb" ,
|
||||
"mht" ,
|
||||
"mhtml",
|
||||
|
|
|
@ -1514,7 +1514,7 @@ static void Mch_CreateIndexes (long GamCod,long MchCod)
|
|||
Lay_ShowErrorAndExit ("Wrong answer type.");
|
||||
|
||||
/* Get shuffle (row[3]) */
|
||||
Question.Shuffle = (row[3][0] == 'Y');
|
||||
Question.Answer.Shuffle = (row[3][0] == 'Y');
|
||||
|
||||
/***** Reorder answer *****/
|
||||
Mch_ReorderAnswer (MchCod,QstInd,&Question);
|
||||
|
@ -1554,8 +1554,8 @@ static void Mch_ReorderAnswer (long MchCod,unsigned QstInd,
|
|||
" WHERE QstCod=%ld"
|
||||
" ORDER BY %s",
|
||||
Question->QstCod,
|
||||
Question->Shuffle ? "RAND()" : // Use RAND() because is really random; RAND(NOW()) repeats order
|
||||
"AnsInd");
|
||||
Question->Answer.Shuffle ? "RAND()" : // Use RAND() because is really random; RAND(NOW()) repeats order
|
||||
"AnsInd");
|
||||
|
||||
/***** For each answer in question... *****/
|
||||
for (NumAns = 0;
|
||||
|
|
269
swad_test.c
269
swad_test.c
|
@ -250,9 +250,6 @@ static void Tst_FreeTextChoiceAnswer (struct Tst_Question *Question,unsigned Num
|
|||
static void Tst_ResetMediaOfQuestion (struct Tst_Question *Question);
|
||||
static void Tst_FreeMediaOfQuestion (struct Tst_Question *Question);
|
||||
|
||||
static void Tst_GetQstDataFromDB (struct Tst_Question *Question,
|
||||
char Stem[Cns_MAX_BYTES_TEXT + 1],
|
||||
char Feedback[Cns_MAX_BYTES_TEXT + 1]);
|
||||
static long Tst_GetMedCodFromDB (long CrsCod,long QstCod,int NumOpt);
|
||||
static void Tst_GetMediaFromDB (long CrsCod,long QstCod,int NumOpt,
|
||||
struct Media *Media);
|
||||
|
@ -4330,8 +4327,8 @@ static void Tst_PutFormEditOneQst (struct Tst_Question *Question,
|
|||
HTM_LABEL_Begin ("class=\"%s\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
|
||||
HTM_INPUT_CHECKBOX ("Shuffle",HTM_DONT_SUBMIT_ON_CHANGE,
|
||||
"value=\"Y\"%s%s",
|
||||
Question->Shuffle ? " checked=\"checked\"" :
|
||||
"",
|
||||
Question->Answer.Shuffle ? " checked=\"checked\"" :
|
||||
"",
|
||||
Question->Answer.Type != Tst_ANS_UNIQUE_CHOICE &&
|
||||
Question->Answer.Type != Tst_ANS_MULTIPLE_CHOICE ? " disabled=\"disabled\"" :
|
||||
"");
|
||||
|
@ -4537,19 +4534,23 @@ void Tst_QstConstructor (struct Tst_Question *Question)
|
|||
|
||||
Tst_ResetTags (&Question->Tags);
|
||||
|
||||
Question->Stem.Text = NULL;
|
||||
Question->Stem.Length = 0;
|
||||
Question->Feedback.Text = NULL;
|
||||
Question->EditTime = (time_t) 0;
|
||||
|
||||
Question->Stem.Text = NULL;
|
||||
Question->Stem.Length = 0;
|
||||
Question->Feedback.Text = NULL;
|
||||
Question->Feedback.Length = 0;
|
||||
Question->Shuffle = false;
|
||||
|
||||
Question->Answer.Type = Tst_ANS_UNIQUE_CHOICE;
|
||||
/***** Initialize answers *****/
|
||||
Question->Answer.Type = Tst_ANS_UNIQUE_CHOICE;
|
||||
Question->Answer.NumOptions = 0;
|
||||
Question->Answer.TF = ' ';
|
||||
Question->Answer.Shuffle = false;
|
||||
Question->Answer.TF = ' ';
|
||||
|
||||
/***** Initialize image attached to stem *****/
|
||||
/* Initialize image attached to stem */
|
||||
Med_MediaConstructor (&Question->Media);
|
||||
|
||||
/* Initialize options */
|
||||
for (NumOpt = 0;
|
||||
NumOpt < Tst_MAX_OPTIONS_PER_QUESTION;
|
||||
NumOpt++)
|
||||
|
@ -4558,7 +4559,7 @@ void Tst_QstConstructor (struct Tst_Question *Question)
|
|||
Question->Answer.Options[NumOpt].Text = NULL;
|
||||
Question->Answer.Options[NumOpt].Feedback = NULL;
|
||||
|
||||
/***** Initialize image attached to option *****/
|
||||
/* Initialize image attached to option */
|
||||
Med_MediaConstructor (&Question->Answer.Options[NumOpt].Media);
|
||||
}
|
||||
Question->Answer.Integer = 0;
|
||||
|
@ -4700,132 +4701,152 @@ Tst_AnswerType_t Tst_GetQstAnswerType (long QstCod)
|
|||
/*****************************************************************************/
|
||||
/****************** Get data of a question from database *********************/
|
||||
/*****************************************************************************/
|
||||
// If question does not exist ==> set question code to -1
|
||||
|
||||
static void Tst_GetQstDataFromDB (struct Tst_Question *Question,
|
||||
char Stem[Cns_MAX_BYTES_TEXT + 1],
|
||||
char Feedback[Cns_MAX_BYTES_TEXT + 1])
|
||||
void Tst_GetQstDataFromDB (struct Tst_Question *Question,
|
||||
char Stem[Cns_MAX_BYTES_TEXT + 1],
|
||||
char Feedback[Cns_MAX_BYTES_TEXT + 1])
|
||||
{
|
||||
MYSQL_RES *mysql_res;
|
||||
MYSQL_ROW row;
|
||||
bool QuestionExists;
|
||||
unsigned long NumRows;
|
||||
unsigned long NumRow;
|
||||
unsigned NumOpt;
|
||||
|
||||
/***** Get question data from database *****/
|
||||
if (!DB_QuerySELECT (&mysql_res,"can not get a question",
|
||||
"SELECT AnsType," // row[0]
|
||||
"Shuffle," // row[1]
|
||||
"Stem," // row[2]
|
||||
"Feedback," // row[3]
|
||||
"MedCod" // row[4]
|
||||
" FROM tst_questions"
|
||||
" WHERE QstCod=%ld"
|
||||
" AND CrsCod=%ld", // Extra check
|
||||
Question->QstCod,
|
||||
Gbl.Hierarchy.Crs.CrsCod))
|
||||
Lay_ShowErrorAndExit ("Question does not exist.");
|
||||
QuestionExists = (DB_QuerySELECT (&mysql_res,"can not get a question",
|
||||
"SELECT UNIX_TIMESTAMP(EditTime)," // row[0]
|
||||
"AnsType," // row[1]
|
||||
"Shuffle," // row[2]
|
||||
"Stem," // row[3]
|
||||
"Feedback," // row[4]
|
||||
"MedCod" // row[5]
|
||||
" FROM tst_questions"
|
||||
" WHERE QstCod=%ld"
|
||||
" AND CrsCod=%ld", // Extra check
|
||||
Question->QstCod,
|
||||
Gbl.Hierarchy.Crs.CrsCod) != 0);
|
||||
|
||||
row = mysql_fetch_row (mysql_res);
|
||||
|
||||
/* Get the type of answer */
|
||||
Question->Answer.Type = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[0]);
|
||||
|
||||
/* Get shuffle (row[1]) */
|
||||
Question->Shuffle = (row[1][0] == 'Y');
|
||||
|
||||
/* Get the stem of the question from the database (row[2]) */
|
||||
Str_Copy (Stem,row[2],
|
||||
Cns_MAX_BYTES_TEXT);
|
||||
|
||||
/* Get the feedback of the question from the database (row[3]) */
|
||||
Feedback[0] = '\0';
|
||||
if (row[3])
|
||||
if (row[3][0])
|
||||
Str_Copy (Feedback,row[3],
|
||||
Cns_MAX_BYTES_TEXT);
|
||||
|
||||
/* Get media (row[4]) */
|
||||
Question->Media.MedCod = Str_ConvertStrCodToLongCod (row[4]);
|
||||
Med_GetMediaDataByCod (&Question->Media);
|
||||
|
||||
/* Free structure that stores the query result */
|
||||
DB_FreeMySQLResult (&mysql_res);
|
||||
|
||||
/***** Get the tags from the database *****/
|
||||
NumRows = Tst_GetTagsQst (Question->QstCod,&mysql_res);
|
||||
for (NumRow = 0;
|
||||
NumRow < NumRows;
|
||||
NumRow++)
|
||||
if (QuestionExists)
|
||||
{
|
||||
row = mysql_fetch_row (mysql_res);
|
||||
Str_Copy (Question->Tags.Txt[NumRow],row[0],
|
||||
Tst_MAX_BYTES_TAG);
|
||||
}
|
||||
|
||||
/* Free structure that stores the query result */
|
||||
DB_FreeMySQLResult (&mysql_res);
|
||||
/* Get edition time (row[0] holds the start UTC time) */
|
||||
Question->EditTime = Dat_GetUNIXTimeFromStr (row[3]);
|
||||
|
||||
/***** Get the answers from the database *****/
|
||||
Tst_GetAnswersQst (Question,&mysql_res,
|
||||
false); // Don't shuffle
|
||||
/*
|
||||
row[0] AnsInd
|
||||
row[1] Answer
|
||||
row[2] Feedback
|
||||
row[3] MedCod
|
||||
row[4] Correct
|
||||
*/
|
||||
for (NumOpt = 0;
|
||||
NumOpt < Question->Answer.NumOptions;
|
||||
NumOpt++)
|
||||
{
|
||||
row = mysql_fetch_row (mysql_res);
|
||||
switch (Question->Answer.Type)
|
||||
/* Get the type of answer (row[1]) */
|
||||
Question->Answer.Type = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[1]);
|
||||
|
||||
/* Get shuffle (row[2]) */
|
||||
Question->Answer.Shuffle = (row[2][0] == 'Y');
|
||||
|
||||
/* Get the stem (row[3]) */
|
||||
Stem[0] = '\0';
|
||||
if (row[3])
|
||||
if (row[3][0])
|
||||
Str_Copy (Stem,row[3],
|
||||
Cns_MAX_BYTES_TEXT);
|
||||
|
||||
/* Get the feedback (row[4]) */
|
||||
Feedback[0] = '\0';
|
||||
if (row[4])
|
||||
if (row[4][0])
|
||||
Str_Copy (Feedback,row[4],
|
||||
Cns_MAX_BYTES_TEXT);
|
||||
|
||||
/* Get media (row[5]) */
|
||||
Question->Media.MedCod = Str_ConvertStrCodToLongCod (row[5]);
|
||||
Med_GetMediaDataByCod (&Question->Media);
|
||||
|
||||
/* Free structure that stores the query result */
|
||||
DB_FreeMySQLResult (&mysql_res);
|
||||
|
||||
/***** Get the tags from the database *****/
|
||||
NumRows = Tst_GetTagsQst (Question->QstCod,&mysql_res);
|
||||
for (NumRow = 0;
|
||||
NumRow < NumRows;
|
||||
NumRow++)
|
||||
{
|
||||
case Tst_ANS_INT:
|
||||
if (Question->Answer.NumOptions != 1)
|
||||
Lay_ShowErrorAndExit ("Wrong answer.");
|
||||
Question->Answer.Integer = Tst_GetIntAnsFromStr (row[1]);
|
||||
break;
|
||||
case Tst_ANS_FLOAT:
|
||||
if (Question->Answer.NumOptions != 2)
|
||||
Lay_ShowErrorAndExit ("Wrong answer.");
|
||||
Question->Answer.FloatingPoint[NumOpt] = Str_GetDoubleFromStr (row[1]);
|
||||
break;
|
||||
case Tst_ANS_TRUE_FALSE:
|
||||
if (Question->Answer.NumOptions != 1)
|
||||
Lay_ShowErrorAndExit ("Wrong answer.");
|
||||
Question->Answer.TF = row[1][0];
|
||||
break;
|
||||
case Tst_ANS_UNIQUE_CHOICE:
|
||||
case Tst_ANS_MULTIPLE_CHOICE:
|
||||
case Tst_ANS_TEXT:
|
||||
if (Question->Answer.NumOptions > Tst_MAX_OPTIONS_PER_QUESTION)
|
||||
Lay_ShowErrorAndExit ("Wrong answer.");
|
||||
if (!Tst_AllocateTextChoiceAnswer (Question,NumOpt))
|
||||
/* Abort on error */
|
||||
Ale_ShowAlertsAndExit ();
|
||||
row = mysql_fetch_row (mysql_res);
|
||||
Str_Copy (Question->Tags.Txt[NumRow],row[0],
|
||||
Tst_MAX_BYTES_TAG);
|
||||
}
|
||||
|
||||
Str_Copy (Question->Answer.Options[NumOpt].Text,row[1],
|
||||
Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
|
||||
/* Free structure that stores the query result */
|
||||
DB_FreeMySQLResult (&mysql_res);
|
||||
|
||||
// Feedback (row[2]) is initialized to empty string
|
||||
if (row[2])
|
||||
if (row[2][0])
|
||||
Str_Copy (Question->Answer.Options[NumOpt].Feedback,row[2],
|
||||
Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
|
||||
/***** Get the answers from the database *****/
|
||||
Tst_GetAnswersQst (Question,&mysql_res,
|
||||
false); // Don't shuffle
|
||||
/*
|
||||
row[0] AnsInd
|
||||
row[1] Answer
|
||||
row[2] Feedback
|
||||
row[3] MedCod
|
||||
row[4] Correct
|
||||
*/
|
||||
for (NumOpt = 0;
|
||||
NumOpt < Question->Answer.NumOptions;
|
||||
NumOpt++)
|
||||
{
|
||||
row = mysql_fetch_row (mysql_res);
|
||||
switch (Question->Answer.Type)
|
||||
{
|
||||
case Tst_ANS_INT:
|
||||
Tst_CheckIfNumberOfAnswersIsOne (Question);
|
||||
Question->Answer.Integer = Tst_GetIntAnsFromStr (row[1]);
|
||||
break;
|
||||
case Tst_ANS_FLOAT:
|
||||
if (Question->Answer.NumOptions != 2)
|
||||
Lay_ShowErrorAndExit ("Wrong answer.");
|
||||
Question->Answer.FloatingPoint[NumOpt] = Str_GetDoubleFromStr (row[1]);
|
||||
break;
|
||||
case Tst_ANS_TRUE_FALSE:
|
||||
Tst_CheckIfNumberOfAnswersIsOne (Question);
|
||||
Question->Answer.TF = row[1][0];
|
||||
break;
|
||||
case Tst_ANS_UNIQUE_CHOICE:
|
||||
case Tst_ANS_MULTIPLE_CHOICE:
|
||||
case Tst_ANS_TEXT:
|
||||
/* Check number of options */
|
||||
if (Question->Answer.NumOptions > Tst_MAX_OPTIONS_PER_QUESTION)
|
||||
Lay_ShowErrorAndExit ("Wrong answer.");
|
||||
|
||||
/* Get media (row[3]) */
|
||||
Question->Answer.Options[NumOpt].Media.MedCod = Str_ConvertStrCodToLongCod (row[3]);
|
||||
Med_GetMediaDataByCod (&Question->Answer.Options[NumOpt].Media);
|
||||
/* Allocate space for text and feedback */
|
||||
if (!Tst_AllocateTextChoiceAnswer (Question,NumOpt))
|
||||
/* Abort on error */
|
||||
Ale_ShowAlertsAndExit ();
|
||||
|
||||
/* Get if this option is correct (row[4]) */
|
||||
Question->Answer.Options[NumOpt].Correct = (row[4][0] == 'Y');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
/* Get text (row[1]) */
|
||||
Question->Answer.Options[NumOpt].Text[0] = '\0';
|
||||
if (row[1])
|
||||
if (row[1][0])
|
||||
Str_Copy (Question->Answer.Options[NumOpt].Text,row[1],
|
||||
Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
|
||||
|
||||
/* Get feedback (row[2]) */
|
||||
Question->Answer.Options[NumOpt].Feedback[0] = '\0';
|
||||
if (row[2])
|
||||
if (row[2][0])
|
||||
Str_Copy (Question->Answer.Options[NumOpt].Feedback,row[2],
|
||||
Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
|
||||
|
||||
/* Get media (row[3]) */
|
||||
Question->Answer.Options[NumOpt].Media.MedCod = Str_ConvertStrCodToLongCod (row[3]);
|
||||
Med_GetMediaDataByCod (&Question->Answer.Options[NumOpt].Media);
|
||||
|
||||
/* Get if this option is correct (row[4]) */
|
||||
Question->Answer.Options[NumOpt].Correct = (row[4][0] == 'Y');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
Question->QstCod = -1L;
|
||||
|
||||
/* Free structure that stores the query result */
|
||||
DB_FreeMySQLResult (&mysql_res);
|
||||
}
|
||||
|
@ -5048,7 +5069,7 @@ static void Tst_GetQstFromForm (struct Tst_Question *Question,
|
|||
Ale_ShowAlerts (NULL);
|
||||
|
||||
/***** Get answers *****/
|
||||
Question->Shuffle = false;
|
||||
Question->Answer.Shuffle = false;
|
||||
switch (Question->Answer.Type)
|
||||
{
|
||||
case Tst_ANS_INT:
|
||||
|
@ -5081,7 +5102,7 @@ static void Tst_GetQstFromForm (struct Tst_Question *Question,
|
|||
case Tst_ANS_UNIQUE_CHOICE:
|
||||
case Tst_ANS_MULTIPLE_CHOICE:
|
||||
/* Get shuffle */
|
||||
Question->Shuffle = Par_GetParToBool ("Shuffle");
|
||||
Question->Answer.Shuffle = Par_GetParToBool ("Shuffle");
|
||||
/* falls through */
|
||||
/* no break */
|
||||
case Tst_ANS_TEXT:
|
||||
|
@ -5970,8 +5991,8 @@ static void Tst_InsertOrUpdateQstIntoDB (struct Tst_Question *Question)
|
|||
"0)", // Score
|
||||
Gbl.Hierarchy.Crs.CrsCod,
|
||||
Tst_StrAnswerTypesDB[Question->Answer.Type],
|
||||
Question->Shuffle ? 'Y' :
|
||||
'N',
|
||||
Question->Answer.Shuffle ? 'Y' :
|
||||
'N',
|
||||
Question->Stem.Text,
|
||||
Question->Feedback.Text ? Question->Feedback.Text :
|
||||
"",
|
||||
|
@ -5991,8 +6012,8 @@ static void Tst_InsertOrUpdateQstIntoDB (struct Tst_Question *Question)
|
|||
"MedCod=%ld"
|
||||
" WHERE QstCod=%ld AND CrsCod=%ld",
|
||||
Tst_StrAnswerTypesDB[Question->Answer.Type],
|
||||
Question->Shuffle ? 'Y' :
|
||||
'N',
|
||||
Question->Answer.Shuffle ? 'Y' :
|
||||
'N',
|
||||
Question->Stem.Text,
|
||||
Question->Feedback.Text ? Question->Feedback.Text :
|
||||
"",
|
||||
|
|
|
@ -94,17 +94,18 @@ struct Tst_Question
|
|||
{
|
||||
long QstCod;
|
||||
struct Tst_Tags Tags;
|
||||
time_t EditTime;
|
||||
struct
|
||||
{
|
||||
char *Text;
|
||||
size_t Length;
|
||||
} Stem, Feedback;
|
||||
struct Media Media;
|
||||
bool Shuffle;
|
||||
struct
|
||||
{
|
||||
Tst_AnswerType_t Type;
|
||||
unsigned NumOptions;
|
||||
bool Shuffle;
|
||||
char TF;
|
||||
struct
|
||||
{
|
||||
|
@ -199,6 +200,9 @@ void Tst_QstDestructor (struct Tst_Question *Question);
|
|||
bool Tst_AllocateTextChoiceAnswer (struct Tst_Question *Question,unsigned NumOpt);
|
||||
|
||||
Tst_AnswerType_t Tst_GetQstAnswerType (long QstCod);
|
||||
void Tst_GetQstDataFromDB (struct Tst_Question *Question,
|
||||
char Stem[Cns_MAX_BYTES_TEXT + 1],
|
||||
char Feedback[Cns_MAX_BYTES_TEXT + 1]);
|
||||
Tst_AnswerType_t Tst_ConvertFromStrAnsTypDBToAnsTyp (const char *StrAnsTypeBD);
|
||||
void Tst_ReceiveQst (void);
|
||||
bool Tst_CheckIfQstFormatIsCorrectAndCountNumOptions (struct Tst_Question *Question);
|
||||
|
|
429
swad_test_exam.c
429
swad_test_exam.c
|
@ -77,6 +77,13 @@ extern struct Globals Gbl;
|
|||
/***************************** Private prototypes ****************************/
|
||||
/*****************************************************************************/
|
||||
|
||||
static void TstExa_WriteQstAndAnsExam (struct UsrData *UsrDat,
|
||||
struct TstExa_Exam *Exam,
|
||||
unsigned NumQst,
|
||||
struct Tst_Question *Question,
|
||||
const char *Stem,
|
||||
const char *Feedback,
|
||||
unsigned Visibility);
|
||||
static void TstExa_ComputeAnswerScore (struct TstExa_Exam *Exam,
|
||||
unsigned NumQst,
|
||||
struct Tst_Question *Question);
|
||||
|
@ -113,31 +120,26 @@ static void TstExa_WriteIntAnsExam (struct UsrData *UsrDat,
|
|||
const struct TstExa_Exam *Exam,
|
||||
unsigned NumQst,
|
||||
const struct Tst_Question *Question,
|
||||
MYSQL_RES *mysql_res,
|
||||
unsigned Visibility);
|
||||
static void TstExa_WriteFloatAnsExam (struct UsrData *UsrDat,
|
||||
const struct TstExa_Exam *Exam,
|
||||
unsigned NumQst,
|
||||
const struct Tst_Question *Question,
|
||||
MYSQL_RES *mysql_res,
|
||||
unsigned Visibility);
|
||||
static void TstExa_WriteTFAnsExam (struct UsrData *UsrDat,
|
||||
const struct TstExa_Exam *Exam,
|
||||
unsigned NumQst,
|
||||
const struct Tst_Question *Question,
|
||||
MYSQL_RES *mysql_res,
|
||||
unsigned Visibility);
|
||||
static void TstExa_WriteChoiceAnsExam (struct UsrData *UsrDat,
|
||||
const struct TstExa_Exam *Exam,
|
||||
unsigned NumQst,
|
||||
struct Tst_Question *Question,
|
||||
MYSQL_RES *mysql_res,
|
||||
unsigned Visibility);
|
||||
static void TstExa_WriteTextAnsExam (struct UsrData *UsrDat,
|
||||
const struct TstExa_Exam *Exam,
|
||||
unsigned NumQst,
|
||||
struct Tst_Question *Question,
|
||||
MYSQL_RES *mysql_res,
|
||||
unsigned Visibility);
|
||||
static void TstExa_WriteHeadUserCorrect (struct UsrData *UsrDat);
|
||||
static void TstExa_WriteScoreStart (unsigned ColSpan);
|
||||
|
@ -222,10 +224,10 @@ void TstExa_UpdateExamInDB (const struct TstExa_Exam *Exam)
|
|||
|
||||
void TstExa_ShowExamAfterAssess (struct TstExa_Exam *Exam)
|
||||
{
|
||||
extern const char *Txt_Question_removed;
|
||||
MYSQL_RES *mysql_res;
|
||||
MYSQL_ROW row;
|
||||
unsigned NumQst;
|
||||
struct Tst_Question Question;
|
||||
char Stem[Cns_MAX_BYTES_TEXT + 1];
|
||||
char Feedback[Cns_MAX_BYTES_TEXT + 1];
|
||||
|
||||
/***** Begin table *****/
|
||||
HTM_TABLE_BeginWideMarginPadding (10);
|
||||
|
@ -240,20 +242,22 @@ void TstExa_ShowExamAfterAssess (struct TstExa_Exam *Exam)
|
|||
{
|
||||
Gbl.RowEvenOdd = NumQst % 2;
|
||||
|
||||
/***** Query database *****/
|
||||
if (Tst_GetOneQuestionByCod (Exam->Questions[NumQst].QstCod,&mysql_res)) // Question exists
|
||||
/***** Create test question *****/
|
||||
Tst_QstConstructor (&Question);
|
||||
Question.QstCod = Exam->Questions[NumQst].QstCod;
|
||||
|
||||
/***** Get question data *****/
|
||||
Tst_GetQstDataFromDB (&Question,Stem,Feedback);
|
||||
if (Question.QstCod > 0) // Question exists
|
||||
{
|
||||
/***** Write question and answers *****/
|
||||
row = mysql_fetch_row (mysql_res);
|
||||
TstExa_WriteQstAndAnsExam (&Gbl.Usrs.Me.UsrDat,
|
||||
Exam,
|
||||
NumQst,
|
||||
row,
|
||||
Exam,NumQst,
|
||||
&Question,Stem,Feedback,
|
||||
TstCfg_GetConfigVisibility ());
|
||||
|
||||
/***** Store test exam question in database *****/
|
||||
TstExa_StoreOneExamQstInDB (Exam,
|
||||
NumQst); // 0, 1, 2, 3...
|
||||
TstExa_StoreOneExamQstInDB (Exam,NumQst);
|
||||
|
||||
/***** Compute total score *****/
|
||||
Exam->Score += Exam->Questions[NumQst].Score;
|
||||
|
@ -264,24 +268,9 @@ void TstExa_ShowExamAfterAssess (struct TstExa_Exam *Exam)
|
|||
if (Gbl.Usrs.Me.Role.Logged == Rol_STD)
|
||||
Tst_UpdateQstScoreInDB (Exam,NumQst);
|
||||
}
|
||||
else
|
||||
{
|
||||
/***** Question does not exists *****/
|
||||
HTM_TR_Begin (NULL);
|
||||
|
||||
HTM_TD_Begin ("class=\"RT COLOR%u\"",Gbl.RowEvenOdd);
|
||||
Tst_WriteNumQst (NumQst + 1);
|
||||
HTM_TD_End ();
|
||||
|
||||
HTM_TD_Begin ("class=\"DAT_LIGHT LT COLOR%u\"",Gbl.RowEvenOdd);
|
||||
HTM_Txt (Txt_Question_removed);
|
||||
HTM_TD_End ();
|
||||
|
||||
HTM_TR_End ();
|
||||
}
|
||||
|
||||
/***** Free structure that stores the query result *****/
|
||||
DB_FreeMySQLResult (&mysql_res);
|
||||
/***** Destroy test question *****/
|
||||
Tst_QstDestructor (&Question);
|
||||
}
|
||||
|
||||
/***** End table *****/
|
||||
|
@ -292,29 +281,30 @@ void TstExa_ShowExamAfterAssess (struct TstExa_Exam *Exam)
|
|||
/********** Write a row of a test, with one question and its answer **********/
|
||||
/*****************************************************************************/
|
||||
|
||||
void TstExa_WriteQstAndAnsExam (struct UsrData *UsrDat,
|
||||
struct TstExa_Exam *Exam,
|
||||
unsigned NumQst,
|
||||
MYSQL_ROW row,
|
||||
unsigned Visibility)
|
||||
static void TstExa_WriteQstAndAnsExam (struct UsrData *UsrDat,
|
||||
struct TstExa_Exam *Exam,
|
||||
unsigned NumQst,
|
||||
struct Tst_Question *Question,
|
||||
const char *Stem,
|
||||
const char *Feedback,
|
||||
unsigned Visibility)
|
||||
{
|
||||
struct Tst_Question Question;
|
||||
extern const char *Txt_Score;
|
||||
extern const char *Txt_Question_removed;
|
||||
extern const char *Txt_Question_modified;
|
||||
bool QuestionExists;
|
||||
bool QuestionUneditedAfterExam = false;
|
||||
bool IsVisibleQstAndAnsTxt = TstVis_IsVisibleQstAndAnsTxt (Visibility);
|
||||
/*
|
||||
row[0] UNIX_TIMESTAMP(EditTime)
|
||||
row[1] AnsType
|
||||
row[2] Shuffle
|
||||
row[3] Stem
|
||||
row[4] Feedback
|
||||
row[5] MedCod
|
||||
row[6] NumHits
|
||||
row[7] NumHitsNotBlank
|
||||
row[8] Score
|
||||
*/
|
||||
|
||||
/***** Create test question *****/
|
||||
Tst_QstConstructor (&Question);
|
||||
Question.QstCod = Exam->Questions[NumQst].QstCod;
|
||||
/***** Does question exist? *****/
|
||||
QuestionExists = (Question->QstCod > 0);
|
||||
|
||||
/***** If this question has been edited later than test time
|
||||
==> don't show question ****/
|
||||
if (QuestionExists)
|
||||
QuestionUneditedAfterExam = (Question->EditTime < Exam->TimeUTC[Dat_START_TIME]);
|
||||
else
|
||||
QuestionUneditedAfterExam = false;
|
||||
|
||||
/***** Begin row *****/
|
||||
HTM_TR_Begin (NULL);
|
||||
|
@ -322,43 +312,56 @@ void TstExa_WriteQstAndAnsExam (struct UsrData *UsrDat,
|
|||
/***** Number of question and answer type (row[1]) *****/
|
||||
HTM_TD_Begin ("class=\"RT COLOR%u\"",Gbl.RowEvenOdd);
|
||||
Tst_WriteNumQst (NumQst + 1);
|
||||
Question.Answer.Type = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[1]);
|
||||
Tst_WriteAnswerType (Question.Answer.Type);
|
||||
if (QuestionUneditedAfterExam)
|
||||
Tst_WriteAnswerType (Question->Answer.Type);
|
||||
HTM_TD_End ();
|
||||
|
||||
/***** Stem, media and answers *****/
|
||||
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
|
||||
|
||||
/* Stem (row[3]) */
|
||||
Tst_WriteQstStem (row[3],"TEST_EXA",IsVisibleQstAndAnsTxt);
|
||||
|
||||
/* Media (row[5]) */
|
||||
if (IsVisibleQstAndAnsTxt)
|
||||
if (QuestionExists)
|
||||
{
|
||||
Question.Media.MedCod = Str_ConvertStrCodToLongCod (row[5]);
|
||||
Med_GetMediaDataByCod (&Question.Media);
|
||||
Med_ShowMedia (&Question.Media,
|
||||
"TEST_MED_SHOW_CONT",
|
||||
"TEST_MED_SHOW");
|
||||
if (QuestionUneditedAfterExam)
|
||||
{
|
||||
/* Stem */
|
||||
Tst_WriteQstStem (Stem,"TEST_EXA",IsVisibleQstAndAnsTxt);
|
||||
|
||||
/* Media */
|
||||
if (IsVisibleQstAndAnsTxt)
|
||||
Med_ShowMedia (&Question->Media,
|
||||
"TEST_MED_SHOW_CONT",
|
||||
"TEST_MED_SHOW");
|
||||
|
||||
/* Answers */
|
||||
TstExa_ComputeAnswerScore (Exam,NumQst,Question);
|
||||
TstExa_WriteAnswersExam (UsrDat,Exam,NumQst,Question,Visibility);
|
||||
}
|
||||
else
|
||||
Ale_ShowAlert (Ale_WARNING,Txt_Question_modified);
|
||||
}
|
||||
else
|
||||
Ale_ShowAlert (Ale_WARNING,Txt_Question_removed);
|
||||
|
||||
/* Answers */
|
||||
TstExa_ComputeAnswerScore (Exam,NumQst,&Question);
|
||||
TstExa_WriteAnswersExam (UsrDat,
|
||||
Exam,NumQst,&Question,
|
||||
Visibility);
|
||||
/* Write score retrieved from database */
|
||||
HTM_DIV_Begin ("class=\"DAT_SMALL LM\"");
|
||||
HTM_TxtColonNBSP (Txt_Score);
|
||||
HTM_SPAN_Begin ("class=\"%s\"",
|
||||
Exam->Questions[NumQst].StrAnswers[0] ?
|
||||
(Exam->Questions[NumQst].Score > 0 ? "ANS_OK" : // Correct/semicorrect
|
||||
"ANS_BAD") : // Wrong
|
||||
"ANS_0"); // Blank answer
|
||||
HTM_Double2Decimals (Exam->Questions[NumQst].Score);
|
||||
HTM_SPAN_End ();
|
||||
HTM_DIV_End ();
|
||||
|
||||
/* Question feedback (row[4]) */
|
||||
if (TstVis_IsVisibleFeedbackTxt (Visibility))
|
||||
Tst_WriteQstFeedback (row[4],"TEST_EXA_LIGHT");
|
||||
/* Question feedback */
|
||||
if (QuestionUneditedAfterExam)
|
||||
if (TstVis_IsVisibleFeedbackTxt (Visibility))
|
||||
Tst_WriteQstFeedback (Feedback,"TEST_EXA_LIGHT");
|
||||
|
||||
HTM_TD_End ();
|
||||
|
||||
/***** End row *****/
|
||||
HTM_TR_End ();
|
||||
|
||||
/***** Destroy test question *****/
|
||||
Tst_QstDestructor (&Question);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
@ -918,54 +921,28 @@ static void TstExa_WriteAnswersExam (struct UsrData *UsrDat,
|
|||
struct Tst_Question *Question,
|
||||
unsigned Visibility)
|
||||
{
|
||||
MYSQL_RES *mysql_res;
|
||||
|
||||
/***** Get answer of a question from database *****/
|
||||
Tst_GetAnswersQst (Question,&mysql_res,
|
||||
false); // Don't shuffle
|
||||
/*
|
||||
row[0] AnsInd
|
||||
row[1] Answer
|
||||
row[2] Feedback
|
||||
row[3] MedCod
|
||||
row[4] Correct
|
||||
*/
|
||||
|
||||
/***** Write answer depending on type *****/
|
||||
switch (Question->Answer.Type)
|
||||
{
|
||||
case Tst_ANS_INT:
|
||||
TstExa_WriteIntAnsExam (UsrDat,Exam,
|
||||
NumQst,Question,mysql_res,
|
||||
Visibility);
|
||||
TstExa_WriteIntAnsExam (UsrDat,Exam,NumQst,Question,Visibility);
|
||||
break;
|
||||
case Tst_ANS_FLOAT:
|
||||
TstExa_WriteFloatAnsExam (UsrDat,Exam,
|
||||
NumQst,Question,mysql_res,
|
||||
Visibility);
|
||||
TstExa_WriteFloatAnsExam (UsrDat,Exam,NumQst,Question,Visibility);
|
||||
break;
|
||||
case Tst_ANS_TRUE_FALSE:
|
||||
TstExa_WriteTFAnsExam (UsrDat,Exam,
|
||||
NumQst,Question,mysql_res,
|
||||
Visibility);
|
||||
TstExa_WriteTFAnsExam (UsrDat,Exam,NumQst,Question,Visibility);
|
||||
break;
|
||||
case Tst_ANS_UNIQUE_CHOICE:
|
||||
case Tst_ANS_MULTIPLE_CHOICE:
|
||||
TstExa_WriteChoiceAnsExam (UsrDat,Exam,
|
||||
NumQst,Question,mysql_res,
|
||||
Visibility);
|
||||
TstExa_WriteChoiceAnsExam (UsrDat,Exam,NumQst,Question,Visibility);
|
||||
break;
|
||||
case Tst_ANS_TEXT:
|
||||
TstExa_WriteTextAnsExam (UsrDat,Exam,
|
||||
NumQst,Question,mysql_res,
|
||||
Visibility);
|
||||
TstExa_WriteTextAnsExam (UsrDat,Exam,NumQst,Question,Visibility);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/***** Free structure that stores the query result *****/
|
||||
DB_FreeMySQLResult (&mysql_res);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
@ -976,27 +953,13 @@ static void TstExa_WriteIntAnsExam (struct UsrData *UsrDat,
|
|||
const struct TstExa_Exam *Exam,
|
||||
unsigned NumQst,
|
||||
const struct Tst_Question *Question,
|
||||
MYSQL_RES *mysql_res,
|
||||
unsigned Visibility)
|
||||
{
|
||||
MYSQL_ROW row;
|
||||
long IntAnswerUsr;
|
||||
long IntAnswerCorr;
|
||||
/*
|
||||
row[0] AnsInd
|
||||
row[1] Answer
|
||||
row[2] Feedback
|
||||
row[3] MedCod
|
||||
row[4] Correct
|
||||
*/
|
||||
|
||||
/***** Check if number of rows is correct *****/
|
||||
Tst_CheckIfNumberOfAnswersIsOne (Question);
|
||||
|
||||
/***** Get the numerical value of the correct answer *****/
|
||||
row = mysql_fetch_row (mysql_res);
|
||||
if (sscanf (row[1],"%ld",&IntAnswerCorr) != 1)
|
||||
Lay_ShowErrorAndExit ("Wrong integer answer.");
|
||||
|
||||
/***** Header with the title of each column *****/
|
||||
HTM_TABLE_BeginPadding (2);
|
||||
HTM_TR_Begin (NULL);
|
||||
|
@ -1012,8 +975,8 @@ static void TstExa_WriteIntAnsExam (struct UsrData *UsrDat,
|
|||
{
|
||||
HTM_TD_Begin ("class=\"%s CM\"",
|
||||
TstVis_IsVisibleCorrectAns (Visibility) ?
|
||||
(IntAnswerUsr == IntAnswerCorr ? "ANS_OK" :
|
||||
"ANS_BAD") :
|
||||
(IntAnswerUsr == Question->Answer.Integer ? "ANS_OK" :
|
||||
"ANS_BAD") :
|
||||
"ANS_0");
|
||||
HTM_Long (IntAnswerUsr);
|
||||
HTM_TD_End ();
|
||||
|
@ -1032,7 +995,7 @@ static void TstExa_WriteIntAnsExam (struct UsrData *UsrDat,
|
|||
HTM_TD_Begin ("class=\"ANS_0 CM\"");
|
||||
if (TstVis_IsVisibleQstAndAnsTxt (Visibility) &&
|
||||
TstVis_IsVisibleCorrectAns (Visibility))
|
||||
HTM_Long (IntAnswerCorr);
|
||||
HTM_Long (Question->Answer.Integer);
|
||||
else
|
||||
Ico_PutIconNotVisible ();
|
||||
HTM_TD_End ();
|
||||
|
@ -1043,17 +1006,17 @@ static void TstExa_WriteIntAnsExam (struct UsrData *UsrDat,
|
|||
if (TstVis_IsVisibleEachQstScore (Visibility))
|
||||
{
|
||||
TstExa_WriteScoreStart (2);
|
||||
if (!Exam->Questions[NumQst].StrAnswers[0]) // If user has omitted the answer
|
||||
if (!Exam->Questions[NumQst].StrAnswers[0]) // If user has omitted the answer
|
||||
{
|
||||
HTM_SPAN_Begin ("class=\"ANS_0\"");
|
||||
HTM_Double2Decimals (0.0);
|
||||
}
|
||||
else if (IntAnswerUsr == IntAnswerCorr) // If correct
|
||||
else if (IntAnswerUsr == Question->Answer.Integer) // If correct
|
||||
{
|
||||
HTM_SPAN_Begin ("class=\"ANS_OK\"");
|
||||
HTM_Double2Decimals (1.0);
|
||||
}
|
||||
else // If wrong
|
||||
else // If wrong
|
||||
{
|
||||
HTM_SPAN_Begin ("class=\"ANS_BAD\"");
|
||||
HTM_Double2Decimals (0.0);
|
||||
|
@ -1073,40 +1036,14 @@ static void TstExa_WriteFloatAnsExam (struct UsrData *UsrDat,
|
|||
const struct TstExa_Exam *Exam,
|
||||
unsigned NumQst,
|
||||
const struct Tst_Question *Question,
|
||||
MYSQL_RES *mysql_res,
|
||||
unsigned Visibility)
|
||||
{
|
||||
MYSQL_ROW row;
|
||||
unsigned i;
|
||||
double FloatAnsUsr = 0.0,Tmp;
|
||||
double FloatAnsCorr[2];
|
||||
/*
|
||||
row[0] AnsInd
|
||||
row[1] Answer
|
||||
row[2] Feedback
|
||||
row[3] MedCod
|
||||
row[4] Correct
|
||||
*/
|
||||
double FloatAnsUsr = 0.0;
|
||||
|
||||
/***** Check if number of rows is correct *****/
|
||||
if (Question->Answer.NumOptions != 2)
|
||||
Lay_ShowErrorAndExit ("Wrong float range.");
|
||||
|
||||
/***** Get the numerical value of the minimum and maximum correct answers *****/
|
||||
for (i = 0;
|
||||
i < 2;
|
||||
i++)
|
||||
{
|
||||
row = mysql_fetch_row (mysql_res);
|
||||
FloatAnsCorr[i] = Str_GetDoubleFromStr (row[1]);
|
||||
}
|
||||
if (FloatAnsCorr[0] > FloatAnsCorr[1]) // The maximum and the minimum are swapped
|
||||
{
|
||||
/* Swap maximum and minimum */
|
||||
Tmp = FloatAnsCorr[0];
|
||||
FloatAnsCorr[0] = FloatAnsCorr[1];
|
||||
FloatAnsCorr[1] = Tmp;
|
||||
}
|
||||
|
||||
/***** Header with the title of each column *****/
|
||||
HTM_TABLE_BeginPadding (2);
|
||||
HTM_TR_Begin (NULL);
|
||||
|
@ -1122,9 +1059,9 @@ static void TstExa_WriteFloatAnsExam (struct UsrData *UsrDat,
|
|||
// A bad formatted floating point answer will interpreted as 0.0
|
||||
HTM_TD_Begin ("class=\"%s CM\"",
|
||||
TstVis_IsVisibleCorrectAns (Visibility) ?
|
||||
((FloatAnsUsr >= FloatAnsCorr[0] &&
|
||||
FloatAnsUsr <= FloatAnsCorr[1]) ? "ANS_OK" :
|
||||
"ANS_BAD") :
|
||||
((FloatAnsUsr >= Question->Answer.FloatingPoint[0] &&
|
||||
FloatAnsUsr <= Question->Answer.FloatingPoint[1]) ? "ANS_OK" :
|
||||
"ANS_BAD") :
|
||||
"ANS_0");
|
||||
HTM_Double (FloatAnsUsr);
|
||||
}
|
||||
|
@ -1138,9 +1075,9 @@ static void TstExa_WriteFloatAnsExam (struct UsrData *UsrDat,
|
|||
TstVis_IsVisibleCorrectAns (Visibility))
|
||||
{
|
||||
HTM_Txt ("[");
|
||||
HTM_Double (FloatAnsCorr[0]);
|
||||
HTM_Double (Question->Answer.FloatingPoint[0]);
|
||||
HTM_Txt ("; ");
|
||||
HTM_Double (FloatAnsCorr[1]);
|
||||
HTM_Double (Question->Answer.FloatingPoint[1]);
|
||||
HTM_Txt ("]");
|
||||
}
|
||||
else
|
||||
|
@ -1153,18 +1090,18 @@ static void TstExa_WriteFloatAnsExam (struct UsrData *UsrDat,
|
|||
if (TstVis_IsVisibleEachQstScore (Visibility))
|
||||
{
|
||||
TstExa_WriteScoreStart (2);
|
||||
if (!Exam->Questions[NumQst].StrAnswers[0]) // If user has omitted the answer
|
||||
if (!Exam->Questions[NumQst].StrAnswers[0]) // If user has omitted the answer
|
||||
{
|
||||
HTM_SPAN_Begin ("class=\"ANS_0\"");
|
||||
HTM_Double2Decimals (0.0);
|
||||
}
|
||||
else if (FloatAnsUsr >= FloatAnsCorr[0] &&
|
||||
FloatAnsUsr <= FloatAnsCorr[1]) // If correct (inside the interval)
|
||||
else if (FloatAnsUsr >= Question->Answer.FloatingPoint[0] &&
|
||||
FloatAnsUsr <= Question->Answer.FloatingPoint[1]) // If correct (inside the interval)
|
||||
{
|
||||
HTM_SPAN_Begin ("class=\"ANS_OK\"");
|
||||
HTM_Double2Decimals (1.0);
|
||||
}
|
||||
else // If wrong (outside the interval)
|
||||
else // If wrong (outside the interval)
|
||||
{
|
||||
HTM_SPAN_Begin ("class=\"ANS_BAD\"");
|
||||
HTM_Double2Decimals (0.0);
|
||||
|
@ -1184,24 +1121,15 @@ static void TstExa_WriteTFAnsExam (struct UsrData *UsrDat,
|
|||
const struct TstExa_Exam *Exam,
|
||||
unsigned NumQst,
|
||||
const struct Tst_Question *Question,
|
||||
MYSQL_RES *mysql_res,
|
||||
unsigned Visibility)
|
||||
{
|
||||
MYSQL_ROW row;
|
||||
char AnsTF;
|
||||
/*
|
||||
row[0] AnsInd
|
||||
row[1] Answer
|
||||
row[2] Feedback
|
||||
row[3] MedCod
|
||||
row[4] Correct
|
||||
*/
|
||||
char AnsTFUsr;
|
||||
|
||||
/***** Check if number of rows is correct *****/
|
||||
Tst_CheckIfNumberOfAnswersIsOne (Question);
|
||||
|
||||
/***** Get answer true or false *****/
|
||||
row = mysql_fetch_row (mysql_res);
|
||||
AnsTF = Exam->Questions[NumQst].StrAnswers[0];
|
||||
AnsTFUsr = Exam->Questions[NumQst].StrAnswers[0];
|
||||
|
||||
/***** Header with the title of each column *****/
|
||||
HTM_TABLE_BeginPadding (2);
|
||||
|
@ -1214,17 +1142,17 @@ static void TstExa_WriteTFAnsExam (struct UsrData *UsrDat,
|
|||
/***** Write the user answer *****/
|
||||
HTM_TD_Begin ("class=\"%s CM\"",
|
||||
TstVis_IsVisibleCorrectAns (Visibility) ?
|
||||
(AnsTF == row[1][0] ? "ANS_OK" :
|
||||
"ANS_BAD") :
|
||||
(AnsTFUsr == Question->Answer.TF ? "ANS_OK" :
|
||||
"ANS_BAD") :
|
||||
"ANS_0");
|
||||
Tst_WriteAnsTF (AnsTF);
|
||||
Tst_WriteAnsTF (AnsTFUsr);
|
||||
HTM_TD_End ();
|
||||
|
||||
/***** Write the correct answer *****/
|
||||
HTM_TD_Begin ("class=\"ANS_0 CM\"");
|
||||
if (TstVis_IsVisibleQstAndAnsTxt (Visibility) &&
|
||||
TstVis_IsVisibleCorrectAns (Visibility))
|
||||
Tst_WriteAnsTF (row[1][0]);
|
||||
Tst_WriteAnsTF (Question->Answer.TF);
|
||||
else
|
||||
Ico_PutIconNotVisible ();
|
||||
HTM_TD_End ();
|
||||
|
@ -1235,17 +1163,17 @@ static void TstExa_WriteTFAnsExam (struct UsrData *UsrDat,
|
|||
if (TstVis_IsVisibleEachQstScore (Visibility))
|
||||
{
|
||||
TstExa_WriteScoreStart (2);
|
||||
if (AnsTF == '\0') // If user has omitted the answer
|
||||
if (AnsTFUsr == '\0') // If user has omitted the answer
|
||||
{
|
||||
HTM_SPAN_Begin ("class=\"ANS_0\"");
|
||||
HTM_Double2Decimals (0.0);
|
||||
}
|
||||
else if (AnsTF == row[1][0]) // If correct
|
||||
else if (AnsTFUsr == Question->Answer.TF) // If correct
|
||||
{
|
||||
HTM_SPAN_Begin ("class=\"ANS_OK\"");
|
||||
HTM_Double2Decimals (1.0);
|
||||
}
|
||||
else // If wrong
|
||||
else // If wrong
|
||||
{
|
||||
HTM_SPAN_Begin ("class=\"ANS_BAD\"");
|
||||
HTM_Double2Decimals (-1.0);
|
||||
|
@ -1265,7 +1193,6 @@ static void TstExa_WriteChoiceAnsExam (struct UsrData *UsrDat,
|
|||
const struct TstExa_Exam *Exam,
|
||||
unsigned NumQst,
|
||||
struct Tst_Question *Question,
|
||||
MYSQL_RES *mysql_res,
|
||||
unsigned Visibility)
|
||||
{
|
||||
extern const char *Txt_TST_Answer_given_by_the_user;
|
||||
|
@ -1279,17 +1206,6 @@ static void TstExa_WriteChoiceAnsExam (struct UsrData *UsrDat,
|
|||
char *Str;
|
||||
} Ans;
|
||||
|
||||
/***** Get text and correctness of answers for this question
|
||||
from database (one row per answer) *****/
|
||||
/*
|
||||
row[0] AnsInd
|
||||
row[1] Answer
|
||||
row[2] Feedback
|
||||
row[3] MedCod
|
||||
row[4] Correct
|
||||
*/
|
||||
Tst_GetChoiceAns (Question,mysql_res);
|
||||
|
||||
/***** Get indexes for this question from string *****/
|
||||
TstExa_GetIndexesFromStr (Exam->Questions[NumQst].StrIndexes,Indexes);
|
||||
|
||||
|
@ -1421,55 +1337,30 @@ static void TstExa_WriteTextAnsExam (struct UsrData *UsrDat,
|
|||
const struct TstExa_Exam *Exam,
|
||||
unsigned NumQst,
|
||||
struct Tst_Question *Question,
|
||||
MYSQL_RES *mysql_res,
|
||||
unsigned Visibility)
|
||||
{
|
||||
unsigned NumOpt;
|
||||
MYSQL_ROW row;
|
||||
char TextAnsUsr[TstExa_MAX_BYTES_ANSWERS_ONE_QST + 1];
|
||||
char TextAnsOK[TstExa_MAX_BYTES_ANSWERS_ONE_QST + 1];
|
||||
bool Correct = false;
|
||||
/*
|
||||
row[0] AnsInd
|
||||
row[1] Answer
|
||||
row[2] Feedback
|
||||
row[3] MedCod
|
||||
row[4] Correct
|
||||
*/
|
||||
|
||||
/***** Get text and correctness of answers for this question from database (one row per answer) *****/
|
||||
for (NumOpt = 0;
|
||||
NumOpt < Question->Answer.NumOptions;
|
||||
NumOpt++)
|
||||
{
|
||||
/***** Get next answer *****/
|
||||
row = mysql_fetch_row (mysql_res);
|
||||
/***** Convert answer text, that is in HTML, to rigorous HTML ******/
|
||||
if (Question->Answer.Options[NumOpt].Text[0])
|
||||
Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
|
||||
Question->Answer.Options[NumOpt].Text,
|
||||
Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false);
|
||||
|
||||
/***** Allocate memory for text in this choice answer *****/
|
||||
if (!Tst_AllocateTextChoiceAnswer (Question,NumOpt))
|
||||
/* Abort on error */
|
||||
Ale_ShowAlertsAndExit ();
|
||||
|
||||
/***** Copy answer text (row[1]) and convert it, that is in HTML, to rigorous HTML ******/
|
||||
Str_Copy (Question->Answer.Options[NumOpt].Text,row[1],
|
||||
Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
|
||||
Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
|
||||
Question->Answer.Options[NumOpt].Text,
|
||||
Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false);
|
||||
|
||||
/***** Copy answer feedback (row[2]) and convert it, that is in HTML, to rigorous HTML ******/
|
||||
/***** Convert answer feedback, that is in HTML, to rigorous HTML ******/
|
||||
if (TstVis_IsVisibleFeedbackTxt (Visibility))
|
||||
if (row[2])
|
||||
if (row[2][0])
|
||||
{
|
||||
Str_Copy (Question->Answer.Options[NumOpt].Feedback,row[2],
|
||||
Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
|
||||
Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
|
||||
Question->Answer.Options[NumOpt].Feedback,
|
||||
Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false);
|
||||
}
|
||||
|
||||
/***** Assign correctness (row[4]) of this answer (this option) *****/
|
||||
Question->Answer.Options[NumOpt].Correct = (row[4][0] == 'Y');
|
||||
if (Question->Answer.Options[NumOpt].Feedback[0])
|
||||
Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
|
||||
Question->Answer.Options[NumOpt].Feedback,
|
||||
Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false);
|
||||
}
|
||||
|
||||
/***** Header with the title of each column *****/
|
||||
|
@ -2437,13 +2328,10 @@ void TstExa_ShowExamAnswers (struct UsrData *UsrDat,
|
|||
struct TstExa_Exam *Exam,
|
||||
unsigned Visibility)
|
||||
{
|
||||
extern const char *Txt_Question_modified;
|
||||
extern const char *Txt_Question_removed;
|
||||
MYSQL_RES *mysql_res;
|
||||
MYSQL_ROW row;
|
||||
unsigned NumQst;
|
||||
bool ThisQuestionHasBeenEdited;
|
||||
time_t EditTimeUTC;
|
||||
struct Tst_Question Question;
|
||||
char Stem[Cns_MAX_BYTES_TEXT + 1];
|
||||
char Feedback[Cns_MAX_BYTES_TEXT + 1];
|
||||
|
||||
for (NumQst = 0;
|
||||
NumQst < Exam->NumQsts;
|
||||
|
@ -2451,60 +2339,21 @@ void TstExa_ShowExamAnswers (struct UsrData *UsrDat,
|
|||
{
|
||||
Gbl.RowEvenOdd = NumQst % 2;
|
||||
|
||||
/***** Query database *****/
|
||||
if (Tst_GetOneQuestionByCod (Exam->Questions[NumQst].QstCod,&mysql_res)) // Question exists
|
||||
{
|
||||
/***** Get row of the result of the query *****/
|
||||
row = mysql_fetch_row (mysql_res);
|
||||
/***** Create test question *****/
|
||||
Tst_QstConstructor (&Question);
|
||||
Question.QstCod = Exam->Questions[NumQst].QstCod;
|
||||
|
||||
/***** If this question has been edited later than test time
|
||||
==> don't show question ****/
|
||||
EditTimeUTC = Dat_GetUNIXTimeFromStr (row[0]);
|
||||
ThisQuestionHasBeenEdited = false;
|
||||
if (EditTimeUTC > Exam->TimeUTC[Dat_START_TIME])
|
||||
ThisQuestionHasBeenEdited = true;
|
||||
/***** Get question data *****/
|
||||
Tst_GetQstDataFromDB (&Question,Stem,Feedback);
|
||||
|
||||
if (ThisQuestionHasBeenEdited)
|
||||
{
|
||||
/***** Question has been edited *****/
|
||||
HTM_TR_Begin (NULL);
|
||||
/***** Write questions and answers *****/
|
||||
TstExa_WriteQstAndAnsExam (UsrDat,
|
||||
Exam,NumQst,
|
||||
&Question,Stem,Feedback,
|
||||
Visibility);
|
||||
|
||||
HTM_TD_Begin ("class=\"BIG_INDEX RT COLOR%u\"",Gbl.RowEvenOdd);
|
||||
HTM_Unsigned (NumQst + 1);
|
||||
HTM_TD_End ();
|
||||
|
||||
HTM_TD_Begin ("class=\"DAT_LIGHT LT COLOR%u\"",Gbl.RowEvenOdd);
|
||||
HTM_Txt (Txt_Question_modified);
|
||||
HTM_TD_End ();
|
||||
|
||||
HTM_TR_End ();
|
||||
}
|
||||
else
|
||||
/***** Write questions and answers *****/
|
||||
TstExa_WriteQstAndAnsExam (UsrDat,
|
||||
Exam,
|
||||
NumQst,
|
||||
row,
|
||||
Visibility);
|
||||
}
|
||||
else
|
||||
{
|
||||
/***** Question does not exists *****/
|
||||
HTM_TR_Begin (NULL);
|
||||
|
||||
HTM_TD_Begin ("class=\"BIG_INDEX RT COLOR%u\"",Gbl.RowEvenOdd);
|
||||
HTM_Unsigned (NumQst + 1);
|
||||
HTM_TD_End ();
|
||||
|
||||
HTM_TD_Begin ("class=\"DAT_LIGHT LT COLOR%u\"",Gbl.RowEvenOdd);
|
||||
HTM_Txt (Txt_Question_removed);
|
||||
HTM_TD_End ();
|
||||
|
||||
HTM_TR_End ();
|
||||
}
|
||||
|
||||
/***** Free structure that stores the query result *****/
|
||||
DB_FreeMySQLResult (&mysql_res);
|
||||
/***** Destroy test question *****/
|
||||
Tst_QstDestructor (&Question);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -71,11 +71,6 @@ void TstExa_CreateExamInDB (struct TstExa_Exam *Exam);
|
|||
void TstExa_UpdateExamInDB (const struct TstExa_Exam *Exam);
|
||||
|
||||
void TstExa_ShowExamAfterAssess (struct TstExa_Exam *Exam);
|
||||
void TstExa_WriteQstAndAnsExam (struct UsrData *UsrDat,
|
||||
struct TstExa_Exam *Result,
|
||||
unsigned NumQst,
|
||||
MYSQL_ROW row,
|
||||
unsigned Visibility);
|
||||
|
||||
void TstExa_ComputeScoresAndStoreExamQuestions (struct TstExa_Exam *Exam,
|
||||
bool UpdateQstScore);
|
||||
|
|
|
@ -675,7 +675,7 @@ static void TsI_ImportQuestionsFromXMLBuffer (const char *XMLBuffer)
|
|||
}
|
||||
|
||||
/* Get shuffle. By default, shuffle is false. */
|
||||
Question.Shuffle = false;
|
||||
Question.Answer.Shuffle = false;
|
||||
for (AnswerElem = QuestionElem->FirstChild;
|
||||
AnswerElem != NULL;
|
||||
AnswerElem = AnswerElem->NextBrother)
|
||||
|
@ -689,7 +689,7 @@ static void TsI_ImportQuestionsFromXMLBuffer (const char *XMLBuffer)
|
|||
Attribute = Attribute->Next)
|
||||
if (!strcmp (Attribute->AttributeName,"shuffle"))
|
||||
{
|
||||
Question.Shuffle = XML_GetAttributteYesNoFromXMLTree (Attribute);
|
||||
Question.Answer.Shuffle = XML_GetAttributteYesNoFromXMLTree (Attribute);
|
||||
break; // Only first attribute "shuffle"
|
||||
}
|
||||
break; // Only first element "answer"
|
||||
|
@ -1023,7 +1023,7 @@ static void TsI_WriteRowImportedQst (struct XMLElement *StemElem,
|
|||
if (Question->Answer.Type == Tst_ANS_UNIQUE_CHOICE ||
|
||||
Question->Answer.Type == Tst_ANS_MULTIPLE_CHOICE)
|
||||
/* Put an icon that indicates whether shuffle is enabled or not */
|
||||
if (Question->Shuffle)
|
||||
if (Question->Answer.Shuffle)
|
||||
Ico_PutIcon ("check.svg",Txt_TST_Answer_given_by_the_teachers,
|
||||
QuestionExists ? "ICO_HIDDEN ICO16x16" :
|
||||
"ICO16x16");
|
||||
|
|
Loading…
Reference in New Issue