diff --git a/.settings/language.settings.xml b/.settings/language.settings.xml
index 549daa6a..53f6545d 100644
--- a/.settings/language.settings.xml
+++ b/.settings/language.settings.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/Makefile b/Makefile
index 401ded7f..12295a84 100644
--- a/Makefile
+++ b/Makefile
@@ -57,9 +57,9 @@ OBJS = swad_account.o swad_action.o swad_agenda.o swad_alert.o \
swad_scope.o swad_search.o swad_session.o swad_setting.o \
swad_statistic.o swad_string.o swad_survey.o swad_syllabus.o \
swad_system_config.o \
- swad_tab.o swad_test.o swad_test_config.o swad_test_import.o \
- swad_test_result.o \
- swad_test_visibility.o swad_theme.o swad_timeline.o swad_timetable.o \
+ swad_tab.o swad_test.o swad_test_config.o swad_test_exam.o \
+ swad_test_import.o swad_test_visibility.o swad_theme.o swad_timeline.o \
+ swad_timetable.o \
swad_user.o \
swad_xml.o \
swad_zip.o
diff --git a/sql/swad.sql b/sql/swad.sql
index c9463f43..84c3f18a 100644
--- a/sql/swad.sql
+++ b/sql/swad.sql
@@ -1304,18 +1304,18 @@ CREATE TABLE IF NOT EXISTS tst_config (
-- Table tst_exam_questions: stores the questions and answers in test exams made by users
--
CREATE TABLE IF NOT EXISTS tst_exam_questions (
- TstCod INT NOT NULL,
+ ExaCod INT NOT NULL,
QstCod INT NOT NULL,
QstInd INT NOT NULL,
Score DOUBLE PRECISION NOT NULL DEFAULT 0,
Indexes TEXT NOT NULL,
Answers TEXT NOT NULL,
- INDEX(TstCod,QstCod));
+ UNIQUE INDEX(ExaCod,QstCod));
--
-- Table tst_exams: stores the test exams made by users
--
CREATE TABLE IF NOT EXISTS tst_exams (
- TstCod INT NOT NULL AUTO_INCREMENT,
+ ExaCod INT NOT NULL AUTO_INCREMENT,
CrsCod INT NOT NULL,
UsrCod INT NOT NULL,
StartTime DATETIME NOT NULL,
@@ -1324,7 +1324,7 @@ CREATE TABLE IF NOT EXISTS tst_exams (
NumQstsNotBlank INT NOT NULL DEFAULT 0,
AllowTeachers ENUM('N','Y') NOT NULL DEFAULT 'N',
Score DOUBLE PRECISION NOT NULL DEFAULT 0,
- UNIQUE INDEX(TstCod),
+ UNIQUE INDEX(ExaCod),
INDEX(CrsCod,UsrCod));
--
-- Table tst_question_tags: stores the tags associated to each test question
diff --git a/swad_API.c b/swad_API.c
index 75ff457f..afee6c47 100644
--- a/swad_API.c
+++ b/swad_API.c
@@ -3949,7 +3949,7 @@ int swad__sendNotice (struct soap *soap,
/****************** Return test configuration in a course ********************/
/*****************************************************************************/
-#define TsR_MAX_BYTES_FEEDBACK_TYPE 32
+#define TstExa_MAX_BYTES_FEEDBACK_TYPE 32
int swad__getTestConfig (struct soap *soap,
char *wsKey,int courseCode, // input
@@ -4001,10 +4001,10 @@ int swad__getTestConfig (struct soap *soap,
getTestConfigOut->minQuestions =
getTestConfigOut->defQuestions =
getTestConfigOut->maxQuestions = 0;
- getTestConfigOut->visibility = TsV_MIN_VISIBILITY;
+ getTestConfigOut->visibility = TstVis_MIN_VISIBILITY;
/* TODO: Remove these lines in 2021 */
- getTestConfigOut->feedback = (char *) soap_malloc (soap,TsR_MAX_BYTES_FEEDBACK_TYPE + 1);
+ getTestConfigOut->feedback = (char *) soap_malloc (soap,TstExa_MAX_BYTES_FEEDBACK_TYPE + 1);
getTestConfigOut->feedback[0] = '\0';
/***** Get test configuration *****/
@@ -4019,26 +4019,26 @@ int swad__getTestConfig (struct soap *soap,
/* Convert from visibility to old feedback */
/* TODO: Remove these lines in 2021 */
- if (!TsV_IsVisibleTotalScore (TstCfg_GetConfigVisibility ()))
+ if (!TstVis_IsVisibleTotalScore (TstCfg_GetConfigVisibility ()))
Str_Copy (getTestConfigOut->feedback,
"nothing",
- TsR_MAX_BYTES_FEEDBACK_TYPE);
- else if (!TsV_IsVisibleEachQstScore (TstCfg_GetConfigVisibility ()))
+ TstExa_MAX_BYTES_FEEDBACK_TYPE);
+ else if (!TstVis_IsVisibleEachQstScore (TstCfg_GetConfigVisibility ()))
Str_Copy (getTestConfigOut->feedback,
"totalResult",
- TsR_MAX_BYTES_FEEDBACK_TYPE);
- else if (!TsV_IsVisibleCorrectAns (TstCfg_GetConfigVisibility ()))
+ TstExa_MAX_BYTES_FEEDBACK_TYPE);
+ else if (!TstVis_IsVisibleCorrectAns (TstCfg_GetConfigVisibility ()))
Str_Copy (getTestConfigOut->feedback,
"eachResult",
- TsR_MAX_BYTES_FEEDBACK_TYPE);
- else if (!TsV_IsVisibleFeedbackTxt (TstCfg_GetConfigVisibility ()))
+ TstExa_MAX_BYTES_FEEDBACK_TYPE);
+ else if (!TstVis_IsVisibleFeedbackTxt (TstCfg_GetConfigVisibility ()))
Str_Copy (getTestConfigOut->feedback,
"eachGoodBad",
- TsR_MAX_BYTES_FEEDBACK_TYPE);
+ TstExa_MAX_BYTES_FEEDBACK_TYPE);
else
Str_Copy (getTestConfigOut->feedback,
"fullFeedback",
- TsR_MAX_BYTES_FEEDBACK_TYPE);
+ TstExa_MAX_BYTES_FEEDBACK_TYPE);
/***** Get number of tests *****/
if (TstCfg_GetConfigPluggable () == TstCfg_PLUGGABLE_YES &&
@@ -4073,7 +4073,7 @@ static int API_GetTstConfig (long CrsCod)
TstCfg_SetConfigMin (0);
TstCfg_SetConfigDef (0);
TstCfg_SetConfigMax (0);
- TstCfg_SetConfigVisibility (TsV_VISIBILITY_DEFAULT);
+ TstCfg_SetConfigVisibility (TstVis_VISIBILITY_DEFAULT);
}
/***** Free structure that stores the query result *****/
@@ -4906,7 +4906,7 @@ int swad__getGames (struct soap *soap,
getGamesOut->gamesArray.__ptr[NumGame].maxGrade = 0.0;
/* Get visibility (row[5]) */
- getGamesOut->gamesArray.__ptr[NumGame].visibility = TsV_GetVisibilityFromStr (row[5]);
+ getGamesOut->gamesArray.__ptr[NumGame].visibility = TstVis_GetVisibilityFromStr (row[5]);
/* Get title of the game (row[6]) */
Length = strlen (row[6]);
diff --git a/swad_account.c b/swad_account.c
index 187d4444..e0eaf61e 100644
--- a/swad_account.c
+++ b/swad_account.c
@@ -45,6 +45,7 @@
#include "swad_parameter.h"
#include "swad_profile.h"
#include "swad_report.h"
+#include "swad_test_exam.h"
#include "swad_timeline.h"
/*****************************************************************************/
@@ -1076,7 +1077,7 @@ void Acc_CompletelyEliminateAccount (struct UsrData *UsrDat,
UsrDat->FullName);
/***** Remove test results made by user in all courses *****/
- TsR_RemoveTestResultsMadeByUsrInAllCrss (UsrDat->UsrCod);
+ TstExa_RemoveExamsMadeByUsrInAllCrss (UsrDat->UsrCod);
/***** Remove user's notifications *****/
Ntf_RemoveUsrNtfs (UsrDat->UsrCod);
diff --git a/swad_action.c b/swad_action.c
index 0ab04afe..dcc894b4 100644
--- a/swad_action.c
+++ b/swad_action.c
@@ -644,12 +644,12 @@ const struct Act_Actions Act_Actions[Act_NUM_ACTIONS] =
[ActRenTag ] = { 143,-1,TabUnk,ActReqTst ,0x220,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Tst_RenameTag ,NULL},
[ActRcvCfgTst ] = { 454,-1,TabUnk,ActReqTst ,0x220,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,TstCfg_ReceiveConfigTst ,NULL},
- [ActReqSeeMyTstRes ] = {1083,-1,TabUnk,ActReqTst ,0x208,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,Dat_SetIniEndDates ,TsR_SelDatesToSeeMyTstResults ,NULL},
- [ActSeeMyTstRes ] = {1084,-1,TabUnk,ActReqTst ,0x208,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,TsR_ShowMyTstResults ,NULL},
- [ActSeeOneTstResMe ] = {1085,-1,TabUnk,ActReqTst ,0x208,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,TsR_ShowOneTstResult ,NULL},
- [ActReqSeeUsrTstRes ] = {1080,-1,TabUnk,ActReqTst ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,Dat_SetIniEndDates ,TsR_SelUsrsToViewUsrsTstResults,NULL},
- [ActSeeUsrTstRes ] = {1081,-1,TabUnk,ActReqTst ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,TsR_GetUsrsAndShowTstResults ,NULL},
- [ActSeeOneTstResOth ] = {1082,-1,TabUnk,ActReqTst ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,TsR_ShowOneTstResult ,NULL},
+ [ActReqSeeMyTstRes ] = {1083,-1,TabUnk,ActReqTst ,0x208,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,Dat_SetIniEndDates ,TstExa_SelDatesToSeeMyExams ,NULL},
+ [ActSeeMyTstRes ] = {1084,-1,TabUnk,ActReqTst ,0x208,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,TstExa_ShowMyExams ,NULL},
+ [ActSeeOneTstResMe ] = {1085,-1,TabUnk,ActReqTst ,0x208,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,TstExa_ShowOneExam ,NULL},
+ [ActReqSeeUsrTstRes ] = {1080,-1,TabUnk,ActReqTst ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,Dat_SetIniEndDates ,TstExa_SelUsrsToViewUsrsExams ,NULL},
+ [ActSeeUsrTstRes ] = {1081,-1,TabUnk,ActReqTst ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,TstExa_GetUsrsAndShowExams ,NULL},
+ [ActSeeOneTstResOth ] = {1082,-1,TabUnk,ActReqTst ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,TstExa_ShowOneExam ,NULL},
[ActSeeGam ] = {1650,-1,TabUnk,ActSeeAllGam ,0x238,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Gam_SeeOneGame ,NULL},
@@ -672,16 +672,16 @@ const struct Act_Actions Act_Actions[Act_NUM_ACTIONS] =
[ActAnsMchQstStd ] = {1651,-1,TabUnk,ActSeeAllGam ,0x008, 0, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_2ND_TAB,Mch_GetMatchBeingPlayed ,Mch_ReceiveQuestionAnswer ,NULL},
[ActRefMchStd ] = {1782,-1,TabUnk,ActSeeAllGam ,0x008, 0, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_AJAX_RFRESH,Mch_GetMatchBeingPlayed ,Mch_RefreshMatchStd ,NULL},
- [ActSeeMyMchResCrs ] = {1796,-1,TabUnk,ActSeeAllGam ,0x208,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,McR_ShowMyMchResultsInCrs ,NULL},
- [ActSeeMyMchResGam ] = {1810,-1,TabUnk,ActSeeAllGam ,0x208,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,McR_ShowMyMchResultsInGam ,NULL},
- [ActSeeMyMchResMch ] = {1812,-1,TabUnk,ActSeeAllGam ,0x208,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,McR_ShowMyMchResultsInMch ,NULL},
- [ActSeeOneMchResMe ] = {1797,-1,TabUnk,ActSeeAllGam ,0x208,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,McR_ShowOneMchResult ,NULL},
+ [ActSeeMyMchResCrs ] = {1796,-1,TabUnk,ActSeeAllGam ,0x208,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,MchRes_ShowMyMchResultsInCrs ,NULL},
+ [ActSeeMyMchResGam ] = {1810,-1,TabUnk,ActSeeAllGam ,0x208,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,MchRes_ShowMyMchResultsInGam ,NULL},
+ [ActSeeMyMchResMch ] = {1812,-1,TabUnk,ActSeeAllGam ,0x208,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,MchRes_ShowMyMchResultsInMch ,NULL},
+ [ActSeeOneMchResMe ] = {1797,-1,TabUnk,ActSeeAllGam ,0x208,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,MchRes_ShowOneMchResult ,NULL},
- [ActReqSeeAllMchRes ] = {1798,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,McR_SelUsrsToViewMchResults ,NULL},
- [ActSeeAllMchResCrs ] = {1799,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,McR_ShowAllMchResultsInCrs ,NULL},
- [ActSeeAllMchResGam ] = {1811,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,McR_ShowAllMchResultsInGam ,NULL},
- [ActSeeAllMchResMch ] = {1813,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,McR_ShowAllMchResultsInMch ,NULL},
- [ActSeeOneMchResOth ] = {1800,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,McR_ShowOneMchResult ,NULL},
+ [ActReqSeeAllMchRes ] = {1798,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,MchRes_SelUsrsToViewMchResults ,NULL},
+ [ActSeeAllMchResCrs ] = {1799,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,MchRes_ShowAllMchResultsInCrs ,NULL},
+ [ActSeeAllMchResGam ] = {1811,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,MchRes_ShowAllMchResultsInGam ,NULL},
+ [ActSeeAllMchResMch ] = {1813,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,MchRes_ShowAllMchResultsInMch ,NULL},
+ [ActSeeOneMchResOth ] = {1800,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,MchRes_ShowOneMchResult ,NULL},
[ActChgVisResMchUsr ] = {1801,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Mch_ToggleVisibilResultsMchUsr ,NULL},
diff --git a/swad_changelog.h b/swad_changelog.h
index 02121190..301a6f56 100644
--- a/swad_changelog.h
+++ b/swad_changelog.h
@@ -497,7 +497,7 @@ enscript -2 --landscape --color --file-align=2 --highlight --line-numbers -o - *
En OpenSWAD:
ps2pdf source.ps destination.pdf
*/
-#define Log_PLATFORM_VERSION "SWAD 19.157 (2020-04-01)"
+#define Log_PLATFORM_VERSION "SWAD 19.158 (2020-04-02)"
#define CSS_FILE "swad19.146.css"
#define JS_FILE "swad19.153.js"
/*
@@ -522,11 +522,19 @@ Param
// TODO: En la lista de conectados central, poner el logo de la institución a la que pertenece el usuario
// TODO: Miguel Damas: al principio de los exámenes tendría que poner cuánto resta cada pregunta
// TODO: Oresti Baños: cambiar ojos por candados en descriptores para prohibir/permitir y dejar los ojos para poder elegir descriptores
-// TODO: Si el alumno ha marcado "Permitir que los profesores...", entonces pedir confirmación al pulsar el botón azul, para evitar que se envíe por error antes de tiempo
-// TODO: Tener en cuenta en los resultados de test (exámenes) el tiempo de inicio y el tiempo de fin
- Cuando el alumno ve un test, se crea un examen (en la base de datos), aunque no se conteste, a partir de los datos del formulario.
- El examen se muestra en pantalla tomándolo del examen en la base de datos, no del formulario.
- Cuando el alumno pulsa en "He terminado" se le pregunta si está seguro y se vuelve a mostrar el examen cogiéndolo de la base de datos.
+// TODO: Los exámenes de test que no se han confirmado
+ deben aparecer en la base de datos con un código especial,
+ no contando en la nota y no mostrándose la fecha de finalización.
+ El botón de confirmar envío de examen debería ser verde.
+
+ Version 19.158: Apr 02, 2020 Lot of code refactoring in tests. (285031 lines)
+ 5 changes necessary in database:
+ALTER TABLE tst_exams RENAME INDEX TstCod TO ExaCod;
+ALTER TABLE tst_exams CHANGE COLUMN TstCod ExaCod INT NOT NULL AUTO_INCREMENT;
+
+DROP INDEX TstCod ON tst_exam_questions;
+ALTER TABLE tst_exam_questions CHANGE COLUMN TstCod ExaCod INT NOT NULL;
+ALTER TABLE tst_exam_questions ADD UNIQUE INDEX(ExaCod,QstCod);
Version 19.157: Apr 01, 2020 Code refactoring in tests.
Test exam is stored in database when it's generated. Not tested. (285023 lines)
diff --git a/swad_course.c b/swad_course.c
index 0aa6ab6c..08e97512 100644
--- a/swad_course.c
+++ b/swad_course.c
@@ -40,6 +40,7 @@
#include "swad_HTML.h"
#include "swad_info.h"
#include "swad_logo.h"
+#include "swad_test_exam.h"
/*****************************************************************************/
/************** External global variables from others modules ****************/
@@ -1891,7 +1892,7 @@ static void Crs_EmptyCourseCompletely (long CrsCod)
Svy_RemoveSurveys (Hie_CRS,CrsCod);
/***** Remove all test exams made in the course *****/
- TsR_RemoveCrsTestResults (CrsCod);
+ TstExa_RemoveCrsExams (CrsCod);
/***** Remove all tests questions in the course *****/
Tst_RemoveCrsTests (CrsCod);
diff --git a/swad_database.c b/swad_database.c
index d7232b58..7f6c6cf7 100644
--- a/swad_database.c
+++ b/swad_database.c
@@ -1465,7 +1465,7 @@ mysql> DESCRIBE mch_indexes;
DB_CreateTable ("CREATE TABLE IF NOT EXISTS mch_indexes ("
"MchCod INT NOT NULL,"
"QstInd INT NOT NULL,"
- "Indexes TEXT NOT NULL," // Tst_MAX_BYTES_INDEXES_ONE_QST
+ "Indexes TEXT NOT NULL," // TstExa_MAX_BYTES_INDEXES_ONE_QST
"UNIQUE INDEX(MchCod,QstInd))");
/***** Table mch_results *****/
@@ -2768,8 +2768,8 @@ mysql> DESCRIBE tst_exam_questions;
+---------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+---------+------+-----+---------+-------+
-| TstCod | int(11) | NO | MUL | NULL | |
-| QstCod | int(11) | NO | | NULL | |
+| ExaCod | int(11) | NO | PRI | NULL | |
+| QstCod | int(11) | NO | PRI | NULL | |
| QstInd | int(11) | NO | | NULL | |
| Score | double | NO | | 0 | |
| Indexes | text | NO | | NULL | |
@@ -2778,13 +2778,13 @@ mysql> DESCRIBE tst_exam_questions;
6 rows in set (0.00 sec)
*/
DB_CreateTable ("CREATE TABLE IF NOT EXISTS tst_exam_questions ("
- "TstCod INT NOT NULL,"
+ "ExaCod INT NOT NULL,"
"QstCod INT NOT NULL,"
"QstInd INT NOT NULL,"
"Score DOUBLE PRECISION NOT NULL DEFAULT 0,"
- "Indexes TEXT NOT NULL," // Tst_MAX_BYTES_INDEXES_ONE_QST
- "Answers TEXT NOT NULL," // Tst_MAX_BYTES_ANSWERS_ONE_QST
- "INDEX(TstCod,QstCod))");
+ "Indexes TEXT NOT NULL," // TstExa_MAX_BYTES_INDEXES_ONE_QST
+ "Answers TEXT NOT NULL," // TstExa_MAX_BYTES_ANSWERS_ONE_QST
+ "UNIQUE INDEX(ExaCod,QstCod))");
/***** Table tst_exams *****/
/*
@@ -2792,7 +2792,7 @@ mysql> DESCRIBE tst_exams;
+-----------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+---------------+------+-----+---------+----------------+
-| TstCod | int(11) | NO | PRI | NULL | auto_increment |
+| ExaCod | int(11) | NO | PRI | NULL | auto_increment |
| CrsCod | int(11) | NO | MUL | NULL | |
| UsrCod | int(11) | NO | | NULL | |
| StartTime | datetime | NO | | NULL | |
@@ -2805,7 +2805,7 @@ mysql> DESCRIBE tst_exams;
9 rows in set (0.00 sec)
*/
DB_CreateTable ("CREATE TABLE IF NOT EXISTS tst_exams ("
- "TstCod INT NOT NULL AUTO_INCREMENT,"
+ "ExaCod INT NOT NULL AUTO_INCREMENT,"
"CrsCod INT NOT NULL,"
"UsrCod INT NOT NULL,"
"StartTime DATETIME NOT NULL,"
@@ -2814,7 +2814,7 @@ mysql> DESCRIBE tst_exams;
"NumQstsNotBlank INT NOT NULL DEFAULT 0,"
"AllowTeachers ENUM('N','Y') NOT NULL DEFAULT 'N',"
"Score DOUBLE PRECISION NOT NULL DEFAULT 0,"
- "UNIQUE INDEX(TstCod),"
+ "UNIQUE INDEX(ExaCod),"
"INDEX(CrsCod,UsrCod))");
/***** Table tst_question_tags *****/
diff --git a/swad_enrolment.c b/swad_enrolment.c
index d0d91874..f5a914e6 100644
--- a/swad_enrolment.c
+++ b/swad_enrolment.c
@@ -44,6 +44,7 @@
#include "swad_notification.h"
#include "swad_parameter.h"
#include "swad_role.h"
+#include "swad_test_exam.h"
#include "swad_user.h"
/*****************************************************************************/
@@ -4112,7 +4113,7 @@ static void Enr_EffectivelyRemUsrFromCrs (struct UsrData *UsrDat,
Brw_RemoveSomeInfoAboutCrsUsrFilesFromDB (UsrDat->UsrCod,Crs->CrsCod);
/***** Remove test results made by user in course *****/
- TsR_RemoveTestResultsMadeByUsrInCrs (UsrDat->UsrCod,Crs->CrsCod);
+ TstExa_RemoveExamsMadeByUsrInCrs (UsrDat->UsrCod,Crs->CrsCod);
/***** Set all the notifications for this user in this course as removed,
except notifications about new messages *****/
diff --git a/swad_game.c b/swad_game.c
index b5997398..c536b10d 100644
--- a/swad_game.c
+++ b/swad_game.c
@@ -541,7 +541,7 @@ static void Gam_ShowOneGame (struct Game *Game,bool ShowOnlyThisGame)
HTM_Double (Game->MaxGrade);
HTM_BR ();
HTM_TxtColonNBSP (Txt_Result_visibility);
- TsV_ShowVisibilityIcons (Game->Visibility,Game->Hidden);
+ TstVis_ShowVisibilityIcons (Game->Visibility,Game->Hidden);
HTM_DIV_End ();
/***** Number of matches in game *****/
@@ -619,13 +619,13 @@ static void Gam_PutIconToShowResultsOfGame (void *Args)
switch (Gbl.Usrs.Me.Role.Logged)
{
case Rol_STD:
- Ico_PutContextualIconToShowResults (ActSeeMyMchResGam,McR_RESULTS_BOX_ID,
+ Ico_PutContextualIconToShowResults (ActSeeMyMchResGam,MchRes_RESULTS_BOX_ID,
Gam_PutParams,&Gbl);
break;
case Rol_NET:
case Rol_TCH:
case Rol_SYS_ADM:
- Ico_PutContextualIconToShowResults (ActSeeAllMchResGam,McR_RESULTS_BOX_ID,
+ Ico_PutContextualIconToShowResults (ActSeeAllMchResGam,MchRes_RESULTS_BOX_ID,
Gam_PutParams,&Gbl);
break;
default:
@@ -990,7 +990,7 @@ void Gam_GetDataOfGameByCod (struct Game *Game)
Game->MaxGrade = 0.0;
/* Get visibility (row[5]) */
- Game->Visibility = TsV_GetVisibilityFromStr (row[5]);
+ Game->Visibility = TstVis_GetVisibilityFromStr (row[5]);
/* Get the title of the game (row[6]) */
Str_Copy (Game->Title,row[6],
@@ -1054,7 +1054,7 @@ static void Gam_ResetGame (struct Game *Game)
Game->CrsCod = -1L;
Game->UsrCod = -1L;
Game->MaxGrade = Gam_MAX_GRADE_DEFAULT;
- Game->Visibility = TsV_VISIBILITY_DEFAULT;
+ Game->Visibility = TstVis_VISIBILITY_DEFAULT;
Game->TimeUTC[Dat_START_TIME] = (time_t) 0;
Game->TimeUTC[Dat_END_TIME ] = (time_t) 0;
Game->Title[0] = '\0';
@@ -1403,7 +1403,7 @@ static void Gam_PutFormsEditionGame (struct Game *Game,
HTM_TD_End ();
HTM_TD_Begin ("class=\"LB\"");
- TsV_PutVisibilityCheckboxes (Game->Visibility);
+ TstVis_PutVisibilityCheckboxes (Game->Visibility);
HTM_TD_End ();
HTM_TR_End ();
@@ -1503,7 +1503,7 @@ static void Gam_ReceiveGameFieldsFromForm (struct Game *Game,
Game->MaxGrade = 0.0;
/***** Get visibility *****/
- Game->Visibility = TsV_GetVisibilityFromForm ();
+ Game->Visibility = TstVis_GetVisibilityFromForm ();
/***** Get game text *****/
Par_GetParToHTML ("Txt",Txt,Cns_MAX_BYTES_TEXT); // Store in HTML format (not rigorous)
diff --git a/swad_match.c b/swad_match.c
index 3bca5d3f..1405cfbc 100644
--- a/swad_match.c
+++ b/swad_match.c
@@ -222,7 +222,7 @@ static void Mch_GetNumPlayers (struct Match *Match);
static void Mch_RemoveMyAnswerToMatchQuestion (const struct Match *Match);
-static void Mch_ComputeScore (struct TsR_Result *Result);
+static void Mch_ComputeScore (struct TstExa_Exam *Result);
static unsigned Mch_GetNumUsrsWhoHaveAnswerMch (long MchCod);
@@ -784,7 +784,7 @@ static void Mch_ListOneOrMoreMatchesResultStd (const struct Match *Match)
/* Result is visible by me */
Gam_SetCurrentGamCod (Match->GamCod); // Used to pass parameter
Mch_SetCurrentMchCod (Match->MchCod); // Used to pass parameter
- Lay_PutContextualLinkOnlyIcon (ActSeeMyMchResMch,McR_RESULTS_BOX_ID,
+ Lay_PutContextualLinkOnlyIcon (ActSeeMyMchResMch,MchRes_RESULTS_BOX_ID,
Mch_PutParamsEdit,&Gbl,
"trophy.svg",
Txt_Results);
@@ -807,7 +807,7 @@ static void Mch_ListOneOrMoreMatchesResultTch (const struct Match *Match)
Mch_SetCurrentMchCod (Match->MchCod); // Used to pass parameter
/* Show match results */
- Lay_PutContextualLinkOnlyIcon (ActSeeAllMchResMch,McR_RESULTS_BOX_ID,
+ Lay_PutContextualLinkOnlyIcon (ActSeeAllMchResMch,MchRes_RESULTS_BOX_ID,
Mch_PutParamsEdit,&Gbl,
"trophy.svg",
Txt_Results);
@@ -1541,7 +1541,7 @@ static void Mch_ReorderAnswer (long MchCod,unsigned QstInd,
long LongNum;
unsigned AnsInd;
char StrOneAnswer[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
- char StrAnswersOneQst[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
+ char StrAnswersOneQst[TstExa_MAX_BYTES_ANSWERS_ONE_QST + 1];
/***** Initialize list of answers to empty string *****/
StrAnswersOneQst[0] = '\0';
@@ -1574,9 +1574,9 @@ static void Mch_ReorderAnswer (long MchCod,unsigned QstInd,
/* Concatenate answer index to list of answers */
if (NumAns)
Str_Concat (StrAnswersOneQst,",",
- Tst_MAX_BYTES_ANSWERS_ONE_QST);
+ TstExa_MAX_BYTES_ANSWERS_ONE_QST);
Str_Concat (StrAnswersOneQst,StrOneAnswer,
- Tst_MAX_BYTES_ANSWERS_ONE_QST);
+ TstExa_MAX_BYTES_ANSWERS_ONE_QST);
}
/***** Free structure that stores the query result *****/
@@ -1600,7 +1600,7 @@ void Mch_GetIndexes (long MchCod,unsigned QstInd,
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
- char StrIndexesOneQst[Tst_MAX_BYTES_INDEXES_ONE_QST + 1];
+ char StrIndexesOneQst[TstExa_MAX_BYTES_INDEXES_ONE_QST + 1];
/***** Get indexes for a question from database *****/
if (!DB_QuerySELECT (&mysql_res,"can not get data of a question",
@@ -1613,14 +1613,14 @@ void Mch_GetIndexes (long MchCod,unsigned QstInd,
/* Get indexes (row[0]) */
Str_Copy (StrIndexesOneQst,row[0],
- Tst_MAX_BYTES_INDEXES_ONE_QST);
+ TstExa_MAX_BYTES_INDEXES_ONE_QST);
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
/***** Get indexes from string *****/
Par_ReplaceCommaBySeparatorMultiple (StrIndexesOneQst);
- Tst_GetIndexesFromStr (StrIndexesOneQst,Indexes);
+ TstExa_GetIndexesFromStr (StrIndexesOneQst,Indexes);
}
/*****************************************************************************/
@@ -3834,7 +3834,7 @@ void Mch_ReceiveQuestionAnswer (void)
unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION];
struct Mch_UsrAnswer PreviousUsrAnswer;
struct Mch_UsrAnswer UsrAnswer;
- struct TsR_Result Result;
+ struct TstExa_Exam Result;
/***** Get data of the match from database *****/
Match.MchCod = Gbl.Games.MchCodBeingPlayed;
@@ -3889,7 +3889,7 @@ void Mch_ReceiveQuestionAnswer (void)
UsrAnswer.AnsInd);
/***** Update student's match result *****/
- McR_GetMatchResultQuestionsFromDB (Match.MchCod,Gbl.Usrs.Me.UsrDat.UsrCod,
+ MchRes_GetMatchResultQuestionsFromDB (Match.MchCod,Gbl.Usrs.Me.UsrDat.UsrCod,
&Result);
Mch_ComputeScore (&Result);
@@ -3952,7 +3952,7 @@ static void Mch_RemoveMyAnswerToMatchQuestion (const struct Match *Match)
/******************** Compute match score for a student **********************/
/*****************************************************************************/
-static void Mch_ComputeScore (struct TsR_Result *Result)
+static void Mch_ComputeScore (struct TstExa_Exam *Result)
{
unsigned NumQst;
struct Tst_Question Question;
@@ -3967,7 +3967,7 @@ static void Mch_ComputeScore (struct TsR_Result *Result)
Question.Answer.Type = Tst_ANS_UNIQUE_CHOICE;
/***** Compute score for this answer ******/
- Tst_ComputeChoiceAnsScore (Result,NumQst,&Question);
+ TstExa_ComputeChoiceAnsScore (Result,NumQst,&Question);
/***** Update total score *****/
Result->Score += Result->Questions[NumQst].Score;
diff --git a/swad_match_result.c b/swad_match_result.c
index 56f0b0f7..eb2fcc82 100644
--- a/swad_match_result.c
+++ b/swad_match_result.c
@@ -70,41 +70,41 @@ extern struct Globals Gbl;
/***************************** Private prototypes ****************************/
/*****************************************************************************/
-static void McR_ListMyMchResultsInCrs (void);
-static void McR_ListMyMchResultsInGam (long GamCod);
-static void McR_ListMyMchResultsInMch (long MchCod);
-static void McR_ShowAllMchResultsInSelectedGames (void);
-static void McR_ListAllMchResultsInSelectedGames (void);
-static void McR_ListAllMchResultsInGam (long GamCod);
-static void McR_ListAllMchResultsInMch (long MchCod);
+static void MchRes_ListMyMchResultsInCrs (void);
+static void MchRes_ListMyMchResultsInGam (long GamCod);
+static void MchRes_ListMyMchResultsInMch (long MchCod);
+static void MchRes_ShowAllMchResultsInSelectedGames (void);
+static void MchRes_ListAllMchResultsInSelectedGames (void);
+static void MchRes_ListAllMchResultsInGam (long GamCod);
+static void MchRes_ListAllMchResultsInMch (long MchCod);
-static void McR_ShowResultsBegin (const char *Title,bool ListGamesToSelect);
-static void McR_ShowResultsEnd (void);
+static void MchRes_ShowResultsBegin (const char *Title,bool ListGamesToSelect);
+static void MchRes_ShowResultsEnd (void);
-static void McR_ListGamesToSelect (void);
-static void McR_ShowHeaderMchResults (Usr_MeOrOther_t MeOrOther);
+static void MchRes_ListGamesToSelect (void);
+static void MchRes_ShowHeaderMchResults (Usr_MeOrOther_t MeOrOther);
-static void McR_BuildGamesSelectedCommas (char **GamesSelectedCommas);
-static void McR_ShowMchResults (Usr_MeOrOther_t MeOrOther,
+static void MchRes_BuildGamesSelectedCommas (char **GamesSelectedCommas);
+static void MchRes_ShowMchResults (Usr_MeOrOther_t MeOrOther,
long MchCod, // <= 0 ==> any
long GamCod, // <= 0 ==> any
const char *GamesSelectedCommas);
-static void McR_ShowMchResultsSummaryRow (unsigned NumResults,
+static void MchRes_ShowMchResultsSummaryRow (unsigned NumResults,
unsigned NumTotalQsts,
unsigned NumTotalQstsNotBlank,
double TotalScoreOfAllResults,
double TotalGrade);
-static void McR_GetMatchResultDataByMchCod (long MchCod,long UsrCod,
- struct TsR_Result *Result);
+static void MchRes_GetMatchResultDataByMchCod (long MchCod,long UsrCod,
+ struct TstExa_Exam *Exam);
-static bool McR_CheckIfICanSeeMatchResult (struct Match *Match,long UsrCod);
-static bool McR_CheckIfICanViewScore (bool ICanViewResult,unsigned Visibility);
+static bool MchRes_CheckIfICanSeeMatchResult (struct Match *Match,long UsrCod);
+static bool MchRes_CheckIfICanViewScore (bool ICanViewResult,unsigned Visibility);
/*****************************************************************************/
/*********** Select users and dates to show their matches results ************/
/*****************************************************************************/
-void McR_SelUsrsToViewMchResults (void)
+void MchRes_SelUsrsToViewMchResults (void)
{
extern const char *Hlp_ASSESSMENT_Games_results;
extern const char *Txt_Results;
@@ -123,7 +123,7 @@ void McR_SelUsrsToViewMchResults (void)
/*************************** Show my matches results *************************/
/*****************************************************************************/
-void McR_ShowMyMchResultsInCrs (void)
+void MchRes_ShowMyMchResultsInCrs (void)
{
extern const char *Txt_Results;
@@ -132,26 +132,26 @@ void McR_ShowMyMchResultsInCrs (void)
Gam_GetListSelectedGamCods ();
/***** List my matches results in the current course *****/
- McR_ShowResultsBegin (Txt_Results,true); // List games to select
- McR_ListMyMchResultsInCrs ();
- McR_ShowResultsEnd ();
+ MchRes_ShowResultsBegin (Txt_Results,true); // List games to select
+ MchRes_ListMyMchResultsInCrs ();
+ MchRes_ShowResultsEnd ();
/***** Free list of games *****/
free (Gbl.Games.GamCodsSelected);
Gam_FreeListGames ();
}
-static void McR_ListMyMchResultsInCrs (void)
+static void MchRes_ListMyMchResultsInCrs (void)
{
char *GamesSelectedCommas = NULL; // Initialized to avoid warning
/***** Table header *****/
- McR_ShowHeaderMchResults (Usr_ME);
+ MchRes_ShowHeaderMchResults (Usr_ME);
/***** List my matches results in the current course *****/
TstCfg_GetConfigFromDB (); // Get feedback type
- McR_BuildGamesSelectedCommas (&GamesSelectedCommas);
- McR_ShowMchResults (Usr_ME,-1L,-1L,GamesSelectedCommas);
+ MchRes_BuildGamesSelectedCommas (&GamesSelectedCommas);
+ MchRes_ShowMchResults (Usr_ME,-1L,-1L,GamesSelectedCommas);
free (GamesSelectedCommas);
}
@@ -159,7 +159,7 @@ static void McR_ListMyMchResultsInCrs (void)
/***************** Show my matches results in a given game *******************/
/*****************************************************************************/
-void McR_ShowMyMchResultsInGam (void)
+void MchRes_ShowMyMchResultsInGam (void)
{
extern const char *Txt_Results_of_game_X;
struct Game Game;
@@ -175,31 +175,31 @@ void McR_ShowMyMchResultsInGam (void)
false); // Do not put form to start new match
/***** List my matches results in game *****/
- McR_ShowResultsBegin (Str_BuildStringStr (Txt_Results_of_game_X,Game.Title),
+ MchRes_ShowResultsBegin (Str_BuildStringStr (Txt_Results_of_game_X,Game.Title),
false); // Do not list games to select
Str_FreeString ();
- McR_ListMyMchResultsInGam (Game.GamCod);
- McR_ShowResultsEnd ();
+ MchRes_ListMyMchResultsInGam (Game.GamCod);
+ MchRes_ShowResultsEnd ();
/***** Game end *****/
Gam_ShowOnlyOneGameEnd ();
}
-static void McR_ListMyMchResultsInGam (long GamCod)
+static void MchRes_ListMyMchResultsInGam (long GamCod)
{
/***** Table header *****/
- McR_ShowHeaderMchResults (Usr_ME);
+ MchRes_ShowHeaderMchResults (Usr_ME);
/***** List my matches results in game *****/
TstCfg_GetConfigFromDB (); // Get feedback type
- McR_ShowMchResults (Usr_ME,-1L,GamCod,NULL);
+ MchRes_ShowMchResults (Usr_ME,-1L,GamCod,NULL);
}
/*****************************************************************************/
/***************** Show my matches results in a given match ******************/
/*****************************************************************************/
-void McR_ShowMyMchResultsInMch (void)
+void MchRes_ShowMyMchResultsInMch (void)
{
extern const char *Txt_Results_of_match_X;
struct Game Game;
@@ -219,42 +219,42 @@ void McR_ShowMyMchResultsInMch (void)
false); // Do not put form to start new match
/***** List my matches results in match *****/
- McR_ShowResultsBegin (Str_BuildStringStr (Txt_Results_of_match_X,Match.Title),
+ MchRes_ShowResultsBegin (Str_BuildStringStr (Txt_Results_of_match_X,Match.Title),
false); // Do not list games to select
Str_FreeString ();
- McR_ListMyMchResultsInMch (Match.MchCod);
- McR_ShowResultsEnd ();
+ MchRes_ListMyMchResultsInMch (Match.MchCod);
+ MchRes_ShowResultsEnd ();
/***** Game end *****/
Gam_ShowOnlyOneGameEnd ();
}
-static void McR_ListMyMchResultsInMch (long MchCod)
+static void MchRes_ListMyMchResultsInMch (long MchCod)
{
/***** Table header *****/
- McR_ShowHeaderMchResults (Usr_ME);
+ MchRes_ShowHeaderMchResults (Usr_ME);
/***** List my matches results in game *****/
TstCfg_GetConfigFromDB (); // Get feedback type
- McR_ShowMchResults (Usr_ME,MchCod,-1L,NULL);
+ MchRes_ShowMchResults (Usr_ME,MchCod,-1L,NULL);
}
/*****************************************************************************/
/****************** Get users and show their matches results *****************/
/*****************************************************************************/
-void McR_ShowAllMchResultsInCrs (void)
+void MchRes_ShowAllMchResultsInCrs (void)
{
Usr_GetSelectedUsrsAndGoToAct (&Gbl.Usrs.Selected,
- McR_ShowAllMchResultsInSelectedGames,
- McR_SelUsrsToViewMchResults);
+ MchRes_ShowAllMchResultsInSelectedGames,
+ MchRes_SelUsrsToViewMchResults);
}
/*****************************************************************************/
/****************** Show matches results for several users *******************/
/*****************************************************************************/
-static void McR_ShowAllMchResultsInSelectedGames (void)
+static void MchRes_ShowAllMchResultsInSelectedGames (void)
{
extern const char *Txt_Results;
@@ -263,25 +263,25 @@ static void McR_ShowAllMchResultsInSelectedGames (void)
Gam_GetListSelectedGamCods ();
/***** List the matches results of the selected users *****/
- McR_ShowResultsBegin (Txt_Results,true); // List games to select
- McR_ListAllMchResultsInSelectedGames ();
- McR_ShowResultsEnd ();
+ MchRes_ShowResultsBegin (Txt_Results,true); // List games to select
+ MchRes_ListAllMchResultsInSelectedGames ();
+ MchRes_ShowResultsEnd ();
/***** Free list of games *****/
free (Gbl.Games.GamCodsSelected);
Gam_FreeListGames ();
}
-static void McR_ListAllMchResultsInSelectedGames (void)
+static void MchRes_ListAllMchResultsInSelectedGames (void)
{
char *GamesSelectedCommas = NULL; // Initialized to avoid warning
const char *Ptr;
/***** Table head *****/
- McR_ShowHeaderMchResults (Usr_OTHER);
+ MchRes_ShowHeaderMchResults (Usr_OTHER);
/***** List the matches results of the selected users *****/
- McR_BuildGamesSelectedCommas (&GamesSelectedCommas);
+ MchRes_BuildGamesSelectedCommas (&GamesSelectedCommas);
Ptr = Gbl.Usrs.Selected.List[Rol_UNK];
while (*Ptr)
{
@@ -293,7 +293,7 @@ static void McR_ListAllMchResultsInSelectedGames (void)
{
/***** Show matches results *****/
Gbl.Usrs.Other.UsrDat.Accepted = Usr_CheckIfUsrHasAcceptedInCurrentCrs (&Gbl.Usrs.Other.UsrDat);
- McR_ShowMchResults (Usr_OTHER,-1L,-1L,GamesSelectedCommas);
+ MchRes_ShowMchResults (Usr_OTHER,-1L,-1L,GamesSelectedCommas);
}
}
free (GamesSelectedCommas);
@@ -303,7 +303,7 @@ static void McR_ListAllMchResultsInSelectedGames (void)
/*** Show matches results of a game for the users who answered in that game **/
/*****************************************************************************/
-void McR_ShowAllMchResultsInGam (void)
+void MchRes_ShowAllMchResultsInGam (void)
{
extern const char *Txt_Results_of_game_X;
struct Game Game;
@@ -319,17 +319,17 @@ void McR_ShowAllMchResultsInGam (void)
false); // Do not put form to start new match
/***** List matches results in game *****/
- McR_ShowResultsBegin (Str_BuildStringStr (Txt_Results_of_game_X,Game.Title),
+ MchRes_ShowResultsBegin (Str_BuildStringStr (Txt_Results_of_game_X,Game.Title),
false); // Do not list games to select
Str_FreeString ();
- McR_ListAllMchResultsInGam (Game.GamCod);
- McR_ShowResultsEnd ();
+ MchRes_ListAllMchResultsInGam (Game.GamCod);
+ MchRes_ShowResultsEnd ();
/***** Game end *****/
Gam_ShowOnlyOneGameEnd ();
}
-static void McR_ListAllMchResultsInGam (long GamCod)
+static void MchRes_ListAllMchResultsInGam (long GamCod)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
@@ -337,7 +337,7 @@ static void McR_ListAllMchResultsInGam (long GamCod)
unsigned long NumUsr;
/***** Table head *****/
- McR_ShowHeaderMchResults (Usr_OTHER);
+ MchRes_ShowHeaderMchResults (Usr_OTHER);
/***** Get all users who have answered any match question in this game *****/
NumUsrs = DB_QuerySELECT (&mysql_res,"can not get users in game",
@@ -371,7 +371,7 @@ static void McR_ListAllMchResultsInGam (long GamCod)
{
/***** Show matches results *****/
Gbl.Usrs.Other.UsrDat.Accepted = Usr_CheckIfUsrHasAcceptedInCurrentCrs (&Gbl.Usrs.Other.UsrDat);
- McR_ShowMchResults (Usr_OTHER,-1L,GamCod,NULL);
+ MchRes_ShowMchResults (Usr_OTHER,-1L,GamCod,NULL);
}
}
}
@@ -384,7 +384,7 @@ static void McR_ListAllMchResultsInGam (long GamCod)
/** Show matches results of a match for the users who answered in that match */
/*****************************************************************************/
-void McR_ShowAllMchResultsInMch (void)
+void MchRes_ShowAllMchResultsInMch (void)
{
extern const char *Txt_Results_of_match_X;
struct Game Game;
@@ -404,17 +404,17 @@ void McR_ShowAllMchResultsInMch (void)
false); // Do not put form to start new match
/***** List matches results in match *****/
- McR_ShowResultsBegin (Str_BuildStringStr (Txt_Results_of_match_X,Match.Title),
+ MchRes_ShowResultsBegin (Str_BuildStringStr (Txt_Results_of_match_X,Match.Title),
false); // Do not list games to select
Str_FreeString ();
- McR_ListAllMchResultsInMch (Match.MchCod);
- McR_ShowResultsEnd ();
+ MchRes_ListAllMchResultsInMch (Match.MchCod);
+ MchRes_ShowResultsEnd ();
/***** Game end *****/
Gam_ShowOnlyOneGameEnd ();
}
-static void McR_ListAllMchResultsInMch (long MchCod)
+static void MchRes_ListAllMchResultsInMch (long MchCod)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
@@ -422,7 +422,7 @@ static void McR_ListAllMchResultsInMch (long MchCod)
unsigned long NumUsr;
/***** Table head *****/
- McR_ShowHeaderMchResults (Usr_OTHER);
+ MchRes_ShowHeaderMchResults (Usr_OTHER);
/***** Get all users who have answered any match question in this game *****/
NumUsrs = DB_QuerySELECT (&mysql_res,"can not get users in match",
@@ -456,7 +456,7 @@ static void McR_ListAllMchResultsInMch (long MchCod)
{
/***** Show matches results *****/
Gbl.Usrs.Other.UsrDat.Accepted = Usr_CheckIfUsrHasAcceptedInCurrentCrs (&Gbl.Usrs.Other.UsrDat);
- McR_ShowMchResults (Usr_OTHER,MchCod,-1L,NULL);
+ MchRes_ShowMchResults (Usr_OTHER,MchCod,-1L,NULL);
}
}
}
@@ -469,26 +469,26 @@ static void McR_ListAllMchResultsInMch (long MchCod)
/************************ Show results (begin / end) *************************/
/*****************************************************************************/
-static void McR_ShowResultsBegin (const char *Title,bool ListGamesToSelect)
+static void MchRes_ShowResultsBegin (const char *Title,bool ListGamesToSelect)
{
extern const char *Hlp_ASSESSMENT_Games_results;
/***** Begin box *****/
- HTM_SECTION_Begin (McR_RESULTS_BOX_ID);
+ HTM_SECTION_Begin (MchRes_RESULTS_BOX_ID);
Box_BoxBegin ("100%",Title,
NULL,NULL,
Hlp_ASSESSMENT_Games_results,Box_NOT_CLOSABLE);
/***** List games to select *****/
if (ListGamesToSelect)
- McR_ListGamesToSelect ();
+ MchRes_ListGamesToSelect ();
/***** Begin match results table *****/
- HTM_SECTION_Begin (McR_RESULTS_TABLE_ID);
+ HTM_SECTION_Begin (MchRes_RESULTS_TABLE_ID);
HTM_TABLE_BeginWidePadding (2);
}
-static void McR_ShowResultsEnd (void)
+static void MchRes_ShowResultsEnd (void)
{
/***** End match results table *****/
HTM_TABLE_End ();
@@ -503,7 +503,7 @@ static void McR_ShowResultsEnd (void)
/********** Write list of those attendance events that have students *********/
/*****************************************************************************/
-static void McR_ListGamesToSelect (void)
+static void MchRes_ListGamesToSelect (void)
{
extern const char *Hlp_ASSESSMENT_Games_results;
extern const char *The_ClassFormLinkInBoxBold[The_NUM_THEMES];
@@ -521,7 +521,7 @@ static void McR_ListGamesToSelect (void)
/***** Begin form to update the results
depending on the games selected *****/
- Frm_StartFormAnchor (Gbl.Action.Act,McR_RESULTS_TABLE_ID);
+ Frm_StartFormAnchor (Gbl.Action.Act,MchRes_RESULTS_TABLE_ID);
Grp_PutParamsCodGrps ();
Usr_PutHiddenParSelectedUsrsCods (&Gbl.Usrs.Selected);
@@ -596,7 +596,7 @@ static void McR_ListGamesToSelect (void)
/********************* Show header of my matches results *********************/
/*****************************************************************************/
-static void McR_ShowHeaderMchResults (Usr_MeOrOther_t MeOrOther)
+static void MchRes_ShowHeaderMchResults (Usr_MeOrOther_t MeOrOther)
{
extern const char *Txt_User[Usr_NUM_SEXS];
extern const char *Txt_Match;
@@ -629,7 +629,7 @@ static void McR_ShowHeaderMchResults (Usr_MeOrOther_t MeOrOther)
/******* from list of selected games ********/
/*****************************************************************************/
-static void McR_BuildGamesSelectedCommas (char **GamesSelectedCommas)
+static void MchRes_BuildGamesSelectedCommas (char **GamesSelectedCommas)
{
size_t MaxLength;
unsigned NumGame;
@@ -658,7 +658,7 @@ static void McR_BuildGamesSelectedCommas (char **GamesSelectedCommas)
/********* Show the matches results of a user in the current course **********/
/*****************************************************************************/
-static void McR_ShowMchResults (Usr_MeOrOther_t MeOrOther,
+static void MchRes_ShowMchResults (Usr_MeOrOther_t MeOrOther,
long MchCod, // <= 0 ==> any
long GamCod, // <= 0 ==> any
const char *GamesSelectedCommas)
@@ -776,11 +776,11 @@ static void McR_ShowMchResults (Usr_MeOrOther_t MeOrOther,
Mch_GetDataOfMatchByCod (&Match);
/* Get visibility (row[7]) */
- Visibility = TsV_GetVisibilityFromStr (row[7]);
+ Visibility = TstVis_GetVisibilityFromStr (row[7]);
/* Show match result? */
- ICanViewResult = McR_CheckIfICanSeeMatchResult (&Match,UsrDat->UsrCod);
- ICanViewScore = McR_CheckIfICanViewScore (ICanViewResult,Visibility);
+ ICanViewResult = MchRes_CheckIfICanSeeMatchResult (&Match,UsrDat->UsrCod);
+ ICanViewScore = MchRes_CheckIfICanViewScore (ICanViewResult,Visibility);
if (NumResult)
HTM_TR_Begin (NULL);
@@ -872,8 +872,8 @@ static void McR_ShowMchResults (Usr_MeOrOther_t MeOrOther,
HTM_TD_Begin ("class=\"DAT RT COLOR%u\"",Gbl.RowEvenOdd);
if (ICanViewScore)
{
- Grade = Tst_ComputeGrade (NumQstsInThisResult,ScoreInThisResult,MaxGrade);
- Tst_ShowGrade (Grade,MaxGrade);
+ Grade = TstExa_ComputeGrade (NumQstsInThisResult,ScoreInThisResult,MaxGrade);
+ TstExa_ShowGrade (Grade,MaxGrade);
TotalGrade += Grade;
}
else
@@ -909,7 +909,7 @@ static void McR_ShowMchResults (Usr_MeOrOther_t MeOrOther,
}
/***** Write totals for this user *****/
- McR_ShowMchResultsSummaryRow (NumResults,
+ MchRes_ShowMchResultsSummaryRow (NumResults,
NumTotalQsts,NumTotalQstsNotBlank,
TotalScoreOfAllResults,
TotalGrade);
@@ -930,7 +930,7 @@ static void McR_ShowMchResults (Usr_MeOrOther_t MeOrOther,
/************** Show row with summary of user's matches results **************/
/*****************************************************************************/
-static void McR_ShowMchResultsSummaryRow (unsigned NumResults,
+static void MchRes_ShowMchResultsSummaryRow (unsigned NumResults,
unsigned NumTotalQsts,
unsigned NumTotalQstsNotBlank,
double TotalScoreOfAllResults,
@@ -988,7 +988,7 @@ static void McR_ShowMchResultsSummaryRow (unsigned NumResults,
/******************* Show one match result of another user *******************/
/*****************************************************************************/
-void McR_ShowOneMchResult (void)
+void MchRes_ShowOneMchResult (void)
{
extern const char *Hlp_ASSESSMENT_Games_results;
extern const char *Txt_The_user_does_not_exist;
@@ -1005,7 +1005,7 @@ void McR_ShowOneMchResult (void)
struct UsrData *UsrDat;
Dat_StartEndTime_t StartEndTime;
char *Id;
- struct TsR_Result Result;
+ struct TstExa_Exam Exam;
bool ShowPhoto;
char PhotoURL[PATH_MAX + 1];
bool ICanViewResult;
@@ -1030,16 +1030,16 @@ void McR_ShowOneMchResult (void)
}
/***** Get match result data *****/
- McR_GetMatchResultDataByMchCod (Match.MchCod,UsrDat->UsrCod,
- &Result);
+ MchRes_GetMatchResultDataByMchCod (Match.MchCod,UsrDat->UsrCod,
+ &Exam);
/***** Check if I can view this match result *****/
switch (Gbl.Usrs.Me.Role.Logged)
{
case Rol_STD:
- ICanViewResult = McR_CheckIfICanSeeMatchResult (&Match,UsrDat->UsrCod);
+ ICanViewResult = MchRes_CheckIfICanSeeMatchResult (&Match,UsrDat->UsrCod);
if (ICanViewResult)
- ICanViewScore = TsV_IsVisibleTotalScore (Game.Visibility);
+ ICanViewScore = TstVis_IsVisibleTotalScore (Game.Visibility);
else
ICanViewScore = false;
break;
@@ -1061,8 +1061,8 @@ void McR_ShowOneMchResult (void)
if (ICanViewResult) // I am allowed to view this match result
{
/***** Get questions and user's answers of the match result from database *****/
- McR_GetMatchResultQuestionsFromDB (Match.MchCod,UsrDat->UsrCod,
- &Result);
+ MchRes_GetMatchResultQuestionsFromDB (Match.MchCod,UsrDat->UsrCod,
+ &Exam);
/***** Begin box *****/
Box_BoxBegin (NULL,Match.Title,
@@ -1120,7 +1120,7 @@ void McR_ShowOneMchResult (void)
if (asprintf (&Id,"match_%u",(unsigned) StartEndTime) < 0)
Lay_NotEnoughMemoryExit ();
HTM_TD_Begin ("id=\"%s\" class=\"DAT LT\"",Id);
- Dat_WriteLocalDateHMSFromUTC (Id,Result.TimeUTC[StartEndTime],
+ Dat_WriteLocalDateHMSFromUTC (Id,Exam.TimeUTC[StartEndTime],
Gbl.Prefs.DateFormat,Dat_SEPARATOR_COMMA,
true,true,true,0x7);
HTM_TD_End ();
@@ -1138,8 +1138,8 @@ void McR_ShowOneMchResult (void)
HTM_TD_Begin ("class=\"DAT LT\"");
HTM_TxtF ("%u (%u %s)",
- Result.NumQsts,
- Result.NumQstsNotBlank,Txt_non_blank_QUESTIONS);
+ Exam.NumQsts,
+ Exam.NumQstsNotBlank,Txt_non_blank_QUESTIONS);
HTM_TD_End ();
HTM_TR_End ();
@@ -1153,7 +1153,7 @@ void McR_ShowOneMchResult (void)
HTM_TD_Begin ("class=\"DAT LT\"");
if (ICanViewScore)
- HTM_Double2Decimals (Result.Score);
+ HTM_Double2Decimals (Exam.Score);
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
@@ -1169,8 +1169,8 @@ void McR_ShowOneMchResult (void)
HTM_TD_Begin ("class=\"DAT LT\"");
if (ICanViewScore)
- Tst_ComputeAndShowGrade (Result.NumQsts,
- Result.Score,
+ TstExa_ComputeAndShowGrade (Exam.NumQsts,
+ Exam.Score,
Game.MaxGrade);
else
Ico_PutIconNotVisible ();
@@ -1192,7 +1192,7 @@ void McR_ShowOneMchResult (void)
HTM_TR_End ();
/***** Write answers and solutions *****/
- TsR_ShowTestResult (UsrDat,&Result,Game.Visibility);
+ TstExa_ShowExamAnswers (UsrDat,&Exam,Game.Visibility);
/***** End table *****/
HTM_TABLE_End ();
@@ -1202,10 +1202,10 @@ void McR_ShowOneMchResult (void)
{
HTM_DIV_Begin ("class=\"DAT_N_BOLD CM\"");
HTM_TxtColonNBSP (Txt_Score);
- HTM_Double2Decimals (Result.Score);
+ HTM_Double2Decimals (Exam.Score);
HTM_BR ();
HTM_TxtColonNBSP (Txt_Grade);
- Tst_ComputeAndShowGrade (Result.NumQsts,Result.Score,Game.MaxGrade);
+ TstExa_ComputeAndShowGrade (Exam.NumQsts,Exam.Score,Game.MaxGrade);
HTM_DIV_End ();
}
@@ -1220,8 +1220,8 @@ void McR_ShowOneMchResult (void)
/************ Get the questions of a match result from database **************/
/*****************************************************************************/
-void McR_GetMatchResultQuestionsFromDB (long MchCod,long UsrCod,
- struct TsR_Result *Result)
+void MchRes_GetMatchResultQuestionsFromDB (long MchCod,long UsrCod,
+ struct TstExa_Exam *Exam)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
@@ -1231,27 +1231,27 @@ void McR_GetMatchResultQuestionsFromDB (long MchCod,long UsrCod,
struct Mch_UsrAnswer UsrAnswer;
/***** Get questions and answers of a match result *****/
- Result->NumQsts = (unsigned)
- DB_QuerySELECT (&mysql_res,"can not get questions and answers"
- " of a match result",
- "SELECT gam_questions.QstCod," // row[0]
- "gam_questions.QstInd," // row[1]
- "mch_indexes.Indexes" // row[2]
- " FROM mch_matches,gam_questions,mch_indexes"
- " WHERE mch_matches.MchCod=%ld"
- " AND mch_matches.GamCod=gam_questions.GamCod"
- " AND mch_matches.MchCod=mch_indexes.MchCod"
- " AND gam_questions.QstInd=mch_indexes.QstInd"
- " ORDER BY gam_questions.QstInd",
- MchCod);
- for (NumQst = 0, Result->NumQstsNotBlank = 0;
- NumQst < Result->NumQsts;
+ Exam->NumQsts = (unsigned)
+ DB_QuerySELECT (&mysql_res,"can not get questions and answers"
+ " of a match result",
+ "SELECT gam_questions.QstCod," // row[0]
+ "gam_questions.QstInd," // row[1]
+ "mch_indexes.Indexes" // row[2]
+ " FROM mch_matches,gam_questions,mch_indexes"
+ " WHERE mch_matches.MchCod=%ld"
+ " AND mch_matches.GamCod=gam_questions.GamCod"
+ " AND mch_matches.MchCod=mch_indexes.MchCod"
+ " AND gam_questions.QstInd=mch_indexes.QstInd"
+ " ORDER BY gam_questions.QstInd",
+ MchCod);
+ for (NumQst = 0, Exam->NumQstsNotBlank = 0;
+ NumQst < Exam->NumQsts;
NumQst++)
{
row = mysql_fetch_row (mysql_res);
/* Get question code (row[0]) */
- if ((Result->Questions[NumQst].QstCod = Str_ConvertStrCodToLongCod (row[0])) < 0)
+ if ((Exam->Questions[NumQst].QstCod = Str_ConvertStrCodToLongCod (row[0])) < 0)
Lay_ShowErrorAndExit ("Wrong code of question.");
/* Get question index (row[1]) */
@@ -1260,24 +1260,24 @@ void McR_GetMatchResultQuestionsFromDB (long MchCod,long UsrCod,
QstInd = (unsigned) LongNum;
/* Get indexes for this question (row[2]) */
- Str_Copy (Result->Questions[NumQst].StrIndexes,row[2],
- Tst_MAX_BYTES_INDEXES_ONE_QST);
+ Str_Copy (Exam->Questions[NumQst].StrIndexes,row[2],
+ TstExa_MAX_BYTES_INDEXES_ONE_QST);
/* Get answers selected by user for this question */
Mch_GetQstAnsFromDB (MchCod,UsrCod,QstInd,&UsrAnswer);
if (UsrAnswer.AnsInd >= 0) // UsrAnswer.AnsInd >= 0 ==> answer selected
{
- snprintf (Result->Questions[NumQst].StrAnswers,Tst_MAX_BYTES_ANSWERS_ONE_QST + 1,
+ snprintf (Exam->Questions[NumQst].StrAnswers,TstExa_MAX_BYTES_ANSWERS_ONE_QST + 1,
"%d",UsrAnswer.AnsInd);
- Result->NumQstsNotBlank++;
+ Exam->NumQstsNotBlank++;
}
else // UsrAnswer.AnsInd < 0 ==> no answer selected
- Result->Questions[NumQst].StrAnswers[0] = '\0'; // Empty answer
+ Exam->Questions[NumQst].StrAnswers[0] = '\0'; // Empty answer
/* Replace each comma by a separator of multiple parameters */
/* In database commas are used as separators instead of special chars */
- Par_ReplaceCommaBySeparatorMultiple (Result->Questions[NumQst].StrIndexes);
- Par_ReplaceCommaBySeparatorMultiple (Result->Questions[NumQst].StrAnswers);
+ Par_ReplaceCommaBySeparatorMultiple (Exam->Questions[NumQst].StrIndexes);
+ Par_ReplaceCommaBySeparatorMultiple (Exam->Questions[NumQst].StrAnswers);
}
/***** Free structure that stores the query result *****/
@@ -1288,8 +1288,8 @@ void McR_GetMatchResultQuestionsFromDB (long MchCod,long UsrCod,
/************* Get data of a match result using its match code ***************/
/*****************************************************************************/
-static void McR_GetMatchResultDataByMchCod (long MchCod,long UsrCod,
- struct TsR_Result *Result)
+static void MchRes_GetMatchResultDataByMchCod (long MchCod,long UsrCod,
+ struct TstExa_Exam *Exam)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
@@ -1318,27 +1318,27 @@ static void McR_GetMatchResultDataByMchCod (long MchCod,long UsrCod,
for (StartEndTime = (Dat_StartEndTime_t) 0;
StartEndTime <= (Dat_StartEndTime_t) (Dat_NUM_START_END_TIME - 1);
StartEndTime++)
- Result->TimeUTC[StartEndTime] = Dat_GetUNIXTimeFromStr (row[StartEndTime]);
+ Exam->TimeUTC[StartEndTime] = Dat_GetUNIXTimeFromStr (row[StartEndTime]);
/* Get number of questions (row[2]) */
- if (sscanf (row[2],"%u",&Result->NumQsts) != 1)
- Result->NumQsts = 0;
+ if (sscanf (row[2],"%u",&Exam->NumQsts) != 1)
+ Exam->NumQsts = 0;
/* Get number of questions not blank (row[3]) */
- if (sscanf (row[3],"%u",&Result->NumQstsNotBlank) != 1)
- Result->NumQstsNotBlank = 0;
+ if (sscanf (row[3],"%u",&Exam->NumQstsNotBlank) != 1)
+ Exam->NumQstsNotBlank = 0;
/* Get score (row[4]) */
Str_SetDecimalPointToUS (); // To get the decimal point as a dot
- if (sscanf (row[4],"%lf",&Result->Score) != 1)
- Result->Score = 0.0;
+ if (sscanf (row[4],"%lf",&Exam->Score) != 1)
+ Exam->Score = 0.0;
Str_SetDecimalPointToLocal (); // Return to local system
}
else
{
- Result->NumQsts = 0;
- Result->NumQstsNotBlank = 0;
- Result->Score = 0.0;
+ Exam->NumQsts = 0;
+ Exam->NumQstsNotBlank = 0;
+ Exam->Score = 0.0;
}
/***** Free structure that stores the query result *****/
@@ -1349,7 +1349,7 @@ static void McR_GetMatchResultDataByMchCod (long MchCod,long UsrCod,
/********************** Get if I can see match result ************************/
/*****************************************************************************/
-static bool McR_CheckIfICanSeeMatchResult (struct Match *Match,long UsrCod)
+static bool MchRes_CheckIfICanSeeMatchResult (struct Match *Match,long UsrCod)
{
bool ItsMe;
@@ -1376,13 +1376,13 @@ static bool McR_CheckIfICanSeeMatchResult (struct Match *Match,long UsrCod)
/********************** Get if I can see match result ************************/
/*****************************************************************************/
-static bool McR_CheckIfICanViewScore (bool ICanViewResult,unsigned Visibility)
+static bool MchRes_CheckIfICanViewScore (bool ICanViewResult,unsigned Visibility)
{
switch (Gbl.Usrs.Me.Role.Logged)
{
case Rol_STD:
if (ICanViewResult)
- return TsV_IsVisibleTotalScore (Visibility);
+ return TstVis_IsVisibleTotalScore (Visibility);
return false;
break;
case Rol_NET:
diff --git a/swad_match_result.h b/swad_match_result.h
index 47c08373..7321564f 100644
--- a/swad_match_result.h
+++ b/swad_match_result.h
@@ -1,7 +1,7 @@
// swad_match_result.h: matches results in games using remote control
-#ifndef _SWAD_MCR
-#define _SWAD_MCR
+#ifndef _SWAD_MCH_RES
+#define _SWAD_MCH_RES
/*
SWAD (Shared Workspace At a Distance in Spanish),
is a web platform developed at the University of Granada (Spain),
@@ -27,29 +27,31 @@
/********************************* Headers ***********************************/
/*****************************************************************************/
+#include "swad_test_exam.h"
+
/*****************************************************************************/
/************************** Public types and constants ***********************/
/*****************************************************************************/
-#define McR_RESULTS_BOX_ID "mcr_box"
-#define McR_RESULTS_TABLE_ID "mcr_table"
+#define MchRes_RESULTS_BOX_ID "mcr_box"
+#define MchRes_RESULTS_TABLE_ID "mcr_table"
/*****************************************************************************/
/***************************** Public prototypes *****************************/
/*****************************************************************************/
-void McR_SelUsrsToViewMchResults (void);
+void MchRes_SelUsrsToViewMchResults (void);
-void McR_ShowMyMchResultsInCrs (void);
-void McR_ShowMyMchResultsInGam (void);
-void McR_ShowMyMchResultsInMch (void);
+void MchRes_ShowMyMchResultsInCrs (void);
+void MchRes_ShowMyMchResultsInGam (void);
+void MchRes_ShowMyMchResultsInMch (void);
-void McR_ShowAllMchResultsInCrs (void);
-void McR_ShowAllMchResultsInGam (void);
-void McR_ShowAllMchResultsInMch (void);
+void MchRes_ShowAllMchResultsInCrs (void);
+void MchRes_ShowAllMchResultsInGam (void);
+void MchRes_ShowAllMchResultsInMch (void);
-void McR_ShowOneMchResult (void);
-void McR_GetMatchResultQuestionsFromDB (long MchCod,long UsrCod,
- struct TsR_Result *Result);
+void MchRes_ShowOneMchResult (void);
+void MchRes_GetMatchResultQuestionsFromDB (long MchCod,long UsrCod,
+ struct TstExa_Exam *Exam);
#endif
diff --git a/swad_test.c b/swad_test.c
index ea7b4c4d..a83bf59d 100644
--- a/swad_test.c
+++ b/swad_test.c
@@ -50,6 +50,7 @@
#include "swad_parameter.h"
#include "swad_theme.h"
#include "swad_test.h"
+#include "swad_test_exam.h"
#include "swad_test_config.h"
#include "swad_test_import.h"
#include "swad_test_visibility.h"
@@ -108,6 +109,13 @@ typedef enum
Tst_STATUS_ERROR = 2,
} Tst_Status_t;
+#define Tst_NUM_REQUEST_OR_CONFIRM 2
+typedef enum
+ {
+ Tst_REQUEST,
+ Tst_CONFIRM,
+ } Tst_RequestOrConfirm_t;
+
/*****************************************************************************/
/************** External global variables from others modules ****************/
/*****************************************************************************/
@@ -131,54 +139,24 @@ static void Tst_ShowFormRequestTest (struct Tst_Test *Test);
static void Tst_PutCheckBoxAllowTeachers (bool AllowTeachers);
-static void Tst_GetQuestionsAndAnswersFromForm (struct TsR_Result *Result);
-
-static void Tst_ComputeAndStoreResultScore (struct TsR_Result *Result,
- bool UpdateQstScore);
-static void Tst_ComputeAnswerScore (struct TsR_Result *Result,
- unsigned NumQst,
- struct Tst_Question *Question);
-static void Tst_ComputeIntAnsScore (struct TsR_Result *Result,
- unsigned NumQst,
- struct Tst_Question *Question);
-static void Tst_GetCorrectIntAnswerFromDB (struct Tst_Question *Question);
-static void Tst_ComputeFloatAnsScore (struct TsR_Result *Result,
- unsigned NumQst,
- struct Tst_Question *Question);
-static void Tst_GetCorrectFloatAnswerFromDB (struct Tst_Question *Question);
-static void Tst_ComputeTFAnsScore (struct TsR_Result *Result,
- unsigned NumQst,
- struct Tst_Question *Question);
-static void Tst_GetCorrectTFAnswerFromDB (struct Tst_Question *Question);
-static void Tst_GetCorrectChoiceAnswerFromDB (struct Tst_Question *Question);
-static void Tst_GetAnswersFromStr (const char StrAnswersOneQst[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1],
- bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION]);
-static void Tst_ComputeScoreQst (struct TsR_Result *Result,
- unsigned NumQst,
- const struct Tst_Question *Question,
- unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION], // Indexes of all answers of this question
- bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION]);
-static void Tst_ComputeTextAnsScore (struct TsR_Result *Result,
- unsigned NumQst,
- struct Tst_Question *Question);
-static void Tst_GetCorrectTextAnswerFromDB (struct Tst_Question *Question);
+static void Tst_GetAnswersFromForm (struct TstExa_Exam *Exam);
static bool Tst_CheckIfNextTstAllowed (void);
static void Tst_SetTstStatus (unsigned NumTst,Tst_Status_t TstStatus);
static Tst_Status_t Tst_GetTstStatus (unsigned NumTst);
-static unsigned Tst_GetNumAccessesTst (void);
-static void Tst_ShowTestQuestionsWhenSeeing (struct TsR_Result *Result);
+static unsigned Tst_GetNumExamsGeneratedByMe (void);
+static void Tst_ShowTestExamToFillIt (struct TstExa_Exam *Exam,
+ unsigned NumExamsGeneratedByMe,
+ Tst_RequestOrConfirm_t RequestOrConfirm);
-static void Tst_ShowTestResultAfterAssess (struct TsR_Result *Result);
-static void Tst_WriteQstAndAnsSeeing (struct TsR_Result *Result,
+static void Tst_WriteQstAndAnsSeeing (struct TstExa_Exam *Exam,
unsigned NumQst,
struct Tst_Question *Question,
MYSQL_ROW row);
static void Tst_PutFormToEditQstMedia (const struct Media *Media,int NumMediaInForm,
bool OptionsDisabled);
-static void Tst_UpdateQstScore (const struct TsR_Result *Result,unsigned NumQst);
-static void Tst_UpdateMyNumAccessTst (unsigned NumAccessesTst);
+static void Tst_IncreaseMyNumAccessTst (void);
static void Tst_UpdateLastAccTst (unsigned NumQsts);
static void Tst_ShowFormRequestEditTests (struct Tst_Test *Test);
@@ -205,8 +183,8 @@ static void Tst_PutInputFieldNumQst (const char *Field,const char *Label,
static void Tst_ShowFormAnswerTypes (const struct Tst_AnswerTypes *AnswerTypes);
static void Tst_GetQuestions (struct Tst_Test *Test,MYSQL_RES **mysql_res);
static void Tst_GetQuestionsForNewTestFromDB (struct Tst_Test *Test,
- struct TsR_Result *Result);
-static void Tst_GetChoiceAnsSeeing (struct TsR_Result *Result,
+ struct TstExa_Exam *Exam);
+static void Tst_GenerateChoiceIndexesDependingOnShuffle (struct TstExa_Exam *Exam,
unsigned NumQst,
bool Shuffle);
@@ -220,78 +198,39 @@ static void Tst_ListOneOrMoreQuestionsForSelection (unsigned NumQsts,
static void Tst_WriteQuestionRowForSelection (unsigned NumQst,
struct Tst_Question *Question);
-static void Tst_WriteAnswersSeeing (struct TsR_Result *Result,
+static void Tst_WriteAnswersSeeing (struct TstExa_Exam *Exam,
unsigned NumQst,
struct Tst_Question *Question);
-static void Tst_WriteAnswersResult (struct UsrData *UsrDat,
- const struct TsR_Result *Result,
- unsigned NumQst,
- struct Tst_Question *Question,
- unsigned Visibility);
static void Tst_WriteIntAnsListing (const struct Tst_Question *Question,
MYSQL_RES *mysql_res);
-static void Tst_WriteIntAnsSeeing (const struct TsR_Result *Result,
+static void Tst_WriteIntAnsSeeing (const struct TstExa_Exam *Exam,
unsigned NumQst);
-static void Tst_WriteIntAnsResult (struct UsrData *UsrDat,
- const struct TsR_Result *Result,
- unsigned NumQst,
- const struct Tst_Question *Question,
- MYSQL_RES *mysql_res,
- unsigned Visibility);
static void Tst_WriteFloatAnsEdit (const struct Tst_Question *Question,
MYSQL_RES *mysql_res);
-static void Tst_WriteFloatAnsSeeing (const struct TsR_Result *Result,
+static void Tst_WriteFloatAnsSeeing (const struct TstExa_Exam *Exam,
unsigned NumQst);
-static void Tst_WriteFloatAnsResult (struct UsrData *UsrDat,
- const struct TsR_Result *Result,
- unsigned NumQst,
- const struct Tst_Question *Question,
- MYSQL_RES *mysql_res,
- unsigned Visibility);
static void Tst_WriteTFAnsListing (const struct Tst_Question *Question,
MYSQL_RES *mysql_res);
-static void Tst_WriteTFAnsSeeing (const struct TsR_Result *Result,
+static void Tst_WriteTFAnsSeeing (const struct TstExa_Exam *Exam,
unsigned NumQst);
-static void Tst_WriteTFAnsResult (struct UsrData *UsrDat,
- const struct TsR_Result *Result,
- unsigned NumQst,
- const struct Tst_Question *Question,
- MYSQL_RES *mysql_res,
- unsigned Visibility);
static void Tst_WriteChoiceAnsListing (struct Tst_Question *Question,
MYSQL_RES *mysql_res);
-static void Tst_WriteChoiceAnsSeeing (const struct TsR_Result *Result,
+static void Tst_WriteChoiceAnsSeeing (const struct TstExa_Exam *Exam,
unsigned NumQst,
struct Tst_Question *Question,
MYSQL_RES *mysql_res);
-static void Tst_WriteChoiceAnsResult (struct UsrData *UsrDat,
- const struct TsR_Result *Result,
- unsigned NumQst,
- struct Tst_Question *Question,
- MYSQL_RES *mysql_res,
- unsigned Visibility);
-static void Tst_GetChoiceAns (struct Tst_Question *Question,MYSQL_RES *mysql_res);
-static void Tst_WriteTextAnsSeeing (const struct TsR_Result *Result,
+static void Tst_WriteTextAnsSeeing (const struct TstExa_Exam *Exam,
unsigned NumQst);
-static void Tst_WriteTextAnsResult (struct UsrData *UsrDat,
- const struct TsR_Result *Result,
- unsigned NumQst,
- struct Tst_Question *Question,
- MYSQL_RES *mysql_res,
- unsigned Visibility);
-static void Tst_WriteHeadUserCorrect (struct UsrData *UsrDat);
-static void Tst_WriteScoreStart (unsigned ColSpan);
-static void Tst_WriteScoreEnd (void);
static void Tst_WriteParamQstCod (unsigned NumQst,long QstCod);
static bool Tst_GetParamsTst (struct Tst_Test *Test,
Tst_ActionToDoWithQuestions_t ActionToDoWithQuestions);
-static unsigned Tst_GetAndCheckParamNumTst (void);
+static unsigned Tst_GetParamNumTst (void);
static unsigned Tst_GetParamNumQsts (void);
static unsigned Tst_CountNumTagsInList (const struct Tst_Tags *Tags);
static int Tst_CountNumAnswerTypesInList (const struct Tst_AnswerTypes *AnswerTypes);
@@ -311,7 +250,6 @@ static void Tst_FreeTextChoiceAnswer (struct Tst_Question *Question,unsigned Num
static void Tst_ResetMediaOfQuestion (struct Tst_Question *Question);
static void Tst_FreeMediaOfQuestion (struct Tst_Question *Question);
-static Tst_AnswerType_t Tst_GetQstAnswerType (long QstCod);
static void Tst_GetQstDataFromDB (struct Tst_Question *Question,
char Stem[Cns_MAX_BYTES_TEXT + 1],
char Feedback[Cns_MAX_BYTES_TEXT + 1]);
@@ -512,13 +450,10 @@ static void Tst_ShowFormRequestTest (struct Tst_Test *Test)
void Tst_ShowNewTest (void)
{
- extern const char *Hlp_ASSESSMENT_Tests;
extern const char *Txt_No_questions_found_matching_your_search_criteria;
- extern const char *Txt_Test;
- extern const char *Txt_Done_assess_test;
struct Tst_Test Test;
- struct TsR_Result Result;
- unsigned NumAccessesTst;
+ struct TstExa_Exam Exam;
+ unsigned NumExamsGeneratedByMe;
/***** Create test *****/
Tst_TstConstructor (&Test);
@@ -532,47 +467,23 @@ void Tst_ShowNewTest (void)
if (Tst_GetParamsTst (&Test,Tst_SHOW_TEST_TO_ANSWER)) // Get parameters from form
{
/***** Get questions *****/
- Tst_GetQuestionsForNewTestFromDB (&Test,&Result);
- if (Result.NumQsts)
+ Tst_GetQuestionsForNewTestFromDB (&Test,&Exam);
+ if (Exam.NumQsts)
{
- /***** Get and update number of hits *****/
- NumAccessesTst = Tst_GetNumAccessesTst () + 1;
- if (Gbl.Usrs.Me.IBelongToCurrentCrs)
- Tst_UpdateMyNumAccessTst (NumAccessesTst);
+ /***** Increase number of exams generated (answered or not) by me *****/
+ Tst_IncreaseMyNumAccessTst ();
+ NumExamsGeneratedByMe = Tst_GetNumExamsGeneratedByMe ();
- /***** Create new test in database to store the result *****/
- TsR_CreateTestResultInDB (&Result);
+ /***** Create new test exam in database *****/
+ TstExa_CreateExamInDB (&Exam);
+ TstExa_ComputeScoresAndStoreExamQuestions (&Exam,
+ false); // Don't update question score
- /***** Begin box *****/
- Box_BoxBegin (NULL,Txt_Test,
- NULL,NULL,
- Hlp_ASSESSMENT_Tests,Box_NOT_CLOSABLE);
- Lay_WriteHeaderClassPhoto (false,false,
- Gbl.Hierarchy.Ins.InsCod,
- Gbl.Hierarchy.Deg.DegCod,
- Gbl.Hierarchy.Crs.CrsCod);
-
- /***** Begin form *****/
- Frm_StartForm (ActAssTst);
- Par_PutHiddenParamLong (NULL,"TstCod",Result.TstCod);
- Par_PutHiddenParamUnsigned (NULL,"NumTst",NumAccessesTst);
- Par_PutHiddenParamUnsigned (NULL,"NumQst",Test.NumQsts);
-
- /***** List the questions *****/
- Tst_ShowTestQuestionsWhenSeeing (&Result);
-
- /***** Test result will be saved? *****/
- Tst_PutCheckBoxAllowTeachers (true);
-
- /***** End form *****/
- Btn_PutConfirmButton (Txt_Done_assess_test);
- Frm_EndForm ();
-
- /***** End box *****/
- Box_BoxEnd ();
+ /***** Show test exam to be answered *****/
+ Tst_ShowTestExamToFillIt (&Exam,NumExamsGeneratedByMe,Tst_REQUEST);
/***** Set test status *****/
- Tst_SetTstStatus (NumAccessesTst,Tst_STATUS_SHOWN_BUT_NOT_ASSESSED);
+ Tst_SetTstStatus (NumExamsGeneratedByMe,Tst_STATUS_SHOWN_BUT_NOT_ASSESSED);
/***** Update date-time of my next allowed access to test *****/
if (Gbl.Usrs.Me.Role.Logged == Rol_STD)
@@ -593,7 +504,7 @@ void Tst_ShowNewTest (void)
}
/*****************************************************************************/
-/************ Put checkbox to allow teachers to see test result **************/
+/************* Put checkbox to allow teachers to see test exam ***************/
/*****************************************************************************/
static void Tst_PutCheckBoxAllowTeachers (bool AllowTeachers)
@@ -601,12 +512,12 @@ static void Tst_PutCheckBoxAllowTeachers (bool AllowTeachers)
extern const char *The_ClassFormInBox[The_NUM_THEMES];
extern const char *Txt_Allow_teachers_to_consult_this_test;
- /***** Test result will be available for teachers? *****/
+ /***** Test exam will be available for teachers? *****/
HTM_DIV_Begin ("class=\"CM\"");
HTM_LABEL_Begin ("class=\"%s\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
HTM_INPUT_CHECKBOX ("AllowTchs",HTM_DONT_SUBMIT_ON_CHANGE,
"value=\"Y\"%s",
- AllowTeachers ? " checked=\"checked\"" : // Teachers can see test result
+ AllowTeachers ? " checked=\"checked\"" : // Teachers can see test exam
"");
HTM_TxtF (" %s",Txt_Allow_teachers_to_consult_this_test);
HTM_LABEL_End ();
@@ -622,34 +533,40 @@ void Tst_RequestAssessTest (void)
extern const char *Txt_The_test_X_has_already_been_assessed_previously;
extern const char *Txt_There_was_an_error_in_assessing_the_test_X;
unsigned NumTst;
- // long TstCod = -1L; // Initialized to avoid warning
- struct TsR_Result Result;
+ struct TstExa_Exam Exam;
/***** Read test configuration from database *****/
TstCfg_GetConfigFromDB ();
- /***** Get number of this test from form *****/
- NumTst = Tst_GetAndCheckParamNumTst ();
+ /***** Get basic parameters of the exam *****/
+ /* Get test exam code from form */
+ if ((Exam.ExaCod = TstExa_GetParamExaCod ()) <= 0)
+ Lay_ShowErrorAndExit ("Wrong test exam.");
+
+ /* Get number of this test from form */
+ NumTst = Tst_GetParamNumTst ();
/****** Get test status in database for this session-course-num.test *****/
switch (Tst_GetTstStatus (NumTst))
{
case Tst_STATUS_SHOWN_BUT_NOT_ASSESSED:
- /***** Get parameters from the form *****/
- /* Get questions and answers from form to assess a test */
- Tst_GetQuestionsAndAnswersFromForm (&Result);
+ /***** Get test exam from database *****/
+ TstExa_GetExamDataByExaCod (&Exam);
+ TstExa_GetExamQuestionsFromDB (&Exam);
+
+ /***** Get answers from form to assess a test *****/
+ Tst_GetAnswersFromForm (&Exam);
+
+ /***** Update test exam in database *****/
+ TstExa_ComputeScoresAndStoreExamQuestions (&Exam,
+ false); // Don't update question score
/***** Show question and button to send the test *****/
/* Start alert */
- Ale_ShowAlertAndButton1 (Ale_INFO,"Por favor, revise el test antes de enviarlo");
+ Ale_ShowAlert (Ale_INFO,"Por favor, revise el test antes de enviarlo."); // TODO: Need translation!!!
- /* Show test again */
-
-
- /* End alert */
- Ale_ShowAlertAndButton2 (ActAssTst,NULL,NULL,
- NULL,NULL,
- Btn_CONFIRM_BUTTON,"Enviar test");
+ /* Show the same test exam to be answered */
+ Tst_ShowTestExamToFillIt (&Exam,NumTst,Tst_CONFIRM);
break;
case Tst_STATUS_ASSESSED:
Ale_ShowAlert (Ale_WARNING,Txt_The_test_X_has_already_been_assessed_previously,
@@ -676,30 +593,33 @@ void Tst_AssessTest (void)
extern const char *Txt_The_test_X_has_already_been_assessed_previously;
extern const char *Txt_There_was_an_error_in_assessing_the_test_X;
unsigned NumTst;
- struct TsR_Result Result;
+ struct TstExa_Exam Exam;
/***** Read test configuration from database *****/
TstCfg_GetConfigFromDB ();
/***** Get basic parameters of the exam *****/
- /* Get test result code from form */
- if ((Result.TstCod = Par_GetParToLong ("TstCod")) <= 0)
- Lay_ShowErrorAndExit ("Wrong test result.");
+ /* Get test exam code from form */
+ if ((Exam.ExaCod = TstExa_GetParamExaCod ()) <= 0)
+ Lay_ShowErrorAndExit ("Wrong test exam.");
/* Get number of this test from form */
- NumTst = Tst_GetAndCheckParamNumTst ();
+ NumTst = Tst_GetParamNumTst ();
/****** Get test status in database for this session-course-num.test *****/
switch (Tst_GetTstStatus (NumTst))
{
case Tst_STATUS_SHOWN_BUT_NOT_ASSESSED:
- /***** Get parameters from the form *****/
- /* Get questions and answers from form to assess a test */
- Tst_GetQuestionsAndAnswersFromForm (&Result);
+ /***** Get test exam from database *****/
+ TstExa_GetExamDataByExaCod (&Exam);
+ TstExa_GetExamQuestionsFromDB (&Exam);
- /***** Update test result in database *****/
- Tst_ComputeAndStoreResultScore (&Result,
- Gbl.Usrs.Me.Role.Logged == Rol_STD); // Update question score?
+ /***** Get answers from form to assess a test *****/
+ Tst_GetAnswersFromForm (&Exam);
+
+ /***** Update test exam in database *****/
+ TstExa_ComputeScoresAndStoreExamQuestions (&Exam,
+ Gbl.Usrs.Me.Role.Logged == Rol_STD); // Update question score?
/***** Begin box *****/
Box_BoxBegin (NULL,Txt_Test_result,
@@ -719,21 +639,19 @@ void Tst_AssessTest (void)
}
/***** Write answers and solutions *****/
- HTM_TABLE_BeginWideMarginPadding (10);
- Tst_ShowTestResultAfterAssess (&Result);
- HTM_TABLE_End ();
+ TstExa_ShowExamAfterAssess (&Exam);
/***** Write total score and grade *****/
- if (TsV_IsVisibleTotalScore (TstCfg_GetConfigVisibility ()))
+ if (TstVis_IsVisibleTotalScore (TstCfg_GetConfigVisibility ()))
{
HTM_DIV_Begin ("class=\"DAT_N_BOLD CM\"");
HTM_TxtColonNBSP (Txt_Score);
- HTM_Double2Decimals (Result.Score);
+ HTM_Double2Decimals (Exam.Score);
HTM_BR ();
HTM_TxtColonNBSP (Txt_Grade);
- Tst_ComputeAndShowGrade (Result.NumQsts,
- Result.Score,
- TsR_SCORE_MAX);
+ TstExa_ComputeAndShowGrade (Exam.NumQsts,
+ Exam.Score,
+ TstExa_SCORE_MAX);
HTM_DIV_End ();
}
@@ -758,623 +676,26 @@ void Tst_AssessTest (void)
/*********** Get questions and answers from form to assess a test ************/
/*****************************************************************************/
-static void Tst_GetQuestionsAndAnswersFromForm (struct TsR_Result *Result)
+static void Tst_GetAnswersFromForm (struct TstExa_Exam *Exam)
{
unsigned NumQst;
- char StrQstIndOrAns[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Qstxx...x", "Indxx...x" or "Ansxx...x"
+ char StrAns[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Ansxx...x"
- /***** Get number of questions *****/
- Result->NumQsts = Tst_GetParamNumQsts ();
-
- /***** Loop for every question computing the score *****/
+ /***** Loop for every question getting user's answers *****/
for (NumQst = 0;
- NumQst < Result->NumQsts;
+ NumQst < Exam->NumQsts;
NumQst++)
{
- /***** Get question codes and user's answers from form *****/
- /* Get question code */
- snprintf (StrQstIndOrAns,sizeof (StrQstIndOrAns),
- "Qst%010u",
- NumQst);
- if ((Result->Questions[NumQst].QstCod = Par_GetParToLong (StrQstIndOrAns)) <= 0)
- Lay_ShowErrorAndExit ("Code of question is missing.");
-
- /* Get indexes for this question */ // TODO: Get indexes from tst_exam_questions in database instead of form
- snprintf (StrQstIndOrAns,sizeof (StrQstIndOrAns),
- "Ind%010u",
- NumQst);
- Par_GetParMultiToText (StrQstIndOrAns,Result->Questions[NumQst].StrIndexes,
- Tst_MAX_BYTES_INDEXES_ONE_QST); /* If choice ==> "0", "1", "2",... */
-
/* Get answers selected by user for this question */
- snprintf (StrQstIndOrAns,sizeof (StrQstIndOrAns),
+ snprintf (StrAns,sizeof (StrAns),
"Ans%010u",
NumQst);
- Par_GetParMultiToText (StrQstIndOrAns,Result->Questions[NumQst].StrAnswers,
- Tst_MAX_BYTES_ANSWERS_ONE_QST); /* If answer type == T/F ==> " ", "T", "F"; if choice ==> "0", "2",... */
+ Par_GetParMultiToText (StrAns,Exam->Questions[NumQst].StrAnswers,
+ TstExa_MAX_BYTES_ANSWERS_ONE_QST); /* If answer type == T/F ==> " ", "T", "F"; if choice ==> "0", "2",... */
}
- /***** Get if test result will be visible by teachers *****/
- Result->AllowTeachers = Par_GetParToBool ("AllowTchs");
- }
-
-/*****************************************************************************/
-/*********** Compute score of each question and store in database ************/
-/*****************************************************************************/
-
-static void Tst_ComputeAndStoreResultScore (struct TsR_Result *Result,
- bool UpdateQstScore)
- {
- unsigned NumQst;
- struct Tst_Question Question;
-
- /***** Initialize total score *****/
- Result->Score = 0.0;
- Result->NumQstsNotBlank = 0;
-
- /***** Compute and store scores of all questions *****/
- for (NumQst = 0;
- NumQst < Result->NumQsts;
- NumQst++)
- {
- /* Compute question score */
- Tst_QstConstructor (&Question);
- Question.QstCod = Result->Questions[NumQst].QstCod;
- Question.Answer.Type = Tst_GetQstAnswerType (Question.QstCod);
- Tst_ComputeAnswerScore (Result,NumQst,&Question);
- Tst_QstDestructor (&Question);
-
- /* Store test result question in database */
- TsR_StoreOneTestResultQstInDB (Result,
- NumQst); // 0, 1, 2, 3...
-
- /* Accumulate total score */
- Result->Score += Result->Questions[NumQst].Score;
- if (Result->Questions[NumQst].AnswerIsNotBlank)
- Result->NumQstsNotBlank++;
-
- /* Update the number of hits and the score of this question in tests database */
- if (UpdateQstScore)
- Tst_UpdateQstScore (Result,NumQst);
- }
-
- /***** Store test result in database *****/
- TsR_UpdateScoreOfTestResultInDB (Result);
- }
-
-/*****************************************************************************/
-/************* Write answers of a question when assessing a test *************/
-/*****************************************************************************/
-
-static void Tst_ComputeAnswerScore (struct TsR_Result *Result,
- unsigned NumQst,
- struct Tst_Question *Question)
- {
- /***** Write answer depending on type *****/
- switch (Question->Answer.Type)
- {
- case Tst_ANS_INT:
- Tst_ComputeIntAnsScore (Result,NumQst,Question);
- break;
- case Tst_ANS_FLOAT:
- Tst_ComputeFloatAnsScore (Result,NumQst,Question);
- break;
- case Tst_ANS_TRUE_FALSE:
- Tst_ComputeTFAnsScore (Result,NumQst,Question);
- break;
- case Tst_ANS_UNIQUE_CHOICE:
- case Tst_ANS_MULTIPLE_CHOICE:
- Tst_ComputeChoiceAnsScore (Result,NumQst,Question);
- break;
- case Tst_ANS_TEXT:
- Tst_ComputeTextAnsScore (Result,NumQst,Question);
- break;
- default:
- break;
- }
- }
-
-/*****************************************************************************/
-/**************** Write integer answer when assessing a test *****************/
-/*****************************************************************************/
-
-static void Tst_ComputeIntAnsScore (struct TsR_Result *Result,
- unsigned NumQst,
- struct Tst_Question *Question)
- {
- long AnswerUsr;
-
- /***** Get the numerical value of the correct answer *****/
- Tst_GetCorrectIntAnswerFromDB (Question);
-
- /***** Compute score *****/
- Result->Questions[NumQst].Score = 0.0; // Default score for blank or wrong answer
- Result->Questions[NumQst].AnswerIsNotBlank = (Result->Questions[NumQst].StrAnswers[0] != '\0');
- if (Result->Questions[NumQst].AnswerIsNotBlank) // If user has answered the answer
- if (sscanf (Result->Questions[NumQst].StrAnswers,"%ld",&AnswerUsr) == 1)
- if (AnswerUsr == Question->Answer.Integer) // Correct answer
- Result->Questions[NumQst].Score = 1.0;
- }
-
-static void Tst_GetCorrectIntAnswerFromDB (struct Tst_Question *Question)
- {
- MYSQL_RES *mysql_res;
- MYSQL_ROW row;
-
- /***** Query database *****/
- Question->Answer.NumOptions =
- (unsigned) DB_QuerySELECT (&mysql_res,"can not get answers of a question",
- "SELECT Answer" // row[0]
- " FROM tst_answers"
- " WHERE QstCod=%ld",
- Question->QstCod);
-
- /***** Check if number of rows is correct *****/
- Tst_CheckIfNumberOfAnswersIsOne (Question);
-
- /***** Get correct answer *****/
- row = mysql_fetch_row (mysql_res);
- if (sscanf (row[0],"%ld",&Question->Answer.Integer) != 1)
- Lay_ShowErrorAndExit ("Wrong integer answer.");
-
- /***** Free structure that stores the query result *****/
- DB_FreeMySQLResult (&mysql_res);
- }
-
-/*****************************************************************************/
-/***************** Write float answer when assessing a test ******************/
-/*****************************************************************************/
-
-static void Tst_ComputeFloatAnsScore (struct TsR_Result *Result,
- unsigned NumQst,
- struct Tst_Question *Question)
- {
- double AnswerUsr;
-
- /***** Check if number of rows is correct *****/
- if (Question->Answer.NumOptions != 2)
- Lay_ShowErrorAndExit ("Wrong float range.");
-
- /***** Get the numerical value of the minimum and maximum correct answers *****/
- Tst_GetCorrectFloatAnswerFromDB (Question);
-
- /***** Compute score *****/
- Result->Questions[NumQst].Score = 0.0; // Default score for blank or wrong answer
- Result->Questions[NumQst].AnswerIsNotBlank = (Result->Questions[NumQst].StrAnswers[0] != '\0');
- if (Result->Questions[NumQst].AnswerIsNotBlank) // If user has answered the answer
- {
- AnswerUsr = Str_GetDoubleFromStr (Result->Questions[NumQst].StrAnswers);
- // A bad formatted floating point answer will interpreted as 0.0
- Result->Questions[NumQst].Score = (AnswerUsr >= Question->Answer.FloatingPoint[0] &&
- AnswerUsr <= Question->Answer.FloatingPoint[1]) ? 1.0 : // If correct (inside the interval)
- 0.0; // If wrong (outside the interval)
- }
- }
-
-static void Tst_GetCorrectFloatAnswerFromDB (struct Tst_Question *Question)
- {
- MYSQL_RES *mysql_res;
- MYSQL_ROW row;
- unsigned NumOpt;
- double Tmp;
-
- /***** Query database *****/
- Question->Answer.NumOptions =
- (unsigned) DB_QuerySELECT (&mysql_res,"can not get answers of a question",
- "SELECT Answer" // row[0]
- " FROM tst_answers"
- " WHERE QstCod=%ld",
- Question->QstCod);
-
- /***** Check if number of rows is correct *****/
- if (Question->Answer.NumOptions != 2)
- Lay_ShowErrorAndExit ("Wrong float range.");
-
- /***** Get float range *****/
- for (NumOpt = 0;
- NumOpt < 2;
- NumOpt++)
- {
- row = mysql_fetch_row (mysql_res);
- Question->Answer.FloatingPoint[NumOpt] = Str_GetDoubleFromStr (row[0]);
- }
- if (Question->Answer.FloatingPoint[0] >
- Question->Answer.FloatingPoint[1]) // The maximum and the minimum are swapped
- {
- /* Swap maximum and minimum */
- Tmp = Question->Answer.FloatingPoint[0];
- Question->Answer.FloatingPoint[0] = Question->Answer.FloatingPoint[1];
- Question->Answer.FloatingPoint[1] = Tmp;
- }
-
- /***** Free structure that stores the query result *****/
- DB_FreeMySQLResult (&mysql_res);
- }
-
-/*****************************************************************************/
-/************** Write false / true answer when assessing a test **************/
-/*****************************************************************************/
-
-static void Tst_ComputeTFAnsScore (struct TsR_Result *Result,
- unsigned NumQst,
- struct Tst_Question *Question)
- {
- /***** Get answer true or false *****/
- Tst_GetCorrectTFAnswerFromDB (Question);
-
- /***** Compute score *****/
- Result->Questions[NumQst].AnswerIsNotBlank = (Result->Questions[NumQst].StrAnswers[0] != '\0');
- if (Result->Questions[NumQst].AnswerIsNotBlank) // User has selected T or F
- Result->Questions[NumQst].Score = (Result->Questions[NumQst].StrAnswers[0] == Question->Answer.TF) ? 1.0 : // Correct
- -1.0; // Wrong
- else
- Result->Questions[NumQst].Score = 0.0;
- }
-
-static void Tst_GetCorrectTFAnswerFromDB (struct Tst_Question *Question)
- {
- MYSQL_RES *mysql_res;
- MYSQL_ROW row;
-
- /***** Query database *****/
- Question->Answer.NumOptions =
- (unsigned) DB_QuerySELECT (&mysql_res,"can not get answers of a question",
- "SELECT Answer" // row[0]
- " FROM tst_answers"
- " WHERE QstCod=%ld",
- Question->QstCod);
-
- /***** Check if number of rows is correct *****/
- Tst_CheckIfNumberOfAnswersIsOne (Question);
-
- /***** Get answer *****/
- row = mysql_fetch_row (mysql_res);
- Question->Answer.TF = row[0][0];
-
- /***** Free structure that stores the query result *****/
- DB_FreeMySQLResult (&mysql_res);
- }
-
-/*****************************************************************************/
-/************ Compute score for single or multiple choice answer *************/
-/*****************************************************************************/
-
-void Tst_ComputeChoiceAnsScore (struct TsR_Result *Result,
- unsigned NumQst,
- struct Tst_Question *Question)
- {
- unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]; // Indexes of all answers of this question
- bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION];
-
- /***** Get correct options of test question from database *****/
- Tst_GetCorrectChoiceAnswerFromDB (Question);
-
- /***** Get indexes for this question from string *****/
- Tst_GetIndexesFromStr (Result->Questions[NumQst].StrIndexes,Indexes);
-
- /***** Get the user's answers for this question from string *****/
- Tst_GetAnswersFromStr (Result->Questions[NumQst].StrAnswers,UsrAnswers);
-
- /***** Compute the total score of this question *****/
- Tst_ComputeScoreQst (Result,NumQst,Question,Indexes,UsrAnswers);
- }
-
-static void Tst_GetCorrectChoiceAnswerFromDB (struct Tst_Question *Question)
- {
- MYSQL_RES *mysql_res;
- MYSQL_ROW row;
- unsigned NumOpt;
-
- /***** Query database *****/
- Question->Answer.NumOptions =
- (unsigned) DB_QuerySELECT (&mysql_res,"can not get answers of a question",
- "SELECT Correct" // row[0]
- " FROM tst_answers"
- " WHERE QstCod=%ld"
- " ORDER BY AnsInd",
- Question->QstCod);
- for (NumOpt = 0;
- NumOpt < Question->Answer.NumOptions;
- NumOpt++)
- {
- /* Get next answer */
- row = mysql_fetch_row (mysql_res);
-
- /* Assign correctness (row[0]) of this answer (this option) */
- Question->Answer.Options[NumOpt].Correct = (row[0][0] == 'Y');
- }
-
- /* Free structure that stores the query result */
- DB_FreeMySQLResult (&mysql_res);
- }
-
-/*****************************************************************************/
-/********************* Get vector of indexes from string *********************/
-/*****************************************************************************/
-
-void Tst_GetIndexesFromStr (const char StrIndexesOneQst[Tst_MAX_BYTES_INDEXES_ONE_QST + 1], // 0 1 2 3, 3 0 2 1, etc.
- unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION])
- {
- unsigned NumOpt;
- const char *Ptr;
- char StrOneIndex[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
-
- /***** Get indexes from string *****/
- for (NumOpt = 0, Ptr = StrIndexesOneQst;
- NumOpt < Tst_MAX_OPTIONS_PER_QUESTION && *Ptr;
- NumOpt++)
- {
- Par_GetNextStrUntilSeparParamMult (&Ptr,StrOneIndex,Cns_MAX_DECIMAL_DIGITS_UINT);
-
- if (sscanf (StrOneIndex,"%u",&(Indexes[NumOpt])) != 1)
- Lay_ShowErrorAndExit ("Wrong index of answer.");
-
- if (Indexes[NumOpt] >= Tst_MAX_OPTIONS_PER_QUESTION)
- Lay_ShowErrorAndExit ("Wrong index of answer.");
- }
-
- /***** Initialize remaining to 0 *****/
- for (;
- NumOpt < Tst_MAX_OPTIONS_PER_QUESTION;
- NumOpt++)
- Indexes[NumOpt] = 0;
- }
-
-/*****************************************************************************/
-/****************** Get vector of user's answers from string *****************/
-/*****************************************************************************/
-
-static void Tst_GetAnswersFromStr (const char StrAnswersOneQst[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1],
- bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION])
- {
- unsigned NumOpt;
- const char *Ptr;
- char StrOneAnswer[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
- unsigned AnsUsr;
-
- /***** Initialize all answers to false *****/
- for (NumOpt = 0;
- NumOpt < Tst_MAX_OPTIONS_PER_QUESTION;
- NumOpt++)
- UsrAnswers[NumOpt] = false;
-
- /***** Set selected answers to true *****/
- for (NumOpt = 0, Ptr = StrAnswersOneQst;
- NumOpt < Tst_MAX_OPTIONS_PER_QUESTION && *Ptr;
- NumOpt++)
- {
- Par_GetNextStrUntilSeparParamMult (&Ptr,StrOneAnswer,Cns_MAX_DECIMAL_DIGITS_UINT);
-
- if (sscanf (StrOneAnswer,"%u",&AnsUsr) != 1)
- Lay_ShowErrorAndExit ("Bad user's answer.");
-
- if (AnsUsr >= Tst_MAX_OPTIONS_PER_QUESTION)
- Lay_ShowErrorAndExit ("Bad user's answer.");
-
- UsrAnswers[AnsUsr] = true;
- }
- }
-
-/*****************************************************************************/
-/*********************** Compute the score of a question *********************/
-/*****************************************************************************/
-
-static void Tst_ComputeScoreQst (struct TsR_Result *Result,
- unsigned NumQst,
- const struct Tst_Question *Question,
- unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION], // Indexes of all answers of this question
- bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION])
- {
- unsigned NumOpt;
- unsigned NumOptTotInQst = 0;
- unsigned NumOptCorrInQst = 0;
- unsigned NumAnsGood = 0;
- unsigned NumAnsBad = 0;
-
- /***** Compute the total score of this question *****/
- for (NumOpt = 0;
- NumOpt < Question->Answer.NumOptions;
- NumOpt++)
- {
- NumOptTotInQst++;
- if (Question->Answer.Options[Indexes[NumOpt]].Correct)
- NumOptCorrInQst++;
-
- if (UsrAnswers[Indexes[NumOpt]]) // This answer has been selected by the user
- {
- if (Question->Answer.Options[Indexes[NumOpt]].Correct)
- NumAnsGood++;
- else
- NumAnsBad++;
- }
- }
-
- /* The answer is blank? */
- Result->Questions[NumQst].AnswerIsNotBlank = NumAnsGood != 0 || NumAnsBad != 0;
- if (Result->Questions[NumQst].AnswerIsNotBlank)
- {
- /* Compute the score */
- if (Question->Answer.Type == Tst_ANS_UNIQUE_CHOICE)
- {
- if (NumOptTotInQst >= 2) // It should be 2 options at least
- Result->Questions[NumQst].Score = (double) NumAnsGood -
- (double) NumAnsBad / (double) (NumOptTotInQst - 1);
- else // 0 or 1 options (impossible)
- Result->Questions[NumQst].Score = (double) NumAnsGood;
- }
- else // AnswerType == Tst_ANS_MULTIPLE_CHOICE
- {
- if (NumOptCorrInQst) // There are correct options in the question
- {
- if (NumOptCorrInQst < NumOptTotInQst) // If there are correct options and wrong options (typical case)
- Result->Questions[NumQst].Score = (double) NumAnsGood / (double) NumOptCorrInQst -
- (double) NumAnsBad / (double) (NumOptTotInQst - NumOptCorrInQst);
- else // Si todas the opciones son correctas (caso raro)
- Result->Questions[NumQst].Score = (double) NumAnsGood / (double) NumOptCorrInQst;
- }
- else
- {
- if (NumOptTotInQst) // There are options but none is correct (extrange case)
- Result->Questions[NumQst].Score = - (double) NumAnsBad / (double) NumOptTotInQst;
- else // There are no options (impossible!)
- Result->Questions[NumQst].Score = 0.0;
- }
- }
- }
- else // Answer is blank
- Result->Questions[NumQst].Score = 0.0;
- }
-
-/*****************************************************************************/
-/********************* Compute score for text answer *************************/
-/*****************************************************************************/
-
-static void Tst_ComputeTextAnsScore (struct TsR_Result *Result,
- unsigned NumQst,
- struct Tst_Question *Question)
- {
- unsigned NumOpt;
- char TextAnsUsr[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
- char TextAnsOK[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
- bool Correct = false;
-
- /***** Get correct answers for this question from database *****/
- Tst_GetCorrectTextAnswerFromDB (Question);
-
- /***** Compute score *****/
- Result->Questions[NumQst].Score = 0.0; // Default score for blank or wrong answer
- Result->Questions[NumQst].AnswerIsNotBlank = (Result->Questions[NumQst].StrAnswers[0] != '\0');
- if (Result->Questions[NumQst].AnswerIsNotBlank) // If user has answered the answer
- {
- /* Filter the user answer */
- Str_Copy (TextAnsUsr,Result->Questions[NumQst].StrAnswers,
- Tst_MAX_BYTES_ANSWERS_ONE_QST);
-
- /* In order to compare student answer to stored answer,
- the text answers are stored avoiding two or more consecurive spaces */
- Str_ReplaceSeveralSpacesForOne (TextAnsUsr);
-
- Str_ConvertToComparable (TextAnsUsr);
-
- for (NumOpt = 0;
- NumOpt < Question->Answer.NumOptions;
- NumOpt++)
- {
- /* Filter this correct answer */
- Str_Copy (TextAnsOK,Question->Answer.Options[NumOpt].Text,
- Tst_MAX_BYTES_ANSWERS_ONE_QST);
- Str_ConvertToComparable (TextAnsOK);
-
- /* Check is user answer is correct */
- if (!strcoll (TextAnsUsr,TextAnsOK))
- {
- Correct = true;
- break;
- }
- }
-
- if (Correct)
- Result->Questions[NumQst].Score = 1.0; // Correct answer
- }
- }
-
-static void Tst_GetCorrectTextAnswerFromDB (struct Tst_Question *Question)
- {
- MYSQL_RES *mysql_res;
- MYSQL_ROW row;
- unsigned NumOpt;
- double Tmp;
-
- /***** Query database *****/
- Question->Answer.NumOptions =
- (unsigned) DB_QuerySELECT (&mysql_res,"can not get answers of a question",
- "SELECT Answer" // row[0]
- " FROM tst_answers"
- " WHERE QstCod=%ld",
- Question->QstCod);
-
- /***** Get text and correctness of answers for this question from database (one row per answer) *****/
- for (NumOpt = 0;
- NumOpt < Question->Answer.NumOptions;
- NumOpt++)
- {
- /***** Get next answer *****/
- row = mysql_fetch_row (mysql_res);
-
- /***** Allocate memory for text in this choice answer *****/
- if (!Tst_AllocateTextChoiceAnswer (Question,NumOpt))
- /* Abort on error */
- Ale_ShowAlertsAndExit ();
-
- /***** Copy answer text (row[1]) and convert it, that is in HTML, to rigorous HTML ******/
- Str_Copy (Question->Answer.Options[NumOpt].Text,row[1],
- Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
- Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
- Question->Answer.Options[NumOpt].Text,
- Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false);
- }
-
- /***** Get float range *****/
- for (NumOpt = 0;
- NumOpt < 2;
- NumOpt++)
- {
- row = mysql_fetch_row (mysql_res);
- Question->Answer.FloatingPoint[NumOpt] = Str_GetDoubleFromStr (row[0]);
- }
- if (Question->Answer.FloatingPoint[0] >
- Question->Answer.FloatingPoint[1]) // The maximum and the minimum are swapped
- {
- /* Swap maximum and minimum */
- Tmp = Question->Answer.FloatingPoint[0];
- Question->Answer.FloatingPoint[0] = Question->Answer.FloatingPoint[1];
- Question->Answer.FloatingPoint[1] = Tmp;
- }
-
- /***** Free structure that stores the query result *****/
- DB_FreeMySQLResult (&mysql_res);
- }
-
-/*****************************************************************************/
-/************ Compute and show total grade out of maximum grade **************/
-/*****************************************************************************/
-
-void Tst_ComputeAndShowGrade (unsigned NumQsts,double Score,double MaxGrade)
- {
- Tst_ShowGrade (Tst_ComputeGrade (NumQsts,Score,MaxGrade),MaxGrade);
- }
-
-/*****************************************************************************/
-/**************** Compute total grade out of maximum grade *******************/
-/*****************************************************************************/
-
-double Tst_ComputeGrade (unsigned NumQsts,double Score,double MaxGrade)
- {
- double MaxScore;
- double Grade;
-
- /***** Compute grade *****/
- if (NumQsts)
- {
- MaxScore = (double) NumQsts;
- Grade = Score * MaxGrade / MaxScore;
- }
- else
- Grade = 0.0;
-
- return Grade;
- }
-
-/*****************************************************************************/
-/****************** Show total grade out of maximum grade ********************/
-/*****************************************************************************/
-
-void Tst_ShowGrade (double Grade,double MaxGrade)
- {
- /***** Write grade over maximum grade *****/
- HTM_Double2Decimals (Grade);
- HTM_Txt ("/");
- HTM_Double2Decimals (MaxGrade);
+ /***** Get if test exam will be visible by teachers *****/
+ Exam->AllowTeachers = Par_GetParToBool ("AllowTchs");
}
/*****************************************************************************/
@@ -1385,7 +706,6 @@ void Tst_ShowGrade (double Grade,double MaxGrade)
static bool Tst_CheckIfNextTstAllowed (void)
{
extern const char *Hlp_ASSESSMENT_Tests;
- extern const char *Txt_Test;
extern const char *Txt_You_can_not_take_a_new_test_until;
extern const char *Txt_Today;
MYSQL_RES *mysql_res;
@@ -1497,20 +817,20 @@ static Tst_Status_t Tst_GetTstStatus (unsigned NumTst)
}
/*****************************************************************************/
-/************************* Get number of hits to test ************************/
+/***************** Get number of test exams generated by me ******************/
/*****************************************************************************/
-static unsigned Tst_GetNumAccessesTst (void)
+static unsigned Tst_GetNumExamsGeneratedByMe (void)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned long NumRows;
- unsigned NumAccessesTst = 0;
+ unsigned NumExamsGeneratedByMe = 0;
if (Gbl.Usrs.Me.IBelongToCurrentCrs)
{
- /***** Get number of hits to test from database *****/
- NumRows = DB_QuerySELECT (&mysql_res,"can not get number of hits to test",
+ /***** 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]
" FROM crs_usr"
" WHERE CrsCod=%ld AND UsrCod=%ld",
@@ -1518,15 +838,15 @@ static unsigned Tst_GetNumAccessesTst (void)
Gbl.Usrs.Me.UsrDat.UsrCod);
if (NumRows == 0)
- NumAccessesTst = 0;
+ NumExamsGeneratedByMe = 0;
else if (NumRows == 1)
{
/* Get number of hits */
row = mysql_fetch_row (mysql_res);
if (row[0] == NULL)
- NumAccessesTst = 0;
- else if (sscanf (row[0],"%u",&NumAccessesTst) != 1)
- NumAccessesTst = 0;
+ NumExamsGeneratedByMe = 0;
+ else if (sscanf (row[0],"%u",&NumExamsGeneratedByMe) != 1)
+ NumExamsGeneratedByMe = 0;
}
else
Lay_ShowErrorAndExit ("Error when getting number of hits to test.");
@@ -1535,56 +855,94 @@ static unsigned Tst_GetNumAccessesTst (void)
DB_FreeMySQLResult (&mysql_res);
}
- return NumAccessesTst;
+ return NumExamsGeneratedByMe;
}
/*****************************************************************************/
-/*** Write the test questions when showing a new test exam to be answered ****/
+/************************ Show a test exam to be answered ********************/
/*****************************************************************************/
-static void Tst_ShowTestQuestionsWhenSeeing (struct TsR_Result *Result)
+static void Tst_ShowTestExamToFillIt (struct TstExa_Exam *Exam,
+ unsigned NumExamsGeneratedByMe,
+ Tst_RequestOrConfirm_t RequestOrConfirm)
{
+ extern const char *Hlp_ASSESSMENT_Tests;
+ extern const char *Txt_Test;
+ // extern const char *Txt_Done_assess_test;
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumQst;
struct Tst_Question Question;
-
- /***** Trivial check *****/
- if (Result->NumQsts == 0)
- return;
-
- /***** Begin table *****/
- HTM_TABLE_BeginWideMarginPadding (10);
-
- /***** Write one row for each question *****/
- for (NumQst = 0;
- NumQst < Result->NumQsts;
- NumQst++)
+ static const Act_Action_t Action[Tst_NUM_REQUEST_OR_CONFIRM] =
{
- Gbl.RowEvenOdd = NumQst % 2;
+ [Tst_REQUEST] = ActReqAssTst,
+ [Tst_CONFIRM] = ActAssTst,
+ };
+ static char *TxtButton[Tst_NUM_REQUEST_OR_CONFIRM] =
+ {
+ [Tst_REQUEST] = "He terminado", // TODO: Need translation!!!
+ [Tst_CONFIRM] = "Enviar", // TODO: Need translation!!!
+ };
- /* Create test question */
- Tst_QstConstructor (&Question);
- Question.QstCod = Result->Questions[NumQst].QstCod;
+ /***** Begin box *****/
+ Box_BoxBegin (NULL,Txt_Test,
+ NULL,NULL,
+ Hlp_ASSESSMENT_Tests,Box_NOT_CLOSABLE);
+ Lay_WriteHeaderClassPhoto (false,false,
+ Gbl.Hierarchy.Ins.InsCod,
+ Gbl.Hierarchy.Deg.DegCod,
+ Gbl.Hierarchy.Crs.CrsCod);
- /* Show question */
- if (Tst_GetOneQuestionByCod (Question.QstCod,&mysql_res)) // Question exists
+ if (Exam->NumQsts)
+ {
+ /***** Begin form *****/
+ Frm_StartForm (Action[RequestOrConfirm]);
+ TstExa_PutParamExaCod (Exam->ExaCod);
+ Par_PutHiddenParamUnsigned (NULL,"NumTst",NumExamsGeneratedByMe);
+
+ /***** Begin table *****/
+ HTM_TABLE_BeginWideMarginPadding (10);
+
+ /***** Write one row for each question *****/
+ for (NumQst = 0;
+ NumQst < Exam->NumQsts;
+ NumQst++)
{
- /* Get row of the result of the query */
- row = mysql_fetch_row (mysql_res);
+ Gbl.RowEvenOdd = NumQst % 2;
- /* Write question and answers */
- Tst_WriteQstAndAnsSeeing (Result,NumQst,&Question,row);
+ /* Create test question */
+ Tst_QstConstructor (&Question);
+ Question.QstCod = Exam->Questions[NumQst].QstCod;
+
+ /* Show question */
+ if (Tst_GetOneQuestionByCod (Question.QstCod,&mysql_res)) // Question exists
+ {
+ /* Get row of the result of the query */
+ row = mysql_fetch_row (mysql_res);
+
+ /* Write question and answers */
+ Tst_WriteQstAndAnsSeeing (Exam,NumQst,&Question,row);
+ }
+ else
+ Lay_ShowErrorAndExit ("Wrong question.");
+
+ /* Destroy test question */
+ Tst_QstDestructor (&Question);
}
- else
- Lay_ShowErrorAndExit ("Wrong question.");
- /* Destroy test question */
- Tst_QstDestructor (&Question);
+ /***** End table *****/
+ HTM_TABLE_End ();
+
+ /***** Test exam will be visible by teachers *****/
+ Tst_PutCheckBoxAllowTeachers (true);
+
+ /***** End form *****/
+ Btn_PutConfirmButton (TxtButton[RequestOrConfirm]);
+ Frm_EndForm ();
}
- /***** End table *****/
- HTM_TABLE_End ();
+ /***** End box *****/
+ Box_BoxEnd ();
}
/*****************************************************************************/
@@ -1616,77 +974,11 @@ void Tst_ShowTagList (unsigned NumTags,MYSQL_RES *mysql_res)
HTM_Txt (Txt_no_tags);
}
-/*****************************************************************************/
-/******************* Show the result of assessing a test *********************/
-/*****************************************************************************/
-
-static void Tst_ShowTestResultAfterAssess (struct TsR_Result *Result)
- {
- extern const char *Txt_Question_removed;
- MYSQL_RES *mysql_res;
- MYSQL_ROW row;
- unsigned NumQst;
-
- /***** Initialize score and number of questions not blank *****/
- Result->NumQstsNotBlank = 0;
- Result->Score = 0.0;
-
- for (NumQst = 0;
- NumQst < Result->NumQsts;
- NumQst++)
- {
- Gbl.RowEvenOdd = NumQst % 2;
-
- /***** Query database *****/
- if (Tst_GetOneQuestionByCod (Result->Questions[NumQst].QstCod,&mysql_res)) // Question exists
- {
- /***** Write question and answers *****/
- row = mysql_fetch_row (mysql_res);
- Tst_WriteQstAndAnsTestResult (&Gbl.Usrs.Me.UsrDat,
- Result,
- NumQst,
- row,
- TstCfg_GetConfigVisibility ());
-
- /***** Store test result question in database *****/
- TsR_StoreOneTestResultQstInDB (Result,
- NumQst); // 0, 1, 2, 3...
-
- /***** Compute total score *****/
- Result->Score += Result->Questions[NumQst].Score;
- if (Result->Questions[NumQst].AnswerIsNotBlank)
- Result->NumQstsNotBlank++;
-
- /***** Update the number of accesses and the score of this question *****/
- if (Gbl.Usrs.Me.Role.Logged == Rol_STD)
- Tst_UpdateQstScore (Result,NumQst);
- }
- else
- {
- /***** Question does not exists *****/
- HTM_TR_Begin (NULL);
-
- HTM_TD_Begin ("class=\"RT COLOR%u\"",Gbl.RowEvenOdd);
- Tst_WriteNumQst (NumQst + 1);
- HTM_TD_End ();
-
- HTM_TD_Begin ("class=\"DAT_LIGHT LT COLOR%u\"",Gbl.RowEvenOdd);
- HTM_Txt (Txt_Question_removed);
- HTM_TD_End ();
-
- HTM_TR_End ();
- }
-
- /***** Free structure that stores the query result *****/
- DB_FreeMySQLResult (&mysql_res);
- }
- }
-
/*****************************************************************************/
/********** Write a row of a test, with one question and its answer **********/
/*****************************************************************************/
-static void Tst_WriteQstAndAnsSeeing (struct TsR_Result *Result,
+static void Tst_WriteQstAndAnsSeeing (struct TstExa_Exam *Exam,
unsigned NumQst,
struct Tst_Question *Question,
MYSQL_ROW row)
@@ -1730,7 +1022,7 @@ static void Tst_WriteQstAndAnsSeeing (struct TsR_Result *Result,
"TEST_MED_SHOW");
/* Answers */
- Tst_WriteAnswersSeeing (Result,NumQst,Question);
+ Tst_WriteAnswersSeeing (Exam,NumQst,Question);
HTM_TD_End ();
@@ -1738,79 +1030,6 @@ static void Tst_WriteQstAndAnsSeeing (struct TsR_Result *Result,
HTM_TR_End ();
}
-/*****************************************************************************/
-/********** Write a row of a test, with one question and its answer **********/
-/*****************************************************************************/
-
-void Tst_WriteQstAndAnsTestResult (struct UsrData *UsrDat,
- struct TsR_Result *Result,
- unsigned NumQst,
- MYSQL_ROW row,
- unsigned Visibility)
- {
- struct Tst_Question Question;
- bool IsVisibleQstAndAnsTxt = TsV_IsVisibleQstAndAnsTxt (Visibility);
- /*
- row[0] UNIX_TIMESTAMP(EditTime)
- row[1] AnsType
- row[2] Shuffle
- row[3] Stem
- row[4] Feedback
- row[5] MedCod
- row[6] NumHits
- row[7] NumHitsNotBlank
- row[8] Score
- */
-
- /***** Create test question *****/
- Tst_QstConstructor (&Question);
- Question.QstCod = Result->Questions[NumQst].QstCod;
-
- /***** Begin row *****/
- HTM_TR_Begin (NULL);
-
- /***** Number of question and answer type (row[1]) *****/
- HTM_TD_Begin ("class=\"RT COLOR%u\"",Gbl.RowEvenOdd);
- Tst_WriteNumQst (NumQst + 1);
- Question.Answer.Type = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[1]);
- Tst_WriteAnswerType (Question.Answer.Type);
- HTM_TD_End ();
-
- /***** Stem, media and answers *****/
- HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
-
- /* Stem (row[3]) */
- Tst_WriteQstStem (row[3],"TEST_EXA",IsVisibleQstAndAnsTxt);
-
- /* Media (row[5]) */
- if (IsVisibleQstAndAnsTxt)
- {
- Question.Media.MedCod = Str_ConvertStrCodToLongCod (row[5]);
- Med_GetMediaDataByCod (&Question.Media);
- Med_ShowMedia (&Question.Media,
- "TEST_MED_SHOW_CONT",
- "TEST_MED_SHOW");
- }
-
- /* Answers */
- Tst_ComputeAnswerScore (Result,NumQst,&Question);
- Tst_WriteAnswersResult (UsrDat,
- Result,NumQst,&Question,
- Visibility);
-
- /* Question feedback (row[4]) */
- if (TsV_IsVisibleFeedbackTxt (Visibility))
- Tst_WriteQstFeedback (row[4],"TEST_EXA_LIGHT");
-
- HTM_TD_End ();
-
- /***** End row *****/
- HTM_TR_End ();
-
- /***** Destroy test question *****/
- Tst_QstDestructor (&Question);
- }
-
/*****************************************************************************/
/********************* Write the number of a test question *******************/
/*****************************************************************************/
@@ -1969,42 +1188,20 @@ void Tst_WriteQstFeedback (const char *Feedback,const char *ClassFeedback)
}
}
-/*****************************************************************************/
-/*********************** Update the score of a question **********************/
-/*****************************************************************************/
-
-static void Tst_UpdateQstScore (const struct TsR_Result *Result,unsigned NumQst)
- {
- /***** Update number of clicks and score of the question *****/
- Str_SetDecimalPointToUS (); // To print the floating point as a dot
- if (Result->Questions[NumQst].AnswerIsNotBlank)
- DB_QueryUPDATE ("can not update the score of a question",
- "UPDATE tst_questions"
- " SET NumHits=NumHits+1,NumHitsNotBlank=NumHitsNotBlank+1,"
- "Score=Score+(%.15lg)"
- " WHERE QstCod=%ld",
- Result->Questions[NumQst].Score,
- Result->Questions[NumQst].QstCod);
- else // The answer is blank
- DB_QueryUPDATE ("can not update the score of a question",
- "UPDATE tst_questions"
- " SET NumHits=NumHits+1"
- " WHERE QstCod=%ld",
- Result->Questions[NumQst].QstCod);
- Str_SetDecimalPointToLocal (); // Return to local system
- }
-
/*****************************************************************************/
/*********** Update my number of accesses to test in this course *************/
/*****************************************************************************/
-static void Tst_UpdateMyNumAccessTst (unsigned NumAccessesTst)
+static void Tst_IncreaseMyNumAccessTst (void)
{
+ /***** Trivial check *****/
+ if (!Gbl.Usrs.Me.IBelongToCurrentCrs)
+ return;
+
/***** Update my number of accesses to test in this course *****/
DB_QueryUPDATE ("can not update the number of accesses to test",
- "UPDATE crs_usr SET NumAccTst=%u"
+ "UPDATE crs_usr SET NumAccTst=NumAccTst+1"
" WHERE CrsCod=%ld AND UsrCod=%ld",
- NumAccessesTst,
Gbl.Hierarchy.Crs.CrsCod,Gbl.Usrs.Me.UsrDat.UsrCod);
}
@@ -2236,7 +1433,7 @@ static void Tst_PutIconsTests (void *TestPtr)
NULL,NULL);
}
- /***** Put icon to view tests results *****/
+ /***** Put icon to view test exams *****/
switch (Gbl.Usrs.Me.Role.Logged)
{
case Rol_STD:
@@ -2861,7 +2058,7 @@ static void Tst_ShowFormConfigTst (void)
HTM_TR_End ();
- /***** Visibility of results *****/
+ /***** Visibility of test exams *****/
HTM_TR_Begin (NULL);
HTM_TD_Begin ("class=\"%s RT\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
@@ -2869,7 +2066,7 @@ static void Tst_ShowFormConfigTst (void)
HTM_TD_End ();
HTM_TD_Begin ("class=\"LB\"");
- TsV_PutVisibilityCheckboxes (TstCfg_GetConfigVisibility ());
+ TstVis_PutVisibilityCheckboxes (TstCfg_GetConfigVisibility ());
HTM_TD_End ();
HTM_TR_End ();
@@ -3233,7 +2430,7 @@ static void Tst_GetQuestions (struct Tst_Test *Test,MYSQL_RES **mysql_res)
/*****************************************************************************/
static void Tst_GetQuestionsForNewTestFromDB (struct Tst_Test *Test,
- struct TsR_Result *Result)
+ struct TstExa_Exam *Exam)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
@@ -3346,14 +2543,14 @@ static void Tst_GetQuestionsForNewTestFromDB (struct Tst_Test *Test,
Lay_ShowAlert (Lay_INFO,Query);
*/
/* Make the query */
- Result->NumQsts =
- Test->NumQsts = (unsigned) DB_QuerySELECT (&mysql_res,"can not get questions",
- "%s",
- Query);
+ Exam->NumQsts =
+ Test->NumQsts = (unsigned) DB_QuerySELECT (&mysql_res,"can not get questions",
+ "%s",
+ Query);
- /***** Get questions and answers from database and store them in result *****/
+ /***** Get questions and answers from database *****/
for (NumQst = 0;
- NumQst < Result->NumQsts;
+ NumQst < Exam->NumQsts;
NumQst++)
{
/* Get question row */
@@ -3365,7 +2562,7 @@ static void Tst_GetQuestionsForNewTestFromDB (struct Tst_Test *Test,
*/
/* Get question code (row[0]) */
- if ((Result->Questions[NumQst].QstCod = Str_ConvertStrCodToLongCod (row[0])) < 0)
+ if ((Exam->Questions[NumQst].QstCod = Str_ConvertStrCodToLongCod (row[0])) < 0)
Lay_ShowErrorAndExit ("Wrong code of question.");
/* Get answer type (row[1]) */
@@ -3381,13 +2578,13 @@ static void Tst_GetQuestionsForNewTestFromDB (struct Tst_Test *Test,
case Tst_ANS_FLOAT:
case Tst_ANS_TRUE_FALSE:
case Tst_ANS_TEXT:
- Result->Questions[NumQst].StrIndexes[0] = '\0';
+ Exam->Questions[NumQst].StrIndexes[0] = '\0';
break;
case Tst_ANS_UNIQUE_CHOICE:
case Tst_ANS_MULTIPLE_CHOICE:
/* If answer type is unique or multiple option,
- get indexes of answers depending on shuffle */
- Tst_GetChoiceAnsSeeing (Result,NumQst,Shuffle);
+ generate indexes of answers depending on shuffle */
+ Tst_GenerateChoiceIndexesDependingOnShuffle (Exam,NumQst,Shuffle);
break;
default:
break;
@@ -3397,32 +2594,33 @@ static void Tst_GetQuestionsForNewTestFromDB (struct Tst_Test *Test,
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. */
- Result->Questions[NumQst].StrAnswers[0] = '\0';
+ Exam->Questions[NumQst].StrAnswers[0] = '\0';
}
- /***** Get if test result will be visible by teachers *****/
- Result->AllowTeachers = Par_GetParToBool ("AllowTchs");
+ /***** Get if test exam will be visible by teachers *****/
+ Exam->AllowTeachers = Par_GetParToBool ("AllowTchs");
}
/*****************************************************************************/
/********* Get single or multiple choice answer when seeing a test ***********/
/*****************************************************************************/
-static void Tst_GetChoiceAnsSeeing (struct TsR_Result *Result,
+static void Tst_GenerateChoiceIndexesDependingOnShuffle (struct TstExa_Exam *Exam,
unsigned NumQst,
bool Shuffle)
{
+ extern const char *Par_SEPARATOR_PARAM_MULTIPLE;
struct Tst_Question Question;
unsigned NumOpt;
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned Index;
bool ErrorInIndex;
- char StrInd[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
+ char StrInd[1 + Cns_MAX_DECIMAL_DIGITS_UINT + 1];
/***** Create test question *****/
Tst_QstConstructor (&Question);
- Question.QstCod = Result->Questions[NumQst].QstCod;
+ Question.QstCod = Exam->Questions[NumQst].QstCod;
/***** Get answers of question from database *****/
Tst_GetAnswersQst (&Question,&mysql_res,Shuffle);
@@ -3455,10 +2653,12 @@ static void Tst_GetChoiceAnsSeeing (struct TsR_Result *Result,
if (ErrorInIndex)
Lay_ShowErrorAndExit ("Wrong index of answer.");
- snprintf (StrInd,sizeof (StrInd),NumOpt == 0 ? "%u" :
- ",%u",Index);
- Str_Concat (Result->Questions[NumQst].StrIndexes,StrInd,
- Tst_MAX_BYTES_INDEXES_ONE_QST);
+ if (NumOpt == 0)
+ snprintf (StrInd,sizeof (StrInd),"%u",Index);
+ else
+ snprintf (StrInd,sizeof (StrInd),"%s%u",Par_SEPARATOR_PARAM_MULTIPLE,Index);
+ Str_Concat (Exam->Questions[NumQst].StrIndexes,StrInd,
+ TstExa_MAX_BYTES_INDEXES_ONE_QST);
}
/***** Free structure that stores the query result *****/
@@ -4097,7 +3297,7 @@ void Tst_WriteAnswersListing (struct Tst_Question *Question)
/************** Write answers of a question when seeing a test ***************/
/*****************************************************************************/
-static void Tst_WriteAnswersSeeing (struct TsR_Result *Result,
+static void Tst_WriteAnswersSeeing (struct TstExa_Exam *Exam,
unsigned NumQst,
struct Tst_Question *Question)
{
@@ -4107,13 +3307,13 @@ static void Tst_WriteAnswersSeeing (struct TsR_Result *Result,
switch (Question->Answer.Type)
{
case Tst_ANS_INT:
- Tst_WriteIntAnsSeeing (Result,NumQst);
+ Tst_WriteIntAnsSeeing (Exam,NumQst);
break;
case Tst_ANS_FLOAT:
- Tst_WriteFloatAnsSeeing (Result,NumQst);
+ Tst_WriteFloatAnsSeeing (Exam,NumQst);
break;
case Tst_ANS_TRUE_FALSE:
- Tst_WriteTFAnsSeeing (Result,NumQst);
+ Tst_WriteTFAnsSeeing (Exam,NumQst);
break;
case Tst_ANS_UNIQUE_CHOICE:
case Tst_ANS_MULTIPLE_CHOICE:
@@ -4122,79 +3322,19 @@ static void Tst_WriteAnswersSeeing (struct TsR_Result *Result,
false); // Don't shuffle
/* Write answer */
- Tst_WriteChoiceAnsSeeing (Result,NumQst,Question,mysql_res);
+ Tst_WriteChoiceAnsSeeing (Exam,NumQst,Question,mysql_res);
/* Free structure that stores the query result */
DB_FreeMySQLResult (&mysql_res);
break;
case Tst_ANS_TEXT:
- Tst_WriteTextAnsSeeing (Result,NumQst);
+ Tst_WriteTextAnsSeeing (Exam,NumQst);
break;
default:
break;
}
}
-/*****************************************************************************/
-/************* Write answers of a question when assessing a test *************/
-/*****************************************************************************/
-
-static void Tst_WriteAnswersResult (struct UsrData *UsrDat,
- const struct TsR_Result *Result,
- unsigned NumQst,
- struct Tst_Question *Question,
- unsigned Visibility)
- {
- MYSQL_RES *mysql_res;
-
- /***** Get answer of a question from 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
- */
-
- /***** Write answer depending on type *****/
- switch (Question->Answer.Type)
- {
- case Tst_ANS_INT:
- Tst_WriteIntAnsResult (UsrDat,Result,
- NumQst,Question,mysql_res,
- Visibility);
- break;
- case Tst_ANS_FLOAT:
- Tst_WriteFloatAnsResult (UsrDat,Result,
- NumQst,Question,mysql_res,
- Visibility);
- break;
- case Tst_ANS_TRUE_FALSE:
- Tst_WriteTFAnsResult (UsrDat,Result,
- NumQst,Question,mysql_res,
- Visibility);
- break;
- case Tst_ANS_UNIQUE_CHOICE:
- case Tst_ANS_MULTIPLE_CHOICE:
- Tst_WriteChoiceAnsResult (UsrDat,Result,
- NumQst,Question,mysql_res,
- Visibility);
- break;
- case Tst_ANS_TEXT:
- Tst_WriteTextAnsResult (UsrDat,Result,
- NumQst,Question,mysql_res,
- Visibility);
- break;
- default:
- break;
- }
-
- /***** Free structure that stores the query result *****/
- DB_FreeMySQLResult (&mysql_res);
- }
-
/*****************************************************************************/
/***************** Check if a question is valid for a game *******************/
/*****************************************************************************/
@@ -4236,116 +3376,19 @@ static void Tst_WriteIntAnsListing (const struct Tst_Question *Question,
/****************** Write integer answer when seeing a test ******************/
/*****************************************************************************/
-static void Tst_WriteIntAnsSeeing (const struct TsR_Result *Result,
+static void Tst_WriteIntAnsSeeing (const struct TstExa_Exam *Exam,
unsigned NumQst)
{
- char StrQstIndOrAns[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Qstxx...x", "Indxx...x" or "Ansxx...x"
+ char StrAns[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Ansxx...x"
/***** Write input field for the answer *****/
- snprintf (StrQstIndOrAns,sizeof (StrQstIndOrAns),
+ snprintf (StrAns,sizeof (StrAns),
"Ans%010u",
NumQst);
- HTM_INPUT_TEXT (StrQstIndOrAns,11,Result->Questions[NumQst].StrAnswers,false,
+ HTM_INPUT_TEXT (StrAns,11,Exam->Questions[NumQst].StrAnswers,false,
"size=\"11\"");
}
-/*****************************************************************************/
-/**************** Write integer answer when assessing a test *****************/
-/*****************************************************************************/
-
-static void Tst_WriteIntAnsResult (struct UsrData *UsrDat,
- const struct TsR_Result *Result,
- unsigned NumQst,
- const struct Tst_Question *Question,
- MYSQL_RES *mysql_res,
- unsigned Visibility)
- {
- MYSQL_ROW row;
- long IntAnswerUsr;
- long IntAnswerCorr;
- /*
- row[0] AnsInd
- row[1] Answer
- row[2] Feedback
- row[3] MedCod
- row[4] Correct
- */
- /***** Check if number of rows is correct *****/
- Tst_CheckIfNumberOfAnswersIsOne (Question);
-
- /***** Get the numerical value of the correct answer *****/
- row = mysql_fetch_row (mysql_res);
- if (sscanf (row[1],"%ld",&IntAnswerCorr) != 1)
- Lay_ShowErrorAndExit ("Wrong integer answer.");
-
- /***** Header with the title of each column *****/
- HTM_TABLE_BeginPadding (2);
- HTM_TR_Begin (NULL);
- Tst_WriteHeadUserCorrect (UsrDat);
- HTM_TR_End ();
-
- HTM_TR_Begin (NULL);
-
- /***** Write the user answer *****/
- if (Result->Questions[NumQst].StrAnswers[0]) // If user has answered the question
- {
- if (sscanf (Result->Questions[NumQst].StrAnswers,"%ld",&IntAnswerUsr) == 1)
- {
- HTM_TD_Begin ("class=\"%s CM\"",
- TsV_IsVisibleCorrectAns (Visibility) ?
- (IntAnswerUsr == IntAnswerCorr ? "ANS_OK" :
- "ANS_BAD") :
- "ANS_0");
- HTM_Long (IntAnswerUsr);
- HTM_TD_End ();
- }
- else
- {
- HTM_TD_Begin ("class=\"ANS_0 CM\"");
- HTM_Txt ("?");
- HTM_TD_End ();
- }
- }
- else // If user has omitted the answer
- HTM_TD_Empty (1);
-
- /***** Write the correct answer *****/
- HTM_TD_Begin ("class=\"ANS_0 CM\"");
- if (TsV_IsVisibleQstAndAnsTxt (Visibility) &&
- TsV_IsVisibleCorrectAns (Visibility))
- HTM_Long (IntAnswerCorr);
- else
- Ico_PutIconNotVisible ();
- HTM_TD_End ();
-
- HTM_TR_End ();
-
- /***** Write the score of this question *****/
- if (TsV_IsVisibleEachQstScore (Visibility))
- {
- Tst_WriteScoreStart (2);
- if (!Result->Questions[NumQst].StrAnswers[0]) // If user has omitted the answer
- {
- HTM_SPAN_Begin ("class=\"ANS_0\"");
- HTM_Double2Decimals (0.0);
- }
- else if (IntAnswerUsr == IntAnswerCorr) // If correct
- {
- HTM_SPAN_Begin ("class=\"ANS_OK\"");
- HTM_Double2Decimals (1.0);
- }
- else // If wrong
- {
- HTM_SPAN_Begin ("class=\"ANS_BAD\"");
- HTM_Double2Decimals (0.0);
- }
- HTM_SPAN_End ();
- Tst_WriteScoreEnd ();
- }
-
- HTM_TABLE_End ();
- }
-
/*****************************************************************************/
/****************** Write float answer when editing a test *******************/
/*****************************************************************************/
@@ -4387,130 +3430,19 @@ static void Tst_WriteFloatAnsEdit (const struct Tst_Question *Question,
/****************** Write float answer when seeing a test ********************/
/*****************************************************************************/
-static void Tst_WriteFloatAnsSeeing (const struct TsR_Result *Result,
+static void Tst_WriteFloatAnsSeeing (const struct TstExa_Exam *Exam,
unsigned NumQst)
{
- char StrQstIndOrAns[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Qstxx...x", "Indxx...x" or "Ansxx...x"
+ char StrAns[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Ansxx...x"
/***** Write input field for the answer *****/
- snprintf (StrQstIndOrAns,sizeof (StrQstIndOrAns),
+ snprintf (StrAns,sizeof (StrAns),
"Ans%010u",
NumQst);
- HTM_INPUT_TEXT (StrQstIndOrAns,Tst_MAX_BYTES_FLOAT_ANSWER,Result->Questions[NumQst].StrAnswers,false,
+ HTM_INPUT_TEXT (StrAns,Tst_MAX_BYTES_FLOAT_ANSWER,Exam->Questions[NumQst].StrAnswers,false,
"size=\"11\"");
}
-/*****************************************************************************/
-/***************** Write float answer when assessing a test ******************/
-/*****************************************************************************/
-
-static void Tst_WriteFloatAnsResult (struct UsrData *UsrDat,
- const struct TsR_Result *Result,
- unsigned NumQst,
- const struct Tst_Question *Question,
- MYSQL_RES *mysql_res,
- unsigned Visibility)
- {
- MYSQL_ROW row;
- unsigned i;
- double FloatAnsUsr = 0.0,Tmp;
- double FloatAnsCorr[2];
- /*
- row[0] AnsInd
- row[1] Answer
- row[2] Feedback
- row[3] MedCod
- row[4] Correct
- */
- /***** Check if number of rows is correct *****/
- if (Question->Answer.NumOptions != 2)
- Lay_ShowErrorAndExit ("Wrong float range.");
-
- /***** Get the numerical value of the minimum and maximum correct answers *****/
- for (i = 0;
- i < 2;
- i++)
- {
- row = mysql_fetch_row (mysql_res);
- FloatAnsCorr[i] = Str_GetDoubleFromStr (row[1]);
- }
- if (FloatAnsCorr[0] > FloatAnsCorr[1]) // The maximum and the minimum are swapped
- {
- /* Swap maximum and minimum */
- Tmp = FloatAnsCorr[0];
- FloatAnsCorr[0] = FloatAnsCorr[1];
- FloatAnsCorr[1] = Tmp;
- }
-
- /***** Header with the title of each column *****/
- HTM_TABLE_BeginPadding (2);
- HTM_TR_Begin (NULL);
- Tst_WriteHeadUserCorrect (UsrDat);
- HTM_TR_End ();
-
- HTM_TR_Begin (NULL);
-
- /***** Write the user answer *****/
- if (Result->Questions[NumQst].StrAnswers[0]) // If user has answered the question
- {
- FloatAnsUsr = Str_GetDoubleFromStr (Result->Questions[NumQst].StrAnswers);
- // A bad formatted floating point answer will interpreted as 0.0
- HTM_TD_Begin ("class=\"%s CM\"",
- TsV_IsVisibleCorrectAns (Visibility) ?
- ((FloatAnsUsr >= FloatAnsCorr[0] &&
- FloatAnsUsr <= FloatAnsCorr[1]) ? "ANS_OK" :
- "ANS_BAD") :
- "ANS_0");
- HTM_Double (FloatAnsUsr);
- }
- else // If user has omitted the answer
- HTM_TD_Begin (NULL);
- HTM_TD_End ();
-
- /***** Write the correct answer *****/
- HTM_TD_Begin ("class=\"ANS_0 CM\"");
- if (TsV_IsVisibleQstAndAnsTxt (Visibility) &&
- TsV_IsVisibleCorrectAns (Visibility))
- {
- HTM_Txt ("[");
- HTM_Double (FloatAnsCorr[0]);
- HTM_Txt ("; ");
- HTM_Double (FloatAnsCorr[1]);
- HTM_Txt ("]");
- }
- else
- Ico_PutIconNotVisible ();
- HTM_TD_End ();
-
- HTM_TR_End ();
-
- /***** Write the score of this question *****/
- if (TsV_IsVisibleEachQstScore (Visibility))
- {
- Tst_WriteScoreStart (2);
- if (!Result->Questions[NumQst].StrAnswers[0]) // If user has omitted the answer
- {
- HTM_SPAN_Begin ("class=\"ANS_0\"");
- HTM_Double2Decimals (0.0);
- }
- else if (FloatAnsUsr >= FloatAnsCorr[0] &&
- FloatAnsUsr <= FloatAnsCorr[1]) // If correct (inside the interval)
- {
- HTM_SPAN_Begin ("class=\"ANS_OK\"");
- HTM_Double2Decimals (1.0);
- }
- else // If wrong (outside the interval)
- {
- HTM_SPAN_Begin ("class=\"ANS_BAD\"");
- HTM_Double2Decimals (0.0);
- }
- HTM_SPAN_End ();
- Tst_WriteScoreEnd ();
- }
-
- HTM_TABLE_End ();
- }
-
/*****************************************************************************/
/*********** Write false / true answer when listing test questions ***********/
/*****************************************************************************/
@@ -4544,7 +3476,7 @@ static void Tst_WriteTFAnsListing (const struct Tst_Question *Question,
/************** Write false / true answer when seeing a test ****************/
/*****************************************************************************/
-static void Tst_WriteTFAnsSeeing (const struct TsR_Result *Result,
+static void Tst_WriteTFAnsSeeing (const struct TstExa_Exam *Exam,
unsigned NumQst)
{
extern const char *Txt_TF_QST[2];
@@ -4555,9 +3487,9 @@ static void Tst_WriteTFAnsSeeing (const struct TsR_Result *Result,
==> the exam may be half filled ==> the answers displayed will be those selected by the user. */
HTM_SELECT_Begin (false,
"name=\"Ans%010u\"",NumQst);
- HTM_OPTION (HTM_Type_STRING,"" ,true ,Result->Questions[NumQst].StrAnswers[0] == '\0'," ");
- HTM_OPTION (HTM_Type_STRING,"T",false,Result->Questions[NumQst].StrAnswers[0] == 'T' ,"%s",Txt_TF_QST[0]);
- HTM_OPTION (HTM_Type_STRING,"F",false,Result->Questions[NumQst].StrAnswers[0] == 'F' ,"%s",Txt_TF_QST[1]);
+ HTM_OPTION (HTM_Type_STRING,"" ,Exam->Questions[NumQst].StrAnswers[0] == '\0',false," ");
+ HTM_OPTION (HTM_Type_STRING,"T",Exam->Questions[NumQst].StrAnswers[0] == 'T' ,false,"%s",Txt_TF_QST[0]);
+ HTM_OPTION (HTM_Type_STRING,"F",Exam->Questions[NumQst].StrAnswers[0] == 'F' ,false,"%s",Txt_TF_QST[1]);
HTM_SELECT_End ();
}
@@ -4583,87 +3515,6 @@ void Tst_WriteAnsTF (char AnsTF)
}
}
-/*****************************************************************************/
-/************** Write false / true answer when assessing a test **************/
-/*****************************************************************************/
-
-static void Tst_WriteTFAnsResult (struct UsrData *UsrDat,
- const struct TsR_Result *Result,
- unsigned NumQst,
- const struct Tst_Question *Question,
- MYSQL_RES *mysql_res,
- unsigned Visibility)
- {
- MYSQL_ROW row;
- char AnsTF;
- /*
- row[0] AnsInd
- row[1] Answer
- row[2] Feedback
- row[3] MedCod
- row[4] Correct
- */
- /***** Check if number of rows is correct *****/
- Tst_CheckIfNumberOfAnswersIsOne (Question);
-
- /***** Get answer true or false *****/
- row = mysql_fetch_row (mysql_res);
- AnsTF = Result->Questions[NumQst].StrAnswers[0];
-
- /***** Header with the title of each column *****/
- HTM_TABLE_BeginPadding (2);
- HTM_TR_Begin (NULL);
- Tst_WriteHeadUserCorrect (UsrDat);
- HTM_TR_End ();
-
- HTM_TR_Begin (NULL);
-
- /***** Write the user answer *****/
- HTM_TD_Begin ("class=\"%s CM\"",
- TsV_IsVisibleCorrectAns (Visibility) ?
- (AnsTF == row[1][0] ? "ANS_OK" :
- "ANS_BAD") :
- "ANS_0");
- Tst_WriteAnsTF (AnsTF);
- HTM_TD_End ();
-
- /***** Write the correct answer *****/
- HTM_TD_Begin ("class=\"ANS_0 CM\"");
- if (TsV_IsVisibleQstAndAnsTxt (Visibility) &&
- TsV_IsVisibleCorrectAns (Visibility))
- Tst_WriteAnsTF (row[1][0]);
- else
- Ico_PutIconNotVisible ();
- HTM_TD_End ();
-
- HTM_TR_End ();
-
- /***** Write the score of this question *****/
- if (TsV_IsVisibleEachQstScore (Visibility))
- {
- Tst_WriteScoreStart (2);
- if (AnsTF == '\0') // If user has omitted the answer
- {
- HTM_SPAN_Begin ("class=\"ANS_0\"");
- HTM_Double2Decimals (0.0);
- }
- else if (AnsTF == row[1][0]) // If correct
- {
- HTM_SPAN_Begin ("class=\"ANS_OK\"");
- HTM_Double2Decimals (1.0);
- }
- else // If wrong
- {
- HTM_SPAN_Begin ("class=\"ANS_BAD\"");
- HTM_Double2Decimals (-1.0);
- }
- HTM_SPAN_End ();
- Tst_WriteScoreEnd ();
- }
-
- HTM_TABLE_End ();
- }
-
/*****************************************************************************/
/**** Write single or multiple choice answer when listing test questions *****/
/*****************************************************************************/
@@ -4767,7 +3618,7 @@ static void Tst_WriteChoiceAnsListing (struct Tst_Question *Question,
/******** Write single or multiple choice answer when seeing a test **********/
/*****************************************************************************/
-static void Tst_WriteChoiceAnsSeeing (const struct TsR_Result *Result,
+static void Tst_WriteChoiceAnsSeeing (const struct TstExa_Exam *Exam,
unsigned NumQst,
struct Tst_Question *Question,
MYSQL_RES *mysql_res)
@@ -4775,7 +3626,7 @@ static void Tst_WriteChoiceAnsSeeing (const struct TsR_Result *Result,
unsigned NumOpt;
unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]; // Indexes of all answers of this question
bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION];
- char StrQstIndOrAns[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Qstxx...x", "Indxx...x" or "Ansxx...x"
+ char StrAns[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Ansxx...x"
/*
row[0] AnsInd
row[1] Answer
@@ -4788,10 +3639,10 @@ static void Tst_WriteChoiceAnsSeeing (const struct TsR_Result *Result,
Tst_GetChoiceAns (Question,mysql_res);
/***** Get indexes for this question from string *****/
- Tst_GetIndexesFromStr (Result->Questions[NumQst].StrIndexes,Indexes);
+ TstExa_GetIndexesFromStr (Exam->Questions[NumQst].StrIndexes,Indexes);
/***** Get the user's answers for this question from string *****/
- Tst_GetAnswersFromStr (Result->Questions[NumQst].StrAnswers,UsrAnswers);
+ TstExa_GetAnswersFromStr (Exam->Questions[NumQst].StrAnswers,UsrAnswers);
/***** Begin table *****/
HTM_TABLE_BeginPadding (2);
@@ -4800,13 +3651,8 @@ static void Tst_WriteChoiceAnsSeeing (const struct TsR_Result *Result,
NumOpt < Question->Answer.NumOptions;
NumOpt++)
{
- /***** Allocate memory for text in this choice answer *****/
- if (!Tst_AllocateTextChoiceAnswer (Question,NumOpt))
- /* Abort on error */
- Ale_ShowAlertsAndExit ();
-
- /***** Indexes are 0,1,2,3... if no shuffle
- or 3,1,0,2... (example) if shuffle *****/
+ /***** Indexes are 0 1 2 3... if no shuffle
+ or 3 1 0 2... (example) if shuffle *****/
HTM_TR_Begin (NULL);
/***** Write selectors and letter of this option *****/
@@ -4815,16 +3661,11 @@ static void Tst_WriteChoiceAnsSeeing (const struct TsR_Result *Result,
==> the exam may be half filled ==> the answers displayed will be those selected by the user. */
HTM_TD_Begin ("class=\"LT\"");
- snprintf (StrQstIndOrAns,sizeof (StrQstIndOrAns), // TODO: Remove indexes from this form because they will be got from tst_exam_questions in database
- "Ind%010u",
- NumQst);
- Par_PutHiddenParamUnsigned (NULL,StrQstIndOrAns,Indexes[NumOpt]);
-
- snprintf (StrQstIndOrAns,sizeof (StrQstIndOrAns),
+ snprintf (StrAns,sizeof (StrAns),
"Ans%010u",
NumQst);
if (Question->Answer.Type == Tst_ANS_UNIQUE_CHOICE)
- HTM_INPUT_RADIO (StrQstIndOrAns,false,
+ HTM_INPUT_RADIO (StrAns,false,
"id=\"Ans%010u_%u\" value=\"%u\"%s"
" onclick=\"selectUnselectRadio(this,this.form.Ans%010u,%u);\"",
NumQst,NumOpt,
@@ -4833,7 +3674,7 @@ static void Tst_WriteChoiceAnsSeeing (const struct TsR_Result *Result,
"",
NumQst,Question->Answer.NumOptions);
else // Answer.Type == Tst_ANS_MULTIPLE_CHOICE
- HTM_INPUT_CHECKBOX (StrQstIndOrAns,HTM_DONT_SUBMIT_ON_CHANGE,
+ HTM_INPUT_CHECKBOX (StrAns,HTM_DONT_SUBMIT_ON_CHANGE,
"id=\"Ans%010u_%u\" value=\"%u\"%s",
NumQst,NumOpt,
Indexes[NumOpt],
@@ -4865,160 +3706,11 @@ static void Tst_WriteChoiceAnsSeeing (const struct TsR_Result *Result,
HTM_TABLE_End ();
}
-/*****************************************************************************/
-/******* Write single or multiple choice answer when assessing a test ********/
-/*****************************************************************************/
-
-static void Tst_WriteChoiceAnsResult (struct UsrData *UsrDat,
- const struct TsR_Result *Result,
- unsigned NumQst,
- struct Tst_Question *Question,
- MYSQL_RES *mysql_res,
- unsigned Visibility)
- {
- extern const char *Txt_TST_Answer_given_by_the_user;
- extern const char *Txt_TST_Answer_given_by_the_teachers;
- unsigned NumOpt;
- unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]; // Indexes of all answers of this question
- bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION];
- struct
- {
- char *Class;
- char *Str;
- } Ans;
-
- /***** Get text and correctness of answers for this question
- from database (one row per answer) *****/
- Tst_GetChoiceAns (Question,mysql_res);
-
- /***** Get indexes for this question from string *****/
- Tst_GetIndexesFromStr (Result->Questions[NumQst].StrIndexes,Indexes);
-
- /***** Get the user's answers for this question from string *****/
- Tst_GetAnswersFromStr (Result->Questions[NumQst].StrAnswers,UsrAnswers);
-
- /***** Begin table *****/
- HTM_TABLE_BeginPadding (2);
- HTM_TR_Begin (NULL);
- Tst_WriteHeadUserCorrect (UsrDat);
- HTM_TD_Empty (2);
- HTM_TR_End ();
-
- /***** Write answers (one row per answer) *****/
- for (NumOpt = 0;
- NumOpt < Question->Answer.NumOptions;
- NumOpt++)
- {
- HTM_TR_Begin (NULL);
-
- /* Draw icon depending on user's answer */
- if (UsrAnswers[Indexes[NumOpt]] == true) // This answer has been selected by the user
- {
- if (TsV_IsVisibleCorrectAns (Visibility))
- {
- if (Question->Answer.Options[Indexes[NumOpt]].Correct)
- {
- Ans.Class = "ANS_OK";
- Ans.Str = "✓";
- }
- else
- {
- Ans.Class = "ANS_BAD";
- Ans.Str = "✗";
- }
- }
- else
- {
- Ans.Class = "ANS_0";
- Ans.Str = "•";
- }
-
- HTM_TD_Begin ("class=\"%s CT\" title=\"%s\"",
- Ans.Class,Txt_TST_Answer_given_by_the_user);
- HTM_Txt (Ans.Str);
- HTM_TD_End ();
- }
- else // This answer has NOT been selected by the user
- HTM_TD_Empty (1);
-
- /* Draw icon that indicates whether the answer is correct */
- if (TsV_IsVisibleCorrectAns (Visibility))
- {
- if (Question->Answer.Options[Indexes[NumOpt]].Correct)
- {
- HTM_TD_Begin ("class=\"ANS_0 CT\" title=\"%s\"",
- Txt_TST_Answer_given_by_the_teachers);
- HTM_Txt ("•");
- HTM_TD_End ();
- }
- else
- HTM_TD_Empty (1);
- }
- else
- {
- HTM_TD_Begin ("class=\"ANS_0 CT\"");
- Ico_PutIconNotVisible ();
- HTM_TD_End ();
- }
-
- /* Answer letter (a, b, c,...) */
- HTM_TD_Begin ("class=\"ANS_TXT LT\"");
- HTM_TxtF ("%c) ",'a' + (char) NumOpt);
- HTM_TD_End ();
-
- /* Answer text and feedback */
- HTM_TD_Begin ("class=\"LT\"");
-
- HTM_DIV_Begin ("class=\"ANS_TXT\"");
- if (TsV_IsVisibleQstAndAnsTxt (Visibility))
- {
- HTM_Txt (Question->Answer.Options[Indexes[NumOpt]].Text);
- Med_ShowMedia (&Question->Answer.Options[Indexes[NumOpt]].Media,
- "TEST_MED_SHOW_CONT",
- "TEST_MED_SHOW");
- }
- else
- Ico_PutIconNotVisible ();
- HTM_DIV_End ();
-
- if (TsV_IsVisibleCorrectAns (Visibility))
- if (Question->Answer.Options[Indexes[NumOpt]].Feedback)
- if (Question->Answer.Options[Indexes[NumOpt]].Feedback[0])
- {
- HTM_DIV_Begin ("class=\"TEST_EXA_LIGHT\"");
- HTM_Txt (Question->Answer.Options[Indexes[NumOpt]].Feedback);
- HTM_DIV_End ();
- }
-
- HTM_TD_End ();
-
- HTM_TR_End ();
- }
-
- /***** Write the score of this question *****/
- if (TsV_IsVisibleEachQstScore (Visibility))
- {
- Tst_WriteScoreStart (4);
- if (Result->Questions[NumQst].Score == 0.0)
- HTM_SPAN_Begin ("class=\"ANS_0\"");
- else if (Result->Questions[NumQst].Score > 0.0)
- HTM_SPAN_Begin ("class=\"ANS_OK\"");
- else
- HTM_SPAN_Begin ("class=\"ANS_BAD\"");
- HTM_Double2Decimals (Result->Questions[NumQst].Score);
- HTM_SPAN_End ();
- Tst_WriteScoreEnd ();
- }
-
- /***** End table *****/
- HTM_TABLE_End ();
- }
-
/*****************************************************************************/
/************************ Get choice answer from row *************************/
/*****************************************************************************/
-static void Tst_GetChoiceAns (struct Tst_Question *Question,MYSQL_RES *mysql_res)
+void Tst_GetChoiceAns (struct Tst_Question *Question,MYSQL_RES *mysql_res)
{
unsigned NumOpt;
MYSQL_ROW row;
@@ -5054,7 +3746,7 @@ static void Tst_GetChoiceAns (struct Tst_Question *Question,MYSQL_RES *mysql_res
/***** Copy answer feedback (row[2]) and convert it,
that is in HTML, to rigorous HTML ******/
- if (TsV_IsVisibleFeedbackTxt (TstCfg_GetConfigVisibility ()))
+ if (TstVis_IsVisibleFeedbackTxt (TstCfg_GetConfigVisibility ()))
if (row[2])
if (row[2][0])
{
@@ -5078,252 +3770,31 @@ static void Tst_GetChoiceAns (struct Tst_Question *Question,MYSQL_RES *mysql_res
/******************** Write text answer when seeing a test *******************/
/*****************************************************************************/
-static void Tst_WriteTextAnsSeeing (const struct TsR_Result *Result,
+static void Tst_WriteTextAnsSeeing (const struct TstExa_Exam *Exam,
unsigned NumQst)
{
- char StrQstIndOrAns[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Qstxx...x", "Indxx...x" or "Ansxx...x"
+ char StrAns[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Ansxx...x"
/***** Write input field for the answer *****/
- snprintf (StrQstIndOrAns,sizeof (StrQstIndOrAns),
+ snprintf (StrAns,sizeof (StrAns),
"Ans%010u",
NumQst);
- HTM_INPUT_TEXT (StrQstIndOrAns,Tst_MAX_BYTES_ANSWERS_ONE_QST,Result->Questions[NumQst].StrAnswers,false,
+ HTM_INPUT_TEXT (StrAns,TstExa_MAX_BYTES_ANSWERS_ONE_QST,Exam->Questions[NumQst].StrAnswers,false,
"size=\"40\"");
}
-/*****************************************************************************/
-/***************** Write text answer when assessing a test *******************/
-/*****************************************************************************/
-
-static void Tst_WriteTextAnsResult (struct UsrData *UsrDat,
- const struct TsR_Result *Result,
- unsigned NumQst,
- struct Tst_Question *Question,
- MYSQL_RES *mysql_res,
- unsigned Visibility)
- {
- unsigned NumOpt;
- MYSQL_ROW row;
- char TextAnsUsr[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
- char TextAnsOK[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
- bool Correct = false;
- /*
- row[0] AnsInd
- row[1] Answer
- row[2] Feedback
- row[3] MedCod
- row[4] Correct
- */
- /***** Get text and correctness of answers for this question from database (one row per answer) *****/
- for (NumOpt = 0;
- NumOpt < Question->Answer.NumOptions;
- NumOpt++)
- {
- /***** Get next answer *****/
- row = mysql_fetch_row (mysql_res);
-
- /***** Allocate memory for text in this choice answer *****/
- if (!Tst_AllocateTextChoiceAnswer (Question,NumOpt))
- /* Abort on error */
- Ale_ShowAlertsAndExit ();
-
- /***** Copy answer text (row[1]) and convert it, that is in HTML, to rigorous HTML ******/
- Str_Copy (Question->Answer.Options[NumOpt].Text,row[1],
- Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
- Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
- Question->Answer.Options[NumOpt].Text,
- Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false);
-
- /***** Copy answer feedback (row[2]) and convert it, that is in HTML, to rigorous HTML ******/
- if (TsV_IsVisibleFeedbackTxt (Visibility))
- if (row[2])
- if (row[2][0])
- {
- Str_Copy (Question->Answer.Options[NumOpt].Feedback,row[2],
- Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
- Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
- Question->Answer.Options[NumOpt].Feedback,
- Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false);
- }
-
- /***** Assign correctness (row[4]) of this answer (this option) *****/
- Question->Answer.Options[NumOpt].Correct = (row[4][0] == 'Y');
- }
-
- /***** Header with the title of each column *****/
- HTM_TABLE_BeginPadding (2);
- HTM_TR_Begin (NULL);
- Tst_WriteHeadUserCorrect (UsrDat);
- HTM_TR_End ();
-
- HTM_TR_Begin (NULL);
-
- /***** Write the user answer *****/
- if (Result->Questions[NumQst].StrAnswers[0]) // If user has answered the question
- {
- /* Filter the user answer */
- Str_Copy (TextAnsUsr,Result->Questions[NumQst].StrAnswers,
- Tst_MAX_BYTES_ANSWERS_ONE_QST);
-
- /* In order to compare student answer to stored answer,
- the text answers are stored avoiding two or more consecurive spaces */
- Str_ReplaceSeveralSpacesForOne (TextAnsUsr);
-
- Str_ConvertToComparable (TextAnsUsr);
-
- for (NumOpt = 0;
- NumOpt < Question->Answer.NumOptions;
- NumOpt++)
- {
- /* Filter this correct answer */
- Str_Copy (TextAnsOK,Question->Answer.Options[NumOpt].Text,
- Tst_MAX_BYTES_ANSWERS_ONE_QST);
- Str_ConvertToComparable (TextAnsOK);
-
- /* Check is user answer is correct */
- if (!strcoll (TextAnsUsr,TextAnsOK))
- {
- Correct = true;
- break;
- }
- }
- HTM_TD_Begin ("class=\"%s CT\"",
- TsV_IsVisibleCorrectAns (Visibility) ?
- (Correct ? "ANS_OK" :
- "ANS_BAD") :
- "ANS_0");
- HTM_Txt (Result->Questions[NumQst].StrAnswers);
- }
- else // If user has omitted the answer
- HTM_TD_Begin (NULL);
- HTM_TD_End ();
-
- /***** Write the correct answers *****/
- if (TsV_IsVisibleQstAndAnsTxt (Visibility) &&
- TsV_IsVisibleCorrectAns (Visibility))
- {
- HTM_TD_Begin ("class=\"CT\"");
- HTM_TABLE_BeginPadding (2);
-
- for (NumOpt = 0;
- NumOpt < Question->Answer.NumOptions;
- NumOpt++)
- {
- HTM_TR_Begin (NULL);
-
- /* Answer letter (a, b, c,...) */
- HTM_TD_Begin ("class=\"ANS_0 LT\"");
- HTM_TxtF ("%c) ",'a' + (char) NumOpt);
- HTM_TD_End ();
-
- /* Answer text and feedback */
- HTM_TD_Begin ("class=\"LT\"");
-
- HTM_DIV_Begin ("class=\"ANS_0\"");
- HTM_Txt (Question->Answer.Options[NumOpt].Text);
- HTM_DIV_End ();
-
- if (TsV_IsVisibleFeedbackTxt (Visibility))
- if (Question->Answer.Options[NumOpt].Feedback)
- if (Question->Answer.Options[NumOpt].Feedback[0])
- {
- HTM_DIV_Begin ("class=\"TEST_EXA_LIGHT\"");
- HTM_Txt (Question->Answer.Options[NumOpt].Feedback);
- HTM_DIV_End ();
- }
-
- HTM_TD_End ();
-
- HTM_TR_End ();
- }
-
- HTM_TABLE_End ();
- HTM_TD_End ();
- }
- else
- {
- HTM_TD_Begin ("class=\"ANS_0 CT\"");
- Ico_PutIconNotVisible ();
- HTM_TD_End ();
- }
- HTM_TR_End ();
-
- /***** Write the score of this question *****/
- if (TsV_IsVisibleEachQstScore (Visibility))
- {
- Tst_WriteScoreStart (4);
- if (!Result->Questions[NumQst].StrAnswers[0]) // If user has omitted the answer
- {
- HTM_SPAN_Begin ("class=\"ANS_0\"");
- HTM_Double2Decimals (0.0);
- }
- else if (Correct) // If correct
- {
- HTM_SPAN_Begin ("class=\"ANS_OK\"");
- HTM_Double2Decimals (1.0);
- }
- else // If wrong
- {
- HTM_SPAN_Begin ("class=\"ANS_BAD\"");
- HTM_Double2Decimals (0.0);
- }
- HTM_SPAN_End ();
- Tst_WriteScoreEnd ();
- }
-
- HTM_TABLE_End ();
- }
-
-/*****************************************************************************/
-/********* Write head with two columns: ********/
-/********* one for the user's answer and other for the correct answer ********/
-/*****************************************************************************/
-
-static void Tst_WriteHeadUserCorrect (struct UsrData *UsrDat)
- {
- extern const char *Txt_User[Usr_NUM_SEXS];
- extern const char *Txt_ROLES_PLURAL_Abc[Rol_NUM_ROLES][Usr_NUM_SEXS];
-
- HTM_TD_Begin ("class=\"DAT_SMALL CM\"");
- HTM_Txt (Txt_User[UsrDat->Sex]);
- HTM_TD_End ();
-
- HTM_TD_Begin ("class=\"DAT_SMALL CM\"");
- HTM_Txt (Txt_ROLES_PLURAL_Abc[Rol_TCH][Usr_SEX_UNKNOWN]);
- HTM_TD_End ();
- }
-
-/*****************************************************************************/
-/*********** Write the start ans the end of the score of an answer ***********/
-/*****************************************************************************/
-
-static void Tst_WriteScoreStart (unsigned ColSpan)
- {
- extern const char *Txt_Score;
-
- HTM_TR_Begin (NULL);
- HTM_TD_Begin ("colspan=\"%u\" class=\"DAT_SMALL LM\"",ColSpan);
- HTM_TxtColonNBSP (Txt_Score);
- }
-
-static void Tst_WriteScoreEnd (void)
- {
- HTM_TD_End ();
- HTM_TR_End ();
- }
-
/*****************************************************************************/
/*************** Write parameter with the code of a question *****************/
/*****************************************************************************/
static void Tst_WriteParamQstCod (unsigned NumQst,long QstCod)
{
- char StrQstIndOrAns[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Qstxx...x", "Indxx...x" or "Ansxx...x"
+ char StrAns[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Ansxx...x"
- snprintf (StrQstIndOrAns,sizeof (StrQstIndOrAns),
+ snprintf (StrAns,sizeof (StrAns),
"Qst%010u",
NumQst);
- Par_PutHiddenParamLong (NULL,StrQstIndOrAns,QstCod);
+ Par_PutHiddenParamLong (NULL,StrAns,QstCod);
}
/*****************************************************************************/
@@ -5344,7 +3815,8 @@ unsigned long Tst_GetTagsQst (long QstCod,MYSQL_RES **mysql_res)
{
/***** Get the tags of a question from database *****/
return DB_QuerySELECT (mysql_res,"can not get the tags of a question",
- "SELECT tst_tags.TagTxt FROM tst_question_tags,tst_tags"
+ "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"
@@ -5364,7 +3836,7 @@ void Tst_GetAndWriteTagsQst (long QstCod)
MYSQL_RES *mysql_res;
MYSQL_ROW row;
- if ((NumRows = Tst_GetTagsQst (QstCod,&mysql_res))) // Result: TagTxt
+ if ((NumRows = Tst_GetTagsQst (QstCod,&mysql_res)))
{
/***** Write the tags *****/
HTM_UL_Begin ("class=\"TEST_TAG_LIST DAT_SMALL\"");
@@ -5434,7 +3906,7 @@ static bool Tst_GetParamsTst (struct Tst_Test *Test,
/* Check number of types of answer */
if (Tst_CountNumAnswerTypesInList (&Test->AnswerTypes) == 0) // If no types of answer selected...
- { // ...write warning alert
+ { // ...write warning alert
Ale_ShowAlert (Ale_WARNING,Txt_You_must_select_one_ore_more_types_of_answer);
Error = true;
}
@@ -5491,10 +3963,10 @@ static bool Tst_GetParamsTst (struct Tst_Test *Test,
}
/*****************************************************************************/
-/******************** Get parameter with the number of test ******************/
+/******** Get parameter with the number of test exam generated by me *********/
/*****************************************************************************/
-static unsigned Tst_GetAndCheckParamNumTst (void)
+static unsigned Tst_GetParamNumTst (void)
{
return (unsigned) Par_GetParToUnsignedLong ("NumTst",
1,
@@ -6103,7 +4575,7 @@ bool Tst_AllocateTextChoiceAnswer (struct Tst_Question *Question,unsigned NumOpt
{
// Tst_FreeTagsList (&Question->Tags); // TODO: Necessary?
- Tst_FreeTextChoiceAnswer (Question,NumOpt);
+ // Tst_FreeTextChoiceAnswer (Question,NumOpt);// TODO: Necessary?
if ((Question->Answer.Options[NumOpt].Text =
(char *) malloc (Tst_MAX_BYTES_ANSWER_OR_FEEDBACK + 1)) == NULL)
@@ -6194,7 +4666,7 @@ static void Tst_FreeMediaOfQuestion (struct Tst_Question *Question)
/*************** Get answer type of a question from database *****************/
/*****************************************************************************/
-static Tst_AnswerType_t Tst_GetQstAnswerType (long QstCod)
+Tst_AnswerType_t Tst_GetQstAnswerType (long QstCod)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
@@ -6509,7 +4981,7 @@ static void Tst_GetQstFromForm (struct Tst_Question *Question,
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];
- char StrMultiAns[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
+ char StrMultiAns[TstExa_MAX_BYTES_ANSWERS_ONE_QST + 1];
char TF[1 + 1]; // (T)rue or (F)alse
const char *Ptr;
unsigned NumCorrectAns;
@@ -6661,7 +5133,7 @@ static void Tst_GetQstFromForm (struct Tst_Question *Question,
}
else if (Question->Answer.Type == Tst_ANS_MULTIPLE_CHOICE)
{
- Par_GetParMultiToText ("AnsMulti",StrMultiAns,Tst_MAX_BYTES_ANSWERS_ONE_QST);
+ Par_GetParMultiToText ("AnsMulti",StrMultiAns,TstExa_MAX_BYTES_ANSWERS_ONE_QST);
Ptr = StrMultiAns;
while (*Ptr)
{
diff --git a/swad_test.h b/swad_test.h
index 96ea07ed..14e9f230 100644
--- a/swad_test.h
+++ b/swad_test.h
@@ -30,7 +30,7 @@
#include "swad_game.h"
#include "swad_media.h"
#include "swad_test_config.h"
-#include "swad_test_result.h"
+// #include "swad_test_exam.h"
/*****************************************************************************/
/***************************** Public constants ******************************/
@@ -46,6 +46,8 @@
#define Tst_MAX_BYTES_ANSWER_TYPE 32
+#define Tst_MAX_OPTIONS_PER_QUESTION 10
+
/*****************************************************************************/
/******************************* Public types ********************************/
/*****************************************************************************/
@@ -155,23 +157,8 @@ void Tst_ShowNewTest (void);
void Tst_RequestAssessTest (void);
void Tst_AssessTest (void);
-void Tst_ComputeChoiceAnsScore (struct TsR_Result *Result,
- unsigned NumQst,
- struct Tst_Question *Question);
-void Tst_GetIndexesFromStr (const char StrIndexesOneQst[Tst_MAX_BYTES_INDEXES_ONE_QST + 1], // 0 1 2 3, 3 0 2 1, etc.
- unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]);
-
-void Tst_ComputeAndShowGrade (unsigned NumQsts,double Score,double MaxGrade);
-double Tst_ComputeGrade (unsigned NumQsts,double Score,double MaxGrade);
-void Tst_ShowGrade (double Grade,double MaxGrade);
-
void Tst_ShowTagList (unsigned NumTags,MYSQL_RES *mysql_res);
-void Tst_WriteQstAndAnsTestResult (struct UsrData *UsrDat,
- struct TsR_Result *Result,
- unsigned NumQst,
- MYSQL_ROW row,
- unsigned Visibility);
void Tst_WriteNumQst (unsigned NumQst);
void Tst_WriteAnswerType (Tst_AnswerType_t AnswerType);
void Tst_WriteQstStem (const char *Stem,const char *ClassStem,bool Visible);
@@ -191,6 +178,7 @@ void Tst_GetAnswersQst (struct Tst_Question *Question,MYSQL_RES **mysql_res,
void Tst_WriteAnswersListing (struct Tst_Question *Question);
bool Tst_CheckIfQuestionIsValidForGame (long QstCod);
void Tst_WriteAnsTF (char AnsTF);
+void Tst_GetChoiceAns (struct Tst_Question *Question,MYSQL_RES *mysql_res);
void Tst_CheckIfNumberOfAnswersIsOne (const struct Tst_Question *Question);
unsigned long Tst_GetTagsQst (long QstCod,MYSQL_RES **mysql_res);
@@ -210,6 +198,7 @@ void Tst_QstDestructor (struct Tst_Question *Question);
bool Tst_AllocateTextChoiceAnswer (struct Tst_Question *Question,unsigned NumOpt);
+Tst_AnswerType_t Tst_GetQstAnswerType (long QstCod);
Tst_AnswerType_t Tst_ConvertFromStrAnsTypDBToAnsTyp (const char *StrAnsTypeBD);
void Tst_ReceiveQst (void);
bool Tst_CheckIfQstFormatIsCorrectAndCountNumOptions (struct Tst_Question *Question);
diff --git a/swad_test_config.c b/swad_test_config.c
index fca6d747..f480656d 100644
--- a/swad_test_config.c
+++ b/swad_test_config.c
@@ -103,7 +103,7 @@ void TstCfg_GetConfigFromDB (void)
Gbl.Hierarchy.Crs.CrsCod);
TstCfg_SetConfigMinTimeNxtTstPerQst (0UL);
- TstCfg_SetConfigVisibility (TsV_VISIBILITY_DEFAULT);
+ TstCfg_SetConfigVisibility (TstVis_VISIBILITY_DEFAULT);
if (NumRows == 0)
{
TstCfg_SetConfigPluggable (TstCfg_PLUGGABLE_UNKNOWN);
@@ -173,7 +173,7 @@ void TstCfg_GetConfigFromRow (MYSQL_ROW row)
TstCfg_SetConfigMinTimeNxtTstPerQst (0UL);
/***** Get visibility (row[5]) *****/
- TstCfg_SetConfigVisibility (TsV_GetVisibilityFromStr (row[5]));
+ TstCfg_SetConfigVisibility (TstVis_GetVisibilityFromStr (row[5]));
}
/*****************************************************************************/
@@ -219,7 +219,7 @@ void TstCfg_ReceiveConfigTst (void)
0));
/***** Get visibility from form *****/
- TstCfg_SetConfigVisibility (TsV_GetVisibilityFromForm ());
+ TstCfg_SetConfigVisibility (TstVis_GetVisibilityFromForm ());
/***** Update database *****/
DB_QueryREPLACE ("can not save configuration of tests",
diff --git a/swad_test_exam.c b/swad_test_exam.c
new file mode 100644
index 00000000..fd04be0b
--- /dev/null
+++ b/swad_test_exam.c
@@ -0,0 +1,2676 @@
+// swad_test_exam.c: test exams made by users
+
+/*
+ SWAD (Shared Workspace At a Distance),
+ is a web platform developed at the University of Granada (Spain),
+ and used to support university teaching.
+
+ This file is part of SWAD core.
+ Copyright (C) 1999-2020 Antonio Cañas Vargas
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+/*****************************************************************************/
+/*********************************** Headers *********************************/
+/*****************************************************************************/
+
+#define _GNU_SOURCE // For asprintf
+#include // For boolean type
+#include // For NULL
+#include // For asprintf
+#include // For free
+#include // For string functions
+
+#include "swad_action.h"
+#include "swad_database.h"
+#include "swad_form.h"
+#include "swad_global.h"
+#include "swad_HTML.h"
+#include "swad_ID.h"
+#include "swad_test.h"
+#include "swad_test_exam.h"
+#include "swad_test_visibility.h"
+#include "swad_user.h"
+
+/*****************************************************************************/
+/***************************** Public constants ******************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/**************************** Private constants ******************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/******************************* Private types *******************************/
+/*****************************************************************************/
+
+#define Tst_NUM_STATUS 2
+typedef enum
+ {
+ Tst_STATUS_SHOWN_BUT_NOT_ASSESSED = 0,
+ Tst_STATUS_ASSESSED = 1,
+ Tst_STATUS_ERROR = 2,
+ } Tst_Status_t;
+
+/*****************************************************************************/
+/************** External global variables from others modules ****************/
+/*****************************************************************************/
+
+extern struct Globals Gbl;
+
+/*****************************************************************************/
+/************************* Private global variables **************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/***************************** Private prototypes ****************************/
+/*****************************************************************************/
+
+static void TstExa_ComputeAnswerScore (struct TstExa_Exam *Exam,
+ unsigned NumQst,
+ struct Tst_Question *Question);
+static void TstExa_ComputeIntAnsScore (struct TstExa_Exam *Exam,
+ unsigned NumQst,
+ struct Tst_Question *Question);
+static void TstExa_GetCorrectIntAnswerFromDB (struct Tst_Question *Question);
+static void TstExa_ComputeFloatAnsScore (struct TstExa_Exam *Exam,
+ unsigned NumQst,
+ struct Tst_Question *Question);
+static void TstExa_GetCorrectFloatAnswerFromDB (struct Tst_Question *Question);
+static void TstExa_ComputeTFAnsScore (struct TstExa_Exam *Exam,
+ unsigned NumQst,
+ struct Tst_Question *Question);
+static void TstExa_GetCorrectTFAnswerFromDB (struct Tst_Question *Question);
+static void TstExa_GetCorrectChoiceAnswerFromDB (struct Tst_Question *Question);
+
+static void TstExa_ComputeScoreQst (struct TstExa_Exam *Exam,
+ unsigned NumQst,
+ const struct Tst_Question *Question,
+ unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION], // Indexes of all answers of this question
+ bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION]);
+static void TstExa_ComputeTextAnsScore (struct TstExa_Exam *Exam,
+ unsigned NumQst,
+ struct Tst_Question *Question);
+static void TstExa_GetCorrectTextAnswerFromDB (struct Tst_Question *Question);
+
+static void TstExa_WriteAnswersExam (struct UsrData *UsrDat,
+ const struct TstExa_Exam *Exam,
+ unsigned NumQst,
+ struct Tst_Question *Question,
+ unsigned Visibility);
+static void TstExa_WriteIntAnsExam (struct UsrData *UsrDat,
+ const struct TstExa_Exam *Exam,
+ unsigned NumQst,
+ const struct Tst_Question *Question,
+ MYSQL_RES *mysql_res,
+ unsigned Visibility);
+static void TstExa_WriteFloatAnsExam (struct UsrData *UsrDat,
+ const struct TstExa_Exam *Exam,
+ unsigned NumQst,
+ const struct Tst_Question *Question,
+ MYSQL_RES *mysql_res,
+ unsigned Visibility);
+static void TstExa_WriteTFAnsExam (struct UsrData *UsrDat,
+ const struct TstExa_Exam *Exam,
+ unsigned NumQst,
+ const struct Tst_Question *Question,
+ MYSQL_RES *mysql_res,
+ unsigned Visibility);
+static void TstExa_WriteChoiceAnsExam (struct UsrData *UsrDat,
+ const struct TstExa_Exam *Exam,
+ unsigned NumQst,
+ struct Tst_Question *Question,
+ MYSQL_RES *mysql_res,
+ unsigned Visibility);
+static void TstExa_WriteTextAnsExam (struct UsrData *UsrDat,
+ const struct TstExa_Exam *Exam,
+ unsigned NumQst,
+ struct Tst_Question *Question,
+ MYSQL_RES *mysql_res,
+ unsigned Visibility);
+static void TstExa_WriteHeadUserCorrect (struct UsrData *UsrDat);
+static void TstExa_WriteScoreStart (unsigned ColSpan);
+static void TstExa_WriteScoreEnd (void);
+
+static void TstExa_StoreOneExamQstInDB (const struct TstExa_Exam *Exam,
+ unsigned NumQst);
+static void Tst_UpdateQstScoreInDB (const struct TstExa_Exam *Exam,unsigned NumQst);
+
+static void TstExa_ShowUsrsExams (void);
+static void TstExa_ShowHeaderExams (void);
+static void TstExa_ShowExams (struct UsrData *UsrDat);
+static void TstExa_ShowExamsSummaryRow (bool ItsMe,
+ unsigned NumExams,
+ unsigned NumTotalQsts,
+ unsigned NumTotalQstsNotBlank,
+ double TotalScoreOfAllTests);
+static void TstExa_ShowTagsPresentInAnExam (long ExaCod);
+
+/*****************************************************************************/
+/***************** Create new blank test exam in database ********************/
+/*****************************************************************************/
+
+void TstExa_CreateExamInDB (struct TstExa_Exam *Exam)
+ {
+ /***** Insert new test exam into table *****/
+ Exam->ExaCod =
+ DB_QueryINSERTandReturnCode ("can not create new test exam",
+ "INSERT INTO tst_exams"
+ " (CrsCod,UsrCod,StartTime,EndTime,NumQsts,AllowTeachers)"
+ " VALUES"
+ " (%ld,%ld,NOW(),NOW(),%u,'%c')",
+ Gbl.Hierarchy.Crs.CrsCod,
+ Gbl.Usrs.Me.UsrDat.UsrCod,
+ Exam->NumQsts,
+ Exam->AllowTeachers ? 'Y' :
+ 'N');
+ }
+
+/*****************************************************************************/
+/********************** Update test exam in database *************************/
+/*****************************************************************************/
+
+void TstExa_UpdateExamInDB (const struct TstExa_Exam *Exam)
+ {
+ /***** Update score in test exam *****/
+ Str_SetDecimalPointToUS (); // To print the floating point as a dot
+ DB_QueryUPDATE ("can not update test exam",
+ "UPDATE tst_exams"
+ " SET EndTime=NOW(),"
+ "NumQstsNotBlank=%u,"
+ "Score='%.15lg'"
+ " WHERE ExaCod=%ld"
+ " AND CrsCod=%ld AND UsrCod=%ld", // Extra checks
+ Exam->NumQstsNotBlank,
+ Exam->Score,
+ Exam->ExaCod,
+ Gbl.Hierarchy.Crs.CrsCod,
+ Gbl.Usrs.Me.UsrDat.UsrCod);
+ Str_SetDecimalPointToLocal (); // Return to local system
+ }
+
+/*****************************************************************************/
+/********************* Show test exam after assessing it *********************/
+/*****************************************************************************/
+
+void TstExa_ShowExamAfterAssess (struct TstExa_Exam *Exam)
+ {
+ extern const char *Txt_Question_removed;
+ MYSQL_RES *mysql_res;
+ MYSQL_ROW row;
+ unsigned NumQst;
+
+ /***** Begin table *****/
+ HTM_TABLE_BeginWideMarginPadding (10);
+
+ /***** Initialize score and number of questions not blank *****/
+ Exam->NumQstsNotBlank = 0;
+ Exam->Score = 0.0;
+
+ for (NumQst = 0;
+ NumQst < Exam->NumQsts;
+ NumQst++)
+ {
+ Gbl.RowEvenOdd = NumQst % 2;
+
+ /***** Query database *****/
+ if (Tst_GetOneQuestionByCod (Exam->Questions[NumQst].QstCod,&mysql_res)) // Question exists
+ {
+ /***** Write question and answers *****/
+ row = mysql_fetch_row (mysql_res);
+ TstExa_WriteQstAndAnsExam (&Gbl.Usrs.Me.UsrDat,
+ Exam,
+ NumQst,
+ row,
+ TstCfg_GetConfigVisibility ());
+
+ /***** Store test exam question in database *****/
+ TstExa_StoreOneExamQstInDB (Exam,
+ NumQst); // 0, 1, 2, 3...
+
+ /***** Compute total score *****/
+ Exam->Score += Exam->Questions[NumQst].Score;
+ if (Exam->Questions[NumQst].AnswerIsNotBlank)
+ Exam->NumQstsNotBlank++;
+
+ /***** Update the number of accesses and the score of this question *****/
+ if (Gbl.Usrs.Me.Role.Logged == Rol_STD)
+ Tst_UpdateQstScoreInDB (Exam,NumQst);
+ }
+ else
+ {
+ /***** Question does not exists *****/
+ HTM_TR_Begin (NULL);
+
+ HTM_TD_Begin ("class=\"RT COLOR%u\"",Gbl.RowEvenOdd);
+ Tst_WriteNumQst (NumQst + 1);
+ HTM_TD_End ();
+
+ HTM_TD_Begin ("class=\"DAT_LIGHT LT COLOR%u\"",Gbl.RowEvenOdd);
+ HTM_Txt (Txt_Question_removed);
+ HTM_TD_End ();
+
+ HTM_TR_End ();
+ }
+
+ /***** Free structure that stores the query result *****/
+ DB_FreeMySQLResult (&mysql_res);
+ }
+
+ /***** End table *****/
+ HTM_TABLE_End ();
+ }
+
+/*****************************************************************************/
+/********** Write a row of a test, with one question and its answer **********/
+/*****************************************************************************/
+
+void TstExa_WriteQstAndAnsExam (struct UsrData *UsrDat,
+ struct TstExa_Exam *Exam,
+ unsigned NumQst,
+ MYSQL_ROW row,
+ unsigned Visibility)
+ {
+ struct Tst_Question Question;
+ bool IsVisibleQstAndAnsTxt = TstVis_IsVisibleQstAndAnsTxt (Visibility);
+ /*
+ row[0] UNIX_TIMESTAMP(EditTime)
+ row[1] AnsType
+ row[2] Shuffle
+ row[3] Stem
+ row[4] Feedback
+ row[5] MedCod
+ row[6] NumHits
+ row[7] NumHitsNotBlank
+ row[8] Score
+ */
+
+ /***** Create test question *****/
+ Tst_QstConstructor (&Question);
+ Question.QstCod = Exam->Questions[NumQst].QstCod;
+
+ /***** Begin row *****/
+ HTM_TR_Begin (NULL);
+
+ /***** Number of question and answer type (row[1]) *****/
+ HTM_TD_Begin ("class=\"RT COLOR%u\"",Gbl.RowEvenOdd);
+ Tst_WriteNumQst (NumQst + 1);
+ Question.Answer.Type = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[1]);
+ Tst_WriteAnswerType (Question.Answer.Type);
+ HTM_TD_End ();
+
+ /***** Stem, media and answers *****/
+ HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
+
+ /* Stem (row[3]) */
+ Tst_WriteQstStem (row[3],"TEST_EXA",IsVisibleQstAndAnsTxt);
+
+ /* Media (row[5]) */
+ if (IsVisibleQstAndAnsTxt)
+ {
+ Question.Media.MedCod = Str_ConvertStrCodToLongCod (row[5]);
+ Med_GetMediaDataByCod (&Question.Media);
+ Med_ShowMedia (&Question.Media,
+ "TEST_MED_SHOW_CONT",
+ "TEST_MED_SHOW");
+ }
+
+ /* Answers */
+ TstExa_ComputeAnswerScore (Exam,NumQst,&Question);
+ TstExa_WriteAnswersExam (UsrDat,
+ Exam,NumQst,&Question,
+ Visibility);
+
+ /* Question feedback (row[4]) */
+ if (TstVis_IsVisibleFeedbackTxt (Visibility))
+ Tst_WriteQstFeedback (row[4],"TEST_EXA_LIGHT");
+
+ HTM_TD_End ();
+
+ /***** End row *****/
+ HTM_TR_End ();
+
+ /***** Destroy test question *****/
+ Tst_QstDestructor (&Question);
+ }
+
+/*****************************************************************************/
+/*********** Compute score of each question and store in database ************/
+/*****************************************************************************/
+
+void TstExa_ComputeScoresAndStoreExamQuestions (struct TstExa_Exam *Exam,
+ bool UpdateQstScore)
+ {
+ unsigned NumQst;
+ struct Tst_Question Question;
+
+ /***** Initialize total score *****/
+ Exam->Score = 0.0;
+ Exam->NumQstsNotBlank = 0;
+
+ /***** Compute and store scores of all questions *****/
+ for (NumQst = 0;
+ NumQst < Exam->NumQsts;
+ NumQst++)
+ {
+ /* Compute question score */
+ Tst_QstConstructor (&Question);
+ Question.QstCod = Exam->Questions[NumQst].QstCod;
+ Question.Answer.Type = Tst_GetQstAnswerType (Question.QstCod);
+ TstExa_ComputeAnswerScore (Exam,NumQst,&Question);
+ Tst_QstDestructor (&Question);
+
+ /* Store test exam question in database */
+ TstExa_StoreOneExamQstInDB (Exam,
+ NumQst); // 0, 1, 2, 3...
+
+ /* Accumulate total score */
+ Exam->Score += Exam->Questions[NumQst].Score;
+ if (Exam->Questions[NumQst].AnswerIsNotBlank)
+ Exam->NumQstsNotBlank++;
+
+
+ /* Update the number of hits and the score of this question in tests database */
+ if (UpdateQstScore)
+ Tst_UpdateQstScoreInDB (Exam,NumQst);
+ }
+
+ /***** Store test exam in database *****/
+ TstExa_UpdateExamInDB (Exam);
+ }
+
+/*****************************************************************************/
+/************* Write answers of a question when assessing a test *************/
+/*****************************************************************************/
+
+static void TstExa_ComputeAnswerScore (struct TstExa_Exam *Exam,
+ unsigned NumQst,
+ struct Tst_Question *Question)
+ {
+ /***** Write answer depending on type *****/
+ switch (Question->Answer.Type)
+ {
+ case Tst_ANS_INT:
+ TstExa_ComputeIntAnsScore (Exam,NumQst,Question);
+ break;
+ case Tst_ANS_FLOAT:
+ TstExa_ComputeFloatAnsScore (Exam,NumQst,Question);
+ break;
+ case Tst_ANS_TRUE_FALSE:
+ TstExa_ComputeTFAnsScore (Exam,NumQst,Question);
+ break;
+ case Tst_ANS_UNIQUE_CHOICE:
+ case Tst_ANS_MULTIPLE_CHOICE:
+ TstExa_ComputeChoiceAnsScore (Exam,NumQst,Question);
+ break;
+ case Tst_ANS_TEXT:
+ TstExa_ComputeTextAnsScore (Exam,NumQst,Question);
+ break;
+ default:
+ break;
+ }
+ }
+
+/*****************************************************************************/
+/**************** Write integer answer when assessing a test *****************/
+/*****************************************************************************/
+
+static void TstExa_ComputeIntAnsScore (struct TstExa_Exam *Exam,
+ unsigned NumQst,
+ struct Tst_Question *Question)
+ {
+ long AnswerUsr;
+
+ /***** Get the numerical value of the correct answer *****/
+ TstExa_GetCorrectIntAnswerFromDB (Question);
+
+ /***** Compute score *****/
+ Exam->Questions[NumQst].Score = 0.0; // Default score for blank or wrong answer
+ Exam->Questions[NumQst].AnswerIsNotBlank = (Exam->Questions[NumQst].StrAnswers[0] != '\0');
+ if (Exam->Questions[NumQst].AnswerIsNotBlank) // If user has answered the answer
+ if (sscanf (Exam->Questions[NumQst].StrAnswers,"%ld",&AnswerUsr) == 1)
+ if (AnswerUsr == Question->Answer.Integer) // Correct answer
+ Exam->Questions[NumQst].Score = 1.0;
+ }
+
+static void TstExa_GetCorrectIntAnswerFromDB (struct Tst_Question *Question)
+ {
+ MYSQL_RES *mysql_res;
+ MYSQL_ROW row;
+
+ /***** Query database *****/
+ Question->Answer.NumOptions =
+ (unsigned) DB_QuerySELECT (&mysql_res,"can not get answers of a question",
+ "SELECT Answer" // row[0]
+ " FROM tst_answers"
+ " WHERE QstCod=%ld",
+ Question->QstCod);
+
+ /***** Check if number of rows is correct *****/
+ Tst_CheckIfNumberOfAnswersIsOne (Question);
+
+ /***** Get correct answer *****/
+ row = mysql_fetch_row (mysql_res);
+ if (sscanf (row[0],"%ld",&Question->Answer.Integer) != 1)
+ Lay_ShowErrorAndExit ("Wrong integer answer.");
+
+ /***** Free structure that stores the query result *****/
+ DB_FreeMySQLResult (&mysql_res);
+ }
+
+/*****************************************************************************/
+/***************** Write float answer when assessing a test ******************/
+/*****************************************************************************/
+
+static void TstExa_ComputeFloatAnsScore (struct TstExa_Exam *Exam,
+ unsigned NumQst,
+ struct Tst_Question *Question)
+ {
+ double AnswerUsr;
+
+ /***** Get the numerical value of the minimum and maximum correct answers *****/
+ TstExa_GetCorrectFloatAnswerFromDB (Question);
+
+ /***** Compute score *****/
+ Exam->Questions[NumQst].Score = 0.0; // Default score for blank or wrong answer
+ Exam->Questions[NumQst].AnswerIsNotBlank = (Exam->Questions[NumQst].StrAnswers[0] != '\0');
+ if (Exam->Questions[NumQst].AnswerIsNotBlank) // If user has answered the answer
+ {
+ AnswerUsr = Str_GetDoubleFromStr (Exam->Questions[NumQst].StrAnswers);
+ // A bad formatted floating point answer will interpreted as 0.0
+ Exam->Questions[NumQst].Score = (AnswerUsr >= Question->Answer.FloatingPoint[0] &&
+ AnswerUsr <= Question->Answer.FloatingPoint[1]) ? 1.0 : // If correct (inside the interval)
+ 0.0; // If wrong (outside the interval)
+ }
+ }
+
+static void TstExa_GetCorrectFloatAnswerFromDB (struct Tst_Question *Question)
+ {
+ MYSQL_RES *mysql_res;
+ MYSQL_ROW row;
+ unsigned NumOpt;
+ double Tmp;
+
+ /***** Query database *****/
+ Question->Answer.NumOptions =
+ (unsigned) DB_QuerySELECT (&mysql_res,"can not get answers of a question",
+ "SELECT Answer" // row[0]
+ " FROM tst_answers"
+ " WHERE QstCod=%ld",
+ Question->QstCod);
+
+ /***** Check if number of rows is correct *****/
+ if (Question->Answer.NumOptions != 2)
+ Lay_ShowErrorAndExit ("Wrong float range.");
+
+ /***** Get float range *****/
+ for (NumOpt = 0;
+ NumOpt < 2;
+ NumOpt++)
+ {
+ row = mysql_fetch_row (mysql_res);
+ Question->Answer.FloatingPoint[NumOpt] = Str_GetDoubleFromStr (row[0]);
+ }
+ if (Question->Answer.FloatingPoint[0] >
+ Question->Answer.FloatingPoint[1]) // The maximum and the minimum are swapped
+ {
+ /* Swap maximum and minimum */
+ Tmp = Question->Answer.FloatingPoint[0];
+ Question->Answer.FloatingPoint[0] = Question->Answer.FloatingPoint[1];
+ Question->Answer.FloatingPoint[1] = Tmp;
+ }
+
+ /***** Free structure that stores the query result *****/
+ DB_FreeMySQLResult (&mysql_res);
+ }
+
+/*****************************************************************************/
+/************** Write false / true answer when assessing a test **************/
+/*****************************************************************************/
+
+static void TstExa_ComputeTFAnsScore (struct TstExa_Exam *Exam,
+ unsigned NumQst,
+ struct Tst_Question *Question)
+ {
+ /***** Get answer true or false *****/
+ TstExa_GetCorrectTFAnswerFromDB (Question);
+
+ /***** Compute score *****/
+ Exam->Questions[NumQst].AnswerIsNotBlank = (Exam->Questions[NumQst].StrAnswers[0] != '\0');
+ if (Exam->Questions[NumQst].AnswerIsNotBlank) // User has selected T or F
+ Exam->Questions[NumQst].Score = (Exam->Questions[NumQst].StrAnswers[0] == Question->Answer.TF) ? 1.0 : // Correct
+ -1.0; // Wrong
+ else
+ Exam->Questions[NumQst].Score = 0.0;
+ }
+
+static void TstExa_GetCorrectTFAnswerFromDB (struct Tst_Question *Question)
+ {
+ MYSQL_RES *mysql_res;
+ MYSQL_ROW row;
+
+ /***** Query database *****/
+ Question->Answer.NumOptions =
+ (unsigned) DB_QuerySELECT (&mysql_res,"can not get answers of a question",
+ "SELECT Answer" // row[0]
+ " FROM tst_answers"
+ " WHERE QstCod=%ld",
+ Question->QstCod);
+
+ /***** Check if number of rows is correct *****/
+ Tst_CheckIfNumberOfAnswersIsOne (Question);
+
+ /***** Get answer *****/
+ row = mysql_fetch_row (mysql_res);
+ Question->Answer.TF = row[0][0];
+
+ /***** Free structure that stores the query result *****/
+ DB_FreeMySQLResult (&mysql_res);
+ }
+
+/*****************************************************************************/
+/************ Compute score for single or multiple choice answer *************/
+/*****************************************************************************/
+
+void TstExa_ComputeChoiceAnsScore (struct TstExa_Exam *Exam,
+ unsigned NumQst,
+ struct Tst_Question *Question)
+ {
+ unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]; // Indexes of all answers of this question
+ bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION];
+
+ /***** Get correct options of test question from database *****/
+ TstExa_GetCorrectChoiceAnswerFromDB (Question);
+
+ /***** Get indexes for this question from string *****/
+ TstExa_GetIndexesFromStr (Exam->Questions[NumQst].StrIndexes,Indexes);
+
+ /***** Get the user's answers for this question from string *****/
+ TstExa_GetAnswersFromStr (Exam->Questions[NumQst].StrAnswers,UsrAnswers);
+
+ /***** Compute the total score of this question *****/
+ TstExa_ComputeScoreQst (Exam,NumQst,Question,Indexes,UsrAnswers);
+ }
+
+static void TstExa_GetCorrectChoiceAnswerFromDB (struct Tst_Question *Question)
+ {
+ MYSQL_RES *mysql_res;
+ MYSQL_ROW row;
+ unsigned NumOpt;
+
+ /***** Query database *****/
+ Question->Answer.NumOptions =
+ (unsigned) DB_QuerySELECT (&mysql_res,"can not get answers of a question",
+ "SELECT Correct" // row[0]
+ " FROM tst_answers"
+ " WHERE QstCod=%ld"
+ " ORDER BY AnsInd",
+ Question->QstCod);
+ for (NumOpt = 0;
+ NumOpt < Question->Answer.NumOptions;
+ NumOpt++)
+ {
+ /* Get next answer */
+ row = mysql_fetch_row (mysql_res);
+
+ /* Assign correctness (row[0]) of this answer (this option) */
+ Question->Answer.Options[NumOpt].Correct = (row[0][0] == 'Y');
+ }
+
+ /* Free structure that stores the query result */
+ DB_FreeMySQLResult (&mysql_res);
+ }
+
+/*****************************************************************************/
+/********************* Get vector of indexes from string *********************/
+/*****************************************************************************/
+
+void TstExa_GetIndexesFromStr (const char StrIndexesOneQst[TstExa_MAX_BYTES_INDEXES_ONE_QST + 1], // 0 1 2 3, 3 0 2 1, etc.
+ unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION])
+ {
+ unsigned NumOpt;
+ const char *Ptr;
+ char StrOneIndex[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
+
+ /***** Get indexes from string *****/
+ for (NumOpt = 0, Ptr = StrIndexesOneQst;
+ NumOpt < Tst_MAX_OPTIONS_PER_QUESTION && *Ptr;
+ NumOpt++)
+ {
+ Par_GetNextStrUntilSeparParamMult (&Ptr,StrOneIndex,Cns_MAX_DECIMAL_DIGITS_UINT);
+
+ if (sscanf (StrOneIndex,"%u",&(Indexes[NumOpt])) != 1)
+ Lay_ShowErrorAndExit ("Wrong index of answer.");
+
+ if (Indexes[NumOpt] >= Tst_MAX_OPTIONS_PER_QUESTION)
+ Lay_ShowErrorAndExit ("Wrong index of answer.");
+ }
+
+ /***** Initialize remaining to 0 *****/
+ for (;
+ NumOpt < Tst_MAX_OPTIONS_PER_QUESTION;
+ NumOpt++)
+ Indexes[NumOpt] = 0;
+ }
+
+/*****************************************************************************/
+/****************** Get vector of user's answers from string *****************/
+/*****************************************************************************/
+
+void TstExa_GetAnswersFromStr (const char StrAnswersOneQst[TstExa_MAX_BYTES_ANSWERS_ONE_QST + 1],
+ bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION])
+ {
+ unsigned NumOpt;
+ const char *Ptr;
+ char StrOneAnswer[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
+ unsigned AnsUsr;
+
+ /***** Initialize all answers to false *****/
+ for (NumOpt = 0;
+ NumOpt < Tst_MAX_OPTIONS_PER_QUESTION;
+ NumOpt++)
+ UsrAnswers[NumOpt] = false;
+
+ /***** Set selected answers to true *****/
+ for (NumOpt = 0, Ptr = StrAnswersOneQst;
+ NumOpt < Tst_MAX_OPTIONS_PER_QUESTION && *Ptr;
+ NumOpt++)
+ {
+ Par_GetNextStrUntilSeparParamMult (&Ptr,StrOneAnswer,Cns_MAX_DECIMAL_DIGITS_UINT);
+
+ if (sscanf (StrOneAnswer,"%u",&AnsUsr) != 1)
+ Lay_ShowErrorAndExit ("Bad user's answer.");
+
+ if (AnsUsr >= Tst_MAX_OPTIONS_PER_QUESTION)
+ Lay_ShowErrorAndExit ("Bad user's answer.");
+
+ UsrAnswers[AnsUsr] = true;
+ }
+ }
+
+/*****************************************************************************/
+/*********************** Compute the score of a question *********************/
+/*****************************************************************************/
+
+static void TstExa_ComputeScoreQst (struct TstExa_Exam *Exam,
+ unsigned NumQst,
+ const struct Tst_Question *Question,
+ unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION], // Indexes of all answers of this question
+ bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION])
+ {
+ unsigned NumOpt;
+ unsigned NumOptTotInQst = 0;
+ unsigned NumOptCorrInQst = 0;
+ unsigned NumAnsGood = 0;
+ unsigned NumAnsBad = 0;
+
+ /***** Compute the total score of this question *****/
+ for (NumOpt = 0;
+ NumOpt < Question->Answer.NumOptions;
+ NumOpt++)
+ {
+ NumOptTotInQst++;
+ if (Question->Answer.Options[Indexes[NumOpt]].Correct)
+ NumOptCorrInQst++;
+
+ if (UsrAnswers[Indexes[NumOpt]]) // This answer has been selected by the user
+ {
+ if (Question->Answer.Options[Indexes[NumOpt]].Correct)
+ NumAnsGood++;
+ else
+ NumAnsBad++;
+ }
+ }
+
+ /* The answer is blank? */
+ Exam->Questions[NumQst].AnswerIsNotBlank = NumAnsGood != 0 || NumAnsBad != 0;
+ if (Exam->Questions[NumQst].AnswerIsNotBlank)
+ {
+ /* Compute the score */
+ if (Question->Answer.Type == Tst_ANS_UNIQUE_CHOICE)
+ {
+ if (NumOptTotInQst >= 2) // It should be 2 options at least
+ Exam->Questions[NumQst].Score = (double) NumAnsGood -
+ (double) NumAnsBad / (double) (NumOptTotInQst - 1);
+ else // 0 or 1 options (impossible)
+ Exam->Questions[NumQst].Score = (double) NumAnsGood;
+ }
+ else // AnswerType == Tst_ANS_MULTIPLE_CHOICE
+ {
+ if (NumOptCorrInQst) // There are correct options in the question
+ {
+ if (NumOptCorrInQst < NumOptTotInQst) // If there are correct options and wrong options (typical case)
+ Exam->Questions[NumQst].Score = (double) NumAnsGood / (double) NumOptCorrInQst -
+ (double) NumAnsBad / (double) (NumOptTotInQst - NumOptCorrInQst);
+ else // Si todas the opciones son correctas (caso raro)
+ Exam->Questions[NumQst].Score = (double) NumAnsGood / (double) NumOptCorrInQst;
+ }
+ else
+ {
+ if (NumOptTotInQst) // There are options but none is correct (extrange case)
+ Exam->Questions[NumQst].Score = - (double) NumAnsBad / (double) NumOptTotInQst;
+ else // There are no options (impossible!)
+ Exam->Questions[NumQst].Score = 0.0;
+ }
+ }
+ }
+ else // Answer is blank
+ Exam->Questions[NumQst].Score = 0.0;
+ }
+
+/*****************************************************************************/
+/********************* Compute score for text answer *************************/
+/*****************************************************************************/
+
+static void TstExa_ComputeTextAnsScore (struct TstExa_Exam *Exam,
+ unsigned NumQst,
+ struct Tst_Question *Question)
+ {
+ unsigned NumOpt;
+ char TextAnsUsr[TstExa_MAX_BYTES_ANSWERS_ONE_QST + 1];
+ char TextAnsOK[TstExa_MAX_BYTES_ANSWERS_ONE_QST + 1];
+ bool Correct = false;
+
+ /***** Get correct answers for this question from database *****/
+ TstExa_GetCorrectTextAnswerFromDB (Question);
+
+ /***** Compute score *****/
+ Exam->Questions[NumQst].Score = 0.0; // Default score for blank or wrong answer
+ Exam->Questions[NumQst].AnswerIsNotBlank = (Exam->Questions[NumQst].StrAnswers[0] != '\0');
+ if (Exam->Questions[NumQst].AnswerIsNotBlank) // If user has answered the answer
+ {
+ /* Filter the user answer */
+ Str_Copy (TextAnsUsr,Exam->Questions[NumQst].StrAnswers,
+ TstExa_MAX_BYTES_ANSWERS_ONE_QST);
+
+ /* In order to compare student answer to stored answer,
+ the text answers are stored avoiding two or more consecurive spaces */
+ Str_ReplaceSeveralSpacesForOne (TextAnsUsr);
+
+ Str_ConvertToComparable (TextAnsUsr);
+
+ for (NumOpt = 0;
+ NumOpt < Question->Answer.NumOptions;
+ NumOpt++)
+ {
+ /* Filter this correct answer */
+ Str_Copy (TextAnsOK,Question->Answer.Options[NumOpt].Text,
+ TstExa_MAX_BYTES_ANSWERS_ONE_QST);
+ Str_ConvertToComparable (TextAnsOK);
+
+ /* Check is user answer is correct */
+ if (!strcoll (TextAnsUsr,TextAnsOK))
+ {
+ Correct = true;
+ break;
+ }
+ }
+
+ if (Correct)
+ Exam->Questions[NumQst].Score = 1.0; // Correct answer
+ }
+ }
+
+static void TstExa_GetCorrectTextAnswerFromDB (struct Tst_Question *Question)
+ {
+ MYSQL_RES *mysql_res;
+ MYSQL_ROW row;
+ unsigned NumOpt;
+
+ /***** Query database *****/
+ Question->Answer.NumOptions =
+ (unsigned) DB_QuerySELECT (&mysql_res,"can not get answers of a question",
+ "SELECT Answer" // row[0]
+ " FROM tst_answers"
+ " WHERE QstCod=%ld",
+ Question->QstCod);
+
+ /***** Get text and correctness of answers for this question from database (one row per answer) *****/
+ for (NumOpt = 0;
+ NumOpt < Question->Answer.NumOptions;
+ NumOpt++)
+ {
+ /***** Get next answer *****/
+ row = mysql_fetch_row (mysql_res);
+
+ /***** Allocate memory for text in this choice answer *****/
+ if (!Tst_AllocateTextChoiceAnswer (Question,NumOpt))
+ /* Abort on error */
+ Ale_ShowAlertsAndExit ();
+
+ /***** Copy answer text (row[1]) and convert it, that is in HTML, to rigorous HTML ******/
+ Str_Copy (Question->Answer.Options[NumOpt].Text,row[1],
+ Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
+ Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
+ Question->Answer.Options[NumOpt].Text,
+ Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false);
+ }
+
+ /***** Free structure that stores the query result *****/
+ DB_FreeMySQLResult (&mysql_res);
+ }
+
+/*****************************************************************************/
+/************ Compute and show total grade out of maximum grade **************/
+/*****************************************************************************/
+
+void TstExa_ComputeAndShowGrade (unsigned NumQsts,double Score,double MaxGrade)
+ {
+ TstExa_ShowGrade (TstExa_ComputeGrade (NumQsts,Score,MaxGrade),MaxGrade);
+ }
+
+/*****************************************************************************/
+/**************** Compute total grade out of maximum grade *******************/
+/*****************************************************************************/
+
+double TstExa_ComputeGrade (unsigned NumQsts,double Score,double MaxGrade)
+ {
+ double MaxScore;
+ double Grade;
+
+ /***** Compute grade *****/
+ if (NumQsts)
+ {
+ MaxScore = (double) NumQsts;
+ Grade = Score * MaxGrade / MaxScore;
+ }
+ else
+ Grade = 0.0;
+
+ return Grade;
+ }
+
+/*****************************************************************************/
+/****************** Show total grade out of maximum grade ********************/
+/*****************************************************************************/
+
+void TstExa_ShowGrade (double Grade,double MaxGrade)
+ {
+ /***** Write grade over maximum grade *****/
+ HTM_Double2Decimals (Grade);
+ HTM_Txt ("/");
+ HTM_Double2Decimals (MaxGrade);
+ }
+
+/*****************************************************************************/
+/************* Write answers of a question when assessing a test *************/
+/*****************************************************************************/
+
+static void TstExa_WriteAnswersExam (struct UsrData *UsrDat,
+ const struct TstExa_Exam *Exam,
+ unsigned NumQst,
+ struct Tst_Question *Question,
+ unsigned Visibility)
+ {
+ MYSQL_RES *mysql_res;
+
+ /***** Get answer of a question from 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
+ */
+
+ /***** Write answer depending on type *****/
+ switch (Question->Answer.Type)
+ {
+ case Tst_ANS_INT:
+ TstExa_WriteIntAnsExam (UsrDat,Exam,
+ NumQst,Question,mysql_res,
+ Visibility);
+ break;
+ case Tst_ANS_FLOAT:
+ TstExa_WriteFloatAnsExam (UsrDat,Exam,
+ NumQst,Question,mysql_res,
+ Visibility);
+ break;
+ case Tst_ANS_TRUE_FALSE:
+ TstExa_WriteTFAnsExam (UsrDat,Exam,
+ NumQst,Question,mysql_res,
+ Visibility);
+ break;
+ case Tst_ANS_UNIQUE_CHOICE:
+ case Tst_ANS_MULTIPLE_CHOICE:
+ TstExa_WriteChoiceAnsExam (UsrDat,Exam,
+ NumQst,Question,mysql_res,
+ Visibility);
+ break;
+ case Tst_ANS_TEXT:
+ TstExa_WriteTextAnsExam (UsrDat,Exam,
+ NumQst,Question,mysql_res,
+ Visibility);
+ break;
+ default:
+ break;
+ }
+
+ /***** Free structure that stores the query result *****/
+ DB_FreeMySQLResult (&mysql_res);
+ }
+
+/*****************************************************************************/
+/******************* Write integer answer in a test exam *********************/
+/*****************************************************************************/
+
+static void TstExa_WriteIntAnsExam (struct UsrData *UsrDat,
+ const struct TstExa_Exam *Exam,
+ unsigned NumQst,
+ const struct Tst_Question *Question,
+ MYSQL_RES *mysql_res,
+ unsigned Visibility)
+ {
+ MYSQL_ROW row;
+ long IntAnswerUsr;
+ long IntAnswerCorr;
+ /*
+ row[0] AnsInd
+ row[1] Answer
+ row[2] Feedback
+ row[3] MedCod
+ row[4] Correct
+ */
+ /***** Check if number of rows is correct *****/
+ Tst_CheckIfNumberOfAnswersIsOne (Question);
+
+ /***** Get the numerical value of the correct answer *****/
+ row = mysql_fetch_row (mysql_res);
+ if (sscanf (row[1],"%ld",&IntAnswerCorr) != 1)
+ Lay_ShowErrorAndExit ("Wrong integer answer.");
+
+ /***** Header with the title of each column *****/
+ HTM_TABLE_BeginPadding (2);
+ HTM_TR_Begin (NULL);
+ TstExa_WriteHeadUserCorrect (UsrDat);
+ HTM_TR_End ();
+
+ HTM_TR_Begin (NULL);
+
+ /***** Write the user answer *****/
+ if (Exam->Questions[NumQst].StrAnswers[0]) // If user has answered the question
+ {
+ if (sscanf (Exam->Questions[NumQst].StrAnswers,"%ld",&IntAnswerUsr) == 1)
+ {
+ HTM_TD_Begin ("class=\"%s CM\"",
+ TstVis_IsVisibleCorrectAns (Visibility) ?
+ (IntAnswerUsr == IntAnswerCorr ? "ANS_OK" :
+ "ANS_BAD") :
+ "ANS_0");
+ HTM_Long (IntAnswerUsr);
+ HTM_TD_End ();
+ }
+ else
+ {
+ HTM_TD_Begin ("class=\"ANS_0 CM\"");
+ HTM_Txt ("?");
+ HTM_TD_End ();
+ }
+ }
+ else // If user has omitted the answer
+ HTM_TD_Empty (1);
+
+ /***** Write the correct answer *****/
+ HTM_TD_Begin ("class=\"ANS_0 CM\"");
+ if (TstVis_IsVisibleQstAndAnsTxt (Visibility) &&
+ TstVis_IsVisibleCorrectAns (Visibility))
+ HTM_Long (IntAnswerCorr);
+ else
+ Ico_PutIconNotVisible ();
+ HTM_TD_End ();
+
+ HTM_TR_End ();
+
+ /***** Write the score of this question *****/
+ if (TstVis_IsVisibleEachQstScore (Visibility))
+ {
+ TstExa_WriteScoreStart (2);
+ if (!Exam->Questions[NumQst].StrAnswers[0]) // If user has omitted the answer
+ {
+ HTM_SPAN_Begin ("class=\"ANS_0\"");
+ HTM_Double2Decimals (0.0);
+ }
+ else if (IntAnswerUsr == IntAnswerCorr) // If correct
+ {
+ HTM_SPAN_Begin ("class=\"ANS_OK\"");
+ HTM_Double2Decimals (1.0);
+ }
+ else // If wrong
+ {
+ HTM_SPAN_Begin ("class=\"ANS_BAD\"");
+ HTM_Double2Decimals (0.0);
+ }
+ HTM_SPAN_End ();
+ TstExa_WriteScoreEnd ();
+ }
+
+ HTM_TABLE_End ();
+ }
+
+/*****************************************************************************/
+/******************** Write float answer in an test exam *********************/
+/*****************************************************************************/
+
+static void TstExa_WriteFloatAnsExam (struct UsrData *UsrDat,
+ const struct TstExa_Exam *Exam,
+ unsigned NumQst,
+ const struct Tst_Question *Question,
+ MYSQL_RES *mysql_res,
+ unsigned Visibility)
+ {
+ MYSQL_ROW row;
+ unsigned i;
+ double FloatAnsUsr = 0.0,Tmp;
+ double FloatAnsCorr[2];
+ /*
+ row[0] AnsInd
+ row[1] Answer
+ row[2] Feedback
+ row[3] MedCod
+ row[4] Correct
+ */
+ /***** Check if number of rows is correct *****/
+ if (Question->Answer.NumOptions != 2)
+ Lay_ShowErrorAndExit ("Wrong float range.");
+
+ /***** Get the numerical value of the minimum and maximum correct answers *****/
+ for (i = 0;
+ i < 2;
+ i++)
+ {
+ row = mysql_fetch_row (mysql_res);
+ FloatAnsCorr[i] = Str_GetDoubleFromStr (row[1]);
+ }
+ if (FloatAnsCorr[0] > FloatAnsCorr[1]) // The maximum and the minimum are swapped
+ {
+ /* Swap maximum and minimum */
+ Tmp = FloatAnsCorr[0];
+ FloatAnsCorr[0] = FloatAnsCorr[1];
+ FloatAnsCorr[1] = Tmp;
+ }
+
+ /***** Header with the title of each column *****/
+ HTM_TABLE_BeginPadding (2);
+ HTM_TR_Begin (NULL);
+ TstExa_WriteHeadUserCorrect (UsrDat);
+ HTM_TR_End ();
+
+ HTM_TR_Begin (NULL);
+
+ /***** Write the user answer *****/
+ if (Exam->Questions[NumQst].StrAnswers[0]) // If user has answered the question
+ {
+ FloatAnsUsr = Str_GetDoubleFromStr (Exam->Questions[NumQst].StrAnswers);
+ // A bad formatted floating point answer will interpreted as 0.0
+ HTM_TD_Begin ("class=\"%s CM\"",
+ TstVis_IsVisibleCorrectAns (Visibility) ?
+ ((FloatAnsUsr >= FloatAnsCorr[0] &&
+ FloatAnsUsr <= FloatAnsCorr[1]) ? "ANS_OK" :
+ "ANS_BAD") :
+ "ANS_0");
+ HTM_Double (FloatAnsUsr);
+ }
+ else // If user has omitted the answer
+ HTM_TD_Begin (NULL);
+ HTM_TD_End ();
+
+ /***** Write the correct answer *****/
+ HTM_TD_Begin ("class=\"ANS_0 CM\"");
+ if (TstVis_IsVisibleQstAndAnsTxt (Visibility) &&
+ TstVis_IsVisibleCorrectAns (Visibility))
+ {
+ HTM_Txt ("[");
+ HTM_Double (FloatAnsCorr[0]);
+ HTM_Txt ("; ");
+ HTM_Double (FloatAnsCorr[1]);
+ HTM_Txt ("]");
+ }
+ else
+ Ico_PutIconNotVisible ();
+ HTM_TD_End ();
+
+ HTM_TR_End ();
+
+ /***** Write the score of this question *****/
+ if (TstVis_IsVisibleEachQstScore (Visibility))
+ {
+ TstExa_WriteScoreStart (2);
+ if (!Exam->Questions[NumQst].StrAnswers[0]) // If user has omitted the answer
+ {
+ HTM_SPAN_Begin ("class=\"ANS_0\"");
+ HTM_Double2Decimals (0.0);
+ }
+ else if (FloatAnsUsr >= FloatAnsCorr[0] &&
+ FloatAnsUsr <= FloatAnsCorr[1]) // If correct (inside the interval)
+ {
+ HTM_SPAN_Begin ("class=\"ANS_OK\"");
+ HTM_Double2Decimals (1.0);
+ }
+ else // If wrong (outside the interval)
+ {
+ HTM_SPAN_Begin ("class=\"ANS_BAD\"");
+ HTM_Double2Decimals (0.0);
+ }
+ HTM_SPAN_End ();
+ TstExa_WriteScoreEnd ();
+ }
+
+ HTM_TABLE_End ();
+ }
+
+/*****************************************************************************/
+/***************** Write false / true answer in a test exam ******************/
+/*****************************************************************************/
+
+static void TstExa_WriteTFAnsExam (struct UsrData *UsrDat,
+ const struct TstExa_Exam *Exam,
+ unsigned NumQst,
+ const struct Tst_Question *Question,
+ MYSQL_RES *mysql_res,
+ unsigned Visibility)
+ {
+ MYSQL_ROW row;
+ char AnsTF;
+ /*
+ row[0] AnsInd
+ row[1] Answer
+ row[2] Feedback
+ row[3] MedCod
+ row[4] Correct
+ */
+ /***** Check if number of rows is correct *****/
+ Tst_CheckIfNumberOfAnswersIsOne (Question);
+
+ /***** Get answer true or false *****/
+ row = mysql_fetch_row (mysql_res);
+ AnsTF = Exam->Questions[NumQst].StrAnswers[0];
+
+ /***** Header with the title of each column *****/
+ HTM_TABLE_BeginPadding (2);
+ HTM_TR_Begin (NULL);
+ TstExa_WriteHeadUserCorrect (UsrDat);
+ HTM_TR_End ();
+
+ HTM_TR_Begin (NULL);
+
+ /***** Write the user answer *****/
+ HTM_TD_Begin ("class=\"%s CM\"",
+ TstVis_IsVisibleCorrectAns (Visibility) ?
+ (AnsTF == row[1][0] ? "ANS_OK" :
+ "ANS_BAD") :
+ "ANS_0");
+ Tst_WriteAnsTF (AnsTF);
+ HTM_TD_End ();
+
+ /***** Write the correct answer *****/
+ HTM_TD_Begin ("class=\"ANS_0 CM\"");
+ if (TstVis_IsVisibleQstAndAnsTxt (Visibility) &&
+ TstVis_IsVisibleCorrectAns (Visibility))
+ Tst_WriteAnsTF (row[1][0]);
+ else
+ Ico_PutIconNotVisible ();
+ HTM_TD_End ();
+
+ HTM_TR_End ();
+
+ /***** Write the score of this question *****/
+ if (TstVis_IsVisibleEachQstScore (Visibility))
+ {
+ TstExa_WriteScoreStart (2);
+ if (AnsTF == '\0') // If user has omitted the answer
+ {
+ HTM_SPAN_Begin ("class=\"ANS_0\"");
+ HTM_Double2Decimals (0.0);
+ }
+ else if (AnsTF == row[1][0]) // If correct
+ {
+ HTM_SPAN_Begin ("class=\"ANS_OK\"");
+ HTM_Double2Decimals (1.0);
+ }
+ else // If wrong
+ {
+ HTM_SPAN_Begin ("class=\"ANS_BAD\"");
+ HTM_Double2Decimals (-1.0);
+ }
+ HTM_SPAN_End ();
+ TstExa_WriteScoreEnd ();
+ }
+
+ HTM_TABLE_End ();
+ }
+
+/*****************************************************************************/
+/********** Write single or multiple choice answer in a test exam ************/
+/*****************************************************************************/
+
+static void TstExa_WriteChoiceAnsExam (struct UsrData *UsrDat,
+ const struct TstExa_Exam *Exam,
+ unsigned NumQst,
+ struct Tst_Question *Question,
+ MYSQL_RES *mysql_res,
+ unsigned Visibility)
+ {
+ extern const char *Txt_TST_Answer_given_by_the_user;
+ extern const char *Txt_TST_Answer_given_by_the_teachers;
+ unsigned NumOpt;
+ unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]; // Indexes of all answers of this question
+ bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION];
+ struct
+ {
+ char *Class;
+ char *Str;
+ } Ans;
+
+ /***** Get text and correctness of answers for this question
+ from database (one row per answer) *****/
+ /*
+ row[0] AnsInd
+ row[1] Answer
+ row[2] Feedback
+ row[3] MedCod
+ row[4] Correct
+ */
+ Tst_GetChoiceAns (Question,mysql_res);
+
+ /***** Get indexes for this question from string *****/
+ TstExa_GetIndexesFromStr (Exam->Questions[NumQst].StrIndexes,Indexes);
+
+ /***** Get the user's answers for this question from string *****/
+ TstExa_GetAnswersFromStr (Exam->Questions[NumQst].StrAnswers,UsrAnswers);
+
+ /***** Begin table *****/
+ HTM_TABLE_BeginPadding (2);
+ HTM_TR_Begin (NULL);
+ TstExa_WriteHeadUserCorrect (UsrDat);
+ HTM_TD_Empty (2);
+ HTM_TR_End ();
+
+ /***** Write answers (one row per answer) *****/
+ for (NumOpt = 0;
+ NumOpt < Question->Answer.NumOptions;
+ NumOpt++)
+ {
+ HTM_TR_Begin (NULL);
+
+ /* Draw icon depending on user's answer */
+ if (UsrAnswers[Indexes[NumOpt]] == true) // This answer has been selected by the user
+ {
+ if (TstVis_IsVisibleCorrectAns (Visibility))
+ {
+ if (Question->Answer.Options[Indexes[NumOpt]].Correct)
+ {
+ Ans.Class = "ANS_OK";
+ Ans.Str = "✓";
+ }
+ else
+ {
+ Ans.Class = "ANS_BAD";
+ Ans.Str = "✗";
+ }
+ }
+ else
+ {
+ Ans.Class = "ANS_0";
+ Ans.Str = "•";
+ }
+
+ HTM_TD_Begin ("class=\"%s CT\" title=\"%s\"",
+ Ans.Class,Txt_TST_Answer_given_by_the_user);
+ HTM_Txt (Ans.Str);
+ HTM_TD_End ();
+ }
+ else // This answer has NOT been selected by the user
+ HTM_TD_Empty (1);
+
+ /* Draw icon that indicates whether the answer is correct */
+ if (TstVis_IsVisibleCorrectAns (Visibility))
+ {
+ if (Question->Answer.Options[Indexes[NumOpt]].Correct)
+ {
+ HTM_TD_Begin ("class=\"ANS_0 CT\" title=\"%s\"",
+ Txt_TST_Answer_given_by_the_teachers);
+ HTM_Txt ("•");
+ HTM_TD_End ();
+ }
+ else
+ HTM_TD_Empty (1);
+ }
+ else
+ {
+ HTM_TD_Begin ("class=\"ANS_0 CT\"");
+ Ico_PutIconNotVisible ();
+ HTM_TD_End ();
+ }
+
+ /* Answer letter (a, b, c,...) */
+ HTM_TD_Begin ("class=\"ANS_TXT LT\"");
+ HTM_TxtF ("%c) ",'a' + (char) NumOpt);
+ HTM_TD_End ();
+
+ /* Answer text and feedback */
+ HTM_TD_Begin ("class=\"LT\"");
+
+ HTM_DIV_Begin ("class=\"ANS_TXT\"");
+ if (TstVis_IsVisibleQstAndAnsTxt (Visibility))
+ {
+ HTM_Txt (Question->Answer.Options[Indexes[NumOpt]].Text);
+ Med_ShowMedia (&Question->Answer.Options[Indexes[NumOpt]].Media,
+ "TEST_MED_SHOW_CONT",
+ "TEST_MED_SHOW");
+ }
+ else
+ Ico_PutIconNotVisible ();
+ HTM_DIV_End ();
+
+ if (TstVis_IsVisibleCorrectAns (Visibility))
+ if (Question->Answer.Options[Indexes[NumOpt]].Feedback)
+ if (Question->Answer.Options[Indexes[NumOpt]].Feedback[0])
+ {
+ HTM_DIV_Begin ("class=\"TEST_EXA_LIGHT\"");
+ HTM_Txt (Question->Answer.Options[Indexes[NumOpt]].Feedback);
+ HTM_DIV_End ();
+ }
+
+ HTM_TD_End ();
+
+ HTM_TR_End ();
+ }
+
+ /***** Write the score of this question *****/
+ if (TstVis_IsVisibleEachQstScore (Visibility))
+ {
+ TstExa_WriteScoreStart (4);
+ if (Exam->Questions[NumQst].Score == 0.0)
+ HTM_SPAN_Begin ("class=\"ANS_0\"");
+ else if (Exam->Questions[NumQst].Score > 0.0)
+ HTM_SPAN_Begin ("class=\"ANS_OK\"");
+ else
+ HTM_SPAN_Begin ("class=\"ANS_BAD\"");
+ HTM_Double2Decimals (Exam->Questions[NumQst].Score);
+ HTM_SPAN_End ();
+ TstExa_WriteScoreEnd ();
+ }
+
+ /***** End table *****/
+ HTM_TABLE_End ();
+ }
+
+/*****************************************************************************/
+/***************** Write text answer when assessing a test *******************/
+/*****************************************************************************/
+
+static void TstExa_WriteTextAnsExam (struct UsrData *UsrDat,
+ const struct TstExa_Exam *Exam,
+ unsigned NumQst,
+ struct Tst_Question *Question,
+ MYSQL_RES *mysql_res,
+ unsigned Visibility)
+ {
+ unsigned NumOpt;
+ MYSQL_ROW row;
+ char TextAnsUsr[TstExa_MAX_BYTES_ANSWERS_ONE_QST + 1];
+ char TextAnsOK[TstExa_MAX_BYTES_ANSWERS_ONE_QST + 1];
+ bool Correct = false;
+ /*
+ row[0] AnsInd
+ row[1] Answer
+ row[2] Feedback
+ row[3] MedCod
+ row[4] Correct
+ */
+ /***** Get text and correctness of answers for this question from database (one row per answer) *****/
+ for (NumOpt = 0;
+ NumOpt < Question->Answer.NumOptions;
+ NumOpt++)
+ {
+ /***** Get next answer *****/
+ row = mysql_fetch_row (mysql_res);
+
+ /***** Allocate memory for text in this choice answer *****/
+ if (!Tst_AllocateTextChoiceAnswer (Question,NumOpt))
+ /* Abort on error */
+ Ale_ShowAlertsAndExit ();
+
+ /***** Copy answer text (row[1]) and convert it, that is in HTML, to rigorous HTML ******/
+ Str_Copy (Question->Answer.Options[NumOpt].Text,row[1],
+ Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
+ Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
+ Question->Answer.Options[NumOpt].Text,
+ Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false);
+
+ /***** Copy answer feedback (row[2]) and convert it, that is in HTML, to rigorous HTML ******/
+ if (TstVis_IsVisibleFeedbackTxt (Visibility))
+ if (row[2])
+ if (row[2][0])
+ {
+ Str_Copy (Question->Answer.Options[NumOpt].Feedback,row[2],
+ Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
+ Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
+ Question->Answer.Options[NumOpt].Feedback,
+ Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false);
+ }
+
+ /***** Assign correctness (row[4]) of this answer (this option) *****/
+ Question->Answer.Options[NumOpt].Correct = (row[4][0] == 'Y');
+ }
+
+ /***** Header with the title of each column *****/
+ HTM_TABLE_BeginPadding (2);
+ HTM_TR_Begin (NULL);
+ TstExa_WriteHeadUserCorrect (UsrDat);
+ HTM_TR_End ();
+
+ HTM_TR_Begin (NULL);
+
+ /***** Write the user answer *****/
+ if (Exam->Questions[NumQst].StrAnswers[0]) // If user has answered the question
+ {
+ /* Filter the user answer */
+ Str_Copy (TextAnsUsr,Exam->Questions[NumQst].StrAnswers,
+ TstExa_MAX_BYTES_ANSWERS_ONE_QST);
+
+ /* In order to compare student answer to stored answer,
+ the text answers are stored avoiding two or more consecurive spaces */
+ Str_ReplaceSeveralSpacesForOne (TextAnsUsr);
+
+ Str_ConvertToComparable (TextAnsUsr);
+
+ for (NumOpt = 0;
+ NumOpt < Question->Answer.NumOptions;
+ NumOpt++)
+ {
+ /* Filter this correct answer */
+ Str_Copy (TextAnsOK,Question->Answer.Options[NumOpt].Text,
+ TstExa_MAX_BYTES_ANSWERS_ONE_QST);
+ Str_ConvertToComparable (TextAnsOK);
+
+ /* Check is user answer is correct */
+ if (!strcoll (TextAnsUsr,TextAnsOK))
+ {
+ Correct = true;
+ break;
+ }
+ }
+ HTM_TD_Begin ("class=\"%s CT\"",
+ TstVis_IsVisibleCorrectAns (Visibility) ?
+ (Correct ? "ANS_OK" :
+ "ANS_BAD") :
+ "ANS_0");
+ HTM_Txt (Exam->Questions[NumQst].StrAnswers);
+ }
+ else // If user has omitted the answer
+ HTM_TD_Begin (NULL);
+ HTM_TD_End ();
+
+ /***** Write the correct answers *****/
+ if (TstVis_IsVisibleQstAndAnsTxt (Visibility) &&
+ TstVis_IsVisibleCorrectAns (Visibility))
+ {
+ HTM_TD_Begin ("class=\"CT\"");
+ HTM_TABLE_BeginPadding (2);
+
+ for (NumOpt = 0;
+ NumOpt < Question->Answer.NumOptions;
+ NumOpt++)
+ {
+ HTM_TR_Begin (NULL);
+
+ /* Answer letter (a, b, c,...) */
+ HTM_TD_Begin ("class=\"ANS_0 LT\"");
+ HTM_TxtF ("%c) ",'a' + (char) NumOpt);
+ HTM_TD_End ();
+
+ /* Answer text and feedback */
+ HTM_TD_Begin ("class=\"LT\"");
+
+ HTM_DIV_Begin ("class=\"ANS_0\"");
+ HTM_Txt (Question->Answer.Options[NumOpt].Text);
+ HTM_DIV_End ();
+
+ if (TstVis_IsVisibleFeedbackTxt (Visibility))
+ if (Question->Answer.Options[NumOpt].Feedback)
+ if (Question->Answer.Options[NumOpt].Feedback[0])
+ {
+ HTM_DIV_Begin ("class=\"TEST_EXA_LIGHT\"");
+ HTM_Txt (Question->Answer.Options[NumOpt].Feedback);
+ HTM_DIV_End ();
+ }
+
+ HTM_TD_End ();
+
+ HTM_TR_End ();
+ }
+
+ HTM_TABLE_End ();
+ HTM_TD_End ();
+ }
+ else
+ {
+ HTM_TD_Begin ("class=\"ANS_0 CT\"");
+ Ico_PutIconNotVisible ();
+ HTM_TD_End ();
+ }
+ HTM_TR_End ();
+
+ /***** Write the score of this question *****/
+ if (TstVis_IsVisibleEachQstScore (Visibility))
+ {
+ TstExa_WriteScoreStart (4);
+ if (!Exam->Questions[NumQst].StrAnswers[0]) // If user has omitted the answer
+ {
+ HTM_SPAN_Begin ("class=\"ANS_0\"");
+ HTM_Double2Decimals (0.0);
+ }
+ else if (Correct) // If correct
+ {
+ HTM_SPAN_Begin ("class=\"ANS_OK\"");
+ HTM_Double2Decimals (1.0);
+ }
+ else // If wrong
+ {
+ HTM_SPAN_Begin ("class=\"ANS_BAD\"");
+ HTM_Double2Decimals (0.0);
+ }
+ HTM_SPAN_End ();
+ TstExa_WriteScoreEnd ();
+ }
+
+ HTM_TABLE_End ();
+ }
+
+/*****************************************************************************/
+/********* Write head with two columns: ********/
+/********* one for the user's answer and other for the correct answer ********/
+/*****************************************************************************/
+
+static void TstExa_WriteHeadUserCorrect (struct UsrData *UsrDat)
+ {
+ extern const char *Txt_User[Usr_NUM_SEXS];
+ extern const char *Txt_ROLES_PLURAL_Abc[Rol_NUM_ROLES][Usr_NUM_SEXS];
+
+ HTM_TD_Begin ("class=\"DAT_SMALL CM\"");
+ HTM_Txt (Txt_User[UsrDat->Sex]);
+ HTM_TD_End ();
+
+ HTM_TD_Begin ("class=\"DAT_SMALL CM\"");
+ HTM_Txt (Txt_ROLES_PLURAL_Abc[Rol_TCH][Usr_SEX_UNKNOWN]);
+ HTM_TD_End ();
+ }
+
+/*****************************************************************************/
+/*********** Write the start ans the end of the score of an answer ***********/
+/*****************************************************************************/
+
+static void TstExa_WriteScoreStart (unsigned ColSpan)
+ {
+ extern const char *Txt_Score;
+
+ HTM_TR_Begin (NULL);
+ HTM_TD_Begin ("colspan=\"%u\" class=\"DAT_SMALL LM\"",ColSpan);
+ HTM_TxtColonNBSP (Txt_Score);
+ }
+
+static void TstExa_WriteScoreEnd (void)
+ {
+ HTM_TD_End ();
+ HTM_TR_End ();
+ }
+
+/*****************************************************************************/
+/************* Store user's answers of an test exam into database ************/
+/*****************************************************************************/
+
+static void TstExa_StoreOneExamQstInDB (const struct TstExa_Exam *Exam,
+ unsigned NumQst)
+ {
+ char StrIndexes[TstExa_MAX_BYTES_INDEXES_ONE_QST + 1];
+ char StrAnswers[TstExa_MAX_BYTES_ANSWERS_ONE_QST + 1];
+
+ /***** Replace each separator of multiple parameters by a comma *****/
+ /* In database commas are used as separators instead of special chars */
+ Par_ReplaceSeparatorMultipleByComma (Exam->Questions[NumQst].StrIndexes,StrIndexes);
+ Par_ReplaceSeparatorMultipleByComma (Exam->Questions[NumQst].StrAnswers,StrAnswers);
+
+ /***** Insert question and user's answers into database *****/
+ Str_SetDecimalPointToUS (); // To print the floating point as a dot
+ DB_QueryREPLACE ("can not update a question of a test exam",
+ "REPLACE INTO tst_exam_questions"
+ " (ExaCod,QstCod,QstInd,Score,Indexes,Answers)"
+ " VALUES"
+ " (%ld,%ld,%u,'%.15lg','%s','%s')",
+ Exam->ExaCod,Exam->Questions[NumQst].QstCod,
+ NumQst, // 0, 1, 2, 3...
+ Exam->Questions[NumQst].Score,
+ StrIndexes,
+ StrAnswers);
+ Str_SetDecimalPointToLocal (); // Return to local system
+ }
+
+/*****************************************************************************/
+/*********************** Update the score of a question **********************/
+/*****************************************************************************/
+
+static void Tst_UpdateQstScoreInDB (const struct TstExa_Exam *Exam,unsigned NumQst)
+ {
+ /***** Update number of clicks and score of the question *****/
+ Str_SetDecimalPointToUS (); // To print the floating point as a dot
+ if (Exam->Questions[NumQst].AnswerIsNotBlank)
+ DB_QueryUPDATE ("can not update the score of a question",
+ "UPDATE tst_questions"
+ " SET NumHits=NumHits+1,NumHitsNotBlank=NumHitsNotBlank+1,"
+ "Score=Score+(%.15lg)"
+ " WHERE QstCod=%ld",
+ Exam->Questions[NumQst].Score,
+ Exam->Questions[NumQst].QstCod);
+ else // The answer is blank
+ DB_QueryUPDATE ("can not update the score of a question",
+ "UPDATE tst_questions"
+ " SET NumHits=NumHits+1"
+ " WHERE QstCod=%ld",
+ Exam->Questions[NumQst].QstCod);
+ Str_SetDecimalPointToLocal (); // Return to local system
+ }
+
+/*****************************************************************************/
+/************* Select users and dates to show their test exams ***************/
+/*****************************************************************************/
+
+void TstExa_SelUsrsToViewUsrsExams (void)
+ {
+ extern const char *Hlp_ASSESSMENT_Tests_results;
+ extern const char *Txt_Results;
+ extern const char *Txt_View_test_results;
+
+ Usr_PutFormToSelectUsrsToGoToAct (&Gbl.Usrs.Selected,
+ ActSeeUsrTstRes,
+ NULL,NULL,
+ Txt_Results,
+ Hlp_ASSESSMENT_Tests_results,
+ Txt_View_test_results,
+ true); // Put form with date range
+ }
+
+/*****************************************************************************/
+/******************** Select dates to show my test exams *********************/
+/*****************************************************************************/
+
+void TstExa_SelDatesToSeeMyExams (void)
+ {
+ extern const char *Hlp_ASSESSMENT_Tests_results;
+ extern const char *Txt_Results;
+ extern const char *Txt_View_test_results;
+ static const Dat_SetHMS SetHMS[Dat_NUM_START_END_TIME] =
+ {
+ Dat_HMS_DO_NOT_SET,
+ Dat_HMS_DO_NOT_SET
+ };
+
+ /***** Begin form *****/
+ Frm_StartForm (ActSeeMyTstRes);
+
+ /***** Begin box and table *****/
+ Box_BoxTableBegin (NULL,Txt_Results,
+ NULL,NULL,
+ Hlp_ASSESSMENT_Tests_results,Box_NOT_CLOSABLE,2);
+ Dat_PutFormStartEndClientLocalDateTimesWithYesterdayToday (SetHMS);
+
+ /***** End table, send button and end box *****/
+ Box_BoxTableWithButtonEnd (Btn_CONFIRM_BUTTON,Txt_View_test_results);
+
+ /***** End form *****/
+ Frm_EndForm ();
+ }
+
+/*****************************************************************************/
+/***************************** Show my test exams ****************************/
+/*****************************************************************************/
+
+void TstExa_ShowMyExams (void)
+ {
+ extern const char *Hlp_ASSESSMENT_Tests_results;
+ extern const char *Txt_Results;
+
+ /***** Get starting and ending dates *****/
+ Dat_GetIniEndDatesFromForm ();
+
+ /***** Begin box and table *****/
+ Box_BoxTableBegin (NULL,Txt_Results,
+ NULL,NULL,
+ Hlp_ASSESSMENT_Tests_results,Box_NOT_CLOSABLE,2);
+
+ /***** Header of the table with the list of users *****/
+ TstExa_ShowHeaderExams ();
+
+ /***** List my test exams *****/
+ TstCfg_GetConfigFromDB (); // To get feedback type
+ TstExa_ShowExams (&Gbl.Usrs.Me.UsrDat);
+
+ /***** End table and box *****/
+ Box_BoxTableEnd ();
+ }
+
+/*****************************************************************************/
+/******************** Get users and show their test exams ********************/
+/*****************************************************************************/
+
+void TstExa_GetUsrsAndShowExams (void)
+ {
+ Usr_GetSelectedUsrsAndGoToAct (&Gbl.Usrs.Selected,
+ TstExa_ShowUsrsExams,
+ TstExa_SelUsrsToViewUsrsExams);
+ }
+
+/*****************************************************************************/
+/********************* Show test exams for several users *********************/
+/*****************************************************************************/
+
+static void TstExa_ShowUsrsExams (void)
+ {
+ extern const char *Hlp_ASSESSMENT_Tests_results;
+ extern const char *Txt_Results;
+ const char *Ptr;
+
+ /***** Get starting and ending dates *****/
+ Dat_GetIniEndDatesFromForm ();
+
+ /***** Begin box and table *****/
+ Box_BoxTableBegin (NULL,Txt_Results,
+ NULL,NULL,
+ Hlp_ASSESSMENT_Tests_results,Box_NOT_CLOSABLE,2);
+
+ /***** Header of the table with the list of users *****/
+ TstExa_ShowHeaderExams ();
+
+ /***** List the test exams of the selected users *****/
+ Ptr = Gbl.Usrs.Selected.List[Rol_UNK];
+ while (*Ptr)
+ {
+ Par_GetNextStrUntilSeparParamMult (&Ptr,Gbl.Usrs.Other.UsrDat.EncryptedUsrCod,
+ Cry_BYTES_ENCRYPTED_STR_SHA256_BASE64);
+ Usr_GetUsrCodFromEncryptedUsrCod (&Gbl.Usrs.Other.UsrDat);
+ if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat,Usr_DONT_GET_PREFS))
+ if (Usr_CheckIfICanViewTst (&Gbl.Usrs.Other.UsrDat))
+ {
+ /***** Show test exams *****/
+ Gbl.Usrs.Other.UsrDat.Accepted = Usr_CheckIfUsrHasAcceptedInCurrentCrs (&Gbl.Usrs.Other.UsrDat);
+ TstExa_ShowExams (&Gbl.Usrs.Other.UsrDat);
+ }
+ }
+
+ /***** End table and box *****/
+ Box_BoxTableEnd ();
+ }
+
+/*****************************************************************************/
+/************************ Show header of my test exams ***********************/
+/*****************************************************************************/
+
+static void TstExa_ShowHeaderExams (void)
+ {
+ extern const char *Txt_User[Usr_NUM_SEXS];
+ extern const char *Txt_START_END_TIME[Dat_NUM_START_END_TIME];
+ extern const char *Txt_Questions;
+ extern const char *Txt_Non_blank_BR_questions;
+ extern const char *Txt_Score;
+ extern const char *Txt_Average_BR_score_BR_per_question_BR_from_0_to_1;
+ extern const char *Txt_Grade;
+
+ HTM_TR_Begin (NULL);
+
+ HTM_TH (1,2,"CT",Txt_User[Usr_SEX_UNKNOWN]);
+ HTM_TH (1,1,"LT",Txt_START_END_TIME[Dat_START_TIME]);
+ HTM_TH (1,1,"LT",Txt_START_END_TIME[Dat_END_TIME ]);
+ HTM_TH (1,1,"RT",Txt_Questions);
+ HTM_TH (1,1,"RT",Txt_Non_blank_BR_questions);
+ HTM_TH (1,1,"RT",Txt_Score);
+ HTM_TH (1,1,"RT",Txt_Average_BR_score_BR_per_question_BR_from_0_to_1);
+ HTM_TH (1,1,"RT",Txt_Grade);
+ HTM_TH_Empty (1);
+
+ HTM_TR_End ();
+ }
+
+/*****************************************************************************/
+/************ Show the test exams of a user in the current course ************/
+/*****************************************************************************/
+
+static void TstExa_ShowExams (struct UsrData *UsrDat)
+ {
+ extern const char *Txt_View_test;
+ MYSQL_RES *mysql_res;
+ MYSQL_ROW row;
+ unsigned NumExams;
+ unsigned NumExam;
+ static unsigned UniqueId = 0;
+ Dat_StartEndTime_t StartEndTime;
+ char *Id;
+ struct TstExa_Exam Exam;
+ unsigned NumTotalQsts = 0;
+ unsigned NumTotalQstsNotBlank = 0;
+ double TotalScoreOfAllTests = 0.0;
+ unsigned NumExamsVisibleByTchs = 0;
+ bool ItsMe = Usr_ItsMe (UsrDat->UsrCod);
+ bool ICanViewTest;
+ bool ICanViewScore;
+ char *ClassDat;
+
+ /***** Make database query *****/
+ /* From here... ...to here
+ ___________|_____ _____|___________
+ -----|______Exam_|_____|-----------------|_____|_Exam______|-----> time
+ Start | End Start | End
+ */
+ NumExams =
+ (unsigned) DB_QuerySELECT (&mysql_res,"can not get test exams of a user",
+ "SELECT ExaCod," // row[0]
+ "UNIX_TIMESTAMP(StartTime)," // row[1]
+ "UNIX_TIMESTAMP(EndTime)," // row[2]
+ "NumQsts," // row[3]
+ "NumQstsNotBlank," // row[4]
+ "AllowTeachers," // row[5]
+ "Score" // row[6]
+ " FROM tst_exams"
+ " WHERE CrsCod=%ld AND UsrCod=%ld"
+ " AND EndTime>=FROM_UNIXTIME(%ld)"
+ " AND StartTime<=FROM_UNIXTIME(%ld)"
+ " ORDER BY ExaCod",
+ Gbl.Hierarchy.Crs.CrsCod,
+ UsrDat->UsrCod,
+ (long) Gbl.DateRange.TimeUTC[Dat_START_TIME],
+ (long) Gbl.DateRange.TimeUTC[Dat_END_TIME ]);
+
+ /***** Show user's data *****/
+ HTM_TR_Begin (NULL);
+ Usr_ShowTableCellWithUsrData (UsrDat,NumExams);
+
+ /***** Get and print test exams *****/
+ if (NumExams)
+ {
+ for (NumExam = 0;
+ NumExam < NumExams;
+ NumExam++)
+ {
+ row = mysql_fetch_row (mysql_res);
+
+ /* Get test code (row[0]) */
+ if ((Exam.ExaCod = Str_ConvertStrCodToLongCod (row[0])) < 0)
+ Lay_ShowErrorAndExit ("Wrong code of test exam.");
+
+ /* Get if teachers are allowed to see this test exams (row[5]) */
+ Exam.AllowTeachers = (row[5][0] == 'Y');
+ ClassDat = Exam.AllowTeachers ? "DAT" :
+ "DAT_LIGHT";
+
+ switch (Gbl.Usrs.Me.Role.Logged)
+ {
+ case Rol_STD:
+ ICanViewTest = ItsMe;
+ ICanViewScore = ItsMe &&
+ TstVis_IsVisibleTotalScore (TstCfg_GetConfigVisibility ());
+ break;
+ case Rol_NET:
+ case Rol_TCH:
+ case Rol_DEG_ADM:
+ case Rol_CTR_ADM:
+ case Rol_INS_ADM:
+ ICanViewTest =
+ ICanViewScore = ItsMe ||
+ Exam.AllowTeachers;
+ break;
+ case Rol_SYS_ADM:
+ ICanViewTest =
+ ICanViewScore = true;
+ break;
+ default:
+ ICanViewTest =
+ ICanViewScore = false;
+ break;
+ }
+
+ if (NumExam)
+ HTM_TR_Begin (NULL);
+
+ /* Write date and time (row[1] and row[2] hold UTC date-times) */
+ Exam.TimeUTC[Dat_START_TIME] = Dat_GetUNIXTimeFromStr (row[1]);
+ Exam.TimeUTC[Dat_END_TIME ] = Dat_GetUNIXTimeFromStr (row[2]);
+ UniqueId++;
+ for (StartEndTime = (Dat_StartEndTime_t) 0;
+ StartEndTime <= (Dat_StartEndTime_t) (Dat_NUM_START_END_TIME - 1);
+ StartEndTime++)
+ {
+ if (asprintf (&Id,"tst_date_%u_%u",(unsigned) StartEndTime,UniqueId) < 0)
+ Lay_NotEnoughMemoryExit ();
+ HTM_TD_Begin ("id=\"%s\" class=\"%s LT COLOR%u\"",
+ Id,ClassDat,Gbl.RowEvenOdd);
+ Dat_WriteLocalDateHMSFromUTC (Id,Exam.TimeUTC[StartEndTime],
+ Gbl.Prefs.DateFormat,Dat_SEPARATOR_BREAK,
+ true,true,false,0x7);
+ HTM_TD_End ();
+ free (Id);
+ }
+
+ /* Get number of questions (row[3]) */
+ if (sscanf (row[3],"%u",&Exam.NumQsts) != 1)
+ Exam.NumQsts = 0;
+ if (Exam.AllowTeachers)
+ NumTotalQsts += Exam.NumQsts;
+
+ /* Get number of questions not blank (row[4]) */
+ if (sscanf (row[4],"%u",&Exam.NumQstsNotBlank) != 1)
+ Exam.NumQstsNotBlank = 0;
+ if (Exam.AllowTeachers)
+ NumTotalQstsNotBlank += Exam.NumQstsNotBlank;
+
+ /* Get score (row[6]) */
+ Str_SetDecimalPointToUS (); // To get the decimal point as a dot
+ if (sscanf (row[6],"%lf",&Exam.Score) != 1)
+ Exam.Score = 0.0;
+ Str_SetDecimalPointToLocal (); // Return to local system
+ if (Exam.AllowTeachers)
+ TotalScoreOfAllTests += Exam.Score;
+
+ /* Write number of questions */
+ HTM_TD_Begin ("class=\"%s RT COLOR%u\"",ClassDat,Gbl.RowEvenOdd);
+ if (ICanViewTest)
+ HTM_Unsigned (Exam.NumQsts);
+ HTM_TD_End ();
+
+ /* Write number of questions not blank */
+ HTM_TD_Begin ("class=\"%s RT COLOR%u\"",ClassDat,Gbl.RowEvenOdd);
+ if (ICanViewTest)
+ HTM_Unsigned (Exam.NumQstsNotBlank);
+ HTM_TD_End ();
+
+ /* Write score */
+ HTM_TD_Begin ("class=\"%s RT COLOR%u\"",ClassDat,Gbl.RowEvenOdd);
+ if (ICanViewScore)
+ HTM_Double2Decimals (Exam.Score);
+ HTM_TD_End ();
+
+ /* Write average score per question */
+ HTM_TD_Begin ("class=\"%s RT COLOR%u\"",ClassDat,Gbl.RowEvenOdd);
+ if (ICanViewScore)
+ HTM_Double2Decimals (Exam.NumQsts ? Exam.Score /
+ (double) Exam.NumQsts :
+ 0.0);
+ HTM_TD_End ();
+
+ /* Write grade */
+ HTM_TD_Begin ("class=\"%s RT COLOR%u\"",ClassDat,Gbl.RowEvenOdd);
+ if (ICanViewScore)
+ TstExa_ComputeAndShowGrade (Exam.NumQsts,
+ Exam.Score,
+ TstExa_SCORE_MAX);
+ HTM_TD_End ();
+
+ /* Link to show this test exam */
+ HTM_TD_Begin ("class=\"RT COLOR%u\"",Gbl.RowEvenOdd);
+ if (ICanViewTest)
+ {
+ Frm_StartForm (Gbl.Action.Act == ActSeeMyTstRes ? ActSeeOneTstResMe :
+ ActSeeOneTstResOth);
+ TstExa_PutParamExaCod (Exam.ExaCod);
+ Ico_PutIconLink ("tasks.svg",Txt_View_test);
+ Frm_EndForm ();
+ }
+ HTM_TD_End ();
+ HTM_TR_End ();
+
+ if (Exam.AllowTeachers)
+ NumExamsVisibleByTchs++;
+ }
+
+ /***** Write totals for this user *****/
+ TstExa_ShowExamsSummaryRow (ItsMe,NumExamsVisibleByTchs,
+ NumTotalQsts,NumTotalQstsNotBlank,
+ TotalScoreOfAllTests);
+ }
+ else
+ {
+ HTM_TD_ColouredEmpty (7);
+ HTM_TR_End ();
+ }
+
+ /***** Free structure that stores the query result *****/
+ DB_FreeMySQLResult (&mysql_res);
+
+ Gbl.RowEvenOdd = 1 - Gbl.RowEvenOdd;
+ }
+
+/*****************************************************************************/
+/****************** Write parameter with code of test exam *******************/
+/*****************************************************************************/
+
+void TstExa_PutParamExaCod (long ExaCod)
+ {
+ Par_PutHiddenParamLong (NULL,"ExaCod",ExaCod);
+ }
+
+/*****************************************************************************/
+/****************** Get parameter with code of test exam *********************/
+/*****************************************************************************/
+
+long TstExa_GetParamExaCod (void)
+ {
+ /***** Get code of exam *****/
+ return Par_GetParToLong ("ExaCod");
+ }
+
+/*****************************************************************************/
+/**************** Show row with summary of user's test exams *****************/
+/*****************************************************************************/
+
+static void TstExa_ShowExamsSummaryRow (bool ItsMe,
+ unsigned NumExams,
+ unsigned NumTotalQsts,
+ unsigned NumTotalQstsNotBlank,
+ double TotalScoreOfAllTests)
+ {
+ extern const char *Txt_Visible_tests;
+ bool ICanViewTotalScore;
+
+ switch (Gbl.Usrs.Me.Role.Logged)
+ {
+ case Rol_STD:
+ ICanViewTotalScore = ItsMe &&
+ TstVis_IsVisibleTotalScore (TstCfg_GetConfigVisibility ());
+ break;
+ case Rol_NET:
+ case Rol_TCH:
+ case Rol_DEG_ADM:
+ case Rol_CTR_ADM:
+ case Rol_INS_ADM:
+ ICanViewTotalScore = ItsMe ||
+ NumExams;
+ break;
+ case Rol_SYS_ADM:
+ ICanViewTotalScore = true;
+ break;
+ default:
+ ICanViewTotalScore = false;
+ break;
+ }
+
+ /***** Start row *****/
+ HTM_TR_Begin (NULL);
+
+ /***** Row title *****/
+ HTM_TD_Begin ("colspan=\"2\" class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd);
+ HTM_TxtColonNBSP (Txt_Visible_tests);
+ HTM_Unsigned (NumExams);
+ HTM_TD_End ();
+
+ /***** Write total number of questions *****/
+ HTM_TD_Begin ("class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd);
+ if (NumExams)
+ HTM_Unsigned (NumTotalQsts);
+ HTM_TD_End ();
+
+ /***** Write total number of questions not blank *****/
+ HTM_TD_Begin ("class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd);
+ if (NumExams)
+ HTM_Unsigned (NumTotalQstsNotBlank);
+ HTM_TD_End ();
+
+ /***** Write total score *****/
+ HTM_TD_Begin ("class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd);
+ if (ICanViewTotalScore)
+ HTM_Double2Decimals (TotalScoreOfAllTests);
+ HTM_TD_End ();
+
+ /***** Write average score per question *****/
+ HTM_TD_Begin ("class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd);
+ if (ICanViewTotalScore)
+ HTM_Double2Decimals (NumTotalQsts ? TotalScoreOfAllTests / (double) NumTotalQsts :
+ 0.0);
+ HTM_TD_End ();
+
+ /***** Write score over Tst_SCORE_MAX *****/
+ HTM_TD_Begin ("class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd);
+ if (ICanViewTotalScore)
+ TstExa_ComputeAndShowGrade (NumTotalQsts,
+ TotalScoreOfAllTests,
+ TstExa_SCORE_MAX);
+ HTM_TD_End ();
+
+ /***** Last cell *****/
+ HTM_TD_Begin ("class=\"DAT_N_LINE_TOP COLOR%u\"",Gbl.RowEvenOdd);
+ HTM_TD_End ();
+
+ /***** End row *****/
+ HTM_TR_End ();
+ }
+
+/*****************************************************************************/
+/******************** Show one test exam of another user *********************/
+/*****************************************************************************/
+
+void TstExa_ShowOneExam (void)
+ {
+ extern const char *Hlp_ASSESSMENT_Tests_results;
+ extern const char *Txt_Test_result;
+ extern const char *Txt_The_user_does_not_exist;
+ extern const char *Txt_ROLES_SINGUL_Abc[Rol_NUM_ROLES][Usr_NUM_SEXS];
+ extern const char *Txt_START_END_TIME[Dat_NUM_START_END_TIME];
+ extern const char *Txt_Questions;
+ extern const char *Txt_non_blank_QUESTIONS;
+ extern const char *Txt_Score;
+ extern const char *Txt_Grade;
+ extern const char *Txt_Tags;
+ struct TstExa_Exam Exam;
+ bool ShowPhoto;
+ char PhotoURL[PATH_MAX + 1];
+ Dat_StartEndTime_t StartEndTime;
+ char *Id;
+ bool ItsMe;
+ bool ICanViewTest;
+ bool ICanViewScore;
+
+ /***** Get the code of the test *****/
+ if ((Exam.ExaCod = TstExa_GetParamExaCod ()) == -1L)
+ Lay_ShowErrorAndExit ("Code of test is missing.");
+
+ /***** Get test exam data *****/
+ TstExa_GetExamDataByExaCod (&Exam);
+ TstCfg_SetConfigVisibility (TstVis_MAX_VISIBILITY);
+
+ /***** Check if I can view this test exam *****/
+ ItsMe = Usr_ItsMe (Gbl.Usrs.Other.UsrDat.UsrCod);
+ switch (Gbl.Usrs.Me.Role.Logged)
+ {
+ case Rol_STD:
+ ICanViewTest = ItsMe;
+ if (ItsMe)
+ {
+ TstCfg_GetConfigFromDB (); // To get feedback type
+ ICanViewScore = TstVis_IsVisibleTotalScore (TstCfg_GetConfigVisibility ());
+ }
+ else
+ ICanViewScore = false;
+ break;
+ case Rol_TCH:
+ case Rol_DEG_ADM:
+ case Rol_CTR_ADM:
+ case Rol_INS_ADM:
+ switch (Gbl.Action.Act)
+ {
+ case ActSeeOneTstResMe:
+ ICanViewTest =
+ ICanViewScore = ItsMe;
+ break;
+ case ActSeeOneTstResOth:
+ ICanViewTest =
+ ICanViewScore = ItsMe ||
+ Exam.AllowTeachers;
+ break;
+ default:
+ ICanViewTest =
+ ICanViewScore = false;
+ break;
+ }
+ break;
+ case Rol_SYS_ADM:
+ ICanViewTest =
+ ICanViewScore = true;
+ break;
+ default:
+ ICanViewTest =
+ ICanViewScore = false;
+ break;
+ }
+
+ if (ICanViewTest) // I am allowed to view this test exam
+ {
+ /***** Get questions and user's answers of the test exam from database *****/
+ TstExa_GetExamQuestionsFromDB (&Exam);
+
+ /***** Begin box *****/
+ Box_BoxBegin (NULL,Txt_Test_result,
+ NULL,NULL,
+ Hlp_ASSESSMENT_Tests_results,Box_NOT_CLOSABLE);
+ Lay_WriteHeaderClassPhoto (false,false,
+ Gbl.Hierarchy.Ins.InsCod,
+ Gbl.Hierarchy.Deg.DegCod,
+ Gbl.Hierarchy.Crs.CrsCod);
+
+ /***** Begin table *****/
+ HTM_TABLE_BeginWideMarginPadding (5);
+
+ /***** Header row *****/
+ /* Get data of the user who made the test */
+ if (!Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat,Usr_DONT_GET_PREFS))
+ Lay_ShowErrorAndExit (Txt_The_user_does_not_exist);
+ if (!Usr_CheckIfICanViewTst (&Gbl.Usrs.Other.UsrDat))
+ Lay_NoPermissionExit ();
+
+ /* User */
+ HTM_TR_Begin (NULL);
+
+ HTM_TD_Begin ("class=\"DAT_N RT\"");
+ HTM_TxtF ("%s:",Txt_ROLES_SINGUL_Abc[Gbl.Usrs.Other.UsrDat.Roles.InCurrentCrs.Role][Gbl.Usrs.Other.UsrDat.Sex]);
+ HTM_TD_End ();
+
+ HTM_TD_Begin ("class=\"DAT LT\"");
+ ID_WriteUsrIDs (&Gbl.Usrs.Other.UsrDat,NULL);
+ HTM_TxtF (" %s",Gbl.Usrs.Other.UsrDat.Surname1);
+ if (Gbl.Usrs.Other.UsrDat.Surname2[0])
+ HTM_TxtF (" %s",Gbl.Usrs.Other.UsrDat.Surname2);
+ if (Gbl.Usrs.Other.UsrDat.FirstName[0])
+ HTM_TxtF (", %s",Gbl.Usrs.Other.UsrDat.FirstName);
+ HTM_BR ();
+ ShowPhoto = Pho_ShowingUsrPhotoIsAllowed (&Gbl.Usrs.Other.UsrDat,PhotoURL);
+ Pho_ShowUsrPhoto (&Gbl.Usrs.Other.UsrDat,ShowPhoto ? PhotoURL :
+ NULL,
+ "PHOTO45x60",Pho_ZOOM,false);
+ HTM_TD_End ();
+
+ HTM_TR_End ();
+
+ /* Test date */
+ for (StartEndTime = (Dat_StartEndTime_t) 0;
+ StartEndTime <= (Dat_StartEndTime_t) (Dat_NUM_START_END_TIME - 1);
+ StartEndTime++)
+ {
+ if (asprintf (&Id,"tst_date_%u",(unsigned) StartEndTime) < 0)
+ Lay_NotEnoughMemoryExit ();
+
+ HTM_TR_Begin (NULL);
+
+ HTM_TD_Begin ("class=\"DAT_N RT\"");
+ HTM_TxtF ("%s:",Txt_START_END_TIME[StartEndTime]);
+ HTM_TD_End ();
+
+ HTM_TD_Begin ("id=\"%s\" class=\"DAT LT\"",Id);
+ Dat_WriteLocalDateHMSFromUTC (Id,Exam.TimeUTC[StartEndTime],
+ Gbl.Prefs.DateFormat,Dat_SEPARATOR_COMMA,
+ true,true,true,0x7);
+ HTM_TD_End ();
+
+ HTM_TR_End ();
+
+ free (Id);
+ }
+
+ /* Number of questions */
+ HTM_TR_Begin (NULL);
+
+ HTM_TD_Begin ("class=\"DAT_N RT\"");
+ HTM_TxtF ("%s:",Txt_Questions);
+ HTM_TD_End ();
+
+ HTM_TD_Begin ("class=\"DAT LT\"");
+ HTM_TxtF ("%u (%u %s)",
+ Exam.NumQsts,
+ Exam.NumQstsNotBlank,Txt_non_blank_QUESTIONS);
+ HTM_TD_End ();
+
+ HTM_TR_End ();
+
+ /* Score */
+ HTM_TR_Begin (NULL);
+
+ HTM_TD_Begin ("class=\"DAT_N RT\"");
+ HTM_TxtF ("%s:",Txt_Score);
+ HTM_TD_End ();
+
+ HTM_TD_Begin ("class=\"DAT LT\"");
+ if (ICanViewScore)
+ HTM_Double2Decimals (Exam.Score);
+ else
+ Ico_PutIconNotVisible ();
+ HTM_TD_End ();
+
+ /* Grade */
+ HTM_TR_Begin (NULL);
+
+ HTM_TD_Begin ("class=\"DAT_N RT\"");
+ HTM_TxtF ("%s:",Txt_Grade);
+ HTM_TD_End ();
+
+ HTM_TD_Begin ("class=\"DAT LT\"");
+ if (ICanViewScore)
+ TstExa_ComputeAndShowGrade (Exam.NumQsts,
+ Exam.Score,
+ TstExa_SCORE_MAX);
+ else
+ Ico_PutIconNotVisible ();
+ HTM_TD_End ();
+
+ HTM_TR_End ();
+
+ /* Tags present in this test */
+ HTM_TR_Begin (NULL);
+
+ HTM_TD_Begin ("class=\"DAT_N RT\"");
+ HTM_TxtF ("%s:",Txt_Tags);
+ HTM_TD_End ();
+
+ HTM_TD_Begin ("class=\"DAT LT\"");
+ TstExa_ShowTagsPresentInAnExam (Exam.ExaCod);
+ HTM_TD_End ();
+
+ HTM_TR_End ();
+
+ /***** Write answers and solutions *****/
+ TstExa_ShowExamAnswers (&Gbl.Usrs.Other.UsrDat,
+ &Exam,
+ TstCfg_GetConfigVisibility ());
+
+ /***** End table *****/
+ HTM_TABLE_End ();
+
+ /***** Write total mark of test *****/
+ if (ICanViewScore)
+ {
+ HTM_DIV_Begin ("class=\"DAT_N_BOLD CM\"");
+ HTM_TxtColonNBSP (Txt_Score);
+ HTM_Double2Decimals (Exam.Score);
+ HTM_BR ();
+ HTM_TxtColonNBSP (Txt_Grade);
+ TstExa_ComputeAndShowGrade (Exam.NumQsts,
+ Exam.Score,
+ TstExa_SCORE_MAX);
+ HTM_DIV_End ();
+ }
+
+ /***** End box *****/
+ Box_BoxEnd ();
+ }
+ else // I am not allowed to view this test exam
+ Lay_NoPermissionExit ();
+ }
+
+/*****************************************************************************/
+/********************* Show test tags in this test exam **********************/
+/*****************************************************************************/
+
+static void TstExa_ShowTagsPresentInAnExam (long ExaCod)
+ {
+ MYSQL_RES *mysql_res;
+ unsigned NumTags;
+
+ /***** Get all tags of questions in this test *****/
+ NumTags = (unsigned)
+ DB_QuerySELECT (&mysql_res,"can not get tags"
+ " present in a test exam",
+ "SELECT tst_tags.TagTxt" // row[0]
+ " FROM"
+ " (SELECT DISTINCT(tst_question_tags.TagCod)"
+ " FROM tst_question_tags,tst_exam_questions"
+ " WHERE tst_exam_questions.ExaCod=%ld"
+ " AND tst_exam_questions.QstCod=tst_question_tags.QstCod)"
+ " AS TagsCods,tst_tags"
+ " WHERE TagsCods.TagCod=tst_tags.TagCod"
+ " ORDER BY tst_tags.TagTxt",
+ ExaCod);
+ Tst_ShowTagList (NumTags,mysql_res);
+
+ /***** Free structure that stores the query result *****/
+ DB_FreeMySQLResult (&mysql_res);
+ }
+
+/*****************************************************************************/
+/************** Show user's and correct answers of a test exam ***************/
+/*****************************************************************************/
+
+void TstExa_ShowExamAnswers (struct UsrData *UsrDat,
+ struct TstExa_Exam *Exam,
+ unsigned Visibility)
+ {
+ extern const char *Txt_Question_modified;
+ extern const char *Txt_Question_removed;
+ MYSQL_RES *mysql_res;
+ MYSQL_ROW row;
+ unsigned NumQst;
+ bool ThisQuestionHasBeenEdited;
+ time_t EditTimeUTC;
+
+ for (NumQst = 0;
+ NumQst < Exam->NumQsts;
+ NumQst++)
+ {
+ Gbl.RowEvenOdd = NumQst % 2;
+
+ /***** Query database *****/
+ if (Tst_GetOneQuestionByCod (Exam->Questions[NumQst].QstCod,&mysql_res)) // Question exists
+ {
+ /***** Get row of the result of the query *****/
+ row = mysql_fetch_row (mysql_res);
+
+ /***** If this question has been edited later than test time
+ ==> don't show question ****/
+ EditTimeUTC = Dat_GetUNIXTimeFromStr (row[0]);
+ ThisQuestionHasBeenEdited = false;
+ if (EditTimeUTC > Exam->TimeUTC[Dat_START_TIME])
+ ThisQuestionHasBeenEdited = true;
+
+ if (ThisQuestionHasBeenEdited)
+ {
+ /***** Question has been edited *****/
+ HTM_TR_Begin (NULL);
+
+ HTM_TD_Begin ("class=\"BIG_INDEX RT COLOR%u\"",Gbl.RowEvenOdd);
+ HTM_Unsigned (NumQst + 1);
+ HTM_TD_End ();
+
+ HTM_TD_Begin ("class=\"DAT_LIGHT LT COLOR%u\"",Gbl.RowEvenOdd);
+ HTM_Txt (Txt_Question_modified);
+ HTM_TD_End ();
+
+ HTM_TR_End ();
+ }
+ else
+ /***** Write questions and answers *****/
+ TstExa_WriteQstAndAnsExam (UsrDat,
+ Exam,
+ NumQst,
+ row,
+ Visibility);
+ }
+ else
+ {
+ /***** Question does not exists *****/
+ HTM_TR_Begin (NULL);
+
+ HTM_TD_Begin ("class=\"BIG_INDEX RT COLOR%u\"",Gbl.RowEvenOdd);
+ HTM_Unsigned (NumQst + 1);
+ HTM_TD_End ();
+
+ HTM_TD_Begin ("class=\"DAT_LIGHT LT COLOR%u\"",Gbl.RowEvenOdd);
+ HTM_Txt (Txt_Question_removed);
+ HTM_TD_End ();
+
+ HTM_TR_End ();
+ }
+
+ /***** Free structure that stores the query result *****/
+ DB_FreeMySQLResult (&mysql_res);
+ }
+ }
+
+/*****************************************************************************/
+/************ Get data of a test exam using its test exam code ***************/
+/*****************************************************************************/
+
+void TstExa_GetExamDataByExaCod (struct TstExa_Exam *Exam)
+ {
+ MYSQL_RES *mysql_res;
+ MYSQL_ROW row;
+
+ /***** Make database query *****/
+ if (DB_QuerySELECT (&mysql_res,"can not get data of a test exam",
+ "SELECT UsrCod," // row[0]
+ "UNIX_TIMESTAMP(StartTime)," // row[1]
+ "UNIX_TIMESTAMP(EndTime)," // row[2]
+ "NumQsts," // row[3]
+ "NumQstsNotBlank," // row[4]
+ "AllowTeachers," // row[5]
+ "Score" // row[6]
+ " FROM tst_exams"
+ " WHERE ExaCod=%ld AND CrsCod=%ld",
+ Exam->ExaCod,
+ Gbl.Hierarchy.Crs.CrsCod) == 1)
+ {
+ row = mysql_fetch_row (mysql_res);
+
+ /* Get user code (row[0]) */
+ Gbl.Usrs.Other.UsrDat.UsrCod = Str_ConvertStrCodToLongCod (row[0]);
+
+ /* Get date-time (row[1] and row[2] hold UTC date-time) */
+ Exam->TimeUTC[Dat_START_TIME] = Dat_GetUNIXTimeFromStr (row[1]);
+ Exam->TimeUTC[Dat_END_TIME ] = Dat_GetUNIXTimeFromStr (row[2]);
+
+ /* Get number of questions (row[3]) */
+ if (sscanf (row[3],"%u",&Exam->NumQsts) != 1)
+ Exam->NumQsts = 0;
+
+ /* Get number of questions not blank (row[4]) */
+ if (sscanf (row[4],"%u",&Exam->NumQstsNotBlank) != 1)
+ Exam->NumQstsNotBlank = 0;
+
+ /* Get if teachers are allowed to see this test exam (row[5]) */
+ Exam->AllowTeachers = (row[5][0] == 'Y');
+
+ /* Get score (row[6]) */
+ Str_SetDecimalPointToUS (); // To get the decimal point as a dot
+ if (sscanf (row[6],"%lf",&Exam->Score) != 1)
+ Exam->Score = 0.0;
+ Str_SetDecimalPointToLocal (); // Return to local system
+ }
+ else
+ {
+ Exam->TimeUTC[Dat_START_TIME] =
+ Exam->TimeUTC[Dat_END_TIME ] = 0;
+ Exam->NumQsts = 0;
+ Exam->NumQstsNotBlank = 0;
+ Exam->AllowTeachers = false;
+ Exam->Score = 0.0;
+ }
+
+ /***** Free structure that stores the query result *****/
+ DB_FreeMySQLResult (&mysql_res);
+ }
+
+/*****************************************************************************/
+/************* Get the questions of a test exam from database ****************/
+/*****************************************************************************/
+
+void TstExa_GetExamQuestionsFromDB (struct TstExa_Exam *Exam)
+ {
+ MYSQL_RES *mysql_res;
+ MYSQL_ROW row;
+ unsigned NumQst;
+
+ /***** Get questions of a test exam from database *****/
+ Exam->NumQsts =
+ (unsigned) DB_QuerySELECT (&mysql_res,"can not get questions"
+ " of a test exam",
+ "SELECT QstCod," // row[0]
+ "Indexes," // row[1]
+ "Answers" // row[2]
+ " FROM tst_exam_questions"
+ " WHERE ExaCod=%ld"
+ " ORDER BY QstInd",
+ Exam->ExaCod);
+
+ /***** Get questions codes *****/
+ for (NumQst = 0;
+ NumQst < Exam->NumQsts;
+ NumQst++)
+ {
+ row = mysql_fetch_row (mysql_res);
+
+ /* Get question code */
+ if ((Exam->Questions[NumQst].QstCod = Str_ConvertStrCodToLongCod (row[0])) < 0)
+ Lay_ShowErrorAndExit ("Wrong code of question.");
+
+ /* Get indexes for this question (row[1]) */
+ Str_Copy (Exam->Questions[NumQst].StrIndexes,row[1],
+ TstExa_MAX_BYTES_INDEXES_ONE_QST);
+
+ /* Get answers selected by user for this question (row[2]) */
+ Str_Copy (Exam->Questions[NumQst].StrAnswers,row[2],
+ TstExa_MAX_BYTES_ANSWERS_ONE_QST);
+
+ /* Replace each comma by a separator of multiple parameters */
+ /* In database commas are used as separators instead of special chars */
+ Par_ReplaceCommaBySeparatorMultiple (Exam->Questions[NumQst].StrIndexes);
+ Par_ReplaceCommaBySeparatorMultiple (Exam->Questions[NumQst].StrAnswers);
+ }
+
+ /***** Free structure that stores the query result *****/
+ DB_FreeMySQLResult (&mysql_res);
+ }
+
+/*****************************************************************************/
+/********************** Remove test exams made by a user *********************/
+/*****************************************************************************/
+
+void TstExa_RemoveExamsMadeByUsrInAllCrss (long UsrCod)
+ {
+ /***** Remove test exams made by the specified user *****/
+ DB_QueryDELETE ("can not remove test exams made by a user",
+ "DELETE FROM tst_exam_questions"
+ " USING tst_exams,tst_exam_questions"
+ " WHERE tst_exams.UsrCod=%ld"
+ " AND tst_exams.ExaCod=tst_exam_questions.ExaCod",
+ UsrCod);
+
+ DB_QueryDELETE ("can not remove test exams made by a user",
+ "DELETE FROM tst_exams"
+ " WHERE UsrCod=%ld",
+ UsrCod);
+ }
+
+/*****************************************************************************/
+/*************** Remove test exams made by a user in a course ****************/
+/*****************************************************************************/
+
+void TstExa_RemoveExamsMadeByUsrInCrs (long UsrCod,long CrsCod)
+ {
+ /***** Remove test exams made by the given user *****/
+ DB_QueryDELETE ("can not remove test exams made by a user in a course",
+ "DELETE FROM tst_exam_questions"
+ " USING tst_exams,tst_exam_questions"
+ " WHERE tst_exams.CrsCod=%ld AND tst_exams.UsrCod=%ld"
+ " AND tst_exams.ExaCod=tst_exam_questions.ExaCod",
+ CrsCod,UsrCod);
+
+ DB_QueryDELETE ("can not remove test exams made by a user in a course",
+ "DELETE FROM tst_exams"
+ " WHERE CrsCod=%ld AND UsrCod=%ld",
+ CrsCod,UsrCod);
+ }
+
+/*****************************************************************************/
+/******************* Remove all test exams made in a course ******************/
+/*****************************************************************************/
+
+void TstExa_RemoveCrsExams (long CrsCod)
+ {
+ /***** Remove questions of test exams made in the course *****/
+ DB_QueryDELETE ("can not remove test exams made in a course",
+ "DELETE FROM tst_exam_questions"
+ " USING tst_exams,tst_exam_questions"
+ " WHERE tst_exams.CrsCod=%ld"
+ " AND tst_exams.ExaCod=tst_exam_questions.ExaCod",
+ CrsCod);
+
+ /***** Remove test exams made in the course *****/
+ DB_QueryDELETE ("can not remove test exams made in a course",
+ "DELETE FROM tst_exams WHERE CrsCod=%ld",
+ CrsCod);
+ }
diff --git a/swad_test_exam.h b/swad_test_exam.h
new file mode 100644
index 00000000..5440676a
--- /dev/null
+++ b/swad_test_exam.h
@@ -0,0 +1,110 @@
+// swad_test_exam.c: test exams made by users
+
+#ifndef _SWAD_TST_EXA
+#define _SWAD_TST_EXA
+/*
+ SWAD (Shared Workspace At a Distance in Spanish),
+ is a web platform developed at the University of Granada (Spain),
+ and used to support university teaching.
+
+ This file is part of SWAD core.
+ Copyright (C) 1999-2020 Antonio Cañas Vargas
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+/*****************************************************************************/
+/********************************* Headers ***********************************/
+/*****************************************************************************/
+
+#include "swad_user.h"
+
+/*****************************************************************************/
+/***************************** Public constants ******************************/
+/*****************************************************************************/
+
+#define TstExa_MAX_BYTES_INDEXES_ONE_QST (Tst_MAX_OPTIONS_PER_QUESTION * (3 + 1))
+#define TstExa_MAX_BYTES_ANSWERS_ONE_QST (Tst_MAX_OPTIONS_PER_QUESTION * (3 + 1))
+
+#define TstExa_SCORE_MAX 10 // Maximum score of a test (10 in Spain). Must be unsigned! // TODO: Make this configurable by teachers
+
+/*****************************************************************************/
+/******************************* Public types ********************************/
+/*****************************************************************************/
+
+struct TstExa_Exam
+ {
+ long ExaCod; // Test exam code
+ time_t TimeUTC[Dat_NUM_START_END_TIME];
+ unsigned NumQsts; // Number of questions
+ unsigned NumQstsNotBlank; // Number of questions not blank
+ bool AllowTeachers; // Are teachers allowed to see this test exam?
+ double Score; // Total score of the test exam
+ struct
+ {
+ long QstCod; // Question code
+ char StrIndexes[TstExa_MAX_BYTES_INDEXES_ONE_QST + 1]; // 0 1 2 3, 3 0 2 1, etc.
+ char StrAnswers[TstExa_MAX_BYTES_ANSWERS_ONE_QST + 1]; // Answers selected by user
+ double Score; // Question score
+ bool AnswerIsNotBlank; // Answer not blank?
+ } Questions[TstCfg_MAX_QUESTIONS_PER_TEST];
+ };
+
+/*****************************************************************************/
+/***************************** Public prototypes *****************************/
+/*****************************************************************************/
+
+void TstExa_CreateExamInDB (struct TstExa_Exam *Exam);
+void TstExa_UpdateExamInDB (const struct TstExa_Exam *Exam);
+
+void TstExa_ShowExamAfterAssess (struct TstExa_Exam *Exam);
+void TstExa_WriteQstAndAnsExam (struct UsrData *UsrDat,
+ struct TstExa_Exam *Result,
+ unsigned NumQst,
+ MYSQL_ROW row,
+ unsigned Visibility);
+
+void TstExa_ComputeScoresAndStoreExamQuestions (struct TstExa_Exam *Exam,
+ bool UpdateQstScore);
+void TstExa_ComputeChoiceAnsScore (struct TstExa_Exam *Result,
+ unsigned NumQst,
+ struct Tst_Question *Question);
+void TstExa_GetIndexesFromStr (const char StrIndexesOneQst[TstExa_MAX_BYTES_INDEXES_ONE_QST + 1], // 0 1 2 3, 3 0 2 1, etc.
+ unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]);
+void TstExa_GetAnswersFromStr (const char StrAnswersOneQst[TstExa_MAX_BYTES_ANSWERS_ONE_QST + 1],
+ bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION]);
+
+void TstExa_ComputeAndShowGrade (unsigned NumQsts,double Score,double MaxGrade);
+double TstExa_ComputeGrade (unsigned NumQsts,double Score,double MaxGrade);
+void TstExa_ShowGrade (double Grade,double MaxGrade);
+
+void TstExa_SelUsrsToViewUsrsExams (void);
+void TstExa_SelDatesToSeeMyExams (void);
+void TstExa_ShowMyExams (void);
+void TstExa_GetUsrsAndShowExams (void);
+
+void TstExa_PutParamExaCod (long ExaCod);
+long TstExa_GetParamExaCod (void);
+
+void TstExa_ShowOneExam (void);
+void TstExa_ShowExamAnswers (struct UsrData *UsrDat,
+ struct TstExa_Exam *Exam,
+ unsigned Visibility);
+void TstExa_GetExamDataByExaCod (struct TstExa_Exam *Exam);
+
+void TstExa_GetExamQuestionsFromDB (struct TstExa_Exam *Exam);
+void TstExa_RemoveExamsMadeByUsrInAllCrss (long UsrCod);
+void TstExa_RemoveExamsMadeByUsrInCrs (long UsrCod,long CrsCod);
+void TstExa_RemoveCrsExams (long CrsCod);
+
+#endif
diff --git a/swad_test_result.c b/swad_test_result.c
deleted file mode 100644
index 97a0f834..00000000
--- a/swad_test_result.c
+++ /dev/null
@@ -1,1157 +0,0 @@
-// swad_test_result.c: test results
-
-/*
- SWAD (Shared Workspace At a Distance),
- is a web platform developed at the University of Granada (Spain),
- and used to support university teaching.
-
- This file is part of SWAD core.
- Copyright (C) 1999-2020 Antonio Cañas Vargas
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-/*****************************************************************************/
-/*********************************** Headers *********************************/
-/*****************************************************************************/
-
-#define _GNU_SOURCE // For asprintf
-#include // For boolean type
-#include // For NULL
-#include // For asprintf
-#include // For free
-
-#include "swad_action.h"
-#include "swad_database.h"
-#include "swad_form.h"
-#include "swad_global.h"
-#include "swad_HTML.h"
-#include "swad_ID.h"
-#include "swad_test.h"
-#include "swad_test_visibility.h"
-#include "swad_user.h"
-
-/*****************************************************************************/
-/***************************** Public constants ******************************/
-/*****************************************************************************/
-
-/*****************************************************************************/
-/**************************** Private constants ******************************/
-/*****************************************************************************/
-
-/*****************************************************************************/
-/******************************* Private types *******************************/
-/*****************************************************************************/
-
-#define Tst_NUM_STATUS 2
-typedef enum
- {
- Tst_STATUS_SHOWN_BUT_NOT_ASSESSED = 0,
- Tst_STATUS_ASSESSED = 1,
- Tst_STATUS_ERROR = 2,
- } Tst_Status_t;
-
-/*****************************************************************************/
-/************** External global variables from others modules ****************/
-/*****************************************************************************/
-
-extern struct Globals Gbl;
-
-/*****************************************************************************/
-/************************* Private global variables **************************/
-/*****************************************************************************/
-
-/*****************************************************************************/
-/***************************** Private prototypes ****************************/
-/*****************************************************************************/
-
-static void TsR_ShowUsrsTstResults (void);
-static void TsR_ShowHeaderTestResults (void);
-static void TsR_ShowTstResults (struct UsrData *UsrDat);
-static void TsR_PutParamTstCod (long TstCod);
-static long TsR_GetParamTstCod (void);
-static void TsR_ShowTestResultsSummaryRow (bool ItsMe,
- unsigned NumExams,
- unsigned NumTotalQsts,
- unsigned NumTotalQstsNotBlank,
- double TotalScoreOfAllTests);
-static void TsR_ShowTstTagsPresentInATestResult (long TstCod);
-static void TsR_GetTestResultDataByTstCod (long TstCod,struct TsR_Result *Result);
-static void TsR_GetTestResultQuestionsFromDB (long TstCod,struct TsR_Result *Result);
-
-/*****************************************************************************/
-/************ Select users and dates to show their test results **************/
-/*****************************************************************************/
-
-void TsR_SelUsrsToViewUsrsTstResults (void)
- {
- extern const char *Hlp_ASSESSMENT_Tests_results;
- extern const char *Txt_Results;
- extern const char *Txt_View_test_results;
-
- Usr_PutFormToSelectUsrsToGoToAct (&Gbl.Usrs.Selected,
- ActSeeUsrTstRes,
- NULL,NULL,
- Txt_Results,
- Hlp_ASSESSMENT_Tests_results,
- Txt_View_test_results,
- true); // Put form with date range
- }
-
-/*****************************************************************************/
-/******************* Select dates to show my test results ********************/
-/*****************************************************************************/
-
-void TsR_SelDatesToSeeMyTstResults (void)
- {
- extern const char *Hlp_ASSESSMENT_Tests_results;
- extern const char *Txt_Results;
- extern const char *Txt_View_test_results;
- static const Dat_SetHMS SetHMS[Dat_NUM_START_END_TIME] =
- {
- Dat_HMS_DO_NOT_SET,
- Dat_HMS_DO_NOT_SET
- };
-
- /***** Begin form *****/
- Frm_StartForm (ActSeeMyTstRes);
-
- /***** Begin box and table *****/
- Box_BoxTableBegin (NULL,Txt_Results,
- NULL,NULL,
- Hlp_ASSESSMENT_Tests_results,Box_NOT_CLOSABLE,2);
- Dat_PutFormStartEndClientLocalDateTimesWithYesterdayToday (SetHMS);
-
- /***** End table, send button and end box *****/
- Box_BoxTableWithButtonEnd (Btn_CONFIRM_BUTTON,Txt_View_test_results);
-
- /***** End form *****/
- Frm_EndForm ();
- }
-
-/*****************************************************************************/
-/***************************** Show my test results **************************/
-/*****************************************************************************/
-
-void TsR_ShowMyTstResults (void)
- {
- extern const char *Hlp_ASSESSMENT_Tests_results;
- extern const char *Txt_Results;
-
- /***** Get starting and ending dates *****/
- Dat_GetIniEndDatesFromForm ();
-
- /***** Begin box and table *****/
- Box_BoxTableBegin (NULL,Txt_Results,
- NULL,NULL,
- Hlp_ASSESSMENT_Tests_results,Box_NOT_CLOSABLE,2);
-
- /***** Header of the table with the list of users *****/
- TsR_ShowHeaderTestResults ();
-
- /***** List my test results *****/
- TstCfg_GetConfigFromDB (); // To get feedback type
- TsR_ShowTstResults (&Gbl.Usrs.Me.UsrDat);
-
- /***** End table and box *****/
- Box_BoxTableEnd ();
- }
-
-/*****************************************************************************/
-/**************** Create new blank test result in database *******************/
-/*****************************************************************************/
-
-void TsR_CreateTestResultInDB (struct TsR_Result *Result)
- {
- /***** Insert new test result into table *****/
- Result->TstCod =
- DB_QueryINSERTandReturnCode ("can not create new test result",
- "INSERT INTO tst_exams"
- " (CrsCod,UsrCod,StartTime,EndTime,NumQsts,AllowTeachers)"
- " VALUES"
- " (%ld,%ld,NOW(),NOW(),%u,'%c')",
- Gbl.Hierarchy.Crs.CrsCod,
- Gbl.Usrs.Me.UsrDat.UsrCod,
- Result->NumQsts,
- Result->AllowTeachers ? 'Y' :
- 'N');
- }
-
-/*****************************************************************************/
-/********************* Store test result in database *************************/
-/*****************************************************************************/
-
-void TsR_UpdateScoreOfTestResultInDB (const struct TsR_Result *Result)
- {
- /***** Update score in test result *****/
- Str_SetDecimalPointToUS (); // To print the floating point as a dot
- DB_QueryUPDATE ("can not update test result",
- "UPDATE tst_exams"
- " SET EndTime=NOW(),"
- "NumQstsNotBlank=%u,"
- "Score='%.15lg'"
- " WHERE TstCod=%ld"
- " AND CrsCod=%ld AND UsrCod=%ld", // Extra checks
- Result->NumQstsNotBlank,
- Result->Score,
- Result->TstCod,
- Gbl.Hierarchy.Crs.CrsCod,
- Gbl.Usrs.Me.UsrDat.UsrCod);
- Str_SetDecimalPointToLocal (); // Return to local system
- }
-
-/*****************************************************************************/
-/******************* Get users and show their test results *******************/
-/*****************************************************************************/
-
-void TsR_GetUsrsAndShowTstResults (void)
- {
- Usr_GetSelectedUsrsAndGoToAct (&Gbl.Usrs.Selected,
- TsR_ShowUsrsTstResults,
- TsR_SelUsrsToViewUsrsTstResults);
- }
-
-/*****************************************************************************/
-/******************** Show test results for several users ********************/
-/*****************************************************************************/
-
-static void TsR_ShowUsrsTstResults (void)
- {
- extern const char *Hlp_ASSESSMENT_Tests_results;
- extern const char *Txt_Results;
- const char *Ptr;
-
- /***** Get starting and ending dates *****/
- Dat_GetIniEndDatesFromForm ();
-
- /***** Begin box and table *****/
- Box_BoxTableBegin (NULL,Txt_Results,
- NULL,NULL,
- Hlp_ASSESSMENT_Tests_results,Box_NOT_CLOSABLE,2);
-
- /***** Header of the table with the list of users *****/
- TsR_ShowHeaderTestResults ();
-
- /***** List the test exams of the selected users *****/
- Ptr = Gbl.Usrs.Selected.List[Rol_UNK];
- while (*Ptr)
- {
- Par_GetNextStrUntilSeparParamMult (&Ptr,Gbl.Usrs.Other.UsrDat.EncryptedUsrCod,
- Cry_BYTES_ENCRYPTED_STR_SHA256_BASE64);
- Usr_GetUsrCodFromEncryptedUsrCod (&Gbl.Usrs.Other.UsrDat);
- if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat,Usr_DONT_GET_PREFS))
- if (Usr_CheckIfICanViewTst (&Gbl.Usrs.Other.UsrDat))
- {
- /***** Show test results *****/
- Gbl.Usrs.Other.UsrDat.Accepted = Usr_CheckIfUsrHasAcceptedInCurrentCrs (&Gbl.Usrs.Other.UsrDat);
- TsR_ShowTstResults (&Gbl.Usrs.Other.UsrDat);
- }
- }
-
- /***** End table and box *****/
- Box_BoxTableEnd ();
- }
-
-/*****************************************************************************/
-/*********************** Show header of my test results **********************/
-/*****************************************************************************/
-
-static void TsR_ShowHeaderTestResults (void)
- {
- extern const char *Txt_User[Usr_NUM_SEXS];
- extern const char *Txt_START_END_TIME[Dat_NUM_START_END_TIME];
- extern const char *Txt_Questions;
- extern const char *Txt_Non_blank_BR_questions;
- extern const char *Txt_Score;
- extern const char *Txt_Average_BR_score_BR_per_question_BR_from_0_to_1;
- extern const char *Txt_Grade;
-
- HTM_TR_Begin (NULL);
-
- HTM_TH (1,2,"CT",Txt_User[Usr_SEX_UNKNOWN]);
- HTM_TH (1,1,"LT",Txt_START_END_TIME[Dat_START_TIME]);
- HTM_TH (1,1,"LT",Txt_START_END_TIME[Dat_END_TIME ]);
- HTM_TH (1,1,"RT",Txt_Questions);
- HTM_TH (1,1,"RT",Txt_Non_blank_BR_questions);
- HTM_TH (1,1,"RT",Txt_Score);
- HTM_TH (1,1,"RT",Txt_Average_BR_score_BR_per_question_BR_from_0_to_1);
- HTM_TH (1,1,"RT",Txt_Grade);
- HTM_TH_Empty (1);
-
- HTM_TR_End ();
- }
-
-/*****************************************************************************/
-/*********** Show the test results of a user in the current course ***********/
-/*****************************************************************************/
-
-static void TsR_ShowTstResults (struct UsrData *UsrDat)
- {
- extern const char *Txt_View_test;
- MYSQL_RES *mysql_res;
- MYSQL_ROW row;
- unsigned NumExams;
- unsigned NumTest;
- static unsigned UniqueId = 0;
- Dat_StartEndTime_t StartEndTime;
- char *Id;
- long TstCod;
- struct TsR_Result Result;
- unsigned NumTotalQsts = 0;
- unsigned NumTotalQstsNotBlank = 0;
- double TotalScoreOfAllTests = 0.0;
- unsigned NumExamsVisibleByTchs = 0;
- bool ItsMe = Usr_ItsMe (UsrDat->UsrCod);
- bool ICanViewTest;
- bool ICanViewScore;
- char *ClassDat;
-
- /***** Make database query *****/
- /* From here... ...to here
- ___________|_____ _____|___________
- -----|______Exam_|_____|-----------------|_____|_Exam______|-----> time
- Start | End Start | End
- */
- NumExams =
- (unsigned) DB_QuerySELECT (&mysql_res,"can not get test exams of a user",
- "SELECT TstCod," // row[0]
- "UNIX_TIMESTAMP(StartTime)," // row[1]
- "UNIX_TIMESTAMP(EndTime)," // row[2]
- "NumQsts," // row[3]
- "NumQstsNotBlank," // row[4]
- "AllowTeachers," // row[5]
- "Score" // row[6]
- " FROM tst_exams"
- " WHERE CrsCod=%ld AND UsrCod=%ld"
- " AND EndTime>=FROM_UNIXTIME(%ld)"
- " AND StartTime<=FROM_UNIXTIME(%ld)"
- " ORDER BY TstCod",
- Gbl.Hierarchy.Crs.CrsCod,
- UsrDat->UsrCod,
- (long) Gbl.DateRange.TimeUTC[Dat_START_TIME],
- (long) Gbl.DateRange.TimeUTC[Dat_END_TIME ]);
-
- /***** Show user's data *****/
- HTM_TR_Begin (NULL);
- Usr_ShowTableCellWithUsrData (UsrDat,NumExams);
-
- /***** Get and print test results *****/
- if (NumExams)
- {
- for (NumTest = 0;
- NumTest < NumExams;
- NumTest++)
- {
- row = mysql_fetch_row (mysql_res);
-
- /* Get test code (row[0]) */
- if ((TstCod = Str_ConvertStrCodToLongCod (row[0])) < 0)
- Lay_ShowErrorAndExit ("Wrong code of test result.");
-
- /* Get if teachers are allowed to see this test result (row[5]) */
- Result.AllowTeachers = (row[5][0] == 'Y');
- ClassDat = Result.AllowTeachers ? "DAT" :
- "DAT_LIGHT";
-
- switch (Gbl.Usrs.Me.Role.Logged)
- {
- case Rol_STD:
- ICanViewTest = ItsMe;
- ICanViewScore = ItsMe &&
- TsV_IsVisibleTotalScore (TstCfg_GetConfigVisibility ());
- break;
- case Rol_NET:
- case Rol_TCH:
- case Rol_DEG_ADM:
- case Rol_CTR_ADM:
- case Rol_INS_ADM:
- ICanViewTest =
- ICanViewScore = ItsMe ||
- Result.AllowTeachers;
- break;
- case Rol_SYS_ADM:
- ICanViewTest =
- ICanViewScore = true;
- break;
- default:
- ICanViewTest =
- ICanViewScore = false;
- break;
- }
-
- if (NumTest)
- HTM_TR_Begin (NULL);
-
- /* Write date and time (row[1] and row[2] hold UTC date-times) */
- Result.TimeUTC[Dat_START_TIME] = Dat_GetUNIXTimeFromStr (row[1]);
- Result.TimeUTC[Dat_END_TIME ] = Dat_GetUNIXTimeFromStr (row[2]);
- UniqueId++;
- for (StartEndTime = (Dat_StartEndTime_t) 0;
- StartEndTime <= (Dat_StartEndTime_t) (Dat_NUM_START_END_TIME - 1);
- StartEndTime++)
- {
- if (asprintf (&Id,"tst_date_%u_%u",(unsigned) StartEndTime,UniqueId) < 0)
- Lay_NotEnoughMemoryExit ();
- HTM_TD_Begin ("id=\"%s\" class=\"%s LT COLOR%u\"",
- Id,ClassDat,Gbl.RowEvenOdd);
- Dat_WriteLocalDateHMSFromUTC (Id,Result.TimeUTC[StartEndTime],
- Gbl.Prefs.DateFormat,Dat_SEPARATOR_BREAK,
- true,true,false,0x7);
- HTM_TD_End ();
- free (Id);
- }
-
- /* Get number of questions (row[3]) */
- if (sscanf (row[3],"%u",&Result.NumQsts) != 1)
- Result.NumQsts = 0;
- if (Result.AllowTeachers)
- NumTotalQsts += Result.NumQsts;
-
- /* Get number of questions not blank (row[4]) */
- if (sscanf (row[4],"%u",&Result.NumQstsNotBlank) != 1)
- Result.NumQstsNotBlank = 0;
- if (Result.AllowTeachers)
- NumTotalQstsNotBlank += Result.NumQstsNotBlank;
-
- /* Get score (row[6]) */
- Str_SetDecimalPointToUS (); // To get the decimal point as a dot
- if (sscanf (row[6],"%lf",&Result.Score) != 1)
- Result.Score = 0.0;
- Str_SetDecimalPointToLocal (); // Return to local system
- if (Result.AllowTeachers)
- TotalScoreOfAllTests += Result.Score;
-
- /* Write number of questions */
- HTM_TD_Begin ("class=\"%s RT COLOR%u\"",ClassDat,Gbl.RowEvenOdd);
- if (ICanViewTest)
- HTM_Unsigned (Result.NumQsts);
- HTM_TD_End ();
-
- /* Write number of questions not blank */
- HTM_TD_Begin ("class=\"%s RT COLOR%u\"",ClassDat,Gbl.RowEvenOdd);
- if (ICanViewTest)
- HTM_Unsigned (Result.NumQstsNotBlank);
- HTM_TD_End ();
-
- /* Write score */
- HTM_TD_Begin ("class=\"%s RT COLOR%u\"",ClassDat,Gbl.RowEvenOdd);
- if (ICanViewScore)
- HTM_Double2Decimals (Result.Score);
- HTM_TD_End ();
-
- /* Write average score per question */
- HTM_TD_Begin ("class=\"%s RT COLOR%u\"",ClassDat,Gbl.RowEvenOdd);
- if (ICanViewScore)
- HTM_Double2Decimals (Result.NumQsts ? Result.Score /
- (double) Result.NumQsts :
- 0.0);
- HTM_TD_End ();
-
- /* Write grade */
- HTM_TD_Begin ("class=\"%s RT COLOR%u\"",ClassDat,Gbl.RowEvenOdd);
- if (ICanViewScore)
- Tst_ComputeAndShowGrade (Result.NumQsts,
- Result.Score,
- TsR_SCORE_MAX);
- HTM_TD_End ();
-
- /* Link to show this result */
- HTM_TD_Begin ("class=\"RT COLOR%u\"",Gbl.RowEvenOdd);
- if (ICanViewTest)
- {
- Frm_StartForm (Gbl.Action.Act == ActSeeMyTstRes ? ActSeeOneTstResMe :
- ActSeeOneTstResOth);
- TsR_PutParamTstCod (TstCod);
- Ico_PutIconLink ("tasks.svg",Txt_View_test);
- Frm_EndForm ();
- }
- HTM_TD_End ();
- HTM_TR_End ();
-
- if (Result.AllowTeachers)
- NumExamsVisibleByTchs++;
- }
-
- /***** Write totals for this user *****/
- TsR_ShowTestResultsSummaryRow (ItsMe,NumExamsVisibleByTchs,
- NumTotalQsts,NumTotalQstsNotBlank,
- TotalScoreOfAllTests);
- }
- else
- {
- HTM_TD_ColouredEmpty (7);
- HTM_TR_End ();
- }
-
- /***** Free structure that stores the query result *****/
- DB_FreeMySQLResult (&mysql_res);
-
- Gbl.RowEvenOdd = 1 - Gbl.RowEvenOdd;
- }
-
-/*****************************************************************************/
-/******************** Write parameter with code of test **********************/
-/*****************************************************************************/
-
-static void TsR_PutParamTstCod (long TstCod)
- {
- Par_PutHiddenParamLong (NULL,"TstCod",TstCod);
- }
-
-/*****************************************************************************/
-/********************* Get parameter with code of test ***********************/
-/*****************************************************************************/
-
-static long TsR_GetParamTstCod (void)
- {
- /***** Get code of test *****/
- return Par_GetParToLong ("TstCod");
- }
-
-/*****************************************************************************/
-/**************** Show row with summary of user's test results ***************/
-/*****************************************************************************/
-
-static void TsR_ShowTestResultsSummaryRow (bool ItsMe,
- unsigned NumExams,
- unsigned NumTotalQsts,
- unsigned NumTotalQstsNotBlank,
- double TotalScoreOfAllTests)
- {
- extern const char *Txt_Visible_tests;
- bool ICanViewTotalScore;
-
- switch (Gbl.Usrs.Me.Role.Logged)
- {
- case Rol_STD:
- ICanViewTotalScore = ItsMe &&
- TsV_IsVisibleTotalScore (TstCfg_GetConfigVisibility ());
- break;
- case Rol_NET:
- case Rol_TCH:
- case Rol_DEG_ADM:
- case Rol_CTR_ADM:
- case Rol_INS_ADM:
- ICanViewTotalScore = ItsMe ||
- NumExams;
- break;
- case Rol_SYS_ADM:
- ICanViewTotalScore = true;
- break;
- default:
- ICanViewTotalScore = false;
- break;
- }
-
- /***** Start row *****/
- HTM_TR_Begin (NULL);
-
- /***** Row title *****/
- HTM_TD_Begin ("colspan=\"2\" class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd);
- HTM_TxtColonNBSP (Txt_Visible_tests);
- HTM_Unsigned (NumExams);
- HTM_TD_End ();
-
- /***** Write total number of questions *****/
- HTM_TD_Begin ("class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd);
- if (NumExams)
- HTM_Unsigned (NumTotalQsts);
- HTM_TD_End ();
-
- /***** Write total number of questions not blank *****/
- HTM_TD_Begin ("class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd);
- if (NumExams)
- HTM_Unsigned (NumTotalQstsNotBlank);
- HTM_TD_End ();
-
- /***** Write total score *****/
- HTM_TD_Begin ("class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd);
- if (ICanViewTotalScore)
- HTM_Double2Decimals (TotalScoreOfAllTests);
- HTM_TD_End ();
-
- /***** Write average score per question *****/
- HTM_TD_Begin ("class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd);
- if (ICanViewTotalScore)
- HTM_Double2Decimals (NumTotalQsts ? TotalScoreOfAllTests / (double) NumTotalQsts :
- 0.0);
- HTM_TD_End ();
-
- /***** Write score over Tst_SCORE_MAX *****/
- HTM_TD_Begin ("class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd);
- if (ICanViewTotalScore)
- Tst_ComputeAndShowGrade (NumTotalQsts,
- TotalScoreOfAllTests,
- TsR_SCORE_MAX);
- HTM_TD_End ();
-
- /***** Last cell *****/
- HTM_TD_Begin ("class=\"DAT_N_LINE_TOP COLOR%u\"",Gbl.RowEvenOdd);
- HTM_TD_End ();
-
- /***** End row *****/
- HTM_TR_End ();
- }
-
-/*****************************************************************************/
-/******************* Show one test result of another user ********************/
-/*****************************************************************************/
-
-void TsR_ShowOneTstResult (void)
- {
- extern const char *Hlp_ASSESSMENT_Tests_results;
- extern const char *Txt_Test_result;
- extern const char *Txt_The_user_does_not_exist;
- extern const char *Txt_ROLES_SINGUL_Abc[Rol_NUM_ROLES][Usr_NUM_SEXS];
- extern const char *Txt_START_END_TIME[Dat_NUM_START_END_TIME];
- extern const char *Txt_Questions;
- extern const char *Txt_non_blank_QUESTIONS;
- extern const char *Txt_Score;
- extern const char *Txt_Grade;
- extern const char *Txt_Tags;
- long TstCod;
- struct TsR_Result Result;
- bool ShowPhoto;
- char PhotoURL[PATH_MAX + 1];
- Dat_StartEndTime_t StartEndTime;
- char *Id;
- bool ItsMe;
- bool ICanViewTest;
- bool ICanViewScore;
-
- /***** Get the code of the test *****/
- if ((TstCod = TsR_GetParamTstCod ()) == -1L)
- Lay_ShowErrorAndExit ("Code of test is missing.");
-
- /***** Get test result data *****/
- TsR_GetTestResultDataByTstCod (TstCod,&Result);
- TstCfg_SetConfigVisibility (TsV_MAX_VISIBILITY);
-
- /***** Check if I can view this test result *****/
- ItsMe = Usr_ItsMe (Gbl.Usrs.Other.UsrDat.UsrCod);
- switch (Gbl.Usrs.Me.Role.Logged)
- {
- case Rol_STD:
- ICanViewTest = ItsMe;
- if (ItsMe)
- {
- TstCfg_GetConfigFromDB (); // To get feedback type
- ICanViewScore = TsV_IsVisibleTotalScore (TstCfg_GetConfigVisibility ());
- }
- else
- ICanViewScore = false;
- break;
- case Rol_TCH:
- case Rol_DEG_ADM:
- case Rol_CTR_ADM:
- case Rol_INS_ADM:
- switch (Gbl.Action.Act)
- {
- case ActSeeOneTstResMe:
- ICanViewTest =
- ICanViewScore = ItsMe;
- break;
- case ActSeeOneTstResOth:
- ICanViewTest =
- ICanViewScore = ItsMe ||
- Result.AllowTeachers;
- break;
- default:
- ICanViewTest =
- ICanViewScore = false;
- break;
- }
- break;
- case Rol_SYS_ADM:
- ICanViewTest =
- ICanViewScore = true;
- break;
- default:
- ICanViewTest =
- ICanViewScore = false;
- break;
- }
-
- if (ICanViewTest) // I am allowed to view this test result
- {
- /***** Get questions and user's answers of the test result from database *****/
- TsR_GetTestResultQuestionsFromDB (TstCod,&Result);
-
- /***** Begin box *****/
- Box_BoxBegin (NULL,Txt_Test_result,
- NULL,NULL,
- Hlp_ASSESSMENT_Tests_results,Box_NOT_CLOSABLE);
- Lay_WriteHeaderClassPhoto (false,false,
- Gbl.Hierarchy.Ins.InsCod,
- Gbl.Hierarchy.Deg.DegCod,
- Gbl.Hierarchy.Crs.CrsCod);
-
- /***** Begin table *****/
- HTM_TABLE_BeginWideMarginPadding (5);
-
- /***** Header row *****/
- /* Get data of the user who made the test */
- if (!Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat,Usr_DONT_GET_PREFS))
- Lay_ShowErrorAndExit (Txt_The_user_does_not_exist);
- if (!Usr_CheckIfICanViewTst (&Gbl.Usrs.Other.UsrDat))
- Lay_NoPermissionExit ();
-
- /* User */
- HTM_TR_Begin (NULL);
-
- HTM_TD_Begin ("class=\"DAT_N RT\"");
- HTM_TxtF ("%s:",Txt_ROLES_SINGUL_Abc[Gbl.Usrs.Other.UsrDat.Roles.InCurrentCrs.Role][Gbl.Usrs.Other.UsrDat.Sex]);
- HTM_TD_End ();
-
- HTM_TD_Begin ("class=\"DAT LT\"");
- ID_WriteUsrIDs (&Gbl.Usrs.Other.UsrDat,NULL);
- HTM_TxtF (" %s",Gbl.Usrs.Other.UsrDat.Surname1);
- if (Gbl.Usrs.Other.UsrDat.Surname2[0])
- HTM_TxtF (" %s",Gbl.Usrs.Other.UsrDat.Surname2);
- if (Gbl.Usrs.Other.UsrDat.FirstName[0])
- HTM_TxtF (", %s",Gbl.Usrs.Other.UsrDat.FirstName);
- HTM_BR ();
- ShowPhoto = Pho_ShowingUsrPhotoIsAllowed (&Gbl.Usrs.Other.UsrDat,PhotoURL);
- Pho_ShowUsrPhoto (&Gbl.Usrs.Other.UsrDat,ShowPhoto ? PhotoURL :
- NULL,
- "PHOTO45x60",Pho_ZOOM,false);
- HTM_TD_End ();
-
- HTM_TR_End ();
-
- /* Test date */
- for (StartEndTime = (Dat_StartEndTime_t) 0;
- StartEndTime <= (Dat_StartEndTime_t) (Dat_NUM_START_END_TIME - 1);
- StartEndTime++)
- {
- if (asprintf (&Id,"tst_date_%u",(unsigned) StartEndTime) < 0)
- Lay_NotEnoughMemoryExit ();
-
- HTM_TR_Begin (NULL);
-
- HTM_TD_Begin ("class=\"DAT_N RT\"");
- HTM_TxtF ("%s:",Txt_START_END_TIME[StartEndTime]);
- HTM_TD_End ();
-
- HTM_TD_Begin ("id=\"%s\" class=\"DAT LT\"",Id);
- Dat_WriteLocalDateHMSFromUTC (Id,Result.TimeUTC[StartEndTime],
- Gbl.Prefs.DateFormat,Dat_SEPARATOR_COMMA,
- true,true,true,0x7);
- HTM_TD_End ();
-
- HTM_TR_End ();
-
- free (Id);
- }
-
- /* Number of questions */
- HTM_TR_Begin (NULL);
-
- HTM_TD_Begin ("class=\"DAT_N RT\"");
- HTM_TxtF ("%s:",Txt_Questions);
- HTM_TD_End ();
-
- HTM_TD_Begin ("class=\"DAT LT\"");
- HTM_TxtF ("%u (%u %s)",
- Result.NumQsts,
- Result.NumQstsNotBlank,Txt_non_blank_QUESTIONS);
- HTM_TD_End ();
-
- HTM_TR_End ();
-
- /* Score */
- HTM_TR_Begin (NULL);
-
- HTM_TD_Begin ("class=\"DAT_N RT\"");
- HTM_TxtF ("%s:",Txt_Score);
- HTM_TD_End ();
-
- HTM_TD_Begin ("class=\"DAT LT\"");
- if (ICanViewScore)
- HTM_Double2Decimals (Result.Score);
- else
- Ico_PutIconNotVisible ();
- HTM_TD_End ();
-
- /* Grade */
- HTM_TR_Begin (NULL);
-
- HTM_TD_Begin ("class=\"DAT_N RT\"");
- HTM_TxtF ("%s:",Txt_Grade);
- HTM_TD_End ();
-
- HTM_TD_Begin ("class=\"DAT LT\"");
- if (ICanViewScore)
- Tst_ComputeAndShowGrade (Result.NumQsts,
- Result.Score,
- TsR_SCORE_MAX);
- else
- Ico_PutIconNotVisible ();
- HTM_TD_End ();
-
- HTM_TR_End ();
-
- /* Tags present in this test */
- HTM_TR_Begin (NULL);
-
- HTM_TD_Begin ("class=\"DAT_N RT\"");
- HTM_TxtF ("%s:",Txt_Tags);
- HTM_TD_End ();
-
- HTM_TD_Begin ("class=\"DAT LT\"");
- TsR_ShowTstTagsPresentInATestResult (TstCod);
- HTM_TD_End ();
-
- HTM_TR_End ();
-
- /***** Write answers and solutions *****/
- TsR_ShowTestResult (&Gbl.Usrs.Other.UsrDat,
- &Result,
- TstCfg_GetConfigVisibility ());
-
- /***** End table *****/
- HTM_TABLE_End ();
-
- /***** Write total mark of test *****/
- if (ICanViewScore)
- {
- HTM_DIV_Begin ("class=\"DAT_N_BOLD CM\"");
- HTM_TxtColonNBSP (Txt_Score);
- HTM_Double2Decimals (Result.Score);
- HTM_BR ();
- HTM_TxtColonNBSP (Txt_Grade);
- Tst_ComputeAndShowGrade (Result.NumQsts,
- Result.Score,
- TsR_SCORE_MAX);
- HTM_DIV_End ();
- }
-
- /***** End box *****/
- Box_BoxEnd ();
- }
- else // I am not allowed to view this test result
- Lay_NoPermissionExit ();
- }
-
-/*****************************************************************************/
-/******************** Show test tags in this test result *********************/
-/*****************************************************************************/
-
-static void TsR_ShowTstTagsPresentInATestResult (long TstCod)
- {
- MYSQL_RES *mysql_res;
- unsigned NumTags;
-
- /***** Get all tags of questions in this test *****/
- NumTags = (unsigned)
- DB_QuerySELECT (&mysql_res,"can not get tags"
- " present in a test result",
- "SELECT tst_tags.TagTxt" // row[0]
- " FROM"
- " (SELECT DISTINCT(tst_question_tags.TagCod)"
- " FROM tst_question_tags,tst_exam_questions"
- " WHERE tst_exam_questions.TstCod=%ld"
- " AND tst_exam_questions.QstCod=tst_question_tags.QstCod)"
- " AS TagsCods,tst_tags"
- " WHERE TagsCods.TagCod=tst_tags.TagCod"
- " ORDER BY tst_tags.TagTxt",
- TstCod);
- Tst_ShowTagList (NumTags,mysql_res);
-
- /***** Free structure that stores the query result *****/
- DB_FreeMySQLResult (&mysql_res);
- }
-
-/*****************************************************************************/
-/************************* Show the result of a test *************************/
-/*****************************************************************************/
-
-void TsR_ShowTestResult (struct UsrData *UsrDat,
- struct TsR_Result *Result,
- unsigned Visibility)
- {
- extern const char *Txt_Question_modified;
- extern const char *Txt_Question_removed;
- MYSQL_RES *mysql_res;
- MYSQL_ROW row;
- unsigned NumQst;
- bool ThisQuestionHasBeenEdited;
- time_t EditTimeUTC;
-
- for (NumQst = 0;
- NumQst < Result->NumQsts;
- NumQst++)
- {
- Gbl.RowEvenOdd = NumQst % 2;
-
- /***** Query database *****/
- if (Tst_GetOneQuestionByCod (Result->Questions[NumQst].QstCod,&mysql_res)) // Question exists
- {
- /***** Get row of the result of the query *****/
- row = mysql_fetch_row (mysql_res);
-
- /***** If this question has been edited later than test time
- ==> don't show question ****/
- EditTimeUTC = Dat_GetUNIXTimeFromStr (row[0]);
- ThisQuestionHasBeenEdited = false;
- if (EditTimeUTC > Result->TimeUTC[Dat_START_TIME])
- ThisQuestionHasBeenEdited = true;
-
- if (ThisQuestionHasBeenEdited)
- {
- /***** Question has been edited *****/
- HTM_TR_Begin (NULL);
-
- HTM_TD_Begin ("class=\"BIG_INDEX RT COLOR%u\"",Gbl.RowEvenOdd);
- HTM_Unsigned (NumQst + 1);
- HTM_TD_End ();
-
- HTM_TD_Begin ("class=\"DAT_LIGHT LT COLOR%u\"",Gbl.RowEvenOdd);
- HTM_Txt (Txt_Question_modified);
- HTM_TD_End ();
-
- HTM_TR_End ();
- }
- else
- /***** Write questions and answers *****/
- Tst_WriteQstAndAnsTestResult (UsrDat,
- Result,
- NumQst,
- row,
- Visibility);
- }
- else
- {
- /***** Question does not exists *****/
- HTM_TR_Begin (NULL);
-
- HTM_TD_Begin ("class=\"BIG_INDEX RT COLOR%u\"",Gbl.RowEvenOdd);
- HTM_Unsigned (NumQst + 1);
- HTM_TD_End ();
-
- HTM_TD_Begin ("class=\"DAT_LIGHT LT COLOR%u\"",Gbl.RowEvenOdd);
- HTM_Txt (Txt_Question_removed);
- HTM_TD_End ();
-
- HTM_TR_End ();
- }
-
- /***** Free structure that stores the query result *****/
- DB_FreeMySQLResult (&mysql_res);
- }
- }
-
-/*****************************************************************************/
-/********* Get data of a test result using its test result code **************/
-/*****************************************************************************/
-
-static void TsR_GetTestResultDataByTstCod (long TstCod,struct TsR_Result *Result)
- {
- MYSQL_RES *mysql_res;
- MYSQL_ROW row;
-
- /***** Make database query *****/
- if (DB_QuerySELECT (&mysql_res,"can not get data"
- " of a test result of a user",
- "SELECT UsrCod," // row[0]
- "UNIX_TIMESTAMP(StartTime)," // row[1]
- "UNIX_TIMESTAMP(EndTime)," // row[2]
- "NumQsts," // row[3]
- "NumQstsNotBlank," // row[4]
- "AllowTeachers," // row[5]
- "Score" // row[6]
- " FROM tst_exams"
- " WHERE TstCod=%ld AND CrsCod=%ld",
- TstCod,
- Gbl.Hierarchy.Crs.CrsCod) == 1)
- {
- row = mysql_fetch_row (mysql_res);
-
- /* Get user code (row[0]) */
- Gbl.Usrs.Other.UsrDat.UsrCod = Str_ConvertStrCodToLongCod (row[0]);
-
- /* Get date-time (row[1] and row[2] hold UTC date-time) */
- Result->TimeUTC[Dat_START_TIME] = Dat_GetUNIXTimeFromStr (row[1]);
- Result->TimeUTC[Dat_END_TIME ] = Dat_GetUNIXTimeFromStr (row[2]);
-
- /* Get number of questions (row[3]) */
- if (sscanf (row[3],"%u",&Result->NumQsts) != 1)
- Result->NumQsts = 0;
-
- /* Get number of questions not blank (row[4]) */
- if (sscanf (row[4],"%u",&Result->NumQstsNotBlank) != 1)
- Result->NumQstsNotBlank = 0;
-
- /* Get if teachers are allowed to see this test result (row[5]) */
- Result->AllowTeachers = (row[5][0] == 'Y');
-
- /* Get score (row[6]) */
- Str_SetDecimalPointToUS (); // To get the decimal point as a dot
- if (sscanf (row[6],"%lf",&Result->Score) != 1)
- Result->Score = 0.0;
- Str_SetDecimalPointToLocal (); // Return to local system
- }
- else
- {
- Result->TimeUTC[Dat_START_TIME] =
- Result->TimeUTC[Dat_END_TIME ] = 0;
- Result->NumQsts = 0;
- Result->NumQstsNotBlank = 0;
- Result->AllowTeachers = false;
- Result->Score = 0.0;
- }
-
- /***** Free structure that stores the query result *****/
- DB_FreeMySQLResult (&mysql_res);
- }
-
-/*****************************************************************************/
-/************ Store user's answers of an test result into database ***********/
-/*****************************************************************************/
-
-void TsR_StoreOneTestResultQstInDB (const struct TsR_Result *Result,
- unsigned NumQst)
- {
- char StrIndexes[Tst_MAX_BYTES_INDEXES_ONE_QST + 1];
- char StrAnswers[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
-
- /***** Replace each separator of multiple parameters by a comma *****/
- /* In database commas are used as separators instead of special chars */
- Par_ReplaceSeparatorMultipleByComma (Result->Questions[NumQst].StrIndexes,StrIndexes);
- Par_ReplaceSeparatorMultipleByComma (Result->Questions[NumQst].StrAnswers,StrAnswers);
-
- /***** Insert question and user's answers into database *****/
- Str_SetDecimalPointToUS (); // To print the floating point as a dot
- DB_QueryINSERT ("can not insert a question of a test result",
- "INSERT INTO tst_exam_questions"
- " (TstCod,QstCod,QstInd,Score,Indexes,Answers)"
- " VALUES"
- " (%ld,%ld,%u,'%.15lg','%s','%s')",
- Result->TstCod,Result->Questions[NumQst].QstCod,
- NumQst, // 0, 1, 2, 3...
- Result->Questions[NumQst].Score,
- StrIndexes,
- StrAnswers);
- Str_SetDecimalPointToLocal (); // Return to local system
- }
-
-/*****************************************************************************/
-/************ Get the questions of a test result from database ***************/
-/*****************************************************************************/
-
-static void TsR_GetTestResultQuestionsFromDB (long TstCod,struct TsR_Result *Result)
- {
- MYSQL_RES *mysql_res;
- MYSQL_ROW row;
- unsigned NumQst;
-
- /***** Get questions of a test result from database *****/
- Result->NumQsts =
- (unsigned) DB_QuerySELECT (&mysql_res,"can not get questions"
- " of a test result",
- "SELECT QstCod," // row[0]
- "Indexes," // row[1]
- "Answers" // row[2]
- " FROM tst_exam_questions"
- " WHERE TstCod=%ld"
- " ORDER BY QstInd",
- TstCod);
-
- /***** Get questions codes *****/
- for (NumQst = 0;
- NumQst < Result->NumQsts;
- NumQst++)
- {
- row = mysql_fetch_row (mysql_res);
-
- /* Get question code */
- if ((Result->Questions[NumQst].QstCod = Str_ConvertStrCodToLongCod (row[0])) < 0)
- Lay_ShowErrorAndExit ("Wrong code of question.");
-
- /* Get indexes for this question (row[1]) */
- Str_Copy (Result->Questions[NumQst].StrIndexes,row[1],
- Tst_MAX_BYTES_INDEXES_ONE_QST);
-
- /* Get answers selected by user for this question (row[2]) */
- Str_Copy (Result->Questions[NumQst].StrAnswers,row[2],
- Tst_MAX_BYTES_ANSWERS_ONE_QST);
-
- /* Replace each comma by a separator of multiple parameters */
- /* In database commas are used as separators instead of special chars */
- Par_ReplaceCommaBySeparatorMultiple (Result->Questions[NumQst].StrIndexes);
- Par_ReplaceCommaBySeparatorMultiple (Result->Questions[NumQst].StrAnswers);
- }
-
- /***** Free structure that stores the query result *****/
- DB_FreeMySQLResult (&mysql_res);
- }
-
-/*****************************************************************************/
-/********************** Remove test results made by a user ********************/
-/*****************************************************************************/
-
-void TsR_RemoveTestResultsMadeByUsrInAllCrss (long UsrCod)
- {
- /***** Remove test results made by the specified user *****/
- DB_QueryDELETE ("can not remove test results made by a user",
- "DELETE FROM tst_exam_questions"
- " USING tst_exams,tst_exam_questions"
- " WHERE tst_exams.UsrCod=%ld"
- " AND tst_exams.TstCod=tst_exam_questions.TstCod",
- UsrCod);
-
- DB_QueryDELETE ("can not remove test results made by a user",
- "DELETE FROM tst_exams"
- " WHERE UsrCod=%ld",
- UsrCod);
- }
-
-/*****************************************************************************/
-/************** Remove test results made by a user in a course ***************/
-/*****************************************************************************/
-
-void TsR_RemoveTestResultsMadeByUsrInCrs (long UsrCod,long CrsCod)
- {
- /***** Remove test results made by the specified user *****/
- DB_QueryDELETE ("can not remove test results made by a user in a course",
- "DELETE FROM tst_exam_questions"
- " USING tst_exams,tst_exam_questions"
- " WHERE tst_exams.CrsCod=%ld AND tst_exams.UsrCod=%ld"
- " AND tst_exams.TstCod=tst_exam_questions.TstCod",
- CrsCod,UsrCod);
-
- DB_QueryDELETE ("can not remove test results made by a user in a course",
- "DELETE FROM tst_exams"
- " WHERE CrsCod=%ld AND UsrCod=%ld",
- CrsCod,UsrCod);
- }
-
-/*****************************************************************************/
-/****************** Remove all test results made in a course *****************/
-/*****************************************************************************/
-
-void TsR_RemoveCrsTestResults (long CrsCod)
- {
- /***** Remove questions of test results made in the course *****/
- DB_QueryDELETE ("can not remove test results made in a course",
- "DELETE FROM tst_exam_questions"
- " USING tst_exams,tst_exam_questions"
- " WHERE tst_exams.CrsCod=%ld"
- " AND tst_exams.TstCod=tst_exam_questions.TstCod",
- CrsCod);
-
- /***** Remove test results made in the course *****/
- DB_QueryDELETE ("can not remove test results made in a course",
- "DELETE FROM tst_exams WHERE CrsCod=%ld",
- CrsCod);
- }
diff --git a/swad_test_result.h b/swad_test_result.h
deleted file mode 100644
index 3a8ab1e5..00000000
--- a/swad_test_result.h
+++ /dev/null
@@ -1,84 +0,0 @@
-// swad_test_results.h: test results
-
-#ifndef _SWAD_TSR
-#define _SWAD_TSR
-/*
- SWAD (Shared Workspace At a Distance in Spanish),
- is a web platform developed at the University of Granada (Spain),
- and used to support university teaching.
-
- This file is part of SWAD core.
- Copyright (C) 1999-2020 Antonio Cañas Vargas
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-/*****************************************************************************/
-/********************************* Headers ***********************************/
-/*****************************************************************************/
-
-#include "swad_user.h"
-
-/*****************************************************************************/
-/***************************** Public constants ******************************/
-/*****************************************************************************/
-
-#define Tst_MAX_OPTIONS_PER_QUESTION 10
-#define Tst_MAX_BYTES_INDEXES_ONE_QST (Tst_MAX_OPTIONS_PER_QUESTION * (3 + 1))
-#define Tst_MAX_BYTES_ANSWERS_ONE_QST (Tst_MAX_OPTIONS_PER_QUESTION * (3 + 1))
-
-#define TsR_SCORE_MAX 10 // Maximum score of a test (10 in Spain). Must be unsigned! // TODO: Make this configurable by teachers
-
-/*****************************************************************************/
-/******************************* Public types ********************************/
-/*****************************************************************************/
-
-struct TsR_Result
- {
- long TstCod; // Exam code
- time_t TimeUTC[Dat_NUM_START_END_TIME];
- unsigned NumQsts; // Number of questions
- unsigned NumQstsNotBlank; // Number of questions not blank
- bool AllowTeachers; // Are teachers allowed to see this test result?
- double Score; // Total score of the test result
- struct
- {
- long QstCod; // Question code
- char StrIndexes[Tst_MAX_BYTES_INDEXES_ONE_QST + 1]; // 0 1 2 3, 3 0 2 1, etc.
- char StrAnswers[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1]; // Answers selected by user
- double Score; // Question score
- bool AnswerIsNotBlank; // Answer not blank?
- } Questions[TstCfg_MAX_QUESTIONS_PER_TEST];
- };
-
-/*****************************************************************************/
-/***************************** Public prototypes *****************************/
-/*****************************************************************************/
-
-void TsR_SelUsrsToViewUsrsTstResults (void);
-void TsR_SelDatesToSeeMyTstResults (void);
-void TsR_ShowMyTstResults (void);
-void TsR_CreateTestResultInDB (struct TsR_Result *Result);
-void TsR_UpdateScoreOfTestResultInDB (const struct TsR_Result *Result);
-void TsR_GetUsrsAndShowTstResults (void);
-void TsR_ShowOneTstResult (void);
-void TsR_ShowTestResult (struct UsrData *UsrDat,
- struct TsR_Result *Result,
- unsigned Visibility);
-void TsR_StoreOneTestResultQstInDB (const struct TsR_Result *Result,
- unsigned NumQst);
-void TsR_RemoveTestResultsMadeByUsrInAllCrss (long UsrCod);
-void TsR_RemoveTestResultsMadeByUsrInCrs (long UsrCod,long CrsCod);
-void TsR_RemoveCrsTestResults (long CrsCod);
-
-#endif
diff --git a/swad_test_visibility.c b/swad_test_visibility.c
index 3f2171a4..ae722514 100644
--- a/swad_test_visibility.c
+++ b/swad_test_visibility.c
@@ -1,4 +1,4 @@
-// swad_test_visibility.c: visibility of test results
+// swad_test_visibility.c: visibility of test exams
/*
SWAD (Shared Workspace At a Distance),
@@ -63,33 +63,33 @@ extern struct Globals Gbl;
/******************************* Show visibility *****************************/
/*****************************************************************************/
-void TsV_ShowVisibilityIcons (unsigned SelectedVisibility,bool Hidden)
+void TstVis_ShowVisibilityIcons (unsigned SelectedVisibility,bool Hidden)
{
- extern const char *Txt_TST_STR_VISIBILITY[TsV_NUM_ITEMS_VISIBILITY];
+ extern const char *Txt_TST_STR_VISIBILITY[TstVis_NUM_ITEMS_VISIBILITY];
extern const char *Txt_TST_HIDDEN_VISIBLE[2];
- static const char *Icons[TsV_NUM_ITEMS_VISIBILITY][2] =
+ static const char *Icons[TstVis_NUM_ITEMS_VISIBILITY][2] =
{
- [TsV_VISIBLE_QST_ANS_TXT ][false] = "file-alt-red.svg",
- [TsV_VISIBLE_QST_ANS_TXT ][true ] = "file-alt-green.svg",
+ [TstVis_VISIBLE_QST_ANS_TXT ][false] = "file-alt-red.svg",
+ [TstVis_VISIBLE_QST_ANS_TXT ][true ] = "file-alt-green.svg",
- [TsV_VISIBLE_FEEDBACK_TXT ][false] = "file-signature-red.svg",
- [TsV_VISIBLE_FEEDBACK_TXT ][true ] = "file-signature-green.svg",
+ [TstVis_VISIBLE_FEEDBACK_TXT ][false] = "file-signature-red.svg",
+ [TstVis_VISIBLE_FEEDBACK_TXT ][true ] = "file-signature-green.svg",
- [TsV_VISIBLE_CORRECT_ANSWER][false] = "spell-check-red.svg",
- [TsV_VISIBLE_CORRECT_ANSWER][true ] = "spell-check-green.svg",
+ [TstVis_VISIBLE_CORRECT_ANSWER][false] = "spell-check-red.svg",
+ [TstVis_VISIBLE_CORRECT_ANSWER][true ] = "spell-check-green.svg",
- [TsV_VISIBLE_EACH_QST_SCORE][false] = "tasks-red.svg",
- [TsV_VISIBLE_EACH_QST_SCORE][true ] = "tasks-green.svg",
+ [TstVis_VISIBLE_EACH_QST_SCORE][false] = "tasks-red.svg",
+ [TstVis_VISIBLE_EACH_QST_SCORE][true ] = "tasks-green.svg",
- [TsV_VISIBLE_TOTAL_SCORE ][false] = "check-circle-regular-red.svg",
- [TsV_VISIBLE_TOTAL_SCORE ][true ] = "check-circle-regular-green.svg",
+ [TstVis_VISIBLE_TOTAL_SCORE ][false] = "check-circle-regular-red.svg",
+ [TstVis_VISIBLE_TOTAL_SCORE ][true ] = "check-circle-regular-green.svg",
};
- TsV_Visibility_t Visibility;
+ TstVis_Visibility_t Visibility;
bool ItemVisible;
char *Title;
- for (Visibility = (TsV_Visibility_t) 0;
- Visibility <= (TsV_Visibility_t) (TsV_NUM_ITEMS_VISIBILITY - 1);
+ for (Visibility = (TstVis_Visibility_t) 0;
+ Visibility <= (TstVis_Visibility_t) (TstVis_NUM_ITEMS_VISIBILITY - 1);
Visibility++)
{
ItemVisible = (SelectedVisibility & (1 << Visibility)) != 0;
@@ -106,25 +106,25 @@ void TsV_ShowVisibilityIcons (unsigned SelectedVisibility,bool Hidden)
}
/*****************************************************************************/
-/************ Put checkboxes in form to select result visibility *************/
+/************* Put checkboxes in form to select exam visibility **************/
/*****************************************************************************/
-void TsV_PutVisibilityCheckboxes (unsigned SelectedVisibility)
+void TstVis_PutVisibilityCheckboxes (unsigned SelectedVisibility)
{
- extern const char *Txt_TST_STR_VISIBILITY[TsV_NUM_ITEMS_VISIBILITY];
- static const char *Icons[TsV_NUM_ITEMS_VISIBILITY] =
+ extern const char *Txt_TST_STR_VISIBILITY[TstVis_NUM_ITEMS_VISIBILITY];
+ static const char *Icons[TstVis_NUM_ITEMS_VISIBILITY] =
{
- [TsV_VISIBLE_QST_ANS_TXT ] = "file-alt.svg",
- [TsV_VISIBLE_FEEDBACK_TXT ] = "file-signature.svg",
- [TsV_VISIBLE_CORRECT_ANSWER] = "spell-check.svg",
- [TsV_VISIBLE_EACH_QST_SCORE] = "tasks.svg",
- [TsV_VISIBLE_TOTAL_SCORE ] = "check-circle-regular.svg",
+ [TstVis_VISIBLE_QST_ANS_TXT ] = "file-alt.svg",
+ [TstVis_VISIBLE_FEEDBACK_TXT ] = "file-signature.svg",
+ [TstVis_VISIBLE_CORRECT_ANSWER] = "spell-check.svg",
+ [TstVis_VISIBLE_EACH_QST_SCORE] = "tasks.svg",
+ [TstVis_VISIBLE_TOTAL_SCORE ] = "check-circle-regular.svg",
};
- TsV_Visibility_t Visibility;
+ TstVis_Visibility_t Visibility;
bool ItemVisible;
- for (Visibility = (TsV_Visibility_t) 0;
- Visibility <= (TsV_Visibility_t) (TsV_NUM_ITEMS_VISIBILITY - 1);
+ for (Visibility = (TstVis_Visibility_t) 0;
+ Visibility <= (TstVis_Visibility_t) (TstVis_NUM_ITEMS_VISIBILITY - 1);
Visibility++)
{
ItemVisible = (SelectedVisibility & (1 << Visibility)) != 0;
@@ -145,18 +145,18 @@ void TsV_PutVisibilityCheckboxes (unsigned SelectedVisibility)
/************************** Get visibility from form *************************/
/*****************************************************************************/
-unsigned TsV_GetVisibilityFromForm (void)
+unsigned TstVis_GetVisibilityFromForm (void)
{
size_t MaxSizeListVisibilitySelected;
char *StrVisibilitySelected;
const char *Ptr;
char UnsignedStr[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
unsigned UnsignedNum;
- TsV_Visibility_t VisibilityItem;
+ TstVis_Visibility_t VisibilityItem;
unsigned Visibility = 0; // Nothing selected
/***** Allocate memory for list of attendance events selected *****/
- MaxSizeListVisibilitySelected = TsV_NUM_ITEMS_VISIBILITY * (Cns_MAX_DECIMAL_DIGITS_UINT + 1);
+ MaxSizeListVisibilitySelected = TstVis_NUM_ITEMS_VISIBILITY * (Cns_MAX_DECIMAL_DIGITS_UINT + 1);
if ((StrVisibilitySelected = (char *) malloc (MaxSizeListVisibilitySelected + 1)) == NULL)
Lay_NotEnoughMemoryExit ();
@@ -172,9 +172,9 @@ unsigned TsV_GetVisibilityFromForm (void)
/* Get next visibility item selected */
Par_GetNextStrUntilSeparParamMult (&Ptr,UnsignedStr,Cns_MAX_DECIMAL_DIGITS_UINT);
if (sscanf (UnsignedStr,"%u",&UnsignedNum) == 1)
- if (UnsignedNum < TsV_NUM_ITEMS_VISIBILITY)
+ if (UnsignedNum < TstVis_NUM_ITEMS_VISIBILITY)
{
- VisibilityItem = (TsV_Visibility_t) UnsignedNum;
+ VisibilityItem = (TstVis_Visibility_t) UnsignedNum;
Visibility |= (1 << VisibilityItem);
}
}
@@ -186,16 +186,16 @@ unsigned TsV_GetVisibilityFromForm (void)
/************************** Get visibility from string *************************/
/*****************************************************************************/
-unsigned TsV_GetVisibilityFromStr (const char *Str)
+unsigned TstVis_GetVisibilityFromStr (const char *Str)
{
unsigned UnsignedNum;
- unsigned Visibility = TsV_MIN_VISIBILITY; // In nothing is read, return minimum visibility
+ unsigned Visibility = TstVis_MIN_VISIBILITY; // In nothing is read, return minimum visibility
/***** Get visibility from string *****/
if (Str)
if (Str[0])
if (sscanf (Str,"%u",&UnsignedNum) == 1)
- Visibility = UnsignedNum & TsV_MAX_VISIBILITY;
+ Visibility = UnsignedNum & TstVis_MAX_VISIBILITY;
return Visibility;
}
@@ -204,27 +204,27 @@ unsigned TsV_GetVisibilityFromStr (const char *Str)
/***************************** Get visibility items **************************/
/*****************************************************************************/
-bool TsV_IsVisibleQstAndAnsTxt (unsigned Visibility)
+bool TstVis_IsVisibleQstAndAnsTxt (unsigned Visibility)
{
- return (Visibility & (1 << TsV_VISIBLE_QST_ANS_TXT)) != 0;
+ return (Visibility & (1 << TstVis_VISIBLE_QST_ANS_TXT)) != 0;
}
-bool TsV_IsVisibleFeedbackTxt (unsigned Visibility)
+bool TstVis_IsVisibleFeedbackTxt (unsigned Visibility)
{
- return (Visibility & (1 << TsV_VISIBLE_FEEDBACK_TXT)) != 0;
+ return (Visibility & (1 << TstVis_VISIBLE_FEEDBACK_TXT)) != 0;
}
-bool TsV_IsVisibleCorrectAns (unsigned Visibility)
+bool TstVis_IsVisibleCorrectAns (unsigned Visibility)
{
- return (Visibility & (1 << TsV_VISIBLE_CORRECT_ANSWER)) != 0;
+ return (Visibility & (1 << TstVis_VISIBLE_CORRECT_ANSWER)) != 0;
}
-bool TsV_IsVisibleEachQstScore (unsigned Visibility)
+bool TstVis_IsVisibleEachQstScore (unsigned Visibility)
{
- return (Visibility & (1 << TsV_VISIBLE_EACH_QST_SCORE)) != 0;
+ return (Visibility & (1 << TstVis_VISIBLE_EACH_QST_SCORE)) != 0;
}
-bool TsV_IsVisibleTotalScore (unsigned Visibility)
+bool TstVis_IsVisibleTotalScore (unsigned Visibility)
{
- return (Visibility & (1 << TsV_VISIBLE_TOTAL_SCORE)) != 0;
+ return (Visibility & (1 << TstVis_VISIBLE_TOTAL_SCORE)) != 0;
}
diff --git a/swad_test_visibility.h b/swad_test_visibility.h
index cecaa440..90d01d59 100644
--- a/swad_test_visibility.h
+++ b/swad_test_visibility.h
@@ -1,7 +1,7 @@
// swad_test_visibility.h: visibility of test results
-#ifndef _SWAD_TSV
-#define _SWAD_TSV
+#ifndef _SWAD_TST_VIS
+#define _SWAD_TST_VIS
/*
SWAD (Shared Workspace At a Distance in Spanish),
is a web platform developed at the University of Granada (Spain),
@@ -37,32 +37,32 @@
/******************************* Public types ********************************/
/*****************************************************************************/
-#define TsV_NUM_ITEMS_VISIBILITY 5
+#define TstVis_NUM_ITEMS_VISIBILITY 5
typedef enum
{
- TsV_VISIBLE_QST_ANS_TXT = 0, // Questions and answers text
- TsV_VISIBLE_FEEDBACK_TXT = 1, // Feedback text
- TsV_VISIBLE_CORRECT_ANSWER = 2, // Correct answers
- TsV_VISIBLE_EACH_QST_SCORE = 3, // Score of each question
- TsV_VISIBLE_TOTAL_SCORE = 4, // Total score
- } TsV_Visibility_t;
-#define TsV_MIN_VISIBILITY 0 // Nothing visible
-#define TsV_MAX_VISIBILITY ((1 << TsV_NUM_ITEMS_VISIBILITY) - 1) // All visible
-#define TsV_VISIBILITY_DEFAULT TsV_MAX_VISIBILITY
+ TstVis_VISIBLE_QST_ANS_TXT = 0, // Questions and answers text
+ TstVis_VISIBLE_FEEDBACK_TXT = 1, // Feedback text
+ TstVis_VISIBLE_CORRECT_ANSWER = 2, // Correct answers
+ TstVis_VISIBLE_EACH_QST_SCORE = 3, // Score of each question
+ TstVis_VISIBLE_TOTAL_SCORE = 4, // Total score
+ } TstVis_Visibility_t;
+#define TstVis_MIN_VISIBILITY 0 // Nothing visible
+#define TstVis_MAX_VISIBILITY ((1 << TstVis_NUM_ITEMS_VISIBILITY) - 1) // All visible
+#define TstVis_VISIBILITY_DEFAULT TstVis_MAX_VISIBILITY
/*****************************************************************************/
/***************************** Public prototypes *****************************/
/*****************************************************************************/
-void TsV_ShowVisibilityIcons (unsigned SelectedVisibility,bool Hidden);
-void TsV_PutVisibilityCheckboxes (unsigned SelectedVisibility);
-unsigned TsV_GetVisibilityFromForm (void);
-unsigned TsV_GetVisibilityFromStr (const char *Str);
+void TstVis_ShowVisibilityIcons (unsigned SelectedVisibility,bool Hidden);
+void TstVis_PutVisibilityCheckboxes (unsigned SelectedVisibility);
+unsigned TstVis_GetVisibilityFromForm (void);
+unsigned TstVis_GetVisibilityFromStr (const char *Str);
-bool TsV_IsVisibleQstAndAnsTxt (unsigned Visibility);
-bool TsV_IsVisibleFeedbackTxt (unsigned Visibility);
-bool TsV_IsVisibleCorrectAns (unsigned Visibility);
-bool TsV_IsVisibleEachQstScore (unsigned Visibility);
-bool TsV_IsVisibleTotalScore (unsigned Visibility);
+bool TstVis_IsVisibleQstAndAnsTxt (unsigned Visibility);
+bool TstVis_IsVisibleFeedbackTxt (unsigned Visibility);
+bool TstVis_IsVisibleCorrectAns (unsigned Visibility);
+bool TstVis_IsVisibleEachQstScore (unsigned Visibility);
+bool TstVis_IsVisibleTotalScore (unsigned Visibility);
#endif
diff --git a/swad_text.c b/swad_text.c
index a8d14977..6299e326 100644
--- a/swad_text.c
+++ b/swad_text.c
@@ -51846,9 +51846,9 @@ const char *Txt_TST_STR_ORDER_SHORT[Tst_NUM_TYPES_ORDER_QST] =
#endif
};
-const char *Txt_TST_STR_VISIBILITY[TsV_NUM_ITEMS_VISIBILITY] =
+const char *Txt_TST_STR_VISIBILITY[TstVis_NUM_ITEMS_VISIBILITY] =
{
- [TsV_VISIBLE_QST_ANS_TXT] =
+ [TstVis_VISIBLE_QST_ANS_TXT] =
#if L==1 // ca
"Text de preguntes i respostes"
#elif L==2 // de
@@ -51869,7 +51869,7 @@ const char *Txt_TST_STR_VISIBILITY[TsV_NUM_ITEMS_VISIBILITY] =
"Texto de perguntas e respostas"
#endif
,
- [TsV_VISIBLE_FEEDBACK_TXT] =
+ [TstVis_VISIBLE_FEEDBACK_TXT] =
#if L==1 // ca
"Text de realimentació"
#elif L==2 // de
@@ -51890,7 +51890,7 @@ const char *Txt_TST_STR_VISIBILITY[TsV_NUM_ITEMS_VISIBILITY] =
"Texto de feedback"
#endif
,
- [TsV_VISIBLE_CORRECT_ANSWER] =
+ [TstVis_VISIBLE_CORRECT_ANSWER] =
#if L==1 // ca
"Respostes correctes"
#elif L==2 // de
@@ -51911,7 +51911,7 @@ const char *Txt_TST_STR_VISIBILITY[TsV_NUM_ITEMS_VISIBILITY] =
"Respostas corretas"
#endif
,
- [TsV_VISIBLE_EACH_QST_SCORE] =
+ [TstVis_VISIBLE_EACH_QST_SCORE] =
#if L==1 // ca
"Puntuació de cada pregunta"
#elif L==2 // de
@@ -51932,7 +51932,7 @@ const char *Txt_TST_STR_VISIBILITY[TsV_NUM_ITEMS_VISIBILITY] =
"Pontuação de cada pergunta"
#endif
,
- [TsV_VISIBLE_TOTAL_SCORE] =
+ [TstVis_VISIBLE_TOTAL_SCORE] =
#if L==1 // ca
"Puntuació total"
#elif L==2 // de