diff --git a/swad_changelog.h b/swad_changelog.h index ad5bf2295..d19d3d1d7 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.47.4 (2021-10-30)" +#define Log_PLATFORM_VERSION "SWAD 21.47.5 (2021-10-30)" #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.47.5: Oct 29, 2021 Queries moved to module swad_questions_database. (321308 lines) Version 21.47.4: Oct 30, 2021 Review of comments. (321253 lines) Version 21.47.3: Oct 29, 2021 Queries moved to module swad_questions_database. (321299 lines) Version 21.47.2: Oct 29, 2021 Queries moved to module swad_questions_database. (321259 lines) diff --git a/swad_question.c b/swad_question.c index bae49ed77..23ab2771b 100644 --- a/swad_question.c +++ b/swad_question.c @@ -3061,19 +3061,9 @@ bool Qst_CheckIfQuestionExistsInDB (struct Qst_Question *Question) unsigned NumOptsExistingQstInDB; unsigned i; - /***** Check if stem exists *****/ - NumQstsWithThisStem = - (unsigned) DB_QuerySELECT (&mysql_res_qst,"can not check if a question exists", - "SELECT QstCod" - " FROM tst_questions" - " WHERE CrsCod=%ld" - " AND AnsType='%s'" - " AND Stem='%s'", - Gbl.Hierarchy.Crs.CrsCod, - Qst_DB_StrAnswerTypes[Question->Answer.Type], - Question->Stem); - - if (NumQstsWithThisStem) // There are questions in database with the same stem that the one of this question + /***** Check if there are existing questions in database + with the same stem that the one of this question *****/ + if ((NumQstsWithThisStem = Qst_DB_GetQstCodFromTypeAnsStem (&mysql_res_qst,Question))) { /***** Check if the answer exists in any of the questions with the same stem *****/ /* For each question with the same stem */ @@ -3429,17 +3419,8 @@ void Qst_ChangeShuffleQst (void) /***** Get a parameter that indicates whether it's possible to shuffle the answers of this question ******/ Shuffle = Par_GetParToBool ("Shuffle"); - /***** Remove the question from all tables *****/ - /* Update the question changing the current shuffle */ - DB_QueryUPDATE ("can not update the shuffle type of a question", - "UPDATE tst_questions" - " SET Shuffle='%c'" - " WHERE QstCod=%ld" - " AND CrsCod=%ld", - Shuffle ? 'Y' : - 'N', - Questions.Question.QstCod, - Gbl.Hierarchy.Crs.CrsCod); + /***** Update the question changing the current shuffle *****/ + Qst_DB_UpdateQstShuffle (Questions.Question.QstCod,Shuffle); /***** Write message *****/ Ale_ShowAlert (Ale_SUCCESS,Shuffle ? Txt_The_answers_of_the_question_with_code_X_will_appear_shuffled : @@ -3508,64 +3489,14 @@ void Qst_InsertOrUpdateQstIntoDB (struct Qst_Question *Question) extern const char *Qst_DB_StrAnswerTypes[Qst_NUM_ANS_TYPES]; if (Question->QstCod < 0) // It's a new question - { /***** Insert question in the table of questions *****/ - Question->QstCod = - DB_QueryINSERTandReturnCode ("can not create question", - "INSERT INTO tst_questions" - " (CrsCod," - "EditTime," - "AnsType," - "Shuffle," - "Stem," - "Feedback," - "MedCod," - "NumHits," - "Score)" - " VALUES" - " (%ld," // CrsCod - "NOW()," // EditTime - "'%s'," // AnsType - "'%c'," // Shuffle - "'%s'," // Stem - "'%s'," // Feedback - "%ld," // MedCod - "0," // NumHits - "0)", // Score - Gbl.Hierarchy.Crs.CrsCod, - Qst_DB_StrAnswerTypes[Question->Answer.Type], - Question->Answer.Shuffle ? 'Y' : - 'N', - Question->Stem, - Question->Feedback ? Question->Feedback : - "", - Question->Media.MedCod); - } + Question->QstCod = Qst_DB_CreateQst (Question); else // It's an existing question { /***** Update existing question *****/ - /* Update question in database */ - DB_QueryUPDATE ("can not update question", - "UPDATE tst_questions" - " SET EditTime=NOW()," - "AnsType='%s'," - "Shuffle='%c'," - "Stem='%s'," - "Feedback='%s'," - "MedCod=%ld" - " WHERE QstCod=%ld" - " AND CrsCod=%ld", - Qst_DB_StrAnswerTypes[Question->Answer.Type], - Question->Answer.Shuffle ? 'Y' : - 'N', - Question->Stem, - Question->Feedback ? Question->Feedback : - "", - Question->Media.MedCod, - Question->QstCod, - Gbl.Hierarchy.Crs.CrsCod); + Qst_DB_UpdateQst (Question); - /* Remove answers and tags from this test question */ + /***** Remove answers and tags from this test question *****/ Qst_DB_RemAnsFromQst (Question->QstCod); Tag_DB_RemTagsFromQst (Question->QstCod); } @@ -3577,75 +3508,18 @@ void Qst_InsertOrUpdateQstIntoDB (struct Qst_Question *Question) void Qst_InsertAnswersIntoDB (struct Qst_Question *Question) { - unsigned NumOpt; - unsigned i; + void (*Qst_DB_CreateAnswer[Qst_NUM_ANS_TYPES]) (struct Qst_Question *Question) = + { + [Qst_ANS_INT ] = Qst_DB_CreateIntAnswer, + [Qst_ANS_FLOAT ] = Qst_DB_CreateFltAnswer, + [Qst_ANS_TRUE_FALSE ] = Qst_DB_CreateTF_Answer, + [Qst_ANS_UNIQUE_CHOICE ] = Qst_DB_CreateChoAnswer, + [Qst_ANS_MULTIPLE_CHOICE] = Qst_DB_CreateChoAnswer, + [Qst_ANS_TEXT ] = Qst_DB_CreateChoAnswer, + }; - /***** Insert answers in the answers table *****/ - switch (Question->Answer.Type) - { - case Qst_ANS_INT: - DB_QueryINSERT ("can not create answer", - "INSERT INTO tst_answers" - " (QstCod,AnsInd,Answer,Feedback,MedCod,Correct)" - " VALUES" - " (%ld,0,%ld,'',-1,'Y')", - Question->QstCod, - Question->Answer.Integer); - break; - case Qst_ANS_FLOAT: - Str_SetDecimalPointToUS (); // To print the floating point as a dot - for (i = 0; - i < 2; - i++) - DB_QueryINSERT ("can not create answer", - "INSERT INTO tst_answers" - " (QstCod,AnsInd,Answer,Feedback,MedCod,Correct)" - " VALUES" - " (%ld,%u,'%.15lg','',-1,'Y')", - Question->QstCod, - i, - Question->Answer.FloatingPoint[i]); - Str_SetDecimalPointToLocal (); // Return to local system - break; - case Qst_ANS_TRUE_FALSE: - DB_QueryINSERT ("can not create answer", - "INSERT INTO tst_answers" - " (QstCod,AnsInd,Answer,Feedback,MedCod,Correct)" - " VALUES" - " (%ld,0,'%c','',-1,'Y')", - Question->QstCod, - Question->Answer.TF); - break; - case Qst_ANS_UNIQUE_CHOICE: - case Qst_ANS_MULTIPLE_CHOICE: - case Qst_ANS_TEXT: - for (NumOpt = 0; - NumOpt < Question->Answer.NumOptions; - NumOpt++) - if (Question->Answer.Options[NumOpt].Text[0] || // Text - Question->Answer.Options[NumOpt].Media.Type != Med_TYPE_NONE) // or media - { - DB_QueryINSERT ("can not create answer", - "INSERT INTO tst_answers" - " (QstCod,AnsInd,Answer,Feedback,MedCod,Correct)" - " VALUES" - " (%ld,%u,'%s','%s',%ld,'%c')", - Question->QstCod,NumOpt, - Question->Answer.Options[NumOpt].Text, - Question->Answer.Options[NumOpt].Feedback ? Question->Answer.Options[NumOpt].Feedback : - "", - Question->Answer.Options[NumOpt].Media.MedCod, - Question->Answer.Options[NumOpt].Correct ? 'Y' : - 'N'); - - /* Update image status */ - if (Question->Answer.Options[NumOpt].Media.Type != Med_TYPE_NONE) - Question->Answer.Options[NumOpt].Media.Status = Med_STORED_IN_DB; - } - break; - default: - break; - } + /***** Create answer *****/ + Qst_DB_CreateAnswer[Question->Answer.Type] (Question); } /*****************************************************************************/ diff --git a/swad_question_database.c b/swad_question_database.c index ab3b83c57..293466f88 100644 --- a/swad_question_database.c +++ b/swad_question_database.c @@ -71,13 +71,77 @@ extern struct Globals Gbl; /***************************** Private prototypes ****************************/ /*****************************************************************************/ +/*****************************************************************************/ +/*********** Insert or update question in the table of questions *************/ +/*****************************************************************************/ + +long Qst_DB_CreateQst (const struct Qst_Question *Question) + { + return + DB_QueryINSERTandReturnCode ("can not create question", + "INSERT INTO tst_questions" + " (CrsCod," + "EditTime," + "AnsType," + "Shuffle," + "Stem," + "Feedback," + "MedCod," + "NumHits," + "Score)" + " VALUES" + " (%ld," // CrsCod + "NOW()," // EditTime + "'%s'," // AnsType + "'%c'," // Shuffle + "'%s'," // Stem + "'%s'," // Feedback + "%ld," // MedCod + "0," // NumHits + "0)", // Score + Gbl.Hierarchy.Crs.CrsCod, + Qst_DB_StrAnswerTypes[Question->Answer.Type], + Question->Answer.Shuffle ? 'Y' : + 'N', + Question->Stem, + Question->Feedback ? Question->Feedback : + "", + Question->Media.MedCod); + } + +/*****************************************************************************/ +/************ Update existing question in the table of questions *************/ +/*****************************************************************************/ + +void Qst_DB_UpdateQst (const struct Qst_Question *Question) + { + DB_QueryUPDATE ("can not update question", + "UPDATE tst_questions" + " SET EditTime=NOW()," + "AnsType='%s'," + "Shuffle='%c'," + "Stem='%s'," + "Feedback='%s'," + "MedCod=%ld" + " WHERE QstCod=%ld" + " AND CrsCod=%ld", // Extra check + Qst_DB_StrAnswerTypes[Question->Answer.Type], + Question->Answer.Shuffle ? 'Y' : + 'N', + Question->Stem, + Question->Feedback ? Question->Feedback : + "", + Question->Media.MedCod, + Question->QstCod, + Gbl.Hierarchy.Crs.CrsCod); + } + /*****************************************************************************/ /*********************** Update the score of a question **********************/ /*****************************************************************************/ void Qst_DB_UpdateQstScore (long QstCod,bool AnswerIsNotBlank,double Score) { - /***** Update number of clicks and score of the question *****/ Str_SetDecimalPointToUS (); // To print the floating point as a dot if (AnswerIsNotBlank) // User's answer is not blank DB_QueryUPDATE ("can not update the score of a question", @@ -97,6 +161,109 @@ void Qst_DB_UpdateQstScore (long QstCod,bool AnswerIsNotBlank,double Score) Str_SetDecimalPointToLocal (); // Return to local system } +/*****************************************************************************/ +/*********************** Change the shuffle of a question ********************/ +/*****************************************************************************/ + +void Qst_DB_UpdateQstShuffle (long QstCod,bool Shuffle) + { + DB_QueryUPDATE ("can not update the shuffle type of a question", + "UPDATE tst_questions" + " SET Shuffle='%c'" + " WHERE QstCod=%ld" + " AND CrsCod=%ld", // Extra check + Shuffle ? 'Y' : + 'N', + QstCod, + Gbl.Hierarchy.Crs.CrsCod); + } + +/*****************************************************************************/ +/*************************** Create integer answer ***************************/ +/*****************************************************************************/ + +void Qst_DB_CreateIntAnswer (const struct Qst_Question *Question) + { + DB_QueryINSERT ("can not create answer", + "INSERT INTO tst_answers" + " (QstCod,AnsInd,Answer,Feedback,MedCod,Correct)" + " VALUES" + " (%ld,0,%ld,'',-1,'Y')", + Question->QstCod, + Question->Answer.Integer); + } + +/*****************************************************************************/ +/**************************** Create float answer ****************************/ +/*****************************************************************************/ + +void Qst_DB_CreateFltAnswer (const struct Qst_Question *Question) + { + unsigned i; + + Str_SetDecimalPointToUS (); // To print the floating point as a dot + for (i = 0; + i < 2; + i++) + DB_QueryINSERT ("can not create answer", + "INSERT INTO tst_answers" + " (QstCod,AnsInd,Answer,Feedback,MedCod,Correct)" + " VALUES" + " (%ld,%u,'%.15lg','',-1,'Y')", + Question->QstCod, + i, + Question->Answer.FloatingPoint[i]); + Str_SetDecimalPointToLocal (); // Return to local system + } + +/*****************************************************************************/ +/***************************** Create T/F answer *****************************/ +/*****************************************************************************/ + +void Qst_DB_CreateTF_Answer (const struct Qst_Question *Question) + { + DB_QueryINSERT ("can not create answer", + "INSERT INTO tst_answers" + " (QstCod,AnsInd,Answer,Feedback,MedCod,Correct)" + " VALUES" + " (%ld,0,'%c','',-1,'Y')", + Question->QstCod, + Question->Answer.TF); + } + +/*****************************************************************************/ +/***************************** Create T/F answer *****************************/ +/*****************************************************************************/ + +void Qst_DB_CreateChoAnswer (struct Qst_Question *Question) + { + unsigned NumOpt; + + for (NumOpt = 0; + NumOpt < Question->Answer.NumOptions; + NumOpt++) + if (Question->Answer.Options[NumOpt].Text[0] || // Text + Question->Answer.Options[NumOpt].Media.Type != Med_TYPE_NONE) // or media + { + DB_QueryINSERT ("can not create answer", + "INSERT INTO tst_answers" + " (QstCod,AnsInd,Answer,Feedback,MedCod,Correct)" + " VALUES" + " (%ld,%u,'%s','%s',%ld,'%c')", + Question->QstCod,NumOpt, + Question->Answer.Options[NumOpt].Text, + Question->Answer.Options[NumOpt].Feedback ? Question->Answer.Options[NumOpt].Feedback : + "", + Question->Answer.Options[NumOpt].Media.MedCod, + Question->Answer.Options[NumOpt].Correct ? 'Y' : + 'N'); + + /* Update image status */ + if (Question->Answer.Options[NumOpt].Media.Type != Med_TYPE_NONE) + Question->Answer.Options[NumOpt].Media.Status = Med_STORED_IN_DB; + } + } + /*****************************************************************************/ /***************** Get several test questions from database ******************/ /*****************************************************************************/ @@ -951,6 +1118,25 @@ long Qst_DB_GetQstMedCod (long CrsCod,long QstCod) CrsCod); } +/*****************************************************************************/ +/****************** Get question code from type and stem *********************/ +/*****************************************************************************/ + +unsigned Qst_DB_GetQstCodFromTypeAnsStem (MYSQL_RES **mysql_res, + const struct Qst_Question *Question) + { + return (unsigned) + DB_QuerySELECT (mysql_res,"can not check if a question exists", + "SELECT QstCod" + " FROM tst_questions" + " WHERE CrsCod=%ld" + " AND AnsType='%s'" + " AND Stem='%s'", + Gbl.Hierarchy.Crs.CrsCod, + Qst_DB_StrAnswerTypes[Question->Answer.Type], + Question->Stem); + } + /*****************************************************************************/ /************ Get number of answers of a question from database **************/ /*****************************************************************************/ diff --git a/swad_question_database.h b/swad_question_database.h index 862e52867..51d5b4233 100644 --- a/swad_question_database.h +++ b/swad_question_database.h @@ -39,7 +39,16 @@ /***************************** Public prototypes *****************************/ /*****************************************************************************/ +long Qst_DB_CreateQst (const struct Qst_Question *Question); +void Qst_DB_UpdateQst (const struct Qst_Question *Question); void Qst_DB_UpdateQstScore (long QstCod,bool AnswerIsNotBlank,double Score); +void Qst_DB_UpdateQstShuffle (long QstCod,bool Shuffle); +//----------------------------------------------------------------------------- +void Qst_DB_CreateIntAnswer (struct Qst_Question *Question); +void Qst_DB_CreateFltAnswer (struct Qst_Question *Question); +void Qst_DB_CreateTF_Answer (struct Qst_Question *Question); +void Qst_DB_CreateChoAnswer (struct Qst_Question *Question); +//----------------------------------------------------------------------------- unsigned Qst_DB_GetQsts (MYSQL_RES **mysql_res, const struct Qst_Questions *Questions); @@ -55,6 +64,8 @@ unsigned Qst_DB_GetNumCrssWithPluggableQsts (HieLvl_Level_t Scope, unsigned Qst_DB_GetQstData (MYSQL_RES **mysql_res,long QstCod); Qst_AnswerType_t Qst_DB_GetQstAnswerType (long QstCod); long Qst_DB_GetQstMedCod (long CrsCod,long QstCod); +unsigned Qst_DB_GetQstCodFromTypeAnsStem (MYSQL_RES **mysql_res, + const struct Qst_Question *Question); unsigned Qst_DB_GetNumAnswersQst (long QstCod); unsigned Qst_DB_GetDataOfAnswers (MYSQL_RES **mysql_res,long QstCod,bool Shuffle);