2020-06-24 20:47:54 +02:00
|
|
|
|
// swad_match_print.c: matches prints in games using remote control
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
SWAD (Shared Workspace At a Distance),
|
|
|
|
|
is a web platform developed at the University of Granada (Spain),
|
|
|
|
|
and used to support university teaching.
|
|
|
|
|
|
|
|
|
|
This file is part of SWAD core.
|
2021-02-09 12:43:45 +01:00
|
|
|
|
Copyright (C) 1999-2021 Antonio Ca<EFBFBD>as Vargas
|
2020-06-24 20:47:54 +02:00
|
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU Affero General Public License as
|
|
|
|
|
published by the Free Software Foundation, either version 3 of the
|
|
|
|
|
License, or (at your option) any later version.
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU Affero General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************************* Headers ***********************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2021-11-26 01:53:31 +01:00
|
|
|
|
#include <math.h> // For fabs
|
|
|
|
|
|
2020-06-24 20:47:54 +02:00
|
|
|
|
#include "swad_database.h"
|
|
|
|
|
#include "swad_date.h"
|
2021-11-26 01:53:31 +01:00
|
|
|
|
#include "swad_error.h"
|
2020-06-24 20:47:54 +02:00
|
|
|
|
#include "swad_global.h"
|
|
|
|
|
#include "swad_match.h"
|
2021-09-26 12:46:51 +02:00
|
|
|
|
#include "swad_match_database.h"
|
2020-06-24 20:47:54 +02:00
|
|
|
|
#include "swad_match_print.h"
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************** External global variables from others modules ****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
extern struct Globals Gbl;
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************************** Private prototypes ****************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2021-11-26 01:53:31 +01:00
|
|
|
|
static void MchPrn_ComputeScore (struct MchPrn_Print *Print);
|
2021-09-26 12:46:51 +02:00
|
|
|
|
static void MchPrn_UpdateMatchPrintInDB (const struct MchPrn_Print *Print);
|
2020-06-24 20:47:54 +02:00
|
|
|
|
|
2021-11-26 01:53:31 +01:00
|
|
|
|
static void MchPrn_ListPrintsToFix (long MchCod);
|
|
|
|
|
|
2020-06-24 20:47:54 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/**************************** Reset match print ******************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void MchPrn_ResetPrint (struct MchPrn_Print *Print)
|
|
|
|
|
{
|
|
|
|
|
Print->MchCod = -1L;
|
|
|
|
|
Print->UsrCod = -1L;
|
2021-09-18 18:22:26 +02:00
|
|
|
|
Print->TimeUTC[Dat_STR_TIME] =
|
|
|
|
|
Print->TimeUTC[Dat_END_TIME] = (time_t) 0;
|
2020-06-24 20:47:54 +02:00
|
|
|
|
Print->NumQsts.All =
|
|
|
|
|
Print->NumQsts.NotBlank = 0;
|
|
|
|
|
Print->Score = 0.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********** Compute score and create/update my result in a match ************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void MchPrn_ComputeScoreAndUpdateMyMatchPrintInDB (long MchCod)
|
|
|
|
|
{
|
|
|
|
|
struct MchPrn_Print Print;
|
|
|
|
|
|
|
|
|
|
/***** Compute my match result *****/
|
|
|
|
|
MchPrn_ResetPrint (&Print);
|
|
|
|
|
Print.MchCod = MchCod;
|
|
|
|
|
Print.UsrCod = Gbl.Usrs.Me.UsrDat.UsrCod;
|
|
|
|
|
Mch_GetMatchQuestionsFromDB (&Print);
|
2021-11-26 01:53:31 +01:00
|
|
|
|
MchPrn_ComputeScore (&Print);
|
2020-06-24 20:47:54 +02:00
|
|
|
|
|
|
|
|
|
/***** Update my match result in database *****/
|
2021-09-26 12:46:51 +02:00
|
|
|
|
MchPrn_UpdateMatchPrintInDB (&Print);
|
2020-06-24 20:47:54 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-26 01:53:31 +01:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************** Compute match print score for a student *******************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void MchPrn_ComputeScore (struct MchPrn_Print *Print)
|
|
|
|
|
{
|
|
|
|
|
unsigned NumQst;
|
|
|
|
|
struct Qst_Question Question;
|
|
|
|
|
|
|
|
|
|
for (NumQst = 0, Print->Score = 0.0;
|
|
|
|
|
NumQst < Print->NumQsts.All;
|
|
|
|
|
NumQst++)
|
|
|
|
|
{
|
|
|
|
|
/***** Create test question *****/
|
|
|
|
|
Qst_QstConstructor (&Question);
|
|
|
|
|
Question.QstCod = Print->PrintedQuestions[NumQst].QstCod;
|
|
|
|
|
Question.Answer.Type = Qst_ANS_UNIQUE_CHOICE;
|
|
|
|
|
|
|
|
|
|
/***** Compute score for this answer ******/
|
|
|
|
|
TstPrn_ComputeAnswerScore (&Print->PrintedQuestions[NumQst],&Question);
|
|
|
|
|
|
|
|
|
|
/***** Update total score *****/
|
|
|
|
|
Print->Score += Print->PrintedQuestions[NumQst].Score;
|
|
|
|
|
|
|
|
|
|
/***** Destroy test question *****/
|
|
|
|
|
Qst_QstDestructor (&Question);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-24 20:47:54 +02:00
|
|
|
|
/*****************************************************************************/
|
2021-09-26 12:46:51 +02:00
|
|
|
|
/************************* Create/update match print *************************/
|
2020-06-24 20:47:54 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2021-09-26 12:46:51 +02:00
|
|
|
|
static void MchPrn_UpdateMatchPrintInDB (const struct MchPrn_Print *Print)
|
2020-06-24 20:47:54 +02:00
|
|
|
|
{
|
2021-09-26 12:46:51 +02:00
|
|
|
|
if (Mch_DB_CheckIfMatchPrintExists (Print)) // Match print exists
|
|
|
|
|
/* Update match print */
|
|
|
|
|
Mch_DB_UpdateMatchPrint (Print);
|
|
|
|
|
else // Match print doesn't exist
|
|
|
|
|
/* Create match print */
|
|
|
|
|
Mch_DB_CreateMatchPrint (Print);
|
2020-06-24 20:47:54 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********* Get data of a match print using match code and user code **********/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void MchPrn_GetMatchPrintDataByMchCodAndUsrCod (struct MchPrn_Print *Print)
|
|
|
|
|
{
|
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
MYSQL_ROW row;
|
|
|
|
|
Dat_StartEndTime_t StartEndTime;
|
|
|
|
|
|
|
|
|
|
/***** Make database query *****/
|
2021-09-26 12:46:51 +02:00
|
|
|
|
if (Mch_DB_GetMatchPrintData (&mysql_res,Print) == 1)
|
2020-06-24 20:47:54 +02:00
|
|
|
|
{
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
|
|
|
|
|
|
|
|
|
/* Get start time (row[0] and row[1] hold UTC date-times) */
|
2021-09-26 12:46:51 +02:00
|
|
|
|
for (StartEndTime = (Dat_StartEndTime_t) 0;
|
2020-06-24 20:47:54 +02:00
|
|
|
|
StartEndTime <= (Dat_StartEndTime_t) (Dat_NUM_START_END_TIME - 1);
|
|
|
|
|
StartEndTime++)
|
|
|
|
|
Print->TimeUTC[StartEndTime] = Dat_GetUNIXTimeFromStr (row[StartEndTime]);
|
|
|
|
|
|
|
|
|
|
/* Get number of questions (row[2]) */
|
|
|
|
|
if (sscanf (row[2],"%u",&Print->NumQsts.All) != 1)
|
|
|
|
|
Print->NumQsts.All = 0;
|
|
|
|
|
|
|
|
|
|
/* Get number of questions not blank (row[3]) */
|
|
|
|
|
if (sscanf (row[3],"%u",&Print->NumQsts.NotBlank) != 1)
|
|
|
|
|
Print->NumQsts.NotBlank = 0;
|
|
|
|
|
|
|
|
|
|
/* Get score (row[4]) */
|
|
|
|
|
Str_SetDecimalPointToUS (); // To get the decimal point as a dot
|
|
|
|
|
if (sscanf (row[4],"%lf",&Print->Score) != 1)
|
|
|
|
|
Print->Score = 0.0;
|
|
|
|
|
Str_SetDecimalPointToLocal (); // Return to local system
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
MchPrn_ResetPrint (Print);
|
|
|
|
|
|
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
}
|
2021-11-26 01:53:31 +01:00
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******** Recompute scores stored in match prints made on 2021-11-25 *********/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void MchPrn_PutLinkToFixMatchesPrintsScores (void)
|
|
|
|
|
{
|
|
|
|
|
Lay_PutContextualLinkIconText (ActFixMchSco,NULL,
|
|
|
|
|
NULL,NULL,
|
|
|
|
|
"recycle.svg",
|
|
|
|
|
"Recalcular puntuación partidas");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MchPrn_FixMatchesPrintsScores (void)
|
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_Matches;
|
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
MYSQL_ROW row;
|
|
|
|
|
unsigned NumMatches;
|
|
|
|
|
unsigned NumMatch;
|
|
|
|
|
unsigned UniqueId;
|
|
|
|
|
struct Mch_Match Match;
|
|
|
|
|
Dat_StartEndTime_t StartEndTime;
|
|
|
|
|
|
|
|
|
|
/***** Reset match *****/
|
|
|
|
|
Mch_ResetMatch (&Match);
|
|
|
|
|
|
|
|
|
|
Box_BoxBegin ("100%",Txt_Matches,
|
|
|
|
|
NULL,NULL,
|
|
|
|
|
NULL,Box_NOT_CLOSABLE);
|
|
|
|
|
|
|
|
|
|
/***** Get data of matches from database *****/
|
|
|
|
|
NumMatches = Mch_DB_GetMatchesBetweenDates (&mysql_res,
|
|
|
|
|
"2021-11-24 20:00:00", // From
|
|
|
|
|
"2021-11-25 16:00:00"); // To
|
|
|
|
|
|
|
|
|
|
/***** Begin table *****/
|
|
|
|
|
HTM_TABLE_BeginWidePadding (2);
|
|
|
|
|
|
|
|
|
|
/***** Write rows *****/
|
|
|
|
|
for (NumMatch = 0, UniqueId = 1;
|
|
|
|
|
NumMatch < NumMatches;
|
|
|
|
|
NumMatch++, UniqueId++)
|
|
|
|
|
{
|
|
|
|
|
Gbl.RowEvenOdd = NumMatch % 2;
|
|
|
|
|
|
|
|
|
|
/***** Get match data *****/
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
|
|
|
|
/*
|
|
|
|
|
row[0]: GamCod
|
|
|
|
|
row[1]: MchCod
|
|
|
|
|
row[2]: UsrCod
|
|
|
|
|
row[3]: UNIX_TIMESTAMP(StartTime)
|
|
|
|
|
row[4]: UNIX_TIMESTAMP(EndTime)
|
|
|
|
|
row[5]: Title
|
|
|
|
|
*/
|
|
|
|
|
/* Code of the game (row[0]) */
|
|
|
|
|
if ((Match.GamCod = Str_ConvertStrCodToLongCod (row[0])) <= 0)
|
|
|
|
|
Err_WrongGameExit ();
|
|
|
|
|
|
|
|
|
|
/* Code of the match (row[1]) */
|
|
|
|
|
if ((Match.MchCod = Str_ConvertStrCodToLongCod (row[1])) <= 0)
|
|
|
|
|
Err_WrongMatchExit ();
|
|
|
|
|
|
|
|
|
|
/* Get match teacher (row[2]) */
|
|
|
|
|
Match.UsrCod = Str_ConvertStrCodToLongCod (row[2]);
|
|
|
|
|
|
|
|
|
|
/* Get start/end times (row[3], row[4] hold start/end UTC times) */
|
|
|
|
|
for (StartEndTime = (Dat_StartEndTime_t) 0;
|
|
|
|
|
StartEndTime <= (Dat_StartEndTime_t) (Dat_NUM_START_END_TIME - 1);
|
|
|
|
|
StartEndTime++)
|
|
|
|
|
Match.TimeUTC[StartEndTime] = Dat_GetUNIXTimeFromStr (row[3 + StartEndTime]);
|
|
|
|
|
|
|
|
|
|
/* Get the title of the match (row[5]) */
|
|
|
|
|
if (row[5])
|
|
|
|
|
Str_Copy (Match.Title,row[5],sizeof (Match.Title) - 1);
|
|
|
|
|
else
|
|
|
|
|
Match.Title[0] = '\0';
|
|
|
|
|
|
|
|
|
|
/***** List match *****/
|
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
|
|
|
|
|
|
/* Game code */
|
|
|
|
|
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
|
|
|
|
|
HTM_Txt ("GamCod: ");
|
|
|
|
|
HTM_Long (Match.GamCod);
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
/* Match code */
|
|
|
|
|
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
|
|
|
|
|
HTM_Txt ("MchCod: ");
|
|
|
|
|
HTM_Long (Match.MchCod);
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
/* Match author */
|
|
|
|
|
Mch_ListOneOrMoreMatchesAuthor (&Match);
|
|
|
|
|
|
|
|
|
|
/* Start/end date/time */
|
|
|
|
|
Mch_ListOneOrMoreMatchesTimes (&Match,UniqueId);
|
|
|
|
|
|
|
|
|
|
/* Match title */
|
|
|
|
|
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
|
|
|
|
|
HTM_Txt (Match.Title);
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
/* Number of players who have played the match */
|
|
|
|
|
Mch_ListOneOrMoreMatchesNumPlayers (&Match);
|
|
|
|
|
|
|
|
|
|
/* Filling */
|
|
|
|
|
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
HTM_TR_End ();
|
|
|
|
|
|
|
|
|
|
/***** Match prints *****/
|
|
|
|
|
MchPrn_ListPrintsToFix (Match.MchCod);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** End table *****/
|
|
|
|
|
HTM_TABLE_End ();
|
|
|
|
|
|
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
|
|
|
|
|
/***** End box *****/
|
|
|
|
|
Box_BoxEnd ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void MchPrn_ListPrintsToFix (long MchCod)
|
|
|
|
|
{
|
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
MYSQL_ROW row;
|
|
|
|
|
unsigned NumPrints;
|
|
|
|
|
unsigned NumPrint;
|
|
|
|
|
struct MchPrn_Print Print;
|
|
|
|
|
double StoredScore;
|
|
|
|
|
|
|
|
|
|
/***** Get data of prints from database *****/
|
|
|
|
|
NumPrints = Mch_DB_GetPrintsInMatch (&mysql_res,MchCod);
|
|
|
|
|
|
|
|
|
|
/***** Write rows *****/
|
|
|
|
|
for (NumPrint = 0;
|
|
|
|
|
NumPrint < NumPrints;
|
|
|
|
|
NumPrint++)
|
|
|
|
|
{
|
|
|
|
|
/***** Get match print data *****/
|
|
|
|
|
Print.MchCod = MchCod;
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
|
|
|
|
/*
|
|
|
|
|
row[0]: UsrCod
|
|
|
|
|
row[1]: NumQsts
|
|
|
|
|
row[2]: NumQstsNotBlank
|
|
|
|
|
row[3]: Score
|
|
|
|
|
*/
|
|
|
|
|
/* Get student code (row[0]) */
|
|
|
|
|
Print.UsrCod = Str_ConvertStrCodToLongCod (row[0]);
|
|
|
|
|
|
|
|
|
|
/* Get number of questions (row[1]) */
|
|
|
|
|
if (sscanf (row[1],"%u",&Print.NumQsts.All) != 1)
|
|
|
|
|
Print.NumQsts.All = 0;
|
|
|
|
|
|
|
|
|
|
/* Get number of questions not blank (row[2]) */
|
|
|
|
|
if (sscanf (row[2],"%u",&Print.NumQsts.NotBlank) != 1)
|
|
|
|
|
Print.NumQsts.NotBlank = 0;
|
|
|
|
|
|
|
|
|
|
/* Get score (row[3]) */
|
|
|
|
|
Str_SetDecimalPointToUS (); // To get the decimal point as a dot
|
|
|
|
|
if (sscanf (row[3],"%lf",&StoredScore) != 1)
|
|
|
|
|
StoredScore = 0.0;
|
|
|
|
|
Str_SetDecimalPointToLocal (); // Return to local system
|
|
|
|
|
|
|
|
|
|
/***** Write row *****/
|
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
|
/* Indent */
|
|
|
|
|
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
/* Student */
|
|
|
|
|
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
|
|
|
|
|
Usr_WriteAuthor1Line (Print.UsrCod,false);
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
/* Number of questions */
|
|
|
|
|
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
|
|
|
|
|
HTM_Txt ("NumQsts: ");
|
|
|
|
|
HTM_Unsigned (Print.NumQsts.All);
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
/* Number of questions not blank */
|
|
|
|
|
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
|
|
|
|
|
HTM_Txt ("NumQstsNotBlank: ");
|
|
|
|
|
HTM_Unsigned (Print.NumQsts.NotBlank);
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
/* Stored score */
|
|
|
|
|
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
|
|
|
|
|
HTM_Txt ("Stored score: ");
|
|
|
|
|
HTM_Double (StoredScore);
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
/* Compute score */
|
|
|
|
|
Mch_GetMatchQuestionsFromDB (&Print);
|
|
|
|
|
MchPrn_ComputeScore (&Print);
|
|
|
|
|
|
|
|
|
|
/* Computed score */
|
|
|
|
|
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
|
|
|
|
|
HTM_Txt ("Computed score: ");
|
|
|
|
|
HTM_Double (Print.Score);
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
/* Store computed score? */
|
|
|
|
|
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
|
|
|
|
|
if (fabs (StoredScore-Print.Score) < 0.0000000001)
|
|
|
|
|
HTM_Txt ("=");
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
HTM_Txt ("!");
|
|
|
|
|
Mch_DB_UpdateMatchPrintScore (&Print);
|
|
|
|
|
}
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
HTM_TR_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
}
|