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,
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
--
CREATE TABLE IF NOT EXISTS exa_questions (
@ -1507,7 +1533,7 @@ CREATE TABLE IF NOT EXISTS tst_config (
Visibility INT NOT NULL DEFAULT 0x1f,
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 (
ExaCod INT NOT NULL,

View File

@ -4635,7 +4635,7 @@ int swad__getTrivialQuestion (struct soap *soap,
" AND tst_tags.TagHidden='Y'"
" AND tst_tags.TagCod=tst_question_tags.TagCod)"
" HAVING S>='%f' AND S<='%f'"
" ORDER BY RAND(NOW()) LIMIT 1",
" ORDER BY RAND() LIMIT 1",
DegreesStr,DegreesStr,
lowerScore,upperScore);
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:
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 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: May 07, 2020 Fixed bug in creation of new exam announcements, reported by Francisco Gómez Mula.
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)
1 change necessary in database:
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.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)

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_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_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 */
#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:
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,"
"UNIX_TIMESTAMP()-UNIX_TIMESTAMP(LastTime) AS Dif"
" FROM connected"

View File

@ -1159,6 +1159,60 @@ mysql> DESCRIBE exa_participants;
"TS TIMESTAMP,"
"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 *****/
/*
mysql> DESCRIBE exa_questions;
@ -1768,7 +1822,7 @@ mysql> DESCRIBE mch_indexes;
DB_CreateTable ("CREATE TABLE IF NOT EXISTS mch_indexes ("
"MchCod INT NOT NULL,"
"QstInd INT NOT NULL,"
"Indexes TEXT NOT NULL," // TstPrn_MAX_BYTES_INDEXES_ONE_QST
"Indexes TEXT NOT NULL," // Tst_MAX_BYTES_INDEXES_ONE_QST
"UNIQUE INDEX(MchCod,QstInd))");
/***** Table mch_results *****/
@ -3184,16 +3238,16 @@ mysql> DESCRIBE tst_exam_questions;
+---------+---------+------+-----+---------+-------+
6 rows in set (0.00 sec)
*/
DB_CreateTable ("CREATE TABLE IF NOT EXISTS tst_exam_questions ("
"ExaCod INT NOT NULL,"
DB_CreateTable ("CREATE TABLE IF NOT EXISTS tst_exam_questions (" // TODO: rename as tst_print_questions
"ExaCod INT NOT NULL," // TODO: rename as PrnCod
"QstCod INT NOT NULL,"
"QstInd INT NOT NULL,"
"Score DOUBLE PRECISION NOT NULL DEFAULT 0,"
"Indexes TEXT NOT NULL," // TstPrn_MAX_BYTES_INDEXES_ONE_QST
"Answers TEXT NOT NULL," // TstPrn_MAX_BYTES_ANSWERS_ONE_QST
"Indexes TEXT NOT NULL," // Tst_MAX_BYTES_INDEXES_ONE_QST
"Answers TEXT NOT NULL," // Tst_MAX_BYTES_ANSWERS_ONE_QST
"UNIQUE INDEX(ExaCod,QstCod))");
/***** Table tst_exams *****/
/***** Table tst_exams *****/ // TODO: rename as tst_prints
/*
mysql> DESCRIBE tst_exams;
+-----------------+---------------+------+-----+---------+----------------+
@ -3212,8 +3266,8 @@ mysql> DESCRIBE tst_exams;
+-----------------+---------------+------+-----+---------+----------------+
10 rows in set (0.00 sec)
*/
DB_CreateTable ("CREATE TABLE IF NOT EXISTS tst_exams ("
"ExaCod INT NOT NULL AUTO_INCREMENT,"
DB_CreateTable ("CREATE TABLE IF NOT EXISTS tst_exams (" // TODO: rename as tst_prints
"ExaCod INT NOT NULL AUTO_INCREMENT," // TODO: rename as PrnCod
"CrsCod INT NOT NULL,"
"UsrCod INT NOT NULL,"
"StartTime DATETIME NOT NULL,"
@ -3223,7 +3277,7 @@ mysql> DESCRIBE tst_exams;
"Sent ENUM('N','Y') NOT NULL DEFAULT 'N',"
"AllowTeachers ENUM('N','Y') NOT NULL DEFAULT 'N',"
"Score DOUBLE PRECISION NOT NULL DEFAULT 0,"
"UNIQUE INDEX(ExaCod),"
"UNIQUE INDEX(ExaCod)," // TODO: rename as PrnCod
"INDEX(CrsCod,UsrCod))");
/***** Table tst_question_tags *****/

View File

@ -1934,7 +1934,7 @@ static void ExaEvt_ReorderAnswer (long EvtCod,unsigned QstInd,
long LongNum;
unsigned AnsInd;
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 *****
StrAnswersOneQst[0] = '\0';
@ -1967,9 +1967,9 @@ static void ExaEvt_ReorderAnswer (long EvtCod,unsigned QstInd,
* Concatenate answer index to list of answers *
if (NumAns)
Str_Concat (StrAnswersOneQst,",",
TstPrn_MAX_BYTES_ANSWERS_ONE_QST);
Tst_MAX_BYTES_ANSWERS_ONE_QST);
Str_Concat (StrAnswersOneQst,StrOneAnswer,
TstPrn_MAX_BYTES_ANSWERS_ONE_QST);
Tst_MAX_BYTES_ANSWERS_ONE_QST);
}
***** Free structure that stores the query result *****
@ -1993,7 +1993,7 @@ void ExaEvt_GetIndexes (long EvtCod,unsigned QstInd,
{
MYSQL_RES *mysql_res;
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 *****/
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]) */
Str_Copy (StrIndexesOneQst,row[0],
TstPrn_MAX_BYTES_INDEXES_ONE_QST);
Tst_MAX_BYTES_INDEXES_ONE_QST);
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
@ -4348,7 +4348,7 @@ static void ExaEvt_ComputeScore (struct TstPrn_Print *Print)
Question.Answer.Type = Tst_ANS_UNIQUE_CHOICE;
/***** Compute score for this answer ******/
TstPrn_ComputeChoiceAnsScore (Print,NumQst,&Question);
TstPrn_ComputeChoiceAnsScore (&Print->PrintedQuestions[NumQst],&Question);
/***** Update total 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,
struct ExaPrn_Print *Print);
static void ExaPrn_ShowQuestionsFromSet (struct ExaPrn_Print *Print,
struct ExaSet_Set *Set);
static unsigned ExaPrn_GetSomeQstsFromSetToPrint (struct ExaPrn_Print *Print,
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 *******************************/
@ -145,6 +152,22 @@ void ExaPrn_ShowNewExamPrint (void)
/***** Get questions from database *****/
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 *****/
HTM_TABLE_End ();
}
@ -163,6 +186,8 @@ static void ExaPrn_GetQuestionsForNewPrintFromDB (struct Exa_Exam *Exam,
unsigned NumSets;
unsigned NumSet;
struct ExaSet_Set Set;
unsigned NumQstsFromSet;
unsigned NumQstInPrint = 0;
/***** Get data of set of questions from database *****/
NumSets = (unsigned)
@ -175,9 +200,10 @@ static void ExaPrn_GetQuestionsForNewPrintFromDB (struct Exa_Exam *Exam,
" ORDER BY SetInd",
Exam->ExaCod);
/***** Show table with sets *****/
/***** Get questions from all sets *****/
Print->NumQsts = 0;
if (NumSets)
/***** Write rows *****/
/***** For each set in exam... *****/
for (NumSet = 0;
NumSet < NumSets;
NumSet++)
@ -227,9 +253,14 @@ static void ExaPrn_GetQuestionsForNewPrintFromDB (struct Exa_Exam *Exam,
HTM_TR_End ();
/***** 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 *****/
DB_FreeMySQLResult (&mysql_res);
}
@ -238,35 +269,35 @@ static void ExaPrn_GetQuestionsForNewPrintFromDB (struct Exa_Exam *Exam,
/************************ Show questions from a set **************************/
/*****************************************************************************/
static void ExaPrn_ShowQuestionsFromSet (struct ExaPrn_Print *Print,
struct ExaSet_Set *Set)
static unsigned ExaPrn_GetSomeQstsFromSetToPrint (struct ExaPrn_Print *Print,
struct ExaSet_Set *Set,
unsigned *NumQstInPrint)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumQsts;
unsigned NumQst;
long QstCod;
unsigned NumQstsInSet;
unsigned NumQstInSet;
Tst_AnswerType_t AnswerType;
bool Shuffle;
/***** Get questions from database *****/
NumQsts = (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"
" ORDER BY RAND(NOW())"
" LIMIT %u",
Set->SetCod,
Set->NumQstsToPrint);
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"
" ORDER BY RAND()" // Don't use RAND(NOW()) because the same ordering will be repeated across sets
" LIMIT %u",
Set->SetCod,
Set->NumQstsToPrint);
/***** Questions in this set *****/
for (NumQst = 0;
NumQst < NumQsts;
NumQst++)
for (NumQstInSet = 0;
NumQstInSet < NumQstsInSet;
NumQstInSet++, (*NumQstInPrint)++)
{
Gbl.RowEvenOdd = 1 - Gbl.RowEvenOdd;
@ -279,7 +310,7 @@ static void ExaPrn_ShowQuestionsFromSet (struct ExaPrn_Print *Print,
*/
/* Get question code (row[0]) */
QstCod = Str_ConvertStrCodToLongCod (row[0]);
Print->PrintedQuestions[*NumQstInPrint].QstCod = Str_ConvertStrCodToLongCod (row[0]);
/* Get answer type (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_TRUE_FALSE:
case Tst_ANS_TEXT:
Print->PrintedQuestions[NumQst].StrIndexes[0] = '\0';
Print->PrintedQuestions[*NumQstInPrint].StrIndexes[0] = '\0';
break;
case Tst_ANS_UNIQUE_CHOICE:
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_GenerateChoiceIndexesDependingOnShuffle (&Print->PrintedQuestions[*NumQstInPrint],Shuffle);
break;
default:
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.
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. */
Print->PrintedQuestions[NumQst].StrAnswers[0] = '\0';
Print->PrintedQuestions[*NumQstInPrint].StrAnswers[0] = '\0';
/* Begin row for this question */
HTM_TR_Begin (NULL);
/* Title */
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 ();
/* 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 */
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]) */
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 */
ExaEvt_GetQstAnsFromDB (EvtCod,UsrCod,QstInd,&UsrAnswer);
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);
Print->NumQstsNotBlank++;
}

