2021-10-27 21:40:19 +02:00
|
|
|
|
// swad_question_database.c: test/exam/game questions, operations with database
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
SWAD (Shared Workspace At a Distance),
|
|
|
|
|
is a web platform developed at the University of Granada (Spain),
|
|
|
|
|
and used to support university teaching.
|
|
|
|
|
|
|
|
|
|
This file is part of SWAD core.
|
|
|
|
|
Copyright (C) 1999-2021 Antonio Ca<EFBFBD>as Vargas
|
|
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU Affero General Public License as
|
|
|
|
|
published by the Free Software Foundation, either version 3 of the
|
|
|
|
|
License, or (at your option) any later version.
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU Affero General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********************************** Headers *********************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include <string.h> // For string functions
|
|
|
|
|
|
|
|
|
|
#include "swad_database.h"
|
|
|
|
|
#include "swad_error.h"
|
|
|
|
|
#include "swad_global.h"
|
|
|
|
|
#include "swad_question.h"
|
|
|
|
|
#include "swad_test_print.h"
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************************** Public constants ******************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
const char *Qst_DB_StrAnswerTypes[Qst_NUM_ANS_TYPES] =
|
|
|
|
|
{
|
|
|
|
|
[Qst_ANS_INT ] = "int",
|
|
|
|
|
[Qst_ANS_FLOAT ] = "float",
|
|
|
|
|
[Qst_ANS_TRUE_FALSE ] = "true_false",
|
|
|
|
|
[Qst_ANS_UNIQUE_CHOICE ] = "unique_choice",
|
|
|
|
|
[Qst_ANS_MULTIPLE_CHOICE] = "multiple_choice",
|
|
|
|
|
[Qst_ANS_TEXT ] = "text",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/**************************** Private constants ******************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2021-10-29 00:32:19 +02:00
|
|
|
|
#define Qst_MAX_BYTES_QUERY_QUESTIONS (16 * 1024 - 1)
|
|
|
|
|
|
2021-10-27 21:40:19 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************** External global variables from others modules ****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
extern struct Globals Gbl;
|
|
|
|
|
|
2021-10-30 20:50:29 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********** 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);
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-27 21:40:19 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********************** Update the score of a question **********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void Qst_DB_UpdateQstScore (long QstCod,bool AnswerIsNotBlank,double Score)
|
|
|
|
|
{
|
|
|
|
|
Str_SetDecimalPointToUS (); // To print the floating point as a dot
|
2021-12-02 19:44:16 +01:00
|
|
|
|
if (AnswerIsNotBlank) // User's answer is not blank
|
2021-10-27 21:40:19 +02:00
|
|
|
|
DB_QueryUPDATE ("can not update the score of a question",
|
|
|
|
|
"UPDATE tst_questions"
|
|
|
|
|
" SET NumHits=NumHits+1,"
|
|
|
|
|
"NumHitsNotBlank=NumHitsNotBlank+1,"
|
|
|
|
|
"Score=Score+(%.15lg)"
|
|
|
|
|
" WHERE QstCod=%ld",
|
|
|
|
|
Score,
|
|
|
|
|
QstCod);
|
|
|
|
|
else // User's answer is blank
|
|
|
|
|
DB_QueryUPDATE ("can not update the score of a question",
|
|
|
|
|
"UPDATE tst_questions"
|
|
|
|
|
" SET NumHits=NumHits+1"
|
|
|
|
|
" WHERE QstCod=%ld",
|
|
|
|
|
QstCod);
|
|
|
|
|
Str_SetDecimalPointToLocal (); // Return to local system
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-30 20:50:29 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********************** 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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-27 21:40:19 +02:00
|
|
|
|
/*****************************************************************************/
|
2021-10-29 00:32:19 +02:00
|
|
|
|
/***************** Get several test questions from database ******************/
|
2021-10-27 21:40:19 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2021-10-29 00:32:19 +02:00
|
|
|
|
unsigned Qst_DB_GetQsts (MYSQL_RES **mysql_res,
|
|
|
|
|
const struct Qst_Questions *Questions)
|
|
|
|
|
{
|
|
|
|
|
extern const char *Qst_DB_StrAnswerTypes[Qst_NUM_ANS_TYPES];
|
|
|
|
|
extern const char *Txt_No_questions_found_matching_your_search_criteria;
|
|
|
|
|
char *Query = NULL;
|
|
|
|
|
long LengthQuery;
|
|
|
|
|
unsigned NumItemInList;
|
|
|
|
|
const char *Ptr;
|
|
|
|
|
char TagText[Tag_MAX_BYTES_TAG + 1];
|
|
|
|
|
char LongStr[Cns_MAX_DECIMAL_DIGITS_LONG + 1];
|
|
|
|
|
char UnsignedStr[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
|
|
|
|
|
Qst_AnswerType_t AnsType;
|
|
|
|
|
char CrsCodStr[Cns_MAX_DECIMAL_DIGITS_LONG + 1];
|
|
|
|
|
unsigned NumQsts;
|
|
|
|
|
|
|
|
|
|
/***** Allocate space for query *****/
|
|
|
|
|
if ((Query = malloc (Qst_MAX_BYTES_QUERY_QUESTIONS + 1)) == NULL)
|
|
|
|
|
Err_NotEnoughMemoryExit ();
|
2021-10-27 21:40:19 +02:00
|
|
|
|
|
2021-10-29 00:32:19 +02:00
|
|
|
|
/***** Select questions *****/
|
|
|
|
|
/* Begin query */
|
|
|
|
|
Str_Copy (Query,"SELECT tst_questions.QstCod" // row[0]
|
|
|
|
|
" FROM tst_questions",Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
if (!Questions->Tags.All)
|
|
|
|
|
Str_Concat (Query,","
|
|
|
|
|
"tst_question_tags,"
|
|
|
|
|
"tst_tags",
|
|
|
|
|
Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
|
|
|
|
|
Str_Concat (Query," WHERE tst_questions.CrsCod='",
|
|
|
|
|
Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
snprintf (CrsCodStr,sizeof (CrsCodStr),"%ld",Gbl.Hierarchy.Crs.CrsCod);
|
|
|
|
|
Str_Concat (Query,CrsCodStr,Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
Str_Concat (Query,"' AND tst_questions.EditTime>=FROM_UNIXTIME('",
|
|
|
|
|
Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
snprintf (LongStr,sizeof (LongStr),"%ld",
|
|
|
|
|
(long) Gbl.DateRange.TimeUTC[Dat_STR_TIME]);
|
|
|
|
|
Str_Concat (Query,LongStr,Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
Str_Concat (Query,"') AND tst_questions.EditTime<=FROM_UNIXTIME('",
|
|
|
|
|
Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
snprintf (LongStr,sizeof (LongStr),"%ld",
|
|
|
|
|
(long) Gbl.DateRange.TimeUTC[Dat_END_TIME]);
|
|
|
|
|
Str_Concat (Query,LongStr,Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
Str_Concat (Query,"')",Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
|
|
|
|
|
/* Add the tags selected */
|
|
|
|
|
if (!Questions->Tags.All)
|
|
|
|
|
{
|
|
|
|
|
Str_Concat (Query," AND tst_questions.QstCod=tst_question_tags.QstCod"
|
|
|
|
|
" AND tst_question_tags.TagCod=tst_tags.TagCod"
|
|
|
|
|
" AND tst_tags.CrsCod='",
|
|
|
|
|
Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
Str_Concat (Query,CrsCodStr,Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
Str_Concat (Query,"'",Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
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 > Qst_MAX_BYTES_QUERY_QUESTIONS - 256)
|
|
|
|
|
Err_QuerySizeExceededExit ();
|
|
|
|
|
Str_Concat (Query,
|
|
|
|
|
NumItemInList ? " OR tst_tags.TagTxt='" :
|
|
|
|
|
" AND (tst_tags.TagTxt='",
|
|
|
|
|
Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
Str_Concat (Query,TagText,Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
Str_Concat (Query,"'",Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
NumItemInList++;
|
|
|
|
|
}
|
|
|
|
|
Str_Concat (Query,")",Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add the types of answer selected */
|
|
|
|
|
if (!Questions->AnswerTypes.All)
|
|
|
|
|
{
|
|
|
|
|
LengthQuery = strlen (Query);
|
|
|
|
|
NumItemInList = 0;
|
|
|
|
|
Ptr = Questions->AnswerTypes.List;
|
|
|
|
|
while (*Ptr)
|
|
|
|
|
{
|
|
|
|
|
Par_GetNextStrUntilSeparParamMult (&Ptr,UnsignedStr,Tag_MAX_BYTES_TAG);
|
|
|
|
|
AnsType = Qst_ConvertFromUnsignedStrToAnsTyp (UnsignedStr);
|
|
|
|
|
LengthQuery = LengthQuery + 35 + strlen (Qst_DB_StrAnswerTypes[AnsType]) + 1;
|
|
|
|
|
if (LengthQuery > Qst_MAX_BYTES_QUERY_QUESTIONS - 256)
|
|
|
|
|
Err_QuerySizeExceededExit ();
|
|
|
|
|
Str_Concat (Query,
|
|
|
|
|
NumItemInList ? " OR tst_questions.AnsType='" :
|
|
|
|
|
" AND (tst_questions.AnsType='",
|
|
|
|
|
Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
Str_Concat (Query,Qst_DB_StrAnswerTypes[AnsType],Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
Str_Concat (Query,"'",Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
NumItemInList++;
|
|
|
|
|
}
|
|
|
|
|
Str_Concat (Query,")",Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* End the query */
|
|
|
|
|
Str_Concat (Query," GROUP BY tst_questions.QstCod",Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
|
|
|
|
|
switch (Questions->SelectedOrder)
|
|
|
|
|
{
|
|
|
|
|
case Qst_ORDER_STEM:
|
|
|
|
|
Str_Concat (Query," ORDER BY tst_questions.Stem",
|
|
|
|
|
Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
break;
|
|
|
|
|
case Qst_ORDER_NUM_HITS:
|
|
|
|
|
Str_Concat (Query," ORDER BY tst_questions.NumHits DESC,"
|
|
|
|
|
"tst_questions.Stem",
|
|
|
|
|
Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
break;
|
|
|
|
|
case Qst_ORDER_AVERAGE_SCORE:
|
|
|
|
|
Str_Concat (Query," ORDER BY tst_questions.Score/tst_questions.NumHits DESC,"
|
|
|
|
|
"tst_questions.NumHits DESC,"
|
|
|
|
|
"tst_questions.Stem",
|
|
|
|
|
Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
break;
|
|
|
|
|
case Qst_ORDER_NUM_HITS_NOT_BLANK:
|
|
|
|
|
Str_Concat (Query," ORDER BY tst_questions.NumHitsNotBlank DESC,"
|
|
|
|
|
"tst_questions.Stem",
|
|
|
|
|
Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
break;
|
|
|
|
|
case Qst_ORDER_AVERAGE_SCORE_NOT_BLANK:
|
|
|
|
|
Str_Concat (Query," ORDER BY tst_questions.Score/tst_questions.NumHitsNotBlank DESC,"
|
|
|
|
|
"tst_questions.NumHitsNotBlank DESC,"
|
|
|
|
|
"tst_questions.Stem",
|
|
|
|
|
Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make the query */
|
|
|
|
|
if ((NumQsts = (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get questions",
|
|
|
|
|
"%s",
|
|
|
|
|
Query)) == 0)
|
|
|
|
|
Ale_ShowAlert (Ale_INFO,Txt_No_questions_found_matching_your_search_criteria);
|
|
|
|
|
|
|
|
|
|
return NumQsts;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************* Get questions for a new test print **********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
unsigned Qst_DB_GetQstsForNewTestPrint (MYSQL_RES **mysql_res,
|
|
|
|
|
const struct Qst_Questions *Questions)
|
2021-10-27 21:40:19 +02:00
|
|
|
|
{
|
|
|
|
|
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 *****/
|
2021-10-29 00:32:19 +02:00
|
|
|
|
if ((Query = malloc (Qst_MAX_BYTES_QUERY_QUESTIONS + 1)) == NULL)
|
2021-10-27 21:40:19 +02:00
|
|
|
|
Err_NotEnoughMemoryExit ();
|
|
|
|
|
|
|
|
|
|
/***** Select questions without hidden tags *****/
|
|
|
|
|
/* Begin query */
|
|
|
|
|
// Reject questions with any tag hidden
|
|
|
|
|
// Select only questions with tags
|
2021-11-06 12:13:05 +01:00
|
|
|
|
// DISTINCT is necessary to not repeat questions
|
2021-10-29 00:32:19 +02:00
|
|
|
|
snprintf (Query,Qst_MAX_BYTES_QUERY_QUESTIONS + 1,
|
2021-11-06 12:13:05 +01:00
|
|
|
|
"SELECT DISTINCT "
|
|
|
|
|
"tst_questions.QstCod," // row[0]
|
|
|
|
|
"tst_questions.AnsType," // row[1]
|
|
|
|
|
"tst_questions.Shuffle" // row[2]
|
2021-10-27 21:40:19 +02:00
|
|
|
|
" 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);
|
|
|
|
|
|
2021-10-30 13:28:48 +02:00
|
|
|
|
if (!Questions->Tags.All) // User has not selected all tags
|
2021-10-27 21:40:19 +02:00
|
|
|
|
{
|
|
|
|
|
/* 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;
|
2021-10-29 00:32:19 +02:00
|
|
|
|
if (LengthQuery > Qst_MAX_BYTES_QUERY_QUESTIONS - 128)
|
2021-10-27 21:40:19 +02:00
|
|
|
|
Err_QuerySizeExceededExit ();
|
|
|
|
|
Str_Concat (Query,
|
|
|
|
|
NumItemInList ? " OR tst_tags.TagTxt='" :
|
|
|
|
|
" AND (tst_tags.TagTxt='",
|
2021-10-29 00:32:19 +02:00
|
|
|
|
Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
Str_Concat (Query,TagText,Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
Str_Concat (Query,"'",Qst_MAX_BYTES_QUERY_QUESTIONS);
|
2021-10-27 21:40:19 +02:00
|
|
|
|
NumItemInList++;
|
|
|
|
|
}
|
2021-10-29 00:32:19 +02:00
|
|
|
|
Str_Concat (Query,")",Qst_MAX_BYTES_QUERY_QUESTIONS);
|
2021-10-27 21:40:19 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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;
|
2021-10-29 00:32:19 +02:00
|
|
|
|
if (LengthQuery > Qst_MAX_BYTES_QUERY_QUESTIONS - 128)
|
2021-10-27 21:40:19 +02:00
|
|
|
|
Err_QuerySizeExceededExit ();
|
|
|
|
|
Str_Concat (Query,
|
|
|
|
|
NumItemInList ? " OR tst_questions.AnsType='" :
|
|
|
|
|
" AND (tst_questions.AnsType='",
|
2021-10-29 00:32:19 +02:00
|
|
|
|
Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
Str_Concat (Query,Qst_DB_StrAnswerTypes[AnswerType],Qst_MAX_BYTES_QUERY_QUESTIONS);
|
|
|
|
|
Str_Concat (Query,"'",Qst_MAX_BYTES_QUERY_QUESTIONS);
|
2021-10-27 21:40:19 +02:00
|
|
|
|
NumItemInList++;
|
|
|
|
|
}
|
2021-10-29 00:32:19 +02:00
|
|
|
|
Str_Concat (Query,")",Qst_MAX_BYTES_QUERY_QUESTIONS);
|
2021-10-27 21:40:19 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* End query */
|
2021-10-29 00:32:19 +02:00
|
|
|
|
Str_Concat (Query," ORDER BY RAND() LIMIT ",Qst_MAX_BYTES_QUERY_QUESTIONS);
|
2021-10-27 21:40:19 +02:00
|
|
|
|
snprintf (StrNumQsts,sizeof (StrNumQsts),"%u",Questions->NumQsts);
|
2021-10-29 00:32:19 +02:00
|
|
|
|
Str_Concat (Query,StrNumQsts,Qst_MAX_BYTES_QUERY_QUESTIONS);
|
2021-10-27 21:40:19 +02:00
|
|
|
|
/*
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-08 20:52:40 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************* Get one question for trivial game **********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
unsigned Qst_DB_GetTrivialQst (MYSQL_RES **mysql_res,
|
|
|
|
|
char DegreesStr[API_MAX_BYTES_DEGREES_STR + 1],
|
|
|
|
|
float lowerScore,float upperScore)
|
|
|
|
|
{
|
|
|
|
|
unsigned NumQsts;
|
|
|
|
|
|
|
|
|
|
Str_SetDecimalPointToUS (); // To print the floating point as a dot
|
|
|
|
|
|
|
|
|
|
NumQsts = (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get test questions",
|
|
|
|
|
"SELECT DISTINCT "
|
|
|
|
|
"tst_questions.QstCod," // row[0]
|
|
|
|
|
"tst_questions.AnsType," // row[1]
|
|
|
|
|
"tst_questions.Shuffle," // row[2]
|
|
|
|
|
"tst_questions.Stem," // row[3]
|
|
|
|
|
"tst_questions.Feedback," // row[4]
|
|
|
|
|
"tst_questions.Score/tst_questions.NumHits AS S" // row[5]
|
|
|
|
|
" FROM crs_courses,"
|
|
|
|
|
"tst_questions"
|
|
|
|
|
" WHERE crs_courses.DegCod IN (%s)"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.AnsType='unique_choice'"
|
|
|
|
|
" AND tst_questions.NumHits>0"
|
|
|
|
|
" AND tst_questions.QstCod NOT IN"
|
|
|
|
|
" (SELECT tst_question_tags.QstCod"
|
|
|
|
|
" FROM crs_courses,"
|
|
|
|
|
"tst_tags,"
|
|
|
|
|
"tst_question_tags"
|
|
|
|
|
" WHERE crs_courses.DegCod IN (%s)"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_tags.CrsCod"
|
|
|
|
|
" AND tst_tags.TagHidden='Y'"
|
|
|
|
|
" AND tst_tags.TagCod=tst_question_tags.TagCod)"
|
|
|
|
|
" HAVING S>='%f'"
|
|
|
|
|
" AND S<='%f'"
|
|
|
|
|
" ORDER BY RAND()"
|
|
|
|
|
" LIMIT 1",
|
|
|
|
|
DegreesStr,
|
|
|
|
|
DegreesStr,
|
|
|
|
|
lowerScore,
|
|
|
|
|
upperScore);
|
|
|
|
|
|
|
|
|
|
Str_SetDecimalPointToLocal (); // Return to local system
|
|
|
|
|
|
|
|
|
|
return NumQsts;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-09 00:38:11 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/** Get number of visible test questions from database giving a course code **/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
unsigned Qst_DB_GetNumQstsInCrs (long CrsCod)
|
|
|
|
|
{
|
|
|
|
|
/***** Get number of questions *****/
|
|
|
|
|
// Reject questions with any tag hidden
|
|
|
|
|
// Select only questions with tags
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of test questions",
|
|
|
|
|
"SELECT COUNT(*)"
|
|
|
|
|
" 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",
|
|
|
|
|
CrsCod,
|
|
|
|
|
CrsCod,
|
|
|
|
|
CrsCod);
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-27 21:40:19 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********************** Get number of test questions ************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
// Returns the number of test questions
|
|
|
|
|
// in this location (all the platform, current degree or current course)
|
|
|
|
|
|
|
|
|
|
unsigned Qst_DB_GetNumQsts (MYSQL_RES **mysql_res,
|
|
|
|
|
HieLvl_Level_t Scope,Qst_AnswerType_t AnsType)
|
|
|
|
|
{
|
|
|
|
|
switch (Scope)
|
|
|
|
|
{
|
|
|
|
|
case HieLvl_SYS:
|
|
|
|
|
if (AnsType == Qst_ANS_UNKNOWN) // Any type
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get number of test questions",
|
|
|
|
|
"SELECT COUNT(*)," // row[0]
|
|
|
|
|
"SUM(NumHits)," // row[1]
|
|
|
|
|
"SUM(Score)" // row[2]
|
|
|
|
|
" FROM tst_questions");
|
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get number of test questions",
|
|
|
|
|
"SELECT COUNT(*)," // row[0]
|
|
|
|
|
"SUM(NumHits)," // row[1]
|
|
|
|
|
"SUM(Score)" // row[2]
|
|
|
|
|
" FROM tst_questions"
|
|
|
|
|
" WHERE AnsType='%s'",
|
|
|
|
|
Qst_DB_StrAnswerTypes[AnsType]);
|
|
|
|
|
case HieLvl_CTY:
|
|
|
|
|
if (AnsType == Qst_ANS_UNKNOWN) // Any type
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get number of test questions",
|
|
|
|
|
"SELECT COUNT(*)," // row[0]
|
|
|
|
|
"SUM(NumHits)," // row[1]
|
|
|
|
|
"SUM(Score)" // row[2]
|
|
|
|
|
" FROM ins_instits,"
|
|
|
|
|
"ctr_centers,"
|
|
|
|
|
"deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions"
|
|
|
|
|
" WHERE ins_instits.CtyCod=%ld"
|
|
|
|
|
" AND ins_instits.InsCod=ctr_centers.InsCod"
|
|
|
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod",
|
|
|
|
|
Gbl.Hierarchy.Cty.CtyCod);
|
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get number of test questions",
|
|
|
|
|
"SELECT COUNT(*)," // row[0]
|
|
|
|
|
"SUM(NumHits)," // row[1]
|
|
|
|
|
"SUM(Score)" // row[2]
|
|
|
|
|
" FROM ins_instits,"
|
|
|
|
|
"ctr_centers,"
|
|
|
|
|
"deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions"
|
|
|
|
|
" WHERE ins_instits.CtyCod=%ld"
|
|
|
|
|
" AND ins_instits.InsCod=ctr_centers.InsCod"
|
|
|
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.AnsType='%s'",
|
|
|
|
|
Gbl.Hierarchy.Cty.CtyCod,
|
|
|
|
|
Qst_DB_StrAnswerTypes[AnsType]);
|
|
|
|
|
case HieLvl_INS:
|
|
|
|
|
if (AnsType == Qst_ANS_UNKNOWN) // Any type
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get number of test questions",
|
|
|
|
|
"SELECT COUNT(*)," // row[0]
|
|
|
|
|
"SUM(NumHits)," // row[1]
|
|
|
|
|
"SUM(Score)" // row[2]
|
|
|
|
|
" FROM ctr_centers,"
|
|
|
|
|
"deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions"
|
|
|
|
|
" WHERE ctr_centers.InsCod=%ld"
|
|
|
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod",
|
|
|
|
|
Gbl.Hierarchy.Ins.InsCod);
|
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get number of test questions",
|
|
|
|
|
"SELECT COUNT(*)," // row[0]
|
|
|
|
|
"SUM(NumHits)," // row[1]
|
|
|
|
|
"SUM(Score)" // row[2]
|
|
|
|
|
" FROM ctr_centers,"
|
|
|
|
|
"deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions"
|
|
|
|
|
" WHERE ctr_centers.InsCod=%ld"
|
|
|
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.AnsType='%s'",
|
|
|
|
|
Gbl.Hierarchy.Ins.InsCod,
|
|
|
|
|
Qst_DB_StrAnswerTypes[AnsType]);
|
|
|
|
|
case HieLvl_CTR:
|
|
|
|
|
if (AnsType == Qst_ANS_UNKNOWN) // Any type
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get number of test questions",
|
|
|
|
|
"SELECT COUNT(*)," // row[0]
|
|
|
|
|
"SUM(NumHits)," // row[1]
|
|
|
|
|
"SUM(Score)" // row[2]
|
|
|
|
|
" FROM deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions"
|
|
|
|
|
" WHERE deg_degrees.CtrCod=%ld"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod",
|
|
|
|
|
Gbl.Hierarchy.Ctr.CtrCod);
|
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get number of test questions",
|
|
|
|
|
"SELECT COUNT(*)," // row[0]
|
|
|
|
|
"SUM(NumHits)," // row[1]
|
|
|
|
|
"SUM(Score)" // row[2]
|
|
|
|
|
" FROM deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions"
|
|
|
|
|
" WHERE deg_degrees.CtrCod=%ld"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.AnsType='%s'",
|
|
|
|
|
Gbl.Hierarchy.Ctr.CtrCod,
|
|
|
|
|
Qst_DB_StrAnswerTypes[AnsType]);
|
|
|
|
|
case HieLvl_DEG:
|
|
|
|
|
if (AnsType == Qst_ANS_UNKNOWN) // Any type
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get number of test questions",
|
|
|
|
|
"SELECT COUNT(*)," // row[0]
|
|
|
|
|
"SUM(NumHits)," // row[1]
|
|
|
|
|
"SUM(Score)" // row[2]
|
|
|
|
|
" FROM crs_courses,"
|
|
|
|
|
"tst_questions"
|
|
|
|
|
" WHERE crs_courses.DegCod=%ld"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod",
|
|
|
|
|
Gbl.Hierarchy.Deg.DegCod);
|
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get number of test questions",
|
|
|
|
|
"SELECT COUNT(*)," // row[0]
|
|
|
|
|
"SUM(NumHits)," // row[1]
|
|
|
|
|
"SUM(Score)" // row[2]
|
|
|
|
|
" FROM crs_courses,"
|
|
|
|
|
"tst_questions"
|
|
|
|
|
" WHERE crs_courses.DegCod=%ld"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.AnsType='%s'",
|
|
|
|
|
Gbl.Hierarchy.Deg.DegCod,
|
|
|
|
|
Qst_DB_StrAnswerTypes[AnsType]);
|
|
|
|
|
case HieLvl_CRS:
|
|
|
|
|
if (AnsType == Qst_ANS_UNKNOWN) // Any type
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get number of test questions",
|
|
|
|
|
"SELECT COUNT(*)," // row[0]
|
|
|
|
|
"SUM(NumHits)," // row[1]
|
|
|
|
|
"SUM(Score)" // row[2]
|
|
|
|
|
" FROM tst_questions"
|
|
|
|
|
" WHERE CrsCod=%ld",
|
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod);
|
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get number of test questions",
|
|
|
|
|
"SELECT COUNT(*)," // row[0]
|
|
|
|
|
"SUM(NumHits)," // row[1]
|
|
|
|
|
"SUM(Score)" // row[2]
|
|
|
|
|
" FROM tst_questions"
|
|
|
|
|
" WHERE CrsCod=%ld"
|
|
|
|
|
" AND AnsType='%s'",
|
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod,
|
|
|
|
|
Qst_DB_StrAnswerTypes[AnsType]);
|
|
|
|
|
default:
|
|
|
|
|
Err_WrongScopeExit ();
|
|
|
|
|
return 0; // Not reached
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/**************** Get number of courses with test questions ******************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
// Returns the number of courses with test questions
|
|
|
|
|
// in this location (all the platform, current degree or current course)
|
|
|
|
|
|
|
|
|
|
unsigned Qst_DB_GetNumCrssWithQsts (HieLvl_Level_t Scope,
|
|
|
|
|
Qst_AnswerType_t AnsType)
|
|
|
|
|
{
|
|
|
|
|
extern const char *Qst_DB_StrAnswerTypes[Qst_NUM_ANS_TYPES];
|
|
|
|
|
|
|
|
|
|
/***** Get number of courses with test questions from database *****/
|
|
|
|
|
switch (Scope)
|
|
|
|
|
{
|
|
|
|
|
case HieLvl_SYS:
|
|
|
|
|
if (AnsType == Qst_ANS_UNKNOWN) // Any type
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT CrsCod)"
|
|
|
|
|
" FROM tst_questions");
|
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT CrsCod)"
|
|
|
|
|
" FROM tst_questions"
|
|
|
|
|
" WHERE AnsType='%s'",
|
|
|
|
|
Qst_DB_StrAnswerTypes[AnsType]);
|
|
|
|
|
case HieLvl_CTY:
|
|
|
|
|
if (AnsType == Qst_ANS_UNKNOWN) // Any type
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
|
|
|
|
" FROM ins_instits,"
|
|
|
|
|
"ctr_centers,"
|
|
|
|
|
"deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions"
|
|
|
|
|
" WHERE ins_instits.CtyCod=%ld"
|
|
|
|
|
" AND ins_instits.InsCod=ctr_centers.InsCod"
|
|
|
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod",
|
|
|
|
|
Gbl.Hierarchy.Cty.CtyCod);
|
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
|
|
|
|
" FROM ins_instits,"
|
|
|
|
|
"ctr_centers,"
|
|
|
|
|
"deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions"
|
|
|
|
|
" WHERE ins_instits.CtyCod=%ld"
|
|
|
|
|
" AND ins_instits.InsCod=ctr_centers.InsCod"
|
|
|
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.AnsType='%s'",
|
|
|
|
|
Gbl.Hierarchy.Cty.CtyCod,
|
|
|
|
|
Qst_DB_StrAnswerTypes[AnsType]);
|
|
|
|
|
case HieLvl_INS:
|
|
|
|
|
if (AnsType == Qst_ANS_UNKNOWN) // Any type
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
|
|
|
|
" FROM ctr_centers,"
|
|
|
|
|
"deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions"
|
|
|
|
|
" WHERE ctr_centers.InsCod=%ld"
|
|
|
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod",
|
|
|
|
|
Gbl.Hierarchy.Ins.InsCod);
|
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
|
|
|
|
" FROM ctr_centers,"
|
|
|
|
|
"deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions"
|
|
|
|
|
" WHERE ctr_centers.InsCod=%ld"
|
|
|
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.AnsType='%s'",
|
|
|
|
|
Gbl.Hierarchy.Ins.InsCod,
|
|
|
|
|
Qst_DB_StrAnswerTypes[AnsType]);
|
|
|
|
|
case HieLvl_CTR:
|
|
|
|
|
if (AnsType == Qst_ANS_UNKNOWN) // Any type
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
|
|
|
|
" FROM deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions"
|
|
|
|
|
" WHERE deg_degrees.CtrCod=%ld"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod",
|
|
|
|
|
Gbl.Hierarchy.Ctr.CtrCod);
|
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
|
|
|
|
" FROM deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions"
|
|
|
|
|
" WHERE deg_degrees.CtrCod=%ld"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.AnsType='%s'",
|
|
|
|
|
Gbl.Hierarchy.Ctr.CtrCod,
|
|
|
|
|
Qst_DB_StrAnswerTypes[AnsType]);
|
|
|
|
|
case HieLvl_DEG:
|
|
|
|
|
if (AnsType == Qst_ANS_UNKNOWN) // Any type
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with test questions",
|
|
|
|
|
"SELECT COUNTDISTINCT (tst_questions.CrsCod)"
|
|
|
|
|
" FROM crs_courses,"
|
|
|
|
|
"tst_questions"
|
|
|
|
|
" WHERE crs_courses.DegCod=%ld"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod",
|
|
|
|
|
Gbl.Hierarchy.Deg.DegCod);
|
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
|
|
|
|
" FROM crs_courses,"
|
|
|
|
|
"tst_questions"
|
|
|
|
|
" WHERE crs_courses.DegCod=%ld"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.AnsType='%s'",
|
|
|
|
|
Gbl.Hierarchy.Deg.DegCod,
|
|
|
|
|
Qst_DB_StrAnswerTypes[AnsType]);
|
|
|
|
|
case HieLvl_CRS:
|
|
|
|
|
if (AnsType == Qst_ANS_UNKNOWN) // Any type
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT CrsCod)"
|
|
|
|
|
" FROM tst_questions"
|
|
|
|
|
" WHERE CrsCod=%ld",
|
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod);
|
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT CrsCod)"
|
|
|
|
|
" FROM tst_questions"
|
|
|
|
|
" WHERE CrsCod=%ld"
|
|
|
|
|
" AND AnsType='%s'",
|
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod,
|
|
|
|
|
Qst_DB_StrAnswerTypes[AnsType]);
|
|
|
|
|
default:
|
|
|
|
|
Err_WrongScopeExit ();
|
|
|
|
|
return 0; // Not reached
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********** Get number of courses with pluggable test questions *************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
// Returns the number of courses with pluggable test questions
|
|
|
|
|
// in this location (all the platform, current degree or current course)
|
|
|
|
|
|
|
|
|
|
unsigned Qst_DB_GetNumCrssWithPluggableQsts (HieLvl_Level_t Scope,
|
|
|
|
|
Qst_AnswerType_t AnsType)
|
|
|
|
|
{
|
|
|
|
|
extern const char *Qst_DB_StrAnswerTypes[Qst_NUM_ANS_TYPES];
|
|
|
|
|
extern const char *Tst_DB_Pluggable[TstCfg_NUM_OPTIONS_PLUGGABLE];
|
|
|
|
|
|
|
|
|
|
/***** Get number of courses with test questions from database *****/
|
|
|
|
|
switch (Scope)
|
|
|
|
|
{
|
|
|
|
|
case HieLvl_SYS:
|
|
|
|
|
if (AnsType == Qst_ANS_UNKNOWN) // Any type
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with pluggable test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
|
|
|
|
" FROM tst_questions,"
|
|
|
|
|
"tst_config"
|
|
|
|
|
" WHERE tst_questions.CrsCod=tst_config.CrsCod"
|
|
|
|
|
" AND tst_config.pluggable='%s'",
|
|
|
|
|
Tst_DB_Pluggable[TstCfg_PLUGGABLE_YES]);
|
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with pluggable test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
|
|
|
|
" FROM tst_questions,"
|
|
|
|
|
"tst_config"
|
|
|
|
|
" WHERE tst_questions.AnsType='%s'"
|
|
|
|
|
" AND tst_questions.CrsCod=tst_config.CrsCod"
|
|
|
|
|
" AND tst_config.pluggable='%s'",
|
|
|
|
|
Qst_DB_StrAnswerTypes[AnsType],
|
|
|
|
|
Tst_DB_Pluggable[TstCfg_PLUGGABLE_YES]);
|
|
|
|
|
case HieLvl_CTY:
|
|
|
|
|
if (AnsType == Qst_ANS_UNKNOWN) // Any type
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with pluggable test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
|
|
|
|
" FROM ins_instits,"
|
|
|
|
|
"ctr_centers,"
|
|
|
|
|
"deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions,"
|
|
|
|
|
"tst_config"
|
|
|
|
|
" WHERE ins_instits.CtyCod=%ld"
|
|
|
|
|
" AND ins_instits.InsCod=ctr_centers.InsCod"
|
|
|
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.CrsCod=tst_config.CrsCod"
|
|
|
|
|
" AND tst_config.pluggable='%s'",
|
|
|
|
|
Gbl.Hierarchy.Cty.CtyCod,
|
|
|
|
|
Tst_DB_Pluggable[TstCfg_PLUGGABLE_YES]);
|
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with pluggable test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
|
|
|
|
" FROM ins_instits,"
|
|
|
|
|
"ctr_centers,"
|
|
|
|
|
"deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions,"
|
|
|
|
|
"tst_config"
|
|
|
|
|
" WHERE ins_instits.CtyCod=%ld"
|
|
|
|
|
" AND ins_instits.InsCod=ctr_centers.InsCod"
|
|
|
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.AnsType='%s'"
|
|
|
|
|
" AND tst_questions.CrsCod=tst_config.CrsCod"
|
|
|
|
|
" AND tst_config.pluggable='%s'",
|
|
|
|
|
Gbl.Hierarchy.Cty.CtyCod,
|
|
|
|
|
Qst_DB_StrAnswerTypes[AnsType],
|
|
|
|
|
Tst_DB_Pluggable[TstCfg_PLUGGABLE_YES]);
|
|
|
|
|
case HieLvl_INS:
|
|
|
|
|
if (AnsType == Qst_ANS_UNKNOWN) // Any type
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with pluggable test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
|
|
|
|
" FROM ctr_centers,"
|
|
|
|
|
"deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions,"
|
|
|
|
|
"tst_config"
|
|
|
|
|
" WHERE ctr_centers.InsCod=%ld"
|
|
|
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.CrsCod=tst_config.CrsCod"
|
|
|
|
|
" AND tst_config.pluggable='%s'",
|
|
|
|
|
Gbl.Hierarchy.Ins.InsCod,
|
|
|
|
|
Tst_DB_Pluggable[TstCfg_PLUGGABLE_YES]);
|
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with pluggable test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
|
|
|
|
" FROM ctr_centers,"
|
|
|
|
|
"deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions,"
|
|
|
|
|
"tst_config"
|
|
|
|
|
" WHERE ctr_centers.InsCod=%ld"
|
|
|
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.AnsType='%s'"
|
|
|
|
|
" AND tst_questions.CrsCod=tst_config.CrsCod"
|
|
|
|
|
" AND tst_config.pluggable='%s'",
|
|
|
|
|
Gbl.Hierarchy.Ins.InsCod,
|
|
|
|
|
Qst_DB_StrAnswerTypes[AnsType],
|
|
|
|
|
Tst_DB_Pluggable[TstCfg_PLUGGABLE_YES]);
|
|
|
|
|
case HieLvl_CTR:
|
|
|
|
|
if (AnsType == Qst_ANS_UNKNOWN) // Any type
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with pluggable test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
|
|
|
|
" FROM deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions,"
|
|
|
|
|
"tst_config"
|
|
|
|
|
" WHERE deg_degrees.CtrCod=%ld"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.CrsCod=tst_config.CrsCod"
|
|
|
|
|
" AND tst_config.pluggable='%s'",
|
|
|
|
|
Gbl.Hierarchy.Ctr.CtrCod,
|
|
|
|
|
Tst_DB_Pluggable[TstCfg_PLUGGABLE_YES]);
|
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with pluggable test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
|
|
|
|
" FROM deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions,"
|
|
|
|
|
"tst_config"
|
|
|
|
|
" WHERE deg_degrees.CtrCod=%ld"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.AnsType='%s'"
|
|
|
|
|
" AND tst_questions.CrsCod=tst_config.CrsCod"
|
|
|
|
|
" AND tst_config.pluggable='%s'",
|
|
|
|
|
Gbl.Hierarchy.Ctr.CtrCod,
|
|
|
|
|
Qst_DB_StrAnswerTypes[AnsType],
|
|
|
|
|
Tst_DB_Pluggable[TstCfg_PLUGGABLE_YES]);
|
|
|
|
|
case HieLvl_DEG:
|
|
|
|
|
if (AnsType == Qst_ANS_UNKNOWN) // Any type
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with pluggable test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
|
|
|
|
" FROM crs_courses,"
|
|
|
|
|
"tst_questions,"
|
|
|
|
|
"tst_config"
|
|
|
|
|
" WHERE crs_courses.DegCod=%ld"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.CrsCod=tst_config.CrsCod"
|
|
|
|
|
" AND tst_config.pluggable='%s'",
|
|
|
|
|
Gbl.Hierarchy.Deg.DegCod,
|
|
|
|
|
Tst_DB_Pluggable[TstCfg_PLUGGABLE_YES]);
|
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with pluggable test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
|
|
|
|
" FROM crs_courses,"
|
|
|
|
|
"tst_questions,"
|
|
|
|
|
"tst_config"
|
|
|
|
|
" WHERE crs_courses.DegCod=%ld"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.AnsType='%s'"
|
|
|
|
|
" AND tst_questions.CrsCod=tst_config.CrsCod"
|
|
|
|
|
" AND tst_config.pluggable='%s'",
|
|
|
|
|
Gbl.Hierarchy.Deg.DegCod,
|
|
|
|
|
Qst_DB_StrAnswerTypes[AnsType],
|
|
|
|
|
Tst_DB_Pluggable[TstCfg_PLUGGABLE_YES]);
|
|
|
|
|
case HieLvl_CRS:
|
|
|
|
|
if (AnsType == Qst_ANS_UNKNOWN) // Any type
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with pluggable test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
|
|
|
|
" FROM tst_questions,"
|
|
|
|
|
"tst_config"
|
|
|
|
|
" WHERE tst_questions.CrsCod=%ld"
|
|
|
|
|
" AND tst_questions.CrsCod=tst_config.CrsCod"
|
|
|
|
|
" AND tst_config.pluggable='%s'",
|
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod,
|
|
|
|
|
Tst_DB_Pluggable[TstCfg_PLUGGABLE_YES]);
|
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with pluggable test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
|
|
|
|
" FROM tst_questions,"
|
|
|
|
|
"tst_config"
|
|
|
|
|
" WHERE tst_questions.CrsCod=%ld"
|
|
|
|
|
" AND tst_questions.AnsType='%s'"
|
|
|
|
|
" AND tst_questions.CrsCod=tst_config.CrsCod"
|
|
|
|
|
" AND tst_config.pluggable='%s'",
|
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod,
|
|
|
|
|
Qst_DB_StrAnswerTypes[AnsType],
|
|
|
|
|
Tst_DB_Pluggable[TstCfg_PLUGGABLE_YES]);
|
|
|
|
|
default:
|
|
|
|
|
Err_WrongScopeExit ();
|
|
|
|
|
return 0; // Not reached
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-10-28 18:48:21 +02:00
|
|
|
|
|
2021-11-09 00:38:11 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******* Get recent test questions from database giving a course code ********/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
unsigned Qst_DB_GetRecentQuestions (MYSQL_RES **mysql_res,
|
|
|
|
|
long CrsCod,time_t BeginTime)
|
|
|
|
|
{
|
|
|
|
|
// DISTINCT is necessary to not repeat questions
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get test questions",
|
|
|
|
|
"SELECT DISTINCT "
|
|
|
|
|
"tst_questions.QstCod," // row[0]
|
|
|
|
|
"tst_questions.AnsType," // row[1]
|
|
|
|
|
"tst_questions.Shuffle," // row[2]
|
|
|
|
|
"tst_questions.Stem," // row[3]
|
|
|
|
|
"tst_questions.Feedback" // row[4]
|
|
|
|
|
" 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"
|
|
|
|
|
" AND (tst_questions.EditTime>=FROM_UNIXTIME(%ld)"
|
|
|
|
|
" OR "
|
|
|
|
|
"tst_tags.ChangeTime>=FROM_UNIXTIME(%ld))"
|
|
|
|
|
" ORDER BY QstCod",
|
|
|
|
|
CrsCod,
|
|
|
|
|
CrsCod,
|
|
|
|
|
CrsCod,
|
|
|
|
|
(long) BeginTime,
|
|
|
|
|
(long) BeginTime);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/** Get answers of recent test questions from database giving a course code **/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
unsigned Qst_DB_GetRecentAnswers (MYSQL_RES **mysql_res,
|
|
|
|
|
long CrsCod,time_t BeginTime)
|
|
|
|
|
{
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get test answers",
|
|
|
|
|
"SELECT QstCod," // row[0]
|
|
|
|
|
"AnsInd," // row[1]
|
|
|
|
|
"Correct," // row[2]
|
|
|
|
|
"Answer," // row[3]
|
|
|
|
|
"Feedback" // row[4]
|
|
|
|
|
" FROM tst_answers"
|
|
|
|
|
" WHERE QstCod IN "
|
|
|
|
|
"(SELECT tst_questions.QstCod"
|
|
|
|
|
" 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"
|
|
|
|
|
" AND (tst_questions.EditTime>=FROM_UNIXTIME(%ld)"
|
|
|
|
|
" OR "
|
|
|
|
|
"tst_tags.ChangeTime>=FROM_UNIXTIME(%ld)))"
|
|
|
|
|
" ORDER BY QstCod,"
|
|
|
|
|
"AnsInd",
|
|
|
|
|
CrsCod,
|
|
|
|
|
CrsCod,
|
|
|
|
|
CrsCod,
|
|
|
|
|
(long) BeginTime,
|
|
|
|
|
(long) BeginTime);
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-29 00:32:19 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/****************** Get data of a question from database *********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
unsigned Qst_DB_GetQstData (MYSQL_RES **mysql_res,long QstCod)
|
|
|
|
|
{
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get a question",
|
|
|
|
|
"SELECT UNIX_TIMESTAMP(EditTime)," // row[0]
|
|
|
|
|
"AnsType," // row[1]
|
|
|
|
|
"Shuffle," // row[2]
|
|
|
|
|
"Stem," // row[3]
|
|
|
|
|
"Feedback," // row[4]
|
|
|
|
|
"MedCod," // row[5]
|
|
|
|
|
"NumHits," // row[6]
|
|
|
|
|
"NumHitsNotBlank," // row[7]
|
|
|
|
|
"Score" // row[8]
|
|
|
|
|
" FROM tst_questions"
|
|
|
|
|
" WHERE QstCod=%ld"
|
|
|
|
|
" AND CrsCod=%ld", // Extra check
|
|
|
|
|
QstCod,
|
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*************** Get answer type of a question from database *****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
Qst_AnswerType_t Qst_DB_GetQstAnswerType (long QstCod)
|
|
|
|
|
{
|
|
|
|
|
char StrAnsTypeDB[256];
|
|
|
|
|
|
|
|
|
|
/***** Get type of answer from database *****/
|
|
|
|
|
DB_QuerySELECTString (StrAnsTypeDB,sizeof (StrAnsTypeDB) - 1,
|
|
|
|
|
"can not get the type of a question",
|
|
|
|
|
"SELECT AnsType"
|
|
|
|
|
" FROM tst_questions"
|
|
|
|
|
" WHERE QstCod=%ld",
|
|
|
|
|
QstCod);
|
|
|
|
|
return Qst_ConvertFromStrAnsTypDBToAnsTyp (StrAnsTypeDB);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******** Get media code associated with the stem of a test question *********/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
long Qst_DB_GetQstMedCod (long CrsCod,long QstCod)
|
|
|
|
|
{
|
|
|
|
|
return DB_QuerySELECTCode ("can not get media",
|
|
|
|
|
"SELECT MedCod"
|
|
|
|
|
" FROM tst_questions"
|
|
|
|
|
" WHERE QstCod=%ld"
|
|
|
|
|
" AND CrsCod=%ld", // Extra check
|
|
|
|
|
QstCod,
|
|
|
|
|
CrsCod);
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-30 20:50:29 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/****************** 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);
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-28 18:48:21 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************ Get number of answers of a question from database **************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
unsigned Qst_DB_GetNumAnswersQst (long QstCod)
|
|
|
|
|
{
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of answers of a question",
|
|
|
|
|
"SELECT COUNT(*)"
|
|
|
|
|
" FROM tst_answers"
|
|
|
|
|
" WHERE QstCod=%ld",
|
|
|
|
|
QstCod);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************** Get answers of a question from database *******************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
unsigned Qst_DB_GetDataOfAnswers (MYSQL_RES **mysql_res,long QstCod,bool Shuffle)
|
|
|
|
|
{
|
|
|
|
|
unsigned NumOptions;
|
|
|
|
|
|
|
|
|
|
if (!(NumOptions = (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get answers of a question",
|
|
|
|
|
"SELECT AnsInd," // row[0]
|
|
|
|
|
"Answer," // row[1]
|
|
|
|
|
"Feedback," // row[2]
|
|
|
|
|
"MedCod," // row[3]
|
|
|
|
|
"Correct" // row[4]
|
|
|
|
|
" FROM tst_answers"
|
|
|
|
|
" WHERE QstCod=%ld"
|
|
|
|
|
" ORDER BY %s",
|
|
|
|
|
QstCod,
|
|
|
|
|
Shuffle ? "RAND()" :
|
|
|
|
|
"AnsInd")))
|
|
|
|
|
Err_WrongAnswerExit ();
|
|
|
|
|
|
|
|
|
|
return NumOptions;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************** Get answers of a question from database *******************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
unsigned Qst_DB_GetTextOfAnswers (MYSQL_RES **mysql_res,long QstCod)
|
|
|
|
|
{
|
|
|
|
|
unsigned NumOptions;
|
|
|
|
|
|
|
|
|
|
if (!(NumOptions = (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get answers of a question",
|
|
|
|
|
"SELECT Answer" // row[0]
|
|
|
|
|
" FROM tst_answers"
|
|
|
|
|
" WHERE QstCod=%ld"
|
|
|
|
|
" ORDER BY AnsInd",
|
|
|
|
|
QstCod)))
|
|
|
|
|
Err_WrongAnswerExit ();
|
|
|
|
|
|
|
|
|
|
return NumOptions;
|
|
|
|
|
}
|
2021-10-29 00:32:19 +02:00
|
|
|
|
|
2021-11-25 15:39:32 +01:00
|
|
|
|
/*****************************************************************************/
|
2021-11-28 00:49:23 +01:00
|
|
|
|
/*************** Get answers correctness for a question **********************/
|
2021-11-25 15:39:32 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
unsigned Qst_DB_GetQstAnswersCorr (MYSQL_RES **mysql_res,long QstCod)
|
|
|
|
|
{
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get correctness of answers of a question",
|
|
|
|
|
"SELECT Correct" // row[0]
|
|
|
|
|
" FROM tst_answers"
|
|
|
|
|
" WHERE QstCod=%ld"
|
|
|
|
|
" ORDER BY AnsInd",
|
|
|
|
|
QstCod);
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-29 00:32:19 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********** Get suffled/not-shuffled answers indexes of question ************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
unsigned Qst_DB_GetShuffledAnswersIndexes (MYSQL_RES **mysql_res,
|
|
|
|
|
const struct Qst_Question *Question)
|
|
|
|
|
{
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get questions of a game",
|
|
|
|
|
"SELECT AnsInd" // row[0]
|
|
|
|
|
" FROM tst_answers"
|
|
|
|
|
" WHERE QstCod=%ld"
|
|
|
|
|
" ORDER BY %s",
|
|
|
|
|
Question->QstCod,
|
|
|
|
|
Question->Answer.Shuffle ? "RAND()" : // Use RAND() because is really random; RAND(NOW()) repeats order
|
|
|
|
|
"AnsInd");
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-29 19:48:36 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/****** Get media codes associated to stems of test questions in course ******/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
unsigned Qst_DB_GetMedCodsFromStemsOfQstsInCrs (MYSQL_RES **mysql_res,long CrsCod)
|
|
|
|
|
{
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get media",
|
|
|
|
|
"SELECT MedCod" // row[0]
|
|
|
|
|
" FROM tst_questions"
|
|
|
|
|
" WHERE CrsCod=%ld",
|
|
|
|
|
CrsCod);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***** Get media codes associated to answers of test questions in course *****/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
unsigned Qst_DB_GetMedCodsFromAnssOfQstsInCrs (MYSQL_RES **mysql_res,long CrsCod)
|
|
|
|
|
{
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get media",
|
|
|
|
|
"SELECT tst_answers.MedCod"
|
|
|
|
|
" FROM tst_questions,"
|
|
|
|
|
"tst_answers"
|
|
|
|
|
" WHERE tst_questions.CrsCod=%ld"
|
|
|
|
|
" AND tst_questions.QstCod=tst_answers.QstCod",
|
|
|
|
|
CrsCod);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************** Get media code associated to stem of a question **************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
unsigned Qst_DB_GetMedCodFromStemOfQst (MYSQL_RES **mysql_res,long CrsCod,long QstCod)
|
|
|
|
|
{
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get media",
|
|
|
|
|
"SELECT MedCod"
|
|
|
|
|
" FROM tst_questions"
|
|
|
|
|
" WHERE QstCod=%ld"
|
|
|
|
|
" AND CrsCod=%ld", // Extra check
|
|
|
|
|
QstCod,
|
|
|
|
|
CrsCod);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************** Get media code associated to stem of a question **************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
unsigned Qst_DB_GetMedCodsFromAnssOfQst (MYSQL_RES **mysql_res,long CrsCod,long QstCod)
|
|
|
|
|
{
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get media",
|
|
|
|
|
"SELECT tst_answers.MedCod"
|
|
|
|
|
" FROM tst_answers,"
|
|
|
|
|
"tst_questions"
|
|
|
|
|
" WHERE tst_answers.QstCod=%ld"
|
|
|
|
|
" AND tst_answers.QstCod=tst_questions.QstCod"
|
|
|
|
|
" AND tst_questions.CrsCod=%ld" // Extra check
|
|
|
|
|
" AND tst_questions.QstCod=%ld", // Extra check
|
|
|
|
|
QstCod,
|
|
|
|
|
CrsCod,
|
|
|
|
|
QstCod);
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-29 00:32:19 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********* Get media code associated to an answer of a test question *********/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2021-10-29 19:48:36 +02:00
|
|
|
|
long Qst_DB_GetMedCodFromAnsOfQst (long QstCod,unsigned AnsInd)
|
2021-10-29 00:32:19 +02:00
|
|
|
|
{
|
|
|
|
|
return DB_QuerySELECTCode ("can not get media",
|
|
|
|
|
"SELECT MedCod"
|
|
|
|
|
" FROM tst_answers"
|
|
|
|
|
" WHERE QstCod=%ld"
|
|
|
|
|
" AND AnsInd=%u",
|
|
|
|
|
QstCod,
|
|
|
|
|
AnsInd);
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-29 19:48:36 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************* Remove all questions in a course **********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void Qst_DB_RemoveQstsInCrs (long CrsCod)
|
|
|
|
|
{
|
|
|
|
|
DB_QueryDELETE ("can not remove questions in a course",
|
|
|
|
|
"DELETE FROM tst_questions"
|
|
|
|
|
" WHERE CrsCod=%ld",
|
|
|
|
|
CrsCod);
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-29 00:32:19 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************** Remove a question from database **********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void Qst_DB_RemoveQst (long CrsCod,long QstCod)
|
|
|
|
|
{
|
|
|
|
|
DB_QueryDELETE ("can not remove a question",
|
|
|
|
|
"DELETE FROM tst_questions"
|
|
|
|
|
" WHERE QstCod=%ld"
|
|
|
|
|
" AND CrsCod=%ld",
|
|
|
|
|
QstCod,
|
|
|
|
|
CrsCod);
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-29 19:48:36 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*************** Remove answers from all questions in a course ****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void Qst_DB_RemAnssFromQstsInCrs (long CrsCod)
|
|
|
|
|
{
|
|
|
|
|
DB_QueryDELETE ("can not remove answers of tests of a course",
|
|
|
|
|
"DELETE FROM tst_answers"
|
|
|
|
|
" USING tst_questions,"
|
|
|
|
|
"tst_answers"
|
|
|
|
|
" WHERE tst_questions.CrsCod=%ld"
|
|
|
|
|
" AND tst_questions.QstCod=tst_answers.QstCod",
|
|
|
|
|
CrsCod);
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-29 00:32:19 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************** Remove answers from a test question ********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void Qst_DB_RemAnsFromQst (long QstCod)
|
|
|
|
|
{
|
|
|
|
|
DB_QueryDELETE ("can not remove the answers of a question",
|
|
|
|
|
"DELETE FROM tst_answers"
|
|
|
|
|
" WHERE QstCod=%ld",
|
|
|
|
|
QstCod);
|
|
|
|
|
}
|