Version 21.44: Oct 25, 2021 Functions moved to module swad_question.

This commit is contained in:
acanas 2021-10-25 13:52:17 +02:00
parent b1854d0dea
commit 3bfa2807fe
12 changed files with 684 additions and 667 deletions

View File

@ -4240,7 +4240,7 @@ static int API_GetTstQuestions (struct soap *soap,
long CrsCod,long BeginTime,
struct swad__getTestsOutput *getTestsOut)
{
extern const char *Tst_StrAnswerTypesXML[Qst_NUM_ANS_TYPES];
extern const char *Qst_StrAnswerTypesXML[Qst_NUM_ANS_TYPES];
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumRow;
@ -4305,7 +4305,7 @@ static int API_GetTstQuestions (struct soap *soap,
getTestsOut->questionsArray.__ptr[NumRow].answerType =
soap_malloc (soap,Qst_MAX_BYTES_ANSWER_TYPE + 1);
Str_Copy (getTestsOut->questionsArray.__ptr[NumRow].answerType,
Tst_StrAnswerTypesXML[AnswerType],
Qst_StrAnswerTypesXML[AnswerType],
Qst_MAX_BYTES_ANSWER_TYPE);
/* Get shuffle (row[2]) */
@ -4340,7 +4340,7 @@ static int API_GetTstAnswers (struct soap *soap,
long CrsCod,long BeginTime,
struct swad__getTestsOutput *getTestsOut)
{
extern const char *Tst_StrAnswerTypesXML[Qst_NUM_ANS_TYPES];
extern const char *Qst_StrAnswerTypesXML[Qst_NUM_ANS_TYPES];
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumRow,NumRows;
@ -4440,7 +4440,7 @@ static int API_GetTstQuestionTags (struct soap *soap,
long CrsCod,long BeginTime,
struct swad__getTestsOutput *getTestsOut)
{
extern const char *Tst_StrAnswerTypesXML[Qst_NUM_ANS_TYPES];
extern const char *Qst_StrAnswerTypesXML[Qst_NUM_ANS_TYPES];
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumRow,NumRows;
@ -4527,7 +4527,7 @@ int swad__getTrivialQuestion (struct soap *soap,
char *wsKey,char *degrees,float lowerScore,float upperScore, // input
struct swad__getTrivialQuestionOutput *getTrivialQuestionOut) // output
{
extern const char *Tst_StrAnswerTypesXML[Qst_NUM_ANS_TYPES];
extern const char *Qst_StrAnswerTypesXML[Qst_NUM_ANS_TYPES];
int ReturnCode;
const char *Ptr;
char LongStr[Cns_MAX_DECIMAL_DIGITS_LONG + 1];
@ -4653,7 +4653,7 @@ int swad__getTrivialQuestion (struct soap *soap,
getTrivialQuestionOut->question.answerType =
soap_malloc (soap,Qst_MAX_BYTES_ANSWER_TYPE + 1);
Str_Copy (getTrivialQuestionOut->question.answerType,
Tst_StrAnswerTypesXML[AnswerType],Qst_MAX_BYTES_ANSWER_TYPE);
Qst_StrAnswerTypesXML[AnswerType],Qst_MAX_BYTES_ANSWER_TYPE);
/* Get shuffle (row[2]) */
getTrivialQuestionOut->question.shuffle = (row[2][0] == 'Y') ? 1 :

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.43 (2021-10-25)"
#define Log_PLATFORM_VERSION "SWAD 21.44 (2021-10-25)"
#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.44: Oct 25, 2021 Functions moved to module swad_question. (320928 lines)
Version 21.43: Oct 25, 2021 New module swad_question for test/exam/game questions. (320919 lines)
Version 21.42.2: Oct 25, 2021 Code refactoring in test questions. (320795 lines)
Version 21.42.1: Oct 25, 2021 Code refactoring in test questions. (320784 lines)

View File

@ -861,7 +861,7 @@ void Exa_DB_RemoveAllSetsFromCrs (long CrsCod)
long Exa_DB_AddQuestionToSet (long SetCod,const struct Qst_Question *Question,long MedCod)
{
extern const char *Tst_StrAnswerTypesDB[Qst_NUM_ANS_TYPES];
extern const char *Qst_DB_StrAnswerTypes[Qst_NUM_ANS_TYPES];
static char CharInvalid[Qst_NUM_VALIDITIES] =
{
[Qst_INVALID_QUESTION] = 'Y',
@ -878,7 +878,7 @@ long Exa_DB_AddQuestionToSet (long SetCod,const struct Qst_Question *Question,lo
"'%s','%s',%ld)",
SetCod,
CharInvalid[Question->Validity],
Tst_StrAnswerTypesDB[Question->Answer.Type],
Qst_DB_StrAnswerTypes[Question->Answer.Type],
Question->Answer.Shuffle ? 'Y' :
'N',
Question->Stem,

View File

@ -33,18 +33,18 @@
// #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_database.h"
#include "swad_error.h"
// #include "swad_exam_set.h"
// #include "swad_figure.h"
// #include "swad_form.h"
// #include "swad_global.h"
#include "swad_global.h"
// #include "swad_hierarchy_level.h"
// #include "swad_HTML.h"
// #include "swad_ID.h"
@ -53,9 +53,9 @@
// #include "swad_media.h"
// #include "swad_parameter.h"
#include "swad_question.h"
// #include "swad_question_import.h"
#include "swad_question_import.h"
// #include "swad_tag_database.h"
// #include "swad_test.h"
#include "swad_test.h"
// #include "swad_test_config.h"
// #include "swad_test_print.h"
// #include "swad_test_visibility.h"
@ -67,6 +67,27 @@
/***************************** Public constants ******************************/
/*****************************************************************************/
// strings are limited to Qst_MAX_BYTES_ANSWER_TYPE characters
const char *Qst_StrAnswerTypesXML[Qst_NUM_ANS_TYPES] =
{
[Qst_ANS_INT ] = "int",
[Qst_ANS_FLOAT ] = "float",
[Qst_ANS_TRUE_FALSE ] = "TF",
[Qst_ANS_UNIQUE_CHOICE ] = "uniqueChoice",
[Qst_ANS_MULTIPLE_CHOICE] = "multipleChoice",
[Qst_ANS_TEXT ] = "text",
};
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 ******************************/
/*****************************************************************************/
@ -89,6 +110,250 @@ extern struct Globals Gbl;
/***************************** Private prototypes ****************************/
/*****************************************************************************/
/*****************************************************************************/
/***************** List several test questions for edition *******************/
/*****************************************************************************/
void Qst_ListQuestionsToEdit (void)
{
struct Qst_Questions Questions;
MYSQL_RES *mysql_res;
/***** Create test *****/
Qst_Constructor (&Questions);
/***** Get parameters, query the database and list the questions *****/
if (Tst_GetParamsTst (&Questions,Tst_EDIT_QUESTIONS)) // Get parameters from the form
{
/***** Get question codes from database *****/
Qst_GetQuestions (&Questions,&mysql_res); // Query database
if (Questions.NumQsts)
{
/* Contextual menu */
if (QstImp_GetCreateXMLParamFromForm ())
{
Mnu_ContextMenuBegin ();
QstImp_CreateXML (Questions.NumQsts,mysql_res); // Create XML file with exported questions...
// ...and put a link to download it
Mnu_ContextMenuEnd ();
}
/* Show the table with the questions */
Qst_ListOneOrMoreQstsForEdition (&Questions,mysql_res);
}
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
else
/* Show the form again */
Qst_ShowFormRequestEditQsts (&Questions);
/***** Destroy test *****/
Qst_Destructor (&Questions);
}
/*****************************************************************************/
/************ List several test questions for selection for exam *************/
/*****************************************************************************/
void Qst_ListQuestionsToSelectForExamSet (struct Exa_Exams *Exams)
{
struct Qst_Questions Questions;
MYSQL_RES *mysql_res;
/***** Create test *****/
Qst_Constructor (&Questions);
/***** Get parameters, query the database and list the questions *****/
if (Tst_GetParamsTst (&Questions,Tst_SELECT_QUESTIONS_FOR_EXAM)) // Get parameters from the form
{
Qst_GetQuestions (&Questions,&mysql_res); // Query database
if (Questions.NumQsts)
/* Show the table with the questions */
Qst_ListOneOrMoreQstsForSelectionForExamSet (Exams,Questions.NumQsts,mysql_res);
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
else
/* Show the form again */
Qst_ShowFormRequestSelectQstsForExamSet (Exams,&Questions);
/***** Destroy test *****/
Qst_Destructor (&Questions);
}
/*****************************************************************************/
/************ List several test questions for selection for game *************/
/*****************************************************************************/
void Qst_ListQuestionsToSelectForGame (struct Gam_Games *Games)
{
struct Qst_Questions Questions;
MYSQL_RES *mysql_res;
/***** Create test *****/
Qst_Constructor (&Questions);
/***** Get parameters, query the database and list the questions *****/
if (Tst_GetParamsTst (&Questions,Tst_SELECT_QUESTIONS_FOR_GAME)) // Get parameters from the form
{
Qst_GetQuestions (&Questions,&mysql_res); // Query database
if (Questions.NumQsts)
/* Show the table with the questions */
Qst_ListOneOrMoreQstsForSelectionForGame (Games,Questions.NumQsts,mysql_res);
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
else
/* Show the form again */
Qst_ShowFormRequestSelectQstsForGame (Games,&Questions);
/***** Destroy test *****/
Qst_Destructor (&Questions);
}
/*****************************************************************************/
/********** Get from the database several test questions for listing *********/
/*****************************************************************************/
#define Qst_MAX_BYTES_QUERY_QUESTIONS (16 * 1024 - 1)
void Qst_GetQuestions (struct Qst_Questions *Questions,MYSQL_RES **mysql_res)
{
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];
/***** Allocate space for query *****/
if ((Query = malloc (Qst_MAX_BYTES_QUERY_QUESTIONS + 1)) == NULL)
Err_NotEnoughMemoryExit ();
/***** 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_ShowErrorAndExit ("Query size exceed.");
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_ShowErrorAndExit ("Query size exceed.");
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 */
Questions->NumQsts = (unsigned) DB_QuerySELECT (mysql_res,"can not get questions",
"%s",
Query);
if (Questions->NumQsts == 0)
Ale_ShowAlert (Ale_INFO,Txt_No_questions_found_matching_your_search_criteria);
}
/*****************************************************************************/
/***************** Change format of answers text / feedback ******************/
/*****************************************************************************/
@ -123,3 +388,37 @@ void Qst_ChangeFormatAnswersFeedback (struct Qst_Question *Question)
Question->Answer.Options[NumOpt].Feedback,
Qst_MAX_BYTES_ANSWER_OR_FEEDBACK,false);
}
/*****************************************************************************/
/** Convert a string with the type of answer in database to type of answer ***/
/*****************************************************************************/
Qst_AnswerType_t Qst_ConvertFromStrAnsTypDBToAnsTyp (const char *StrAnsTypeDB)
{
Qst_AnswerType_t AnsType;
if (StrAnsTypeDB != NULL)
if (StrAnsTypeDB[0])
for (AnsType = (Qst_AnswerType_t) 0;
AnsType <= (Qst_AnswerType_t) (Qst_NUM_ANS_TYPES - 1);
AnsType++)
if (!strcmp (StrAnsTypeDB,Qst_DB_StrAnswerTypes[AnsType]))
return AnsType;
return Qst_ANS_UNKNOWN;
}
/*****************************************************************************/
/************ Convert a string with an unsigned to answer type ***************/
/*****************************************************************************/
Qst_AnswerType_t Qst_ConvertFromUnsignedStrToAnsTyp (const char *UnsignedStr)
{
unsigned AnsType;
if (sscanf (UnsignedStr,"%u",&AnsType) != 1)
Err_WrongAnswerExit ();
if (AnsType >= Qst_NUM_ANS_TYPES)
Err_WrongAnswerExit ();
return (Qst_AnswerType_t) AnsType;
}

View File

@ -30,10 +30,13 @@
#include <stdbool.h> // For boolean type
#include <time.h> // For time_t
// #include "swad_question_type.h"
#include "swad_exam.h"
#include "swad_game.h"
#include "swad_question_type.h"
#include "swad_media.h"
#include "swad_string.h"
#include "swad_tag.h"
// #include "swad_test.h"
// #include "swad_test_config.h"
// #include "swad_test_visibility.h"
// #include "swad_user.h"
@ -42,24 +45,30 @@
/***************************** Public constants ******************************/
/*****************************************************************************/
#define Qst_MAX_BYTES_ANSWER_TYPE 32
#define Qst_MAX_BYTES_FLOAT_ANSWER 30 // Maximum length of the strings that store an floating point answer
#define Qst_MAX_OPTIONS_PER_QUESTION 10
#define Qst_MAX_BYTES_INDEXES_ONE_QST (Qst_MAX_OPTIONS_PER_QUESTION * (3 + 1))
#define Qst_MAX_CHARS_ANSWER_OR_FEEDBACK (1024 - 1) // 1023
#define Qst_MAX_BYTES_ANSWER_OR_FEEDBACK ((Qst_MAX_CHARS_ANSWER_OR_FEEDBACK + 1) * Str_MAX_BYTES_PER_CHAR - 1) // 16383
#define Qst_MAX_CHARS_ANSWERS_ONE_QST (128 - 1) // 127
#define Qst_MAX_BYTES_ANSWERS_ONE_QST ((Qst_MAX_CHARS_ANSWERS_ONE_QST + 1) * Str_MAX_BYTES_PER_CHAR - 1) // 2047
/*****************************************************************************/
/******************************* Public types ********************************/
/*****************************************************************************/
struct Qst_AnswerTypes
{
bool All;
char List[Qst_MAX_BYTES_LIST_ANSWER_TYPES + 1];
};
#define Qst_NUM_TYPES_ORDER_QST 5
typedef enum
{
Qst_ORDER_STEM = 0,
Qst_ORDER_NUM_HITS = 1,
Qst_ORDER_AVERAGE_SCORE = 2,
Qst_ORDER_NUM_HITS_NOT_BLANK = 3,
Qst_ORDER_AVERAGE_SCORE_NOT_BLANK = 4,
} Qst_QuestionsOrder_t;
#define Qst_DEFAULT_ORDER Qst_ORDER_STEM
#define Qst_NUM_VALIDITIES 2
typedef enum
{
@ -67,19 +76,6 @@ typedef enum
Qst_VALID_QUESTION,
} Qst_Validity_t;
#define Qst_NUM_ANS_TYPES 6
#define Qst_MAX_BYTES_LIST_ANSWER_TYPES (Qst_NUM_ANS_TYPES * (Cns_MAX_DECIMAL_DIGITS_UINT + 1))
typedef enum
{
Qst_ANS_INT = 0,
Qst_ANS_FLOAT = 1,
Qst_ANS_TRUE_FALSE = 2,
Qst_ANS_UNIQUE_CHOICE = 3,
Qst_ANS_MULTIPLE_CHOICE = 4,
Qst_ANS_TEXT = 5,
Qst_ANS_UNKNOWN = 6, // Unknown/all/any type of answer
} Qst_AnswerType_t;
struct Qst_Question
{
long QstCod;
@ -110,11 +106,29 @@ struct Qst_Question
Qst_Validity_t Validity; // If a question in an exam has been marked as invalid
};
struct Qst_Questions
{
struct Tag_Tags Tags; // Selected tags
struct Qst_AnswerTypes AnswerTypes; // Selected answer types
Qst_QuestionsOrder_t SelectedOrder; // Order for listing questions
unsigned NumQsts; // Number of questions
struct Qst_Question Question; // Selected / editing question
};
/*****************************************************************************/
/***************************** Public prototypes *****************************/
/*****************************************************************************/
void Qst_ListQuestionsToEdit (void);
void Qst_ListQuestionsToSelectForExamSet (struct Exa_Exams *Exams);
void Qst_ListQuestionsToSelectForGame (struct Gam_Games *Games);
void Qst_GetQuestions (struct Qst_Questions *Questions,MYSQL_RES **mysql_res);
void Qst_ChangeFormatAnswersText (struct Qst_Question *Question);
void Qst_ChangeFormatAnswersFeedback (struct Qst_Question *Question);
Qst_AnswerType_t Qst_ConvertFromStrAnsTypDBToAnsTyp (const char *StrAnsTypeDB);
Qst_AnswerType_t Qst_ConvertFromUnsignedStrToAnsTyp (const char *UnsignedStr);
#endif

View File

@ -67,7 +67,7 @@ extern struct Globals Gbl;
/***************************** Private prototypes ****************************/
/*****************************************************************************/
static void QstImp_PutParamsExportQsts (void *Test);
static void QstImp_PutParamsExportQsts (void *Questions);
static void QstImp_PutCreateXMLParam (void);
static void QstImp_ExportQuestion (struct Qst_Question *Question,FILE *FileXML);
@ -90,13 +90,13 @@ static void QstImp_WriteRowImportedQst (struct XMLElement *StemElem,
/**************** Put a link (form) to export test questions *****************/
/*****************************************************************************/
void QstImp_PutIconToExportQuestions (struct Tst_Test *Test)
void QstImp_PutIconToExportQuestions (struct Qst_Questions *Questions)
{
extern const char *Txt_Export_questions;
/***** Put a link to create a file with questions *****/
Lay_PutContextualLinkOnlyIcon (ActLstTstQst,NULL,
QstImp_PutParamsExportQsts,Test,
QstImp_PutParamsExportQsts,Questions,
"file-import.svg",
Txt_Export_questions);
}
@ -105,13 +105,13 @@ void QstImp_PutIconToExportQuestions (struct Tst_Test *Test)
/****************** Put params to export test questions **********************/
/*****************************************************************************/
static void QstImp_PutParamsExportQsts (void *Test)
static void QstImp_PutParamsExportQsts (void *Questions)
{
if (Test)
if (Questions)
{
Qst_PutParamsEditQst (Test);
Qst_PutParamsEditQst (Questions);
Par_PutHiddenParamChar ("OnlyThisQst",'N');
Par_PutHiddenParamUnsigned (NULL,"Order",(unsigned) (((struct Tst_Test *) Test)->SelectedOrder));
Par_PutHiddenParamUnsigned (NULL,"Order",(unsigned) (((struct Qst_Questions *) Questions)->SelectedOrder));
QstImp_PutCreateXMLParam ();
}
}
@ -255,14 +255,14 @@ void QstImp_CreateXML (unsigned NumQsts,MYSQL_RES *mysql_res)
static void QstImp_ExportQuestion (struct Qst_Question *Question,FILE *FileXML)
{
extern const char *Tst_StrAnswerTypesXML[Qst_NUM_ANS_TYPES];
extern const char *Qst_StrAnswerTypesXML[Qst_NUM_ANS_TYPES];
extern const char *Txt_NEW_LINE;
if (Qst_GetQstDataFromDB (Question))
{
/***** Write the answer type *****/
fprintf (FileXML,"<question type=\"%s\">%s",
Tst_StrAnswerTypesXML[Question->Answer.Type],Txt_NEW_LINE);
Qst_StrAnswerTypesXML[Question->Answer.Type],Txt_NEW_LINE);
/***** Write the question tags *****/
fprintf (FileXML,"<tags>%s",Txt_NEW_LINE);
@ -680,7 +680,7 @@ static void QstImp_ImportQuestionsFromXMLBuffer (const char *XMLBuffer)
static Qst_AnswerType_t QstImp_ConvertFromStrAnsTypXMLToAnsTyp (const char *StrAnsTypeXML)
{
extern const char *Tst_StrAnswerTypesXML[Qst_NUM_ANS_TYPES];
extern const char *Qst_StrAnswerTypesXML[Qst_NUM_ANS_TYPES];
Qst_AnswerType_t AnsType;
if (StrAnsTypeXML != NULL)
@ -688,7 +688,7 @@ static Qst_AnswerType_t QstImp_ConvertFromStrAnsTypXMLToAnsTyp (const char *StrA
AnsType <= (Qst_AnswerType_t) (Qst_NUM_ANS_TYPES - 1);
AnsType++)
// comparison must be case insensitive, because users can edit XML
if (!strcasecmp (StrAnsTypeXML,Tst_StrAnswerTypesXML[AnsType]))
if (!strcasecmp (StrAnsTypeXML,Qst_StrAnswerTypesXML[AnsType]))
return AnsType;
Err_WrongAnswerExit ();

View File

@ -27,6 +27,8 @@
/********************************* Headers ***********************************/
/*****************************************************************************/
#include "swad_test.h"
/*****************************************************************************/
/***************************** Public constants ******************************/
/*****************************************************************************/
@ -39,7 +41,7 @@
/***************************** Public prototypes *****************************/
/*****************************************************************************/
void QstImp_PutIconToExportQuestions (struct Tst_Test *Test);
void QstImp_PutIconToExportQuestions (struct Qst_Questions *Questions);
bool QstImp_GetCreateXMLParamFromForm (void);
void QstImp_PutIconToImportQuestions (void);
void QstImp_CreateXML (unsigned long NumRows,MYSQL_RES *mysql_res);

View File

@ -31,12 +31,33 @@
#include "swad_tag.h"
/*****************************************************************************/
/***************************** Public constants ******************************/
/************************* Public constants and types ************************/
/*****************************************************************************/
/*****************************************************************************/
/******************************* Public types ********************************/
/*****************************************************************************/
#define Qst_NUM_ANS_TYPES 6
#define Qst_MAX_BYTES_LIST_ANSWER_TYPES (Qst_NUM_ANS_TYPES * (Cns_MAX_DECIMAL_DIGITS_UINT + 1))
typedef enum
{
Qst_ANS_INT = 0,
Qst_ANS_FLOAT = 1,
Qst_ANS_TRUE_FALSE = 2,
Qst_ANS_UNIQUE_CHOICE = 3,
Qst_ANS_MULTIPLE_CHOICE = 4,
Qst_ANS_TEXT = 5,
Qst_ANS_UNKNOWN = 6, // Unknown/all/any type of answer
} Qst_AnswerType_t;
#define Qst_MAX_BYTES_ANSWER_TYPE 32
#define Qst_MAX_BYTES_FLOAT_ANSWER 30 // Maximum length of the strings that store an floating point answer
#define Qst_MAX_OPTIONS_PER_QUESTION 10
#define Qst_MAX_BYTES_INDEXES_ONE_QST (Qst_MAX_OPTIONS_PER_QUESTION * (3 + 1))
#define Qst_MAX_CHARS_ANSWERS_ONE_QST (128 - 1) // 127
#define Qst_MAX_BYTES_ANSWERS_ONE_QST ((Qst_MAX_CHARS_ANSWERS_ONE_QST + 1) * Str_MAX_BYTES_PER_CHAR - 1) // 2047
/*****************************************************************************/
/***************************** Public prototypes *****************************/

File diff suppressed because it is too large Load Diff

View File

@ -45,32 +45,6 @@
/******************************* Public types ********************************/
/*****************************************************************************/
struct Qst_AnswerTypes
{
bool All;
char List[Qst_MAX_BYTES_LIST_ANSWER_TYPES + 1];
};
#define Qst_NUM_TYPES_ORDER_QST 5
typedef enum
{
Qst_ORDER_STEM = 0,
Qst_ORDER_NUM_HITS = 1,
Qst_ORDER_AVERAGE_SCORE = 2,
Qst_ORDER_NUM_HITS_NOT_BLANK = 3,
Qst_ORDER_AVERAGE_SCORE_NOT_BLANK = 4,
} Qst_QuestionsOrder_t;
#define Qst_DEFAULT_ORDER Qst_ORDER_STEM
struct Tst_Test
{
struct Tag_Tags Tags; // Selected tags
struct Qst_AnswerTypes AnswerTypes; // Selected answer types
Qst_QuestionsOrder_t SelectedOrder; // Order for listing questions
unsigned NumQsts; // Number of questions
struct Qst_Question Question; // Selected / editing question
};
typedef enum
{
Tst_SHOW_TEST_TO_ANSWER, // Showing a test to a student
@ -98,6 +72,10 @@ struct Tst_Stats
/*****************************************************************************/
void Tst_RequestTest (void);
void Qst_Constructor (struct Qst_Questions *Questions);
void Qst_Destructor (struct Qst_Questions *Questions);
void Tst_ShowNewTest (void);
void Tst_ReceiveTestDraft (void);
void Tst_AssessTest (void);
@ -111,14 +89,24 @@ void Qst_WriteQstStem (const char *Stem,const char *ClassStem,bool Visible);
void Qst_WriteQstFeedback (const char *Feedback,const char *ClassFeedback);
void Qst_RequestEditQsts (void);
void Qst_ShowFormRequestEditQsts (struct Qst_Questions *Questions);
void Qst_RequestSelectQstsForExamSet (struct Exa_Exams *Exams);
void Qst_RequestSelectQstsForGame (struct Gam_Games *Games);
void Qst_ShowFormRequestSelectQstsForExamSet (struct Exa_Exams *Exams,
struct Qst_Questions *Questions);
void Qst_ShowFormRequestSelectQstsForGame (struct Gam_Games *Games,
struct Qst_Questions *Questions);
void Qst_ListQuestionsToEdit (void);
void Qst_ListQuestionsToSelectForExamSet (struct Exa_Exams *Exams);
void Qst_ListQuestionsToSelectForGame (struct Gam_Games *Games);
void Qst_ListOneOrMoreQstsForEdition (struct Qst_Questions *Questions,
MYSQL_RES *mysql_res);
void Qst_ListOneOrMoreQstsForSelectionForExamSet (struct Exa_Exams *Exams,
unsigned NumQsts,
MYSQL_RES *mysql_res);
void Qst_ListOneOrMoreQstsForSelectionForGame (struct Gam_Games *Games,
unsigned NumQsts,
MYSQL_RES *mysql_res);
void Qst_PutParamsEditQst (void *Test);
void Qst_PutParamsEditQst (void *Questions);
unsigned Qst_GetNumAnswersQst (long QstCod);
void Qst_GetAnswersQst (struct Qst_Question *Question,MYSQL_RES **mysql_res,
@ -133,6 +121,8 @@ void Qst_WriteParamQstCod (unsigned NumQst,long QstCod);
void Qst_CheckIfNumberOfAnswersIsOne (const struct Qst_Question *Question);
bool Tst_GetParamsTst (struct Qst_Questions *Questions,
Tst_ActionToDoWithQuestions_t ActionToDoWithQuestions);
void Tst_ShowFormConfig (void);
bool Tst_CheckIfCourseHaveTestsAndPluggableIsUnknown (void);
@ -148,7 +138,6 @@ bool Qst_AllocateTextChoiceAnswer (struct Qst_Question *Question,unsigned NumOpt
Qst_AnswerType_t Qst_GetQstAnswerTypeFromDB (long QstCod);
bool Qst_GetQstDataFromDB (struct Qst_Question *Question);
Qst_AnswerType_t Qst_ConvertFromStrAnsTypDBToAnsTyp (const char *StrAnsTypeDB);
void Qst_ReceiveQst (void);
bool Qst_CheckIfQstFormatIsCorrectAndCountNumOptions (struct Qst_Question *Question);
@ -168,8 +157,6 @@ void Qst_PutParamQstCod (void *QstCod);
void Qst_InsertOrUpdateQstTagsAnsIntoDB (struct Qst_Question *Question);
void Qst_UpdateQstScoreInDB (struct TstPrn_PrintedQuestion *PrintedQuestion);
void Tst_RemoveCrsTests (long CrsCod);
void Qst_RemoveCrsQsts (long CrsCod);

View File

@ -115,6 +115,8 @@ static void TstPrn_WriteQstAndAnsExam (struct UsrData *UsrDat,
bool QuestionExists,
unsigned Visibility);
static void TstPrn_UpdateQstScoreInDB (struct TstPrn_PrintedQuestion *PrintedQuestion);
//-----------------------------------------------------------------------------
static void TstPrn_GetCorrectAndComputeIntAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Qst_Question *Question);
@ -634,7 +636,7 @@ void TstPrn_ShowPrintAfterAssess (struct TstPrn_Print *Print)
/***** Update the number of accesses and the score of this question *****/
if (Gbl.Usrs.Me.Role.Logged == Rol_STD)
Qst_UpdateQstScoreInDB (&Print->PrintedQuestions[QstInd]);
TstPrn_UpdateQstScoreInDB (&Print->PrintedQuestions[QstInd]);
/***** Destroy test question *****/
Qst_QstDestructor (&Question);
@ -796,7 +798,7 @@ void TstPrn_ComputeScoresAndStoreQuestionsOfPrint (struct TstPrn_Print *Print,
/* Update the number of hits and the score of this question in tests database */
if (UpdateQstScore)
Qst_UpdateQstScoreInDB (&Print->PrintedQuestions[QstInd]);
TstPrn_UpdateQstScoreInDB (&Print->PrintedQuestions[QstInd]);
}
}
@ -822,6 +824,32 @@ void TstPrn_ComputeAnswerScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
TstPrn_GetCorrectAndComputeAnsScore[Question->Answer.Type] (PrintedQuestion,Question);
}
/*****************************************************************************/
/*********************** Update the score of a question **********************/
/*****************************************************************************/
static void TstPrn_UpdateQstScoreInDB (struct TstPrn_PrintedQuestion *PrintedQuestion)
{
/***** Update number of clicks and score of the question *****/
Str_SetDecimalPointToUS (); // To print the floating point as a dot
if (PrintedQuestion->StrAnswers[0]) // User's answer is not blank
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",
PrintedQuestion->Score,
PrintedQuestion->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",
PrintedQuestion->QstCod);
Str_SetDecimalPointToLocal (); // Return to local system
}
/*****************************************************************************/
/******* Get correct answer and compute score for each type of answer ********/
/*****************************************************************************/

View File

@ -29,6 +29,7 @@
#include "swad_question.h"
#include "swad_question_type.h"
#include "swad_test.h"
#include "swad_test_config.h"
#include "swad_test_visibility.h"
#include "swad_user.h"