Version 21.46.1: Oct 26, 2021 Queries moved to module swad_test_database.

This commit is contained in:
acanas 2021-10-26 17:33:20 +02:00
parent 4120cb75b7
commit d38b53b196
21 changed files with 274 additions and 244 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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 ****************/
/*****************************************************************************/

View File

@ -40,6 +40,7 @@
/*****************************************************************************/
void Err_NotEnoughMemoryExit (void);
void Err_QuerySizeExceededExit (void);
void Err_WrongActionExit (void);
void Err_WrongScopeExit (void);
void Err_WrongCountrExit (void);

View File

@ -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);
}

View File

@ -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;

View File

@ -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;

View File

@ -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);
}

View File

@ -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 **********************/
/*****************************************************************************/

View File

@ -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);

View File

@ -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",

View File

@ -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 ***********************/
/*****************************************************************************/

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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,