From d38b53b19656d0513a144f229984973a9f1af9ad Mon Sep 17 00:00:00 2001 From: acanas Date: Tue, 26 Oct 2021 17:33:20 +0200 Subject: [PATCH] Version 21.46.1: Oct 26, 2021 Queries moved to module swad_test_database. --- swad_changelog.h | 3 +- swad_course.c | 8 +- swad_error.c | 9 ++ swad_error.h | 1 + swad_exam_result.c | 6 +- swad_exam_set.c | 2 +- swad_game.c | 2 +- swad_match_result.c | 6 +- swad_question.c | 48 +++++++- swad_question.h | 3 + swad_statistic_database.c | 2 +- swad_tag.c | 21 ++++ swad_tag.h | 2 + swad_test.c | 233 +++----------------------------------- swad_test.h | 4 - swad_test_config.c | 4 +- swad_test_config.h | 2 +- swad_test_database.c | 129 ++++++++++++++++++++- swad_test_database.h | 5 + swad_test_print.c | 26 ++++- swad_test_print.h | 2 + 21 files changed, 274 insertions(+), 244 deletions(-) diff --git a/swad_changelog.h b/swad_changelog.h index b82edc93..aafec1a8 100644 --- a/swad_changelog.h +++ b/swad_changelog.h @@ -602,13 +602,14 @@ TODO: FIX BUG, URGENT! En las fechas como par TODO: En las encuestas, que los estudiantes no puedan ver los resultados hasta que no finalice el plazo. */ -#define Log_PLATFORM_VERSION "SWAD 21.46 (2021-10-26)" +#define Log_PLATFORM_VERSION "SWAD 21.46.1 (2021-10-26)" #define CSS_FILE "swad20.45.css" #define JS_FILE "swad20.69.1.js" /* TODO: Rename CENTRE to CENTER in help wiki. TODO: Rename ASSESSMENT.Announcements to ASSESSMENT.Calls_for_exams + Version 21.46.1: Oct 26, 2021 Queries moved to module swad_test_database. (? lines) Version 21.46: Oct 26, 2021 New module swad_test_database for database queries related to self-assessment tests. (321036 lines) Version 21.45.1: Oct 25, 2021 Code refactoring in test questions. (320932 lines) Version 21.45: Oct 25, 2021 Functions moved to module swad_question. (320930 lines) diff --git a/swad_course.c b/swad_course.c index 85d315ce..e36ffe40 100644 --- a/swad_course.c +++ b/swad_course.c @@ -63,6 +63,7 @@ #include "swad_setting_database.h" #include "swad_survey.h" #include "swad_test.h" +#include "swad_test_database.h" /*****************************************************************************/ /************** External global variables from others modules ****************/ @@ -1838,8 +1839,11 @@ static void Crs_EmptyCourseCompletely (long CrsCod) /***** Remove all exams in the course *****/ Exa_RemoveCrsExams (CrsCod); - /***** Remove all tests in the course *****/ - Tst_RemoveCrsTests (CrsCod); + /***** Remove all test prints made in the course *****/ + TstPrn_RemoveCrsPrints (CrsCod); + + /***** Remove test configuration of the course *****/ + Tst_DB_RemoveTstConfig (CrsCod); /***** Remove all questions in the course *****/ Qst_RemoveCrsQsts (CrsCod); diff --git a/swad_error.c b/swad_error.c index 525eb102..4a8ce397 100644 --- a/swad_error.c +++ b/swad_error.c @@ -65,6 +65,15 @@ void Err_NotEnoughMemoryExit (void) Err_ShowErrorAndExit ("Not enough memory."); } +/*****************************************************************************/ +/********** Write error message and exit when not space for query ************/ +/*****************************************************************************/ + +void Err_QuerySizeExceededExit (void) + { + Err_ShowErrorAndExit ("Query size exceeded."); + } + /*****************************************************************************/ /************* Write error message and exit when wrong action ****************/ /*****************************************************************************/ diff --git a/swad_error.h b/swad_error.h index 097cf9a5..76658768 100644 --- a/swad_error.h +++ b/swad_error.h @@ -40,6 +40,7 @@ /*****************************************************************************/ void Err_NotEnoughMemoryExit (void); +void Err_QuerySizeExceededExit (void); void Err_WrongActionExit (void); void Err_WrongScopeExit (void); void Err_WrongCountrExit (void); diff --git a/swad_exam_result.c b/swad_exam_result.c index 49ec2af4..3954769b 100644 --- a/swad_exam_result.c +++ b/swad_exam_result.c @@ -181,7 +181,7 @@ static void ExaRes_ListMyResultsInCrs (struct Exa_Exams *Exams) ExaRes_ShowHeaderResults (Usr_ME); /***** List my sessions results in the current course *****/ - TstCfg_GetConfigFromDB (); // Get feedback type + TstCfg_GetConfig (); // Get feedback type ExaRes_BuildExamsSelectedCommas (Exams,&ExamsSelectedCommas); ExaRes_ShowResults (Exams,Usr_ME,-1L,-1L,ExamsSelectedCommas); free (ExamsSelectedCommas); @@ -235,7 +235,7 @@ static void ExaRes_ListMyResultsInExa (struct Exa_Exams *Exams,long ExaCod) ExaRes_ShowHeaderResults (Usr_ME); /***** List my sessions results in exam *****/ - TstCfg_GetConfigFromDB (); // Get feedback type + TstCfg_GetConfig (); // Get feedback type ExaRes_ShowResults (Exams,Usr_ME,-1L,ExaCod,NULL); } @@ -287,7 +287,7 @@ static void ExaRes_ListMyResultsInSes (struct Exa_Exams *Exams,long SesCod) ExaRes_ShowHeaderResults (Usr_ME); /***** List my sessions results in exam *****/ - TstCfg_GetConfigFromDB (); // Get feedback type + TstCfg_GetConfig (); // Get feedback type ExaRes_ShowResults (Exams,Usr_ME,SesCod,-1L,NULL); } diff --git a/swad_exam_set.c b/swad_exam_set.c index 3095ce89..7d1bd26f 100644 --- a/swad_exam_set.c +++ b/swad_exam_set.c @@ -1274,7 +1274,7 @@ void ExaSet_AddQstsToSet (void) ExaSet_MAX_BYTES_LIST_SELECTED_QUESTIONS); /* Check number of questions */ - if (Tst_CountNumQuestionsInList (Exams.ListQuestions)) // If questions selected... + if (Qst_CountNumQuestionsInList (Exams.ListQuestions)) // If questions selected... { /***** Insert questions in database *****/ Ptr = Exams.ListQuestions; diff --git a/swad_game.c b/swad_game.c index 0b4bbf9e..fe4a24e1 100644 --- a/swad_game.c +++ b/swad_game.c @@ -1897,7 +1897,7 @@ void Gam_AddQstsToGame (void) /* Check number of questions */ NumQstsAdded = 0; - if (Tst_CountNumQuestionsInList (Games.ListQuestions)) // If questions selected... + if (Qst_CountNumQuestionsInList (Games.ListQuestions)) // If questions selected... { /***** Insert questions in database *****/ Ptr = Games.ListQuestions; diff --git a/swad_match_result.c b/swad_match_result.c index 4790d8dd..6ae860e3 100644 --- a/swad_match_result.c +++ b/swad_match_result.c @@ -147,7 +147,7 @@ static void MchRes_ListMyMchResultsInCrs (struct Gam_Games *Games) MchRes_ShowHeaderMchResults (Usr_ME); /***** List my matches results in the current course *****/ - TstCfg_GetConfigFromDB (); // Get feedback type + TstCfg_GetConfig (); // Get feedback type MchRes_BuildGamesSelectedCommas (Games,&GamesSelectedCommas); MchRes_ShowMchResults (Games,Usr_ME,-1L,-1L,GamesSelectedCommas); free (GamesSelectedCommas); @@ -197,7 +197,7 @@ static void MchRes_ListMyMchResultsInGam (struct Gam_Games *Games,long GamCod) MchRes_ShowHeaderMchResults (Usr_ME); /***** List my matches results in game *****/ - TstCfg_GetConfigFromDB (); // Get feedback type + TstCfg_GetConfig (); // Get feedback type MchRes_ShowMchResults (Games,Usr_ME,-1L,GamCod,NULL); } @@ -249,7 +249,7 @@ static void MchRes_ListMyMchResultsInMch (struct Gam_Games *Games,long MchCod) MchRes_ShowHeaderMchResults (Usr_ME); /***** List my matches results in game *****/ - TstCfg_GetConfigFromDB (); // Get feedback type + TstCfg_GetConfig (); // Get feedback type MchRes_ShowMchResults (Games,Usr_ME,MchCod,-1L,NULL); } diff --git a/swad_question.c b/swad_question.c index 4ca05416..00c79541 100644 --- a/swad_question.c +++ b/swad_question.c @@ -1692,7 +1692,7 @@ void Qst_GetQuestions (struct Qst_Questions *Questions,MYSQL_RES **mysql_res) Par_GetNextStrUntilSeparParamMult (&Ptr,TagText,Tag_MAX_BYTES_TAG); LengthQuery = LengthQuery + 35 + strlen (TagText) + 1; if (LengthQuery > Qst_MAX_BYTES_QUERY_QUESTIONS - 256) - Err_ShowErrorAndExit ("Query size exceed."); + Err_QuerySizeExceededExit (); Str_Concat (Query, NumItemInList ? " OR tst_tags.TagTxt='" : " AND (tst_tags.TagTxt='", @@ -1716,7 +1716,7 @@ void Qst_GetQuestions (struct Qst_Questions *Questions,MYSQL_RES **mysql_res) AnsType = Qst_ConvertFromUnsignedStrToAnsTyp (UnsignedStr); LengthQuery = LengthQuery + 35 + strlen (Qst_DB_StrAnswerTypes[AnsType]) + 1; if (LengthQuery > Qst_MAX_BYTES_QUERY_QUESTIONS - 256) - Err_ShowErrorAndExit ("Query size exceed."); + Err_QuerySizeExceededExit (); Str_Concat (Query, NumItemInList ? " OR tst_questions.AnsType='" : " AND (tst_questions.AnsType='", @@ -3757,6 +3757,50 @@ void Qst_InsertAnswersIntoDB (struct Qst_Question *Question) } } +/*****************************************************************************/ +/**** Count the number of types of answers in the list of types of answers ***/ +/*****************************************************************************/ + +unsigned Qst_CountNumAnswerTypesInList (const struct Qst_AnswerTypes *AnswerTypes) + { + const char *Ptr; + unsigned NumAnsTypes = 0; + char UnsignedStr[Cns_MAX_DECIMAL_DIGITS_UINT + 1]; + + /***** Go over the list of answer types counting the number of types of answer *****/ + Ptr = AnswerTypes->List; + while (*Ptr) + { + Par_GetNextStrUntilSeparParamMult (&Ptr,UnsignedStr,Cns_MAX_DECIMAL_DIGITS_UINT); + Qst_ConvertFromUnsignedStrToAnsTyp (UnsignedStr); + NumAnsTypes++; + } + return NumAnsTypes; + } + +/*****************************************************************************/ +/**** Count the number of questions in the list of selected question codes ***/ +/*****************************************************************************/ + +unsigned Qst_CountNumQuestionsInList (const char *ListQuestions) + { + const char *Ptr; + unsigned NumQuestions = 0; + char LongStr[Cns_MAX_DECIMAL_DIGITS_LONG + 1]; + long QstCod; + + /***** Go over list of questions counting the number of questions *****/ + Ptr = ListQuestions; + while (*Ptr) + { + Par_GetNextStrUntilSeparParamMult (&Ptr,LongStr,Cns_MAX_DECIMAL_DIGITS_LONG); + if (sscanf (LongStr,"%ld",&QstCod) != 1) + Err_WrongQuestionExit (); + NumQuestions++; + } + return NumQuestions; + } + /*****************************************************************************/ /********************* Remove all questions in a course **********************/ /*****************************************************************************/ diff --git a/swad_question.h b/swad_question.h index 1c2fc68c..859737c3 100644 --- a/swad_question.h +++ b/swad_question.h @@ -268,6 +268,9 @@ void Qst_InsertOrUpdateQstTagsAnsIntoDB (struct Qst_Question *Question); void Qst_InsertOrUpdateQstIntoDB (struct Qst_Question *Question); void Qst_InsertAnswersIntoDB (struct Qst_Question *Question); +unsigned Qst_CountNumAnswerTypesInList (const struct Qst_AnswerTypes *AnswerTypes); +unsigned Qst_CountNumQuestionsInList (const char *ListQuestions); + void Qst_RemoveCrsQsts (long CrsCod); void Qst_DB_RemAnsFromQst (long QstCod); void Qst_RemoveMediaFromStemOfQst (long CrsCod,long QstCod); diff --git a/swad_statistic_database.c b/swad_statistic_database.c index 472f2af5..2ff94cd4 100644 --- a/swad_statistic_database.c +++ b/swad_statistic_database.c @@ -430,7 +430,7 @@ unsigned Sta_DB_GetHits (MYSQL_RES **mysql_res, { LengthQuery = LengthQuery + 25 + 10 + 1; if (LengthQuery > Sta_DB_MAX_BYTES_QUERY - 128) - Err_ShowErrorAndExit ("Query is too large."); + Err_QuerySizeExceededExit (); sprintf (SubQuery, NumUsr ? " OR %s.UsrCod=%ld" : " AND (%s.UsrCod=%ld", diff --git a/swad_tag.c b/swad_tag.c index fba3effe..493a4816 100644 --- a/swad_tag.c +++ b/swad_tag.c @@ -277,6 +277,27 @@ void Tag_InsertTagsIntoDB (long QstCod,const struct Tag_Tags *Tags) } } +/*****************************************************************************/ +/***************** Count number of tags in the list of tags ******************/ +/*****************************************************************************/ + +unsigned Tag_CountNumTagsInList (const struct Tag_Tags *Tags) + { + const char *Ptr; + unsigned NumTags = 0; + char TagText[Tag_MAX_BYTES_TAG + 1]; + + /***** Go over the list of tags counting the number of tags *****/ + Ptr = Tags->List; + while (*Ptr) + { + Par_GetNextStrUntilSeparParamMult (&Ptr,TagText,Tag_MAX_BYTES_TAG); + NumTags++; + } + + return NumTags; + } + /*****************************************************************************/ /********************* Show a form to select test tags ***********************/ /*****************************************************************************/ diff --git a/swad_tag.h b/swad_tag.h index daa11b4e..eb07a0a9 100644 --- a/swad_tag.h +++ b/swad_tag.h @@ -67,6 +67,8 @@ void Tag_RenameTag (void); void Tag_InsertTagsIntoDB (long QstCod,const struct Tag_Tags *Tags); +unsigned Tag_CountNumTagsInList (const struct Tag_Tags *Tags); + void Tag_ShowFormSelTags (const struct Tag_Tags *Tags, MYSQL_RES *mysql_res, bool ShowOnlyEnabledTags); diff --git a/swad_test.c b/swad_test.c index e3b93422..138abe18 100644 --- a/swad_test.c +++ b/swad_test.c @@ -92,19 +92,15 @@ extern struct Globals Gbl; static void Tst_ShowFormRequestTest (struct Qst_Questions *Questions); -static void TstPrn_GetAnswersFromForm (struct TstPrn_Print *Print); - static bool Tst_CheckIfNextTstAllowed (void); -static void Tst_GetQuestionsForNewTestFromDB (struct Qst_Questions *Questions, +static void Tst_GetQuestionsForNewTest (struct Qst_Questions *Questions, struct TstPrn_Print *Print); static void Tst_GenerateChoiceIndexes (struct TstPrn_PrintedQuestion *PrintedQuestion, bool Shuffle); static unsigned Tst_GetParamNumTst (void); static unsigned Tst_GetParamNumQsts (void); -static unsigned Tst_CountNumTagsInList (const struct Tag_Tags *Tags); -static int Tst_CountNumAnswerTypesInList (const struct Qst_AnswerTypes *AnswerTypes); /*****************************************************************************/ /********************* Request a self-assessment test ************************/ @@ -114,13 +110,13 @@ void Tst_RequestTest (void) { struct Qst_Questions Questions; - /***** Create test *****/ + /***** Create questions *****/ Qst_Constructor (&Questions); /***** Show form to generate a self-assessment test *****/ Tst_ShowFormRequestTest (&Questions); - /***** Destroy test *****/ + /***** Destroy questions *****/ Qst_Destructor (&Questions); } @@ -138,7 +134,7 @@ static void Tst_ShowFormRequestTest (struct Qst_Questions *Questions) MYSQL_RES *mysql_res; /***** Read test configuration from database *****/ - TstCfg_GetConfigFromDB (); + TstCfg_GetConfig (); /***** Begin box *****/ Box_BoxBegin (NULL,Txt_Test, @@ -219,7 +215,7 @@ void Tst_ShowNewTest (void) Qst_Constructor (&Questions); /***** Read test configuration from database *****/ - TstCfg_GetConfigFromDB (); + TstCfg_GetConfig (); if (Tst_CheckIfNextTstAllowed ()) { @@ -228,7 +224,7 @@ void Tst_ShowNewTest (void) { /***** Get questions *****/ TstPrn_ResetPrint (&Print); - Tst_GetQuestionsForNewTestFromDB (&Questions,&Print); + Tst_GetQuestionsForNewTest (&Questions,&Print); if (Print.NumQsts.All) { /***** Increase number of exams generated (answered or not) by me *****/ @@ -273,7 +269,7 @@ void Tst_ReceiveTestDraft (void) struct TstPrn_Print Print; /***** Read test configuration from database *****/ - TstCfg_GetConfigFromDB (); + TstCfg_GetConfig (); /***** Get basic parameters of the exam *****/ /* Get test print code from form */ @@ -329,7 +325,7 @@ void Tst_AssessTest (void) struct TstPrn_Print Print; /***** Read test configuration from database *****/ - TstCfg_GetConfigFromDB (); + TstCfg_GetConfig (); /***** Get basic parameters of the exam *****/ /* Get test print code from form */ @@ -401,28 +397,6 @@ void Tst_AssessTest (void) } } -/*****************************************************************************/ -/******** Get questions and answers from form to assess a test print *********/ -/*****************************************************************************/ - -static void TstPrn_GetAnswersFromForm (struct TstPrn_Print *Print) - { - unsigned QstInd; - char StrAns[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Ansxx...x" - - /***** Loop for every question getting user's answers *****/ - for (QstInd = 0; - QstInd < Print->NumQsts.All; - QstInd++) - { - /* Get answers selected by user for this question */ - snprintf (StrAns,sizeof (StrAns),"Ans%010u",QstInd); - Par_GetParMultiToText (StrAns,Print->PrintedQuestions[QstInd].StrAnswers, - Qst_MAX_BYTES_ANSWERS_ONE_QST); /* If answer type == T/F ==> " ", "T", "F"; if choice ==> "0", "2",... */ - Par_ReplaceSeparatorMultipleByComma (Print->PrintedQuestions[QstInd].StrAnswers); - } - } - /*****************************************************************************/ /************** Check minimum date-time of next access to test ***************/ /*****************************************************************************/ @@ -518,21 +492,13 @@ void Tst_PutIconsTests (__attribute__((unused)) void *Args) #define Tst_MAX_BYTES_QUERY_QUESTIONS (16 * 1024 - 1) -static void Tst_GetQuestionsForNewTestFromDB (struct Qst_Questions *Questions, - struct TstPrn_Print *Print) +static void Tst_GetQuestionsForNewTest (struct Qst_Questions *Questions, + struct TstPrn_Print *Print) { - extern const char *Qst_DB_StrAnswerTypes[Qst_NUM_ANS_TYPES]; MYSQL_RES *mysql_res; MYSQL_ROW row; - char *Query = NULL; - long LengthQuery; - unsigned NumItemInList; - const char *Ptr; - char TagText[Tag_MAX_BYTES_TAG + 1]; - char UnsignedStr[Cns_MAX_DECIMAL_DIGITS_UINT + 1]; Qst_AnswerType_t AnswerType; bool Shuffle; - char StrNumQsts[Cns_MAX_DECIMAL_DIGITS_UINT + 1]; unsigned QstInd; /***** Trivial check: number of questions *****/ @@ -540,96 +506,10 @@ static void Tst_GetQuestionsForNewTestFromDB (struct Qst_Questions *Questions, Questions->NumQsts > TstCfg_MAX_QUESTIONS_PER_TEST) Err_ShowErrorAndExit ("Wrong number of questions."); - /***** Allocate space for query *****/ - if ((Query = malloc (Tst_MAX_BYTES_QUERY_QUESTIONS + 1)) == NULL) - Err_NotEnoughMemoryExit (); - - /***** Select questions without hidden tags *****/ - /* Begin query */ - // Reject questions with any tag hidden - // Select only questions with tags - // DISTINCTROW is necessary to not repeat questions - snprintf (Query,Tst_MAX_BYTES_QUERY_QUESTIONS + 1, - "SELECT DISTINCTROW tst_questions.QstCod," // row[0] - "tst_questions.AnsType," // row[1] - "tst_questions.Shuffle" // row[2] - " FROM tst_questions,tst_question_tags,tst_tags" - " WHERE tst_questions.CrsCod=%ld" - " AND tst_questions.QstCod NOT IN" - " (SELECT tst_question_tags.QstCod" - " FROM tst_tags,tst_question_tags" - " WHERE tst_tags.CrsCod=%ld" - " AND tst_tags.TagHidden='Y'" - " AND tst_tags.TagCod=tst_question_tags.TagCod)" - " AND tst_questions.QstCod=tst_question_tags.QstCod" - " AND tst_question_tags.TagCod=tst_tags.TagCod" - " AND tst_tags.CrsCod=%ld", - Gbl.Hierarchy.Crs.CrsCod, - Gbl.Hierarchy.Crs.CrsCod, - Gbl.Hierarchy.Crs.CrsCod); - - if (!Questions->Tags.All) // User has not selected all the tags - { - /* Add selected tags */ - LengthQuery = strlen (Query); - NumItemInList = 0; - Ptr = Questions->Tags.List; - while (*Ptr) - { - Par_GetNextStrUntilSeparParamMult (&Ptr,TagText,Tag_MAX_BYTES_TAG); - LengthQuery = LengthQuery + 35 + strlen (TagText) + 1; - if (LengthQuery > Tst_MAX_BYTES_QUERY_QUESTIONS - 128) - Err_ShowErrorAndExit ("Query size exceed."); - Str_Concat (Query, - NumItemInList ? " OR tst_tags.TagTxt='" : - " AND (tst_tags.TagTxt='", - Tst_MAX_BYTES_QUERY_QUESTIONS); - Str_Concat (Query,TagText,Tst_MAX_BYTES_QUERY_QUESTIONS); - Str_Concat (Query,"'",Tst_MAX_BYTES_QUERY_QUESTIONS); - NumItemInList++; - } - Str_Concat (Query,")",Tst_MAX_BYTES_QUERY_QUESTIONS); - } - - /* Add answer types selected */ - if (!Questions->AnswerTypes.All) - { - LengthQuery = strlen (Query); - NumItemInList = 0; - Ptr = Questions->AnswerTypes.List; - while (*Ptr) - { - Par_GetNextStrUntilSeparParamMult (&Ptr,UnsignedStr,Tag_MAX_BYTES_TAG); - AnswerType = Qst_ConvertFromUnsignedStrToAnsTyp (UnsignedStr); - LengthQuery = LengthQuery + 35 + strlen (Qst_DB_StrAnswerTypes[AnswerType]) + 1; - if (LengthQuery > Tst_MAX_BYTES_QUERY_QUESTIONS - 128) - Err_ShowErrorAndExit ("Query size exceed."); - Str_Concat (Query, - NumItemInList ? " OR tst_questions.AnsType='" : - " AND (tst_questions.AnsType='", - Tst_MAX_BYTES_QUERY_QUESTIONS); - Str_Concat (Query,Qst_DB_StrAnswerTypes[AnswerType],Tst_MAX_BYTES_QUERY_QUESTIONS); - Str_Concat (Query,"'",Tst_MAX_BYTES_QUERY_QUESTIONS); - NumItemInList++; - } - Str_Concat (Query,")",Tst_MAX_BYTES_QUERY_QUESTIONS); - } - - /* End query */ - Str_Concat (Query," ORDER BY RAND() LIMIT ",Tst_MAX_BYTES_QUERY_QUESTIONS); - snprintf (StrNumQsts,sizeof (StrNumQsts),"%u",Questions->NumQsts); - Str_Concat (Query,StrNumQsts,Tst_MAX_BYTES_QUERY_QUESTIONS); -/* - if (Gbl.Usrs.Me.Roles.LoggedRole == Rol_SYS_ADM) - Lay_ShowAlert (Lay_INFO,Query); -*/ - /* Make the query */ - Print->NumQsts.All = - Questions->NumQsts = (unsigned) DB_QuerySELECT (&mysql_res,"can not get questions", - "%s", - Query); - /***** Get questions and answers from database *****/ + Print->NumQsts.All = + Questions->NumQsts = Tst_DB_GetQuestionsForNewTest (&mysql_res,Questions); + for (QstInd = 0; QstInd < Print->NumQsts.All; QstInd++) @@ -770,7 +650,7 @@ bool Tst_GetParamsTst (struct Qst_Questions *Questions, Par_GetParMultiToText ("ChkTag",Questions->Tags.List,Tag_MAX_BYTES_TAGS_LIST); /* Check number of tags selected */ - if (Tst_CountNumTagsInList (&Questions->Tags) == 0) // If no tags selected... + if (Tag_CountNumTagsInList (&Questions->Tags) == 0) // If no tags selected... { // ...write alert Ale_ShowAlert (Ale_WARNING,Txt_You_must_select_one_ore_more_tags); Error = true; @@ -789,8 +669,8 @@ bool Tst_GetParamsTst (struct Qst_Questions *Questions, Par_GetParMultiToText ("AnswerType",Questions->AnswerTypes.List,Qst_MAX_BYTES_LIST_ANSWER_TYPES); /* Check number of types of answer */ - if (Tst_CountNumAnswerTypesInList (&Questions->AnswerTypes) == 0) // If no types of answer selected... - { // ...write warning alert + if (Qst_CountNumAnswerTypesInList (&Questions->AnswerTypes) == 0) // If no types of answer selected... + { // ...write warning alert Ale_ShowAlert (Ale_WARNING,Txt_You_must_select_one_ore_more_types_of_answer); Error = true; } @@ -869,84 +749,3 @@ static unsigned Tst_GetParamNumQsts (void) (unsigned long) TstCfg_GetConfigMax (), (unsigned long) TstCfg_GetConfigDef ()); } - -/*****************************************************************************/ -/***************** Count number of tags in the list of tags ******************/ -/*****************************************************************************/ - -static unsigned Tst_CountNumTagsInList (const struct Tag_Tags *Tags) - { - const char *Ptr; - unsigned NumTags = 0; - char TagText[Tag_MAX_BYTES_TAG + 1]; - - /***** Go over the list of tags counting the number of tags *****/ - Ptr = Tags->List; - while (*Ptr) - { - Par_GetNextStrUntilSeparParamMult (&Ptr,TagText,Tag_MAX_BYTES_TAG); - NumTags++; - } - - return NumTags; - } - -/*****************************************************************************/ -/**** Count the number of types of answers in the list of types of answers ***/ -/*****************************************************************************/ - -static int Tst_CountNumAnswerTypesInList (const struct Qst_AnswerTypes *AnswerTypes) - { - const char *Ptr; - int NumAnsTypes = 0; - char UnsignedStr[Cns_MAX_DECIMAL_DIGITS_UINT + 1]; - - /***** Go over the list of answer types counting the number of types of answer *****/ - Ptr = AnswerTypes->List; - while (*Ptr) - { - Par_GetNextStrUntilSeparParamMult (&Ptr,UnsignedStr,Cns_MAX_DECIMAL_DIGITS_UINT); - Qst_ConvertFromUnsignedStrToAnsTyp (UnsignedStr); - NumAnsTypes++; - } - return NumAnsTypes; - } - -/*****************************************************************************/ -/**** Count the number of questions in the list of selected question codes ***/ -/*****************************************************************************/ - -unsigned Tst_CountNumQuestionsInList (const char *ListQuestions) - { - const char *Ptr; - unsigned NumQuestions = 0; - char LongStr[Cns_MAX_DECIMAL_DIGITS_LONG + 1]; - long QstCod; - - /***** Go over list of questions counting the number of questions *****/ - Ptr = ListQuestions; - while (*Ptr) - { - Par_GetNextStrUntilSeparParamMult (&Ptr,LongStr,Cns_MAX_DECIMAL_DIGITS_LONG); - if (sscanf (LongStr,"%ld",&QstCod) != 1) - Err_WrongQuestionExit (); - NumQuestions++; - } - return NumQuestions; - } - -/*****************************************************************************/ -/************************* Remove all tests in a course **********************/ -/*****************************************************************************/ - -void Tst_RemoveCrsTests (long CrsCod) - { - /***** Remove all test prints made in the course *****/ - TstPrn_RemoveCrsPrints (CrsCod); - - /***** Remove test configuration of the course *****/ - DB_QueryDELETE ("can not remove configuration of tests of a course", - "DELETE FROM tst_config" - " WHERE CrsCod=%ld", - CrsCod); - } diff --git a/swad_test.h b/swad_test.h index 90f4bf56..fa26711b 100644 --- a/swad_test.h +++ b/swad_test.h @@ -69,8 +69,4 @@ void Tst_PutIconsTests (__attribute__((unused)) void *Args); bool Tst_GetParamsTst (struct Qst_Questions *Questions, Tst_ActionToDoWithQuestions_t ActionToDoWithQuestions); -unsigned Tst_CountNumQuestionsInList (const char *ListQuestions); - -void Tst_RemoveCrsTests (long CrsCod); - #endif diff --git a/swad_test_config.c b/swad_test_config.c index 5a915d13..0b05d08b 100644 --- a/swad_test_config.c +++ b/swad_test_config.c @@ -179,7 +179,7 @@ static void TstCfg_ShowFormConfig (void) Qst_Constructor (&Questions); /***** Read test configuration from database *****/ - TstCfg_GetConfigFromDB (); + TstCfg_GetConfig (); /***** Begin box *****/ Box_BoxBegin (NULL,Txt_Configure_tests, @@ -313,7 +313,7 @@ static void TstCfg_PutInputFieldNumQsts (const char *Field,const char *Label, /*************** Get configuration of test for current course ****************/ /*****************************************************************************/ -void TstCfg_GetConfigFromDB (void) +void TstCfg_GetConfig (void) { MYSQL_RES *mysql_res; MYSQL_ROW row; diff --git a/swad_test_config.h b/swad_test_config.h index 2df2e2f0..d9917bb3 100644 --- a/swad_test_config.h +++ b/swad_test_config.h @@ -59,7 +59,7 @@ typedef enum void TstCfg_CheckAndShowFormConfig (void); bool TstCfg_CheckIfPluggableIsUnknownAndCrsHasTests (void); -void TstCfg_GetConfigFromDB (void); +void TstCfg_GetConfig (void); void TstCfg_GetConfigFromRow (MYSQL_ROW row); void TstCfg_ReceiveConfigTst (void); diff --git a/swad_test_database.c b/swad_test_database.c index 1dde6cac..19709b20 100644 --- a/swad_test_database.c +++ b/swad_test_database.c @@ -33,14 +33,14 @@ // #include // For NULL // #include // For asprintf // #include // For exit, system, malloc, free, etc -// #include // For string functions +#include // For string functions // #include // For mkdir // #include // For mkdir // #include "swad_action.h" // #include "swad_box.h" #include "swad_database.h" -// #include "swad_error.h" +#include "swad_error.h" // #include "swad_exam_set.h" // #include "swad_figure.h" // #include "swad_form.h" @@ -52,12 +52,12 @@ // #include "swad_match.h" // #include "swad_media.h" // #include "swad_parameter.h" -// #include "swad_question.h" +#include "swad_question.h" // #include "swad_question_import.h" // #include "swad_tag_database.h" // #include "swad_test.h" #include "swad_test_config.h" -// #include "swad_test_print.h" +#include "swad_test_print.h" // #include "swad_test_visibility.h" // #include "swad_theme.h" // #include "swad_user.h" @@ -161,3 +161,124 @@ unsigned Tst_DB_GetNumPrintsGeneratedByMe (MYSQL_RES **mysql_res) Gbl.Usrs.Me.UsrDat.UsrCod, Gbl.Hierarchy.Crs.CrsCod); } + +/*****************************************************************************/ +/************** Get questions for a new test from the database ***************/ +/*****************************************************************************/ + +#define Tst_MAX_BYTES_QUERY_QUESTIONS (16 * 1024 - 1) + +unsigned Tst_DB_GetQuestionsForNewTest (MYSQL_RES **mysql_res, + const struct Qst_Questions *Questions) + { + extern const char *Qst_DB_StrAnswerTypes[Qst_NUM_ANS_TYPES]; + char *Query = NULL; + long LengthQuery; + unsigned NumItemInList; + const char *Ptr; + char TagText[Tag_MAX_BYTES_TAG + 1]; + char UnsignedStr[Cns_MAX_DECIMAL_DIGITS_UINT + 1]; + Qst_AnswerType_t AnswerType; + char StrNumQsts[Cns_MAX_DECIMAL_DIGITS_UINT + 1]; + + /***** Allocate space for query *****/ + if ((Query = malloc (Tst_MAX_BYTES_QUERY_QUESTIONS + 1)) == NULL) + Err_NotEnoughMemoryExit (); + + /***** Select questions without hidden tags *****/ + /* Begin query */ + // Reject questions with any tag hidden + // Select only questions with tags + // DISTINCTROW is necessary to not repeat questions + snprintf (Query,Tst_MAX_BYTES_QUERY_QUESTIONS + 1, + "SELECT DISTINCTROW tst_questions.QstCod," // row[0] + "tst_questions.AnsType," // row[1] + "tst_questions.Shuffle" // row[2] + " FROM tst_questions,tst_question_tags,tst_tags" + " WHERE tst_questions.CrsCod=%ld" + " AND tst_questions.QstCod NOT IN" + " (SELECT tst_question_tags.QstCod" + " FROM tst_tags,tst_question_tags" + " WHERE tst_tags.CrsCod=%ld" + " AND tst_tags.TagHidden='Y'" + " AND tst_tags.TagCod=tst_question_tags.TagCod)" + " AND tst_questions.QstCod=tst_question_tags.QstCod" + " AND tst_question_tags.TagCod=tst_tags.TagCod" + " AND tst_tags.CrsCod=%ld", + Gbl.Hierarchy.Crs.CrsCod, + Gbl.Hierarchy.Crs.CrsCod, + Gbl.Hierarchy.Crs.CrsCod); + + if (!Questions->Tags.All) // User has not selected all the tags + { + /* Add selected tags */ + LengthQuery = strlen (Query); + NumItemInList = 0; + Ptr = Questions->Tags.List; + while (*Ptr) + { + Par_GetNextStrUntilSeparParamMult (&Ptr,TagText,Tag_MAX_BYTES_TAG); + LengthQuery = LengthQuery + 35 + strlen (TagText) + 1; + if (LengthQuery > Tst_MAX_BYTES_QUERY_QUESTIONS - 128) + Err_QuerySizeExceededExit (); + Str_Concat (Query, + NumItemInList ? " OR tst_tags.TagTxt='" : + " AND (tst_tags.TagTxt='", + Tst_MAX_BYTES_QUERY_QUESTIONS); + Str_Concat (Query,TagText,Tst_MAX_BYTES_QUERY_QUESTIONS); + Str_Concat (Query,"'",Tst_MAX_BYTES_QUERY_QUESTIONS); + NumItemInList++; + } + Str_Concat (Query,")",Tst_MAX_BYTES_QUERY_QUESTIONS); + } + + /* Add answer types selected */ + if (!Questions->AnswerTypes.All) + { + LengthQuery = strlen (Query); + NumItemInList = 0; + Ptr = Questions->AnswerTypes.List; + while (*Ptr) + { + Par_GetNextStrUntilSeparParamMult (&Ptr,UnsignedStr,Tag_MAX_BYTES_TAG); + AnswerType = Qst_ConvertFromUnsignedStrToAnsTyp (UnsignedStr); + LengthQuery = LengthQuery + 35 + strlen (Qst_DB_StrAnswerTypes[AnswerType]) + 1; + if (LengthQuery > Tst_MAX_BYTES_QUERY_QUESTIONS - 128) + Err_QuerySizeExceededExit (); + Str_Concat (Query, + NumItemInList ? " OR tst_questions.AnsType='" : + " AND (tst_questions.AnsType='", + Tst_MAX_BYTES_QUERY_QUESTIONS); + Str_Concat (Query,Qst_DB_StrAnswerTypes[AnswerType],Tst_MAX_BYTES_QUERY_QUESTIONS); + Str_Concat (Query,"'",Tst_MAX_BYTES_QUERY_QUESTIONS); + NumItemInList++; + } + Str_Concat (Query,")",Tst_MAX_BYTES_QUERY_QUESTIONS); + } + + /* End query */ + Str_Concat (Query," ORDER BY RAND() LIMIT ",Tst_MAX_BYTES_QUERY_QUESTIONS); + snprintf (StrNumQsts,sizeof (StrNumQsts),"%u",Questions->NumQsts); + Str_Concat (Query,StrNumQsts,Tst_MAX_BYTES_QUERY_QUESTIONS); +/* + if (Gbl.Usrs.Me.Roles.LoggedRole == Rol_SYS_ADM) + Lay_ShowAlert (Lay_INFO,Query); +*/ + /* Make the query */ + return (unsigned) + DB_QuerySELECT (mysql_res,"can not get questions", + "%s", + Query); + } + +/*****************************************************************************/ +/****************** Remove test configuration in a course ********************/ +/*****************************************************************************/ + +void Tst_DB_RemoveTstConfig (long CrsCod) + { + DB_QueryDELETE ("can not remove configuration of tests of a course", + "DELETE FROM tst_config" + " WHERE CrsCod=%ld", + CrsCod); + } diff --git a/swad_test_database.h b/swad_test_database.h index fef73822..33c99607 100644 --- a/swad_test_database.h +++ b/swad_test_database.h @@ -54,4 +54,9 @@ void Tst_DB_UpdateLastAccTst (unsigned NumQsts); unsigned Tst_DB_GetDateNextTstAllowed (MYSQL_RES **mysql_res); unsigned Tst_DB_GetNumPrintsGeneratedByMe (MYSQL_RES **mysql_res); +unsigned Tst_DB_GetQuestionsForNewTest (MYSQL_RES **mysql_res, + const struct Qst_Questions *Questions); + +void Tst_DB_RemoveTstConfig (long CrsCod); + #endif diff --git a/swad_test_print.c b/swad_test_print.c index ddc701b9..1fc88dc0 100644 --- a/swad_test_print.c +++ b/swad_test_print.c @@ -762,6 +762,28 @@ static void TstPrn_WriteQstAndAnsExam (struct UsrData *UsrDat, HTM_TR_End (); } +/*****************************************************************************/ +/******** Get questions and answers from form to assess a test print *********/ +/*****************************************************************************/ + +void TstPrn_GetAnswersFromForm (struct TstPrn_Print *Print) + { + unsigned QstInd; + char StrAns[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Ansxx...x" + + /***** Loop for every question getting user's answers *****/ + for (QstInd = 0; + QstInd < Print->NumQsts.All; + QstInd++) + { + /* Get answers selected by user for this question */ + snprintf (StrAns,sizeof (StrAns),"Ans%010u",QstInd); + Par_GetParMultiToText (StrAns,Print->PrintedQuestions[QstInd].StrAnswers, + Qst_MAX_BYTES_ANSWERS_ONE_QST); /* If answer type == T/F ==> " ", "T", "F"; if choice ==> "0", "2",... */ + Par_ReplaceSeparatorMultipleByComma (Print->PrintedQuestions[QstInd].StrAnswers); + } + } + /*****************************************************************************/ /*********** Compute score of each question and store in database ************/ /*****************************************************************************/ @@ -1956,7 +1978,7 @@ void TstPrn_ShowMyPrints (void) TstPrn_ShowHeaderPrints (Usr_ME); /***** List my tests *****/ - TstCfg_GetConfigFromDB (); // To get visibility + TstCfg_GetConfig (); // To get visibility TstPrn_ShowUsrPrints (&Gbl.Usrs.Me.UsrDat); /***** End table and box *****/ @@ -2435,7 +2457,7 @@ void TstPrn_ShowOnePrint (void) /***** Get if I can see print result and score *****/ if (Gbl.Usrs.Me.Role.Logged == Rol_STD) - TstCfg_GetConfigFromDB (); // To get visibility + TstCfg_GetConfig (); // To get visibility TstRes_CheckIfICanSeePrintResult (&Print,Gbl.Usrs.Other.UsrDat.UsrCod,&ICanView); if (ICanView.Result) // I am allowed to view this test print result diff --git a/swad_test_print.h b/swad_test_print.h index 2e93e32b..3109fb6d 100644 --- a/swad_test_print.h +++ b/swad_test_print.h @@ -100,6 +100,8 @@ void TstPrn_ShowTestPrintToFillIt (struct TstPrn_Print *Print, void TstPrn_ShowPrintAfterAssess (struct TstPrn_Print *Print); +void TstPrn_GetAnswersFromForm (struct TstPrn_Print *Print); + void TstPrn_ComputeScoresAndStoreQuestionsOfPrint (struct TstPrn_Print *Print, bool UpdateQstScore); void TstPrn_ComputeAnswerScore (struct TstPrn_PrintedQuestion *PrintedQuestion,