mirror of
https://github.com/acanas/swad-core.git
synced 2024-06-08 01:35:32 +02:00
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(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
|
||||
--
|
||||
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,
|
||||
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
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS exa_sets (
|
||||
|
|
|
@ -548,10 +548,33 @@ enscript -2 --landscape --color --file-align=2 --highlight --line-numbers -o - *
|
|||
En OpenSWAD:
|
||||
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 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.
|
||||
Unckeck radio button in exam prints. (303057 lines)
|
||||
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(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 *****/
|
||||
/*
|
||||
mysql> DESCRIBE exa_results;
|
||||
|
@ -1258,6 +1242,59 @@ mysql> DESCRIBE exa_results;
|
|||
"Score DOUBLE PRECISION NOT NULL DEFAULT 0,"
|
||||
"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 *****/
|
||||
/*
|
||||
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 *****/
|
||||
ExaEvt_RemoveEventsInExamFromAllTables (ExaCod);
|
||||
|
||||
/***** Remove exam question *****/
|
||||
/***** 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);
|
||||
|
||||
/***** Remove exam *****/
|
||||
|
@ -1203,10 +1212,19 @@ void Exa_RemoveExamsCrs (long CrsCod)
|
|||
|
||||
/***** Remove the questions in exams *****/
|
||||
DB_QueryDELETE ("can not remove questions in course exams",
|
||||
"DELETE FROM exa_questions"
|
||||
" USING exa_exams,exa_questions"
|
||||
"DELETE FROM exa_set_questions"
|
||||
" USING exa_exams,exa_sets,exa_set_questions"
|
||||
" 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);
|
||||
|
||||
/***** Remove the exams *****/
|
||||
|
@ -1678,7 +1696,7 @@ long Exa_GetQstCodFromQstInd (long ExaCod,unsigned QstInd)
|
|||
|
||||
/***** Get question code of the question to be moved up *****/
|
||||
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",
|
||||
ExaCod,QstInd))
|
||||
Lay_ShowErrorAndExit ("Error: wrong question index.");
|
||||
|
@ -1710,7 +1728,7 @@ unsigned Exa_GetPrevQuestionIndexInExam (long ExaCod,unsigned QstInd)
|
|||
// Although indexes are always continuous...
|
||||
// ...this implementation works even with non continuous indexes
|
||||
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",
|
||||
ExaCod,QstInd))
|
||||
Lay_ShowErrorAndExit ("Error: previous question index not found.");
|
||||
|
@ -1744,7 +1762,7 @@ unsigned Exa_GetNextQuestionIndexInExam (long ExaCod,unsigned QstInd)
|
|||
// Although indexes are always continuous...
|
||||
// ...this implementation works even with non continuous indexes
|
||||
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",
|
||||
ExaCod,QstInd))
|
||||
Lay_ShowErrorAndExit ("Error: next question index not found.");
|
||||
|
@ -1964,69 +1982,69 @@ double Exa_GetNumQstsPerCrsExam (Hie_Level_t Scope)
|
|||
case Hie_SYS:
|
||||
DB_QuerySELECT (&mysql_res,"can not get number of questions per exam",
|
||||
"SELECT AVG(NumQsts) FROM"
|
||||
" (SELECT COUNT(exa_questions.QstCod) AS NumQsts"
|
||||
" FROM exa_exams,exa_questions"
|
||||
" WHERE exa_exams.ExaCod=exa_questions.ExaCod"
|
||||
" GROUP BY exa_questions.ExaCod) AS NumQstsTable");
|
||||
" (SELECT COUNT(exa_set_questions.QstCod) AS NumQsts"
|
||||
" FROM exa_exams,exa_set_questions"
|
||||
" WHERE exa_exams.ExaCod=exa_set_questions.ExaCod"
|
||||
" GROUP BY exa_set_questions.ExaCod) AS NumQstsTable");
|
||||
break;
|
||||
case Hie_CTY:
|
||||
DB_QuerySELECT (&mysql_res,"can not get number of questions per exam",
|
||||
"SELECT AVG(NumQsts) FROM"
|
||||
" (SELECT COUNT(exa_questions.QstCod) AS NumQsts"
|
||||
" FROM institutions,centres,degrees,courses,exa_exams,exa_questions"
|
||||
" (SELECT COUNT(exa_set_questions.QstCod) AS NumQsts"
|
||||
" FROM institutions,centres,degrees,courses,exa_exams,exa_set_questions"
|
||||
" WHERE institutions.CtyCod=%ld"
|
||||
" AND institutions.InsCod=centres.InsCod"
|
||||
" AND centres.CtrCod=degrees.CtrCod"
|
||||
" AND degrees.DegCod=courses.DegCod"
|
||||
" AND courses.CrsCod=exa_exams.CrsCod"
|
||||
" AND exa_exams.ExaCod=exa_questions.ExaCod"
|
||||
" GROUP BY exa_questions.ExaCod) AS NumQstsTable",
|
||||
" AND exa_exams.ExaCod=exa_set_questions.ExaCod"
|
||||
" GROUP BY exa_set_questions.ExaCod) AS NumQstsTable",
|
||||
Gbl.Hierarchy.Cty.CtyCod);
|
||||
break;
|
||||
case Hie_INS:
|
||||
DB_QuerySELECT (&mysql_res,"can not get number of questions per exam",
|
||||
"SELECT AVG(NumQsts) FROM"
|
||||
" (SELECT COUNT(exa_questions.QstCod) AS NumQsts"
|
||||
" FROM centres,degrees,courses,exa_exams,exa_questions"
|
||||
" (SELECT COUNT(exa_set_questions.QstCod) AS NumQsts"
|
||||
" FROM centres,degrees,courses,exa_exams,exa_set_questions"
|
||||
" WHERE centres.InsCod=%ld"
|
||||
" AND centres.CtrCod=degrees.CtrCod"
|
||||
" AND degrees.DegCod=courses.DegCod"
|
||||
" AND courses.CrsCod=exa_exams.CrsCod"
|
||||
" AND exa_exams.ExaCod=exa_questions.ExaCod"
|
||||
" GROUP BY exa_questions.ExaCod) AS NumQstsTable",
|
||||
" AND exa_exams.ExaCod=exa_set_questions.ExaCod"
|
||||
" GROUP BY exa_set_questions.ExaCod) AS NumQstsTable",
|
||||
Gbl.Hierarchy.Ins.InsCod);
|
||||
break;
|
||||
case Hie_CTR:
|
||||
DB_QuerySELECT (&mysql_res,"can not get number of questions per exam",
|
||||
"SELECT AVG(NumQsts) FROM"
|
||||
" (SELECT COUNT(exa_questions.QstCod) AS NumQsts"
|
||||
" FROM degrees,courses,exa_exams,exa_questions"
|
||||
" (SELECT COUNT(exa_set_questions.QstCod) AS NumQsts"
|
||||
" FROM degrees,courses,exa_exams,exa_set_questions"
|
||||
" WHERE degrees.CtrCod=%ld"
|
||||
" AND degrees.DegCod=courses.DegCod"
|
||||
" AND courses.CrsCod=exa_exams.CrsCod"
|
||||
" AND exa_exams.ExaCod=exa_questions.ExaCod"
|
||||
" GROUP BY exa_questions.ExaCod) AS NumQstsTable",
|
||||
" AND exa_exams.ExaCod=exa_set_questions.ExaCod"
|
||||
" GROUP BY exa_set_questions.ExaCod) AS NumQstsTable",
|
||||
Gbl.Hierarchy.Ctr.CtrCod);
|
||||
break;
|
||||
case Hie_DEG:
|
||||
DB_QuerySELECT (&mysql_res,"can not get number of questions per exam",
|
||||
"SELECT AVG(NumQsts) FROM"
|
||||
" (SELECT COUNT(exa_questions.QstCod) AS NumQsts"
|
||||
" FROM courses,exa_exams,exa_questions"
|
||||
" (SELECT COUNT(exa_set_questions.QstCod) AS NumQsts"
|
||||
" FROM courses,exa_exams,exa_set_questions"
|
||||
" WHERE courses.DegCod=%ld"
|
||||
" AND courses.CrsCod=exa_exams.CrsCod"
|
||||
" AND exa_exams.ExaCod=exa_questions.ExaCod"
|
||||
" GROUP BY exa_questions.ExaCod) AS NumQstsTable",
|
||||
" AND exa_exams.ExaCod=exa_set_questions.ExaCod"
|
||||
" GROUP BY exa_set_questions.ExaCod) AS NumQstsTable",
|
||||
Gbl.Hierarchy.Deg.DegCod);
|
||||
break;
|
||||
case Hie_CRS:
|
||||
DB_QuerySELECT (&mysql_res,"can not get number of questions per exam",
|
||||
"SELECT AVG(NumQsts) FROM"
|
||||
" (SELECT COUNT(exa_questions.QstCod) AS NumQsts"
|
||||
" FROM exa_exams,exa_questions"
|
||||
" (SELECT COUNT(exa_set_questions.QstCod) AS NumQsts"
|
||||
" FROM exa_exams,exa_set_questions"
|
||||
" WHERE exa_exams.Cod=%ld"
|
||||
" AND exa_exams.ExaCod=exa_questions.ExaCod"
|
||||
" GROUP BY exa_questions.ExaCod) AS NumQstsTable",
|
||||
" AND exa_exams.ExaCod=exa_set_questions.ExaCod"
|
||||
" GROUP BY exa_set_questions.ExaCod) AS NumQstsTable",
|
||||
Gbl.Hierarchy.Crs.CrsCod);
|
||||
break;
|
||||
default:
|
||||
|
@ -2060,9 +2078,9 @@ void Exa_ShowTstTagsPresentInAnExam (long ExaCod)
|
|||
"SELECT tst_tags.TagTxt" // row[0]
|
||||
" FROM"
|
||||
" (SELECT DISTINCT(tst_question_tags.TagCod)"
|
||||
" FROM tst_question_tags,exa_questions"
|
||||
" WHERE exa_questions.ExaCod=%ld"
|
||||
" AND exa_questions.QstCod=tst_question_tags.QstCod)"
|
||||
" FROM tst_question_tags,exa_set_questions"
|
||||
" WHERE exa_set_questions.ExaCod=%ld"
|
||||
" AND exa_set_questions.QstCod=tst_question_tags.QstCod)"
|
||||
" AS TagsCods,tst_tags"
|
||||
" WHERE TagsCods.TagCod=tst_tags.TagCod"
|
||||
" ORDER BY tst_tags.TagTxt",
|
||||
|
@ -2089,9 +2107,9 @@ void Exa_GetScoreRange (long ExaCod,double *MinScore,double *MaxScore)
|
|||
NumRows = (unsigned)
|
||||
DB_QuerySELECT (&mysql_res,"can not get data of a question",
|
||||
"SELECT COUNT(tst_answers.AnsInd) AS N"
|
||||
" FROM tst_answers,exa_questions"
|
||||
" WHERE exa_questions.ExaCod=%ld"
|
||||
" AND exa_questions.QstCod=tst_answers.QstCod"
|
||||
" FROM tst_answers,exa_set_questions"
|
||||
" WHERE exa_set_questions.ExaCod=%ld"
|
||||
" AND exa_set_questions.QstCod=tst_answers.QstCod"
|
||||
" GROUP BY tst_answers.QstCod",
|
||||
ExaCod);
|
||||
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
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/******************** 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 ******************/
|
||||
/*****************************************************************************/
|
||||
|
|
|
@ -93,6 +93,8 @@ static void ExaPrn_GetQuestionsForNewPrintFromDB (struct ExaPrn_Print *Print);
|
|||
static unsigned ExaPrn_GetSomeQstsFromSetToPrint (struct ExaPrn_Print *Print,
|
||||
struct ExaSet_Set *Set,
|
||||
unsigned *NumQstInPrint);
|
||||
static void ExaPrn_GenerateChoiceIndexes (struct TstPrn_PrintedQuestion *PrintedQuestion,
|
||||
bool Shuffle);
|
||||
static void ExaPrn_CreatePrintInDB (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 *****/
|
||||
NumQstsInSet = (unsigned)
|
||||
DB_QuerySELECT (&mysql_res,"can not get questions from set",
|
||||
"SELECT tst_questions.QstCod," // row[0]
|
||||
"tst_questions.AnsType," // row[1]
|
||||
"tst_questions.Shuffle" // row[2]
|
||||
" FROM exa_questions,tst_questions"
|
||||
" WHERE exa_questions.setCod=%ld"
|
||||
" AND exa_questions.QstCod=tst_questions.QstCod"
|
||||
"SELECT QstCod," // row[0]
|
||||
"AnsType," // row[1]
|
||||
"Shuffle" // row[2]
|
||||
" FROM exa_set_questions"
|
||||
" WHERE SetCod=%ld"
|
||||
" ORDER BY RAND()" // Don't use RAND(NOW()) because the same ordering will be repeated across sets
|
||||
" LIMIT %u",
|
||||
Set->SetCod,
|
||||
|
@ -414,7 +415,7 @@ static unsigned ExaPrn_GetSomeQstsFromSetToPrint (struct ExaPrn_Print *Print,
|
|||
case Tst_ANS_MULTIPLE_CHOICE:
|
||||
/* If answer type is unique or multiple option,
|
||||
generate indexes of answers depending on shuffle */
|
||||
Tst_GenerateChoiceIndexesDependingOnShuffle (&Print->PrintedQuestions[*NumQstInPrint],Shuffle);
|
||||
ExaPrn_GenerateChoiceIndexes (&Print->PrintedQuestions[*NumQstInPrint],Shuffle);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -430,6 +431,71 @@ static unsigned ExaPrn_GetSomeQstsFromSetToPrint (struct ExaPrn_Print *Print,
|
|||
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 *******************/
|
||||
/*****************************************************************************/
|
||||
|
@ -571,8 +637,7 @@ static void ExaPrn_ShowTableWithQstsToFill (struct ExaPrn_Print *Print)
|
|||
Question.QstCod = Print->PrintedQuestions[NumQst].QstCod;
|
||||
|
||||
/* Show question */
|
||||
if (!Tst_GetQstDataFromDB (&Question)) // Question exists
|
||||
Lay_ShowErrorAndExit ("Wrong question.");
|
||||
ExaSet_GetQstDataFromDB (&Question,Print->ExaCod);
|
||||
|
||||
/* Write question and answers */
|
||||
ExaPrn_WriteQstAndAnsToFill (Print,NumQst,&Question);
|
||||
|
@ -981,7 +1046,7 @@ static void ExaPrn_ComputeScoreAndStoreQuestionOfPrint (struct ExaPrn_Print *Pri
|
|||
/***** Compute question score *****/
|
||||
Tst_QstConstructor (&Question);
|
||||
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);
|
||||
Tst_QstDestructor (&Question);
|
||||
|
||||
|
@ -1000,7 +1065,6 @@ static void ExaPrn_ComputeScoreAndStoreQuestionOfPrint (struct ExaPrn_Print *Pri
|
|||
NumQst); // 0, 1, 2, 3...
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/************* Get the questions of an exam print from database **************/
|
||||
/*****************************************************************************/
|
||||
|
|
|
@ -1326,15 +1326,15 @@ void ExaRes_GetExamResultQuestionsFromDB (long EvtCod,long UsrCod,
|
|||
Print->NumQsts = (unsigned)
|
||||
DB_QuerySELECT (&mysql_res,"can not get questions and answers"
|
||||
" of a event result",
|
||||
"SELECT exa_questions.QstCod," // row[0]
|
||||
"exa_questions.QstInd," // row[1]
|
||||
"SELECT exa_set_questions.QstCod," // row[0]
|
||||
"exa_set_questions.QstInd," // row[1]
|
||||
"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"
|
||||
" AND exa_events.ExaCod=exa_questions.ExaCod"
|
||||
" AND exa_events.ExaCod=exa_set_questions.ExaCod"
|
||||
" AND exa_events.EvtCod=exa_indexes.EvtCod"
|
||||
" AND exa_questions.QstInd=exa_indexes.QstInd"
|
||||
" ORDER BY exa_questions.QstInd",
|
||||
" AND exa_set_questions.QstInd=exa_indexes.QstInd"
|
||||
" ORDER BY exa_set_questions.QstInd",
|
||||
EvtCod);
|
||||
for (NumQst = 0, Print->NumQstsNotBlank = 0;
|
||||
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,
|
||||
MYSQL_RES *mysql_res,
|
||||
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_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,
|
||||
unsigned SetIndTop,unsigned SetIndBottom);
|
||||
|
||||
|
@ -153,7 +160,7 @@ void ExaSet_PutParamsOneSet (void *Exams)
|
|||
static void ExaSet_PutParamsOneQst (void *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 *****/
|
||||
return
|
||||
(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",
|
||||
SetCod);
|
||||
}
|
||||
|
@ -1042,11 +1049,10 @@ static void ExaSet_ListSetQuestions (struct Exa_Exams *Exams,
|
|||
/***** Get data of questions from database *****/
|
||||
NumQsts = (unsigned)
|
||||
DB_QuerySELECT (&mysql_res,"can not get exam questions",
|
||||
"SELECT exa_questions.QstCod" // row[0]
|
||||
" FROM exa_questions LEFT JOIN tst_questions" // LEFT JOIN because the question could be removed in table of test questions
|
||||
" ON (exa_questions.QstCod=tst_questions.QstCod)"
|
||||
" WHERE exa_questions.SetCod=%ld"
|
||||
" ORDER BY tst_questions.Stem",
|
||||
"SELECT QstCod" // row[0]
|
||||
" FROM exa_set_questions"
|
||||
" WHERE SetCod=%ld"
|
||||
" ORDER BY Stem",
|
||||
Set->SetCod);
|
||||
|
||||
/***** Begin box *****/
|
||||
|
@ -1298,12 +1304,10 @@ static void ExaSet_ListOneOrMoreQuestionsForEdition (struct Exa_Exams *Exams,
|
|||
extern const char *Txt_Questions;
|
||||
extern const char *Txt_No_INDEX;
|
||||
extern const char *Txt_Code;
|
||||
extern const char *Txt_Tags;
|
||||
extern const char *Txt_Question;
|
||||
unsigned NumQst;
|
||||
MYSQL_ROW row;
|
||||
struct Tst_Question Question;
|
||||
bool QuestionExists;
|
||||
char *Anchor;
|
||||
|
||||
/***** 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_Code);
|
||||
HTM_TH (1,1,"CT",Txt_Tags);
|
||||
HTM_TH (1,1,"CT",Txt_Question);
|
||||
|
||||
HTM_TR_End ();
|
||||
|
@ -1360,13 +1363,13 @@ static void ExaSet_ListOneOrMoreQuestionsForEdition (struct Exa_Exams *Exams,
|
|||
/* Put icon to edit the question */
|
||||
if (ICanEditQuestions)
|
||||
Ico_PutContextualIconToEdit (ActEdiOneTstQst,NULL,
|
||||
Tst_PutParamQstCod,&Question.QstCod);
|
||||
ExaSet_PutParamQstCod,&Question.QstCod);
|
||||
|
||||
HTM_TD_End ();
|
||||
|
||||
/***** Question *****/
|
||||
QuestionExists = Tst_GetQstDataFromDB (&Question);
|
||||
Tst_ListQuestionForEdition (&Question,NumQst + 1,QuestionExists,Anchor);
|
||||
ExaSet_GetQstDataFromDB (&Question,Exams->ExaCod);
|
||||
ExaSet_ListQuestionForEdition (&Question,NumQst + 1,Anchor);
|
||||
|
||||
/***** End row *****/
|
||||
HTM_TR_End ();
|
||||
|
@ -1382,6 +1385,233 @@ static void ExaSet_ListOneOrMoreQuestionsForEdition (struct Exa_Exams *Exams,
|
|||
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 ***************/
|
||||
/*****************************************************************************/
|
||||
|
@ -1448,13 +1678,7 @@ void ExaSet_AddQstsToSet (void)
|
|||
if (sscanf (LongStr,"%ld",&QstCod) != 1)
|
||||
Lay_ShowErrorAndExit ("Wrong question code.");
|
||||
|
||||
/* Insert question in the table of questions */
|
||||
DB_QueryINSERT ("can not add question to set",
|
||||
"INSERT INTO exa_questions"
|
||||
" (SetCod,QstCod)"
|
||||
" VALUES"
|
||||
" (%ld,%ld)",
|
||||
Set.SetCod,QstCod);
|
||||
ExaSet_CopyQstFromBankToExamSet (&Set,QstCod);
|
||||
}
|
||||
}
|
||||
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 *****************/
|
||||
/*****************************************************************************/
|
||||
|
@ -1581,10 +1892,10 @@ void ExaSet_RemoveSet (void)
|
|||
/***** Remove the set from all the tables *****/
|
||||
/* Remove questions associated to set */
|
||||
DB_QueryDELETE ("can not remove questions associated to set",
|
||||
"DELETE FROM exa_questions"
|
||||
" USING exa_questions,exa_sets"
|
||||
" WHERE exa_questions.SetCod=%ld"
|
||||
" AND exa_questions.SetCod=exa_sets.SetCod"
|
||||
"DELETE FROM exa_set_questions"
|
||||
" USING exa_set_questions,exa_sets"
|
||||
" WHERE exa_set_questions.SetCod=%ld"
|
||||
" AND exa_set_questions.SetCod=exa_sets.SetCod"
|
||||
" AND exa_sets.ExaCod=%ld", // Extra check
|
||||
Set.SetCod,Set.ExaCod);
|
||||
|
||||
|
@ -1771,7 +2082,7 @@ void ExaSet_RequestRemoveQstFromSet (void)
|
|||
Exams.SetCod = Set.SetCod;
|
||||
|
||||
/***** Get question index *****/
|
||||
Exams.QstCod = Tst_GetParamQstCod ();
|
||||
Exams.QstCod = ExaSet_GetParamQstCod ();
|
||||
|
||||
/***** Build anchor string *****/
|
||||
Frm_SetAnchorStr (Set.SetCod,&Anchor);
|
||||
|
@ -1828,14 +2139,15 @@ void ExaSet_RemoveQstFromSet (void)
|
|||
Exams.SetCod = Set.SetCod;
|
||||
|
||||
/***** Get question index *****/
|
||||
QstCod = Tst_GetParamQstCod ();
|
||||
QstCod = ExaSet_GetParamQstCod ();
|
||||
|
||||
/***** Remove the question from set *****/
|
||||
/* Remove the question itself */
|
||||
DB_QueryDELETE ("can not remove a question from a set",
|
||||
"DELETE FROM exa_questions"
|
||||
" WHERE SetCod=%ld AND QstCod=%ld",
|
||||
Set.SetCod,QstCod);
|
||||
"DELETE FROM exa_set_questions"
|
||||
" WHERE QstCod=%ld"
|
||||
" AND SetCod=%ld", // Extra check
|
||||
QstCod,Set.SetCod);
|
||||
if (!mysql_affected_rows (&Gbl.mysql))
|
||||
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
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/************ 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 ***********/
|
||||
/*****************************************************************************/
|
||||
|
|
|
@ -28,10 +28,8 @@
|
|||
/********************************* Headers ***********************************/
|
||||
/*****************************************************************************/
|
||||
|
||||
// #include "swad_date.h"
|
||||
// #include "swad_exam_event.h"
|
||||
#include "swad_exam_type.h"
|
||||
// #include "swad_scope.h"
|
||||
#include "swad_test_type.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
/************************** Public types and constants ***********************/
|
||||
|
@ -63,6 +61,11 @@ void ExaSet_ListExamSets (struct Exa_Exams *Exams,
|
|||
|
||||
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_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;
|
||||
|
||||
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],
|
||||
Length);
|
||||
}
|
||||
|
@ -324,7 +324,7 @@ void Med_GetMediaDataByCod (struct Media *Media)
|
|||
Length = Med_MAX_BYTES_TITLE;
|
||||
|
||||
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],
|
||||
Length);
|
||||
}
|
||||
|
@ -1405,13 +1405,6 @@ static bool Med_MoveTmpFileToDefDir (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 *****/
|
||||
Media->MedCod = DB_QueryINSERTandReturnCode ("can not create media",
|
||||
"INSERT INTO media"
|
||||
|
@ -1436,9 +1429,8 @@ void Med_ShowMedia (const struct Media *Media,
|
|||
char PathMedPriv[PATH_MAX + 1];
|
||||
|
||||
/***** If no media to show ==> nothing to do *****/
|
||||
if (Media->Status != Med_STORED_IN_DB)
|
||||
return;
|
||||
if (Media->Type == Med_TYPE_NONE)
|
||||
if (Media->Status != Med_STORED_IN_DB ||
|
||||
Media->Type == Med_TYPE_NONE)
|
||||
return;
|
||||
|
||||
/***** Start media container *****/
|
||||
|
@ -1808,6 +1800,140 @@ static void Med_AlertThirdPartyCookies (void)
|
|||
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 ***************/
|
||||
/*****************************************************************************/
|
||||
|
|
|
@ -155,6 +155,8 @@ void Med_StoreMediaInDB (struct Media *Media);
|
|||
void Med_ShowMedia (const struct Media *Media,
|
||||
const char *ClassContainer,const char *ClassMedia);
|
||||
|
||||
long Med_CloneMedia (const struct Media *MediaSrc);
|
||||
|
||||
void Med_RemoveMediaFromAllRows (unsigned NumMedia,MYSQL_RES *mysql_res);
|
||||
void Med_RemoveMedia (long MedCod);
|
||||
|
||||
|
|
|
@ -3197,13 +3197,13 @@ static void Msg_ShowASentOrReceivedMessage (struct Msg_Messages *Messages,
|
|||
HTM_TxtColonNBSP (Txt_MSG_Content);
|
||||
HTM_TD_End ();
|
||||
|
||||
/***** Initialize image *****/
|
||||
/***** Initialize media *****/
|
||||
Med_MediaConstructor (&Media);
|
||||
|
||||
/***** Get message content and optional image *****/
|
||||
/***** Get message content and optional media *****/
|
||||
Msg_GetMsgContent (MsgCod,Content,&Media);
|
||||
|
||||
/***** Show content and image *****/
|
||||
/***** Show content and media *****/
|
||||
HTM_TD_Begin ("colspan=\"2\" class=\"MSG_TXT LT\"");
|
||||
if (Content[0])
|
||||
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 ();
|
||||
|
||||
/***** Free image *****/
|
||||
/***** Free 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",
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/**************************** Private constants ******************************/
|
||||
/*****************************************************************************/
|
||||
|
||||
#define Tst_MAX_BYTES_TAGS_LIST (16 * 1024)
|
||||
|
||||
static const char *Tst_StrAnswerTypesDB[Tst_NUM_ANS_TYPES] =
|
||||
const char *Tst_StrAnswerTypesDB[Tst_NUM_ANS_TYPES] =
|
||||
{
|
||||
[Tst_ANS_INT ] = "int",
|
||||
[Tst_ANS_FLOAT ] = "float",
|
||||
|
@ -90,6 +84,12 @@ static const char *Tst_StrAnswerTypesDB[Tst_NUM_ANS_TYPES] =
|
|||
[Tst_ANS_TEXT ] = "text",
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/**************************** Private constants ******************************/
|
||||
/*****************************************************************************/
|
||||
|
||||
#define Tst_MAX_BYTES_TAGS_LIST (16 * 1024)
|
||||
|
||||
// Test images will be saved with:
|
||||
// - maximum width 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,
|
||||
|
@ -2662,7 +2662,7 @@ static void Tst_GetQuestionsForNewTestFromDB (struct Tst_Test *Test,
|
|||
case Tst_ANS_MULTIPLE_CHOICE:
|
||||
/* If answer type is unique or multiple option,
|
||||
generate indexes of answers depending on shuffle */
|
||||
Tst_GenerateChoiceIndexesDependingOnShuffle (&Print->PrintedQuestions[NumQst],Shuffle);
|
||||
Tst_GenerateChoiceIndexes (&Print->PrintedQuestions[NumQst],Shuffle);
|
||||
break;
|
||||
default:
|
||||
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,
|
||||
bool Shuffle)
|
||||
void Tst_GenerateChoiceIndexes (struct TstPrn_PrintedQuestion *PrintedQuestion,
|
||||
bool Shuffle)
|
||||
{
|
||||
// extern const char *Par_SEPARATOR_PARAM_MULTIPLE;
|
||||
struct Tst_Question Question;
|
||||
unsigned NumOpt;
|
||||
MYSQL_RES *mysql_res;
|
||||
|
@ -2733,7 +2732,6 @@ void Tst_GenerateChoiceIndexesDependingOnShuffle (struct TstPrn_PrintedQuestion
|
|||
if (NumOpt == 0)
|
||||
snprintf (StrInd,sizeof (StrInd),"%u",Index);
|
||||
else
|
||||
// snprintf (StrInd,sizeof (StrInd),"%s%u",Par_SEPARATOR_PARAM_MULTIPLE,Index);
|
||||
snprintf (StrInd,sizeof (StrInd),",%u",Index);
|
||||
Str_Concat (PrintedQuestion->StrIndexes,StrInd,
|
||||
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 *****************/
|
||||
/*****************************************************************************/
|
||||
|
||||
Tst_AnswerType_t Tst_GetQstAnswerType (long QstCod)
|
||||
Tst_AnswerType_t Tst_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 a question",
|
||||
if (!DB_QuerySELECT (&mysql_res,"can not get the type of a question",
|
||||
"SELECT AnsType" // row[0]
|
||||
" FROM tst_questions"
|
||||
" WHERE QstCod=%ld"
|
||||
" AND CrsCod=%ld", // Extra check
|
||||
QstCod,Gbl.Hierarchy.Crs.CrsCod))
|
||||
" WHERE QstCod=%ld",
|
||||
QstCod))
|
||||
Lay_ShowErrorAndExit ("Question does not exist.");
|
||||
|
||||
/* Get type of answer */
|
||||
|
|
|
@ -122,8 +122,8 @@ void Tst_ListQuestionsToEdit (void);
|
|||
void Tst_ListQuestionsToSelectForSet (struct Exa_Exams *Exams);
|
||||
void Tst_ListQuestionsToSelectForGame (struct Gam_Games *Games);
|
||||
|
||||
void Tst_GenerateChoiceIndexesDependingOnShuffle (struct TstPrn_PrintedQuestion *PrintedQuestion,
|
||||
bool Shuffle);
|
||||
void Tst_GenerateChoiceIndexes (struct TstPrn_PrintedQuestion *PrintedQuestion,
|
||||
bool Shuffle);
|
||||
|
||||
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);
|
||||
|
||||
Tst_AnswerType_t Tst_GetQstAnswerType (long QstCod);
|
||||
Tst_AnswerType_t Tst_GetQstAnswerTypeFromDB (long QstCod);
|
||||
bool Tst_GetQstDataFromDB (struct Tst_Question *Question);
|
||||
Tst_AnswerType_t Tst_ConvertFromStrAnsTypDBToAnsTyp (const char *StrAnsTypeBD);
|
||||
void Tst_ReceiveQst (void);
|
||||
|
|
|
@ -374,7 +374,7 @@ void TstPrn_ComputeScoresAndStoreQuestionsOfPrint (struct TstPrn_Print *Print,
|
|||
/* Compute question score */
|
||||
Tst_QstConstructor (&Question);
|
||||
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);
|
||||
Tst_QstDestructor (&Question);
|
||||
|
||||
|
|
|
@ -26,6 +26,9 @@
|
|||
/*****************************************************************************/
|
||||
/********************************* Headers ***********************************/
|
||||
/*****************************************************************************/
|
||||
|
||||
#include "swad_media.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
/***************************** Public constants ******************************/
|
||||
/*****************************************************************************/
|
||||
|
|
Loading…
Reference in New Issue
Block a user