Version19.10

This commit is contained in:
Antonio Cañas Vargas 2019-09-23 19:17:12 +02:00
parent dd1e1866bd
commit 2bff106d1e
7 changed files with 277 additions and 146 deletions

View File

@ -611,7 +611,7 @@ CREATE TABLE IF NOT EXISTS gam_games (
UNIQUE INDEX(GamCod), UNIQUE INDEX(GamCod),
INDEX(CrsCod)); INDEX(CrsCod));
-- --
-- Table mch_answers: stores the answers to the matches -- Table mch_answers: stores the users' answers to the matches
-- --
CREATE TABLE IF NOT EXISTS mch_answers (" CREATE TABLE IF NOT EXISTS mch_answers ("
MchCod INT NOT NULL," MchCod INT NOT NULL,"
@ -658,6 +658,14 @@ CREATE TABLE IF NOT EXISTS mch_players (
TS TIMESTAMP, TS TIMESTAMP,
UNIQUE INDEX(MchCod,UsrCod)); UNIQUE INDEX(MchCod,UsrCod));
-- --
-- Table mch_indexes: stores the order of answers in a match
--
CREATE TABLE IF NOT EXISTS mch_indexes (
MchCod INT NOT NULL,
QstInd INT NOT NULL,
Indexes TEXT NOT NULL,
UNIQUE INDEX(MchCod,QstInd));
--
-- Table mch_results: stores the current match results -- Table mch_results: stores the current match results
-- --
CREATE TABLE IF NOT EXISTS mch_results ( CREATE TABLE IF NOT EXISTS mch_results (

View File

@ -450,6 +450,8 @@ Lo de mutear anuncios, en principio prefiero hacer una opci
// TODO: En Eventos de asistencia, poner un icono de enlace a horario de la asignatura ¿y otro a grupos? // TODO: En Eventos de asistencia, poner un icono de enlace a horario de la asignatura ¿y otro a grupos?
// TODO: Reportado por Javier Fernández Baldomero. Un profesor debería poder cambiar la foto de un estudiante confirmado. Sale el icono, pero luego dice ue no hay permiso // TODO: Reportado por Javier Fernández Baldomero. Un profesor debería poder cambiar la foto de un estudiante confirmado. Sale el icono, pero luego dice ue no hay permiso
// TODO: URGENTE: Reportado por Javier Fernández Baldomero. Al pasar lista con SWADroid, los estudiantes sin foto no salen en la lista de alumnos de SWADroid.
/*****************************************************************************/ /*****************************************************************************/
/****************************** Public constants *****************************/ /****************************** Public constants *****************************/
/*****************************************************************************/ /*****************************************************************************/
@ -468,10 +470,14 @@ 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.9.3 (2019-09-23)" #define Log_PLATFORM_VERSION "SWAD 19.10 (2019-09-23)"
#define CSS_FILE "swad19.3.css" #define CSS_FILE "swad19.3.css"
#define JS_FILE "swad18.130.2.js" #define JS_FILE "swad18.130.2.js"
/* /*
Version 19.10: Sep 23, 2019 View matches results. Not finished. (245709 lines)
1 change necessary in database:
CREATE TABLE IF NOT EXISTS mch_indexes (MchCod INT NOT NULL,QstInd INT NOT NULL,Indexes TEXT NOT NULL,UNIQUE INDEX(MchCod,QstInd));
Version 19.9.3: Sep 23, 2019 Code refactoring in tables. (245597 lines) Version 19.9.3: Sep 23, 2019 Code refactoring in tables. (245597 lines)
Version 19.9.2: Sep 23, 2019 View matches results. Not finished. (245598 lines) Version 19.9.2: Sep 23, 2019 View matches results. Not finished. (245598 lines)
2 changes necessary in database: 2 changes necessary in database:

View File

@ -1433,6 +1433,24 @@ mysql> DESCRIBE mch_players;
"TS TIMESTAMP," "TS TIMESTAMP,"
"UNIQUE INDEX(MchCod,UsrCod))"); "UNIQUE INDEX(MchCod,UsrCod))");
/***** Table mch_indexes *****/
/*
mysql> DESCRIBE mch_indexes;
+---------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+---------+------+-----+---------+-------+
| MchCod | int(11) | NO | PRI | NULL | |
| QstInd | int(11) | NO | PRI | NULL | |
| Indexes | text | NO | | NULL | |
+---------+---------+------+-----+---------+-------+
3 rows in set (0.00 sec)
*/
DB_CreateTable ("CREATE TABLE IF NOT EXISTS mch_indexes ("
"MchCod INT NOT NULL,"
"QstInd INT NOT NULL,"
"Indexes TEXT NOT NULL," // Tst_MAX_BYTES_INDEXES_ONE_QST
"UNIQUE INDEX(MchCod,QstInd))");
/***** Table mch_results *****/ /***** Table mch_results *****/
/* /*
mysql> DESCRIBE mch_results; mysql> DESCRIBE mch_results;

View File

@ -41,6 +41,7 @@
#include "swad_notification.h" #include "swad_notification.h"
#include "swad_parameter.h" #include "swad_parameter.h"
#include "swad_string.h" #include "swad_string.h"
#include "swad_table.h"
#include "swad_user.h" #include "swad_user.h"
/*****************************************************************************/ /*****************************************************************************/

View File

@ -191,8 +191,8 @@ static bool Mch_GetIfMatchIsBeingPlayed (long MchCod);
static void Mch_RegisterMeAsPlayerInMatch (long MchCod); static void Mch_RegisterMeAsPlayerInMatch (long MchCod);
static void Mch_GetNumPlayers (struct Match *Match); static void Mch_GetNumPlayers (struct Match *Match);
static int Mch_GetQstAnsFromDB (long MchCod,unsigned QstInd); static int Mch_GetQstAnsFromDB (long MchCod,long UsrCod,unsigned QstInd);
static void Mch_ComputeScore (struct Match *Match,unsigned NumQsts, static void Mch_ComputeScore (struct Match *Match,unsigned NumQsts,long UsrCod,
unsigned *NumQstsNotBlank,double *TotalScore); unsigned *NumQstsNotBlank,double *TotalScore);
static unsigned Mch_GetNumUsrsWhoHaveChosenAns (long MchCod,unsigned QstInd,unsigned AnsInd); static unsigned Mch_GetNumUsrsWhoHaveChosenAns (long MchCod,unsigned QstInd,unsigned AnsInd);
@ -201,6 +201,7 @@ static void Mch_DrawBarNumUsrs (unsigned NumAnswerersAns,unsigned NumAnswerersQs
static void Mch_ShowHeaderMchResults (void); static void Mch_ShowHeaderMchResults (void);
static void Mch_ShowMchResults (Usr_MeOrOther_t MeOrOther); static void Mch_ShowMchResults (Usr_MeOrOther_t MeOrOther);
static void Mch_GetMatchResultQuestionsFromDB (long MchCod,long UsrCod);
static void Mch_GetMatchResultDataByMchCod (long MchCod,long UsrCod, static void Mch_GetMatchResultDataByMchCod (long MchCod,long UsrCod,
time_t TimeUTC[Dat_NUM_START_END_TIME], time_t TimeUTC[Dat_NUM_START_END_TIME],
unsigned *NumQsts, unsigned *NumQsts,
@ -1888,6 +1889,7 @@ static void Mch_ShowQuestionAndAnswersStd (struct Match *Match)
/***** Get student's answer to this question /***** Get student's answer to this question
(<0 ==> no answer) *****/ (<0 ==> no answer) *****/
StdAnsInd = Mch_GetQstAnsFromDB (Match->MchCod, StdAnsInd = Mch_GetQstAnsFromDB (Match->MchCod,
Gbl.Usrs.Me.UsrDat.UsrCod,
Match->Status.QstInd); Match->Status.QstInd);
/***** Get number of options in this question *****/ /***** Get number of options in this question *****/
@ -2240,32 +2242,32 @@ void Mch_RefreshMatchStd (void)
/**** Receive previous question answer in a match question from database *****/ /**** Receive previous question answer in a match question from database *****/
/*****************************************************************************/ /*****************************************************************************/
static int Mch_GetQstAnsFromDB (long MchCod,unsigned QstInd) static int Mch_GetQstAnsFromDB (long MchCod,long UsrCod,unsigned QstInd)
{ {
MYSQL_RES *mysql_res; MYSQL_RES *mysql_res;
MYSQL_ROW row; MYSQL_ROW row;
unsigned NumRows; unsigned NumRows;
int StdAnsInd = -1; // <0 ==> no answer selected int AnsInd = -1; // <0 ==> no answer selected
/***** Get student's answer *****/ /***** Get student's answer *****/
NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get student's answer to a match question", NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get student's answer to a match question",
"SELECT AnsInd FROM mch_answers" "SELECT AnsInd FROM mch_answers"
" WHERE MchCod=%ld AND UsrCod=%ld AND QstInd=%u", " WHERE MchCod=%ld"
MchCod, " AND UsrCod=%ld"
Gbl.Usrs.Me.UsrDat.UsrCod, " AND QstInd=%u",
QstInd); MchCod,UsrCod,QstInd);
if (NumRows) // Answer found... if (NumRows) // Answer found...
{ {
/***** Get answer index *****/ /***** Get answer index *****/
row = mysql_fetch_row (mysql_res); row = mysql_fetch_row (mysql_res);
if (sscanf (row[0],"%d",&StdAnsInd) != 1) if (sscanf (row[0],"%d",&AnsInd) != 1)
Lay_ShowErrorAndExit ("Error when getting student's answer to a match question."); Lay_ShowErrorAndExit ("Error when getting student's answer to a match question.");
} }
/***** Free structure that stores the query result *****/ /***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res); DB_FreeMySQLResult (&mysql_res);
return StdAnsInd; return AnsInd;
} }
/*****************************************************************************/ /*****************************************************************************/
@ -2311,7 +2313,7 @@ void Mch_ReceiveQstAnsFromStd (void)
/***** Get previous student's answer to this question /***** Get previous student's answer to this question
(<0 ==> no answer) *****/ (<0 ==> no answer) *****/
PreviousStdAnsInd = Mch_GetQstAnsFromDB (Match.MchCod,QstInd); PreviousStdAnsInd = Mch_GetQstAnsFromDB (Match.MchCod,Gbl.Usrs.Me.UsrDat.UsrCod,QstInd);
/***** Store student's answer *****/ /***** Store student's answer *****/
if (PreviousStdAnsInd == (int) StdAnsInd) if (PreviousStdAnsInd == (int) StdAnsInd)
@ -2329,7 +2331,8 @@ void Mch_ReceiveQstAnsFromStd (void)
/***** Update student's match result *****/ /***** Update student's match result *****/
NumQsts = Gam_GetNumQstsGame (Match.GamCod); NumQsts = Gam_GetNumQstsGame (Match.GamCod);
Mch_ComputeScore (&Match,NumQsts,&NumQstsNotBlank,&TotalScore); Mch_ComputeScore (&Match,NumQsts,Gbl.Usrs.Me.UsrDat.UsrCod,
&NumQstsNotBlank,&TotalScore);
Str_SetDecimalPointToUS (); // To print the floating point as a dot Str_SetDecimalPointToUS (); // To print the floating point as a dot
if (DB_QueryCOUNT ("can not get if match result exists", if (DB_QueryCOUNT ("can not get if match result exists",
@ -2374,113 +2377,73 @@ void Mch_ReceiveQstAnsFromStd (void)
/******************** Compute match score for a student **********************/ /******************** Compute match score for a student **********************/
/*****************************************************************************/ /*****************************************************************************/
static void Mch_ComputeScore (struct Match *Match,unsigned NumQsts, static void Mch_ComputeScore (struct Match *Match,unsigned NumQsts,long UsrCod,
unsigned *NumQstsNotBlank,double *TotalScore) unsigned *NumQstsNotBlank,double *TotalScore)
{ {
MYSQL_RES *mysql_res; MYSQL_RES *mysql_res;
MYSQL_ROW row; MYSQL_ROW row;
unsigned NumQst; unsigned NumQst;
unsigned NumQstNotBlank;
unsigned QstInd; unsigned QstInd;
unsigned NumOpt;
long QstCod;
long LongNum;
double ScoreThisQst; double ScoreThisQst;
bool AnswerIsNotBlank; bool AnswerIsNotBlank;
struct UsrAnswer long LongNum;
{ int AnsInd;
unsigned QstInd;
unsigned AnsInd;
} *UsrAnswers;
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 AnswersUsr[Tst_MAX_OPTIONS_PER_QUESTION]; bool AnswersUsr[Tst_MAX_OPTIONS_PER_QUESTION];
/***** Get user's answers *****/ /***** Get questions and answers of a match result *****/
/* Query database */ NumQsts = (unsigned)
*NumQstsNotBlank = (unsigned) DB_QuerySELECT (&mysql_res,"can not get questions and answers"
DB_QuerySELECT (&mysql_res,"can not get user's answers", " of a match result",
"SELECT QstInd," // row[0] "SELECT gam_questions.QstCod," // row[0]
"AnsInd" // row[1] "gam_questions.QstInd," // row[1]
" FROM mch_answers" "mch_indexes.Indexes" // row[2]
" WHERE MchCod=%ld AND UsrCod=%ld", " FROM mch_matches,gam_questions,mch_questions"
Match->MchCod,Gbl.Usrs.Me.UsrDat.UsrCod); " WHERE mch_matches.MchCod=%ld"
" AND mch_matches.GamCod=gam_questions.GamCod"
/* Allocate memory for answers */ " AND mch_matches.MchCod=mch_indexes.MchCod"
if ((UsrAnswers = (struct UsrAnswer *) malloc (*NumQstsNotBlank * " AND mch_matches.QstInd=mch_indexes.QstInd"
sizeof (struct UsrAnswer))) == NULL) " ORDER BY gam_questions.QstInd");
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 each question in match... *****/
for (NumQst = 0, *TotalScore = 0.0; for (NumQst = 0, *NumQstsNotBlank = 0, *TotalScore = 0.0;
NumQst < NumQsts; NumQst < NumQsts;
NumQst++) NumQst++)
{ {
QstInd = NumQst + 1; row = mysql_fetch_row (mysql_res);
/***** Get question code *****/ /* Get question code (row[0]) */
QstCod = Gam_GetQstCodFromQstInd (Match->GamCod,QstInd); if ((Gbl.Test.QstCodes[NumQst] = Str_ConvertStrCodToLongCod (row[0])) < 0)
Lay_ShowErrorAndExit ("Wrong code of question.");
/***** Get answers of test question from database *****/ /* Get question index (row[1]) */
/* Query database */ if ((LongNum = Str_ConvertStrCodToLongCod (row[0])) < 0)
Gbl.Test.Answer.NumOptions = (unsigned) Lay_ShowErrorAndExit ("Wrong code of question.");
DB_QuerySELECT (&mysql_res,"can not get answers of a question", QstInd = (unsigned) LongNum;
"SELECT Correct" // row[0]
" FROM tst_answers" /* Get indexes for this question (row[2]) */
" WHERE QstCod=%ld ORDER BY AnsInd", Str_Copy (Gbl.Test.StrIndexesOneQst[NumQst],row[1],
QstCod); Tst_MAX_BYTES_INDEXES_ONE_QST);
for (NumOpt = 0;
NumOpt < Gbl.Test.Answer.NumOptions; /***** Get answers selected by user for this question *****/
NumOpt++) AnsInd = Mch_GetQstAnsFromDB (Match->MchCod,UsrCod,QstInd);
if (AnsInd >= 0) // AnsInd >= 0 ==> answer selected
{ {
/* Get next answer */ snprintf (Gbl.Test.StrAnswersOneQst[NumQst],Tst_MAX_BYTES_ANSWERS_ONE_QST + 1,
row = mysql_fetch_row (mysql_res); "%d",AnsInd);
(*NumQstsNotBlank)++;
/* Assign correctness (row[0]) of this answer (this option) */
Gbl.Test.Answer.Options[NumOpt].Correct = (row[0][0] == 'Y');
} }
else // AnsInd < 0 ==> no answer selected
Gbl.Test.StrAnswersOneQst[NumQst][0] = '\0'; // Empty answer
/* Free structure that stores the query result */ /***** Get indexes for this question from string *****/
DB_FreeMySQLResult (&mysql_res); Tst_GetIndexesFromStr (Gbl.Test.StrIndexesOneQst[NumQst],Indexes);
/***** Get indexes for this question *****/ /***** Get the user's answers for this question from string *****/
// TODO: Answers should be shuffled? Tst_GetAnswersFromStr (Gbl.Test.StrAnswersOneQst[NumQst],AnswersUsr);
for (NumOpt = 0;
NumOpt < Gbl.Test.Answer.NumOptions;
NumOpt++)
Indexes[NumOpt] = NumOpt;
/***** Get the user's answers for this question *****/ /***** Get correct answers of test question from database *****/
for (NumOpt = 0; Tst_GetCorrectAnswersFromDB (Gbl.Test.QstCodes[NumQst]);
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 *****/ /***** Compute the total score of this question *****/
Tst_ComputeScoreQst (Indexes,AnswersUsr,&ScoreThisQst,&AnswerIsNotBlank); Tst_ComputeScoreQst (Indexes,AnswersUsr,&ScoreThisQst,&AnswerIsNotBlank);
@ -3068,21 +3031,30 @@ void Mch_ShowOneMchResult (void)
extern const char *Txt_Score; extern const char *Txt_Score;
extern const char *Txt_out_of_PART_OF_A_SCORE; extern const char *Txt_out_of_PART_OF_A_SCORE;
long MchCod; long MchCod;
Usr_MeOrOther_t MeOrOther = (Gbl.Action.Act == ActSeeOneMchResMe) ? Usr_ME :
Usr_OTHER;
time_t TimeUTC[Dat_NUM_START_END_TIME]; // Match result UTC date-time time_t TimeUTC[Dat_NUM_START_END_TIME]; // Match result UTC date-time
unsigned NumQstsNotBlank; unsigned NumQstsNotBlank;
double TotalScore; double TotalScore;
bool ShowPhoto; bool ShowPhoto;
char PhotoURL[PATH_MAX + 1]; char PhotoURL[PATH_MAX + 1];
bool ItsMe; bool ItsMe;
bool ICanViewTest; bool ICanViewResult;
bool ICanViewScore; bool ICanViewScore;
/***** Get the code of the test *****/ /***** Get form parameters *****/
/* Get match code */
if ((MchCod = Mch_GetParamMchCod ()) == -1L) if ((MchCod = Mch_GetParamMchCod ()) == -1L)
Lay_ShowErrorAndExit ("Code of match is missing."); Lay_ShowErrorAndExit ("Code of match is missing.");
/* Get user's code */
if (MeOrOther == Usr_OTHER)
Usr_GetParamOtherUsrCodEncrypted (&Gbl.Usrs.Other.UsrDat);
/***** Get test result data *****/ /***** Get test result data *****/
Mch_GetMatchResultDataByMchCod (MchCod,Gbl.Usrs.Other.UsrDat.UsrCod, Mch_GetMatchResultDataByMchCod (MchCod,
MeOrOther == Usr_ME ? Gbl.Usrs.Me.UsrDat.UsrCod :
Gbl.Usrs.Other.UsrDat.UsrCod,
TimeUTC, TimeUTC,
&Gbl.Test.NumQsts, &Gbl.Test.NumQsts,
&NumQstsNotBlank, &NumQstsNotBlank,
@ -3094,7 +3066,7 @@ void Mch_ShowOneMchResult (void)
switch (Gbl.Usrs.Me.Role.Logged) switch (Gbl.Usrs.Me.Role.Logged)
{ {
case Rol_STD: case Rol_STD:
ICanViewTest = ItsMe; ICanViewResult = ItsMe;
if (ItsMe) if (ItsMe)
{ {
Tst_GetConfigTstFromDB (); // To get feedback type Tst_GetConfigTstFromDB (); // To get feedback type
@ -3110,34 +3082,35 @@ void Mch_ShowOneMchResult (void)
switch (Gbl.Action.Act) switch (Gbl.Action.Act)
{ {
case ActSeeOneTstResMe: case ActSeeOneTstResMe:
ICanViewTest = ICanViewResult =
ICanViewScore = ItsMe; ICanViewScore = ItsMe;
break; break;
case ActSeeOneTstResOth: case ActSeeOneTstResOth:
ICanViewTest = ICanViewResult =
ICanViewScore = ItsMe || ICanViewScore = true;
Gbl.Test.AllowTeachers;
break; break;
default: default:
ICanViewTest = ICanViewResult =
ICanViewScore = false; ICanViewScore = false;
break; break;
} }
break; break;
case Rol_SYS_ADM: case Rol_SYS_ADM:
ICanViewTest = ICanViewResult =
ICanViewScore = true; ICanViewScore = true;
break; break;
default: default:
ICanViewTest = ICanViewResult =
ICanViewScore = false; ICanViewScore = false;
break; break;
} }
if (ICanViewTest) // I am allowed to view this test result if (ICanViewResult) // I am allowed to view this match result
{ {
/***** Get questions and user's answers of the test result from database *****/ /***** Get questions and user's answers of the match result from database *****/
// Tst_GetTestResultQuestionsFromDB (TstCod); // TODO: Change to matches results Mch_GetMatchResultQuestionsFromDB (MchCod,
MeOrOther == Usr_ME ? Gbl.Usrs.Me.UsrDat.UsrCod :
Gbl.Usrs.Other.UsrDat.UsrCod);
/***** Start box *****/ /***** Start box *****/
Box_StartBox (NULL,Txt_Match_result,NULL, Box_StartBox (NULL,Txt_Match_result,NULL,
@ -3151,7 +3124,7 @@ void Mch_ShowOneMchResult (void)
Tbl_StartTableWideMargin (10); Tbl_StartTableWideMargin (10);
/***** Header row *****/ /***** Header row *****/
/* Get data of the user who made the test */ /* Get data of the user who made the match */
if (!Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat,Usr_DONT_GET_PREFS)) if (!Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat,Usr_DONT_GET_PREFS))
Lay_ShowErrorAndExit ("User does not exists."); Lay_ShowErrorAndExit ("User does not exists.");
if (!Usr_CheckIfICanViewTst (&Gbl.Usrs.Other.UsrDat)) if (!Usr_CheckIfICanViewTst (&Gbl.Usrs.Other.UsrDat))
@ -3181,7 +3154,7 @@ void Mch_ShowOneMchResult (void)
fprintf (Gbl.F.Out,"</td>" fprintf (Gbl.F.Out,"</td>"
"</tr>"); "</tr>");
/* Test dates */ /* Match dates */
fprintf (Gbl.F.Out,"<tr>" fprintf (Gbl.F.Out,"<tr>"
"<td class=\"DAT_N RIGHT_TOP\">" "<td class=\"DAT_N RIGHT_TOP\">"
"%s:" "%s:"
@ -3250,6 +3223,70 @@ void Mch_ShowOneMchResult (void)
Lay_ShowErrorAndExit ("You can not view this match result."); Lay_ShowErrorAndExit ("You can not view this match result.");
} }
/*****************************************************************************/
/************ Get the questions of a match result from database **************/
/*****************************************************************************/
static void Mch_GetMatchResultQuestionsFromDB (long MchCod,long UsrCod)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumQsts;
unsigned NumQst;
long LongNum;
unsigned QstInd;
int AnsInd;
/***** Get questions and answers of a match result *****/
NumQsts = (unsigned)
DB_QuerySELECT (&mysql_res,"can not get questions and answers"
" of a match result",
"SELECT gam_questions.QstCod," // row[0]
"gam_questions.QstInd," // row[1]
"mch_indexes.Indexes" // row[2]
" FROM mch_matches,gam_questions,mch_questions"
" WHERE mch_matches.MchCod=%ld"
" AND mch_matches.GamCod=gam_questions.GamCod"
" AND mch_matches.MchCod=mch_indexes.MchCod"
" AND mch_matches.QstInd=mch_indexes.QstInd"
" ORDER BY gam_questions.QstInd");
for (NumQst = 0;
NumQst < NumQsts;
NumQst++)
{
row = mysql_fetch_row (mysql_res);
/* Get question code (row[0]) */
if ((Gbl.Test.QstCodes[NumQst] = Str_ConvertStrCodToLongCod (row[0])) < 0)
Lay_ShowErrorAndExit ("Wrong code of question.");
/* Get question index (row[1]) */
if ((LongNum = Str_ConvertStrCodToLongCod (row[0])) < 0)
Lay_ShowErrorAndExit ("Wrong code of question.");
QstInd = (unsigned) LongNum;
/* Get indexes for this question (row[2]) */
Str_Copy (Gbl.Test.StrIndexesOneQst[NumQst],row[1],
Tst_MAX_BYTES_INDEXES_ONE_QST);
/* Get answers selected by user for this question */
AnsInd = Mch_GetQstAnsFromDB (MchCod,UsrCod,QstInd);
if (AnsInd >= 0) // AnsInd >= 0 ==> answer selected
snprintf (Gbl.Test.StrAnswersOneQst[NumQst],Tst_MAX_BYTES_ANSWERS_ONE_QST + 1,
"%d",AnsInd);
else // AnsInd < 0 ==> no answer selected
Gbl.Test.StrAnswersOneQst[NumQst][0] = '\0'; // Empty answer
/* Replace each comma by a separator of multiple parameters */
/* In database commas are used as separators instead of special chars */
Par_ReplaceCommaBySeparatorMultiple (Gbl.Test.StrIndexesOneQst[NumQst]);
Par_ReplaceCommaBySeparatorMultiple (Gbl.Test.StrAnswersOneQst[NumQst]);
}
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
/*****************************************************************************/ /*****************************************************************************/
/************* Get data of a match result using its match code ***************/ /************* Get data of a match result using its match code ***************/
/*****************************************************************************/ /*****************************************************************************/

View File

@ -3285,7 +3285,8 @@ unsigned Tst_GetAnswersQst (long QstCod,MYSQL_RES **mysql_res,bool Shuffle)
"MedCod," // row[3] "MedCod," // row[3]
"Correct" // row[4] "Correct" // row[4]
" FROM tst_answers" " FROM tst_answers"
" WHERE QstCod=%ld ORDER BY %s", " WHERE QstCod=%ld"
" ORDER BY %s",
QstCod, QstCod,
Shuffle ? "RAND(NOW())" : Shuffle ? "RAND(NOW())" :
"AnsInd"); "AnsInd");
@ -3295,6 +3296,35 @@ unsigned Tst_GetAnswersQst (long QstCod,MYSQL_RES **mysql_res,bool Shuffle)
return (unsigned) NumRows; return (unsigned) NumRows;
} }
void Tst_GetCorrectAnswersFromDB (long QstCod)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumOpt;
/***** 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 and write the answers of a test question ***************/ /**************** Get and write the answers of a test question ***************/
/*****************************************************************************/ /*****************************************************************************/
@ -3795,10 +3825,7 @@ 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_user;
extern const char *Txt_TST_Answer_given_by_the_teachers; extern const char *Txt_TST_Answer_given_by_the_teachers;
unsigned NumOpt; unsigned NumOpt;
char StrOneIndex[10 + 1];
const char *Ptr;
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
int AnsUsr;
bool AnswersUsr[Tst_MAX_OPTIONS_PER_QUESTION]; bool AnswersUsr[Tst_MAX_OPTIONS_PER_QUESTION];
struct struct
{ {
@ -3811,32 +3838,10 @@ static void Tst_WriteChoiceAnsAssessTest (struct UsrData *UsrDat,
Tst_GetChoiceAns (mysql_res); Tst_GetChoiceAns (mysql_res);
/***** Get indexes for this question from string *****/ /***** Get indexes for this question from string *****/
for (NumOpt = 0, Ptr = Gbl.Test.StrIndexesOneQst[NumQst]; Tst_GetIndexesFromStr (Gbl.Test.StrIndexesOneQst[NumQst],Indexes);
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 *****/ /***** Get the user's answers for this question from string *****/
for (NumOpt = 0; Tst_GetAnswersFromStr (Gbl.Test.StrAnswersOneQst[NumQst],AnswersUsr);
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 *****/ /***** Compute the total score of this question *****/
Tst_ComputeScoreQst (Indexes,AnswersUsr,ScoreThisQst,AnswerIsNotBlank); Tst_ComputeScoreQst (Indexes,AnswersUsr,ScoreThisQst,AnswerIsNotBlank);
@ -4003,6 +4008,57 @@ void Tst_GetChoiceAns (MYSQL_RES *mysql_res)
} }
} }
/*****************************************************************************/
/********************* Get vector of indexes from string *********************/
/*****************************************************************************/
void Tst_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;
char StrOneIndex[10 + 1];
for (NumOpt = 0, Ptr = StrIndexesOneQst;
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 vector of user's answers from string *****************/
/*****************************************************************************/
void Tst_GetAnswersFromStr (const char StrAnswersOneQst[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1],
bool AnswersUsr[Tst_MAX_OPTIONS_PER_QUESTION])
{
unsigned NumOpt;
const char *Ptr;
char StrOneAnswer[10 + 1];
int AnsUsr;
for (NumOpt = 0;
NumOpt < Tst_MAX_OPTIONS_PER_QUESTION;
NumOpt++)
AnswersUsr[NumOpt] = false;
for (NumOpt = 0, Ptr = StrAnswersOneQst;
NumOpt < Gbl.Test.Answer.NumOptions;
NumOpt++)
if (*Ptr)
{
Par_GetNextStrUntilSeparParamMult (&Ptr,StrOneAnswer,10);
if (sscanf (StrOneAnswer,"%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 score of this question ********************/ /********************* Compute the score of this question ********************/
/*****************************************************************************/ /*****************************************************************************/

View File

@ -154,11 +154,16 @@ bool Tst_GetOneQuestionByCod (long QstCod,MYSQL_RES **mysql_res);
void Tst_WriteParamEditQst (void); void Tst_WriteParamEditQst (void);
unsigned Tst_GetNumAnswersQst (long QstCod); unsigned Tst_GetNumAnswersQst (long QstCod);
unsigned Tst_GetAnswersQst (long QstCod,MYSQL_RES **mysql_res,bool Shuffle); unsigned Tst_GetAnswersQst (long QstCod,MYSQL_RES **mysql_res,bool Shuffle);
void Tst_GetCorrectAnswersFromDB (long QstCod);
void Tst_WriteAnswersMatchResult (long MchCod,unsigned QstInd,long QstCod, void Tst_WriteAnswersMatchResult (long MchCod,unsigned QstInd,long QstCod,
const char *Class,bool ShowResult); const char *Class,bool ShowResult);
bool Tst_CheckIfQuestionIsValidForGame (long QstCod); bool Tst_CheckIfQuestionIsValidForGame (long QstCod);
void Tst_WriteAnsTF (char AnsTF); void Tst_WriteAnsTF (char AnsTF);
void Tst_GetChoiceAns (MYSQL_RES *mysql_res); void Tst_GetChoiceAns (MYSQL_RES *mysql_res);
void Tst_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 Tst_GetAnswersFromStr (const char StrAnswersOneQst[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1],
bool AnswersUsr[Tst_MAX_OPTIONS_PER_QUESTION]);
void Tst_ComputeScoreQst (unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION], void Tst_ComputeScoreQst (unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION],
bool AnswersUsr[Tst_MAX_OPTIONS_PER_QUESTION], bool AnswersUsr[Tst_MAX_OPTIONS_PER_QUESTION],
double *ScoreThisQst,bool *AnswerIsNotBlank); double *ScoreThisQst,bool *AnswerIsNotBlank);