Version19.152

This commit is contained in:
acanas 2020-03-23 17:14:41 +01:00
parent 2e74679789
commit 5592e9b8b3
12 changed files with 306 additions and 236 deletions

View File

@ -497,7 +497,7 @@ enscript -2 --landscape --color --file-align=2 --highlight --line-numbers -o - *
En OpenSWAD: En OpenSWAD:
ps2pdf source.ps destination.pdf ps2pdf source.ps destination.pdf
*/ */
#define Log_PLATFORM_VERSION "SWAD 19.151.4 (2020-03-22)" #define Log_PLATFORM_VERSION "SWAD 19.152 (2020-03-23)"
#define CSS_FILE "swad19.146.css" #define CSS_FILE "swad19.146.css"
#define JS_FILE "swad19.91.1.js" #define JS_FILE "swad19.91.1.js"
/* /*
@ -524,6 +524,7 @@ Param
// TODO: Oresti Baños: cambiar ojos por candados en descriptores para prohibir/permitir y dejar los ojos para poder elegir descriptores // TODO: Oresti Baños: cambiar ojos por candados en descriptores para prohibir/permitir y dejar los ojos para poder elegir descriptores
// TODO: Si el alumno ha marcado "Permitir que los profesores...", entonces pedir confirmación al pulsar el botón azul, para evitar que se envíe por error antes de tiempo // TODO: Si el alumno ha marcado "Permitir que los profesores...", entonces pedir confirmación al pulsar el botón azul, para evitar que se envíe por error antes de tiempo
Version 19.152: Mar 23, 2020 Code refactoring in tests. (283525 lines)
Version 19.151.4: Mar 22, 2020 Code refactoring in tests. (283471 lines) Version 19.151.4: Mar 22, 2020 Code refactoring in tests. (283471 lines)
Version 19.151.3: Mar 22, 2020 Code refactoring in tests. (283433 lines) Version 19.151.3: Mar 22, 2020 Code refactoring in tests. (283433 lines)
Version 19.151.2: Mar 21, 2020 Code refactoring in tests. (283446 lines) Version 19.151.2: Mar 21, 2020 Code refactoring in tests. (283446 lines)

View File

@ -156,7 +156,7 @@ static void Gam_ListGameQuestions (struct Game *Game);
static void Gam_ListOneOrMoreQuestionsForEdition (long GamCod,unsigned NumQsts, static void Gam_ListOneOrMoreQuestionsForEdition (long GamCod,unsigned NumQsts,
MYSQL_RES *mysql_res, MYSQL_RES *mysql_res,
bool ICanEditQuestions); bool ICanEditQuestions);
static void Gam_ListQuestionForEdition (long QstCod,const char *StrQstInd); static void Gam_ListQuestionForEdition (long QstCod,unsigned QstInd);
static void Gam_PutIconToAddNewQuestions (void); static void Gam_PutIconToAddNewQuestions (void);
static void Gam_PutButtonToAddNewQuestions (void); static void Gam_PutButtonToAddNewQuestions (void);
@ -1972,7 +1972,7 @@ static void Gam_ListOneOrMoreQuestionsForEdition (long GamCod,unsigned NumQsts,
HTM_TD_End (); HTM_TD_End ();
/***** Question *****/ /***** Question *****/
Gam_ListQuestionForEdition (QstCod,StrQstInd); Gam_ListQuestionForEdition (QstCod,QstInd);
HTM_TR_End (); HTM_TR_End ();
@ -1988,9 +1988,8 @@ static void Gam_ListOneOrMoreQuestionsForEdition (long GamCod,unsigned NumQsts,
/********************** List game question for edition ***********************/ /********************** List game question for edition ***********************/
/*****************************************************************************/ /*****************************************************************************/
static void Gam_ListQuestionForEdition (long QstCod,const char *StrQstInd) static void Gam_ListQuestionForEdition (long QstCod,unsigned QstInd)
{ {
extern const char *Txt_TST_STR_ANSWER_TYPES[Tst_NUM_ANS_TYPES];
extern const char *Txt_Question_removed; extern const char *Txt_Question_removed;
MYSQL_RES *mysql_res; MYSQL_RES *mysql_res;
MYSQL_ROW row; MYSQL_ROW row;
@ -2018,23 +2017,14 @@ static void Gam_ListQuestionForEdition (long QstCod,const char *StrQstInd)
*/ */
} }
/***** Number of question and answer type *****/ /***** Number of question and answer type (row[1]) *****/
HTM_TD_Begin ("class=\"RT COLOR%u\"",Gbl.RowEvenOdd); HTM_TD_Begin ("class=\"RT COLOR%u\"",Gbl.RowEvenOdd);
Tst_WriteNumQst (QstInd);
/* Write number of question */
HTM_DIV_Begin ("class=\"BIG_INDEX\"");
HTM_Txt (StrQstInd);
HTM_DIV_End ();
/* Write answer type (row[1]) */
HTM_DIV_Begin ("class=\"DAT_SMALL\"");
if (QstExists) if (QstExists)
{ {
AnswerType = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[1]); AnswerType = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[1]);
HTM_Txt (Txt_TST_STR_ANSWER_TYPES[AnswerType]); Tst_WriteAnswerType (AnswerType);
} }
HTM_DIV_End ();
HTM_TD_End (); HTM_TD_End ();
/***** Write question code *****/ /***** Write question code *****/

View File

