From 2bff106d1e178094033e6999ee2a9c88611edeb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20Ca=C3=B1as=20Vargas?= Date: Mon, 23 Sep 2019 19:17:12 +0200 Subject: [PATCH] Version19.10 --- sql/swad.sql | 10 +- swad_changelog.h | 8 +- swad_database.c | 18 ++++ swad_mark.c | 1 + swad_match.c | 269 +++++++++++++++++++++++++++-------------------- swad_test.c | 112 +++++++++++++++----- swad_test.h | 5 + 7 files changed, 277 insertions(+), 146 deletions(-) diff --git a/sql/swad.sql b/sql/swad.sql index f92eaaa05..32b65e081 100644 --- a/sql/swad.sql +++ b/sql/swad.sql @@ -611,7 +611,7 @@ CREATE TABLE IF NOT EXISTS gam_games ( UNIQUE INDEX(GamCod), INDEX(CrsCod)); -- --- Table mch_answers: stores the answers to the matches +-- Table mch_answers: stores the users' answers to the matches -- CREATE TABLE IF NOT EXISTS mch_answers (" MchCod INT NOT NULL," @@ -658,6 +658,14 @@ CREATE TABLE IF NOT EXISTS mch_players ( TS TIMESTAMP, UNIQUE INDEX(MchCod,UsrCod)); -- +-- Table mch_indexes: stores the order of answers in a match +-- +CREATE TABLE IF NOT EXISTS mch_indexes ( + MchCod INT NOT NULL, + QstInd INT NOT NULL, + Indexes TEXT NOT NULL, + UNIQUE INDEX(MchCod,QstInd)); +-- -- Table mch_results: stores the current match results -- CREATE TABLE IF NOT EXISTS mch_results ( diff --git a/swad_changelog.h b/swad_changelog.h index e7a6e47d2..83f8c18f1 100644 --- a/swad_changelog.h +++ b/swad_changelog.h @@ -450,6 +450,8 @@ Lo de mutear anuncios, en principio prefiero hacer una opci // TODO: En Eventos de asistencia, poner un icono de enlace a horario de la asignatura ¿y otro a grupos? // TODO: Reportado por Javier Fernández Baldomero. Un profesor debería poder cambiar la foto de un estudiante confirmado. Sale el icono, pero luego dice ue no hay permiso +// TODO: URGENTE: Reportado por Javier Fernández Baldomero. Al pasar lista con SWADroid, los estudiantes sin foto no salen en la lista de alumnos de SWADroid. + /*****************************************************************************/ /****************************** Public constants *****************************/ /*****************************************************************************/ @@ -468,10 +470,14 @@ enscript -2 --landscape --color --file-align=2 --highlight --line-numbers -o - * En OpenSWAD: ps2pdf source.ps destination.pdf */ -#define Log_PLATFORM_VERSION "SWAD 19.9.3 (2019-09-23)" +#define Log_PLATFORM_VERSION "SWAD 19.10 (2019-09-23)" #define CSS_FILE "swad19.3.css" #define JS_FILE "swad18.130.2.js" /* + Version 19.10: Sep 23, 2019 View matches results. Not finished. (245709 lines) + 1 change necessary in database: +CREATE TABLE IF NOT EXISTS mch_indexes (MchCod INT NOT NULL,QstInd INT NOT NULL,Indexes TEXT NOT NULL,UNIQUE INDEX(MchCod,QstInd)); + Version 19.9.3: Sep 23, 2019 Code refactoring in tables. (245597 lines) Version 19.9.2: Sep 23, 2019 View matches results. Not finished. (245598 lines) 2 changes necessary in database: diff --git a/swad_database.c b/swad_database.c index 7c265664e..392d31605 100644 --- a/swad_database.c +++ b/swad_database.c @@ -1433,6 +1433,24 @@ mysql> DESCRIBE mch_players; "TS TIMESTAMP," "UNIQUE INDEX(MchCod,UsrCod))"); + /***** Table mch_indexes *****/ +/* +mysql> DESCRIBE mch_indexes; ++---------+---------+------+-----+---------+-------+ +| Field | Type | Null | Key | Default | Extra | ++---------+---------+------+-----+---------+-------+ +| MchCod | int(11) | NO | PRI | NULL | | +| QstInd | int(11) | NO | PRI | NULL | | +| Indexes | text | NO | | NULL | | ++---------+---------+------+-----+---------+-------+ +3 rows in set (0.00 sec) +*/ + DB_CreateTable ("CREATE TABLE IF NOT EXISTS mch_indexes (" + "MchCod INT NOT NULL," + "QstInd INT NOT NULL," + "Indexes TEXT NOT NULL," // Tst_MAX_BYTES_INDEXES_ONE_QST + "UNIQUE INDEX(MchCod,QstInd))"); + /***** Table mch_results *****/ /* mysql> DESCRIBE mch_results; diff --git a/swad_mark.c b/swad_mark.c index 9e00dd37c..b02fc4f08 100644 --- a/swad_mark.c +++ b/swad_mark.c @@ -41,6 +41,7 @@ #include "swad_notification.h" #include "swad_parameter.h" #include "swad_string.h" +#include "swad_table.h" #include "swad_user.h" /*****************************************************************************/ diff --git a/swad_match.c b/swad_match.c index d282ec85a..7e2c946be 100644 --- a/swad_match.c +++ b/swad_match.c @@ -191,8 +191,8 @@ static bool Mch_GetIfMatchIsBeingPlayed (long MchCod); static void Mch_RegisterMeAsPlayerInMatch (long MchCod); static void Mch_GetNumPlayers (struct Match *Match); -static int Mch_GetQstAnsFromDB (long MchCod,unsigned QstInd); -static void Mch_ComputeScore (struct Match *Match,unsigned NumQsts, +static int Mch_GetQstAnsFromDB (long MchCod,long UsrCod,unsigned QstInd); +static void Mch_ComputeScore (struct Match *Match,unsigned NumQsts,long UsrCod, unsigned *NumQstsNotBlank,double *TotalScore); static unsigned Mch_GetNumUsrsWhoHaveChosenAns (long MchCod,unsigned QstInd,unsigned AnsInd); @@ -201,6 +201,7 @@ static void Mch_DrawBarNumUsrs (unsigned NumAnswerersAns,unsigned NumAnswerersQs static void Mch_ShowHeaderMchResults (void); static void Mch_ShowMchResults (Usr_MeOrOther_t MeOrOther); +static void Mch_GetMatchResultQuestionsFromDB (long MchCod,long UsrCod); static void Mch_GetMatchResultDataByMchCod (long MchCod,long UsrCod, time_t TimeUTC[Dat_NUM_START_END_TIME], unsigned *NumQsts, @@ -1888,6 +1889,7 @@ static void Mch_ShowQuestionAndAnswersStd (struct Match *Match) /***** Get student's answer to this question (<0 ==> no answer) *****/ StdAnsInd = Mch_GetQstAnsFromDB (Match->MchCod, + Gbl.Usrs.Me.UsrDat.UsrCod, Match->Status.QstInd); /***** Get number of options in this question *****/ @@ -2240,32 +2242,32 @@ void Mch_RefreshMatchStd (void) /**** Receive previous question answer in a match question from database *****/ /*****************************************************************************/ -static int Mch_GetQstAnsFromDB (long MchCod,unsigned QstInd) +static int Mch_GetQstAnsFromDB (long MchCod,long UsrCod,unsigned QstInd) { MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRows; - int StdAnsInd = -1; // <0 ==> no answer selected + int AnsInd = -1; // <0 ==> no answer selected /***** Get student's answer *****/ NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get student's answer to a match question", "SELECT AnsInd FROM mch_answers" - " WHERE MchCod=%ld AND UsrCod=%ld AND QstInd=%u", - MchCod, - Gbl.Usrs.Me.UsrDat.UsrCod, - QstInd); + " WHERE MchCod=%ld" + " AND UsrCod=%ld" + " AND QstInd=%u", + MchCod,UsrCod,QstInd); if (NumRows) // Answer found... { /***** Get answer index *****/ row = mysql_fetch_row (mysql_res); - if (sscanf (row[0],"%d",&StdAnsInd) != 1) + if (sscanf (row[0],"%d",&AnsInd) != 1) Lay_ShowErrorAndExit ("Error when getting student's answer to a match question."); } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); - return StdAnsInd; + return AnsInd; } /*****************************************************************************/ @@ -2311,7 +2313,7 @@ void Mch_ReceiveQstAnsFromStd (void) /***** Get previous student's answer to this question (<0 ==> no answer) *****/ - PreviousStdAnsInd = Mch_GetQstAnsFromDB (Match.MchCod,QstInd); + PreviousStdAnsInd = Mch_GetQstAnsFromDB (Match.MchCod,Gbl.Usrs.Me.UsrDat.UsrCod,QstInd); /***** Store student's answer *****/ if (PreviousStdAnsInd == (int) StdAnsInd) @@ -2329,7 +2331,8 @@ void Mch_ReceiveQstAnsFromStd (void) /***** Update student's match result *****/ NumQsts = Gam_GetNumQstsGame (Match.GamCod); - Mch_ComputeScore (&Match,NumQsts,&NumQstsNotBlank,&TotalScore); + Mch_ComputeScore (&Match,NumQsts,Gbl.Usrs.Me.UsrDat.UsrCod, + &NumQstsNotBlank,&TotalScore); Str_SetDecimalPointToUS (); // To print the floating point as a dot if (DB_QueryCOUNT ("can not get if match result exists", @@ -2374,113 +2377,73 @@ void Mch_ReceiveQstAnsFromStd (void) /******************** Compute match score for a student **********************/ /*****************************************************************************/ -static void Mch_ComputeScore (struct Match *Match,unsigned NumQsts, +static void Mch_ComputeScore (struct Match *Match,unsigned NumQsts,long UsrCod, unsigned *NumQstsNotBlank,double *TotalScore) { MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumQst; - unsigned NumQstNotBlank; unsigned QstInd; - unsigned NumOpt; - long QstCod; - long LongNum; double ScoreThisQst; bool AnswerIsNotBlank; - struct UsrAnswer - { - unsigned QstInd; - unsigned AnsInd; - } *UsrAnswers; + long LongNum; + int AnsInd; unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]; // Indexes of all answers of this question bool AnswersUsr[Tst_MAX_OPTIONS_PER_QUESTION]; - /***** Get user's answers *****/ - /* Query database */ - *NumQstsNotBlank = (unsigned) - DB_QuerySELECT (&mysql_res,"can not get user's answers", - "SELECT QstInd," // row[0] - "AnsInd" // row[1] - " FROM mch_answers" - " WHERE MchCod=%ld AND UsrCod=%ld", - Match->MchCod,Gbl.Usrs.Me.UsrDat.UsrCod); - - /* Allocate memory for answers */ - if ((UsrAnswers = (struct UsrAnswer *) malloc (*NumQstsNotBlank * - sizeof (struct UsrAnswer))) == NULL) - Lay_NotEnoughMemoryExit (); - - /* Get answers from database */ - for (NumQstNotBlank = 0; - NumQstNotBlank < *NumQstsNotBlank; - NumQstNotBlank++) - { - row = mysql_fetch_row (mysql_res); - - /* Get question index (row[0]) */ - if ((LongNum = Str_ConvertStrCodToLongCod (row[0])) < 0) - Lay_ShowErrorAndExit ("Wrong question index."); - UsrAnswers[NumQstNotBlank].QstInd = (unsigned) LongNum; - - /* Get answer index (row[1]) */ - if ((LongNum = Str_ConvertStrCodToLongCod (row[1])) < 0) - Lay_ShowErrorAndExit ("Wrong answer index."); - UsrAnswers[NumQstNotBlank].AnsInd = (unsigned) LongNum; - } - - /* Free structure that stores the query result */ - DB_FreeMySQLResult (&mysql_res); + /***** Get questions and answers of a match result *****/ + NumQsts = (unsigned) + DB_QuerySELECT (&mysql_res,"can not get questions and answers" + " of a match result", + "SELECT gam_questions.QstCod," // row[0] + "gam_questions.QstInd," // row[1] + "mch_indexes.Indexes" // row[2] + " FROM mch_matches,gam_questions,mch_questions" + " WHERE mch_matches.MchCod=%ld" + " AND mch_matches.GamCod=gam_questions.GamCod" + " AND mch_matches.MchCod=mch_indexes.MchCod" + " AND mch_matches.QstInd=mch_indexes.QstInd" + " ORDER BY gam_questions.QstInd"); /***** For each question in match... *****/ - for (NumQst = 0, *TotalScore = 0.0; + for (NumQst = 0, *NumQstsNotBlank = 0, *TotalScore = 0.0; NumQst < NumQsts; NumQst++) { - QstInd = NumQst + 1; + row = mysql_fetch_row (mysql_res); - /***** Get question code *****/ - QstCod = Gam_GetQstCodFromQstInd (Match->GamCod,QstInd); + /* Get question code (row[0]) */ + if ((Gbl.Test.QstCodes[NumQst] = Str_ConvertStrCodToLongCod (row[0])) < 0) + Lay_ShowErrorAndExit ("Wrong code of question."); - /***** Get answers of test question from database *****/ - /* Query database */ - Gbl.Test.Answer.NumOptions = (unsigned) - DB_QuerySELECT (&mysql_res,"can not get answers of a question", - "SELECT Correct" // row[0] - " FROM tst_answers" - " WHERE QstCod=%ld ORDER BY AnsInd", - QstCod); - for (NumOpt = 0; - NumOpt < Gbl.Test.Answer.NumOptions; - NumOpt++) + /* Get question index (row[1]) */ + if ((LongNum = Str_ConvertStrCodToLongCod (row[0])) < 0) + Lay_ShowErrorAndExit ("Wrong code of question."); + QstInd = (unsigned) LongNum; + + /* Get indexes for this question (row[2]) */ + Str_Copy (Gbl.Test.StrIndexesOneQst[NumQst],row[1], + Tst_MAX_BYTES_INDEXES_ONE_QST); + + /***** Get answers selected by user for this question *****/ + AnsInd = Mch_GetQstAnsFromDB (Match->MchCod,UsrCod,QstInd); + if (AnsInd >= 0) // AnsInd >= 0 ==> answer selected { - /* Get next answer */ - row = mysql_fetch_row (mysql_res); - - /* Assign correctness (row[0]) of this answer (this option) */ - Gbl.Test.Answer.Options[NumOpt].Correct = (row[0][0] == 'Y'); + snprintf (Gbl.Test.StrAnswersOneQst[NumQst],Tst_MAX_BYTES_ANSWERS_ONE_QST + 1, + "%d",AnsInd); + (*NumQstsNotBlank)++; } + else // AnsInd < 0 ==> no answer selected + Gbl.Test.StrAnswersOneQst[NumQst][0] = '\0'; // Empty answer - /* Free structure that stores the query result */ - DB_FreeMySQLResult (&mysql_res); + /***** Get indexes for this question from string *****/ + Tst_GetIndexesFromStr (Gbl.Test.StrIndexesOneQst[NumQst],Indexes); - /***** Get indexes for this question *****/ - // TODO: Answers should be shuffled? - for (NumOpt = 0; - NumOpt < Gbl.Test.Answer.NumOptions; - NumOpt++) - Indexes[NumOpt] = NumOpt; + /***** Get the user's answers for this question from string *****/ + Tst_GetAnswersFromStr (Gbl.Test.StrAnswersOneQst[NumQst],AnswersUsr); - /***** Get the user's answers for this question *****/ - for (NumOpt = 0; - NumOpt < Gbl.Test.Answer.NumOptions; - NumOpt++) - AnswersUsr[NumOpt] = false; - for (NumQstNotBlank = 0; - NumQstNotBlank < *NumQstsNotBlank; - NumQstNotBlank++) - if (UsrAnswers[NumQstNotBlank].QstInd == QstInd) - if (UsrAnswers[NumQstNotBlank].AnsInd < Gbl.Test.Answer.NumOptions) - AnswersUsr[UsrAnswers[NumQstNotBlank].AnsInd] = true; + /***** Get correct answers of test question from database *****/ + Tst_GetCorrectAnswersFromDB (Gbl.Test.QstCodes[NumQst]); /***** Compute the total score of this question *****/ Tst_ComputeScoreQst (Indexes,AnswersUsr,&ScoreThisQst,&AnswerIsNotBlank); @@ -3068,21 +3031,30 @@ void Mch_ShowOneMchResult (void) extern const char *Txt_Score; extern const char *Txt_out_of_PART_OF_A_SCORE; long MchCod; + Usr_MeOrOther_t MeOrOther = (Gbl.Action.Act == ActSeeOneMchResMe) ? Usr_ME : + Usr_OTHER; time_t TimeUTC[Dat_NUM_START_END_TIME]; // Match result UTC date-time unsigned NumQstsNotBlank; double TotalScore; bool ShowPhoto; char PhotoURL[PATH_MAX + 1]; bool ItsMe; - bool ICanViewTest; + bool ICanViewResult; bool ICanViewScore; - /***** Get the code of the test *****/ + /***** Get form parameters *****/ + /* Get match code */ if ((MchCod = Mch_GetParamMchCod ()) == -1L) Lay_ShowErrorAndExit ("Code of match is missing."); + /* Get user's code */ + if (MeOrOther == Usr_OTHER) + Usr_GetParamOtherUsrCodEncrypted (&Gbl.Usrs.Other.UsrDat); + /***** Get test result data *****/ - Mch_GetMatchResultDataByMchCod (MchCod,Gbl.Usrs.Other.UsrDat.UsrCod, + Mch_GetMatchResultDataByMchCod (MchCod, + MeOrOther == Usr_ME ? Gbl.Usrs.Me.UsrDat.UsrCod : + Gbl.Usrs.Other.UsrDat.UsrCod, TimeUTC, &Gbl.Test.NumQsts, &NumQstsNotBlank, @@ -3094,7 +3066,7 @@ void Mch_ShowOneMchResult (void) switch (Gbl.Usrs.Me.Role.Logged) { case Rol_STD: - ICanViewTest = ItsMe; + ICanViewResult = ItsMe; if (ItsMe) { Tst_GetConfigTstFromDB (); // To get feedback type @@ -3110,34 +3082,35 @@ void Mch_ShowOneMchResult (void) switch (Gbl.Action.Act) { case ActSeeOneTstResMe: - ICanViewTest = - ICanViewScore = ItsMe; + ICanViewResult = + ICanViewScore = ItsMe; break; case ActSeeOneTstResOth: - ICanViewTest = - ICanViewScore = ItsMe || - Gbl.Test.AllowTeachers; + ICanViewResult = + ICanViewScore = true; break; default: - ICanViewTest = - ICanViewScore = false; + ICanViewResult = + ICanViewScore = false; break; } break; case Rol_SYS_ADM: - ICanViewTest = - ICanViewScore = true; + ICanViewResult = + ICanViewScore = true; break; default: - ICanViewTest = - ICanViewScore = false; + ICanViewResult = + ICanViewScore = false; break; } - if (ICanViewTest) // I am allowed to view this test result + if (ICanViewResult) // I am allowed to view this match result { - /***** Get questions and user's answers of the test result from database *****/ - // Tst_GetTestResultQuestionsFromDB (TstCod); // TODO: Change to matches results + /***** Get questions and user's answers of the match result from database *****/ + Mch_GetMatchResultQuestionsFromDB (MchCod, + MeOrOther == Usr_ME ? Gbl.Usrs.Me.UsrDat.UsrCod : + Gbl.Usrs.Other.UsrDat.UsrCod); /***** Start box *****/ Box_StartBox (NULL,Txt_Match_result,NULL, @@ -3151,7 +3124,7 @@ void Mch_ShowOneMchResult (void) Tbl_StartTableWideMargin (10); /***** Header row *****/ - /* Get data of the user who made the test */ + /* Get data of the user who made the match */ if (!Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat,Usr_DONT_GET_PREFS)) Lay_ShowErrorAndExit ("User does not exists."); if (!Usr_CheckIfICanViewTst (&Gbl.Usrs.Other.UsrDat)) @@ -3181,7 +3154,7 @@ void Mch_ShowOneMchResult (void) fprintf (Gbl.F.Out,"" ""); - /* Test dates */ + /* Match dates */ fprintf (Gbl.F.Out,"" "" "%s:" @@ -3250,6 +3223,70 @@ void Mch_ShowOneMchResult (void) Lay_ShowErrorAndExit ("You can not view this match result."); } +/*****************************************************************************/ +/************ Get the questions of a match result from database **************/ +/*****************************************************************************/ + +static void Mch_GetMatchResultQuestionsFromDB (long MchCod,long UsrCod) + { + MYSQL_RES *mysql_res; + MYSQL_ROW row; + unsigned NumQsts; + unsigned NumQst; + long LongNum; + unsigned QstInd; + int AnsInd; + + /***** Get questions and answers of a match result *****/ + NumQsts = (unsigned) + DB_QuerySELECT (&mysql_res,"can not get questions and answers" + " of a match result", + "SELECT gam_questions.QstCod," // row[0] + "gam_questions.QstInd," // row[1] + "mch_indexes.Indexes" // row[2] + " FROM mch_matches,gam_questions,mch_questions" + " WHERE mch_matches.MchCod=%ld" + " AND mch_matches.GamCod=gam_questions.GamCod" + " AND mch_matches.MchCod=mch_indexes.MchCod" + " AND mch_matches.QstInd=mch_indexes.QstInd" + " ORDER BY gam_questions.QstInd"); + for (NumQst = 0; + NumQst < NumQsts; + NumQst++) + { + row = mysql_fetch_row (mysql_res); + + /* Get question code (row[0]) */ + if ((Gbl.Test.QstCodes[NumQst] = Str_ConvertStrCodToLongCod (row[0])) < 0) + Lay_ShowErrorAndExit ("Wrong code of question."); + + /* Get question index (row[1]) */ + if ((LongNum = Str_ConvertStrCodToLongCod (row[0])) < 0) + Lay_ShowErrorAndExit ("Wrong code of question."); + QstInd = (unsigned) LongNum; + + /* Get indexes for this question (row[2]) */ + Str_Copy (Gbl.Test.StrIndexesOneQst[NumQst],row[1], + Tst_MAX_BYTES_INDEXES_ONE_QST); + + /* Get answers selected by user for this question */ + AnsInd = Mch_GetQstAnsFromDB (MchCod,UsrCod,QstInd); + if (AnsInd >= 0) // AnsInd >= 0 ==> answer selected + snprintf (Gbl.Test.StrAnswersOneQst[NumQst],Tst_MAX_BYTES_ANSWERS_ONE_QST + 1, + "%d",AnsInd); + else // AnsInd < 0 ==> no answer selected + Gbl.Test.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]); + } + + /***** Free structure that stores the query result *****/ + DB_FreeMySQLResult (&mysql_res); + } + /*****************************************************************************/ /************* Get data of a match result using its match code ***************/ /*****************************************************************************/ diff --git a/swad_test.c b/swad_test.c index a3c456c35..6733cf4db 100644 --- a/swad_test.c +++ b/swad_test.c @@ -3285,7 +3285,8 @@ unsigned Tst_GetAnswersQst (long QstCod,MYSQL_RES **mysql_res,bool Shuffle) "MedCod," // row[3] "Correct" // row[4] " FROM tst_answers" - " WHERE QstCod=%ld ORDER BY %s", + " WHERE QstCod=%ld" + " ORDER BY %s", QstCod, Shuffle ? "RAND(NOW())" : "AnsInd"); @@ -3295,6 +3296,35 @@ unsigned Tst_GetAnswersQst (long QstCod,MYSQL_RES **mysql_res,bool Shuffle) return (unsigned) NumRows; } +void Tst_GetCorrectAnswersFromDB (long QstCod) + { + MYSQL_RES *mysql_res; + MYSQL_ROW row; + unsigned NumOpt; + + /***** Query database *****/ + Gbl.Test.Answer.NumOptions = (unsigned) + DB_QuerySELECT (&mysql_res,"can not get answers of a question", + "SELECT Correct" // row[0] + " FROM tst_answers" + " WHERE QstCod=%ld" + " ORDER BY AnsInd", + QstCod); + for (NumOpt = 0; + NumOpt < Gbl.Test.Answer.NumOptions; + NumOpt++) + { + /* Get next answer */ + row = mysql_fetch_row (mysql_res); + + /* Assign correctness (row[0]) of this answer (this option) */ + Gbl.Test.Answer.Options[NumOpt].Correct = (row[0][0] == 'Y'); + } + + /* Free structure that stores the query result */ + DB_FreeMySQLResult (&mysql_res); + } + /*****************************************************************************/ /**************** Get and write the answers of a test question ***************/ /*****************************************************************************/ @@ -3795,10 +3825,7 @@ static void Tst_WriteChoiceAnsAssessTest (struct UsrData *UsrDat, extern const char *Txt_TST_Answer_given_by_the_user; extern const char *Txt_TST_Answer_given_by_the_teachers; unsigned NumOpt; - char StrOneIndex[10 + 1]; - const char *Ptr; unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]; // Indexes of all answers of this question - int AnsUsr; bool AnswersUsr[Tst_MAX_OPTIONS_PER_QUESTION]; struct { @@ -3811,32 +3838,10 @@ static void Tst_WriteChoiceAnsAssessTest (struct UsrData *UsrDat, Tst_GetChoiceAns (mysql_res); /***** Get indexes for this question from string *****/ - for (NumOpt = 0, Ptr = Gbl.Test.StrIndexesOneQst[NumQst]; - NumOpt < Gbl.Test.Answer.NumOptions; - NumOpt++) - { - Par_GetNextStrUntilSeparParamMult (&Ptr,StrOneIndex,10); - if (sscanf (StrOneIndex,"%u",&(Indexes[NumOpt])) != 1) - Lay_ShowErrorAndExit ("Wrong index of answer when assessing a test."); - } + Tst_GetIndexesFromStr (Gbl.Test.StrIndexesOneQst[NumQst],Indexes); /***** Get the user's answers for this question from string *****/ - for (NumOpt = 0; - NumOpt < Tst_MAX_OPTIONS_PER_QUESTION; - NumOpt++) - AnswersUsr[NumOpt] = false; - for (NumOpt = 0, Ptr = Gbl.Test.StrAnswersOneQst[NumQst]; - NumOpt < Gbl.Test.Answer.NumOptions; - NumOpt++) - if (*Ptr) - { - Par_GetNextStrUntilSeparParamMult (&Ptr,StrOneIndex,10); - if (sscanf (StrOneIndex,"%d",&AnsUsr) != 1) - Lay_ShowErrorAndExit ("Bad user's answer."); - if (AnsUsr < 0 || AnsUsr >= Tst_MAX_OPTIONS_PER_QUESTION) - Lay_ShowErrorAndExit ("Bad user's answer."); - AnswersUsr[AnsUsr] = true; - } + Tst_GetAnswersFromStr (Gbl.Test.StrAnswersOneQst[NumQst],AnswersUsr); /***** Compute the total score of this question *****/ Tst_ComputeScoreQst (Indexes,AnswersUsr,ScoreThisQst,AnswerIsNotBlank); @@ -4003,6 +4008,57 @@ void Tst_GetChoiceAns (MYSQL_RES *mysql_res) } } +/*****************************************************************************/ +/********************* Get vector of indexes from string *********************/ +/*****************************************************************************/ + +void Tst_GetIndexesFromStr (const char StrIndexesOneQst[Tst_MAX_BYTES_INDEXES_ONE_QST + 1], // 0 1 2 3, 3 0 2 1, etc. + unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]) + { + unsigned NumOpt; + const char *Ptr; + char StrOneIndex[10 + 1]; + + for (NumOpt = 0, Ptr = StrIndexesOneQst; + NumOpt < Gbl.Test.Answer.NumOptions; + NumOpt++) + { + Par_GetNextStrUntilSeparParamMult (&Ptr,StrOneIndex,10); + if (sscanf (StrOneIndex,"%u",&(Indexes[NumOpt])) != 1) + Lay_ShowErrorAndExit ("Wrong index of answer when assessing a test."); + } + } + +/*****************************************************************************/ +/****************** Get vector of user's answers from string *****************/ +/*****************************************************************************/ + +void Tst_GetAnswersFromStr (const char StrAnswersOneQst[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1], + bool AnswersUsr[Tst_MAX_OPTIONS_PER_QUESTION]) + { + unsigned NumOpt; + const char *Ptr; + char StrOneAnswer[10 + 1]; + int AnsUsr; + + for (NumOpt = 0; + NumOpt < Tst_MAX_OPTIONS_PER_QUESTION; + NumOpt++) + AnswersUsr[NumOpt] = false; + for (NumOpt = 0, Ptr = StrAnswersOneQst; + NumOpt < Gbl.Test.Answer.NumOptions; + NumOpt++) + if (*Ptr) + { + Par_GetNextStrUntilSeparParamMult (&Ptr,StrOneAnswer,10); + if (sscanf (StrOneAnswer,"%d",&AnsUsr) != 1) + Lay_ShowErrorAndExit ("Bad user's answer."); + if (AnsUsr < 0 || AnsUsr >= Tst_MAX_OPTIONS_PER_QUESTION) + Lay_ShowErrorAndExit ("Bad user's answer."); + AnswersUsr[AnsUsr] = true; + } + } + /*****************************************************************************/ /********************* Compute the score of this question ********************/ /*****************************************************************************/ diff --git a/swad_test.h b/swad_test.h index 20ed39119..86ed5a0b1 100644 --- a/swad_test.h +++ b/swad_test.h @@ -154,11 +154,16 @@ bool Tst_GetOneQuestionByCod (long QstCod,MYSQL_RES **mysql_res); void Tst_WriteParamEditQst (void); unsigned Tst_GetNumAnswersQst (long QstCod); unsigned Tst_GetAnswersQst (long QstCod,MYSQL_RES **mysql_res,bool Shuffle); +void Tst_GetCorrectAnswersFromDB (long QstCod); void Tst_WriteAnswersMatchResult (long MchCod,unsigned QstInd,long QstCod, const char *Class,bool ShowResult); bool Tst_CheckIfQuestionIsValidForGame (long QstCod); void Tst_WriteAnsTF (char AnsTF); void Tst_GetChoiceAns (MYSQL_RES *mysql_res); +void Tst_GetIndexesFromStr (const char StrIndexesOneQst[Tst_MAX_BYTES_INDEXES_ONE_QST + 1], // 0 1 2 3, 3 0 2 1, etc. + unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]); +void Tst_GetAnswersFromStr (const char StrAnswersOneQst[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1], + bool AnswersUsr[Tst_MAX_OPTIONS_PER_QUESTION]); void Tst_ComputeScoreQst (unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION], bool AnswersUsr[Tst_MAX_OPTIONS_PER_QUESTION], double *ScoreThisQst,bool *AnswerIsNotBlank);