mirror of https://github.com/acanas/swad-core.git
Version19.226
This commit is contained in:
parent
d9c02b0523
commit
1a0d5b4471
33
sql/swad.sql
33
sql/swad.sql
|
@ -551,13 +551,6 @@ CREATE TABLE IF NOT EXISTS exa_prints (
|
||||||
UNIQUE INDEX(PrnCod),
|
UNIQUE INDEX(PrnCod),
|
||||||
UNIQUE INDEX(EvtCod,UsrCod));
|
UNIQUE INDEX(EvtCod,UsrCod));
|
||||||
--
|
--
|
||||||
-- Table exa_questions: stores the questions in the set of questions for exams
|
|
||||||
--
|
|
||||||
CREATE TABLE IF NOT EXISTS exa_questions (
|
|
||||||
SetCod INT NOT NULL,
|
|
||||||
QstCod INT NOT NULL,
|
|
||||||
UNIQUE INDEX(SetCod,QstCod));
|
|
||||||
--
|
|
||||||
-- Table exa_results: stores exam results
|
-- Table exa_results: stores exam results
|
||||||
--
|
--
|
||||||
CREATE TABLE IF NOT EXISTS exa_results (
|
CREATE TABLE IF NOT EXISTS exa_results (
|
||||||
|
@ -570,6 +563,32 @@ CREATE TABLE IF NOT EXISTS exa_results (
|
||||||
Score DOUBLE PRECISION NOT NULL DEFAULT 0,
|
Score DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||||
UNIQUE INDEX(EvtCod,UsrCod));
|
UNIQUE INDEX(EvtCod,UsrCod));
|
||||||
--
|
--
|
||||||
|
-- Table exa_set_answers: stores the answers of questions in exam sets
|
||||||
|
--
|
||||||
|
CREATE TABLE IF NOT EXISTS exa_set_answers (
|
||||||
|
QstCod INT NOT NULL,
|
||||||
|
AnsInd TINYINT NOT NULL,
|
||||||
|
Answer TEXT NOT NULL,
|
||||||
|
Feedback TEXT NOT NULL,
|
||||||
|
MedCod INT NOT NULL DEFAULT -1,
|
||||||
|
Correct ENUM('N','Y') NOT NULL,
|
||||||
|
UNIQUE INDEX(QstCod,AnsInd),
|
||||||
|
INDEX(MedCod));
|
||||||
|
--
|
||||||
|
-- Table exa_set_questions: stores the questions in exam sets
|
||||||
|
--
|
||||||
|
CREATE TABLE IF NOT EXISTS exa_set_questions (
|
||||||
|
QstCod INT NOT NULL AUTO_INCREMENT,
|
||||||
|
SetCod INT NOT NULL,
|
||||||
|
AnsType ENUM ('int','float','true_false','unique_choice','multiple_choice','text') NOT NULL,
|
||||||
|
Shuffle ENUM('N','Y') NOT NULL,
|
||||||
|
Stem TEXT NOT NULL,
|
||||||
|
Feedback TEXT NOT NULL,
|
||||||
|
MedCod INT NOT NULL DEFAULT -1,
|
||||||
|
UNIQUE INDEX(QstCod),
|
||||||
|
UNIQUE INDEX(SetCod,QstCod),
|
||||||
|
INDEX(MedCod));
|
||||||
|
--
|
||||||
-- Table exa_sets: stores the question sets in the exams
|
-- Table exa_sets: stores the question sets in the exams
|
||||||
--
|
--
|
||||||
CREATE TABLE IF NOT EXISTS exa_sets (
|
CREATE TABLE IF NOT EXISTS exa_sets (
|
||||||
|
|
|
@ -548,10 +548,33 @@ enscript -2 --landscape --color --file-align=2 --highlight --line-numbers -o - *
|
||||||
En OpenSWAD:
|
En OpenSWAD:
|
||||||
ps2pdf source.ps destination.pdf
|
ps2pdf source.ps destination.pdf
|
||||||
*/
|
*/
|
||||||
#define Log_PLATFORM_VERSION "SWAD 19.225.1 (2020-05-12)"
|
#define Log_PLATFORM_VERSION "SWAD 19.226 (2020-05-12)"
|
||||||
#define CSS_FILE "swad19.217.css"
|
#define CSS_FILE "swad19.217.css"
|
||||||
#define JS_FILE "swad19.223.js"
|
#define JS_FILE "swad19.223.js"
|
||||||
/*
|
/*
|
||||||
|
// TODO: Public link to images on exams should be cached during the current session. When session is closed ==> remove public link
|
||||||
|
|
||||||
|
Version 19.226: May 12, 2020 Questions and answer are cloned from test bank to exams. (303468 lines)
|
||||||
|
18 changes necessary in database:
|
||||||
|
CREATE TABLE IF NOT EXISTS exa_set_answers (QstCod INT NOT NULL,AnsInd TINYINT NOT NULL,Answer TEXT NOT NULL,Feedback TEXT NOT NULL,MedCod INT NOT NULL DEFAULT -1,Correct ENUM('N','Y') NOT NULL,UNIQUE INDEX(QstCod,AnsInd),INDEX(MedCod));
|
||||||
|
CREATE TABLE IF NOT EXISTS exa_set_questions (QstCod INT NOT NULL AUTO_INCREMENT,SetCod INT NOT NULL,AnsType ENUM ('int','float','true_false','unique_choice','multiple_choice','text') NOT NULL,Shuffle ENUM('N','Y') NOT NULL,Stem TEXT NOT NULL,Feedback TEXT NOT NULL,MedCod INT NOT NULL DEFAULT -1,UNIQUE INDEX(QstCod),UNIQUE INDEX(SetCod,QstCod),INDEX(MedCod));
|
||||||
|
DELETE FROM exa_answers;
|
||||||
|
DELETE FROM exa_events;
|
||||||
|
DELETE FROM exa_exams;
|
||||||
|
DELETE FROM exa_groups;
|
||||||
|
DELETE FROM exa_happening;
|
||||||
|
DELETE FROM exa_indexes;
|
||||||
|
DELETE FROM exa_participants;
|
||||||
|
DELETE FROM exa_print_questions;
|
||||||
|
DELETE FROM exa_prints;
|
||||||
|
DELETE FROM exa_questions;
|
||||||
|
DELETE FROM exa_results;
|
||||||
|
DELETE FROM exa_set_answers;
|
||||||
|
DELETE FROM exa_set_questions;
|
||||||
|
DELETE FROM exa_sets;
|
||||||
|
DELETE FROM exa_times;
|
||||||
|
DROP TABLE exa_questions;
|
||||||
|
|
||||||
Version 19.225.1: May 12, 2020 Code refactoring in exam prints.
|
Version 19.225.1: May 12, 2020 Code refactoring in exam prints.
|
||||||
Unckeck radio button in exam prints. (303057 lines)
|
Unckeck radio button in exam prints. (303057 lines)
|
||||||
Version 19.225: May 12, 2020 Stored unique/multiple choice questions in exam print.
|
Version 19.225: May 12, 2020 Stored unique/multiple choice questions in exam print.
|
||||||
|
|
|
@ -1216,22 +1216,6 @@ mysql> DESCRIBE exa_prints;
|
||||||
"UNIQUE INDEX(PrnCod),"
|
"UNIQUE INDEX(PrnCod),"
|
||||||
"UNIQUE INDEX(EvtCod,UsrCod))");
|
"UNIQUE INDEX(EvtCod,UsrCod))");
|
||||||
|
|
||||||
/***** Table exa_questions *****/
|
|
||||||
/*
|
|
||||||
mysql> DESCRIBE exa_questions;
|
|
||||||
+--------+---------+------+-----+---------+-------+
|
|
||||||
| Field | Type | Null | Key | Default | Extra |
|
|
||||||
+--------+---------+------+-----+---------+-------+
|
|
||||||
| SetCod | int(11) | NO | PRI | NULL | |
|
|
||||||
| QstCod | int(11) | NO | PRI | NULL | |
|
|
||||||
+--------+---------+------+-----+---------+-------+
|
|
||||||
2 rows in set (0.00 sec)
|
|
||||||
*/
|
|
||||||
DB_CreateTable ("CREATE TABLE IF NOT EXISTS exa_questions ("
|
|
||||||
"SetCod INT NOT NULL,"
|
|
||||||
"QstCod INT NOT NULL,"
|
|
||||||
"UNIQUE INDEX(SetCod,QstCod))");
|
|
||||||
|
|
||||||
/***** Table exa_results *****/
|
/***** Table exa_results *****/
|
||||||
/*
|
/*
|
||||||
mysql> DESCRIBE exa_results;
|
mysql> DESCRIBE exa_results;
|
||||||
|
@ -1258,6 +1242,59 @@ mysql> DESCRIBE exa_results;
|
||||||
"Score DOUBLE PRECISION NOT NULL DEFAULT 0,"
|
"Score DOUBLE PRECISION NOT NULL DEFAULT 0,"
|
||||||
"UNIQUE INDEX(EvtCod,UsrCod))");
|
"UNIQUE INDEX(EvtCod,UsrCod))");
|
||||||
|
|
||||||
|
/***** Table exa_set_answers *****/
|
||||||
|
/*
|
||||||
|
mysql> DESCRIBE exa_set_answers;
|
||||||
|
+----------+---------------+------+-----+---------+-------+
|
||||||
|
| Field | Type | Null | Key | Default | Extra |
|
||||||
|
+----------+---------------+------+-----+---------+-------+
|
||||||
|
| QstCod | int(11) | NO | PRI | NULL | |
|
||||||
|
| AnsInd | tinyint(4) | NO | PRI | NULL | |
|
||||||
|
| Answer | text | NO | | NULL | |
|
||||||
|
| Feedback | text | NO | | NULL | |
|
||||||
|
| MedCod | int(11) | NO | MUL | -1 | |
|
||||||
|
| Correct | enum('N','Y') | NO | | NULL | |
|
||||||
|
+----------+---------------+------+-----+---------+-------+
|
||||||
|
6 rows in set (0.00 sec)
|
||||||
|
*/
|
||||||
|
DB_CreateTable ("CREATE TABLE IF NOT EXISTS exa_set_answers ("
|
||||||
|
"QstCod INT NOT NULL,"
|
||||||
|
"AnsInd TINYINT NOT NULL,"
|
||||||
|
"Answer TEXT NOT NULL," // Tst_MAX_BYTES_ANSWER_OR_FEEDBACK
|
||||||
|
"Feedback TEXT NOT NULL," // Tst_MAX_BYTES_ANSWER_OR_FEEDBACK
|
||||||
|
"MedCod INT NOT NULL DEFAULT -1,"
|
||||||
|
"Correct ENUM('N','Y') NOT NULL,"
|
||||||
|
"UNIQUE INDEX(QstCod,AnsInd),"
|
||||||
|
"INDEX(MedCod))");
|
||||||
|
|
||||||
|
/***** Table exa_set_questions *****/
|
||||||
|
/*
|
||||||
|
mysql> DESCRIBE exa_set_questions;
|
||||||
|
+----------+---------------+------+-----+---------+-------+
|
||||||
|
| Field | Type | Null | Key | Default | Extra |
|
||||||
|
+----------+---------------+------+-----+---------+-------+
|
||||||
|
| QstCod | int(11) | NO | PRI | NULL | |
|
||||||
|
| AnsInd | tinyint(4) | NO | PRI | NULL | |
|
||||||
|
| Answer | text | NO | | NULL | |
|
||||||
|
| Feedback | text | NO | | NULL | |
|
||||||
|
| MedCod | int(11) | NO | MUL | -1 | |
|
||||||
|
| Correct | enum('N','Y') | NO | | NULL | |
|
||||||
|
+----------+---------------+------+-----+---------+-------+
|
||||||
|
6 rows in set (0.00 sec)
|
||||||
|
|
||||||
|
*/
|
||||||
|
DB_CreateTable ("CREATE TABLE IF NOT EXISTS exa_set_questions ("
|
||||||
|
"QstCod INT NOT NULL AUTO_INCREMENT,"
|
||||||
|
"SetCod INT NOT NULL,"
|
||||||
|
"AnsType ENUM ('int','float','true_false','unique_choice','multiple_choice','text') NOT NULL,"
|
||||||
|
"Shuffle ENUM('N','Y') NOT NULL,"
|
||||||
|
"Stem TEXT NOT NULL," // Cns_MAX_BYTES_TEXT
|
||||||
|
"Feedback TEXT NOT NULL," // Cns_MAX_BYTES_TEXT
|
||||||
|
"MedCod INT NOT NULL DEFAULT -1,"
|
||||||
|
"UNIQUE INDEX(QstCod),"
|
||||||
|
"UNIQUE INDEX(SetCod,QstCod),"
|
||||||
|
"INDEX(MedCod))");
|
||||||
|
|
||||||
/***** Table exa_sets *****/
|
/***** Table exa_sets *****/
|
||||||
/*
|
/*
|
||||||
mysql> DESCRIBE exa_sets;
|
mysql> DESCRIBE exa_sets;
|
||||||
|
|
94
swad_exam.c
94
swad_exam.c
|
@ -1181,9 +1181,18 @@ static void Exa_RemoveExamFromAllTables (long ExaCod)
|
||||||
/***** Remove all events in this exam *****/
|
/***** Remove all events in this exam *****/
|
||||||
ExaEvt_RemoveEventsInExamFromAllTables (ExaCod);
|
ExaEvt_RemoveEventsInExamFromAllTables (ExaCod);
|
||||||
|
|
||||||
/***** Remove exam question *****/
|
/***** Remove exam questions *****/
|
||||||
DB_QueryDELETE ("can not remove exam questions",
|
DB_QueryDELETE ("can not remove exam questions",
|
||||||
"DELETE FROM exa_questions WHERE ExaCod=%ld",
|
"DELETE FROM exa_set_questions"
|
||||||
|
" USING exa_sets,exa_set_questions"
|
||||||
|
" WHERE exa_sets.ExaCod=%ld"
|
||||||
|
" AND exa_sets.SetCod=exa_set_questions.SetCod",
|
||||||
|
ExaCod);
|
||||||
|
|
||||||
|
/***** Remove exam sets *****/
|
||||||
|
DB_QueryDELETE ("can not remove exam sets",
|
||||||
|
"DELETE FROM exa_sets"
|
||||||
|
" WHERE ExaCod=%ld",
|
||||||
ExaCod);
|
ExaCod);
|
||||||
|
|
||||||
/***** Remove exam *****/
|
/***** Remove exam *****/
|
||||||
|
@ -1203,10 +1212,19 @@ void Exa_RemoveExamsCrs (long CrsCod)
|
||||||
|
|
||||||
/***** Remove the questions in exams *****/
|
/***** Remove the questions in exams *****/
|
||||||
DB_QueryDELETE ("can not remove questions in course exams",
|
DB_QueryDELETE ("can not remove questions in course exams",
|
||||||
"DELETE FROM exa_questions"
|
"DELETE FROM exa_set_questions"
|
||||||
" USING exa_exams,exa_questions"
|
" USING exa_exams,exa_sets,exa_set_questions"
|
||||||
" WHERE exa_exams.CrsCod=%ld"
|
" WHERE exa_exams.CrsCod=%ld"
|
||||||
" AND exa_exams.ExaCod=exa_questions.ExaCod",
|
" AND exa_exams.ExaCod=exa_sets.ExaCod",
|
||||||
|
" AND exa_sets.SetCod=exa_set_questions.SetCod",
|
||||||
|
CrsCod);
|
||||||
|
|
||||||
|
/***** Remove the sets in exams *****/
|
||||||
|
DB_QueryDELETE ("can not remove sets in course exams",
|
||||||
|
"DELETE FROM exa_sets"
|
||||||
|
" USING exa_exams,exa_sets"
|
||||||
|
" WHERE exa_exams.CrsCod=%ld"
|
||||||
|
" AND exa_exams.ExaCod=exa_sets.ExaCod",
|
||||||
CrsCod);
|
CrsCod);
|
||||||
|
|
||||||
/***** Remove the exams *****/
|
/***** Remove the exams *****/
|
||||||
|
@ -1678,7 +1696,7 @@ long Exa_GetQstCodFromQstInd (long ExaCod,unsigned QstInd)
|
||||||
|
|
||||||
/***** Get question code of the question to be moved up *****/
|
/***** Get question code of the question to be moved up *****/
|
||||||
if (!DB_QuerySELECT (&mysql_res,"can not get question code",
|
if (!DB_QuerySELECT (&mysql_res,"can not get question code",
|
||||||
"SELECT QstCod FROM exa_questions"
|
"SELECT QstCod FROM exa_set_questions"
|
||||||
" WHERE ExaCod=%ld AND QstInd=%u",
|
" WHERE ExaCod=%ld AND QstInd=%u",
|
||||||
ExaCod,QstInd))
|
ExaCod,QstInd))
|
||||||
Lay_ShowErrorAndExit ("Error: wrong question index.");
|
Lay_ShowErrorAndExit ("Error: wrong question index.");
|
||||||
|
@ -1710,7 +1728,7 @@ unsigned Exa_GetPrevQuestionIndexInExam (long ExaCod,unsigned QstInd)
|
||||||
// Although indexes are always continuous...
|
// Although indexes are always continuous...
|
||||||
// ...this implementation works even with non continuous indexes
|
// ...this implementation works even with non continuous indexes
|
||||||
if (!DB_QuerySELECT (&mysql_res,"can not get previous question index",
|
if (!DB_QuerySELECT (&mysql_res,"can not get previous question index",
|
||||||
"SELECT MAX(QstInd) FROM exa_questions"
|
"SELECT MAX(QstInd) FROM exa_set_questions"
|
||||||
" WHERE ExaCod=%ld AND QstInd<%u",
|
" WHERE ExaCod=%ld AND QstInd<%u",
|
||||||
ExaCod,QstInd))
|
ExaCod,QstInd))
|
||||||
Lay_ShowErrorAndExit ("Error: previous question index not found.");
|
Lay_ShowErrorAndExit ("Error: previous question index not found.");
|
||||||
|
@ -1744,7 +1762,7 @@ unsigned Exa_GetNextQuestionIndexInExam (long ExaCod,unsigned QstInd)
|
||||||
// Although indexes are always continuous...
|
// Although indexes are always continuous...
|
||||||
// ...this implementation works even with non continuous indexes
|
// ...this implementation works even with non continuous indexes
|
||||||
if (!DB_QuerySELECT (&mysql_res,"can not get next question index",
|
if (!DB_QuerySELECT (&mysql_res,"can not get next question index",
|
||||||
"SELECT MIN(QstInd) FROM exa_questions"
|
"SELECT MIN(QstInd) FROM exa_set_questions"
|
||||||
" WHERE ExaCod=%ld AND QstInd>%u",
|
" WHERE ExaCod=%ld AND QstInd>%u",
|
||||||
ExaCod,QstInd))
|
ExaCod,QstInd))
|
||||||
Lay_ShowErrorAndExit ("Error: next question index not found.");
|
Lay_ShowErrorAndExit ("Error: next question index not found.");
|
||||||
|
@ -1964,69 +1982,69 @@ double Exa_GetNumQstsPerCrsExam (Hie_Level_t Scope)
|
||||||
case Hie_SYS:
|
case Hie_SYS:
|
||||||
DB_QuerySELECT (&mysql_res,"can not get number of questions per exam",
|
DB_QuerySELECT (&mysql_res,"can not get number of questions per exam",
|
||||||
"SELECT AVG(NumQsts) FROM"
|
"SELECT AVG(NumQsts) FROM"
|
||||||
" (SELECT COUNT(exa_questions.QstCod) AS NumQsts"
|
" (SELECT COUNT(exa_set_questions.QstCod) AS NumQsts"
|
||||||
" FROM exa_exams,exa_questions"
|
" FROM exa_exams,exa_set_questions"
|
||||||
" WHERE exa_exams.ExaCod=exa_questions.ExaCod"
|
" WHERE exa_exams.ExaCod=exa_set_questions.ExaCod"
|
||||||
" GROUP BY exa_questions.ExaCod) AS NumQstsTable");
|
" GROUP BY exa_set_questions.ExaCod) AS NumQstsTable");
|
||||||
break;
|
break;
|
||||||
case Hie_CTY:
|
case Hie_CTY:
|
||||||
DB_QuerySELECT (&mysql_res,"can not get number of questions per exam",
|
DB_QuerySELECT (&mysql_res,"can not get number of questions per exam",
|
||||||
"SELECT AVG(NumQsts) FROM"
|
"SELECT AVG(NumQsts) FROM"
|
||||||
" (SELECT COUNT(exa_questions.QstCod) AS NumQsts"
|
" (SELECT COUNT(exa_set_questions.QstCod) AS NumQsts"
|
||||||
" FROM institutions,centres,degrees,courses,exa_exams,exa_questions"
|
" FROM institutions,centres,degrees,courses,exa_exams,exa_set_questions"
|
||||||
" WHERE institutions.CtyCod=%ld"
|
" WHERE institutions.CtyCod=%ld"
|
||||||
" AND institutions.InsCod=centres.InsCod"
|
" AND institutions.InsCod=centres.InsCod"
|
||||||
" AND centres.CtrCod=degrees.CtrCod"
|
" AND centres.CtrCod=degrees.CtrCod"
|
||||||
" AND degrees.DegCod=courses.DegCod"
|
" AND degrees.DegCod=courses.DegCod"
|
||||||
" AND courses.CrsCod=exa_exams.CrsCod"
|
" AND courses.CrsCod=exa_exams.CrsCod"
|
||||||
" AND exa_exams.ExaCod=exa_questions.ExaCod"
|
" AND exa_exams.ExaCod=exa_set_questions.ExaCod"
|
||||||
" GROUP BY exa_questions.ExaCod) AS NumQstsTable",
|
" GROUP BY exa_set_questions.ExaCod) AS NumQstsTable",
|
||||||
Gbl.Hierarchy.Cty.CtyCod);
|
Gbl.Hierarchy.Cty.CtyCod);
|
||||||
break;
|
break;
|
||||||
case Hie_INS:
|
case Hie_INS:
|
||||||
DB_QuerySELECT (&mysql_res,"can not get number of questions per exam",
|
DB_QuerySELECT (&mysql_res,"can not get number of questions per exam",
|
||||||
"SELECT AVG(NumQsts) FROM"
|
"SELECT AVG(NumQsts) FROM"
|
||||||
" (SELECT COUNT(exa_questions.QstCod) AS NumQsts"
|
" (SELECT COUNT(exa_set_questions.QstCod) AS NumQsts"
|
||||||
" FROM centres,degrees,courses,exa_exams,exa_questions"
|
" FROM centres,degrees,courses,exa_exams,exa_set_questions"
|
||||||
" WHERE centres.InsCod=%ld"
|
" WHERE centres.InsCod=%ld"
|
||||||
" AND centres.CtrCod=degrees.CtrCod"
|
" AND centres.CtrCod=degrees.CtrCod"
|
||||||
" AND degrees.DegCod=courses.DegCod"
|
" AND degrees.DegCod=courses.DegCod"
|
||||||
" AND courses.CrsCod=exa_exams.CrsCod"
|
" AND courses.CrsCod=exa_exams.CrsCod"
|
||||||
" AND exa_exams.ExaCod=exa_questions.ExaCod"
|
" AND exa_exams.ExaCod=exa_set_questions.ExaCod"
|
||||||
" GROUP BY exa_questions.ExaCod) AS NumQstsTable",
|
" GROUP BY exa_set_questions.ExaCod) AS NumQstsTable",
|
||||||
Gbl.Hierarchy.Ins.InsCod);
|
Gbl.Hierarchy.Ins.InsCod);
|
||||||
break;
|
break;
|
||||||
case Hie_CTR:
|
case Hie_CTR:
|
||||||
DB_QuerySELECT (&mysql_res,"can not get number of questions per exam",
|
DB_QuerySELECT (&mysql_res,"can not get number of questions per exam",
|
||||||
"SELECT AVG(NumQsts) FROM"
|
"SELECT AVG(NumQsts) FROM"
|
||||||
" (SELECT COUNT(exa_questions.QstCod) AS NumQsts"
|
" (SELECT COUNT(exa_set_questions.QstCod) AS NumQsts"
|
||||||
" FROM degrees,courses,exa_exams,exa_questions"
|
" FROM degrees,courses,exa_exams,exa_set_questions"
|
||||||
" WHERE degrees.CtrCod=%ld"
|
" WHERE degrees.CtrCod=%ld"
|
||||||
" AND degrees.DegCod=courses.DegCod"
|
" AND degrees.DegCod=courses.DegCod"
|
||||||
" AND courses.CrsCod=exa_exams.CrsCod"
|
" AND courses.CrsCod=exa_exams.CrsCod"
|
||||||
" AND exa_exams.ExaCod=exa_questions.ExaCod"
|
" AND exa_exams.ExaCod=exa_set_questions.ExaCod"
|
||||||
" GROUP BY exa_questions.ExaCod) AS NumQstsTable",
|
" GROUP BY exa_set_questions.ExaCod) AS NumQstsTable",
|
||||||
Gbl.Hierarchy.Ctr.CtrCod);
|
Gbl.Hierarchy.Ctr.CtrCod);
|
||||||
break;
|
break;
|
||||||
case Hie_DEG:
|
case Hie_DEG:
|
||||||
DB_QuerySELECT (&mysql_res,"can not get number of questions per exam",
|
DB_QuerySELECT (&mysql_res,"can not get number of questions per exam",
|
||||||
"SELECT AVG(NumQsts) FROM"
|
"SELECT AVG(NumQsts) FROM"
|
||||||
" (SELECT COUNT(exa_questions.QstCod) AS NumQsts"
|
" (SELECT COUNT(exa_set_questions.QstCod) AS NumQsts"
|
||||||
" FROM courses,exa_exams,exa_questions"
|
" FROM courses,exa_exams,exa_set_questions"
|
||||||
" WHERE courses.DegCod=%ld"
|
" WHERE courses.DegCod=%ld"
|
||||||
" AND courses.CrsCod=exa_exams.CrsCod"
|
" AND courses.CrsCod=exa_exams.CrsCod"
|
||||||
" AND exa_exams.ExaCod=exa_questions.ExaCod"
|
" AND exa_exams.ExaCod=exa_set_questions.ExaCod"
|
||||||
" GROUP BY exa_questions.ExaCod) AS NumQstsTable",
|
" GROUP BY exa_set_questions.ExaCod) AS NumQstsTable",
|
||||||
Gbl.Hierarchy.Deg.DegCod);
|
Gbl.Hierarchy.Deg.DegCod);
|
||||||
break;
|
break;
|
||||||
case Hie_CRS:
|
case Hie_CRS:
|
||||||
DB_QuerySELECT (&mysql_res,"can not get number of questions per exam",
|
DB_QuerySELECT (&mysql_res,"can not get number of questions per exam",
|
||||||
"SELECT AVG(NumQsts) FROM"
|
"SELECT AVG(NumQsts) FROM"
|
||||||
" (SELECT COUNT(exa_questions.QstCod) AS NumQsts"
|
" (SELECT COUNT(exa_set_questions.QstCod) AS NumQsts"
|
||||||
" FROM exa_exams,exa_questions"
|
" FROM exa_exams,exa_set_questions"
|
||||||
" WHERE exa_exams.Cod=%ld"
|
" WHERE exa_exams.Cod=%ld"
|
||||||
" AND exa_exams.ExaCod=exa_questions.ExaCod"
|
" AND exa_exams.ExaCod=exa_set_questions.ExaCod"
|
||||||
" GROUP BY exa_questions.ExaCod) AS NumQstsTable",
|
" GROUP BY exa_set_questions.ExaCod) AS NumQstsTable",
|
||||||
Gbl.Hierarchy.Crs.CrsCod);
|
Gbl.Hierarchy.Crs.CrsCod);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -2060,9 +2078,9 @@ void Exa_ShowTstTagsPresentInAnExam (long ExaCod)
|
||||||
"SELECT tst_tags.TagTxt" // row[0]
|
"SELECT tst_tags.TagTxt" // row[0]
|
||||||
" FROM"
|
" FROM"
|
||||||
" (SELECT DISTINCT(tst_question_tags.TagCod)"
|
" (SELECT DISTINCT(tst_question_tags.TagCod)"
|
||||||
" FROM tst_question_tags,exa_questions"
|
" FROM tst_question_tags,exa_set_questions"
|
||||||
" WHERE exa_questions.ExaCod=%ld"
|
" WHERE exa_set_questions.ExaCod=%ld"
|
||||||
" AND exa_questions.QstCod=tst_question_tags.QstCod)"
|
" AND exa_set_questions.QstCod=tst_question_tags.QstCod)"
|
||||||
" AS TagsCods,tst_tags"
|
" AS TagsCods,tst_tags"
|
||||||
" WHERE TagsCods.TagCod=tst_tags.TagCod"
|
" WHERE TagsCods.TagCod=tst_tags.TagCod"
|
||||||
" ORDER BY tst_tags.TagTxt",
|
" ORDER BY tst_tags.TagTxt",
|
||||||
|
@ -2089,9 +2107,9 @@ void Exa_GetScoreRange (long ExaCod,double *MinScore,double *MaxScore)
|
||||||
NumRows = (unsigned)
|
NumRows = (unsigned)
|
||||||
DB_QuerySELECT (&mysql_res,"can not get data of a question",
|
DB_QuerySELECT (&mysql_res,"can not get data of a question",
|
||||||
"SELECT COUNT(tst_answers.AnsInd) AS N"
|
"SELECT COUNT(tst_answers.AnsInd) AS N"
|
||||||
" FROM tst_answers,exa_questions"
|
" FROM tst_answers,exa_set_questions"
|
||||||
" WHERE exa_questions.ExaCod=%ld"
|
" WHERE exa_set_questions.ExaCod=%ld"
|
||||||
" AND exa_questions.QstCod=tst_answers.QstCod"
|
" AND exa_set_questions.QstCod=tst_answers.QstCod"
|
||||||
" GROUP BY tst_answers.QstCod",
|
" GROUP BY tst_answers.QstCod",
|
||||||
ExaCod);
|
ExaCod);
|
||||||
for (NumRow = 0, *MinScore = *MaxScore = 0.0;
|
for (NumRow = 0, *MinScore = *MaxScore = 0.0;
|
||||||
|
|
|
@ -1847,146 +1847,6 @@ static void ExaEvt_UpdateEvent (struct ExaEvt_Event *Event)
|
||||||
ExaEvt_CreateGrps (Event->EvtCod); // Associate new groups
|
ExaEvt_CreateGrps (Event->EvtCod); // Associate new groups
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/******************** Create indexes for an exam event ***********************/
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* Everytime a new exam event is created,
|
|
||||||
the answers of each shufflable question are shuffled.
|
|
||||||
The shuffling is stored in a table of indexes
|
|
||||||
that will be read when showing an exam event */
|
|
||||||
/*
|
|
||||||
static void ExaEvt_CreateIndexes (long ExaCod,long EvtCod)
|
|
||||||
{
|
|
||||||
MYSQL_RES *mysql_res;
|
|
||||||
MYSQL_ROW row;
|
|
||||||
unsigned NumQsts;
|
|
||||||
unsigned NumQst;
|
|
||||||
struct Tst_Question Question;
|
|
||||||
long LongNum;
|
|
||||||
unsigned QstInd;
|
|
||||||
|
|
||||||
***** Get questions of the exam *****
|
|
||||||
NumQsts = (unsigned)
|
|
||||||
DB_QuerySELECT (&mysql_res,"can not get questions of an exam",
|
|
||||||
"SELECT exa_questions.QstCod," // row[0]
|
|
||||||
"exa_questions.QstInd," // row[1]
|
|
||||||
"tst_questions.AnsType," // row[2]
|
|
||||||
"tst_questions.Shuffle" // row[3]
|
|
||||||
" FROM exa_questions,tst_questions"
|
|
||||||
" WHERE exa_questions.ExaCod=%ld"
|
|
||||||
" AND exa_questions.QstCod=tst_questions.QstCod"
|
|
||||||
" ORDER BY exa_questions.QstInd",
|
|
||||||
ExaCod);
|
|
||||||
|
|
||||||
***** For each question in exam... *****
|
|
||||||
for (NumQst = 0;
|
|
||||||
NumQst < NumQsts;
|
|
||||||
NumQst++)
|
|
||||||
{
|
|
||||||
***** Create test question *****
|
|
||||||
Tst_QstConstructor (&Question);
|
|
||||||
|
|
||||||
***** Get question data *****
|
|
||||||
row = mysql_fetch_row (mysql_res);
|
|
||||||
*
|
|
||||||
exa_questions.QstCod row[0]
|
|
||||||
exa_questions.QstInd row[1]
|
|
||||||
tst_questions.AnsType row[2]
|
|
||||||
tst_questions.Shuffle row[3]
|
|
||||||
*
|
|
||||||
|
|
||||||
* Get question code (row[0]) *
|
|
||||||
if ((Question.QstCod = Str_ConvertStrCodToLongCod (row[0])) < 0)
|
|
||||||
Lay_ShowErrorAndExit ("Wrong code of question.");
|
|
||||||
|
|
||||||
* Get question index (row[1]) *
|
|
||||||
if ((LongNum = Str_ConvertStrCodToLongCod (row[1])) < 0)
|
|
||||||
Lay_ShowErrorAndExit ("Wrong question index.");
|
|
||||||
QstInd = (unsigned) LongNum;
|
|
||||||
|
|
||||||
* Get answer type (row[2]) *
|
|
||||||
Question.Answer.Type = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[2]);
|
|
||||||
if (Question.Answer.Type != Tst_ANS_UNIQUE_CHOICE)
|
|
||||||
Lay_ShowErrorAndExit ("Wrong answer type.");
|
|
||||||
|
|
||||||
* Get shuffle (row[3]) *
|
|
||||||
Question.Answer.Shuffle = (row[3][0] == 'Y');
|
|
||||||
|
|
||||||
***** Reorder answer *****
|
|
||||||
ExaEvt_ReorderAnswer (EvtCod,QstInd,&Question);
|
|
||||||
|
|
||||||
***** Destroy test question *****
|
|
||||||
Tst_QstDestructor (&Question);
|
|
||||||
}
|
|
||||||
|
|
||||||
***** Free structure that stores the query result *****
|
|
||||||
DB_FreeMySQLResult (&mysql_res);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
/*****************************************************************************/
|
|
||||||
/**************** Reorder answers of an exam event question ******************/
|
|
||||||
/*****************************************************************************/
|
|
||||||
/*
|
|
||||||
static void ExaEvt_ReorderAnswer (long EvtCod,unsigned QstInd,
|
|
||||||
const struct Tst_Question *Question)
|
|
||||||
{
|
|
||||||
MYSQL_RES *mysql_res;
|
|
||||||
MYSQL_ROW row;
|
|
||||||
unsigned NumAnss;
|
|
||||||
unsigned NumAns;
|
|
||||||
long LongNum;
|
|
||||||
unsigned AnsInd;
|
|
||||||
char StrOneAnswer[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
|
|
||||||
char StrAnswersOneQst[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
|
|
||||||
|
|
||||||
***** Initialize list of answers to empty string *****
|
|
||||||
StrAnswersOneQst[0] = '\0';
|
|
||||||
|
|
||||||
***** Get questions of the exam *****
|
|
||||||
NumAnss = (unsigned)
|
|
||||||
DB_QuerySELECT (&mysql_res,"can not get questions of an exam",
|
|
||||||
"SELECT AnsInd" // row[0]
|
|
||||||
" FROM tst_answers"
|
|
||||||
" WHERE QstCod=%ld"
|
|
||||||
" ORDER BY %s",
|
|
||||||
Question->QstCod,
|
|
||||||
Question->Answer.Shuffle ? "RAND()" : // Use RAND() because is really random; RAND(NOW()) repeats order
|
|
||||||
"AnsInd");
|
|
||||||
|
|
||||||
***** For each answer in question... *****
|
|
||||||
for (NumAns = 0;
|
|
||||||
NumAns < NumAnss;
|
|
||||||
NumAns++)
|
|
||||||
{
|
|
||||||
row = mysql_fetch_row (mysql_res);
|
|
||||||
|
|
||||||
* Get answer index (row[0]) *
|
|
||||||
if ((LongNum = Str_ConvertStrCodToLongCod (row[0])) < 0)
|
|
||||||
Lay_ShowErrorAndExit ("Wrong answer index.");
|
|
||||||
AnsInd = (unsigned) LongNum;
|
|
||||||
snprintf (StrOneAnswer,sizeof (StrOneAnswer),
|
|
||||||
"%u",AnsInd);
|
|
||||||
|
|
||||||
* Concatenate answer index to list of answers *
|
|
||||||
if (NumAns)
|
|
||||||
Str_Concat (StrAnswersOneQst,",",
|
|
||||||
Tst_MAX_BYTES_ANSWERS_ONE_QST);
|
|
||||||
Str_Concat (StrAnswersOneQst,StrOneAnswer,
|
|
||||||
Tst_MAX_BYTES_ANSWERS_ONE_QST);
|
|
||||||
}
|
|
||||||
|
|
||||||
***** Free structure that stores the query result *****
|
|
||||||
DB_FreeMySQLResult (&mysql_res);
|
|
||||||
|
|
||||||
***** Create entry for this question in table of exam event indexes *****
|
|
||||||
DB_QueryINSERT ("can not create exam event indexes",
|
|
||||||
"INSERT INTO exa_indexes"
|
|
||||||
" (EvtCod,QstInd,Indexes)"
|
|
||||||
" VALUES"
|
|
||||||
" (%ld,%u,'%s')",
|
|
||||||
EvtCod,QstInd,StrAnswersOneQst);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/***************** Get indexes for a question from database ******************/
|
/***************** Get indexes for a question from database ******************/
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
|
@ -93,6 +93,8 @@ static void ExaPrn_GetQuestionsForNewPrintFromDB (struct ExaPrn_Print *Print);
|
||||||
static unsigned ExaPrn_GetSomeQstsFromSetToPrint (struct ExaPrn_Print *Print,
|
static unsigned ExaPrn_GetSomeQstsFromSetToPrint (struct ExaPrn_Print *Print,
|
||||||
struct ExaSet_Set *Set,
|
struct ExaSet_Set *Set,
|
||||||
unsigned *NumQstInPrint);
|
unsigned *NumQstInPrint);
|
||||||
|
static void ExaPrn_GenerateChoiceIndexes (struct TstPrn_PrintedQuestion *PrintedQuestion,
|
||||||
|
bool Shuffle);
|
||||||
static void ExaPrn_CreatePrintInDB (struct ExaPrn_Print *Print);
|
static void ExaPrn_CreatePrintInDB (struct ExaPrn_Print *Print);
|
||||||
|
|
||||||
static void ExaPrn_GetPrintQuestionsFromDB (struct ExaPrn_Print *Print);
|
static void ExaPrn_GetPrintQuestionsFromDB (struct ExaPrn_Print *Print);
|
||||||
|
@ -363,12 +365,11 @@ static unsigned ExaPrn_GetSomeQstsFromSetToPrint (struct ExaPrn_Print *Print,
|
||||||
/***** Get questions from database *****/
|
/***** Get questions from database *****/
|
||||||
NumQstsInSet = (unsigned)
|
NumQstsInSet = (unsigned)
|
||||||
DB_QuerySELECT (&mysql_res,"can not get questions from set",
|
DB_QuerySELECT (&mysql_res,"can not get questions from set",
|
||||||
"SELECT tst_questions.QstCod," // row[0]
|
"SELECT QstCod," // row[0]
|
||||||
"tst_questions.AnsType," // row[1]
|
"AnsType," // row[1]
|
||||||
"tst_questions.Shuffle" // row[2]
|
"Shuffle" // row[2]
|
||||||
" FROM exa_questions,tst_questions"
|
" FROM exa_set_questions"
|
||||||
" WHERE exa_questions.setCod=%ld"
|
" WHERE SetCod=%ld"
|
||||||
" AND exa_questions.QstCod=tst_questions.QstCod"
|
|
||||||
" ORDER BY RAND()" // Don't use RAND(NOW()) because the same ordering will be repeated across sets
|
" ORDER BY RAND()" // Don't use RAND(NOW()) because the same ordering will be repeated across sets
|
||||||
" LIMIT %u",
|
" LIMIT %u",
|
||||||
Set->SetCod,
|
Set->SetCod,
|
||||||
|
@ -414,7 +415,7 @@ static unsigned ExaPrn_GetSomeQstsFromSetToPrint (struct ExaPrn_Print *Print,
|
||||||
case Tst_ANS_MULTIPLE_CHOICE:
|
case Tst_ANS_MULTIPLE_CHOICE:
|
||||||
/* If answer type is unique or multiple option,
|
/* If answer type is unique or multiple option,
|
||||||
generate indexes of answers depending on shuffle */
|
generate indexes of answers depending on shuffle */
|
||||||
Tst_GenerateChoiceIndexesDependingOnShuffle (&Print->PrintedQuestions[*NumQstInPrint],Shuffle);
|
ExaPrn_GenerateChoiceIndexes (&Print->PrintedQuestions[*NumQstInPrint],Shuffle);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -430,6 +431,71 @@ static unsigned ExaPrn_GetSomeQstsFromSetToPrint (struct ExaPrn_Print *Print,
|
||||||
return NumQstsInSet;
|
return NumQstsInSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/*************** Generate choice indexes depending on shuffle ****************/
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static void ExaPrn_GenerateChoiceIndexes (struct TstPrn_PrintedQuestion *PrintedQuestion,
|
||||||
|
bool Shuffle)
|
||||||
|
{
|
||||||
|
struct Tst_Question Question;
|
||||||
|
unsigned NumOpt;
|
||||||
|
MYSQL_RES *mysql_res;
|
||||||
|
MYSQL_ROW row;
|
||||||
|
unsigned Index;
|
||||||
|
bool ErrorInIndex;
|
||||||
|
char StrInd[1 + Cns_MAX_DECIMAL_DIGITS_UINT + 1];
|
||||||
|
|
||||||
|
/***** Create test question *****/
|
||||||
|
Tst_QstConstructor (&Question);
|
||||||
|
Question.QstCod = PrintedQuestion->QstCod;
|
||||||
|
|
||||||
|
/***** Get answers of question from database *****/
|
||||||
|
ExaSet_GetAnswersQst (&Question,&mysql_res,Shuffle);
|
||||||
|
/*
|
||||||
|
row[0] AnsInd
|
||||||
|
row[1] Answer
|
||||||
|
row[2] Feedback
|
||||||
|
row[3] MedCod
|
||||||
|
row[4] Correct
|
||||||
|
*/
|
||||||
|
|
||||||
|
for (NumOpt = 0;
|
||||||
|
NumOpt < Question.Answer.NumOptions;
|
||||||
|
NumOpt++)
|
||||||
|
{
|
||||||
|
/***** Get next answer *****/
|
||||||
|
row = mysql_fetch_row (mysql_res);
|
||||||
|
|
||||||
|
/***** Assign index (row[0]).
|
||||||
|
Index is 0,1,2,3... if no shuffle
|
||||||
|
or 1,3,0,2... (example) if shuffle *****/
|
||||||
|
ErrorInIndex = false;
|
||||||
|
if (sscanf (row[0],"%u",&Index) == 1)
|
||||||
|
{
|
||||||
|
if (Index >= Tst_MAX_OPTIONS_PER_QUESTION)
|
||||||
|
ErrorInIndex = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ErrorInIndex = true;
|
||||||
|
if (ErrorInIndex)
|
||||||
|
Lay_ShowErrorAndExit ("Wrong index of answer.");
|
||||||
|
|
||||||
|
if (NumOpt == 0)
|
||||||
|
snprintf (StrInd,sizeof (StrInd),"%u",Index);
|
||||||
|
else
|
||||||
|
snprintf (StrInd,sizeof (StrInd),",%u",Index);
|
||||||
|
Str_Concat (PrintedQuestion->StrIndexes,StrInd,
|
||||||
|
Tst_MAX_BYTES_INDEXES_ONE_QST);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***** Free structure that stores the query result *****/
|
||||||
|
DB_FreeMySQLResult (&mysql_res);
|
||||||
|
|
||||||
|
/***** Destroy test question *****/
|
||||||
|
Tst_QstDestructor (&Question);
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/***************** Create new blank exam print in database *******************/
|
/***************** Create new blank exam print in database *******************/
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
@ -571,8 +637,7 @@ static void ExaPrn_ShowTableWithQstsToFill (struct ExaPrn_Print *Print)
|
||||||
Question.QstCod = Print->PrintedQuestions[NumQst].QstCod;
|
Question.QstCod = Print->PrintedQuestions[NumQst].QstCod;
|
||||||
|
|
||||||
/* Show question */
|
/* Show question */
|
||||||
if (!Tst_GetQstDataFromDB (&Question)) // Question exists
|
ExaSet_GetQstDataFromDB (&Question,Print->ExaCod);
|
||||||
Lay_ShowErrorAndExit ("Wrong question.");
|
|
||||||
|
|
||||||
/* Write question and answers */
|
/* Write question and answers */
|
||||||
ExaPrn_WriteQstAndAnsToFill (Print,NumQst,&Question);
|
ExaPrn_WriteQstAndAnsToFill (Print,NumQst,&Question);
|
||||||
|
@ -981,7 +1046,7 @@ static void ExaPrn_ComputeScoreAndStoreQuestionOfPrint (struct ExaPrn_Print *Pri
|
||||||
/***** Compute question score *****/
|
/***** Compute question score *****/
|
||||||
Tst_QstConstructor (&Question);
|
Tst_QstConstructor (&Question);
|
||||||
Question.QstCod = Print->PrintedQuestions[NumQst].QstCod;
|
Question.QstCod = Print->PrintedQuestions[NumQst].QstCod;
|
||||||
Question.Answer.Type = Tst_GetQstAnswerType (Question.QstCod);
|
Question.Answer.Type = ExaSet_GetQstAnswerTypeFromDB (Question.QstCod);
|
||||||
TstPrn_ComputeAnswerScore (&Print->PrintedQuestions[NumQst],&Question);
|
TstPrn_ComputeAnswerScore (&Print->PrintedQuestions[NumQst],&Question);
|
||||||
Tst_QstDestructor (&Question);
|
Tst_QstDestructor (&Question);
|
||||||
|
|
||||||
|
@ -1000,7 +1065,6 @@ static void ExaPrn_ComputeScoreAndStoreQuestionOfPrint (struct ExaPrn_Print *Pri
|
||||||
NumQst); // 0, 1, 2, 3...
|
NumQst); // 0, 1, 2, 3...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/************* Get the questions of an exam print from database **************/
|
/************* Get the questions of an exam print from database **************/
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
|
@ -1326,15 +1326,15 @@ void ExaRes_GetExamResultQuestionsFromDB (long EvtCod,long UsrCod,
|
||||||
Print->NumQsts = (unsigned)
|
Print->NumQsts = (unsigned)
|
||||||
DB_QuerySELECT (&mysql_res,"can not get questions and answers"
|
DB_QuerySELECT (&mysql_res,"can not get questions and answers"
|
||||||
" of a event result",
|
" of a event result",
|
||||||
"SELECT exa_questions.QstCod," // row[0]
|
"SELECT exa_set_questions.QstCod," // row[0]
|
||||||
"exa_questions.QstInd," // row[1]
|
"exa_set_questions.QstInd," // row[1]
|
||||||
"exa_indexes.Indexes" // row[2]
|
"exa_indexes.Indexes" // row[2]
|
||||||
" FROM exa_events,exa_questions,exa_indexes"
|
" FROM exa_events,exa_set_questions,exa_indexes"
|
||||||
" WHERE exa_events.EvtCod=%ld"
|
" WHERE exa_events.EvtCod=%ld"
|
||||||
" AND exa_events.ExaCod=exa_questions.ExaCod"
|
" AND exa_events.ExaCod=exa_set_questions.ExaCod"
|
||||||
" AND exa_events.EvtCod=exa_indexes.EvtCod"
|
" AND exa_events.EvtCod=exa_indexes.EvtCod"
|
||||||
" AND exa_questions.QstInd=exa_indexes.QstInd"
|
" AND exa_set_questions.QstInd=exa_indexes.QstInd"
|
||||||
" ORDER BY exa_questions.QstInd",
|
" ORDER BY exa_set_questions.QstInd",
|
||||||
EvtCod);
|
EvtCod);
|
||||||
for (NumQst = 0, Print->NumQstsNotBlank = 0;
|
for (NumQst = 0, Print->NumQstsNotBlank = 0;
|
||||||
NumQst < Print->NumQsts;
|
NumQst < Print->NumQsts;
|
||||||
|
|
391
swad_exam_set.c
391
swad_exam_set.c
|
@ -123,10 +123,17 @@ static void ExaSet_ListOneOrMoreQuestionsForEdition (struct Exa_Exams *Exams,
|
||||||
unsigned NumQsts,
|
unsigned NumQsts,
|
||||||
MYSQL_RES *mysql_res,
|
MYSQL_RES *mysql_res,
|
||||||
bool ICanEditQuestions);
|
bool ICanEditQuestions);
|
||||||
|
static void ExaSet_ListQuestionForEdition (const struct Tst_Question *Question,
|
||||||
|
unsigned QstInd,const char *Anchor);
|
||||||
|
|
||||||
static void ExaSet_AllocateListSelectedQuestions (struct Exa_Exams *Exams);
|
static void ExaSet_AllocateListSelectedQuestions (struct Exa_Exams *Exams);
|
||||||
static void ExaSet_FreeListsSelectedQuestions (struct Exa_Exams *Exams);
|
static void ExaSet_FreeListsSelectedQuestions (struct Exa_Exams *Exams);
|
||||||
|
|
||||||
|
static void ExaSet_CopyQstFromBankToExamSet (struct ExaSet_Set *Set,long QstCod);
|
||||||
|
|
||||||
|
static long ExaSet_GetParamQstCod (void);
|
||||||
|
static void ExaSet_PutParamQstCod (void *QstCod); // Should be a pointer to long
|
||||||
|
|
||||||
static void ExaSet_ExchangeSets (long ExaCod,
|
static void ExaSet_ExchangeSets (long ExaCod,
|
||||||
unsigned SetIndTop,unsigned SetIndBottom);
|
unsigned SetIndTop,unsigned SetIndBottom);
|
||||||
|
|
||||||
|
@ -153,7 +160,7 @@ void ExaSet_PutParamsOneSet (void *Exams)
|
||||||
static void ExaSet_PutParamsOneQst (void *Exams)
|
static void ExaSet_PutParamsOneQst (void *Exams)
|
||||||
{
|
{
|
||||||
ExaSet_PutParamsOneSet (Exams);
|
ExaSet_PutParamsOneSet (Exams);
|
||||||
Tst_PutParamQstCod (&(((struct Exa_Exams *) Exams)->QstCod));
|
ExaSet_PutParamQstCod (&(((struct Exa_Exams *) Exams)->QstCod));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
@ -175,7 +182,7 @@ static unsigned ExaSet_GetNumQstsInSet (long SetCod)
|
||||||
/***** Get number of questions in set from database *****/
|
/***** Get number of questions in set from database *****/
|
||||||
return
|
return
|
||||||
(unsigned) DB_QueryCOUNT ("can not get number of questions in a set",
|
(unsigned) DB_QueryCOUNT ("can not get number of questions in a set",
|
||||||
"SELECT COUNT(*) FROM exa_questions"
|
"SELECT COUNT(*) FROM exa_set_questions"
|
||||||
" WHERE SetCod=%ld",
|
" WHERE SetCod=%ld",
|
||||||
SetCod);
|
SetCod);
|
||||||
}
|
}
|
||||||
|
@ -1042,11 +1049,10 @@ static void ExaSet_ListSetQuestions (struct Exa_Exams *Exams,
|
||||||
/***** Get data of questions from database *****/
|
/***** Get data of questions from database *****/
|
||||||
NumQsts = (unsigned)
|
NumQsts = (unsigned)
|
||||||
DB_QuerySELECT (&mysql_res,"can not get exam questions",
|
DB_QuerySELECT (&mysql_res,"can not get exam questions",
|
||||||
"SELECT exa_questions.QstCod" // row[0]
|
"SELECT QstCod" // row[0]
|
||||||
" FROM exa_questions LEFT JOIN tst_questions" // LEFT JOIN because the question could be removed in table of test questions
|
" FROM exa_set_questions"
|
||||||
" ON (exa_questions.QstCod=tst_questions.QstCod)"
|
" WHERE SetCod=%ld"
|
||||||
" WHERE exa_questions.SetCod=%ld"
|
" ORDER BY Stem",
|
||||||
" ORDER BY tst_questions.Stem",
|
|
||||||
Set->SetCod);
|
Set->SetCod);
|
||||||
|
|
||||||
/***** Begin box *****/
|
/***** Begin box *****/
|
||||||
|
@ -1298,12 +1304,10 @@ static void ExaSet_ListOneOrMoreQuestionsForEdition (struct Exa_Exams *Exams,
|
||||||
extern const char *Txt_Questions;
|
extern const char *Txt_Questions;
|
||||||
extern const char *Txt_No_INDEX;
|
extern const char *Txt_No_INDEX;
|
||||||
extern const char *Txt_Code;
|
extern const char *Txt_Code;
|
||||||
extern const char *Txt_Tags;
|
|
||||||
extern const char *Txt_Question;
|
extern const char *Txt_Question;
|
||||||
unsigned NumQst;
|
unsigned NumQst;
|
||||||
MYSQL_ROW row;
|
MYSQL_ROW row;
|
||||||
struct Tst_Question Question;
|
struct Tst_Question Question;
|
||||||
bool QuestionExists;
|
|
||||||
char *Anchor;
|
char *Anchor;
|
||||||
|
|
||||||
/***** Build anchor string *****/
|
/***** Build anchor string *****/
|
||||||
|
@ -1317,7 +1321,6 @@ static void ExaSet_ListOneOrMoreQuestionsForEdition (struct Exa_Exams *Exams,
|
||||||
|
|
||||||
HTM_TH (1,1,"CT",Txt_No_INDEX);
|
HTM_TH (1,1,"CT",Txt_No_INDEX);
|
||||||
HTM_TH (1,1,"CT",Txt_Code);
|
HTM_TH (1,1,"CT",Txt_Code);
|
||||||
HTM_TH (1,1,"CT",Txt_Tags);
|
|
||||||
HTM_TH (1,1,"CT",Txt_Question);
|
HTM_TH (1,1,"CT",Txt_Question);
|
||||||
|
|
||||||
HTM_TR_End ();
|
HTM_TR_End ();
|
||||||
|
@ -1360,13 +1363,13 @@ static void ExaSet_ListOneOrMoreQuestionsForEdition (struct Exa_Exams *Exams,
|
||||||
/* Put icon to edit the question */
|
/* Put icon to edit the question */
|
||||||
if (ICanEditQuestions)
|
if (ICanEditQuestions)
|
||||||
Ico_PutContextualIconToEdit (ActEdiOneTstQst,NULL,
|
Ico_PutContextualIconToEdit (ActEdiOneTstQst,NULL,
|
||||||
Tst_PutParamQstCod,&Question.QstCod);
|
ExaSet_PutParamQstCod,&Question.QstCod);
|
||||||
|
|
||||||
HTM_TD_End ();
|
HTM_TD_End ();
|
||||||
|
|
||||||
/***** Question *****/
|
/***** Question *****/
|
||||||
QuestionExists = Tst_GetQstDataFromDB (&Question);
|
ExaSet_GetQstDataFromDB (&Question,Exams->ExaCod);
|
||||||
Tst_ListQuestionForEdition (&Question,NumQst + 1,QuestionExists,Anchor);
|
ExaSet_ListQuestionForEdition (&Question,NumQst + 1,Anchor);
|
||||||
|
|
||||||
/***** End row *****/
|
/***** End row *****/
|
||||||
HTM_TR_End ();
|
HTM_TR_End ();
|
||||||
|
@ -1382,6 +1385,233 @@ static void ExaSet_ListOneOrMoreQuestionsForEdition (struct Exa_Exams *Exams,
|
||||||
Frm_FreeAnchorStr (Anchor);
|
Frm_FreeAnchorStr (Anchor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/*************** Get answer type of a question from database *****************/
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
Tst_AnswerType_t ExaSet_GetQstAnswerTypeFromDB (long QstCod)
|
||||||
|
{
|
||||||
|
MYSQL_RES *mysql_res;
|
||||||
|
MYSQL_ROW row;
|
||||||
|
Tst_AnswerType_t AnswerType;
|
||||||
|
|
||||||
|
/***** Get type of answer from database *****/
|
||||||
|
if (!DB_QuerySELECT (&mysql_res,"can not get the type of a question",
|
||||||
|
"SELECT AnsType" // row[0]
|
||||||
|
" FROM exa_set_questions"
|
||||||
|
" WHERE QstCod=%ld",
|
||||||
|
QstCod))
|
||||||
|
Lay_ShowErrorAndExit ("Question does not exist.");
|
||||||
|
|
||||||
|
/* Get type of answer */
|
||||||
|
row = mysql_fetch_row (mysql_res);
|
||||||
|
AnswerType = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[0]);
|
||||||
|
|
||||||
|
/* Free structure that stores the query result */
|
||||||
|
DB_FreeMySQLResult (&mysql_res);
|
||||||
|
|
||||||
|
return AnswerType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/*************** Get data of a question in a set from database ***************/
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
void ExaSet_GetQstDataFromDB (struct Tst_Question *Question,long ExaCod)
|
||||||
|
{
|
||||||
|
MYSQL_RES *mysql_res;
|
||||||
|
MYSQL_ROW row;
|
||||||
|
bool QuestionExists;
|
||||||
|
unsigned NumOpt;
|
||||||
|
|
||||||
|
/***** Get question data from database *****/
|
||||||
|
QuestionExists = (DB_QuerySELECT (&mysql_res,"can not get a question",
|
||||||
|
"SELECT AnsType," // row[0]
|
||||||
|
"Shuffle," // row[1]
|
||||||
|
"Stem," // row[2]
|
||||||
|
"Feedback," // row[3]
|
||||||
|
"MedCod" // row[4]
|
||||||
|
" FROM exa_set_questions"
|
||||||
|
" WHERE QstCod=%ld"
|
||||||
|
" AND SetCod IN"
|
||||||
|
" (SELECT SetCod FROM exa_sets WHERE ExaCod=%ld)", // Extra check
|
||||||
|
Question->QstCod,
|
||||||
|
ExaCod) != 0);
|
||||||
|
|
||||||
|
if (QuestionExists)
|
||||||
|
{
|
||||||
|
row = mysql_fetch_row (mysql_res);
|
||||||
|
|
||||||
|
/* Get the type of answer (row[0]) */
|
||||||
|
Question->Answer.Type = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[0]);
|
||||||
|
|
||||||
|
/* Get shuffle (row[1]) */
|
||||||
|
Question->Answer.Shuffle = (row[1][0] == 'Y');
|
||||||
|
|
||||||
|
/* Get the stem (row[2]) */
|
||||||
|
Question->Stem[0] = '\0';
|
||||||
|
if (row[2])
|
||||||
|
if (row[2][0])
|
||||||
|
Str_Copy (Question->Stem,row[2],
|
||||||
|
Cns_MAX_BYTES_TEXT);
|
||||||
|
|
||||||
|
/* Get the feedback (row[3]) */
|
||||||
|
Question->Feedback[0] = '\0';
|
||||||
|
if (row[3])
|
||||||
|
if (row[3][0])
|
||||||
|
Str_Copy (Question->Feedback,row[3],
|
||||||
|
Cns_MAX_BYTES_TEXT);
|
||||||
|
|
||||||
|
/* Get media (row[4]) */
|
||||||
|
Question->Media.MedCod = Str_ConvertStrCodToLongCod (row[4]);
|
||||||
|
Med_GetMediaDataByCod (&Question->Media);
|
||||||
|
|
||||||
|
/* Free structure that stores the query result */
|
||||||
|
DB_FreeMySQLResult (&mysql_res);
|
||||||
|
|
||||||
|
/***** Get the answers from the database *****/
|
||||||
|
ExaSet_GetAnswersQst (Question,&mysql_res,
|
||||||
|
false); // Don't shuffle
|
||||||
|
/*
|
||||||
|
row[0] AnsInd
|
||||||
|
row[1] Answer
|
||||||
|
row[2] Feedback
|
||||||
|
row[3] MedCod
|
||||||
|
row[4] Correct
|
||||||
|
*/
|
||||||
|
for (NumOpt = 0;
|
||||||
|
NumOpt < Question->Answer.NumOptions;
|
||||||
|
NumOpt++)
|
||||||
|
{
|
||||||
|
row = mysql_fetch_row (mysql_res);
|
||||||
|
switch (Question->Answer.Type)
|
||||||
|
{
|
||||||
|
case Tst_ANS_INT:
|
||||||
|
Tst_CheckIfNumberOfAnswersIsOne (Question);
|
||||||
|
Question->Answer.Integer = Tst_GetIntAnsFromStr (row[1]);
|
||||||
|
break;
|
||||||
|
case Tst_ANS_FLOAT:
|
||||||
|
if (Question->Answer.NumOptions != 2)
|
||||||
|
Lay_ShowErrorAndExit ("Wrong answer.");
|
||||||
|
Question->Answer.FloatingPoint[NumOpt] = Str_GetDoubleFromStr (row[1]);
|
||||||
|
break;
|
||||||
|
case Tst_ANS_TRUE_FALSE:
|
||||||
|
Tst_CheckIfNumberOfAnswersIsOne (Question);
|
||||||
|
Question->Answer.TF = row[1][0];
|
||||||
|
break;
|
||||||
|
case Tst_ANS_UNIQUE_CHOICE:
|
||||||
|
case Tst_ANS_MULTIPLE_CHOICE:
|
||||||
|
case Tst_ANS_TEXT:
|
||||||
|
/* Check number of options */
|
||||||
|
if (Question->Answer.NumOptions > Tst_MAX_OPTIONS_PER_QUESTION)
|
||||||
|
Lay_ShowErrorAndExit ("Wrong answer.");
|
||||||
|
|
||||||
|
/* Allocate space for text and feedback */
|
||||||
|
if (!Tst_AllocateTextChoiceAnswer (Question,NumOpt))
|
||||||
|
/* Abort on error */
|
||||||
|
Ale_ShowAlertsAndExit ();
|
||||||
|
|
||||||
|
/* Get text (row[1]) */
|
||||||
|
Question->Answer.Options[NumOpt].Text[0] = '\0';
|
||||||
|
if (row[1])
|
||||||
|
if (row[1][0])
|
||||||
|
Str_Copy (Question->Answer.Options[NumOpt].Text,row[1],
|
||||||
|
Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
|
||||||
|
|
||||||
|
/* Get feedback (row[2]) */
|
||||||
|
Question->Answer.Options[NumOpt].Feedback[0] = '\0';
|
||||||
|
if (row[2])
|
||||||
|
if (row[2][0])
|
||||||
|
Str_Copy (Question->Answer.Options[NumOpt].Feedback,row[2],
|
||||||
|
Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
|
||||||
|
|
||||||
|
/* Get media (row[3]) */
|
||||||
|
Question->Answer.Options[NumOpt].Media.MedCod = Str_ConvertStrCodToLongCod (row[3]);
|
||||||
|
Med_GetMediaDataByCod (&Question->Answer.Options[NumOpt].Media);
|
||||||
|
|
||||||
|
/* Get if this option is correct (row[4]) */
|
||||||
|
Question->Answer.Options[NumOpt].Correct = (row[4][0] == 'Y');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free structure that stores the query result */
|
||||||
|
DB_FreeMySQLResult (&mysql_res);
|
||||||
|
|
||||||
|
if (!QuestionExists)
|
||||||
|
Lay_ShowErrorAndExit ("Wrong question.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/*************** Get answers of a test question from database ****************/
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
void ExaSet_GetAnswersQst (struct Tst_Question *Question,MYSQL_RES **mysql_res,
|
||||||
|
bool Shuffle)
|
||||||
|
{
|
||||||
|
/***** Get answers of a question from database *****/
|
||||||
|
Question->Answer.NumOptions = (unsigned)
|
||||||
|
DB_QuerySELECT (mysql_res,"can not get answers of a question",
|
||||||
|
"SELECT AnsInd," // row[0]
|
||||||
|
"Answer," // row[1]
|
||||||
|
"Feedback," // row[2]
|
||||||
|
"MedCod," // row[3]
|
||||||
|
"Correct" // row[4]
|
||||||
|
" FROM exa_set_answers"
|
||||||
|
" WHERE QstCod=%ld"
|
||||||
|
" ORDER BY %s",
|
||||||
|
Question->QstCod,
|
||||||
|
Shuffle ? "RAND()" :
|
||||||
|
"AnsInd");
|
||||||
|
if (!Question->Answer.NumOptions)
|
||||||
|
Ale_ShowAlert (Ale_ERROR,"Error when getting answers of a question.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/********************* List question in set for edition **********************/
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static void ExaSet_ListQuestionForEdition (const struct Tst_Question *Question,
|
||||||
|
unsigned QstInd,const char *Anchor)
|
||||||
|
{
|
||||||
|
/***** Number of question and answer type (row[1]) *****/
|
||||||
|
HTM_TD_Begin ("class=\"RT COLOR%u\"",Gbl.RowEvenOdd);
|
||||||
|
Tst_WriteNumQst (QstInd);
|
||||||
|
Tst_WriteAnswerType (Question->Answer.Type);
|
||||||
|
HTM_TD_End ();
|
||||||
|
|
||||||
|
/***** Write question code *****/
|
||||||
|
HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd);
|
||||||
|
HTM_TxtF ("%ld ",Question->QstCod);
|
||||||
|
HTM_TD_End ();
|
||||||
|
|
||||||
|
/***** Write stem (row[3]) and media *****/
|
||||||
|
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
|
||||||
|
HTM_ARTICLE_Begin (Anchor);
|
||||||
|
|
||||||
|
/* Write stem */
|
||||||
|
Tst_WriteQstStem (Question->Stem,"TEST_EDI",
|
||||||
|
true); // Visible
|
||||||
|
|
||||||
|
/* Show media */
|
||||||
|
Med_ShowMedia (&Question->Media,
|
||||||
|
"TEST_MED_EDIT_LIST_STEM_CONTAINER",
|
||||||
|
"TEST_MED_EDIT_LIST_STEM");
|
||||||
|
|
||||||
|
/* Show feedback */
|
||||||
|
Tst_WriteQstFeedback (Question->Feedback,"TEST_EDI_LIGHT");
|
||||||
|
|
||||||
|
/* Show answers */
|
||||||
|
Tst_WriteAnswersListing (Question);
|
||||||
|
|
||||||
|
HTM_ARTICLE_End ();
|
||||||
|
HTM_TD_End ();
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/************* Add selected test questions to set of questions ***************/
|
/************* Add selected test questions to set of questions ***************/
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
@ -1448,13 +1678,7 @@ void ExaSet_AddQstsToSet (void)
|
||||||
if (sscanf (LongStr,"%ld",&QstCod) != 1)
|
if (sscanf (LongStr,"%ld",&QstCod) != 1)
|
||||||
Lay_ShowErrorAndExit ("Wrong question code.");
|
Lay_ShowErrorAndExit ("Wrong question code.");
|
||||||
|
|
||||||
/* Insert question in the table of questions */
|
ExaSet_CopyQstFromBankToExamSet (&Set,QstCod);
|
||||||
DB_QueryINSERT ("can not add question to set",
|
|
||||||
"INSERT INTO exa_questions"
|
|
||||||
" (SetCod,QstCod)"
|
|
||||||
" VALUES"
|
|
||||||
" (%ld,%ld)",
|
|
||||||
Set.SetCod,QstCod);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1495,6 +1719,93 @@ static void ExaSet_FreeListsSelectedQuestions (struct Exa_Exams *Exams)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/******* Copy question and answers from back of questions to exam set ********/
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static void ExaSet_CopyQstFromBankToExamSet (struct ExaSet_Set *Set,long QstCod)
|
||||||
|
{
|
||||||
|
extern const char *Tst_StrAnswerTypesDB[Tst_NUM_ANS_TYPES];
|
||||||
|
extern const char *Txt_Question_removed;
|
||||||
|
struct Tst_Question Question;
|
||||||
|
long CloneMedCod;
|
||||||
|
long QstCodInSet;
|
||||||
|
unsigned NumOpt;
|
||||||
|
MYSQL_RES *mysql_res;
|
||||||
|
MYSQL_ROW row;
|
||||||
|
|
||||||
|
/***** Create test question *****/
|
||||||
|
Tst_QstConstructor (&Question);
|
||||||
|
Question.QstCod = QstCod;
|
||||||
|
|
||||||
|
/***** Get data of question from database *****/
|
||||||
|
if (Tst_GetQstDataFromDB (&Question))
|
||||||
|
{
|
||||||
|
/***** Clone media *****/
|
||||||
|
CloneMedCod = Med_CloneMedia (&Question.Media);
|
||||||
|
Ale_ShowAlert (Ale_INFO,"DEBUG: CloneMedCod = %ld",CloneMedCod);
|
||||||
|
|
||||||
|
/***** Insert question in table of questions *****/
|
||||||
|
QstCodInSet = DB_QueryINSERTandReturnCode ("can not add question to set",
|
||||||
|
"INSERT INTO exa_set_questions"
|
||||||
|
" (SetCod,AnsType,Shuffle,Stem,Feedback,MedCod)"
|
||||||
|
" VALUES"
|
||||||
|
" (%ld,'%s','%c','%s','%s',%ld)",
|
||||||
|
Set->SetCod,
|
||||||
|
Tst_StrAnswerTypesDB[Question.Answer.Type],
|
||||||
|
Question.Answer.Shuffle ? 'Y' :
|
||||||
|
'N',
|
||||||
|
Question.Stem,
|
||||||
|
Question.Feedback,
|
||||||
|
CloneMedCod);
|
||||||
|
|
||||||
|
/***** Get the answers from the database *****/
|
||||||
|
Tst_GetAnswersQst (&Question,&mysql_res,
|
||||||
|
false); // Don't shuffle
|
||||||
|
/*
|
||||||
|
row[0] AnsInd
|
||||||
|
row[1] Answer
|
||||||
|
row[2] Feedback
|
||||||
|
row[3] MedCod
|
||||||
|
row[4] Correct
|
||||||
|
*/
|
||||||
|
for (NumOpt = 0;
|
||||||
|
NumOpt < Question.Answer.NumOptions;
|
||||||
|
NumOpt++)
|
||||||
|
{
|
||||||
|
row = mysql_fetch_row (mysql_res);
|
||||||
|
|
||||||
|
/* Get media (row[3]) */
|
||||||
|
Question.Answer.Options[NumOpt].Media.MedCod = Str_ConvertStrCodToLongCod (row[3]);
|
||||||
|
Med_GetMediaDataByCod (&Question.Answer.Options[NumOpt].Media);
|
||||||
|
|
||||||
|
/* Clone media */
|
||||||
|
CloneMedCod = Med_CloneMedia (&Question.Answer.Options[NumOpt].Media);
|
||||||
|
|
||||||
|
/* Copy answer option to exam set */
|
||||||
|
DB_QueryINSERT ("can not add answer to set",
|
||||||
|
"INSERT INTO exa_set_answers"
|
||||||
|
" (QstCod,AnsInd,Answer,Feedback,MedCod,Correct)"
|
||||||
|
" VALUES"
|
||||||
|
" (%ld,%u,'%s','%s',%ld,'%s')",
|
||||||
|
QstCodInSet, // Question code in set
|
||||||
|
NumOpt, // Answer index (number of option)
|
||||||
|
row[1], // Copy of text
|
||||||
|
row[2], // Copy of feedback
|
||||||
|
CloneMedCod, // Media code of the new cloned media
|
||||||
|
row[4]); // Copy of correct
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free structure that stores the query result */
|
||||||
|
DB_FreeMySQLResult (&mysql_res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Ale_ShowAlert (Ale_WARNING,Txt_Question_removed);
|
||||||
|
|
||||||
|
/***** Destroy test question *****/
|
||||||
|
Tst_QstDestructor (&Question);
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/***************** Request the removal of a set of questions *****************/
|
/***************** Request the removal of a set of questions *****************/
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
@ -1581,10 +1892,10 @@ void ExaSet_RemoveSet (void)
|
||||||
/***** Remove the set from all the tables *****/
|
/***** Remove the set from all the tables *****/
|
||||||
/* Remove questions associated to set */
|
/* Remove questions associated to set */
|
||||||
DB_QueryDELETE ("can not remove questions associated to set",
|
DB_QueryDELETE ("can not remove questions associated to set",
|
||||||
"DELETE FROM exa_questions"
|
"DELETE FROM exa_set_questions"
|
||||||
" USING exa_questions,exa_sets"
|
" USING exa_set_questions,exa_sets"
|
||||||
" WHERE exa_questions.SetCod=%ld"
|
" WHERE exa_set_questions.SetCod=%ld"
|
||||||
" AND exa_questions.SetCod=exa_sets.SetCod"
|
" AND exa_set_questions.SetCod=exa_sets.SetCod"
|
||||||
" AND exa_sets.ExaCod=%ld", // Extra check
|
" AND exa_sets.ExaCod=%ld", // Extra check
|
||||||
Set.SetCod,Set.ExaCod);
|
Set.SetCod,Set.ExaCod);
|
||||||
|
|
||||||
|
@ -1771,7 +2082,7 @@ void ExaSet_RequestRemoveQstFromSet (void)
|
||||||
Exams.SetCod = Set.SetCod;
|
Exams.SetCod = Set.SetCod;
|
||||||
|
|
||||||
/***** Get question index *****/
|
/***** Get question index *****/
|
||||||
Exams.QstCod = Tst_GetParamQstCod ();
|
Exams.QstCod = ExaSet_GetParamQstCod ();
|
||||||
|
|
||||||
/***** Build anchor string *****/
|
/***** Build anchor string *****/
|
||||||
Frm_SetAnchorStr (Set.SetCod,&Anchor);
|
Frm_SetAnchorStr (Set.SetCod,&Anchor);
|
||||||
|
@ -1828,14 +2139,15 @@ void ExaSet_RemoveQstFromSet (void)
|
||||||
Exams.SetCod = Set.SetCod;
|
Exams.SetCod = Set.SetCod;
|
||||||
|
|
||||||
/***** Get question index *****/
|
/***** Get question index *****/
|
||||||
QstCod = Tst_GetParamQstCod ();
|
QstCod = ExaSet_GetParamQstCod ();
|
||||||
|
|
||||||
/***** Remove the question from set *****/
|
/***** Remove the question from set *****/
|
||||||
/* Remove the question itself */
|
/* Remove the question itself */
|
||||||
DB_QueryDELETE ("can not remove a question from a set",
|
DB_QueryDELETE ("can not remove a question from a set",
|
||||||
"DELETE FROM exa_questions"
|
"DELETE FROM exa_set_questions"
|
||||||
" WHERE SetCod=%ld AND QstCod=%ld",
|
" WHERE QstCod=%ld"
|
||||||
Set.SetCod,QstCod);
|
" AND SetCod=%ld", // Extra check
|
||||||
|
QstCod,Set.SetCod);
|
||||||
if (!mysql_affected_rows (&Gbl.mysql))
|
if (!mysql_affected_rows (&Gbl.mysql))
|
||||||
Lay_ShowErrorAndExit ("The question to be removed does not exist.");
|
Lay_ShowErrorAndExit ("The question to be removed does not exist.");
|
||||||
|
|
||||||
|
@ -1847,6 +2159,27 @@ void ExaSet_RemoveQstFromSet (void)
|
||||||
false); // It's not a new exam
|
false); // It's not a new exam
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/************ Get the parameter with the code of a test question *************/
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static long ExaSet_GetParamQstCod (void)
|
||||||
|
{
|
||||||
|
/***** Get code of test question *****/
|
||||||
|
return Par_GetParToLong ("QstCod");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/************ Put parameter with question code to edit, remove... ************/
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static void ExaSet_PutParamQstCod (void *QstCod) // Should be a pointer to long
|
||||||
|
{
|
||||||
|
if (QstCod)
|
||||||
|
if (*((long *) QstCod) > 0) // If question exists
|
||||||
|
Par_PutHiddenParamLong (NULL,"QstCod",*((long *) QstCod));
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/*********** Exchange the order of two consecutive sets in an exam ***********/
|
/*********** Exchange the order of two consecutive sets in an exam ***********/
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
|
@ -28,10 +28,8 @@
|
||||||
/********************************* Headers ***********************************/
|
/********************************* Headers ***********************************/
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
// #include "swad_date.h"
|
|
||||||
// #include "swad_exam_event.h"
|
|
||||||
#include "swad_exam_type.h"
|
#include "swad_exam_type.h"
|
||||||
// #include "swad_scope.h"
|
#include "swad_test_type.h"
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/************************** Public types and constants ***********************/
|
/************************** Public types and constants ***********************/
|
||||||
|
@ -63,6 +61,11 @@ void ExaSet_ListExamSets (struct Exa_Exams *Exams,
|
||||||
|
|
||||||
void ExaSet_ResetSet (struct ExaSet_Set *Set);
|
void ExaSet_ResetSet (struct ExaSet_Set *Set);
|
||||||
|
|
||||||
|
Tst_AnswerType_t ExaSet_GetQstAnswerTypeFromDB (long QstCod);
|
||||||
|
void ExaSet_GetQstDataFromDB (struct Tst_Question *Question,long ExaCod);
|
||||||
|
void ExaSet_GetAnswersQst (struct Tst_Question *Question,MYSQL_RES **mysql_res,
|
||||||
|
bool Shuffle);
|
||||||
|
|
||||||
void ExaSet_AddQstsToSet (void);
|
void ExaSet_AddQstsToSet (void);
|
||||||
|
|
||||||
void ExaSet_RequestRemoveSet (void);
|
void ExaSet_RequestRemoveSet (void);
|
||||||
|
|
150
swad_media.c
150
swad_media.c
|
@ -307,7 +307,7 @@ void Med_GetMediaDataByCod (struct Media *Media)
|
||||||
Length = Cns_MAX_BYTES_WWW;
|
Length = Cns_MAX_BYTES_WWW;
|
||||||
|
|
||||||
if ((Media->URL = (char *) malloc (Length + 1)) == NULL)
|
if ((Media->URL = (char *) malloc (Length + 1)) == NULL)
|
||||||
Lay_ShowErrorAndExit ("Error allocating memory for image URL.");
|
Lay_ShowErrorAndExit ("Error allocating memory for media URL.");
|
||||||
Str_Copy (Media->URL,row[2],
|
Str_Copy (Media->URL,row[2],
|
||||||
Length);
|
Length);
|
||||||
}
|
}
|
||||||
|
@ -324,7 +324,7 @@ void Med_GetMediaDataByCod (struct Media *Media)
|
||||||
Length = Med_MAX_BYTES_TITLE;
|
Length = Med_MAX_BYTES_TITLE;
|
||||||
|
|
||||||
if ((Media->Title = (char *) malloc (Length + 1)) == NULL)
|
if ((Media->Title = (char *) malloc (Length + 1)) == NULL)
|
||||||
Lay_ShowErrorAndExit ("Error allocating memory for image title.");
|
Lay_ShowErrorAndExit ("Error allocating memory for media title.");
|
||||||
Str_Copy (Media->Title,row[3],
|
Str_Copy (Media->Title,row[3],
|
||||||
Length);
|
Length);
|
||||||
}
|
}
|
||||||
|
@ -1405,13 +1405,6 @@ static bool Med_MoveTmpFileToDefDir (struct Media *Media,
|
||||||
|
|
||||||
void Med_StoreMediaInDB (struct Media *Media)
|
void Med_StoreMediaInDB (struct Media *Media)
|
||||||
{
|
{
|
||||||
/***** Trivial case *****/
|
|
||||||
if (Media->Status != Med_MOVED)
|
|
||||||
{
|
|
||||||
Med_ResetMedia (Media); // No media inserted in database
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***** Insert media into database *****/
|
/***** Insert media into database *****/
|
||||||
Media->MedCod = DB_QueryINSERTandReturnCode ("can not create media",
|
Media->MedCod = DB_QueryINSERTandReturnCode ("can not create media",
|
||||||
"INSERT INTO media"
|
"INSERT INTO media"
|
||||||
|
@ -1436,9 +1429,8 @@ void Med_ShowMedia (const struct Media *Media,
|
||||||
char PathMedPriv[PATH_MAX + 1];
|
char PathMedPriv[PATH_MAX + 1];
|
||||||
|
|
||||||
/***** If no media to show ==> nothing to do *****/
|
/***** If no media to show ==> nothing to do *****/
|
||||||
if (Media->Status != Med_STORED_IN_DB)
|
if (Media->Status != Med_STORED_IN_DB ||
|
||||||
return;
|
Media->Type == Med_TYPE_NONE)
|
||||||
if (Media->Type == Med_TYPE_NONE)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/***** Start media container *****/
|
/***** Start media container *****/
|
||||||
|
@ -1808,6 +1800,140 @@ static void Med_AlertThirdPartyCookies (void)
|
||||||
Btn_NO_BUTTON,NULL);
|
Btn_NO_BUTTON,NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/************************* Create duplicate of media *************************/
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
#define Med_NUM_MEDIA 2
|
||||||
|
#define Med_SRC 0
|
||||||
|
#define Med_DST 1
|
||||||
|
|
||||||
|
long Med_CloneMedia (const struct Media *MediaSrc)
|
||||||
|
{
|
||||||
|
long MedCod = -1L;
|
||||||
|
struct Media MediaDst;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
char Path[PATH_MAX + 1];
|
||||||
|
char FullPath[PATH_MAX + 1 + NAME_MAX + 1];
|
||||||
|
} MediaPriv[Med_NUM_MEDIA];
|
||||||
|
size_t Length;
|
||||||
|
|
||||||
|
/***** If no media ==> nothing to do *****/
|
||||||
|
if (MediaSrc->Type == Med_TYPE_NONE)
|
||||||
|
return MedCod;
|
||||||
|
|
||||||
|
/***** If no media name ==> nothing to do *****/
|
||||||
|
if (!MediaSrc->Name)
|
||||||
|
return MedCod;
|
||||||
|
if (!MediaSrc->Name[0])
|
||||||
|
return MedCod;
|
||||||
|
|
||||||
|
/***** Initialize media *****/
|
||||||
|
Med_MediaConstructor (&MediaDst);
|
||||||
|
|
||||||
|
/***** Copy type *****/
|
||||||
|
MediaDst.Type = MediaSrc->Type;
|
||||||
|
|
||||||
|
/***** Assign a unique name for the destination media *****/
|
||||||
|
Cry_CreateUniqueNameEncrypted (MediaDst.Name);
|
||||||
|
|
||||||
|
/***** Copy media URL *****/
|
||||||
|
Med_FreeMediaURL (&MediaDst);
|
||||||
|
if (MediaSrc->URL)
|
||||||
|
{
|
||||||
|
/* Get and limit length of the URL */
|
||||||
|
Length = strlen (MediaSrc->URL);
|
||||||
|
if (Length > Cns_MAX_BYTES_WWW)
|
||||||
|
Length = Cns_MAX_BYTES_WWW;
|
||||||
|
if ((MediaDst.URL = (char *) malloc (Length + 1)) == NULL)
|
||||||
|
Lay_ShowErrorAndExit ("Error allocating memory for media URL.");
|
||||||
|
Str_Copy (MediaDst.URL,MediaSrc->URL,
|
||||||
|
Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***** Copy media title *****/
|
||||||
|
Med_FreeMediaTitle (&MediaDst);
|
||||||
|
if (MediaSrc->Title)
|
||||||
|
{
|
||||||
|
/* Get and limit length of the title */
|
||||||
|
Length = strlen (MediaSrc->Title);
|
||||||
|
if (Length > Cns_MAX_BYTES_WWW)
|
||||||
|
Length = Cns_MAX_BYTES_WWW;
|
||||||
|
if ((MediaDst.Title = (char *) malloc (Length + 1)) == NULL)
|
||||||
|
Lay_ShowErrorAndExit ("Error allocating memory for media title.");
|
||||||
|
Str_Copy (MediaDst.Title,MediaSrc->Title,
|
||||||
|
Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***** Create duplicate of files *****/
|
||||||
|
switch (MediaSrc->Type)
|
||||||
|
{
|
||||||
|
case Med_JPG:
|
||||||
|
case Med_GIF:
|
||||||
|
case Med_MP4:
|
||||||
|
case Med_WEBM:
|
||||||
|
case Med_OGG:
|
||||||
|
/***** Create private directories if not exist *****/
|
||||||
|
/* Create private directory for images/videos if it does not exist */
|
||||||
|
Fil_CreateDirIfNotExists (Cfg_PATH_MEDIA_PRIVATE);
|
||||||
|
|
||||||
|
/* Build paths to private directories */
|
||||||
|
snprintf (MediaPriv[Med_SRC].Path,sizeof (MediaPriv[Med_SRC].Path),
|
||||||
|
"%s/%c%c",
|
||||||
|
Cfg_PATH_MEDIA_PRIVATE,MediaSrc->Name[0],MediaSrc->Name[1]);
|
||||||
|
snprintf (MediaPriv[Med_DST].Path,sizeof (MediaPriv[Med_DST].Path),
|
||||||
|
"%s/%c%c",
|
||||||
|
Cfg_PATH_MEDIA_PRIVATE,MediaDst.Name[0],MediaDst.Name[1]);
|
||||||
|
Fil_CreateDirIfNotExists (MediaPriv[Med_DST].Path);
|
||||||
|
|
||||||
|
/* Build paths to private files */
|
||||||
|
snprintf (MediaPriv[Med_SRC].FullPath,sizeof (MediaPriv[Med_SRC].FullPath),
|
||||||
|
"%s/%s.%s",
|
||||||
|
MediaPriv[Med_SRC].Path,MediaSrc->Name,Med_Extensions[MediaSrc->Type]);
|
||||||
|
snprintf (MediaPriv[Med_DST].FullPath,sizeof (MediaPriv[Med_DST].FullPath),
|
||||||
|
"%s/%s.%s",
|
||||||
|
MediaPriv[Med_DST].Path,MediaDst.Name,Med_Extensions[MediaSrc->Type]);
|
||||||
|
|
||||||
|
/* Copy file */
|
||||||
|
Ale_ShowAlert (Ale_INFO,"DEBUG<br />"
|
||||||
|
"MediaPriv[Med_SRC].FullPath = "%s"<br />"
|
||||||
|
"MediaPriv[Med_DST].FullPath = "%s"",
|
||||||
|
MediaPriv[Med_SRC].FullPath,
|
||||||
|
MediaPriv[Med_DST].FullPath);
|
||||||
|
|
||||||
|
Fil_FastCopyOfFiles (MediaPriv[Med_SRC].FullPath,
|
||||||
|
MediaPriv[Med_DST].FullPath);
|
||||||
|
|
||||||
|
if (MediaSrc->Type == Med_GIF)
|
||||||
|
{
|
||||||
|
/* Build private paths to PNG */
|
||||||
|
snprintf (MediaPriv[Med_SRC].FullPath,sizeof (MediaPriv[Med_SRC].FullPath),
|
||||||
|
"%s/%s.png",
|
||||||
|
MediaPriv[Med_SRC].Path,MediaSrc->Name);
|
||||||
|
snprintf (MediaPriv[Med_DST].FullPath,sizeof (MediaPriv[Med_DST].FullPath),
|
||||||
|
"%s/%s.png",
|
||||||
|
MediaPriv[Med_DST].Path,MediaDst.Name);
|
||||||
|
|
||||||
|
/* Copy PNG file */
|
||||||
|
Fil_FastCopyOfFiles (MediaPriv[Med_SRC].FullPath,
|
||||||
|
MediaPriv[Med_DST].FullPath);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***** Code to return *****/
|
||||||
|
Med_StoreMediaInDB (&MediaDst);
|
||||||
|
MedCod = MediaDst.MedCod;
|
||||||
|
|
||||||
|
/***** Free media *****/
|
||||||
|
Med_MediaDestructor (&MediaDst);
|
||||||
|
|
||||||
|
return MedCod;
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/********** Remove several media files and entries in database ***************/
|
/********** Remove several media files and entries in database ***************/
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
|
@ -155,6 +155,8 @@ void Med_StoreMediaInDB (struct Media *Media);
|
||||||
void Med_ShowMedia (const struct Media *Media,
|
void Med_ShowMedia (const struct Media *Media,
|
||||||
const char *ClassContainer,const char *ClassMedia);
|
const char *ClassContainer,const char *ClassMedia);
|
||||||
|
|
||||||
|
long Med_CloneMedia (const struct Media *MediaSrc);
|
||||||
|
|
||||||
void Med_RemoveMediaFromAllRows (unsigned NumMedia,MYSQL_RES *mysql_res);
|
void Med_RemoveMediaFromAllRows (unsigned NumMedia,MYSQL_RES *mysql_res);
|
||||||
void Med_RemoveMedia (long MedCod);
|
void Med_RemoveMedia (long MedCod);
|
||||||
|
|
||||||
|
|
|
@ -3197,13 +3197,13 @@ static void Msg_ShowASentOrReceivedMessage (struct Msg_Messages *Messages,
|
||||||
HTM_TxtColonNBSP (Txt_MSG_Content);
|
HTM_TxtColonNBSP (Txt_MSG_Content);
|
||||||
HTM_TD_End ();
|
HTM_TD_End ();
|
||||||
|
|
||||||
/***** Initialize image *****/
|
/***** Initialize media *****/
|
||||||
Med_MediaConstructor (&Media);
|
Med_MediaConstructor (&Media);
|
||||||
|
|
||||||
/***** Get message content and optional image *****/
|
/***** Get message content and optional media *****/
|
||||||
Msg_GetMsgContent (MsgCod,Content,&Media);
|
Msg_GetMsgContent (MsgCod,Content,&Media);
|
||||||
|
|
||||||
/***** Show content and image *****/
|
/***** Show content and media *****/
|
||||||
HTM_TD_Begin ("colspan=\"2\" class=\"MSG_TXT LT\"");
|
HTM_TD_Begin ("colspan=\"2\" class=\"MSG_TXT LT\"");
|
||||||
if (Content[0])
|
if (Content[0])
|
||||||
Msg_WriteMsgContent (Content,Cns_MAX_BYTES_LONG_TEXT,true,false);
|
Msg_WriteMsgContent (Content,Cns_MAX_BYTES_LONG_TEXT,true,false);
|
||||||
|
@ -3212,7 +3212,7 @@ static void Msg_ShowASentOrReceivedMessage (struct Msg_Messages *Messages,
|
||||||
|
|
||||||
HTM_TR_End ();
|
HTM_TR_End ();
|
||||||
|
|
||||||
/***** Free image *****/
|
/***** Free media *****/
|
||||||
Med_MediaDestructor (&Media);
|
Med_MediaDestructor (&Media);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
35
swad_test.c
35
swad_test.c
|
@ -74,13 +74,7 @@ const char *Tst_StrAnswerTypesXML[Tst_NUM_ANS_TYPES] =
|
||||||
[Tst_ANS_TEXT ] = "text",
|
[Tst_ANS_TEXT ] = "text",
|
||||||
};
|
};
|
||||||
|
|
||||||
/*****************************************************************************/
|
const char *Tst_StrAnswerTypesDB[Tst_NUM_ANS_TYPES] =
|
||||||
/**************************** Private constants ******************************/
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
#define Tst_MAX_BYTES_TAGS_LIST (16 * 1024)
|
|
||||||
|
|
||||||
static const char *Tst_StrAnswerTypesDB[Tst_NUM_ANS_TYPES] =
|
|
||||||
{
|
{
|
||||||
[Tst_ANS_INT ] = "int",
|
[Tst_ANS_INT ] = "int",
|
||||||
[Tst_ANS_FLOAT ] = "float",
|
[Tst_ANS_FLOAT ] = "float",
|
||||||
|
@ -90,6 +84,12 @@ static const char *Tst_StrAnswerTypesDB[Tst_NUM_ANS_TYPES] =
|
||||||
[Tst_ANS_TEXT ] = "text",
|
[Tst_ANS_TEXT ] = "text",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/**************************** Private constants ******************************/
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
#define Tst_MAX_BYTES_TAGS_LIST (16 * 1024)
|
||||||
|
|
||||||
// Test images will be saved with:
|
// Test images will be saved with:
|
||||||
// - maximum width of Tst_IMAGE_SAVED_MAX_HEIGHT
|
// - maximum width of Tst_IMAGE_SAVED_MAX_HEIGHT
|
||||||
// - maximum height of Tst_IMAGE_SAVED_MAX_HEIGHT
|
// - maximum height of Tst_IMAGE_SAVED_MAX_HEIGHT
|
||||||
|
@ -936,7 +936,7 @@ static void TstPrn_WriteQstAndAnsToFill (struct TstPrn_PrintedQuestion *PrintedQ
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/******************* List exam/game question for edition *********************/
|
/********************* List game question for edition ************************/
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
void Tst_ListQuestionForEdition (const struct Tst_Question *Question,
|
void Tst_ListQuestionForEdition (const struct Tst_Question *Question,
|
||||||
|
@ -2662,7 +2662,7 @@ static void Tst_GetQuestionsForNewTestFromDB (struct Tst_Test *Test,
|
||||||
case Tst_ANS_MULTIPLE_CHOICE:
|
case Tst_ANS_MULTIPLE_CHOICE:
|
||||||
/* If answer type is unique or multiple option,
|
/* If answer type is unique or multiple option,
|
||||||
generate indexes of answers depending on shuffle */
|
generate indexes of answers depending on shuffle */
|
||||||
Tst_GenerateChoiceIndexesDependingOnShuffle (&Print->PrintedQuestions[NumQst],Shuffle);
|
Tst_GenerateChoiceIndexes (&Print->PrintedQuestions[NumQst],Shuffle);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -2680,13 +2680,12 @@ static void Tst_GetQuestionsForNewTestFromDB (struct Tst_Test *Test,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/********* Get single or multiple choice answer when seeing a test ***********/
|
/*************** Generate choice indexes depending on shuffle ****************/
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
void Tst_GenerateChoiceIndexesDependingOnShuffle (struct TstPrn_PrintedQuestion *PrintedQuestion,
|
void Tst_GenerateChoiceIndexes (struct TstPrn_PrintedQuestion *PrintedQuestion,
|
||||||
bool Shuffle)
|
bool Shuffle)
|
||||||
{
|
{
|
||||||
// extern const char *Par_SEPARATOR_PARAM_MULTIPLE;
|
|
||||||
struct Tst_Question Question;
|
struct Tst_Question Question;
|
||||||
unsigned NumOpt;
|
unsigned NumOpt;
|
||||||
MYSQL_RES *mysql_res;
|
MYSQL_RES *mysql_res;
|
||||||
|
@ -2733,7 +2732,6 @@ void Tst_GenerateChoiceIndexesDependingOnShuffle (struct TstPrn_PrintedQuestion
|
||||||
if (NumOpt == 0)
|
if (NumOpt == 0)
|
||||||
snprintf (StrInd,sizeof (StrInd),"%u",Index);
|
snprintf (StrInd,sizeof (StrInd),"%u",Index);
|
||||||
else
|
else
|
||||||
// snprintf (StrInd,sizeof (StrInd),"%s%u",Par_SEPARATOR_PARAM_MULTIPLE,Index);
|
|
||||||
snprintf (StrInd,sizeof (StrInd),",%u",Index);
|
snprintf (StrInd,sizeof (StrInd),",%u",Index);
|
||||||
Str_Concat (PrintedQuestion->StrIndexes,StrInd,
|
Str_Concat (PrintedQuestion->StrIndexes,StrInd,
|
||||||
Tst_MAX_BYTES_INDEXES_ONE_QST);
|
Tst_MAX_BYTES_INDEXES_ONE_QST);
|
||||||
|
@ -4699,19 +4697,18 @@ static void Tst_FreeMediaOfQuestion (struct Tst_Question *Question)
|
||||||
/*************** Get answer type of a question from database *****************/
|
/*************** Get answer type of a question from database *****************/
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
Tst_AnswerType_t Tst_GetQstAnswerType (long QstCod)
|
Tst_AnswerType_t Tst_GetQstAnswerTypeFromDB (long QstCod)
|
||||||
{
|
{
|
||||||
MYSQL_RES *mysql_res;
|
MYSQL_RES *mysql_res;
|
||||||
MYSQL_ROW row;
|
MYSQL_ROW row;
|
||||||
Tst_AnswerType_t AnswerType;
|
Tst_AnswerType_t AnswerType;
|
||||||
|
|
||||||
/***** Get type of answer from database *****/
|
/***** Get type of answer from database *****/
|
||||||
if (!DB_QuerySELECT (&mysql_res,"can not get a question",
|
if (!DB_QuerySELECT (&mysql_res,"can not get the type of a question",
|
||||||
"SELECT AnsType" // row[0]
|
"SELECT AnsType" // row[0]
|
||||||
" FROM tst_questions"
|
" FROM tst_questions"
|
||||||
" WHERE QstCod=%ld"
|
" WHERE QstCod=%ld",
|
||||||
" AND CrsCod=%ld", // Extra check
|
QstCod))
|
||||||
QstCod,Gbl.Hierarchy.Crs.CrsCod))
|
|
||||||
Lay_ShowErrorAndExit ("Question does not exist.");
|
Lay_ShowErrorAndExit ("Question does not exist.");
|
||||||
|
|
||||||
/* Get type of answer */
|
/* Get type of answer */
|
||||||
|
|
|
@ -122,8 +122,8 @@ void Tst_ListQuestionsToEdit (void);
|
||||||
void Tst_ListQuestionsToSelectForSet (struct Exa_Exams *Exams);
|
void Tst_ListQuestionsToSelectForSet (struct Exa_Exams *Exams);
|
||||||
void Tst_ListQuestionsToSelectForGame (struct Gam_Games *Games);
|
void Tst_ListQuestionsToSelectForGame (struct Gam_Games *Games);
|
||||||
|
|
||||||
void Tst_GenerateChoiceIndexesDependingOnShuffle (struct TstPrn_PrintedQuestion *PrintedQuestion,
|
void Tst_GenerateChoiceIndexes (struct TstPrn_PrintedQuestion *PrintedQuestion,
|
||||||
bool Shuffle);
|
bool Shuffle);
|
||||||
|
|
||||||
void Tst_WriteParamEditQst (const struct Tst_Test *Test);
|
void Tst_WriteParamEditQst (const struct Tst_Test *Test);
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ void Tst_QstDestructor (struct Tst_Question *Question);
|
||||||
|
|
||||||
bool Tst_AllocateTextChoiceAnswer (struct Tst_Question *Question,unsigned NumOpt);
|
bool Tst_AllocateTextChoiceAnswer (struct Tst_Question *Question,unsigned NumOpt);
|
||||||
|
|
||||||
Tst_AnswerType_t Tst_GetQstAnswerType (long QstCod);
|
Tst_AnswerType_t Tst_GetQstAnswerTypeFromDB (long QstCod);
|
||||||
bool Tst_GetQstDataFromDB (struct Tst_Question *Question);
|
bool Tst_GetQstDataFromDB (struct Tst_Question *Question);
|
||||||
Tst_AnswerType_t Tst_ConvertFromStrAnsTypDBToAnsTyp (const char *StrAnsTypeBD);
|
Tst_AnswerType_t Tst_ConvertFromStrAnsTypDBToAnsTyp (const char *StrAnsTypeBD);
|
||||||
void Tst_ReceiveQst (void);
|
void Tst_ReceiveQst (void);
|
||||||
|
|
|
@ -374,7 +374,7 @@ void TstPrn_ComputeScoresAndStoreQuestionsOfPrint (struct TstPrn_Print *Print,
|
||||||
/* Compute question score */
|
/* Compute question score */
|
||||||
Tst_QstConstructor (&Question);
|
Tst_QstConstructor (&Question);
|
||||||
Question.QstCod = Print->PrintedQuestions[NumQst].QstCod;
|
Question.QstCod = Print->PrintedQuestions[NumQst].QstCod;
|
||||||
Question.Answer.Type = Tst_GetQstAnswerType (Question.QstCod);
|
Question.Answer.Type = Tst_GetQstAnswerTypeFromDB (Question.QstCod);
|
||||||
TstPrn_ComputeAnswerScore (&Print->PrintedQuestions[NumQst],&Question);
|
TstPrn_ComputeAnswerScore (&Print->PrintedQuestions[NumQst],&Question);
|
||||||
Tst_QstDestructor (&Question);
|
Tst_QstDestructor (&Question);
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,9 @@
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/********************************* Headers ***********************************/
|
/********************************* Headers ***********************************/
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
#include "swad_media.h"
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/***************************** Public constants ******************************/
|
/***************************** Public constants ******************************/
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
Loading…
Reference in New Issue