View File

@ -1612,7 +1612,7 @@ static void Mch_ReorderAnswer (long MchCod,unsigned QstInd,
long LongNum;
unsigned AnsInd;
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 *****/
StrAnswersOneQst[0] = '\0';
@ -1645,9 +1645,9 @@ static void Mch_ReorderAnswer (long MchCod,unsigned QstInd,
/* Concatenate answer index to list of answers */
if (NumAns)
Str_Concat (StrAnswersOneQst,",",
TstPrn_MAX_BYTES_ANSWERS_ONE_QST);
Tst_MAX_BYTES_ANSWERS_ONE_QST);
Str_Concat (StrAnswersOneQst,StrOneAnswer,
TstPrn_MAX_BYTES_ANSWERS_ONE_QST);
Tst_MAX_BYTES_ANSWERS_ONE_QST);
}
/***** Free structure that stores the query result *****/
@ -1671,7 +1671,7 @@ void Mch_GetIndexes (long MchCod,unsigned QstInd,
{
MYSQL_RES *mysql_res;
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 *****/
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]) */
Str_Copy (StrIndexesOneQst,row[0],
TstPrn_MAX_BYTES_INDEXES_ONE_QST);
Tst_MAX_BYTES_INDEXES_ONE_QST);
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
@ -4017,7 +4017,7 @@ static void Mch_ComputeScore (struct TstPrn_Print *Print)
Question.Answer.Type = Tst_ANS_UNIQUE_CHOICE;
/***** Compute score for this answer ******/
TstPrn_ComputeChoiceAnsScore (Print,NumQst,&Question);
TstPrn_ComputeChoiceAnsScore (&Print->PrintedQuestions[NumQst],&Question);
/***** Update total 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]) */
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 */
Mch_GetQstAnsFromDB (MchCod,UsrCod,QstInd,&UsrAnswer);
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);
Print->NumQstsNotBlank++;
}

View File

@ -467,8 +467,8 @@ void Tst_ShowNewTest (void)
NumExamsGeneratedByMe = Tst_GetNumExamsGeneratedByMe ();
/***** Create new test exam in database *****/
TstPrn_CreateExamInDB (&Print);
TstPrn_ComputeScoresAndStoreExamQuestions (&Print,
TstPrn_CreatePrintInDB (&Print);
TstPrn_ComputeScoresAndStoreQuestionsOfPrint (&Print,
false); // Don't update question score
/***** Show test exam to be answered *****/
@ -552,7 +552,7 @@ void Tst_ReceiveTestDraft (void)
Tst_GetAnswersFromForm (&Print);
/***** Update test exam in database *****/
TstPrn_ComputeScoresAndStoreExamQuestions (&Print,
TstPrn_ComputeScoresAndStoreQuestionsOfPrint (&Print,
false); // Don't update question score
TstPrn_UpdateExamInDB (&Print);
@ -612,7 +612,7 @@ void Tst_AssessTest (void)
Print.AllowTeachers = Par_GetParToBool ("AllowTchs");
/***** Update test exam in database *****/
TstPrn_ComputeScoresAndStoreExamQuestions (&Print,
TstPrn_ComputeScoresAndStoreQuestionsOfPrint (&Print,
Gbl.Usrs.Me.Role.Logged == Rol_STD); // Update question score?
TstPrn_UpdateExamInDB (&Print);
@ -646,7 +646,7 @@ void Tst_AssessTest (void)
HTM_TxtColonNBSP (Txt_Grade);
TstPrn_ComputeAndShowGrade (Print.NumQsts,
Print.Score,
TstPrn_SCORE_MAX);
Tst_SCORE_MAX);
HTM_DIV_End ();
}
@ -674,7 +674,7 @@ static void Tst_GetAnswersFromForm (struct TstPrn_Print *Print)
"Ans%010u",
NumQst);
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 */
Str_Concat (Query," ORDER BY RAND(NOW()) LIMIT ",
Str_Concat (Query," ORDER BY RAND() LIMIT ",
Tst_MAX_BYTES_QUERY_TEST);
snprintf (StrNumQsts,sizeof (StrNumQsts),
"%u",
@ -2737,7 +2737,7 @@ void Tst_GenerateChoiceIndexesDependingOnShuffle (struct TstPrn_PrintedQuestion
else
snprintf (StrInd,sizeof (StrInd),"%s%u",Par_SEPARATOR_PARAM_MULTIPLE,Index);
Str_Concat (PrintedQuestion->StrIndexes,StrInd,
TstPrn_MAX_BYTES_INDEXES_ONE_QST);
Tst_MAX_BYTES_INDEXES_ONE_QST);
}
/***** 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"
" ORDER BY %s",
Question->QstCod,
Shuffle ? "RAND(NOW())" :
Shuffle ? "RAND()" :
"AnsInd");
if (!Question->Answer.NumOptions)
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),
"Ans%010u",
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,
"size=\"40\"");
}
@ -5049,7 +5049,7 @@ static void Tst_GetQstFromForm (struct Tst_Question *Question)
char TagStr[6 + Cns_MAX_DECIMAL_DIGITS_UINT + 1];
char AnsStr[6 + Cns_MAX_DECIMAL_DIGITS_UINT + 1];
char FbStr[5 + Cns_MAX_DECIMAL_DIGITS_UINT + 1];
char StrMultiAns[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
const char *Ptr;
unsigned NumCorrectAns;
@ -5201,7 +5201,7 @@ static void Tst_GetQstFromForm (struct Tst_Question *Question)
}
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;
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 *******************/
/*****************************************************************************/

View File

@ -177,6 +177,8 @@ void Tst_PutParamQstCod (void *QstCod);
void Tst_InsertOrUpdateQstTagsAnsIntoDB (struct Tst_Question *Question);
void Tst_UpdateQstScoreInDB (struct TstPrn_PrintedQuestion *PrintedQuestion);
void Tst_RemoveCrsTests (long CrsCod);
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,
bool QuestionExists,
unsigned Visibility);
static void TstPrn_ComputeAnswerScore (struct TstPrn_Print *Print,
unsigned NumQst,
struct Tst_Question *Question);
static void TstPrn_ComputeIntAnsScore (struct TstPrn_Print *Print,
unsigned NumQst,
static void TstPrn_ComputeIntAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *Question);
static void TstPrn_GetCorrectIntAnswerFromDB (struct Tst_Question *Question);
static void TstPrn_ComputeFloatAnsScore (struct TstPrn_Print *Print,
unsigned NumQst,
static void TstPrn_ComputeFloatAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *Question);
static void TstPrn_GetCorrectFloatAnswerFromDB (struct Tst_Question *Question);
static void TstPrn_ComputeTFAnsScore (struct TstPrn_Print *Print,
unsigned NumQst,
static void TstPrn_ComputeTFAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *Question);
static void TstPrn_GetCorrectTFAnswerFromDB (struct Tst_Question *Question);
static void TstPrn_GetCorrectChoiceAnswerFromDB (struct Tst_Question *Question);
static void TstPrn_ComputeScoreQst (struct TstPrn_Print *Print,
unsigned NumQst,
static void TstPrn_ComputeScoreQst (struct TstPrn_PrintedQuestion *PrintedQuestion,
const struct Tst_Question *Question,
unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION], // Indexes of all answers of this question
bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION]);
static void TstPrn_ComputeTextAnsScore (struct TstPrn_Print *Print,
unsigned NumQst,
static void TstPrn_ComputeTextAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
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);
static void TstPrn_WriteHeadUserCorrect (struct UsrData *UsrDat);
static void TstPrn_StoreOneExamQstInDB (const struct TstPrn_Print *Print,
unsigned NumQst);
static void Tst_UpdateQstScoreInDB (const struct TstPrn_Print *Print,unsigned NumQst);
static void TstPrn_StoreOneQstOfPrintInDB (const struct TstPrn_Print *Print,
unsigned NumQst);
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 =
DB_QueryINSERTandReturnCode ("can not create new test exam",
DB_QueryINSERTandReturnCode ("can not create new test exam print",
"INSERT INTO tst_exams"
" (CrsCod,UsrCod,StartTime,EndTime,NumQsts,"
"Sent,AllowTeachers,Score)"
" (CrsCod,UsrCod,StartTime,EndTime,NumQsts,NumQstsNotBlank,Sent,AllowTeachers,Score)"
" VALUES"
" (%ld,%ld,NOW(),NOW(),%u,"
"'N','N',0)",
" (%ld,%ld,NOW(),NOW(),%u,0,'N','N',0)",
Gbl.Hierarchy.Crs.CrsCod,
Gbl.Usrs.Me.UsrDat.UsrCod,
Print->NumQsts);
@ -259,7 +248,7 @@ void TstPrn_ShowExamAfterAssess (struct TstPrn_Print *Print)
TstCfg_GetConfigVisibility ());
/***** Store test exam question in database *****/
TstPrn_StoreOneExamQstInDB (Print,NumQst);
TstPrn_StoreOneQstOfPrintInDB (Print,NumQst);
/***** Compute total 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 *****/
if (Gbl.Usrs.Me.Role.Logged == Rol_STD)
Tst_UpdateQstScoreInDB (Print,NumQst);
Tst_UpdateQstScoreInDB (&Print->PrintedQuestions[NumQst]);
/***** Destroy test question *****/
Tst_QstDestructor (&Question);
@ -328,7 +317,7 @@ static void TstPrn_WriteQstAndAnsExam (struct UsrData *UsrDat,
"TEST_MED_SHOW");
/* Answers */
TstPrn_ComputeAnswerScore (Print,NumQst,Question);
TstPrn_ComputeAnswerScore (&Print->PrintedQuestions[NumQst],Question);
TstPrn_WriteAnswersExam (UsrDat,Print,NumQst,Question,Visibility);
}
else
@ -367,8 +356,8 @@ static void TstPrn_WriteQstAndAnsExam (struct UsrData *UsrDat,
/*********** Compute score of each question and store in database ************/
/*****************************************************************************/
void TstPrn_ComputeScoresAndStoreExamQuestions (struct TstPrn_Print *Print,
bool UpdateQstScore)
void TstPrn_ComputeScoresAndStoreQuestionsOfPrint (struct TstPrn_Print *Print,
bool UpdateQstScore)
{
unsigned NumQst;
struct Tst_Question Question;
@ -386,12 +375,12 @@ void TstPrn_ComputeScoresAndStoreExamQuestions (struct TstPrn_Print *Print,
Tst_QstConstructor (&Question);
Question.QstCod = Print->PrintedQuestions[NumQst].QstCod;
Question.Answer.Type = Tst_GetQstAnswerType (Question.QstCod);
TstPrn_ComputeAnswerScore (Print,NumQst,&Question);
TstPrn_ComputeAnswerScore (&Print->PrintedQuestions[NumQst],&Question);
Tst_QstDestructor (&Question);
/* Store test exam question in database */
TstPrn_StoreOneExamQstInDB (Print,
NumQst); // 0, 1, 2, 3...
TstPrn_StoreOneQstOfPrintInDB (Print,
NumQst); // 0, 1, 2, 3...
/* Accumulate total 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 */
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 *************/
/*****************************************************************************/
static void TstPrn_ComputeAnswerScore (struct TstPrn_Print *Print,
unsigned NumQst,
struct Tst_Question *Question)
void TstPrn_ComputeAnswerScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *Question)
{
/***** Write answer depending on type *****/
switch (Question->Answer.Type)
{
case Tst_ANS_INT:
TstPrn_ComputeIntAnsScore (Print,NumQst,Question);
TstPrn_ComputeIntAnsScore (PrintedQuestion,Question);
break;
case Tst_ANS_FLOAT:
TstPrn_ComputeFloatAnsScore (Print,NumQst,Question);
TstPrn_ComputeFloatAnsScore (PrintedQuestion,Question);
break;
case Tst_ANS_TRUE_FALSE:
TstPrn_ComputeTFAnsScore (Print,NumQst,Question);
TstPrn_ComputeTFAnsScore (PrintedQuestion,Question);
break;
case Tst_ANS_UNIQUE_CHOICE:
case Tst_ANS_MULTIPLE_CHOICE:
TstPrn_ComputeChoiceAnsScore (Print,NumQst,Question);
TstPrn_ComputeChoiceAnsScore (PrintedQuestion,Question);
break;
case Tst_ANS_TEXT:
TstPrn_ComputeTextAnsScore (Print,NumQst,Question);
TstPrn_ComputeTextAnsScore (PrintedQuestion,Question);
break;
default:
break;
@ -440,8 +428,7 @@ static void TstPrn_ComputeAnswerScore (struct TstPrn_Print *Print,
/**************** Write integer answer when assessing a test *****************/
/*****************************************************************************/
static void TstPrn_ComputeIntAnsScore (struct TstPrn_Print *Print,
unsigned NumQst,
static void TstPrn_ComputeIntAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *Question)
{
long AnswerUsr;
@ -450,12 +437,12 @@ static void TstPrn_ComputeIntAnsScore (struct TstPrn_Print *Print,
TstPrn_GetCorrectIntAnswerFromDB (Question);
/***** Compute score *****/
Print->PrintedQuestions[NumQst].Score = 0.0; // Default score for blank or wrong answer
Print->PrintedQuestions[NumQst].AnswerIsNotBlank = (Print->PrintedQuestions[NumQst].StrAnswers[0] != '\0');
if (Print->PrintedQuestions[NumQst].AnswerIsNotBlank) // If user has answered the answer
if (sscanf (Print->PrintedQuestions[NumQst].StrAnswers,"%ld",&AnswerUsr) == 1)
PrintedQuestion->Score = 0.0; // Default score for blank or wrong answer
PrintedQuestion->AnswerIsNotBlank = (PrintedQuestion->StrAnswers[0] != '\0');
if (PrintedQuestion->AnswerIsNotBlank) // If user has answered the answer
if (sscanf (PrintedQuestion->StrAnswers,"%ld",&AnswerUsr) == 1)
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)
@ -487,8 +474,7 @@ static void TstPrn_GetCorrectIntAnswerFromDB (struct Tst_Question *Question)
/***************** Write float answer when assessing a test ******************/
/*****************************************************************************/
static void TstPrn_ComputeFloatAnsScore (struct TstPrn_Print *Print,
unsigned NumQst,
static void TstPrn_ComputeFloatAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *Question)
{
double AnswerUsr;
@ -497,16 +483,16 @@ static void TstPrn_ComputeFloatAnsScore (struct TstPrn_Print *Print,
TstPrn_GetCorrectFloatAnswerFromDB (Question);
/***** Compute score *****/
Print->PrintedQuestions[NumQst].Score = 0.0; // Default score for blank or wrong answer
Print->PrintedQuestions[NumQst].AnswerIsNotBlank = (Print->PrintedQuestions[NumQst].StrAnswers[0] != '\0');
if (Print->PrintedQuestions[NumQst].AnswerIsNotBlank) // If user has answered the answer
PrintedQuestion->Score = 0.0; // Default score for blank or wrong answer
PrintedQuestion->AnswerIsNotBlank = (PrintedQuestion->StrAnswers[0] != '\0');
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
Print->PrintedQuestions[NumQst].Score = (AnswerUsr >= Question->Answer.FloatingPoint[0] &&
AnswerUsr <= Question->Answer.FloatingPoint[1]) ? 1.0 : // If correct (inside the interval)
0.0; // If wrong (outside the interval)
PrintedQuestion->Score = (AnswerUsr >= Question->Answer.FloatingPoint[0] &&
AnswerUsr <= Question->Answer.FloatingPoint[1]) ? 1.0 : // If correct (inside the interval)
0.0; // If wrong (outside the interval)
}
}
@ -554,20 +540,19 @@ static void TstPrn_GetCorrectFloatAnswerFromDB (struct Tst_Question *Question)
/************** Write false / true answer when assessing a test **************/
/*****************************************************************************/
static void TstPrn_ComputeTFAnsScore (struct TstPrn_Print *Print,
unsigned NumQst,
static void TstPrn_ComputeTFAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *Question)
{
/***** Get answer true or false *****/
TstPrn_GetCorrectTFAnswerFromDB (Question);
/***** Compute score *****/
Print->PrintedQuestions[NumQst].AnswerIsNotBlank = (Print->PrintedQuestions[NumQst].StrAnswers[0] != '\0');
if (Print->PrintedQuestions[NumQst].AnswerIsNotBlank) // User has selected T or F
Print->PrintedQuestions[NumQst].Score = (Print->PrintedQuestions[NumQst].StrAnswers[0] == Question->Answer.TF) ? 1.0 : // Correct
-1.0; // Wrong
PrintedQuestion->AnswerIsNotBlank = (PrintedQuestion->StrAnswers[0] != '\0');
if (PrintedQuestion->AnswerIsNotBlank) // User has selected T or F
PrintedQuestion->Score = (PrintedQuestion->StrAnswers[0] == Question->Answer.TF) ? 1.0 : // Correct
-1.0; // Wrong
else
Print->PrintedQuestions[NumQst].Score = 0.0;
PrintedQuestion->Score = 0.0;
}
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 *************/
/*****************************************************************************/
void TstPrn_ComputeChoiceAnsScore (struct TstPrn_Print *Print,
unsigned NumQst,
void TstPrn_ComputeChoiceAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *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);
/***** 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 *****/
TstPrn_GetAnswersFromStr (Print->PrintedQuestions[NumQst].StrAnswers,UsrAnswers);
TstPrn_GetAnswersFromStr (PrintedQuestion->StrAnswers,UsrAnswers);
/***** 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)
@ -651,8 +635,8 @@ static void TstPrn_GetCorrectChoiceAnswerFromDB (struct Tst_Question *Question)
/********************* 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.
unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION])
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 NumOpt;
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 *****************/
/*****************************************************************************/
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])
{
unsigned NumOpt;
@ -718,8 +702,7 @@ void TstPrn_GetAnswersFromStr (const char StrAnswersOneQst[TstPrn_MAX_BYTES_ANSW
/*********************** Compute the score of a question *********************/
/*****************************************************************************/
static void TstPrn_ComputeScoreQst (struct TstPrn_Print *Print,
unsigned NumQst,
static void TstPrn_ComputeScoreQst (struct TstPrn_PrintedQuestion *PrintedQuestion,
const struct Tst_Question *Question,
unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION], // Indexes of all answers of this question
bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION])
@ -749,64 +732,63 @@ static void TstPrn_ComputeScoreQst (struct TstPrn_Print *Print,
}
/* The answer is blank? */
Print->PrintedQuestions[NumQst].AnswerIsNotBlank = NumAnsGood != 0 || NumAnsBad != 0;
if (Print->PrintedQuestions[NumQst].AnswerIsNotBlank)
PrintedQuestion->AnswerIsNotBlank = NumAnsGood != 0 || NumAnsBad != 0;
if (PrintedQuestion->AnswerIsNotBlank)
{
/* Compute the score */
if (Question->Answer.Type == Tst_ANS_UNIQUE_CHOICE)
{
if (NumOptTotInQst >= 2) // It should be 2 options at least
Print->PrintedQuestions[NumQst].Score = (double) NumAnsGood -
(double) NumAnsBad / (double) (NumOptTotInQst - 1);
PrintedQuestion->Score = (double) NumAnsGood -
(double) NumAnsBad / (double) (NumOptTotInQst - 1);
else // 0 or 1 options (impossible)
Print->PrintedQuestions[NumQst].Score = (double) NumAnsGood;
PrintedQuestion->Score = (double) NumAnsGood;
}
else // AnswerType == Tst_ANS_MULTIPLE_CHOICE
{
if (NumOptCorrInQst) // There are correct options in the question
{
if (NumOptCorrInQst < NumOptTotInQst) // If there are correct options and wrong options (typical case)
Print->PrintedQuestions[NumQst].Score = (double) NumAnsGood / (double) NumOptCorrInQst -
(double) NumAnsBad / (double) (NumOptTotInQst - NumOptCorrInQst);
PrintedQuestion->Score = (double) NumAnsGood / (double) NumOptCorrInQst -
(double) NumAnsBad / (double) (NumOptTotInQst - NumOptCorrInQst);
else // If all options are correct (extrange case)
Print->PrintedQuestions[NumQst].Score = (double) NumAnsGood / (double) NumOptCorrInQst;
PrintedQuestion->Score = (double) NumAnsGood / (double) NumOptCorrInQst;
}
else
{
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!)
Print->PrintedQuestions[NumQst].Score = 0.0;
PrintedQuestion->Score = 0.0;
}
}
}
else // Answer is blank
Print->PrintedQuestions[NumQst].Score = 0.0;
PrintedQuestion->Score = 0.0;
}
/*****************************************************************************/
/********************* Compute score for text answer *************************/
/*****************************************************************************/
static void TstPrn_ComputeTextAnsScore (struct TstPrn_Print *Print,
unsigned NumQst,
static void TstPrn_ComputeTextAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *Question)
{
unsigned NumOpt;
char TextAnsUsr[TstPrn_MAX_BYTES_ANSWERS_ONE_QST + 1];
char TextAnsOK[TstPrn_MAX_BYTES_ANSWERS_ONE_QST + 1];
char TextAnsUsr[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
char TextAnsOK[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
/***** Get correct answers for this question from database *****/
TstPrn_GetCorrectTextAnswerFromDB (Question);
/***** Compute score *****/
Print->PrintedQuestions[NumQst].Score = 0.0; // Default score for blank or wrong answer
Print->PrintedQuestions[NumQst].AnswerIsNotBlank = (Print->PrintedQuestions[NumQst].StrAnswers[0] != '\0');
if (Print->PrintedQuestions[NumQst].AnswerIsNotBlank) // If user has answered the answer
PrintedQuestion->Score = 0.0; // Default score for blank or wrong answer
PrintedQuestion->AnswerIsNotBlank = (PrintedQuestion->StrAnswers[0] != '\0');
if (PrintedQuestion->AnswerIsNotBlank) // If user has answered the answer
{
/* Filter the user answer */
Str_Copy (TextAnsUsr,Print->PrintedQuestions[NumQst].StrAnswers,
TstPrn_MAX_BYTES_ANSWERS_ONE_QST);
Str_Copy (TextAnsUsr,PrintedQuestion->StrAnswers,
Tst_MAX_BYTES_ANSWERS_ONE_QST);
/* In order to compare student answer to stored answer,
the text answers are stored avoiding two or more consecurive spaces */
@ -819,12 +801,12 @@ static void TstPrn_ComputeTextAnsScore (struct TstPrn_Print *Print,
{
/* Filter this correct answer */
Str_Copy (TextAnsOK,Question->Answer.Options[NumOpt].Text,
TstPrn_MAX_BYTES_ANSWERS_ONE_QST);
Tst_MAX_BYTES_ANSWERS_ONE_QST);
Str_ConvertToComparable (TextAnsOK);
/* Check is user answer is correct */
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 NumOpt;
char TextAnsUsr[TstPrn_MAX_BYTES_ANSWERS_ONE_QST + 1];
char TextAnsOK[TstPrn_MAX_BYTES_ANSWERS_ONE_QST + 1];
char TextAnsUsr[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
char TextAnsOK[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
bool Correct = false;
/***** 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 */
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,
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 */
Str_Copy (TextAnsOK,Question->Answer.Options[NumOpt].Text,
TstPrn_MAX_BYTES_ANSWERS_ONE_QST);
Tst_MAX_BYTES_ANSWERS_ONE_QST);
Str_ConvertToComparable (TextAnsOK);
/* 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,
unsigned NumQst)
static void TstPrn_StoreOneQstOfPrintInDB (const struct TstPrn_Print *Print,
unsigned NumQst)
{
char StrIndexes[TstPrn_MAX_BYTES_INDEXES_ONE_QST + 1];
char StrAnswers[TstPrn_MAX_BYTES_ANSWERS_ONE_QST + 1];
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 */
@ -1427,31 +1409,6 @@ static void TstPrn_StoreOneExamQstInDB (const struct TstPrn_Print *Print,
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 ***************/
/*****************************************************************************/
@ -1798,7 +1755,7 @@ static void TstPrn_ShowExams (struct UsrData *UsrDat)
HTM_TD_Begin ("class=\"%s RT COLOR%u\"",ClassDat,Gbl.RowEvenOdd);
if (ICanView.Score)
TstPrn_ComputeAndShowGrade (Print.NumQsts,Print.Score,
TstPrn_SCORE_MAX);
Tst_SCORE_MAX);
HTM_TD_End ();
/* 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);
if (ICanViewTotalScore)
TstPrn_ComputeAndShowGrade (NumTotalQsts,TotalScoreOfAllTests,
TstPrn_SCORE_MAX);
Tst_SCORE_MAX);
HTM_TD_End ();
/***** Last cell *****/
@ -2128,7 +2085,7 @@ void TstPrn_ShowOneExam (void)
HTM_TD_Begin ("class=\"DAT LT\"");
if (ICanViewScore)
TstPrn_ComputeAndShowGrade (Print.NumQsts,Print.Score,
TstPrn_SCORE_MAX);
Tst_SCORE_MAX);
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
@ -2164,7 +2121,7 @@ void TstPrn_ShowOneExam (void)
HTM_BR ();
HTM_TxtColonNBSP (Txt_Grade);
TstPrn_ComputeAndShowGrade (Print.NumQsts,Print.Score,
TstPrn_SCORE_MAX);
Tst_SCORE_MAX);
HTM_DIV_End ();
}
@ -2344,11 +2301,11 @@ void TstPrn_GetExamQuestionsFromDB (struct TstPrn_Print *Print)
/* Get indexes for this question (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]) */
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 */
/* In database commas are used as separators instead of special chars */

View File

@ -35,13 +35,6 @@
/***************************** 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 ********************************/
/*****************************************************************************/
@ -49,8 +42,8 @@
struct TstPrn_PrintedQuestion
{
long QstCod; // Question code
char StrIndexes[TstPrn_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 StrIndexes[Tst_MAX_BYTES_INDEXES_ONE_QST + 1]; // 0 1 2 3, 3 0 2 1, etc.
char StrAnswers[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1]; // Answers selected by user
double Score; // Question score
bool AnswerIsNotBlank; // Answer not blank?
};
@ -73,19 +66,20 @@ struct TstPrn_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_ShowExamAfterAssess (struct TstPrn_Print *Print);
void TstPrn_ComputeScoresAndStoreExamQuestions (struct TstPrn_Print *Print,
void TstPrn_ComputeScoresAndStoreQuestionsOfPrint (struct TstPrn_Print *Print,
bool UpdateQstScore);
void TstPrn_ComputeChoiceAnsScore (struct TstPrn_Print *Print,
unsigned NumQst,
void TstPrn_ComputeAnswerScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *Question);
void TstPrn_ComputeChoiceAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
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]);
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]);
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_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 ********************************/
/*****************************************************************************/

View File

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