Version19.219

This commit is contained in:
acanas 2020-05-09 01:37:00 +02:00
parent e10db11881
commit 3bc30a31fa
17 changed files with 424 additions and 226 deletions

View File

@ -524,6 +524,32 @@ CREATE TABLE IF NOT EXISTS exa_participants (
TS TIMESTAMP, TS TIMESTAMP,
UNIQUE INDEX(EvtCod,UsrCod)); UNIQUE INDEX(EvtCod,UsrCod));
-- --
-- Table exa_print_questions: stores the questions and answers in exam prints made by users
--
CREATE TABLE IF NOT EXISTS exa_print_questions (
PrnCod INT NOT NULL,
QstCod INT NOT NULL,
QstInd INT NOT NULL,
Score DOUBLE PRECISION NOT NULL DEFAULT 0,
Indexes TEXT NOT NULL,
Answers TEXT NOT NULL,
UNIQUE INDEX(PrnCod,QstCod));
--
-- Table exa_prints: stores the exam prints of every exam event
--
CREATE TABLE IF NOT EXISTS exa_prints (
PrnCod INT NOT NULL AUTO_INCREMENT,
EvtCod INT NOT NULL,
UsrCod INT NOT NULL,
StartTime DATETIME NOT NULL,
EndTime DATETIME NOT NULL,
NumQsts INT NOT NULL DEFAULT 0,
NumQstsNotBlank INT NOT NULL DEFAULT 0,
Sent ENUM('N','Y') NOT NULL DEFAULT 'N',
Score DOUBLE PRECISION NOT NULL DEFAULT 0,
UNIQUE INDEX(PrnCod),
UNIQUE INDEX(EvtCod,UsrCod));
--
-- Table exa_questions: stores the questions in the set of questions for exams -- Table exa_questions: stores the questions in the set of questions for exams
-- --
CREATE TABLE IF NOT EXISTS exa_questions ( CREATE TABLE IF NOT EXISTS exa_questions (
@ -1507,7 +1533,7 @@ CREATE TABLE IF NOT EXISTS tst_config (
Visibility INT NOT NULL DEFAULT 0x1f, Visibility INT NOT NULL DEFAULT 0x1f,
UNIQUE INDEX(CrsCod)); UNIQUE INDEX(CrsCod));
-- --
-- Table tst_exam_questions: stores the questions and answers in test exams made by users -- Table tst_exam_questions: stores the questions and answers in test prints made by users
-- --
CREATE TABLE IF NOT EXISTS tst_exam_questions ( CREATE TABLE IF NOT EXISTS tst_exam_questions (
ExaCod INT NOT NULL, ExaCod INT NOT NULL,

View File

@ -4635,7 +4635,7 @@ int swad__getTrivialQuestion (struct soap *soap,
" AND tst_tags.TagHidden='Y'" " AND tst_tags.TagHidden='Y'"
" AND tst_tags.TagCod=tst_question_tags.TagCod)" " AND tst_tags.TagCod=tst_question_tags.TagCod)"
" HAVING S>='%f' AND S<='%f'" " HAVING S>='%f' AND S<='%f'"
" ORDER BY RAND(NOW()) LIMIT 1", " ORDER BY RAND() LIMIT 1",
DegreesStr,DegreesStr, DegreesStr,DegreesStr,
lowerScore,upperScore); lowerScore,upperScore);
Str_SetDecimalPointToLocal (); // Return to local system Str_SetDecimalPointToLocal (); // Return to local system

View File

@ -548,10 +548,19 @@ 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.218.1 (2020-05-07)" #define Log_PLATFORM_VERSION "SWAD 19.219 (2020-05-07)"
#define CSS_FILE "swad19.217.css" #define CSS_FILE "swad19.217.css"
#define JS_FILE "swad19.193.1.js" #define JS_FILE "swad19.193.1.js"
/* /*
Arreglar bug: cuando se crea un nuevo proyecto no debería salir de nuevo el formulario de creación sino el formulario que incluye la adición de usuarios al proyecto recién creado.
cuando se modifica se hace correctamente, así que se trata de hacer lo mismo cuando se crea que cuando se modifica.
Arreglar bug en rol de usuario. Reported by Francisco Ocaña Lara.
Version 19.219: May 09, 2020 Create exam print. (302347 lines)
2 change necessary in database:
CREATE TABLE IF NOT EXISTS exa_prints (PrnCod INT NOT NULL AUTO_INCREMENT,EvtCod INT NOT NULL,UsrCod INT NOT NULL,StartTime DATETIME NOT NULL,EndTime DATETIME NOT NULL,NumQsts INT NOT NULL DEFAULT 0,NumQstsNotBlank INT NOT NULL DEFAULT 0,Sent ENUM('N','Y') NOT NULL DEFAULT 'N',Score DOUBLE PRECISION NOT NULL DEFAULT 0,UNIQUE INDEX(PrnCod),UNIQUE INDEX(EvtCod,UsrCod));
CREATE TABLE IF NOT EXISTS exa_print_questions (PrnCod INT NOT NULL,QstCod INT NOT NULL,QstInd INT NOT NULL,Score DOUBLE PRECISION NOT NULL DEFAULT 0,Indexes TEXT NOT NULL,Answers TEXT NOT NULL,UNIQUE INDEX(PrnCod,QstCod));
Version 19.218.1: May 07, 2020 Fixed minor bug in test results and match results. (302171 lines) Version 19.218.1: May 07, 2020 Fixed minor bug in test results and match results. (302171 lines)
Version 19.218: May 07, 2020 Fixed bug in creation of new exam announcements, reported by Francisco Gómez Mula. Version 19.218: May 07, 2020 Fixed bug in creation of new exam announcements, reported by Francisco Gómez Mula.
Changes in exam announcementes. (302170 lines) Changes in exam announcementes. (302170 lines)
@ -578,7 +587,7 @@ ALTER TABLE room_MAC ENGINE=MyISAM;
Version 19.211: May 05, 2020 Exam events can be hidden/unhidden. (301215 lines) Version 19.211: May 05, 2020 Exam events can be hidden/unhidden. (301215 lines)
1 change necessary in database: 1 change necessary in database:
ALTER TABLE exa_events ADD COLUMN Hidden ENUM('N','Y') NOT NULL DEFAULT 'N' AFTER ExaCod; ALTER TABLE exa_events ADD COLUMN Hidden ENUM('N','Y') NOT NULL DEFAULT 'N' AFTER ExaCod;
----
Version 19.210.4: May 05, 2020 Fixed bug searching courses. (301103 lines) Version 19.210.4: May 05, 2020 Fixed bug searching courses. (301103 lines)
Version 19.210.3: May 03, 2020 All figures cacheable are cached everytime they are calculated. (301089 lines) Version 19.210.3: May 03, 2020 All figures cacheable are cached everytime they are calculated. (301089 lines)
Version 19.210.2: May 03, 2020 More figures cached. (301125 lines) Version 19.210.2: May 03, 2020 More figures cached. (301125 lines)

View File

@ -262,7 +262,7 @@
#define Cfg_MIN_NUM_USERS_TO_CONFIRM_SHOW_BIG_LIST 500 // If the number of users in a list is greater than this, ask me for confirmation before showing the list #define Cfg_MIN_NUM_USERS_TO_CONFIRM_SHOW_BIG_LIST 500 // If the number of users in a list is greater than this, ask me for confirmation before showing the list
#define Cfg_MIN_PHOTOS_TO_SHOW_AVERAGE 10 // If the number of students with photo in a degree is less than this, don't show average photo of the degree #define Cfg_MIN_PHOTOS_TO_SHOW_AVERAGE 10 // If the number of students with photo in a degree is less than this, don't show average photo of the degree
#define Cfg_MAX_RECIPIENTS 250 // A student can not send a message to more than this number of recipients #define Cfg_MAX_RECIPIENTS 250 // A student can not send a message to more than this number of recipients
#define Cfg_MAX_CONNECTED_SHOWN 15 // Show (in right column) only these connected users with more recent activity #define Cfg_MAX_CONNECTED_SHOWN 10 // Show (in right column) only these connected users with more recent activity
/* Courses */ /* Courses */
#define Cfg_MIN_NUM_COURSES_TO_CONFIRM_SHOW_BIG_LIST 500 // If the number of courses in a list is greater than this, ask me for confirmation before showing the list #define Cfg_MIN_NUM_COURSES_TO_CONFIRM_SHOW_BIG_LIST 500 // If the number of courses in a list is greater than this, ask me for confirmation before showing the list

View File

@ -908,7 +908,7 @@ static void Con_ShowConnectedUsrsCurrentLocationOneByOneOnMainZone (Rol_Role_t R
{ {
case Rol_GST: case Rol_GST:
NumUsrs = (unsigned) DB_QuerySELECT (&mysql_res,"can not get list of connected users" NumUsrs = (unsigned) DB_QuerySELECT (&mysql_res,"can not get list of connected users"
" who belong to this location", " who belong to this location",
"SELECT UsrCod,LastCrsCod," "SELECT UsrCod,LastCrsCod,"
"UNIX_TIMESTAMP()-UNIX_TIMESTAMP(LastTime) AS Dif" "UNIX_TIMESTAMP()-UNIX_TIMESTAMP(LastTime) AS Dif"
" FROM connected" " FROM connected"

View File

@ -1159,6 +1159,60 @@ mysql> DESCRIBE exa_participants;
"TS TIMESTAMP," "TS TIMESTAMP,"
"UNIQUE INDEX(EvtCod,UsrCod))"); "UNIQUE INDEX(EvtCod,UsrCod))");
/***** Table exa_print_questions *****/
/*
mysql> DESCRIBE exa_print_questions; +---------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+---------+------+-----+---------+-------+
| PrnCod | int(11) | NO | PRI | NULL | |
| QstCod | int(11) | NO | PRI | NULL | |
| QstInd | int(11) | NO | | NULL | |
| Score | double | NO | | 0 | |
| Indexes | text | NO | | NULL | |
| Answers | text | NO | | NULL | |
+---------+---------+------+-----+---------+-------+
6 rows in set (0.00 sec)
*/
DB_CreateTable ("CREATE TABLE IF NOT EXISTS exa_print_questions ("
"PrnCod INT NOT NULL,"
"QstCod INT NOT NULL,"
"QstInd INT NOT NULL,"
"Score DOUBLE PRECISION NOT NULL DEFAULT 0,"
"Indexes TEXT NOT NULL," // Tst_MAX_BYTES_INDEXES_ONE_QST
"Answers TEXT NOT NULL," // Tst_MAX_BYTES_ANSWERS_ONE_QST
"UNIQUE INDEX(PrnCod,QstCod))");
/***** Table exa_prints *****/
/*
mysql> DESCRIBE exa_prints;
+-----------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+---------------+------+-----+---------+----------------+
| PrnCod | int(11) | NO | PRI | NULL | auto_increment |
| EvtCod | int(11) | NO | MUL | NULL | |
| UsrCod | int(11) | NO | | NULL | |
| StartTime | datetime | NO | | NULL | |
| EndTime | datetime | NO | | NULL | |
| NumQsts | int(11) | NO | | 0 | |
| NumQstsNotBlank | int(11) | NO | | 0 | |
| Sent | enum('N','Y') | NO | | N | |
| Score | double | NO | | 0 | |
+-----------------+---------------+------+-----+---------+----------------+
9 rows in set (0.00 sec)
*/
DB_CreateTable ("CREATE TABLE IF NOT EXISTS exa_prints ("
"PrnCod INT NOT NULL AUTO_INCREMENT,"
"EvtCod INT NOT NULL,"
"UsrCod INT NOT NULL,"
"StartTime DATETIME NOT NULL,"
"EndTime DATETIME NOT NULL,"
"NumQsts INT NOT NULL DEFAULT 0,"
"NumQstsNotBlank INT NOT NULL DEFAULT 0,"
"Sent ENUM('N','Y') NOT NULL DEFAULT 'N',"
"Score DOUBLE PRECISION NOT NULL DEFAULT 0,"
"UNIQUE INDEX(PrnCod),"
"UNIQUE INDEX(EvtCod,UsrCod))");
/***** Table exa_questions *****/ /***** Table exa_questions *****/
/* /*
mysql> DESCRIBE exa_questions; mysql> DESCRIBE exa_questions;
@ -1768,7 +1822,7 @@ mysql> DESCRIBE mch_indexes;
DB_CreateTable ("CREATE TABLE IF NOT EXISTS mch_indexes (" DB_CreateTable ("CREATE TABLE IF NOT EXISTS mch_indexes ("
"MchCod INT NOT NULL," "MchCod INT NOT NULL,"
"QstInd INT NOT NULL," "QstInd INT NOT NULL,"
"Indexes TEXT NOT NULL," // TstPrn_MAX_BYTES_INDEXES_ONE_QST "Indexes TEXT NOT NULL," // Tst_MAX_BYTES_INDEXES_ONE_QST
"UNIQUE INDEX(MchCod,QstInd))"); "UNIQUE INDEX(MchCod,QstInd))");
/***** Table mch_results *****/ /***** Table mch_results *****/
@ -3184,16 +3238,16 @@ mysql> DESCRIBE tst_exam_questions;
+---------+---------+------+-----+---------+-------+ +---------+---------+------+-----+---------+-------+
6 rows in set (0.00 sec) 6 rows in set (0.00 sec)
*/ */
DB_CreateTable ("CREATE TABLE IF NOT EXISTS tst_exam_questions (" DB_CreateTable ("CREATE TABLE IF NOT EXISTS tst_exam_questions (" // TODO: rename as tst_print_questions
"ExaCod INT NOT NULL," "ExaCod INT NOT NULL," // TODO: rename as PrnCod
"QstCod INT NOT NULL," "QstCod INT NOT NULL,"
"QstInd INT NOT NULL," "QstInd INT NOT NULL,"
"Score DOUBLE PRECISION NOT NULL DEFAULT 0," "Score DOUBLE PRECISION NOT NULL DEFAULT 0,"
"Indexes TEXT NOT NULL," // TstPrn_MAX_BYTES_INDEXES_ONE_QST "Indexes TEXT NOT NULL," // Tst_MAX_BYTES_INDEXES_ONE_QST
"Answers TEXT NOT NULL," // TstPrn_MAX_BYTES_ANSWERS_ONE_QST "Answers TEXT NOT NULL," // Tst_MAX_BYTES_ANSWERS_ONE_QST
"UNIQUE INDEX(ExaCod,QstCod))"); "UNIQUE INDEX(ExaCod,QstCod))");
/***** Table tst_exams *****/ /***** Table tst_exams *****/ // TODO: rename as tst_prints
/* /*
mysql> DESCRIBE tst_exams; mysql> DESCRIBE tst_exams;
+-----------------+---------------+------+-----+---------+----------------+ +-----------------+---------------+------+-----+---------+----------------+
@ -3212,8 +3266,8 @@ mysql> DESCRIBE tst_exams;
+-----------------+---------------+------+-----+---------+----------------+ +-----------------+---------------+------+-----+---------+----------------+
10 rows in set (0.00 sec) 10 rows in set (0.00 sec)
*/ */
DB_CreateTable ("CREATE TABLE IF NOT EXISTS tst_exams (" DB_CreateTable ("CREATE TABLE IF NOT EXISTS tst_exams (" // TODO: rename as tst_prints
"ExaCod INT NOT NULL AUTO_INCREMENT," "ExaCod INT NOT NULL AUTO_INCREMENT," // TODO: rename as PrnCod
"CrsCod INT NOT NULL," "CrsCod INT NOT NULL,"
"UsrCod INT NOT NULL," "UsrCod INT NOT NULL,"
"StartTime DATETIME NOT NULL," "StartTime DATETIME NOT NULL,"
@ -3223,7 +3277,7 @@ mysql> DESCRIBE tst_exams;
"Sent ENUM('N','Y') NOT NULL DEFAULT 'N'," "Sent ENUM('N','Y') NOT NULL DEFAULT 'N',"
"AllowTeachers ENUM('N','Y') NOT NULL DEFAULT 'N'," "AllowTeachers ENUM('N','Y') NOT NULL DEFAULT 'N',"
"Score DOUBLE PRECISION NOT NULL DEFAULT 0," "Score DOUBLE PRECISION NOT NULL DEFAULT 0,"
"UNIQUE INDEX(ExaCod)," "UNIQUE INDEX(ExaCod)," // TODO: rename as PrnCod
"INDEX(CrsCod,UsrCod))"); "INDEX(CrsCod,UsrCod))");
/***** Table tst_question_tags *****/ /***** Table tst_question_tags *****/

View File

@ -1934,7 +1934,7 @@ static void ExaEvt_ReorderAnswer (long EvtCod,unsigned QstInd,
long LongNum; long LongNum;
unsigned AnsInd; unsigned AnsInd;
char StrOneAnswer[Cns_MAX_DECIMAL_DIGITS_UINT + 1]; char StrOneAnswer[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
char StrAnswersOneQst[TstPrn_MAX_BYTES_ANSWERS_ONE_QST + 1]; char StrAnswersOneQst[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
***** Initialize list of answers to empty string ***** ***** Initialize list of answers to empty string *****
StrAnswersOneQst[0] = '\0'; StrAnswersOneQst[0] = '\0';
@ -1967,9 +1967,9 @@ static void ExaEvt_ReorderAnswer (long EvtCod,unsigned QstInd,
* Concatenate answer index to list of answers * * Concatenate answer index to list of answers *
if (NumAns) if (NumAns)
Str_Concat (StrAnswersOneQst,",", Str_Concat (StrAnswersOneQst,",",
TstPrn_MAX_BYTES_ANSWERS_ONE_QST); Tst_MAX_BYTES_ANSWERS_ONE_QST);
Str_Concat (StrAnswersOneQst,StrOneAnswer, Str_Concat (StrAnswersOneQst,StrOneAnswer,
TstPrn_MAX_BYTES_ANSWERS_ONE_QST); Tst_MAX_BYTES_ANSWERS_ONE_QST);
} }
***** Free structure that stores the query result ***** ***** Free structure that stores the query result *****
@ -1993,7 +1993,7 @@ void ExaEvt_GetIndexes (long EvtCod,unsigned QstInd,
{ {
MYSQL_RES *mysql_res; MYSQL_RES *mysql_res;
MYSQL_ROW row; MYSQL_ROW row;
char StrIndexesOneQst[TstPrn_MAX_BYTES_INDEXES_ONE_QST + 1]; char StrIndexesOneQst[Tst_MAX_BYTES_INDEXES_ONE_QST + 1];
/***** Get indexes for a question from database *****/ /***** Get indexes for a question from database *****/
if (!DB_QuerySELECT (&mysql_res,"can not get data of a question", if (!DB_QuerySELECT (&mysql_res,"can not get data of a question",
@ -2006,7 +2006,7 @@ void ExaEvt_GetIndexes (long EvtCod,unsigned QstInd,
/* Get indexes (row[0]) */ /* Get indexes (row[0]) */
Str_Copy (StrIndexesOneQst,row[0], Str_Copy (StrIndexesOneQst,row[0],
TstPrn_MAX_BYTES_INDEXES_ONE_QST); Tst_MAX_BYTES_INDEXES_ONE_QST);
/***** Free structure that stores the query result *****/ /***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res); DB_FreeMySQLResult (&mysql_res);
@ -4348,7 +4348,7 @@ static void ExaEvt_ComputeScore (struct TstPrn_Print *Print)
Question.Answer.Type = Tst_ANS_UNIQUE_CHOICE; Question.Answer.Type = Tst_ANS_UNIQUE_CHOICE;
/***** Compute score for this answer ******/ /***** Compute score for this answer ******/
TstPrn_ComputeChoiceAnsScore (Print,NumQst,&Question); TstPrn_ComputeChoiceAnsScore (&Print->PrintedQuestions[NumQst],&Question);
/***** Update total score *****/ /***** Update total score *****/
Print->Score += Print->PrintedQuestions[NumQst].Score; Print->Score += Print->PrintedQuestions[NumQst].Score;

View File

@ -86,8 +86,15 @@ static void ExaPrn_ResetPrintExceptPrnCod (struct ExaPrn_Print *Print);
static void ExaPrn_GetQuestionsForNewPrintFromDB (struct Exa_Exam *Exam, static void ExaPrn_GetQuestionsForNewPrintFromDB (struct Exa_Exam *Exam,
struct ExaPrn_Print *Print); struct ExaPrn_Print *Print);
static void ExaPrn_ShowQuestionsFromSet (struct ExaPrn_Print *Print, static unsigned ExaPrn_GetSomeQstsFromSetToPrint (struct ExaPrn_Print *Print,
struct ExaSet_Set *Set); struct ExaSet_Set *Set,
unsigned *NumQstInPrint);
static void ExaPrn_CreatePrintInDB (const struct ExaEvt_Event *Event,
struct ExaPrn_Print *Print);
static void ExaPrn_ComputeScoresAndStoreQuestionsOfPrint (struct ExaPrn_Print *Print,
bool UpdateQstScore);
static void ExaPrn_StoreOneQstOfPrintInDB (const struct ExaPrn_Print *Print,
unsigned NumQst);
/*****************************************************************************/ /*****************************************************************************/
/**************************** Reset exam print *******************************/ /**************************** Reset exam print *******************************/
@ -145,6 +152,22 @@ void ExaPrn_ShowNewExamPrint (void)
/***** Get questions from database *****/ /***** Get questions from database *****/
ExaPrn_GetQuestionsForNewPrintFromDB (&Exam,&Print); ExaPrn_GetQuestionsForNewPrintFromDB (&Exam,&Print);
if (Print.NumQsts)
{
/***** Create new exam print in database *****/
ExaPrn_CreatePrintInDB (&Event,&Print);
ExaPrn_ComputeScoresAndStoreQuestionsOfPrint (&Print,
false); // Don't update question score
/***** Show test exam to be answered *****/
// Tst_ShowTestExamToFillIt (&Print,NumExamsGeneratedByMe,Tst_REQUEST);
}
// else // No questions found
// {
// Ale_ShowAlert (Ale_INFO,Txt_No_questions_found_matching_your_search_criteria);
// Tst_ShowFormRequestTest (&Test); // Show the form again
// }
/***** End table *****/ /***** End table *****/
HTM_TABLE_End (); HTM_TABLE_End ();
} }
@ -163,6 +186,8 @@ static void ExaPrn_GetQuestionsForNewPrintFromDB (struct Exa_Exam *Exam,
unsigned NumSets; unsigned NumSets;
unsigned NumSet; unsigned NumSet;
struct ExaSet_Set Set; struct ExaSet_Set Set;
unsigned NumQstsFromSet;
unsigned NumQstInPrint = 0;
/***** Get data of set of questions from database *****/ /***** Get data of set of questions from database *****/
NumSets = (unsigned) NumSets = (unsigned)
@ -175,9 +200,10 @@ static void ExaPrn_GetQuestionsForNewPrintFromDB (struct Exa_Exam *Exam,
" ORDER BY SetInd", " ORDER BY SetInd",
Exam->ExaCod); Exam->ExaCod);
/***** Show table with sets *****/ /***** Get questions from all sets *****/
Print->NumQsts = 0;
if (NumSets) if (NumSets)
/***** Write rows *****/ /***** For each set in exam... *****/
for (NumSet = 0; for (NumSet = 0;
NumSet < NumSets; NumSet < NumSets;
NumSet++) NumSet++)
@ -227,9 +253,14 @@ static void ExaPrn_GetQuestionsForNewPrintFromDB (struct Exa_Exam *Exam,
HTM_TR_End (); HTM_TR_End ();
/***** Questions in this set *****/ /***** Questions in this set *****/
ExaPrn_ShowQuestionsFromSet (Print,&Set); NumQstsFromSet = ExaPrn_GetSomeQstsFromSetToPrint (Print,&Set,&NumQstInPrint);
Print->NumQsts += NumQstsFromSet;
} }
/***** Check *****/
if (Print->NumQsts != NumQstInPrint)
Lay_ShowErrorAndExit ("Wrong number of questions.");
/***** Free structure that stores the query result *****/ /***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res); DB_FreeMySQLResult (&mysql_res);
} }
@ -238,35 +269,35 @@ static void ExaPrn_GetQuestionsForNewPrintFromDB (struct Exa_Exam *Exam,
/************************ Show questions from a set **************************/ /************************ Show questions from a set **************************/
/*****************************************************************************/ /*****************************************************************************/
static void ExaPrn_ShowQuestionsFromSet (struct ExaPrn_Print *Print, static unsigned ExaPrn_GetSomeQstsFromSetToPrint (struct ExaPrn_Print *Print,
struct ExaSet_Set *Set) struct ExaSet_Set *Set,
unsigned *NumQstInPrint)
{ {
MYSQL_RES *mysql_res; MYSQL_RES *mysql_res;
MYSQL_ROW row; MYSQL_ROW row;
unsigned NumQsts; unsigned NumQstsInSet;
unsigned NumQst; unsigned NumQstInSet;
long QstCod;
Tst_AnswerType_t AnswerType; Tst_AnswerType_t AnswerType;
bool Shuffle; bool Shuffle;
/***** Get questions from database *****/ /***** Get questions from database *****/
NumQsts = (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 tst_questions.QstCod," // row[0]
"tst_questions.AnsType," // row[1] "tst_questions.AnsType," // row[1]
"tst_questions.Shuffle" // row[2] "tst_questions.Shuffle" // row[2]
" FROM exa_questions,tst_questions" " FROM exa_questions,tst_questions"
" WHERE exa_questions.setCod=%ld" " WHERE exa_questions.setCod=%ld"
" AND exa_questions.QstCod=tst_questions.QstCod" " AND exa_questions.QstCod=tst_questions.QstCod"
" ORDER BY RAND(NOW())" " 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,
Set->NumQstsToPrint); Set->NumQstsToPrint);
/***** Questions in this set *****/ /***** Questions in this set *****/
for (NumQst = 0; for (NumQstInSet = 0;
NumQst < NumQsts; NumQstInSet < NumQstsInSet;
NumQst++) NumQstInSet++, (*NumQstInPrint)++)
{ {
Gbl.RowEvenOdd = 1 - Gbl.RowEvenOdd; Gbl.RowEvenOdd = 1 - Gbl.RowEvenOdd;
@ -279,7 +310,7 @@ static void ExaPrn_ShowQuestionsFromSet (struct ExaPrn_Print *Print,
*/ */
/* Get question code (row[0]) */ /* Get question code (row[0]) */
QstCod = Str_ConvertStrCodToLongCod (row[0]); Print->PrintedQuestions[*NumQstInPrint].QstCod = Str_ConvertStrCodToLongCod (row[0]);
/* Get answer type (row[1]) */ /* Get answer type (row[1]) */
AnswerType = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[1]); AnswerType = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[1]);
@ -294,13 +325,13 @@ static void ExaPrn_ShowQuestionsFromSet (struct ExaPrn_Print *Print,
case Tst_ANS_FLOAT: case Tst_ANS_FLOAT:
case Tst_ANS_TRUE_FALSE: case Tst_ANS_TRUE_FALSE:
case Tst_ANS_TEXT: case Tst_ANS_TEXT:
Print->PrintedQuestions[NumQst].StrIndexes[0] = '\0'; Print->PrintedQuestions[*NumQstInPrint].StrIndexes[0] = '\0';
break; break;
case Tst_ANS_UNIQUE_CHOICE: case Tst_ANS_UNIQUE_CHOICE:
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_GenerateChoiceIndexesDependingOnShuffle (&Print->PrintedQuestions[*NumQstInPrint],Shuffle);
break; break;
default: default:
break; break;
@ -310,14 +341,14 @@ static void ExaPrn_ShowQuestionsFromSet (struct ExaPrn_Print *Print,
Initially user has not answered the question ==> initially all the answers will be blank. Initially user has not answered the question ==> initially all the answers will be blank.
If the user does not confirm the submission of their exam ==> If the user does not confirm the submission of their exam ==>
==> the exam may be half filled ==> the answers displayed will be those selected by the user. */ ==> the exam may be half filled ==> the answers displayed will be those selected by the user. */
Print->PrintedQuestions[NumQst].StrAnswers[0] = '\0'; Print->PrintedQuestions[*NumQstInPrint].StrAnswers[0] = '\0';
/* Begin row for this question */ /* Begin row for this question */
HTM_TR_Begin (NULL); HTM_TR_Begin (NULL);
/* Title */ /* Title */
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd); HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
HTM_TxtF ("Pregunta %ld",QstCod); HTM_TxtF ("Pregunta %ld",Print->PrintedQuestions[*NumQstInPrint].QstCod);
HTM_TD_End (); HTM_TD_End ();
/* Number of questions to appear in exam print */ /* Number of questions to appear in exam print */
@ -328,4 +359,97 @@ static void ExaPrn_ShowQuestionsFromSet (struct ExaPrn_Print *Print,
/* End title for this question */ /* End title for this question */
HTM_TR_End (); HTM_TR_End ();
} }
return NumQstsInSet;
}
/*****************************************************************************/
/***************** Create new blank exam print in database *******************/
/*****************************************************************************/
static void ExaPrn_CreatePrintInDB (const struct ExaEvt_Event *Event,
struct ExaPrn_Print *Print)
{
/***** Insert new exam print into table *****/
Print->PrnCod =
DB_QueryINSERTandReturnCode ("can not create new exam print",
"INSERT INTO exa_prints"
" (EvtCod,UsrCod,StartTime,EndTime,NumQsts,NumQstsNotBlank,Sent,Score)"
" VALUES"
" (%ld,%ld,NOW(),NOW(),%u,0,'N',0)",
Event->EvtCod,
Gbl.Usrs.Me.UsrDat.UsrCod,
Print->NumQsts);
}
/*****************************************************************************/
/*********** Compute score of each question and store in database ************/
/*****************************************************************************/
static void ExaPrn_ComputeScoresAndStoreQuestionsOfPrint (struct ExaPrn_Print *Print,
bool UpdateQstScore)
{
unsigned NumQst;
struct Tst_Question Question;
/***** Initialize total score *****/
Print->Score = 0.0;
Print->NumQstsNotBlank = 0;
/***** Compute and store scores of all questions *****/
for (NumQst = 0;
NumQst < Print->NumQsts;
NumQst++)
{
/* Compute question score */
Tst_QstConstructor (&Question);
Question.QstCod = Print->PrintedQuestions[NumQst].QstCod;
Question.Answer.Type = Tst_GetQstAnswerType (Question.QstCod);
TstPrn_ComputeAnswerScore (&Print->PrintedQuestions[NumQst],&Question);
Tst_QstDestructor (&Question);
/* Store test exam question in database */
ExaPrn_StoreOneQstOfPrintInDB (Print,
NumQst); // 0, 1, 2, 3...
/* Accumulate total score */
Print->Score += Print->PrintedQuestions[NumQst].Score;
if (Print->PrintedQuestions[NumQst].AnswerIsNotBlank)
Print->NumQstsNotBlank++;
/* Update the number of hits and the score of this question in tests database */
if (UpdateQstScore)
Tst_UpdateQstScoreInDB (&Print->PrintedQuestions[NumQst]);
}
}
/*****************************************************************************/
/************* Store user's answers of an test exam into database ************/
/*****************************************************************************/
static void ExaPrn_StoreOneQstOfPrintInDB (const struct ExaPrn_Print *Print,
unsigned NumQst)
{
char StrIndexes[Tst_MAX_BYTES_INDEXES_ONE_QST + 1];
char StrAnswers[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
/***** Replace each separator of multiple parameters by a comma *****/
/* In database commas are used as separators instead of special chars */
Par_ReplaceSeparatorMultipleByComma (Print->PrintedQuestions[NumQst].StrIndexes,StrIndexes);
Par_ReplaceSeparatorMultipleByComma (Print->PrintedQuestions[NumQst].StrAnswers,StrAnswers);
/***** Insert question and user's answers into database *****/
Str_SetDecimalPointToUS (); // To print the floating point as a dot
DB_QueryREPLACE ("can not update a question in an exam print",
"REPLACE INTO exa_print_questions"
" (PrnCod,QstCod,QstInd,Score,Indexes,Answers)"
" VALUES"
" (%ld,%ld,%u,'%.15lg','%s','%s')",
Print->PrnCod,Print->PrintedQuestions[NumQst].QstCod,
NumQst, // 0, 1, 2, 3...
Print->PrintedQuestions[NumQst].Score,
StrIndexes,
StrAnswers);
Str_SetDecimalPointToLocal (); // Return to local system
} }

View File

@ -1353,13 +1353,13 @@ void ExaRes_GetExamResultQuestionsFromDB (long EvtCod,long UsrCod,
/* Get indexes for this question (row[2]) */ /* Get indexes for this question (row[2]) */
Str_Copy (Print->PrintedQuestions[NumQst].StrIndexes,row[2], Str_Copy (Print->PrintedQuestions[NumQst].StrIndexes,row[2],
TstPrn_MAX_BYTES_INDEXES_ONE_QST); Tst_MAX_BYTES_INDEXES_ONE_QST);
/* Get answers selected by user for this question */ /* Get answers selected by user for this question */
ExaEvt_GetQstAnsFromDB (EvtCod,UsrCod,QstInd,&UsrAnswer); ExaEvt_GetQstAnsFromDB (EvtCod,UsrCod,QstInd,&UsrAnswer);
if (UsrAnswer.AnsInd >= 0) // UsrAnswer.AnsInd >= 0 ==> answer selected if (UsrAnswer.AnsInd >= 0) // UsrAnswer.AnsInd >= 0 ==> answer selected
{ {
snprintf (Print->PrintedQuestions[NumQst].StrAnswers,TstPrn_MAX_BYTES_ANSWERS_ONE_QST + 1, snprintf (Print->PrintedQuestions[NumQst].StrAnswers,Tst_MAX_BYTES_ANSWERS_ONE_QST + 1,
"%d",UsrAnswer.AnsInd); "%d",UsrAnswer.AnsInd);
Print->NumQstsNotBlank++; Print->NumQstsNotBlank++;
} }

View File

@ -1612,7 +1612,7 @@ static void Mch_ReorderAnswer (long MchCod,unsigned QstInd,
long LongNum; long LongNum;
unsigned AnsInd; unsigned AnsInd;
char StrOneAnswer[Cns_MAX_DECIMAL_DIGITS_UINT + 1]; char StrOneAnswer[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
char StrAnswersOneQst[TstPrn_MAX_BYTES_ANSWERS_ONE_QST + 1]; char StrAnswersOneQst[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
/***** Initialize list of answers to empty string *****/ /***** Initialize list of answers to empty string *****/
StrAnswersOneQst[0] = '\0'; StrAnswersOneQst[0] = '\0';
@ -1645,9 +1645,9 @@ static void Mch_ReorderAnswer (long MchCod,unsigned QstInd,
/* Concatenate answer index to list of answers */ /* Concatenate answer index to list of answers */
if (NumAns) if (NumAns)
Str_Concat (StrAnswersOneQst,",", Str_Concat (StrAnswersOneQst,",",
TstPrn_MAX_BYTES_ANSWERS_ONE_QST); Tst_MAX_BYTES_ANSWERS_ONE_QST);
Str_Concat (StrAnswersOneQst,StrOneAnswer, Str_Concat (StrAnswersOneQst,StrOneAnswer,
TstPrn_MAX_BYTES_ANSWERS_ONE_QST); Tst_MAX_BYTES_ANSWERS_ONE_QST);
} }
/***** Free structure that stores the query result *****/ /***** Free structure that stores the query result *****/
@ -1671,7 +1671,7 @@ void Mch_GetIndexes (long MchCod,unsigned QstInd,
{ {
MYSQL_RES *mysql_res; MYSQL_RES *mysql_res;
MYSQL_ROW row; MYSQL_ROW row;
char StrIndexesOneQst[TstPrn_MAX_BYTES_INDEXES_ONE_QST + 1]; char StrIndexesOneQst[Tst_MAX_BYTES_INDEXES_ONE_QST + 1];
/***** Get indexes for a question from database *****/ /***** Get indexes for a question from database *****/
if (!DB_QuerySELECT (&mysql_res,"can not get data of a question", if (!DB_QuerySELECT (&mysql_res,"can not get data of a question",
@ -1684,7 +1684,7 @@ void Mch_GetIndexes (long MchCod,unsigned QstInd,
/* Get indexes (row[0]) */ /* Get indexes (row[0]) */
Str_Copy (StrIndexesOneQst,row[0], Str_Copy (StrIndexesOneQst,row[0],
TstPrn_MAX_BYTES_INDEXES_ONE_QST); Tst_MAX_BYTES_INDEXES_ONE_QST);
/***** Free structure that stores the query result *****/ /***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res); DB_FreeMySQLResult (&mysql_res);
@ -4017,7 +4017,7 @@ static void Mch_ComputeScore (struct TstPrn_Print *Print)
Question.Answer.Type = Tst_ANS_UNIQUE_CHOICE; Question.Answer.Type = Tst_ANS_UNIQUE_CHOICE;
/***** Compute score for this answer ******/ /***** Compute score for this answer ******/
TstPrn_ComputeChoiceAnsScore (Print,NumQst,&Question); TstPrn_ComputeChoiceAnsScore (&Print->PrintedQuestions[NumQst],&Question);
/***** Update total score *****/ /***** Update total score *****/
Print->Score += Print->PrintedQuestions[NumQst].Score; Print->Score += Print->PrintedQuestions[NumQst].Score;

View File

@ -1344,13 +1344,13 @@ void MchRes_GetMatchResultQuestionsFromDB (long MchCod,long UsrCod,
/* Get indexes for this question (row[2]) */ /* Get indexes for this question (row[2]) */
Str_Copy (Print->PrintedQuestions[NumQst].StrIndexes,row[2], Str_Copy (Print->PrintedQuestions[NumQst].StrIndexes,row[2],
TstPrn_MAX_BYTES_INDEXES_ONE_QST); Tst_MAX_BYTES_INDEXES_ONE_QST);
/* Get answers selected by user for this question */ /* Get answers selected by user for this question */
Mch_GetQstAnsFromDB (MchCod,UsrCod,QstInd,&UsrAnswer); Mch_GetQstAnsFromDB (MchCod,UsrCod,QstInd,&UsrAnswer);
if (UsrAnswer.AnsInd >= 0) // UsrAnswer.AnsInd >= 0 ==> answer selected if (UsrAnswer.AnsInd >= 0) // UsrAnswer.AnsInd >= 0 ==> answer selected
{ {
snprintf (Print->PrintedQuestions[NumQst].StrAnswers,TstPrn_MAX_BYTES_ANSWERS_ONE_QST + 1, snprintf (Print->PrintedQuestions[NumQst].StrAnswers,Tst_MAX_BYTES_ANSWERS_ONE_QST + 1,
"%d",UsrAnswer.AnsInd); "%d",UsrAnswer.AnsInd);
Print->NumQstsNotBlank++; Print->NumQstsNotBlank++;
} }

View File

@ -467,8 +467,8 @@ void Tst_ShowNewTest (void)
NumExamsGeneratedByMe = Tst_GetNumExamsGeneratedByMe (); NumExamsGeneratedByMe = Tst_GetNumExamsGeneratedByMe ();
/***** Create new test exam in database *****/ /***** Create new test exam in database *****/
TstPrn_CreateExamInDB (&Print); TstPrn_CreatePrintInDB (&Print);
TstPrn_ComputeScoresAndStoreExamQuestions (&Print, TstPrn_ComputeScoresAndStoreQuestionsOfPrint (&Print,
false); // Don't update question score false); // Don't update question score
/***** Show test exam to be answered *****/ /***** Show test exam to be answered *****/
@ -552,7 +552,7 @@ void Tst_ReceiveTestDraft (void)
Tst_GetAnswersFromForm (&Print); Tst_GetAnswersFromForm (&Print);
/***** Update test exam in database *****/ /***** Update test exam in database *****/
TstPrn_ComputeScoresAndStoreExamQuestions (&Print, TstPrn_ComputeScoresAndStoreQuestionsOfPrint (&Print,
false); // Don't update question score false); // Don't update question score
TstPrn_UpdateExamInDB (&Print); TstPrn_UpdateExamInDB (&Print);
@ -612,7 +612,7 @@ void Tst_AssessTest (void)
Print.AllowTeachers = Par_GetParToBool ("AllowTchs"); Print.AllowTeachers = Par_GetParToBool ("AllowTchs");
/***** Update test exam in database *****/ /***** Update test exam in database *****/
TstPrn_ComputeScoresAndStoreExamQuestions (&Print, TstPrn_ComputeScoresAndStoreQuestionsOfPrint (&Print,
Gbl.Usrs.Me.Role.Logged == Rol_STD); // Update question score? Gbl.Usrs.Me.Role.Logged == Rol_STD); // Update question score?
TstPrn_UpdateExamInDB (&Print); TstPrn_UpdateExamInDB (&Print);
@ -646,7 +646,7 @@ void Tst_AssessTest (void)
HTM_TxtColonNBSP (Txt_Grade); HTM_TxtColonNBSP (Txt_Grade);
TstPrn_ComputeAndShowGrade (Print.NumQsts, TstPrn_ComputeAndShowGrade (Print.NumQsts,
Print.Score, Print.Score,
TstPrn_SCORE_MAX); Tst_SCORE_MAX);
HTM_DIV_End (); HTM_DIV_End ();
} }
@ -674,7 +674,7 @@ static void Tst_GetAnswersFromForm (struct TstPrn_Print *Print)
"Ans%010u", "Ans%010u",
NumQst); NumQst);
Par_GetParMultiToText (StrAns,Print->PrintedQuestions[NumQst].StrAnswers, Par_GetParMultiToText (StrAns,Print->PrintedQuestions[NumQst].StrAnswers,
TstPrn_MAX_BYTES_ANSWERS_ONE_QST); /* If answer type == T/F ==> " ", "T", "F"; if choice ==> "0", "2",... */ Tst_MAX_BYTES_ANSWERS_ONE_QST); /* If answer type == T/F ==> " ", "T", "F"; if choice ==> "0", "2",... */
} }
} }
@ -2611,7 +2611,7 @@ static void Tst_GetQuestionsForNewTestFromDB (struct Tst_Test *Test,
} }
/* End query */ /* End query */
Str_Concat (Query," ORDER BY RAND(NOW()) LIMIT ", Str_Concat (Query," ORDER BY RAND() LIMIT ",
Tst_MAX_BYTES_QUERY_TEST); Tst_MAX_BYTES_QUERY_TEST);
snprintf (StrNumQsts,sizeof (StrNumQsts), snprintf (StrNumQsts,sizeof (StrNumQsts),
"%u", "%u",
@ -2737,7 +2737,7 @@ void Tst_GenerateChoiceIndexesDependingOnShuffle (struct TstPrn_PrintedQuestion
else else
snprintf (StrInd,sizeof (StrInd),"%s%u",Par_SEPARATOR_PARAM_MULTIPLE,Index); snprintf (StrInd,sizeof (StrInd),"%s%u",Par_SEPARATOR_PARAM_MULTIPLE,Index);
Str_Concat (PrintedQuestion->StrIndexes,StrInd, Str_Concat (PrintedQuestion->StrIndexes,StrInd,
TstPrn_MAX_BYTES_INDEXES_ONE_QST); Tst_MAX_BYTES_INDEXES_ONE_QST);
} }
/***** Free structure that stores the query result *****/ /***** Free structure that stores the query result *****/
@ -3319,7 +3319,7 @@ void Tst_GetAnswersQst (struct Tst_Question *Question,MYSQL_RES **mysql_res,
" WHERE QstCod=%ld" " WHERE QstCod=%ld"
" ORDER BY %s", " ORDER BY %s",
Question->QstCod, Question->QstCod,
Shuffle ? "RAND(NOW())" : Shuffle ? "RAND()" :
"AnsInd"); "AnsInd");
if (!Question->Answer.NumOptions) if (!Question->Answer.NumOptions)
Ale_ShowAlert (Ale_ERROR,"Error when getting answers of a question."); Ale_ShowAlert (Ale_ERROR,"Error when getting answers of a question.");
@ -3762,7 +3762,7 @@ static void Tst_WriteTextAnsSeeing (const struct TstPrn_Print *Print,
snprintf (StrAns,sizeof (StrAns), snprintf (StrAns,sizeof (StrAns),
"Ans%010u", "Ans%010u",
NumQst); NumQst);
HTM_INPUT_TEXT (StrAns,TstPrn_MAX_CHARS_ANSWERS_ONE_QST,Print->PrintedQuestions[NumQst].StrAnswers, HTM_INPUT_TEXT (StrAns,Tst_MAX_CHARS_ANSWERS_ONE_QST,Print->PrintedQuestions[NumQst].StrAnswers,
HTM_DONT_SUBMIT_ON_CHANGE, HTM_DONT_SUBMIT_ON_CHANGE,
"size=\"40\""); "size=\"40\"");
} }
@ -5049,7 +5049,7 @@ static void Tst_GetQstFromForm (struct Tst_Question *Question)
char TagStr[6 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; char TagStr[6 + Cns_MAX_DECIMAL_DIGITS_UINT + 1];
char AnsStr[6 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; char AnsStr[6 + Cns_MAX_DECIMAL_DIGITS_UINT + 1];
char FbStr[5 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; char FbStr[5 + Cns_MAX_DECIMAL_DIGITS_UINT + 1];
char StrMultiAns[TstPrn_MAX_BYTES_ANSWERS_ONE_QST + 1]; char StrMultiAns[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
char TF[1 + 1]; // (T)rue or (F)alse char TF[1 + 1]; // (T)rue or (F)alse
const char *Ptr; const char *Ptr;
unsigned NumCorrectAns; unsigned NumCorrectAns;
@ -5201,7 +5201,7 @@ static void Tst_GetQstFromForm (struct Tst_Question *Question)
} }
else if (Question->Answer.Type == Tst_ANS_MULTIPLE_CHOICE) else if (Question->Answer.Type == Tst_ANS_MULTIPLE_CHOICE)
{ {
Par_GetParMultiToText ("AnsMulti",StrMultiAns,TstPrn_MAX_BYTES_ANSWERS_ONE_QST); Par_GetParMultiToText ("AnsMulti",StrMultiAns,Tst_MAX_BYTES_ANSWERS_ONE_QST);
Ptr = StrMultiAns; Ptr = StrMultiAns;
while (*Ptr) while (*Ptr)
{ {
@ -6156,6 +6156,31 @@ static void Tst_InsertAnswersIntoDB (struct Tst_Question *Question)
} }
} }
/*****************************************************************************/
/*********************** Update the score of a question **********************/
/*****************************************************************************/
void Tst_UpdateQstScoreInDB (struct TstPrn_PrintedQuestion *PrintedQuestion)
{
/***** Update number of clicks and score of the question *****/
Str_SetDecimalPointToUS (); // To print the floating point as a dot
if (PrintedQuestion->AnswerIsNotBlank)
DB_QueryUPDATE ("can not update the score of a question",
"UPDATE tst_questions"
" SET NumHits=NumHits+1,NumHitsNotBlank=NumHitsNotBlank+1,"
"Score=Score+(%.15lg)"
" WHERE QstCod=%ld",
PrintedQuestion->Score,
PrintedQuestion->QstCod);
else // The answer is blank
DB_QueryUPDATE ("can not update the score of a question",
"UPDATE tst_questions"
" SET NumHits=NumHits+1"
" WHERE QstCod=%ld",
PrintedQuestion->QstCod);
Str_SetDecimalPointToLocal (); // Return to local system
}
/*****************************************************************************/ /*****************************************************************************/
/******************* Remove all test questions in a course *******************/ /******************* Remove all test questions in a course *******************/
/*****************************************************************************/ /*****************************************************************************/

View File

@ -177,6 +177,8 @@ void Tst_PutParamQstCod (void *QstCod);
void Tst_InsertOrUpdateQstTagsAnsIntoDB (struct Tst_Question *Question); void Tst_InsertOrUpdateQstTagsAnsIntoDB (struct Tst_Question *Question);
void Tst_UpdateQstScoreInDB (struct TstPrn_PrintedQuestion *PrintedQuestion);
void Tst_RemoveCrsTests (long CrsCod); void Tst_RemoveCrsTests (long CrsCod);
void Tst_GetTestStats (Tst_AnswerType_t AnsType,struct Tst_Stats *Stats); void Tst_GetTestStats (Tst_AnswerType_t AnsType,struct Tst_Stats *Stats);

View File

@ -78,30 +78,22 @@ static void TstPrn_WriteQstAndAnsExam (struct UsrData *UsrDat,
struct Tst_Question *Question, struct Tst_Question *Question,
bool QuestionExists, bool QuestionExists,
unsigned Visibility); unsigned Visibility);
static void TstPrn_ComputeAnswerScore (struct TstPrn_Print *Print, static void TstPrn_ComputeIntAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
unsigned NumQst,
struct Tst_Question *Question);
static void TstPrn_ComputeIntAnsScore (struct TstPrn_Print *Print,
unsigned NumQst,
struct Tst_Question *Question); struct Tst_Question *Question);
static void TstPrn_GetCorrectIntAnswerFromDB (struct Tst_Question *Question); static void TstPrn_GetCorrectIntAnswerFromDB (struct Tst_Question *Question);
static void TstPrn_ComputeFloatAnsScore (struct TstPrn_Print *Print, static void TstPrn_ComputeFloatAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
unsigned NumQst,
struct Tst_Question *Question); struct Tst_Question *Question);
static void TstPrn_GetCorrectFloatAnswerFromDB (struct Tst_Question *Question); static void TstPrn_GetCorrectFloatAnswerFromDB (struct Tst_Question *Question);
static void TstPrn_ComputeTFAnsScore (struct TstPrn_Print *Print, static void TstPrn_ComputeTFAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
unsigned NumQst,
struct Tst_Question *Question); struct Tst_Question *Question);
static void TstPrn_GetCorrectTFAnswerFromDB (struct Tst_Question *Question); static void TstPrn_GetCorrectTFAnswerFromDB (struct Tst_Question *Question);
static void TstPrn_GetCorrectChoiceAnswerFromDB (struct Tst_Question *Question); static void TstPrn_GetCorrectChoiceAnswerFromDB (struct Tst_Question *Question);
static void TstPrn_ComputeScoreQst (struct TstPrn_Print *Print, static void TstPrn_ComputeScoreQst (struct TstPrn_PrintedQuestion *PrintedQuestion,
unsigned NumQst,
const struct Tst_Question *Question, const struct Tst_Question *Question,
unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION], // Indexes of all answers of this question unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION], // Indexes of all answers of this question
bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION]); bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION]);
static void TstPrn_ComputeTextAnsScore (struct TstPrn_Print *Print, static void TstPrn_ComputeTextAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
unsigned NumQst,
struct Tst_Question *Question); struct Tst_Question *Question);
static void TstPrn_GetCorrectTextAnswerFromDB (struct Tst_Question *Question); static void TstPrn_GetCorrectTextAnswerFromDB (struct Tst_Question *Question);
@ -137,9 +129,8 @@ static void TstPrn_WriteTextAnsExam (struct UsrData *UsrDat,
unsigned Visibility); unsigned Visibility);
static void TstPrn_WriteHeadUserCorrect (struct UsrData *UsrDat); static void TstPrn_WriteHeadUserCorrect (struct UsrData *UsrDat);
static void TstPrn_StoreOneExamQstInDB (const struct TstPrn_Print *Print, static void TstPrn_StoreOneQstOfPrintInDB (const struct TstPrn_Print *Print,
unsigned NumQst); unsigned NumQst);
static void Tst_UpdateQstScoreInDB (const struct TstPrn_Print *Print,unsigned NumQst);
static void TstPrn_PutFormToSelectUsrsToViewUsrsExams (__attribute__((unused)) void *Args); static void TstPrn_PutFormToSelectUsrsToViewUsrsExams (__attribute__((unused)) void *Args);
@ -175,20 +166,18 @@ static void TstPrn_ResetExamExceptExaCod (struct TstPrn_Print *Print)
} }
/*****************************************************************************/ /*****************************************************************************/
/***************** Create new blank test exam in database ********************/ /************** Create new blank test exam print in database *****************/
/*****************************************************************************/ /*****************************************************************************/
void TstPrn_CreateExamInDB (struct TstPrn_Print *Print) void TstPrn_CreatePrintInDB (struct TstPrn_Print *Print)
{ {
/***** Insert new test exam into table *****/ /***** Insert new test exam print into table *****/
Print->PrnCod = Print->PrnCod =
DB_QueryINSERTandReturnCode ("can not create new test exam", DB_QueryINSERTandReturnCode ("can not create new test exam print",
"INSERT INTO tst_exams" "INSERT INTO tst_exams"
" (CrsCod,UsrCod,StartTime,EndTime,NumQsts," " (CrsCod,UsrCod,StartTime,EndTime,NumQsts,NumQstsNotBlank,Sent,AllowTeachers,Score)"
"Sent,AllowTeachers,Score)"
" VALUES" " VALUES"
" (%ld,%ld,NOW(),NOW(),%u," " (%ld,%ld,NOW(),NOW(),%u,0,'N','N',0)",
"'N','N',0)",
Gbl.Hierarchy.Crs.CrsCod, Gbl.Hierarchy.Crs.CrsCod,
Gbl.Usrs.Me.UsrDat.UsrCod, Gbl.Usrs.Me.UsrDat.UsrCod,
Print->NumQsts); Print->NumQsts);
@ -259,7 +248,7 @@ void TstPrn_ShowExamAfterAssess (struct TstPrn_Print *Print)
TstCfg_GetConfigVisibility ()); TstCfg_GetConfigVisibility ());
/***** Store test exam question in database *****/ /***** Store test exam question in database *****/
TstPrn_StoreOneExamQstInDB (Print,NumQst); TstPrn_StoreOneQstOfPrintInDB (Print,NumQst);
/***** Compute total score *****/ /***** Compute total score *****/
Print->Score += Print->PrintedQuestions[NumQst].Score; Print->Score += Print->PrintedQuestions[NumQst].Score;
@ -268,7 +257,7 @@ void TstPrn_ShowExamAfterAssess (struct TstPrn_Print *Print)
/***** Update the number of accesses and the score of this question *****/ /***** Update the number of accesses and the score of this question *****/
if (Gbl.Usrs.Me.Role.Logged == Rol_STD) if (Gbl.Usrs.Me.Role.Logged == Rol_STD)
Tst_UpdateQstScoreInDB (Print,NumQst); Tst_UpdateQstScoreInDB (&Print->PrintedQuestions[NumQst]);
/***** Destroy test question *****/ /***** Destroy test question *****/
Tst_QstDestructor (&Question); Tst_QstDestructor (&Question);
@ -328,7 +317,7 @@ static void TstPrn_WriteQstAndAnsExam (struct UsrData *UsrDat,
"TEST_MED_SHOW"); "TEST_MED_SHOW");
/* Answers */ /* Answers */
TstPrn_ComputeAnswerScore (Print,NumQst,Question); TstPrn_ComputeAnswerScore (&Print->PrintedQuestions[NumQst],Question);
TstPrn_WriteAnswersExam (UsrDat,Print,NumQst,Question,Visibility); TstPrn_WriteAnswersExam (UsrDat,Print,NumQst,Question,Visibility);
} }
else else
@ -367,8 +356,8 @@ static void TstPrn_WriteQstAndAnsExam (struct UsrData *UsrDat,
/*********** Compute score of each question and store in database ************/ /*********** Compute score of each question and store in database ************/
/*****************************************************************************/ /*****************************************************************************/
void TstPrn_ComputeScoresAndStoreExamQuestions (struct TstPrn_Print *Print, void TstPrn_ComputeScoresAndStoreQuestionsOfPrint (struct TstPrn_Print *Print,
bool UpdateQstScore) bool UpdateQstScore)
{ {
unsigned NumQst; unsigned NumQst;
struct Tst_Question Question; struct Tst_Question Question;
@ -386,12 +375,12 @@ void TstPrn_ComputeScoresAndStoreExamQuestions (struct TstPrn_Print *Print,
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_GetQstAnswerType (Question.QstCod);
TstPrn_ComputeAnswerScore (Print,NumQst,&Question); TstPrn_ComputeAnswerScore (&Print->PrintedQuestions[NumQst],&Question);
Tst_QstDestructor (&Question); Tst_QstDestructor (&Question);
/* Store test exam question in database */ /* Store test exam question in database */
TstPrn_StoreOneExamQstInDB (Print, TstPrn_StoreOneQstOfPrintInDB (Print,
NumQst); // 0, 1, 2, 3... NumQst); // 0, 1, 2, 3...
/* Accumulate total score */ /* Accumulate total score */
Print->Score += Print->PrintedQuestions[NumQst].Score; Print->Score += Print->PrintedQuestions[NumQst].Score;
@ -400,7 +389,7 @@ void TstPrn_ComputeScoresAndStoreExamQuestions (struct TstPrn_Print *Print,
/* Update the number of hits and the score of this question in tests database */ /* Update the number of hits and the score of this question in tests database */
if (UpdateQstScore) if (UpdateQstScore)
Tst_UpdateQstScoreInDB (Print,NumQst); Tst_UpdateQstScoreInDB (&Print->PrintedQuestions[NumQst]);
} }
} }
@ -408,28 +397,27 @@ void TstPrn_ComputeScoresAndStoreExamQuestions (struct TstPrn_Print *Print,
/************* Write answers of a question when assessing a test *************/ /************* Write answers of a question when assessing a test *************/
/*****************************************************************************/ /*****************************************************************************/
static void TstPrn_ComputeAnswerScore (struct TstPrn_Print *Print, void TstPrn_ComputeAnswerScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
unsigned NumQst, struct Tst_Question *Question)
struct Tst_Question *Question)
{ {
/***** Write answer depending on type *****/ /***** Write answer depending on type *****/
switch (Question->Answer.Type) switch (Question->Answer.Type)
{ {
case Tst_ANS_INT: case Tst_ANS_INT:
TstPrn_ComputeIntAnsScore (Print,NumQst,Question); TstPrn_ComputeIntAnsScore (PrintedQuestion,Question);
break; break;
case Tst_ANS_FLOAT: case Tst_ANS_FLOAT:
TstPrn_ComputeFloatAnsScore (Print,NumQst,Question); TstPrn_ComputeFloatAnsScore (PrintedQuestion,Question);
break; break;
case Tst_ANS_TRUE_FALSE: case Tst_ANS_TRUE_FALSE:
TstPrn_ComputeTFAnsScore (Print,NumQst,Question); TstPrn_ComputeTFAnsScore (PrintedQuestion,Question);
break; break;
case Tst_ANS_UNIQUE_CHOICE: case Tst_ANS_UNIQUE_CHOICE:
case Tst_ANS_MULTIPLE_CHOICE: case Tst_ANS_MULTIPLE_CHOICE:
TstPrn_ComputeChoiceAnsScore (Print,NumQst,Question); TstPrn_ComputeChoiceAnsScore (PrintedQuestion,Question);
break; break;
case Tst_ANS_TEXT: case Tst_ANS_TEXT:
TstPrn_ComputeTextAnsScore (Print,NumQst,Question); TstPrn_ComputeTextAnsScore (PrintedQuestion,Question);
break; break;
default: default:
break; break;
@ -440,8 +428,7 @@ static void TstPrn_ComputeAnswerScore (struct TstPrn_Print *Print,
/**************** Write integer answer when assessing a test *****************/ /**************** Write integer answer when assessing a test *****************/
/*****************************************************************************/ /*****************************************************************************/
static void TstPrn_ComputeIntAnsScore (struct TstPrn_Print *Print, static void TstPrn_ComputeIntAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
unsigned NumQst,
struct Tst_Question *Question) struct Tst_Question *Question)
{ {
long AnswerUsr; long AnswerUsr;
@ -450,12 +437,12 @@ static void TstPrn_ComputeIntAnsScore (struct TstPrn_Print *Print,
TstPrn_GetCorrectIntAnswerFromDB (Question); TstPrn_GetCorrectIntAnswerFromDB (Question);
/***** Compute score *****/ /***** Compute score *****/
Print->PrintedQuestions[NumQst].Score = 0.0; // Default score for blank or wrong answer PrintedQuestion->Score = 0.0; // Default score for blank or wrong answer
Print->PrintedQuestions[NumQst].AnswerIsNotBlank = (Print->PrintedQuestions[NumQst].StrAnswers[0] != '\0'); PrintedQuestion->AnswerIsNotBlank = (PrintedQuestion->StrAnswers[0] != '\0');
if (Print->PrintedQuestions[NumQst].AnswerIsNotBlank) // If user has answered the answer if (PrintedQuestion->AnswerIsNotBlank) // If user has answered the answer
if (sscanf (Print->PrintedQuestions[NumQst].StrAnswers,"%ld",&AnswerUsr) == 1) if (sscanf (PrintedQuestion->StrAnswers,"%ld",&AnswerUsr) == 1)
if (AnswerUsr == Question->Answer.Integer) // Correct answer if (AnswerUsr == Question->Answer.Integer) // Correct answer
Print->PrintedQuestions[NumQst].Score = 1.0; PrintedQuestion->Score = 1.0;
} }
static void TstPrn_GetCorrectIntAnswerFromDB (struct Tst_Question *Question) static void TstPrn_GetCorrectIntAnswerFromDB (struct Tst_Question *Question)
@ -487,8 +474,7 @@ static void TstPrn_GetCorrectIntAnswerFromDB (struct Tst_Question *Question)
/***************** Write float answer when assessing a test ******************/ /***************** Write float answer when assessing a test ******************/
/*****************************************************************************/ /*****************************************************************************/
static void TstPrn_ComputeFloatAnsScore (struct TstPrn_Print *Print, static void TstPrn_ComputeFloatAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
unsigned NumQst,
struct Tst_Question *Question) struct Tst_Question *Question)
{ {
double AnswerUsr; double AnswerUsr;
@ -497,16 +483,16 @@ static void TstPrn_ComputeFloatAnsScore (struct TstPrn_Print *Print,
TstPrn_GetCorrectFloatAnswerFromDB (Question); TstPrn_GetCorrectFloatAnswerFromDB (Question);
/***** Compute score *****/ /***** Compute score *****/
Print->PrintedQuestions[NumQst].Score = 0.0; // Default score for blank or wrong answer PrintedQuestion->Score = 0.0; // Default score for blank or wrong answer
Print->PrintedQuestions[NumQst].AnswerIsNotBlank = (Print->PrintedQuestions[NumQst].StrAnswers[0] != '\0'); PrintedQuestion->AnswerIsNotBlank = (PrintedQuestion->StrAnswers[0] != '\0');
if (Print->PrintedQuestions[NumQst].AnswerIsNotBlank) // If user has answered the answer if (PrintedQuestion->AnswerIsNotBlank) // If user has answered the answer
{ {
AnswerUsr = Str_GetDoubleFromStr (Print->PrintedQuestions[NumQst].StrAnswers); AnswerUsr = Str_GetDoubleFromStr (PrintedQuestion->StrAnswers);
// A bad formatted floating point answer will interpreted as 0.0 // A bad formatted floating point answer will interpreted as 0.0
Print->PrintedQuestions[NumQst].Score = (AnswerUsr >= Question->Answer.FloatingPoint[0] && PrintedQuestion->Score = (AnswerUsr >= Question->Answer.FloatingPoint[0] &&
AnswerUsr <= Question->Answer.FloatingPoint[1]) ? 1.0 : // If correct (inside the interval) AnswerUsr <= Question->Answer.FloatingPoint[1]) ? 1.0 : // If correct (inside the interval)
0.0; // If wrong (outside the interval) 0.0; // If wrong (outside the interval)
} }
} }
@ -554,20 +540,19 @@ static void TstPrn_GetCorrectFloatAnswerFromDB (struct Tst_Question *Question)
/************** Write false / true answer when assessing a test **************/ /************** Write false / true answer when assessing a test **************/
/*****************************************************************************/ /*****************************************************************************/
static void TstPrn_ComputeTFAnsScore (struct TstPrn_Print *Print, static void TstPrn_ComputeTFAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
unsigned NumQst,
struct Tst_Question *Question) struct Tst_Question *Question)
{ {
/***** Get answer true or false *****/ /***** Get answer true or false *****/
TstPrn_GetCorrectTFAnswerFromDB (Question); TstPrn_GetCorrectTFAnswerFromDB (Question);
/***** Compute score *****/ /***** Compute score *****/
Print->PrintedQuestions[NumQst].AnswerIsNotBlank = (Print->PrintedQuestions[NumQst].StrAnswers[0] != '\0'); PrintedQuestion->AnswerIsNotBlank = (PrintedQuestion->StrAnswers[0] != '\0');
if (Print->PrintedQuestions[NumQst].AnswerIsNotBlank) // User has selected T or F if (PrintedQuestion->AnswerIsNotBlank) // User has selected T or F
Print->PrintedQuestions[NumQst].Score = (Print->PrintedQuestions[NumQst].StrAnswers[0] == Question->Answer.TF) ? 1.0 : // Correct PrintedQuestion->Score = (PrintedQuestion->StrAnswers[0] == Question->Answer.TF) ? 1.0 : // Correct
-1.0; // Wrong -1.0; // Wrong
else else
Print->PrintedQuestions[NumQst].Score = 0.0; PrintedQuestion->Score = 0.0;
} }
static void TstPrn_GetCorrectTFAnswerFromDB (struct Tst_Question *Question) static void TstPrn_GetCorrectTFAnswerFromDB (struct Tst_Question *Question)
@ -598,8 +583,7 @@ static void TstPrn_GetCorrectTFAnswerFromDB (struct Tst_Question *Question)
/************ Compute score for single or multiple choice answer *************/ /************ Compute score for single or multiple choice answer *************/
/*****************************************************************************/ /*****************************************************************************/
void TstPrn_ComputeChoiceAnsScore (struct TstPrn_Print *Print, void TstPrn_ComputeChoiceAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
unsigned NumQst,
struct Tst_Question *Question) struct Tst_Question *Question)
{ {
unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]; // Indexes of all answers of this question unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]; // Indexes of all answers of this question
@ -609,13 +593,13 @@ void TstPrn_ComputeChoiceAnsScore (struct TstPrn_Print *Print,
TstPrn_GetCorrectChoiceAnswerFromDB (Question); TstPrn_GetCorrectChoiceAnswerFromDB (Question);
/***** Get indexes for this question from string *****/ /***** Get indexes for this question from string *****/
TstPrn_GetIndexesFromStr (Print->PrintedQuestions[NumQst].StrIndexes,Indexes); TstPrn_GetIndexesFromStr (PrintedQuestion->StrIndexes,Indexes);
/***** Get the user's answers for this question from string *****/ /***** Get the user's answers for this question from string *****/
TstPrn_GetAnswersFromStr (Print->PrintedQuestions[NumQst].StrAnswers,UsrAnswers); TstPrn_GetAnswersFromStr (PrintedQuestion->StrAnswers,UsrAnswers);
/***** Compute the total score of this question *****/ /***** Compute the total score of this question *****/
TstPrn_ComputeScoreQst (Print,NumQst,Question,Indexes,UsrAnswers); TstPrn_ComputeScoreQst (PrintedQuestion,Question,Indexes,UsrAnswers);
} }
static void TstPrn_GetCorrectChoiceAnswerFromDB (struct Tst_Question *Question) static void TstPrn_GetCorrectChoiceAnswerFromDB (struct Tst_Question *Question)
@ -651,8 +635,8 @@ static void TstPrn_GetCorrectChoiceAnswerFromDB (struct Tst_Question *Question)
/********************* Get vector of indexes from string *********************/ /********************* Get vector of indexes from string *********************/
/*****************************************************************************/ /*****************************************************************************/
void TstPrn_GetIndexesFromStr (const char StrIndexesOneQst[TstPrn_MAX_BYTES_INDEXES_ONE_QST + 1], // 0 1 2 3, 3 0 2 1, etc. void TstPrn_GetIndexesFromStr (const char StrIndexesOneQst[Tst_MAX_BYTES_INDEXES_ONE_QST + 1], // 0 1 2 3, 3 0 2 1, etc.
unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]) unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION])
{ {
unsigned NumOpt; unsigned NumOpt;
const char *Ptr; const char *Ptr;
@ -683,7 +667,7 @@ void TstPrn_GetIndexesFromStr (const char StrIndexesOneQst[TstPrn_MAX_BYTES_INDE
/****************** Get vector of user's answers from string *****************/ /****************** Get vector of user's answers from string *****************/
/*****************************************************************************/ /*****************************************************************************/
void TstPrn_GetAnswersFromStr (const char StrAnswersOneQst[TstPrn_MAX_BYTES_ANSWERS_ONE_QST + 1], void TstPrn_GetAnswersFromStr (const char StrAnswersOneQst[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1],
bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION]) bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION])
{ {
unsigned NumOpt; unsigned NumOpt;
@ -718,8 +702,7 @@ void TstPrn_GetAnswersFromStr (const char StrAnswersOneQst[TstPrn_MAX_BYTES_ANSW
/*********************** Compute the score of a question *********************/ /*********************** Compute the score of a question *********************/
/*****************************************************************************/ /*****************************************************************************/
static void TstPrn_ComputeScoreQst (struct TstPrn_Print *Print, static void TstPrn_ComputeScoreQst (struct TstPrn_PrintedQuestion *PrintedQuestion,
unsigned NumQst,
const struct Tst_Question *Question, const struct Tst_Question *Question,
unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION], // Indexes of all answers of this question unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION], // Indexes of all answers of this question
bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION]) bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION])
@ -749,64 +732,63 @@ static void TstPrn_ComputeScoreQst (struct TstPrn_Print *Print,
} }
/* The answer is blank? */ /* The answer is blank? */
Print->PrintedQuestions[NumQst].AnswerIsNotBlank = NumAnsGood != 0 || NumAnsBad != 0; PrintedQuestion->AnswerIsNotBlank = NumAnsGood != 0 || NumAnsBad != 0;
if (Print->PrintedQuestions[NumQst].AnswerIsNotBlank) if (PrintedQuestion->AnswerIsNotBlank)
{ {
/* Compute the score */ /* Compute the score */
if (Question->Answer.Type == Tst_ANS_UNIQUE_CHOICE) if (Question->Answer.Type == Tst_ANS_UNIQUE_CHOICE)
{ {
if (NumOptTotInQst >= 2) // It should be 2 options at least if (NumOptTotInQst >= 2) // It should be 2 options at least
Print->PrintedQuestions[NumQst].Score = (double) NumAnsGood - PrintedQuestion->Score = (double) NumAnsGood -
(double) NumAnsBad / (double) (NumOptTotInQst - 1); (double) NumAnsBad / (double) (NumOptTotInQst - 1);
else // 0 or 1 options (impossible) else // 0 or 1 options (impossible)
Print->PrintedQuestions[NumQst].Score = (double) NumAnsGood; PrintedQuestion->Score = (double) NumAnsGood;
} }
else // AnswerType == Tst_ANS_MULTIPLE_CHOICE else // AnswerType == Tst_ANS_MULTIPLE_CHOICE
{ {
if (NumOptCorrInQst) // There are correct options in the question if (NumOptCorrInQst) // There are correct options in the question
{ {
if (NumOptCorrInQst < NumOptTotInQst) // If there are correct options and wrong options (typical case) if (NumOptCorrInQst < NumOptTotInQst) // If there are correct options and wrong options (typical case)
Print->PrintedQuestions[NumQst].Score = (double) NumAnsGood / (double) NumOptCorrInQst - PrintedQuestion->Score = (double) NumAnsGood / (double) NumOptCorrInQst -
(double) NumAnsBad / (double) (NumOptTotInQst - NumOptCorrInQst); (double) NumAnsBad / (double) (NumOptTotInQst - NumOptCorrInQst);
else // If all options are correct (extrange case) else // If all options are correct (extrange case)
Print->PrintedQuestions[NumQst].Score = (double) NumAnsGood / (double) NumOptCorrInQst; PrintedQuestion->Score = (double) NumAnsGood / (double) NumOptCorrInQst;
} }
else else
{ {
if (NumOptTotInQst) // There are options but none is correct (extrange case) if (NumOptTotInQst) // There are options but none is correct (extrange case)
Print->PrintedQuestions[NumQst].Score = - (double) NumAnsBad / (double) NumOptTotInQst; PrintedQuestion->Score = - (double) NumAnsBad / (double) NumOptTotInQst;
else // There are no options (impossible!) else // There are no options (impossible!)
Print->PrintedQuestions[NumQst].Score = 0.0; PrintedQuestion->Score = 0.0;
} }
} }
} }
else // Answer is blank else // Answer is blank
Print->PrintedQuestions[NumQst].Score = 0.0; PrintedQuestion->Score = 0.0;
} }
/*****************************************************************************/ /*****************************************************************************/
/********************* Compute score for text answer *************************/ /********************* Compute score for text answer *************************/
/*****************************************************************************/ /*****************************************************************************/
static void TstPrn_ComputeTextAnsScore (struct TstPrn_Print *Print, static void TstPrn_ComputeTextAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
unsigned NumQst,
struct Tst_Question *Question) struct Tst_Question *Question)
{ {
unsigned NumOpt; unsigned NumOpt;
char TextAnsUsr[TstPrn_MAX_BYTES_ANSWERS_ONE_QST + 1]; char TextAnsUsr[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
char TextAnsOK[TstPrn_MAX_BYTES_ANSWERS_ONE_QST + 1]; char TextAnsOK[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
/***** Get correct answers for this question from database *****/ /***** Get correct answers for this question from database *****/
TstPrn_GetCorrectTextAnswerFromDB (Question); TstPrn_GetCorrectTextAnswerFromDB (Question);
/***** Compute score *****/ /***** Compute score *****/
Print->PrintedQuestions[NumQst].Score = 0.0; // Default score for blank or wrong answer PrintedQuestion->Score = 0.0; // Default score for blank or wrong answer
Print->PrintedQuestions[NumQst].AnswerIsNotBlank = (Print->PrintedQuestions[NumQst].StrAnswers[0] != '\0'); PrintedQuestion->AnswerIsNotBlank = (PrintedQuestion->StrAnswers[0] != '\0');
if (Print->PrintedQuestions[NumQst].AnswerIsNotBlank) // If user has answered the answer if (PrintedQuestion->AnswerIsNotBlank) // If user has answered the answer
{ {
/* Filter the user answer */ /* Filter the user answer */
Str_Copy (TextAnsUsr,Print->PrintedQuestions[NumQst].StrAnswers, Str_Copy (TextAnsUsr,PrintedQuestion->StrAnswers,
TstPrn_MAX_BYTES_ANSWERS_ONE_QST); Tst_MAX_BYTES_ANSWERS_ONE_QST);
/* In order to compare student answer to stored answer, /* In order to compare student answer to stored answer,
the text answers are stored avoiding two or more consecurive spaces */ the text answers are stored avoiding two or more consecurive spaces */
@ -819,12 +801,12 @@ static void TstPrn_ComputeTextAnsScore (struct TstPrn_Print *Print,
{ {
/* Filter this correct answer */ /* Filter this correct answer */
Str_Copy (TextAnsOK,Question->Answer.Options[NumOpt].Text, Str_Copy (TextAnsOK,Question->Answer.Options[NumOpt].Text,
TstPrn_MAX_BYTES_ANSWERS_ONE_QST); Tst_MAX_BYTES_ANSWERS_ONE_QST);
Str_ConvertToComparable (TextAnsOK); Str_ConvertToComparable (TextAnsOK);
/* Check is user answer is correct */ /* Check is user answer is correct */
if (!strcoll (TextAnsUsr,TextAnsOK)) if (!strcoll (TextAnsUsr,TextAnsOK))
Print->PrintedQuestions[NumQst].Score = 1.0; // Correct answer PrintedQuestion->Score = 1.0; // Correct answer
} }
} }
} }
@ -1254,8 +1236,8 @@ static void TstPrn_WriteTextAnsExam (struct UsrData *UsrDat,
unsigned Visibility) unsigned Visibility)
{ {
unsigned NumOpt; unsigned NumOpt;
char TextAnsUsr[TstPrn_MAX_BYTES_ANSWERS_ONE_QST + 1]; char TextAnsUsr[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
char TextAnsOK[TstPrn_MAX_BYTES_ANSWERS_ONE_QST + 1]; char TextAnsOK[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
bool Correct = false; bool Correct = false;
/***** Get text and correctness of answers for this question from database (one row per answer) *****/ /***** Get text and correctness of answers for this question from database (one row per answer) *****/
@ -1290,7 +1272,7 @@ static void TstPrn_WriteTextAnsExam (struct UsrData *UsrDat,
{ {
/* Filter the user answer */ /* Filter the user answer */
Str_Copy (TextAnsUsr,Print->PrintedQuestions[NumQst].StrAnswers, Str_Copy (TextAnsUsr,Print->PrintedQuestions[NumQst].StrAnswers,
TstPrn_MAX_BYTES_ANSWERS_ONE_QST); Tst_MAX_BYTES_ANSWERS_ONE_QST);
/* In order to compare student answer to stored answer, /* In order to compare student answer to stored answer,
the text answers are stored avoiding two or more consecurive spaces */ the text answers are stored avoiding two or more consecurive spaces */
@ -1304,7 +1286,7 @@ static void TstPrn_WriteTextAnsExam (struct UsrData *UsrDat,
{ {
/* Filter this correct answer */ /* Filter this correct answer */
Str_Copy (TextAnsOK,Question->Answer.Options[NumOpt].Text, Str_Copy (TextAnsOK,Question->Answer.Options[NumOpt].Text,
TstPrn_MAX_BYTES_ANSWERS_ONE_QST); Tst_MAX_BYTES_ANSWERS_ONE_QST);
Str_ConvertToComparable (TextAnsOK); Str_ConvertToComparable (TextAnsOK);
/* Check is user answer is correct */ /* Check is user answer is correct */
@ -1398,14 +1380,14 @@ static void TstPrn_WriteHeadUserCorrect (struct UsrData *UsrDat)
} }
/*****************************************************************************/ /*****************************************************************************/
/************* Store user's answers of an test exam into database ************/ /************ Store user's answers of an test print into database ************/
/*****************************************************************************/ /*****************************************************************************/
static void TstPrn_StoreOneExamQstInDB (const struct TstPrn_Print *Print, static void TstPrn_StoreOneQstOfPrintInDB (const struct TstPrn_Print *Print,
unsigned NumQst) unsigned NumQst)
{ {
char StrIndexes[TstPrn_MAX_BYTES_INDEXES_ONE_QST + 1]; char StrIndexes[Tst_MAX_BYTES_INDEXES_ONE_QST + 1];
char StrAnswers[TstPrn_MAX_BYTES_ANSWERS_ONE_QST + 1]; char StrAnswers[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
/***** Replace each separator of multiple parameters by a comma *****/ /***** Replace each separator of multiple parameters by a comma *****/
/* In database commas are used as separators instead of special chars */ /* In database commas are used as separators instead of special chars */
@ -1427,31 +1409,6 @@ static void TstPrn_StoreOneExamQstInDB (const struct TstPrn_Print *Print,
Str_SetDecimalPointToLocal (); // Return to local system Str_SetDecimalPointToLocal (); // Return to local system
} }
/*****************************************************************************/
/*********************** Update the score of a question **********************/
/*****************************************************************************/
static void Tst_UpdateQstScoreInDB (const struct TstPrn_Print *Print,unsigned NumQst)
{
/***** Update number of clicks and score of the question *****/
Str_SetDecimalPointToUS (); // To print the floating point as a dot
if (Print->PrintedQuestions[NumQst].AnswerIsNotBlank)
DB_QueryUPDATE ("can not update the score of a question",
"UPDATE tst_questions"
" SET NumHits=NumHits+1,NumHitsNotBlank=NumHitsNotBlank+1,"
"Score=Score+(%.15lg)"
" WHERE QstCod=%ld",
Print->PrintedQuestions[NumQst].Score,
Print->PrintedQuestions[NumQst].QstCod);
else // The answer is blank
DB_QueryUPDATE ("can not update the score of a question",
"UPDATE tst_questions"
" SET NumHits=NumHits+1"
" WHERE QstCod=%ld",
Print->PrintedQuestions[NumQst].QstCod);
Str_SetDecimalPointToLocal (); // Return to local system
}
/*****************************************************************************/ /*****************************************************************************/
/************* Select users and dates to show their test exams ***************/ /************* Select users and dates to show their test exams ***************/
/*****************************************************************************/ /*****************************************************************************/
@ -1798,7 +1755,7 @@ static void TstPrn_ShowExams (struct UsrData *UsrDat)
HTM_TD_Begin ("class=\"%s RT COLOR%u\"",ClassDat,Gbl.RowEvenOdd); HTM_TD_Begin ("class=\"%s RT COLOR%u\"",ClassDat,Gbl.RowEvenOdd);
if (ICanView.Score) if (ICanView.Score)
TstPrn_ComputeAndShowGrade (Print.NumQsts,Print.Score, TstPrn_ComputeAndShowGrade (Print.NumQsts,Print.Score,
TstPrn_SCORE_MAX); Tst_SCORE_MAX);
HTM_TD_End (); HTM_TD_End ();
/* Link to show this test exam */ /* Link to show this test exam */
@ -1927,7 +1884,7 @@ static void TstPrn_ShowExamsSummaryRow (bool ItsMe,
HTM_TD_Begin ("class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd); HTM_TD_Begin ("class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd);
if (ICanViewTotalScore) if (ICanViewTotalScore)
TstPrn_ComputeAndShowGrade (NumTotalQsts,TotalScoreOfAllTests, TstPrn_ComputeAndShowGrade (NumTotalQsts,TotalScoreOfAllTests,
TstPrn_SCORE_MAX); Tst_SCORE_MAX);
HTM_TD_End (); HTM_TD_End ();
/***** Last cell *****/ /***** Last cell *****/
@ -2128,7 +2085,7 @@ void TstPrn_ShowOneExam (void)
HTM_TD_Begin ("class=\"DAT LT\""); HTM_TD_Begin ("class=\"DAT LT\"");
if (ICanViewScore) if (ICanViewScore)
TstPrn_ComputeAndShowGrade (Print.NumQsts,Print.Score, TstPrn_ComputeAndShowGrade (Print.NumQsts,Print.Score,
TstPrn_SCORE_MAX); Tst_SCORE_MAX);
else else
Ico_PutIconNotVisible (); Ico_PutIconNotVisible ();
HTM_TD_End (); HTM_TD_End ();
@ -2164,7 +2121,7 @@ void TstPrn_ShowOneExam (void)
HTM_BR (); HTM_BR ();
HTM_TxtColonNBSP (Txt_Grade); HTM_TxtColonNBSP (Txt_Grade);
TstPrn_ComputeAndShowGrade (Print.NumQsts,Print.Score, TstPrn_ComputeAndShowGrade (Print.NumQsts,Print.Score,
TstPrn_SCORE_MAX); Tst_SCORE_MAX);
HTM_DIV_End (); HTM_DIV_End ();
} }
@ -2344,11 +2301,11 @@ void TstPrn_GetExamQuestionsFromDB (struct TstPrn_Print *Print)
/* Get indexes for this question (row[2]) */ /* Get indexes for this question (row[2]) */
Str_Copy (Print->PrintedQuestions[NumQst].StrIndexes,row[2], Str_Copy (Print->PrintedQuestions[NumQst].StrIndexes,row[2],
TstPrn_MAX_BYTES_INDEXES_ONE_QST); Tst_MAX_BYTES_INDEXES_ONE_QST);
/* Get answers selected by user for this question (row[3]) */ /* Get answers selected by user for this question (row[3]) */
Str_Copy (Print->PrintedQuestions[NumQst].StrAnswers,row[3], Str_Copy (Print->PrintedQuestions[NumQst].StrAnswers,row[3],
TstPrn_MAX_BYTES_ANSWERS_ONE_QST); Tst_MAX_BYTES_ANSWERS_ONE_QST);
/* Replace each comma by a separator of multiple parameters */ /* Replace each comma by a separator of multiple parameters */
/* In database commas are used as separators instead of special chars */ /* In database commas are used as separators instead of special chars */

View File

@ -35,13 +35,6 @@
/***************************** Public constants ******************************/ /***************************** Public constants ******************************/
/*****************************************************************************/ /*****************************************************************************/
#define TstPrn_MAX_BYTES_INDEXES_ONE_QST (Tst_MAX_OPTIONS_PER_QUESTION * (3 + 1))
#define TstPrn_MAX_CHARS_ANSWERS_ONE_QST (128 - 1) // 127
#define TstPrn_MAX_BYTES_ANSWERS_ONE_QST ((TstPrn_MAX_CHARS_ANSWERS_ONE_QST + 1) * Str_MAX_BYTES_PER_CHAR - 1) // 2047
#define TstPrn_SCORE_MAX 10 // Maximum score of a test (10 in Spain). Must be unsigned! // TODO: Make this configurable by teachers
/*****************************************************************************/ /*****************************************************************************/
/******************************* Public types ********************************/ /******************************* Public types ********************************/
/*****************************************************************************/ /*****************************************************************************/
@ -49,8 +42,8 @@
struct TstPrn_PrintedQuestion struct TstPrn_PrintedQuestion
{ {
long QstCod; // Question code long QstCod; // Question code
char StrIndexes[TstPrn_MAX_BYTES_INDEXES_ONE_QST + 1]; // 0 1 2 3, 3 0 2 1, etc. char StrIndexes[Tst_MAX_BYTES_INDEXES_ONE_QST + 1]; // 0 1 2 3, 3 0 2 1, etc.
char StrAnswers[TstPrn_MAX_BYTES_ANSWERS_ONE_QST + 1]; // Answers selected by user char StrAnswers[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1]; // Answers selected by user
double Score; // Question score double Score; // Question score
bool AnswerIsNotBlank; // Answer not blank? bool AnswerIsNotBlank; // Answer not blank?
}; };
@ -73,19 +66,20 @@ struct TstPrn_Print
/*****************************************************************************/ /*****************************************************************************/
void TstPrn_ResetResult (struct TstPrn_Print *Print); void TstPrn_ResetResult (struct TstPrn_Print *Print);
void TstPrn_CreateExamInDB (struct TstPrn_Print *Print); void TstPrn_CreatePrintInDB (struct TstPrn_Print *Print);
void TstPrn_UpdateExamInDB (const struct TstPrn_Print *Print); void TstPrn_UpdateExamInDB (const struct TstPrn_Print *Print);
void TstPrn_ShowExamAfterAssess (struct TstPrn_Print *Print); void TstPrn_ShowExamAfterAssess (struct TstPrn_Print *Print);
void TstPrn_ComputeScoresAndStoreExamQuestions (struct TstPrn_Print *Print, void TstPrn_ComputeScoresAndStoreQuestionsOfPrint (struct TstPrn_Print *Print,
bool UpdateQstScore); bool UpdateQstScore);
void TstPrn_ComputeChoiceAnsScore (struct TstPrn_Print *Print, void TstPrn_ComputeAnswerScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
unsigned NumQst, struct Tst_Question *Question);
void TstPrn_ComputeChoiceAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *Question); struct Tst_Question *Question);
void TstPrn_GetIndexesFromStr (const char StrIndexesOneQst[TstPrn_MAX_BYTES_INDEXES_ONE_QST + 1], // 0 1 2 3, 3 0 2 1, etc. void TstPrn_GetIndexesFromStr (const char StrIndexesOneQst[Tst_MAX_BYTES_INDEXES_ONE_QST + 1], // 0 1 2 3, 3 0 2 1, etc.
unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]); unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]);
void TstPrn_GetAnswersFromStr (const char StrAnswersOneQst[TstPrn_MAX_BYTES_ANSWERS_ONE_QST + 1], void TstPrn_GetAnswersFromStr (const char StrAnswersOneQst[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1],
bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION]); bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION]);
void TstPrn_ComputeAndShowGrade (unsigned NumQsts,double Score,double MaxGrade); void TstPrn_ComputeAndShowGrade (unsigned NumQsts,double Score,double MaxGrade);

View File

@ -38,6 +38,13 @@
#define Tst_MAX_OPTIONS_PER_QUESTION 10 #define Tst_MAX_OPTIONS_PER_QUESTION 10
#define Tst_MAX_BYTES_INDEXES_ONE_QST (Tst_MAX_OPTIONS_PER_QUESTION * (3 + 1))
#define Tst_MAX_CHARS_ANSWERS_ONE_QST (128 - 1) // 127
#define Tst_MAX_BYTES_ANSWERS_ONE_QST ((Tst_MAX_CHARS_ANSWERS_ONE_QST + 1) * Str_MAX_BYTES_PER_CHAR - 1) // 2047
#define Tst_SCORE_MAX 10 // Maximum score of a test (10 in Spain). Must be unsigned! // TODO: Make this configurable by teachers
/*****************************************************************************/ /*****************************************************************************/
/******************************* Public types ********************************/ /******************************* Public types ********************************/
/*****************************************************************************/ /*****************************************************************************/

View File

@ -4091,7 +4091,7 @@ long Usr_GetRamdomStdFromCrs (long CrsCod)
" from the current course", " from the current course",
"SELECT UsrCod FROM crs_usr" "SELECT UsrCod FROM crs_usr"
" WHERE CrsCod=%ld AND Role=%u" " WHERE CrsCod=%ld AND Role=%u"
" ORDER BY RAND(NOW()) LIMIT 1", " ORDER BY RAND() LIMIT 1",
CrsCod,(unsigned) Rol_STD)) CrsCod,(unsigned) Rol_STD))
{ {
/***** Get user code *****/ /***** Get user code *****/
@ -4121,7 +4121,7 @@ long Usr_GetRamdomStdFromGrp (long GrpCod)
"SELECT crs_grp_usr.UsrCod FROM crs_grp_usr,crs_usr" "SELECT crs_grp_usr.UsrCod FROM crs_grp_usr,crs_usr"
" WHERE crs_grp_usr.GrpCod=%ld" " WHERE crs_grp_usr.GrpCod=%ld"
" AND crs_grp_usr.UsrCod=crs_usr.UsrCod" " AND crs_grp_usr.UsrCod=crs_usr.UsrCod"
" AND crs_usr.Role=%u ORDER BY RAND(NOW()) LIMIT 1", " AND crs_usr.Role=%u ORDER BY RAND() LIMIT 1",
GrpCod,(unsigned) Rol_STD)) GrpCod,(unsigned) Rol_STD))
{ {
/***** Get user code *****/ /***** Get user code *****/