From 5592e9b8b3429726c620cf4a9df6c987269da2aa Mon Sep 17 00:00:00 2001 From: acanas Date: Mon, 23 Mar 2020 17:14:41 +0100 Subject: [PATCH] Version19.152 --- swad_changelog.h | 3 +- swad_game.c | 22 +-- swad_global.h | 2 + swad_match.c | 19 +-- swad_match_result.c | 21 +-- swad_match_result.h | 3 +- swad_string.c | 19 ++- swad_string.h | 2 +- swad_test.c | 343 ++++++++++++++++++++++++++------------------ swad_test.h | 25 ++-- swad_test_result.c | 62 ++++---- swad_test_result.h | 21 ++- 12 files changed, 306 insertions(+), 236 deletions(-) diff --git a/swad_changelog.h b/swad_changelog.h index 806222f5..2f2f069f 100644 --- a/swad_changelog.h +++ b/swad_changelog.h @@ -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.151.4 (2020-03-22)" +#define Log_PLATFORM_VERSION "SWAD 19.152 (2020-03-23)" #define CSS_FILE "swad19.146.css" #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: 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.3: Mar 22, 2020 Code refactoring in tests. (283433 lines) Version 19.151.2: Mar 21, 2020 Code refactoring in tests. (283446 lines) diff --git a/swad_game.c b/swad_game.c index a6621820..f706d605 100644 --- a/swad_game.c +++ b/swad_game.c @@ -156,7 +156,7 @@ static void Gam_ListGameQuestions (struct Game *Game); static void Gam_ListOneOrMoreQuestionsForEdition (long GamCod,unsigned NumQsts, MYSQL_RES *mysql_res, 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_PutButtonToAddNewQuestions (void); @@ -1972,7 +1972,7 @@ static void Gam_ListOneOrMoreQuestionsForEdition (long GamCod,unsigned NumQsts, HTM_TD_End (); /***** Question *****/ - Gam_ListQuestionForEdition (QstCod,StrQstInd); + Gam_ListQuestionForEdition (QstCod,QstInd); HTM_TR_End (); @@ -1988,9 +1988,8 @@ static void Gam_ListOneOrMoreQuestionsForEdition (long GamCod,unsigned NumQsts, /********************** 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; MYSQL_RES *mysql_res; 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); - - /* 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\""); + Tst_WriteNumQst (QstInd); if (QstExists) { AnswerType = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[1]); - HTM_Txt (Txt_TST_STR_ANSWER_TYPES[AnswerType]); + Tst_WriteAnswerType (AnswerType); } - HTM_DIV_End (); - HTM_TD_End (); /***** Write question code *****/ diff --git a/swad_global.h b/swad_global.h index 340f3973..1c09970c 100644 --- a/swad_global.h +++ b/swad_global.h @@ -649,12 +649,14 @@ struct Globals bool PutIconPrint; } ContextualIcons; } TimeTable; + /* struct { 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 } Test; + */ struct { struct DateTime DateIni; // TODO: Remove in future versions diff --git a/swad_match.c b/swad_match.c index 3ce47631..74665963 100644 --- a/swad_match.c +++ b/swad_match.c @@ -222,7 +222,7 @@ static void Mch_GetNumPlayers (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); @@ -3674,6 +3674,7 @@ void Mch_ReceiveQuestionAnswer (void) struct Mch_UsrAnswer UsrAnswer; unsigned NumQsts; unsigned NumQstsNotBlank; + struct Tst_UsrAnswers UsrAnswers; double TotalScore; /***** Get data of the match from database *****/ @@ -3730,8 +3731,9 @@ void Mch_ReceiveQuestionAnswer (void) /***** Update student's match result *****/ McR_GetMatchResultQuestionsFromDB (Match.MchCod,Gbl.Usrs.Me.UsrDat.UsrCod, - &NumQsts,&NumQstsNotBlank); - TotalScore = Mch_ComputeScore (NumQsts); + &NumQsts,&NumQstsNotBlank, + &UsrAnswers); + TotalScore = Mch_ComputeScore (NumQsts,&UsrAnswers); Str_SetDecimalPointToUS (); // To print the floating point as a dot 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 **********************/ /*****************************************************************************/ -static double Mch_ComputeScore (unsigned NumQsts) +static double Mch_ComputeScore (unsigned NumQsts,const struct Tst_UsrAnswers *UsrAnswers) { unsigned NumQst; struct Tst_Question Question; @@ -3807,16 +3809,17 @@ static double Mch_ComputeScore (unsigned NumQsts) NumQst++) { /***** 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 *****/ - Tst_GetAnswersFromStr (Gbl.Test.StrAnswersOneQst[NumQst],AnswersUsr); + Tst_GetAnswersFromStr (UsrAnswers->StrAnswersOneQst[NumQst],AnswersUsr); /***** 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 *****/ - Tst_ComputeScoreQst (&Question,Indexes,AnswersUsr,&ScoreThisQst,&AnswerIsNotBlank); + Tst_ComputeScoreQst (&Question, + Indexes,AnswersUsr,&ScoreThisQst,&AnswerIsNotBlank); /***** Compute total score *****/ TotalScore += ScoreThisQst; diff --git a/swad_match_result.c b/swad_match_result.c index 06a8bc08..0561cd96 100644 --- a/swad_match_result.c +++ b/swad_match_result.c @@ -1007,6 +1007,7 @@ void McR_ShowOneMchResult (void) char *Id; unsigned NumQsts; unsigned NumQstsNotBlank; + struct Tst_UsrAnswers UsrAnswers; double TotalScore; bool ShowPhoto; 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 *****/ McR_GetMatchResultQuestionsFromDB (Match.MchCod,UsrDat->UsrCod, - &NumQsts,&NumQstsNotBlank); + &NumQsts,&NumQstsNotBlank, + &UsrAnswers); /***** Begin box *****/ Box_BoxBegin (NULL,Match.Title,NULL, @@ -1192,7 +1194,7 @@ void McR_ShowOneMchResult (void) HTM_TR_End (); /***** Write answers and solutions *****/ - TsR_ShowTestResult (UsrDat,NumQsts,TimeUTC[Dat_START_TIME], + TsR_ShowTestResult (UsrDat,NumQsts,&UsrAnswers,TimeUTC[Dat_START_TIME], Game.Visibility); /***** End table *****/ @@ -1222,7 +1224,8 @@ void McR_ShowOneMchResult (void) /*****************************************************************************/ void McR_GetMatchResultQuestionsFromDB (long MchCod,long UsrCod, - unsigned *NumQsts,unsigned *NumQstsNotBlank) + unsigned *NumQsts,unsigned *NumQstsNotBlank, + struct Tst_UsrAnswers *UsrAnswers) { MYSQL_RES *mysql_res; MYSQL_ROW row; @@ -1252,7 +1255,7 @@ void McR_GetMatchResultQuestionsFromDB (long MchCod,long UsrCod, row = mysql_fetch_row (mysql_res); /* 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."); /* Get question index (row[1]) */ @@ -1261,24 +1264,24 @@ void McR_GetMatchResultQuestionsFromDB (long MchCod,long UsrCod, QstInd = (unsigned) LongNum; /* 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); /* Get answers selected by user for this question */ Mch_GetQstAnsFromDB (MchCod,UsrCod,QstInd,&UsrAnswer); 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); (*NumQstsNotBlank)++; } 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 */ /* In database commas are used as separators instead of special chars */ - Par_ReplaceCommaBySeparatorMultiple (Gbl.Test.StrIndexesOneQst[NumQst]); - Par_ReplaceCommaBySeparatorMultiple (Gbl.Test.StrAnswersOneQst[NumQst]); + Par_ReplaceCommaBySeparatorMultiple (UsrAnswers->StrIndexesOneQst[NumQst]); + Par_ReplaceCommaBySeparatorMultiple (UsrAnswers->StrAnswersOneQst[NumQst]); } /***** Free structure that stores the query result *****/ diff --git a/swad_match_result.h b/swad_match_result.h index 55c748cc..356d4cbc 100644 --- a/swad_match_result.h +++ b/swad_match_result.h @@ -50,6 +50,7 @@ void McR_ShowAllMchResultsInMch (void); void McR_ShowOneMchResult (void); void McR_GetMatchResultQuestionsFromDB (long MchCod,long UsrCod, - unsigned *NumQsts,unsigned *NumQstsNotBlank); + unsigned *NumQsts,unsigned *NumQstsNotBlank, + struct Tst_UsrAnswers *UsrAnswers); #endif diff --git a/swad_string.c b/swad_string.c index 3246d3b8..aa647b03 100644 --- a/swad_string.c +++ b/swad_string.c @@ -927,25 +927,30 @@ void Str_ConvertStrFloatCommaToStrFloatPoint (char *Str) /*****************************************************************************/ // This function may change Str on wrong double -double Str_GetDoubleFromStr (char *Str) +double Str_GetDoubleFromStr (const char *Str) { + char *StrPoint; double DoubleNum; /***** Trivial check *****/ if (Str == NULL) // If no string... 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 (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 - if (sscanf (Str,"%lf",&DoubleNum) != 1) - { // If the string does not hold a valid number... - DoubleNum = 0.0; // ...the number is reset to 0 - Str[0] = '\0'; // ...and the string is reset to "" - } + if (sscanf (StrPoint,"%lf",&DoubleNum) != 1) // If the string does not hold a valid number... + DoubleNum = 0.0; // ...the number is reset to 0 Str_SetDecimalPointToLocal (); // Return to local system + /***** Free temporary string *****/ + free (StrPoint); + return DoubleNum; } diff --git a/swad_string.h b/swad_string.h index 8e039b91..28ad21fb 100644 --- a/swad_string.h +++ b/swad_string.h @@ -94,7 +94,7 @@ char Str_ConvertToLowerLetter (char Ch); void Str_DoubleNumToStr (char **Str,double Number); void Str_DoubleNumToStrFewDigits (char **Str,double Number); void Str_ConvertStrFloatCommaToStrFloatPoint (char *Str); -double Str_GetDoubleFromStr (char *Str); +double Str_GetDoubleFromStr (const char *Str); void Str_SetDecimalPointToUS (void); void Str_SetDecimalPointToLocal (void); diff --git a/swad_test.c b/swad_test.c index c0fd1764..178dd20d 100644 --- a/swad_test.c +++ b/swad_test.c @@ -131,16 +131,24 @@ static void Tst_ResetAnswerTypes (struct Tst_AnswerTypes *AnswerTypes); static void Tst_ShowFormRequestTest (const struct Tst_Tags *Tags, 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 void Tst_SetTstStatus (unsigned NumTst,Tst_Status_t TstStatus); static Tst_Status_t Tst_GetTstStatus (unsigned NumTst); 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_ShowTestResultAfterAssess (long TstCod,unsigned NumQsts, +static void Tst_ShowTestResultAfterAssess (long TstCod, + unsigned NumQsts, unsigned *NumQstsNotBlank, + const struct Tst_UsrAnswers *UsrAnswers, double *TotalScore); +static void Tst_WriteQstAndAnsTestToAnswer (unsigned NumQst, + long QstCod, + MYSQL_ROW row); + static void Tst_PutFormToEditQstMedia (const struct Media *Media,int NumMediaInForm, bool OptionsDisabled); 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, Tst_AnswerType_t AnswerType,bool Shuffle); static void Tst_WriteAnswersTestResult (struct UsrData *UsrDat, + const struct Tst_UsrAnswers *UsrAnswers, unsigned NumQst,long QstCod, Tst_AnswerType_t AnswerType, unsigned Visibility, @@ -216,6 +225,7 @@ static void Tst_WriteAnswersTestResult (struct UsrData *UsrDat, static void Tst_WriteTFAnsViewTest (unsigned NumQst); static void Tst_WriteTFAnsAssessTest (struct UsrData *UsrDat, + const struct Tst_UsrAnswers *UsrAnswers, unsigned NumQst, const struct Tst_Question *Question, MYSQL_RES *mysql_res, @@ -227,6 +237,7 @@ static void Tst_WriteChoiceAnsViewTest (unsigned NumQst,long QstCod, Tst_AnswerType_t AnswerType, bool Shuffle); static void Tst_WriteChoiceAnsAssessTest (struct UsrData *UsrDat, + const struct Tst_UsrAnswers *UsrAnswers, unsigned NumQst, struct Tst_Question *Question, 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_WriteTextAnsAssessTest (struct UsrData *UsrDat, + const struct Tst_UsrAnswers *UsrAnswers, unsigned NumQst, struct Tst_Question *Question, MYSQL_RES *mysql_res, @@ -246,6 +258,7 @@ static void Tst_WriteTextAnsAssessTest (struct UsrData *UsrDat, static void Tst_WriteIntAnsViewTest (unsigned NumQst); static void Tst_WriteIntAnsAssessTest (struct UsrData *UsrDat, + const struct Tst_UsrAnswers *UsrAnswers, unsigned NumQst, const struct Tst_Question *Question, MYSQL_RES *mysql_res, @@ -255,6 +268,7 @@ static void Tst_WriteIntAnsAssessTest (struct UsrData *UsrDat, static void Tst_WriteFloatAnsViewTest (unsigned NumQst); static void Tst_WriteFloatAnsAssessTest (struct UsrData *UsrDat, + const struct Tst_UsrAnswers *UsrAnswers, unsigned NumQst, const struct Tst_Question *Question, MYSQL_RES *mysql_res, @@ -577,6 +591,7 @@ void Tst_AssessTest (void) bool AllowTeachers; // Can teachers of this course see the test result? long TstCod = -1L; // Initialized to avoid warning unsigned NumQstsNotBlank; + struct Tst_UsrAnswers UsrAnswers; double TotalScore; /***** Read test configuration from database *****/ @@ -597,7 +612,7 @@ void Tst_AssessTest (void) AllowTeachers = Par_GetParToBool ("AllowTchs"); /***** 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 *****/ TstCod = TsR_CreateTestResultInDB (AllowTeachers,NumQsts); @@ -620,7 +635,8 @@ void Tst_AssessTest (void) /***** Write answers and solutions *****/ HTM_TABLE_BeginWideMarginPadding (10); - Tst_ShowTestResultAfterAssess (TstCod,NumQsts,&NumQstsNotBlank,&TotalScore); + Tst_ShowTestResultAfterAssess (TstCod,NumQsts,&NumQstsNotBlank, + &UsrAnswers,&TotalScore); HTM_TABLE_End (); /***** Write total score and grade *****/ @@ -660,7 +676,8 @@ void Tst_AssessTest (void) /*********** 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; 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), "Qst%03u", 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."); /* Get indexes for this question */ snprintf (StrQstIndOrAns,sizeof (StrQstIndOrAns), "Ind%03u", 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",... */ /* Get answers selected by user for this question */ snprintf (StrQstIndOrAns,sizeof (StrQstIndOrAns), "Ans%03u", 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",... */ } } @@ -905,7 +922,8 @@ static unsigned Tst_GetNumAccessesTst (void) // 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 -static void Tst_ShowTestQuestionsWhenSeeing (unsigned NumQsts,MYSQL_RES *mysql_res) +static void Tst_ShowTestQuestionsWhenSeeing (unsigned NumQsts, + MYSQL_RES *mysql_res) { unsigned NumQst; long QstCod; @@ -936,32 +954,12 @@ static void Tst_ShowOneTestQuestionWhenSeeing (unsigned NumQst,long QstCod) { MYSQL_RES *mysql_res; MYSQL_ROW row; - double ScoreThisQst; // Not used here - bool AnswerIsNotBlank; // Not used here if (Tst_GetOneQuestionByCod (QstCod,&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 - */ - 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 + Tst_WriteQstAndAnsTestToAnswer (NumQst,QstCod,row); } else Lay_ShowErrorAndExit ("Wrong question."); @@ -1000,8 +998,10 @@ void Tst_ShowTagList (unsigned NumTags,MYSQL_RES *mysql_res) /******************* 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, + const struct Tst_UsrAnswers *UsrAnswers, double *TotalScore) { extern const char *Txt_Question_removed; @@ -1022,33 +1022,19 @@ static void Tst_ShowTestResultAfterAssess (long TstCod,unsigned NumQsts, Gbl.RowEvenOdd = NumQst % 2; /***** 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 *****/ - Tst_WriteQstAndAnsTest (Tst_SHOW_TEST_RESULT, - &Gbl.Usrs.Me.UsrDat, - NumQst, - Gbl.Test.QstCodes[NumQst], - row, - TstCfg_GetConfigVisibility (), - &ScoreThisQst,&AnswerIsNotBlank); + row = mysql_fetch_row (mysql_res); + Tst_WriteQstAndAnsTestResult (&Gbl.Usrs.Me.UsrDat, + UsrAnswers, + NumQst, + row, + TstCfg_GetConfigVisibility (), + &ScoreThisQst,&AnswerIsNotBlank); /***** Store test result question in database *****/ - TsR_StoreOneTestResultQstInDB (TstCod,Gbl.Test.QstCodes[NumQst], + TsR_StoreOneTestResultQstInDB (TstCod,UsrAnswers, NumQst, // 0, 1, 2, 3... ScoreThisQst); @@ -1059,15 +1045,15 @@ static void Tst_ShowTestResultAfterAssess (long TstCod,unsigned NumQsts, /***** Update the number of accesses and the score of this question *****/ if (Gbl.Usrs.Me.Role.Logged == Rol_STD) - Tst_UpdateScoreQst (Gbl.Test.QstCodes[NumQst],ScoreThisQst,AnswerIsNotBlank); + Tst_UpdateScoreQst (UsrAnswers->QstCodes[NumQst],ScoreThisQst,AnswerIsNotBlank); } 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_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); @@ -1086,15 +1072,72 @@ static void Tst_ShowTestResultAfterAssess (long TstCod,unsigned NumQsts, /********** Write a row of a test, with one question and its answer **********/ /*****************************************************************************/ -void Tst_WriteQstAndAnsTest (Tst_ActionToDoWithQuestions_t ActionToDoWithQuestions, - struct UsrData *UsrDat, - unsigned NumQst, - long QstCod, - MYSQL_ROW row, - unsigned Visibility, - double *ScoreThisQst,bool *AnswerIsNotBlank) +static void Tst_WriteQstAndAnsTestToAnswer (unsigned NumQst, + long QstCod, + MYSQL_ROW row) + { + struct Tst_Question Question; + /* + 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; bool IsVisibleQstAndAnsTxt = TsV_IsVisibleQstAndAnsTxt (Visibility); /* @@ -1108,30 +1151,27 @@ void Tst_WriteQstAndAnsTest (Tst_ActionToDoWithQuestions_t ActionToDoWithQuestio 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); - - /***** Write number of question *****/ - HTM_DIV_Begin ("class=\"BIG_INDEX\""); - HTM_Unsigned (NumQst + 1); - HTM_DIV_End (); - - /***** Write answer type (row[1]) *****/ + Tst_WriteNumQst (NumQst + 1); Question.Answer.Type = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[1]); - HTM_DIV_Begin ("class=\"DAT_SMALL\""); - HTM_Txt (Txt_TST_STR_ANSWER_TYPES[Question.Answer.Type]); - HTM_DIV_End (); - + Tst_WriteAnswerType (Question.Answer.Type); HTM_TD_End (); - /***** Write stem (row[3]) *****/ + /***** Stem, media and answers *****/ HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd); + + /* Stem (row[3]) */ Tst_WriteQstStem (row[3],"TEST_EXA",IsVisibleQstAndAnsTxt); - /***** Get and show media (row[5]) *****/ + /* Media (row[5]) */ if (IsVisibleQstAndAnsTxt) { Question.Media.MedCod = Str_ConvertStrCodToLongCod (row[5]); @@ -1141,31 +1181,50 @@ void Tst_WriteQstAndAnsTest (Tst_ActionToDoWithQuestions_t ActionToDoWithQuestio "TEST_MED_SHOW"); } - /***** Write answers depending on shuffle (row[2]) and feedback (row[4]) *****/ - switch (ActionToDoWithQuestions) - { - case Tst_SHOW_TEST_TO_ANSWER: - Tst_WriteAnswersTestToAnswer (NumQst,QstCod,Question.Answer.Type,(row[2][0] == 'Y')); - break; - case Tst_SHOW_TEST_RESULT: - Tst_WriteAnswersTestResult (UsrDat,NumQst,QstCod,Question.Answer.Type, - Visibility, - ScoreThisQst,AnswerIsNotBlank); + /* Answers */ + Tst_WriteAnswersTestResult (UsrDat,UsrAnswers, + NumQst,UsrAnswers->QstCodes[NumQst],Question.Answer.Type, + Visibility, + ScoreThisQst,AnswerIsNotBlank); + + /* Question feedback (row[4]) */ + if (TsV_IsVisibleFeedbackTxt (Visibility)) + Tst_WriteQstFeedback (row[4],"TEST_EXA_LIGHT"); - /* Write question feedback (row[4]) */ - if (TsV_IsVisibleFeedbackTxt (Visibility)) - Tst_WriteQstFeedback (row[4],"TEST_EXA_LIGHT"); - break; - default: - break; - } HTM_TD_End (); + + /***** End row *****/ HTM_TR_End (); /***** Destroy test 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 *********************/ /*****************************************************************************/ @@ -2852,7 +2911,6 @@ static void Tst_WriteQuestionRowForEdition (const struct Tst_Tags *Tags, unsigned long NumRow, long QstCod) { - extern const char *Txt_TST_STR_ANSWER_TYPES[Tst_NUM_ANS_TYPES]; MYSQL_RES *mysql_res; MYSQL_ROW row; struct Tst_Question Question; @@ -2907,27 +2965,19 @@ static void Tst_WriteQuestionRowForEdition (const struct Tst_Tags *Tags, HTM_TD_End (); + /* Number of question and answer type (row[1]) */ HTM_TD_Begin ("class=\"RT COLOR%u\"",Gbl.RowEvenOdd); - - /* Write number of question */ - HTM_DIV_Begin ("class=\"BIG_INDEX\""); - HTM_UnsignedLong (NumRow + 1); - HTM_DIV_End (); - - /* Write answer type (row[1]) */ + Tst_WriteNumQst ((unsigned) NumRow + 1); Question.Answer.Type = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[1]); - HTM_DIV_Begin ("class=\"DAT_SMALL\""); - HTM_Txt (Txt_TST_STR_ANSWER_TYPES[Question.Answer.Type]); - HTM_DIV_End (); - + Tst_WriteAnswerType (Question.Answer.Type); HTM_TD_End (); - /* Write question code */ + /* Question code */ HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd); HTM_TxtF ("%ld ",QstCod); 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]); if (asprintf (&Id,"tst_date_%u",++UniqueId) < 0) Lay_NotEnoughMemoryExit (); @@ -2939,12 +2989,12 @@ static void Tst_WriteQuestionRowForEdition (const struct Tst_Tags *Tags, HTM_TD_End (); free (Id); - /* Write the question tags */ + /* Question tags */ HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd); Tst_GetAndWriteTagsQst (QstCod); HTM_TD_End (); - /* Write if shuffle is enabled (row[2]) */ + /* Shuffle (row[2]) */ HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd); if (Question.Answer.Type == Tst_ANS_UNIQUE_CHOICE || Question.Answer.Type == Tst_ANS_MULTIPLE_CHOICE) @@ -2966,7 +3016,7 @@ static void Tst_WriteQuestionRowForEdition (const struct Tst_Tags *Tags, } HTM_TD_End (); - /* Write stem (row[3]) */ + /* Stem (row[3]) */ HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd); Tst_WriteQstStem (row[3],"TEST_EDI", true); // Visible @@ -2978,7 +3028,7 @@ static void Tst_WriteQuestionRowForEdition (const struct Tst_Tags *Tags, "TEST_MED_EDIT_LIST_CONT", "TEST_MED_EDIT_LIST"); - /* Write feedback (row[4]) and answers */ + /* Feedback (row[4]) and answers */ Tst_WriteQstFeedback (row[4],"TEST_EDI_LIGHT"); Tst_WriteAnswersEdit (QstCod,Question.Answer.Type); HTM_TD_End (); @@ -3001,12 +3051,12 @@ static void Tst_WriteQuestionRowForEdition (const struct Tst_Tags *Tags, Lay_ShowErrorAndExit ("Wrong score of a question."); 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_UnsignedLong (NumHitsThisQst); HTM_TD_End (); - /* Write average score */ + /* Average score */ HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd); if (NumHitsThisQst) HTM_Double2Decimals (TotalScoreThisQst / @@ -3015,12 +3065,12 @@ static void Tst_WriteQuestionRowForEdition (const struct Tst_Tags *Tags, HTM_Txt ("N.A."); 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_UnsignedLong (NumHitsNotBlankThisQst); HTM_TD_End (); - /* Write average score (not blank) */ + /* Average score (not blank) */ HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd); if (NumHitsNotBlankThisQst) HTM_Double2Decimals (TotalScoreThisQst / @@ -3054,7 +3104,6 @@ static void Tst_ListOneOrMoreQuestionsForSelection (unsigned long NumRows, extern const char *Txt_Date; extern const char *Txt_Tags; 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_Question; 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, + const struct Tst_UsrAnswers *UsrAnswers, unsigned NumQst,long QstCod, Tst_AnswerType_t AnswerType, unsigned Visibility, @@ -3604,28 +3654,33 @@ static void Tst_WriteAnswersTestResult (struct UsrData *UsrDat, switch (Question.Answer.Type) { case Tst_ANS_INT: - Tst_WriteIntAnsAssessTest (UsrDat,NumQst,&Question,mysql_res, + Tst_WriteIntAnsAssessTest (UsrDat,UsrAnswers, + NumQst,&Question,mysql_res, Visibility, ScoreThisQst,AnswerIsNotBlank); break; case Tst_ANS_FLOAT: - Tst_WriteFloatAnsAssessTest (UsrDat,NumQst,&Question,mysql_res, + Tst_WriteFloatAnsAssessTest (UsrDat,UsrAnswers, + NumQst,&Question,mysql_res, Visibility, ScoreThisQst,AnswerIsNotBlank); break; case Tst_ANS_TRUE_FALSE: - Tst_WriteTFAnsAssessTest (UsrDat,NumQst,&Question,mysql_res, + Tst_WriteTFAnsAssessTest (UsrDat,UsrAnswers, + NumQst,&Question,mysql_res, Visibility, ScoreThisQst,AnswerIsNotBlank); break; case Tst_ANS_UNIQUE_CHOICE: case Tst_ANS_MULTIPLE_CHOICE: - Tst_WriteChoiceAnsAssessTest (UsrDat,NumQst,&Question,mysql_res, + Tst_WriteChoiceAnsAssessTest (UsrDat,UsrAnswers, + NumQst,&Question,mysql_res, Visibility, ScoreThisQst,AnswerIsNotBlank); break; case Tst_ANS_TEXT: - Tst_WriteTextAnsAssessTest (UsrDat,NumQst,&Question,mysql_res, + Tst_WriteTextAnsAssessTest (UsrDat,UsrAnswers, + NumQst,&Question,mysql_res, Visibility, ScoreThisQst,AnswerIsNotBlank); break; @@ -3698,6 +3753,7 @@ void Tst_WriteAnsTF (char AnsTF) /*****************************************************************************/ static void Tst_WriteTFAnsAssessTest (struct UsrData *UsrDat, + const struct Tst_UsrAnswers *UsrAnswers, unsigned NumQst, const struct Tst_Question *Question, MYSQL_RES *mysql_res, @@ -3721,7 +3777,7 @@ static void Tst_WriteTFAnsAssessTest (struct UsrData *UsrDat, row = mysql_fetch_row (mysql_res); /***** 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) { *AnswerIsNotBlank = false; @@ -3919,6 +3975,7 @@ static void Tst_WriteChoiceAnsViewTest (unsigned NumQst,long QstCod, /*****************************************************************************/ static void Tst_WriteChoiceAnsAssessTest (struct UsrData *UsrDat, + const struct Tst_UsrAnswers *UsrAnswers, unsigned NumQst, struct Tst_Question *Question, MYSQL_RES *mysql_res, @@ -3942,10 +3999,10 @@ static void Tst_WriteChoiceAnsAssessTest (struct UsrData *UsrDat, Tst_GetChoiceAns (mysql_res,Question); /***** 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 *****/ - Tst_GetAnswersFromStr (Gbl.Test.StrAnswersOneQst[NumQst],AnswersUsr); + Tst_GetAnswersFromStr (UsrAnswers->StrAnswersOneQst[NumQst],AnswersUsr); /***** Compute the total score of this question *****/ Tst_ComputeScoreQst (Question,Indexes,AnswersUsr,ScoreThisQst,AnswerIsNotBlank); @@ -4417,6 +4474,7 @@ static void Tst_WriteTextAnsViewTest (unsigned NumQst) /*****************************************************************************/ static void Tst_WriteTextAnsAssessTest (struct UsrData *UsrDat, + const struct Tst_UsrAnswers *UsrAnswers, unsigned NumQst, struct Tst_Question *Question, MYSQL_RES *mysql_res, @@ -4481,10 +4539,10 @@ static void Tst_WriteTextAnsAssessTest (struct UsrData *UsrDat, HTM_TR_Begin (NULL); /***** 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 */ - Str_Copy (TextAnsUsr,Gbl.Test.StrAnswersOneQst[NumQst], + Str_Copy (TextAnsUsr,UsrAnswers->StrAnswersOneQst[NumQst], Tst_MAX_BYTES_ANSWERS_ONE_QST); /* In order to compare student answer to stored answer, @@ -4514,7 +4572,7 @@ static void Tst_WriteTextAnsAssessTest (struct UsrData *UsrDat, (Correct ? "ANS_OK" : "ANS_BAD") : "ANS_0"); - HTM_Txt (Gbl.Test.StrAnswersOneQst[NumQst]); + HTM_Txt (UsrAnswers->StrAnswersOneQst[NumQst]); } else // If user has omitted the answer HTM_TD_Begin (NULL); @@ -4571,7 +4629,7 @@ static void Tst_WriteTextAnsAssessTest (struct UsrData *UsrDat, HTM_TR_End (); /***** 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; *ScoreThisQst = 0.0; @@ -4589,7 +4647,7 @@ static void Tst_WriteTextAnsAssessTest (struct UsrData *UsrDat, if (TsV_IsVisibleEachQstScore (Visibility)) { 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_Double2Decimals (0.0); @@ -4632,6 +4690,7 @@ static void Tst_WriteIntAnsViewTest (unsigned NumQst) /*****************************************************************************/ static void Tst_WriteIntAnsAssessTest (struct UsrData *UsrDat, + const struct Tst_UsrAnswers *UsrAnswers, unsigned NumQst, const struct Tst_Question *Question, MYSQL_RES *mysql_res, @@ -4666,9 +4725,9 @@ static void Tst_WriteIntAnsAssessTest (struct UsrData *UsrDat, HTM_TR_Begin (NULL); /***** 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\"", TsV_IsVisibleCorrectAns (Visibility) ? @@ -4680,7 +4739,6 @@ static void Tst_WriteIntAnsAssessTest (struct UsrData *UsrDat, } else { - Gbl.Test.StrAnswersOneQst[NumQst][0] = '\0'; HTM_TD_Begin ("class=\"ANS_0 CM\""); HTM_Txt ("?"); HTM_TD_End (); @@ -4701,7 +4759,7 @@ static void Tst_WriteIntAnsAssessTest (struct UsrData *UsrDat, HTM_TR_End (); /***** 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; *ScoreThisQst = 0.0; @@ -4719,7 +4777,7 @@ static void Tst_WriteIntAnsAssessTest (struct UsrData *UsrDat, if (TsV_IsVisibleEachQstScore (Visibility)) { 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_Double2Decimals (0.0); @@ -4762,6 +4820,7 @@ static void Tst_WriteFloatAnsViewTest (unsigned NumQst) /*****************************************************************************/ static void Tst_WriteFloatAnsAssessTest (struct UsrData *UsrDat, + const struct Tst_UsrAnswers *UsrAnswers, unsigned NumQst, const struct Tst_Question *Question, MYSQL_RES *mysql_res, @@ -4809,10 +4868,10 @@ static void Tst_WriteFloatAnsAssessTest (struct UsrData *UsrDat, HTM_TR_Begin (NULL); /***** 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]); - if (Gbl.Test.StrAnswersOneQst[NumQst][0]) // It's a correct floating point number + FloatAnsUsr = Str_GetDoubleFromStr (UsrAnswers->StrAnswersOneQst[NumQst]); + if (UsrAnswers->StrAnswersOneQst[NumQst][0]) // It's a correct floating point number { HTM_TD_Begin ("class=\"%s CM\"", TsV_IsVisibleCorrectAns (Visibility) ? @@ -4850,7 +4909,7 @@ static void Tst_WriteFloatAnsAssessTest (struct UsrData *UsrDat, HTM_TR_End (); /***** 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; *ScoreThisQst = 0.0; @@ -4869,7 +4928,7 @@ static void Tst_WriteFloatAnsAssessTest (struct UsrData *UsrDat, if (TsV_IsVisibleEachQstScore (Visibility)) { 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_Double2Decimals (0.0); diff --git a/swad_test.h b/swad_test.h index 78da33b0..4051599d 100644 --- a/swad_test.h +++ b/swad_test.h @@ -41,10 +41,6 @@ #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_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_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_AnswerTypes AnswerTypes; Tst_QuestionsOrder_t SelectedOrder; - 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 + // struct Tst_UsrAnswers UsrAnswers; }; 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_WriteQstAndAnsTest (Tst_ActionToDoWithQuestions_t ActionToDoWithQuestions, - struct UsrData *UsrDat, - unsigned NumQst, - long QstCod, - MYSQL_ROW row, - unsigned Visibility, - double *ScoreThisQst,bool *AnswerIsNotBlank); +void Tst_WriteQstAndAnsTestResult (struct UsrData *UsrDat, + const struct Tst_UsrAnswers *UsrAnswers, + unsigned NumQst, + MYSQL_ROW row, + unsigned Visibility, + 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_WriteQstFeedback (const char *Feedback,const char *ClassFeedback); diff --git a/swad_test_result.c b/swad_test_result.c index a2e3ac21..9f48883b 100644 --- a/swad_test_result.c +++ b/swad_test_result.c @@ -92,7 +92,7 @@ static void TsR_GetTestResultDataByTstCod (long TstCod, unsigned *NumQsts, unsigned *NumQstsNotBlank, 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 **************/ @@ -606,6 +606,7 @@ void TsR_ShowOneTstResult (void) time_t TstTimeUTC = 0; // Test result UTC date-time, initialized to avoid warning unsigned NumQsts; unsigned NumQstsNotBlank; + struct Tst_UsrAnswers UsrAnswers; double TotalScore; bool ShowPhoto; char PhotoURL[PATH_MAX + 1]; @@ -674,7 +675,7 @@ void TsR_ShowOneTstResult (void) if (ICanViewTest) // I am allowed to view this test result { /***** Get questions and user's answers of the test result from database *****/ - NumQsts = TsR_GetTestResultQuestionsFromDB (TstCod); + NumQsts = TsR_GetTestResultQuestionsFromDB (TstCod,&UsrAnswers); /***** Begin box *****/ Box_BoxBegin (NULL,Txt_Test_result,NULL, @@ -791,7 +792,9 @@ void TsR_ShowOneTstResult (void) /***** Write answers and solutions *****/ TsR_ShowTestResult (&Gbl.Usrs.Other.UsrDat, - NumQsts,TstTimeUTC, + NumQsts, + &UsrAnswers, + TstTimeUTC, TstCfg_GetConfigVisibility ()); /***** End table *****/ @@ -850,7 +853,9 @@ static void TsR_ShowTstTagsPresentInATestResult (long TstCod) /*****************************************************************************/ void TsR_ShowTestResult (struct UsrData *UsrDat, - unsigned NumQsts,time_t TstTimeUTC, + unsigned NumQsts, + const struct Tst_UsrAnswers *UsrAnswers, + time_t TstTimeUTC, unsigned Visibility) { extern const char *Txt_Question_modified; @@ -870,21 +875,10 @@ void TsR_ShowTestResult (struct UsrData *UsrDat, Gbl.RowEvenOdd = NumQst % 2; /***** 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 - */ /***** If this question has been edited later than test time ==> don't show question ****/ @@ -910,13 +904,13 @@ void TsR_ShowTestResult (struct UsrData *UsrDat, } else /***** Write questions and answers *****/ - Tst_WriteQstAndAnsTest (Tst_SHOW_TEST_RESULT, - UsrDat, - NumQst,Gbl.Test.QstCodes[NumQst], - row, - Visibility, - &ScoreThisQst, // Not used here - &AnswerIsNotBlank); // Not used here + Tst_WriteQstAndAnsTestResult (UsrDat, + UsrAnswers, + NumQst, + row, + Visibility, + &ScoreThisQst, // Not used here + &AnswerIsNotBlank); // Not used here } else { @@ -1001,15 +995,17 @@ static void TsR_GetTestResultDataByTstCod (long TstCod, /************ 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 Answers[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1]; /***** Replace each separator of multiple parameters by a comma *****/ /* In database commas are used as separators instead of special chars */ - Par_ReplaceSeparatorMultipleByComma (Gbl.Test.StrIndexesOneQst[NumQst],Indexes); - Par_ReplaceSeparatorMultipleByComma (Gbl.Test.StrAnswersOneQst[NumQst],Answers); + Par_ReplaceSeparatorMultipleByComma (UsrAnswers->StrIndexesOneQst[NumQst],Indexes); + Par_ReplaceSeparatorMultipleByComma (UsrAnswers->StrAnswersOneQst[NumQst],Answers); /***** Insert question and user's answers into database *****/ 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)" " VALUES" " (%ld,%ld,%u,'%.15lg','%s','%s')", - TstCod,QstCod, + TstCod,UsrAnswers->QstCodes[NumQst], NumQst, // 0, 1, 2, 3... Score, Indexes, @@ -1030,7 +1026,7 @@ void TsR_StoreOneTestResultQstInDB (long TstCod,long QstCod,unsigned NumQst,doub /************ 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_ROW row; @@ -1056,21 +1052,21 @@ static unsigned TsR_GetTestResultQuestionsFromDB (long TstCod) row = mysql_fetch_row (mysql_res); /* 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."); /* 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); /* 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); /* Replace each comma by a separator of multiple parameters */ /* In database commas are used as separators instead of special chars */ - Par_ReplaceCommaBySeparatorMultiple (Gbl.Test.StrIndexesOneQst[NumQst]); - Par_ReplaceCommaBySeparatorMultiple (Gbl.Test.StrAnswersOneQst[NumQst]); + Par_ReplaceCommaBySeparatorMultiple (UsrAnswers->StrIndexesOneQst[NumQst]); + Par_ReplaceCommaBySeparatorMultiple (UsrAnswers->StrAnswersOneQst[NumQst]); } /***** Free structure that stores the query result *****/ diff --git a/swad_test_result.h b/swad_test_result.h index 84600b6c..3a81c9b3 100644 --- a/swad_test_result.h +++ b/swad_test_result.h @@ -33,12 +33,25 @@ /***************************** 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 /*****************************************************************************/ /******************************* 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 *****************************/ /*****************************************************************************/ @@ -52,9 +65,13 @@ void TsR_StoreScoreOfTestResultInDB (long TstCod, void TsR_GetUsrsAndShowTstResults (void); void TsR_ShowOneTstResult (void); void TsR_ShowTestResult (struct UsrData *UsrDat, - unsigned NumQsts,time_t TstTimeUTC, + unsigned NumQsts, + const struct Tst_UsrAnswers *UsrAnswers, + time_t TstTimeUTC, 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_RemoveTestResultsMadeByUsrInCrs (long UsrCod,long CrsCod); void TsR_RemoveCrsTestResults (long CrsCod);