@ -649,12 +649,14 @@ struct Globals
bool PutIconPrint; bool PutIconPrint;
} ContextualIcons; } ContextualIcons;
} TimeTable; } TimeTable;
/*
struct struct
{ {
long QstCodes[TstCfg_MAX_QUESTIONS_PER_TEST]; // Codes of the sent/received questions in a test long QstCodes[TstCfg_MAX_QUESTIONS_PER_TEST]; // Codes of the sent/received questions in a test
char StrIndexesOneQst[TstCfg_MAX_QUESTIONS_PER_TEST][Tst_MAX_BYTES_INDEXES_ONE_QST + 1]; // 0 1 2 3, 3 0 2 1, etc. char StrIndexesOneQst[TstCfg_MAX_QUESTIONS_PER_TEST][Tst_MAX_BYTES_INDEXES_ONE_QST + 1]; // 0 1 2 3, 3 0 2 1, etc.
char StrAnswersOneQst[TstCfg_MAX_QUESTIONS_PER_TEST][Tst_MAX_BYTES_ANSWERS_ONE_QST + 1]; // Answers selected by user char StrAnswersOneQst[TstCfg_MAX_QUESTIONS_PER_TEST][Tst_MAX_BYTES_ANSWERS_ONE_QST + 1]; // Answers selected by user
} Test; } Test;
*/
struct struct
{ {
struct DateTime DateIni; // TODO: Remove in future versions struct DateTime DateIni; // TODO: Remove in future versions

View File

@ -222,7 +222,7 @@ static void Mch_GetNumPlayers (struct Match *Match);
static void Mch_RemoveMyAnswerToMatchQuestion (const struct Match *Match); static void Mch_RemoveMyAnswerToMatchQuestion (const struct Match *Match);
static double Mch_ComputeScore (unsigned NumQsts); static double Mch_ComputeScore (unsigned NumQsts,const struct Tst_UsrAnswers *UsrAnswers);
static unsigned Mch_GetNumUsrsWhoHaveAnswerMch (long MchCod); static unsigned Mch_GetNumUsrsWhoHaveAnswerMch (long MchCod);
@ -3674,6 +3674,7 @@ void Mch_ReceiveQuestionAnswer (void)
struct Mch_UsrAnswer UsrAnswer; struct Mch_UsrAnswer UsrAnswer;
unsigned NumQsts; unsigned NumQsts;
unsigned NumQstsNotBlank; unsigned NumQstsNotBlank;
struct Tst_UsrAnswers UsrAnswers;
double TotalScore; double TotalScore;
/***** Get data of the match from database *****/ /***** Get data of the match from database *****/
@ -3730,8 +3731,9 @@ void Mch_ReceiveQuestionAnswer (void)
/***** Update student's match result *****/ /***** Update student's match result *****/
McR_GetMatchResultQuestionsFromDB (Match.MchCod,Gbl.Usrs.Me.UsrDat.UsrCod, McR_GetMatchResultQuestionsFromDB (Match.MchCod,Gbl.Usrs.Me.UsrDat.UsrCod,
&NumQsts,&NumQstsNotBlank); &NumQsts,&NumQstsNotBlank,
TotalScore = Mch_ComputeScore (NumQsts); &UsrAnswers);
TotalScore = Mch_ComputeScore (NumQsts,&UsrAnswers);
Str_SetDecimalPointToUS (); // To print the floating point as a dot Str_SetDecimalPointToUS (); // To print the floating point as a dot
if (DB_QueryCOUNT ("can not get if match result exists", if (DB_QueryCOUNT ("can not get if match result exists",
@ -3788,7 +3790,7 @@ static void Mch_RemoveMyAnswerToMatchQuestion (const struct Match *Match)
/******************** Compute match score for a student **********************/ /******************** Compute match score for a student **********************/
/*****************************************************************************/ /*****************************************************************************/
static double Mch_ComputeScore (unsigned NumQsts) static double Mch_ComputeScore (unsigned NumQsts,const struct Tst_UsrAnswers *UsrAnswers)
{ {
unsigned NumQst; unsigned NumQst;
struct Tst_Question Question; struct Tst_Question Question;
@ -3807,16 +3809,17 @@ static double Mch_ComputeScore (unsigned NumQsts)
NumQst++) NumQst++)
{ {
/***** Get indexes for this question from string *****/ /***** Get indexes for this question from string *****/
Tst_GetIndexesFromStr (Gbl.Test.StrIndexesOneQst[NumQst],Indexes); Tst_GetIndexesFromStr (UsrAnswers->StrIndexesOneQst[NumQst],Indexes);
/***** Get the user's answers for this question from string *****/ /***** Get the user's answers for this question from string *****/
Tst_GetAnswersFromStr (Gbl.Test.StrAnswersOneQst[NumQst],AnswersUsr); Tst_GetAnswersFromStr (UsrAnswers->StrAnswersOneQst[NumQst],AnswersUsr);
/***** Get correct answers of test question from database *****/ /***** Get correct answers of test question from database *****/
Tst_GetCorrectAnswersFromDB (Gbl.Test.QstCodes[NumQst],&Question); Tst_GetCorrectAnswersFromDB (UsrAnswers->QstCodes[NumQst],&Question);
/***** Compute the total score of this question *****/ /***** Compute the total score of this question *****/
Tst_ComputeScoreQst (&Question,Indexes,AnswersUsr,&ScoreThisQst,&AnswerIsNotBlank); Tst_ComputeScoreQst (&Question,
Indexes,AnswersUsr,&ScoreThisQst,&AnswerIsNotBlank);
/***** Compute total score *****/ /***** Compute total score *****/
TotalScore += ScoreThisQst; TotalScore += ScoreThisQst;

View File

@ -1007,6 +1007,7 @@ void McR_ShowOneMchResult (void)
char *Id; char *Id;
unsigned NumQsts; unsigned NumQsts;
unsigned NumQstsNotBlank; unsigned NumQstsNotBlank;
struct Tst_UsrAnswers UsrAnswers;
double TotalScore; double TotalScore;
bool ShowPhoto; bool ShowPhoto;
char PhotoURL[PATH_MAX + 1]; char PhotoURL[PATH_MAX + 1];
@ -1067,7 +1068,8 @@ void McR_ShowOneMchResult (void)
{ {
/***** Get questions and user's answers of the match result from database *****/ /***** Get questions and user's answers of the match result from database *****/
McR_GetMatchResultQuestionsFromDB (Match.MchCod,UsrDat->UsrCod, McR_GetMatchResultQuestionsFromDB (Match.MchCod,UsrDat->UsrCod,
&NumQsts,&NumQstsNotBlank); &NumQsts,&NumQstsNotBlank,
&UsrAnswers);
/***** Begin box *****/ /***** Begin box *****/
Box_BoxBegin (NULL,Match.Title,NULL, Box_BoxBegin (NULL,Match.Title,NULL,
@ -1192,7 +1194,7 @@ void McR_ShowOneMchResult (void)
HTM_TR_End (); HTM_TR_End ();
/***** Write answers and solutions *****/ /***** Write answers and solutions *****/
TsR_ShowTestResult (UsrDat,NumQsts,TimeUTC[Dat_START_TIME], TsR_ShowTestResult (UsrDat,NumQsts,&UsrAnswers,TimeUTC[Dat_START_TIME],
Game.Visibility); Game.Visibility);
/***** End table *****/ /***** End table *****/
@ -1222,7 +1224,8 @@ void McR_ShowOneMchResult (void)
/*****************************************************************************/ /*****************************************************************************/
void McR_GetMatchResultQuestionsFromDB (long MchCod,long UsrCod, void McR_GetMatchResultQuestionsFromDB (long MchCod,long UsrCod,
unsigned *NumQsts,unsigned *NumQstsNotBlank) unsigned *NumQsts,unsigned *NumQstsNotBlank,
struct Tst_UsrAnswers *UsrAnswers)
{ {
MYSQL_RES *mysql_res; MYSQL_RES *mysql_res;
MYSQL_ROW row; MYSQL_ROW row;
@ -1252,7 +1255,7 @@ void McR_GetMatchResultQuestionsFromDB (long MchCod,long UsrCod,
row = mysql_fetch_row (mysql_res); row = mysql_fetch_row (mysql_res);
/* Get question code (row[0]) */ /* Get question code (row[0]) */
if ((Gbl.Test.QstCodes[NumQst] = Str_ConvertStrCodToLongCod (row[0])) < 0) if ((UsrAnswers->QstCodes[NumQst] = Str_ConvertStrCodToLongCod (row[0])) < 0)
Lay_ShowErrorAndExit ("Wrong code of question."); Lay_ShowErrorAndExit ("Wrong code of question.");
/* Get question index (row[1]) */ /* Get question index (row[1]) */
@ -1261,24 +1264,24 @@ void McR_GetMatchResultQuestionsFromDB (long MchCod,long UsrCod,
QstInd = (unsigned) LongNum; QstInd = (unsigned) LongNum;
/* Get indexes for this question (row[2]) */ /* Get indexes for this question (row[2]) */
Str_Copy (Gbl.Test.StrIndexesOneQst[NumQst],row[2], Str_Copy (UsrAnswers->StrIndexesOneQst[NumQst],row[2],
Tst_MAX_BYTES_INDEXES_ONE_QST); Tst_MAX_BYTES_INDEXES_ONE_QST);
/* Get answers selected by user for this question */ /* Get answers selected by user for this question */
Mch_GetQstAnsFromDB (MchCod,UsrCod,QstInd,&UsrAnswer); Mch_GetQstAnsFromDB (MchCod,UsrCod,QstInd,&UsrAnswer);
if (UsrAnswer.AnsInd >= 0) // UsrAnswer.AnsInd >= 0 ==> answer selected if (UsrAnswer.AnsInd >= 0) // UsrAnswer.AnsInd >= 0 ==> answer selected
{ {
snprintf (Gbl.Test.StrAnswersOneQst[NumQst],Tst_MAX_BYTES_ANSWERS_ONE_QST + 1, snprintf (UsrAnswers->StrAnswersOneQst[NumQst],Tst_MAX_BYTES_ANSWERS_ONE_QST + 1,
"%d",UsrAnswer.AnsInd); "%d",UsrAnswer.AnsInd);
(*NumQstsNotBlank)++; (*NumQstsNotBlank)++;
} }
else // UsrAnswer.AnsInd < 0 ==> no answer selected else // UsrAnswer.AnsInd < 0 ==> no answer selected
Gbl.Test.StrAnswersOneQst[NumQst][0] = '\0'; // Empty answer UsrAnswers->StrAnswersOneQst[NumQst][0] = '\0'; // Empty answer
/* Replace each comma by a separator of multiple parameters */ /* Replace each comma by a separator of multiple parameters */
/* In database commas are used as separators instead of special chars */ /* In database commas are used as separators instead of special chars */
Par_ReplaceCommaBySeparatorMultiple (Gbl.Test.StrIndexesOneQst[NumQst]); Par_ReplaceCommaBySeparatorMultiple (UsrAnswers->StrIndexesOneQst[NumQst]);
Par_ReplaceCommaBySeparatorMultiple (Gbl.Test.StrAnswersOneQst[NumQst]); Par_ReplaceCommaBySeparatorMultiple (UsrAnswers->StrAnswersOneQst[NumQst]);
} }
/***** Free structure that stores the query result *****/ /***** Free structure that stores the query result *****/

View File

@ -50,6 +50,7 @@ void McR_ShowAllMchResultsInMch (void);
void McR_ShowOneMchResult (void); void McR_ShowOneMchResult (void);
void McR_GetMatchResultQuestionsFromDB (long MchCod,long UsrCod, void McR_GetMatchResultQuestionsFromDB (long MchCod,long UsrCod,
unsigned *NumQsts,unsigned *NumQstsNotBlank); unsigned *NumQsts,unsigned *NumQstsNotBlank,
struct Tst_UsrAnswers *UsrAnswers);
#endif #endif

View File

@ -927,25 +927,30 @@ void Str_ConvertStrFloatCommaToStrFloatPoint (char *Str)
/*****************************************************************************/ /*****************************************************************************/
// This function may change Str on wrong double // This function may change Str on wrong double
double Str_GetDoubleFromStr (char *Str) double Str_GetDoubleFromStr (const char *Str)
{ {
char *StrPoint;
double DoubleNum; double DoubleNum;
/***** Trivial check *****/ /***** Trivial check *****/
if (Str == NULL) // If no string... if (Str == NULL) // If no string...
return 0.0; // ...the number is reset to 0 return 0.0; // ...the number is reset to 0
/***** Copy source string to temporary string to convert to point *****/
if (asprintf (&StrPoint,"%s",Str) < 0)
Lay_NotEnoughMemoryExit ();
/***** The string is "scanned" in floating point /***** The string is "scanned" in floating point
(it must have a point, not a comma as decimal separator) *****/ (it must have a point, not a comma as decimal separator) *****/
Str_ConvertStrFloatCommaToStrFloatPoint (Str); Str_ConvertStrFloatCommaToStrFloatPoint (StrPoint);
Str_SetDecimalPointToUS (); // To get the decimal point as a dot Str_SetDecimalPointToUS (); // To get the decimal point as a dot
if (sscanf (Str,"%lf",&DoubleNum) != 1) if (sscanf (StrPoint,"%lf",&DoubleNum) != 1) // If the string does not hold a valid number...
{ // If the string does not hold a valid number... DoubleNum = 0.0; // ...the number is reset to 0
DoubleNum = 0.0; // ...the number is reset to 0
Str[0] = '\0'; // ...and the string is reset to ""
}
Str_SetDecimalPointToLocal (); // Return to local system Str_SetDecimalPointToLocal (); // Return to local system
/***** Free temporary string *****/
free (StrPoint);
return DoubleNum; return DoubleNum;
} }

View File

@ -94,7 +94,7 @@ char Str_ConvertToLowerLetter (char Ch);
void Str_DoubleNumToStr (char **Str,double Number); void Str_DoubleNumToStr (char **Str,double Number);
void Str_DoubleNumToStrFewDigits (char **Str,double Number); void Str_DoubleNumToStrFewDigits (char **Str,double Number);
void Str_ConvertStrFloatCommaToStrFloatPoint (char *Str); void Str_ConvertStrFloatCommaToStrFloatPoint (char *Str);
double Str_GetDoubleFromStr (char *Str); double Str_GetDoubleFromStr (const char *Str);
void Str_SetDecimalPointToUS (void); void Str_SetDecimalPointToUS (void);
void Str_SetDecimalPointToLocal (void); void Str_SetDecimalPointToLocal (void);

View File

@ -131,16 +131,24 @@ static void Tst_ResetAnswerTypes (struct Tst_AnswerTypes *AnswerTypes);
static void Tst_ShowFormRequestTest (const struct Tst_Tags *Tags, static void Tst_ShowFormRequestTest (const struct Tst_Tags *Tags,
const struct Tst_AnswerTypes *AnswerTypes); const struct Tst_AnswerTypes *AnswerTypes);
static void Tst_GetQuestionsAndAnswersFromForm (unsigned NumQsts); static void Tst_GetQuestionsAndAnswersFromForm (unsigned NumQsts,
struct Tst_UsrAnswers *UsrAnswers);
static bool Tst_CheckIfNextTstAllowed (void); static bool Tst_CheckIfNextTstAllowed (void);
static void Tst_SetTstStatus (unsigned NumTst,Tst_Status_t TstStatus); static void Tst_SetTstStatus (unsigned NumTst,Tst_Status_t TstStatus);
static Tst_Status_t Tst_GetTstStatus (unsigned NumTst); static Tst_Status_t Tst_GetTstStatus (unsigned NumTst);
static unsigned Tst_GetNumAccessesTst (void); static unsigned Tst_GetNumAccessesTst (void);
static void Tst_ShowTestQuestionsWhenSeeing (unsigned NumQsts,MYSQL_RES *mysql_res); static void Tst_ShowTestQuestionsWhenSeeing (unsigned NumQsts,
MYSQL_RES *mysql_res);
static void Tst_ShowOneTestQuestionWhenSeeing (unsigned NumQst,long QstCod); static void Tst_ShowOneTestQuestionWhenSeeing (unsigned NumQst,long QstCod);
static void Tst_ShowTestResultAfterAssess (long TstCod,unsigned NumQsts, static void Tst_ShowTestResultAfterAssess (long TstCod,
unsigned NumQsts,
unsigned *NumQstsNotBlank, unsigned *NumQstsNotBlank,
const struct Tst_UsrAnswers *UsrAnswers,
double *TotalScore); double *TotalScore);
static void Tst_WriteQstAndAnsTestToAnswer (unsigned NumQst,
long QstCod,
MYSQL_ROW row);
static void Tst_PutFormToEditQstMedia (const struct Media *Media,int NumMediaInForm, static void Tst_PutFormToEditQstMedia (const struct Media *Media,int NumMediaInForm,
bool OptionsDisabled); bool OptionsDisabled);
static void Tst_UpdateScoreQst (long QstCod,double ScoreThisQst,bool AnswerIsNotBlank); static void Tst_UpdateScoreQst (long QstCod,double ScoreThisQst,bool AnswerIsNotBlank);
@ -209,6 +217,7 @@ static void Tst_GetParamGblAnswerTypes (struct Tst_AnswerTypes *AnswerTypesDst);
static void Tst_WriteAnswersTestToAnswer (unsigned NumQst,long QstCod, static void Tst_WriteAnswersTestToAnswer (unsigned NumQst,long QstCod,
Tst_AnswerType_t AnswerType,bool Shuffle); Tst_AnswerType_t AnswerType,bool Shuffle);
static void Tst_WriteAnswersTestResult (struct UsrData *UsrDat, static void Tst_WriteAnswersTestResult (struct UsrData *UsrDat,
const struct Tst_UsrAnswers *UsrAnswers,
unsigned NumQst,long QstCod, unsigned NumQst,long QstCod,
Tst_AnswerType_t AnswerType, Tst_AnswerType_t AnswerType,
unsigned Visibility, unsigned Visibility,
@ -216,6 +225,7 @@ static void Tst_WriteAnswersTestResult (struct UsrData *UsrDat,
static void Tst_WriteTFAnsViewTest (unsigned NumQst); static void Tst_WriteTFAnsViewTest (unsigned NumQst);
static void Tst_WriteTFAnsAssessTest (struct UsrData *UsrDat, static void Tst_WriteTFAnsAssessTest (struct UsrData *UsrDat,
const struct Tst_UsrAnswers *UsrAnswers,
unsigned NumQst, unsigned NumQst,
const struct Tst_Question *Question, const struct Tst_Question *Question,
MYSQL_RES *mysql_res, MYSQL_RES *mysql_res,
@ -227,6 +237,7 @@ static void Tst_WriteChoiceAnsViewTest (unsigned NumQst,long QstCod,
Tst_AnswerType_t AnswerType, Tst_AnswerType_t AnswerType,
bool Shuffle); bool Shuffle);
static void Tst_WriteChoiceAnsAssessTest (struct UsrData *UsrDat, static void Tst_WriteChoiceAnsAssessTest (struct UsrData *UsrDat,
const struct Tst_UsrAnswers *UsrAnswers,
unsigned NumQst, unsigned NumQst,
struct Tst_Question *Question, struct Tst_Question *Question,
MYSQL_RES *mysql_res, MYSQL_RES *mysql_res,
@ -237,6 +248,7 @@ static void Tst_GetChoiceAns (MYSQL_RES *mysql_res,struct Tst_Question *Question
static void Tst_WriteTextAnsViewTest (unsigned NumQst); static void Tst_WriteTextAnsViewTest (unsigned NumQst);
static void Tst_WriteTextAnsAssessTest (struct UsrData *UsrDat, static void Tst_WriteTextAnsAssessTest (struct UsrData *UsrDat,
const struct Tst_UsrAnswers *UsrAnswers,
unsigned NumQst, unsigned NumQst,
struct Tst_Question *Question, struct Tst_Question *Question,
MYSQL_RES *mysql_res, MYSQL_RES *mysql_res,
@ -246,6 +258,7 @@ static void Tst_WriteTextAnsAssessTest (struct UsrData *UsrDat,
static void Tst_WriteIntAnsViewTest (unsigned NumQst); static void Tst_WriteIntAnsViewTest (unsigned NumQst);
static void Tst_WriteIntAnsAssessTest (struct UsrData *UsrDat, static void Tst_WriteIntAnsAssessTest (struct UsrData *UsrDat,
const struct Tst_UsrAnswers *UsrAnswers,
unsigned NumQst, unsigned NumQst,
const struct Tst_Question *Question, const struct Tst_Question *Question,
MYSQL_RES *mysql_res, MYSQL_RES *mysql_res,
@ -255,6 +268,7 @@ static void Tst_WriteIntAnsAssessTest (struct UsrData *UsrDat,
static void Tst_WriteFloatAnsViewTest (unsigned NumQst); static void Tst_WriteFloatAnsViewTest (unsigned NumQst);
static void Tst_WriteFloatAnsAssessTest (struct UsrData *UsrDat, static void Tst_WriteFloatAnsAssessTest (struct UsrData *UsrDat,
const struct Tst_UsrAnswers *UsrAnswers,
unsigned NumQst, unsigned NumQst,
const struct Tst_Question *Question, const struct Tst_Question *Question,
MYSQL_RES *mysql_res, MYSQL_RES *mysql_res,
@ -577,6 +591,7 @@ void Tst_AssessTest (void)
bool AllowTeachers; // Can teachers of this course see the test result? bool AllowTeachers; // Can teachers of this course see the test result?
long TstCod = -1L; // Initialized to avoid warning long TstCod = -1L; // Initialized to avoid warning
unsigned NumQstsNotBlank; unsigned NumQstsNotBlank;
struct Tst_UsrAnswers UsrAnswers;
double TotalScore; double TotalScore;
/***** Read test configuration from database *****/ /***** Read test configuration from database *****/
@ -597,7 +612,7 @@ void Tst_AssessTest (void)
AllowTeachers = Par_GetParToBool ("AllowTchs"); AllowTeachers = Par_GetParToBool ("AllowTchs");
/***** Get questions and answers from form to assess a test *****/ /***** Get questions and answers from form to assess a test *****/
Tst_GetQuestionsAndAnswersFromForm (NumQsts); Tst_GetQuestionsAndAnswersFromForm (NumQsts,&UsrAnswers);
/***** Create new test in database to store the result *****/ /***** Create new test in database to store the result *****/
TstCod = TsR_CreateTestResultInDB (AllowTeachers,NumQsts); TstCod = TsR_CreateTestResultInDB (AllowTeachers,NumQsts);
@ -620,7 +635,8 @@ void Tst_AssessTest (void)
/***** Write answers and solutions *****/ /***** Write answers and solutions *****/
HTM_TABLE_BeginWideMarginPadding (10); HTM_TABLE_BeginWideMarginPadding (10);
Tst_ShowTestResultAfterAssess (TstCod,NumQsts,&NumQstsNotBlank,&TotalScore); Tst_ShowTestResultAfterAssess (TstCod,NumQsts,&NumQstsNotBlank,
&UsrAnswers,&TotalScore);
HTM_TABLE_End (); HTM_TABLE_End ();
/***** Write total score and grade *****/ /***** Write total score and grade *****/
@ -660,7 +676,8 @@ void Tst_AssessTest (void)
/*********** Get questions and answers from form to assess a test ************/ /*********** Get questions and answers from form to assess a test ************/
/*****************************************************************************/ /*****************************************************************************/
static void Tst_GetQuestionsAndAnswersFromForm (unsigned NumQsts) static void Tst_GetQuestionsAndAnswersFromForm (unsigned NumQsts,
struct Tst_UsrAnswers *UsrAnswers)
{ {
unsigned NumQst; unsigned NumQst;
char StrQstIndOrAns[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Qstxx...x", "Indxx...x" or "Ansxx...x" char StrQstIndOrAns[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Qstxx...x", "Indxx...x" or "Ansxx...x"
@ -674,21 +691,21 @@ static void Tst_GetQuestionsAndAnswersFromForm (unsigned NumQsts)
snprintf (StrQstIndOrAns,sizeof (StrQstIndOrAns), snprintf (StrQstIndOrAns,sizeof (StrQstIndOrAns),
"Qst%03u", "Qst%03u",
NumQst); NumQst);
if ((Gbl.Test.QstCodes[NumQst] = Par_GetParToLong (StrQstIndOrAns)) <= 0) if ((UsrAnswers->QstCodes[NumQst] = Par_GetParToLong (StrQstIndOrAns)) <= 0)
Lay_ShowErrorAndExit ("Code of question is missing."); Lay_ShowErrorAndExit ("Code of question is missing.");
/* Get indexes for this question */ /* Get indexes for this question */
snprintf (StrQstIndOrAns,sizeof (StrQstIndOrAns), snprintf (StrQstIndOrAns,sizeof (StrQstIndOrAns),
"Ind%03u", "Ind%03u",
NumQst); NumQst);
Par_GetParMultiToText (StrQstIndOrAns,Gbl.Test.StrIndexesOneQst[NumQst], Par_GetParMultiToText (StrQstIndOrAns,UsrAnswers->StrIndexesOneQst[NumQst],
Tst_MAX_BYTES_INDEXES_ONE_QST); /* If choice ==> "0", "1", "2",... */ Tst_MAX_BYTES_INDEXES_ONE_QST); /* If choice ==> "0", "1", "2",... */
/* Get answers selected by user for this question */ /* Get answers selected by user for this question */
snprintf (StrQstIndOrAns,sizeof (StrQstIndOrAns), snprintf (StrQstIndOrAns,sizeof (StrQstIndOrAns),
"Ans%03u", "Ans%03u",
NumQst); NumQst);
Par_GetParMultiToText (StrQstIndOrAns,Gbl.Test.StrAnswersOneQst[NumQst], Par_GetParMultiToText (StrQstIndOrAns,UsrAnswers->StrAnswersOneQst[NumQst],
Tst_MAX_BYTES_ANSWERS_ONE_QST); /* If answer type == T/F ==> " ", "T", "F"; if choice ==> "0", "2",... */ Tst_MAX_BYTES_ANSWERS_ONE_QST); /* If answer type == T/F ==> " ", "T", "F"; if choice ==> "0", "2",... */
} }
} }
@ -905,7 +922,8 @@ static unsigned Tst_GetNumAccessesTst (void)
// NumRows must hold the number of rows of a MySQL query // NumRows must hold the number of rows of a MySQL query
// In each row mysql_res holds: in the column 0 the code of a question, in the column 1 the type of answer, and in the column 2 the stem // In each row mysql_res holds: in the column 0 the code of a question, in the column 1 the type of answer, and in the column 2 the stem
static void Tst_ShowTestQuestionsWhenSeeing (unsigned NumQsts,MYSQL_RES *mysql_res) static void Tst_ShowTestQuestionsWhenSeeing (unsigned NumQsts,
MYSQL_RES *mysql_res)
{ {
unsigned NumQst; unsigned NumQst;
long QstCod; long QstCod;
@ -936,32 +954,12 @@ static void Tst_ShowOneTestQuestionWhenSeeing (unsigned NumQst,long QstCod)
{ {
MYSQL_RES *mysql_res; MYSQL_RES *mysql_res;
MYSQL_ROW row; MYSQL_ROW row;
double ScoreThisQst; // Not used here
bool AnswerIsNotBlank; // Not used here
if (Tst_GetOneQuestionByCod (QstCod,&mysql_res)) // Question exists if (Tst_GetOneQuestionByCod (QstCod,&mysql_res)) // Question exists
{ {
/***** Get row of the result of the query *****/ /***** Get row of the result of the query *****/
row = mysql_fetch_row (mysql_res); row = mysql_fetch_row (mysql_res);
/* Tst_WriteQstAndAnsTestToAnswer (NumQst,QstCod,row);
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
*/
Tst_WriteQstAndAnsTest (Tst_SHOW_TEST_TO_ANSWER,
&Gbl.Usrs.Me.UsrDat,
NumQst,
QstCod,
row,
TsV_MAX_VISIBILITY, // All visible here
&ScoreThisQst, // Not used here
&AnswerIsNotBlank); // Not used here
} }
else else
Lay_ShowErrorAndExit ("Wrong question."); Lay_ShowErrorAndExit ("Wrong question.");
@ -1000,8 +998,10 @@ void Tst_ShowTagList (unsigned NumTags,MYSQL_RES *mysql_res)
/******************* Show the result of assessing a test *********************/ /******************* Show the result of assessing a test *********************/
/*****************************************************************************/ /*****************************************************************************/
static void Tst_ShowTestResultAfterAssess (long TstCod,unsigned NumQsts, static void Tst_ShowTestResultAfterAssess (long TstCod,
unsigned NumQsts,
unsigned *NumQstsNotBlank, unsigned *NumQstsNotBlank,
const struct Tst_UsrAnswers *UsrAnswers,
double *TotalScore) double *TotalScore)
{ {
extern const char *Txt_Question_removed; extern const char *Txt_Question_removed;
@ -1022,33 +1022,19 @@ static void Tst_ShowTestResultAfterAssess (long TstCod,unsigned NumQsts,
Gbl.RowEvenOdd = NumQst % 2; Gbl.RowEvenOdd = NumQst % 2;
/***** Query database *****/ /***** Query database *****/
if (Tst_GetOneQuestionByCod (Gbl.Test.QstCodes[NumQst],&mysql_res)) // Question exists if (Tst_GetOneQuestionByCod (UsrAnswers->QstCodes[NumQst],&mysql_res)) // Question exists
{ {
/***** Get row of the result of the query *****/
row = mysql_fetch_row (mysql_res);
/*
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
*/
/***** Write question and answers *****/ /***** Write question and answers *****/
Tst_WriteQstAndAnsTest (Tst_SHOW_TEST_RESULT, row = mysql_fetch_row (mysql_res);
&Gbl.Usrs.Me.UsrDat, Tst_WriteQstAndAnsTestResult (&Gbl.Usrs.Me.UsrDat,
NumQst, UsrAnswers,
Gbl.Test.QstCodes[NumQst], NumQst,
row, row,
TstCfg_GetConfigVisibility (), TstCfg_GetConfigVisibility (),
&ScoreThisQst,&AnswerIsNotBlank); &ScoreThisQst,&AnswerIsNotBlank);
/***** Store test result question in database *****/ /***** Store test result question in database *****/
TsR_StoreOneTestResultQstInDB (TstCod,Gbl.Test.QstCodes[NumQst], TsR_StoreOneTestResultQstInDB (TstCod,UsrAnswers,
NumQst, // 0, 1, 2, 3... NumQst, // 0, 1, 2, 3...
ScoreThisQst); ScoreThisQst);
@ -1059,15 +1045,15 @@ static void Tst_ShowTestResultAfterAssess (long TstCod,unsigned NumQsts,
/***** Update the number of accesses and the score of this question *****/ /***** Update the number of accesses and the score of this question *****/
if (Gbl.Usrs.Me.Role.Logged == Rol_STD) if (Gbl.Usrs.Me.Role.Logged == Rol_STD)
Tst_UpdateScoreQst (Gbl.Test.QstCodes[NumQst],ScoreThisQst,AnswerIsNotBlank); Tst_UpdateScoreQst (UsrAnswers->QstCodes[NumQst],ScoreThisQst,AnswerIsNotBlank);
} }
else else
{ {
/***** Question does not exists *****/ /***** Question does not exists *****/
HTM_TR_Begin (NULL); HTM_TR_Begin (NULL);
HTM_TD_Begin ("class=\"BIG_INDEX RT COLOR%u\"",Gbl.RowEvenOdd); HTM_TD_Begin ("class=\"RT COLOR%u\"",Gbl.RowEvenOdd);
HTM_Unsigned (NumQst + 1); Tst_WriteNumQst (NumQst + 1);
HTM_TD_End (); HTM_TD_End ();
HTM_TD_Begin ("class=\"DAT_LIGHT LT COLOR%u\"",Gbl.RowEvenOdd); HTM_TD_Begin ("class=\"DAT_LIGHT LT COLOR%u\"",Gbl.RowEvenOdd);
@ -1086,15 +1072,72 @@ static void Tst_ShowTestResultAfterAssess (long TstCod,unsigned NumQsts,
/********** Write a row of a test, with one question and its answer **********/ /********** Write a row of a test, with one question and its answer **********/
/*****************************************************************************/ /*****************************************************************************/
void Tst_WriteQstAndAnsTest (Tst_ActionToDoWithQuestions_t ActionToDoWithQuestions, static void Tst_WriteQstAndAnsTestToAnswer (unsigned NumQst,
struct UsrData *UsrDat, long QstCod,
unsigned NumQst, MYSQL_ROW row)
long QstCod, {
MYSQL_ROW row, struct Tst_Question Question;
unsigned Visibility, /*
double *ScoreThisQst,bool *AnswerIsNotBlank) 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);
/***** Begin row *****/
HTM_TR_Begin (NULL);
/***** 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);
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",true);
/* Media (row[5]) */
Question.Media.MedCod = Str_ConvertStrCodToLongCod (row[5]);
Med_GetMediaDataByCod (&Question.Media);
Med_ShowMedia (&Question.Media,
"TEST_MED_SHOW_CONT",
"TEST_MED_SHOW");
/* Answers depending on shuffle (row[2]) */
Tst_WriteAnswersTestToAnswer (NumQst,QstCod,Question.Answer.Type,(row[2][0] == 'Y'));
HTM_TD_End ();
/***** End row *****/
HTM_TR_End ();
/***** Destroy test question *****/
Tst_QstDestructor (&Question);
}
/*****************************************************************************/
/********** Write a row of a test, with one question and its answer **********/
/*****************************************************************************/
void Tst_WriteQstAndAnsTestResult (struct UsrData *UsrDat,
const struct Tst_UsrAnswers *UsrAnswers,
unsigned NumQst,
MYSQL_ROW row,
unsigned Visibility,
double *ScoreThisQst,bool *AnswerIsNotBlank)
{ {
extern const char *Txt_TST_STR_ANSWER_TYPES[Tst_NUM_ANS_TYPES];
struct Tst_Question Question; struct Tst_Question Question;
bool IsVisibleQstAndAnsTxt = TsV_IsVisibleQstAndAnsTxt (Visibility); bool IsVisibleQstAndAnsTxt = TsV_IsVisibleQstAndAnsTxt (Visibility);
/* /*
@ -1108,30 +1151,27 @@ void Tst_WriteQstAndAnsTest (Tst_ActionToDoWithQuestions_t ActionToDoWithQuestio
row[7] NumHitsNotBlank row[7] NumHitsNotBlank
row[8] Score row[8] Score
*/ */
/***** Create test question *****/ /***** Create test question *****/
Tst_QstConstructor (&Question); Tst_QstConstructor (&Question);
/***** Begin row *****/
HTM_TR_Begin (NULL); HTM_TR_Begin (NULL);
/***** Number of question and answer type (row[1]) *****/
HTM_TD_Begin ("class=\"RT COLOR%u\"",Gbl.RowEvenOdd); HTM_TD_Begin ("class=\"RT COLOR%u\"",Gbl.RowEvenOdd);
Tst_WriteNumQst (NumQst + 1);
/***** Write number of question *****/
HTM_DIV_Begin ("class=\"BIG_INDEX\"");
HTM_Unsigned (NumQst + 1);
HTM_DIV_End ();
/***** Write answer type (row[1]) *****/
Question.Answer.Type = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[1]); Question.Answer.Type = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[1]);
HTM_DIV_Begin ("class=\"DAT_SMALL\""); Tst_WriteAnswerType (Question.Answer.Type);
HTM_Txt (Txt_TST_STR_ANSWER_TYPES[Question.Answer.Type]);
HTM_DIV_End ();
HTM_TD_End (); HTM_TD_End ();
/***** Write stem (row[3]) *****/ /***** Stem, media and answers *****/
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd); HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
/* Stem (row[3]) */
Tst_WriteQstStem (row[3],"TEST_EXA",IsVisibleQstAndAnsTxt); Tst_WriteQstStem (row[3],"TEST_EXA",IsVisibleQstAndAnsTxt);
/***** Get and show media (row[5]) *****/ /* Media (row[5]) */
if (IsVisibleQstAndAnsTxt) if (IsVisibleQstAndAnsTxt)
{ {
Question.Media.MedCod = Str_ConvertStrCodToLongCod (row[5]); Question.Media.MedCod = Str_ConvertStrCodToLongCod (row[5]);
@ -1141,31 +1181,50 @@ void Tst_WriteQstAndAnsTest (Tst_ActionToDoWithQuestions_t ActionToDoWithQuestio
"TEST_MED_SHOW"); "TEST_MED_SHOW");
} }
/***** Write answers depending on shuffle (row[2]) and feedback (row[4]) *****/ /* Answers */
switch (ActionToDoWithQuestions) Tst_WriteAnswersTestResult (UsrDat,UsrAnswers,
{ NumQst,UsrAnswers->QstCodes[NumQst],Question.Answer.Type,
case Tst_SHOW_TEST_TO_ANSWER: Visibility,
Tst_WriteAnswersTestToAnswer (NumQst,QstCod,Question.Answer.Type,(row[2][0] == 'Y')); ScoreThisQst,AnswerIsNotBlank);
break;
case Tst_SHOW_TEST_RESULT: /* Question feedback (row[4]) */
Tst_WriteAnswersTestResult (UsrDat,NumQst,QstCod,Question.Answer.Type, if (TsV_IsVisibleFeedbackTxt (Visibility))
Visibility, Tst_WriteQstFeedback (row[4],"TEST_EXA_LIGHT");
ScoreThisQst,AnswerIsNotBlank);
/* Write question feedback (row[4]) */
if (TsV_IsVisibleFeedbackTxt (Visibility))
Tst_WriteQstFeedback (row[4],"TEST_EXA_LIGHT");
break;
default:
break;
}
HTM_TD_End (); HTM_TD_End ();
/***** End row *****/
HTM_TR_End (); HTM_TR_End ();
/***** Destroy test question *****/ /***** Destroy test question *****/
Tst_QstDestructor (&Question); Tst_QstDestructor (&Question);
} }
/*****************************************************************************/
/********************* Write the number of a test question *******************/
/*****************************************************************************/
// Number of question should be 1, 2, 3...
void Tst_WriteNumQst (unsigned NumQst)
{
HTM_DIV_Begin ("class=\"BIG_INDEX\"");
HTM_Unsigned (NumQst);
HTM_DIV_End ();
}
/*****************************************************************************/
/************************** Write the type of answer *************************/
/*****************************************************************************/
void Tst_WriteAnswerType (Tst_AnswerType_t AnswerType)
{
extern const char *Txt_TST_STR_ANSWER_TYPES[Tst_NUM_ANS_TYPES];
HTM_DIV_Begin ("class=\"DAT_SMALL\"");
HTM_Txt (Txt_TST_STR_ANSWER_TYPES[AnswerType]);
HTM_DIV_End ();
}
/*****************************************************************************/ /*****************************************************************************/
/********************* Write the stem of a test question *********************/ /********************* Write the stem of a test question *********************/
/*****************************************************************************/ /*****************************************************************************/
@ -2852,7 +2911,6 @@ static void Tst_WriteQuestionRowForEdition (const struct Tst_Tags *Tags,
unsigned long NumRow, unsigned long NumRow,
long QstCod) long QstCod)
{ {
extern const char *Txt_TST_STR_ANSWER_TYPES[Tst_NUM_ANS_TYPES];
MYSQL_RES *mysql_res; MYSQL_RES *mysql_res;
MYSQL_ROW row; MYSQL_ROW row;
struct Tst_Question Question; struct Tst_Question Question;
@ -2907,27 +2965,19 @@ static void Tst_WriteQuestionRowForEdition (const struct Tst_Tags *Tags,
HTM_TD_End (); HTM_TD_End ();
/* Number of question and answer type (row[1]) */
HTM_TD_Begin ("class=\"RT COLOR%u\"",Gbl.RowEvenOdd); HTM_TD_Begin ("class=\"RT COLOR%u\"",Gbl.RowEvenOdd);
Tst_WriteNumQst ((unsigned) NumRow + 1);
/* Write number of question */
HTM_DIV_Begin ("class=\"BIG_INDEX\"");
HTM_UnsignedLong (NumRow + 1);
HTM_DIV_End ();
/* Write answer type (row[1]) */
Question.Answer.Type = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[1]); Question.Answer.Type = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[1]);
HTM_DIV_Begin ("class=\"DAT_SMALL\""); Tst_WriteAnswerType (Question.Answer.Type);
HTM_Txt (Txt_TST_STR_ANSWER_TYPES[Question.Answer.Type]);
HTM_DIV_End ();
HTM_TD_End (); HTM_TD_End ();
/* Write question code */ /* Question code */
HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd); HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd);
HTM_TxtF ("%ld&nbsp;",QstCod); HTM_TxtF ("%ld&nbsp;",QstCod);
HTM_TD_End (); HTM_TD_End ();
/* Write the date (row[0] has the UTC date-time) */ /* Date (row[0] has the UTC date-time) */
TimeUTC = Dat_GetUNIXTimeFromStr (row[0]); TimeUTC = Dat_GetUNIXTimeFromStr (row[0]);
if (asprintf (&Id,"tst_date_%u",++UniqueId) < 0) if (asprintf (&Id,"tst_date_%u",++UniqueId) < 0)
Lay_NotEnoughMemoryExit (); Lay_NotEnoughMemoryExit ();
@ -2939,12 +2989,12 @@ static void Tst_WriteQuestionRowForEdition (const struct Tst_Tags *Tags,
HTM_TD_End (); HTM_TD_End ();
free (Id); free (Id);
/* Write the question tags */ /* Question tags */
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd); HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
Tst_GetAndWriteTagsQst (QstCod); Tst_GetAndWriteTagsQst (QstCod);
HTM_TD_End (); HTM_TD_End ();
/* Write if shuffle is enabled (row[2]) */ /* Shuffle (row[2]) */
HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd); HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd);
if (Question.Answer.Type == Tst_ANS_UNIQUE_CHOICE || if (Question.Answer.Type == Tst_ANS_UNIQUE_CHOICE ||
Question.Answer.Type == Tst_ANS_MULTIPLE_CHOICE) Question.Answer.Type == Tst_ANS_MULTIPLE_CHOICE)
@ -2966,7 +3016,7 @@ static void Tst_WriteQuestionRowForEdition (const struct Tst_Tags *Tags,
} }
HTM_TD_End (); HTM_TD_End ();
/* Write stem (row[3]) */ /* Stem (row[3]) */
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd); HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
Tst_WriteQstStem (row[3],"TEST_EDI", Tst_WriteQstStem (row[3],"TEST_EDI",
true); // Visible true); // Visible
@ -2978,7 +3028,7 @@ static void Tst_WriteQuestionRowForEdition (const struct Tst_Tags *Tags,
"TEST_MED_EDIT_LIST_CONT", "TEST_MED_EDIT_LIST_CONT",
"TEST_MED_EDIT_LIST"); "TEST_MED_EDIT_LIST");
/* Write feedback (row[4]) and answers */ /* Feedback (row[4]) and answers */
Tst_WriteQstFeedback (row[4],"TEST_EDI_LIGHT"); Tst_WriteQstFeedback (row[4],"TEST_EDI_LIGHT");
Tst_WriteAnswersEdit (QstCod,Question.Answer.Type); Tst_WriteAnswersEdit (QstCod,Question.Answer.Type);
HTM_TD_End (); HTM_TD_End ();
@ -3001,12 +3051,12 @@ static void Tst_WriteQuestionRowForEdition (const struct Tst_Tags *Tags,
Lay_ShowErrorAndExit ("Wrong score of a question."); Lay_ShowErrorAndExit ("Wrong score of a question.");
Str_SetDecimalPointToLocal (); // Return to local system Str_SetDecimalPointToLocal (); // Return to local system
/* Write number of times this question has been answered */ /* Number of times this question has been answered */
HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd); HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd);
HTM_UnsignedLong (NumHitsThisQst); HTM_UnsignedLong (NumHitsThisQst);
HTM_TD_End (); HTM_TD_End ();
/* Write average score */ /* Average score */
HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd); HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd);
if (NumHitsThisQst) if (NumHitsThisQst)
HTM_Double2Decimals (TotalScoreThisQst / HTM_Double2Decimals (TotalScoreThisQst /
@ -3015,12 +3065,12 @@ static void Tst_WriteQuestionRowForEdition (const struct Tst_Tags *Tags,
HTM_Txt ("N.A."); HTM_Txt ("N.A.");
HTM_TD_End (); HTM_TD_End ();
/* Write number of times this question has been answered (not blank) */ /* Number of times this question has been answered (not blank) */
HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd); HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd);
HTM_UnsignedLong (NumHitsNotBlankThisQst); HTM_UnsignedLong (NumHitsNotBlankThisQst);
HTM_TD_End (); HTM_TD_End ();
/* Write average score (not blank) */ /* Average score (not blank) */
HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd); HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd);
if (NumHitsNotBlankThisQst) if (NumHitsNotBlankThisQst)
HTM_Double2Decimals (TotalScoreThisQst / HTM_Double2Decimals (TotalScoreThisQst /
@ -3054,7 +3104,6 @@ static void Tst_ListOneOrMoreQuestionsForSelection (unsigned long NumRows,
extern const char *Txt_Date; extern const char *Txt_Date;
extern const char *Txt_Tags; extern const char *Txt_Tags;
extern const char *Txt_Type; extern const char *Txt_Type;
extern const char *Txt_TST_STR_ANSWER_TYPES[Tst_NUM_ANS_TYPES];
extern const char *Txt_Shuffle; extern const char *Txt_Shuffle;
extern const char *Txt_Question; extern const char *Txt_Question;
extern const char *Txt_Add_questions; extern const char *Txt_Add_questions;
@ -3578,6 +3627,7 @@ static void Tst_WriteAnswersTestToAnswer (unsigned NumQst,long QstCod,
/*****************************************************************************/ /*****************************************************************************/
static void Tst_WriteAnswersTestResult (struct UsrData *UsrDat, static void Tst_WriteAnswersTestResult (struct UsrData *UsrDat,
const struct Tst_UsrAnswers *UsrAnswers,
unsigned NumQst,long QstCod, unsigned NumQst,long QstCod,
Tst_AnswerType_t AnswerType, Tst_AnswerType_t AnswerType,
unsigned Visibility, unsigned Visibility,
@ -3604,28 +3654,33 @@ static void Tst_WriteAnswersTestResult (struct UsrData *UsrDat,
switch (Question.Answer.Type) switch (Question.Answer.Type)
{ {
case Tst_ANS_INT: case Tst_ANS_INT:
Tst_WriteIntAnsAssessTest (UsrDat,NumQst,&Question,mysql_res, Tst_WriteIntAnsAssessTest (UsrDat,UsrAnswers,
NumQst,&Question,mysql_res,
Visibility, Visibility,
ScoreThisQst,AnswerIsNotBlank); ScoreThisQst,AnswerIsNotBlank);
break; break;
case Tst_ANS_FLOAT: case Tst_ANS_FLOAT:
Tst_WriteFloatAnsAssessTest (UsrDat,NumQst,&Question,mysql_res, Tst_WriteFloatAnsAssessTest (UsrDat,UsrAnswers,
NumQst,&Question,mysql_res,
Visibility, Visibility,
ScoreThisQst,AnswerIsNotBlank); ScoreThisQst,AnswerIsNotBlank);
break; break;
case Tst_ANS_TRUE_FALSE: case Tst_ANS_TRUE_FALSE:
Tst_WriteTFAnsAssessTest (UsrDat,NumQst,&Question,mysql_res, Tst_WriteTFAnsAssessTest (UsrDat,UsrAnswers,
NumQst,&Question,mysql_res,
Visibility, Visibility,
ScoreThisQst,AnswerIsNotBlank); ScoreThisQst,AnswerIsNotBlank);
break; break;
case Tst_ANS_UNIQUE_CHOICE: case Tst_ANS_UNIQUE_CHOICE:
case Tst_ANS_MULTIPLE_CHOICE: case Tst_ANS_MULTIPLE_CHOICE:
Tst_WriteChoiceAnsAssessTest (UsrDat,NumQst,&Question,mysql_res, Tst_WriteChoiceAnsAssessTest (UsrDat,UsrAnswers,
NumQst,&Question,mysql_res,
Visibility, Visibility,
ScoreThisQst,AnswerIsNotBlank); ScoreThisQst,AnswerIsNotBlank);
break; break;
case Tst_ANS_TEXT: case Tst_ANS_TEXT:
Tst_WriteTextAnsAssessTest (UsrDat,NumQst,&Question,mysql_res, Tst_WriteTextAnsAssessTest (UsrDat,UsrAnswers,
NumQst,&Question,mysql_res,
Visibility, Visibility,
ScoreThisQst,AnswerIsNotBlank); ScoreThisQst,AnswerIsNotBlank);
break; break;
@ -3698,6 +3753,7 @@ void Tst_WriteAnsTF (char AnsTF)
/*****************************************************************************/ /*****************************************************************************/
static void Tst_WriteTFAnsAssessTest (struct UsrData *UsrDat, static void Tst_WriteTFAnsAssessTest (struct UsrData *UsrDat,
const struct Tst_UsrAnswers *UsrAnswers,
unsigned NumQst, unsigned NumQst,
const struct Tst_Question *Question, const struct Tst_Question *Question,
MYSQL_RES *mysql_res, MYSQL_RES *mysql_res,
@ -3721,7 +3777,7 @@ static void Tst_WriteTFAnsAssessTest (struct UsrData *UsrDat,
row = mysql_fetch_row (mysql_res); row = mysql_fetch_row (mysql_res);
/***** Compute the mark for this question *****/ /***** Compute the mark for this question *****/
AnsTF = Gbl.Test.StrAnswersOneQst[NumQst][0]; AnsTF = UsrAnswers->StrAnswersOneQst[NumQst][0];
if (AnsTF == '\0') // User has omitted the answer (the answer is blank) if (AnsTF == '\0') // User has omitted the answer (the answer is blank)
{ {
*AnswerIsNotBlank = false; *AnswerIsNotBlank = false;
@ -3919,6 +3975,7 @@ static void Tst_WriteChoiceAnsViewTest (unsigned NumQst,long QstCod,
/*****************************************************************************/ /*****************************************************************************/
static void Tst_WriteChoiceAnsAssessTest (struct UsrData *UsrDat, static void Tst_WriteChoiceAnsAssessTest (struct UsrData *UsrDat,
const struct Tst_UsrAnswers *UsrAnswers,
unsigned NumQst, unsigned NumQst,
struct Tst_Question *Question, struct Tst_Question *Question,
MYSQL_RES *mysql_res, MYSQL_RES *mysql_res,
@ -3942,10 +3999,10 @@ static void Tst_WriteChoiceAnsAssessTest (struct UsrData *UsrDat,
Tst_GetChoiceAns (mysql_res,Question); Tst_GetChoiceAns (mysql_res,Question);
/***** Get indexes for this question from string *****/ /***** Get indexes for this question from string *****/
Tst_GetIndexesFromStr (Gbl.Test.StrIndexesOneQst[NumQst],Indexes); Tst_GetIndexesFromStr (UsrAnswers->StrIndexesOneQst[NumQst],Indexes);
/***** Get the user's answers for this question from string *****/ /***** Get the user's answers for this question from string *****/
Tst_GetAnswersFromStr (Gbl.Test.StrAnswersOneQst[NumQst],AnswersUsr); Tst_GetAnswersFromStr (UsrAnswers->StrAnswersOneQst[NumQst],AnswersUsr);
/***** Compute the total score of this question *****/ /***** Compute the total score of this question *****/
Tst_ComputeScoreQst (Question,Indexes,AnswersUsr,ScoreThisQst,AnswerIsNotBlank); Tst_ComputeScoreQst (Question,Indexes,AnswersUsr,ScoreThisQst,AnswerIsNotBlank);
@ -4417,6 +4474,7 @@ static void Tst_WriteTextAnsViewTest (unsigned NumQst)
/*****************************************************************************/ /*****************************************************************************/
static void Tst_WriteTextAnsAssessTest (struct UsrData *UsrDat, static void Tst_WriteTextAnsAssessTest (struct UsrData *UsrDat,
const struct Tst_UsrAnswers *UsrAnswers,
unsigned NumQst, unsigned NumQst,
struct Tst_Question *Question, struct Tst_Question *Question,
MYSQL_RES *mysql_res, MYSQL_RES *mysql_res,
@ -4481,10 +4539,10 @@ static void Tst_WriteTextAnsAssessTest (struct UsrData *UsrDat,
HTM_TR_Begin (NULL); HTM_TR_Begin (NULL);
/***** Write the user answer *****/ /***** Write the user answer *****/
if (Gbl.Test.StrAnswersOneQst[NumQst][0]) // If user has answered the question if (UsrAnswers->StrAnswersOneQst[NumQst][0]) // If user has answered the question
{ {
/* Filter the user answer */ /* Filter the user answer */
Str_Copy (TextAnsUsr,Gbl.Test.StrAnswersOneQst[NumQst], Str_Copy (TextAnsUsr,UsrAnswers->StrAnswersOneQst[NumQst],
Tst_MAX_BYTES_ANSWERS_ONE_QST); Tst_MAX_BYTES_ANSWERS_ONE_QST);
/* In order to compare student answer to stored answer, /* In order to compare student answer to stored answer,
@ -4514,7 +4572,7 @@ static void Tst_WriteTextAnsAssessTest (struct UsrData *UsrDat,
(Correct ? "ANS_OK" : (Correct ? "ANS_OK" :
"ANS_BAD") : "ANS_BAD") :
"ANS_0"); "ANS_0");
HTM_Txt (Gbl.Test.StrAnswersOneQst[NumQst]); HTM_Txt (UsrAnswers->StrAnswersOneQst[NumQst]);
} }
else // If user has omitted the answer else // If user has omitted the answer
HTM_TD_Begin (NULL); HTM_TD_Begin (NULL);
@ -4571,7 +4629,7 @@ static void Tst_WriteTextAnsAssessTest (struct UsrData *UsrDat,
HTM_TR_End (); HTM_TR_End ();
/***** Compute the mark *****/ /***** Compute the mark *****/
if (!Gbl.Test.StrAnswersOneQst[NumQst][0]) // If user has omitted the answer if (!UsrAnswers->StrAnswersOneQst[NumQst][0]) // If user has omitted the answer
{ {
*AnswerIsNotBlank = false; *AnswerIsNotBlank = false;
*ScoreThisQst = 0.0; *ScoreThisQst = 0.0;
@ -4589,7 +4647,7 @@ static void Tst_WriteTextAnsAssessTest (struct UsrData *UsrDat,
if (TsV_IsVisibleEachQstScore (Visibility)) if (TsV_IsVisibleEachQstScore (Visibility))
{ {
Tst_WriteScoreStart (4); Tst_WriteScoreStart (4);
if (!Gbl.Test.StrAnswersOneQst[NumQst][0]) // If user has omitted the answer if (!UsrAnswers->StrAnswersOneQst[NumQst][0]) // If user has omitted the answer
{ {
HTM_SPAN_Begin ("class=\"ANS_0\""); HTM_SPAN_Begin ("class=\"ANS_0\"");
HTM_Double2Decimals (0.0); HTM_Double2Decimals (0.0);
@ -4632,6 +4690,7 @@ static void Tst_WriteIntAnsViewTest (unsigned NumQst)
/*****************************************************************************/ /*****************************************************************************/
static void Tst_WriteIntAnsAssessTest (struct UsrData *UsrDat, static void Tst_WriteIntAnsAssessTest (struct UsrData *UsrDat,
const struct Tst_UsrAnswers *UsrAnswers,
unsigned NumQst, unsigned NumQst,
const struct Tst_Question *Question, const struct Tst_Question *Question,
MYSQL_RES *mysql_res, MYSQL_RES *mysql_res,
@ -4666,9 +4725,9 @@ static void Tst_WriteIntAnsAssessTest (struct UsrData *UsrDat,
HTM_TR_Begin (NULL); HTM_TR_Begin (NULL);
/***** Write the user answer *****/ /***** Write the user answer *****/
if (Gbl.Test.StrAnswersOneQst[NumQst][0]) // If user has answered the question if (UsrAnswers->StrAnswersOneQst[NumQst][0]) // If user has answered the question
{ {
if (sscanf (Gbl.Test.StrAnswersOneQst[NumQst],"%ld",&IntAnswerUsr) == 1) if (sscanf (UsrAnswers->StrAnswersOneQst[NumQst],"%ld",&IntAnswerUsr) == 1)
{ {
HTM_TD_Begin ("class=\"%s CM\"", HTM_TD_Begin ("class=\"%s CM\"",
TsV_IsVisibleCorrectAns (Visibility) ? TsV_IsVisibleCorrectAns (Visibility) ?
@ -4680,7 +4739,6 @@ static void Tst_WriteIntAnsAssessTest (struct UsrData *UsrDat,
} }
else else
{ {
Gbl.Test.StrAnswersOneQst[NumQst][0] = '\0';
HTM_TD_Begin ("class=\"ANS_0 CM\""); HTM_TD_Begin ("class=\"ANS_0 CM\"");
HTM_Txt ("?"); HTM_Txt ("?");
HTM_TD_End (); HTM_TD_End ();
@ -4701,7 +4759,7 @@ static void Tst_WriteIntAnsAssessTest (struct UsrData *UsrDat,
HTM_TR_End (); HTM_TR_End ();
/***** Compute the score *****/ /***** Compute the score *****/
if (!Gbl.Test.StrAnswersOneQst[NumQst][0]) // If user has omitted the answer if (!UsrAnswers->StrAnswersOneQst[NumQst][0]) // If user has omitted the answer
{ {
*AnswerIsNotBlank = false; *AnswerIsNotBlank = false;
*ScoreThisQst = 0.0; *ScoreThisQst = 0.0;
@ -4719,7 +4777,7 @@ static void Tst_WriteIntAnsAssessTest (struct UsrData *UsrDat,
if (TsV_IsVisibleEachQstScore (Visibility)) if (TsV_IsVisibleEachQstScore (Visibility))
{ {
Tst_WriteScoreStart (2); Tst_WriteScoreStart (2);
if (!Gbl.Test.StrAnswersOneQst[NumQst][0]) // If user has omitted the answer if (!UsrAnswers->StrAnswersOneQst[NumQst][0]) // If user has omitted the answer
{ {
HTM_SPAN_Begin ("class=\"ANS_0\""); HTM_SPAN_Begin ("class=\"ANS_0\"");
HTM_Double2Decimals (0.0); HTM_Double2Decimals (0.0);
@ -4762,6 +4820,7 @@ static void Tst_WriteFloatAnsViewTest (unsigned NumQst)
/*****************************************************************************/ /*****************************************************************************/
static void Tst_WriteFloatAnsAssessTest (struct UsrData *UsrDat, static void Tst_WriteFloatAnsAssessTest (struct UsrData *UsrDat,
const struct Tst_UsrAnswers *UsrAnswers,
unsigned NumQst, unsigned NumQst,
const struct Tst_Question *Question, const struct Tst_Question *Question,
MYSQL_RES *mysql_res, MYSQL_RES *mysql_res,
@ -4809,10 +4868,10 @@ static void Tst_WriteFloatAnsAssessTest (struct UsrData *UsrDat,
HTM_TR_Begin (NULL); HTM_TR_Begin (NULL);
/***** Write the user answer *****/ /***** Write the user answer *****/
if (Gbl.Test.StrAnswersOneQst[NumQst][0]) // If user has answered the question if (UsrAnswers->StrAnswersOneQst[NumQst][0]) // If user has answered the question
{ {
FloatAnsUsr = Str_GetDoubleFromStr (Gbl.Test.StrAnswersOneQst[NumQst]); FloatAnsUsr = Str_GetDoubleFromStr (UsrAnswers->StrAnswersOneQst[NumQst]);
if (Gbl.Test.StrAnswersOneQst[NumQst][0]) // It's a correct floating point number if (UsrAnswers->StrAnswersOneQst[NumQst][0]) // It's a correct floating point number
{ {
HTM_TD_Begin ("class=\"%s CM\"", HTM_TD_Begin ("class=\"%s CM\"",
TsV_IsVisibleCorrectAns (Visibility) ? TsV_IsVisibleCorrectAns (Visibility) ?
@ -4850,7 +4909,7 @@ static void Tst_WriteFloatAnsAssessTest (struct UsrData *UsrDat,
HTM_TR_End (); HTM_TR_End ();
/***** Compute mark *****/ /***** Compute mark *****/
if (!Gbl.Test.StrAnswersOneQst[NumQst][0]) // If user has omitted the answer if (!UsrAnswers->StrAnswersOneQst[NumQst][0]) // If user has omitted the answer
{ {
*AnswerIsNotBlank = false; *AnswerIsNotBlank = false;
*ScoreThisQst = 0.0; *ScoreThisQst = 0.0;
@ -4869,7 +4928,7 @@ static void Tst_WriteFloatAnsAssessTest (struct UsrData *UsrDat,
if (TsV_IsVisibleEachQstScore (Visibility)) if (TsV_IsVisibleEachQstScore (Visibility))
{ {
Tst_WriteScoreStart (2); Tst_WriteScoreStart (2);
if (!Gbl.Test.StrAnswersOneQst[NumQst][0]) // If user has omitted the answer if (!UsrAnswers->StrAnswersOneQst[NumQst][0]) // If user has omitted the answer
{ {
HTM_SPAN_Begin ("class=\"ANS_0\""); HTM_SPAN_Begin ("class=\"ANS_0\"");
HTM_Double2Decimals (0.0); HTM_Double2Decimals (0.0);

View File

@ -41,10 +41,6 @@
#define Tst_MAX_CHARS_TAG (128 - 1) // 127 #define Tst_MAX_CHARS_TAG (128 - 1) // 127
#define Tst_MAX_BYTES_TAG ((Tst_MAX_CHARS_TAG + 1) * Str_MAX_BYTES_PER_CHAR - 1) // 2047 #define Tst_MAX_BYTES_TAG ((Tst_MAX_CHARS_TAG + 1) * Str_MAX_BYTES_PER_CHAR - 1) // 2047
#define Tst_MAX_OPTIONS_PER_QUESTION 10
#define Tst_MAX_BYTES_INDEXES_ONE_QST (Tst_MAX_OPTIONS_PER_QUESTION * (3 + 1))
#define Tst_MAX_BYTES_ANSWERS_ONE_QST (Tst_MAX_OPTIONS_PER_QUESTION * (3 + 1))
#define Tst_MAX_CHARS_ANSWER_OR_FEEDBACK (1024 - 1) // 1023 #define Tst_MAX_CHARS_ANSWER_OR_FEEDBACK (1024 - 1) // 1023
#define Tst_MAX_BYTES_ANSWER_OR_FEEDBACK ((Tst_MAX_CHARS_ANSWER_OR_FEEDBACK + 1) * Str_MAX_BYTES_PER_CHAR - 1) // 16383 #define Tst_MAX_BYTES_ANSWER_OR_FEEDBACK ((Tst_MAX_CHARS_ANSWER_OR_FEEDBACK + 1) * Str_MAX_BYTES_PER_CHAR - 1) // 16383
@ -97,11 +93,7 @@ struct Tst_Test
struct Tst_Tags Tags; struct Tst_Tags Tags;
struct Tst_AnswerTypes AnswerTypes; struct Tst_AnswerTypes AnswerTypes;
Tst_QuestionsOrder_t SelectedOrder; Tst_QuestionsOrder_t SelectedOrder;
long QstCodes[TstCfg_MAX_QUESTIONS_PER_TEST]; // Codes of the sent/received questions in a test // struct Tst_UsrAnswers UsrAnswers;
char StrIndexesOneQst[TstCfg_MAX_QUESTIONS_PER_TEST]
[Tst_MAX_BYTES_INDEXES_ONE_QST + 1]; // 0 1 2 3, 3 0 2 1, etc.
char StrAnswersOneQst[TstCfg_MAX_QUESTIONS_PER_TEST]
[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1]; // Answers selected by user
}; };
struct Tst_Question struct Tst_Question
@ -165,13 +157,14 @@ void Tst_ShowGrade (double Grade,double MaxGrade);
void Tst_ShowTagList (unsigned NumTags,MYSQL_RES *mysql_res); void Tst_ShowTagList (unsigned NumTags,MYSQL_RES *mysql_res);
void Tst_WriteQstAndAnsTest (Tst_ActionToDoWithQuestions_t ActionToDoWithQuestions, void Tst_WriteQstAndAnsTestResult (struct UsrData *UsrDat,
struct UsrData *UsrDat, const struct Tst_UsrAnswers *UsrAnswers,
unsigned NumQst, unsigned NumQst,
long QstCod, MYSQL_ROW row,
MYSQL_ROW row, unsigned Visibility,
unsigned Visibility, double *ScoreThisQst,bool *AnswerIsNotBlank);
double *ScoreThisQst,bool *AnswerIsNotBlank); void Tst_WriteNumQst (unsigned NumQst);
void Tst_WriteAnswerType (Tst_AnswerType_t AnswerType);
void Tst_WriteQstStem (const char *Stem,const char *ClassStem,bool Visible); void Tst_WriteQstStem (const char *Stem,const char *ClassStem,bool Visible);
void Tst_WriteQstFeedback (const char *Feedback,const char *ClassFeedback); void Tst_WriteQstFeedback (const char *Feedback,const char *ClassFeedback);

View File

@ -92,7 +92,7 @@ static void TsR_GetTestResultDataByTstCod (long TstCod,
unsigned *NumQsts, unsigned *NumQsts,
unsigned *NumQstsNotBlank, unsigned *NumQstsNotBlank,
double *Score); double *Score);
static unsigned TsR_GetTestResultQuestionsFromDB (long TstCod); static unsigned TsR_GetTestResultQuestionsFromDB (long TstCod,struct Tst_UsrAnswers *UsrAnswers);
/*****************************************************************************/ /*****************************************************************************/
/************ Select users and dates to show their test results **************/ /************ Select users and dates to show their test results **************/
@ -606,6 +606,7 @@ void TsR_ShowOneTstResult (void)
time_t TstTimeUTC = 0; // Test result UTC date-time, initialized to avoid warning time_t TstTimeUTC = 0; // Test result UTC date-time, initialized to avoid warning
unsigned NumQsts; unsigned NumQsts;
unsigned NumQstsNotBlank; unsigned NumQstsNotBlank;
struct Tst_UsrAnswers UsrAnswers;
double TotalScore; double TotalScore;
bool ShowPhoto; bool ShowPhoto;
char PhotoURL[PATH_MAX + 1]; char PhotoURL[PATH_MAX + 1];
@ -674,7 +675,7 @@ void TsR_ShowOneTstResult (void)
if (ICanViewTest) // I am allowed to view this test result if (ICanViewTest) // I am allowed to view this test result
{ {
/***** Get questions and user's answers of the test result from database *****/ /***** Get questions and user's answers of the test result from database *****/
NumQsts = TsR_GetTestResultQuestionsFromDB (TstCod); NumQsts = TsR_GetTestResultQuestionsFromDB (TstCod,&UsrAnswers);
/***** Begin box *****/ /***** Begin box *****/
Box_BoxBegin (NULL,Txt_Test_result,NULL, Box_BoxBegin (NULL,Txt_Test_result,NULL,
@ -791,7 +792,9 @@ void TsR_ShowOneTstResult (void)
/***** Write answers and solutions *****/ /***** Write answers and solutions *****/
TsR_ShowTestResult (&Gbl.Usrs.Other.UsrDat, TsR_ShowTestResult (&Gbl.Usrs.Other.UsrDat,
NumQsts,TstTimeUTC, NumQsts,
&UsrAnswers,
TstTimeUTC,
TstCfg_GetConfigVisibility ()); TstCfg_GetConfigVisibility ());
/***** End table *****/ /***** End table *****/
@ -850,7 +853,9 @@ static void TsR_ShowTstTagsPresentInATestResult (long TstCod)
/*****************************************************************************/ /*****************************************************************************/
void TsR_ShowTestResult (struct UsrData *UsrDat, void TsR_ShowTestResult (struct UsrData *UsrDat,
unsigned NumQsts,time_t TstTimeUTC, unsigned NumQsts,
const struct Tst_UsrAnswers *UsrAnswers,
time_t TstTimeUTC,
unsigned Visibility) unsigned Visibility)
{ {
extern const char *Txt_Question_modified; extern const char *Txt_Question_modified;
@ -870,21 +875,10 @@ void TsR_ShowTestResult (struct UsrData *UsrDat,
Gbl.RowEvenOdd = NumQst % 2; Gbl.RowEvenOdd = NumQst % 2;
/***** Query database *****/ /***** Query database *****/
if (Tst_GetOneQuestionByCod (Gbl.Test.QstCodes[NumQst],&mysql_res)) // Question exists if (Tst_GetOneQuestionByCod (UsrAnswers->QstCodes[NumQst],&mysql_res)) // Question exists
{ {
/***** Get row of the result of the query *****/ /***** Get row of the result of the query *****/
row = mysql_fetch_row (mysql_res); row = mysql_fetch_row (mysql_res);
/*
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
*/
/***** If this question has been edited later than test time /***** If this question has been edited later than test time
==> don't show question ****/ ==> don't show question ****/
@ -910,13 +904,13 @@ void TsR_ShowTestResult (struct UsrData *UsrDat,
} }
else else
/***** Write questions and answers *****/ /***** Write questions and answers *****/
Tst_WriteQstAndAnsTest (Tst_SHOW_TEST_RESULT, Tst_WriteQstAndAnsTestResult (UsrDat,
UsrDat, UsrAnswers,
NumQst,Gbl.Test.QstCodes[NumQst], NumQst,
row, row,
Visibility, Visibility,
&ScoreThisQst, // Not used here &ScoreThisQst, // Not used here
&AnswerIsNotBlank); // Not used here &AnswerIsNotBlank); // Not used here
} }
else else
{ {
@ -1001,15 +995,17 @@ static void TsR_GetTestResultDataByTstCod (long TstCod,
/************ Store user's answers of an test result into database ***********/ /************ Store user's answers of an test result into database ***********/
/*****************************************************************************/ /*****************************************************************************/
void TsR_StoreOneTestResultQstInDB (long TstCod,long QstCod,unsigned NumQst,double Score) void TsR_StoreOneTestResultQstInDB (long TstCod,
const struct Tst_UsrAnswers *UsrAnswers,
unsigned NumQst,double Score)
{ {
char Indexes[Tst_MAX_BYTES_INDEXES_ONE_QST + 1]; char Indexes[Tst_MAX_BYTES_INDEXES_ONE_QST + 1];
char Answers[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1]; char Answers[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
/***** Replace each separator of multiple parameters by a comma *****/ /***** Replace each separator of multiple parameters by a comma *****/
/* In database commas are used as separators instead of special chars */ /* In database commas are used as separators instead of special chars */
Par_ReplaceSeparatorMultipleByComma (Gbl.Test.StrIndexesOneQst[NumQst],Indexes); Par_ReplaceSeparatorMultipleByComma (UsrAnswers->StrIndexesOneQst[NumQst],Indexes);
Par_ReplaceSeparatorMultipleByComma (Gbl.Test.StrAnswersOneQst[NumQst],Answers); Par_ReplaceSeparatorMultipleByComma (UsrAnswers->StrAnswersOneQst[NumQst],Answers);
/***** Insert question and user's answers into database *****/ /***** Insert question and user's answers into database *****/
Str_SetDecimalPointToUS (); // To print the floating point as a dot Str_SetDecimalPointToUS (); // To print the floating point as a dot
@ -1018,7 +1014,7 @@ void TsR_StoreOneTestResultQstInDB (long TstCod,long QstCod,unsigned NumQst,doub
" (TstCod,QstCod,QstInd,Score,Indexes,Answers)" " (TstCod,QstCod,QstInd,Score,Indexes,Answers)"
" VALUES" " VALUES"
" (%ld,%ld,%u,'%.15lg','%s','%s')", " (%ld,%ld,%u,'%.15lg','%s','%s')",
TstCod,QstCod, TstCod,UsrAnswers->QstCodes[NumQst],
NumQst, // 0, 1, 2, 3... NumQst, // 0, 1, 2, 3...
Score, Score,
Indexes, Indexes,
@ -1030,7 +1026,7 @@ void TsR_StoreOneTestResultQstInDB (long TstCod,long QstCod,unsigned NumQst,doub
/************ Get the questions of a test result from database ***************/ /************ Get the questions of a test result from database ***************/
/*****************************************************************************/ /*****************************************************************************/
static unsigned TsR_GetTestResultQuestionsFromDB (long TstCod) static unsigned TsR_GetTestResultQuestionsFromDB (long TstCod,struct Tst_UsrAnswers *UsrAnswers)
{ {
MYSQL_RES *mysql_res; MYSQL_RES *mysql_res;
MYSQL_ROW row; MYSQL_ROW row;
@ -1056,21 +1052,21 @@ static unsigned TsR_GetTestResultQuestionsFromDB (long TstCod)
row = mysql_fetch_row (mysql_res); row = mysql_fetch_row (mysql_res);
/* Get question code */ /* Get question code */
if ((Gbl.Test.QstCodes[NumQst] = Str_ConvertStrCodToLongCod (row[0])) < 0) if ((UsrAnswers->QstCodes[NumQst] = Str_ConvertStrCodToLongCod (row[0])) < 0)
Lay_ShowErrorAndExit ("Wrong code of question."); Lay_ShowErrorAndExit ("Wrong code of question.");
/* Get indexes for this question (row[1]) */ /* Get indexes for this question (row[1]) */
Str_Copy (Gbl.Test.StrIndexesOneQst[NumQst],row[1], Str_Copy (UsrAnswers->StrIndexesOneQst[NumQst],row[1],
Tst_MAX_BYTES_INDEXES_ONE_QST); Tst_MAX_BYTES_INDEXES_ONE_QST);
/* Get answers selected by user for this question (row[2]) */ /* Get answers selected by user for this question (row[2]) */
Str_Copy (Gbl.Test.StrAnswersOneQst[NumQst],row[2], Str_Copy (UsrAnswers->StrAnswersOneQst[NumQst],row[2],
Tst_MAX_BYTES_ANSWERS_ONE_QST); Tst_MAX_BYTES_ANSWERS_ONE_QST);
/* Replace each comma by a separator of multiple parameters */ /* Replace each comma by a separator of multiple parameters */
/* In database commas are used as separators instead of special chars */ /* In database commas are used as separators instead of special chars */
Par_ReplaceCommaBySeparatorMultiple (Gbl.Test.StrIndexesOneQst[NumQst]); Par_ReplaceCommaBySeparatorMultiple (UsrAnswers->StrIndexesOneQst[NumQst]);
Par_ReplaceCommaBySeparatorMultiple (Gbl.Test.StrAnswersOneQst[NumQst]); Par_ReplaceCommaBySeparatorMultiple (UsrAnswers->StrAnswersOneQst[NumQst]);
} }
/***** Free structure that stores the query result *****/ /***** Free structure that stores the query result *****/

View File

@ -33,12 +33,25 @@
/***************************** Public constants ******************************/ /***************************** Public constants ******************************/
/*****************************************************************************/ /*****************************************************************************/
#define Tst_MAX_OPTIONS_PER_QUESTION 10
#define Tst_MAX_BYTES_INDEXES_ONE_QST (Tst_MAX_OPTIONS_PER_QUESTION * (3 + 1))
#define Tst_MAX_BYTES_ANSWERS_ONE_QST (Tst_MAX_OPTIONS_PER_QUESTION * (3 + 1))
#define TsR_SCORE_MAX 10 // Maximum score of a test (10 in Spain). Must be unsigned! // TODO: Make this configurable by teachers #define TsR_SCORE_MAX 10 // Maximum score of a test (10 in Spain). Must be unsigned! // TODO: Make this configurable by teachers
/*****************************************************************************/ /*****************************************************************************/
/******************************* Public types ********************************/ /******************************* Public types ********************************/
/*****************************************************************************/ /*****************************************************************************/
struct Tst_UsrAnswers
{
long QstCodes[TstCfg_MAX_QUESTIONS_PER_TEST]; // Codes of the sent/received questions in a test
char StrIndexesOneQst[TstCfg_MAX_QUESTIONS_PER_TEST]
[Tst_MAX_BYTES_INDEXES_ONE_QST + 1]; // 0 1 2 3, 3 0 2 1, etc.
char StrAnswersOneQst[TstCfg_MAX_QUESTIONS_PER_TEST]
[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1]; // Answers selected by user
};
/*****************************************************************************/ /*****************************************************************************/
/***************************** Public prototypes *****************************/ /***************************** Public prototypes *****************************/
/*****************************************************************************/ /*****************************************************************************/
@ -52,9 +65,13 @@ void TsR_StoreScoreOfTestResultInDB (long TstCod,
void TsR_GetUsrsAndShowTstResults (void); void TsR_GetUsrsAndShowTstResults (void);
void TsR_ShowOneTstResult (void); void TsR_ShowOneTstResult (void);
void TsR_ShowTestResult (struct UsrData *UsrDat, void TsR_ShowTestResult (struct UsrData *UsrDat,
unsigned NumQsts,time_t TstTimeUTC, unsigned NumQsts,
const struct Tst_UsrAnswers *UsrAnswers,
time_t TstTimeUTC,
unsigned Visibility); unsigned Visibility);
void TsR_StoreOneTestResultQstInDB (long TstCod,long QstCod,unsigned NumQst,double Score); void TsR_StoreOneTestResultQstInDB (long TstCod,
const struct Tst_UsrAnswers *UsrAnswers,
unsigned NumQst,double Score);
void TsR_RemoveTestResultsMadeByUsrInAllCrss (long UsrCod); void TsR_RemoveTestResultsMadeByUsrInAllCrss (long UsrCod);
void TsR_RemoveTestResultsMadeByUsrInCrs (long UsrCod,long CrsCod); void TsR_RemoveTestResultsMadeByUsrInCrs (long UsrCod,long CrsCod);
void TsR_RemoveCrsTestResults (long CrsCod); void TsR_RemoveCrsTestResults (long CrsCod);