2014-12-01 23:55:08 +01:00
|
|
|
|
// swad_test.c: self-assessment tests
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
SWAD (Shared Workspace At a Distance),
|
|
|
|
|
is a web platform developed at the University of Granada (Spain),
|
|
|
|
|
and used to support university teaching.
|
|
|
|
|
|
|
|
|
|
This file is part of SWAD core.
|
2021-02-09 12:43:45 +01:00
|
|
|
|
Copyright (C) 1999-2021 Antonio Ca<EFBFBD>as Vargas
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU Affero General Public License as
|
|
|
|
|
published by the Free Software Foundation, either version 3 of the
|
|
|
|
|
License, or (at your option) any later version.
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU Affero General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********************************** Headers *********************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2019-11-01 22:53:39 +01:00
|
|
|
|
#define _GNU_SOURCE // For asprintf
|
2014-12-01 23:55:08 +01:00
|
|
|
|
#include <limits.h> // For UINT_MAX
|
|
|
|
|
#include <linux/limits.h> // For PATH_MAX
|
|
|
|
|
#include <mysql/mysql.h> // To access MySQL databases
|
2015-10-16 02:24:29 +02:00
|
|
|
|
#include <stdbool.h> // For boolean type
|
2019-12-29 12:39:00 +01:00
|
|
|
|
#include <stddef.h> // For NULL
|
2019-12-30 22:32:06 +01:00
|
|
|
|
#include <stdio.h> // For asprintf
|
2014-12-01 23:55:08 +01:00
|
|
|
|
#include <stdlib.h> // For exit, system, malloc, free, etc
|
|
|
|
|
#include <string.h> // For string functions
|
|
|
|
|
#include <sys/stat.h> // For mkdir
|
|
|
|
|
#include <sys/types.h> // For mkdir
|
|
|
|
|
|
|
|
|
|
#include "swad_action.h"
|
2017-06-10 21:38:10 +02:00
|
|
|
|
#include "swad_box.h"
|
2014-12-01 23:55:08 +01:00
|
|
|
|
#include "swad_database.h"
|
2021-04-26 15:27:27 +02:00
|
|
|
|
#include "swad_error.h"
|
2020-05-07 02:22:57 +02:00
|
|
|
|
#include "swad_exam_set.h"
|
2020-04-14 17:15:17 +02:00
|
|
|
|
#include "swad_figure.h"
|
2018-11-09 20:47:39 +01:00
|
|
|
|
#include "swad_form.h"
|
2014-12-01 23:55:08 +01:00
|
|
|
|
#include "swad_global.h"
|
2021-05-27 23:30:16 +02:00
|
|
|
|
#include "swad_hierarchy_level.h"
|
2019-10-23 19:05:05 +02:00
|
|
|
|
#include "swad_HTML.h"
|
2014-12-01 23:55:08 +01:00
|
|
|
|
#include "swad_ID.h"
|
2018-12-08 16:43:13 +01:00
|
|
|
|
#include "swad_language.h"
|
2019-09-14 12:59:34 +02:00
|
|
|
|
#include "swad_match.h"
|
2019-03-02 21:49:11 +01:00
|
|
|
|
#include "swad_media.h"
|
2014-12-01 23:55:08 +01:00
|
|
|
|
#include "swad_parameter.h"
|
|
|
|
|
#include "swad_theme.h"
|
|
|
|
|
#include "swad_test.h"
|
2020-03-21 15:41:25 +01:00
|
|
|
|
#include "swad_test_config.h"
|
2014-12-01 23:55:08 +01:00
|
|
|
|
#include "swad_test_import.h"
|
2020-05-07 18:33:26 +02:00
|
|
|
|
#include "swad_test_print.h"
|
2020-02-18 09:19:33 +01:00
|
|
|
|
#include "swad_test_visibility.h"
|
2014-12-01 23:55:08 +01:00
|
|
|
|
#include "swad_user.h"
|
|
|
|
|
#include "swad_xml.h"
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************************** Public constants ******************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2017-03-08 03:48:23 +01:00
|
|
|
|
// strings are limited to Tst_MAX_BYTES_ANSWER_TYPE characters
|
2014-12-01 23:55:08 +01:00
|
|
|
|
const char *Tst_StrAnswerTypesXML[Tst_NUM_ANS_TYPES] =
|
|
|
|
|
{
|
2019-11-22 01:04:03 +01:00
|
|
|
|
[Tst_ANS_INT ] = "int",
|
|
|
|
|
[Tst_ANS_FLOAT ] = "float",
|
|
|
|
|
[Tst_ANS_TRUE_FALSE ] = "TF",
|
|
|
|
|
[Tst_ANS_UNIQUE_CHOICE ] = "uniqueChoice",
|
|
|
|
|
[Tst_ANS_MULTIPLE_CHOICE] = "multipleChoice",
|
|
|
|
|
[Tst_ANS_TEXT ] = "text",
|
2014-12-01 23:55:08 +01:00
|
|
|
|
};
|
|
|
|
|
|
2020-05-13 00:28:32 +02:00
|
|
|
|
const char *Tst_StrAnswerTypesDB[Tst_NUM_ANS_TYPES] =
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2019-12-15 20:02:34 +01:00
|
|
|
|
[Tst_ANS_INT ] = "int",
|
|
|
|
|
[Tst_ANS_FLOAT ] = "float",
|
|
|
|
|
[Tst_ANS_TRUE_FALSE ] = "true_false",
|
|
|
|
|
[Tst_ANS_UNIQUE_CHOICE ] = "unique_choice",
|
|
|
|
|
[Tst_ANS_MULTIPLE_CHOICE] = "multiple_choice",
|
|
|
|
|
[Tst_ANS_TEXT ] = "text",
|
2014-12-01 23:55:08 +01:00
|
|
|
|
};
|
|
|
|
|
|
2020-05-13 00:28:32 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/**************************** Private constants ******************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2016-04-08 16:37:59 +02:00
|
|
|
|
// Test images will be saved with:
|
|
|
|
|
// - maximum width of Tst_IMAGE_SAVED_MAX_HEIGHT
|
|
|
|
|
// - maximum height of Tst_IMAGE_SAVED_MAX_HEIGHT
|
2016-04-01 13:37:49 +02:00
|
|
|
|
// - maintaining the original aspect ratio (aspect ratio recommended: 3:2)
|
2016-04-08 16:37:59 +02:00
|
|
|
|
#define Tst_IMAGE_SAVED_MAX_WIDTH 768
|
2020-02-13 22:33:31 +01:00
|
|
|
|
#define Tst_IMAGE_SAVED_MAX_HEIGHT 768
|
|
|
|
|
#define Tst_IMAGE_SAVED_QUALITY 90 // 1 to 100
|
2016-04-01 13:37:49 +02:00
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
2019-11-21 16:47:07 +01:00
|
|
|
|
/******************************* Private types *******************************/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************** External global variables from others modules ****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
extern struct Globals Gbl;
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2019-11-21 16:47:07 +01:00
|
|
|
|
/************************* Private global variables **************************/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2019-11-21 16:47:07 +01:00
|
|
|
|
/***************************** Private prototypes ****************************/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-03-26 21:39:44 +01:00
|
|
|
|
static void Tst_TstConstructor (struct Tst_Test *Test);
|
|
|
|
|
static void Tst_TstDestructor (struct Tst_Test *Test);
|
2020-03-21 15:41:25 +01:00
|
|
|
|
|
2020-03-24 01:50:39 +01:00
|
|
|
|
static void Tst_ShowFormRequestTest (struct Tst_Test *Test);
|
2020-03-21 15:41:25 +01:00
|
|
|
|
|
2020-05-11 02:28:38 +02:00
|
|
|
|
static void TstPrn_GetAnswersFromForm (struct TstPrn_Print *Print);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
static bool Tst_CheckIfNextTstAllowed (void);
|
2020-04-02 03:28:08 +02:00
|
|
|
|
static unsigned Tst_GetNumExamsGeneratedByMe (void);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
2021-02-15 16:25:55 +01:00
|
|
|
|
static void Tst_PutFormToEditQstMedia (const struct Med_Media *Media,int NumMedia,
|
2016-04-05 10:47:36 +02:00
|
|
|
|
bool OptionsDisabled);
|
2020-04-02 03:28:08 +02:00
|
|
|
|
static void Tst_IncreaseMyNumAccessTst (void);
|
2020-03-22 01:15:27 +01:00
|
|
|
|
static void Tst_UpdateLastAccTst (unsigned NumQsts);
|
2020-03-21 15:41:25 +01:00
|
|
|
|
|
2020-03-24 01:50:39 +01:00
|
|
|
|
static void Tst_ShowFormRequestEditTests (struct Tst_Test *Test);
|
2020-04-27 14:20:21 +02:00
|
|
|
|
static void Tst_ShowFormRequestSelectTestsForSet (struct Exa_Exams *Exams,
|
|
|
|
|
struct Tst_Test *Test);
|
2020-04-08 03:06:45 +02:00
|
|
|
|
static void Tst_ShowFormRequestSelectTestsForGame (struct Gam_Games *Games,
|
|
|
|
|
struct Tst_Test *Test);
|
2016-11-07 10:35:36 +01:00
|
|
|
|
static bool Tst_CheckIfICanEditTests (void);
|
2020-05-17 18:45:27 +02:00
|
|
|
|
static void Tst_PutIconsRequestBankQsts (__attribute__((unused)) void *Args);
|
|
|
|
|
static void Tst_PutIconsEditBankQsts (void *Test);
|
2020-05-16 18:52:32 +02:00
|
|
|
|
static void Tst_PutIconsTests (__attribute__((unused)) void *Args);
|
2017-09-07 12:00:01 +02:00
|
|
|
|
static void Tst_PutButtonToAddQuestion (void);
|
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
static void Tst_ShowFormConfigTst (void);
|
2020-03-21 15:41:25 +01:00
|
|
|
|
|
2016-12-26 15:17:30 +01:00
|
|
|
|
static void Tst_PutInputFieldNumQst (const char *Field,const char *Label,
|
|
|
|
|
unsigned Value);
|
2020-03-21 15:41:25 +01:00
|
|
|
|
|
2020-03-21 22:18:24 +01:00
|
|
|
|
static void Tst_ShowFormAnswerTypes (const struct Tst_AnswerTypes *AnswerTypes);
|
2020-03-24 01:50:39 +01:00
|
|
|
|
static void Tst_GetQuestions (struct Tst_Test *Test,MYSQL_RES **mysql_res);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
static void Tst_GetQuestionsForNewTestFromDB (struct Tst_Test *Test,
|
2020-05-07 18:33:26 +02:00
|
|
|
|
struct TstPrn_Print *Print);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
2020-03-26 21:39:44 +01:00
|
|
|
|
static void Tst_ListOneQstToEdit (struct Tst_Test *Test);
|
|
|
|
|
static void Tst_ListOneOrMoreQuestionsForEdition (struct Tst_Test *Test,
|
2017-09-01 00:52:19 +02:00
|
|
|
|
MYSQL_RES *mysql_res);
|
2020-10-14 00:59:24 +02:00
|
|
|
|
static void Tst_WriteHeadingRowQuestionsForEdition (struct Tst_Test *Test);
|
2021-04-26 17:29:04 +02:00
|
|
|
|
static void Tst_WriteQuestionListing (struct Tst_Test *Test,unsigned QstInd);
|
2020-04-27 14:20:21 +02:00
|
|
|
|
static void Tst_ListOneOrMoreQuestionsForSelectionForSet (struct Exa_Exams *Exams,
|
|
|
|
|
unsigned NumQsts,
|
|
|
|
|
MYSQL_RES *mysql_res);
|
|
|
|
|
static void Tst_ListOneOrMoreQuestionsForSelectionForGame (struct Gam_Games *Games,
|
2020-04-22 03:15:04 +02:00
|
|
|
|
unsigned NumQsts,
|
|
|
|
|
MYSQL_RES *mysql_res);
|
2020-05-30 20:25:19 +02:00
|
|
|
|
static void Tst_PutCheckboxToSelectAllQuestions (void);
|
2021-04-26 17:29:04 +02:00
|
|
|
|
static void Tst_WriteQuestionRowForSelection (unsigned QstInd,
|
2020-03-27 21:54:13 +01:00
|
|
|
|
struct Tst_Question *Question);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-05-22 20:10:45 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
2020-06-17 20:20:16 +02:00
|
|
|
|
static void Tst_WriteIntAnsBank (struct Tst_Question *Question,
|
|
|
|
|
const char *ClassTxt,
|
|
|
|
|
__attribute__((unused)) const char *ClassFeedback);
|
|
|
|
|
static void Tst_WriteFltAnsBank (struct Tst_Question *Question,
|
|
|
|
|
const char *ClassTxt,
|
|
|
|
|
__attribute__((unused)) const char *ClassFeedback);
|
|
|
|
|
static void Tst_WriteTF_AnsBank (struct Tst_Question *Question,
|
|
|
|
|
const char *ClassTxt,
|
|
|
|
|
__attribute__((unused)) const char *ClassFeedback);
|
|
|
|
|
static void Tst_WriteChoAnsBank (struct Tst_Question *Question,
|
|
|
|
|
const char *ClassTxt,
|
|
|
|
|
const char *ClassFeedback);
|
2020-05-22 20:10:45 +02:00
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2020-04-02 03:28:08 +02:00
|
|
|
|
|
2020-03-24 00:58:42 +01:00
|
|
|
|
static bool Tst_GetParamsTst (struct Tst_Test *Test,
|
2020-03-21 15:41:25 +01:00
|
|
|
|
Tst_ActionToDoWithQuestions_t ActionToDoWithQuestions);
|
2020-04-02 03:28:08 +02:00
|
|
|
|
static unsigned Tst_GetParamNumTst (void);
|
2020-03-22 01:15:27 +01:00
|
|
|
|
static unsigned Tst_GetParamNumQsts (void);
|
2020-05-17 17:11:04 +02:00
|
|
|
|
static unsigned Tst_CountNumTagsInList (const struct Tag_Tags *Tags);
|
2020-03-21 22:18:24 +01:00
|
|
|
|
static int Tst_CountNumAnswerTypesInList (const struct Tst_AnswerTypes *AnswerTypes);
|
2020-03-21 15:41:25 +01:00
|
|
|
|
|
2020-04-04 19:20:50 +02:00
|
|
|
|
static void Tst_PutFormEditOneQst (struct Tst_Question *Question);
|
2016-12-26 15:17:30 +01:00
|
|
|
|
static void Tst_PutFloatInputField (const char *Label,const char *Field,
|
2020-03-19 20:57:54 +01:00
|
|
|
|
const struct Tst_Question *Question,
|
|
|
|
|
unsigned Index);
|
|
|
|
|
static void Tst_PutTFInputField (const struct Tst_Question *Question,
|
2020-03-18 01:57:08 +01:00
|
|
|
|
const char *Label,char Value);
|
2016-04-03 01:24:20 +02:00
|
|
|
|
|
2020-03-19 20:57:54 +01:00
|
|
|
|
static void Tst_FreeTextChoiceAnswers (struct Tst_Question *Question);
|
|
|
|
|
static void Tst_FreeTextChoiceAnswer (struct Tst_Question *Question,unsigned NumOpt);
|
2016-04-06 19:26:09 +02:00
|
|
|
|
|
2020-03-19 20:57:54 +01:00
|
|
|
|
static void Tst_ResetMediaOfQuestion (struct Tst_Question *Question);
|
|
|
|
|
static void Tst_FreeMediaOfQuestion (struct Tst_Question *Question);
|
2016-04-04 21:51:21 +02:00
|
|
|
|
|
2020-03-17 00:35:11 +01:00
|
|
|
|
static long Tst_GetMedCodFromDB (long CrsCod,long QstCod,int NumOpt);
|
|
|
|
|
static void Tst_GetMediaFromDB (long CrsCod,long QstCod,int NumOpt,
|
2021-02-11 00:58:53 +01:00
|
|
|
|
struct Med_Media *Media);
|
2016-04-03 01:24:20 +02:00
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
static Tst_AnswerType_t Tst_ConvertFromUnsignedStrToAnsTyp (const char *UnsignedStr);
|
2020-04-04 19:20:50 +02:00
|
|
|
|
static void Tst_GetQstFromForm (struct Tst_Question *Question);
|
2020-03-25 01:36:22 +01:00
|
|
|
|
static void Tst_MoveMediaToDefinitiveDirectories (struct Tst_Question *Question);
|
2016-04-04 21:51:21 +02:00
|
|
|
|
|
2020-04-27 19:37:49 +02:00
|
|
|
|
static void Tst_PutIconToRemoveOneQst (void *QstCod);
|
|
|
|
|
static void Tst_PutParamsRemoveOnlyThisQst (void *QstCod);
|
2020-03-17 00:35:11 +01:00
|
|
|
|
static void Tst_RemoveOneQstFromDB (long CrsCod,long QstCod);
|
2016-04-05 10:05:52 +02:00
|
|
|
|
|
2020-03-25 01:36:22 +01:00
|
|
|
|
static void Tst_InsertOrUpdateQstIntoDB (struct Tst_Question *Question);
|
|
|
|
|
static void Tst_InsertAnswersIntoDB (struct Tst_Question *Question);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-17 00:35:11 +01:00
|
|
|
|
static void Tst_RemAnsFromQst (long QstCod);
|
2016-04-04 21:51:21 +02:00
|
|
|
|
|
2020-05-18 22:59:07 +02:00
|
|
|
|
static void Tst_RemoveMediaFromStemOfQst (long CrsCod,long QstCod);
|
2019-03-19 11:20:29 +01:00
|
|
|
|
static void Tst_RemoveMediaFromAllAnsOfQst (long CrsCod,long QstCod);
|
2020-05-18 22:59:07 +02:00
|
|
|
|
|
|
|
|
|
static void Tst_RemoveAllMedFilesFromStemOfAllQstsInCrs (long CrsCod);
|
2019-03-17 01:38:10 +01:00
|
|
|
|
static void Tst_RemoveAllMedFilesFromAnsOfAllQstsInCrs (long CrsCod);
|
2016-04-04 21:51:21 +02:00
|
|
|
|
|
2021-05-27 23:30:16 +02:00
|
|
|
|
static unsigned Tst_GetNumTstQuestions (HieLvl_Level_t Scope,Tst_AnswerType_t AnsType,struct Tst_Stats *Stats);
|
|
|
|
|
static unsigned Tst_GetNumCoursesWithTstQuestions (HieLvl_Level_t Scope,Tst_AnswerType_t AnsType);
|
|
|
|
|
static unsigned Tst_GetNumCoursesWithPluggableTstQuestions (HieLvl_Level_t Scope,Tst_AnswerType_t AnsType);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-21 15:41:25 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************* Request a self-assessment test ************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void Tst_RequestTest (void)
|
|
|
|
|
{
|
2020-03-24 00:58:42 +01:00
|
|
|
|
struct Tst_Test Test;
|
2020-03-21 15:41:25 +01:00
|
|
|
|
|
2020-03-26 21:39:44 +01:00
|
|
|
|
/***** Create test *****/
|
|
|
|
|
Tst_TstConstructor (&Test);
|
2020-03-21 22:18:24 +01:00
|
|
|
|
|
2020-03-21 15:41:25 +01:00
|
|
|
|
/***** Show form to generate a self-assessment test *****/
|
2020-03-24 00:58:42 +01:00
|
|
|
|
Tst_ShowFormRequestTest (&Test);
|
2020-03-26 21:39:44 +01:00
|
|
|
|
|
|
|
|
|
/***** Destroy test *****/
|
|
|
|
|
Tst_TstDestructor (&Test);
|
2020-03-21 15:41:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2020-03-26 21:39:44 +01:00
|
|
|
|
/***************************** Test constructor ******************************/
|
2020-03-21 15:41:25 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-03-26 21:39:44 +01:00
|
|
|
|
static void Tst_TstConstructor (struct Tst_Test *Test)
|
2020-03-21 15:41:25 +01:00
|
|
|
|
{
|
2020-03-25 01:36:22 +01:00
|
|
|
|
/***** Reset tags *****/
|
2020-05-17 17:11:04 +02:00
|
|
|
|
Tag_ResetTags (&Test->Tags);
|
2020-03-25 01:36:22 +01:00
|
|
|
|
|
|
|
|
|
/***** Reset answer types *****/
|
|
|
|
|
Test->AnswerTypes.All = false;
|
|
|
|
|
Test->AnswerTypes.List[0] = '\0';
|
|
|
|
|
|
|
|
|
|
/***** Reset selected order *****/
|
|
|
|
|
Test->SelectedOrder = Tst_DEFAULT_ORDER;
|
2020-03-26 21:39:44 +01:00
|
|
|
|
|
|
|
|
|
/***** Question constructor *****/
|
|
|
|
|
Tst_QstConstructor (&Test->Question);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/****************************** Test destructor ******************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void Tst_TstDestructor (struct Tst_Test *Test)
|
|
|
|
|
{
|
|
|
|
|
/***** Question destructor *****/
|
|
|
|
|
Tst_QstDestructor (&Test->Question);
|
|
|
|
|
|
|
|
|
|
/***** Free tag list *****/
|
2020-05-17 17:11:04 +02:00
|
|
|
|
Tag_FreeTagsList (&Test->Tags);
|
2020-03-26 21:39:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*************** Show form to generate a self-assessment test ****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-03-24 01:50:39 +01:00
|
|
|
|
static void Tst_ShowFormRequestTest (struct Tst_Test *Test)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2016-11-13 20:18:49 +01:00
|
|
|
|
extern const char *Hlp_ASSESSMENT_Tests;
|
2020-05-16 18:52:32 +02:00
|
|
|
|
extern const char *Txt_Test;
|
2020-05-07 14:15:39 +02:00
|
|
|
|
extern const char *Txt_Number_of_questions;
|
2016-11-21 13:15:08 +01:00
|
|
|
|
extern const char *Txt_Generate_test;
|
2016-03-21 02:13:19 +01:00
|
|
|
|
extern const char *Txt_No_test_questions;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
|
|
|
|
|
/***** Read test configuration from database *****/
|
2020-03-21 15:41:25 +01:00
|
|
|
|
TstCfg_GetConfigFromDB ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2019-10-26 02:19:42 +02:00
|
|
|
|
/***** Begin box *****/
|
2020-05-16 18:52:32 +02:00
|
|
|
|
Box_BoxBegin (NULL,Txt_Test,
|
|
|
|
|
Tst_PutIconsTests,NULL,
|
2017-06-12 15:03:29 +02:00
|
|
|
|
Hlp_ASSESSMENT_Tests,Box_NOT_CLOSABLE);
|
2016-03-20 23:38:22 +01:00
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/***** Get tags *****/
|
2020-05-17 17:11:04 +02:00
|
|
|
|
if ((Test->Tags.Num = Tag_GetEnabledTagsFromThisCrs (&mysql_res)) != 0)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
/***** Check if minimum date-time of next access to test is older than now *****/
|
|
|
|
|
if (Tst_CheckIfNextTstAllowed ())
|
|
|
|
|
{
|
2021-03-02 00:54:26 +01:00
|
|
|
|
Frm_BeginForm (ActSeeTst);
|
2017-05-01 21:17:38 +02:00
|
|
|
|
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TABLE_BeginPadding (2);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/***** Selection of tags *****/
|
2020-05-17 15:01:08 +02:00
|
|
|
|
Tag_ShowFormSelTags (&Test->Tags,mysql_res,true);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/***** Selection of types of answers *****/
|
2020-03-24 00:58:42 +01:00
|
|
|
|
Tst_ShowFormAnswerTypes (&Test->AnswerTypes);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/***** Number of questions to generate ****/
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TR_Begin (NULL);
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2019-12-27 21:10:39 +01:00
|
|
|
|
/* Label */
|
2020-05-07 14:15:39 +02:00
|
|
|
|
Frm_LabelColumn ("RT","NumQst",Txt_Number_of_questions);
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2019-12-27 21:10:39 +01:00
|
|
|
|
/* Data */
|
2019-12-26 22:29:04 +01:00
|
|
|
|
HTM_TD_Begin ("class=\"LT\"");
|
2019-11-27 01:01:27 +01:00
|
|
|
|
HTM_INPUT_LONG ("NumQst",
|
2020-03-21 15:41:25 +01:00
|
|
|
|
(long) TstCfg_GetConfigMin (),
|
|
|
|
|
(long) TstCfg_GetConfigMax (),
|
|
|
|
|
(long) TstCfg_GetConfigDef (),
|
2020-04-27 03:16:55 +02:00
|
|
|
|
HTM_DONT_SUBMIT_ON_CHANGE,
|
2020-03-21 15:41:25 +01:00
|
|
|
|
TstCfg_GetConfigMin () == TstCfg_GetConfigMax (),
|
|
|
|
|
"id=\"NumQst\"");
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_End ();
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TR_End ();
|
2017-05-01 21:17:38 +02:00
|
|
|
|
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TABLE_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/***** Send button *****/
|
2017-06-11 19:02:40 +02:00
|
|
|
|
Btn_PutConfirmButton (Txt_Generate_test);
|
2018-11-09 20:47:39 +01:00
|
|
|
|
Frm_EndForm ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2016-03-21 02:13:19 +01:00
|
|
|
|
/***** Warning message *****/
|
2019-02-16 19:29:27 +01:00
|
|
|
|
Ale_ShowAlert (Ale_INFO,Txt_No_test_questions);
|
2016-03-21 02:13:19 +01:00
|
|
|
|
|
|
|
|
|
/***** Button to create a new question *****/
|
2016-11-07 10:35:36 +01:00
|
|
|
|
if (Tst_CheckIfICanEditTests ())
|
2016-05-30 18:26:29 +02:00
|
|
|
|
Tst_PutButtonToAddQuestion ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-06-12 14:16:33 +02:00
|
|
|
|
/***** End box *****/
|
2019-10-25 22:48:34 +02:00
|
|
|
|
Box_BoxEnd ();
|
2016-03-20 23:38:22 +01:00
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************** Generate self-assessment test ************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2016-11-21 13:15:08 +01:00
|
|
|
|
void Tst_ShowNewTest (void)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_No_questions_found_matching_your_search_criteria;
|
2020-03-24 00:58:42 +01:00
|
|
|
|
struct Tst_Test Test;
|
2020-05-07 18:33:26 +02:00
|
|
|
|
struct TstPrn_Print Print;
|
2020-04-02 03:28:08 +02:00
|
|
|
|
unsigned NumExamsGeneratedByMe;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-26 21:39:44 +01:00
|
|
|
|
/***** Create test *****/
|
|
|
|
|
Tst_TstConstructor (&Test);
|
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/***** Read test configuration from database *****/
|
2020-03-21 15:41:25 +01:00
|
|
|
|
TstCfg_GetConfigFromDB ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
if (Tst_CheckIfNextTstAllowed ())
|
|
|
|
|
{
|
|
|
|
|
/***** Check that all parameters used to generate a test are valid *****/
|
2020-03-24 01:50:39 +01:00
|
|
|
|
if (Tst_GetParamsTst (&Test,Tst_SHOW_TEST_TO_ANSWER)) // Get parameters from form
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
/***** Get questions *****/
|
2020-05-11 02:28:38 +02:00
|
|
|
|
TstPrn_ResetPrint (&Print);
|
2020-05-07 18:33:26 +02:00
|
|
|
|
Tst_GetQuestionsForNewTestFromDB (&Test,&Print);
|
2020-06-24 02:15:50 +02:00
|
|
|
|
if (Print.NumQsts.All)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2020-04-02 03:28:08 +02:00
|
|
|
|
/***** Increase number of exams generated (answered or not) by me *****/
|
|
|
|
|
Tst_IncreaseMyNumAccessTst ();
|
|
|
|
|
NumExamsGeneratedByMe = Tst_GetNumExamsGeneratedByMe ();
|
|
|
|
|
|
|
|
|
|
/***** Create new test exam in database *****/
|
2020-05-09 01:37:00 +02:00
|
|
|
|
TstPrn_CreatePrintInDB (&Print);
|
|
|
|
|
TstPrn_ComputeScoresAndStoreQuestionsOfPrint (&Print,
|
2020-05-11 02:28:38 +02:00
|
|
|
|
false); // Don't update question score
|
2020-04-02 03:28:08 +02:00
|
|
|
|
|
|
|
|
|
/***** Show test exam to be answered *****/
|
2020-06-17 02:31:42 +02:00
|
|
|
|
TstPrn_ShowTestPrintToFillIt (&Print,NumExamsGeneratedByMe,TstPrn_REQUEST);
|
2016-03-21 01:28:16 +01:00
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/***** Update date-time of my next allowed access to test *****/
|
2017-06-04 18:18:54 +02:00
|
|
|
|
if (Gbl.Usrs.Me.Role.Logged == Rol_STD)
|
2020-03-24 01:50:39 +01:00
|
|
|
|
Tst_UpdateLastAccTst (Test.NumQsts);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
2020-03-30 16:40:12 +02:00
|
|
|
|
else // No questions found
|
|
|
|
|
{
|
|
|
|
|
Ale_ShowAlert (Ale_INFO,Txt_No_questions_found_matching_your_search_criteria);
|
|
|
|
|
Tst_ShowFormRequestTest (&Test); // Show the form again
|
|
|
|
|
}
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
2020-03-24 00:58:42 +01:00
|
|
|
|
Tst_ShowFormRequestTest (&Test); // Show the form again
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
2020-03-26 21:39:44 +01:00
|
|
|
|
|
|
|
|
|
/***** Destroy test *****/
|
|
|
|
|
Tst_TstDestructor (&Test);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-30 00:30:08 +02:00
|
|
|
|
/*****************************************************************************/
|
2020-04-16 21:03:22 +02:00
|
|
|
|
/** Receive the draft of a test exam already (total or partially) answered ***/
|
2020-03-30 00:30:08 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-16 21:03:22 +02:00
|
|
|
|
void Tst_ReceiveTestDraft (void)
|
2020-03-30 00:30:08 +02:00
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_The_test_X_has_already_been_assessed_previously;
|
2020-05-07 18:33:26 +02:00
|
|
|
|
extern const char *Txt_Please_review_your_answers_before_submitting_the_exam;
|
2020-03-30 00:30:08 +02:00
|
|
|
|
unsigned NumTst;
|
2020-05-07 18:33:26 +02:00
|
|
|
|
struct TstPrn_Print Print;
|
2020-03-30 00:30:08 +02:00
|
|
|
|
|
|
|
|
|
/***** Read test configuration from database *****/
|
|
|
|
|
TstCfg_GetConfigFromDB ();
|
|
|
|
|
|
2020-04-02 03:28:08 +02:00
|
|
|
|
/***** Get basic parameters of the exam *****/
|
|
|
|
|
/* Get test exam code from form */
|
2020-05-11 02:28:38 +02:00
|
|
|
|
TstPrn_ResetPrint (&Print);
|
2020-05-09 21:07:50 +02:00
|
|
|
|
if ((Print.PrnCod = TstPrn_GetParamPrnCod ()) <= 0)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_WrongTestExit ();
|
2020-04-02 03:28:08 +02:00
|
|
|
|
|
|
|
|
|
/* Get number of this test from form */
|
|
|
|
|
NumTst = Tst_GetParamNumTst ();
|
2020-03-30 00:30:08 +02:00
|
|
|
|
|
2020-05-10 01:42:30 +02:00
|
|
|
|
/***** Get test exam print from database *****/
|
|
|
|
|
TstPrn_GetPrintDataByPrnCod (&Print);
|
2020-04-16 21:03:22 +02:00
|
|
|
|
|
2020-03-30 00:30:08 +02:00
|
|
|
|
/****** Get test status in database for this session-course-num.test *****/
|
2020-05-07 18:33:26 +02:00
|
|
|
|
if (Print.Sent)
|
2020-04-16 21:03:22 +02:00
|
|
|
|
Ale_ShowAlert (Ale_WARNING,Txt_The_test_X_has_already_been_assessed_previously,
|
|
|
|
|
NumTst);
|
2020-05-07 18:33:26 +02:00
|
|
|
|
else // Print not yet sent
|
2020-03-30 00:30:08 +02:00
|
|
|
|
{
|
2020-05-10 01:42:30 +02:00
|
|
|
|
/***** Get test exam print questions from database *****/
|
|
|
|
|
TstPrn_GetPrintQuestionsFromDB (&Print);
|
2020-03-30 00:30:08 +02:00
|
|
|
|
|
2020-04-16 21:03:22 +02:00
|
|
|
|
/***** Get answers from form to assess a test *****/
|
2020-05-11 02:28:38 +02:00
|
|
|
|
TstPrn_GetAnswersFromForm (&Print);
|
2020-03-30 00:30:08 +02:00
|
|
|
|
|
2020-04-16 21:03:22 +02:00
|
|
|
|
/***** Update test exam in database *****/
|
2020-05-09 01:37:00 +02:00
|
|
|
|
TstPrn_ComputeScoresAndStoreQuestionsOfPrint (&Print,
|
2020-05-11 02:28:38 +02:00
|
|
|
|
false); // Don't update question score
|
|
|
|
|
TstPrn_UpdatePrintInDB (&Print);
|
2020-03-30 00:30:08 +02:00
|
|
|
|
|
2020-04-16 21:03:22 +02:00
|
|
|
|
/***** Show question and button to send the test *****/
|
2021-03-02 00:54:26 +01:00
|
|
|
|
/* Begin alert */
|
2020-05-07 18:33:26 +02:00
|
|
|
|
Ale_ShowAlert (Ale_WARNING,Txt_Please_review_your_answers_before_submitting_the_exam);
|
2020-03-30 00:30:08 +02:00
|
|
|
|
|
2020-04-16 21:03:22 +02:00
|
|
|
|
/* Show the same test exam to be answered */
|
2020-06-17 02:31:42 +02:00
|
|
|
|
TstPrn_ShowTestPrintToFillIt (&Print,NumTst,TstPrn_CONFIRM);
|
2020-03-30 00:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
2016-11-21 13:15:08 +01:00
|
|
|
|
/******************************** Assess a test ******************************/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2016-11-21 13:15:08 +01:00
|
|
|
|
void Tst_AssessTest (void)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2016-11-13 20:18:49 +01:00
|
|
|
|
extern const char *Hlp_ASSESSMENT_Tests;
|
2020-05-17 02:28:30 +02:00
|
|
|
|
extern const char *Txt_Result;
|
2016-03-21 01:28:16 +01:00
|
|
|
|
extern const char *Txt_Test_No_X_that_you_make_in_this_course;
|
2019-11-28 09:12:34 +01:00
|
|
|
|
extern const char *Txt_Score;
|
2019-11-28 09:45:32 +01:00
|
|
|
|
extern const char *Txt_Grade;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
extern const char *Txt_The_test_X_has_already_been_assessed_previously;
|
|
|
|
|
unsigned NumTst;
|
2020-05-07 18:33:26 +02:00
|
|
|
|
struct TstPrn_Print Print;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/***** Read test configuration from database *****/
|
2020-03-21 15:41:25 +01:00
|
|
|
|
TstCfg_GetConfigFromDB ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Get basic parameters of the exam *****/
|
2020-04-02 03:28:08 +02:00
|
|
|
|
/* Get test exam code from form */
|
2020-05-11 02:28:38 +02:00
|
|
|
|
TstPrn_ResetPrint (&Print);
|
2020-05-09 21:07:50 +02:00
|
|
|
|
if ((Print.PrnCod = TstPrn_GetParamPrnCod ()) <= 0)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_WrongTestExit ();
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
|
|
|
|
/* Get number of this test from form */
|
2020-04-02 03:28:08 +02:00
|
|
|
|
NumTst = Tst_GetParamNumTst ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-16 21:03:22 +02:00
|
|
|
|
/***** Get test exam from database *****/
|
2020-05-10 01:42:30 +02:00
|
|
|
|
TstPrn_GetPrintDataByPrnCod (&Print);
|
2020-04-16 21:03:22 +02:00
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/****** Get test status in database for this session-course-num.test *****/
|
2020-05-07 18:33:26 +02:00
|
|
|
|
if (Print.Sent)
|
2020-04-16 21:03:22 +02:00
|
|
|
|
Ale_ShowAlert (Ale_WARNING,Txt_The_test_X_has_already_been_assessed_previously,
|
|
|
|
|
NumTst);
|
2020-05-07 18:33:26 +02:00
|
|
|
|
else // Print not yet sent
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2020-04-16 21:03:22 +02:00
|
|
|
|
/***** Get test exam questions from database *****/
|
2020-05-10 01:42:30 +02:00
|
|
|
|
TstPrn_GetPrintQuestionsFromDB (&Print);
|
2020-04-16 21:03:22 +02:00
|
|
|
|
|
|
|
|
|
/***** Get answers from form to assess a test *****/
|
2020-05-11 02:28:38 +02:00
|
|
|
|
TstPrn_GetAnswersFromForm (&Print);
|
2020-04-16 21:03:22 +02:00
|
|
|
|
|
|
|
|
|
/***** Get if test exam will be visible by teachers *****/
|
2020-05-07 18:33:26 +02:00
|
|
|
|
Print.Sent = true; // The exam has been finished and sent by student
|
|
|
|
|
Print.AllowTeachers = Par_GetParToBool ("AllowTchs");
|
2020-04-16 21:03:22 +02:00
|
|
|
|
|
|
|
|
|
/***** Update test exam in database *****/
|
2020-05-09 01:37:00 +02:00
|
|
|
|
TstPrn_ComputeScoresAndStoreQuestionsOfPrint (&Print,
|
2020-05-11 02:28:38 +02:00
|
|
|
|
Gbl.Usrs.Me.Role.Logged == Rol_STD); // Update question score?
|
|
|
|
|
TstPrn_UpdatePrintInDB (&Print);
|
2020-04-16 21:03:22 +02:00
|
|
|
|
|
|
|
|
|
/***** Begin box *****/
|
2020-05-17 02:28:30 +02:00
|
|
|
|
Box_BoxBegin (NULL,Txt_Result,
|
2020-04-16 21:03:22 +02:00
|
|
|
|
NULL,NULL,
|
|
|
|
|
Hlp_ASSESSMENT_Tests,Box_NOT_CLOSABLE);
|
|
|
|
|
Lay_WriteHeaderClassPhoto (false,false,
|
|
|
|
|
Gbl.Hierarchy.Ins.InsCod,
|
|
|
|
|
Gbl.Hierarchy.Deg.DegCod,
|
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod);
|
|
|
|
|
|
|
|
|
|
/***** Header *****/
|
|
|
|
|
if (Gbl.Usrs.Me.IBelongToCurrentCrs)
|
|
|
|
|
{
|
|
|
|
|
HTM_DIV_Begin ("class=\"TEST_SUBTITLE\"");
|
|
|
|
|
HTM_TxtF (Txt_Test_No_X_that_you_make_in_this_course,NumTst);
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-16 21:03:22 +02:00
|
|
|
|
/***** Write answers and solutions *****/
|
2020-05-18 14:34:31 +02:00
|
|
|
|
TstPrn_ShowPrintAfterAssess (&Print);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-16 21:03:22 +02:00
|
|
|
|
/***** Write total score and grade *****/
|
|
|
|
|
if (TstVis_IsVisibleTotalScore (TstCfg_GetConfigVisibility ()))
|
|
|
|
|
{
|
|
|
|
|
HTM_DIV_Begin ("class=\"DAT_N_BOLD CM\"");
|
|
|
|
|
HTM_TxtColonNBSP (Txt_Score);
|
2020-05-07 18:33:26 +02:00
|
|
|
|
HTM_Double2Decimals (Print.Score);
|
2020-04-16 21:03:22 +02:00
|
|
|
|
HTM_BR ();
|
|
|
|
|
HTM_TxtColonNBSP (Txt_Grade);
|
2020-06-24 02:15:50 +02:00
|
|
|
|
TstPrn_ComputeAndShowGrade (Print.NumQsts.All,Print.Score,Tst_SCORE_MAX);
|
2020-04-16 21:03:22 +02:00
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-16 21:03:22 +02:00
|
|
|
|
/***** End box *****/
|
|
|
|
|
Box_BoxEnd ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2020-05-11 02:28:38 +02:00
|
|
|
|
/****** Get questions and answers from form to assess a test exam print ******/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-05-11 02:28:38 +02:00
|
|
|
|
static void TstPrn_GetAnswersFromForm (struct TstPrn_Print *Print)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2021-04-26 17:29:04 +02:00
|
|
|
|
unsigned QstInd;
|
2020-04-02 03:28:08 +02:00
|
|
|
|
char StrAns[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Ansxx...x"
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-02 03:28:08 +02:00
|
|
|
|
/***** Loop for every question getting user's answers *****/
|
2021-04-26 17:29:04 +02:00
|
|
|
|
for (QstInd = 0;
|
|
|
|
|
QstInd < Print->NumQsts.All;
|
|
|
|
|
QstInd++)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
/* Get answers selected by user for this question */
|
2021-04-26 17:29:04 +02:00
|
|
|
|
snprintf (StrAns,sizeof (StrAns),"Ans%010u",QstInd);
|
|
|
|
|
Par_GetParMultiToText (StrAns,Print->PrintedQuestions[QstInd].StrAnswers,
|
2020-05-09 01:37:00 +02:00
|
|
|
|
Tst_MAX_BYTES_ANSWERS_ONE_QST); /* If answer type == T/F ==> " ", "T", "F"; if choice ==> "0", "2",... */
|
2021-04-26 17:29:04 +02:00
|
|
|
|
Par_ReplaceSeparatorMultipleByComma (Print->PrintedQuestions[QstInd].StrAnswers);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2020-04-02 03:28:08 +02:00
|
|
|
|
/************** Check minimum date-time of next access to test ***************/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
2020-04-02 03:28:08 +02:00
|
|
|
|
// Return true if allowed date-time of next access to test is older than now
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-02 03:28:08 +02:00
|
|
|
|
static bool Tst_CheckIfNextTstAllowed (void)
|
2019-11-28 09:12:34 +01:00
|
|
|
|
{
|
2020-04-02 03:28:08 +02:00
|
|
|
|
extern const char *Hlp_ASSESSMENT_Tests;
|
|
|
|
|
extern const char *Txt_You_can_not_take_a_new_test_until;
|
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
MYSQL_ROW row;
|
|
|
|
|
long NumSecondsFromNowToNextAccTst = -1L; // Access allowed when this number <= 0
|
|
|
|
|
time_t TimeNextTestUTC = (time_t) 0;
|
2019-11-28 09:45:32 +01:00
|
|
|
|
|
2020-04-02 03:28:08 +02:00
|
|
|
|
/***** Teachers and superusers are allowed to do all tests they want *****/
|
|
|
|
|
if (Gbl.Usrs.Me.Role.Logged == Rol_TCH ||
|
|
|
|
|
Gbl.Usrs.Me.Role.Logged == Rol_SYS_ADM)
|
|
|
|
|
return true;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-02 03:28:08 +02:00
|
|
|
|
/***** Get date of next allowed access to test from database *****/
|
|
|
|
|
if (DB_QuerySELECT (&mysql_res,"can not get last access to test",
|
|
|
|
|
"SELECT UNIX_TIMESTAMP(LastAccTst+INTERVAL (NumQstsLastTst*%lu) SECOND)-"
|
|
|
|
|
"UNIX_TIMESTAMP()," // row[0]
|
|
|
|
|
"UNIX_TIMESTAMP(LastAccTst+INTERVAL (NumQstsLastTst*%lu) SECOND)" // row[1]
|
2021-03-24 13:12:54 +01:00
|
|
|
|
" FROM crs_user_settings"
|
|
|
|
|
" WHERE UsrCod=%ld"
|
|
|
|
|
" AND CrsCod=%ld",
|
2020-04-02 03:28:08 +02:00
|
|
|
|
TstCfg_GetConfigMinTimeNxtTstPerQst (),
|
|
|
|
|
TstCfg_GetConfigMinTimeNxtTstPerQst (),
|
2021-03-24 13:12:54 +01:00
|
|
|
|
Gbl.Usrs.Me.UsrDat.UsrCod,
|
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod) == 1)
|
2019-11-28 01:41:13 +01:00
|
|
|
|
{
|
2020-04-02 03:28:08 +02:00
|
|
|
|
/* Get seconds from now to next access to test */
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
|
|
|
|
if (row[0])
|
|
|
|
|
if (sscanf (row[0],"%ld",&NumSecondsFromNowToNextAccTst) == 1)
|
|
|
|
|
/* Time UTC of next access allowed (row[1]) */
|
|
|
|
|
TimeNextTestUTC = Dat_GetUNIXTimeFromStr (row[1]);
|
|
|
|
|
}
|
|
|
|
|
else
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_ShowErrorAndExit ("Error when reading date of next allowed access to test.");
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
2020-04-02 03:28:08 +02:00
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
2020-04-02 03:28:08 +02:00
|
|
|
|
/***** Check if access is allowed *****/
|
|
|
|
|
if (NumSecondsFromNowToNextAccTst > 0)
|
|
|
|
|
{
|
|
|
|
|
/***** Write warning *****/
|
|
|
|
|
Ale_ShowAlert (Ale_WARNING,"%s:<br /><span id=\"date_next_test\"></span>."
|
|
|
|
|
"<script type=\"text/javascript\">"
|
|
|
|
|
"writeLocalDateHMSFromUTC('date_next_test',%ld,"
|
2020-06-22 22:47:54 +02:00
|
|
|
|
"%u,', ',%u,true,true,true,0x7);"
|
2020-04-02 03:28:08 +02:00
|
|
|
|
"</script>",
|
|
|
|
|
Txt_You_can_not_take_a_new_test_until,
|
|
|
|
|
(long) TimeNextTestUTC,
|
2020-06-22 22:47:54 +02:00
|
|
|
|
(unsigned) Gbl.Prefs.DateFormat,
|
|
|
|
|
(unsigned) Gbl.Prefs.Language);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
2020-04-02 03:28:08 +02:00
|
|
|
|
return false;
|
2019-11-28 01:41:13 +01:00
|
|
|
|
}
|
2020-04-02 03:28:08 +02:00
|
|
|
|
return true;
|
2019-11-28 09:12:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
2020-04-02 03:28:08 +02:00
|
|
|
|
/***************** Get number of test exams generated by me ******************/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-02 03:28:08 +02:00
|
|
|
|
static unsigned Tst_GetNumExamsGeneratedByMe (void)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
MYSQL_ROW row;
|
2020-04-02 03:28:08 +02:00
|
|
|
|
unsigned long NumRows;
|
|
|
|
|
unsigned NumExamsGeneratedByMe = 0;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-02 03:28:08 +02:00
|
|
|
|
if (Gbl.Usrs.Me.IBelongToCurrentCrs)
|
|
|
|
|
{
|
|
|
|
|
/***** Get number of test exams generated by me from database *****/
|
|
|
|
|
NumRows = DB_QuerySELECT (&mysql_res,"can not get number of test exams generated",
|
|
|
|
|
"SELECT NumAccTst" // row[0]
|
2021-03-24 13:12:54 +01:00
|
|
|
|
" FROM crs_user_settings"
|
|
|
|
|
" WHERE UsrCod=%ld"
|
|
|
|
|
" AND CrsCod=%ld",
|
2020-04-30 02:29:44 +02:00
|
|
|
|
Gbl.Usrs.Me.UsrDat.UsrCod,
|
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
2020-04-02 03:28:08 +02:00
|
|
|
|
if (NumRows == 0)
|
|
|
|
|
NumExamsGeneratedByMe = 0;
|
|
|
|
|
else if (NumRows == 1)
|
|
|
|
|
{
|
|
|
|
|
/* Get number of hits */
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
|
|
|
|
if (row[0] == NULL)
|
|
|
|
|
NumExamsGeneratedByMe = 0;
|
|
|
|
|
else if (sscanf (row[0],"%u",&NumExamsGeneratedByMe) != 1)
|
|
|
|
|
NumExamsGeneratedByMe = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_ShowErrorAndExit ("Error when getting number of hits to test.");
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
2020-04-02 03:28:08 +02:00
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-02 03:28:08 +02:00
|
|
|
|
return NumExamsGeneratedByMe;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-02 03:28:08 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************************** Show list of test tags ***************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void Tst_ShowTagList (unsigned NumTags,MYSQL_RES *mysql_res)
|
2020-03-17 13:10:44 +01:00
|
|
|
|
{
|
2020-04-02 03:28:08 +02:00
|
|
|
|
extern const char *Txt_no_tags;
|
2020-03-17 13:10:44 +01:00
|
|
|
|
MYSQL_ROW row;
|
2020-04-02 03:28:08 +02:00
|
|
|
|
unsigned NumTag;
|
2020-03-17 13:10:44 +01:00
|
|
|
|
|
2020-04-02 03:28:08 +02:00
|
|
|
|
if (NumTags)
|
2020-03-17 13:10:44 +01:00
|
|
|
|
{
|
2020-04-02 03:28:08 +02:00
|
|
|
|
/***** Write the tags *****/
|
|
|
|
|
HTM_UL_Begin (NULL);
|
|
|
|
|
for (NumTag = 0;
|
|
|
|
|
NumTag < NumTags;
|
|
|
|
|
NumTag++)
|
|
|
|
|
{
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
|
|
|
|
HTM_LI_Begin (NULL);
|
|
|
|
|
HTM_Txt (row[0]);
|
|
|
|
|
HTM_LI_End ();
|
|
|
|
|
}
|
|
|
|
|
HTM_UL_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
2020-04-02 03:28:08 +02:00
|
|
|
|
else
|
|
|
|
|
HTM_Txt (Txt_no_tags);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-27 16:00:53 +02:00
|
|
|
|
/*****************************************************************************/
|
2020-05-13 00:28:32 +02:00
|
|
|
|
/********************* List game question for edition ************************/
|
2020-04-27 16:00:53 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-06-17 02:31:42 +02:00
|
|
|
|
void Tst_ListQuestionForEdition (struct Tst_Question *Question,
|
2020-04-27 16:00:53 +02:00
|
|
|
|
unsigned QstInd,bool QuestionExists,
|
|
|
|
|
const char *Anchor)
|
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_Question_removed;
|
|
|
|
|
|
|
|
|
|
/***** Number of question and answer type (row[1]) *****/
|
|
|
|
|
HTM_TD_Begin ("class=\"RT COLOR%u\"",Gbl.RowEvenOdd);
|
2021-09-24 21:49:06 +02:00
|
|
|
|
Tst_WriteNumQst (QstInd,"BIG_INDEX");
|
|
|
|
|
if (QuestionExists)
|
|
|
|
|
Tst_WriteAnswerType (Question->Answer.Type,"DAT_SMALL");
|
2020-04-27 16:00:53 +02:00
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
/***** Write question code *****/
|
|
|
|
|
HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd);
|
2021-09-24 21:49:06 +02:00
|
|
|
|
HTM_TxtF ("%ld ",Question->QstCod);
|
2020-04-27 16:00:53 +02:00
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
/***** Write the question tags *****/
|
|
|
|
|
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
|
2021-09-24 21:49:06 +02:00
|
|
|
|
if (QuestionExists)
|
|
|
|
|
Tst_GetAndWriteTagsQst (Question->QstCod);
|
2020-04-27 16:00:53 +02:00
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
/***** Write stem (row[3]) and media *****/
|
|
|
|
|
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
|
2021-09-24 21:49:06 +02:00
|
|
|
|
HTM_ARTICLE_Begin (Anchor);
|
|
|
|
|
if (QuestionExists)
|
|
|
|
|
{
|
|
|
|
|
/* Write stem */
|
|
|
|
|
Tst_WriteQstStem (Question->Stem,"TEST_TXT",
|
|
|
|
|
true); // Visible
|
2020-04-27 16:00:53 +02:00
|
|
|
|
|
2021-09-24 21:49:06 +02:00
|
|
|
|
/* Show media */
|
|
|
|
|
Med_ShowMedia (&Question->Media,
|
|
|
|
|
"TEST_MED_EDIT_LIST_CONT",
|
|
|
|
|
"TEST_MED_EDIT_LIST");
|
2020-04-27 16:00:53 +02:00
|
|
|
|
|
2021-09-24 21:49:06 +02:00
|
|
|
|
/* Show feedback */
|
|
|
|
|
Tst_WriteQstFeedback (Question->Feedback,"TEST_TXT_LIGHT");
|
2020-04-27 16:00:53 +02:00
|
|
|
|
|
2021-09-24 21:49:06 +02:00
|
|
|
|
/* Show answers */
|
|
|
|
|
Tst_WriteAnswersBank (Question,"TEST_TXT","TEST_TXT_LIGHT");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
HTM_SPAN_Begin ("class=\"DAT_LIGHT\"");
|
|
|
|
|
HTM_Txt (Txt_Question_removed);
|
|
|
|
|
HTM_SPAN_End ();
|
|
|
|
|
}
|
|
|
|
|
HTM_ARTICLE_End ();
|
2020-04-27 16:00:53 +02:00
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/********************* Write the number of a test question *******************/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
2020-04-01 03:11:05 +02:00
|
|
|
|
// Number of question should be 1, 2, 3...
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-06-17 20:20:16 +02:00
|
|
|
|
void Tst_WriteNumQst (unsigned NumQst,const char *Class)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2020-06-17 20:20:16 +02:00
|
|
|
|
HTM_DIV_Begin ("class=\"%s\"",Class);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_Unsigned (NumQst);
|
|
|
|
|
HTM_DIV_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/************************** Write the type of answer *************************/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-06-17 20:20:16 +02:00
|
|
|
|
void Tst_WriteAnswerType (Tst_AnswerType_t AnswerType,const char *Class)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2020-04-01 03:11:05 +02:00
|
|
|
|
extern const char *Txt_TST_STR_ANSWER_TYPES[Tst_NUM_ANS_TYPES];
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-06-17 20:20:16 +02:00
|
|
|
|
HTM_DIV_Begin ("class=\"%s\"",Class);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_Txt (Txt_TST_STR_ANSWER_TYPES[AnswerType]);
|
|
|
|
|
HTM_DIV_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/********************* Write the stem of a test question *********************/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
void Tst_WriteQstStem (const char *Stem,const char *ClassStem,bool Visible)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2020-04-01 03:11:05 +02:00
|
|
|
|
unsigned long StemLength;
|
|
|
|
|
char *StemRigorousHTML;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** DIV begin *****/
|
|
|
|
|
HTM_DIV_Begin ("class=\"%s\"",ClassStem);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Write stem *****/
|
2020-04-04 19:20:50 +02:00
|
|
|
|
if (Stem && Visible)
|
2016-05-31 20:28:17 +02:00
|
|
|
|
{
|
2020-04-04 19:20:50 +02:00
|
|
|
|
if (Stem[0])
|
|
|
|
|
{
|
|
|
|
|
/* Convert the stem, that is in HTML, to rigorous HTML */
|
|
|
|
|
StemLength = strlen (Stem) * Str_MAX_BYTES_PER_CHAR;
|
2021-02-15 16:25:55 +01:00
|
|
|
|
if ((StemRigorousHTML = malloc (StemLength + 1)) == NULL)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_NotEnoughMemoryExit ();
|
2021-02-15 16:25:55 +01:00
|
|
|
|
Str_Copy (StemRigorousHTML,Stem,StemLength);
|
2016-05-31 20:28:17 +02:00
|
|
|
|
|
2020-04-04 19:20:50 +02:00
|
|
|
|
Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
|
|
|
|
|
StemRigorousHTML,StemLength,false);
|
2016-05-31 20:28:17 +02:00
|
|
|
|
|
2020-04-04 19:20:50 +02:00
|
|
|
|
/* Write stem text */
|
|
|
|
|
HTM_Txt (StemRigorousHTML);
|
2016-05-31 20:28:17 +02:00
|
|
|
|
|
2020-04-04 19:20:50 +02:00
|
|
|
|
/* Free memory allocated for the stem */
|
|
|
|
|
free (StemRigorousHTML);
|
|
|
|
|
}
|
2016-05-31 11:21:04 +02:00
|
|
|
|
}
|
2020-04-01 03:11:05 +02:00
|
|
|
|
else
|
|
|
|
|
Ico_PutIconNotVisible ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** DIV end *****/
|
|
|
|
|
HTM_DIV_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-21 15:41:25 +01:00
|
|
|
|
/*****************************************************************************/
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/************* Put form to upload a new image for a test question ************/
|
2020-03-21 15:41:25 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2021-02-15 16:25:55 +01:00
|
|
|
|
static void Tst_PutFormToEditQstMedia (const struct Med_Media *Media,int NumMedia,
|
2020-04-01 03:11:05 +02:00
|
|
|
|
bool OptionsDisabled)
|
2020-03-21 15:41:25 +01:00
|
|
|
|
{
|
2020-04-01 03:11:05 +02:00
|
|
|
|
extern const char *The_ClassFormInBox[The_NUM_THEMES];
|
|
|
|
|
extern const char *Txt_No_image_video;
|
|
|
|
|
extern const char *Txt_Current_image_video;
|
|
|
|
|
extern const char *Txt_Change_image_video;
|
|
|
|
|
static unsigned UniqueId = 0;
|
|
|
|
|
struct ParamUploadMedia ParamUploadMedia;
|
2020-03-21 15:41:25 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
if (Media->Name[0])
|
2020-03-21 15:41:25 +01:00
|
|
|
|
{
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Set names of parameters depending on number of image in form *****/
|
2021-02-15 16:25:55 +01:00
|
|
|
|
Med_SetParamNames (&ParamUploadMedia,NumMedia);
|
2020-03-21 15:41:25 +01:00
|
|
|
|
|
2021-03-02 00:54:26 +01:00
|
|
|
|
/***** Begin container *****/
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_DIV_Begin ("class=\"TEST_MED_EDIT_FORM\"");
|
2020-03-21 15:41:25 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Choice 1: No media *****/
|
|
|
|
|
HTM_LABEL_Begin ("class=\"%s\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
|
|
|
|
|
HTM_INPUT_RADIO (ParamUploadMedia.Action,false,
|
|
|
|
|
"value=\"%u\"%s",
|
|
|
|
|
(unsigned) Med_ACTION_NO_MEDIA,
|
|
|
|
|
OptionsDisabled ? " disabled=\"disabled\"" : "");
|
|
|
|
|
HTM_Txt (Txt_No_image_video);
|
|
|
|
|
HTM_LABEL_End ();
|
|
|
|
|
HTM_BR ();
|
2020-03-21 15:41:25 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Choice 2: Current media *****/
|
|
|
|
|
HTM_LABEL_Begin ("class=\"%s\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
|
|
|
|
|
HTM_INPUT_RADIO (ParamUploadMedia.Action,false,
|
|
|
|
|
"value=\"%u\"%s checked=\"checked\"",
|
|
|
|
|
(unsigned) Med_ACTION_KEEP_MEDIA,
|
|
|
|
|
OptionsDisabled ? " disabled=\"disabled\"" : "");
|
|
|
|
|
HTM_Txt (Txt_Current_image_video);
|
|
|
|
|
HTM_LABEL_End ();
|
|
|
|
|
Med_ShowMedia (Media,
|
|
|
|
|
"TEST_MED_EDIT_ONE_CONT",
|
|
|
|
|
"TEST_MED_EDIT_ONE");
|
2020-03-21 15:41:25 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Choice 3: Change media *****/
|
|
|
|
|
UniqueId++;
|
|
|
|
|
HTM_LABEL_Begin ("class=\"%s\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
|
|
|
|
|
HTM_INPUT_RADIO (ParamUploadMedia.Action,false,
|
|
|
|
|
"id=\"chg_img_%u\" value=\"%u\"%s",
|
|
|
|
|
UniqueId,
|
|
|
|
|
(unsigned) Med_ACTION_NEW_MEDIA,
|
|
|
|
|
OptionsDisabled ? " disabled=\"disabled\"" : "");
|
|
|
|
|
HTM_TxtColonNBSP (Txt_Change_image_video);
|
|
|
|
|
HTM_LABEL_End ();
|
2021-02-15 16:25:55 +01:00
|
|
|
|
Med_PutMediaUploader (NumMedia,"TEST_MED_INPUT");
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
|
|
|
|
/***** End container *****/
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
else // No current image
|
|
|
|
|
/***** Attached media *****/
|
2021-02-15 16:25:55 +01:00
|
|
|
|
Med_PutMediaUploader (NumMedia,"TEST_MED_INPUT");
|
2020-03-21 15:41:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/******************* Write the feedback of a test question *******************/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
void Tst_WriteQstFeedback (const char *Feedback,const char *ClassFeedback)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2020-04-01 03:11:05 +02:00
|
|
|
|
unsigned long FeedbackLength;
|
|
|
|
|
char *FeedbackRigorousHTML;
|
|
|
|
|
|
|
|
|
|
if (Feedback)
|
|
|
|
|
if (Feedback[0])
|
|
|
|
|
{
|
|
|
|
|
/***** Convert the feedback, that is in HTML, to rigorous HTML *****/
|
|
|
|
|
FeedbackLength = strlen (Feedback) * Str_MAX_BYTES_PER_CHAR;
|
2021-02-15 16:25:55 +01:00
|
|
|
|
if ((FeedbackRigorousHTML = malloc (FeedbackLength + 1)) == NULL)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_NotEnoughMemoryExit ();
|
2021-02-15 16:25:55 +01:00
|
|
|
|
Str_Copy (FeedbackRigorousHTML,Feedback,FeedbackLength);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
|
|
|
|
|
FeedbackRigorousHTML,FeedbackLength,false);
|
|
|
|
|
|
|
|
|
|
/***** Write the feedback *****/
|
|
|
|
|
HTM_DIV_Begin ("class=\"%s\"",ClassFeedback);
|
|
|
|
|
HTM_Txt (FeedbackRigorousHTML);
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
|
|
|
|
|
/***** Free memory allocated for the feedback *****/
|
|
|
|
|
free (FeedbackRigorousHTML);
|
|
|
|
|
}
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/*********** Update my number of accesses to test in this course *************/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-02 03:28:08 +02:00
|
|
|
|
static void Tst_IncreaseMyNumAccessTst (void)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2020-04-02 03:28:08 +02:00
|
|
|
|
/***** Trivial check *****/
|
|
|
|
|
if (!Gbl.Usrs.Me.IBelongToCurrentCrs)
|
|
|
|
|
return;
|
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Update my number of accesses to test in this course *****/
|
|
|
|
|
DB_QueryUPDATE ("can not update the number of accesses to test",
|
2021-03-24 13:12:54 +01:00
|
|
|
|
"UPDATE crs_user_settings"
|
|
|
|
|
" SET NumAccTst=NumAccTst+1"
|
|
|
|
|
" WHERE UsrCod=%ld"
|
|
|
|
|
" AND CrsCod=%ld",
|
2020-04-30 02:29:44 +02:00
|
|
|
|
Gbl.Usrs.Me.UsrDat.UsrCod,
|
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/************ Update date-time of my next allowed access to test *************/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
static void Tst_UpdateLastAccTst (unsigned NumQsts)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Update date-time and number of questions of this test *****/
|
|
|
|
|
DB_QueryUPDATE ("can not update time and number of questions of this test",
|
2021-03-24 13:12:54 +01:00
|
|
|
|
"UPDATE crs_user_settings"
|
|
|
|
|
" SET LastAccTst=NOW(),"
|
|
|
|
|
"NumQstsLastTst=%u"
|
|
|
|
|
" WHERE UsrCod=%ld"
|
|
|
|
|
" AND CrsCod=%ld",
|
2020-04-01 03:11:05 +02:00
|
|
|
|
NumQsts,
|
2020-04-30 02:29:44 +02:00
|
|
|
|
Gbl.Usrs.Me.UsrDat.UsrCod,
|
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
}
|
2019-10-10 10:41:00 +02:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********************** Request the edition of tests ************************/
|
|
|
|
|
/*****************************************************************************/
|
2019-10-10 10:41:00 +02:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
void Tst_RequestEditTests (void)
|
|
|
|
|
{
|
|
|
|
|
struct Tst_Test Test;
|
2019-11-04 20:41:35 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Create test *****/
|
|
|
|
|
Tst_TstConstructor (&Test);
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Show form to generate a self-assessment test *****/
|
|
|
|
|
Tst_ShowFormRequestEditTests (&Test);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Destroy test *****/
|
|
|
|
|
Tst_TstDestructor (&Test);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/******* Select tags and dates for edition of the self-assessment test *******/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
static void Tst_ShowFormRequestEditTests (struct Tst_Test *Test)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2020-05-18 22:59:07 +02:00
|
|
|
|
extern const char *Hlp_ASSESSMENT_Questions_editing_questions;
|
2016-03-21 02:13:19 +01:00
|
|
|
|
extern const char *Txt_No_test_questions;
|
2020-05-16 18:52:32 +02:00
|
|
|
|
extern const char *Txt_Question_bank;
|
2020-04-01 03:11:05 +02:00
|
|
|
|
extern const char *Txt_Show_questions;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
MYSQL_RES *mysql_res;
|
2020-04-01 03:11:05 +02:00
|
|
|
|
static const Dat_SetHMS SetHMS[Dat_NUM_START_END_TIME] =
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2021-09-18 18:22:26 +02:00
|
|
|
|
[Dat_STR_TIME] = Dat_HMS_DO_NOT_SET,
|
|
|
|
|
[Dat_END_TIME] = Dat_HMS_DO_NOT_SET
|
2020-04-01 03:11:05 +02:00
|
|
|
|
};
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Begin box *****/
|
2020-05-16 18:52:32 +02:00
|
|
|
|
Box_BoxBegin (NULL,Txt_Question_bank,
|
2020-05-17 18:45:27 +02:00
|
|
|
|
Tst_PutIconsRequestBankQsts,NULL,
|
2020-05-18 22:59:07 +02:00
|
|
|
|
Hlp_ASSESSMENT_Questions_editing_questions,Box_NOT_CLOSABLE);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Get tags already present in the table of questions *****/
|
2020-05-17 17:11:04 +02:00
|
|
|
|
if ((Test->Tags.Num = Tag_GetAllTagsFromCurrentCrs (&mysql_res)))
|
2020-04-01 03:11:05 +02:00
|
|
|
|
{
|
2021-03-02 00:54:26 +01:00
|
|
|
|
Frm_BeginForm (ActLstTstQst);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
Par_PutHiddenParamUnsigned (NULL,"Order",(unsigned) Tst_DEFAULT_ORDER);
|
2019-10-10 10:41:00 +02:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_TABLE_BeginPadding (2);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Selection of tags *****/
|
2020-05-17 15:01:08 +02:00
|
|
|
|
Tag_ShowFormSelTags (&Test->Tags,mysql_res,false);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Selection of types of answers *****/
|
|
|
|
|
Tst_ShowFormAnswerTypes (&Test->AnswerTypes);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Starting and ending dates in the search *****/
|
|
|
|
|
Dat_PutFormStartEndClientLocalDateTimesWithYesterdayToday (SetHMS);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_TABLE_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Send button *****/
|
|
|
|
|
Btn_PutConfirmButton (Txt_Show_questions);
|
|
|
|
|
Frm_EndForm ();
|
|
|
|
|
}
|
|
|
|
|
else // No test questions
|
|
|
|
|
{
|
|
|
|
|
/***** Warning message *****/
|
|
|
|
|
Ale_ShowAlert (Ale_INFO,Txt_No_test_questions);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Button to create a new question *****/
|
|
|
|
|
Tst_PutButtonToAddQuestion ();
|
|
|
|
|
}
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** End box *****/
|
|
|
|
|
Box_BoxEnd ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/* Free structure that stores the query result */
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/******************* Select test questions for a game ************************/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-27 14:20:21 +02:00
|
|
|
|
void Tst_RequestSelectTestsForSet (struct Exa_Exams *Exams)
|
2020-04-22 03:15:04 +02:00
|
|
|
|
{
|
|
|
|
|
struct Tst_Test Test;
|
|
|
|
|
|
|
|
|
|
/***** Create test *****/
|
|
|
|
|
Tst_TstConstructor (&Test);
|
|
|
|
|
|
|
|
|
|
/***** Show form to select test for exam *****/
|
2020-04-27 14:20:21 +02:00
|
|
|
|
Tst_ShowFormRequestSelectTestsForSet (Exams,&Test); // No tags selected
|
2020-04-22 03:15:04 +02:00
|
|
|
|
|
|
|
|
|
/***** Destroy test *****/
|
|
|
|
|
Tst_TstDestructor (&Test);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************* Select test questions for a game ************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-08 03:06:45 +02:00
|
|
|
|
void Tst_RequestSelectTestsForGame (struct Gam_Games *Games)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2020-03-26 21:39:44 +01:00
|
|
|
|
struct Tst_Test Test;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-26 21:39:44 +01:00
|
|
|
|
/***** Create test *****/
|
|
|
|
|
Tst_TstConstructor (&Test);
|
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Show form to select test for game *****/
|
2020-04-08 03:06:45 +02:00
|
|
|
|
Tst_ShowFormRequestSelectTestsForGame (Games,&Test); // No tags selected
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Destroy test *****/
|
|
|
|
|
Tst_TstDestructor (&Test);
|
|
|
|
|
}
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************** Show form to select test questions for a exam ****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-27 14:20:21 +02:00
|
|
|
|
static void Tst_ShowFormRequestSelectTestsForSet (struct Exa_Exams *Exams,
|
|
|
|
|
struct Tst_Test *Test)
|
2020-04-22 03:15:04 +02:00
|
|
|
|
{
|
|
|
|
|
extern const char *Hlp_ASSESSMENT_Exams_questions;
|
|
|
|
|
extern const char *Txt_No_test_questions;
|
|
|
|
|
extern const char *Txt_Select_questions;
|
|
|
|
|
extern const char *Txt_Show_questions;
|
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
static const Dat_SetHMS SetHMS[Dat_NUM_START_END_TIME] =
|
|
|
|
|
{
|
2021-09-18 18:22:26 +02:00
|
|
|
|
[Dat_STR_TIME] = Dat_HMS_DO_NOT_SET,
|
|
|
|
|
[Dat_END_TIME] = Dat_HMS_DO_NOT_SET
|
2020-04-22 03:15:04 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/***** Begin box *****/
|
|
|
|
|
Box_BoxBegin (NULL,Txt_Select_questions,
|
|
|
|
|
NULL,NULL,
|
|
|
|
|
Hlp_ASSESSMENT_Exams_questions,Box_NOT_CLOSABLE);
|
|
|
|
|
|
|
|
|
|
/***** Get tags already present in the table of questions *****/
|
2020-05-17 17:11:04 +02:00
|
|
|
|
if ((Test->Tags.Num = Tag_GetAllTagsFromCurrentCrs (&mysql_res)))
|
2020-04-22 03:15:04 +02:00
|
|
|
|
{
|
2021-03-02 00:54:26 +01:00
|
|
|
|
Frm_BeginForm (ActLstTstQstForSet);
|
2020-04-27 14:20:21 +02:00
|
|
|
|
ExaSet_PutParamsOneSet (Exams);
|
2020-04-22 03:15:04 +02:00
|
|
|
|
|
|
|
|
|
HTM_TABLE_BeginPadding (2);
|
|
|
|
|
|
|
|
|
|
/***** Selection of tags *****/
|
2020-05-17 15:01:08 +02:00
|
|
|
|
Tag_ShowFormSelTags (&Test->Tags,mysql_res,false);
|
2020-04-22 03:15:04 +02:00
|
|
|
|
|
2020-04-27 14:20:21 +02:00
|
|
|
|
/***** Selection of types of answers *****/
|
|
|
|
|
Tst_ShowFormAnswerTypes (&Test->AnswerTypes);
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
/***** Starting and ending dates in the search *****/
|
|
|
|
|
Dat_PutFormStartEndClientLocalDateTimesWithYesterdayToday (SetHMS);
|
|
|
|
|
|
|
|
|
|
HTM_TABLE_End ();
|
|
|
|
|
|
|
|
|
|
/***** Send button *****/
|
|
|
|
|
Btn_PutConfirmButton (Txt_Show_questions);
|
|
|
|
|
Frm_EndForm ();
|
|
|
|
|
}
|
|
|
|
|
else // No test questions
|
|
|
|
|
{
|
|
|
|
|
/***** Warning message *****/
|
|
|
|
|
Ale_ShowAlert (Ale_INFO,Txt_No_test_questions);
|
|
|
|
|
|
|
|
|
|
/***** Button to create a new question *****/
|
|
|
|
|
Tst_PutButtonToAddQuestion ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** End box *****/
|
|
|
|
|
Box_BoxEnd ();
|
|
|
|
|
|
|
|
|
|
/* Free structure that stores the query result */
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************** Show form to select test questions for a game ****************/
|
|
|
|
|
/*****************************************************************************/
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2020-04-08 03:06:45 +02:00
|
|
|
|
static void Tst_ShowFormRequestSelectTestsForGame (struct Gam_Games *Games,
|
|
|
|
|
struct Tst_Test *Test)
|
2020-04-01 03:11:05 +02:00
|
|
|
|
{
|
|
|
|
|
extern const char *Hlp_ASSESSMENT_Games_questions;
|
|
|
|
|
extern const char *Txt_No_test_questions;
|
|
|
|
|
extern const char *Txt_Select_questions;
|
|
|
|
|
extern const char *Txt_Show_questions;
|
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
static const Dat_SetHMS SetHMS[Dat_NUM_START_END_TIME] =
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2021-09-18 18:22:26 +02:00
|
|
|
|
[Dat_STR_TIME] = Dat_HMS_DO_NOT_SET,
|
|
|
|
|
[Dat_END_TIME] = Dat_HMS_DO_NOT_SET
|
2020-04-01 03:11:05 +02:00
|
|
|
|
};
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Begin box *****/
|
|
|
|
|
Box_BoxBegin (NULL,Txt_Select_questions,
|
|
|
|
|
NULL,NULL,
|
|
|
|
|
Hlp_ASSESSMENT_Games_questions,Box_NOT_CLOSABLE);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Get tags already present in the table of questions *****/
|
2020-05-17 17:11:04 +02:00
|
|
|
|
if ((Test->Tags.Num = Tag_GetAllTagsFromCurrentCrs (&mysql_res)))
|
2020-04-01 03:11:05 +02:00
|
|
|
|
{
|
2021-03-02 00:54:26 +01:00
|
|
|
|
Frm_BeginForm (ActGamLstTstQst);
|
2020-04-08 03:06:45 +02:00
|
|
|
|
Gam_PutParams (Games);
|
2020-02-17 09:31:32 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_TABLE_BeginPadding (2);
|
2020-02-17 09:31:32 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Selection of tags *****/
|
2020-05-17 15:01:08 +02:00
|
|
|
|
Tag_ShowFormSelTags (&Test->Tags,mysql_res,false);
|
2020-02-17 09:31:32 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Starting and ending dates in the search *****/
|
|
|
|
|
Dat_PutFormStartEndClientLocalDateTimesWithYesterdayToday (SetHMS);
|
2020-02-17 09:31:32 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_TABLE_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Send button *****/
|
|
|
|
|
Btn_PutConfirmButton (Txt_Show_questions);
|
|
|
|
|
Frm_EndForm ();
|
|
|
|
|
}
|
|
|
|
|
else // No test questions
|
|
|
|
|
{
|
|
|
|
|
/***** Warning message *****/
|
|
|
|
|
Ale_ShowAlert (Ale_INFO,Txt_No_test_questions);
|
2015-04-11 23:46:21 +02:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Button to create a new question *****/
|
|
|
|
|
Tst_PutButtonToAddQuestion ();
|
|
|
|
|
}
|
2016-05-05 12:37:45 +02:00
|
|
|
|
|
2017-06-12 14:16:33 +02:00
|
|
|
|
/***** End box *****/
|
2019-10-25 22:48:34 +02:00
|
|
|
|
Box_BoxEnd ();
|
2020-03-26 21:39:44 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/* Free structure that stores the query result */
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/************************* Check if I can edit tests *************************/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
static bool Tst_CheckIfICanEditTests (void)
|
2016-12-26 15:17:30 +01:00
|
|
|
|
{
|
2020-04-01 03:11:05 +02:00
|
|
|
|
return (bool) (Gbl.Usrs.Me.Role.Logged == Rol_TCH ||
|
|
|
|
|
Gbl.Usrs.Me.Role.Logged == Rol_SYS_ADM);
|
|
|
|
|
}
|
2019-11-04 01:29:46 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************* Put contextual icons in tests *************************/
|
|
|
|
|
/*****************************************************************************/
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2020-05-17 18:45:27 +02:00
|
|
|
|
static void Tst_PutIconsRequestBankQsts (__attribute__((unused)) void *Args)
|
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_New_question;
|
|
|
|
|
|
|
|
|
|
/***** Put icon to create a new test question *****/
|
|
|
|
|
Ico_PutContextualIconToAdd (ActEdiOneTstQst,NULL,
|
|
|
|
|
NULL,NULL,
|
|
|
|
|
Txt_New_question);
|
|
|
|
|
|
|
|
|
|
/***** Put icon to edit tags *****/
|
|
|
|
|
Tag_PutIconToEditTags ();
|
|
|
|
|
|
|
|
|
|
/***** Put icon to import questions *****/
|
|
|
|
|
TsI_PutIconToImportQuestions ();
|
|
|
|
|
|
|
|
|
|
/***** Put icon to show a figure *****/
|
|
|
|
|
Fig_PutIconToShowFigure (Fig_TESTS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************* Put contextual icons in tests *************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void Tst_PutIconsEditBankQsts (void *Test)
|
2020-04-01 03:11:05 +02:00
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_New_question;
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2020-05-16 18:52:32 +02:00
|
|
|
|
/***** Put form to remove selected test questions *****/
|
|
|
|
|
switch (Gbl.Action.Act)
|
2020-04-01 03:11:05 +02:00
|
|
|
|
{
|
2020-05-16 18:52:32 +02:00
|
|
|
|
case ActLstTstQst: // List selected test questions for edition
|
|
|
|
|
case ActReqRemSevTstQst: // Request removal of selected questions
|
|
|
|
|
case ActReqRemOneTstQst: // Request removal of a question
|
|
|
|
|
case ActRemOneTstQst: // Remove a question
|
|
|
|
|
case ActChgShfTstQst: // Change shuffle of a question
|
2020-10-13 22:34:31 +02:00
|
|
|
|
Ico_PutContextualIconToRemove (ActReqRemSevTstQst,NULL,
|
2020-10-14 00:59:24 +02:00
|
|
|
|
Tst_PutParamsEditQst,Test);
|
2020-05-16 18:52:32 +02:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2020-05-16 18:52:32 +02:00
|
|
|
|
if (Gbl.Action.Act != ActEdiOneTstQst)
|
|
|
|
|
/***** Put form to create a new test question *****/
|
|
|
|
|
Ico_PutContextualIconToAdd (ActEdiOneTstQst,NULL,
|
|
|
|
|
NULL,NULL,
|
|
|
|
|
Txt_New_question);
|
2020-04-27 19:37:49 +02:00
|
|
|
|
|
2020-05-17 18:45:27 +02:00
|
|
|
|
/***** Put icon to edit tags *****/
|
|
|
|
|
Tag_PutIconToEditTags ();
|
|
|
|
|
|
|
|
|
|
/***** Put icon to export questions *****/
|
|
|
|
|
TsI_PutIconToExportQuestions (Test);
|
2020-05-17 15:01:08 +02:00
|
|
|
|
|
2020-05-16 18:52:32 +02:00
|
|
|
|
/***** Put icon to show a figure *****/
|
|
|
|
|
Fig_PutIconToShowFigure (Fig_TESTS);
|
|
|
|
|
}
|
2020-04-27 19:37:49 +02:00
|
|
|
|
|
2020-05-16 18:52:32 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************* Put contextual icons in tests *************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void Tst_PutIconsTests (__attribute__((unused)) void *Args)
|
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_New_question;
|
|
|
|
|
|
2020-04-27 19:37:49 +02:00
|
|
|
|
switch (Gbl.Usrs.Me.Role.Logged)
|
|
|
|
|
{
|
|
|
|
|
case Rol_STD:
|
2020-05-18 02:38:51 +02:00
|
|
|
|
/***** Put icon to view test results *****/
|
2020-04-27 19:37:49 +02:00
|
|
|
|
Ico_PutContextualIconToShowResults (ActReqSeeMyTstRes,NULL,
|
|
|
|
|
NULL,NULL);
|
|
|
|
|
break;
|
|
|
|
|
case Rol_NET:
|
|
|
|
|
case Rol_TCH:
|
|
|
|
|
case Rol_SYS_ADM:
|
2020-05-25 12:35:23 +02:00
|
|
|
|
/***** Put icon to go to test configuration *****/
|
2020-05-18 02:38:51 +02:00
|
|
|
|
Ico_PutContextualIconToConfigure (ActCfgTst,
|
|
|
|
|
NULL,NULL);
|
|
|
|
|
|
2020-05-25 12:35:23 +02:00
|
|
|
|
/***** Put icon to edit tags *****/
|
|
|
|
|
Tag_PutIconToEditTags ();
|
|
|
|
|
|
2020-05-18 02:38:51 +02:00
|
|
|
|
/***** Put icon to view test results *****/
|
2020-04-27 19:37:49 +02:00
|
|
|
|
Ico_PutContextualIconToShowResults (ActReqSeeUsrTstRes,NULL,
|
|
|
|
|
NULL,NULL);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** Put icon to show a figure *****/
|
|
|
|
|
Fig_PutIconToShowFigure (Fig_TESTS);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
}
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/**************** Put button to create a new test question *******************/
|
|
|
|
|
/*****************************************************************************/
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
static void Tst_PutButtonToAddQuestion (void)
|
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_New_question;
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2021-03-02 00:54:26 +01:00
|
|
|
|
Frm_BeginForm (ActEdiOneTstQst);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
Btn_PutConfirmButton (Txt_New_question);
|
|
|
|
|
Frm_EndForm ();
|
|
|
|
|
}
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************************** Form to rename tags ***************************/
|
|
|
|
|
/*****************************************************************************/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
void Tst_ShowFormConfig (void)
|
|
|
|
|
{
|
2020-04-23 20:24:36 +02:00
|
|
|
|
extern const char *Txt_Please_specify_if_you_allow_downloading_the_question_bank_from_other_applications;
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** If current course has tests and pluggable is unknown... *****/
|
|
|
|
|
if (Tst_CheckIfCourseHaveTestsAndPluggableIsUnknown ())
|
2020-04-23 20:24:36 +02:00
|
|
|
|
Ale_ShowAlert (Ale_WARNING,Txt_Please_specify_if_you_allow_downloading_the_question_bank_from_other_applications);
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Form to configure test *****/
|
|
|
|
|
Tst_ShowFormConfigTst ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*************** Get configuration of test for current course ****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
// Returns true if course has test tags and pluggable is unknown
|
|
|
|
|
// Return false if course has no test tags or pluggable is known
|
|
|
|
|
|
|
|
|
|
bool Tst_CheckIfCourseHaveTestsAndPluggableIsUnknown (void)
|
|
|
|
|
{
|
|
|
|
|
extern const char *TstCfg_PluggableDB[TstCfg_NUM_OPTIONS_PLUGGABLE];
|
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
MYSQL_ROW row;
|
2021-04-16 13:51:12 +02:00
|
|
|
|
unsigned NumRows;
|
2020-04-01 03:11:05 +02:00
|
|
|
|
TstCfg_Pluggable_t Pluggable;
|
|
|
|
|
|
|
|
|
|
/***** Get pluggability of tests for current course from database *****/
|
2021-04-16 13:51:12 +02:00
|
|
|
|
NumRows = (unsigned)
|
|
|
|
|
DB_QuerySELECT (&mysql_res,"can not get configuration of test",
|
|
|
|
|
"SELECT Pluggable" // row[0]
|
|
|
|
|
" FROM tst_config"
|
|
|
|
|
" WHERE CrsCod=%ld",
|
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
|
|
|
|
if (NumRows == 0)
|
|
|
|
|
TstCfg_SetConfigPluggable (TstCfg_PLUGGABLE_UNKNOWN);
|
|
|
|
|
else // NumRows == 1
|
|
|
|
|
{
|
|
|
|
|
/***** Get whether test are visible via plugins or not *****/
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
|
|
|
|
|
|
|
|
|
TstCfg_SetConfigPluggable (TstCfg_PLUGGABLE_UNKNOWN);
|
2020-04-23 20:24:36 +02:00
|
|
|
|
for (Pluggable = TstCfg_PLUGGABLE_NO;
|
2020-04-01 03:11:05 +02:00
|
|
|
|
Pluggable <= TstCfg_PLUGGABLE_YES;
|
|
|
|
|
Pluggable++)
|
|
|
|
|
if (!strcmp (row[0],TstCfg_PluggableDB[Pluggable]))
|
|
|
|
|
{
|
|
|
|
|
TstCfg_SetConfigPluggable (Pluggable);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
|
|
|
|
|
/***** Get if current course has tests from database *****/
|
|
|
|
|
if (TstCfg_GetConfigPluggable () == TstCfg_PLUGGABLE_UNKNOWN)
|
2020-05-17 17:11:04 +02:00
|
|
|
|
return Tag_CheckIfCurrentCrsHasTestTags (); // Return true if course has tests
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
|
|
|
|
return false; // Pluggable is not unknown
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************* Show a form to to configure test **********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void Tst_ShowFormConfigTst (void)
|
|
|
|
|
{
|
2020-04-23 20:24:36 +02:00
|
|
|
|
extern const char *Hlp_ASSESSMENT_Tests_configuring_tests;
|
2020-04-01 03:11:05 +02:00
|
|
|
|
extern const char *The_ClassFormInBox[The_NUM_THEMES];
|
|
|
|
|
extern const char *Txt_Configure_tests;
|
|
|
|
|
extern const char *Txt_Plugins;
|
|
|
|
|
extern const char *Txt_TST_PLUGGABLE[TstCfg_NUM_OPTIONS_PLUGGABLE];
|
2020-05-07 14:15:39 +02:00
|
|
|
|
extern const char *Txt_Number_of_questions;
|
2020-04-01 03:11:05 +02:00
|
|
|
|
extern const char *Txt_minimum;
|
|
|
|
|
extern const char *Txt_default;
|
|
|
|
|
extern const char *Txt_maximum;
|
|
|
|
|
extern const char *Txt_Minimum_time_seconds_per_question_between_two_tests;
|
|
|
|
|
extern const char *Txt_Result_visibility;
|
|
|
|
|
extern const char *Txt_Save_changes;
|
|
|
|
|
struct Tst_Test Test;
|
|
|
|
|
TstCfg_Pluggable_t Pluggable;
|
|
|
|
|
char StrMinTimeNxtTstPerQst[Cns_MAX_DECIMAL_DIGITS_ULONG + 1];
|
|
|
|
|
|
|
|
|
|
/***** Create test *****/
|
|
|
|
|
Tst_TstConstructor (&Test);
|
|
|
|
|
|
|
|
|
|
/***** Read test configuration from database *****/
|
|
|
|
|
TstCfg_GetConfigFromDB ();
|
|
|
|
|
|
|
|
|
|
/***** Begin box *****/
|
|
|
|
|
Box_BoxBegin (NULL,Txt_Configure_tests,
|
2020-05-16 18:52:32 +02:00
|
|
|
|
Tst_PutIconsTests,NULL,
|
2020-04-23 20:24:36 +02:00
|
|
|
|
Hlp_ASSESSMENT_Tests_configuring_tests,Box_NOT_CLOSABLE);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
|
|
|
|
/***** Begin form *****/
|
2021-03-02 00:54:26 +01:00
|
|
|
|
Frm_BeginForm (ActRcvCfgTst);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
|
|
|
|
/***** Tests are visible from plugins? *****/
|
|
|
|
|
HTM_TABLE_BeginCenterPadding (2);
|
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
|
|
|
|
|
|
HTM_TD_Begin ("class=\"%s RT\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
|
2020-06-18 20:06:17 +02:00
|
|
|
|
HTM_TxtColon (Txt_Plugins);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
HTM_TD_Begin ("class=\"LB\"");
|
2020-04-23 20:24:36 +02:00
|
|
|
|
for (Pluggable = TstCfg_PLUGGABLE_NO;
|
2020-04-01 03:11:05 +02:00
|
|
|
|
Pluggable <= TstCfg_PLUGGABLE_YES;
|
|
|
|
|
Pluggable++)
|
|
|
|
|
{
|
|
|
|
|
HTM_LABEL_Begin ("class=\"DAT\"");
|
|
|
|
|
HTM_INPUT_RADIO ("Pluggable",false,
|
|
|
|
|
"value=\"%u\"%s",
|
|
|
|
|
(unsigned) Pluggable,
|
|
|
|
|
Pluggable == TstCfg_GetConfigPluggable () ? " checked=\"checked\"" :
|
2020-04-23 20:24:36 +02:00
|
|
|
|
"");
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_Txt (Txt_TST_PLUGGABLE[Pluggable]);
|
|
|
|
|
HTM_LABEL_End ();
|
|
|
|
|
HTM_BR ();
|
|
|
|
|
}
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
HTM_TR_End ();
|
|
|
|
|
|
|
|
|
|
/***** Number of questions *****/
|
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
|
|
|
|
|
|
HTM_TD_Begin ("class=\"%s RT\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
|
2020-06-18 20:06:17 +02:00
|
|
|
|
HTM_TxtColon (Txt_Number_of_questions);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
HTM_TD_Begin ("class=\"LB\"");
|
|
|
|
|
HTM_TABLE_BeginPadding (2);
|
|
|
|
|
Tst_PutInputFieldNumQst ("NumQstMin",Txt_minimum,
|
|
|
|
|
TstCfg_GetConfigMin ()); // Minimum number of questions
|
|
|
|
|
Tst_PutInputFieldNumQst ("NumQstDef",Txt_default,
|
|
|
|
|
TstCfg_GetConfigDef ()); // Default number of questions
|
|
|
|
|
Tst_PutInputFieldNumQst ("NumQstMax",Txt_maximum,
|
|
|
|
|
TstCfg_GetConfigMax ()); // Maximum number of questions
|
|
|
|
|
HTM_TABLE_End ();
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
HTM_TR_End ();
|
|
|
|
|
|
|
|
|
|
/***** Minimum time between consecutive tests, per question *****/
|
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
|
|
|
|
|
|
/* Label */
|
|
|
|
|
Frm_LabelColumn ("RT","MinTimeNxtTstPerQst",
|
|
|
|
|
Txt_Minimum_time_seconds_per_question_between_two_tests);
|
|
|
|
|
|
|
|
|
|
/* Data */
|
|
|
|
|
HTM_TD_Begin ("class=\"LB\"");
|
2021-02-15 16:25:55 +01:00
|
|
|
|
snprintf (StrMinTimeNxtTstPerQst,sizeof (StrMinTimeNxtTstPerQst),"%lu",
|
|
|
|
|
TstCfg_GetConfigMinTimeNxtTstPerQst ());
|
2020-04-27 03:16:55 +02:00
|
|
|
|
HTM_INPUT_TEXT ("MinTimeNxtTstPerQst",Cns_MAX_DECIMAL_DIGITS_ULONG,StrMinTimeNxtTstPerQst,
|
|
|
|
|
HTM_DONT_SUBMIT_ON_CHANGE,
|
2020-04-01 03:11:05 +02:00
|
|
|
|
"id=\"MinTimeNxtTstPerQst\" size=\"7\" required=\"required\"");
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
HTM_TR_End ();
|
|
|
|
|
|
2020-04-02 03:28:08 +02:00
|
|
|
|
/***** Visibility of test exams *****/
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
|
|
|
|
|
|
HTM_TD_Begin ("class=\"%s RT\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
|
2020-06-18 20:06:17 +02:00
|
|
|
|
HTM_TxtColon (Txt_Result_visibility);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
HTM_TD_Begin ("class=\"LB\"");
|
2020-04-02 03:28:08 +02:00
|
|
|
|
TstVis_PutVisibilityCheckboxes (TstCfg_GetConfigVisibility ());
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
HTM_TR_End ();
|
|
|
|
|
|
|
|
|
|
HTM_TABLE_End ();
|
|
|
|
|
|
|
|
|
|
/***** Send button *****/
|
|
|
|
|
Btn_PutConfirmButton (Txt_Save_changes);
|
|
|
|
|
|
|
|
|
|
/***** End form *****/
|
|
|
|
|
Frm_EndForm ();
|
|
|
|
|
|
|
|
|
|
/***** End box *****/
|
|
|
|
|
Box_BoxEnd ();
|
|
|
|
|
|
|
|
|
|
/***** Destroy test *****/
|
|
|
|
|
Tst_TstDestructor (&Test);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*************** Get configuration of test for current course ****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void Tst_PutInputFieldNumQst (const char *Field,const char *Label,
|
|
|
|
|
unsigned Value)
|
|
|
|
|
{
|
|
|
|
|
char StrValue[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
|
|
|
|
|
|
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
|
|
|
|
|
|
HTM_TD_Begin ("class=\"RM\"");
|
|
|
|
|
HTM_LABEL_Begin ("for=\"%s\" class=\"DAT\"",Field);
|
|
|
|
|
HTM_Txt (Label);
|
|
|
|
|
HTM_LABEL_End ();
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
HTM_TD_Begin ("class=\"LM\"");
|
2021-02-15 16:25:55 +01:00
|
|
|
|
snprintf (StrValue,sizeof (StrValue),"%u",Value);
|
2020-04-27 03:16:55 +02:00
|
|
|
|
HTM_INPUT_TEXT (Field,Cns_MAX_DECIMAL_DIGITS_UINT,StrValue,
|
|
|
|
|
HTM_DONT_SUBMIT_ON_CHANGE,
|
2020-04-01 03:11:05 +02:00
|
|
|
|
"id=\"%s\" size=\"3\" required=\"required\"",Field);
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
HTM_TR_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************** Show form for select the types of answers *****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void Tst_ShowFormAnswerTypes (const struct Tst_AnswerTypes *AnswerTypes)
|
|
|
|
|
{
|
|
|
|
|
extern const char *The_ClassFormInBox[The_NUM_THEMES];
|
|
|
|
|
extern const char *Txt_Types_of_answers;
|
|
|
|
|
extern const char *Txt_All_types_of_answers;
|
|
|
|
|
extern const char *Txt_TST_STR_ANSWER_TYPES[Tst_NUM_ANS_TYPES];
|
|
|
|
|
Tst_AnswerType_t AnsType;
|
|
|
|
|
bool Checked;
|
|
|
|
|
char UnsignedStr[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
|
|
|
|
|
const char *Ptr;
|
|
|
|
|
|
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
|
|
|
|
|
|
/***** Label *****/
|
|
|
|
|
HTM_TD_Begin ("class=\"RT %s\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
|
2020-06-18 20:06:17 +02:00
|
|
|
|
HTM_TxtColon (Txt_Types_of_answers);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
/***** Select all types of answers *****/
|
|
|
|
|
HTM_TD_Begin ("class=\"LT\"");
|
|
|
|
|
HTM_TABLE_BeginPadding (2);
|
|
|
|
|
|
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
|
|
|
|
|
|
HTM_TD_Begin ("class=\"LM\"");
|
|
|
|
|
HTM_LABEL_Begin ("class=\"%s\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
|
|
|
|
|
HTM_INPUT_CHECKBOX ("AllAnsTypes",HTM_DONT_SUBMIT_ON_CHANGE,
|
|
|
|
|
"value=\"Y\"%s onclick=\"togglecheckChildren(this,'AnswerType');\"",
|
|
|
|
|
AnswerTypes->All ? " checked=\"checked\"" :
|
|
|
|
|
"");
|
|
|
|
|
HTM_TxtF (" %s",Txt_All_types_of_answers);
|
|
|
|
|
HTM_LABEL_End ();
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
HTM_TR_End ();
|
|
|
|
|
|
|
|
|
|
/***** Type of answer *****/
|
|
|
|
|
for (AnsType = (Tst_AnswerType_t) 0;
|
|
|
|
|
AnsType <= (Tst_AnswerType_t) (Tst_NUM_ANS_TYPES - 1);
|
|
|
|
|
AnsType++)
|
|
|
|
|
{
|
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
|
|
|
|
|
|
Checked = false;
|
|
|
|
|
Ptr = AnswerTypes->List;
|
|
|
|
|
while (*Ptr)
|
|
|
|
|
{
|
|
|
|
|
Par_GetNextStrUntilSeparParamMult (&Ptr,UnsignedStr,Cns_MAX_DECIMAL_DIGITS_UINT);
|
|
|
|
|
if (Tst_ConvertFromUnsignedStrToAnsTyp (UnsignedStr) == AnsType)
|
|
|
|
|
{
|
|
|
|
|
Checked = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
HTM_TD_Begin ("class=\"LM\"");
|
|
|
|
|
HTM_LABEL_Begin ("class=\"DAT\"");
|
|
|
|
|
HTM_INPUT_CHECKBOX ("AnswerType",HTM_DONT_SUBMIT_ON_CHANGE,
|
|
|
|
|
"value=\"%u\"%s onclick=\"checkParent(this,'AllAnsTypes');\"",
|
|
|
|
|
(unsigned) AnsType,
|
|
|
|
|
Checked ? " checked=\"checked\"" :
|
|
|
|
|
"");
|
|
|
|
|
HTM_TxtF (" %s",Txt_TST_STR_ANSWER_TYPES[AnsType]);
|
|
|
|
|
HTM_LABEL_End ();
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
HTM_TR_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HTM_TABLE_End ();
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
HTM_TR_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************** List several test questions for edition *******************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void Tst_ListQuestionsToEdit (void)
|
|
|
|
|
{
|
|
|
|
|
struct Tst_Test Test;
|
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
|
|
|
|
|
/***** Create test *****/
|
|
|
|
|
Tst_TstConstructor (&Test);
|
|
|
|
|
|
|
|
|
|
/***** Get parameters, query the database and list the questions *****/
|
|
|
|
|
if (Tst_GetParamsTst (&Test,Tst_EDIT_TEST)) // Get parameters from the form
|
|
|
|
|
{
|
|
|
|
|
/***** Get question codes from database *****/
|
|
|
|
|
Tst_GetQuestions (&Test,&mysql_res); // Query database
|
|
|
|
|
if (Test.NumQsts)
|
|
|
|
|
{
|
|
|
|
|
/* Contextual menu */
|
|
|
|
|
if (TsI_GetCreateXMLParamFromForm ())
|
2020-05-17 18:45:27 +02:00
|
|
|
|
{
|
|
|
|
|
Mnu_ContextMenuBegin ();
|
2020-04-01 03:11:05 +02:00
|
|
|
|
TsI_CreateXML (Test.NumQsts,mysql_res); // Create XML file with exported questions...
|
|
|
|
|
// ...and put a link to download it
|
2020-05-17 18:45:27 +02:00
|
|
|
|
Mnu_ContextMenuEnd ();
|
|
|
|
|
}
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
|
|
|
|
/* Show the table with the questions */
|
|
|
|
|
Tst_ListOneOrMoreQuestionsForEdition (&Test,mysql_res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
/* Show the form again */
|
|
|
|
|
Tst_ShowFormRequestEditTests (&Test);
|
|
|
|
|
|
|
|
|
|
/***** Destroy test *****/
|
|
|
|
|
Tst_TstDestructor (&Test);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2020-04-22 03:15:04 +02:00
|
|
|
|
/************ List several test questions for selection for exam *************/
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-27 14:20:21 +02:00
|
|
|
|
void Tst_ListQuestionsToSelectForSet (struct Exa_Exams *Exams)
|
2020-04-01 03:11:05 +02:00
|
|
|
|
{
|
|
|
|
|
struct Tst_Test Test;
|
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
|
|
|
|
|
/***** Create test *****/
|
|
|
|
|
Tst_TstConstructor (&Test);
|
|
|
|
|
|
|
|
|
|
/***** Get parameters, query the database and list the questions *****/
|
2020-04-27 14:20:21 +02:00
|
|
|
|
if (Tst_GetParamsTst (&Test,Tst_SELECT_QUESTIONS_FOR_EXAM)) // Get parameters from the form
|
2020-04-01 03:11:05 +02:00
|
|
|
|
{
|
|
|
|
|
Tst_GetQuestions (&Test,&mysql_res); // Query database
|
|
|
|
|
if (Test.NumQsts)
|
|
|
|
|
/* Show the table with the questions */
|
2020-04-27 14:20:21 +02:00
|
|
|
|
Tst_ListOneOrMoreQuestionsForSelectionForSet (Exams,Test.NumQsts,mysql_res);
|
2020-04-22 03:15:04 +02:00
|
|
|
|
|
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
/* Show the form again */
|
2020-04-27 14:20:21 +02:00
|
|
|
|
Tst_ShowFormRequestSelectTestsForSet (Exams,&Test);
|
2020-04-22 03:15:04 +02:00
|
|
|
|
|
|
|
|
|
/***** Destroy test *****/
|
|
|
|
|
Tst_TstDestructor (&Test);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************ List several test questions for selection for game *************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void Tst_ListQuestionsToSelectForGame (struct Gam_Games *Games)
|
|
|
|
|
{
|
|
|
|
|
struct Tst_Test Test;
|
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
|
|
|
|
|
/***** Create test *****/
|
|
|
|
|
Tst_TstConstructor (&Test);
|
|
|
|
|
|
|
|
|
|
/***** Get parameters, query the database and list the questions *****/
|
|
|
|
|
if (Tst_GetParamsTst (&Test,Tst_SELECT_QUESTIONS_FOR_GAME)) // Get parameters from the form
|
|
|
|
|
{
|
|
|
|
|
Tst_GetQuestions (&Test,&mysql_res); // Query database
|
|
|
|
|
if (Test.NumQsts)
|
|
|
|
|
/* Show the table with the questions */
|
|
|
|
|
Tst_ListOneOrMoreQuestionsForSelectionForGame (Games,Test.NumQsts,mysql_res);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
/* Show the form again */
|
2020-04-08 03:06:45 +02:00
|
|
|
|
Tst_ShowFormRequestSelectTestsForGame (Games,&Test);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
|
|
|
|
/***** Destroy test *****/
|
|
|
|
|
Tst_TstDestructor (&Test);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********** Get from the database several test questions for listing *********/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#define Tst_MAX_BYTES_QUERY_TEST (16 * 1024 - 1)
|
|
|
|
|
|
|
|
|
|
static void Tst_GetQuestions (struct Tst_Test *Test,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;
|
2020-05-17 17:11:04 +02:00
|
|
|
|
char TagText[Tag_MAX_BYTES_TAG + 1];
|
2020-04-01 03:11:05 +02:00
|
|
|
|
char LongStr[Cns_MAX_DECIMAL_DIGITS_LONG + 1];
|
|
|
|
|
char UnsignedStr[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
|
|
|
|
|
Tst_AnswerType_t AnsType;
|
|
|
|
|
char CrsCodStr[Cns_MAX_DECIMAL_DIGITS_LONG + 1];
|
|
|
|
|
|
|
|
|
|
/***** Allocate space for query *****/
|
2021-02-15 16:25:55 +01:00
|
|
|
|
if ((Query = malloc (Tst_MAX_BYTES_QUERY_TEST + 1)) == NULL)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_NotEnoughMemoryExit ();
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
|
|
|
|
/***** Select questions *****/
|
2021-07-08 15:00:17 +02:00
|
|
|
|
/* Begin query */
|
2021-02-15 16:25:55 +01:00
|
|
|
|
Str_Copy (Query,"SELECT tst_questions.QstCod" // row[0]
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM tst_questions",Tst_MAX_BYTES_QUERY_TEST);
|
2020-03-24 00:58:42 +01:00
|
|
|
|
if (!Test->Tags.All)
|
2021-02-15 16:25:55 +01:00
|
|
|
|
Str_Concat (Query,",tst_question_tags,tst_tags",Tst_MAX_BYTES_QUERY_TEST);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2021-02-15 16:25:55 +01:00
|
|
|
|
Str_Concat (Query," WHERE tst_questions.CrsCod='",Tst_MAX_BYTES_QUERY_TEST);
|
|
|
|
|
snprintf (CrsCodStr,sizeof (CrsCodStr),"%ld",Gbl.Hierarchy.Crs.CrsCod);
|
|
|
|
|
Str_Concat (Query,CrsCodStr,Tst_MAX_BYTES_QUERY_TEST);
|
2018-10-30 02:37:09 +01:00
|
|
|
|
Str_Concat (Query,"' AND tst_questions.EditTime>=FROM_UNIXTIME('",
|
2017-03-08 03:48:23 +01:00
|
|
|
|
Tst_MAX_BYTES_QUERY_TEST);
|
2021-02-15 16:25:55 +01:00
|
|
|
|
snprintf (LongStr,sizeof (LongStr),"%ld",
|
2021-09-18 18:22:26 +02:00
|
|
|
|
(long) Gbl.DateRange.TimeUTC[Dat_STR_TIME]);
|
2021-02-15 16:25:55 +01:00
|
|
|
|
Str_Concat (Query,LongStr,Tst_MAX_BYTES_QUERY_TEST);
|
2018-10-30 02:37:09 +01:00
|
|
|
|
Str_Concat (Query,"') AND tst_questions.EditTime<=FROM_UNIXTIME('",
|
2017-03-08 03:48:23 +01:00
|
|
|
|
Tst_MAX_BYTES_QUERY_TEST);
|
2021-02-15 16:25:55 +01:00
|
|
|
|
snprintf (LongStr,sizeof (LongStr),"%ld",
|
2020-02-14 10:02:58 +01:00
|
|
|
|
(long) Gbl.DateRange.TimeUTC[Dat_END_TIME]);
|
2021-02-15 16:25:55 +01:00
|
|
|
|
Str_Concat (Query,LongStr,Tst_MAX_BYTES_QUERY_TEST);
|
|
|
|
|
Str_Concat (Query,"')",Tst_MAX_BYTES_QUERY_TEST);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/* Add the tags selected */
|
2020-03-24 00:58:42 +01:00
|
|
|
|
if (!Test->Tags.All)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2018-10-30 02:37:09 +01:00
|
|
|
|
Str_Concat (Query," AND tst_questions.QstCod=tst_question_tags.QstCod"
|
2017-01-16 01:51:01 +01:00
|
|
|
|
" AND tst_question_tags.TagCod=tst_tags.TagCod"
|
2017-01-17 03:33:05 +01:00
|
|
|
|
" AND tst_tags.CrsCod='",
|
2017-03-08 03:48:23 +01:00
|
|
|
|
Tst_MAX_BYTES_QUERY_TEST);
|
2021-02-15 16:25:55 +01:00
|
|
|
|
Str_Concat (Query,CrsCodStr,Tst_MAX_BYTES_QUERY_TEST);
|
|
|
|
|
Str_Concat (Query,"'",Tst_MAX_BYTES_QUERY_TEST);
|
2018-10-30 02:37:09 +01:00
|
|
|
|
LengthQuery = strlen (Query);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
NumItemInList = 0;
|
2020-03-24 00:58:42 +01:00
|
|
|
|
Ptr = Test->Tags.List;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
while (*Ptr)
|
|
|
|
|
{
|
2020-05-17 17:11:04 +02:00
|
|
|
|
Par_GetNextStrUntilSeparParamMult (&Ptr,TagText,Tag_MAX_BYTES_TAG);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
LengthQuery = LengthQuery + 35 + strlen (TagText) + 1;
|
2017-03-08 03:48:23 +01:00
|
|
|
|
if (LengthQuery > Tst_MAX_BYTES_QUERY_TEST - 256)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_ShowErrorAndExit ("Query size exceed.");
|
2018-10-30 02:37:09 +01:00
|
|
|
|
Str_Concat (Query,
|
2017-01-16 01:51:01 +01:00
|
|
|
|
NumItemInList ? " OR tst_tags.TagTxt='" :
|
|
|
|
|
" AND (tst_tags.TagTxt='",
|
2017-03-08 03:48:23 +01:00
|
|
|
|
Tst_MAX_BYTES_QUERY_TEST);
|
2021-02-15 16:25:55 +01:00
|
|
|
|
Str_Concat (Query,TagText,Tst_MAX_BYTES_QUERY_TEST);
|
|
|
|
|
Str_Concat (Query,"'",Tst_MAX_BYTES_QUERY_TEST);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
NumItemInList++;
|
|
|
|
|
}
|
2021-02-15 16:25:55 +01:00
|
|
|
|
Str_Concat (Query,")",Tst_MAX_BYTES_QUERY_TEST);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add the types of answer selected */
|
2020-03-24 00:58:42 +01:00
|
|
|
|
if (!Test->AnswerTypes.All)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2018-10-30 02:37:09 +01:00
|
|
|
|
LengthQuery = strlen (Query);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
NumItemInList = 0;
|
2020-03-24 00:58:42 +01:00
|
|
|
|
Ptr = Test->AnswerTypes.List;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
while (*Ptr)
|
|
|
|
|
{
|
2020-05-17 17:11:04 +02:00
|
|
|
|
Par_GetNextStrUntilSeparParamMult (&Ptr,UnsignedStr,Tag_MAX_BYTES_TAG);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
AnsType = Tst_ConvertFromUnsignedStrToAnsTyp (UnsignedStr);
|
|
|
|
|
LengthQuery = LengthQuery + 35 + strlen (Tst_StrAnswerTypesDB[AnsType]) + 1;
|
2017-03-08 03:48:23 +01:00
|
|
|
|
if (LengthQuery > Tst_MAX_BYTES_QUERY_TEST - 256)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_ShowErrorAndExit ("Query size exceed.");
|
2018-10-30 02:37:09 +01:00
|
|
|
|
Str_Concat (Query,
|
2017-01-16 01:51:01 +01:00
|
|
|
|
NumItemInList ? " OR tst_questions.AnsType='" :
|
|
|
|
|
" AND (tst_questions.AnsType='",
|
2017-03-08 03:48:23 +01:00
|
|
|
|
Tst_MAX_BYTES_QUERY_TEST);
|
2021-02-15 16:25:55 +01:00
|
|
|
|
Str_Concat (Query,Tst_StrAnswerTypesDB[AnsType],Tst_MAX_BYTES_QUERY_TEST);
|
|
|
|
|
Str_Concat (Query,"'",Tst_MAX_BYTES_QUERY_TEST);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
NumItemInList++;
|
|
|
|
|
}
|
2021-02-15 16:25:55 +01:00
|
|
|
|
Str_Concat (Query,")",Tst_MAX_BYTES_QUERY_TEST);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* End the query */
|
2021-02-15 16:25:55 +01:00
|
|
|
|
Str_Concat (Query," GROUP BY tst_questions.QstCod",Tst_MAX_BYTES_QUERY_TEST);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-24 00:58:42 +01:00
|
|
|
|
switch (Test->SelectedOrder)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
case Tst_ORDER_STEM:
|
2018-10-30 02:37:09 +01:00
|
|
|
|
Str_Concat (Query," ORDER BY tst_questions.Stem",
|
2017-03-08 03:48:23 +01:00
|
|
|
|
Tst_MAX_BYTES_QUERY_TEST);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
break;
|
|
|
|
|
case Tst_ORDER_NUM_HITS:
|
2018-10-30 02:37:09 +01:00
|
|
|
|
Str_Concat (Query," ORDER BY tst_questions.NumHits DESC,"
|
2018-10-29 22:22:02 +01:00
|
|
|
|
"tst_questions.Stem",
|
2017-03-08 03:48:23 +01:00
|
|
|
|
Tst_MAX_BYTES_QUERY_TEST);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
break;
|
|
|
|
|
case Tst_ORDER_AVERAGE_SCORE:
|
2018-10-30 02:37:09 +01:00
|
|
|
|
Str_Concat (Query," ORDER BY tst_questions.Score/tst_questions.NumHits DESC,"
|
2018-10-29 22:22:02 +01:00
|
|
|
|
"tst_questions.NumHits DESC,"
|
|
|
|
|
"tst_questions.Stem",
|
2017-03-08 03:48:23 +01:00
|
|
|
|
Tst_MAX_BYTES_QUERY_TEST);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
break;
|
|
|
|
|
case Tst_ORDER_NUM_HITS_NOT_BLANK:
|
2018-10-30 02:37:09 +01:00
|
|
|
|
Str_Concat (Query," ORDER BY tst_questions.NumHitsNotBlank DESC,"
|
2018-10-29 22:22:02 +01:00
|
|
|
|
"tst_questions.Stem",
|
2017-03-08 03:48:23 +01:00
|
|
|
|
Tst_MAX_BYTES_QUERY_TEST);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
break;
|
|
|
|
|
case Tst_ORDER_AVERAGE_SCORE_NOT_BLANK:
|
2018-10-30 02:37:09 +01:00
|
|
|
|
Str_Concat (Query," ORDER BY tst_questions.Score/tst_questions.NumHitsNotBlank DESC,"
|
2018-10-29 22:22:02 +01:00
|
|
|
|
"tst_questions.NumHitsNotBlank DESC,"
|
|
|
|
|
"tst_questions.Stem",
|
2017-03-08 03:48:23 +01:00
|
|
|
|
Tst_MAX_BYTES_QUERY_TEST);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make the query */
|
2020-03-24 01:50:39 +01:00
|
|
|
|
Test->NumQsts = (unsigned) DB_QuerySELECT (mysql_res,"can not get questions",
|
|
|
|
|
"%s",
|
|
|
|
|
Query);
|
|
|
|
|
if (Test->NumQsts == 0)
|
2019-02-16 19:29:27 +01:00
|
|
|
|
Ale_ShowAlert (Ale_INFO,Txt_No_questions_found_matching_your_search_criteria);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/************** Get questions for a new test from the database ***************/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
static void Tst_GetQuestionsForNewTestFromDB (struct Tst_Test *Test,
|
2020-05-07 18:33:26 +02:00
|
|
|
|
struct TstPrn_Print *Print)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2020-04-01 03:11:05 +02:00
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
MYSQL_ROW row;
|
2018-10-30 03:29:40 +01:00
|
|
|
|
char *Query = NULL;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
long LengthQuery;
|
|
|
|
|
unsigned NumItemInList;
|
|
|
|
|
const char *Ptr;
|
2020-05-17 17:11:04 +02:00
|
|
|
|
char TagText[Tag_MAX_BYTES_TAG + 1];
|
2019-11-08 01:10:32 +01:00
|
|
|
|
char UnsignedStr[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
|
2020-04-30 20:15:21 +02:00
|
|
|
|
Tst_AnswerType_t AnswerType;
|
2020-04-01 03:11:05 +02:00
|
|
|
|
bool Shuffle;
|
2019-11-08 01:10:32 +01:00
|
|
|
|
char StrNumQsts[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
|
2021-04-26 17:29:04 +02:00
|
|
|
|
unsigned QstInd;
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
|
|
|
|
/***** Trivial check: number of questions *****/
|
|
|
|
|
if (Test->NumQsts == 0 ||
|
|
|
|
|
Test->NumQsts > TstCfg_MAX_QUESTIONS_PER_TEST)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_ShowErrorAndExit ("Wrong number of questions.");
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2018-10-29 22:22:02 +01:00
|
|
|
|
/***** Allocate space for query *****/
|
2021-02-15 16:25:55 +01:00
|
|
|
|
if ((Query = malloc (Tst_MAX_BYTES_QUERY_TEST + 1)) == NULL)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_NotEnoughMemoryExit ();
|
2018-10-29 22:22:02 +01:00
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/***** Select questions without hidden tags *****/
|
2021-07-08 15:00:17 +02:00
|
|
|
|
/* Begin query */
|
2014-12-01 23:55:08 +01:00
|
|
|
|
// Reject questions with any tag hidden
|
|
|
|
|
// Select only questions with tags
|
2015-06-16 20:12:38 +02:00
|
|
|
|
// DISTINCTROW is necessary to not repeat questions
|
2018-10-30 02:37:09 +01:00
|
|
|
|
snprintf (Query,Tst_MAX_BYTES_QUERY_TEST + 1,
|
2020-04-01 03:11:05 +02:00
|
|
|
|
"SELECT DISTINCTROW tst_questions.QstCod," // row[0]
|
|
|
|
|
"tst_questions.AnsType," // row[1]
|
|
|
|
|
"tst_questions.Shuffle" // row[2]
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM tst_questions,tst_question_tags,tst_tags"
|
2018-10-29 22:22:02 +01:00
|
|
|
|
" WHERE tst_questions.CrsCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" 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",
|
2019-04-04 10:45:15 +02:00
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod,
|
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod,
|
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-24 00:58:42 +01:00
|
|
|
|
if (!Test->Tags.All) // User has not selected all the tags
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
/* Add selected tags */
|
2018-10-30 02:37:09 +01:00
|
|
|
|
LengthQuery = strlen (Query);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
NumItemInList = 0;
|
2020-03-24 00:58:42 +01:00
|
|
|
|
Ptr = Test->Tags.List;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
while (*Ptr)
|
|
|
|
|
{
|
2020-05-17 17:11:04 +02:00
|
|
|
|
Par_GetNextStrUntilSeparParamMult (&Ptr,TagText,Tag_MAX_BYTES_TAG);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
LengthQuery = LengthQuery + 35 + strlen (TagText) + 1;
|
2017-03-08 03:48:23 +01:00
|
|
|
|
if (LengthQuery > Tst_MAX_BYTES_QUERY_TEST - 128)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_ShowErrorAndExit ("Query size exceed.");
|
2018-10-30 02:37:09 +01:00
|
|
|
|
Str_Concat (Query,
|
2017-01-16 01:51:01 +01:00
|
|
|
|
NumItemInList ? " OR tst_tags.TagTxt='" :
|
|
|
|
|
" AND (tst_tags.TagTxt='",
|
2017-03-08 03:48:23 +01:00
|
|
|
|
Tst_MAX_BYTES_QUERY_TEST);
|
2021-02-15 16:25:55 +01:00
|
|
|
|
Str_Concat (Query,TagText,Tst_MAX_BYTES_QUERY_TEST);
|
|
|
|
|
Str_Concat (Query,"'",Tst_MAX_BYTES_QUERY_TEST);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
NumItemInList++;
|
|
|
|
|
}
|
2021-02-15 16:25:55 +01:00
|
|
|
|
Str_Concat (Query,")",Tst_MAX_BYTES_QUERY_TEST);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add answer types selected */
|
2020-03-24 00:58:42 +01:00
|
|
|
|
if (!Test->AnswerTypes.All)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2020-04-01 03:11:05 +02:00
|
|
|
|
LengthQuery = strlen (Query);
|
|
|
|
|
NumItemInList = 0;
|
|
|
|
|
Ptr = Test->AnswerTypes.List;
|
|
|
|
|
while (*Ptr)
|
|
|
|
|
{
|
2020-05-17 17:11:04 +02:00
|
|
|
|
Par_GetNextStrUntilSeparParamMult (&Ptr,UnsignedStr,Tag_MAX_BYTES_TAG);
|
2020-04-30 20:15:21 +02:00
|
|
|
|
AnswerType = Tst_ConvertFromUnsignedStrToAnsTyp (UnsignedStr);
|
|
|
|
|
LengthQuery = LengthQuery + 35 + strlen (Tst_StrAnswerTypesDB[AnswerType]) + 1;
|
2020-04-01 03:11:05 +02:00
|
|
|
|
if (LengthQuery > Tst_MAX_BYTES_QUERY_TEST - 128)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_ShowErrorAndExit ("Query size exceed.");
|
2020-04-01 03:11:05 +02:00
|
|
|
|
Str_Concat (Query,
|
|
|
|
|
NumItemInList ? " OR tst_questions.AnsType='" :
|
|
|
|
|
" AND (tst_questions.AnsType='",
|
|
|
|
|
Tst_MAX_BYTES_QUERY_TEST);
|
2021-02-15 16:25:55 +01:00
|
|
|
|
Str_Concat (Query,Tst_StrAnswerTypesDB[AnswerType],Tst_MAX_BYTES_QUERY_TEST);
|
|
|
|
|
Str_Concat (Query,"'",Tst_MAX_BYTES_QUERY_TEST);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
NumItemInList++;
|
|
|
|
|
}
|
2021-02-15 16:25:55 +01:00
|
|
|
|
Str_Concat (Query,")",Tst_MAX_BYTES_QUERY_TEST);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* End query */
|
2021-02-15 16:25:55 +01:00
|
|
|
|
Str_Concat (Query," ORDER BY RAND() LIMIT ",Tst_MAX_BYTES_QUERY_TEST);
|
|
|
|
|
snprintf (StrNumQsts,sizeof (StrNumQsts),"%u",Test->NumQsts);
|
|
|
|
|
Str_Concat (Query,StrNumQsts,Tst_MAX_BYTES_QUERY_TEST);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/*
|
|
|
|
|
if (Gbl.Usrs.Me.Roles.LoggedRole == Rol_SYS_ADM)
|
|
|
|
|
Lay_ShowAlert (Lay_INFO,Query);
|
|
|
|
|
*/
|
|
|
|
|
/* Make the query */
|
2020-06-24 02:15:50 +02:00
|
|
|
|
Print->NumQsts.All =
|
|
|
|
|
Test->NumQsts = (unsigned) DB_QuerySELECT (&mysql_res,"can not get questions",
|
|
|
|
|
"%s",
|
|
|
|
|
Query);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
2020-04-02 03:28:08 +02:00
|
|
|
|
/***** Get questions and answers from database *****/
|
2021-04-26 17:29:04 +02:00
|
|
|
|
for (QstInd = 0;
|
|
|
|
|
QstInd < Print->NumQsts.All;
|
|
|
|
|
QstInd++)
|
2020-04-01 03:11:05 +02:00
|
|
|
|
{
|
|
|
|
|
/* Get question row */
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
|
|
|
|
/*
|
|
|
|
|
QstCod row[0]
|
|
|
|
|
AnsType row[1]
|
|
|
|
|
Shuffle row[2]
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* Get question code (row[0]) */
|
2021-04-29 19:52:35 +02:00
|
|
|
|
if ((Print->PrintedQuestions[QstInd].QstCod = Str_ConvertStrCodToLongCod (row[0])) <= 0)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_ShowErrorAndExit ("Wrong code of question.");
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
|
|
|
|
/* Get answer type (row[1]) */
|
2020-04-30 20:15:21 +02:00
|
|
|
|
AnswerType = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[1]);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
|
|
|
|
/* Get shuffle (row[2]) */
|
|
|
|
|
Shuffle = (row[2][0] == 'Y');
|
|
|
|
|
|
|
|
|
|
/* Set indexes of answers */
|
2020-04-30 20:15:21 +02:00
|
|
|
|
switch (AnswerType)
|
2020-04-01 03:11:05 +02:00
|
|
|
|
{
|
|
|
|
|
case Tst_ANS_INT:
|
|
|
|
|
case Tst_ANS_FLOAT:
|
|
|
|
|
case Tst_ANS_TRUE_FALSE:
|
|
|
|
|
case Tst_ANS_TEXT:
|
2021-04-26 17:29:04 +02:00
|
|
|
|
Print->PrintedQuestions[QstInd].StrIndexes[0] = '\0';
|
2020-04-01 03:11:05 +02:00
|
|
|
|
break;
|
|
|
|
|
case Tst_ANS_UNIQUE_CHOICE:
|
|
|
|
|
case Tst_ANS_MULTIPLE_CHOICE:
|
|
|
|
|
/* If answer type is unique or multiple option,
|
2020-04-02 03:28:08 +02:00
|
|
|
|
generate indexes of answers depending on shuffle */
|
2021-04-26 17:29:04 +02:00
|
|
|
|
Tst_GenerateChoiceIndexes (&Print->PrintedQuestions[QstInd],Shuffle);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Reset user's answers.
|
|
|
|
|
Initially user has not answered the question ==> initially all the answers will be blank.
|
|
|
|
|
If the user does not confirm the submission of their exam ==>
|
|
|
|
|
==> the exam may be half filled ==> the answers displayed will be those selected by the user. */
|
2021-04-26 17:29:04 +02:00
|
|
|
|
Print->PrintedQuestions[QstInd].StrAnswers[0] = '\0';
|
2020-04-01 03:11:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-02 03:28:08 +02:00
|
|
|
|
/***** Get if test exam will be visible by teachers *****/
|
2020-05-07 18:33:26 +02:00
|
|
|
|
Print->AllowTeachers = Par_GetParToBool ("AllowTchs");
|
2020-04-01 03:11:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2020-05-13 00:28:32 +02:00
|
|
|
|
/*************** Generate choice indexes depending on shuffle ****************/
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-05-13 00:28:32 +02:00
|
|
|
|
void Tst_GenerateChoiceIndexes (struct TstPrn_PrintedQuestion *PrintedQuestion,
|
|
|
|
|
bool Shuffle)
|
2020-04-01 03:11:05 +02:00
|
|
|
|
{
|
|
|
|
|
struct Tst_Question Question;
|
|
|
|
|
unsigned NumOpt;
|
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
MYSQL_ROW row;
|
|
|
|
|
unsigned Index;
|
|
|
|
|
bool ErrorInIndex;
|
2020-04-02 03:28:08 +02:00
|
|
|
|
char StrInd[1 + Cns_MAX_DECIMAL_DIGITS_UINT + 1];
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
|
|
|
|
/***** Create test question *****/
|
|
|
|
|
Tst_QstConstructor (&Question);
|
2020-05-07 18:33:26 +02:00
|
|
|
|
Question.QstCod = PrintedQuestion->QstCod;
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
|
|
|
|
/***** Get answers of question from database *****/
|
|
|
|
|
Tst_GetAnswersQst (&Question,&mysql_res,Shuffle);
|
|
|
|
|
/*
|
|
|
|
|
row[0] AnsInd
|
|
|
|
|
row[1] Answer
|
|
|
|
|
row[2] Feedback
|
|
|
|
|
row[3] MedCod
|
|
|
|
|
row[4] Correct
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
for (NumOpt = 0;
|
|
|
|
|
NumOpt < Question.Answer.NumOptions;
|
|
|
|
|
NumOpt++)
|
|
|
|
|
{
|
|
|
|
|
/***** Get next answer *****/
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
|
|
|
|
|
|
|
|
|
/***** Assign index (row[0]).
|
|
|
|
|
Index is 0,1,2,3... if no shuffle
|
|
|
|
|
or 1,3,0,2... (example) if shuffle *****/
|
|
|
|
|
ErrorInIndex = false;
|
|
|
|
|
if (sscanf (row[0],"%u",&Index) == 1)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2020-04-01 03:11:05 +02:00
|
|
|
|
if (Index >= Tst_MAX_OPTIONS_PER_QUESTION)
|
|
|
|
|
ErrorInIndex = true;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
2020-04-01 03:11:05 +02:00
|
|
|
|
else
|
|
|
|
|
ErrorInIndex = true;
|
|
|
|
|
if (ErrorInIndex)
|
2021-09-25 22:49:05 +02:00
|
|
|
|
Err_WrongAnswerIndexExit ();
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
2021-02-15 16:25:55 +01:00
|
|
|
|
snprintf (StrInd,sizeof (StrInd),NumOpt ? ",%u" :
|
|
|
|
|
"%u",Index);
|
2020-05-07 18:33:26 +02:00
|
|
|
|
Str_Concat (PrintedQuestion->StrIndexes,StrInd,
|
2021-02-15 16:25:55 +01:00
|
|
|
|
sizeof (PrintedQuestion->StrIndexes) - 1);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
|
|
|
|
|
/***** Destroy test question *****/
|
|
|
|
|
Tst_QstDestructor (&Question);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********************** List a test question for edition ********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-03-26 21:39:44 +01:00
|
|
|
|
static void Tst_ListOneQstToEdit (struct Tst_Test *Test)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2020-03-17 00:35:11 +01:00
|
|
|
|
extern const char *Hlp_ASSESSMENT_Tests;
|
|
|
|
|
extern const char *Txt_Questions;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-24 01:50:39 +01:00
|
|
|
|
/***** List only one question *****/
|
|
|
|
|
Test->NumQsts = 1;
|
|
|
|
|
|
2020-03-17 00:35:11 +01:00
|
|
|
|
/***** Begin box *****/
|
2020-03-26 02:54:30 +01:00
|
|
|
|
Box_BoxBegin (NULL,Txt_Questions,
|
2020-05-17 18:45:27 +02:00
|
|
|
|
Tst_PutIconsEditBankQsts,Test,
|
2020-03-17 00:35:11 +01:00
|
|
|
|
Hlp_ASSESSMENT_Tests,Box_NOT_CLOSABLE);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-17 00:35:11 +01:00
|
|
|
|
/***** Write the heading *****/
|
2020-05-17 22:16:39 +02:00
|
|
|
|
HTM_TABLE_BeginWideMarginPadding (5);
|
2020-03-24 01:50:39 +01:00
|
|
|
|
Tst_WriteHeadingRowQuestionsForEdition (Test);
|
2020-03-17 00:35:11 +01:00
|
|
|
|
|
|
|
|
|
/***** Write question row *****/
|
2020-03-30 18:54:17 +02:00
|
|
|
|
Tst_WriteQuestionListing (Test,0);
|
2020-03-17 00:35:11 +01:00
|
|
|
|
|
|
|
|
|
/***** End table *****/
|
|
|
|
|
HTM_TABLE_End ();
|
|
|
|
|
|
|
|
|
|
/***** Button to add a new question *****/
|
|
|
|
|
Tst_PutButtonToAddQuestion ();
|
|
|
|
|
|
|
|
|
|
/***** End box *****/
|
|
|
|
|
Box_BoxEnd ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/****************** List for edition one or more test questions **************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-03-26 21:39:44 +01:00
|
|
|
|
static void Tst_ListOneOrMoreQuestionsForEdition (struct Tst_Test *Test,
|
2017-09-01 00:52:19 +02:00
|
|
|
|
MYSQL_RES *mysql_res)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2016-11-13 20:18:49 +01:00
|
|
|
|
extern const char *Hlp_ASSESSMENT_Tests;
|
2015-04-12 18:01:06 +02:00
|
|
|
|
extern const char *Txt_Questions;
|
2021-04-26 17:29:04 +02:00
|
|
|
|
unsigned QstInd;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
MYSQL_ROW row;
|
|
|
|
|
|
2019-10-26 02:19:42 +02:00
|
|
|
|
/***** Begin box *****/
|
2020-03-26 02:54:30 +01:00
|
|
|
|
Box_BoxBegin (NULL,Txt_Questions,
|
2020-05-17 18:45:27 +02:00
|
|
|
|
Tst_PutIconsEditBankQsts,Test,
|
2017-07-16 20:50:01 +02:00
|
|
|
|
Hlp_ASSESSMENT_Tests,Box_NOT_CLOSABLE);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/***** Write the heading *****/
|
2020-05-17 22:16:39 +02:00
|
|
|
|
HTM_TABLE_BeginWideMarginPadding (5);
|
2020-03-24 01:50:39 +01:00
|
|
|
|
Tst_WriteHeadingRowQuestionsForEdition (Test);
|
2020-03-17 00:35:11 +01:00
|
|
|
|
|
|
|
|
|
/***** Write rows *****/
|
2021-04-26 17:29:04 +02:00
|
|
|
|
for (QstInd = 0;
|
|
|
|
|
QstInd < Test->NumQsts;
|
|
|
|
|
QstInd++)
|
2020-03-17 00:35:11 +01:00
|
|
|
|
{
|
2021-04-26 17:29:04 +02:00
|
|
|
|
Gbl.RowEvenOdd = QstInd % 2;
|
2020-03-17 00:35:11 +01:00
|
|
|
|
|
2020-03-25 01:36:22 +01:00
|
|
|
|
/***** Create test question *****/
|
2020-03-26 21:39:44 +01:00
|
|
|
|
Tst_QstConstructor (&Test->Question);
|
2020-03-25 01:36:22 +01:00
|
|
|
|
|
2020-03-17 00:35:11 +01:00
|
|
|
|
/***** Get question code (row[0]) *****/
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
2021-04-29 19:52:35 +02:00
|
|
|
|
if ((Test->Question.QstCod = Str_ConvertStrCodToLongCod (row[0])) <= 0)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_WrongQuestionExit ();
|
2020-03-17 00:35:11 +01:00
|
|
|
|
|
|
|
|
|
/***** Write question row *****/
|
2021-04-26 17:29:04 +02:00
|
|
|
|
Tst_WriteQuestionListing (Test,QstInd);
|
2020-03-25 01:36:22 +01:00
|
|
|
|
|
|
|
|
|
/***** Destroy test question *****/
|
2020-03-26 21:39:44 +01:00
|
|
|
|
Tst_QstDestructor (&Test->Question);
|
2020-03-17 00:35:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** End table *****/
|
|
|
|
|
HTM_TABLE_End ();
|
|
|
|
|
|
|
|
|
|
/***** Button to add a new question *****/
|
|
|
|
|
Tst_PutButtonToAddQuestion ();
|
|
|
|
|
|
|
|
|
|
/***** End box *****/
|
|
|
|
|
Box_BoxEnd ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********** Write heading row in listing of questions for edition ***********/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-10-14 00:59:24 +02:00
|
|
|
|
static void Tst_WriteHeadingRowQuestionsForEdition (struct Tst_Test *Test)
|
2020-03-17 00:35:11 +01:00
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_No_INDEX;
|
|
|
|
|
extern const char *Txt_Code;
|
|
|
|
|
extern const char *Txt_Date;
|
|
|
|
|
extern const char *Txt_Tags;
|
|
|
|
|
extern const char *Txt_Shuffle;
|
|
|
|
|
extern const char *Txt_TST_STR_ORDER_FULL[Tst_NUM_TYPES_ORDER_QST];
|
|
|
|
|
extern const char *Txt_TST_STR_ORDER_SHORT[Tst_NUM_TYPES_ORDER_QST];
|
|
|
|
|
Tst_QuestionsOrder_t Order;
|
|
|
|
|
|
|
|
|
|
/***** Begin row *****/
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TR_Begin (NULL);
|
2019-10-12 00:07:52 +02:00
|
|
|
|
|
2020-03-17 00:35:11 +01:00
|
|
|
|
/***** First columns *****/
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TH_Empty (1);
|
|
|
|
|
HTM_TH (1,1,"CT",Txt_No_INDEX);
|
|
|
|
|
HTM_TH (1,1,"CT",Txt_Code);
|
|
|
|
|
HTM_TH (1,1,"CT",Txt_Date);
|
|
|
|
|
HTM_TH (1,1,"CT",Txt_Tags);
|
|
|
|
|
HTM_TH (1,1,"CT",Txt_Shuffle);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-17 00:35:11 +01:00
|
|
|
|
/***** Columns which data can be ordered *****/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/* Stem and answers of question */
|
|
|
|
|
/* Number of times that the question has been answered */
|
|
|
|
|
/* Average score */
|
2019-12-15 20:02:34 +01:00
|
|
|
|
for (Order = (Tst_QuestionsOrder_t) 0;
|
|
|
|
|
Order <= (Tst_QuestionsOrder_t) (Tst_NUM_TYPES_ORDER_QST - 1);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
Order++)
|
|
|
|
|
{
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TH_Begin (1,1,"LT");
|
2019-10-13 17:25:00 +02:00
|
|
|
|
|
2020-03-24 01:50:39 +01:00
|
|
|
|
if (Test->NumQsts > 1)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2021-03-02 00:54:26 +01:00
|
|
|
|
Frm_BeginForm (ActLstTstQst);
|
2020-10-14 00:59:24 +02:00
|
|
|
|
Tst_PutParamsEditQst (Test);
|
2019-11-03 13:19:32 +01:00
|
|
|
|
Par_PutHiddenParamUnsigned (NULL,"Order",(unsigned) Order);
|
2019-11-20 10:17:42 +01:00
|
|
|
|
HTM_BUTTON_SUBMIT_Begin (Txt_TST_STR_ORDER_FULL[Order],"BT_LINK TIT_TBL",NULL);
|
2020-03-24 00:58:42 +01:00
|
|
|
|
if (Order == Test->SelectedOrder)
|
2019-11-10 16:41:47 +01:00
|
|
|
|
HTM_U_Begin ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
2019-11-10 12:36:37 +01:00
|
|
|
|
HTM_Txt (Txt_TST_STR_ORDER_SHORT[Order]);
|
2020-03-24 01:50:39 +01:00
|
|
|
|
if (Test->NumQsts > 1)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2020-03-24 00:58:42 +01:00
|
|
|
|
if (Order == Test->SelectedOrder)
|
2019-11-10 16:41:47 +01:00
|
|
|
|
HTM_U_End ();
|
2019-11-18 20:12:10 +01:00
|
|
|
|
HTM_BUTTON_End ();
|
2018-11-09 20:47:39 +01:00
|
|
|
|
Frm_EndForm ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
2019-10-13 17:25:00 +02:00
|
|
|
|
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TH_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-17 00:35:11 +01:00
|
|
|
|
/***** End row *****/
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TR_End ();
|
2020-03-17 00:35:11 +01:00
|
|
|
|
}
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-17 00:35:11 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********** Write question row in listing of questions for edition ***********/
|
|
|
|
|
/*****************************************************************************/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2021-04-26 17:29:04 +02:00
|
|
|
|
static void Tst_WriteQuestionListing (struct Tst_Test *Test,unsigned QstInd)
|
2020-03-17 00:35:11 +01:00
|
|
|
|
{
|
|
|
|
|
static unsigned UniqueId = 0;
|
|
|
|
|
char *Id;
|
|
|
|
|
|
2020-03-25 01:36:22 +01:00
|
|
|
|
/***** Get and show question data *****/
|
2020-04-04 19:20:50 +02:00
|
|
|
|
if (Tst_GetQstDataFromDB (&Test->Question))
|
2020-03-17 00:35:11 +01:00
|
|
|
|
{
|
|
|
|
|
/***** Begin table row *****/
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TR_Begin (NULL);
|
2019-10-10 10:41:00 +02:00
|
|
|
|
|
|
|
|
|
/***** Icons *****/
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_Begin ("class=\"BT%u\"",Gbl.RowEvenOdd);
|
2017-07-16 20:50:01 +02:00
|
|
|
|
|
|
|
|
|
/* Write icon to remove the question */
|
2020-10-14 00:59:24 +02:00
|
|
|
|
Ico_PutContextualIconToRemove (ActReqRemOneTstQst,NULL,
|
|
|
|
|
Tst_PutParamsEditQst,Test);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/* Write icon to edit the question */
|
2020-03-26 02:54:30 +01:00
|
|
|
|
Ico_PutContextualIconToEdit (ActEdiOneTstQst,NULL,
|
2020-03-27 14:56:54 +01:00
|
|
|
|
Tst_PutParamQstCod,&Test->Question.QstCod);
|
2017-07-16 20:50:01 +02:00
|
|
|
|
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-04 02:07:54 +02:00
|
|
|
|
/* Number of question and answer type */
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_Begin ("class=\"RT COLOR%u\"",Gbl.RowEvenOdd);
|
2021-04-26 17:29:04 +02:00
|
|
|
|
Tst_WriteNumQst (QstInd + 1,"BIG_INDEX");
|
2020-06-17 20:20:16 +02:00
|
|
|
|
Tst_WriteAnswerType (Test->Question.Answer.Type,"DAT_SMALL");
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-23 17:14:41 +01:00
|
|
|
|
/* Question code */
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd);
|
2020-03-26 21:39:44 +01:00
|
|
|
|
HTM_TxtF ("%ld ",Test->Question.QstCod);
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-23 17:14:41 +01:00
|
|
|
|
/* Date (row[0] has the UTC date-time) */
|
2020-03-17 00:35:11 +01:00
|
|
|
|
if (asprintf (&Id,"tst_date_%u",++UniqueId) < 0)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_NotEnoughMemoryExit ();
|
2019-11-01 22:53:39 +01:00
|
|
|
|
HTM_TD_Begin ("id=\"%s\" class=\"DAT_SMALL CT COLOR%u\"",
|
2020-03-17 00:35:11 +01:00
|
|
|
|
Id,Gbl.RowEvenOdd);
|
2020-04-04 02:07:54 +02:00
|
|
|
|
Dat_WriteLocalDateHMSFromUTC (Id,Test->Question.EditTime,
|
2019-11-02 12:10:58 +01:00
|
|
|
|
Gbl.Prefs.DateFormat,Dat_SEPARATOR_BREAK,
|
2019-11-02 11:45:41 +01:00
|
|
|
|
true,true,false,0x7);
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_End ();
|
2019-11-06 19:45:20 +01:00
|
|
|
|
free (Id);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-23 17:14:41 +01:00
|
|
|
|
/* Question tags */
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
|
2020-03-26 21:39:44 +01:00
|
|
|
|
Tst_GetAndWriteTagsQst (Test->Question.QstCod);
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-23 17:14:41 +01:00
|
|
|
|
/* Shuffle (row[2]) */
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd);
|
2020-03-26 21:39:44 +01:00
|
|
|
|
if (Test->Question.Answer.Type == Tst_ANS_UNIQUE_CHOICE ||
|
|
|
|
|
Test->Question.Answer.Type == Tst_ANS_MULTIPLE_CHOICE)
|
2020-03-17 00:35:11 +01:00
|
|
|
|
{
|
2021-03-02 00:54:26 +01:00
|
|
|
|
Frm_BeginForm (ActChgShfTstQst);
|
2020-10-14 00:59:24 +02:00
|
|
|
|
Tst_PutParamsEditQst (Test);
|
2020-03-24 00:58:42 +01:00
|
|
|
|
Par_PutHiddenParamUnsigned (NULL,"Order",(unsigned) Test->SelectedOrder);
|
2020-03-17 00:35:11 +01:00
|
|
|
|
HTM_INPUT_CHECKBOX ("Shuffle",HTM_SUBMIT_ON_CHANGE,
|
|
|
|
|
"value=\"Y\"%s",
|
2020-04-04 02:07:54 +02:00
|
|
|
|
Test->Question.Answer.Shuffle ? " checked=\"checked\"" :
|
|
|
|
|
"");
|
2020-03-17 00:35:11 +01:00
|
|
|
|
Frm_EndForm ();
|
|
|
|
|
}
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-23 17:14:41 +01:00
|
|
|
|
/* Stem (row[3]) */
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
|
2020-06-17 20:20:16 +02:00
|
|
|
|
Tst_WriteQstStem (Test->Question.Stem,"TEST_TXT",
|
2020-03-17 00:35:11 +01:00
|
|
|
|
true); // Visible
|
2019-03-18 15:42:22 +01:00
|
|
|
|
|
2020-03-17 13:10:44 +01:00
|
|
|
|
/***** Get and show media (row[5]) *****/
|
2020-03-26 21:39:44 +01:00
|
|
|
|
Med_ShowMedia (&Test->Question.Media,
|
2020-03-17 00:35:11 +01:00
|
|
|
|
"TEST_MED_EDIT_LIST_CONT",
|
|
|
|
|
"TEST_MED_EDIT_LIST");
|
2019-03-18 15:42:22 +01:00
|
|
|
|
|
2020-03-23 17:14:41 +01:00
|
|
|
|
/* Feedback (row[4]) and answers */
|
2020-06-17 20:20:16 +02:00
|
|
|
|
Tst_WriteQstFeedback (Test->Question.Feedback,"TEST_TXT_LIGHT");
|
|
|
|
|
Tst_WriteAnswersBank (&Test->Question,"TEST_TXT","TEST_TXT_LIGHT");
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-23 17:14:41 +01:00
|
|
|
|
/* Number of times this question has been answered */
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd);
|
2020-04-04 02:07:54 +02:00
|
|
|
|
HTM_UnsignedLong (Test->Question.NumHits);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_TD_End ();
|
2019-03-18 15:42:22 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/* Average score */
|
|
|
|
|
HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd);
|
2020-04-04 02:07:54 +02:00
|
|
|
|
if (Test->Question.NumHits)
|
2020-04-04 19:20:50 +02:00
|
|
|
|
HTM_Double2Decimals (Test->Question.Score /
|
|
|
|
|
(double) Test->Question.NumHits);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
else
|
|
|
|
|
HTM_Txt ("N.A.");
|
|
|
|
|
HTM_TD_End ();
|
2019-03-18 15:42:22 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/* Number of times this question has been answered (not blank) */
|
|
|
|
|
HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd);
|
2020-04-04 02:07:54 +02:00
|
|
|
|
HTM_UnsignedLong (Test->Question.NumHitsNotBlank);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
/* Average score (not blank) */
|
|
|
|
|
HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd);
|
2020-04-04 02:07:54 +02:00
|
|
|
|
if (Test->Question.NumHitsNotBlank)
|
2020-04-04 19:20:50 +02:00
|
|
|
|
HTM_Double2Decimals (Test->Question.Score /
|
|
|
|
|
(double) Test->Question.NumHitsNotBlank);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
else
|
|
|
|
|
HTM_Txt ("N.A.");
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_End ();
|
2020-03-17 00:35:11 +01:00
|
|
|
|
|
|
|
|
|
/***** End table row *****/
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TR_End ();
|
2017-07-16 20:50:01 +02:00
|
|
|
|
}
|
2020-03-22 19:34:53 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/*************** List for selection one or more test questions ***************/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-27 14:20:21 +02:00
|
|
|
|
static void Tst_ListOneOrMoreQuestionsForSelectionForSet (struct Exa_Exams *Exams,
|
|
|
|
|
unsigned NumQsts,
|
|
|
|
|
MYSQL_RES *mysql_res)
|
2020-04-22 03:15:04 +02:00
|
|
|
|
{
|
|
|
|
|
extern const char *Hlp_ASSESSMENT_Exams_questions;
|
|
|
|
|
extern const char *Txt_Questions;
|
|
|
|
|
extern const char *Txt_No_INDEX;
|
|
|
|
|
extern const char *Txt_Code;
|
|
|
|
|
extern const char *Txt_Date;
|
|
|
|
|
extern const char *Txt_Tags;
|
|
|
|
|
extern const char *Txt_Type;
|
|
|
|
|
extern const char *Txt_Shuffle;
|
|
|
|
|
extern const char *Txt_Question;
|
|
|
|
|
extern const char *Txt_Add_questions;
|
2021-04-26 17:29:04 +02:00
|
|
|
|
unsigned QstInd;
|
2020-04-22 03:15:04 +02:00
|
|
|
|
struct Tst_Question Question;
|
|
|
|
|
MYSQL_ROW row;
|
|
|
|
|
|
|
|
|
|
/***** Begin box *****/
|
|
|
|
|
Box_BoxBegin (NULL,Txt_Questions,
|
|
|
|
|
NULL,NULL,
|
|
|
|
|
Hlp_ASSESSMENT_Exams_questions,Box_NOT_CLOSABLE);
|
|
|
|
|
|
|
|
|
|
/***** Begin form *****/
|
2021-03-02 00:54:26 +01:00
|
|
|
|
Frm_BeginForm (ActAddQstToExa);
|
2020-04-27 14:20:21 +02:00
|
|
|
|
ExaSet_PutParamsOneSet (Exams);
|
2020-04-22 03:15:04 +02:00
|
|
|
|
|
2020-05-30 20:03:00 +02:00
|
|
|
|
/***** Select all questions *****/
|
2020-05-30 20:25:19 +02:00
|
|
|
|
Tst_PutCheckboxToSelectAllQuestions ();
|
2020-05-30 20:03:00 +02:00
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
/***** Write the heading *****/
|
2020-05-17 22:16:39 +02:00
|
|
|
|
HTM_TABLE_BeginWideMarginPadding (5);
|
2020-04-22 03:15:04 +02:00
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
|
|
|
|
|
|
HTM_TH_Empty (1);
|
|
|
|
|
|
|
|
|
|
HTM_TH (1,1,"CT",Txt_No_INDEX);
|
|
|
|
|
HTM_TH (1,1,"CT",Txt_Code);
|
|
|
|
|
HTM_TH (1,1,"CT",Txt_Date);
|
|
|
|
|
HTM_TH (1,1,"LT",Txt_Tags);
|
|
|
|
|
HTM_TH (1,1,"CT",Txt_Type);
|
|
|
|
|
HTM_TH (1,1,"CT",Txt_Shuffle);
|
|
|
|
|
HTM_TH (1,1,"CT",Txt_Question);
|
|
|
|
|
|
|
|
|
|
HTM_TR_End ();
|
|
|
|
|
|
|
|
|
|
/***** Write rows *****/
|
2021-04-26 17:29:04 +02:00
|
|
|
|
for (QstInd = 0;
|
|
|
|
|
QstInd < NumQsts;
|
|
|
|
|
QstInd++)
|
2020-04-22 03:15:04 +02:00
|
|
|
|
{
|
2021-04-26 17:29:04 +02:00
|
|
|
|
Gbl.RowEvenOdd = QstInd % 2;
|
2020-04-22 03:15:04 +02:00
|
|
|
|
|
|
|
|
|
/* Create test question */
|
|
|
|
|
Tst_QstConstructor (&Question);
|
|
|
|
|
|
|
|
|
|
/* Get question code (row[0]) */
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
2021-04-29 19:52:35 +02:00
|
|
|
|
if ((Question.QstCod = Str_ConvertStrCodToLongCod (row[0])) <= 0)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_WrongQuestionExit ();
|
2020-04-22 03:15:04 +02:00
|
|
|
|
|
|
|
|
|
/* Write question row */
|
2021-04-26 17:29:04 +02:00
|
|
|
|
Tst_WriteQuestionRowForSelection (QstInd,&Question);
|
2020-04-22 03:15:04 +02:00
|
|
|
|
|
|
|
|
|
/* Destroy test question */
|
|
|
|
|
Tst_QstDestructor (&Question);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** End table *****/
|
|
|
|
|
HTM_TABLE_End ();
|
|
|
|
|
|
|
|
|
|
/***** Button to add questions *****/
|
2020-05-11 13:23:42 +02:00
|
|
|
|
Btn_PutCreateButton (Txt_Add_questions);
|
2020-04-22 03:15:04 +02:00
|
|
|
|
|
|
|
|
|
/***** End form *****/
|
|
|
|
|
Frm_EndForm ();
|
|
|
|
|
|
|
|
|
|
/***** End box *****/
|
|
|
|
|
Box_BoxEnd ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*************** List for selection one or more test questions ***************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void Tst_ListOneOrMoreQuestionsForSelectionForGame (struct Gam_Games *Games,
|
|
|
|
|
unsigned NumQsts,
|
|
|
|
|
MYSQL_RES *mysql_res)
|
2019-09-23 19:17:12 +02:00
|
|
|
|
{
|
2020-04-01 03:11:05 +02:00
|
|
|
|
extern const char *Hlp_ASSESSMENT_Games_questions;
|
|
|
|
|
extern const char *Txt_Questions;
|
|
|
|
|
extern const char *Txt_No_INDEX;
|
|
|
|
|
extern const char *Txt_Code;
|
|
|
|
|
extern const char *Txt_Date;
|
|
|
|
|
extern const char *Txt_Tags;
|
|
|
|
|
extern const char *Txt_Type;
|
|
|
|
|
extern const char *Txt_Shuffle;
|
|
|
|
|
extern const char *Txt_Question;
|
|
|
|
|
extern const char *Txt_Add_questions;
|
2021-04-26 17:29:04 +02:00
|
|
|
|
unsigned QstInd;
|
2020-04-01 03:11:05 +02:00
|
|
|
|
struct Tst_Question Question;
|
2019-09-23 19:17:12 +02:00
|
|
|
|
MYSQL_ROW row;
|
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Begin box *****/
|
|
|
|
|
Box_BoxBegin (NULL,Txt_Questions,
|
|
|
|
|
NULL,NULL,
|
|
|
|
|
Hlp_ASSESSMENT_Games_questions,Box_NOT_CLOSABLE);
|
2019-09-23 19:17:12 +02:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Begin form *****/
|
2021-03-02 00:54:26 +01:00
|
|
|
|
Frm_BeginForm (ActAddTstQstToGam);
|
2020-04-08 03:06:45 +02:00
|
|
|
|
Gam_PutParams (Games);
|
2019-09-23 19:17:12 +02:00
|
|
|
|
|
2020-05-30 20:16:34 +02:00
|
|
|
|
/***** Select all questions *****/
|
2020-05-30 20:25:19 +02:00
|
|
|
|
Tst_PutCheckboxToSelectAllQuestions ();
|
2020-05-30 20:16:34 +02:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Write the heading *****/
|
2020-05-17 22:16:39 +02:00
|
|
|
|
HTM_TABLE_BeginWideMarginPadding (5);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_TR_Begin (NULL);
|
2019-09-23 19:17:12 +02:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_TH_Empty (1);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_TH (1,1,"CT",Txt_No_INDEX);
|
|
|
|
|
HTM_TH (1,1,"CT",Txt_Code);
|
|
|
|
|
HTM_TH (1,1,"CT",Txt_Date);
|
|
|
|
|
HTM_TH (1,1,"LT",Txt_Tags);
|
|
|
|
|
HTM_TH (1,1,"CT",Txt_Type);
|
|
|
|
|
HTM_TH (1,1,"CT",Txt_Shuffle);
|
|
|
|
|
HTM_TH (1,1,"CT",Txt_Question);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_TR_End ();
|
2020-03-18 01:57:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Write rows *****/
|
2021-04-26 17:29:04 +02:00
|
|
|
|
for (QstInd = 0;
|
|
|
|
|
QstInd < NumQsts;
|
|
|
|
|
QstInd++)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2021-04-26 17:29:04 +02:00
|
|
|
|
Gbl.RowEvenOdd = QstInd % 2;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/* Create test question */
|
|
|
|
|
Tst_QstConstructor (&Question);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/* Get question code (row[0]) */
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
2021-04-29 19:52:35 +02:00
|
|
|
|
if ((Question.QstCod = Str_ConvertStrCodToLongCod (row[0])) <= 0)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_WrongQuestionExit ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/* Write question row */
|
2021-04-26 17:29:04 +02:00
|
|
|
|
Tst_WriteQuestionRowForSelection (QstInd,&Question);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/* Destroy test question */
|
|
|
|
|
Tst_QstDestructor (&Question);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
2020-03-18 01:57:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** End table *****/
|
|
|
|
|
HTM_TABLE_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Button to add questions *****/
|
2020-05-11 13:23:42 +02:00
|
|
|
|
Btn_PutCreateButton (Txt_Add_questions);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** End form *****/
|
|
|
|
|
Frm_EndForm ();
|
2019-05-22 09:36:18 +02:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** End box *****/
|
|
|
|
|
Box_BoxEnd ();
|
2017-09-04 17:03:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-30 20:25:19 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************** Select all questions to add them to set/game *****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void Tst_PutCheckboxToSelectAllQuestions (void)
|
|
|
|
|
{
|
|
|
|
|
extern const char *The_ClassFormInBox[The_NUM_THEMES];
|
|
|
|
|
extern const char *Txt_All_questions;
|
|
|
|
|
|
|
|
|
|
/***** Checkbox to select all listed questions *****/
|
|
|
|
|
HTM_LABEL_Begin ("class=\"%s\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
|
|
|
|
|
HTM_INPUT_CHECKBOX ("AllQsts",HTM_DONT_SUBMIT_ON_CHANGE,
|
|
|
|
|
"value=\"Y\" onclick=\"togglecheckChildren(this,'QstCods');\"");
|
|
|
|
|
HTM_TxtF (" %s",Txt_All_questions);
|
|
|
|
|
HTM_LABEL_End ();
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/********************** Write question row for selection *********************/
|
2020-03-30 18:54:17 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2021-04-26 17:29:04 +02:00
|
|
|
|
static void Tst_WriteQuestionRowForSelection (unsigned QstInd,
|
2020-04-01 03:11:05 +02:00
|
|
|
|
struct Tst_Question *Question)
|
2020-03-30 18:54:17 +02:00
|
|
|
|
{
|
2020-04-01 03:11:05 +02:00
|
|
|
|
extern const char *Txt_TST_STR_ANSWER_TYPES[Tst_NUM_ANS_TYPES];
|
|
|
|
|
static unsigned UniqueId = 0;
|
|
|
|
|
char *Id;
|
2020-03-30 18:54:17 +02:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Get and show questvoidion data *****/
|
2020-04-04 19:20:50 +02:00
|
|
|
|
if (Tst_GetQstDataFromDB (Question))
|
2020-04-01 03:11:05 +02:00
|
|
|
|
{
|
|
|
|
|
/***** Begin table row *****/
|
|
|
|
|
HTM_TR_Begin (NULL);
|
2020-03-30 18:54:17 +02:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Icons *****/
|
|
|
|
|
HTM_TD_Begin ("class=\"BT%u\"",Gbl.RowEvenOdd);
|
2020-03-30 18:54:17 +02:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/* Write checkbox to select the question */
|
|
|
|
|
HTM_INPUT_CHECKBOX ("QstCods",HTM_DONT_SUBMIT_ON_CHANGE,
|
2020-05-30 20:03:00 +02:00
|
|
|
|
"value=\"%ld\" onclick=\"checkParent(this,'AllQsts');\"",
|
2020-04-01 03:11:05 +02:00
|
|
|
|
Question->QstCod);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/* Write number of question */
|
|
|
|
|
HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd);
|
2021-04-26 17:29:04 +02:00
|
|
|
|
HTM_TxtF ("%u ",QstInd + 1);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_TD_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/* Write question code */
|
|
|
|
|
HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd);
|
|
|
|
|
HTM_TxtF ("%ld ",Question->QstCod);
|
|
|
|
|
HTM_TD_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/* Write the date (row[0] has the UTC date-time) */
|
|
|
|
|
if (asprintf (&Id,"tst_date_%u",++UniqueId) < 0)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_NotEnoughMemoryExit ();
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_TD_Begin ("id=\"%s\" class=\"DAT_SMALL CT COLOR%u\">",
|
|
|
|
|
Id,Gbl.RowEvenOdd);
|
2020-04-04 02:07:54 +02:00
|
|
|
|
Dat_WriteLocalDateHMSFromUTC (Id,Question->EditTime,
|
2020-04-01 03:11:05 +02:00
|
|
|
|
Gbl.Prefs.DateFormat,Dat_SEPARATOR_BREAK,
|
|
|
|
|
true,true,false,0x7);
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
free (Id);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/* Write the question tags */
|
|
|
|
|
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
|
|
|
|
|
Tst_GetAndWriteTagsQst (Question->QstCod);
|
|
|
|
|
HTM_TD_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-04 02:07:54 +02:00
|
|
|
|
/* Write the question type */
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd);
|
|
|
|
|
HTM_TxtF ("%s ",Txt_TST_STR_ANSWER_TYPES[Question->Answer.Type]);
|
|
|
|
|
HTM_TD_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-04 02:07:54 +02:00
|
|
|
|
/* Write if shuffle is enabled */
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd);
|
|
|
|
|
HTM_INPUT_CHECKBOX ("Shuffle",HTM_DONT_SUBMIT_ON_CHANGE,
|
|
|
|
|
"value=\"Y\"%s disabled=\"disabled\"",
|
2020-04-04 02:07:54 +02:00
|
|
|
|
Question->Answer.Shuffle ? " checked=\"checked\"" :
|
|
|
|
|
"");
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_TD_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-04 02:07:54 +02:00
|
|
|
|
/* Write stem */
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
|
2020-06-17 20:20:16 +02:00
|
|
|
|
Tst_WriteQstStem (Question->Stem,"TEST_TXT",
|
2020-04-01 03:11:05 +02:00
|
|
|
|
true); // Visible
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-04 02:07:54 +02:00
|
|
|
|
/***** Get and show media *****/
|
2020-04-01 03:11:05 +02:00
|
|
|
|
Med_ShowMedia (&Question->Media,
|
|
|
|
|
"TEST_MED_EDIT_LIST_CONT",
|
|
|
|
|
"TEST_MED_EDIT_LIST");
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-04 02:07:54 +02:00
|
|
|
|
/* Write feedback */
|
2020-06-17 20:20:16 +02:00
|
|
|
|
Tst_WriteQstFeedback (Question->Feedback,"TEST_TXT_LIGHT");
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/* Write answers */
|
2020-06-17 20:20:16 +02:00
|
|
|
|
Tst_WriteAnswersBank (Question,"TEST_TXT","TEST_TXT_LIGHT");
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_TD_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** End table row *****/
|
|
|
|
|
HTM_TR_End ();
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-10-10 10:41:00 +02:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/*****************************************************************************/
|
2020-10-14 00:59:24 +02:00
|
|
|
|
/************ Put hidden parameters for edition of test questions ************/
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/*****************************************************************************/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-10-14 00:59:24 +02:00
|
|
|
|
void Tst_PutParamsEditQst (void *Test)
|
2020-04-01 03:11:05 +02:00
|
|
|
|
{
|
2020-10-14 00:59:24 +02:00
|
|
|
|
if (Test)
|
|
|
|
|
{
|
|
|
|
|
Par_PutHiddenParamChar ("AllTags",((struct Tst_Test *) Test)->Tags.All ? 'Y' :
|
|
|
|
|
'N');
|
|
|
|
|
Par_PutHiddenParamString (NULL,"ChkTag",((struct Tst_Test *) Test)->Tags.List ? ((struct Tst_Test *) Test)->Tags.List :
|
|
|
|
|
"");
|
|
|
|
|
Par_PutHiddenParamChar ("AllAnsTypes",((struct Tst_Test *) Test)->AnswerTypes.All ? 'Y' :
|
|
|
|
|
'N');
|
|
|
|
|
Par_PutHiddenParamString (NULL,"AnswerType",((struct Tst_Test *) Test)->AnswerTypes.List);
|
|
|
|
|
|
|
|
|
|
Tst_PutParamQstCod (&((struct Tst_Test *) Test)->Question.QstCod);
|
|
|
|
|
// if (Test->NumQsts == 1)
|
|
|
|
|
// Par_PutHiddenParamChar ("OnlyThisQst",'Y'); // If there are only one row, don't list again after removing
|
|
|
|
|
Dat_WriteParamsIniEndDates ();
|
|
|
|
|
}
|
2020-04-01 03:11:05 +02:00
|
|
|
|
}
|
2019-10-10 10:41:00 +02:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*************** Get answers of a test question from database ****************/
|
|
|
|
|
/*****************************************************************************/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
unsigned Tst_GetNumAnswersQst (long QstCod)
|
|
|
|
|
{
|
2021-04-17 01:57:19 +02:00
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of answers of a question",
|
|
|
|
|
"SELECT COUNT(*)"
|
|
|
|
|
" FROM tst_answers"
|
|
|
|
|
" WHERE QstCod=%ld",
|
|
|
|
|
QstCod);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
}
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
void Tst_GetAnswersQst (struct Tst_Question *Question,MYSQL_RES **mysql_res,
|
|
|
|
|
bool Shuffle)
|
|
|
|
|
{
|
|
|
|
|
/***** Get answers of a question from database *****/
|
|
|
|
|
Question->Answer.NumOptions = (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get answers of a question",
|
|
|
|
|
"SELECT AnsInd," // row[0]
|
|
|
|
|
"Answer," // row[1]
|
|
|
|
|
"Feedback," // row[2]
|
|
|
|
|
"MedCod," // row[3]
|
|
|
|
|
"Correct" // row[4]
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM tst_answers"
|
2020-04-01 03:11:05 +02:00
|
|
|
|
" WHERE QstCod=%ld"
|
|
|
|
|
" ORDER BY %s",
|
|
|
|
|
Question->QstCod,
|
2020-05-09 01:37:00 +02:00
|
|
|
|
Shuffle ? "RAND()" :
|
2020-04-01 03:11:05 +02:00
|
|
|
|
"AnsInd");
|
|
|
|
|
if (!Question->Answer.NumOptions)
|
2021-09-25 11:43:52 +02:00
|
|
|
|
Err_WrongAnswerExit ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2020-06-17 02:31:42 +02:00
|
|
|
|
/***************** Change format of answers text / feedback ******************/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-06-17 02:31:42 +02:00
|
|
|
|
void Tst_ChangeFormatAnswersText (struct Tst_Question *Question)
|
2020-03-30 18:54:17 +02:00
|
|
|
|
{
|
2020-06-17 02:31:42 +02:00
|
|
|
|
unsigned NumOpt;
|
2020-05-22 20:10:45 +02:00
|
|
|
|
|
2020-06-17 02:31:42 +02:00
|
|
|
|
/***** Change format of answers text *****/
|
|
|
|
|
for (NumOpt = 0;
|
|
|
|
|
NumOpt < Question->Answer.NumOptions;
|
|
|
|
|
NumOpt++)
|
|
|
|
|
/* Convert answer text, that is in HTML, to rigorous HTML */
|
|
|
|
|
if (Question->Answer.Options[NumOpt].Text[0])
|
|
|
|
|
Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
|
|
|
|
|
Question->Answer.Options[NumOpt].Text,
|
|
|
|
|
Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Tst_ChangeFormatAnswersFeedback (struct Tst_Question *Question)
|
|
|
|
|
{
|
|
|
|
|
unsigned NumOpt;
|
|
|
|
|
|
|
|
|
|
/***** Change format of answers text and feedback *****/
|
|
|
|
|
for (NumOpt = 0;
|
|
|
|
|
NumOpt < Question->Answer.NumOptions;
|
|
|
|
|
NumOpt++)
|
|
|
|
|
/* Convert answer feedback, that is in HTML, to rigorous HTML */
|
|
|
|
|
if (Question->Answer.Options[NumOpt].Feedback)
|
|
|
|
|
if (Question->Answer.Options[NumOpt].Feedback[0])
|
|
|
|
|
Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
|
|
|
|
|
Question->Answer.Options[NumOpt].Feedback,
|
|
|
|
|
Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
}
|
2020-03-30 18:54:17 +02:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/*****************************************************************************/
|
2020-06-17 02:31:42 +02:00
|
|
|
|
/**************** Get and write the answers of a test question ***************/
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/*****************************************************************************/
|
2020-03-30 18:54:17 +02:00
|
|
|
|
|
2020-06-17 20:20:16 +02:00
|
|
|
|
void Tst_WriteAnswersBank (struct Tst_Question *Question,
|
|
|
|
|
const char *ClassTxt,
|
|
|
|
|
const char *ClassFeedback)
|
2020-04-01 03:11:05 +02:00
|
|
|
|
{
|
2020-06-17 20:20:16 +02:00
|
|
|
|
void (*Tst_WriteAnsBank[Tst_NUM_ANS_TYPES]) (struct Tst_Question *Question,
|
|
|
|
|
const char *ClassTxt,
|
|
|
|
|
const char *ClassFeedback) =
|
2020-05-22 20:10:45 +02:00
|
|
|
|
{
|
2020-06-17 02:31:42 +02:00
|
|
|
|
[Tst_ANS_INT ] = Tst_WriteIntAnsBank,
|
|
|
|
|
[Tst_ANS_FLOAT ] = Tst_WriteFltAnsBank,
|
|
|
|
|
[Tst_ANS_TRUE_FALSE ] = Tst_WriteTF_AnsBank,
|
|
|
|
|
[Tst_ANS_UNIQUE_CHOICE ] = Tst_WriteChoAnsBank,
|
|
|
|
|
[Tst_ANS_MULTIPLE_CHOICE] = Tst_WriteChoAnsBank,
|
|
|
|
|
[Tst_ANS_TEXT ] = Tst_WriteChoAnsBank,
|
2020-05-22 20:10:45 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/***** Write answers *****/
|
2020-06-17 20:20:16 +02:00
|
|
|
|
Tst_WriteAnsBank[Question->Answer.Type] (Question,ClassTxt,ClassFeedback);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
}
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/****************** Write integer answer when editing a test *****************/
|
|
|
|
|
/*****************************************************************************/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-06-17 20:20:16 +02:00
|
|
|
|
static void Tst_WriteIntAnsBank (struct Tst_Question *Question,
|
|
|
|
|
const char *ClassTxt,
|
|
|
|
|
__attribute__((unused)) const char *ClassFeedback)
|
2020-04-01 03:11:05 +02:00
|
|
|
|
{
|
2020-06-17 20:20:16 +02:00
|
|
|
|
HTM_SPAN_Begin ("class=\"%s\"",ClassTxt);
|
2021-09-24 21:49:06 +02:00
|
|
|
|
HTM_TxtF ("(%ld)",Question->Answer.Integer);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_SPAN_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-09-23 01:48:28 +02:00
|
|
|
|
/*****************************************************************************/
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/****************** Write float answer when editing a test *******************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-06-17 20:20:16 +02:00
|
|
|
|
static void Tst_WriteFltAnsBank (struct Tst_Question *Question,
|
|
|
|
|
const char *ClassTxt,
|
|
|
|
|
__attribute__((unused)) const char *ClassFeedback)
|
2020-04-01 03:11:05 +02:00
|
|
|
|
{
|
2020-06-17 20:20:16 +02:00
|
|
|
|
HTM_SPAN_Begin ("class=\"%s\"",ClassTxt);
|
2021-09-24 21:49:06 +02:00
|
|
|
|
HTM_Txt ("([");
|
|
|
|
|
HTM_Double (Question->Answer.FloatingPoint[0]);
|
|
|
|
|
HTM_Txt ("; ");
|
|
|
|
|
HTM_Double (Question->Answer.FloatingPoint[1]);
|
|
|
|
|
HTM_Txt ("])");
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_SPAN_End ();
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-23 19:17:12 +02:00
|
|
|
|
/*****************************************************************************/
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/*********** Write false / true answer when listing test questions ***********/
|
2019-09-23 19:17:12 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-06-17 20:20:16 +02:00
|
|
|
|
static void Tst_WriteTF_AnsBank (struct Tst_Question *Question,
|
|
|
|
|
const char *ClassTxt,
|
|
|
|
|
__attribute__((unused)) const char *ClassFeedback)
|
2019-09-23 19:17:12 +02:00
|
|
|
|
{
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Write answer *****/
|
2020-06-17 20:20:16 +02:00
|
|
|
|
HTM_SPAN_Begin ("class=\"%s\"",ClassTxt);
|
2021-09-24 21:49:06 +02:00
|
|
|
|
HTM_Txt ("(");
|
|
|
|
|
Tst_WriteAnsTF (Question->Answer.TF);
|
|
|
|
|
HTM_Txt (")");
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_SPAN_End ();
|
2019-09-23 19:17:12 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/**** Write single or multiple choice answer when listing test questions *****/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-06-17 20:20:16 +02:00
|
|
|
|
static void Tst_WriteChoAnsBank (struct Tst_Question *Question,
|
|
|
|
|
const char *ClassTxt,
|
|
|
|
|
const char *ClassFeedback)
|
2020-04-01 03:11:05 +02:00
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_TST_Answer_given_by_the_teachers;
|
|
|
|
|
unsigned NumOpt;
|
2020-06-17 02:31:42 +02:00
|
|
|
|
|
|
|
|
|
/***** Change format of answers text *****/
|
|
|
|
|
Tst_ChangeFormatAnswersText (Question);
|
|
|
|
|
|
|
|
|
|
/***** Change format of answers feedback *****/
|
|
|
|
|
Tst_ChangeFormatAnswersFeedback (Question);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
|
|
|
|
HTM_TABLE_BeginPadding (2);
|
2021-09-24 21:49:06 +02:00
|
|
|
|
for (NumOpt = 0;
|
|
|
|
|
NumOpt < Question->Answer.NumOptions;
|
|
|
|
|
NumOpt++)
|
|
|
|
|
{
|
|
|
|
|
HTM_TR_Begin (NULL);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
2021-09-24 21:49:06 +02:00
|
|
|
|
/* Put an icon that indicates whether the answer is correct or wrong */
|
|
|
|
|
HTM_TD_Begin ("class=\"BT%u\"",Gbl.RowEvenOdd);
|
|
|
|
|
if (Question->Answer.Options[NumOpt].Correct)
|
|
|
|
|
Ico_PutIcon ("check.svg",Txt_TST_Answer_given_by_the_teachers,"CONTEXT_ICO_16x16");
|
|
|
|
|
HTM_TD_End ();
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
2021-09-24 21:49:06 +02:00
|
|
|
|
/* Write the number of option */
|
|
|
|
|
HTM_TD_Begin ("class=\"%s LT\"",ClassTxt);
|
|
|
|
|
HTM_TxtF ("%c) ",'a' + (char) NumOpt);
|
|
|
|
|
HTM_TD_End ();
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
2021-09-24 21:49:06 +02:00
|
|
|
|
HTM_TD_Begin ("class=\"LT\"");
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
2021-09-24 21:49:06 +02:00
|
|
|
|
/* Write the text of the answer and the media */
|
|
|
|
|
HTM_DIV_Begin ("class=\"%s\"",ClassTxt);
|
|
|
|
|
HTM_Txt (Question->Answer.Options[NumOpt].Text);
|
|
|
|
|
Med_ShowMedia (&Question->Answer.Options[NumOpt].Media,
|
|
|
|
|
"TEST_MED_EDIT_LIST_CONT",
|
|
|
|
|
"TEST_MED_EDIT_LIST");
|
|
|
|
|
HTM_DIV_End ();
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
2021-09-24 21:49:06 +02:00
|
|
|
|
/* Write the text of the feedback */
|
|
|
|
|
HTM_DIV_Begin ("class=\"%s\"",ClassFeedback);
|
|
|
|
|
HTM_Txt (Question->Answer.Options[NumOpt].Feedback);
|
|
|
|
|
HTM_DIV_End ();
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
2021-09-24 21:49:06 +02:00
|
|
|
|
HTM_TD_End ();
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
2021-09-24 21:49:06 +02:00
|
|
|
|
HTM_TR_End ();
|
|
|
|
|
}
|
2020-04-01 03:11:05 +02:00
|
|
|
|
HTM_TABLE_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-22 20:10:45 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************** Write false / true answer when seeing a test *****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void Tst_WriteAnsTF (char AnsTF)
|
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_TF_QST[2];
|
|
|
|
|
|
|
|
|
|
switch (AnsTF)
|
|
|
|
|
{
|
|
|
|
|
case 'T': // true
|
|
|
|
|
HTM_Txt (Txt_TF_QST[0]);
|
|
|
|
|
break;
|
|
|
|
|
case 'F': // false
|
|
|
|
|
HTM_Txt (Txt_TF_QST[1]);
|
|
|
|
|
break;
|
|
|
|
|
default: // no answer
|
|
|
|
|
HTM_NBSP ();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*************** Write parameter with the code of a question *****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2021-04-26 17:29:04 +02:00
|
|
|
|
void Tst_WriteParamQstCod (unsigned QstInd,long QstCod)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2020-04-02 03:28:08 +02:00
|
|
|
|
char StrAns[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Ansxx...x"
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2021-04-26 17:29:04 +02:00
|
|
|
|
snprintf (StrAns,sizeof (StrAns),"Qst%010u",QstInd);
|
2020-04-02 03:28:08 +02:00
|
|
|
|
Par_PutHiddenParamLong (NULL,StrAns,QstCod);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************* Check if number of answers is one *********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-03-19 20:57:54 +01:00
|
|
|
|
void Tst_CheckIfNumberOfAnswersIsOne (const struct Tst_Question *Question)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (Question->Answer.NumOptions != 1)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_WrongAnswerExit ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************************* Get tags of a test question ***********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2021-04-16 13:51:12 +02:00
|
|
|
|
unsigned Tst_GetTagsQst (long QstCod,MYSQL_RES **mysql_res)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
/***** Get the tags of a question from database *****/
|
2021-04-16 13:51:12 +02:00
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get the tags of a question",
|
|
|
|
|
"SELECT tst_tags.TagTxt" // row[0]
|
|
|
|
|
" FROM tst_question_tags,"
|
|
|
|
|
"tst_tags"
|
|
|
|
|
" WHERE tst_question_tags.QstCod=%ld"
|
|
|
|
|
" AND tst_question_tags.TagCod=tst_tags.TagCod"
|
|
|
|
|
" AND tst_tags.CrsCod=%ld"
|
|
|
|
|
" ORDER BY tst_question_tags.TagInd",
|
|
|
|
|
QstCod,
|
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************** Get and write tags of a test question ******************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2017-09-06 15:03:43 +02:00
|
|
|
|
void Tst_GetAndWriteTagsQst (long QstCod)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_no_tags;
|
2021-04-16 13:51:12 +02:00
|
|
|
|
unsigned NumTags;
|
|
|
|
|
unsigned NumTag;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
MYSQL_ROW row;
|
|
|
|
|
|
2021-04-16 13:51:12 +02:00
|
|
|
|
if ((NumTags = Tst_GetTagsQst (QstCod,&mysql_res)))
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
/***** Write the tags *****/
|
2019-10-26 12:25:27 +02:00
|
|
|
|
HTM_UL_Begin ("class=\"TEST_TAG_LIST DAT_SMALL\"");
|
2021-04-16 13:51:12 +02:00
|
|
|
|
for (NumTag = 0;
|
|
|
|
|
NumTag < NumTags;
|
|
|
|
|
NumTag++)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
2019-10-26 22:49:13 +02:00
|
|
|
|
HTM_LI_Begin (NULL);
|
2019-11-10 12:36:37 +01:00
|
|
|
|
HTM_Txt (row[0]);
|
2019-10-26 22:49:13 +02:00
|
|
|
|
HTM_LI_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
2019-10-26 02:19:42 +02:00
|
|
|
|
HTM_UL_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
2019-11-07 10:24:00 +01:00
|
|
|
|
{
|
|
|
|
|
HTM_SPAN_Begin ("class=\"DAT_SMALL\"");
|
2019-11-11 00:15:44 +01:00
|
|
|
|
HTM_TxtF ("(%s)",Txt_no_tags);
|
2019-11-07 10:24:00 +01:00
|
|
|
|
HTM_SPAN_End ();
|
|
|
|
|
}
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************ Get parameters for the selection of test questions *************/
|
|
|
|
|
/*****************************************************************************/
|
2016-04-05 02:59:34 +02:00
|
|
|
|
// Return true (OK) if all parameters are found, or false (error) if any necessary parameter is not found
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-24 00:58:42 +01:00
|
|
|
|
static bool Tst_GetParamsTst (struct Tst_Test *Test,
|
2020-03-21 15:41:25 +01:00
|
|
|
|
Tst_ActionToDoWithQuestions_t ActionToDoWithQuestions)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_You_must_select_one_ore_more_tags;
|
|
|
|
|
extern const char *Txt_You_must_select_one_ore_more_types_of_answer;
|
|
|
|
|
extern const char *Txt_The_number_of_questions_must_be_in_the_interval_X;
|
|
|
|
|
bool Error = false;
|
2019-11-08 01:10:32 +01:00
|
|
|
|
char UnsignedStr[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
|
2014-12-01 23:55:08 +01:00
|
|
|
|
unsigned UnsignedNum;
|
|
|
|
|
|
|
|
|
|
/***** Tags *****/
|
|
|
|
|
/* Get parameter that indicates whether all tags are selected */
|
2020-03-24 00:58:42 +01:00
|
|
|
|
Test->Tags.All = Par_GetParToBool ("AllTags");
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/* Get the tags */
|
2021-02-15 16:25:55 +01:00
|
|
|
|
if ((Test->Tags.List = malloc (Tag_MAX_BYTES_TAGS_LIST + 1)) == NULL)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_NotEnoughMemoryExit ();
|
2020-05-17 17:11:04 +02:00
|
|
|
|
Par_GetParMultiToText ("ChkTag",Test->Tags.List,Tag_MAX_BYTES_TAGS_LIST);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/* Check number of tags selected */
|
2020-03-24 00:58:42 +01:00
|
|
|
|
if (Tst_CountNumTagsInList (&Test->Tags) == 0) // If no tags selected...
|
2020-03-21 15:41:25 +01:00
|
|
|
|
{ // ...write alert
|
2019-02-16 19:29:27 +01:00
|
|
|
|
Ale_ShowAlert (Ale_WARNING,Txt_You_must_select_one_ore_more_tags);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
Error = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** Types of answer *****/
|
2017-07-16 13:54:11 +02:00
|
|
|
|
switch (ActionToDoWithQuestions)
|
|
|
|
|
{
|
2017-09-04 17:03:49 +02:00
|
|
|
|
case Tst_SHOW_TEST_TO_ANSWER:
|
|
|
|
|
case Tst_EDIT_TEST:
|
2020-04-27 14:20:21 +02:00
|
|
|
|
case Tst_SELECT_QUESTIONS_FOR_EXAM:
|
2017-07-16 13:54:11 +02:00
|
|
|
|
/* Get parameter that indicates if all types of answer are selected */
|
2020-03-24 00:58:42 +01:00
|
|
|
|
Test->AnswerTypes.All = Par_GetParToBool ("AllAnsTypes");
|
2017-07-16 13:54:11 +02:00
|
|
|
|
|
|
|
|
|
/* Get types of answer */
|
2020-03-24 00:58:42 +01:00
|
|
|
|
Par_GetParMultiToText ("AnswerType",Test->AnswerTypes.List,Tst_MAX_BYTES_LIST_ANSWER_TYPES);
|
2017-07-16 13:54:11 +02:00
|
|
|
|
|
|
|
|
|
/* Check number of types of answer */
|
2020-03-24 00:58:42 +01:00
|
|
|
|
if (Tst_CountNumAnswerTypesInList (&Test->AnswerTypes) == 0) // If no types of answer selected...
|
2020-04-02 03:28:08 +02:00
|
|
|
|
{ // ...write warning alert
|
2019-02-16 19:29:27 +01:00
|
|
|
|
Ale_ShowAlert (Ale_WARNING,Txt_You_must_select_one_ore_more_types_of_answer);
|
2017-07-16 13:54:11 +02:00
|
|
|
|
Error = true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case Tst_SELECT_QUESTIONS_FOR_GAME:
|
|
|
|
|
/* The unique allowed type of answer in a game is unique choice */
|
2020-03-24 00:58:42 +01:00
|
|
|
|
Test->AnswerTypes.All = false;
|
2021-02-15 16:25:55 +01:00
|
|
|
|
snprintf (Test->AnswerTypes.List,sizeof (Test->AnswerTypes.List),"%u",
|
2018-10-18 02:02:32 +02:00
|
|
|
|
(unsigned) Tst_ANS_UNIQUE_CHOICE);
|
2017-07-16 13:54:11 +02:00
|
|
|
|
break;
|
2017-09-01 14:36:25 +02:00
|
|
|
|
default:
|
|
|
|
|
break;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** Get other parameters, depending on action *****/
|
2017-07-16 13:54:11 +02:00
|
|
|
|
switch (ActionToDoWithQuestions)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2017-09-04 17:03:49 +02:00
|
|
|
|
case Tst_SHOW_TEST_TO_ANSWER:
|
2020-03-24 01:50:39 +01:00
|
|
|
|
Test->NumQsts = Tst_GetParamNumQsts ();
|
|
|
|
|
if (Test->NumQsts < TstCfg_GetConfigMin () ||
|
|
|
|
|
Test->NumQsts > TstCfg_GetConfigMax ())
|
2017-07-16 13:54:11 +02:00
|
|
|
|
{
|
2019-02-16 19:29:27 +01:00
|
|
|
|
Ale_ShowAlert (Ale_WARNING,Txt_The_number_of_questions_must_be_in_the_interval_X,
|
2020-03-21 15:41:25 +01:00
|
|
|
|
TstCfg_GetConfigMin (),TstCfg_GetConfigMax ());
|
2017-07-16 13:54:11 +02:00
|
|
|
|
Error = true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2017-09-04 17:03:49 +02:00
|
|
|
|
case Tst_EDIT_TEST:
|
2017-07-16 13:54:11 +02:00
|
|
|
|
/* Get starting and ending dates */
|
|
|
|
|
Dat_GetIniEndDatesFromForm ();
|
|
|
|
|
|
|
|
|
|
/* Get ordering criteria */
|
2019-11-08 01:10:32 +01:00
|
|
|
|
Par_GetParMultiToText ("Order",UnsignedStr,Cns_MAX_DECIMAL_DIGITS_UINT);
|
2017-07-16 13:54:11 +02:00
|
|
|
|
if (sscanf (UnsignedStr,"%u",&UnsignedNum) == 1)
|
2020-03-24 00:58:42 +01:00
|
|
|
|
Test->SelectedOrder = (Tst_QuestionsOrder_t)
|
|
|
|
|
((UnsignedNum < Tst_NUM_TYPES_ORDER_QST) ? UnsignedNum :
|
|
|
|
|
0);
|
2017-07-16 13:54:11 +02:00
|
|
|
|
else
|
2020-03-24 00:58:42 +01:00
|
|
|
|
Test->SelectedOrder = (Tst_QuestionsOrder_t) 0;
|
2017-07-16 13:54:11 +02:00
|
|
|
|
break;
|
2020-04-27 14:20:21 +02:00
|
|
|
|
case Tst_SELECT_QUESTIONS_FOR_EXAM:
|
2017-07-16 13:54:11 +02:00
|
|
|
|
case Tst_SELECT_QUESTIONS_FOR_GAME:
|
|
|
|
|
/* Get starting and ending dates */
|
|
|
|
|
Dat_GetIniEndDatesFromForm ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2017-07-16 13:54:11 +02:00
|
|
|
|
/* Order question by stem */
|
2020-03-24 00:58:42 +01:00
|
|
|
|
Test->SelectedOrder = Tst_ORDER_STEM;
|
2017-07-16 13:54:11 +02:00
|
|
|
|
break;
|
2017-09-01 14:36:25 +02:00
|
|
|
|
default:
|
|
|
|
|
break;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-04-05 02:59:34 +02:00
|
|
|
|
return !Error;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2020-04-02 03:28:08 +02:00
|
|
|
|
/******** Get parameter with the number of test exam generated by me *********/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-02 03:28:08 +02:00
|
|
|
|
static unsigned Tst_GetParamNumTst (void)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2017-01-29 21:41:08 +01:00
|
|
|
|
return (unsigned) Par_GetParToUnsignedLong ("NumTst",
|
|
|
|
|
1,
|
|
|
|
|
UINT_MAX,
|
|
|
|
|
1);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***** Get parameter with the number of questions to generate in an test *****/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-03-22 01:15:27 +01:00
|
|
|
|
static unsigned Tst_GetParamNumQsts (void)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2020-03-22 01:15:27 +01:00
|
|
|
|
return (unsigned) Par_GetParToUnsignedLong ("NumQst",
|
|
|
|
|
(unsigned long) TstCfg_GetConfigMin (),
|
|
|
|
|
(unsigned long) TstCfg_GetConfigMax (),
|
|
|
|
|
(unsigned long) TstCfg_GetConfigDef ());
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************** Count number of tags in the list of tags ******************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-05-17 17:11:04 +02:00
|
|
|
|
static unsigned Tst_CountNumTagsInList (const struct Tag_Tags *Tags)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
const char *Ptr;
|
2020-03-21 15:41:25 +01:00
|
|
|
|
unsigned NumTags = 0;
|
2020-05-17 17:11:04 +02:00
|
|
|
|
char TagText[Tag_MAX_BYTES_TAG + 1];
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-21 15:41:25 +01:00
|
|
|
|
/***** Go over the list of tags counting the number of tags *****/
|
|
|
|
|
Ptr = Tags->List;
|
|
|
|
|
while (*Ptr)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2020-05-17 17:11:04 +02:00
|
|
|
|
Par_GetNextStrUntilSeparParamMult (&Ptr,TagText,Tag_MAX_BYTES_TAG);
|
2020-03-21 15:41:25 +01:00
|
|
|
|
NumTags++;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
2020-03-21 15:41:25 +01:00
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
return NumTags;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/**** Count the number of types of answers in the list of types of answers ***/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-03-21 22:18:24 +01:00
|
|
|
|
static int Tst_CountNumAnswerTypesInList (const struct Tst_AnswerTypes *AnswerTypes)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
const char *Ptr;
|
|
|
|
|
int NumAnsTypes = 0;
|
2019-11-08 01:10:32 +01:00
|
|
|
|
char UnsignedStr[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-21 22:18:24 +01:00
|
|
|
|
/***** Go over the list of answer types counting the number of types of answer *****/
|
|
|
|
|
Ptr = AnswerTypes->List;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
while (*Ptr)
|
|
|
|
|
{
|
2019-11-08 01:10:32 +01:00
|
|
|
|
Par_GetNextStrUntilSeparParamMult (&Ptr,UnsignedStr,Cns_MAX_DECIMAL_DIGITS_UINT);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
Tst_ConvertFromUnsignedStrToAnsTyp (UnsignedStr);
|
|
|
|
|
NumAnsTypes++;
|
|
|
|
|
}
|
|
|
|
|
return NumAnsTypes;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-27 14:20:21 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/**** 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)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_WrongQuestionExit ();
|
2020-04-27 14:20:21 +02:00
|
|
|
|
NumQuestions++;
|
|
|
|
|
}
|
|
|
|
|
return NumQuestions;
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************** Show form to edit one test question ********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void Tst_ShowFormEditOneQst (void)
|
|
|
|
|
{
|
2020-04-04 02:07:54 +02:00
|
|
|
|
extern const char *Txt_Question_removed;
|
2020-03-17 14:47:58 +01:00
|
|
|
|
struct Tst_Question Question;
|
2020-04-04 02:07:54 +02:00
|
|
|
|
bool PutFormToEditQuestion;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2016-04-06 19:26:09 +02:00
|
|
|
|
/***** Create test question *****/
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Tst_QstConstructor (&Question);
|
2020-03-18 01:57:08 +01:00
|
|
|
|
|
|
|
|
|
/***** Get question data *****/
|
2020-04-27 19:37:49 +02:00
|
|
|
|
Question.QstCod = Tst_GetParamQstCod ();
|
2020-04-04 02:07:54 +02:00
|
|
|
|
if (Question.QstCod > 0) // Question already exists in the database
|
2020-04-04 19:20:50 +02:00
|
|
|
|
PutFormToEditQuestion = Tst_GetQstDataFromDB (&Question);
|
2020-04-04 02:07:54 +02:00
|
|
|
|
else // New question
|
|
|
|
|
PutFormToEditQuestion = true;
|
2016-04-06 19:26:09 +02:00
|
|
|
|
|
|
|
|
|
/***** Put form to edit question *****/
|
2020-04-04 02:07:54 +02:00
|
|
|
|
if (PutFormToEditQuestion)
|
2020-04-04 19:20:50 +02:00
|
|
|
|
Tst_PutFormEditOneQst (&Question);
|
2020-04-04 02:07:54 +02:00
|
|
|
|
else
|
|
|
|
|
Ale_ShowAlert (Ale_WARNING,Txt_Question_removed);
|
2016-04-06 19:26:09 +02:00
|
|
|
|
|
|
|
|
|
/***** Destroy test question *****/
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Tst_QstDestructor (&Question);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************** Show form to edit one test question ********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
// This function may be called from three places:
|
2016-11-27 14:34:36 +01:00
|
|
|
|
// 1. By clicking "New question" icon
|
|
|
|
|
// 2. By clicking "Edit" icon in a listing of existing questions
|
2014-12-01 23:55:08 +01:00
|
|
|
|
// 3. From the action associated to reception of a question, on error in the parameters received from the form
|
|
|
|
|
|
2020-04-04 19:20:50 +02:00
|
|
|
|
static void Tst_PutFormEditOneQst (struct Tst_Question *Question)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2020-05-18 22:59:07 +02:00
|
|
|
|
extern const char *Hlp_ASSESSMENT_Questions_writing_a_question;
|
2019-02-22 21:47:50 +01:00
|
|
|
|
extern const char *The_ClassFormInBox[The_NUM_THEMES];
|
2014-12-01 23:55:08 +01:00
|
|
|
|
extern const char *Txt_Question_code_X;
|
|
|
|
|
extern const char *Txt_New_question;
|
|
|
|
|
extern const char *Txt_Tags;
|
|
|
|
|
extern const char *Txt_new_tag;
|
2019-09-12 23:53:00 +02:00
|
|
|
|
extern const char *Txt_Wording;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
extern const char *Txt_Feedback;
|
2016-04-05 17:33:33 +02:00
|
|
|
|
extern const char *Txt_optional;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
extern const char *Txt_Type;
|
|
|
|
|
extern const char *Txt_TST_STR_ANSWER_TYPES[Tst_NUM_ANS_TYPES];
|
|
|
|
|
extern const char *Txt_Answers;
|
|
|
|
|
extern const char *Txt_Integer_number;
|
|
|
|
|
extern const char *Txt_Real_number_between_A_and_B_1;
|
|
|
|
|
extern const char *Txt_Real_number_between_A_and_B_2;
|
|
|
|
|
extern const char *Txt_TF_QST[2];
|
|
|
|
|
extern const char *Txt_Shuffle;
|
2016-04-07 14:40:50 +02:00
|
|
|
|
extern const char *Txt_Expand;
|
|
|
|
|
extern const char *Txt_Contract;
|
2019-02-18 18:27:45 +01:00
|
|
|
|
extern const char *Txt_Save_changes;
|
2015-04-11 20:18:30 +02:00
|
|
|
|
extern const char *Txt_Create_question;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
MYSQL_ROW row;
|
2020-03-24 01:50:39 +01:00
|
|
|
|
unsigned NumTags;
|
|
|
|
|
unsigned IndTag;
|
|
|
|
|
unsigned NumTag;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
unsigned NumOpt;
|
|
|
|
|
Tst_AnswerType_t AnsType;
|
2019-11-07 00:34:20 +01:00
|
|
|
|
bool IsThisTag;
|
|
|
|
|
bool TagFound;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
bool OptionsDisabled;
|
2016-04-07 10:23:28 +02:00
|
|
|
|
bool AnswerHasContent;
|
2016-04-07 17:14:35 +02:00
|
|
|
|
bool DisplayRightColumn;
|
2019-11-08 01:10:32 +01:00
|
|
|
|
char StrTagTxt[6 + Cns_MAX_DECIMAL_DIGITS_UINT + 1];
|
|
|
|
|
char StrInteger[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
|
2019-12-30 22:32:06 +01:00
|
|
|
|
char *Title;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2019-10-26 02:19:42 +02:00
|
|
|
|
/***** Begin box *****/
|
2020-03-25 01:36:22 +01:00
|
|
|
|
if (Question->QstCod > 0) // The question already has assigned a code
|
2015-04-11 13:05:44 +02:00
|
|
|
|
{
|
2020-03-25 01:36:22 +01:00
|
|
|
|
Box_BoxBegin (NULL,Str_BuildStringLong (Txt_Question_code_X,Question->QstCod),
|
2020-03-27 14:56:54 +01:00
|
|
|
|
Tst_PutIconToRemoveOneQst,&Question->QstCod,
|
2020-05-18 22:59:07 +02:00
|
|
|
|
Hlp_ASSESSMENT_Questions_writing_a_question,Box_NOT_CLOSABLE);
|
2019-12-30 21:47:07 +01:00
|
|
|
|
Str_FreeString ();
|
2015-04-11 13:05:44 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
2020-03-26 02:54:30 +01:00
|
|
|
|
Box_BoxBegin (NULL,Txt_New_question,
|
|
|
|
|
NULL,NULL,
|
2020-05-18 22:59:07 +02:00
|
|
|
|
Hlp_ASSESSMENT_Questions_writing_a_question,Box_NOT_CLOSABLE);
|
2016-04-05 10:05:52 +02:00
|
|
|
|
|
2019-10-20 22:00:28 +02:00
|
|
|
|
/***** Begin form *****/
|
2021-03-02 00:54:26 +01:00
|
|
|
|
Frm_BeginForm (ActRcvTstQst);
|
2020-03-27 14:56:54 +01:00
|
|
|
|
Tst_PutParamQstCod (&Question->QstCod);
|
2016-04-05 10:05:52 +02:00
|
|
|
|
|
2019-10-20 22:00:28 +02:00
|
|
|
|
/***** Begin table *****/
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TABLE_BeginPadding (2); // Table for this question
|
2015-04-11 13:05:44 +02:00
|
|
|
|
|
|
|
|
|
/***** Help for text editor *****/
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
|
HTM_TD_Begin ("colspan=\"2\"");
|
2015-04-11 13:05:44 +02:00
|
|
|
|
Lay_HelpPlainEditor ();
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
HTM_TR_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/***** Get tags already existing for questions in current course *****/
|
2020-05-17 17:11:04 +02:00
|
|
|
|
NumTags = Tag_GetAllTagsFromCurrentCrs (&mysql_res);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/***** Write the tags *****/
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TR_Begin (NULL);
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_Begin ("class=\"RT %s\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
|
2020-06-18 20:06:17 +02:00
|
|
|
|
HTM_TxtColon (Txt_Tags);
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_End ();
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_Begin ("class=\"LT\"");
|
|
|
|
|
HTM_TABLE_BeginPadding (2); // Table for tags
|
2017-05-01 21:17:38 +02:00
|
|
|
|
|
2020-03-24 01:50:39 +01:00
|
|
|
|
for (IndTag = 0;
|
2020-05-17 17:11:04 +02:00
|
|
|
|
IndTag < Tag_MAX_TAGS_PER_QUESTION;
|
2020-03-24 01:50:39 +01:00
|
|
|
|
IndTag++)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TR_Begin (NULL);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/***** Write the tags already existing in a selector *****/
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_Begin ("class=\"LM\"");
|
2020-04-27 03:16:55 +02:00
|
|
|
|
HTM_SELECT_Begin (HTM_DONT_SUBMIT_ON_CHANGE,
|
2020-03-24 01:50:39 +01:00
|
|
|
|
"id=\"SelTag%u\" name=\"SelTag%u\""
|
2019-11-05 15:47:35 +01:00
|
|
|
|
" class=\"TAG_SEL\" onchange=\"changeTxtTag('%u')\"",
|
2020-03-24 01:50:39 +01:00
|
|
|
|
IndTag,IndTag,IndTag);
|
2019-11-07 00:34:20 +01:00
|
|
|
|
HTM_OPTION (HTM_Type_STRING,"",false,false," ");
|
2017-04-25 14:48:47 +02:00
|
|
|
|
mysql_data_seek (mysql_res,0);
|
2019-11-07 00:34:20 +01:00
|
|
|
|
TagFound = false;
|
2020-04-14 23:23:15 +02:00
|
|
|
|
for (NumTag = 1;
|
2020-03-24 01:50:39 +01:00
|
|
|
|
NumTag <= NumTags;
|
|
|
|
|
NumTag++)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
2019-03-18 15:42:22 +01:00
|
|
|
|
/*
|
|
|
|
|
row[0] TagCod
|
|
|
|
|
row[1] TagTxt
|
|
|
|
|
row[2] TagHidden
|
|
|
|
|
*/
|
2019-11-07 00:34:20 +01:00
|
|
|
|
IsThisTag = false;
|
2020-03-25 01:36:22 +01:00
|
|
|
|
if (!strcasecmp (Question->Tags.Txt[IndTag],row[1]))
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2019-11-11 00:15:44 +01:00
|
|
|
|
HTM_Txt (" selected=\"selected\"");
|
2019-11-07 00:34:20 +01:00
|
|
|
|
IsThisTag = true;
|
|
|
|
|
TagFound = true;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
2019-11-07 00:34:20 +01:00
|
|
|
|
HTM_OPTION (HTM_Type_STRING,row[1],
|
|
|
|
|
IsThisTag,false,
|
|
|
|
|
"%s",row[1]);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
/* If it's a new tag received from the form */
|
2020-03-25 01:36:22 +01:00
|
|
|
|
if (!TagFound && Question->Tags.Txt[IndTag][0])
|
|
|
|
|
HTM_OPTION (HTM_Type_STRING,Question->Tags.Txt[IndTag],
|
2019-11-07 00:34:20 +01:00
|
|
|
|
true,false,
|
2020-03-25 01:36:22 +01:00
|
|
|
|
"%s",Question->Tags.Txt[IndTag]);
|
2019-11-07 00:34:20 +01:00
|
|
|
|
HTM_OPTION (HTM_Type_STRING,"",
|
|
|
|
|
false,false,
|
|
|
|
|
"[%s]",Txt_new_tag);
|
2019-11-05 08:46:38 +01:00
|
|
|
|
HTM_SELECT_End ();
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/***** Input of a new tag *****/
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_Begin ("class=\"RM\"");
|
2021-02-15 16:25:55 +01:00
|
|
|
|
snprintf (StrTagTxt,sizeof (StrTagTxt),"TagTxt%u",IndTag);
|
2020-05-17 17:11:04 +02:00
|
|
|
|
HTM_INPUT_TEXT (StrTagTxt,Tag_MAX_CHARS_TAG,Question->Tags.Txt[IndTag],
|
2020-04-27 03:16:55 +02:00
|
|
|
|
HTM_DONT_SUBMIT_ON_CHANGE,
|
2019-11-13 09:49:52 +01:00
|
|
|
|
"id=\"%s\" class=\"TAG_TXT\" onchange=\"changeSelTag('%u')\"",
|
2020-03-24 01:50:39 +01:00
|
|
|
|
StrTagTxt,IndTag);
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TR_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
2017-05-01 21:17:38 +02:00
|
|
|
|
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TABLE_End (); // Table for tags
|
|
|
|
|
HTM_TD_End ();
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TR_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/* Free structure that stores the query result */
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
|
2016-04-04 10:11:05 +02:00
|
|
|
|
/***** Stem and image *****/
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TR_Begin (NULL);
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2019-12-27 21:10:39 +01:00
|
|
|
|
/* Label */
|
2019-12-27 15:45:19 +01:00
|
|
|
|
Frm_LabelColumn ("RT","Stem",Txt_Wording);
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2019-12-27 21:10:39 +01:00
|
|
|
|
/* Data */
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_Begin ("class=\"LT\"");
|
2019-10-31 17:42:05 +01:00
|
|
|
|
HTM_TEXTAREA_Begin ("id=\"Stem\" name=\"Stem\" class=\"STEM_TEXTAREA\""
|
|
|
|
|
" rows=\"5\" required=\"required\"");
|
2020-04-04 19:20:50 +02:00
|
|
|
|
HTM_Txt (Question->Stem);
|
2019-10-31 17:42:05 +01:00
|
|
|
|
HTM_TEXTAREA_End ();
|
2019-11-09 21:08:20 +01:00
|
|
|
|
HTM_BR ();
|
2020-03-17 14:47:58 +01:00
|
|
|
|
Tst_PutFormToEditQstMedia (&Question->Media,-1,
|
2016-04-14 21:33:24 +02:00
|
|
|
|
false);
|
2016-03-29 10:24:14 +02:00
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/***** Feedback *****/
|
2019-11-02 23:40:52 +01:00
|
|
|
|
HTM_LABEL_Begin ("class=\"%s\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
|
2019-11-11 10:59:24 +01:00
|
|
|
|
HTM_TxtF ("%s (%s):",Txt_Feedback,Txt_optional);
|
2019-11-09 21:08:20 +01:00
|
|
|
|
HTM_BR ();
|
2019-10-31 17:42:05 +01:00
|
|
|
|
HTM_TEXTAREA_Begin ("name=\"Feedback\" class=\"STEM_TEXTAREA\" rows=\"2\"");
|
2020-04-04 19:20:50 +02:00
|
|
|
|
if (Question->Feedback[0])
|
|
|
|
|
HTM_Txt (Question->Feedback);
|
2019-10-31 17:42:05 +01:00
|
|
|
|
HTM_TEXTAREA_End ();
|
2019-11-02 12:59:31 +01:00
|
|
|
|
HTM_LABEL_End ();
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_End ();
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TR_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/***** Type of answer *****/
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TR_Begin (NULL);
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_Begin ("class=\"RT %s\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
|
2020-06-18 20:06:17 +02:00
|
|
|
|
HTM_TxtColon (Txt_Type);
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_End ();
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_Begin ("class=\"%s LT\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
|
2019-12-15 20:02:34 +01:00
|
|
|
|
for (AnsType = (Tst_AnswerType_t) 0;
|
|
|
|
|
AnsType <= (Tst_AnswerType_t) (Tst_NUM_ANS_TYPES - 1);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
AnsType++)
|
|
|
|
|
{
|
2019-11-03 10:41:31 +01:00
|
|
|
|
HTM_LABEL_Begin (NULL);
|
2019-11-04 18:17:39 +01:00
|
|
|
|
HTM_INPUT_RADIO ("AnswerType",false,
|
|
|
|
|
"value=\"%u\"%s onclick=\"enableDisableAns(this.form);\"",
|
|
|
|
|
(unsigned) AnsType,
|
2020-03-19 20:57:54 +01:00
|
|
|
|
AnsType == Question->Answer.Type ? " checked=\"checked\"" :
|
|
|
|
|
"");
|
2019-11-11 10:59:24 +01:00
|
|
|
|
HTM_TxtF ("%s ",Txt_TST_STR_ANSWER_TYPES[AnsType]);
|
2019-11-02 12:59:31 +01:00
|
|
|
|
HTM_LABEL_End ();
|
2019-11-09 21:08:20 +01:00
|
|
|
|
HTM_BR ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_End ();
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TR_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/***** Answers *****/
|
|
|
|
|
/* Integer answer */
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TR_Begin (NULL);
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_Begin ("class=\"RT %s\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
|
2020-06-18 20:06:17 +02:00
|
|
|
|
HTM_TxtColon (Txt_Answers);
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_End ();
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_Begin ("class=\"LT\"");
|
2019-11-02 23:40:52 +01:00
|
|
|
|
HTM_LABEL_Begin ("class=\"%s\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
|
2020-01-11 15:22:02 +01:00
|
|
|
|
HTM_TxtColonNBSP (Txt_Integer_number);
|
2021-02-15 16:25:55 +01:00
|
|
|
|
snprintf (StrInteger,sizeof (StrInteger),"%ld",Question->Answer.Integer);
|
2020-04-27 03:16:55 +02:00
|
|
|
|
HTM_INPUT_TEXT ("AnsInt",Cns_MAX_DECIMAL_DIGITS_LONG,StrInteger,
|
|
|
|
|
HTM_DONT_SUBMIT_ON_CHANGE,
|
2019-11-04 01:29:46 +01:00
|
|
|
|
"size=\"11\" required=\"required\"%s",
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.Type == Tst_ANS_INT ? "" :
|
|
|
|
|
" disabled=\"disabled\"");
|
2019-11-02 12:59:31 +01:00
|
|
|
|
HTM_LABEL_End ();
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_End ();
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TR_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/* Floating point answer */
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
|
HTM_TD_Empty (1);
|
|
|
|
|
HTM_TD_Begin ("class=\"LT\"");
|
2016-12-26 15:17:30 +01:00
|
|
|
|
Tst_PutFloatInputField (Txt_Real_number_between_A_and_B_1,"AnsFloatMin",
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question,0);
|
2016-12-26 15:17:30 +01:00
|
|
|
|
Tst_PutFloatInputField (Txt_Real_number_between_A_and_B_2,"AnsFloatMax",
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question,1);
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
HTM_TR_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/* T/F answer */
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
|
HTM_TD_Empty (1);
|
|
|
|
|
HTM_TD_Begin ("class=\"LT\"");
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Tst_PutTFInputField (Question,Txt_TF_QST[0],'T');
|
|
|
|
|
Tst_PutTFInputField (Question,Txt_TF_QST[1],'F');
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
HTM_TR_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/* Questions can be shuffled? */
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TR_Begin (NULL);
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_Empty (1);
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_Begin ("class=\"LT\"");
|
2019-11-02 23:40:52 +01:00
|
|
|
|
HTM_LABEL_Begin ("class=\"%s\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
|
2020-03-12 13:53:37 +01:00
|
|
|
|
HTM_INPUT_CHECKBOX ("Shuffle",HTM_DONT_SUBMIT_ON_CHANGE,
|
2019-11-04 20:41:35 +01:00
|
|
|
|
"value=\"Y\"%s%s",
|
2020-04-03 19:13:00 +02:00
|
|
|
|
Question->Answer.Shuffle ? " checked=\"checked\"" :
|
|
|
|
|
"",
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.Type != Tst_ANS_UNIQUE_CHOICE &&
|
|
|
|
|
Question->Answer.Type != Tst_ANS_MULTIPLE_CHOICE ? " disabled=\"disabled\"" :
|
|
|
|
|
"");
|
2019-11-10 12:36:37 +01:00
|
|
|
|
HTM_Txt (Txt_Shuffle);
|
2019-11-02 12:59:31 +01:00
|
|
|
|
HTM_LABEL_End ();
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_End ();
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TR_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/* Simple or multiple choice answers */
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
|
HTM_TD_Empty (1);
|
|
|
|
|
HTM_TD_Begin ("class=\"LT\"");
|
|
|
|
|
HTM_TABLE_BeginPadding (2); // Table with choice answers
|
2017-05-01 21:17:38 +02:00
|
|
|
|
|
2020-03-19 20:57:54 +01:00
|
|
|
|
OptionsDisabled = Question->Answer.Type != Tst_ANS_UNIQUE_CHOICE &&
|
|
|
|
|
Question->Answer.Type != Tst_ANS_MULTIPLE_CHOICE &&
|
|
|
|
|
Question->Answer.Type != Tst_ANS_TEXT;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
for (NumOpt = 0;
|
|
|
|
|
NumOpt < Tst_MAX_OPTIONS_PER_QUESTION;
|
|
|
|
|
NumOpt++)
|
|
|
|
|
{
|
2016-04-05 14:32:02 +02:00
|
|
|
|
Gbl.RowEvenOdd = NumOpt % 2;
|
|
|
|
|
|
2016-04-07 14:40:50 +02:00
|
|
|
|
AnswerHasContent = false;
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (Question->Answer.Options[NumOpt].Text)
|
|
|
|
|
if (Question->Answer.Options[NumOpt].Text[0] || // Text
|
|
|
|
|
Question->Answer.Options[NumOpt].Media.Type != Med_TYPE_NONE) // or media
|
2016-04-07 14:40:50 +02:00
|
|
|
|
AnswerHasContent = true;
|
2016-11-19 19:39:17 +01:00
|
|
|
|
DisplayRightColumn = NumOpt < 2 || // Display at least the two first options
|
|
|
|
|
AnswerHasContent;
|
2016-04-07 14:40:50 +02:00
|
|
|
|
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TR_Begin (NULL);
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
|
|
|
|
/***** Left column: selectors *****/
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_Begin ("class=\"TEST_EDI_ANS_LEFT_COL COLOR%u\"",Gbl.RowEvenOdd);
|
2016-11-19 19:39:17 +01:00
|
|
|
|
|
|
|
|
|
/* Radio selector for unique choice answers */
|
2019-11-04 18:17:39 +01:00
|
|
|
|
HTM_INPUT_RADIO ("AnsUni",false,
|
|
|
|
|
"value=\"%u\"%s%s%s onclick=\"enableDisableAns(this.form);\"",
|
|
|
|
|
NumOpt,
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.Options[NumOpt].Correct ? " checked=\"checked\"" :
|
|
|
|
|
"",
|
2020-03-18 18:49:45 +01:00
|
|
|
|
NumOpt < 2 ? " required=\"required\"" : // First or second options required
|
|
|
|
|
"",
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.Type == Tst_ANS_UNIQUE_CHOICE ? "" :
|
|
|
|
|
" disabled=\"disabled\"");
|
2016-11-19 19:39:17 +01:00
|
|
|
|
|
|
|
|
|
/* Checkbox for multiple choice answers */
|
2020-03-12 13:53:37 +01:00
|
|
|
|
HTM_INPUT_CHECKBOX ("AnsMulti",HTM_DONT_SUBMIT_ON_CHANGE,
|
2019-11-04 20:41:35 +01:00
|
|
|
|
"value=\"%u\"%s%s",
|
|
|
|
|
NumOpt,
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.Options[NumOpt].Correct ? " checked=\"checked\"" :
|
|
|
|
|
"",
|
|
|
|
|
Question->Answer.Type == Tst_ANS_MULTIPLE_CHOICE ? "" :
|
|
|
|
|
" disabled=\"disabled\"");
|
2016-11-19 19:39:17 +01:00
|
|
|
|
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_End ();
|
2016-04-07 17:14:35 +02:00
|
|
|
|
|
|
|
|
|
/***** Center column: letter of the answer and expand / contract icon *****/
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_Begin ("class=\"%s TEST_EDI_ANS_CENTER_COL COLOR%u\"",
|
2019-10-10 23:14:13 +02:00
|
|
|
|
The_ClassFormInBox[Gbl.Prefs.Theme],Gbl.RowEvenOdd);
|
2019-11-11 00:15:44 +01:00
|
|
|
|
HTM_TxtF ("%c)",'a' + (char) NumOpt);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2016-04-07 17:14:35 +02:00
|
|
|
|
/* Icon to expand (show the answer) */
|
2019-10-28 20:38:29 +01:00
|
|
|
|
HTM_A_Begin ("href=\"\" id=\"exp_%u\"%s"
|
|
|
|
|
" onclick=\"toggleAnswer('%u');return false;\"",
|
|
|
|
|
NumOpt,
|
|
|
|
|
DisplayRightColumn ? " style=\"display:none;\"" : // Answer does have content ==> Hide icon
|
|
|
|
|
"",
|
|
|
|
|
NumOpt);
|
2019-12-30 22:32:06 +01:00
|
|
|
|
if (asprintf (&Title,"%s %c)",Txt_Expand,'a' + (char) NumOpt) < 0)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_NotEnoughMemoryExit ();
|
2019-12-30 22:32:06 +01:00
|
|
|
|
Ico_PutIcon ("caret-right.svg",Title,"ICO16x16");
|
|
|
|
|
free (Title);
|
2019-10-28 13:56:04 +01:00
|
|
|
|
HTM_A_End ();
|
2016-04-07 17:14:35 +02:00
|
|
|
|
|
|
|
|
|
/* Icon to contract (hide the answer) */
|
2019-10-28 20:38:29 +01:00
|
|
|
|
HTM_A_Begin ("href=\"\" id=\"con_%u\"%s"
|
|
|
|
|
" onclick=\"toggleAnswer(%u);return false;\"",
|
|
|
|
|
NumOpt,
|
|
|
|
|
DisplayRightColumn ? "" :
|
|
|
|
|
" style=\"display:none;\"", // Answer does not have content ==> Hide icon
|
|
|
|
|
NumOpt);
|
2019-12-30 22:32:06 +01:00
|
|
|
|
if (asprintf (&Title,"%s %c)",Txt_Contract,'a' + (char) NumOpt) < 0)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_NotEnoughMemoryExit ();
|
2019-12-30 22:32:06 +01:00
|
|
|
|
Ico_PutIcon ("caret-down.svg",Title,"ICO16x16");
|
|
|
|
|
free (Title);
|
2019-10-28 13:56:04 +01:00
|
|
|
|
HTM_A_End ();
|
2016-04-07 10:23:28 +02:00
|
|
|
|
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_End ();
|
2016-04-07 14:40:50 +02:00
|
|
|
|
|
|
|
|
|
/***** Right column: content of the answer *****/
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_Begin ("class=\"TEST_EDI_ANS_RIGHT_COL COLOR%u\"",Gbl.RowEvenOdd);
|
2019-10-28 20:38:29 +01:00
|
|
|
|
HTM_DIV_Begin ("id=\"ans_%u\"%s",
|
|
|
|
|
NumOpt,
|
|
|
|
|
DisplayRightColumn ? "" :
|
|
|
|
|
" style=\"display:none;\""); // Answer does not have content ==> Hide column
|
2016-04-07 10:23:28 +02:00
|
|
|
|
|
|
|
|
|
/* Answer text */
|
2019-10-31 17:42:05 +01:00
|
|
|
|
HTM_TEXTAREA_Begin ("name=\"AnsStr%u\" class=\"ANSWER_TEXTAREA\" rows=\"5\"%s",
|
|
|
|
|
NumOpt,OptionsDisabled ? " disabled=\"disabled\"" :
|
|
|
|
|
"");
|
2016-04-07 10:23:28 +02:00
|
|
|
|
if (AnswerHasContent)
|
2020-03-19 20:57:54 +01:00
|
|
|
|
HTM_Txt (Question->Answer.Options[NumOpt].Text);
|
2019-10-31 17:42:05 +01:00
|
|
|
|
HTM_TEXTAREA_End ();
|
2016-04-05 14:32:02 +02:00
|
|
|
|
|
2019-03-02 21:49:11 +01:00
|
|
|
|
/* Media */
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Tst_PutFormToEditQstMedia (&Question->Answer.Options[NumOpt].Media,
|
2016-04-14 21:33:24 +02:00
|
|
|
|
(int) NumOpt,
|
2016-04-05 21:44:06 +02:00
|
|
|
|
OptionsDisabled);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/* Feedback */
|
2019-11-02 23:40:52 +01:00
|
|
|
|
HTM_LABEL_Begin ("class=\"%s\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
|
2019-11-11 10:59:24 +01:00
|
|
|
|
HTM_TxtF ("%s (%s):",Txt_Feedback,Txt_optional);
|
2019-11-09 21:08:20 +01:00
|
|
|
|
HTM_BR ();
|
2019-10-31 17:42:05 +01:00
|
|
|
|
HTM_TEXTAREA_Begin ("name=\"FbStr%u\" class=\"ANSWER_TEXTAREA\" rows=\"2\"%s",
|
|
|
|
|
NumOpt,OptionsDisabled ? " disabled=\"disabled\"" :
|
|
|
|
|
"");
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (Question->Answer.Options[NumOpt].Feedback)
|
|
|
|
|
if (Question->Answer.Options[NumOpt].Feedback[0])
|
|
|
|
|
HTM_Txt (Question->Answer.Options[NumOpt].Feedback);
|
2019-10-31 17:42:05 +01:00
|
|
|
|
HTM_TEXTAREA_End ();
|
2019-11-02 12:59:31 +01:00
|
|
|
|
HTM_LABEL_End ();
|
2016-04-07 10:23:28 +02:00
|
|
|
|
|
|
|
|
|
/* End of right column */
|
2019-10-23 20:07:56 +02:00
|
|
|
|
HTM_DIV_End ();
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TD_End ();
|
2019-10-07 21:15:14 +02:00
|
|
|
|
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TR_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TABLE_End (); // Table with choice answers
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
HTM_TR_End ();
|
2015-04-11 13:05:44 +02:00
|
|
|
|
|
2016-04-05 10:05:52 +02:00
|
|
|
|
/***** End table *****/
|
2019-10-23 19:05:05 +02:00
|
|
|
|
HTM_TABLE_End (); // Table for this question
|
2016-04-05 10:05:52 +02:00
|
|
|
|
|
|
|
|
|
/***** Send button *****/
|
2020-03-25 01:36:22 +01:00
|
|
|
|
if (Question->QstCod > 0) // The question already has assigned a code
|
2019-02-18 18:27:45 +01:00
|
|
|
|
Btn_PutConfirmButton (Txt_Save_changes);
|
2015-04-11 20:18:30 +02:00
|
|
|
|
else
|
2017-06-11 19:02:40 +02:00
|
|
|
|
Btn_PutCreateButton (Txt_Create_question);
|
2015-04-11 13:05:44 +02:00
|
|
|
|
|
|
|
|
|
/***** End form *****/
|
2018-11-09 20:47:39 +01:00
|
|
|
|
Frm_EndForm ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2017-06-12 14:16:33 +02:00
|
|
|
|
/***** End box *****/
|
2019-10-25 22:48:34 +02:00
|
|
|
|
Box_BoxEnd ();
|
2016-12-26 15:17:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************* Put input field for floating answer *******************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void Tst_PutFloatInputField (const char *Label,const char *Field,
|
2020-03-19 20:57:54 +01:00
|
|
|
|
const struct Tst_Question *Question,
|
|
|
|
|
unsigned Index)
|
2016-12-26 15:17:30 +01:00
|
|
|
|
{
|
2019-02-22 21:47:50 +01:00
|
|
|
|
extern const char *The_ClassFormInBox[The_NUM_THEMES];
|
2019-11-11 10:59:24 +01:00
|
|
|
|
char StrDouble[32];
|
2016-12-26 15:17:30 +01:00
|
|
|
|
|
2019-11-02 23:40:52 +01:00
|
|
|
|
HTM_LABEL_Begin ("class=\"%s\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
|
2019-11-11 10:59:24 +01:00
|
|
|
|
HTM_TxtF ("%s ",Label);
|
2021-02-15 16:25:55 +01:00
|
|
|
|
snprintf (StrDouble,sizeof (StrDouble),"%.15lg",
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.FloatingPoint[Index]);
|
2020-04-27 03:16:55 +02:00
|
|
|
|
HTM_INPUT_TEXT (Field,Tst_MAX_BYTES_FLOAT_ANSWER,StrDouble,
|
|
|
|
|
HTM_DONT_SUBMIT_ON_CHANGE,
|
2019-11-04 01:29:46 +01:00
|
|
|
|
"size=\"11\" required=\"required\"%s",
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.Type == Tst_ANS_FLOAT ? "" :
|
|
|
|
|
" disabled=\"disabled\"");
|
2019-11-02 12:59:31 +01:00
|
|
|
|
HTM_LABEL_End ();
|
2016-12-26 16:30:46 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********************** Put input field for T/F answer **********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-03-19 20:57:54 +01:00
|
|
|
|
static void Tst_PutTFInputField (const struct Tst_Question *Question,
|
2020-03-18 01:57:08 +01:00
|
|
|
|
const char *Label,char Value)
|
2016-12-26 16:30:46 +01:00
|
|
|
|
{
|
2019-02-22 21:47:50 +01:00
|
|
|
|
extern const char *The_ClassFormInBox[The_NUM_THEMES];
|
2016-12-26 16:30:46 +01:00
|
|
|
|
|
2019-11-02 23:40:52 +01:00
|
|
|
|
HTM_LABEL_Begin ("class=\"%s\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
|
2019-11-04 18:17:39 +01:00
|
|
|
|
HTM_INPUT_RADIO ("AnsTF",false,
|
|
|
|
|
"value=\"%c\"%s%s required=\"required\"",
|
|
|
|
|
Value,
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.TF == Value ? " checked=\"checked\"" :
|
|
|
|
|
"",
|
|
|
|
|
Question->Answer.Type == Tst_ANS_TRUE_FALSE ? "" :
|
|
|
|
|
" disabled=\"disabled\"");
|
2019-11-10 12:36:37 +01:00
|
|
|
|
HTM_Txt (Label);
|
2019-11-02 12:59:31 +01:00
|
|
|
|
HTM_LABEL_End ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************* Initialize a new question to zero *********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-03-19 20:57:54 +01:00
|
|
|
|
void Tst_QstConstructor (struct Tst_Question *Question)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
unsigned NumOpt;
|
|
|
|
|
|
2020-04-04 19:20:50 +02:00
|
|
|
|
/***** Reset question tags *****/
|
2020-05-17 17:11:04 +02:00
|
|
|
|
Tag_ResetTags (&Question->Tags);
|
2020-03-25 01:36:22 +01:00
|
|
|
|
|
2020-04-04 19:20:50 +02:00
|
|
|
|
/***** Reset edition time *****/
|
2020-04-03 19:13:00 +02:00
|
|
|
|
Question->EditTime = (time_t) 0;
|
|
|
|
|
|
2020-04-04 19:20:50 +02:00
|
|
|
|
/***** Allocate memory for stem and feedback *****/
|
2021-02-15 16:25:55 +01:00
|
|
|
|
if ((Question->Stem = malloc (Cns_MAX_BYTES_TEXT + 1)) == NULL)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_NotEnoughMemoryExit ();
|
2020-04-04 19:20:50 +02:00
|
|
|
|
Question->Stem[0] = '\0';
|
|
|
|
|
|
2021-02-15 16:25:55 +01:00
|
|
|
|
if ((Question->Feedback = malloc (Cns_MAX_BYTES_TEXT + 1)) == NULL)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_NotEnoughMemoryExit ();
|
2020-04-04 19:20:50 +02:00
|
|
|
|
Question->Feedback[0] = '\0';
|
2020-03-17 14:47:58 +01:00
|
|
|
|
|
2020-04-03 19:13:00 +02:00
|
|
|
|
/***** Initialize answers *****/
|
|
|
|
|
Question->Answer.Type = Tst_ANS_UNIQUE_CHOICE;
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.NumOptions = 0;
|
2020-04-03 19:13:00 +02:00
|
|
|
|
Question->Answer.Shuffle = false;
|
|
|
|
|
Question->Answer.TF = ' ';
|
2016-04-08 23:30:43 +02:00
|
|
|
|
|
2020-04-03 19:13:00 +02:00
|
|
|
|
/* Initialize image attached to stem */
|
2020-03-17 14:47:58 +01:00
|
|
|
|
Med_MediaConstructor (&Question->Media);
|
2016-04-08 23:30:43 +02:00
|
|
|
|
|
2020-04-03 19:13:00 +02:00
|
|
|
|
/* Initialize options */
|
2014-12-01 23:55:08 +01:00
|
|
|
|
for (NumOpt = 0;
|
|
|
|
|
NumOpt < Tst_MAX_OPTIONS_PER_QUESTION;
|
|
|
|
|
NumOpt++)
|
|
|
|
|
{
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.Options[NumOpt].Correct = false;
|
|
|
|
|
Question->Answer.Options[NumOpt].Text = NULL;
|
|
|
|
|
Question->Answer.Options[NumOpt].Feedback = NULL;
|
2016-04-08 23:30:43 +02:00
|
|
|
|
|
2020-04-03 19:13:00 +02:00
|
|
|
|
/* Initialize image attached to option */
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Med_MediaConstructor (&Question->Answer.Options[NumOpt].Media);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.Integer = 0;
|
|
|
|
|
Question->Answer.FloatingPoint[0] =
|
|
|
|
|
Question->Answer.FloatingPoint[1] = 0.0;
|
2020-04-04 02:07:54 +02:00
|
|
|
|
|
2020-04-04 19:20:50 +02:00
|
|
|
|
/***** Initialize stats *****/
|
2020-04-04 02:07:54 +02:00
|
|
|
|
Question->NumHits =
|
|
|
|
|
Question->NumHitsNotBlank = 0;
|
|
|
|
|
Question->Score = 0.0;
|
2020-06-17 20:20:16 +02:00
|
|
|
|
|
|
|
|
|
/***** Mark question as valid *****/
|
|
|
|
|
Question->Validity = Tst_VALID_QUESTION;
|
2016-04-04 21:51:21 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-04-06 19:26:09 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************** Free memory allocated for test question *******************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-03-19 20:57:54 +01:00
|
|
|
|
void Tst_QstDestructor (struct Tst_Question *Question)
|
2016-04-06 19:26:09 +02:00
|
|
|
|
{
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Tst_FreeTextChoiceAnswers (Question);
|
|
|
|
|
Tst_FreeMediaOfQuestion (Question);
|
2020-04-04 19:20:50 +02:00
|
|
|
|
if (Question->Feedback)
|
|
|
|
|
{
|
|
|
|
|
free (Question->Feedback);
|
|
|
|
|
Question->Feedback = NULL;
|
|
|
|
|
}
|
|
|
|
|
if (Question->Stem)
|
|
|
|
|
{
|
|
|
|
|
free (Question->Stem);
|
|
|
|
|
Question->Stem = NULL;
|
|
|
|
|
}
|
2016-04-06 19:26:09 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************* Allocate memory for a choice answer *********************/
|
|
|
|
|
/*****************************************************************************/
|
2020-03-25 01:36:22 +01:00
|
|
|
|
// Return false on error
|
2016-04-06 19:26:09 +02:00
|
|
|
|
|
2020-03-25 01:36:22 +01:00
|
|
|
|
bool Tst_AllocateTextChoiceAnswer (struct Tst_Question *Question,unsigned NumOpt)
|
2016-04-06 19:26:09 +02:00
|
|
|
|
{
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if ((Question->Answer.Options[NumOpt].Text =
|
2021-02-15 16:25:55 +01:00
|
|
|
|
malloc (Tst_MAX_BYTES_ANSWER_OR_FEEDBACK + 1)) == NULL)
|
2016-04-06 19:26:09 +02:00
|
|
|
|
{
|
2019-03-09 20:12:44 +01:00
|
|
|
|
Ale_CreateAlert (Ale_ERROR,NULL,
|
|
|
|
|
"Not enough memory to store answer.");
|
2020-03-25 01:36:22 +01:00
|
|
|
|
return false;
|
2016-04-06 19:26:09 +02:00
|
|
|
|
}
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if ((Question->Answer.Options[NumOpt].Feedback =
|
2021-02-15 16:25:55 +01:00
|
|
|
|
malloc (Tst_MAX_BYTES_ANSWER_OR_FEEDBACK + 1)) == NULL)
|
2016-04-06 19:26:09 +02:00
|
|
|
|
{
|
2019-03-09 20:12:44 +01:00
|
|
|
|
Ale_CreateAlert (Ale_ERROR,NULL,
|
|
|
|
|
"Not enough memory to store feedback.");
|
2020-03-25 01:36:22 +01:00
|
|
|
|
return false;
|
2016-04-06 19:26:09 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.Options[NumOpt].Text[0] =
|
|
|
|
|
Question->Answer.Options[NumOpt].Feedback[0] = '\0';
|
2020-03-25 01:36:22 +01:00
|
|
|
|
return true;
|
2016-04-06 19:26:09 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************** Free memory of all choice answers **********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-03-19 20:57:54 +01:00
|
|
|
|
static void Tst_FreeTextChoiceAnswers (struct Tst_Question *Question)
|
2016-04-06 19:26:09 +02:00
|
|
|
|
{
|
|
|
|
|
unsigned NumOpt;
|
|
|
|
|
|
|
|
|
|
for (NumOpt = 0;
|
|
|
|
|
NumOpt < Tst_MAX_OPTIONS_PER_QUESTION;
|
|
|
|
|
NumOpt++)
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Tst_FreeTextChoiceAnswer (Question,NumOpt);
|
2016-04-06 19:26:09 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************** Free memory of a choice answer ***********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-03-19 20:57:54 +01:00
|
|
|
|
static void Tst_FreeTextChoiceAnswer (struct Tst_Question *Question,unsigned NumOpt)
|
2016-04-06 19:26:09 +02:00
|
|
|
|
{
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (Question->Answer.Options[NumOpt].Text)
|
2016-04-06 19:26:09 +02:00
|
|
|
|
{
|
2020-03-19 20:57:54 +01:00
|
|
|
|
free (Question->Answer.Options[NumOpt].Text);
|
|
|
|
|
Question->Answer.Options[NumOpt].Text = NULL;
|
2016-04-06 19:26:09 +02:00
|
|
|
|
}
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (Question->Answer.Options[NumOpt].Feedback)
|
2016-04-06 19:26:09 +02:00
|
|
|
|
{
|
2020-03-19 20:57:54 +01:00
|
|
|
|
free (Question->Answer.Options[NumOpt].Feedback);
|
|
|
|
|
Question->Answer.Options[NumOpt].Feedback = NULL;
|
2016-04-06 19:26:09 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-04 21:51:21 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************** Initialize images of a question to zero *******************/
|
|
|
|
|
/*****************************************************************************/
|
2016-04-09 02:04:45 +02:00
|
|
|
|
|
2020-03-19 20:57:54 +01:00
|
|
|
|
static void Tst_ResetMediaOfQuestion (struct Tst_Question *Question)
|
2016-04-04 21:51:21 +02:00
|
|
|
|
{
|
|
|
|
|
unsigned NumOpt;
|
|
|
|
|
|
2019-03-17 14:47:58 +01:00
|
|
|
|
/***** Reset media for stem *****/
|
2020-03-17 14:47:58 +01:00
|
|
|
|
Med_ResetMedia (&Question->Media);
|
2016-04-08 23:30:43 +02:00
|
|
|
|
|
2019-03-17 14:47:58 +01:00
|
|
|
|
/***** Reset media for every answer option *****/
|
2016-04-04 21:51:21 +02:00
|
|
|
|
for (NumOpt = 0;
|
|
|
|
|
NumOpt < Tst_MAX_OPTIONS_PER_QUESTION;
|
|
|
|
|
NumOpt++)
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Med_ResetMedia (&Question->Answer.Options[NumOpt].Media);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
2016-04-09 02:04:45 +02:00
|
|
|
|
|
2016-04-06 01:10:04 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********************** Free images of a question ***************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-03-19 20:57:54 +01:00
|
|
|
|
static void Tst_FreeMediaOfQuestion (struct Tst_Question *Question)
|
2016-04-06 01:10:04 +02:00
|
|
|
|
{
|
|
|
|
|
unsigned NumOpt;
|
|
|
|
|
|
2020-03-17 14:47:58 +01:00
|
|
|
|
Med_MediaDestructor (&Question->Media);
|
2016-04-06 01:10:04 +02:00
|
|
|
|
for (NumOpt = 0;
|
|
|
|
|
NumOpt < Tst_MAX_OPTIONS_PER_QUESTION;
|
|
|
|
|
NumOpt++)
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Med_MediaDestructor (&Question->Answer.Options[NumOpt].Media);
|
2016-04-06 01:10:04 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*************** Get answer type of a question from database *****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-05-13 00:28:32 +02:00
|
|
|
|
Tst_AnswerType_t Tst_GetQstAnswerTypeFromDB (long QstCod)
|
2020-04-01 03:11:05 +02:00
|
|
|
|
{
|
2021-04-16 13:51:12 +02:00
|
|
|
|
char StrAnsTypeDB[256];
|
2020-04-01 03:11:05 +02:00
|
|
|
|
|
|
|
|
|
/***** Get type of answer from database *****/
|
2021-04-16 13:51:12 +02:00
|
|
|
|
DB_QuerySELECTString (StrAnsTypeDB,sizeof (StrAnsTypeDB) - 1,
|
|
|
|
|
"can not get the type of a question",
|
|
|
|
|
"SELECT AnsType"
|
|
|
|
|
" FROM tst_questions"
|
|
|
|
|
" WHERE QstCod=%ld",
|
|
|
|
|
QstCod);
|
|
|
|
|
return Tst_ConvertFromStrAnsTypDBToAnsTyp (StrAnsTypeDB);
|
2020-04-01 03:11:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-04-03 01:24:20 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/****************** Get data of a question from database *********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-04 19:20:50 +02:00
|
|
|
|
bool Tst_GetQstDataFromDB (struct Tst_Question *Question)
|
2016-04-03 01:24:20 +02:00
|
|
|
|
{
|
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
MYSQL_ROW row;
|
2020-04-03 19:13:00 +02:00
|
|
|
|
bool QuestionExists;
|
2021-04-16 13:51:12 +02:00
|
|
|
|
unsigned NumTags;
|
|
|
|
|
unsigned NumTag;
|
2016-04-03 01:24:20 +02:00
|
|
|
|
unsigned NumOpt;
|
|
|
|
|
|
2020-04-01 03:11:05 +02:00
|
|
|
|
/***** Get question data from database *****/
|
2020-04-03 19:13:00 +02:00
|
|
|
|
QuestionExists = (DB_QuerySELECT (&mysql_res,"can not get a question",
|
|
|
|
|
"SELECT UNIX_TIMESTAMP(EditTime)," // row[0]
|
|
|
|
|
"AnsType," // row[1]
|
|
|
|
|
"Shuffle," // row[2]
|
|
|
|
|
"Stem," // row[3]
|
|
|
|
|
"Feedback," // row[4]
|
2020-04-04 02:07:54 +02:00
|
|
|
|
"MedCod," // row[5]
|
|
|
|
|
"NumHits," // row[6]
|
|
|
|
|
"NumHitsNotBlank," // row[7]
|
|
|
|
|
"Score" // row[8]
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM tst_questions"
|
2020-04-03 19:13:00 +02:00
|
|
|
|
" WHERE QstCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND CrsCod=%ld", // Extra check
|
2020-04-03 19:13:00 +02:00
|
|
|
|
Question->QstCod,
|
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod) != 0);
|
|
|
|
|
|
|
|
|
|
if (QuestionExists)
|
2016-04-03 01:24:20 +02:00
|
|
|
|
{
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
|
|
|
|
|
2020-04-03 19:13:00 +02:00
|
|
|
|
/* Get edition time (row[0] holds the start UTC time) */
|
2020-04-14 02:37:24 +02:00
|
|
|
|
Question->EditTime = Dat_GetUNIXTimeFromStr (row[0]);
|
2016-04-03 01:24:20 +02:00
|
|
|
|
|
2020-04-03 19:13:00 +02:00
|
|
|
|
/* Get the type of answer (row[1]) */
|
|
|
|
|
Question->Answer.Type = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[1]);
|
2016-04-03 01:24:20 +02:00
|
|
|
|
|
2020-04-03 19:13:00 +02:00
|
|
|
|
/* Get shuffle (row[2]) */
|
|
|
|
|
Question->Answer.Shuffle = (row[2][0] == 'Y');
|
|
|
|
|
|
2021-02-15 22:49:44 +01:00
|
|
|
|
/* Get the stem (row[3]) and the feedback (row[4]) */
|
|
|
|
|
Question->Stem [0] = '\0';
|
2020-04-03 19:13:00 +02:00
|
|
|
|
if (row[3])
|
|
|
|
|
if (row[3][0])
|
2021-02-15 22:49:44 +01:00
|
|
|
|
Str_Copy (Question->Stem ,row[3],Cns_MAX_BYTES_TEXT);
|
2020-04-04 19:20:50 +02:00
|
|
|
|
Question->Feedback[0] = '\0';
|
2020-04-03 19:13:00 +02:00
|
|
|
|
if (row[4])
|
|
|
|
|
if (row[4][0])
|
2021-02-15 16:25:55 +01:00
|
|
|
|
Str_Copy (Question->Feedback,row[4],Cns_MAX_BYTES_TEXT);
|
2020-04-03 19:13:00 +02:00
|
|
|
|
|
|
|
|
|
/* Get media (row[5]) */
|
|
|
|
|
Question->Media.MedCod = Str_ConvertStrCodToLongCod (row[5]);
|
|
|
|
|
Med_GetMediaDataByCod (&Question->Media);
|
2016-04-03 01:24:20 +02:00
|
|
|
|
|
2020-04-04 02:07:54 +02:00
|
|
|
|
/* Get number of hits
|
|
|
|
|
(number of times that the question has been answered,
|
|
|
|
|
including blank answers) (row[6]) */
|
|
|
|
|
if (sscanf (row[6],"%lu",&Question->NumHits) != 1)
|
|
|
|
|
Question->NumHits = 0;
|
|
|
|
|
|
|
|
|
|
/* Get number of hits not blank
|
|
|
|
|
(number of times that the question has been answered
|
|
|
|
|
with a not blank answer) (row[7]) */
|
|
|
|
|
if (sscanf (row[7],"%lu",&Question->NumHitsNotBlank) != 1)
|
|
|
|
|
Question->NumHitsNotBlank = 0;
|
|
|
|
|
|
|
|
|
|
/* Get the acumulated score of the question (row[8]) */
|
|
|
|
|
Str_SetDecimalPointToUS (); // To get the decimal point as a dot
|
|
|
|
|
if (sscanf (row[8],"%lf",&Question->Score) != 1)
|
|
|
|
|
Question->Score = 0.0;
|
|
|
|
|
Str_SetDecimalPointToLocal (); // Return to local system
|
|
|
|
|
|
2020-04-03 19:13:00 +02:00
|
|
|
|
/* Free structure that stores the query result */
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
2016-04-03 01:24:20 +02:00
|
|
|
|
|
2020-04-03 19:13:00 +02:00
|
|
|
|
/***** Get the tags from the database *****/
|
2021-04-16 13:51:12 +02:00
|
|
|
|
NumTags = Tst_GetTagsQst (Question->QstCod,&mysql_res);
|
|
|
|
|
for (NumTag = 0;
|
|
|
|
|
NumTag < NumTags;
|
|
|
|
|
NumTag++)
|
2020-04-03 19:13:00 +02:00
|
|
|
|
{
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
2021-04-16 13:51:12 +02:00
|
|
|
|
Str_Copy (Question->Tags.Txt[NumTag],row[0],
|
|
|
|
|
sizeof (Question->Tags.Txt[NumTag]) - 1);
|
2020-04-03 19:13:00 +02:00
|
|
|
|
}
|
2016-04-04 21:51:21 +02:00
|
|
|
|
|
2020-04-03 19:13:00 +02:00
|
|
|
|
/* Free structure that stores the query result */
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
|
|
|
|
|
/***** Get the answers from the database *****/
|
|
|
|
|
Tst_GetAnswersQst (Question,&mysql_res,
|
|
|
|
|
false); // Don't shuffle
|
|
|
|
|
/*
|
|
|
|
|
row[0] AnsInd
|
|
|
|
|
row[1] Answer
|
|
|
|
|
row[2] Feedback
|
|
|
|
|
row[3] MedCod
|
|
|
|
|
row[4] Correct
|
|
|
|
|
*/
|
|
|
|
|
for (NumOpt = 0;
|
|
|
|
|
NumOpt < Question->Answer.NumOptions;
|
|
|
|
|
NumOpt++)
|
|
|
|
|
{
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
|
|
|
|
switch (Question->Answer.Type)
|
|
|
|
|
{
|
|
|
|
|
case Tst_ANS_INT:
|
|
|
|
|
Tst_CheckIfNumberOfAnswersIsOne (Question);
|
|
|
|
|
Question->Answer.Integer = Tst_GetIntAnsFromStr (row[1]);
|
|
|
|
|
break;
|
|
|
|
|
case Tst_ANS_FLOAT:
|
|
|
|
|
if (Question->Answer.NumOptions != 2)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_WrongAnswerExit ();
|
2020-04-03 19:13:00 +02:00
|
|
|
|
Question->Answer.FloatingPoint[NumOpt] = Str_GetDoubleFromStr (row[1]);
|
|
|
|
|
break;
|
|
|
|
|
case Tst_ANS_TRUE_FALSE:
|
|
|
|
|
Tst_CheckIfNumberOfAnswersIsOne (Question);
|
|
|
|
|
Question->Answer.TF = row[1][0];
|
|
|
|
|
break;
|
|
|
|
|
case Tst_ANS_UNIQUE_CHOICE:
|
|
|
|
|
case Tst_ANS_MULTIPLE_CHOICE:
|
|
|
|
|
case Tst_ANS_TEXT:
|
|
|
|
|
/* Check number of options */
|
|
|
|
|
if (Question->Answer.NumOptions > Tst_MAX_OPTIONS_PER_QUESTION)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_WrongAnswerExit ();
|
2020-04-03 19:13:00 +02:00
|
|
|
|
|
|
|
|
|
/* Allocate space for text and feedback */
|
|
|
|
|
if (!Tst_AllocateTextChoiceAnswer (Question,NumOpt))
|
|
|
|
|
/* Abort on error */
|
|
|
|
|
Ale_ShowAlertsAndExit ();
|
|
|
|
|
|
2021-02-15 22:49:44 +01:00
|
|
|
|
/* Get text (row[1]) and feedback (row[2])*/
|
|
|
|
|
Question->Answer.Options[NumOpt].Text [0] = '\0';
|
2020-04-03 19:13:00 +02:00
|
|
|
|
if (row[1])
|
|
|
|
|
if (row[1][0])
|
2021-02-15 22:49:44 +01:00
|
|
|
|
Str_Copy (Question->Answer.Options[NumOpt].Text ,row[1],
|
2020-04-03 19:13:00 +02:00
|
|
|
|
Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
|
|
|
|
|
Question->Answer.Options[NumOpt].Feedback[0] = '\0';
|
|
|
|
|
if (row[2])
|
|
|
|
|
if (row[2][0])
|
|
|
|
|
Str_Copy (Question->Answer.Options[NumOpt].Feedback,row[2],
|
|
|
|
|
Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
|
|
|
|
|
|
|
|
|
|
/* Get media (row[3]) */
|
|
|
|
|
Question->Answer.Options[NumOpt].Media.MedCod = Str_ConvertStrCodToLongCod (row[3]);
|
|
|
|
|
Med_GetMediaDataByCod (&Question->Answer.Options[NumOpt].Media);
|
|
|
|
|
|
|
|
|
|
/* Get if this option is correct (row[4]) */
|
|
|
|
|
Question->Answer.Options[NumOpt].Correct = (row[4][0] == 'Y');
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-04-03 01:24:20 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-03 19:13:00 +02:00
|
|
|
|
|
2016-04-03 01:24:20 +02:00
|
|
|
|
/* Free structure that stores the query result */
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
2020-04-04 02:07:54 +02:00
|
|
|
|
|
|
|
|
|
return QuestionExists;
|
2016-04-03 01:24:20 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2019-03-19 11:20:29 +01:00
|
|
|
|
/******* Get media code associated with a test question from database ********/
|
2016-04-03 01:24:20 +02:00
|
|
|
|
/*****************************************************************************/
|
2019-03-19 01:41:27 +01:00
|
|
|
|
// NumOpt < 0 ==> media associated to stem
|
|
|
|
|
// NumOpt >= 0 ==> media associated to answer
|
2016-04-03 01:24:20 +02:00
|
|
|
|
|
2020-03-17 00:35:11 +01:00
|
|
|
|
static long Tst_GetMedCodFromDB (long CrsCod,long QstCod,int NumOpt)
|
2016-04-03 01:24:20 +02:00
|
|
|
|
{
|
2021-04-05 23:45:24 +02:00
|
|
|
|
/***** Trivial check: question code should be > 0 *****/
|
|
|
|
|
if (QstCod <= 0)
|
|
|
|
|
return -1L;
|
|
|
|
|
|
|
|
|
|
/***** Query depending on NumOpt *****/
|
|
|
|
|
if (NumOpt < 0)
|
|
|
|
|
// Get media associated to stem
|
|
|
|
|
return DB_QuerySELECTCode ("can not get media",
|
|
|
|
|
"SELECT MedCod"
|
|
|
|
|
" FROM tst_questions"
|
|
|
|
|
" WHERE QstCod=%ld"
|
|
|
|
|
" AND CrsCod=%ld",
|
|
|
|
|
QstCod,CrsCod);
|
|
|
|
|
else
|
|
|
|
|
// Get media associated to answer
|
|
|
|
|
return DB_QuerySELECTCode ("can not get media",
|
|
|
|
|
"SELECT MedCod"
|
|
|
|
|
" FROM tst_answers"
|
|
|
|
|
" WHERE QstCod=%ld"
|
|
|
|
|
" AND AnsInd=%u",
|
|
|
|
|
QstCod,(unsigned) NumOpt);
|
2019-03-19 11:20:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***** Get possible media associated with a test question from database ******/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
// NumOpt < 0 ==> media associated to stem
|
|
|
|
|
// NumOpt >= 0 ==> media associated to an answer option
|
|
|
|
|
|
2020-03-17 00:35:11 +01:00
|
|
|
|
static void Tst_GetMediaFromDB (long CrsCod,long QstCod,int NumOpt,
|
2021-02-11 00:58:53 +01:00
|
|
|
|
struct Med_Media *Media)
|
2019-03-19 11:20:29 +01:00
|
|
|
|
{
|
|
|
|
|
/***** Get media *****/
|
2020-03-17 00:35:11 +01:00
|
|
|
|
Media->MedCod = Tst_GetMedCodFromDB (CrsCod,QstCod,NumOpt);
|
2019-03-19 11:20:29 +01:00
|
|
|
|
Med_GetMediaDataByCod (Media);
|
2016-04-03 01:24:20 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/** Convert a string with the type of answer in database to type of answer ***/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2021-04-16 13:51:12 +02:00
|
|
|
|
Tst_AnswerType_t Tst_ConvertFromStrAnsTypDBToAnsTyp (const char *StrAnsTypeDB)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
Tst_AnswerType_t AnsType;
|
|
|
|
|
|
2021-04-16 13:51:12 +02:00
|
|
|
|
if (StrAnsTypeDB != NULL)
|
|
|
|
|
if (StrAnsTypeDB[0])
|
|
|
|
|
for (AnsType = (Tst_AnswerType_t) 0;
|
|
|
|
|
AnsType <= (Tst_AnswerType_t) (Tst_NUM_ANS_TYPES - 1);
|
|
|
|
|
AnsType++)
|
|
|
|
|
if (!strcmp (StrAnsTypeDB,Tst_StrAnswerTypesDB[AnsType]))
|
|
|
|
|
return AnsType;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-05-11 14:56:49 +02:00
|
|
|
|
return Tst_ANS_UNKNOWN;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************ Convert a string with an unsigned to answer type ***************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static Tst_AnswerType_t Tst_ConvertFromUnsignedStrToAnsTyp (const char *UnsignedStr)
|
|
|
|
|
{
|
|
|
|
|
unsigned AnsType;
|
|
|
|
|
|
|
|
|
|
if (sscanf (UnsignedStr,"%u",&AnsType) != 1)
|
2021-09-25 22:49:05 +02:00
|
|
|
|
Err_WrongAnswerExit ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
if (AnsType >= Tst_NUM_ANS_TYPES)
|
2021-09-25 22:49:05 +02:00
|
|
|
|
Err_WrongAnswerExit ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
return (Tst_AnswerType_t) AnsType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*************** Receive a question of the self-assessment test **************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void Tst_ReceiveQst (void)
|
|
|
|
|
{
|
2020-03-24 00:58:42 +01:00
|
|
|
|
struct Tst_Test Test;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-26 21:39:44 +01:00
|
|
|
|
/***** Create test *****/
|
|
|
|
|
Tst_TstConstructor (&Test);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/***** Get parameters of the question from form *****/
|
2020-04-04 19:20:50 +02:00
|
|
|
|
Tst_GetQstFromForm (&Test.Question);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/***** Make sure that tags, text and answer are not empty *****/
|
2020-03-26 21:39:44 +01:00
|
|
|
|
if (Tst_CheckIfQstFormatIsCorrectAndCountNumOptions (&Test.Question))
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2016-04-04 21:51:21 +02:00
|
|
|
|
/***** Move images to definitive directories *****/
|
2020-03-26 21:39:44 +01:00
|
|
|
|
Tst_MoveMediaToDefinitiveDirectories (&Test.Question);
|
2016-04-04 01:02:41 +02:00
|
|
|
|
|
|
|
|
|
/***** Insert or update question, tags and answer in the database *****/
|
2020-03-26 21:39:44 +01:00
|
|
|
|
Tst_InsertOrUpdateQstTagsAnsIntoDB (&Test.Question);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/***** Show the question just inserted in the database *****/
|
2021-02-15 16:25:55 +01:00
|
|
|
|
snprintf (Test.AnswerTypes.List,sizeof (Test.AnswerTypes.List),"%u",
|
2020-03-26 21:39:44 +01:00
|
|
|
|
(unsigned) Test.Question.Answer.Type);
|
|
|
|
|
Tst_ListOneQstToEdit (&Test);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
else // Question is wrong
|
2016-04-04 03:00:12 +02:00
|
|
|
|
{
|
2016-04-04 21:51:21 +02:00
|
|
|
|
/***** Whether images has been received or not, reset images *****/
|
2020-03-26 21:39:44 +01:00
|
|
|
|
Tst_ResetMediaOfQuestion (&Test.Question);
|
2016-04-04 03:00:12 +02:00
|
|
|
|
|
2016-04-03 01:24:20 +02:00
|
|
|
|
/***** Put form to edit question again *****/
|
2020-04-04 19:20:50 +02:00
|
|
|
|
Tst_PutFormEditOneQst (&Test.Question);
|
2016-04-04 03:00:12 +02:00
|
|
|
|
}
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-26 21:39:44 +01:00
|
|
|
|
/***** Destroy test *****/
|
|
|
|
|
Tst_TstDestructor (&Test);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/**************** Get parameters of a test question from form ****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-04 19:20:50 +02:00
|
|
|
|
static void Tst_GetQstFromForm (struct Tst_Question *Question)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
unsigned NumTag;
|
|
|
|
|
unsigned NumTagRead;
|
|
|
|
|
unsigned NumOpt;
|
2019-11-08 01:10:32 +01:00
|
|
|
|
char UnsignedStr[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
|
|
|
|
|
char TagStr[6 + Cns_MAX_DECIMAL_DIGITS_UINT + 1];
|
|
|
|
|
char AnsStr[6 + Cns_MAX_DECIMAL_DIGITS_UINT + 1];
|
|
|
|
|
char FbStr[5 + Cns_MAX_DECIMAL_DIGITS_UINT + 1];
|
2020-05-09 01:37:00 +02:00
|
|
|
|
char StrMultiAns[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
|
2017-02-24 19:04:52 +01:00
|
|
|
|
char TF[1 + 1]; // (T)rue or (F)alse
|
2014-12-01 23:55:08 +01:00
|
|
|
|
const char *Ptr;
|
|
|
|
|
unsigned NumCorrectAns;
|
|
|
|
|
|
|
|
|
|
/***** Get question code *****/
|
2020-04-27 19:37:49 +02:00
|
|
|
|
Question->QstCod = Tst_GetParamQstCod ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/***** Get answer type *****/
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.Type = (Tst_AnswerType_t)
|
|
|
|
|
Par_GetParToUnsignedLong ("AnswerType",
|
|
|
|
|
0,
|
|
|
|
|
Tst_NUM_ANS_TYPES - 1,
|
2020-05-11 14:56:49 +02:00
|
|
|
|
(unsigned long) Tst_ANS_UNKNOWN);
|
|
|
|
|
if (Question->Answer.Type == Tst_ANS_UNKNOWN)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_WrongAnswerExit ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/***** Get question tags *****/
|
|
|
|
|
for (NumTag = 0;
|
2020-05-17 17:11:04 +02:00
|
|
|
|
NumTag < Tag_MAX_TAGS_PER_QUESTION;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
NumTag++)
|
|
|
|
|
{
|
2021-02-15 16:25:55 +01:00
|
|
|
|
snprintf (TagStr,sizeof (TagStr),"TagTxt%u",NumTag);
|
2020-05-17 17:11:04 +02:00
|
|
|
|
Par_GetParToText (TagStr,Question->Tags.Txt[NumTag],Tag_MAX_BYTES_TAG);
|
2016-03-30 01:28:58 +02:00
|
|
|
|
|
2020-03-25 01:36:22 +01:00
|
|
|
|
if (Question->Tags.Txt[NumTag][0])
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
Str_ChangeFormat (Str_FROM_FORM,Str_TO_TEXT,
|
2020-05-17 17:11:04 +02:00
|
|
|
|
Question->Tags.Txt[NumTag],Tag_MAX_BYTES_TAG,true);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/* Check if not repeated */
|
|
|
|
|
for (NumTagRead = 0;
|
|
|
|
|
NumTagRead < NumTag;
|
|
|
|
|
NumTagRead++)
|
2020-03-25 01:36:22 +01:00
|
|
|
|
if (!strcmp (Question->Tags.Txt[NumTagRead],Question->Tags.Txt[NumTag]))
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2020-03-25 01:36:22 +01:00
|
|
|
|
Question->Tags.Txt[NumTag][0] = '\0';
|
2014-12-01 23:55:08 +01:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** Get question stem *****/
|
2020-04-04 19:20:50 +02:00
|
|
|
|
Par_GetParToHTML ("Stem",Question->Stem,Cns_MAX_BYTES_TEXT);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2016-04-06 01:10:04 +02:00
|
|
|
|
/***** Get question feedback *****/
|
2020-04-04 19:20:50 +02:00
|
|
|
|
Par_GetParToHTML ("Feedback",Question->Feedback,Cns_MAX_BYTES_TEXT);
|
2016-04-06 01:10:04 +02:00
|
|
|
|
|
2019-03-19 01:41:27 +01:00
|
|
|
|
/***** Get media associated to the stem (action, file and title) *****/
|
2020-03-17 14:47:58 +01:00
|
|
|
|
Question->Media.Width = Tst_IMAGE_SAVED_MAX_WIDTH;
|
|
|
|
|
Question->Media.Height = Tst_IMAGE_SAVED_MAX_HEIGHT;
|
|
|
|
|
Question->Media.Quality = Tst_IMAGE_SAVED_QUALITY;
|
2020-03-25 01:36:22 +01:00
|
|
|
|
Med_GetMediaFromForm (Gbl.Hierarchy.Crs.CrsCod,Question->QstCod,
|
2020-03-17 00:35:11 +01:00
|
|
|
|
-1, // < 0 ==> the image associated to the stem
|
2020-03-17 14:47:58 +01:00
|
|
|
|
&Question->Media,
|
2020-03-17 00:35:11 +01:00
|
|
|
|
Tst_GetMediaFromDB,
|
2019-03-17 14:47:58 +01:00
|
|
|
|
NULL);
|
|
|
|
|
Ale_ShowAlerts (NULL);
|
2016-04-04 12:13:37 +02:00
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/***** Get answers *****/
|
2020-04-03 19:13:00 +02:00
|
|
|
|
Question->Answer.Shuffle = false;
|
2020-03-19 20:57:54 +01:00
|
|
|
|
switch (Question->Answer.Type)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
case Tst_ANS_INT:
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (!Tst_AllocateTextChoiceAnswer (Question,0))
|
2019-03-09 20:12:44 +01:00
|
|
|
|
/* Abort on error */
|
|
|
|
|
Ale_ShowAlertsAndExit ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Par_GetParToText ("AnsInt",Question->Answer.Options[0].Text,
|
2019-11-08 01:10:32 +01:00
|
|
|
|
Cns_MAX_DECIMAL_DIGITS_LONG);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
break;
|
|
|
|
|
case Tst_ANS_FLOAT:
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (!Tst_AllocateTextChoiceAnswer (Question,0))
|
2019-03-09 20:12:44 +01:00
|
|
|
|
/* Abort on error */
|
|
|
|
|
Ale_ShowAlertsAndExit ();
|
|
|
|
|
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Par_GetParToText ("AnsFloatMin",Question->Answer.Options[0].Text,
|
2017-01-29 21:41:08 +01:00
|
|
|
|
Tst_MAX_BYTES_FLOAT_ANSWER);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (!Tst_AllocateTextChoiceAnswer (Question,1))
|
2019-03-09 20:12:44 +01:00
|
|
|
|
/* Abort on error */
|
|
|
|
|
Ale_ShowAlertsAndExit ();
|
|
|
|
|
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Par_GetParToText ("AnsFloatMax",Question->Answer.Options[1].Text,
|
2017-01-29 21:41:08 +01:00
|
|
|
|
Tst_MAX_BYTES_FLOAT_ANSWER);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
break;
|
|
|
|
|
case Tst_ANS_TRUE_FALSE:
|
2017-02-24 19:04:52 +01:00
|
|
|
|
Par_GetParToText ("AnsTF",TF,1);
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.TF = TF[0];
|
2014-12-01 23:55:08 +01:00
|
|
|
|
break;
|
|
|
|
|
case Tst_ANS_UNIQUE_CHOICE:
|
|
|
|
|
case Tst_ANS_MULTIPLE_CHOICE:
|
|
|
|
|
/* Get shuffle */
|
2020-04-03 19:13:00 +02:00
|
|
|
|
Question->Answer.Shuffle = Par_GetParToBool ("Shuffle");
|
2018-10-04 21:57:25 +02:00
|
|
|
|
/* falls through */
|
|
|
|
|
/* no break */
|
2014-12-01 23:55:08 +01:00
|
|
|
|
case Tst_ANS_TEXT:
|
|
|
|
|
/* Get the texts of the answers */
|
|
|
|
|
for (NumOpt = 0;
|
|
|
|
|
NumOpt < Tst_MAX_OPTIONS_PER_QUESTION;
|
|
|
|
|
NumOpt++)
|
|
|
|
|
{
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (!Tst_AllocateTextChoiceAnswer (Question,NumOpt))
|
2019-03-09 20:12:44 +01:00
|
|
|
|
/* Abort on error */
|
|
|
|
|
Ale_ShowAlertsAndExit ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/* Get answer */
|
2021-02-15 16:25:55 +01:00
|
|
|
|
snprintf (AnsStr,sizeof (AnsStr),"AnsStr%u",NumOpt);
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Par_GetParToHTML (AnsStr,Question->Answer.Options[NumOpt].Text,
|
2017-03-08 03:48:23 +01:00
|
|
|
|
Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (Question->Answer.Type == Tst_ANS_TEXT)
|
2016-11-27 14:34:36 +01:00
|
|
|
|
/* In order to compare student answer to stored answer,
|
|
|
|
|
the text answers are stored avoiding two or more consecurive spaces */
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Str_ReplaceSeveralSpacesForOne (Question->Answer.Options[NumOpt].Text);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/* Get feedback */
|
2021-02-15 16:25:55 +01:00
|
|
|
|
snprintf (FbStr,sizeof (FbStr),"FbStr%u",NumOpt);
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Par_GetParToHTML (FbStr,Question->Answer.Options[NumOpt].Feedback,
|
2017-03-08 03:48:23 +01:00
|
|
|
|
Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
|
2016-04-04 14:48:12 +02:00
|
|
|
|
|
2019-03-19 01:41:27 +01:00
|
|
|
|
/* Get media associated to the answer (action, file and title) */
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (Question->Answer.Type == Tst_ANS_UNIQUE_CHOICE ||
|
|
|
|
|
Question->Answer.Type == Tst_ANS_MULTIPLE_CHOICE)
|
2016-04-04 14:48:12 +02:00
|
|
|
|
{
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.Options[NumOpt].Media.Width = Tst_IMAGE_SAVED_MAX_WIDTH;
|
|
|
|
|
Question->Answer.Options[NumOpt].Media.Height = Tst_IMAGE_SAVED_MAX_HEIGHT;
|
|
|
|
|
Question->Answer.Options[NumOpt].Media.Quality = Tst_IMAGE_SAVED_QUALITY;
|
2020-03-25 01:36:22 +01:00
|
|
|
|
Med_GetMediaFromForm (Gbl.Hierarchy.Crs.CrsCod,Question->QstCod,
|
2020-03-17 00:35:11 +01:00
|
|
|
|
(int) NumOpt, // >= 0 ==> the image associated to an answer
|
2020-03-19 20:57:54 +01:00
|
|
|
|
&Question->Answer.Options[NumOpt].Media,
|
2019-03-17 14:47:58 +01:00
|
|
|
|
Tst_GetMediaFromDB,
|
|
|
|
|
NULL);
|
|
|
|
|
Ale_ShowAlerts (NULL);
|
2016-04-04 14:48:12 +02:00
|
|
|
|
}
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get the numbers of correct answers */
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (Question->Answer.Type == Tst_ANS_UNIQUE_CHOICE)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2017-01-29 21:41:08 +01:00
|
|
|
|
NumCorrectAns = (unsigned) Par_GetParToUnsignedLong ("AnsUni",
|
|
|
|
|
0,
|
|
|
|
|
Tst_MAX_OPTIONS_PER_QUESTION - 1,
|
|
|
|
|
0);
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.Options[NumCorrectAns].Correct = true;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
2020-03-19 20:57:54 +01:00
|
|
|
|
else if (Question->Answer.Type == Tst_ANS_MULTIPLE_CHOICE)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2020-05-09 01:37:00 +02:00
|
|
|
|
Par_GetParMultiToText ("AnsMulti",StrMultiAns,Tst_MAX_BYTES_ANSWERS_ONE_QST);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
Ptr = StrMultiAns;
|
|
|
|
|
while (*Ptr)
|
|
|
|
|
{
|
2019-11-08 01:10:32 +01:00
|
|
|
|
Par_GetNextStrUntilSeparParamMult (&Ptr,UnsignedStr,Cns_MAX_DECIMAL_DIGITS_UINT);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
if (sscanf (UnsignedStr,"%u",&NumCorrectAns) != 1)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_WrongAnswerExit ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
if (NumCorrectAns >= Tst_MAX_OPTIONS_PER_QUESTION)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_WrongAnswerExit ();
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.Options[NumCorrectAns].Correct = true;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else // Tst_ANS_TEXT
|
|
|
|
|
for (NumOpt = 0;
|
|
|
|
|
NumOpt < Tst_MAX_OPTIONS_PER_QUESTION;
|
|
|
|
|
NumOpt++)
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (Question->Answer.Options[NumOpt].Text[0])
|
|
|
|
|
Question->Answer.Options[NumOpt].Correct = true; // All the answers are correct
|
2014-12-01 23:55:08 +01:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-21 15:41:25 +01:00
|
|
|
|
/***** Adjust variables related to this test question *****/
|
2020-03-25 01:36:22 +01:00
|
|
|
|
for (NumTag = 0, Question->Tags.Num = 0;
|
2020-05-17 17:11:04 +02:00
|
|
|
|
NumTag < Tag_MAX_TAGS_PER_QUESTION;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
NumTag++)
|
2020-03-25 01:36:22 +01:00
|
|
|
|
if (Question->Tags.Txt[NumTag][0])
|
|
|
|
|
Question->Tags.Num++;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********************** Check if a question is correct **********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
// Returns false if question format is wrong
|
2020-03-19 20:57:54 +01:00
|
|
|
|
// Counts Question->Answer.NumOptions
|
|
|
|
|
// Computes Question->Answer.Integer and Question->Answer.FloatingPoint[0..1]
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-25 01:36:22 +01:00
|
|
|
|
bool Tst_CheckIfQstFormatIsCorrectAndCountNumOptions (struct Tst_Question *Question)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_You_must_type_at_least_one_tag_for_the_question;
|
|
|
|
|
extern const char *Txt_You_must_type_the_stem_of_the_question;
|
|
|
|
|
extern const char *Txt_You_must_select_a_T_F_answer;
|
|
|
|
|
extern const char *Txt_You_can_not_leave_empty_intermediate_answers;
|
|
|
|
|
extern const char *Txt_You_must_type_at_least_the_first_two_answers;
|
|
|
|
|
extern const char *Txt_You_must_mark_an_answer_as_correct;
|
|
|
|
|
extern const char *Txt_You_must_type_at_least_the_first_answer;
|
|
|
|
|
extern const char *Txt_You_must_enter_an_integer_value_as_the_correct_answer;
|
|
|
|
|
extern const char *Txt_You_must_enter_the_range_of_floating_point_values_allowed_as_answer;
|
|
|
|
|
extern const char *Txt_The_lower_limit_of_correct_answers_must_be_less_than_or_equal_to_the_upper_limit;
|
|
|
|
|
unsigned NumOpt;
|
|
|
|
|
unsigned NumLastOpt;
|
|
|
|
|
bool ThereIsEndOfAnswers;
|
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
/***** This function also counts the number of options. Initialize this number to 0. *****/
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.NumOptions = 0;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/***** A question must have at least one tag *****/
|
2020-03-25 01:36:22 +01:00
|
|
|
|
if (!Question->Tags.Num) // There are no tags with text
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2019-02-16 19:29:27 +01:00
|
|
|
|
Ale_ShowAlert (Ale_WARNING,Txt_You_must_type_at_least_one_tag_for_the_question);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-04 19:20:50 +02:00
|
|
|
|
/***** A question must have a stem *****/
|
|
|
|
|
if (!Question->Stem[0])
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2019-02-16 19:29:27 +01:00
|
|
|
|
Ale_ShowAlert (Ale_WARNING,Txt_You_must_type_the_stem_of_the_question);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** Check answer *****/
|
2020-03-19 20:57:54 +01:00
|
|
|
|
switch (Question->Answer.Type)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
case Tst_ANS_INT:
|
2019-03-17 14:47:58 +01:00
|
|
|
|
/* First option should be filled */
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (!Question->Answer.Options[0].Text)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2019-02-16 19:29:27 +01:00
|
|
|
|
Ale_ShowAlert (Ale_WARNING,Txt_You_must_enter_an_integer_value_as_the_correct_answer);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (!Question->Answer.Options[0].Text[0])
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2019-02-16 19:29:27 +01:00
|
|
|
|
Ale_ShowAlert (Ale_WARNING,Txt_You_must_enter_an_integer_value_as_the_correct_answer);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2019-03-17 14:47:58 +01:00
|
|
|
|
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.Integer = Tst_GetIntAnsFromStr (Question->Answer.Options[0].Text);
|
|
|
|
|
Question->Answer.NumOptions = 1;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
break;
|
|
|
|
|
case Tst_ANS_FLOAT:
|
2019-03-17 14:47:58 +01:00
|
|
|
|
/* First two options should be filled */
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (!Question->Answer.Options[0].Text ||
|
|
|
|
|
!Question->Answer.Options[1].Text)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2019-02-16 19:29:27 +01:00
|
|
|
|
Ale_ShowAlert (Ale_WARNING,Txt_You_must_enter_the_range_of_floating_point_values_allowed_as_answer);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (!Question->Answer.Options[0].Text[0] ||
|
|
|
|
|
!Question->Answer.Options[1].Text[0])
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2019-02-16 19:29:27 +01:00
|
|
|
|
Ale_ShowAlert (Ale_WARNING,Txt_You_must_enter_the_range_of_floating_point_values_allowed_as_answer);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2019-03-17 14:47:58 +01:00
|
|
|
|
|
|
|
|
|
/* Lower limit should be <= upper limit */
|
2014-12-01 23:55:08 +01:00
|
|
|
|
for (i = 0;
|
|
|
|
|
i < 2;
|
|
|
|
|
i++)
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.FloatingPoint[i] = Str_GetDoubleFromStr (Question->Answer.Options[i].Text);
|
|
|
|
|
if (Question->Answer.FloatingPoint[0] >
|
|
|
|
|
Question->Answer.FloatingPoint[1])
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2019-02-16 19:29:27 +01:00
|
|
|
|
Ale_ShowAlert (Ale_WARNING,Txt_The_lower_limit_of_correct_answers_must_be_less_than_or_equal_to_the_upper_limit);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2019-03-17 14:47:58 +01:00
|
|
|
|
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.NumOptions = 2;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
break;
|
|
|
|
|
case Tst_ANS_TRUE_FALSE:
|
2019-03-17 14:47:58 +01:00
|
|
|
|
/* Answer should be 'T' or 'F' */
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (Question->Answer.TF != 'T' &&
|
|
|
|
|
Question->Answer.TF != 'F')
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2019-02-16 19:29:27 +01:00
|
|
|
|
Ale_ShowAlert (Ale_WARNING,Txt_You_must_select_a_T_F_answer);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2019-03-17 14:47:58 +01:00
|
|
|
|
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.NumOptions = 1;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
break;
|
|
|
|
|
case Tst_ANS_UNIQUE_CHOICE:
|
|
|
|
|
case Tst_ANS_MULTIPLE_CHOICE:
|
2019-03-17 14:47:58 +01:00
|
|
|
|
/* No option should be empty before a non-empty option */
|
2014-12-01 23:55:08 +01:00
|
|
|
|
for (NumOpt = 0, NumLastOpt = 0, ThereIsEndOfAnswers = false;
|
|
|
|
|
NumOpt < Tst_MAX_OPTIONS_PER_QUESTION;
|
|
|
|
|
NumOpt++)
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (Question->Answer.Options[NumOpt].Text)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (Question->Answer.Options[NumOpt].Text[0] || // Text
|
|
|
|
|
Question->Answer.Options[NumOpt].Media.Type != Med_TYPE_NONE) // or media
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
if (ThereIsEndOfAnswers)
|
|
|
|
|
{
|
2019-02-16 19:29:27 +01:00
|
|
|
|
Ale_ShowAlert (Ale_WARNING,Txt_You_can_not_leave_empty_intermediate_answers);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
NumLastOpt = NumOpt;
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.NumOptions++;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
ThereIsEndOfAnswers = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
ThereIsEndOfAnswers = true;
|
|
|
|
|
|
2019-03-17 14:47:58 +01:00
|
|
|
|
/* The two first options must be filled */
|
2014-12-01 23:55:08 +01:00
|
|
|
|
if (NumLastOpt < 1)
|
|
|
|
|
{
|
2019-02-16 19:29:27 +01:00
|
|
|
|
Ale_ShowAlert (Ale_WARNING,Txt_You_must_type_at_least_the_first_two_answers);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 14:47:58 +01:00
|
|
|
|
/* Its mandatory to mark at least one option as correct */
|
2014-12-01 23:55:08 +01:00
|
|
|
|
for (NumOpt = 0;
|
|
|
|
|
NumOpt <= NumLastOpt;
|
|
|
|
|
NumOpt++)
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (Question->Answer.Options[NumOpt].Correct)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
break;
|
|
|
|
|
if (NumOpt > NumLastOpt)
|
|
|
|
|
{
|
2019-02-16 19:29:27 +01:00
|
|
|
|
Ale_ShowAlert (Ale_WARNING,Txt_You_must_mark_an_answer_as_correct);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case Tst_ANS_TEXT:
|
2019-03-17 14:47:58 +01:00
|
|
|
|
/* First option should be filled */
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (!Question->Answer.Options[0].Text) // If the first answer is empty
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2019-03-17 14:47:58 +01:00
|
|
|
|
Ale_ShowAlert (Ale_WARNING,Txt_You_must_type_at_least_the_first_answer);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (!Question->Answer.Options[0].Text[0]) // If the first answer is empty
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2019-02-16 19:29:27 +01:00
|
|
|
|
Ale_ShowAlert (Ale_WARNING,Txt_You_must_type_at_least_the_first_answer);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 14:47:58 +01:00
|
|
|
|
/* No option should be empty before a non-empty option */
|
2014-12-01 23:55:08 +01:00
|
|
|
|
for (NumOpt=0, ThereIsEndOfAnswers=false;
|
|
|
|
|
NumOpt<Tst_MAX_OPTIONS_PER_QUESTION;
|
|
|
|
|
NumOpt++)
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (Question->Answer.Options[NumOpt].Text)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (Question->Answer.Options[NumOpt].Text[0])
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
if (ThereIsEndOfAnswers)
|
|
|
|
|
{
|
2019-02-16 19:29:27 +01:00
|
|
|
|
Ale_ShowAlert (Ale_WARNING,Txt_You_can_not_leave_empty_intermediate_answers);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.NumOptions++;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
ThereIsEndOfAnswers = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
ThereIsEndOfAnswers = true;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true; // Question format without errors
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-15 20:02:34 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********** Check if a test question already exists in database *************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-03-25 01:36:22 +01:00
|
|
|
|
bool Tst_CheckIfQuestionExistsInDB (struct Tst_Question *Question)
|
2019-12-15 20:02:34 +01:00
|
|
|
|
{
|
|
|
|
|
extern const char *Tst_StrAnswerTypesDB[Tst_NUM_ANS_TYPES];
|
|
|
|
|
MYSQL_RES *mysql_res_qst;
|
|
|
|
|
MYSQL_RES *mysql_res_ans;
|
|
|
|
|
MYSQL_ROW row;
|
|
|
|
|
bool IdenticalQuestionFound = false;
|
|
|
|
|
bool IdenticalAnswers;
|
|
|
|
|
unsigned NumQst;
|
|
|
|
|
unsigned NumQstsWithThisStem;
|
|
|
|
|
unsigned NumOpt;
|
|
|
|
|
unsigned NumOptsExistingQstInDB;
|
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
/***** Check if stem exists *****/
|
|
|
|
|
NumQstsWithThisStem =
|
|
|
|
|
(unsigned) DB_QuerySELECT (&mysql_res_qst,"can not check"
|
|
|
|
|
" if a question exists",
|
2021-03-29 16:38:56 +02:00
|
|
|
|
"SELECT QstCod"
|
|
|
|
|
" FROM tst_questions"
|
|
|
|
|
" WHERE CrsCod=%ld"
|
|
|
|
|
" AND AnsType='%s'"
|
|
|
|
|
" AND Stem='%s'",
|
2019-12-15 20:02:34 +01:00
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod,
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Tst_StrAnswerTypesDB[Question->Answer.Type],
|
2020-04-04 19:20:50 +02:00
|
|
|
|
Question->Stem);
|
2019-12-15 20:02:34 +01:00
|
|
|
|
|
|
|
|
|
if (NumQstsWithThisStem) // There are questions in database with the same stem that the one of this question
|
|
|
|
|
{
|
|
|
|
|
/***** Check if the answer exists in any of the questions with the same stem *****/
|
|
|
|
|
/* For each question with the same stem */
|
|
|
|
|
for (NumQst = 0;
|
|
|
|
|
!IdenticalQuestionFound && NumQst < NumQstsWithThisStem;
|
|
|
|
|
NumQst++)
|
|
|
|
|
{
|
2020-03-17 00:35:11 +01:00
|
|
|
|
/* Get question code */
|
2021-04-05 23:45:24 +02:00
|
|
|
|
if ((Question->QstCod = DB_GetNextCode (mysql_res_qst)) < 0)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_WrongQuestionExit ();
|
2019-12-15 20:02:34 +01:00
|
|
|
|
|
|
|
|
|
/* Get answers from this question */
|
|
|
|
|
NumOptsExistingQstInDB =
|
|
|
|
|
(unsigned) DB_QuerySELECT (&mysql_res_ans,"can not get the answer"
|
|
|
|
|
" of a question",
|
2021-04-05 23:45:24 +02:00
|
|
|
|
"SELECT Answer" // row[0]
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM tst_answers"
|
|
|
|
|
" WHERE QstCod=%ld"
|
|
|
|
|
" ORDER BY AnsInd",
|
2020-03-25 01:36:22 +01:00
|
|
|
|
Question->QstCod);
|
2019-12-15 20:02:34 +01:00
|
|
|
|
|
2020-03-19 20:57:54 +01:00
|
|
|
|
switch (Question->Answer.Type)
|
2019-12-15 20:02:34 +01:00
|
|
|
|
{
|
|
|
|
|
case Tst_ANS_INT:
|
|
|
|
|
row = mysql_fetch_row (mysql_res_ans);
|
2020-03-19 20:57:54 +01:00
|
|
|
|
IdenticalQuestionFound = (Tst_GetIntAnsFromStr (row[0]) == Question->Answer.Integer);
|
2019-12-15 20:02:34 +01:00
|
|
|
|
break;
|
|
|
|
|
case Tst_ANS_FLOAT:
|
|
|
|
|
for (IdenticalAnswers = true, i = 0;
|
|
|
|
|
IdenticalAnswers && i < 2;
|
|
|
|
|
i++)
|
|
|
|
|
{
|
|
|
|
|
row = mysql_fetch_row (mysql_res_ans);
|
2020-03-19 20:57:54 +01:00
|
|
|
|
IdenticalAnswers = (Str_GetDoubleFromStr (row[0]) == Question->Answer.FloatingPoint[i]);
|
2019-12-15 20:02:34 +01:00
|
|
|
|
}
|
|
|
|
|
IdenticalQuestionFound = IdenticalAnswers;
|
|
|
|
|
break;
|
|
|
|
|
case Tst_ANS_TRUE_FALSE:
|
|
|
|
|
row = mysql_fetch_row (mysql_res_ans);
|
2020-03-19 20:57:54 +01:00
|
|
|
|
IdenticalQuestionFound = (Str_ConvertToUpperLetter (row[0][0]) == Question->Answer.TF);
|
2019-12-15 20:02:34 +01:00
|
|
|
|
break;
|
|
|
|
|
case Tst_ANS_UNIQUE_CHOICE:
|
|
|
|
|
case Tst_ANS_MULTIPLE_CHOICE:
|
|
|
|
|
case Tst_ANS_TEXT:
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (NumOptsExistingQstInDB == Question->Answer.NumOptions)
|
2019-12-15 20:02:34 +01:00
|
|
|
|
{
|
|
|
|
|
for (IdenticalAnswers = true, NumOpt = 0;
|
|
|
|
|
IdenticalAnswers && NumOpt < NumOptsExistingQstInDB;
|
|
|
|
|
NumOpt++)
|
|
|
|
|
{
|
|
|
|
|
row = mysql_fetch_row (mysql_res_ans);
|
|
|
|
|
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (strcasecmp (row[0],Question->Answer.Options[NumOpt].Text))
|
2019-12-15 20:02:34 +01:00
|
|
|
|
IdenticalAnswers = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else // Different number of answers (options)
|
|
|
|
|
IdenticalAnswers = false;
|
|
|
|
|
IdenticalQuestionFound = IdenticalAnswers;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Free structure that stores the query result for answers */
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res_ans);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else // Stem does not exist
|
|
|
|
|
IdenticalQuestionFound = false;
|
|
|
|
|
|
|
|
|
|
/* Free structure that stores the query result for questions */
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res_qst);
|
|
|
|
|
|
|
|
|
|
return IdenticalQuestionFound;
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-04 21:51:21 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/* Move images associates to a test question to their definitive directories */
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-03-25 01:36:22 +01:00
|
|
|
|
static void Tst_MoveMediaToDefinitiveDirectories (struct Tst_Question *Question)
|
2016-04-04 21:51:21 +02:00
|
|
|
|
{
|
|
|
|
|
unsigned NumOpt;
|
2019-03-19 11:20:29 +01:00
|
|
|
|
long CurrentMedCodInDB;
|
2016-04-04 21:51:21 +02:00
|
|
|
|
|
2019-03-19 01:41:27 +01:00
|
|
|
|
/***** Media associated to question stem *****/
|
2020-03-25 01:36:22 +01:00
|
|
|
|
CurrentMedCodInDB = Tst_GetMedCodFromDB (Gbl.Hierarchy.Crs.CrsCod,Question->QstCod,
|
2020-03-17 00:35:11 +01:00
|
|
|
|
-1L); // Get current media code associated to stem
|
2020-03-17 14:47:58 +01:00
|
|
|
|
Med_RemoveKeepOrStoreMedia (CurrentMedCodInDB,&Question->Media);
|
2019-03-18 15:42:22 +01:00
|
|
|
|
|
2019-03-19 01:41:27 +01:00
|
|
|
|
/****** Move media associated to answers *****/
|
2020-03-19 20:57:54 +01:00
|
|
|
|
switch (Question->Answer.Type)
|
2020-03-18 18:49:45 +01:00
|
|
|
|
{
|
|
|
|
|
case Tst_ANS_UNIQUE_CHOICE:
|
|
|
|
|
case Tst_ANS_MULTIPLE_CHOICE:
|
|
|
|
|
for (NumOpt = 0;
|
2020-03-19 20:57:54 +01:00
|
|
|
|
NumOpt < Question->Answer.NumOptions;
|
2020-03-18 18:49:45 +01:00
|
|
|
|
NumOpt++)
|
|
|
|
|
{
|
2020-03-25 01:36:22 +01:00
|
|
|
|
CurrentMedCodInDB = Tst_GetMedCodFromDB (Gbl.Hierarchy.Crs.CrsCod,Question->QstCod,
|
2020-03-18 18:49:45 +01:00
|
|
|
|
NumOpt); // Get current media code associated to this option
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Med_RemoveKeepOrStoreMedia (CurrentMedCodInDB,&Question->Answer.Options[NumOpt].Media);
|
2020-03-18 18:49:45 +01:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-04-04 21:51:21 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************** Get a integer number from a string *********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
long Tst_GetIntAnsFromStr (char *Str)
|
|
|
|
|
{
|
|
|
|
|
long LongNum;
|
|
|
|
|
|
|
|
|
|
if (Str == NULL)
|
|
|
|
|
return 0.0;
|
|
|
|
|
|
|
|
|
|
/***** The string is "scanned" as long *****/
|
|
|
|
|
if (sscanf (Str,"%ld",&LongNum) != 1) // If the string does not hold a valid integer number...
|
|
|
|
|
{
|
|
|
|
|
LongNum = 0L; // ...the number is reset to 0
|
|
|
|
|
Str[0] = '\0'; // ...and the string is reset to ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return LongNum;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-17 00:35:11 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************** Request the removal of selected questions *****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void Tst_RequestRemoveSelectedQsts (void)
|
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_Do_you_really_want_to_remove_the_selected_questions;
|
|
|
|
|
extern const char *Txt_Remove_questions;
|
2020-03-24 00:58:42 +01:00
|
|
|
|
struct Tst_Test Test;
|
2020-03-17 00:35:11 +01:00
|
|
|
|
|
2020-03-26 21:39:44 +01:00
|
|
|
|
/***** Create test *****/
|
|
|
|
|
Tst_TstConstructor (&Test);
|
|
|
|
|
|
2020-03-17 00:35:11 +01:00
|
|
|
|
/***** Get parameters *****/
|
2020-03-24 01:50:39 +01:00
|
|
|
|
if (Tst_GetParamsTst (&Test,Tst_EDIT_TEST)) // Get parameters from the form
|
2020-03-21 15:41:25 +01:00
|
|
|
|
{
|
2020-03-17 00:35:11 +01:00
|
|
|
|
/***** Show question and button to remove question *****/
|
|
|
|
|
Ale_ShowAlertAndButton (ActRemSevTstQst,NULL,NULL,
|
2020-10-14 00:59:24 +02:00
|
|
|
|
Tst_PutParamsEditQst,&Test,
|
|
|
|
|
Btn_REMOVE_BUTTON,Txt_Remove_questions,
|
|
|
|
|
Ale_QUESTION,Txt_Do_you_really_want_to_remove_the_selected_questions);
|
2020-03-21 15:41:25 +01:00
|
|
|
|
}
|
2020-03-17 00:35:11 +01:00
|
|
|
|
else
|
|
|
|
|
Ale_ShowAlert (Ale_ERROR,"Wrong parameters.");
|
|
|
|
|
|
|
|
|
|
/***** Continue editing questions *****/
|
|
|
|
|
Tst_ListQuestionsToEdit ();
|
2020-03-26 21:39:44 +01:00
|
|
|
|
|
|
|
|
|
/***** Destroy test *****/
|
|
|
|
|
Tst_TstDestructor (&Test);
|
2020-03-17 00:35:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************************** Remove several questions *************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void Tst_RemoveSelectedQsts (void)
|
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_Questions_removed_X;
|
2020-03-24 00:58:42 +01:00
|
|
|
|
struct Tst_Test Test;
|
2020-03-17 00:35:11 +01:00
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
MYSQL_ROW row;
|
2020-03-24 01:50:39 +01:00
|
|
|
|
unsigned NumQst;
|
2020-03-17 00:35:11 +01:00
|
|
|
|
long QstCod;
|
|
|
|
|
|
2020-03-26 21:39:44 +01:00
|
|
|
|
/***** Create test *****/
|
|
|
|
|
Tst_TstConstructor (&Test);
|
|
|
|
|
|
2020-03-17 00:35:11 +01:00
|
|
|
|
/***** Get parameters *****/
|
2020-03-24 01:50:39 +01:00
|
|
|
|
if (Tst_GetParamsTst (&Test,Tst_EDIT_TEST)) // Get parameters
|
2020-03-17 00:35:11 +01:00
|
|
|
|
{
|
|
|
|
|
/***** Get question codes *****/
|
2020-03-24 01:50:39 +01:00
|
|
|
|
Tst_GetQuestions (&Test,&mysql_res); // Query database
|
2020-03-17 00:35:11 +01:00
|
|
|
|
|
|
|
|
|
/***** Remove questions one by one *****/
|
2020-03-24 01:50:39 +01:00
|
|
|
|
for (NumQst = 0;
|
|
|
|
|
NumQst < Test.NumQsts;
|
|
|
|
|
NumQst++)
|
2020-03-17 00:35:11 +01:00
|
|
|
|
{
|
|
|
|
|
/* Get question code (row[0]) */
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
2021-04-29 19:52:35 +02:00
|
|
|
|
if ((QstCod = Str_ConvertStrCodToLongCod (row[0])) <= 0)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_WrongQuestionExit ();
|
2020-03-17 00:35:11 +01:00
|
|
|
|
|
|
|
|
|
/* Remove test question from database */
|
|
|
|
|
Tst_RemoveOneQstFromDB (Gbl.Hierarchy.Crs.CrsCod,QstCod);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
|
|
|
|
|
/***** Write message *****/
|
2020-03-24 01:50:39 +01:00
|
|
|
|
Ale_ShowAlert (Ale_SUCCESS,Txt_Questions_removed_X,Test.NumQsts);
|
2020-03-17 00:35:11 +01:00
|
|
|
|
}
|
2020-03-26 21:39:44 +01:00
|
|
|
|
|
|
|
|
|
/***** Destroy test *****/
|
|
|
|
|
Tst_TstDestructor (&Test);
|
2020-03-17 00:35:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-04-05 10:05:52 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************* Put icon to remove one question ***********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-27 19:37:49 +02:00
|
|
|
|
static void Tst_PutIconToRemoveOneQst (void *QstCod)
|
2016-04-05 10:05:52 +02:00
|
|
|
|
{
|
2020-10-13 22:34:31 +02:00
|
|
|
|
Ico_PutContextualIconToRemove (ActReqRemOneTstQst,NULL,
|
2020-04-27 19:37:49 +02:00
|
|
|
|
Tst_PutParamsRemoveOnlyThisQst,QstCod);
|
2016-04-05 10:05:52 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
2016-04-05 02:59:34 +02:00
|
|
|
|
/******************** Request the removal of a question **********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-03-17 00:35:11 +01:00
|
|
|
|
void Tst_RequestRemoveOneQst (void)
|
2016-04-05 02:59:34 +02:00
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_Do_you_really_want_to_remove_the_question_X;
|
|
|
|
|
extern const char *Txt_Remove_question;
|
|
|
|
|
bool EditingOnlyThisQst;
|
2020-03-24 00:58:42 +01:00
|
|
|
|
struct Tst_Test Test;
|
2016-04-05 02:59:34 +02:00
|
|
|
|
|
2020-03-26 21:39:44 +01:00
|
|
|
|
/***** Create test *****/
|
|
|
|
|
Tst_TstConstructor (&Test);
|
2020-03-25 01:36:22 +01:00
|
|
|
|
|
2016-04-05 10:47:36 +02:00
|
|
|
|
/***** Get main parameters from form *****/
|
|
|
|
|
/* Get the question code */
|
2020-04-27 19:37:49 +02:00
|
|
|
|
Test.Question.QstCod = Tst_GetParamQstCod ();
|
2020-03-26 21:39:44 +01:00
|
|
|
|
if (Test.Question.QstCod <= 0)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_WrongQuestionExit ();
|
2016-04-05 02:59:34 +02:00
|
|
|
|
|
2016-04-05 10:47:36 +02:00
|
|
|
|
/* Get a parameter that indicates whether it's necessary
|
|
|
|
|
to continue listing the rest of questions */
|
2017-01-28 20:32:50 +01:00
|
|
|
|
EditingOnlyThisQst = Par_GetParToBool ("OnlyThisQst");
|
2016-04-05 02:59:34 +02:00
|
|
|
|
|
2017-04-28 14:02:08 +02:00
|
|
|
|
/* Get other parameters */
|
|
|
|
|
if (!EditingOnlyThisQst)
|
2020-03-24 01:50:39 +01:00
|
|
|
|
if (!Tst_GetParamsTst (&Test,Tst_EDIT_TEST))
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_ShowErrorAndExit ("Wrong test parameters.");
|
2016-04-05 02:59:34 +02:00
|
|
|
|
|
2017-04-28 14:02:08 +02:00
|
|
|
|
/***** Show question and button to remove question *****/
|
2020-03-27 14:56:54 +01:00
|
|
|
|
if (EditingOnlyThisQst)
|
|
|
|
|
Ale_ShowAlertAndButton (ActRemOneTstQst,NULL,NULL,
|
|
|
|
|
Tst_PutParamsRemoveOnlyThisQst,&Test.Question.QstCod,
|
|
|
|
|
Btn_REMOVE_BUTTON,Txt_Remove_question,
|
|
|
|
|
Ale_QUESTION,Txt_Do_you_really_want_to_remove_the_question_X,
|
|
|
|
|
Test.Question.QstCod);
|
|
|
|
|
else
|
|
|
|
|
Ale_ShowAlertAndButton (ActRemOneTstQst,NULL,NULL,
|
2020-10-14 00:59:24 +02:00
|
|
|
|
Tst_PutParamsEditQst,&Test,
|
2020-03-27 14:56:54 +01:00
|
|
|
|
Btn_REMOVE_BUTTON,Txt_Remove_question,
|
|
|
|
|
Ale_QUESTION,Txt_Do_you_really_want_to_remove_the_question_X,
|
|
|
|
|
Test.Question.QstCod);
|
2016-04-05 02:59:34 +02:00
|
|
|
|
|
|
|
|
|
/***** Continue editing questions *****/
|
|
|
|
|
if (EditingOnlyThisQst)
|
2020-03-26 21:39:44 +01:00
|
|
|
|
Tst_ListOneQstToEdit (&Test);
|
2020-03-25 01:36:22 +01:00
|
|
|
|
else
|
|
|
|
|
Tst_ListQuestionsToEdit ();
|
|
|
|
|
|
2020-03-26 21:39:44 +01:00
|
|
|
|
/***** Destroy test *****/
|
|
|
|
|
Tst_TstDestructor (&Test);
|
2016-04-05 02:59:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-28 14:02:08 +02:00
|
|
|
|
/*****************************************************************************/
|
2020-03-17 00:35:11 +01:00
|
|
|
|
/***** Put parameters to remove question when editing only one question ******/
|
2017-04-28 14:02:08 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-27 19:37:49 +02:00
|
|
|
|
static void Tst_PutParamsRemoveOnlyThisQst (void *QstCod)
|
2017-04-28 14:02:08 +02:00
|
|
|
|
{
|
2020-04-27 19:37:49 +02:00
|
|
|
|
if (QstCod)
|
2020-03-26 21:39:44 +01:00
|
|
|
|
{
|
2020-04-27 19:37:49 +02:00
|
|
|
|
Tst_PutParamQstCod (QstCod);
|
2020-03-26 21:39:44 +01:00
|
|
|
|
Par_PutHiddenParamChar ("OnlyThisQst",'Y');
|
|
|
|
|
}
|
2017-04-28 14:02:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-04-05 02:59:34 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************************** Remove a question *****************************/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-03-17 00:35:11 +01:00
|
|
|
|
void Tst_RemoveOneQst (void)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_Question_removed;
|
2020-03-17 00:35:11 +01:00
|
|
|
|
long QstCod;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
bool EditingOnlyThisQst;
|
|
|
|
|
|
|
|
|
|
/***** Get the question code *****/
|
2020-04-27 19:37:49 +02:00
|
|
|
|
QstCod = Tst_GetParamQstCod ();
|
2020-03-17 00:35:11 +01:00
|
|
|
|
if (QstCod <= 0)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_WrongQuestionExit ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2016-04-05 02:59:34 +02:00
|
|
|
|
/***** Get a parameter that indicates whether it's necessary
|
|
|
|
|
to continue listing the rest of questions ******/
|
2017-01-28 20:32:50 +01:00
|
|
|
|
EditingOnlyThisQst = Par_GetParToBool ("OnlyThisQst");
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-17 00:35:11 +01:00
|
|
|
|
/***** Remove test question from database *****/
|
|
|
|
|
Tst_RemoveOneQstFromDB (Gbl.Hierarchy.Crs.CrsCod,QstCod);
|
|
|
|
|
|
|
|
|
|
/***** Write message *****/
|
|
|
|
|
Ale_ShowAlert (Ale_SUCCESS,Txt_Question_removed);
|
|
|
|
|
|
|
|
|
|
/***** Continue editing questions *****/
|
|
|
|
|
if (!EditingOnlyThisQst)
|
|
|
|
|
Tst_ListQuestionsToEdit ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************** Remove a question from database **********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void Tst_RemoveOneQstFromDB (long CrsCod,long QstCod)
|
|
|
|
|
{
|
2019-03-19 11:20:29 +01:00
|
|
|
|
/***** Remove media associated to question *****/
|
2020-05-18 22:59:07 +02:00
|
|
|
|
Tst_RemoveMediaFromStemOfQst (CrsCod,QstCod);
|
2020-03-17 00:35:11 +01:00
|
|
|
|
Tst_RemoveMediaFromAllAnsOfQst (CrsCod,QstCod);
|
2019-03-19 11:20:29 +01:00
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/***** Remove the question from all the tables *****/
|
|
|
|
|
/* Remove answers and tags from this test question */
|
2020-03-17 00:35:11 +01:00
|
|
|
|
Tst_RemAnsFromQst (QstCod);
|
2020-05-17 17:11:04 +02:00
|
|
|
|
Tag_RemTagsFromQst (QstCod);
|
|
|
|
|
Tag_RemoveUnusedTagsFromCrs (CrsCod);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/* Remove the question itself */
|
2018-11-02 22:27:26 +01:00
|
|
|
|
DB_QueryDELETE ("can not remove a question",
|
|
|
|
|
"DELETE FROM tst_questions"
|
2021-04-17 13:40:55 +02:00
|
|
|
|
" WHERE QstCod=%ld"
|
|
|
|
|
" AND CrsCod=%ld",
|
|
|
|
|
QstCod,
|
|
|
|
|
CrsCod);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********************** Change the shuffle of a question ********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void Tst_ChangeShuffleQst (void)
|
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_The_answers_of_the_question_with_code_X_will_appear_shuffled;
|
|
|
|
|
extern const char *Txt_The_answers_of_the_question_with_code_X_will_appear_without_shuffling;
|
2020-03-26 21:39:44 +01:00
|
|
|
|
struct Tst_Test Test;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
bool EditingOnlyThisQst;
|
|
|
|
|
bool Shuffle;
|
|
|
|
|
|
2020-03-26 21:39:44 +01:00
|
|
|
|
/***** Create test *****/
|
|
|
|
|
Tst_TstConstructor (&Test);
|
2020-03-25 01:36:22 +01:00
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/***** Get the question code *****/
|
2020-04-27 19:37:49 +02:00
|
|
|
|
Test.Question.QstCod = Tst_GetParamQstCod ();
|
2020-03-26 21:39:44 +01:00
|
|
|
|
if (Test.Question.QstCod <= 0)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_WrongQuestionExit ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/***** Get a parameter that indicates whether it's necessary to continue listing the rest of questions ******/
|
2017-01-28 20:32:50 +01:00
|
|
|
|
EditingOnlyThisQst = Par_GetParToBool ("OnlyThisQst");
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/***** Get a parameter that indicates whether it's possible to shuffle the answers of this question ******/
|
2017-01-28 20:32:50 +01:00
|
|
|
|
Shuffle = Par_GetParToBool ("Shuffle");
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/***** Remove the question from all the tables *****/
|
|
|
|
|
/* Update the question changing the current shuffle */
|
2018-11-03 12:16:40 +01:00
|
|
|
|
DB_QueryUPDATE ("can not update the shuffle type of a question",
|
2021-04-17 13:11:37 +02:00
|
|
|
|
"UPDATE tst_questions"
|
|
|
|
|
" SET Shuffle='%c'"
|
|
|
|
|
" WHERE QstCod=%ld"
|
|
|
|
|
" AND CrsCod=%ld",
|
2018-11-03 12:16:40 +01:00
|
|
|
|
Shuffle ? 'Y' :
|
|
|
|
|
'N',
|
2021-04-17 13:11:37 +02:00
|
|
|
|
Test.Question.QstCod,
|
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/***** Write message *****/
|
2019-02-16 19:29:27 +01:00
|
|
|
|
Ale_ShowAlert (Ale_SUCCESS,Shuffle ? Txt_The_answers_of_the_question_with_code_X_will_appear_shuffled :
|
|
|
|
|
Txt_The_answers_of_the_question_with_code_X_will_appear_without_shuffling,
|
2020-03-26 21:39:44 +01:00
|
|
|
|
Test.Question.QstCod);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/***** Continue editing questions *****/
|
|
|
|
|
if (EditingOnlyThisQst)
|
2020-03-26 21:39:44 +01:00
|
|
|
|
Tst_ListOneQstToEdit (&Test);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
else
|
|
|
|
|
Tst_ListQuestionsToEdit ();
|
2020-03-25 01:36:22 +01:00
|
|
|
|
|
2020-03-26 21:39:44 +01:00
|
|
|
|
/***** Destroy test *****/
|
|
|
|
|
Tst_TstDestructor (&Test);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************ Get the parameter with the code of a test question *************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-27 19:37:49 +02:00
|
|
|
|
long Tst_GetParamQstCod (void)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2017-01-28 20:32:50 +01:00
|
|
|
|
/***** Get code of test question *****/
|
|
|
|
|
return Par_GetParToLong ("QstCod");
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-28 14:02:08 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************ Put parameter with question code to edit, remove... ************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-24 02:49:51 +02:00
|
|
|
|
void Tst_PutParamQstCod (void *QstCod) // Should be a pointer to long
|
2020-03-17 00:35:11 +01:00
|
|
|
|
{
|
2020-04-24 02:49:51 +02:00
|
|
|
|
if (QstCod)
|
|
|
|
|
if (*((long *) QstCod) > 0) // If question exists
|
|
|
|
|
Par_PutHiddenParamLong (NULL,"QstCod",*((long *) QstCod));
|
2017-04-28 14:02:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
2020-04-27 19:37:49 +02:00
|
|
|
|
/******** Insert or update question, tags and answer in the database *********/
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-03-25 01:36:22 +01:00
|
|
|
|
void Tst_InsertOrUpdateQstTagsAnsIntoDB (struct Tst_Question *Question)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
/***** Insert or update question in the table of questions *****/
|
2020-03-25 01:36:22 +01:00
|
|
|
|
Tst_InsertOrUpdateQstIntoDB (Question);
|
2021-04-27 21:56:40 +02:00
|
|
|
|
|
2020-03-25 01:36:22 +01:00
|
|
|
|
if (Question->QstCod > 0)
|
2020-03-17 00:35:11 +01:00
|
|
|
|
{
|
|
|
|
|
/***** Insert tags in the tags table *****/
|
2020-05-17 17:11:04 +02:00
|
|
|
|
Tag_InsertTagsIntoDB (Question->QstCod,&Question->Tags);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-17 00:35:11 +01:00
|
|
|
|
/***** Remove unused tags in current course *****/
|
2020-05-17 17:11:04 +02:00
|
|
|
|
Tag_RemoveUnusedTagsFromCrs (Gbl.Hierarchy.Crs.CrsCod);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2020-03-17 00:35:11 +01:00
|
|
|
|
/***** Insert answers in the answers table *****/
|
2020-03-25 01:36:22 +01:00
|
|
|
|
Tst_InsertAnswersIntoDB (Question);
|
2020-03-17 00:35:11 +01:00
|
|
|
|
}
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********** Insert or update question in the table of questions *************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-03-25 01:36:22 +01:00
|
|
|
|
static void Tst_InsertOrUpdateQstIntoDB (struct Tst_Question *Question)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2020-03-25 01:36:22 +01:00
|
|
|
|
if (Question->QstCod < 0) // It's a new question
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2016-04-01 21:56:00 +02:00
|
|
|
|
/***** Insert question in the table of questions *****/
|
2020-03-25 01:36:22 +01:00
|
|
|
|
Question->QstCod =
|
2018-11-03 01:45:36 +01:00
|
|
|
|
DB_QueryINSERTandReturnCode ("can not create question",
|
|
|
|
|
"INSERT INTO tst_questions"
|
2020-03-17 14:47:58 +01:00
|
|
|
|
" (CrsCod,"
|
|
|
|
|
"EditTime,"
|
|
|
|
|
"AnsType,"
|
|
|
|
|
"Shuffle,"
|
|
|
|
|
"Stem,"
|
|
|
|
|
"Feedback,"
|
|
|
|
|
"MedCod,"
|
|
|
|
|
"NumHits,"
|
|
|
|
|
"Score)"
|
2018-11-03 01:45:36 +01:00
|
|
|
|
" VALUES"
|
2020-03-17 14:47:58 +01:00
|
|
|
|
" (%ld," // CrsCod
|
|
|
|
|
"NOW()," // EditTime
|
|
|
|
|
"'%s'," // AnsType
|
|
|
|
|
"'%c'," // Shuffle
|
|
|
|
|
"'%s'," // Stem
|
|
|
|
|
"'%s'," // Feedback
|
|
|
|
|
"%ld," // MedCod
|
|
|
|
|
"0," // NumHits
|
|
|
|
|
"0)", // Score
|
2019-04-04 10:45:15 +02:00
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod,
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Tst_StrAnswerTypesDB[Question->Answer.Type],
|
2020-04-03 19:13:00 +02:00
|
|
|
|
Question->Answer.Shuffle ? 'Y' :
|
|
|
|
|
'N',
|
2020-04-04 19:20:50 +02:00
|
|
|
|
Question->Stem,
|
|
|
|
|
Question->Feedback ? Question->Feedback :
|
2021-04-17 11:27:33 +02:00
|
|
|
|
"",
|
2020-03-17 14:47:58 +01:00
|
|
|
|
Question->Media.MedCod);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
2020-03-17 00:35:11 +01:00
|
|
|
|
else // It's an existing question
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2016-04-01 21:56:00 +02:00
|
|
|
|
/***** Update existing question *****/
|
2016-04-03 18:51:48 +02:00
|
|
|
|
/* Update question in database */
|
2018-11-03 12:16:40 +01:00
|
|
|
|
DB_QueryUPDATE ("can not update question",
|
|
|
|
|
"UPDATE tst_questions"
|
2021-04-17 13:11:37 +02:00
|
|
|
|
" SET EditTime=NOW(),"
|
|
|
|
|
"AnsType='%s',"
|
|
|
|
|
"Shuffle='%c',"
|
|
|
|
|
"Stem='%s',"
|
|
|
|
|
"Feedback='%s',"
|
|
|
|
|
"MedCod=%ld"
|
|
|
|
|
" WHERE QstCod=%ld"
|
|
|
|
|
" AND CrsCod=%ld",
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Tst_StrAnswerTypesDB[Question->Answer.Type],
|
2020-04-03 19:13:00 +02:00
|
|
|
|
Question->Answer.Shuffle ? 'Y' :
|
|
|
|
|
'N',
|
2020-04-04 19:20:50 +02:00
|
|
|
|
Question->Stem,
|
|
|
|
|
Question->Feedback ? Question->Feedback :
|
2021-04-17 13:11:37 +02:00
|
|
|
|
"",
|
2020-03-17 14:47:58 +01:00
|
|
|
|
Question->Media.MedCod,
|
2021-04-17 13:11:37 +02:00
|
|
|
|
Question->QstCod,
|
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod);
|
2016-04-03 18:51:48 +02:00
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/* Remove answers and tags from this test question */
|
2020-03-25 01:36:22 +01:00
|
|
|
|
Tst_RemAnsFromQst (Question->QstCod);
|
2020-05-17 17:11:04 +02:00
|
|
|
|
Tag_RemTagsFromQst (Question->QstCod);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************* Insert answers in the answers table *********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-03-25 01:36:22 +01:00
|
|
|
|
static void Tst_InsertAnswersIntoDB (struct Tst_Question *Question)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
unsigned NumOpt;
|
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
/***** Insert answers in the answers table *****/
|
2020-03-19 20:57:54 +01:00
|
|
|
|
switch (Question->Answer.Type)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
case Tst_ANS_INT:
|
2018-11-02 19:37:11 +01:00
|
|
|
|
DB_QueryINSERT ("can not create answer",
|
|
|
|
|
"INSERT INTO tst_answers"
|
2019-03-18 15:42:22 +01:00
|
|
|
|
" (QstCod,AnsInd,Answer,Feedback,MedCod,Correct)"
|
2018-11-02 19:37:11 +01:00
|
|
|
|
" VALUES"
|
2019-03-18 15:42:22 +01:00
|
|
|
|
" (%ld,0,%ld,'',-1,'Y')",
|
2020-03-25 01:36:22 +01:00
|
|
|
|
Question->QstCod,
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.Integer);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
break;
|
|
|
|
|
case Tst_ANS_FLOAT:
|
2019-11-27 09:01:45 +01:00
|
|
|
|
Str_SetDecimalPointToUS (); // To print the floating point as a dot
|
2014-12-01 23:55:08 +01:00
|
|
|
|
for (i = 0;
|
|
|
|
|
i < 2;
|
|
|
|
|
i++)
|
2018-11-02 19:37:11 +01:00
|
|
|
|
DB_QueryINSERT ("can not create answer",
|
|
|
|
|
"INSERT INTO tst_answers"
|
2019-03-18 15:42:22 +01:00
|
|
|
|
" (QstCod,AnsInd,Answer,Feedback,MedCod,Correct)"
|
2018-11-02 19:37:11 +01:00
|
|
|
|
" VALUES"
|
2020-01-11 15:22:02 +01:00
|
|
|
|
" (%ld,%u,'%.15lg','',-1,'Y')",
|
2021-04-17 11:27:33 +02:00
|
|
|
|
Question->QstCod,
|
|
|
|
|
i,
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.FloatingPoint[i]);
|
2016-06-04 14:21:01 +02:00
|
|
|
|
Str_SetDecimalPointToLocal (); // Return to local system
|
2014-12-01 23:55:08 +01:00
|
|
|
|
break;
|
|
|
|
|
case Tst_ANS_TRUE_FALSE:
|
2018-11-02 19:37:11 +01:00
|
|
|
|
DB_QueryINSERT ("can not create answer",
|
|
|
|
|
"INSERT INTO tst_answers"
|
2019-03-18 15:42:22 +01:00
|
|
|
|
" (QstCod,AnsInd,Answer,Feedback,MedCod,Correct)"
|
2018-11-02 19:37:11 +01:00
|
|
|
|
" VALUES"
|
2019-03-18 15:42:22 +01:00
|
|
|
|
" (%ld,0,'%c','',-1,'Y')",
|
2020-03-25 01:36:22 +01:00
|
|
|
|
Question->QstCod,
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.TF);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
break;
|
|
|
|
|
case Tst_ANS_UNIQUE_CHOICE:
|
|
|
|
|
case Tst_ANS_MULTIPLE_CHOICE:
|
|
|
|
|
case Tst_ANS_TEXT:
|
|
|
|
|
for (NumOpt = 0;
|
2020-03-19 20:57:54 +01:00
|
|
|
|
NumOpt < Question->Answer.NumOptions;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
NumOpt++)
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (Question->Answer.Options[NumOpt].Text[0] || // Text
|
|
|
|
|
Question->Answer.Options[NumOpt].Media.Type != Med_TYPE_NONE) // or media
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2018-11-02 19:37:11 +01:00
|
|
|
|
DB_QueryINSERT ("can not create answer",
|
|
|
|
|
"INSERT INTO tst_answers"
|
2019-03-18 15:42:22 +01:00
|
|
|
|
" (QstCod,AnsInd,Answer,Feedback,MedCod,Correct)"
|
2018-11-02 19:37:11 +01:00
|
|
|
|
" VALUES"
|
2019-03-18 15:42:22 +01:00
|
|
|
|
" (%ld,%u,'%s','%s',%ld,'%c')",
|
2020-03-25 01:36:22 +01:00
|
|
|
|
Question->QstCod,NumOpt,
|
2020-03-19 20:57:54 +01:00
|
|
|
|
Question->Answer.Options[NumOpt].Text,
|
|
|
|
|
Question->Answer.Options[NumOpt].Feedback ? Question->Answer.Options[NumOpt].Feedback :
|
|
|
|
|
"",
|
|
|
|
|
Question->Answer.Options[NumOpt].Media.MedCod,
|
|
|
|
|
Question->Answer.Options[NumOpt].Correct ? 'Y' :
|
|
|
|
|
'N');
|
2016-04-04 21:51:21 +02:00
|
|
|
|
|
|
|
|
|
/* Update image status */
|
2020-03-19 20:57:54 +01:00
|
|
|
|
if (Question->Answer.Options[NumOpt].Media.Type != Med_TYPE_NONE)
|
|
|
|
|
Question->Answer.Options[NumOpt].Media.Status = Med_STORED_IN_DB;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-09 01:37:00 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********************** Update the score of a question **********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void Tst_UpdateQstScoreInDB (struct TstPrn_PrintedQuestion *PrintedQuestion)
|
|
|
|
|
{
|
|
|
|
|
/***** Update number of clicks and score of the question *****/
|
2020-05-22 20:10:45 +02:00
|
|
|
|
Str_SetDecimalPointToUS (); // To print the floating point as a dot
|
|
|
|
|
if (PrintedQuestion->StrAnswers[0]) // User's answer is not blank
|
2020-05-09 01:37:00 +02:00
|
|
|
|
DB_QueryUPDATE ("can not update the score of a question",
|
|
|
|
|
"UPDATE tst_questions"
|
2021-04-17 13:11:37 +02:00
|
|
|
|
" SET NumHits=NumHits+1,"
|
|
|
|
|
"NumHitsNotBlank=NumHitsNotBlank+1,"
|
|
|
|
|
"Score=Score+(%.15lg)"
|
2020-05-09 01:37:00 +02:00
|
|
|
|
" WHERE QstCod=%ld",
|
|
|
|
|
PrintedQuestion->Score,
|
|
|
|
|
PrintedQuestion->QstCod);
|
2020-05-22 20:10:45 +02:00
|
|
|
|
else // User's answer is blank
|
2020-05-09 01:37:00 +02:00
|
|
|
|
DB_QueryUPDATE ("can not update the score of a question",
|
|
|
|
|
"UPDATE tst_questions"
|
2021-04-17 13:11:37 +02:00
|
|
|
|
" SET NumHits=NumHits+1"
|
2020-05-09 01:37:00 +02:00
|
|
|
|
" WHERE QstCod=%ld",
|
|
|
|
|
PrintedQuestion->QstCod);
|
|
|
|
|
Str_SetDecimalPointToLocal (); // Return to local system
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-16 13:02:30 +01:00
|
|
|
|
/*****************************************************************************/
|
2020-04-16 21:03:22 +02:00
|
|
|
|
/******************* Remove all test questions in a course *******************/
|
2020-02-16 13:02:30 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void Tst_RemoveCrsTests (long CrsCod)
|
|
|
|
|
{
|
2020-05-18 22:59:07 +02:00
|
|
|
|
/***** Remove all test exam prints made in the course *****/
|
|
|
|
|
TstPrn_RemoveCrsPrints (CrsCod);
|
|
|
|
|
|
2020-02-16 13:02:30 +01:00
|
|
|
|
/***** Remove test configuration of the course *****/
|
|
|
|
|
DB_QueryDELETE ("can not remove configuration of tests of a course",
|
2021-04-17 13:40:55 +02:00
|
|
|
|
"DELETE FROM tst_config"
|
|
|
|
|
" WHERE CrsCod=%ld",
|
2020-02-16 13:02:30 +01:00
|
|
|
|
CrsCod);
|
|
|
|
|
|
|
|
|
|
/***** Remove associations between test questions
|
|
|
|
|
and test tags in the course *****/
|
|
|
|
|
DB_QueryDELETE ("can not remove tags associated"
|
|
|
|
|
" to questions of tests of a course",
|
|
|
|
|
"DELETE FROM tst_question_tags"
|
2021-04-17 13:40:55 +02:00
|
|
|
|
" USING tst_questions,"
|
|
|
|
|
"tst_question_tags"
|
2020-02-16 13:02:30 +01:00
|
|
|
|
" WHERE tst_questions.CrsCod=%ld"
|
2021-04-17 13:40:55 +02:00
|
|
|
|
" AND tst_questions.QstCod=tst_question_tags.QstCod",
|
2020-02-16 13:02:30 +01:00
|
|
|
|
CrsCod);
|
|
|
|
|
|
|
|
|
|
/***** Remove test tags in the course *****/
|
|
|
|
|
DB_QueryDELETE ("can not remove tags of test of a course",
|
2021-04-17 13:40:55 +02:00
|
|
|
|
"DELETE FROM tst_tags"
|
|
|
|
|
" WHERE CrsCod=%ld",
|
2020-02-16 13:02:30 +01:00
|
|
|
|
CrsCod);
|
|
|
|
|
|
2020-05-18 22:59:07 +02:00
|
|
|
|
/***** Remove media associated to test questions in the course *****/
|
|
|
|
|
Tst_RemoveAllMedFilesFromStemOfAllQstsInCrs (CrsCod);
|
|
|
|
|
Tst_RemoveAllMedFilesFromAnsOfAllQstsInCrs (CrsCod);
|
|
|
|
|
|
2020-02-16 13:02:30 +01:00
|
|
|
|
/***** Remove test answers in the course *****/
|
|
|
|
|
DB_QueryDELETE ("can not remove answers of tests of a course",
|
2020-05-18 22:59:07 +02:00
|
|
|
|
"DELETE FROM tst_answers"
|
2021-04-17 13:40:55 +02:00
|
|
|
|
" USING tst_questions,"
|
|
|
|
|
"tst_answers"
|
2020-02-16 13:02:30 +01:00
|
|
|
|
" WHERE tst_questions.CrsCod=%ld"
|
2021-04-17 13:40:55 +02:00
|
|
|
|
" AND tst_questions.QstCod=tst_answers.QstCod",
|
2020-02-16 13:02:30 +01:00
|
|
|
|
CrsCod);
|
|
|
|
|
|
|
|
|
|
/***** Remove test questions in the course *****/
|
|
|
|
|
DB_QueryDELETE ("can not remove test questions of a course",
|
2021-04-17 13:40:55 +02:00
|
|
|
|
"DELETE FROM tst_questions"
|
|
|
|
|
" WHERE CrsCod=%ld",
|
2020-02-16 13:02:30 +01:00
|
|
|
|
CrsCod);
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************** Remove answers from a test question ********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-03-17 00:35:11 +01:00
|
|
|
|
static void Tst_RemAnsFromQst (long QstCod)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
/***** Remove answers *****/
|
2018-11-02 22:27:26 +01:00
|
|
|
|
DB_QueryDELETE ("can not remove the answers of a question",
|
2021-04-17 13:40:55 +02:00
|
|
|
|
"DELETE FROM tst_answers"
|
|
|
|
|
" WHERE QstCod=%ld",
|
2020-03-17 00:35:11 +01:00
|
|
|
|
QstCod);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-04-07 12:36:58 +02:00
|
|
|
|
/*****************************************************************************/
|
2020-05-18 22:59:07 +02:00
|
|
|
|
/************ Remove media associated to stem of a test question *************/
|
2016-04-07 12:36:58 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-05-18 22:59:07 +02:00
|
|
|
|
static void Tst_RemoveMediaFromStemOfQst (long CrsCod,long QstCod)
|
2016-04-04 21:51:21 +02:00
|
|
|
|
{
|
|
|
|
|
MYSQL_RES *mysql_res;
|
2019-03-02 21:49:11 +01:00
|
|
|
|
unsigned NumMedia;
|
2016-04-04 21:51:21 +02:00
|
|
|
|
|
2020-05-18 22:59:07 +02:00
|
|
|
|
/***** Get media code associated to stem of test question from database *****/
|
2019-03-02 21:49:11 +01:00
|
|
|
|
NumMedia =
|
2019-03-18 15:42:22 +01:00
|
|
|
|
(unsigned) DB_QuerySELECT (&mysql_res,"can not get media",
|
2021-04-05 23:45:24 +02:00
|
|
|
|
"SELECT MedCod"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM tst_questions"
|
2020-05-18 22:59:07 +02:00
|
|
|
|
" WHERE QstCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND CrsCod=%ld", // Extra check
|
2020-05-18 22:59:07 +02:00
|
|
|
|
QstCod,CrsCod);
|
2016-04-04 21:51:21 +02:00
|
|
|
|
|
2020-05-18 22:59:07 +02:00
|
|
|
|
/***** Go over result removing media *****/
|
2019-03-18 15:42:22 +01:00
|
|
|
|
Med_RemoveMediaFromAllRows (NumMedia,mysql_res);
|
2016-04-04 21:51:21 +02:00
|
|
|
|
|
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-07 12:36:58 +02:00
|
|
|
|
/*****************************************************************************/
|
2019-03-18 19:35:23 +01:00
|
|
|
|
/******* Remove all media associated to all answers of a test question *******/
|
2016-04-04 21:51:21 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2019-03-19 11:20:29 +01:00
|
|
|
|
static void Tst_RemoveMediaFromAllAnsOfQst (long CrsCod,long QstCod)
|
2016-04-04 21:51:21 +02:00
|
|
|
|
{
|
|
|
|
|
MYSQL_RES *mysql_res;
|
2019-03-02 21:49:11 +01:00
|
|
|
|
unsigned NumMedia;
|
2016-04-04 21:51:21 +02:00
|
|
|
|
|
2019-03-18 19:35:23 +01:00
|
|
|
|
/***** Get media codes associated to answers of test questions from database *****/
|
2021-04-16 13:51:12 +02:00
|
|
|
|
NumMedia = (unsigned)
|
|
|
|
|
DB_QuerySELECT (&mysql_res,"can not get media",
|
|
|
|
|
"SELECT tst_answers.MedCod"
|
|
|
|
|
" FROM tst_answers,"
|
|
|
|
|
"tst_questions"
|
|
|
|
|
" WHERE tst_answers.QstCod=%ld"
|
|
|
|
|
" AND tst_answers.QstCod=tst_questions.QstCod"
|
|
|
|
|
" AND tst_questions.CrsCod=%ld" // Extra check
|
|
|
|
|
" AND tst_questions.QstCod=%ld", // Extra check
|
|
|
|
|
QstCod,
|
|
|
|
|
CrsCod,
|
|
|
|
|
QstCod);
|
2016-04-07 12:36:58 +02:00
|
|
|
|
|
2019-03-18 19:35:23 +01:00
|
|
|
|
/***** Go over result removing media *****/
|
2019-03-18 15:42:22 +01:00
|
|
|
|
Med_RemoveMediaFromAllRows (NumMedia,mysql_res);
|
2020-05-18 22:59:07 +02:00
|
|
|
|
|
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/** Remove all media associated to stems of all test questions in a course ***/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void Tst_RemoveAllMedFilesFromStemOfAllQstsInCrs (long CrsCod)
|
|
|
|
|
{
|
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
unsigned NumMedia;
|
|
|
|
|
|
|
|
|
|
/***** Get media codes associated to stems of test questions from database *****/
|
|
|
|
|
NumMedia =
|
|
|
|
|
(unsigned) DB_QuerySELECT (&mysql_res,"can not get media",
|
|
|
|
|
"SELECT MedCod" // row[0]
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM tst_questions"
|
2020-05-18 22:59:07 +02:00
|
|
|
|
" WHERE CrsCod=%ld",
|
|
|
|
|
CrsCod);
|
|
|
|
|
|
|
|
|
|
/***** Go over result removing media files *****/
|
|
|
|
|
Med_RemoveMediaFromAllRows (NumMedia,mysql_res);
|
2016-04-07 12:36:58 +02:00
|
|
|
|
|
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2019-03-18 19:35:23 +01:00
|
|
|
|
/* Remove media associated to all answers of all test questions in a course **/
|
2016-04-07 12:36:58 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2019-03-17 01:38:10 +01:00
|
|
|
|
static void Tst_RemoveAllMedFilesFromAnsOfAllQstsInCrs (long CrsCod)
|
2016-04-07 12:36:58 +02:00
|
|
|
|
{
|
|
|
|
|
MYSQL_RES *mysql_res;
|
2019-03-02 21:49:11 +01:00
|
|
|
|
unsigned NumMedia;
|
2016-04-07 12:36:58 +02:00
|
|
|
|
|
2019-03-02 21:49:11 +01:00
|
|
|
|
/***** Get names of media files associated to answers of test questions from database *****/
|
2021-04-16 13:51:12 +02:00
|
|
|
|
NumMedia = (unsigned)
|
|
|
|
|
DB_QuerySELECT (&mysql_res,"can not get media",
|
|
|
|
|
"SELECT tst_answers.MedCod"
|
|
|
|
|
" FROM tst_questions,"
|
|
|
|
|
"tst_answers"
|
|
|
|
|
" WHERE tst_questions.CrsCod=%ld"
|
|
|
|
|
" AND tst_questions.QstCod=tst_answers.QstCod",
|
|
|
|
|
CrsCod);
|
2016-04-04 21:51:21 +02:00
|
|
|
|
|
2019-03-02 21:49:11 +01:00
|
|
|
|
/***** Go over result removing media files *****/
|
2019-03-18 15:42:22 +01:00
|
|
|
|
Med_RemoveMediaFromAllRows (NumMedia,mysql_res);
|
2016-04-04 21:51:21 +02:00
|
|
|
|
|
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-01 23:55:08 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********************** Get stats about test questions **********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void Tst_GetTestStats (Tst_AnswerType_t AnsType,struct Tst_Stats *Stats)
|
|
|
|
|
{
|
|
|
|
|
Stats->NumQsts = 0;
|
|
|
|
|
Stats->NumCoursesWithQuestions = Stats->NumCoursesWithPluggableQuestions = 0;
|
|
|
|
|
Stats->AvgQstsPerCourse = 0.0;
|
|
|
|
|
Stats->NumHits = 0L;
|
|
|
|
|
Stats->AvgHitsPerCourse = 0.0;
|
|
|
|
|
Stats->AvgHitsPerQuestion = 0.0;
|
|
|
|
|
Stats->TotalScore = 0.0;
|
|
|
|
|
Stats->AvgScorePerQuestion = 0.0;
|
|
|
|
|
|
|
|
|
|
if (Tst_GetNumTstQuestions (Gbl.Scope.Current,AnsType,Stats))
|
|
|
|
|
{
|
|
|
|
|
if ((Stats->NumCoursesWithQuestions = Tst_GetNumCoursesWithTstQuestions (Gbl.Scope.Current,AnsType)) != 0)
|
|
|
|
|
{
|
|
|
|
|
Stats->NumCoursesWithPluggableQuestions = Tst_GetNumCoursesWithPluggableTstQuestions (Gbl.Scope.Current,AnsType);
|
2019-11-11 00:15:44 +01:00
|
|
|
|
Stats->AvgQstsPerCourse = (double) Stats->NumQsts / (double) Stats->NumCoursesWithQuestions;
|
|
|
|
|
Stats->AvgHitsPerCourse = (double) Stats->NumHits / (double) Stats->NumCoursesWithQuestions;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
2019-11-11 00:15:44 +01:00
|
|
|
|
Stats->AvgHitsPerQuestion = (double) Stats->NumHits / (double) Stats->NumQsts;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
if (Stats->NumHits)
|
|
|
|
|
Stats->AvgScorePerQuestion = Stats->TotalScore / (double) Stats->NumHits;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********************** Get number of test questions ************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
// Returns the number of test questions
|
|
|
|
|
// in this location (all the platform, current degree or current course)
|
|
|
|
|
|
2021-05-27 23:30:16 +02:00
|
|
|
|
static unsigned Tst_GetNumTstQuestions (HieLvl_Level_t Scope,Tst_AnswerType_t AnsType,struct Tst_Stats *Stats)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
MYSQL_ROW row;
|
|
|
|
|
|
|
|
|
|
/***** Get number of test questions from database *****/
|
|
|
|
|
switch (Scope)
|
|
|
|
|
{
|
2021-05-27 23:30:16 +02:00
|
|
|
|
case HieLvl_SYS:
|
2020-05-11 14:56:49 +02:00
|
|
|
|
if (AnsType == Tst_ANS_UNKNOWN) // Any type
|
2018-11-02 11:33:58 +01:00
|
|
|
|
DB_QuerySELECT (&mysql_res,"can not get number of test questions",
|
2021-04-05 23:45:24 +02:00
|
|
|
|
"SELECT COUNT(*)," // row[0]
|
|
|
|
|
"SUM(NumHits)," // row[1]
|
|
|
|
|
"SUM(Score)" // row[2]
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM tst_questions");
|
2014-12-01 23:55:08 +01:00
|
|
|
|
else
|
2018-11-02 11:33:58 +01:00
|
|
|
|
DB_QuerySELECT (&mysql_res,"can not get number of test questions",
|
2021-04-05 23:45:24 +02:00
|
|
|
|
"SELECT COUNT(*)," // row[0]
|
|
|
|
|
"SUM(NumHits)," // row[1]
|
|
|
|
|
"SUM(Score)" // row[2]
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM tst_questions"
|
2018-11-02 11:33:58 +01:00
|
|
|
|
" WHERE AnsType='%s'",
|
|
|
|
|
Tst_StrAnswerTypesDB[AnsType]);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
break;
|
2021-05-27 23:30:16 +02:00
|
|
|
|
case HieLvl_CTY:
|
2020-05-11 14:56:49 +02:00
|
|
|
|
if (AnsType == Tst_ANS_UNKNOWN) // Any type
|
2018-11-02 11:33:58 +01:00
|
|
|
|
DB_QuerySELECT (&mysql_res,"can not get number of test questions",
|
2021-04-05 23:45:24 +02:00
|
|
|
|
"SELECT COUNT(*)," // row[0]
|
|
|
|
|
"SUM(NumHits)," // row[1]
|
|
|
|
|
"SUM(Score)" // row[2]
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM ins_instits,"
|
|
|
|
|
"ctr_centers,"
|
|
|
|
|
"deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions"
|
2021-03-07 21:52:56 +01:00
|
|
|
|
" WHERE ins_instits.CtyCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND ins_instits.InsCod=ctr_centers.InsCod"
|
|
|
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod",
|
2019-04-03 20:57:04 +02:00
|
|
|
|
Gbl.Hierarchy.Cty.CtyCod);
|
2015-03-09 11:03:55 +01:00
|
|
|
|
else
|
2018-11-02 11:33:58 +01:00
|
|
|
|
DB_QuerySELECT (&mysql_res,"can not get number of test questions",
|
2021-04-05 23:45:24 +02:00
|
|
|
|
"SELECT COUNT(*)," // row[0]
|
|
|
|
|
"SUM(NumHits)," // row[1]
|
|
|
|
|
"SUM(Score)" // row[2]
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM ins_instits,"
|
|
|
|
|
"ctr_centers,"
|
|
|
|
|
"deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions"
|
2021-03-07 21:52:56 +01:00
|
|
|
|
" WHERE ins_instits.CtyCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND ins_instits.InsCod=ctr_centers.InsCod"
|
|
|
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.AnsType='%s'",
|
2019-04-03 20:57:04 +02:00
|
|
|
|
Gbl.Hierarchy.Cty.CtyCod,
|
2018-11-02 11:33:58 +01:00
|
|
|
|
Tst_StrAnswerTypesDB[AnsType]);
|
2015-03-09 11:03:55 +01:00
|
|
|
|
break;
|
2021-05-27 23:30:16 +02:00
|
|
|
|
case HieLvl_INS:
|
2020-05-11 14:56:49 +02:00
|
|
|
|
if (AnsType == Tst_ANS_UNKNOWN) // Any type
|
2018-11-02 11:33:58 +01:00
|
|
|
|
DB_QuerySELECT (&mysql_res,"can not get number of test questions",
|
2021-04-05 23:45:24 +02:00
|
|
|
|
"SELECT COUNT(*)," // row[0]
|
|
|
|
|
"SUM(NumHits)," // row[1]
|
|
|
|
|
"SUM(Score)" // row[2]
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM ctr_centers,"
|
|
|
|
|
"deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions"
|
2021-03-07 21:21:04 +01:00
|
|
|
|
" WHERE ctr_centers.InsCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod",
|
2019-04-03 20:57:04 +02:00
|
|
|
|
Gbl.Hierarchy.Ins.InsCod);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
else
|
2018-11-02 11:33:58 +01:00
|
|
|
|
DB_QuerySELECT (&mysql_res,"can not get number of test questions",
|
2021-04-05 23:45:24 +02:00
|
|
|
|
"SELECT COUNT(*)," // row[0]
|
|
|
|
|
"SUM(NumHits)," // row[1]
|
|
|
|
|
"SUM(Score)" // row[2]
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM ctr_centers,"
|
|
|
|
|
"deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions"
|
2021-03-07 21:21:04 +01:00
|
|
|
|
" WHERE ctr_centers.InsCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.AnsType='%s'",
|
2019-04-03 20:57:04 +02:00
|
|
|
|
Gbl.Hierarchy.Ins.InsCod,
|
2018-11-02 11:33:58 +01:00
|
|
|
|
Tst_StrAnswerTypesDB[AnsType]);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
break;
|
2021-05-27 23:30:16 +02:00
|
|
|
|
case HieLvl_CTR:
|
2020-05-11 14:56:49 +02:00
|
|
|
|
if (AnsType == Tst_ANS_UNKNOWN) // Any type
|
2018-11-02 11:33:58 +01:00
|
|
|
|
DB_QuerySELECT (&mysql_res,"can not get number of test questions",
|
2021-04-05 23:45:24 +02:00
|
|
|
|
"SELECT COUNT(*)," // row[0]
|
|
|
|
|
"SUM(NumHits)," // row[1]
|
|
|
|
|
"SUM(Score)" // row[2]
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions"
|
2021-03-06 00:38:52 +01:00
|
|
|
|
" WHERE deg_degrees.CtrCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod",
|
2019-04-03 20:57:04 +02:00
|
|
|
|
Gbl.Hierarchy.Ctr.CtrCod);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
else
|
2018-11-02 11:33:58 +01:00
|
|
|
|
DB_QuerySELECT (&mysql_res,"can not get number of test questions",
|
2021-04-05 23:45:24 +02:00
|
|
|
|
"SELECT COUNT(*)," // row[0]
|
|
|
|
|
"SUM(NumHits)," // row[1]
|
|
|
|
|
"SUM(Score)" // row[2]
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions"
|
2021-03-06 00:38:52 +01:00
|
|
|
|
" WHERE deg_degrees.CtrCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.AnsType='%s'",
|
2019-04-03 20:57:04 +02:00
|
|
|
|
Gbl.Hierarchy.Ctr.CtrCod,
|
2018-11-02 11:33:58 +01:00
|
|
|
|
Tst_StrAnswerTypesDB[AnsType]);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
break;
|
2021-05-27 23:30:16 +02:00
|
|
|
|
case HieLvl_DEG:
|
2020-05-11 14:56:49 +02:00
|
|
|
|
if (AnsType == Tst_ANS_UNKNOWN) // Any type
|
2018-11-02 11:33:58 +01:00
|
|
|
|
DB_QuerySELECT (&mysql_res,"can not get number of test questions",
|
2021-04-05 23:45:24 +02:00
|
|
|
|
"SELECT COUNT(*)," // row[0]
|
|
|
|
|
"SUM(NumHits)," // row[1]
|
|
|
|
|
"SUM(Score)" // row[2]
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM crs_courses,"
|
|
|
|
|
"tst_questions"
|
2021-03-07 17:45:36 +01:00
|
|
|
|
" WHERE crs_courses.DegCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod",
|
2019-04-03 20:57:04 +02:00
|
|
|
|
Gbl.Hierarchy.Deg.DegCod);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
else
|
2018-11-02 11:33:58 +01:00
|
|
|
|
DB_QuerySELECT (&mysql_res,"can not get number of test questions",
|
2021-04-05 23:45:24 +02:00
|
|
|
|
"SELECT COUNT(*)," // row[0]
|
|
|
|
|
"SUM(NumHits)," // row[1]
|
|
|
|
|
"SUM(Score)" // row[2]
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM crs_courses,"
|
|
|
|
|
"tst_questions"
|
2021-03-07 17:45:36 +01:00
|
|
|
|
" WHERE crs_courses.DegCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.AnsType='%s'",
|
2019-04-03 20:57:04 +02:00
|
|
|
|
Gbl.Hierarchy.Deg.DegCod,
|
2018-11-02 11:33:58 +01:00
|
|
|
|
Tst_StrAnswerTypesDB[AnsType]);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
break;
|
2021-05-27 23:30:16 +02:00
|
|
|
|
case HieLvl_CRS:
|
2020-05-11 14:56:49 +02:00
|
|
|
|
if (AnsType == Tst_ANS_UNKNOWN) // Any type
|
2018-11-02 11:33:58 +01:00
|
|
|
|
DB_QuerySELECT (&mysql_res,"can not get number of test questions",
|
2021-04-05 23:45:24 +02:00
|
|
|
|
"SELECT COUNT(*)," // row[0]
|
|
|
|
|
"SUM(NumHits)," // row[1]
|
|
|
|
|
"SUM(Score)" // row[2]
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM tst_questions"
|
2018-11-02 11:33:58 +01:00
|
|
|
|
" WHERE CrsCod=%ld",
|
2019-04-04 10:45:15 +02:00
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
else
|
2018-11-02 11:33:58 +01:00
|
|
|
|
DB_QuerySELECT (&mysql_res,"can not get number of test questions",
|
2021-04-05 23:45:24 +02:00
|
|
|
|
"SELECT COUNT(*)," // row[0]
|
|
|
|
|
"SUM(NumHits)," // row[1]
|
|
|
|
|
"SUM(Score)" // row[2]
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM tst_questions"
|
|
|
|
|
" WHERE CrsCod=%ld"
|
|
|
|
|
" AND AnsType='%s'",
|
2019-04-04 10:45:15 +02:00
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod,
|
2018-11-02 11:33:58 +01:00
|
|
|
|
Tst_StrAnswerTypesDB[AnsType]);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
break;
|
|
|
|
|
default:
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_WrongScopeExit ();
|
2014-12-01 23:55:08 +01:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** Get number of questions *****/
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
|
|
|
|
if (sscanf (row[0],"%u",&(Stats->NumQsts)) != 1)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_ShowErrorAndExit ("Error when getting number of test questions.");
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
if (Stats->NumQsts)
|
|
|
|
|
{
|
|
|
|
|
if (sscanf (row[1],"%lu",&(Stats->NumHits)) != 1)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_ShowErrorAndExit ("Error when getting total number of hits in test questions.");
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
2016-06-04 14:21:01 +02:00
|
|
|
|
Str_SetDecimalPointToUS (); // To get the decimal point as a dot
|
2014-12-01 23:55:08 +01:00
|
|
|
|
if (sscanf (row[2],"%lf",&(Stats->TotalScore)) != 1)
|
2021-04-26 15:27:27 +02:00
|
|
|
|
Err_ShowErrorAndExit ("Error when getting total score in test questions.");
|
2016-06-04 14:21:01 +02:00
|
|
|
|
Str_SetDecimalPointToLocal (); // Return to local system
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Stats->NumHits = 0L;
|
|
|
|
|
Stats->TotalScore = 0.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
|
|
|
|
|
return Stats->NumQsts;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/**************** Get number of courses with test questions ******************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
// Returns the number of courses with test questions
|
|
|
|
|
// in this location (all the platform, current degree or current course)
|
|
|
|
|
|
2021-05-27 23:30:16 +02:00
|
|
|
|
static unsigned Tst_GetNumCoursesWithTstQuestions (HieLvl_Level_t Scope,Tst_AnswerType_t AnsType)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
|
|
|
|
/***** Get number of courses with test questions from database *****/
|
|
|
|
|
switch (Scope)
|
|
|
|
|
{
|
2021-05-27 23:30:16 +02:00
|
|
|
|
case HieLvl_SYS:
|
2020-05-11 14:56:49 +02:00
|
|
|
|
if (AnsType == Tst_ANS_UNKNOWN) // Any type
|
2021-03-07 17:45:36 +01:00
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with test questions",
|
2018-11-02 11:33:58 +01:00
|
|
|
|
"SELECT COUNT(DISTINCT CrsCod)"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM tst_questions");
|
2021-03-07 17:45:36 +01:00
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT CrsCod)"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM tst_questions"
|
2021-03-07 17:45:36 +01:00
|
|
|
|
" WHERE AnsType='%s'",
|
|
|
|
|
Tst_StrAnswerTypesDB[AnsType]);
|
2021-05-27 23:30:16 +02:00
|
|
|
|
case HieLvl_CTY:
|
2020-05-11 14:56:49 +02:00
|
|
|
|
if (AnsType == Tst_ANS_UNKNOWN) // Any type
|
2021-03-07 17:45:36 +01:00
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with test questions",
|
2018-11-02 11:33:58 +01:00
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM ins_instits,"
|
|
|
|
|
"ctr_centers,"
|
|
|
|
|
"deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions"
|
2021-03-07 21:52:56 +01:00
|
|
|
|
" WHERE ins_instits.CtyCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND ins_instits.InsCod=ctr_centers.InsCod"
|
|
|
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod",
|
2019-04-03 20:57:04 +02:00
|
|
|
|
Gbl.Hierarchy.Cty.CtyCod);
|
2021-03-07 17:45:36 +01:00
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM ins_instits,"
|
|
|
|
|
"ctr_centers,"
|
|
|
|
|
"deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions"
|
2021-03-07 21:52:56 +01:00
|
|
|
|
" WHERE ins_instits.CtyCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND ins_instits.InsCod=ctr_centers.InsCod"
|
|
|
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.AnsType='%s'",
|
2021-03-07 17:45:36 +01:00
|
|
|
|
Gbl.Hierarchy.Cty.CtyCod,
|
|
|
|
|
Tst_StrAnswerTypesDB[AnsType]);
|
2021-05-27 23:30:16 +02:00
|
|
|
|
case HieLvl_INS:
|
2020-05-11 14:56:49 +02:00
|
|
|
|
if (AnsType == Tst_ANS_UNKNOWN) // Any type
|
2021-03-07 17:45:36 +01:00
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with test questions",
|
2018-11-02 11:33:58 +01:00
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM ctr_centers,"
|
|
|
|
|
"deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions"
|
2021-03-07 21:21:04 +01:00
|
|
|
|
" WHERE ctr_centers.InsCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod",
|
2019-04-03 20:57:04 +02:00
|
|
|
|
Gbl.Hierarchy.Ins.InsCod);
|
2021-03-07 17:45:36 +01:00
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM ctr_centers,"
|
|
|
|
|
"deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions"
|
2021-03-07 21:21:04 +01:00
|
|
|
|
" WHERE ctr_centers.InsCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.AnsType='%s'",
|
2021-03-07 17:45:36 +01:00
|
|
|
|
Gbl.Hierarchy.Ins.InsCod,
|
|
|
|
|
Tst_StrAnswerTypesDB[AnsType]);
|
2021-05-27 23:30:16 +02:00
|
|
|
|
case HieLvl_CTR:
|
2020-05-11 14:56:49 +02:00
|
|
|
|
if (AnsType == Tst_ANS_UNKNOWN) // Any type
|
2021-03-07 17:45:36 +01:00
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with test questions",
|
2018-11-02 11:33:58 +01:00
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions"
|
2021-03-06 00:38:52 +01:00
|
|
|
|
" WHERE deg_degrees.CtrCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod",
|
2019-04-03 20:57:04 +02:00
|
|
|
|
Gbl.Hierarchy.Ctr.CtrCod);
|
2021-03-07 17:45:36 +01:00
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions"
|
2021-03-07 17:45:36 +01:00
|
|
|
|
" WHERE deg_degrees.CtrCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.AnsType='%s'",
|
2021-03-07 17:45:36 +01:00
|
|
|
|
Gbl.Hierarchy.Ctr.CtrCod,
|
|
|
|
|
Tst_StrAnswerTypesDB[AnsType]);
|
2021-05-27 23:30:16 +02:00
|
|
|
|
case HieLvl_DEG:
|
2020-05-11 14:56:49 +02:00
|
|
|
|
if (AnsType == Tst_ANS_UNKNOWN) // Any type
|
2021-03-07 17:45:36 +01:00
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with test questions",
|
2018-11-02 11:33:58 +01:00
|
|
|
|
"SELECT COUNTDISTINCT (tst_questions.CrsCod)"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM crs_courses,"
|
|
|
|
|
"tst_questions"
|
2021-03-07 17:45:36 +01:00
|
|
|
|
" WHERE crs_courses.DegCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod",
|
2019-04-03 20:57:04 +02:00
|
|
|
|
Gbl.Hierarchy.Deg.DegCod);
|
2021-03-07 17:45:36 +01:00
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM crs_courses,"
|
|
|
|
|
"tst_questions"
|
2021-03-07 17:45:36 +01:00
|
|
|
|
" WHERE crs_courses.DegCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.AnsType='%s'",
|
2021-03-07 17:45:36 +01:00
|
|
|
|
Gbl.Hierarchy.Deg.DegCod,
|
|
|
|
|
Tst_StrAnswerTypesDB[AnsType]);
|
2021-05-27 23:30:16 +02:00
|
|
|
|
case HieLvl_CRS:
|
2020-05-11 14:56:49 +02:00
|
|
|
|
if (AnsType == Tst_ANS_UNKNOWN) // Any type
|
2021-03-07 17:45:36 +01:00
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with test questions",
|
2018-11-02 11:33:58 +01:00
|
|
|
|
"SELECT COUNT(DISTINCT CrsCod)"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM tst_questions"
|
2018-11-02 11:33:58 +01:00
|
|
|
|
" WHERE CrsCod=%ld",
|
2019-04-04 10:45:15 +02:00
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod);
|
2021-03-07 17:45:36 +01:00
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT CrsCod)"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM tst_questions"
|
2021-03-07 17:45:36 +01:00
|
|
|
|
" WHERE CrsCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND AnsType='%s'",
|
2021-03-07 17:45:36 +01:00
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod,
|
|
|
|
|
Tst_StrAnswerTypesDB[AnsType]);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
default:
|
2021-03-07 17:45:36 +01:00
|
|
|
|
return 0;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********** Get number of courses with pluggable test questions *************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
// Returns the number of courses with pluggable test questions
|
|
|
|
|
// in this location (all the platform, current degree or current course)
|
|
|
|
|
|
2021-05-27 23:30:16 +02:00
|
|
|
|
static unsigned Tst_GetNumCoursesWithPluggableTstQuestions (HieLvl_Level_t Scope,Tst_AnswerType_t AnsType)
|
2014-12-01 23:55:08 +01:00
|
|
|
|
{
|
2020-03-21 15:41:25 +01:00
|
|
|
|
extern const char *TstCfg_PluggableDB[TstCfg_NUM_OPTIONS_PLUGGABLE];
|
2014-12-01 23:55:08 +01:00
|
|
|
|
|
|
|
|
|
/***** Get number of courses with test questions from database *****/
|
|
|
|
|
switch (Scope)
|
|
|
|
|
{
|
2021-05-27 23:30:16 +02:00
|
|
|
|
case HieLvl_SYS:
|
2020-05-11 14:56:49 +02:00
|
|
|
|
if (AnsType == Tst_ANS_UNKNOWN) // Any type
|
2021-03-07 17:45:36 +01:00
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with pluggable test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM tst_questions,"
|
|
|
|
|
"tst_config"
|
2021-03-07 17:45:36 +01:00
|
|
|
|
" WHERE tst_questions.CrsCod=tst_config.CrsCod"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND tst_config.pluggable='%s'",
|
2021-03-07 17:45:36 +01:00
|
|
|
|
TstCfg_PluggableDB[TstCfg_PLUGGABLE_YES]);
|
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with pluggable test questions",
|
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM tst_questions,"
|
|
|
|
|
"tst_config"
|
2021-03-07 17:45:36 +01:00
|
|
|
|
" WHERE tst_questions.AnsType='%s'"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND tst_questions.CrsCod=tst_config.CrsCod"
|
|
|
|
|
" AND tst_config.pluggable='%s'",
|
2021-03-07 17:45:36 +01:00
|
|
|
|
Tst_StrAnswerTypesDB[AnsType],
|
|
|
|
|
TstCfg_PluggableDB[TstCfg_PLUGGABLE_YES]);
|
2021-05-27 23:30:16 +02:00
|
|
|
|
case HieLvl_CTY:
|
2020-05-11 14:56:49 +02:00
|
|
|
|
if (AnsType == Tst_ANS_UNKNOWN) // Any type
|
2021-03-07 17:45:36 +01:00
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with pluggable test questions",
|
2018-11-02 11:33:58 +01:00
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM ins_instits,"
|
|
|
|
|
"ctr_centers,"
|
|
|
|
|
"deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions,"
|
|
|
|
|
"tst_config"
|
2021-03-07 21:52:56 +01:00
|
|
|
|
" WHERE ins_instits.CtyCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND ins_instits.InsCod=ctr_centers.InsCod"
|
|
|
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.CrsCod=tst_config.CrsCod"
|
|
|
|
|
" AND tst_config.pluggable='%s'",
|
2019-04-03 20:57:04 +02:00
|
|
|
|
Gbl.Hierarchy.Cty.CtyCod,
|
2020-03-21 15:41:25 +01:00
|
|
|
|
TstCfg_PluggableDB[TstCfg_PLUGGABLE_YES]);
|
2021-03-07 17:45:36 +01:00
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with pluggable test questions",
|
2018-11-02 11:33:58 +01:00
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM ins_instits,"
|
|
|
|
|
"ctr_centers,"
|
|
|
|
|
"deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions,"
|
|
|
|
|
"tst_config"
|
2021-03-07 21:52:56 +01:00
|
|
|
|
" WHERE ins_instits.CtyCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND ins_instits.InsCod=ctr_centers.InsCod"
|
|
|
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.AnsType='%s'"
|
|
|
|
|
" AND tst_questions.CrsCod=tst_config.CrsCod"
|
|
|
|
|
" AND tst_config.pluggable='%s'",
|
2019-04-03 20:57:04 +02:00
|
|
|
|
Gbl.Hierarchy.Cty.CtyCod,
|
2018-11-02 11:33:58 +01:00
|
|
|
|
Tst_StrAnswerTypesDB[AnsType],
|
2020-03-21 15:41:25 +01:00
|
|
|
|
TstCfg_PluggableDB[TstCfg_PLUGGABLE_YES]);
|
2021-05-27 23:30:16 +02:00
|
|
|
|
case HieLvl_INS:
|
2020-05-11 14:56:49 +02:00
|
|
|
|
if (AnsType == Tst_ANS_UNKNOWN) // Any type
|
2021-03-07 17:45:36 +01:00
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with pluggable test questions",
|
2018-11-02 11:33:58 +01:00
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM ctr_centers,"
|
|
|
|
|
"deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions,"
|
|
|
|
|
"tst_config"
|
2021-03-07 21:21:04 +01:00
|
|
|
|
" WHERE ctr_centers.InsCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.CrsCod=tst_config.CrsCod"
|
|
|
|
|
" AND tst_config.pluggable='%s'",
|
2019-04-03 20:57:04 +02:00
|
|
|
|
Gbl.Hierarchy.Ins.InsCod,
|
2020-03-21 15:41:25 +01:00
|
|
|
|
TstCfg_PluggableDB[TstCfg_PLUGGABLE_YES]);
|
2021-03-07 17:45:36 +01:00
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with pluggable test questions",
|
2018-11-02 11:33:58 +01:00
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM ctr_centers,"
|
|
|
|
|
"deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions,"
|
|
|
|
|
"tst_config"
|
2021-03-07 21:21:04 +01:00
|
|
|
|
" WHERE ctr_centers.InsCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.AnsType='%s'"
|
|
|
|
|
" AND tst_questions.CrsCod=tst_config.CrsCod"
|
|
|
|
|
" AND tst_config.pluggable='%s'",
|
2019-04-03 20:57:04 +02:00
|
|
|
|
Gbl.Hierarchy.Ins.InsCod,
|
2018-11-02 11:33:58 +01:00
|
|
|
|
Tst_StrAnswerTypesDB[AnsType],
|
2020-03-21 15:41:25 +01:00
|
|
|
|
TstCfg_PluggableDB[TstCfg_PLUGGABLE_YES]);
|
2021-05-27 23:30:16 +02:00
|
|
|
|
case HieLvl_CTR:
|
2020-05-11 14:56:49 +02:00
|
|
|
|
if (AnsType == Tst_ANS_UNKNOWN) // Any type
|
2021-03-07 17:45:36 +01:00
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with pluggable test questions",
|
2018-11-02 11:33:58 +01:00
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions,"
|
|
|
|
|
"tst_config"
|
2021-03-06 00:38:52 +01:00
|
|
|
|
" WHERE deg_degrees.CtrCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.CrsCod=tst_config.CrsCod"
|
|
|
|
|
" AND tst_config.pluggable='%s'",
|
2019-04-03 20:57:04 +02:00
|
|
|
|
Gbl.Hierarchy.Ctr.CtrCod,
|
2020-03-21 15:41:25 +01:00
|
|
|
|
TstCfg_PluggableDB[TstCfg_PLUGGABLE_YES]);
|
2021-03-07 17:45:36 +01:00
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with pluggable test questions",
|
2018-11-02 11:33:58 +01:00
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM deg_degrees,"
|
|
|
|
|
"crs_courses,"
|
|
|
|
|
"tst_questions,"
|
|
|
|
|
"tst_config"
|
2021-03-06 00:38:52 +01:00
|
|
|
|
" WHERE deg_degrees.CtrCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.AnsType='%s'"
|
|
|
|
|
" AND tst_questions.CrsCod=tst_config.CrsCod"
|
|
|
|
|
" AND tst_config.pluggable='%s'",
|
2019-04-03 20:57:04 +02:00
|
|
|
|
Gbl.Hierarchy.Ctr.CtrCod,
|
2018-11-02 11:33:58 +01:00
|
|
|
|
Tst_StrAnswerTypesDB[AnsType],
|
2020-03-21 15:41:25 +01:00
|
|
|
|
TstCfg_PluggableDB[TstCfg_PLUGGABLE_YES]);
|
2021-05-27 23:30:16 +02:00
|
|
|
|
case HieLvl_DEG:
|
2020-05-11 14:56:49 +02:00
|
|
|
|
if (AnsType == Tst_ANS_UNKNOWN) // Any type
|
2021-03-07 17:45:36 +01:00
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with pluggable test questions",
|
2018-11-02 11:33:58 +01:00
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM crs_courses,"
|
|
|
|
|
"tst_questions,"
|
|
|
|
|
"tst_config"
|
2021-03-07 17:45:36 +01:00
|
|
|
|
" WHERE crs_courses.DegCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.CrsCod=tst_config.CrsCod"
|
|
|
|
|
" AND tst_config.pluggable='%s'",
|
2019-04-03 20:57:04 +02:00
|
|
|
|
Gbl.Hierarchy.Deg.DegCod,
|
2020-03-21 15:41:25 +01:00
|
|
|
|
TstCfg_PluggableDB[TstCfg_PLUGGABLE_YES]);
|
2021-03-07 17:45:36 +01:00
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with pluggable test questions",
|
2018-11-02 11:33:58 +01:00
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM crs_courses,"
|
|
|
|
|
"tst_questions,"
|
|
|
|
|
"tst_config"
|
2021-03-07 17:45:36 +01:00
|
|
|
|
" WHERE crs_courses.DegCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND crs_courses.CrsCod=tst_questions.CrsCod"
|
|
|
|
|
" AND tst_questions.AnsType='%s'"
|
|
|
|
|
" AND tst_questions.CrsCod=tst_config.CrsCod"
|
|
|
|
|
" AND tst_config.pluggable='%s'",
|
2019-04-03 20:57:04 +02:00
|
|
|
|
Gbl.Hierarchy.Deg.DegCod,
|
2018-11-02 11:33:58 +01:00
|
|
|
|
Tst_StrAnswerTypesDB[AnsType],
|
2020-03-21 15:41:25 +01:00
|
|
|
|
TstCfg_PluggableDB[TstCfg_PLUGGABLE_YES]);
|
2021-05-27 23:30:16 +02:00
|
|
|
|
case HieLvl_CRS:
|
2020-05-11 14:56:49 +02:00
|
|
|
|
if (AnsType == Tst_ANS_UNKNOWN) // Any type
|
2021-03-07 17:45:36 +01:00
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with pluggable test questions",
|
2018-11-02 11:33:58 +01:00
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM tst_questions,"
|
|
|
|
|
"tst_config"
|
2018-11-02 11:33:58 +01:00
|
|
|
|
" WHERE tst_questions.CrsCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND tst_questions.CrsCod=tst_config.CrsCod"
|
|
|
|
|
" AND tst_config.pluggable='%s'",
|
2019-04-04 10:45:15 +02:00
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod,
|
2020-03-21 15:41:25 +01:00
|
|
|
|
TstCfg_PluggableDB[TstCfg_PLUGGABLE_YES]);
|
2021-03-07 17:45:36 +01:00
|
|
|
|
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QueryCOUNT ("can not get number of courses with pluggable test questions",
|
2018-11-02 11:33:58 +01:00
|
|
|
|
"SELECT COUNT(DISTINCT tst_questions.CrsCod)"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" FROM tst_questions,"
|
|
|
|
|
"tst_config"
|
2018-11-02 11:33:58 +01:00
|
|
|
|
" WHERE tst_questions.CrsCod=%ld"
|
2021-03-29 16:38:56 +02:00
|
|
|
|
" AND tst_questions.AnsType='%s'"
|
|
|
|
|
" AND tst_questions.CrsCod=tst_config.CrsCod"
|
|
|
|
|
" AND tst_config.pluggable='%s'",
|
2019-04-04 10:45:15 +02:00
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod,
|
2018-11-02 11:33:58 +01:00
|
|
|
|
Tst_StrAnswerTypesDB[AnsType],
|
2020-03-21 15:41:25 +01:00
|
|
|
|
TstCfg_PluggableDB[TstCfg_PLUGGABLE_YES]);
|
2014-12-01 23:55:08 +01:00
|
|
|
|
default:
|
2021-03-07 17:45:36 +01:00
|
|
|
|
return 0;
|
2014-12-01 23:55:08 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-09-24 21:49:06 +02:00
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********** Get suffled/not-shuffled answers indexes of question ************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
unsigned Tst_DB_GetShuffledAnswersIndexes (MYSQL_RES **mysql_res,
|
|
|
|
|
const struct Tst_Question *Question)
|
|
|
|
|
{
|
|
|
|
|
return (unsigned)
|
|
|
|
|
DB_QuerySELECT (mysql_res,"can not get questions of a game",
|
|
|
|
|
"SELECT AnsInd" // row[0]
|
|
|
|
|
" FROM tst_answers"
|
|
|
|
|
" WHERE QstCod=%ld"
|
|
|
|
|
" ORDER BY %s",
|
|
|
|
|
Question->QstCod,
|
|
|
|
|
Question->Answer.Shuffle ? "RAND()" : // Use RAND() because is really random; RAND(NOW()) repeats order
|
|
|
|
|
"AnsInd");
|
|
|
|
|
}
|