mirror of https://github.com/acanas/swad-core.git
Version 21.46.1: Oct 26, 2021 Queries moved to module swad_test_database.
This commit is contained in:
parent
4120cb75b7
commit
d38b53b196
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 ****************/
|
||||
/*****************************************************************************/
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
/*****************************************************************************/
|
||||
|
||||
void Err_NotEnoughMemoryExit (void);
|
||||
void Err_QuerySizeExceededExit (void);
|
||||
void Err_WrongActionExit (void);
|
||||
void Err_WrongScopeExit (void);
|
||||
void Err_WrongCountrExit (void);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 **********************/
|
||||
/*****************************************************************************/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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",
|
||||
|
|
21
swad_tag.c
21
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 ***********************/
|
||||
/*****************************************************************************/
|
||||
|
|
|
@ -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);
|
||||
|
|
233
swad_test.c
233
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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -33,14 +33,14 @@
|
|||
// #include <stddef.h> // For NULL
|
||||
// #include <stdio.h> // For asprintf
|
||||
// #include <stdlib.h> // For exit, system, malloc, free, etc
|
||||
// #include <string.h> // For string functions
|
||||
#include <string.h> // For string functions
|
||||
// #include <sys/stat.h> // For mkdir
|
||||
// #include <sys/types.h> // 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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue