mirror of https://github.com/acanas/swad-core.git
Version19.9
This commit is contained in:
parent
1f02939f6c
commit
f946925237
|
@ -661,13 +661,11 @@ CREATE TABLE IF NOT EXISTS mch_players (
|
|||
-- Table mch_results: stores the current match results
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS mch_results (
|
||||
MchResCod INT NOT NULL AUTO_INCREMENT,
|
||||
MchCod INT NOT NULL,
|
||||
UsrCod INT NOT NULL,
|
||||
NumQsts INT NOT NULL DEFAULT 0,
|
||||
NumQstsNotBlank INT NOT NULL DEFAULT 0,
|
||||
Score DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
UNIQUE INDEX(MchResCod),
|
||||
UNIQUE INDEX(MchCod,UsrCod));
|
||||
--
|
||||
-- Table gam_questions: stores the questions in the games
|
||||
|
|
|
@ -468,10 +468,15 @@ enscript -2 --landscape --color --file-align=2 --highlight --line-numbers -o - *
|
|||
En OpenSWAD:
|
||||
ps2pdf source.ps destination.pdf
|
||||
*/
|
||||
#define Log_PLATFORM_VERSION "SWAD 19.8.2 (2019-09-22)"
|
||||
#define Log_PLATFORM_VERSION "SWAD 19.9 (2019-09-23)"
|
||||
#define CSS_FILE "swad19.3.css"
|
||||
#define JS_FILE "swad18.130.2.js"
|
||||
/*
|
||||
Version 19.9: Sep 23, 2019 View matches results. Not finished. (245579 lines)
|
||||
2 changes necessary in database:
|
||||
DROP TABLE IF EXISTS mch_results;
|
||||
CREATE TABLE IF NOT EXISTS mch_results (MchCod INT NOT NULL,UsrCod INT NOT NULL,NumQsts INT NOT NULL DEFAULT 0,NumQstsNotBlank INT NOT NULL DEFAULT 0,Score DOUBLE PRECISION NOT NULL DEFAULT 0,UNIQUE INDEX(MchCod,UsrCod));
|
||||
|
||||
Version 19.8.2: Sep 22, 2019 View matches results. Not finished. (245429 lines)
|
||||
Version 19.8.1: Sep 22, 2019 View matches results. Not finished. (245474 lines)
|
||||
Version 19.8: Sep 22, 2019 View matches results. Not finished.
|
||||
|
|
|
@ -1436,26 +1436,23 @@ mysql> DESCRIBE mch_players;
|
|||
/***** Table mch_results *****/
|
||||
/*
|
||||
mysql> DESCRIBE mch_results;
|
||||
+-----------------+---------+------+-----+---------+----------------+
|
||||
| Field | Type | Null | Key | Default | Extra |
|
||||
+-----------------+---------+------+-----+---------+----------------+
|
||||
| MchResCod | int(11) | NO | PRI | NULL | auto_increment |
|
||||
| MchCod | int(11) | NO | MUL | NULL | |
|
||||
| UsrCod | int(11) | NO | | NULL | |
|
||||
| NumQsts | int(11) | NO | | 0 | |
|
||||
| NumQstsNotBlank | int(11) | NO | | 0 | |
|
||||
| Score | double | NO | | 0 | |
|
||||
+-----------------+---------+------+-----+---------+----------------+
|
||||
6 rows in set (0.01 sec)
|
||||
+-----------------+---------+------+-----+---------+-------+
|
||||
| Field | Type | Null | Key | Default | Extra |
|
||||
+-----------------+---------+------+-----+---------+-------+
|
||||
| MchCod | int(11) | NO | PRI | NULL | |
|
||||
| UsrCod | int(11) | NO | PRI | NULL | |
|
||||
| NumQsts | int(11) | NO | | 0 | |
|
||||
| NumQstsNotBlank | int(11) | NO | | 0 | |
|
||||
| Score | double | NO | | 0 | |
|
||||
+-----------------+---------+------+-----+---------+-------+
|
||||
5 rows in set (0.00 sec)
|
||||
*/
|
||||
DB_CreateTable ("CREATE TABLE IF NOT EXISTS mch_results ("
|
||||
"MchResCod INT NOT NULL AUTO_INCREMENT,"
|
||||
"MchCod INT NOT NULL,"
|
||||
"UsrCod INT NOT NULL,"
|
||||
"NumQsts INT NOT NULL DEFAULT 0,"
|
||||
"NumQstsNotBlank INT NOT NULL DEFAULT 0,"
|
||||
"Score DOUBLE PRECISION NOT NULL DEFAULT 0,"
|
||||
"UNIQUE INDEX(MchResCod),"
|
||||
"UNIQUE INDEX(MchCod,UsrCod))");
|
||||
|
||||
/***** Table gam_questions *****/
|
||||
|
|
139
swad_match.c
139
swad_match.c
|
@ -192,6 +192,8 @@ static void Mch_RegisterMeAsPlayerInMatch (long MchCod);
|
|||
static void Mch_GetNumPlayers (struct Match *Match);
|
||||
|
||||
static int Mch_GetQstAnsFromDB (long MchCod,unsigned QstInd);
|
||||
static void Mch_ComputeScore (struct Match *Match,unsigned NumQsts,
|
||||
unsigned *NumQstsNotBlank,double *TotalScore);
|
||||
|
||||
static unsigned Mch_GetNumUsrsWhoHaveChosenAns (long MchCod,unsigned QstInd,unsigned AnsInd);
|
||||
static unsigned Mch_GetNumUsrsWhoHaveAnswerMch (long MchCod);
|
||||
|
@ -2286,6 +2288,9 @@ void Mch_ReceiveQstAnsFromStd (void)
|
|||
unsigned QstInd;
|
||||
unsigned StdAnsInd;
|
||||
int PreviousStdAnsInd;
|
||||
unsigned NumQsts;
|
||||
unsigned NumQstsNotBlank;
|
||||
double TotalScore;
|
||||
|
||||
/***** Remove old players.
|
||||
This function must be called before getting match status. *****/
|
||||
|
@ -2331,6 +2336,20 @@ void Mch_ReceiveQstAnsFromStd (void)
|
|||
" VALUES"
|
||||
" (%ld,%ld,%u,%u)",
|
||||
Match.MchCod,Gbl.Usrs.Me.UsrDat.UsrCod,QstInd,StdAnsInd);
|
||||
|
||||
/***** Update student's match result *****/
|
||||
NumQsts = Gam_GetNumQstsGame (Match.GamCod);
|
||||
Mch_ComputeScore (&Match,NumQsts,&NumQstsNotBlank,&TotalScore);
|
||||
|
||||
Str_SetDecimalPointToUS (); // To print the floating point as a dot
|
||||
DB_QueryREPLACE ("can not update match result",
|
||||
"REPLACE mch_results"
|
||||
" (MchCod,UsrCod,NumQsts,NumQstsNotBlank,Score)"
|
||||
" VALUES"
|
||||
" (%ld,%ld,%u,%u,'%lf')",
|
||||
Match.MchCod,Gbl.Usrs.Me.UsrDat.UsrCod,
|
||||
NumQsts,NumQstsNotBlank,TotalScore);
|
||||
Str_SetDecimalPointToLocal (); // Return to local system
|
||||
}
|
||||
|
||||
/***** Show current match status *****/
|
||||
|
@ -2339,6 +2358,126 @@ void Mch_ReceiveQstAnsFromStd (void)
|
|||
fprintf (Gbl.F.Out,"</div>");
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/******************** Compute match score for a student **********************/
|
||||
/*****************************************************************************/
|
||||
|
||||
static void Mch_ComputeScore (struct Match *Match,unsigned NumQsts,
|
||||
unsigned *NumQstsNotBlank,double *TotalScore)
|
||||
{
|
||||
MYSQL_RES *mysql_res;
|
||||
MYSQL_ROW row;
|
||||
unsigned NumQst;
|
||||
unsigned NumQstNotBlank;
|
||||
unsigned QstInd;
|
||||
unsigned NumOpt;
|
||||
long QstCod;
|
||||
long LongNum;
|
||||
double ScoreThisQst;
|
||||
bool AnswerIsNotBlank;
|
||||
struct UsrAnswer
|
||||
{
|
||||
unsigned QstInd;
|
||||
unsigned AnsInd;
|
||||
} *UsrAnswers;
|
||||
unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]; // Indexes of all answers of this question
|
||||
bool AnswersUsr[Tst_MAX_OPTIONS_PER_QUESTION];
|
||||
|
||||
/***** Get user's answers *****/
|
||||
/* Query database */
|
||||
*NumQstsNotBlank = (unsigned)
|
||||
DB_QuerySELECT (&mysql_res,"can not get user's answers",
|
||||
"SELECT QstInd," // row[0]
|
||||
"AnsInd" // row[1]
|
||||
" FROM mch_answers"
|
||||
" WHERE MchCod=%ld AND UsrCod=%ld",
|
||||
Match->MchCod,Gbl.Usrs.Me.UsrDat.UsrCod);
|
||||
|
||||
/* Allocate memory for answers */
|
||||
if ((UsrAnswers = (struct UsrAnswer *) malloc (*NumQstsNotBlank *
|
||||
sizeof (struct UsrAnswer))) == NULL)
|
||||
Lay_NotEnoughMemoryExit ();
|
||||
|
||||
/* Get answers from database */
|
||||
for (NumQstNotBlank = 0;
|
||||
NumQstNotBlank < *NumQstsNotBlank;
|
||||
NumQstNotBlank++)
|
||||
{
|
||||
row = mysql_fetch_row (mysql_res);
|
||||
|
||||
/* Get question index (row[0]) */
|
||||
if ((LongNum = Str_ConvertStrCodToLongCod (row[0])) < 0)
|
||||
Lay_ShowErrorAndExit ("Wrong question index.");
|
||||
UsrAnswers[NumQstNotBlank].QstInd = (unsigned) LongNum;
|
||||
|
||||
/* Get answer index (row[1]) */
|
||||
if ((LongNum = Str_ConvertStrCodToLongCod (row[1])) < 0)
|
||||
Lay_ShowErrorAndExit ("Wrong answer index.");
|
||||
UsrAnswers[NumQstNotBlank].AnsInd = (unsigned) LongNum;
|
||||
}
|
||||
|
||||
/* Free structure that stores the query result */
|
||||
DB_FreeMySQLResult (&mysql_res);
|
||||
|
||||
/***** For each question in match... *****/
|
||||
for (NumQst = 0, *TotalScore = 0.0;
|
||||
NumQst < NumQsts;
|
||||
NumQst++)
|
||||
{
|
||||
QstInd = NumQst + 1;
|
||||
|
||||
/***** Get question code *****/
|
||||
QstCod = Gam_GetQstCodFromQstInd (Match->GamCod,QstInd);
|
||||
|
||||
/***** Get answers of test question from database *****/
|
||||
/* Query database */
|
||||
Gbl.Test.Answer.NumOptions = (unsigned)
|
||||
DB_QuerySELECT (&mysql_res,"can not get answers of a question",
|
||||
"SELECT Correct" // row[0]
|
||||
" FROM tst_answers"
|
||||
" WHERE QstCod=%ld ORDER BY AnsInd",
|
||||
QstCod);
|
||||
for (NumOpt = 0;
|
||||
NumOpt < Gbl.Test.Answer.NumOptions;
|
||||
NumOpt++)
|
||||
{
|
||||
/* Get next answer */
|
||||
row = mysql_fetch_row (mysql_res);
|
||||
|
||||
/* Assign correctness (row[0]) of this answer (this option) */
|
||||
Gbl.Test.Answer.Options[NumOpt].Correct = (row[0][0] == 'Y');
|
||||
}
|
||||
|
||||
/* Free structure that stores the query result */
|
||||
DB_FreeMySQLResult (&mysql_res);
|
||||
|
||||
/***** Get indexes for this question *****/
|
||||
// TODO: Answers should be shuffled?
|
||||
for (NumOpt = 0;
|
||||
NumOpt < Gbl.Test.Answer.NumOptions;
|
||||
NumOpt++)
|
||||
Indexes[NumOpt] = NumOpt;
|
||||
|
||||
/***** Get the user's answers for this question *****/
|
||||
for (NumOpt = 0;
|
||||
NumOpt < Gbl.Test.Answer.NumOptions;
|
||||
NumOpt++)
|
||||
AnswersUsr[NumOpt] = false;
|
||||
for (NumQstNotBlank = 0;
|
||||
NumQstNotBlank < *NumQstsNotBlank;
|
||||
NumQstNotBlank++)
|
||||
if (UsrAnswers[NumQstNotBlank].QstInd == QstInd)
|
||||
if (UsrAnswers[NumQstNotBlank].AnsInd < Gbl.Test.Answer.NumOptions)
|
||||
AnswersUsr[UsrAnswers[NumQstNotBlank].AnsInd] = true;
|
||||
|
||||
/***** Compute the total score of this question *****/
|
||||
Tst_ComputeScoreQst (Indexes,AnswersUsr,&ScoreThisQst,&AnswerIsNotBlank);
|
||||
|
||||
/***** Compute total score *****/
|
||||
*TotalScore += ScoreThisQst;
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/*** Get number of users who selected this answer and draw proportional bar **/
|
||||
/*****************************************************************************/
|
||||
|
|
|
@ -2638,7 +2638,7 @@ void Prj_GetDataOfProjectByCod (struct Project *Prj)
|
|||
{
|
||||
MYSQL_RES *mysql_res;
|
||||
MYSQL_ROW row;
|
||||
long NumLong;
|
||||
long LongNum;
|
||||
Prj_Proposal_t Proposal;
|
||||
|
||||
if (Prj->PrjCod > 0)
|
||||
|
@ -2693,9 +2693,9 @@ void Prj_GetDataOfProjectByCod (struct Project *Prj)
|
|||
Prj_NONPREASSIG;
|
||||
|
||||
/* Get if project is preassigned or not (row[6]) */
|
||||
NumLong = Str_ConvertStrCodToLongCod (row[6]);
|
||||
if (NumLong >= 0)
|
||||
Prj->NumStds = (unsigned) NumLong;
|
||||
LongNum = Str_ConvertStrCodToLongCod (row[6]);
|
||||
if (LongNum >= 0)
|
||||
Prj->NumStds = (unsigned) LongNum;
|
||||
else
|
||||
Prj->NumStds = 1;
|
||||
|
||||
|
|
300
swad_test.c
300
swad_test.c
|
@ -3284,7 +3284,8 @@ unsigned Tst_GetAnswersQst (long QstCod,MYSQL_RES **mysql_res,bool Shuffle)
|
|||
"Feedback," // row[2]
|
||||
"MedCod," // row[3]
|
||||
"Correct" // row[4]
|
||||
" FROM tst_answers WHERE QstCod=%ld ORDER BY %s",
|
||||
" FROM tst_answers"
|
||||
" WHERE QstCod=%ld ORDER BY %s",
|
||||
QstCod,
|
||||
Shuffle ? "RAND(NOW())" :
|
||||
"AnsInd");
|
||||
|
@ -3794,7 +3795,6 @@ static void Tst_WriteChoiceAnsAssessTest (struct UsrData *UsrDat,
|
|||
extern const char *Txt_TST_Answer_given_by_the_user;
|
||||
extern const char *Txt_TST_Answer_given_by_the_teachers;
|
||||
unsigned NumOpt;
|
||||
MYSQL_ROW row;
|
||||
char StrOneIndex[10 + 1];
|
||||
const char *Ptr;
|
||||
unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]; // Indexes of all answers of this question
|
||||
|
@ -3805,10 +3805,152 @@ static void Tst_WriteChoiceAnsAssessTest (struct UsrData *UsrDat,
|
|||
char *Class;
|
||||
char *Str;
|
||||
} Ans;
|
||||
unsigned NumOptTotInQst = 0;
|
||||
unsigned NumOptCorrInQst = 0;
|
||||
unsigned NumAnsGood = 0;
|
||||
unsigned NumAnsBad = 0;
|
||||
|
||||
/***** Get text and correctness of answers for this question
|
||||
from database (one row per answer) *****/
|
||||
Tst_GetChoiceAns (mysql_res);
|
||||
|
||||
/***** Get indexes for this question from string *****/
|
||||
for (NumOpt = 0, Ptr = Gbl.Test.StrIndexesOneQst[NumQst];
|
||||
NumOpt < Gbl.Test.Answer.NumOptions;
|
||||
NumOpt++)
|
||||
{
|
||||
Par_GetNextStrUntilSeparParamMult (&Ptr,StrOneIndex,10);
|
||||
if (sscanf (StrOneIndex,"%u",&(Indexes[NumOpt])) != 1)
|
||||
Lay_ShowErrorAndExit ("Wrong index of answer when assessing a test.");
|
||||
}
|
||||
|
||||
/***** Get the user's answers for this question from string *****/
|
||||
for (NumOpt = 0;
|
||||
NumOpt < Tst_MAX_OPTIONS_PER_QUESTION;
|
||||
NumOpt++)
|
||||
AnswersUsr[NumOpt] = false;
|
||||
for (NumOpt = 0, Ptr = Gbl.Test.StrAnswersOneQst[NumQst];
|
||||
NumOpt < Gbl.Test.Answer.NumOptions;
|
||||
NumOpt++)
|
||||
if (*Ptr)
|
||||
{
|
||||
Par_GetNextStrUntilSeparParamMult (&Ptr,StrOneIndex,10);
|
||||
if (sscanf (StrOneIndex,"%d",&AnsUsr) != 1)
|
||||
Lay_ShowErrorAndExit ("Bad user's answer.");
|
||||
if (AnsUsr < 0 || AnsUsr >= Tst_MAX_OPTIONS_PER_QUESTION)
|
||||
Lay_ShowErrorAndExit ("Bad user's answer.");
|
||||
AnswersUsr[AnsUsr] = true;
|
||||
}
|
||||
|
||||
/***** Compute the total score of this question *****/
|
||||
Tst_ComputeScoreQst (Indexes,AnswersUsr,ScoreThisQst,AnswerIsNotBlank);
|
||||
|
||||
/***** Start table *****/
|
||||
Tbl_StartTable (2);
|
||||
fprintf (Gbl.F.Out,"<tr>");
|
||||
Tst_WriteHeadUserCorrect (UsrDat);
|
||||
fprintf (Gbl.F.Out,"<td></td>"
|
||||
"<td></td>"
|
||||
"</tr>");
|
||||
|
||||
/***** Write answers (one row per answer) *****/
|
||||
for (NumOpt = 0;
|
||||
NumOpt < Gbl.Test.Answer.NumOptions;
|
||||
NumOpt++)
|
||||
{
|
||||
fprintf (Gbl.F.Out,"<tr>");
|
||||
|
||||
/* Draw icon depending on user's answer */
|
||||
if (AnswersUsr[Indexes[NumOpt]] == true) // This answer has been selected by the user
|
||||
{
|
||||
if (Gbl.Test.Config.Feedback == Tst_FEEDBACK_EACH_GOOD_BAD ||
|
||||
Gbl.Test.Config.Feedback == Tst_FEEDBACK_FULL_FEEDBACK)
|
||||
{
|
||||
if (Gbl.Test.Answer.Options[Indexes[NumOpt]].Correct)
|
||||
{
|
||||
Ans.Class = "ANS_OK";
|
||||
Ans.Str = "✓";
|
||||
}
|
||||
else
|
||||
{
|
||||
Ans.Class = "ANS_BAD";
|
||||
Ans.Str = "✗";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Ans.Class = "ANS_0";
|
||||
Ans.Str = "•";
|
||||
}
|
||||
fprintf (Gbl.F.Out,"<td class=\"%s CENTER_TOP\" title=\"%s\">%s</td>",
|
||||
Ans.Class,Txt_TST_Answer_given_by_the_user,Ans.Str);
|
||||
}
|
||||
else // This answer has NOT been selected by the user
|
||||
fprintf (Gbl.F.Out,"<td></td>");
|
||||
|
||||
/* Draw icon that indicates whether the answer is correct */
|
||||
if (Gbl.Test.Config.Feedback == Tst_FEEDBACK_EACH_GOOD_BAD ||
|
||||
Gbl.Test.Config.Feedback == Tst_FEEDBACK_FULL_FEEDBACK)
|
||||
{
|
||||
if (Gbl.Test.Answer.Options[Indexes[NumOpt]].Correct)
|
||||
fprintf (Gbl.F.Out,"<td class=\"ANS_0 CENTER_TOP\" title=\"%s\">•</td>",
|
||||
Txt_TST_Answer_given_by_the_teachers);
|
||||
else
|
||||
fprintf (Gbl.F.Out,"<td></td>");
|
||||
}
|
||||
else
|
||||
fprintf (Gbl.F.Out,"<td class=\"ANS_0 CENTER_TOP\">?</td>");
|
||||
|
||||
/* Answer letter (a, b, c,...) */
|
||||
fprintf (Gbl.F.Out,"<td class=\"ANS_TXT LEFT_TOP\">"
|
||||
"%c) "
|
||||
"</td>",
|
||||
'a' + (char) NumOpt);
|
||||
|
||||
/* Answer text and feedback */
|
||||
fprintf (Gbl.F.Out,"<td class=\"LEFT_TOP\">"
|
||||
"<div class=\"ANS_TXT\">"
|
||||
"%s",
|
||||
Gbl.Test.Answer.Options[Indexes[NumOpt]].Text);
|
||||
Med_ShowMedia (&Gbl.Test.Answer.Options[Indexes[NumOpt]].Media,
|
||||
"TEST_MED_SHOW_CONTAINER",
|
||||
"TEST_MED_SHOW");
|
||||
fprintf (Gbl.F.Out,"</div>");
|
||||
if (Gbl.Test.Config.Feedback == Tst_FEEDBACK_FULL_FEEDBACK)
|
||||
if (Gbl.Test.Answer.Options[Indexes[NumOpt]].Feedback)
|
||||
if (Gbl.Test.Answer.Options[Indexes[NumOpt]].Feedback[0])
|
||||
fprintf (Gbl.F.Out,"<div class=\"TEST_EXA_LIGHT\">"
|
||||
"%s"
|
||||
"</div>",
|
||||
Gbl.Test.Answer.Options[Indexes[NumOpt]].Feedback);
|
||||
fprintf (Gbl.F.Out,"</td>"
|
||||
"</tr>");
|
||||
}
|
||||
|
||||
/***** Write the total score of this question *****/
|
||||
if (Gbl.Test.Config.Feedback == Tst_FEEDBACK_EACH_RESULT ||
|
||||
Gbl.Test.Config.Feedback == Tst_FEEDBACK_EACH_GOOD_BAD ||
|
||||
Gbl.Test.Config.Feedback == Tst_FEEDBACK_FULL_FEEDBACK)
|
||||
{
|
||||
Tst_WriteScoreStart (4);
|
||||
if (*ScoreThisQst == 0.0)
|
||||
fprintf (Gbl.F.Out,"ANS_0");
|
||||
else if (*ScoreThisQst > 0.0)
|
||||
fprintf (Gbl.F.Out,"ANS_OK");
|
||||
else
|
||||
fprintf (Gbl.F.Out,"ANS_BAD");
|
||||
fprintf (Gbl.F.Out,"\">%.2lf",*ScoreThisQst);
|
||||
Tst_WriteScoreEnd ();
|
||||
}
|
||||
|
||||
/***** End table *****/
|
||||
Tbl_EndTable ();
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/************************ Get choice answer from row *************************/
|
||||
/*****************************************************************************/
|
||||
|
||||
void Tst_GetChoiceAns (MYSQL_RES *mysql_res)
|
||||
{
|
||||
unsigned NumOpt;
|
||||
MYSQL_ROW row;
|
||||
|
||||
/***** Get text and correctness of answers for this question
|
||||
from database (one row per answer) *****/
|
||||
|
@ -3859,118 +4001,31 @@ static void Tst_WriteChoiceAnsAssessTest (struct UsrData *UsrDat,
|
|||
/***** Assign correctness (row[4]) of this answer (this option) *****/
|
||||
Gbl.Test.Answer.Options[NumOpt].Correct = (row[4][0] == 'Y');
|
||||
}
|
||||
}
|
||||
|
||||
/***** Get indexes for this question from string *****/
|
||||
for (NumOpt = 0, Ptr = Gbl.Test.StrIndexesOneQst[NumQst];
|
||||
NumOpt < Gbl.Test.Answer.NumOptions;
|
||||
NumOpt++)
|
||||
{
|
||||
Par_GetNextStrUntilSeparParamMult (&Ptr,StrOneIndex,10);
|
||||
if (sscanf (StrOneIndex,"%u",&(Indexes[NumOpt])) != 1)
|
||||
Lay_ShowErrorAndExit ("Wrong index of answer when assessing a test.");
|
||||
}
|
||||
/*****************************************************************************/
|
||||
/********************* Compute the score of this question ********************/
|
||||
/*****************************************************************************/
|
||||
|
||||
/***** Get the user's answers for this question from string *****/
|
||||
for (NumOpt = 0;
|
||||
NumOpt < Tst_MAX_OPTIONS_PER_QUESTION;
|
||||
NumOpt++)
|
||||
AnswersUsr[NumOpt] = false;
|
||||
for (NumOpt = 0, Ptr = Gbl.Test.StrAnswersOneQst[NumQst];
|
||||
NumOpt < Gbl.Test.Answer.NumOptions;
|
||||
NumOpt++)
|
||||
if (*Ptr)
|
||||
{
|
||||
Par_GetNextStrUntilSeparParamMult (&Ptr,StrOneIndex,10);
|
||||
if (sscanf (StrOneIndex,"%d",&AnsUsr) != 1)
|
||||
Lay_ShowErrorAndExit ("Bad user's answer.");
|
||||
if (AnsUsr < 0 || AnsUsr >= Tst_MAX_OPTIONS_PER_QUESTION)
|
||||
Lay_ShowErrorAndExit ("Bad user's answer.");
|
||||
AnswersUsr[AnsUsr] = true;
|
||||
}
|
||||
void Tst_ComputeScoreQst (unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION], // Indexes of all answers of this question
|
||||
bool AnswersUsr[Tst_MAX_OPTIONS_PER_QUESTION],
|
||||
double *ScoreThisQst,bool *AnswerIsNotBlank)
|
||||
{
|
||||
unsigned NumOpt;
|
||||
unsigned NumOptTotInQst = 0;
|
||||
unsigned NumOptCorrInQst = 0;
|
||||
unsigned NumAnsGood = 0;
|
||||
unsigned NumAnsBad = 0;
|
||||
|
||||
/***** Start table *****/
|
||||
Tbl_StartTable (2);
|
||||
fprintf (Gbl.F.Out,"<tr>");
|
||||
Tst_WriteHeadUserCorrect (UsrDat);
|
||||
fprintf (Gbl.F.Out,"<td></td>"
|
||||
"<td></td>"
|
||||
"</tr>");
|
||||
|
||||
/***** Write answers (one row per answer) *****/
|
||||
/***** Compute the total score of this question *****/
|
||||
for (NumOpt = 0;
|
||||
NumOpt < Gbl.Test.Answer.NumOptions;
|
||||
NumOpt++)
|
||||
{
|
||||
fprintf (Gbl.F.Out,"<tr>");
|
||||
|
||||
/* Draw icon depending on user's answer */
|
||||
if (AnswersUsr[Indexes[NumOpt]] == true) // This answer has been selected by the user
|
||||
{
|
||||
if (Gbl.Test.Config.Feedback == Tst_FEEDBACK_EACH_GOOD_BAD ||
|
||||
Gbl.Test.Config.Feedback == Tst_FEEDBACK_FULL_FEEDBACK)
|
||||
{
|
||||
if (Gbl.Test.Answer.Options[Indexes[NumOpt]].Correct)
|
||||
{
|
||||
Ans.Class = "ANS_OK";
|
||||
Ans.Str = "✓";
|
||||
}
|
||||
else
|
||||
{
|
||||
Ans.Class = "ANS_BAD";
|
||||
Ans.Str = "✗";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Ans.Class = "ANS_0";
|
||||
Ans.Str = "•";
|
||||
}
|
||||
fprintf (Gbl.F.Out,"<td class=\"%s CENTER_TOP\" title=\"%s\">%s</td>",
|
||||
Ans.Class,Txt_TST_Answer_given_by_the_user,Ans.Str);
|
||||
}
|
||||
else // This answer has NOT been selected by the user
|
||||
fprintf (Gbl.F.Out,"<td></td>");
|
||||
|
||||
/* Draw icon that indicates whether the answer is correct */
|
||||
|
||||
if (Gbl.Test.Config.Feedback == Tst_FEEDBACK_EACH_GOOD_BAD ||
|
||||
Gbl.Test.Config.Feedback == Tst_FEEDBACK_FULL_FEEDBACK)
|
||||
{
|
||||
if (Gbl.Test.Answer.Options[Indexes[NumOpt]].Correct)
|
||||
fprintf (Gbl.F.Out,"<td class=\"ANS_0 CENTER_TOP\" title=\"%s\">•</td>",
|
||||
Txt_TST_Answer_given_by_the_teachers);
|
||||
else
|
||||
fprintf (Gbl.F.Out,"<td></td>");
|
||||
}
|
||||
else
|
||||
fprintf (Gbl.F.Out,"<td class=\"ANS_0 CENTER_TOP\">?</td>");
|
||||
|
||||
/* Answer letter (a, b, c,...) */
|
||||
fprintf (Gbl.F.Out,"<td class=\"ANS_TXT LEFT_TOP\">"
|
||||
"%c) "
|
||||
"</td>",
|
||||
'a' + (char) NumOpt);
|
||||
|
||||
/* Answer text and feedback */
|
||||
fprintf (Gbl.F.Out,"<td class=\"LEFT_TOP\">"
|
||||
"<div class=\"ANS_TXT\">"
|
||||
"%s",
|
||||
Gbl.Test.Answer.Options[Indexes[NumOpt]].Text);
|
||||
Med_ShowMedia (&Gbl.Test.Answer.Options[Indexes[NumOpt]].Media,
|
||||
"TEST_MED_SHOW_CONTAINER",
|
||||
"TEST_MED_SHOW");
|
||||
fprintf (Gbl.F.Out,"</div>");
|
||||
if (Gbl.Test.Config.Feedback == Tst_FEEDBACK_FULL_FEEDBACK)
|
||||
if (Gbl.Test.Answer.Options[Indexes[NumOpt]].Feedback)
|
||||
if (Gbl.Test.Answer.Options[Indexes[NumOpt]].Feedback[0])
|
||||
fprintf (Gbl.F.Out,"<div class=\"TEST_EXA_LIGHT\">"
|
||||
"%s"
|
||||
"</div>",
|
||||
Gbl.Test.Answer.Options[Indexes[NumOpt]].Feedback);
|
||||
fprintf (Gbl.F.Out,"</td>"
|
||||
"</tr>");
|
||||
|
||||
NumOptTotInQst++;
|
||||
if (Gbl.Test.Answer.Options[Indexes[NumOpt]].Correct)
|
||||
NumOptCorrInQst++;
|
||||
|
||||
if (AnswersUsr[Indexes[NumOpt]] == true) // This answer has been selected by the user
|
||||
{
|
||||
if (Gbl.Test.Answer.Options[Indexes[NumOpt]].Correct)
|
||||
|
@ -3978,14 +4033,10 @@ static void Tst_WriteChoiceAnsAssessTest (struct UsrData *UsrDat,
|
|||
else
|
||||
NumAnsBad++;
|
||||
}
|
||||
if (Gbl.Test.Answer.Options[Indexes[NumOpt]].Correct)
|
||||
NumOptCorrInQst++;
|
||||
}
|
||||
|
||||
/***** The answer is blank? *****/
|
||||
/* The answer is blank? */
|
||||
*AnswerIsNotBlank = NumAnsGood != 0 || NumAnsBad != 0;
|
||||
|
||||
/***** Compute and write the total score of this question *****/
|
||||
if (*AnswerIsNotBlank)
|
||||
{
|
||||
/* Compute the score */
|
||||
|
@ -4018,25 +4069,6 @@ static void Tst_WriteChoiceAnsAssessTest (struct UsrData *UsrDat,
|
|||
}
|
||||
else // Answer is blank
|
||||
*ScoreThisQst = 0.0;
|
||||
|
||||
/* Write the score */
|
||||
if (Gbl.Test.Config.Feedback == Tst_FEEDBACK_EACH_RESULT ||
|
||||
Gbl.Test.Config.Feedback == Tst_FEEDBACK_EACH_GOOD_BAD ||
|
||||
Gbl.Test.Config.Feedback == Tst_FEEDBACK_FULL_FEEDBACK)
|
||||
{
|
||||
Tst_WriteScoreStart (4);
|
||||
if (*ScoreThisQst == 0.0)
|
||||
fprintf (Gbl.F.Out,"ANS_0");
|
||||
else if (*ScoreThisQst > 0.0)
|
||||
fprintf (Gbl.F.Out,"ANS_OK");
|
||||
else
|
||||
fprintf (Gbl.F.Out,"ANS_BAD");
|
||||
fprintf (Gbl.F.Out,"\">%.2lf",*ScoreThisQst);
|
||||
Tst_WriteScoreEnd ();
|
||||
}
|
||||
|
||||
/***** End table *****/
|
||||
Tbl_EndTable ();
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
|
@ -158,6 +158,10 @@ void Tst_WriteAnswersMatchResult (long MchCod,unsigned QstInd,long QstCod,
|
|||
const char *Class,bool ShowResult);
|
||||
bool Tst_CheckIfQuestionIsValidForGame (long QstCod);
|
||||
void Tst_WriteAnsTF (char AnsTF);
|
||||
void Tst_GetChoiceAns (MYSQL_RES *mysql_res);
|
||||
void Tst_ComputeScoreQst (unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION],
|
||||
bool AnswersUsr[Tst_MAX_OPTIONS_PER_QUESTION],
|
||||
double *ScoreThisQst,bool *AnswerIsNotBlank);
|
||||
void Tst_CheckIfNumberOfAnswersIsOne (void);
|
||||
|
||||
unsigned long Tst_GetTagsQst (long QstCod,MYSQL_RES **mysql_res);
|
||||
|
|
Loading…
Reference in New Issue