swad-core/swad_match.c

4477 lines
149 KiB
C
Raw Normal View History

2019-09-14 12:59:34 +02:00
// swad_match.c: matches 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
2019-09-14 12:59:34 +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 ***********************************/
/*****************************************************************************/
#define _GNU_SOURCE // For asprintf
#include <linux/limits.h> // For PATH_MAX
2019-12-29 12:39:00 +01:00
#include <stddef.h> // For NULL
2019-09-14 12:59:34 +02:00
#include <stdio.h> // For asprintf
#include <stdlib.h> // For free
2019-09-14 12:59:34 +02:00
#include <string.h> // For string functions
#include "swad_database.h"
2019-09-30 01:23:24 +02:00
#include "swad_date.h"
2019-09-14 12:59:34 +02:00
#include "swad_form.h"
#include "swad_game.h"
#include "swad_global.h"
2019-10-23 19:05:05 +02:00
#include "swad_HTML.h"
2019-09-14 12:59:34 +02:00
#include "swad_match.h"
2019-09-28 02:31:42 +02:00
#include "swad_match_result.h"
2019-09-14 12:59:34 +02:00
#include "swad_role.h"
#include "swad_setting.h"
#include "swad_test.h"
/*****************************************************************************/
/************** External global variables from others modules ****************/
/*****************************************************************************/
extern struct Globals Gbl;
/*****************************************************************************/
/***************************** Private constants *****************************/
/*****************************************************************************/
#define Mch_ICON_CLOSE "fas fa-times"
#define Mch_ICON_PLAY "fas fa-play"
#define Mch_ICON_PAUSE "fas fa-pause"
#define Mch_ICON_PREVIOUS "fas fa-step-backward"
#define Mch_ICON_NEXT "fas fa-step-forward"
#define Mch_ICON_RESULTS "fas fa-chart-bar"
2019-12-13 01:04:09 +01:00
#define Mch_COUNTDOWN_SECONDS_LARGE 60
#define Mch_COUNTDOWN_SECONDS_MEDIUM 30
#define Mch_COUNTDOWN_SECONDS_SMALL 10
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
/******************************* Private types *******************************/
/*****************************************************************************/
2019-12-03 02:46:46 +01:00
typedef enum
{
Mch_CHANGE_STATUS_BY_STUDENT,
Mch_REFRESH_STATUS_BY_SERVER,
} Mch_Update_t;
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
/***************************** Private constants *****************************/
/*****************************************************************************/
const char *Mch_ShowingStringsDB[Mch_NUM_SHOWING] =
{
2019-12-10 21:52:22 +01:00
[Mch_START ] = "start",
[Mch_STEM ] = "stem",
[Mch_ANSWERS] = "answers",
[Mch_RESULTS] = "results",
[Mch_END ] = "end",
2019-09-14 12:59:34 +02:00
};
2019-10-23 02:18:24 +02:00
#define Mch_MAX_COLS 4
2019-10-22 22:39:37 +02:00
#define Mch_NUM_COLS_DEFAULT 1
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
/***************************** Private variables *****************************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
long Mch_MchCodBeingPlayed; // Used to refresh game via AJAX
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
/***************************** Private prototypes ****************************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_SetMchCodBeingPlayed (long MchCod);
2019-09-25 00:42:13 +02:00
2020-04-08 03:06:45 +02:00
static void Mch_PutIconsInListOfMatches (void *Games);
static void Mch_PutIconToCreateNewMatch (struct Gam_Games *Games);
static void Mch_ListOneOrMoreMatches (struct Gam_Games *Games,
const struct Gam_Game *Game,
2019-09-14 12:59:34 +02:00
unsigned NumMatches,
MYSQL_RES *mysql_res);
2019-09-29 19:36:32 +02:00
static void Mch_ListOneOrMoreMatchesHeading (bool ICanEditMatches);
2019-09-29 18:44:40 +02:00
static bool Mch_CheckIfICanEditMatches (void);
2020-04-08 03:06:45 +02:00
static bool Mch_CheckIfICanEditThisMatch (const struct Mch_Match *Match);
2020-06-23 12:57:31 +02:00
static bool Mch_CheckIfVisibilityOfResultsCanBeChanged (const struct Mch_Match *Match);
2020-04-08 03:06:45 +02:00
static void Mch_ListOneOrMoreMatchesIcons (struct Gam_Games *Games,
2020-11-25 01:50:13 +01:00
const struct Mch_Match *Match,
const char *Anchor);
2020-04-08 03:06:45 +02:00
static void Mch_ListOneOrMoreMatchesAuthor (const struct Mch_Match *Match);
static void Mch_ListOneOrMoreMatchesTimes (const struct Mch_Match *Match,unsigned UniqueId);
2020-11-25 01:50:13 +01:00
static void Mch_ListOneOrMoreMatchesTitleGrps (const struct Mch_Match *Match,
const char *Anchor);
2020-04-08 03:06:45 +02:00
static void Mch_GetAndWriteNamesOfGrpsAssociatedToMatch (const struct Mch_Match *Match);
static void Mch_ListOneOrMoreMatchesNumPlayers (const struct Mch_Match *Match);
static void Mch_ListOneOrMoreMatchesStatus (struct Mch_Match *Match,unsigned NumQsts);
static void Mch_ListOneOrMoreMatchesResult (struct Gam_Games *Games,
const struct Mch_Match *Match);
static void Mch_ListOneOrMoreMatchesResultStd (struct Gam_Games *Games,
const struct Mch_Match *Match);
static void Mch_ListOneOrMoreMatchesResultTch (struct Gam_Games *Games,
const struct Mch_Match *Match);
2019-09-25 00:42:13 +02:00
2019-09-14 12:59:34 +02:00
static void Mch_GetMatchDataFromRow (MYSQL_RES *mysql_res,
2020-04-08 03:06:45 +02:00
struct Mch_Match *Match);
2019-09-14 12:59:34 +02:00
static Mch_Showing_t Mch_GetShowingFromStr (const char *Str);
2019-09-27 20:00:47 +02:00
static void Mch_RemoveMatchFromAllTables (long MchCod);
static void Mch_RemoveMatchFromTable (long MchCod,const char *TableName);
2019-09-28 01:12:53 +02:00
static void Mch_RemoveMatchesInGameFromTable (long GamCod,const char *TableName);
2020-05-18 22:59:07 +02:00
static void Mch_RemoveMatchesInCourseFromTable (long CrsCod,const char *TableName);
static void Mch_RemoveMatchesMadeByUsrFromTable (long UsrCod,const char *TableName);
static void Mch_RemoveMatchesMadeByUsrInCrsFromTable (long UsrCod,long CrsCod,const char *TableName);
2019-09-27 20:00:47 +02:00
2020-04-08 03:06:45 +02:00
static void Mch_PutParamsPlay (void *MchCod);
2019-09-30 01:10:57 +02:00
static void Mch_PutParamMchCod (long MchCod);
2019-09-14 12:59:34 +02:00
2020-11-25 01:50:13 +01:00
static void Mch_PutFormExistingMatch (struct Gam_Games *Games,
const struct Mch_Match *Match,
const char *Anchor);
2020-04-08 03:06:45 +02:00
static void Mch_PutFormNewMatch (const struct Gam_Game *Game);
2020-11-25 01:50:13 +01:00
static void Mch_ShowLstGrpsToEditMatch (long MchCod);
static void Mch_UpdateMatchTitleAndGrps (const struct Mch_Match *Match);
2019-09-14 12:59:34 +02:00
2020-11-25 01:50:13 +01:00
static long Mch_CreateMatch (long GamCod,char Title[Mch_MAX_BYTES_TITLE + 1]);
2019-09-23 21:39:54 +02:00
static void Mch_CreateIndexes (long GamCod,long MchCod);
static void Mch_ReorderAnswer (long MchCod,unsigned QstInd,
2020-03-25 01:36:22 +01:00
const struct Tst_Question *Question);
2019-09-14 12:59:34 +02:00
static void Mch_CreateGrps (long MchCod);
2020-04-08 03:06:45 +02:00
static void Mch_UpdateMatchStatusInDB (const struct Mch_Match *Match);
2019-09-14 12:59:34 +02:00
2020-04-08 03:06:45 +02:00
static void Mch_UpdateElapsedTimeInQuestion (const struct Mch_Match *Match);
static void Mch_GetElapsedTimeInQuestion (const struct Mch_Match *Match,
2019-09-14 12:59:34 +02:00
struct Time *Time);
2020-04-08 03:06:45 +02:00
static void Mch_GetElapsedTimeInMatch (const struct Mch_Match *Match,
2019-09-14 12:59:34 +02:00
struct Time *Time);
static void Mch_GetElapsedTime (unsigned NumRows,MYSQL_RES *mysql_res,
struct Time *Time);
2020-04-08 03:06:45 +02:00
static void Mch_SetMatchStatusToPrev (struct Mch_Match *Match);
static void Mch_SetMatchStatusToPrevQst (struct Mch_Match *Match);
static void Mch_SetMatchStatusToStart (struct Mch_Match *Match);
2019-09-17 15:42:41 +02:00
2020-04-08 03:06:45 +02:00
static void Mch_SetMatchStatusToNext (struct Mch_Match *Match);
static void Mch_SetMatchStatusToNextQst (struct Mch_Match *Match);
static void Mch_SetMatchStatusToEnd (struct Mch_Match *Match);
2019-09-17 15:42:41 +02:00
2020-04-08 03:06:45 +02:00
static void Mch_ShowMatchStatusForTch (struct Mch_Match *Match);
static void Mch_ShowMatchStatusForStd (struct Mch_Match *Match,Mch_Update_t Update);
2019-09-26 18:19:07 +02:00
2020-04-08 03:06:45 +02:00
static void Mch_ShowLeftColumnTch (struct Mch_Match *Match);
static void Mch_ShowRefreshablePartTch (struct Mch_Match *Match);
static void Mch_WriteElapsedTimeInMch (struct Mch_Match *Match);
static void Mch_WriteElapsedTimeInQst (struct Mch_Match *Match);
static void Mch_WriteNumRespondersQst (struct Mch_Match *Match);
static void Mch_PutFormCountdown (struct Mch_Match *Match,long Seconds,const char *Color);
static void Mch_PutCountdownAndHourglassIcon (struct Mch_Match *Match);
static void Mch_PutFormsCountdown (struct Mch_Match *Match);
2019-12-12 09:14:26 +01:00
2020-04-08 03:06:45 +02:00
static void Mch_ShowRightColumnTch (const struct Mch_Match *Match);
static void Mch_ShowLeftColumnStd (const struct Mch_Match *Match,
2019-12-03 02:46:46 +01:00
const struct Mch_UsrAnswer *UsrAnswer);
2020-04-08 03:06:45 +02:00
static void Mch_ShowRightColumnStd (struct Mch_Match *Match,
2019-12-03 02:46:46 +01:00
const struct Mch_UsrAnswer *UsrAnswer,
Mch_Update_t Update);
2019-09-26 18:19:07 +02:00
2020-04-08 03:06:45 +02:00
static void Mch_ShowNumQstInMch (const struct Mch_Match *Match);
static void Mch_PutMatchControlButtons (const struct Mch_Match *Match);
static void Mch_ShowFormColumns (const struct Mch_Match *Match);
2019-10-23 00:49:03 +02:00
static void Mch_PutParamNumCols (unsigned NumCols);
2020-04-08 03:06:45 +02:00
static void Mch_ShowMatchTitleTch (const struct Mch_Match *Match);
static void Mch_ShowMatchTitleStd (const struct Mch_Match *Match);
2020-03-12 13:53:37 +01:00
2020-04-08 03:06:45 +02:00
static void Mch_PutCheckboxResult (const struct Mch_Match *Match);
static void Mch_PutIfAnswered (const struct Mch_Match *Match,bool Answered);
static void Mch_PutIconToRemoveMyAnswer (const struct Mch_Match *Match);
static void Mch_ShowQuestionAndAnswersTch (const struct Mch_Match *Match);
static void Mch_WriteAnswersMatchResult (const struct Mch_Match *Match,
2020-06-17 02:31:42 +02:00
struct Tst_Question *Question,
2020-03-27 21:54:13 +01:00
const char *Class,bool ShowResult);
2020-08-28 14:45:30 +02:00
static void Mch_WriteChoiceAnsViewMatch (const struct Mch_Match *Match,
struct Tst_Question *Question,
const char *Class,bool ShowResult);
2020-04-08 03:06:45 +02:00
static bool Mch_ShowQuestionAndAnswersStd (const struct Mch_Match *Match,
2019-12-03 02:46:46 +01:00
const struct Mch_UsrAnswer *UsrAnswer,
Mch_Update_t Update);
2019-10-21 13:36:28 +02:00
2020-04-08 03:06:45 +02:00
static void Mch_ShowMatchScore (const struct Mch_Match *Match);
2019-12-14 01:31:32 +01:00
static void Mch_DrawEmptyScoreRow (unsigned NumRow,double MinScore,double MaxScore);
2019-10-21 15:07:00 +02:00
static void Mch_DrawScoreRow (double Score,double MinScore,double MaxScore,
2019-10-21 21:36:31 +02:00
unsigned NumRow,unsigned NumUsrs,unsigned MaxUsrs);
static const char *Mch_GetClassBorder (unsigned NumRow);
2019-09-14 12:59:34 +02:00
2019-09-24 01:41:51 +02:00
static void Mch_PutParamNumOpt (unsigned NumOpt);
static unsigned Mch_GetParamNumOpt (void);
2019-09-14 12:59:34 +02:00
2019-10-17 22:47:44 +02:00
static void Mch_PutBigButton (Act_Action_t NextAction,const char *Id,
long MchCod,const char *Icon,const char *Txt);
2019-12-13 00:36:01 +01:00
static void Mch_PutBigButtonHidden (const char *Icon);
2019-09-14 12:59:34 +02:00
static void Mch_PutBigButtonClose (void);
static void Mch_ShowWaitImage (const char *Txt);
static void Mch_RemoveOldPlayers (void);
static void Mch_UpdateMatchAsBeingPlayed (long MchCod);
static void Mch_SetMatchAsNotBeingPlayed (long MchCod);
static bool Mch_GetIfMatchIsBeingPlayed (long MchCod);
2020-04-08 03:06:45 +02:00
static void Mch_GetNumPlayers (struct Mch_Match *Match);
2019-09-14 12:59:34 +02:00
2020-05-18 14:34:31 +02:00
static void Mch_UpdateMyAnswerToMatchQuestion (const struct Mch_Match *Match,
const struct Mch_UsrAnswer *UsrAnswer);
2020-04-08 03:06:45 +02:00
static void Mch_RemoveMyAnswerToMatchQuestion (const struct Mch_Match *Match);
2019-12-03 02:46:46 +01:00
2020-05-18 14:34:31 +02:00
static unsigned Mch_GetNumUsrsWhoHavePlayedMch (long MchCod);
2019-09-14 12:59:34 +02:00
2020-04-08 03:06:45 +02:00
/*****************************************************************************/
/*************** Set/Get match code of the match being played ****************/
/*****************************************************************************/
static void Mch_SetMchCodBeingPlayed (long MchCod)
{
Mch_MchCodBeingPlayed = MchCod;
}
long Mch_GetMchCodBeingPlayed (void)
{
return Mch_MchCodBeingPlayed;
}
2019-09-29 22:38:32 +02:00
2020-04-23 23:09:28 +02:00
/*****************************************************************************/
/********************************* Reset match *******************************/
/*****************************************************************************/
void Mch_ResetMatch (struct Mch_Match *Match)
{
2020-05-05 20:09:28 +02:00
Dat_StartEndTime_t StartEndTime;
2020-04-23 23:09:28 +02:00
/***** Initialize to empty match *****/
Match->MchCod = -1L;
Match->GamCod = -1L;
Match->UsrCod = -1L;
2020-05-05 20:09:28 +02:00
for (StartEndTime = (Dat_StartEndTime_t) 0;
StartEndTime <= (Dat_StartEndTime_t) (Dat_NUM_START_END_TIME - 1);
StartEndTime++)
Match->TimeUTC[StartEndTime] = (time_t) 0;
2020-04-23 23:09:28 +02:00
Match->Title[0] = '\0';
Match->Status.QstInd = 0;
Match->Status.QstCod = -1L;
Match->Status.QstStartTimeUTC = (time_t) 0;
Match->Status.Showing = Mch_SHOWING_DEFAULT;
Match->Status.Countdown = 0;
Match->Status.NumCols = 1;
Match->Status.ShowQstResults = false;
Match->Status.ShowUsrResults = false;
Match->Status.Playing = false;
Match->Status.NumPlayers = 0;
};
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
/************************* List the matches of a game ************************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
void Mch_ListMatches (struct Gam_Games *Games,
struct Gam_Game *Game,
bool PutFormNewMatch)
2019-09-14 12:59:34 +02:00
{
extern const char *Hlp_ASSESSMENT_Games_matches;
extern const char *Txt_Matches;
char *SubQuery;
MYSQL_RES *mysql_res;
unsigned NumMatches;
/***** Get data of matches from database *****/
/* Fill subquery for game */
2019-09-29 17:33:39 +02:00
if (Gbl.Crs.Grps.WhichGrps == Grp_MY_GROUPS)
2019-09-14 12:59:34 +02:00
{
if (asprintf (&SubQuery," AND"
2020-04-28 03:14:25 +02:00
" (MchCod NOT IN"
" (SELECT MchCod"
" FROM mch_groups)"
" OR"
" MchCod IN"
" (SELECT mch_groups.MchCod"
" FROM grp_users,"
"mch_groups"
" WHERE grp_users.UsrCod=%ld"
" AND grp_users.GrpCod=mch_groups.GrpCod))",
2019-09-14 12:59:34 +02:00
Gbl.Usrs.Me.UsrDat.UsrCod) < 0)
Lay_NotEnoughMemoryExit ();
}
else // Gbl.Crs.Grps.WhichGrps == Grp_ALL_GROUPS
if (asprintf (&SubQuery,"%s","") < 0)
Lay_NotEnoughMemoryExit ();
/* Make query */
NumMatches = (unsigned)
DB_QuerySELECT (&mysql_res,"can not get matches",
"SELECT MchCod," // row[ 0]
"GamCod," // row[ 1]
"UsrCod," // row[ 2]
"UNIX_TIMESTAMP(StartTime)," // row[ 3]
"UNIX_TIMESTAMP(EndTime)," // row[ 4]
"Title," // row[ 5]
"QstInd," // row[ 6]
"QstCod," // row[ 7]
"Showing," // row[ 8]
"Countdown," // row[ 9]
"NumCols," // row[10]
"ShowQstResults," // row[11]
"ShowUsrResults" // row[12]
" FROM mch_matches"
" WHERE GamCod=%ld%s"
" ORDER BY MchCod",
Game->GamCod,
SubQuery);
2019-09-14 12:59:34 +02:00
/* Free allocated memory for subquery */
2019-11-06 19:45:20 +01:00
free (SubQuery);
2019-09-14 12:59:34 +02:00
2019-10-26 02:19:42 +02:00
/***** Begin box *****/
2020-04-08 03:06:45 +02:00
Games->GamCod = Game->GamCod;
2020-03-26 02:54:30 +01:00
Box_BoxBegin ("100%",Txt_Matches,
2020-04-08 03:06:45 +02:00
Mch_PutIconsInListOfMatches,Games,
2019-09-14 12:59:34 +02:00
Hlp_ASSESSMENT_Games_matches,Box_NOT_CLOSABLE);
2019-09-29 17:33:39 +02:00
/***** Select whether show only my groups or all groups *****/
2020-05-15 01:07:46 +02:00
switch (Gbl.Usrs.Me.Role.Logged)
2019-09-29 17:33:39 +02:00
{
2020-05-15 01:07:46 +02:00
case Rol_NET:
case Rol_TCH:
case Rol_SYS_ADM:
if (Gbl.Crs.Grps.NumGrps)
{
Set_BeginSettingsHead ();
2020-05-15 01:07:46 +02:00
Grp_ShowFormToSelWhichGrps (ActSeeGam,
Gam_PutParams,Games);
Set_EndSettingsHead ();
}
break;
default:
break;
2019-09-29 17:33:39 +02:00
}
2020-05-15 01:07:46 +02:00
/***** Show the table with the matches *****/
2019-09-14 12:59:34 +02:00
if (NumMatches)
2020-04-08 03:06:45 +02:00
Mch_ListOneOrMoreMatches (Games,Game,NumMatches,mysql_res);
2019-09-14 12:59:34 +02:00
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
/***** Put button to play a new match in this game *****/
switch (Gbl.Usrs.Me.Role.Logged)
{
case Rol_NET:
case Rol_TCH:
case Rol_SYS_ADM:
if (PutFormNewMatch)
2020-11-25 01:50:13 +01:00
Mch_PutFormNewMatch (Game); // Form to fill in data and start playing a new match
2019-09-14 12:59:34 +02:00
else
2020-04-08 03:06:45 +02:00
Gam_PutButtonNewMatch (Games,Game->GamCod); // Button to create a new match
2019-09-14 12:59:34 +02:00
break;
default:
break;
}
/***** End box *****/
2019-10-25 22:48:34 +02:00
Box_BoxEnd ();
2019-09-14 12:59:34 +02:00
}
/*****************************************************************************/
/********************** Get match data using its code ************************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
void Mch_GetDataOfMatchByCod (struct Mch_Match *Match)
2019-09-14 12:59:34 +02:00
{
MYSQL_RES *mysql_res;
unsigned NumRows;
2019-09-14 12:59:34 +02:00
/***** Get data of match from database *****/
2020-05-05 20:09:28 +02:00
NumRows = (unsigned)
DB_QuerySELECT (&mysql_res,"can not get matches",
"SELECT MchCod," // row[ 0]
"GamCod," // row[ 1]
"UsrCod," // row[ 2]
"UNIX_TIMESTAMP(StartTime)," // row[ 3]
"UNIX_TIMESTAMP(EndTime)," // row[ 4]
"Title," // row[ 5]
"QstInd," // row[ 6]
"QstCod," // row[ 7]
"Showing," // row[ 8]
"Countdown," // row[ 9]
"NumCols," // row[10]
"ShowQstResults," // row[11]
"ShowUsrResults" // row[12]
" FROM mch_matches"
" WHERE MchCod=%ld"
" AND GamCod IN" // Extra check
" (SELECT GamCod"
" FROM gam_games"
" WHERE CrsCod='%ld')",
Match->MchCod,
Gbl.Hierarchy.Crs.CrsCod);
2019-09-14 12:59:34 +02:00
if (NumRows) // Match found...
2020-05-05 20:09:28 +02:00
/* Get match data from row */
2019-09-14 12:59:34 +02:00
Mch_GetMatchDataFromRow (mysql_res,Match);
else
/* Initialize to empty match */
2020-05-05 20:09:28 +02:00
Mch_ResetMatch (Match);
2019-09-14 12:59:34 +02:00
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
2019-12-07 02:12:13 +01:00
/*****************************************************************************/
/****************** Put icons in list of matches of a game *******************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_PutIconsInListOfMatches (void *Games)
2019-12-07 02:12:13 +01:00
{
2020-03-26 02:54:30 +01:00
bool ICanEditMatches;
2019-12-07 02:12:13 +01:00
2020-04-08 03:06:45 +02:00
if (Games)
2020-03-26 02:54:30 +01:00
{
/***** Put icon to create a new match in current game *****/
ICanEditMatches = Mch_CheckIfICanEditMatches ();
if (ICanEditMatches)
2020-04-08 03:06:45 +02:00
Mch_PutIconToCreateNewMatch ((struct Gam_Games *) Games);
2020-03-26 02:54:30 +01:00
}
2019-12-07 02:12:13 +01:00
}
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
2019-09-29 17:33:39 +02:00
/********************* Put icon to create a new match ************************/
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_PutIconToCreateNewMatch (struct Gam_Games *Games)
2019-09-14 12:59:34 +02:00
{
extern const char *Txt_New_match;
2019-09-29 17:33:39 +02:00
/***** Put form to create a new match *****/
2020-03-26 02:54:30 +01:00
Ico_PutContextualIconToAdd (ActReqNewMch,Mch_NEW_MATCH_SECTION_ID,
2020-04-08 03:06:45 +02:00
Gam_PutParams,Games,
2019-09-14 12:59:34 +02:00
Txt_New_match);
}
/*****************************************************************************/
/*********************** List game matches for edition ***********************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_ListOneOrMoreMatches (struct Gam_Games *Games,
const struct Gam_Game *Game,
2019-09-14 12:59:34 +02:00
unsigned NumMatches,
MYSQL_RES *mysql_res)
2019-09-25 00:42:13 +02:00
{
unsigned NumMatch;
unsigned UniqueId;
2020-04-08 03:06:45 +02:00
struct Mch_Match Match;
2020-11-25 01:50:13 +01:00
char *Anchor;
2019-09-29 19:36:32 +02:00
bool ICanEditMatches = Mch_CheckIfICanEditMatches ();
2019-09-25 00:42:13 +02:00
2020-04-28 12:34:56 +02:00
/***** Trivial check *****/
if (!NumMatches)
return;
2020-04-23 23:09:28 +02:00
/***** Reset match *****/
Mch_ResetMatch (&Match);
2019-09-25 00:42:13 +02:00
/***** Write the heading *****/
2019-12-08 16:46:25 +01:00
HTM_TABLE_BeginWidePadding (2);
2019-09-29 19:36:32 +02:00
Mch_ListOneOrMoreMatchesHeading (ICanEditMatches);
2019-09-25 00:42:13 +02:00
/***** Write rows *****/
for (NumMatch = 0, UniqueId = 1;
NumMatch < NumMatches;
NumMatch++, UniqueId++)
{
Gbl.RowEvenOdd = NumMatch % 2;
/***** Get match data from row *****/
Mch_GetMatchDataFromRow (mysql_res,&Match);
2019-12-05 19:54:28 +01:00
if (Mch_CheckIfICanPlayThisMatchBasedOnGrps (&Match))
{
2020-11-25 01:50:13 +01:00
/***** Build anchor string *****/
if (asprintf (&Anchor,"mch_%ld",Match.MchCod) < 0)
Lay_NotEnoughMemoryExit ();
/***** First row for this match with match data ****/
/* Begin first row */
2019-12-05 19:54:28 +01:00
HTM_TR_Begin (NULL);
2019-09-25 00:42:13 +02:00
2019-12-05 19:54:28 +01:00
/* Icons */
if (ICanEditMatches)
2020-11-25 01:50:13 +01:00
Mch_ListOneOrMoreMatchesIcons (Games,&Match,Anchor);
2019-09-25 00:42:13 +02:00
2019-12-05 19:54:28 +01:00
/* Match player */
Mch_ListOneOrMoreMatchesAuthor (&Match);
2019-09-25 00:42:13 +02:00
2019-12-05 19:54:28 +01:00
/* Start/end date/time */
Mch_ListOneOrMoreMatchesTimes (&Match,UniqueId);
2019-09-25 00:42:13 +02:00
2019-12-05 19:54:28 +01:00
/* Title and groups */
2020-11-25 01:50:13 +01:00
Mch_ListOneOrMoreMatchesTitleGrps (&Match,Anchor);
2019-09-25 00:42:13 +02:00
2020-05-18 14:34:31 +02:00
/* Number of players who have played the match */
2019-12-05 19:54:28 +01:00
Mch_ListOneOrMoreMatchesNumPlayers (&Match);
2019-09-25 00:42:13 +02:00
2019-12-05 19:54:28 +01:00
/* Match status */
Mch_ListOneOrMoreMatchesStatus (&Match,Game->NumQsts);
2019-09-25 00:42:13 +02:00
2019-12-05 19:54:28 +01:00
/* Match result visible? */
2020-04-08 03:06:45 +02:00
Mch_ListOneOrMoreMatchesResult (Games,&Match);
2020-05-05 20:09:28 +02:00
2020-11-25 01:50:13 +01:00
/* End first row */
HTM_TR_End ();
/***** Second row for this match used for edition ****/
2020-11-25 22:50:35 +01:00
if (Gbl.Action.Act == ActEdiMch && // Editing...
Match.MchCod == Games->MchCod.Selected) // ...this match
2020-11-25 01:50:13 +01:00
/***** Check if I can edit this match *****/
if (Mch_CheckIfICanEditThisMatch (&Match))
{
/* Begin second row */
HTM_TR_Begin (NULL);
/* Form to edit match */
HTM_TD_Begin ("colspan=\"8\" class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
Mch_PutFormExistingMatch (Games,&Match,Anchor); // Form to fill in data and edit this match
HTM_TD_End ();
/* End second row */
HTM_TR_End ();
}
/***** Free anchor string *****/
free (Anchor);
2019-12-05 19:54:28 +01:00
}
2019-09-25 00:42:13 +02:00
}
/***** End table *****/
2019-10-23 19:05:05 +02:00
HTM_TABLE_End ();
2019-09-25 00:42:13 +02:00
}
/*****************************************************************************/
/***************** Put a column for match start and end times ****************/
/*****************************************************************************/
2019-09-29 19:36:32 +02:00
static void Mch_ListOneOrMoreMatchesHeading (bool ICanEditMatches)
2019-09-14 12:59:34 +02:00
{
extern const char *Txt_ROLES_SINGUL_Abc[Rol_NUM_ROLES][Usr_NUM_SEXS];
extern const char *Txt_START_END_TIME[Dat_NUM_START_END_TIME];
extern const char *Txt_Match;
2019-09-17 20:44:29 +02:00
extern const char *Txt_Players;
2019-09-14 12:59:34 +02:00
extern const char *Txt_Status;
2020-02-19 00:45:26 +01:00
extern const char *Txt_Results;
2019-09-14 12:59:34 +02:00
2019-09-29 18:44:40 +02:00
/***** Start row *****/
2019-10-23 19:05:05 +02:00
HTM_TR_Begin (NULL);
2019-09-29 18:44:40 +02:00
/***** Column for icons *****/
2019-09-29 19:36:32 +02:00
if (ICanEditMatches)
2019-10-23 19:05:05 +02:00
HTM_TH_Empty (1);
2019-09-29 18:44:40 +02:00
/***** The rest of columns *****/
2019-10-23 19:05:05 +02:00
HTM_TH (1,1,"LT",Txt_ROLES_SINGUL_Abc[Rol_TCH][Usr_SEX_UNKNOWN]);
HTM_TH (1,1,"LT",Txt_START_END_TIME[Gam_ORDER_BY_START_DATE]);
2020-03-30 13:21:11 +02:00
HTM_TH (1,1,"LT",Txt_START_END_TIME[Gam_ORDER_BY_END_DATE ]);
2019-10-23 19:05:05 +02:00
HTM_TH (1,1,"LT",Txt_Match);
HTM_TH (1,1,"RT",Txt_Players);
HTM_TH (1,1,"CT",Txt_Status);
2020-02-19 00:45:26 +01:00
HTM_TH (1,1,"CT",Txt_Results);
2019-09-29 18:44:40 +02:00
/***** End row *****/
2019-10-23 19:05:05 +02:00
HTM_TR_End ();
2019-09-29 18:44:40 +02:00
}
/*****************************************************************************/
2019-09-29 23:40:33 +02:00
/*********************** Check if I can edit matches *************************/
2019-09-29 18:44:40 +02:00
/*****************************************************************************/
static bool Mch_CheckIfICanEditMatches (void)
{
switch (Gbl.Usrs.Me.Role.Logged)
{
case Rol_NET:
case Rol_TCH:
case Rol_SYS_ADM:
return true;
default:
return false;
}
2019-09-25 00:42:13 +02:00
}
2019-09-14 12:59:34 +02:00
2019-09-29 23:40:33 +02:00
/*****************************************************************************/
2019-09-30 01:10:57 +02:00
/***************** Check if I can edit (remove/resume) a match ***************/
2019-09-29 23:40:33 +02:00
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static bool Mch_CheckIfICanEditThisMatch (const struct Mch_Match *Match)
2019-09-29 23:40:33 +02:00
{
switch (Gbl.Usrs.Me.Role.Logged)
{
case Rol_NET:
return (Match->UsrCod == Gbl.Usrs.Me.UsrDat.UsrCod); // Only if I am the creator
case Rol_TCH:
case Rol_SYS_ADM:
return true;
default:
return false;
}
}
2020-06-23 12:57:31 +02:00
/*****************************************************************************/
/*********** Check if visibility of match results can be changed *************/
/*****************************************************************************/
static bool Mch_CheckIfVisibilityOfResultsCanBeChanged (const struct Mch_Match *Match)
{
if (Match->Status.ShowUsrResults || // Results are currently visible
Match->Status.Showing == Mch_END) // Match has finished
if (Mch_CheckIfICanEditThisMatch (Match))
return true;
return false;
}
2019-09-25 09:29:44 +02:00
/*****************************************************************************/
/************************* Put a column for icons ****************************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_ListOneOrMoreMatchesIcons (struct Gam_Games *Games,
2020-11-25 01:50:13 +01:00
const struct Mch_Match *Match,
const char *Anchor)
2019-09-25 09:29:44 +02:00
{
2019-10-23 19:05:05 +02:00
HTM_TD_Begin ("class=\"BT%u\"",Gbl.RowEvenOdd);
2019-09-25 09:29:44 +02:00
2019-09-30 01:10:57 +02:00
if (Mch_CheckIfICanEditThisMatch (Match))
2019-09-29 23:40:33 +02:00
{
2020-11-25 01:50:13 +01:00
Games->GamCod = Match->GamCod;
Games->MchCod.Current = Match->MchCod;
2020-10-13 22:34:31 +02:00
/***** Put icon to remove the match *****/
Ico_PutContextualIconToRemove (ActReqRemMch,NULL,
Mch_PutParamsEdit,Games);
/***** Put icon to edit the match *****/
2020-11-25 01:50:13 +01:00
Ico_PutContextualIconToEdit (ActEdiMch,Anchor,
2020-10-13 22:34:31 +02:00
Mch_PutParamsEdit,Games);
2019-09-29 23:40:33 +02:00
}
else
Ico_PutIconRemovalNotAllowed ();
2019-09-25 09:29:44 +02:00
2019-10-23 19:05:05 +02:00
HTM_TD_End ();
2019-09-25 09:29:44 +02:00
}
/*****************************************************************************/
/************* Put a column for teacher who created the match ****************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_ListOneOrMoreMatchesAuthor (const struct Mch_Match *Match)
2019-09-25 09:29:44 +02:00
{
/***** Match author (teacher) *****/
2019-10-23 19:05:05 +02:00
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
2019-09-25 09:29:44 +02:00
Usr_WriteAuthor1Line (Match->UsrCod,false);
2019-10-23 19:05:05 +02:00
HTM_TD_End ();
2019-09-25 09:29:44 +02:00
}
2019-09-25 00:42:13 +02:00
/*****************************************************************************/
/***************** Put a column for match start and end times ****************/
/*****************************************************************************/
2019-09-14 12:59:34 +02:00
2020-04-08 03:06:45 +02:00
static void Mch_ListOneOrMoreMatchesTimes (const struct Mch_Match *Match,unsigned UniqueId)
2019-09-25 00:42:13 +02:00
{
Dat_StartEndTime_t StartEndTime;
2019-11-01 22:53:39 +01:00
char *Id;
2019-09-14 12:59:34 +02:00
2019-12-15 20:02:34 +01:00
for (StartEndTime = (Dat_StartEndTime_t) 0;
2019-09-25 00:42:13 +02:00
StartEndTime <= (Dat_StartEndTime_t) (Dat_NUM_START_END_TIME - 1);
StartEndTime++)
{
2019-11-01 22:53:39 +01:00
if (asprintf (&Id,"mch_time_%u_%u",(unsigned) StartEndTime,UniqueId) < 0)
Lay_NotEnoughMemoryExit ();
HTM_TD_Begin ("id=\"%s\" class=\"%s LT COLOR%u\"",
Id,
2019-12-10 21:52:22 +01:00
Match->Status.Showing == Mch_END ? "DATE_RED" :
"DATE_GREEN",
2019-10-10 23:14:13 +02:00
Gbl.RowEvenOdd);
2019-11-01 23:35:55 +01:00
Dat_WriteLocalDateHMSFromUTC (Id,Match->TimeUTC[StartEndTime],
2019-11-02 12:10:58 +01:00
Gbl.Prefs.DateFormat,Dat_SEPARATOR_BREAK,
2019-11-02 11:45:41 +01:00
true,true,true,0x7);
2019-10-23 19:05:05 +02:00
HTM_TD_End ();
2019-11-06 19:45:20 +01:00
free (Id);
2019-09-25 00:42:13 +02:00
}
}
2019-09-14 12:59:34 +02:00
2019-09-25 00:42:13 +02:00
/*****************************************************************************/
/***************** Put a column for match title and grous ********************/
/*****************************************************************************/
2019-09-14 12:59:34 +02:00
2020-11-25 01:50:13 +01:00
static void Mch_ListOneOrMoreMatchesTitleGrps (const struct Mch_Match *Match,
const char *Anchor)
2019-09-25 00:42:13 +02:00
{
2019-12-05 19:54:28 +01:00
extern const char *Txt_Play;
extern const char *Txt_Resume;
2019-10-23 19:05:05 +02:00
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
2020-11-25 01:50:13 +01:00
HTM_ARTICLE_Begin (Anchor);
2019-09-14 12:59:34 +02:00
2019-12-05 19:54:28 +01:00
/***** Match title *****/
Frm_BeginForm (Gbl.Usrs.Me.Role.Logged == Rol_STD ? ActJoiMch :
2019-12-05 19:54:28 +01:00
ActResMch);
2020-04-08 03:06:45 +02:00
Mch_PutParamMchCod (Match->MchCod);
2019-12-05 19:54:28 +01:00
HTM_BUTTON_SUBMIT_Begin (Gbl.Usrs.Me.Role.Logged == Rol_STD ? Txt_Play :
Txt_Resume,
2019-12-13 18:22:59 +01:00
"BT_LINK LT ASG_TITLE",NULL);
2019-11-10 12:36:37 +01:00
HTM_Txt (Match->Title);
2019-12-05 19:54:28 +01:00
HTM_BUTTON_End ();
Frm_EndForm ();
2019-09-14 12:59:34 +02:00
2019-09-25 00:42:13 +02:00
/***** Groups whose students can answer this match *****/
if (Gbl.Crs.Grps.NumGrps)
Mch_GetAndWriteNamesOfGrpsAssociatedToMatch (Match);
2019-09-14 12:59:34 +02:00
2020-11-25 01:50:13 +01:00
HTM_ARTICLE_End ();
2019-10-23 19:05:05 +02:00
HTM_TD_End ();
2019-09-25 00:42:13 +02:00
}
2019-09-14 12:59:34 +02:00
2019-09-25 00:42:13 +02:00
/*****************************************************************************/
/************* Get and write the names of the groups of a match **************/
/*****************************************************************************/
2019-09-14 12:59:34 +02:00
2020-04-08 03:06:45 +02:00
static void Mch_GetAndWriteNamesOfGrpsAssociatedToMatch (const struct Mch_Match *Match)
2019-09-25 00:42:13 +02:00
{
extern const char *Txt_Group;
extern const char *Txt_Groups;
extern const char *Txt_and;
extern const char *Txt_The_whole_course;
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumGrps;
unsigned NumGrp;
2019-09-14 12:59:34 +02:00
2019-09-25 00:42:13 +02:00
/***** Get groups associated to a match from database *****/
NumGrps = (unsigned)
DB_QuerySELECT (&mysql_res,"can not get groups of a match",
"SELECT grp_types.GrpTypName," // row[0]
"grp_groups.GrpName" // row[1]
" FROM mch_groups,"
"grp_groups,"
"grp_types"
" WHERE mch_groups.MchCod=%ld"
" AND mch_groups.GrpCod=grp_groups.GrpCod"
" AND grp_groups.GrpTypCod=grp_types.GrpTypCod"
" ORDER BY grp_types.GrpTypName,"
"grp_groups.GrpName",
Match->MchCod);
2019-09-14 12:59:34 +02:00
2019-09-25 00:42:13 +02:00
/***** Write heading *****/
2019-10-24 00:04:40 +02:00
HTM_DIV_Begin ("class=\"ASG_GRP\"");
HTM_TxtColonNBSP (NumGrps == 1 ? Txt_Group :
2020-01-11 15:22:02 +01:00
Txt_Groups);
2019-09-17 20:44:29 +02:00
2019-09-25 00:42:13 +02:00
/***** Write groups *****/
if (NumGrps) // Groups found...
2019-09-25 00:42:13 +02:00
{
/* Get and write the group types and names */
for (NumGrp = 0;
NumGrp < NumGrps;
NumGrp++)
2019-09-25 00:42:13 +02:00
{
/* Get next group */
row = mysql_fetch_row (mysql_res);
2019-09-14 12:59:34 +02:00
2019-09-25 00:42:13 +02:00
/* Write group type name and group name */
2019-11-11 10:59:24 +01:00
HTM_TxtF ("%s&nbsp;%s",row[0],row[1]);
2019-09-25 00:18:23 +02:00
if (NumGrps >= 2)
2019-09-25 00:42:13 +02:00
{
if (NumGrp == NumGrps - 2)
2019-11-11 00:15:44 +01:00
HTM_TxtF (" %s ",Txt_and);
if (NumGrps >= 3)
if (NumGrp < NumGrps - 2)
2019-11-10 16:41:47 +01:00
HTM_Txt (", ");
2019-09-25 00:42:13 +02:00
}
}
2019-09-14 12:59:34 +02:00
}
2019-09-25 00:42:13 +02:00
else
2019-11-11 10:59:24 +01:00
HTM_TxtF ("%s&nbsp;%s",Txt_The_whole_course,Gbl.Hierarchy.Crs.ShrtName);
2019-09-14 12:59:34 +02:00
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-09-25 00:42:13 +02:00
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
2019-09-14 12:59:34 +02:00
}
2019-09-25 09:29:44 +02:00
/*****************************************************************************/
/******************* Put a column for number of players **********************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_ListOneOrMoreMatchesNumPlayers (const struct Mch_Match *Match)
2019-09-25 09:29:44 +02:00
{
/***** Number of players who have answered any question in the match ******/
2019-10-23 19:05:05 +02:00
HTM_TD_Begin ("class=\"DAT RT COLOR%u\"",Gbl.RowEvenOdd);
2020-05-18 14:34:31 +02:00
HTM_Unsigned (Mch_GetNumUsrsWhoHavePlayedMch (Match->MchCod));
2019-10-23 19:05:05 +02:00
HTM_TD_End ();
2019-09-25 09:29:44 +02:00
}
2019-09-25 00:28:57 +02:00
/*****************************************************************************/
/********************** Put a column for match status ************************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_ListOneOrMoreMatchesStatus (struct Mch_Match *Match,unsigned NumQsts)
2019-09-25 00:28:57 +02:00
{
extern const char *Txt_Play;
extern const char *Txt_Resume;
2019-10-23 19:05:05 +02:00
HTM_TD_Begin ("class=\"DAT CT COLOR%u\"",Gbl.RowEvenOdd);
2019-09-25 00:28:57 +02:00
2019-12-10 21:52:22 +01:00
if (Match->Status.Showing != Mch_END) // Match not over
2019-10-23 20:07:56 +02:00
{
2019-09-25 00:28:57 +02:00
/* Current question index / total of questions */
2019-10-24 00:04:40 +02:00
HTM_DIV_Begin ("class=\"DAT\"");
2019-11-11 00:15:44 +01:00
HTM_TxtF ("%u/%u",Match->Status.QstInd,NumQsts);
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
}
2019-09-25 00:28:57 +02:00
2019-12-05 19:54:28 +01:00
/* Icon to join match or resume match */
Lay_PutContextualLinkOnlyIcon (Gbl.Usrs.Me.Role.Logged == Rol_STD ? ActJoiMch :
ActResMch,
NULL,
2020-04-08 03:06:45 +02:00
Mch_PutParamsPlay,&Match->MchCod,
2019-12-10 21:52:22 +01:00
Match->Status.Showing == Mch_END ? "flag-checkered.svg" :
"play.svg",
2019-12-05 19:54:28 +01:00
Gbl.Usrs.Me.Role.Logged == Rol_STD ? Txt_Play :
Txt_Resume);
2019-09-25 00:28:57 +02:00
2019-10-23 19:05:05 +02:00
HTM_TD_End ();
2019-09-25 00:28:57 +02:00
}
/*****************************************************************************/
/**************** Put a column for visibility of match result ****************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_ListOneOrMoreMatchesResult (struct Gam_Games *Games,
const struct Mch_Match *Match)
2019-09-25 00:28:57 +02:00
{
2019-10-23 19:05:05 +02:00
HTM_TD_Begin ("class=\"DAT CT COLOR%u\"",Gbl.RowEvenOdd);
2019-09-25 00:28:57 +02:00
switch (Gbl.Usrs.Me.Role.Logged)
{
case Rol_STD:
2020-04-08 03:06:45 +02:00
Mch_ListOneOrMoreMatchesResultStd (Games,Match);
2019-09-25 00:28:57 +02:00
break;
case Rol_NET:
case Rol_TCH:
case Rol_SYS_ADM:
2020-04-08 03:06:45 +02:00
Mch_ListOneOrMoreMatchesResultTch (Games,Match);
2019-09-25 00:28:57 +02:00
break;
default:
2019-12-05 19:54:28 +01:00
Rol_WrongRoleExit ();
2019-09-25 00:28:57 +02:00
break;
}
2019-10-23 19:05:05 +02:00
HTM_TD_End ();
2019-09-25 00:28:57 +02:00
}
2020-04-08 03:06:45 +02:00
static void Mch_ListOneOrMoreMatchesResultStd (struct Gam_Games *Games,
const struct Mch_Match *Match)
2019-12-05 19:54:28 +01:00
{
2019-12-08 23:19:16 +01:00
extern const char *Txt_Results;
2019-12-05 19:54:28 +01:00
/***** Is match result visible or hidden? *****/
if (Match->Status.ShowUsrResults)
{
/* Result is visible by me */
2020-04-08 03:06:45 +02:00
Games->GamCod = Match->GamCod;
2020-11-25 01:50:13 +01:00
Games->MchCod.Current = Match->MchCod;
2020-04-02 03:28:08 +02:00
Lay_PutContextualLinkOnlyIcon (ActSeeMyMchResMch,MchRes_RESULTS_BOX_ID,
2020-04-08 03:06:45 +02:00
Mch_PutParamsEdit,Games,
2019-12-08 23:19:16 +01:00
"trophy.svg",
Txt_Results);
2019-12-05 19:54:28 +01:00
}
else
/* Result is forbidden to me */
2020-02-19 00:45:26 +01:00
Ico_PutIconNotVisible ();
2019-12-05 19:54:28 +01:00
}
2020-04-08 03:06:45 +02:00
static void Mch_ListOneOrMoreMatchesResultTch (struct Gam_Games *Games,
const struct Mch_Match *Match)
2019-12-05 19:54:28 +01:00
{
2019-12-08 23:19:16 +01:00
extern const char *Txt_Visible_results;
extern const char *Txt_Hidden_results;
extern const char *Txt_Results;
2019-12-05 19:54:28 +01:00
2020-06-23 12:57:31 +02:00
Games->GamCod = Match->GamCod;
2020-11-25 01:50:13 +01:00
Games->MchCod.Current = Match->MchCod;
2019-12-08 23:19:16 +01:00
2020-06-23 12:57:31 +02:00
/***** Show match results *****/
if (Mch_CheckIfICanEditThisMatch (Match))
2020-05-18 22:59:07 +02:00
Lay_PutContextualLinkOnlyIcon (ActSeeUsrMchResMch,MchRes_RESULTS_BOX_ID,
2020-04-08 03:06:45 +02:00
Mch_PutParamsEdit,Games,
2019-12-08 23:19:16 +01:00
"trophy.svg",
Txt_Results);
2020-06-23 12:57:31 +02:00
/***** Check if visibility of session results can be changed *****/
if (Mch_CheckIfVisibilityOfResultsCanBeChanged (Match))
{
2019-12-05 19:54:28 +01:00
/* I can edit visibility */
Lay_PutContextualLinkOnlyIcon (ActChgVisResMchUsr,NULL,
2020-04-08 03:06:45 +02:00
Mch_PutParamsEdit,Games,
2020-02-19 00:45:26 +01:00
Match->Status.ShowUsrResults ? "eye-green.svg" :
"eye-slash-red.svg",
2019-12-08 23:19:16 +01:00
Match->Status.ShowUsrResults ? Txt_Visible_results :
Txt_Hidden_results);
2019-12-05 19:54:28 +01:00
}
else
/* I can not edit visibility */
2020-02-19 00:45:26 +01:00
Ico_PutIconOff (Match->Status.ShowUsrResults ? "eye-green.svg" :
"eye-slash-red.svg",
2019-12-08 23:19:16 +01:00
Match->Status.ShowUsrResults ? Txt_Visible_results :
Txt_Hidden_results);
2019-12-05 19:54:28 +01:00
}
2019-09-25 21:58:12 +02:00
/*****************************************************************************/
/******************** Toggle visibility of match results *********************/
/*****************************************************************************/
2020-05-16 02:04:36 +02:00
void Mch_ToggleVisResultsMchUsr (void)
2019-09-25 21:58:12 +02:00
{
2020-04-08 03:06:45 +02:00
struct Gam_Games Games;
struct Gam_Game Game;
struct Mch_Match Match;
2020-04-23 23:09:28 +02:00
/***** Reset games context *****/
2020-04-08 03:06:45 +02:00
Gam_ResetGames (&Games);
2019-09-25 21:58:12 +02:00
2020-04-23 23:09:28 +02:00
/***** Reset game and match *****/
Gam_ResetGame (&Game);
Mch_ResetMatch (&Match);
2019-09-29 22:38:32 +02:00
/***** Get and check parameters *****/
2020-04-08 03:06:45 +02:00
Mch_GetAndCheckParameters (&Games,&Game,&Match);
2019-09-25 21:58:12 +02:00
2020-06-23 12:57:31 +02:00
/***** Check if visibility of match results can be changed *****/
if (!Mch_CheckIfVisibilityOfResultsCanBeChanged (&Match))
2019-10-26 01:56:36 +02:00
Lay_NoPermissionExit ();
2019-09-30 01:10:57 +02:00
2019-09-25 21:58:12 +02:00
/***** Toggle visibility of match results *****/
Match.Status.ShowUsrResults = !Match.Status.ShowUsrResults;
DB_QueryUPDATE ("can not toggle visibility of match results",
"UPDATE mch_matches"
" SET ShowUsrResults='%c'"
2019-09-25 21:58:12 +02:00
" WHERE MchCod=%ld",
Match.Status.ShowUsrResults ? 'Y' :
'N',
Match.MchCod);
/***** Show current game *****/
2020-04-08 03:06:45 +02:00
Gam_ShowOnlyOneGame (&Games,&Game,
2019-12-08 16:46:25 +01:00
false, // Do not list game questions
false); // Do not put form to start new match
2019-09-25 21:58:12 +02:00
}
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
/******************** Get game data from a database row **********************/
/*****************************************************************************/
static void Mch_GetMatchDataFromRow (MYSQL_RES *mysql_res,
2020-04-08 03:06:45 +02:00
struct Mch_Match *Match)
2019-09-14 12:59:34 +02:00
{
MYSQL_ROW row;
2019-09-22 19:50:24 +02:00
Dat_StartEndTime_t StartEndTime;
2019-10-22 23:42:22 +02:00
long LongNum;
2019-09-14 12:59:34 +02:00
/***** Get match data *****/
row = mysql_fetch_row (mysql_res);
/*
2019-09-25 21:58:12 +02:00
row[ 0] MchCod
row[ 1] GamCod
row[ 2] UsrCod
row[ 3] UNIX_TIMESTAMP(StartTime)
row[ 4] UNIX_TIMESTAMP(EndTime)
row[ 5] Title
2019-09-14 12:59:34 +02:00
*/
/***** Get match data *****/
/* Code of the match (row[0]) */
if ((Match->MchCod = Str_ConvertStrCodToLongCod (row[0])) <= 0)
Lay_WrongMatchExit ();
2019-09-14 12:59:34 +02:00
/* Code of the game (row[1]) */
if ((Match->GamCod = Str_ConvertStrCodToLongCod (row[1])) <= 0)
Lay_WrongGameExit ();
2019-09-14 12:59:34 +02:00
/* Get match teacher (row[2]) */
Match->UsrCod = Str_ConvertStrCodToLongCod (row[2]);
2019-09-22 19:50:24 +02:00
/* Get start/end times (row[3], row[4] hold start/end UTC times) */
2019-12-15 20:02:34 +01:00
for (StartEndTime = (Dat_StartEndTime_t) 0;
2019-09-22 19:50:24 +02:00
StartEndTime <= (Dat_StartEndTime_t) (Dat_NUM_START_END_TIME - 1);
StartEndTime++)
Match->TimeUTC[StartEndTime] = Dat_GetUNIXTimeFromStr (row[3 + StartEndTime]);
2019-09-14 12:59:34 +02:00
2020-11-25 01:50:13 +01:00
/* Get the title of the match (row[5]) */
2019-09-14 12:59:34 +02:00
if (row[5])
Str_Copy (Match->Title,row[5],sizeof (Match->Title) - 1);
2019-09-14 12:59:34 +02:00
else
Match->Title[0] = '\0';
/***** Get current match status *****/
/*
2019-09-25 21:58:12 +02:00
row[ 6] QstInd
row[ 7] QstCod
row[ 8] Showing
2019-12-12 18:24:23 +01:00
row[ 9] Countdown
row[10] NumCols
row[11] ShowQstResults
row[12] ShowUsrResults
2019-09-14 12:59:34 +02:00
*/
/* Current question index (row[6]) */
2020-02-27 00:19:55 +01:00
Match->Status.QstInd = Str_ConvertStrToUnsigned (row[6]);
2019-09-14 12:59:34 +02:00
/* Current question code (row[7]) */
Match->Status.QstCod = Str_ConvertStrCodToLongCod (row[7]);
2019-09-25 21:58:12 +02:00
/* Get what to show (stem, answers, results) (row(8)) */
Match->Status.Showing = Mch_GetShowingFromStr (row[8]);
2019-09-14 12:59:34 +02:00
2019-12-12 18:24:23 +01:00
/* Get countdown (row[9]) */
Match->Status.Countdown = Str_ConvertStrCodToLongCod (row[9]);
/* Get number of columns (row[10]) */
LongNum = Str_ConvertStrCodToLongCod (row[10]);
2019-10-22 23:42:22 +02:00
Match->Status.NumCols = (LongNum <= 1 ) ? 1 :
((LongNum >= Mch_MAX_COLS) ? Mch_MAX_COLS :
(unsigned) LongNum);
2019-12-12 18:24:23 +01:00
/* Get whether to show question results or not (row(11)) */
Match->Status.ShowQstResults = (row[11][0] == 'Y');
2019-09-25 21:58:12 +02:00
2019-12-12 18:24:23 +01:00
/* Get whether to show user results or not (row(12)) */
Match->Status.ShowUsrResults = (row[12][0] == 'Y');
2019-09-14 12:59:34 +02:00
/***** Get whether the match is being played or not *****/
2019-12-10 21:52:22 +01:00
if (Match->Status.Showing == Mch_END) // Match over
2019-09-17 15:42:41 +02:00
Match->Status.Playing = false;
2019-12-10 21:52:22 +01:00
else // Match not over
2019-09-17 15:42:41 +02:00
Match->Status.Playing = Mch_GetIfMatchIsBeingPlayed (Match->MchCod);
2019-09-14 12:59:34 +02:00
}
/*****************************************************************************/
/****************** Get parameter with what is being shown *******************/
/*****************************************************************************/
static Mch_Showing_t Mch_GetShowingFromStr (const char *Str)
{
Mch_Showing_t Showing;
2019-12-12 18:24:23 +01:00
for (Showing = (Mch_Showing_t) 0;
2019-09-14 12:59:34 +02:00
Showing <= (Mch_Showing_t) (Mch_NUM_SHOWING - 1);
Showing++)
if (!strcmp (Str,Mch_ShowingStringsDB[Showing]))
return Showing;
return (Mch_Showing_t) Mch_SHOWING_DEFAULT;
}
/*****************************************************************************/
/************** Request the removal of a match (game instance) ***************/
/*****************************************************************************/
2019-09-29 21:33:27 +02:00
void Mch_RequestRemoveMatch (void)
2019-09-14 12:59:34 +02:00
{
extern const char *Txt_Do_you_really_want_to_remove_the_match_X;
extern const char *Txt_Remove_match;
2020-04-08 03:06:45 +02:00
struct Gam_Games Games;
struct Gam_Game Game;
struct Mch_Match Match;
2020-04-23 23:09:28 +02:00
/***** Reset games context *****/
2020-04-08 03:06:45 +02:00
Gam_ResetGames (&Games);
2019-09-14 12:59:34 +02:00
2020-04-23 23:09:28 +02:00
/***** Reset game and match *****/
Gam_ResetGame (&Game);
Mch_ResetMatch (&Match);
2019-09-29 22:38:32 +02:00
/***** Get and check parameters *****/
2020-04-08 03:06:45 +02:00
Mch_GetAndCheckParameters (&Games,&Game,&Match);
2019-09-14 12:59:34 +02:00
/***** Show question and button to remove question *****/
2020-03-26 02:54:30 +01:00
Ale_ShowAlertAndButton (ActRemMch,NULL,NULL,
2020-04-08 03:06:45 +02:00
Mch_PutParamsEdit,&Games,
2019-09-14 12:59:34 +02:00
Btn_REMOVE_BUTTON,Txt_Remove_match,
Ale_QUESTION,Txt_Do_you_really_want_to_remove_the_match_X,
Match.Title);
/***** Show current game *****/
2020-04-08 03:06:45 +02:00
Gam_ShowOnlyOneGame (&Games,&Game,
2019-12-08 16:46:25 +01:00
false, // Do not list game questions
false); // Do not put form to start new match
2019-09-14 12:59:34 +02:00
}
/*****************************************************************************/
/********************** Remove a match (game instance) ***********************/
/*****************************************************************************/
2019-09-29 21:33:27 +02:00
void Mch_RemoveMatch (void)
2019-09-14 12:59:34 +02:00
{
extern const char *Txt_Match_X_removed;
2020-04-08 03:06:45 +02:00
struct Gam_Games Games;
struct Gam_Game Game;
struct Mch_Match Match;
2020-04-23 23:09:28 +02:00
/***** Reset games context *****/
2020-04-08 03:06:45 +02:00
Gam_ResetGames (&Games);
2019-09-14 12:59:34 +02:00
2020-04-23 23:09:28 +02:00
/***** Reset game and match *****/
Gam_ResetGame (&Game);
Mch_ResetMatch (&Match);
2019-09-29 22:38:32 +02:00
/***** Get and check parameters *****/
2020-04-08 03:06:45 +02:00
Mch_GetAndCheckParameters (&Games,&Game,&Match);
2019-09-27 20:00:47 +02:00
2019-09-29 23:40:33 +02:00
/***** Check if I can remove this match *****/
2019-09-30 01:10:57 +02:00
if (!Mch_CheckIfICanEditThisMatch (&Match))
2019-10-26 01:56:36 +02:00
Lay_NoPermissionExit ();
2019-09-29 23:40:33 +02:00
2019-09-27 20:00:47 +02:00
/***** Remove the match from all database tables *****/
Mch_RemoveMatchFromAllTables (Match.MchCod);
2019-09-14 12:59:34 +02:00
/***** Write message *****/
Ale_ShowAlert (Ale_SUCCESS,Txt_Match_X_removed,
Match.Title);
/***** Show current game *****/
2020-04-08 03:06:45 +02:00
Gam_ShowOnlyOneGame (&Games,&Game,
2019-12-08 16:46:25 +01:00
false, // Do not list game questions
false); // Do not put form to start new match
2019-09-14 12:59:34 +02:00
}
2019-09-27 20:00:47 +02:00
/*****************************************************************************/
/********************** Remove match from all tables *************************/
/*****************************************************************************/
2019-09-29 23:13:20 +02:00
/*
mysql> SELECT table_name FROM information_schema.tables WHERE table_name LIKE 'mch%';
*/
2019-09-27 20:00:47 +02:00
static void Mch_RemoveMatchFromAllTables (long MchCod)
{
2019-09-28 01:12:53 +02:00
/***** Remove match from secondary tables *****/
2019-09-29 23:13:20 +02:00
Mch_RemoveMatchFromTable (MchCod,"mch_players");
Mch_RemoveMatchFromTable (MchCod,"mch_playing");
Mch_RemoveMatchFromTable (MchCod,"mch_results");
Mch_RemoveMatchFromTable (MchCod,"mch_answers");
Mch_RemoveMatchFromTable (MchCod,"mch_times");
Mch_RemoveMatchFromTable (MchCod,"mch_groups");
Mch_RemoveMatchFromTable (MchCod,"mch_indexes");
2019-09-28 01:12:53 +02:00
/***** Remove match from main table *****/
DB_QueryDELETE ("can not remove match",
"DELETE FROM mch_matches"
" WHERE MchCod=%ld",
2019-09-28 01:12:53 +02:00
MchCod);
2019-09-27 20:00:47 +02:00
}
2019-09-29 23:13:20 +02:00
static void Mch_RemoveMatchFromTable (long MchCod,const char *TableName)
{
/***** Remove match from secondary table *****/
DB_QueryDELETE ("can not remove match from table",
"DELETE FROM %s"
" WHERE MchCod=%ld",
2019-09-29 23:13:20 +02:00
TableName,
MchCod);
}
2019-09-27 21:45:53 +02:00
/*****************************************************************************/
/******************** Remove match in game from all tables *******************/
/*****************************************************************************/
2019-09-28 01:12:53 +02:00
void Mch_RemoveMatchesInGameFromAllTables (long GamCod)
{
/***** Remove matches from secondary tables *****/
2019-09-29 23:13:20 +02:00
Mch_RemoveMatchesInGameFromTable (GamCod,"mch_players");
Mch_RemoveMatchesInGameFromTable (GamCod,"mch_playing");
Mch_RemoveMatchesInGameFromTable (GamCod,"mch_results");
Mch_RemoveMatchesInGameFromTable (GamCod,"mch_answers");
Mch_RemoveMatchesInGameFromTable (GamCod,"mch_times");
Mch_RemoveMatchesInGameFromTable (GamCod,"mch_groups");
Mch_RemoveMatchesInGameFromTable (GamCod,"mch_indexes");
2019-09-28 01:12:53 +02:00
/***** Remove matches from main table *****/
DB_QueryDELETE ("can not remove matches of a game",
"DELETE FROM mch_matches"
" WHERE GamCod=%ld",
2019-09-28 01:12:53 +02:00
GamCod);
}
2019-09-29 23:13:20 +02:00
static void Mch_RemoveMatchesInGameFromTable (long GamCod,const char *TableName)
{
/***** Remove matches in game from secondary table *****/
DB_QueryDELETE ("can not remove matches of a game from table",
"DELETE FROM %s"
" USING mch_matches,"
"%s"
2019-09-29 23:13:20 +02:00
" WHERE mch_matches.GamCod=%ld"
" AND mch_matches.MchCod=%s.MchCod",
2019-09-29 23:13:20 +02:00
TableName,
TableName,
GamCod,
TableName);
}
2019-09-28 01:12:53 +02:00
/*****************************************************************************/
/******************* Remove match in course from all tables ******************/
/*****************************************************************************/
2020-05-18 22:59:07 +02:00
void Mch_RemoveMatchesInCourseFromAllTables (long CrsCod)
2019-09-27 21:45:53 +02:00
{
2019-09-28 01:12:53 +02:00
/***** Remove matches from secondary tables *****/
2020-05-18 22:59:07 +02:00
Mch_RemoveMatchesInCourseFromTable (CrsCod,"mch_players");
Mch_RemoveMatchesInCourseFromTable (CrsCod,"mch_playing");
Mch_RemoveMatchesInCourseFromTable (CrsCod,"mch_results");
Mch_RemoveMatchesInCourseFromTable (CrsCod,"mch_answers");
Mch_RemoveMatchesInCourseFromTable (CrsCod,"mch_times");
Mch_RemoveMatchesInCourseFromTable (CrsCod,"mch_groups");
Mch_RemoveMatchesInCourseFromTable (CrsCod,"mch_indexes");
2019-09-28 01:12:53 +02:00
/***** Remove matches from main table *****/
2019-10-04 14:57:04 +02:00
DB_QueryDELETE ("can not remove matches of a course",
"DELETE FROM mch_matches"
" USING gam_games,"
"mch_matches"
2019-09-28 01:12:53 +02:00
" WHERE gam_games.CrsCod=%ld"
" AND gam_games.GamCod=mch_matches.GamCod",
2019-09-28 01:12:53 +02:00
CrsCod);
2019-09-27 21:45:53 +02:00
}
2020-05-18 22:59:07 +02:00
static void Mch_RemoveMatchesInCourseFromTable (long CrsCod,const char *TableName)
2019-09-27 21:45:53 +02:00
{
2019-09-29 23:13:20 +02:00
/***** Remove matches in course from secondary table *****/
DB_QueryDELETE ("can not remove matches of a course from table",
2019-09-27 21:45:53 +02:00
"DELETE FROM %s"
" USING gam_games,"
"mch_matches,"
"%s"
2019-09-29 23:13:20 +02:00
" WHERE gam_games.CrsCod=%ld"
" AND gam_games.GamCod=mch_matches.GamCod"
" AND mch_matches.MchCod=%s.MchCod",
2019-09-27 21:45:53 +02:00
TableName,
TableName,
2019-09-29 23:13:20 +02:00
CrsCod,
2019-09-27 21:45:53 +02:00
TableName);
2019-09-27 20:00:47 +02:00
}
2019-09-28 01:12:53 +02:00
/*****************************************************************************/
2020-05-18 22:59:07 +02:00
/*************** Remove matches made by user in all courses ******************/
2019-09-28 01:12:53 +02:00
/*****************************************************************************/
2020-05-18 22:59:07 +02:00
void Mch_RemoveMatchesMadeByUsrInAllCrss (long UsrCod)
2019-09-29 23:13:20 +02:00
{
/***** Remove student from secondary tables *****/
2020-05-18 22:59:07 +02:00
Mch_RemoveMatchesMadeByUsrFromTable (UsrCod,"mch_players");
Mch_RemoveMatchesMadeByUsrFromTable (UsrCod,"mch_results");
Mch_RemoveMatchesMadeByUsrFromTable (UsrCod,"mch_answers");
2019-09-29 23:13:20 +02:00
}
2020-05-18 22:59:07 +02:00
static void Mch_RemoveMatchesMadeByUsrFromTable (long UsrCod,const char *TableName)
{
/***** Remove matches in course from secondary table *****/
DB_QueryDELETE ("can not remove matches of a user from table",
"DELETE FROM %s"
" WHERE UsrCod=%ld",
TableName,
UsrCod);
}
/*****************************************************************************/
/***************** Remove matches made by user in a course *******************/
/*****************************************************************************/
void Mch_RemoveMatchesMadeByUsrInCrs (long UsrCod,long CrsCod)
{
/***** Remove student from secondary tables *****/
Mch_RemoveMatchesMadeByUsrInCrsFromTable (UsrCod,CrsCod,"mch_players");
Mch_RemoveMatchesMadeByUsrInCrsFromTable (UsrCod,CrsCod,"mch_results");
Mch_RemoveMatchesMadeByUsrInCrsFromTable (UsrCod,CrsCod,"mch_answers");
}
static void Mch_RemoveMatchesMadeByUsrInCrsFromTable (long UsrCod,long CrsCod,const char *TableName)
2019-09-28 01:12:53 +02:00
{
/***** Remove matches in course from secondary table *****/
2019-09-29 23:13:20 +02:00
DB_QueryDELETE ("can not remove matches of a user from table",
2019-09-28 01:12:53 +02:00
"DELETE FROM %s"
" USING gam_games,"
"mch_matches,"
"%s"
2019-09-28 01:12:53 +02:00
" WHERE gam_games.CrsCod=%ld"
" AND gam_games.GamCod=mch_matches.GamCod"
" AND mch_matches.MchCod=%s.MchCod"
" AND %s.UsrCod=%ld",
2019-09-28 01:12:53 +02:00
TableName,
TableName,
CrsCod,
2019-09-29 23:13:20 +02:00
TableName,
TableName,
UsrCod);
2019-09-28 01:12:53 +02:00
}
2020-10-13 22:34:31 +02:00
/*****************************************************************************/
/************************ Edit a match (game instance) ***********************/
/*****************************************************************************/
void Mch_EditMatch (void)
{
struct Gam_Games Games;
struct Gam_Game Game;
struct Mch_Match Match;
/***** Reset games context *****/
Gam_ResetGames (&Games);
/***** Reset game and match *****/
Gam_ResetGame (&Game);
Mch_ResetMatch (&Match);
/***** Get and check parameters *****/
Mch_GetAndCheckParameters (&Games,&Game,&Match);
/***** Check if I can edit this match *****/
if (!Mch_CheckIfICanEditThisMatch (&Match))
Lay_NoPermissionExit ();
/***** Show current game *****/
Gam_ShowOnlyOneGame (&Games,&Game,
false, // Do not list game questions
false); // Do not put form to start new match
}
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
2019-09-29 17:33:39 +02:00
/*********************** Params used to edit a match *************************/
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
void Mch_PutParamsEdit (void *Games)
2019-09-14 12:59:34 +02:00
{
2020-04-08 03:06:45 +02:00
if (Games)
2020-03-26 02:54:30 +01:00
{
2020-04-08 03:06:45 +02:00
Gam_PutParams (Games);
2020-11-25 01:50:13 +01:00
Mch_PutParamMchCod (((struct Gam_Games *) Games)->MchCod.Current);
2020-03-26 02:54:30 +01:00
}
2019-09-29 22:38:32 +02:00
}
/*****************************************************************************/
/*********************** Params used to edit a match *************************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_PutParamsPlay (void *MchCod)
2019-09-29 22:38:32 +02:00
{
2020-04-08 03:06:45 +02:00
if (MchCod)
2020-03-26 02:54:30 +01:00
{
2020-04-08 18:18:46 +02:00
if (*((long *) MchCod) > 0)
Mch_PutParamMchCod (*((long *) MchCod));
2020-03-26 02:54:30 +01:00
}
2019-09-14 12:59:34 +02:00
}
/*****************************************************************************/
/******************** Write parameter with code of match **********************/
/*****************************************************************************/
2019-09-30 01:10:57 +02:00
static void Mch_PutParamMchCod (long MchCod)
2019-09-14 12:59:34 +02:00
{
2019-11-03 13:19:32 +01:00
Par_PutHiddenParamLong (NULL,"MchCod",MchCod);
2019-09-14 12:59:34 +02:00
}
2019-09-29 22:38:32 +02:00
/*****************************************************************************/
/************************** Get and check parameters *************************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
void Mch_GetAndCheckParameters (struct Gam_Games *Games,
struct Gam_Game *Game,
struct Mch_Match *Match)
2019-09-29 22:38:32 +02:00
{
/***** Get parameters *****/
/* Get parameters of game */
2020-04-08 03:06:45 +02:00
if ((Game->GamCod = Gam_GetParams (Games)) <= 0)
Lay_WrongGameExit ();
2020-03-27 14:56:54 +01:00
Grp_GetParamWhichGroups ();
2019-09-29 22:38:32 +02:00
Gam_GetDataOfGameByCod (Game);
/* Get match code */
2020-04-08 03:06:45 +02:00
if ((Match->MchCod = Mch_GetParamMchCod ()) <= 0)
Lay_WrongMatchExit ();
2019-09-29 22:38:32 +02:00
Mch_GetDataOfMatchByCod (Match);
/***** Ensure parameters are correct *****/
if (Game->GamCod != Match->GamCod)
Lay_WrongGameExit ();
2019-09-29 22:38:32 +02:00
if (Game->CrsCod != Gbl.Hierarchy.Crs.CrsCod)
Lay_WrongGameExit ();
2020-11-25 01:50:13 +01:00
/***** Initialize context *****/
Games->GamCod = Game->GamCod;
Games->MchCod.Current =
Games->MchCod.Selected = Match->MchCod;
2019-09-29 22:38:32 +02:00
}
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
/********************* Get parameter with code of match **********************/
/*****************************************************************************/
2019-09-28 02:31:42 +02:00
long Mch_GetParamMchCod (void)
2019-09-14 12:59:34 +02:00
{
/***** Get code of match *****/
return Par_GetParToLong ("MchCod");
}
/*****************************************************************************/
2020-11-25 01:50:13 +01:00
/******************* Put a form to change and existing match *****************/
/*****************************************************************************/
static void Mch_PutFormExistingMatch (struct Gam_Games *Games,
const struct Mch_Match *Match,
const char *Anchor)
{
extern const char *Hlp_ASSESSMENT_Games_matches;
extern const char *Txt_Title;
extern const char *Txt_Save_changes;
/***** Begin form *****/
Frm_StartFormAnchor (ActChgMch,Anchor);
Mch_PutParamsEdit (Games);
/***** Begin box and table *****/
Box_BoxTableBegin (NULL,Match->Title,
NULL,NULL,
Hlp_ASSESSMENT_Games_matches,Box_CLOSABLE,2);
/***** Match title *****/
HTM_TR_Begin (NULL);
/* Label */
Frm_LabelColumn ("RT","Title",Txt_Title);
/* Data */
HTM_TD_Begin ("class=\"LT\"");
HTM_INPUT_TEXT ("Title",Mch_MAX_CHARS_TITLE,Match->Title,
HTM_DONT_SUBMIT_ON_CHANGE,
"id=\"Title\" size=\"45\" required=\"required\"");
HTM_TD_End ();
HTM_TR_End ();
/***** Groups *****/
Mch_ShowLstGrpsToEditMatch (Match->MchCod);
/***** End table *****/
HTM_TABLE_End ();
/***** Put button to submit the form *****/
Btn_PutConfirmButton (Txt_Save_changes);
/***** End box *****/
Box_BoxEnd ();
/***** End form *****/
Frm_EndForm ();
}
/*****************************************************************************/
/********************** Put a form to create a new match *********************/
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_PutFormNewMatch (const struct Gam_Game *Game)
2019-09-14 12:59:34 +02:00
{
2019-10-03 22:12:31 +02:00
extern const char *Hlp_ASSESSMENT_Games_matches;
2019-09-14 12:59:34 +02:00
extern const char *Txt_New_match;
extern const char *Txt_Title;
extern const char *Txt_Play;
/***** Begin section for a new match *****/
2019-10-26 01:56:36 +02:00
HTM_SECTION_Begin (Mch_NEW_MATCH_SECTION_ID);
2019-09-14 12:59:34 +02:00
2019-10-20 22:00:28 +02:00
/***** Begin form *****/
Frm_BeginForm (ActNewMch);
2019-09-14 12:59:34 +02:00
Gam_PutParamGameCod (Game->GamCod);
Gam_PutParamQstInd (0); // Start by first question in game
2019-10-26 02:19:42 +02:00
/***** Begin box and table *****/
2020-03-26 02:54:30 +01:00
Box_BoxTableBegin (NULL,Txt_New_match,
NULL,NULL,
2019-10-03 22:12:31 +02:00
Hlp_ASSESSMENT_Games_matches,Box_NOT_CLOSABLE,2);
2019-09-14 12:59:34 +02:00
/***** Match title *****/
2019-10-23 19:05:05 +02:00
HTM_TR_Begin (NULL);
2019-10-07 17:36:41 +02:00
2019-12-27 21:10:39 +01:00
/* Label */
2019-12-27 15:45:19 +01:00
Frm_LabelColumn ("RT","Title",Txt_Title);
2019-10-07 17:36:41 +02:00
2019-12-27 21:10:39 +01:00
/* Data */
2019-12-26 22:29:04 +01:00
HTM_TD_Begin ("class=\"LT\"");
2020-11-25 01:50:13 +01:00
HTM_INPUT_TEXT ("Title",Mch_MAX_CHARS_TITLE,Game->Title,
2020-04-27 03:16:55 +02:00
HTM_DONT_SUBMIT_ON_CHANGE,
2019-11-12 15:41:58 +01:00
"id=\"Title\" size=\"45\" required=\"required\"");
2019-10-23 19:05:05 +02:00
HTM_TD_End ();
2019-10-07 17:36:41 +02:00
2019-10-23 19:05:05 +02:00
HTM_TR_End ();
2019-09-14 12:59:34 +02:00
/***** Groups *****/
2020-11-25 01:50:13 +01:00
Mch_ShowLstGrpsToEditMatch (-1L);
2019-09-14 12:59:34 +02:00
/***** End table *****/
2019-10-23 19:05:05 +02:00
HTM_TABLE_End ();
2019-09-14 12:59:34 +02:00
2019-11-18 15:48:46 +01:00
/***** Put icon to submit the form *****/
HTM_INPUT_IMAGE (Cfg_URL_ICON_PUBLIC,"play.svg",
Txt_Play,"CONTEXT_OPT ICO_HIGHLIGHT ICO64x64");
2019-09-14 12:59:34 +02:00
/***** End box *****/
2019-10-25 22:48:34 +02:00
Box_BoxEnd ();
2019-09-14 12:59:34 +02:00
/***** End form *****/
Frm_EndForm ();
/***** End section for a new match *****/
2019-10-26 01:56:36 +02:00
HTM_SECTION_End ();
2019-09-14 12:59:34 +02:00
}
/*****************************************************************************/
/***************** Show list of groups to create a new match *****************/
/*****************************************************************************/
2020-11-25 01:50:13 +01:00
static void Mch_ShowLstGrpsToEditMatch (long MchCod)
2019-09-14 12:59:34 +02:00
{
extern const char *The_ClassFormInBox[The_NUM_THEMES];
extern const char *Txt_Groups;
extern const char *Txt_The_whole_course;
unsigned NumGrpTyp;
/***** Get list of groups types and groups in this course *****/
Grp_GetListGrpTypesAndGrpsInThisCrs (Grp_ONLY_GROUP_TYPES_WITH_GROUPS);
if (Gbl.Crs.Grps.GrpTypes.Num)
{
2019-10-26 02:19:42 +02:00
/***** Begin box and table *****/
2019-10-23 19:05:05 +02:00
HTM_TR_Begin (NULL);
2019-10-07 17:36:41 +02:00
2019-10-23 19:05:05 +02:00
HTM_TD_Begin ("class=\"%s RT\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
2020-06-18 20:06:17 +02:00
HTM_TxtColon (Txt_Groups);
2019-10-23 19:05:05 +02:00
HTM_TD_End ();
2019-10-07 17:36:41 +02:00
2019-10-23 19:05:05 +02:00
HTM_TD_Begin ("class=\"LT\"");
2020-03-26 02:54:30 +01:00
Box_BoxTableBegin ("95%",NULL,
NULL,NULL,
2019-09-14 12:59:34 +02:00
NULL,Box_NOT_CLOSABLE,0);
/***** First row: checkbox to select the whole course *****/
2019-10-23 19:05:05 +02:00
HTM_TR_Begin (NULL);
2019-12-26 22:29:04 +01:00
2019-10-23 19:05:05 +02:00
HTM_TD_Begin ("colspan=\"7\" class=\"DAT LM\"");
2019-11-03 10:41:31 +01:00
HTM_LABEL_Begin (NULL);
2020-04-28 03:14:25 +02:00
HTM_INPUT_CHECKBOX ("WholeCrs",HTM_DONT_SUBMIT_ON_CHANGE,
2020-11-25 01:50:13 +01:00
"id=\"WholeCrs\" value=\"Y\"%s"
" onclick=\"uncheckChildren(this,'GrpCods')\"",
Grp_CheckIfAssociatedToGrps ("mch_groups","MchCod",MchCod) ? "" :
" checked=\"checked\"");
2019-11-11 10:59:24 +01:00
HTM_TxtF ("%s&nbsp;%s",Txt_The_whole_course,Gbl.Hierarchy.Crs.ShrtName);
2019-11-02 12:59:31 +01:00
HTM_LABEL_End ();
2019-10-23 19:05:05 +02:00
HTM_TD_End ();
2019-10-07 17:36:41 +02:00
2019-10-23 19:05:05 +02:00
HTM_TR_End ();
2019-09-14 12:59:34 +02:00
/***** List the groups for each group type *****/
for (NumGrpTyp = 0;
NumGrpTyp < Gbl.Crs.Grps.GrpTypes.Num;
NumGrpTyp++)
if (Gbl.Crs.Grps.GrpTypes.LstGrpTypes[NumGrpTyp].NumGrps)
2020-05-06 01:43:48 +02:00
Grp_ListGrpsToEditAsgAttSvyEvtMch (&Gbl.Crs.Grps.GrpTypes.LstGrpTypes[NumGrpTyp],
2020-11-25 01:50:13 +01:00
Grp_MATCH,MchCod);
2019-09-14 12:59:34 +02:00
/***** End table and box *****/
2019-11-25 23:18:08 +01:00
Box_BoxTableEnd ();
2019-10-23 19:05:05 +02:00
HTM_TD_End ();
HTM_TR_End ();
2019-09-14 12:59:34 +02:00
}
/***** Free list of groups types and groups in this course *****/
Grp_FreeListGrpTypesAndGrps ();
}
/*****************************************************************************/
/********************* Create a new match (by a teacher) *********************/
/*****************************************************************************/
2020-11-25 01:50:13 +01:00
void Mch_CreateNewMatch (void)
2019-09-14 12:59:34 +02:00
{
long GamCod;
2020-11-25 01:50:13 +01:00
char Title[Mch_MAX_BYTES_TITLE + 1];
2019-09-14 12:59:34 +02:00
/***** Get form parameters *****/
/* Get match code */
if ((GamCod = Gam_GetParamGameCod ()) < 0)
Lay_WrongGameExit ();
2019-09-14 12:59:34 +02:00
/* Get match title */
2020-11-25 01:50:13 +01:00
Par_GetParToText ("Title",Title,Mch_MAX_BYTES_TITLE);
2019-09-14 12:59:34 +02:00
2020-11-25 01:50:13 +01:00
/* Get groups for this match */
2019-09-14 12:59:34 +02:00
Grp_GetParCodsSeveralGrps ();
/***** Create a new match *****/
2020-04-08 03:06:45 +02:00
Mch_SetMchCodBeingPlayed (Mch_CreateMatch (GamCod,Title));
2019-09-14 12:59:34 +02:00
/***** Free memory for list of selected groups *****/
Grp_FreeListCodSelectedGrps ();
}
2020-11-25 01:50:13 +01:00
/*****************************************************************************/
/************************ Change a match (by a teacher) **********************/
/*****************************************************************************/
void Mch_ChangeMatch (void)
{
struct Gam_Games Games;
struct Gam_Game Game;
struct Mch_Match Match;
/***** Reset games context *****/
Gam_ResetGames (&Games);
/***** Reset game and match *****/
Gam_ResetGame (&Game);
Mch_ResetMatch (&Match);
/***** Get and check parameters *****/
Mch_GetAndCheckParameters (&Games,&Game,&Match);
/***** Check if I can update this match *****/
if (!Mch_CheckIfICanEditThisMatch (&Match))
Lay_NoPermissionExit ();
/***** Get match title and groups *****/
/* Get match title */
Par_GetParToText ("Title",Match.Title,Mch_MAX_BYTES_TITLE);
/* Get groups for this match */
Grp_GetParCodsSeveralGrps ();
/***** Update match *****/
Mch_UpdateMatchTitleAndGrps (&Match);
/***** Free memory for list of selected groups *****/
Grp_FreeListCodSelectedGrps ();
/***** Show current game *****/
Gam_ShowOnlyOneGame (&Games,&Game,
false, // Do not list game questions
false); // Do not put form to start new match
}
/*****************************************************************************/
/*************** Update title and groups of an existing match ****************/
/*****************************************************************************/
static void Mch_UpdateMatchTitleAndGrps (const struct Mch_Match *Match)
{
/***** Update match title into database *****/
DB_QueryUPDATE ("can not update match",
"UPDATE mch_matches"
" SET Title='%s'"
2020-11-25 01:50:13 +01:00
" WHERE MchCod=%ld",
Match->Title,
Match->MchCod);
/***** Update groups associated to the match *****/
Mch_RemoveMatchFromTable (Match->MchCod,"mch_groups"); // Remove all groups associated to this match
if (Gbl.Crs.Grps.LstGrpsSel.NumGrps)
Mch_CreateGrps (Match->MchCod); // Associate selected groups
}
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
/******* Show button to actually start / resume a match (by a teacher) *******/
/*****************************************************************************/
2019-09-29 21:33:27 +02:00
void Mch_ResumeMatch (void)
2019-09-14 12:59:34 +02:00
{
2020-04-08 03:06:45 +02:00
struct Mch_Match Match;
2019-09-14 12:59:34 +02:00
2020-04-23 23:09:28 +02:00
/***** Reset match *****/
Mch_ResetMatch (&Match);
2019-09-14 12:59:34 +02:00
/***** Remove old players.
2019-10-01 21:18:53 +02:00
This function must be called by a teacher
before getting match status. *****/
2019-09-14 12:59:34 +02:00
Mch_RemoveOldPlayers ();
/***** Get data of the match from database *****/
2020-04-08 03:06:45 +02:00
Match.MchCod = Mch_GetMchCodBeingPlayed ();
2019-09-14 12:59:34 +02:00
Mch_GetDataOfMatchByCod (&Match);
2019-09-30 01:10:57 +02:00
/***** Check if I have permission to resume match *****/
if (!Mch_CheckIfICanEditThisMatch (&Match))
2019-10-26 01:56:36 +02:00
Lay_NoPermissionExit ();
2019-09-30 01:10:57 +02:00
2019-09-14 12:59:34 +02:00
/***** Update match status in database *****/
Mch_UpdateMatchStatusInDB (&Match);
/***** Show current match status *****/
2019-10-24 00:04:40 +02:00
HTM_DIV_Begin ("id=\"match\" class=\"MCH_CONT\"");
2019-09-14 12:59:34 +02:00
Mch_ShowMatchStatusForTch (&Match);
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-09-14 12:59:34 +02:00
}
/*****************************************************************************/
/********************** Create a new match in a game *************************/
/*****************************************************************************/
2020-11-25 01:50:13 +01:00
static long Mch_CreateMatch (long GamCod,char Title[Mch_MAX_BYTES_TITLE + 1])
2019-09-14 12:59:34 +02:00
{
long MchCod;
/***** Insert this new match into database *****/
MchCod =
DB_QueryINSERTandReturnCode ("can not create match",
"INSERT mch_matches"
" (GamCod,UsrCod,StartTime,EndTime,Title,"
"QstInd,QstCod,Showing,Countdown,"
"NumCols,ShowQstResults,ShowUsrResults)"
" VALUES"
" (%ld," // GamCod
"%ld," // UsrCod
"NOW()," // StartTime
"NOW()," // EndTime
"'%s'," // Title
"0," // QstInd: Match has not started, so not the first question yet
"-1," // QstCod: Non-existent question
"'%s'," // Showing: What is being shown
"-1," // Countdown: No countdown
"%u," // NumCols: Number of columns in answers
"'N'," // ShowQstResults: Don't show question results initially
"'N')", // ShowUsrResults: Don't show user results initially
GamCod,
Gbl.Usrs.Me.UsrDat.UsrCod, // Game creator
Title,
Mch_ShowingStringsDB[Mch_SHOWING_DEFAULT],
Mch_NUM_COLS_DEFAULT);
2019-09-14 12:59:34 +02:00
2019-09-23 21:39:54 +02:00
/***** Create indexes for answers *****/
Mch_CreateIndexes (GamCod,MchCod);
2019-09-14 12:59:34 +02:00
/***** Create groups associated to the match *****/
if (Gbl.Crs.Grps.LstGrpsSel.NumGrps)
Mch_CreateGrps (MchCod);
return MchCod;
}
2019-09-23 21:39:54 +02:00
/*****************************************************************************/
/*********************** Create indexes for a match **************************/
/*****************************************************************************/
/* Everytime a new match is created,
the answers of each shufflable question are shuffled.
The shuffling is stored in a table of indexes
that will be read when showing a match */
static void Mch_CreateIndexes (long GamCod,long MchCod)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumQsts;
unsigned NumQst;
2020-03-25 01:36:22 +01:00
struct Tst_Question Question;
2019-09-23 21:39:54 +02:00
long LongNum;
unsigned QstInd;
/***** Get questions of the game *****/
NumQsts = (unsigned)
DB_QuerySELECT (&mysql_res,"can not get questions of a game",
"SELECT gam_questions.QstCod," // row[0]
"gam_questions.QstInd," // row[1]
"tst_questions.AnsType," // row[2]
"tst_questions.Shuffle" // row[3]
" FROM gam_questions,tst_questions"
" WHERE gam_questions.GamCod=%ld"
" AND gam_questions.QstCod=tst_questions.QstCod"
" ORDER BY gam_questions.QstInd",
GamCod);
2019-09-23 21:39:54 +02:00
/***** For each question in game... *****/
for (NumQst = 0;
NumQst < NumQsts;
NumQst++)
{
2020-03-25 01:36:22 +01:00
/***** Create test question *****/
Tst_QstConstructor (&Question);
2019-09-23 21:39:54 +02:00
/***** Get question data *****/
row = mysql_fetch_row (mysql_res);
2020-03-25 01:36:22 +01:00
/*
gam_questions.QstCod row[0]
gam_questions.QstInd row[1]
tst_questions.AnsType row[2]
tst_questions.Shuffle row[3]
*/
2019-09-23 21:39:54 +02:00
/* Get question code (row[0]) */
2020-03-25 01:36:22 +01:00
if ((Question.QstCod = Str_ConvertStrCodToLongCod (row[0])) < 0)
Lay_WrongQuestionExit ();
2019-09-23 21:39:54 +02:00
/* Get question index (row[1]) */
if ((LongNum = Str_ConvertStrCodToLongCod (row[1])) < 0)
Lay_ShowErrorAndExit ("Wrong question index.");
QstInd = (unsigned) LongNum;
/* Get answer type (row[2]) */
2020-03-25 01:36:22 +01:00
Question.Answer.Type = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[2]);
if (Question.Answer.Type != Tst_ANS_UNIQUE_CHOICE)
2019-09-23 21:39:54 +02:00
Lay_ShowErrorAndExit ("Wrong answer type.");
/* Get shuffle (row[3]) */
2020-04-03 19:13:00 +02:00
Question.Answer.Shuffle = (row[3][0] == 'Y');
2019-09-23 21:39:54 +02:00
/***** Reorder answer *****/
2020-03-25 01:36:22 +01:00
Mch_ReorderAnswer (MchCod,QstInd,&Question);
/***** Destroy test question *****/
Tst_QstDestructor (&Question);
2019-09-23 21:39:54 +02:00
}
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
/*****************************************************************************/
/******************* Reorder answers of a match question *********************/
/*****************************************************************************/
static void Mch_ReorderAnswer (long MchCod,unsigned QstInd,
2020-03-25 01:36:22 +01:00
const struct Tst_Question *Question)
2019-09-23 21:39:54 +02:00
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumAnss;
unsigned NumAns;
long LongNum;
unsigned AnsInd;
2019-11-08 01:10:32 +01:00
char StrOneAnswer[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
2020-05-09 01:37:00 +02:00
char StrAnswersOneQst[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
2019-09-23 21:39:54 +02:00
/***** Initialize list of answers to empty string *****/
StrAnswersOneQst[0] = '\0';
/***** Get questions of the game *****/
NumAnss = (unsigned)
DB_QuerySELECT (&mysql_res,"can not get questions of a game",
"SELECT AnsInd" // row[0]
" FROM tst_answers"
" WHERE QstCod=%ld"
" ORDER BY %s",
Question->QstCod,
Question->Answer.Shuffle ? "RAND()" : // Use RAND() because is really random; RAND(NOW()) repeats order
"AnsInd");
2019-09-23 21:39:54 +02:00
/***** For each answer in question... *****/
for (NumAns = 0;
NumAns < NumAnss;
NumAns++)
{
row = mysql_fetch_row (mysql_res);
/* Get answer index (row[0]) */
if ((LongNum = Str_ConvertStrCodToLongCod (row[0])) < 0)
Lay_ShowErrorAndExit ("Wrong answer index.");
AnsInd = (unsigned) LongNum;
snprintf (StrOneAnswer,sizeof (StrOneAnswer),"%u",AnsInd);
2019-09-23 21:39:54 +02:00
/* Concatenate answer index to list of answers */
if (NumAns)
Str_Concat (StrAnswersOneQst,",",sizeof (StrAnswersOneQst) - 1);
Str_Concat (StrAnswersOneQst,StrOneAnswer,sizeof (StrAnswersOneQst) - 1);
2019-09-23 21:39:54 +02:00
}
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
/***** Create entry for this question in table of match indexes *****/
DB_QueryINSERT ("can not create match indexes",
"INSERT INTO mch_indexes"
" (MchCod,QstInd,Indexes)"
" VALUES"
" (%ld,%u,'%s')",
MchCod,
QstInd,
StrAnswersOneQst);
2019-09-23 21:39:54 +02:00
}
2019-09-24 01:41:51 +02:00
/*****************************************************************************/
/***************** Get indexes for a question from database ******************/
/*****************************************************************************/
void Mch_GetIndexes (long MchCod,unsigned QstInd,
unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION])
{
2020-05-09 01:37:00 +02:00
char StrIndexesOneQst[Tst_MAX_BYTES_INDEXES_ONE_QST + 1];
2019-09-24 01:41:51 +02:00
/***** Get indexes for a question from database *****/
DB_QuerySELECTString (StrIndexesOneQst,sizeof (StrIndexesOneQst) - 1,
"can not get data of a question",
"SELECT Indexes" // row[0]
" FROM mch_indexes"
" WHERE MchCod=%ld"
" AND QstInd=%u",
MchCod,
QstInd);
if (!StrIndexesOneQst[0])
2019-09-24 01:41:51 +02:00
Lay_ShowErrorAndExit ("No indexes found for a question.");
/***** Get indexes from string *****/
2020-05-07 18:33:26 +02:00
TstPrn_GetIndexesFromStr (StrIndexesOneQst,Indexes);
2019-09-24 01:41:51 +02:00
}
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
/******************* Create groups associated to a match *********************/
/*****************************************************************************/
static void Mch_CreateGrps (long MchCod)
{
unsigned NumGrpSel;
/***** Create groups associated to the match *****/
for (NumGrpSel = 0;
NumGrpSel < Gbl.Crs.Grps.LstGrpsSel.NumGrps;
NumGrpSel++)
/* Create group */
DB_QueryINSERT ("can not associate a group to a match",
2019-09-17 01:37:07 +02:00
"INSERT INTO mch_groups"
2019-09-14 12:59:34 +02:00
" (MchCod,GrpCod)"
" VALUES"
" (%ld,%ld)",
MchCod,
Gbl.Crs.Grps.LstGrpsSel.GrpCods[NumGrpSel]);
2019-09-14 12:59:34 +02:00
}
2019-09-27 20:22:32 +02:00
/*****************************************************************************/
/********************* Remove one group from all matches *********************/
/*****************************************************************************/
void Mch_RemoveGroup (long GrpCod)
{
/***** Remove group from all the matches *****/
DB_QueryDELETE ("can not remove group"
" from the associations between matches and groups",
"DELETE FROM mch_groups"
" WHERE GrpCod=%ld",
2019-09-27 20:22:32 +02:00
GrpCod);
}
/*****************************************************************************/
/***************** Remove groups of one type from all matches ****************/
/*****************************************************************************/
void Mch_RemoveGroupsOfType (long GrpTypCod)
{
/***** Remove group from all the matches *****/
DB_QueryDELETE ("can not remove groups of a type"
" from the associations between matches and groups",
"DELETE FROM mch_groups"
" USING grp_groups,"
"mch_groups"
" WHERE grp_groups.GrpTypCod=%ld"
" AND grp_groups.GrpCod=mch_groups.GrpCod",
2019-09-27 20:22:32 +02:00
GrpTypCod);
}
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
/***************** Insert/update a game match being played *******************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_UpdateMatchStatusInDB (const struct Mch_Match *Match)
2019-09-14 12:59:34 +02:00
{
2019-12-12 18:24:23 +01:00
char *MchSubQuery;
2019-09-17 21:01:57 +02:00
/***** Update end time only if match is currently being played *****/
2019-12-03 15:39:48 +01:00
if (Match->Status.Playing) // Match is being played
2019-12-12 18:24:23 +01:00
{
2020-03-12 13:53:37 +01:00
if (asprintf (&MchSubQuery,"mch_matches.EndTime=NOW(),") < 0)
2019-12-12 18:24:23 +01:00
Lay_NotEnoughMemoryExit ();
}
2019-12-03 15:39:48 +01:00
else // Match is paused, not being played
2019-12-12 18:24:23 +01:00
{
if (asprintf (&MchSubQuery,"%s","") < 0)
Lay_NotEnoughMemoryExit ();
}
2019-09-17 21:01:57 +02:00
2019-09-14 12:59:34 +02:00
/***** Update match status in database *****/
DB_QueryUPDATE ("can not update match being played",
"UPDATE mch_matches,"
"gam_games"
" SET %s"
"mch_matches.QstInd=%u,"
"mch_matches.QstCod=%ld,"
"mch_matches.Showing='%s',"
"mch_matches.Countdown=%ld,"
"mch_matches.NumCols=%u,"
"mch_matches.ShowQstResults='%c',"
"mch_matches.ShowUsrResults='%c'"
2019-09-17 01:37:07 +02:00
" WHERE mch_matches.MchCod=%ld"
" AND mch_matches.GamCod=gam_games.GamCod"
" AND gam_games.CrsCod=%ld", // Extra check
2019-09-17 21:01:57 +02:00
MchSubQuery,
Match->Status.QstInd,
Match->Status.QstCod,
2019-09-14 12:59:34 +02:00
Mch_ShowingStringsDB[Match->Status.Showing],
2019-12-12 18:24:23 +01:00
Match->Status.Countdown,
2019-10-22 23:42:22 +02:00
Match->Status.NumCols,
2020-06-23 12:57:31 +02:00
Match->Status.ShowQstResults ? 'Y' :
'N',
Match->Status.ShowUsrResults ? 'Y' :
'N',
Match->MchCod,
Gbl.Hierarchy.Crs.CrsCod);
2019-12-12 18:24:23 +01:00
free (MchSubQuery);
2019-09-14 12:59:34 +02:00
2019-12-03 15:39:48 +01:00
if (Match->Status.Playing) // Match is being played
2019-09-14 12:59:34 +02:00
/* Update match as being played */
Mch_UpdateMatchAsBeingPlayed (Match->MchCod);
2019-12-03 15:39:48 +01:00
else // Match is paused, not being played
2019-09-14 12:59:34 +02:00
/* Update match as not being played */
Mch_SetMatchAsNotBeingPlayed (Match->MchCod);
}
/*****************************************************************************/
/********** Update elapsed time in current question (by a teacher) ***********/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_UpdateElapsedTimeInQuestion (const struct Mch_Match *Match)
2019-09-14 12:59:34 +02:00
{
/***** Update elapsed time in current question in database *****/
2019-12-03 15:39:48 +01:00
if (Match->Status.Playing && // Match is being played
2019-12-10 21:52:22 +01:00
Match->Status.Showing != Mch_START &&
Match->Status.Showing != Mch_END)
2019-09-14 12:59:34 +02:00
DB_QueryINSERT ("can not update elapsed time in question",
"INSERT INTO mch_times"
" (MchCod,QstInd,ElapsedTime)"
" VALUES"
" (%ld,%u,SEC_TO_TIME(%u))"
2019-09-14 12:59:34 +02:00
" ON DUPLICATE KEY"
" UPDATE ElapsedTime=ADDTIME(ElapsedTime,SEC_TO_TIME(%u))",
Match->MchCod,
Match->Status.QstInd,
2019-10-01 21:18:53 +02:00
Cfg_SECONDS_TO_REFRESH_MATCH_TCH,
Cfg_SECONDS_TO_REFRESH_MATCH_TCH);
2019-09-14 12:59:34 +02:00
}
/*****************************************************************************/
/******************* Get elapsed time in a match question ********************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_GetElapsedTimeInQuestion (const struct Mch_Match *Match,
2019-09-14 12:59:34 +02:00
struct Time *Time)
{
MYSQL_RES *mysql_res;
unsigned NumRows;
/***** Query database *****/
NumRows = (unsigned)
DB_QuerySELECT (&mysql_res,"can not get elapsed time",
"SELECT ElapsedTime" // row[0]
" FROM mch_times"
" WHERE MchCod=%ld"
" AND QstInd=%u",
Match->MchCod,
Match->Status.QstInd);
2019-09-14 12:59:34 +02:00
/***** Get elapsed time from query result *****/
Mch_GetElapsedTime (NumRows,mysql_res,Time);
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
/*****************************************************************************/
/*********************** Get elapsed time in a match *************************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_GetElapsedTimeInMatch (const struct Mch_Match *Match,
2019-09-14 12:59:34 +02:00
struct Time *Time)
{
MYSQL_RES *mysql_res;
unsigned NumRows;
/***** Query database *****/
NumRows = (unsigned)
DB_QuerySELECT (&mysql_res,"can not get elapsed time",
"SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(ElapsedTime)))" // row[0]
" FROM mch_times"
" WHERE MchCod=%ld",
Match->MchCod);
2019-09-14 12:59:34 +02:00
/***** Get elapsed time from query result *****/
Mch_GetElapsedTime (NumRows,mysql_res,Time);
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
/*****************************************************************************/
/*********************** Get elapsed time in a match *************************/
/*****************************************************************************/
static void Mch_GetElapsedTime (unsigned NumRows,MYSQL_RES *mysql_res,
struct Time *Time)
{
MYSQL_ROW row;
bool ElapsedTimeGotFromDB = false;
/***** Get time from H...H:MM:SS string *****/
if (NumRows)
{
row = mysql_fetch_row (mysql_res);
if (row[0])
/* Get the elapsed time (row[0]) */
if (sscanf (row[0],"%u:%02u:%02u",
&Time->Hour,
&Time->Minute,
&Time->Second) == 3)
2019-09-14 12:59:34 +02:00
ElapsedTimeGotFromDB = true;
}
/***** Initialize time to default value (0) *****/
if (!ElapsedTimeGotFromDB)
Time->Hour =
Time->Minute =
Time->Second = 0;
}
/*****************************************************************************/
2019-10-18 00:05:21 +02:00
/********************** Play/pause match (by a teacher) **********************/
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
2019-10-18 00:05:21 +02:00
void Mch_PlayPauseMatch (void)
2019-09-14 12:59:34 +02:00
{
2020-04-08 03:06:45 +02:00
struct Mch_Match Match;
2019-09-14 12:59:34 +02:00
2020-04-23 23:09:28 +02:00
/***** Reset match *****/
Mch_ResetMatch (&Match);
2019-09-14 12:59:34 +02:00
/***** Remove old players.
2019-10-01 21:18:53 +02:00
This function must be called by a teacher
before getting match status. *****/
2019-09-14 12:59:34 +02:00
Mch_RemoveOldPlayers ();
/***** Get data of the match from database *****/
2020-04-08 03:06:45 +02:00
Match.MchCod = Mch_GetMchCodBeingPlayed ();
2019-09-14 12:59:34 +02:00
Mch_GetDataOfMatchByCod (&Match);
2019-10-17 22:47:44 +02:00
/***** Update status *****/
2019-12-03 15:39:48 +01:00
if (Match.Status.Playing) // Match is being played ==> pause it
2019-12-10 21:52:22 +01:00
Match.Status.Playing = false; // Pause match
2019-12-03 15:39:48 +01:00
else // Match is paused, not being played ==> play it
2019-12-10 21:52:22 +01:00
/* If not over, update status */
if (Match.Status.Showing != Mch_END) // Match not over
Match.Status.Playing = true; // Start/resume match
2019-09-14 12:59:34 +02:00
/***** Update match status in database *****/
Mch_UpdateMatchStatusInDB (&Match);
/***** Show current match status *****/
2019-10-24 00:04:40 +02:00
HTM_DIV_Begin ("id=\"match\" class=\"MCH_CONT\"");
2019-09-14 12:59:34 +02:00
Mch_ShowMatchStatusForTch (&Match);
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-09-14 12:59:34 +02:00
}
2019-10-23 00:49:03 +02:00
/*****************************************************************************/
/******* Change number of columns in answers of a match (by a teacher) *******/
/*****************************************************************************/
void Mch_ChangeNumColsMch (void)
{
2020-04-08 03:06:45 +02:00
struct Mch_Match Match;
2019-10-23 00:49:03 +02:00
2020-04-23 23:09:28 +02:00
/***** Reset match *****/
Mch_ResetMatch (&Match);
2019-10-23 00:49:03 +02:00
/***** Remove old players.
This function must be called by a teacher
before getting match status. *****/
Mch_RemoveOldPlayers ();
/***** Get data of the match from database *****/
2020-04-08 03:06:45 +02:00
Match.MchCod = Mch_GetMchCodBeingPlayed ();
2019-10-23 00:49:03 +02:00
Mch_GetDataOfMatchByCod (&Match);
/***** Get number of columns *****/
Match.Status.NumCols = (unsigned)
Par_GetParToUnsignedLong ("NumCols",
1,
Mch_MAX_COLS,
Mch_NUM_COLS_DEFAULT);
/***** Update match status in database *****/
Mch_UpdateMatchStatusInDB (&Match);
/***** Show current match status *****/
2019-10-24 00:04:40 +02:00
HTM_DIV_Begin ("id=\"match\" class=\"MCH_CONT\"");
2019-10-23 00:49:03 +02:00
Mch_ShowMatchStatusForTch (&Match);
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-10-23 00:49:03 +02:00
}
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
2019-09-14 19:46:26 +02:00
/********* Toggle the display of results in a match (by a teacher) ***********/
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
2020-05-16 02:04:36 +02:00
void Mch_ToggleVisResultsMchQst (void)
2019-09-14 12:59:34 +02:00
{
2020-04-08 03:06:45 +02:00
struct Mch_Match Match;
2019-09-14 12:59:34 +02:00
2020-04-23 23:09:28 +02:00
/***** Reset match *****/
Mch_ResetMatch (&Match);
2019-09-14 12:59:34 +02:00
/***** Remove old players.
2019-10-01 21:18:53 +02:00
This function must be called by a teacher
before getting match status. *****/
2019-09-14 12:59:34 +02:00
Mch_RemoveOldPlayers ();
/***** Get data of the match from database *****/
2020-04-08 03:06:45 +02:00
Match.MchCod = Mch_GetMchCodBeingPlayed ();
2019-09-14 12:59:34 +02:00
Mch_GetDataOfMatchByCod (&Match);
/***** Update status *****/
2019-09-25 21:58:12 +02:00
Match.Status.ShowQstResults = !Match.Status.ShowQstResults; // Toggle display
if (Match.Status.Showing == Mch_RESULTS &&
!Match.Status.ShowQstResults)
2019-09-14 19:46:26 +02:00
Match.Status.Showing = Mch_ANSWERS; // Hide results
2019-09-14 12:59:34 +02:00
/***** Update match status in database *****/
Mch_UpdateMatchStatusInDB (&Match);
/***** Show current match status *****/
2019-10-24 00:04:40 +02:00
HTM_DIV_Begin ("id=\"match\" class=\"MCH_CONT\"");
2019-09-14 12:59:34 +02:00
Mch_ShowMatchStatusForTch (&Match);
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-09-14 12:59:34 +02:00
}
/*****************************************************************************/
/************* Show previous question in a match (by a teacher) **************/
/*****************************************************************************/
2019-09-29 21:33:27 +02:00
void Mch_BackMatch (void)
2019-09-14 12:59:34 +02:00
{
2020-04-08 03:06:45 +02:00
struct Mch_Match Match;
2019-09-14 12:59:34 +02:00
2020-04-23 23:09:28 +02:00
/***** Reset match *****/
Mch_ResetMatch (&Match);
2019-09-14 12:59:34 +02:00
/***** Remove old players.
2019-10-01 21:18:53 +02:00
This function must be called by a teacher
before getting match status. *****/
2019-09-14 12:59:34 +02:00
Mch_RemoveOldPlayers ();
/***** Get data of the match from database *****/
2020-04-08 03:06:45 +02:00
Match.MchCod = Mch_GetMchCodBeingPlayed ();
2019-09-14 12:59:34 +02:00
Mch_GetDataOfMatchByCod (&Match);
/***** Update status *****/
Mch_SetMatchStatusToPrev (&Match);
/***** Update match status in database *****/
Mch_UpdateMatchStatusInDB (&Match);
/***** Show current match status *****/
2019-10-24 00:04:40 +02:00
HTM_DIV_Begin ("id=\"match\" class=\"MCH_CONT\"");
2019-09-14 12:59:34 +02:00
Mch_ShowMatchStatusForTch (&Match);
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-09-14 12:59:34 +02:00
}
/*****************************************************************************/
/*************** Show next question in a match (by a teacher) ****************/
/*****************************************************************************/
2019-09-29 21:33:27 +02:00
void Mch_ForwardMatch (void)
2019-09-14 12:59:34 +02:00
{
2020-04-08 03:06:45 +02:00
struct Mch_Match Match;
2019-09-14 12:59:34 +02:00
2020-04-23 23:09:28 +02:00
/***** Reset match *****/
Mch_ResetMatch (&Match);
2019-09-14 12:59:34 +02:00
/***** Remove old players.
2019-10-01 21:18:53 +02:00
This function must be called by a teacher
before getting match status. *****/
2019-09-14 12:59:34 +02:00
Mch_RemoveOldPlayers ();
/***** Get data of the match from database *****/
2020-04-08 03:06:45 +02:00
Match.MchCod = Mch_GetMchCodBeingPlayed ();
2019-09-14 12:59:34 +02:00
Mch_GetDataOfMatchByCod (&Match);
/***** Update status *****/
Mch_SetMatchStatusToNext (&Match);
/***** Update match status in database *****/
Mch_UpdateMatchStatusInDB (&Match);
/***** Show current match status *****/
2019-10-24 00:04:40 +02:00
HTM_DIV_Begin ("id=\"match\" class=\"MCH_CONT\"");
2019-09-14 12:59:34 +02:00
Mch_ShowMatchStatusForTch (&Match);
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-09-14 12:59:34 +02:00
}
/*****************************************************************************/
/************** Set match status to previous (backward) status ***************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_SetMatchStatusToPrev (struct Mch_Match *Match)
2019-09-14 12:59:34 +02:00
{
/***** What to show *****/
2019-12-10 21:52:22 +01:00
switch (Match->Status.Showing)
{
case Mch_START:
Mch_SetMatchStatusToStart (Match);
break;
case Mch_STEM:
case Mch_END:
Mch_SetMatchStatusToPrevQst (Match);
break;
case Mch_ANSWERS:
Match->Status.Showing = Mch_STEM;
break;
case Mch_RESULTS:
Match->Status.Showing = Mch_ANSWERS;
break;
}
2020-06-23 12:57:31 +02:00
Match->Status.Countdown = -1L; // No countdown
/***** Force showing results to false when match is not at the end *****/
Match->Status.ShowUsrResults = false; // Force results to be hidden
2019-09-17 15:42:41 +02:00
}
2019-12-14 01:31:32 +01:00
/*****************************************************************************/
/****************** Set match status to previous question ********************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_SetMatchStatusToPrevQst (struct Mch_Match *Match)
2019-09-17 15:42:41 +02:00
{
/***** Get index of the previous question *****/
Match->Status.QstInd = Gam_GetPrevQuestionIndexInGame (Match->GamCod,
Match->Status.QstInd);
2019-12-10 21:52:22 +01:00
if (Match->Status.QstInd) // Start of questions not reached
2019-09-14 12:59:34 +02:00
{
2019-09-17 15:42:41 +02:00
Match->Status.QstCod = Gam_GetQstCodFromQstInd (Match->GamCod,
Match->Status.QstInd);
2019-09-25 21:58:12 +02:00
Match->Status.Showing = Match->Status.ShowQstResults ? Mch_RESULTS :
Mch_ANSWERS;
2019-09-14 12:59:34 +02:00
}
2019-12-10 21:52:22 +01:00
else // Start of questions reached
Mch_SetMatchStatusToStart (Match);
2019-09-14 12:59:34 +02:00
}
2019-12-14 01:31:32 +01:00
/*****************************************************************************/
/************************ Set match status to start **************************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_SetMatchStatusToStart (struct Mch_Match *Match)
2019-09-17 15:42:41 +02:00
{
Match->Status.QstInd = 0; // Before first question
Match->Status.QstCod = -1L;
Match->Status.Playing = false;
2019-12-10 21:52:22 +01:00
Match->Status.Showing = Mch_START;
2019-09-17 15:42:41 +02:00
}
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
/**************** Set match status to next (forward) status ******************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_SetMatchStatusToNext (struct Mch_Match *Match)
2019-09-14 12:59:34 +02:00
{
/***** What to show *****/
2019-12-10 21:52:22 +01:00
switch (Match->Status.Showing)
{
case Mch_START:
Mch_SetMatchStatusToNextQst (Match);
break;
case Mch_STEM:
Match->Status.Showing = Mch_ANSWERS;
break;
case Mch_ANSWERS:
if (Match->Status.ShowQstResults)
Match->Status.Showing = Mch_RESULTS;
else
2019-09-17 15:42:41 +02:00
Mch_SetMatchStatusToNextQst (Match);
2019-12-10 21:52:22 +01:00
break;
case Mch_RESULTS:
Mch_SetMatchStatusToNextQst (Match);
break;
case Mch_END:
Mch_SetMatchStatusToEnd (Match);
break;
}
2019-12-12 18:24:23 +01:00
Match->Status.Countdown = -1L; // No countdown
2019-09-17 15:42:41 +02:00
}
2019-12-14 01:31:32 +01:00
/*****************************************************************************/
/****************** Set match status to next question ************************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_SetMatchStatusToNextQst (struct Mch_Match *Match)
2019-09-17 15:42:41 +02:00
{
/***** Get index of the next question *****/
Match->Status.QstInd = Gam_GetNextQuestionIndexInGame (Match->GamCod,
Match->Status.QstInd);
/***** Get question code *****/
2019-12-10 21:52:22 +01:00
if (Match->Status.QstInd < Mch_AFTER_LAST_QUESTION) // End of questions not reached
2019-09-17 15:42:41 +02:00
{
Match->Status.QstCod = Gam_GetQstCodFromQstInd (Match->GamCod,
Match->Status.QstInd);
Match->Status.Showing = Mch_STEM;
2019-09-14 19:46:26 +02:00
}
2019-12-10 21:52:22 +01:00
else // End of questions reached
Mch_SetMatchStatusToEnd (Match);
2019-09-14 12:59:34 +02:00
}
2019-12-14 01:31:32 +01:00
/*****************************************************************************/
/************************* Set match status to end ***************************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_SetMatchStatusToEnd (struct Mch_Match *Match)
2019-09-17 15:42:41 +02:00
{
Match->Status.QstInd = Mch_AFTER_LAST_QUESTION; // After last question
Match->Status.QstCod = -1L;
Match->Status.Playing = false;
2019-12-10 21:52:22 +01:00
Match->Status.Showing = Mch_END;
2019-09-17 15:42:41 +02:00
}
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
/******* Show current match status (number, question, answers, button) *******/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_ShowMatchStatusForTch (struct Mch_Match *Match)
2019-09-14 12:59:34 +02:00
{
/***** Left column *****/
Mch_ShowLeftColumnTch (Match);
/***** Right column *****/
2019-09-26 18:19:07 +02:00
Mch_ShowRightColumnTch (Match);
2019-09-14 12:59:34 +02:00
}
/*****************************************************************************/
/************ Show current question being played for a student ***************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_ShowMatchStatusForStd (struct Mch_Match *Match,Mch_Update_t Update)
2019-09-14 12:59:34 +02:00
{
2019-09-29 19:36:32 +02:00
bool ICanPlayThisMatchBasedOnGrps;
2019-12-03 02:46:46 +01:00
struct Mch_UsrAnswer UsrAnswer;
2019-09-14 12:59:34 +02:00
2019-09-29 19:36:32 +02:00
/***** Can I play this match? *****/
2019-12-05 19:54:28 +01:00
ICanPlayThisMatchBasedOnGrps = Mch_CheckIfICanPlayThisMatchBasedOnGrps (Match);
2019-09-29 19:36:32 +02:00
if (!ICanPlayThisMatchBasedOnGrps)
2019-10-26 01:56:36 +02:00
Lay_NoPermissionExit ();
2019-09-14 12:59:34 +02:00
2019-12-03 02:46:46 +01:00
/***** Get student's answer to this question
(<0 ==> no answer) *****/
Mch_GetQstAnsFromDB (Match->MchCod,
Gbl.Usrs.Me.UsrDat.UsrCod,
Match->Status.QstInd,
&UsrAnswer);
2019-09-14 12:59:34 +02:00
/***** Left column *****/
2019-12-03 02:46:46 +01:00
Mch_ShowLeftColumnStd (Match,&UsrAnswer);
2019-09-14 12:59:34 +02:00
/***** Right column *****/
2019-12-03 02:46:46 +01:00
Mch_ShowRightColumnStd (Match,&UsrAnswer,Update);
2019-09-14 12:59:34 +02:00
}
2019-09-26 01:15:21 +02:00
/*****************************************************************************/
2019-12-10 21:52:22 +01:00
/********************** Get number of matches in a game **********************/
2019-09-26 01:15:21 +02:00
/*****************************************************************************/
2019-09-28 02:31:42 +02:00
unsigned Mch_GetNumMchsInGame (long GamCod)
2019-09-26 01:15:21 +02:00
{
2019-09-26 21:27:36 +02:00
/***** Trivial check *****/
if (GamCod < 0) // A non-existing game...
return 0; // ...has no matches
2019-09-26 01:15:21 +02:00
/***** Get number of matches in a game from database *****/
return (unsigned)
DB_QueryCOUNT ("can not get number of matches of a game",
"SELECT COUNT(*)"
" FROM mch_matches"
" WHERE GamCod=%ld",
GamCod);
2019-09-26 01:15:21 +02:00
}
2019-12-10 21:52:22 +01:00
/*****************************************************************************/
/*************** Get number of unfinished matches in a game ******************/
/*****************************************************************************/
unsigned Mch_GetNumUnfinishedMchsInGame (long GamCod)
{
/***** Trivial check *****/
if (GamCod < 0) // A non-existing game...
return 0; // ...has no matches
/***** Get number of matches in a game from database *****/
return (unsigned)
DB_QueryCOUNT ("can not get number of unfinished matches of a game",
"SELECT COUNT(*)"
" FROM mch_matches"
" WHERE GamCod=%ld"
" AND Showing<>'%s'",
GamCod,
Mch_ShowingStringsDB[Mch_END]);
2019-12-10 21:52:22 +01:00
}
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
/************ Check if I belong to any of the groups of a match **************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
bool Mch_CheckIfICanPlayThisMatchBasedOnGrps (const struct Mch_Match *Match)
2019-09-14 12:59:34 +02:00
{
2019-09-29 19:36:32 +02:00
switch (Gbl.Usrs.Me.Role.Logged)
{
case Rol_STD:
2019-12-05 19:54:28 +01:00
/***** Check if I belong to any of the groups
associated to the match *****/
return (DB_QueryCOUNT ("can not check if I can play a match",
"SELECT COUNT(*)"
" FROM mch_matches"
2019-12-05 19:54:28 +01:00
" WHERE MchCod=%ld"
" AND (MchCod NOT IN"
" (SELECT MchCod"
" FROM mch_groups)"
" OR"
" MchCod IN"
" (SELECT mch_groups.MchCod"
" FROM grp_users,"
"mch_groups"
" WHERE grp_users.UsrCod=%ld"
" AND grp_users.GrpCod=mch_groups.GrpCod))",
Match->MchCod,
Gbl.Usrs.Me.UsrDat.UsrCod) != 0);
2019-09-29 19:36:32 +02:00
break;
case Rol_NET:
2019-12-05 19:54:28 +01:00
/***** Only if I am the creator *****/
return (Match->UsrCod == Gbl.Usrs.Me.UsrDat.UsrCod);
2019-09-29 19:36:32 +02:00
case Rol_TCH:
case Rol_SYS_ADM:
return true;
default:
return false;
}
2019-09-14 12:59:34 +02:00
}
/*****************************************************************************/
2019-09-26 18:19:07 +02:00
/*********** Show left column when playing a match (as a teacher) ************/
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_ShowLeftColumnTch (struct Mch_Match *Match)
2019-09-26 19:54:09 +02:00
{
/***** Start left container *****/
2020-03-12 13:53:37 +01:00
HTM_DIV_Begin ("class=\"MCH_LEFT_TCH\"");
2019-09-30 16:25:44 +02:00
/***** Refreshable part *****/
2019-10-24 00:04:40 +02:00
HTM_DIV_Begin ("id=\"match_left\" class=\"MCH_REFRESHABLE_TEACHER\"");
2019-09-30 16:25:44 +02:00
Mch_ShowRefreshablePartTch (Match);
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-09-26 19:54:09 +02:00
2019-12-14 01:31:32 +01:00
/***** Put forms to start countdown *****/
Mch_PutFormsCountdown (Match);
2019-09-30 16:25:44 +02:00
/***** Buttons *****/
Mch_PutMatchControlButtons (Match);
2019-10-22 20:43:03 +02:00
/***** Put forms to choice which projects to show *****/
Set_BeginSettingsHead ();
2019-10-22 22:39:37 +02:00
Mch_ShowFormColumns (Match);
2019-10-22 20:43:03 +02:00
Set_EndSettingsHead ();
2019-09-30 16:25:44 +02:00
/***** Write button to request viewing results *****/
2019-10-31 08:47:16 +01:00
Mch_PutCheckboxResult (Match);
2019-09-26 19:54:09 +02:00
/***** End left container *****/
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-09-26 19:54:09 +02:00
}
2019-12-14 01:31:32 +01:00
/*****************************************************************************/
/***************** Show left refreshable part for teachers *******************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_ShowRefreshablePartTch (struct Mch_Match *Match)
2019-09-14 12:59:34 +02:00
{
2019-12-12 09:14:26 +01:00
/***** Write elapsed time in match *****/
Mch_WriteElapsedTimeInMch (Match);
/***** Write number of question *****/
Mch_ShowNumQstInMch (Match);
/***** Write elapsed time in question *****/
Mch_WriteElapsedTimeInQst (Match);
/***** Number of users who have responded this question *****/
Mch_WriteNumRespondersQst (Match);
2019-12-14 01:31:32 +01:00
/***** Write hourglass *****/
Mch_PutCountdownAndHourglassIcon (Match);
2019-12-12 09:14:26 +01:00
}
2019-12-14 01:31:32 +01:00
/*****************************************************************************/
/******************** Write elapsed time in current match ********************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_WriteElapsedTimeInMch (struct Mch_Match *Match)
2019-12-12 09:14:26 +01:00
{
2019-09-14 12:59:34 +02:00
struct Time Time;
2020-03-12 13:53:37 +01:00
HTM_DIV_Begin ("class=\"MCH_TOP CT\"");
2019-09-14 12:59:34 +02:00
2019-12-12 09:14:26 +01:00
/***** Get elapsed time in match *****/
2019-09-14 12:59:34 +02:00
Mch_GetElapsedTimeInMatch (Match,&Time);
2019-12-12 09:14:26 +01:00
/***** Write elapsed time in hh:mm'ss" format *****/
2019-09-14 12:59:34 +02:00
Dat_WriteHoursMinutesSeconds (&Time);
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-12-12 09:14:26 +01:00
}
2019-09-14 12:59:34 +02:00
2019-12-14 01:31:32 +01:00
/*****************************************************************************/
/****************** Write elapsed time in current question *******************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_WriteElapsedTimeInQst (struct Mch_Match *Match)
2019-12-12 09:14:26 +01:00
{
struct Time Time;
2019-09-14 12:59:34 +02:00
2019-10-24 00:04:40 +02:00
HTM_DIV_Begin ("class=\"MCH_TIME_QST\"");
2019-12-12 09:14:26 +01:00
switch (Match->Status.Showing)
2019-09-14 12:59:34 +02:00
{
2019-12-12 09:14:26 +01:00
case Mch_START:
case Mch_END:
HTM_Hyphen (); // Do not write elapsed time
break;
default:
Mch_GetElapsedTimeInQuestion (Match,&Time);
Dat_WriteHoursMinutesSeconds (&Time);
break;
2019-09-14 12:59:34 +02:00
}
2019-12-12 09:14:26 +01:00
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-12-12 09:14:26 +01:00
}
2019-09-14 12:59:34 +02:00
2019-12-14 01:31:32 +01:00
/*****************************************************************************/
/*************** Write number of responders to a match question **************/
/*****************************************************************************/
2019-12-12 18:24:23 +01:00
2020-04-08 03:06:45 +02:00
static void Mch_WriteNumRespondersQst (struct Mch_Match *Match)
2019-12-13 23:38:29 +01:00
{
2019-12-14 01:31:32 +01:00
extern const char *Txt_MATCH_respond;
2019-12-13 23:38:29 +01:00
2019-12-14 01:31:32 +01:00
/***** Begin block *****/
HTM_DIV_Begin ("class=\"MCH_NUM_ANSWERERS\"");
HTM_Txt (Txt_MATCH_respond);
HTM_BR ();
HTM_STRONG_Begin ();
2019-12-13 23:38:29 +01:00
2019-12-14 01:31:32 +01:00
/***** Write number of responders *****/
switch (Match->Status.Showing)
2019-12-13 23:38:29 +01:00
{
2019-12-14 01:31:32 +01:00
case Mch_START:
case Mch_END:
HTM_Hyphen (); // Do not write number of responders
break;
default:
HTM_Unsigned (Mch_GetNumUsrsWhoAnsweredQst (Match->MchCod,
Match->Status.QstInd));
break;
2019-12-13 23:38:29 +01:00
}
2019-12-12 18:24:23 +01:00
2019-12-14 01:31:32 +01:00
/***** Write number of players *****/
if (Match->Status.Playing) // Match is being played
2019-12-13 00:36:01 +01:00
{
2019-12-14 01:31:32 +01:00
/* Get current number of players */
Mch_GetNumPlayers (Match);
2019-12-12 18:24:23 +01:00
2019-12-14 01:31:32 +01:00
/* Show current number of players */
HTM_TxtF ("/%u",Match->Status.NumPlayers);
2019-12-13 00:36:01 +01:00
}
2019-12-12 18:24:23 +01:00
2019-12-14 01:31:32 +01:00
/***** End block *****/
HTM_STRONG_End ();
2019-12-12 18:24:23 +01:00
HTM_DIV_End ();
}
2019-12-14 01:31:32 +01:00
/*****************************************************************************/
/*************** Write current countdown and hourglass icon ******************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_PutCountdownAndHourglassIcon (struct Mch_Match *Match)
2019-12-13 01:04:09 +01:00
{
extern const char *Txt_Countdown;
const char *Class;
const char *Icon;
/***** Set hourglass icon depending on countdown *****/
if (Match->Status.Showing == Mch_END) // Match over
{
Class = "BT_LINK_OFF MCH_BUTTON_HIDDEN MCH_GREEN";
Icon = "fa-hourglass-start";
}
else if (Match->Status.Countdown < 0) // No countdown
{
Class = "BT_LINK_OFF MCH_BUTTON_OFF MCH_GREEN";
Icon = "fa-hourglass-start";
}
else if (Match->Status.Countdown > Mch_COUNTDOWN_SECONDS_MEDIUM) // Countdown in progress
{
Class = "BT_LINK_OFF MCH_BUTTON_OFF MCH_LIMEGREEN";
Icon = "fa-hourglass-start";
}
else if (Match->Status.Countdown > Mch_COUNTDOWN_SECONDS_SMALL) // Countdown in progress
{
Class = "BT_LINK_OFF MCH_BUTTON_OFF MCH_YELLOW";
Icon = "fa-hourglass-half";
}
else // Countdown about to end
{
Class = "BT_LINK_OFF MCH_BUTTON_OFF MCH_RED";
Icon = "fa-hourglass-end";
}
2019-12-13 01:21:09 +01:00
/***** Write countdown and put hourglass icon *****/
2019-12-14 01:31:32 +01:00
HTM_DIV_Begin ("class=\"MCH_SHOW_HOURGLASS\"");
2019-12-13 01:04:09 +01:00
HTM_DIV_Begin ("class=\"MCH_BIGBUTTON_CONT\"");
HTM_BUTTON_BUTTON_Begin (Txt_Countdown,Class,NULL);
2019-12-13 01:21:09 +01:00
/* Countdown */
2019-12-13 01:04:09 +01:00
if (Match->Status.Countdown > 0)
HTM_TxtF ("&nbsp;%02ld&Prime;",Match->Status.Countdown);
else
HTM_NBSP ();
2019-12-13 01:21:09 +01:00
HTM_BR ();
/* Icon */
HTM_TxtF ("<i class=\"fas %s\"></i>",Icon);
2019-12-13 01:04:09 +01:00
HTM_BUTTON_End ();
HTM_DIV_End ();
2019-12-14 01:31:32 +01:00
HTM_DIV_End ();
2019-12-13 01:04:09 +01:00
}
/*****************************************************************************/
2019-12-14 01:31:32 +01:00
/******************** Put all forms to start countdowns **********************/
2019-12-13 01:04:09 +01:00
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_PutFormsCountdown (struct Mch_Match *Match)
2019-12-12 09:14:26 +01:00
{
/***** Begin container *****/
2019-12-14 01:31:32 +01:00
HTM_DIV_Begin ("class=\"MCH_SHOW_HOURGLASS\"");
2019-12-12 09:14:26 +01:00
2019-12-14 01:31:32 +01:00
/***** Put forms to start countdown *****/
Mch_PutFormCountdown (Match,-1 ,"MCH_GREEN" );
Mch_PutFormCountdown (Match,Mch_COUNTDOWN_SECONDS_LARGE ,"MCH_LIMEGREEN");
Mch_PutFormCountdown (Match,Mch_COUNTDOWN_SECONDS_MEDIUM,"MCH_YELLOW" );
Mch_PutFormCountdown (Match,Mch_COUNTDOWN_SECONDS_SMALL ,"MCH_RED" );
2019-12-12 09:14:26 +01:00
2019-12-14 01:31:32 +01:00
/***** End container *****/
HTM_DIV_End ();
}
/*****************************************************************************/
/****** Put a form to start a countdown with a given number of seconds *******/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_PutFormCountdown (struct Mch_Match *Match,long Seconds,const char *Color)
2019-12-14 01:31:32 +01:00
{
extern const char *Txt_Countdown;
char *OnSubmit;
bool PutForm = Match->Status.Showing != Mch_END;
if (PutForm)
2019-12-12 09:14:26 +01:00
{
2020-05-11 02:28:38 +02:00
/***** Begin form *****/
2019-12-14 01:31:32 +01:00
if (asprintf (&OnSubmit,"updateMatchTch('match_left',"
"'act=%ld&ses=%s&MchCod=%ld&Countdown=%ld');"
" return false;", // return false is necessary to not submit form
Act_GetActCod (ActMchCntDwn),Gbl.Session.Id,
Match->MchCod,Seconds) < 0)
Lay_NotEnoughMemoryExit ();
Frm_BeginFormOnSubmit (ActUnk,OnSubmit);
2019-12-14 01:31:32 +01:00
}
2019-09-30 14:55:55 +02:00
2019-12-14 01:31:32 +01:00
/***** Put icon *****/
HTM_DIV_Begin ("class=\"MCH_SMALLBUTTON_CONT\"");
2019-12-30 21:47:07 +01:00
HTM_BUTTON_SUBMIT_Begin (PutForm ? Txt_Countdown :
NULL,
Str_BuildStringStr (PutForm ? "BT_LINK MCH_BUTTON_ON %s" :
"BT_LINK_OFF MCH_BUTTON_HIDDEN %s",
Color),
NULL);
Str_FreeString ();
2019-12-14 01:31:32 +01:00
HTM_NBSP ();
if (Seconds >= 0)
HTM_TxtF ("%ld&Prime;",Seconds);
else
{
HTM_Txt ("&infin;");
HTM_NBSP ();
2019-10-18 00:05:21 +02:00
}
2019-12-12 09:14:26 +01:00
2019-12-14 01:31:32 +01:00
HTM_BUTTON_End ();
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-12-14 01:31:32 +01:00
/***** End form *****/
if (PutForm)
{
Frm_EndForm ();
free (OnSubmit);
}
2019-09-14 12:59:34 +02:00
}
/*****************************************************************************/
2019-09-26 18:19:07 +02:00
/********** Show right column when playing a match (as a teacher) ************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_ShowRightColumnTch (const struct Mch_Match *Match)
2019-09-26 18:19:07 +02:00
{
/***** Start right container *****/
2020-03-12 13:53:37 +01:00
HTM_DIV_Begin ("class=\"MCH_RIGHT_TCH\"");
2019-09-26 18:19:07 +02:00
/***** Top row: match title *****/
2020-03-12 13:53:37 +01:00
Mch_ShowMatchTitleTch (Match);
2019-09-26 18:19:07 +02:00
/***** Bottom row: current question and possible answers *****/
2019-12-10 21:52:22 +01:00
if (Match->Status.Showing == Mch_END) // Match over
2019-10-21 13:36:28 +02:00
Mch_ShowMatchScore (Match);
2019-12-10 21:52:22 +01:00
else // Match not over
Mch_ShowQuestionAndAnswersTch (Match);
2019-09-26 18:19:07 +02:00
/***** End right container *****/
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-09-26 18:19:07 +02:00
}
/*****************************************************************************/
/*********** Show left column when playing a match (as a student) ************/
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_ShowLeftColumnStd (const struct Mch_Match *Match,
2019-12-03 02:46:46 +01:00
const struct Mch_UsrAnswer *UsrAnswer)
2019-09-14 12:59:34 +02:00
{
2019-12-03 02:46:46 +01:00
bool Answered = UsrAnswer->NumOpt >= 0;
2019-09-14 12:59:34 +02:00
/***** Start left container *****/
2020-03-12 13:53:37 +01:00
HTM_DIV_Begin ("class=\"MCH_LEFT_STD\"");
2019-09-14 12:59:34 +02:00
/***** Top *****/
2020-03-12 13:53:37 +01:00
HTM_DIV_Begin ("class=\"MCH_TOP CT\"");
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-09-14 12:59:34 +02:00
/***** Write number of question *****/
2019-12-12 09:14:26 +01:00
Mch_ShowNumQstInMch (Match);
2019-09-14 12:59:34 +02:00
2019-12-12 09:14:26 +01:00
switch (Match->Status.Showing)
2019-12-04 01:19:31 +01:00
{
2019-12-12 09:14:26 +01:00
case Mch_START:
case Mch_END:
break;
default:
/***** Write whether question is answered or not *****/
Mch_PutIfAnswered (Match,Answered);
if (Match->Status.Playing && // Match is being played
Match->Status.Showing == Mch_ANSWERS && // Teacher's screen is showing question answers
Answered) // I have answered this question
/***** Put icon to remove my answet *****/
Mch_PutIconToRemoveMyAnswer (Match);
break;
2019-12-04 01:19:31 +01:00
}
2019-12-03 02:46:46 +01:00
2019-09-14 12:59:34 +02:00
/***** End left container *****/
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-09-14 12:59:34 +02:00
}
2019-09-26 18:19:07 +02:00
/*****************************************************************************/
/********** Show right column when playing a match (as a student) ************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_ShowRightColumnStd (struct Mch_Match *Match,
2019-12-03 02:46:46 +01:00
const struct Mch_UsrAnswer *UsrAnswer,
Mch_Update_t Update)
2019-09-26 18:19:07 +02:00
{
extern const char *Txt_Please_wait_;
/***** Start right container *****/
2020-03-12 13:53:37 +01:00
HTM_DIV_Begin ("class=\"MCH_RIGHT_STD\"");
2019-09-26 18:19:07 +02:00
/***** Top row *****/
2020-03-12 13:53:37 +01:00
Mch_ShowMatchTitleStd (Match);
2019-09-26 18:19:07 +02:00
/***** Bottom row *****/
2019-12-10 21:52:22 +01:00
if (Match->Status.Playing) // Match is being played
2019-09-26 18:19:07 +02:00
{
2019-12-10 21:52:22 +01:00
if (Match->Status.Showing == Mch_END) // Match over
Mch_ShowWaitImage (Txt_Please_wait_);
else // Match not over
2019-09-29 21:33:27 +02:00
{
2019-10-24 00:04:40 +02:00
HTM_DIV_Begin ("class=\"MCH_BOTTOM\"");
2019-09-26 18:19:07 +02:00
2019-09-29 21:33:27 +02:00
/***** Update players ******/
2019-12-02 09:57:17 +01:00
if (Mch_RegisterMeAsPlayerInMatch (Match))
2019-12-04 01:19:31 +01:00
{
if (Match->Status.Showing == Mch_ANSWERS) // Teacher's screen is showing question answers
/* Show current question and possible answers */
2019-12-04 22:22:42 +01:00
if (!Mch_ShowQuestionAndAnswersStd (Match,UsrAnswer,Update))
Ale_ShowAlert (Ale_ERROR,"Wrong question.");
2019-12-04 01:19:31 +01:00
}
2019-12-02 09:57:17 +01:00
else
Ale_ShowAlert (Ale_ERROR,"You can not join this match.");
2019-09-26 18:19:07 +02:00
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-09-29 21:33:27 +02:00
}
2019-09-26 18:19:07 +02:00
}
2019-12-10 21:52:22 +01:00
else // Match is not being played
2019-09-29 21:33:27 +02:00
Mch_ShowWaitImage (Txt_Please_wait_);
2019-09-26 18:19:07 +02:00
/***** End right container *****/
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-09-26 18:19:07 +02:00
}
2019-09-14 19:46:26 +02:00
/*****************************************************************************/
/********************* Show number of question in game ***********************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_ShowNumQstInMch (const struct Mch_Match *Match)
2019-09-14 19:46:26 +02:00
{
extern const char *Txt_MATCH_Start;
extern const char *Txt_MATCH_End;
unsigned NumQsts = Gam_GetNumQstsGame (Match->GamCod);
2019-10-24 00:04:40 +02:00
HTM_DIV_Begin ("class=\"MCH_NUM_QST\"");
2019-12-10 21:52:22 +01:00
switch (Match->Status.Showing)
{
case Mch_START: // Not started
HTM_Txt (Txt_MATCH_Start);
break;
case Mch_END: // Match over
HTM_Txt (Txt_MATCH_End);
break;
default:
HTM_TxtF ("%u/%u",Match->Status.QstInd,NumQsts);
break;
}
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-09-14 19:46:26 +02:00
}
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
/********************** Put buttons to control a match ***********************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_PutMatchControlButtons (const struct Mch_Match *Match)
2019-09-14 12:59:34 +02:00
{
extern const char *Txt_Go_back;
extern const char *Txt_Go_forward;
extern const char *Txt_Pause;
extern const char *Txt_Start;
extern const char *Txt_Resume;
/***** Start buttons container *****/
2019-12-13 00:36:01 +01:00
HTM_DIV_Begin ("class=\"MCH_BUTTONS_CONT\"");
2019-09-14 12:59:34 +02:00
/***** Left button *****/
2019-12-13 00:36:01 +01:00
HTM_DIV_Begin ("class=\"MCH_BUTTON_LEFT_CONT\"");
2019-12-12 09:14:26 +01:00
switch (Match->Status.Showing)
{
case Mch_START:
/* Put button to close browser tab */
Mch_PutBigButtonClose ();
break;
default:
/* Put button to go back */
Mch_PutBigButton (ActBckMch,"backward",Match->MchCod,
Mch_ICON_PREVIOUS,Txt_Go_back);
break;
}
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-09-14 12:59:34 +02:00
/***** Center button *****/
2019-12-13 00:36:01 +01:00
HTM_DIV_Begin ("class=\"MCH_BUTTON_CENTER_CONT\"");
2019-12-03 15:39:48 +01:00
if (Match->Status.Playing) // Match is being played
2019-09-14 12:59:34 +02:00
/* Put button to pause match */
2019-10-17 22:47:44 +02:00
Mch_PutBigButton (ActPlyPauMch,"play_pause",Match->MchCod,
2019-09-14 12:59:34 +02:00
Mch_ICON_PAUSE,Txt_Pause);
2019-12-03 15:39:48 +01:00
else // Match is paused, not being played
2019-09-14 12:59:34 +02:00
{
2019-12-10 21:52:22 +01:00
switch (Match->Status.Showing)
{
case Mch_START: // Match just started, before first question
/* Put button to start playing match */
Mch_PutBigButton (ActPlyPauMch,"play_pause",Match->MchCod,
Mch_ICON_PLAY,Txt_Start);
break;
case Mch_END: // Match over
/* Put disabled button to play match */
2019-12-13 00:36:01 +01:00
Mch_PutBigButtonHidden (Mch_ICON_PLAY);
2019-12-10 21:52:22 +01:00
break;
default:
/* Put button to resume match */
Mch_PutBigButton (ActPlyPauMch,"play_pause",Match->MchCod,
Mch_ICON_PLAY,Txt_Resume);
}
2019-09-14 12:59:34 +02:00
}
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-09-14 12:59:34 +02:00
/***** Right button *****/
2019-12-13 00:36:01 +01:00
HTM_DIV_Begin ("class=\"MCH_BUTTON_RIGHT_CONT\"");
2019-12-10 21:52:22 +01:00
if (Match->Status.Showing == Mch_END) // Match over
2019-09-14 12:59:34 +02:00
/* Put button to close browser tab */
Mch_PutBigButtonClose ();
2019-12-10 21:52:22 +01:00
else // Match not over
2019-09-14 12:59:34 +02:00
/* Put button to show answers */
2019-10-17 22:47:44 +02:00
Mch_PutBigButton (ActFwdMch,"forward",Match->MchCod,
2019-09-14 12:59:34 +02:00
Mch_ICON_NEXT,Txt_Go_forward);
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-09-14 12:59:34 +02:00
/***** End buttons container *****/
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-09-14 12:59:34 +02:00
}
/*****************************************************************************/
2019-10-22 22:39:37 +02:00
/** Show form to choice whether to show answers in one column or two columns */
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_ShowFormColumns (const struct Mch_Match *Match)
2019-10-22 22:39:37 +02:00
{
2019-10-22 23:42:22 +02:00
extern const char *Txt_column;
2019-10-22 22:39:37 +02:00
extern const char *Txt_columns;
unsigned NumCols;
2019-10-22 23:42:22 +02:00
static const char *NumColsIcon[1 + Mch_MAX_COLS] =
2019-10-22 22:39:37 +02:00
{
2019-10-23 02:18:24 +02:00
"", // Not used
"1col.png", // 1 column
"2col.png", // 2 columns
"3col.png", // 3 columns
"4col.png", // 4 columns
2019-10-22 22:39:37 +02:00
};
2019-10-22 23:42:22 +02:00
/***** Begin selector *****/
Set_BeginOneSettingSelector ();
2019-10-22 23:42:22 +02:00
for (NumCols = 1;
NumCols <= Mch_MAX_COLS;
2019-10-22 22:39:37 +02:00
NumCols++)
{
2019-12-30 20:41:40 +01:00
/* Begin container for this option */
2019-10-24 00:04:40 +02:00
HTM_DIV_Begin ("class=\"%s\"",
2020-03-12 13:53:37 +01:00
(Match->Status.NumCols == NumCols) ? "MCH_NUM_COL_ON" :
"MCH_NUM_COL_OFF");
2019-10-24 00:04:40 +02:00
2019-12-30 20:41:40 +01:00
/* Begin form */
Frm_BeginForm (ActChgNumColMch);
2019-10-22 22:39:37 +02:00
Mch_PutParamMchCod (Match->MchCod); // Current match being played
2019-10-23 00:49:03 +02:00
Mch_PutParamNumCols (NumCols); // Number of columns
2019-10-22 22:39:37 +02:00
2019-12-30 20:41:40 +01:00
/* Number of columns */
Ico_PutSettingIconLink (NumColsIcon[NumCols],
2019-12-30 21:47:07 +01:00
Str_BuildStringLongStr ((long) NumCols,
NumCols == 1 ? Txt_column :
Txt_columns));
Str_FreeString ();
2019-10-22 23:42:22 +02:00
2019-12-30 20:41:40 +01:00
/* End form */
2019-10-22 22:39:37 +02:00
Frm_EndForm ();
2019-10-22 23:42:22 +02:00
2019-12-30 20:41:40 +01:00
/* End container for this option */
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-10-22 22:39:37 +02:00
}
2019-10-22 23:42:22 +02:00
/***** End selector *****/
2019-10-22 22:39:37 +02:00
Set_EndOneSettingSelector ();
}
/*****************************************************************************/
2019-10-23 00:49:03 +02:00
/******** Write parameter with number of columns in answers of match *********/
2019-10-22 22:39:37 +02:00
/*****************************************************************************/
2019-09-14 19:46:26 +02:00
2019-10-23 00:49:03 +02:00
static void Mch_PutParamNumCols (unsigned NumCols) // Number of columns
{
2019-11-03 13:19:32 +01:00
Par_PutHiddenParamUnsigned (NULL,"NumCols",NumCols);
2019-10-23 00:49:03 +02:00
}
2019-09-14 19:46:26 +02:00
2019-10-31 08:47:16 +01:00
/*****************************************************************************/
/***************** Put checkbox to select if show results ********************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_PutCheckboxResult (const struct Mch_Match *Match)
2019-10-31 08:47:16 +01:00
{
extern const char *Txt_View_results;
2019-12-12 18:24:23 +01:00
/***** Begin container *****/
2019-11-04 22:42:03 +01:00
HTM_DIV_Begin ("class=\"MCH_SHOW_RESULTS\"");
2019-10-31 08:47:16 +01:00
2019-12-12 18:24:23 +01:00
/***** Begin form *****/
Frm_BeginForm (ActChgVisResMchQst);
2019-10-31 08:47:16 +01:00
Mch_PutParamMchCod (Match->MchCod); // Current match being played
/***** Put icon with link *****/
2019-12-04 01:19:31 +01:00
HTM_BUTTON_SUBMIT_Begin (Txt_View_results,"BT_LINK DAT ICO_HIGHLIGHT",NULL);
2019-11-11 00:15:44 +01:00
HTM_TxtF ("<i class=\"%s\"></i>",
Match->Status.ShowQstResults ? "fas fa-toggle-on" :
"fas fa-toggle-off");
2019-11-11 10:59:24 +01:00
HTM_TxtF ("&nbsp;%s",Txt_View_results);
2019-12-04 01:19:31 +01:00
HTM_BUTTON_End ();
2019-10-31 08:47:16 +01:00
/***** End form *****/
Frm_EndForm ();
/***** End container *****/
2019-11-04 22:42:03 +01:00
HTM_DIV_End ();
2019-10-31 08:47:16 +01:00
}
2019-10-22 20:43:03 +02:00
2019-12-03 02:46:46 +01:00
/*****************************************************************************/
/***************** Put checkbox to select if show results ********************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_PutIfAnswered (const struct Mch_Match *Match,bool Answered)
2019-12-03 02:46:46 +01:00
{
2019-12-03 19:36:30 +01:00
extern const char *Txt_View_my_answer;
2019-12-04 01:19:31 +01:00
extern const char *Txt_MATCH_QUESTION_Answered;
extern const char *Txt_MATCH_QUESTION_Unanswered;
2019-12-03 02:46:46 +01:00
/***** Begin container *****/
2019-12-12 18:24:23 +01:00
HTM_DIV_Begin ("class=\"MCH_SHOW_ANSWERED\"");
2019-12-03 02:46:46 +01:00
/***** Put icon with link *****/
2019-12-04 01:19:31 +01:00
if (Match->Status.Playing && // Match is being played
Match->Status.Showing == Mch_ANSWERS && // Teacher's screen is showing question answers
Answered) // I have answered this question
2019-12-03 02:46:46 +01:00
{
2020-05-11 02:28:38 +02:00
/* Begin form */
Frm_BeginForm (ActSeeMchAnsQstStd);
2019-12-03 02:46:46 +01:00
Mch_PutParamMchCod (Match->MchCod); // Current match being played
2019-12-14 01:31:32 +01:00
HTM_BUTTON_OnMouseDown_Begin (Txt_View_my_answer,"BT_LINK DAT_SMALL_GREEN");
2019-12-03 19:36:30 +01:00
HTM_TxtF ("<i class=\"%s\"></i>","fas fa-check-circle");
2019-12-04 01:19:31 +01:00
HTM_TxtF ("&nbsp;%s",Txt_MATCH_QUESTION_Answered);
2019-12-03 19:36:30 +01:00
HTM_BUTTON_End ();
2019-12-03 02:46:46 +01:00
/* End form */
Frm_EndForm ();
}
else
{
2019-12-04 01:19:31 +01:00
HTM_DIV_Begin ("class=\"%s\"",Answered ? "DAT_SMALL_GREEN" :
"DAT_SMALL_RED");
2019-12-03 02:46:46 +01:00
HTM_TxtF ("<i class=\"%s\" title=\"%s\"></i>",
Answered ? "fas fa-check-circle" :
"fas fa-exclamation-circle",
2019-12-04 01:19:31 +01:00
Answered ? Txt_MATCH_QUESTION_Answered :
Txt_MATCH_QUESTION_Unanswered);
HTM_TxtF ("&nbsp;%s",Answered ? Txt_MATCH_QUESTION_Answered :
Txt_MATCH_QUESTION_Unanswered);
2019-12-03 02:46:46 +01:00
HTM_DIV_End ();
}
/***** End container *****/
HTM_DIV_End ();
}
/*****************************************************************************/
/***************** Put checkbox to select if show results ********************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_PutIconToRemoveMyAnswer (const struct Mch_Match *Match)
2019-12-03 02:46:46 +01:00
{
2019-12-04 01:19:31 +01:00
extern const char *Txt_Delete_my_answer;
2019-12-03 02:46:46 +01:00
2020-05-11 02:28:38 +02:00
/***** Begin container *****/
2019-12-12 18:24:23 +01:00
HTM_DIV_Begin ("class=\"MCH_REM_MY_ANS\"");
2019-12-03 02:46:46 +01:00
2020-05-11 02:28:38 +02:00
/***** Begin form *****/
Frm_BeginForm (ActRemMchAnsQstStd);
2019-12-03 02:46:46 +01:00
Mch_PutParamMchCod (Match->MchCod); // Current match being played
Gam_PutParamQstInd (Match->Status.QstInd); // Current question index shown
/***** Put icon with link *****/
2019-12-13 00:36:01 +01:00
HTM_DIV_Begin ("class=\"MCH_BIGBUTTON_CONT\"");
2019-12-14 01:31:32 +01:00
HTM_BUTTON_OnMouseDown_Begin (Txt_Delete_my_answer,"BT_LINK MCH_BUTTON_ON ICO_DARKRED");
2019-12-04 01:19:31 +01:00
HTM_Txt ("<i class=\"fas fa-trash\"></i>");
HTM_BUTTON_End ();
2019-12-03 02:46:46 +01:00
HTM_DIV_End ();
/***** End form *****/
Frm_EndForm ();
/***** End container *****/
HTM_DIV_End ();
}
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
/***************************** Show match title ******************************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_ShowMatchTitleTch (const struct Mch_Match *Match)
2019-09-14 12:59:34 +02:00
{
/***** Match title *****/
2020-03-12 13:53:37 +01:00
HTM_DIV_Begin ("class=\"MCH_TOP LT\"");
HTM_Txt (Match->Title);
HTM_DIV_End ();
}
2020-04-08 03:06:45 +02:00
static void Mch_ShowMatchTitleStd (const struct Mch_Match *Match)
2020-03-12 13:53:37 +01:00
{
/***** Match title *****/
HTM_DIV_Begin ("class=\"MCH_TOP CT\"");
2019-11-10 12:36:37 +01:00
HTM_Txt (Match->Title);
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-09-14 12:59:34 +02:00
}
/*****************************************************************************/
/***** Show question and its answers when playing a match (as a teacher) *****/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_ShowQuestionAndAnswersTch (const struct Mch_Match *Match)
2019-09-14 12:59:34 +02:00
{
extern const char *Txt_MATCH_Paused;
2020-03-07 00:14:35 +01:00
extern const char *Txt_Question_removed;
2020-03-27 21:54:13 +01:00
struct Tst_Question Question;
/***** Create test question *****/
Tst_QstConstructor (&Question);
Question.QstCod = Match->Status.QstCod;
2019-09-14 12:59:34 +02:00
2019-12-12 09:14:26 +01:00
/***** Trivial check: do not show anything on match start and end *****/
switch (Match->Status.Showing)
{
case Mch_START:
case Mch_END:
return;
default:
break;
}
2019-09-14 12:59:34 +02:00
/***** Get data of question from database *****/
2020-04-04 19:20:50 +02:00
if (Tst_GetQstDataFromDB (&Question))
2020-03-07 00:14:35 +01:00
{
/***** Show question *****/
2020-04-04 02:07:54 +02:00
/* Check answer type */
2020-03-27 21:54:13 +01:00
if (Question.Answer.Type != Tst_ANS_UNIQUE_CHOICE)
2020-03-07 00:14:35 +01:00
Lay_ShowErrorAndExit ("Wrong answer type.");
2019-09-14 12:59:34 +02:00
2020-03-07 00:14:35 +01:00
/* Begin container */
HTM_DIV_Begin ("class=\"MCH_BOTTOM\""); // Bottom
2019-09-14 12:59:34 +02:00
2020-04-04 02:07:54 +02:00
/* Write stem */
2020-04-04 19:20:50 +02:00
Tst_WriteQstStem (Question.Stem,"MCH_TCH_STEM",
2020-03-07 00:14:35 +01:00
true); // Visible
2019-09-14 12:59:34 +02:00
2020-03-07 00:14:35 +01:00
/* Show media */
2020-03-27 21:54:13 +01:00
Med_ShowMedia (&Question.Media,
2020-05-18 14:34:31 +02:00
"TEST_MED_EDIT_LIST_CONT",
"TEST_MED_EDIT_LIST");
2019-09-14 12:59:34 +02:00
2020-03-07 00:14:35 +01:00
/***** Write answers? *****/
switch (Match->Status.Showing)
{
case Mch_ANSWERS:
if (Match->Status.Playing) // Match is being played
/* Write answers */
Mch_WriteAnswersMatchResult (Match,
2020-03-27 21:54:13 +01:00
&Question,
2020-03-07 00:14:35 +01:00
"MCH_TCH_ANS",
2020-03-27 21:54:13 +01:00
false); // Don't show result
2020-03-18 18:49:45 +01:00
else // Match is paused, not being played
2020-03-07 00:14:35 +01:00
Mch_ShowWaitImage (Txt_MATCH_Paused);
break;
case Mch_RESULTS:
/* Write answers with results */
2019-10-23 01:30:11 +02:00
Mch_WriteAnswersMatchResult (Match,
2020-03-27 21:54:13 +01:00
&Question,
2019-10-21 21:36:31 +02:00
"MCH_TCH_ANS",
2020-03-27 21:54:13 +01:00
true); // Show result
2020-03-07 00:14:35 +01:00
break;
default:
/* Don't write anything */
break;
}
2019-09-14 12:59:34 +02:00
2020-03-07 00:14:35 +01:00
/* End container */
HTM_DIV_End (); // Bottom
}
else
Ale_ShowAlert (Ale_WARNING,Txt_Question_removed);
2020-03-27 21:54:13 +01:00
/***** Destroy test question *****/
Tst_QstDestructor (&Question);
2019-09-14 12:59:34 +02:00
}
2019-10-23 01:30:11 +02:00
/*****************************************************************************/
2020-03-30 18:54:17 +02:00
/************* Write answers of a question when seeing a match ***************/
2019-10-23 01:30:11 +02:00
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_WriteAnswersMatchResult (const struct Mch_Match *Match,
2020-06-17 02:31:42 +02:00
struct Tst_Question *Question,
2020-03-27 21:54:13 +01:00
const char *Class,bool ShowResult)
2019-10-23 01:30:11 +02:00
{
/***** Write answer depending on type *****/
2020-03-27 21:54:13 +01:00
if (Question->Answer.Type == Tst_ANS_UNIQUE_CHOICE)
2020-03-27 22:02:01 +01:00
Mch_WriteChoiceAnsViewMatch (Match,
2020-03-27 21:54:13 +01:00
Question,
Class,ShowResult);
2019-10-23 01:30:11 +02:00
else
Ale_ShowAlert (Ale_ERROR,"Type of answer not valid in a game.");
}
2020-03-27 22:02:01 +01:00
/*****************************************************************************/
2020-03-30 18:54:17 +02:00
/******** Write single or multiple choice answer when seeing a match *********/
2020-03-27 22:02:01 +01:00
/*****************************************************************************/
2020-08-28 14:45:30 +02:00
static void Mch_WriteChoiceAnsViewMatch (const struct Mch_Match *Match,
struct Tst_Question *Question,
const char *Class,bool ShowResult)
2020-03-27 22:02:01 +01:00
{
unsigned NumOpt;
bool RowIsOpen = false;
unsigned NumRespondersQst;
unsigned NumRespondersAns;
unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]; // Indexes of all answers of this question
/***** Get number of users who have answered this question from database *****/
NumRespondersQst = Mch_GetNumUsrsWhoAnsweredQst (Match->MchCod,Match->Status.QstInd);
2020-06-17 02:31:42 +02:00
/***** Change format of answers text *****/
Tst_ChangeFormatAnswersText (Question);
2020-03-27 22:02:01 +01:00
/***** Get indexes for this question in match *****/
Mch_GetIndexes (Match->MchCod,Match->Status.QstInd,Indexes);
/***** Begin table *****/
HTM_TABLE_BeginWidePadding (0);
/***** Show options distributed in columns *****/
for (NumOpt = 0;
NumOpt < Question->Answer.NumOptions;
NumOpt++)
{
/***** Start row? *****/
if (NumOpt % Match->Status.NumCols == 0)
{
HTM_TR_Begin (NULL);
RowIsOpen = true;
}
/***** Write letter for this option *****/
HTM_TD_Begin ("class=\"MCH_TCH_BUTTON_TD\"");
HTM_DIV_Begin ("class=\"MCH_TCH_BUTTON BT_%c\"",'A' + (char) NumOpt);
HTM_TxtF ("%c",'a' + (char) NumOpt);
HTM_DIV_End ();
HTM_TD_End ();
/***** Write the option text and the result *****/
HTM_TD_Begin ("class=\"LT\"");
HTM_LABEL_Begin ("for=\"Ans%06u_%u\" class=\"%s\"",Match->Status.QstInd,NumOpt,Class);
HTM_Txt (Question->Answer.Options[Indexes[NumOpt]].Text);
HTM_LABEL_End ();
Med_ShowMedia (&Question->Answer.Options[Indexes[NumOpt]].Media,
"TEST_MED_SHOW_CONT",
"TEST_MED_SHOW");
/* Show result (number of users who answered? */
if (ShowResult)
{
/* Get number of users who selected this answer */
NumRespondersAns = Mch_GetNumUsrsWhoHaveChosenAns (Match->MchCod,Match->Status.QstInd,Indexes[NumOpt]);
/* Draw proportional bar for this answer */
Mch_DrawBarNumUsrs (NumRespondersAns,NumRespondersQst,
Question->Answer.Options[Indexes[NumOpt]].Correct);
}
else
/* Draw empty bar for this answer
in order to show the same layout that the one shown with results */
Mch_DrawBarNumUsrs (0,0,
false); // Not used when length of bar is 0
HTM_TD_End ();
/***** End row? *****/
if (NumOpt % Match->Status.NumCols == Match->Status.NumCols - 1)
{
HTM_TR_End ();
RowIsOpen = false;
}
}
/***** End row? *****/
if (RowIsOpen)
HTM_TR_End ();
/***** End table *****/
HTM_TABLE_End ();
}
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
/***** Show question and its answers when playing a match (as a student) *****/
/*****************************************************************************/
2019-12-04 22:22:42 +01:00
// Return true on valid question, false on invalid question
2019-09-14 12:59:34 +02:00
2020-04-08 03:06:45 +02:00
static bool Mch_ShowQuestionAndAnswersStd (const struct Mch_Match *Match,
2019-12-03 02:46:46 +01:00
const struct Mch_UsrAnswer *UsrAnswer,
Mch_Update_t Update)
2019-09-14 12:59:34 +02:00
{
unsigned NumOptions;
unsigned NumOpt;
2019-11-11 00:15:44 +01:00
char *Class;
2019-09-14 12:59:34 +02:00
2019-12-04 22:22:42 +01:00
/***** Get number of options in this question *****/
NumOptions = Tst_GetNumAnswersQst (Match->Status.QstCod);
/***** Begin table *****/
HTM_TABLE_BeginWidePadding (8);
for (NumOpt = 0;
NumOpt < NumOptions;
NumOpt++)
2019-09-14 12:59:34 +02:00
{
2019-12-04 22:22:42 +01:00
/***** Start row *****/
HTM_TR_Begin (NULL);
2019-09-14 12:59:34 +02:00
2019-12-04 22:22:42 +01:00
/***** Write letter for this option *****/
/* Begin table cell */
HTM_TD_Begin ("class=\"MCH_STD_CELL\"");
/* Form with button.
Sumitting onmousedown instead of default onclick
is necessary in order to be fast
and not lose clicks due to refresh */
Frm_BeginForm (ActAnsMchQstStd);
2019-12-04 22:22:42 +01:00
Mch_PutParamMchCod (Match->MchCod); // Current match being played
Gam_PutParamQstInd (Match->Status.QstInd); // Current question index shown
Mch_PutParamNumOpt (NumOpt); // Number of button
if (asprintf (&Class,"MCH_STD_BUTTON%s BT_%c",
UsrAnswer->NumOpt == (int) NumOpt && // Student's answer
Update == Mch_CHANGE_STATUS_BY_STUDENT ? " MCH_STD_ANSWER_SELECTED" :
"",
'A' + (char) NumOpt) < 0)
Lay_NotEnoughMemoryExit ();
2019-12-14 01:31:32 +01:00
HTM_BUTTON_OnMouseDown_Begin (NULL,Class);
2019-12-04 22:22:42 +01:00
HTM_TxtF ("%c",'a' + (char) NumOpt);
HTM_BUTTON_End ();
free (Class);
2019-09-14 12:59:34 +02:00
2019-12-04 22:22:42 +01:00
Frm_EndForm ();
2019-12-04 01:19:31 +01:00
2019-12-04 22:22:42 +01:00
/* End table cell */
HTM_TD_End ();
/***** End row *****/
HTM_TR_End ();
2019-09-14 12:59:34 +02:00
}
2019-12-04 22:22:42 +01:00
/***** End table *****/
HTM_TABLE_End ();
return true;
2019-09-14 12:59:34 +02:00
}
2019-10-01 01:01:35 +02:00
/*****************************************************************************/
2019-10-21 13:36:28 +02:00
/***************************** Show match scores *****************************/
2019-10-01 01:01:35 +02:00
/*****************************************************************************/
2019-10-21 21:36:31 +02:00
#define Mch_NUM_ROWS_SCORE 50
2019-10-21 13:36:28 +02:00
2020-04-08 03:06:45 +02:00
static void Mch_ShowMatchScore (const struct Mch_Match *Match)
2019-10-01 01:01:35 +02:00
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
2019-10-21 13:36:28 +02:00
unsigned NumScores;
unsigned NumScore;
double MinScore;
double MaxScore;
double Range;
double NumRowsPerScorePoint;
double Score;
unsigned MaxUsrs;
2019-10-21 13:36:28 +02:00
unsigned NumUsrs;
unsigned NumRowForThisScore;
2019-10-01 01:01:35 +02:00
unsigned NumRow;
2019-10-21 13:36:28 +02:00
/***** Get minimum and maximum scores *****/
Gam_GetScoreRange (Match->GamCod,&MinScore,&MaxScore);
Range = MaxScore - MinScore;
if (Range == 0.0)
return;
NumRowsPerScorePoint = (double) Mch_NUM_ROWS_SCORE / Range;
/***** Get maximum number of users *****/
MaxUsrs = DB_QuerySELECTUnsigned ("can not get max users",
"SELECT MAX(NumUsrs)"
" FROM (SELECT COUNT(*) AS NumUsrs"
" FROM mch_results"
" WHERE MchCod=%ld"
" GROUP BY Score"
" ORDER BY Score) AS Scores",
Match->MchCod);
2019-10-21 13:36:28 +02:00
/***** Get scores from database *****/
NumScores = (unsigned)
DB_QuerySELECT (&mysql_res,"can not get scores",
"SELECT Score," // row[0]
"COUNT(*) AS NumUsrs" // row[1]
" FROM mch_results"
" WHERE MchCod=%ld"
" GROUP BY Score"
" ORDER BY Score DESC",
Match->MchCod);
2019-10-21 13:36:28 +02:00
/***** Begin table ****/
2019-10-23 19:05:05 +02:00
HTM_TABLE_BeginWide ();
2019-10-21 13:36:28 +02:00
/***** Get and draw scores *****/
for (NumScore = 0, NumRow = 0;
NumScore < NumScores;
NumScore++)
2019-10-01 01:01:35 +02:00
{
2019-10-21 13:36:28 +02:00
/***** Get score and number of users from database *****/
2019-10-01 01:01:35 +02:00
row = mysql_fetch_row (mysql_res);
/* Get score (row[0]) */
2019-10-21 13:36:28 +02:00
Str_SetDecimalPointToUS (); // To get the decimal point as a dot
if (sscanf (row[0],"%lf",&Score) != 1)
Score = 0.0;
2019-10-01 01:01:35 +02:00
Str_SetDecimalPointToLocal (); // Return to local system
/* Get number of users (row[1]) *****/
2019-10-21 13:36:28 +02:00
if (sscanf (row[1],"%u",&NumUsrs) != 1)
NumUsrs = 0;
/***** Draw empty rows until reaching the adequate row *****/
NumRowForThisScore = (unsigned) ((MaxScore - Score) * NumRowsPerScorePoint);
2019-10-21 16:43:18 +02:00
if (NumRowForThisScore == Mch_NUM_ROWS_SCORE)
NumRowForThisScore = Mch_NUM_ROWS_SCORE - 1;
2019-10-21 13:36:28 +02:00
for (;
NumRow < NumRowForThisScore;
NumRow++)
2019-12-14 01:31:32 +01:00
Mch_DrawEmptyScoreRow (NumRow,MinScore,MaxScore);
2019-10-21 13:36:28 +02:00
/***** Draw row for this score *****/
2019-10-21 21:36:31 +02:00
Mch_DrawScoreRow (Score,MinScore,MaxScore,NumRow,NumUsrs,MaxUsrs);
2019-10-21 13:36:28 +02:00
NumRow++;
2019-10-01 01:01:35 +02:00
}
2019-10-21 13:36:28 +02:00
/***** Draw final empty rows *****/
2019-10-01 01:01:35 +02:00
for (;
2019-10-21 13:36:28 +02:00
NumRow < Mch_NUM_ROWS_SCORE;
2019-10-01 01:01:35 +02:00
NumRow++)
2019-12-14 01:31:32 +01:00
Mch_DrawEmptyScoreRow (NumRow,MinScore,MaxScore);
2019-10-01 01:01:35 +02:00
2019-10-21 13:36:28 +02:00
/***** End table *****/
2019-10-23 19:05:05 +02:00
HTM_TABLE_End ();
2019-10-21 13:36:28 +02:00
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
2019-12-14 01:31:32 +01:00
/*****************************************************************************/
/*************************** Draw empty score row ****************************/
/*****************************************************************************/
static void Mch_DrawEmptyScoreRow (unsigned NumRow,double MinScore,double MaxScore)
2019-10-21 13:36:28 +02:00
{
/***** Draw row *****/
2019-10-23 19:05:05 +02:00
HTM_TR_Begin (NULL);
2019-10-21 13:36:28 +02:00
/* Write score */
2019-10-23 19:05:05 +02:00
HTM_TD_Begin ("class=\"MCH_SCO_SCO\"");
2019-10-21 13:36:28 +02:00
if (NumRow == 0)
{
2020-01-11 15:22:02 +01:00
HTM_DoubleFewDigits (MaxScore);
2019-11-14 08:59:11 +01:00
HTM_NBSP ();
2019-10-01 01:01:35 +02:00
}
2019-10-21 13:36:28 +02:00
else if (NumRow == Mch_NUM_ROWS_SCORE - 1)
{
2020-01-11 15:22:02 +01:00
HTM_DoubleFewDigits (MinScore);
2019-11-14 08:59:11 +01:00
HTM_NBSP ();
2019-10-21 13:36:28 +02:00
}
2019-10-23 19:05:05 +02:00
HTM_TD_End ();
2019-10-01 01:01:35 +02:00
2019-10-21 13:36:28 +02:00
/* Empty column with bar and number of users */
2019-10-23 19:05:05 +02:00
HTM_TD_Begin ("class=\"MCH_SCO_NUM%s\"",Mch_GetClassBorder (NumRow));
HTM_TD_End ();
2019-10-21 13:36:28 +02:00
2019-10-23 19:05:05 +02:00
HTM_TR_End ();
2019-10-21 13:36:28 +02:00
}
2019-10-01 01:01:35 +02:00
2019-12-14 01:31:32 +01:00
/*****************************************************************************/
/******************************* Draw score row ******************************/
/*****************************************************************************/
2019-10-21 15:07:00 +02:00
static void Mch_DrawScoreRow (double Score,double MinScore,double MaxScore,
2019-10-21 21:36:31 +02:00
unsigned NumRow,unsigned NumUsrs,unsigned MaxUsrs)
2019-10-21 13:36:28 +02:00
{
2019-10-21 21:36:31 +02:00
extern const char *Txt_ROLES_SINGUL_abc[Rol_NUM_ROLES][Usr_NUM_SEXS];
extern const char *Txt_ROLES_PLURAL_abc[Rol_NUM_ROLES][Usr_NUM_SEXS];
2019-10-21 15:07:00 +02:00
unsigned Color;
2019-10-21 13:36:28 +02:00
unsigned BarWidth;
2019-10-30 23:55:49 +01:00
char *Icon;
2019-10-01 01:01:35 +02:00
2019-10-21 15:07:00 +02:00
/***** Compute color *****/
/*
+----------------- MaxScore
| score9_1x1.png
+-----------------
| score8_1x1.png
+-----------------
| score7_1x1.png
+-----------------
| score6_1x1.png
+-----------------
| score5_1x1.png
+-----------------
| score4_1x1.png
+-----------------
| score3_1x1.png
+-----------------
| score2_1x1.png
+-----------------
| score1_1x1.png
+-----------------
| score0_1x1.png
+----------------- MinScore
*/
Color = (unsigned) (((Score - MinScore) / (MaxScore - MinScore)) * 10.0);
2019-10-21 16:43:18 +02:00
if (Color == 10)
2019-10-21 15:07:00 +02:00
Color = 9;
2019-10-21 13:36:28 +02:00
/***** Compute bar width *****/
if (MaxUsrs > 0)
2019-10-01 01:01:35 +02:00
{
2019-10-21 13:36:28 +02:00
BarWidth = (unsigned) (((NumUsrs * 95.0) / MaxUsrs) + 0.5);
if (BarWidth == 0)
BarWidth = 1;
2019-10-01 01:01:35 +02:00
}
2019-10-21 13:36:28 +02:00
else
BarWidth = 0;
2019-10-01 01:01:35 +02:00
2019-10-21 13:36:28 +02:00
/***** Draw row *****/
2019-10-23 19:05:05 +02:00
HTM_TR_Begin (NULL);
2019-10-01 01:01:35 +02:00
2019-10-21 13:36:28 +02:00
/* Write score */
2019-10-23 19:05:05 +02:00
HTM_TD_Begin ("class=\"MCH_SCO_SCO\"");
2020-01-11 15:22:02 +01:00
HTM_DoubleFewDigits (Score);
2019-11-14 08:59:11 +01:00
HTM_NBSP ();
2019-10-23 19:05:05 +02:00
HTM_TD_End ();
2019-10-21 13:36:28 +02:00
/* Draw bar and write number of users for this score */
2019-10-23 19:05:05 +02:00
HTM_TD_Begin ("class=\"MCH_SCO_NUM%s\"",Mch_GetClassBorder (NumRow));
2019-10-30 23:55:49 +01:00
if (asprintf (&Icon,"score%u_1x1.png",Color) < 0) // Background
Lay_NotEnoughMemoryExit ();
2019-12-30 21:47:07 +01:00
HTM_IMG (Cfg_URL_ICON_PUBLIC,Icon,
Str_BuildStringLongStr ((long) NumUsrs,
NumUsrs == 1 ? Txt_ROLES_SINGUL_abc[Rol_STD][Usr_SEX_UNKNOWN] :
Txt_ROLES_PLURAL_abc[Rol_STD][Usr_SEX_UNKNOWN]),
2019-10-30 23:55:49 +01:00
"class=\"MCH_SCO_BAR\" style=\"width:%u%%;\"",BarWidth);
2019-12-30 21:47:07 +01:00
Str_FreeString ();
2019-11-06 19:45:20 +01:00
free (Icon);
2019-12-30 21:47:07 +01:00
HTM_TxtF ("&nbsp;%u",NumUsrs);
2019-10-23 19:05:05 +02:00
HTM_TD_End ();
2019-10-21 13:36:28 +02:00
2019-10-23 19:05:05 +02:00
HTM_TR_End ();
2019-10-01 01:01:35 +02:00
}
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
2019-09-24 01:41:51 +02:00
/****** Write parameter with number of option (button) pressed by user *******/
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
2019-10-21 21:36:31 +02:00
static const char *Mch_GetClassBorder (unsigned NumRow)
{
return NumRow == 0 ? " MCH_SCO_TOP" :
(NumRow == Mch_NUM_ROWS_SCORE - 1 ? " MCH_SCO_BOT" :
" MCH_SCO_MID");
}
/*****************************************************************************/
/****** Write parameter with number of option (button) pressed by user *******/
/*****************************************************************************/
2019-09-24 01:41:51 +02:00
static void Mch_PutParamNumOpt (unsigned NumOpt)
2019-09-14 12:59:34 +02:00
{
2019-11-03 13:19:32 +01:00
Par_PutHiddenParamUnsigned (NULL,"NumOpt",NumOpt);
2019-09-14 12:59:34 +02:00
}
/*****************************************************************************/
2019-09-24 01:41:51 +02:00
/******* Get parameter with number of option (button) pressed by user ********/
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
2019-09-24 01:41:51 +02:00
static unsigned Mch_GetParamNumOpt (void)
2019-09-14 12:59:34 +02:00
{
2019-09-24 01:41:51 +02:00
long NumOpt;
2019-09-14 12:59:34 +02:00
2019-09-24 01:41:51 +02:00
NumOpt = Par_GetParToLong ("NumOpt");
if (NumOpt < 0)
Lay_ShowErrorAndExit ("Wrong number of option.");
2019-09-14 12:59:34 +02:00
2019-09-24 01:41:51 +02:00
return (unsigned) NumOpt;
2019-09-14 12:59:34 +02:00
}
/*****************************************************************************/
/*********************** Put a big button to do action ***********************/
/*****************************************************************************/
2019-10-17 22:47:44 +02:00
static void Mch_PutBigButton (Act_Action_t NextAction,const char *Id,
long MchCod,const char *Icon,const char *Txt)
2019-09-14 12:59:34 +02:00
{
2019-10-20 22:00:28 +02:00
/***** Begin form *****/
Frm_BeginFormId (NextAction,Id);
2019-09-22 13:54:11 +02:00
Mch_PutParamMchCod (MchCod);
2019-09-14 12:59:34 +02:00
/***** Put icon with link *****/
2019-12-13 00:36:01 +01:00
HTM_DIV_Begin ("class=\"MCH_BIGBUTTON_CONT\"");
2019-12-04 01:19:31 +01:00
HTM_BUTTON_SUBMIT_Begin (Txt,"BT_LINK MCH_BUTTON_ON ICO_BLACK",NULL);
2019-11-11 00:15:44 +01:00
HTM_TxtF ("<i class=\"%s\"></i>",Icon);
2019-12-04 01:19:31 +01:00
HTM_BUTTON_End ();
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-09-14 12:59:34 +02:00
/***** End form *****/
Frm_EndForm ();
}
2019-12-14 01:31:32 +01:00
/*****************************************************************************/
/************************** Put a big button hidden **************************/
/*****************************************************************************/
2019-12-13 00:36:01 +01:00
static void Mch_PutBigButtonHidden (const char *Icon)
2019-09-14 12:59:34 +02:00
{
/***** Put inactive icon *****/
2019-12-13 00:36:01 +01:00
HTM_DIV_Begin ("class=\"MCH_BIGBUTTON_CONT\"");
HTM_BUTTON_BUTTON_Begin (NULL,"BT_LINK_OFF MCH_BUTTON_HIDDEN ICO_BLACK",NULL);
2019-11-11 00:15:44 +01:00
HTM_TxtF ("<i class=\"%s\"></i>",Icon);
2019-12-04 01:19:31 +01:00
HTM_BUTTON_End ();
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-09-14 12:59:34 +02:00
}
2019-12-14 01:31:32 +01:00
/*****************************************************************************/
/********************** Put a big button to close window *********************/
/*****************************************************************************/
2019-09-14 12:59:34 +02:00
static void Mch_PutBigButtonClose (void)
{
extern const char *Txt_Close;
/***** Put icon with link *****/
2019-12-13 00:36:01 +01:00
HTM_DIV_Begin ("class=\"MCH_BIGBUTTON_CONT\"");
HTM_BUTTON_BUTTON_Begin (Txt_Close,"BT_LINK MCH_BUTTON_ON ICO_DARKRED","window.close();");
2019-11-11 00:15:44 +01:00
HTM_TxtF ("<i class=\"%s\"></i>",Mch_ICON_CLOSE);
2019-12-04 01:19:31 +01:00
HTM_BUTTON_End ();
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-09-14 12:59:34 +02:00
}
/*****************************************************************************/
/****************************** Show wait image ******************************/
/*****************************************************************************/
static void Mch_ShowWaitImage (const char *Txt)
{
2019-12-13 00:36:01 +01:00
HTM_DIV_Begin ("class=\"MCH_WAIT_CONT\"");
2020-03-12 13:53:37 +01:00
Ico_PutIcon ("Spin-1s-200px.gif",Txt,"MCH_WAIT_IMG");
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-09-14 12:59:34 +02:00
}
/*****************************************************************************/
/**************************** Remove old players *****************************/
/*****************************************************************************/
static void Mch_RemoveOldPlayers (void)
{
2019-10-01 21:18:53 +02:00
/***** Delete matches not being played by teacher *****/
2019-09-14 12:59:34 +02:00
DB_QueryDELETE ("can not update matches as not being played",
2019-09-17 01:37:07 +02:00
"DELETE FROM mch_playing"
2019-09-14 12:59:34 +02:00
" WHERE TS<FROM_UNIXTIME(UNIX_TIMESTAMP()-%lu)",
2019-10-01 21:18:53 +02:00
Cfg_SECONDS_TO_REFRESH_MATCH_TCH*3);
2019-09-14 12:59:34 +02:00
2019-10-01 21:18:53 +02:00
/***** Delete players (students) who have left matches *****/
2019-09-14 12:59:34 +02:00
DB_QueryDELETE ("can not update match players",
2019-09-17 01:37:07 +02:00
"DELETE FROM mch_players"
2019-09-14 12:59:34 +02:00
" WHERE TS<FROM_UNIXTIME(UNIX_TIMESTAMP()-%lu)",
2019-10-01 21:18:53 +02:00
Cfg_SECONDS_TO_REFRESH_MATCH_STD*3);
2019-09-14 12:59:34 +02:00
}
2019-12-14 01:31:32 +01:00
/*****************************************************************************/
/********************** Update match as being played *************************/
/*****************************************************************************/
2019-09-14 12:59:34 +02:00
static void Mch_UpdateMatchAsBeingPlayed (long MchCod)
{
/***** Insert match as being played *****/
DB_QueryREPLACE ("can not set match as being played",
"REPLACE mch_playing"
" (MchCod)"
" VALUE"
" (%ld)",
2019-09-14 12:59:34 +02:00
MchCod);
}
2019-12-14 01:31:32 +01:00
/*****************************************************************************/
/**************** Update match as paused, not being played *******************/
/*****************************************************************************/
2019-09-14 12:59:34 +02:00
static void Mch_SetMatchAsNotBeingPlayed (long MchCod)
{
/***** Delete all match players ******/
DB_QueryDELETE ("can not update match players",
2019-09-17 01:37:07 +02:00
"DELETE FROM mch_players"
2019-09-14 12:59:34 +02:00
" WHERE MchCod=%ld",
MchCod);
/***** Delete match as being played ******/
DB_QueryDELETE ("can not set match as not being played",
2019-09-17 01:37:07 +02:00
"DELETE FROM mch_playing"
2019-09-14 12:59:34 +02:00
" WHERE MchCod=%ld",
MchCod);
}
2019-12-14 01:31:32 +01:00
/*****************************************************************************/
/*********************** Get if match is being played ************************/
/*****************************************************************************/
2019-09-14 12:59:34 +02:00
static bool Mch_GetIfMatchIsBeingPlayed (long MchCod)
{
/***** Get if a match is being played or not *****/
return (DB_QueryCOUNT ("can not get if match is being played",
"SELECT COUNT(*)"
" FROM mch_playing"
2019-09-14 12:59:34 +02:00
" WHERE MchCod=%ld",
MchCod) != 0);
}
2019-12-14 01:31:32 +01:00
/*****************************************************************************/
/*************************** Get number of players ***************************/
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_GetNumPlayers (struct Mch_Match *Match)
2019-09-14 12:59:34 +02:00
{
/***** Get number of players who are playing a match *****/
Match->Status.NumPlayers = (unsigned)
DB_QueryCOUNT ("can not get number of players",
"SELECT COUNT(*)"
" FROM mch_players"
" WHERE MchCod=%ld",
Match->MchCod);
2019-09-14 12:59:34 +02:00
}
2019-12-02 09:57:17 +01:00
/*****************************************************************************/
/******************* Register me as a player in a match **********************/
/*****************************************************************************/
// Return true on success
2020-04-08 03:06:45 +02:00
bool Mch_RegisterMeAsPlayerInMatch (struct Mch_Match *Match)
2019-12-02 09:57:17 +01:00
{
/***** Trivial check: match code must be > 0 *****/
if (Match->MchCod <= 0)
return false;
/***** Trivial check: match must be being played *****/
2019-12-03 15:39:48 +01:00
if (!Match->Status.Playing) // Match is paused, not being played
2019-12-02 09:57:17 +01:00
return false;
/***** Trivial check: match must not be over *****/
2019-12-10 21:52:22 +01:00
if (Match->Status.Showing == Mch_END) // Match over
2019-12-02 09:57:17 +01:00
return false;
/***** Trivial check: only a student can join a match *****/
if (Gbl.Usrs.Me.Role.Logged != Rol_STD) // I am not logged as student
return false;
/***** Insert me as match player *****/
DB_QueryREPLACE ("can not insert match player",
"REPLACE mch_players"
" (MchCod,UsrCod)"
" VALUES"
" (%ld,%ld)",
Match->MchCod,
Gbl.Usrs.Me.UsrDat.UsrCod);
2019-12-02 09:57:17 +01:00
return true;
}
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
/********************** Get code of match being played ***********************/
/*****************************************************************************/
void Mch_GetMatchBeingPlayed (void)
{
2020-04-08 03:06:45 +02:00
long MchCodBeingPlayed;
2019-09-14 12:59:34 +02:00
/***** Get match code ****/
2020-04-08 03:06:45 +02:00
if ((MchCodBeingPlayed = Mch_GetParamMchCod ()) <= 0)
Lay_WrongMatchExit ();
2020-04-08 03:06:45 +02:00
Mch_SetMchCodBeingPlayed (MchCodBeingPlayed);
2019-09-14 12:59:34 +02:00
}
/*****************************************************************************/
2019-12-03 02:46:46 +01:00
/********************* Show match being played as student ********************/
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
2019-09-29 21:33:27 +02:00
void Mch_JoinMatchAsStd (void)
2019-09-14 12:59:34 +02:00
{
2020-04-08 03:06:45 +02:00
struct Mch_Match Match;
2019-09-14 12:59:34 +02:00
2020-04-23 23:09:28 +02:00
/***** Reset match *****/
Mch_ResetMatch (&Match);
2019-09-14 12:59:34 +02:00
/***** Get data of the match from database *****/
2020-04-08 03:06:45 +02:00
Match.MchCod = Mch_GetMchCodBeingPlayed ();
2019-09-14 12:59:34 +02:00
Mch_GetDataOfMatchByCod (&Match);
/***** Show current match status *****/
2019-10-24 00:04:40 +02:00
HTM_DIV_Begin ("id=\"match\" class=\"MCH_CONT\"");
2019-12-03 02:46:46 +01:00
Mch_ShowMatchStatusForStd (&Match,Mch_CHANGE_STATUS_BY_STUDENT);
HTM_DIV_End ();
}
/*****************************************************************************/
/****** Remove student's answer to a question and show match as student ******/
/*****************************************************************************/
2020-08-26 12:06:25 +02:00
void Mch_RemMyQstAnsAndShowMchStatus (void)
2019-12-03 02:46:46 +01:00
{
2020-04-08 03:06:45 +02:00
struct Mch_Match Match;
2019-12-03 02:46:46 +01:00
unsigned QstInd;
2020-04-23 23:09:28 +02:00
/***** Reset match *****/
Mch_ResetMatch (&Match);
2019-12-03 02:46:46 +01:00
/***** Get data of the match from database *****/
2020-04-08 03:06:45 +02:00
Match.MchCod = Mch_GetMchCodBeingPlayed ();
2019-12-03 02:46:46 +01:00
Mch_GetDataOfMatchByCod (&Match);
/***** Get question index from form *****/
QstInd = Gam_GetParamQstInd ();
2020-08-26 12:06:25 +02:00
/***** Remove my answer to this question *****/
Mch_RemoveMyQuestionAnswer (&Match,QstInd);
/***** Show current match status *****/
HTM_DIV_Begin ("id=\"match\" class=\"MCH_CONT\"");
Mch_ShowMatchStatusForStd (&Match,Mch_CHANGE_STATUS_BY_STUDENT);
HTM_DIV_End ();
}
/*****************************************************************************/
/******************** Remove student's answer to a question ******************/
/*****************************************************************************/
void Mch_RemoveMyQuestionAnswer (const struct Mch_Match *Match,unsigned QstInd)
{
2019-12-03 02:46:46 +01:00
/***** Check that teacher's screen is showing answers
and question index is the current one being played *****/
2020-08-26 12:06:25 +02:00
if (Match->Status.Playing && // Match is being played
Match->Status.Showing == Mch_ANSWERS && // Teacher's screen is showing answers
QstInd == Match->Status.QstInd) // Removing answer to the current question being played
2020-05-18 14:34:31 +02:00
{
/***** Remove my answer to this question *****/
2020-08-26 12:06:25 +02:00
Mch_RemoveMyAnswerToMatchQuestion (Match);
2019-12-03 02:46:46 +01:00
2020-05-18 14:34:31 +02:00
/***** Compute score and update my match result *****/
2020-08-26 12:06:25 +02:00
MchPrn_ComputeScoreAndUpdateMyMatchPrintInDB (Match->MchCod);
2020-05-18 14:34:31 +02:00
}
2019-09-14 12:59:34 +02:00
}
2019-12-13 23:38:29 +01:00
/*****************************************************************************/
/******************** Start match countdown (by a teacher) *******************/
/*****************************************************************************/
void Mch_StartCountdown (void)
{
2020-04-08 03:06:45 +02:00
struct Mch_Match Match;
2019-12-13 23:38:29 +01:00
long NewCountdown;
2020-04-23 23:09:28 +02:00
/***** Reset match *****/
Mch_ResetMatch (&Match);
2019-12-13 23:38:29 +01:00
/***** Get countdown parameter ****/
NewCountdown = Par_GetParToLong ("Countdown");
/***** Remove old players.
This function must be called by a teacher
before getting match status. *****/
Mch_RemoveOldPlayers ();
/***** Get data of the match from database *****/
2020-04-08 03:06:45 +02:00
Match.MchCod = Mch_GetMchCodBeingPlayed ();
2019-12-13 23:38:29 +01:00
Mch_GetDataOfMatchByCod (&Match);
/***** Start countdown *****/
Match.Status.Countdown = NewCountdown;
/***** Update match status in database *****/
Mch_UpdateMatchStatusInDB (&Match);
/***** Show current match status *****/
Mch_ShowRefreshablePartTch (&Match);
}
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
/****************** Refresh match for a teacher via AJAX *********************/
/*****************************************************************************/
void Mch_RefreshMatchTch (void)
{
2020-04-08 03:06:45 +02:00
struct Mch_Match Match;
2019-12-12 18:24:23 +01:00
enum {REFRESH_LEFT,REFRESH_ALL} WhatToRefresh;
2019-09-14 12:59:34 +02:00
if (!Gbl.Session.IsOpen) // If session has been closed, do not write anything
return;
2020-04-23 23:09:28 +02:00
/***** Reset match *****/
Mch_ResetMatch (&Match);
2019-09-14 12:59:34 +02:00
/***** Remove old players.
2019-10-01 21:18:53 +02:00
This function must be called by a teacher
before getting match status. *****/
2019-09-14 12:59:34 +02:00
Mch_RemoveOldPlayers ();
/***** Get data of the match from database *****/
2020-04-08 03:06:45 +02:00
Match.MchCod = Mch_GetMchCodBeingPlayed ();
2019-09-14 12:59:34 +02:00
Mch_GetDataOfMatchByCod (&Match);
2019-12-12 18:24:23 +01:00
/***** Update countdown *****/
// If current countdown is < 0 ==> no countdown in progress
WhatToRefresh = REFRESH_LEFT;
if (Match.Status.Playing && // Match is being played
Match.Status.Countdown >= 0) // Countdown in progress
{
/* Decrease countdown */
Match.Status.Countdown -= Cfg_SECONDS_TO_REFRESH_MATCH_TCH;
/* On countdown reached, set match status to next (forward) status */
if (Match.Status.Countdown <= 0) // End of countdown reached
{
Mch_SetMatchStatusToNext (&Match);
WhatToRefresh = REFRESH_ALL; // Refresh the whole page
}
}
2019-09-14 12:59:34 +02:00
/***** Update match status in database *****/
Mch_UpdateMatchStatusInDB (&Match);
/***** Update elapsed time in this question *****/
Mch_UpdateElapsedTimeInQuestion (&Match);
/***** Show current match status *****/
2019-12-12 18:24:23 +01:00
switch (WhatToRefresh)
{
case REFRESH_LEFT: // Refresh only left part
2019-12-14 01:31:32 +01:00
HTM_Txt ("match_left|0|"); // 0 ==> do not evaluate MatJax scripts after updating HTML
2019-12-12 18:24:23 +01:00
Mch_ShowRefreshablePartTch (&Match);
break;
case REFRESH_ALL: // Refresh the whole page
2019-12-14 01:31:32 +01:00
HTM_Txt ("match|1|"); // 1 ==> evaluate MatJax scripts after updating HTML
2019-12-12 18:24:23 +01:00
Mch_ShowMatchStatusForTch (&Match);
break;
}
2019-09-14 12:59:34 +02:00
}
/*****************************************************************************/
/*************** Refresh current game for a student via AJAX *****************/
/*****************************************************************************/
void Mch_RefreshMatchStd (void)
{
2020-04-08 03:06:45 +02:00
struct Mch_Match Match;
2019-09-14 12:59:34 +02:00
if (!Gbl.Session.IsOpen) // If session has been closed, do not write anything
return;
2020-04-23 23:09:28 +02:00
/***** Reset match *****/
Mch_ResetMatch (&Match);
2019-09-14 12:59:34 +02:00
/***** Get data of the match from database *****/
2020-04-08 03:06:45 +02:00
Match.MchCod = Mch_GetMchCodBeingPlayed ();
2019-09-14 12:59:34 +02:00
Mch_GetDataOfMatchByCod (&Match);
/***** Show current match status *****/
2019-12-03 02:46:46 +01:00
Mch_ShowMatchStatusForStd (&Match,Mch_REFRESH_STATUS_BY_SERVER);
2019-09-14 12:59:34 +02:00
}
/*****************************************************************************/
/**** Receive previous question answer in a match question from database *****/
/*****************************************************************************/
2019-09-28 02:31:42 +02:00
void Mch_GetQstAnsFromDB (long MchCod,long UsrCod,unsigned QstInd,
struct Mch_UsrAnswer *UsrAnswer)
2019-09-14 12:59:34 +02:00
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumRows;
2019-09-24 01:41:51 +02:00
/***** Set default values for number of option and answer index *****/
UsrAnswer->NumOpt = -1; // < 0 ==> no answer selected
UsrAnswer->AnsInd = -1; // < 0 ==> no answer selected
2019-09-14 12:59:34 +02:00
/***** Get student's answer *****/
NumRows = (unsigned)
DB_QuerySELECT (&mysql_res,"can not get user's answer to a match question",
"SELECT NumOpt," // row[0]
"AnsInd" // row[1]
" FROM mch_answers"
" WHERE MchCod=%ld"
" AND UsrCod=%ld"
" AND QstInd=%u",
MchCod,
UsrCod,
QstInd);
2019-09-14 12:59:34 +02:00
if (NumRows) // Answer found...
{
row = mysql_fetch_row (mysql_res);
2019-09-24 01:41:51 +02:00
/***** Get number of option index (row[0]) *****/
if (sscanf (row[0],"%d",&(UsrAnswer->NumOpt)) != 1)
Lay_ShowErrorAndExit ("Error when getting student's answer to a match question.");
/***** Get answer index (row[1]) *****/
if (sscanf (row[1],"%d",&(UsrAnswer->AnsInd)) != 1)
2019-09-14 12:59:34 +02:00
Lay_ShowErrorAndExit ("Error when getting student's answer to a match question.");
}
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
/*****************************************************************************/
/********* Receive question answer from student when playing a match *********/
/*****************************************************************************/
2019-09-29 21:33:27 +02:00
void Mch_ReceiveQuestionAnswer (void)
2019-09-14 12:59:34 +02:00
{
2020-04-08 03:06:45 +02:00
struct Mch_Match Match;
2020-08-22 13:05:15 +02:00
unsigned QstInd; // 0 means that the game has not started. First question has index 1.
2019-09-24 01:41:51 +02:00
struct Mch_UsrAnswer UsrAnswer;
2019-09-14 12:59:34 +02:00
2020-04-23 23:09:28 +02:00
/***** Reset match *****/
Mch_ResetMatch (&Match);
2019-09-14 12:59:34 +02:00
/***** Get data of the match from database *****/
2020-04-08 03:06:45 +02:00
Match.MchCod = Mch_GetMchCodBeingPlayed ();
2019-09-14 12:59:34 +02:00
Mch_GetDataOfMatchByCod (&Match);
/***** Get question index from form *****/
QstInd = Gam_GetParamQstInd ();
2020-08-22 13:05:15 +02:00
/***** Get number of option selected by student from form *****/
UsrAnswer.NumOpt = Mch_GetParamNumOpt ();
/***** Store answer *****/
Mch_StoreQuestionAnswer (&Match,QstInd,&UsrAnswer);
/***** Show current match status *****/
HTM_DIV_Begin ("id=\"match\" class=\"MCH_CONT\"");
Mch_ShowMatchStatusForStd (&Match,Mch_CHANGE_STATUS_BY_STUDENT);
HTM_DIV_End ();
}
/*****************************************************************************/
/********** Store question answer from student when playing a match **********/
/*****************************************************************************/
2020-08-26 12:06:25 +02:00
void Mch_StoreQuestionAnswer (const struct Mch_Match *Match,unsigned QstInd,
2020-08-22 13:05:15 +02:00
struct Mch_UsrAnswer *UsrAnswer)
{
unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION];
struct Mch_UsrAnswer PreviousUsrAnswer;
2019-10-01 23:05:07 +02:00
/***** Check that teacher's screen is showing answers
and question index is the current one being played *****/
2020-08-22 13:05:15 +02:00
if (Match->Status.Playing && // Match is being played
Match->Status.Showing == Mch_ANSWERS && // Teacher's screen is showing answers
QstInd == Match->Status.QstInd) // Receiving an answer to the current question being played
2019-09-14 12:59:34 +02:00
{
2019-12-03 02:46:46 +01:00
/***** Get indexes for this question from database *****/
2020-08-22 13:05:15 +02:00
Mch_GetIndexes (Match->MchCod,Match->Status.QstInd,Indexes);
2019-12-03 02:46:46 +01:00
2019-09-14 12:59:34 +02:00
/***** Get answer index *****/
2019-09-24 01:41:51 +02:00
/*
Indexes[4] = {0,3,1,2}
2020-05-18 14:34:31 +02:00
+--------+--------+----------+---------+
2019-09-24 01:41:51 +02:00
| Button | Option | Answer | Correct |
| letter | number | index | |
| screen | screen | database | |
+--------+--------+----------+---------+
| a | 0 | 0 | |
| b | 1 | 3 | |
| c | 2 | 1 | Y | <---- User press button #2 (index = 1, correct)
| d | 3 | 2 | |
+--------+--------+----------+---------+
2020-08-22 13:05:15 +02:00
UsrAnswer->NumOpt = 2
UsrAnswer->AnsInd = 1
2019-09-24 01:41:51 +02:00
*/
2020-08-22 13:05:15 +02:00
UsrAnswer->AnsInd = Indexes[UsrAnswer->NumOpt];
2019-09-14 12:59:34 +02:00
/***** Get previous student's answer to this question
(<0 ==> no answer) *****/
2020-08-22 13:05:15 +02:00
Mch_GetQstAnsFromDB (Match->MchCod,Gbl.Usrs.Me.UsrDat.UsrCod,Match->Status.QstInd,
2019-09-24 01:41:51 +02:00
&PreviousUsrAnswer);
2019-09-14 12:59:34 +02:00
/***** Store student's answer *****/
2020-08-22 13:05:15 +02:00
if (UsrAnswer->NumOpt >= 0 &&
UsrAnswer->AnsInd >= 0 &&
UsrAnswer->AnsInd != PreviousUsrAnswer.AnsInd)
2020-05-18 14:34:31 +02:00
{
/***** Update my answer to this question *****/
2020-08-22 13:05:15 +02:00
Mch_UpdateMyAnswerToMatchQuestion (Match,UsrAnswer);
2020-05-18 14:34:31 +02:00
/***** Compute score and update my match result *****/
2020-08-22 13:05:15 +02:00
MchPrn_ComputeScoreAndUpdateMyMatchPrintInDB (Match->MchCod);
2020-05-18 14:34:31 +02:00
}
2019-09-14 12:59:34 +02:00
}
}
2019-12-03 02:46:46 +01:00
/*****************************************************************************/
2020-05-18 14:34:31 +02:00
/******************** Update my answer to match question *********************/
/*****************************************************************************/
static void Mch_UpdateMyAnswerToMatchQuestion (const struct Mch_Match *Match,
const struct Mch_UsrAnswer *UsrAnswer)
{
DB_QueryREPLACE ("can not register your answer to the match question",
"REPLACE mch_answers"
" (MchCod,UsrCod,QstInd,NumOpt,AnsInd)"
" VALUES"
" (%ld,%ld,%u,%d,%d)",
Match->MchCod,
Gbl.Usrs.Me.UsrDat.UsrCod,
Match->Status.QstInd,
2020-05-18 14:34:31 +02:00
UsrAnswer->NumOpt,
UsrAnswer->AnsInd);
}
/*****************************************************************************/
/******************* Remove my answer to match question **********************/
2019-12-03 02:46:46 +01:00
/*****************************************************************************/
2020-04-08 03:06:45 +02:00
static void Mch_RemoveMyAnswerToMatchQuestion (const struct Mch_Match *Match)
2019-12-03 02:46:46 +01:00
{
DB_QueryDELETE ("can not remove your answer to the match question",
"DELETE FROM mch_answers"
" WHERE MchCod=%ld"
" AND UsrCod=%ld"
" AND QstInd=%u",
Match->MchCod,
Gbl.Usrs.Me.UsrDat.UsrCod,
Match->Status.QstInd);
2019-12-03 02:46:46 +01:00
}
2020-05-18 14:34:31 +02:00
/*****************************************************************************/
/*************** Get the questions of a match from database ******************/
/*****************************************************************************/
2020-06-24 20:10:57 +02:00
void Mch_GetMatchQuestionsFromDB (struct MchPrn_Print *Print)
2020-05-18 14:34:31 +02:00
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumQst;
long LongNum;
unsigned QstInd;
struct Mch_UsrAnswer UsrAnswer;
/***** Get questions and answers of a match result *****/
2020-06-24 02:15:50 +02:00
Print->NumQsts.All = (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_indexes"
" WHERE mch_matches.MchCod=%ld"
" AND mch_matches.GamCod=gam_questions.GamCod"
" AND mch_matches.MchCod=mch_indexes.MchCod"
" AND gam_questions.QstInd=mch_indexes.QstInd"
" ORDER BY gam_questions.QstInd",
Print->MchCod);
2020-06-24 02:15:50 +02:00
for (NumQst = 0, Print->NumQsts.NotBlank = 0;
NumQst < Print->NumQsts.All;
2020-05-18 14:34:31 +02:00
NumQst++)
{
row = mysql_fetch_row (mysql_res);
/* Get question code (row[0]) */
if ((Print->PrintedQuestions[NumQst].QstCod = Str_ConvertStrCodToLongCod (row[0])) < 0)
Lay_WrongQuestionExit ();
2020-05-18 14:34:31 +02:00
/* Get question index (row[1]) */
if ((LongNum = Str_ConvertStrCodToLongCod (row[1])) < 0)
Lay_WrongQuestionExit ();
2020-05-18 14:34:31 +02:00
QstInd = (unsigned) LongNum;
/* Get indexes for this question (row[2]) */
Str_Copy (Print->PrintedQuestions[NumQst].StrIndexes,row[2],
sizeof (Print->PrintedQuestions[NumQst].StrIndexes) - 1);
2020-05-18 14:34:31 +02:00
/* Get answers selected by user for this question */
2020-06-24 20:10:57 +02:00
Mch_GetQstAnsFromDB (Print->MchCod,Print->UsrCod,QstInd,&UsrAnswer);
2020-05-18 14:34:31 +02:00
if (UsrAnswer.AnsInd >= 0) // UsrAnswer.AnsInd >= 0 ==> answer selected
{
snprintf (Print->PrintedQuestions[NumQst].StrAnswers,
sizeof (Print->PrintedQuestions[NumQst].StrAnswers),
"%d",UsrAnswer.AnsInd);
2020-06-24 02:15:50 +02:00
Print->NumQsts.NotBlank++;
2020-05-18 14:34:31 +02:00
}
else // UsrAnswer.AnsInd < 0 ==> no answer selected
Print->PrintedQuestions[NumQst].StrAnswers[0] = '\0'; // Empty answer
}
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
2019-09-23 01:48:28 +02:00
/*****************************************************************************/
/******************** Compute match score for a student **********************/
/*****************************************************************************/
2020-06-24 20:10:57 +02:00
void Mch_ComputeScore (struct MchPrn_Print *Print)
2019-09-23 01:48:28 +02:00
{
unsigned NumQst;
2020-03-19 20:57:54 +01:00
struct Tst_Question Question;
2019-09-23 01:48:28 +02:00
2020-05-07 18:33:26 +02:00
for (NumQst = 0, Print->Score = 0.0;
2020-06-24 02:15:50 +02:00
NumQst < Print->NumQsts.All;
2019-09-23 19:17:12 +02:00
NumQst++)
2019-09-23 01:48:28 +02:00
{
2020-03-25 01:36:22 +01:00
/***** Create test question *****/
Tst_QstConstructor (&Question);
2020-05-07 18:33:26 +02:00
Question.QstCod = Print->PrintedQuestions[NumQst].QstCod;
2020-03-25 01:36:22 +01:00
Question.Answer.Type = Tst_ANS_UNIQUE_CHOICE;
2019-09-23 19:17:12 +02:00
2020-04-01 03:11:05 +02:00
/***** Compute score for this answer ******/
2020-05-13 12:53:27 +02:00
TstPrn_ComputeAnswerScore (&Print->PrintedQuestions[NumQst],&Question);
2019-09-23 01:48:28 +02:00
2020-03-23 21:40:17 +01:00
/***** Update total score *****/
2020-05-07 18:33:26 +02:00
Print->Score += Print->PrintedQuestions[NumQst].Score;
2019-09-24 21:29:01 +02:00
2020-03-25 01:36:22 +01:00
/***** Destroy test question *****/
Tst_QstDestructor (&Question);
}
2019-09-23 01:48:28 +02:00
}
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
2019-12-12 09:14:26 +01:00
/********** Get number of users who answered a question in a match ***********/
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
2019-12-12 09:14:26 +01:00
unsigned Mch_GetNumUsrsWhoAnsweredQst (long MchCod,unsigned QstInd)
2019-09-14 12:59:34 +02:00
{
2019-12-12 09:14:26 +01:00
/***** Get number of users who answered
a question in a match from database *****/
return (unsigned)
DB_QueryCOUNT ("can not get number of users who answered a question",
"SELECT COUNT(*)"
" FROM mch_answers"
" WHERE MchCod=%ld"
" AND QstInd=%u",
MchCod,
QstInd);
2019-09-14 12:59:34 +02:00
}
/*****************************************************************************/
2019-09-16 00:15:40 +02:00
/*** Get number of users who have chosen a given answer of a game question ***/
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
2020-03-12 13:53:37 +01:00
unsigned Mch_GetNumUsrsWhoHaveChosenAns (long MchCod,unsigned QstInd,unsigned AnsInd)
2019-09-14 12:59:34 +02:00
{
/***** Get number of users who have chosen
an answer of a question from database *****/
return (unsigned)
DB_QueryCOUNT ("can not get number of users who have chosen an answer",
"SELECT COUNT(*)"
" FROM mch_answers"
" WHERE MchCod=%ld"
" AND QstInd=%u"
" AND AnsInd=%u",
MchCod,
QstInd,
AnsInd);
2019-09-14 12:59:34 +02:00
}
2019-09-17 20:44:29 +02:00
/*****************************************************************************/
2020-05-18 14:34:31 +02:00
/************ Get number of users who have played a given match **************/
2019-09-17 20:44:29 +02:00
/*****************************************************************************/
2020-05-18 14:34:31 +02:00
static unsigned Mch_GetNumUsrsWhoHavePlayedMch (long MchCod)
2019-09-17 20:44:29 +02:00
{
2020-05-18 14:34:31 +02:00
/***** Get number of users who have played the match
(users who have a result for this match, even blank result)
from database *****/
return (unsigned)
DB_QueryCOUNT ("can not get number of users who have played a match",
"SELECT COUNT(*)"
" FROM mch_results"
" WHERE MchCod=%ld",
MchCod);
2019-09-17 20:44:29 +02:00
}
2019-09-14 12:59:34 +02:00
/*****************************************************************************/
/***************** Draw a bar with the percentage of answers *****************/
/*****************************************************************************/
2019-09-16 00:15:40 +02:00
#define Mch_MAX_BAR_WIDTH 100
2019-09-14 12:59:34 +02:00
2020-03-12 13:53:37 +01:00
void Mch_DrawBarNumUsrs (unsigned NumRespondersAns,unsigned NumRespondersQst,bool Correct)
2019-09-14 12:59:34 +02:00
{
extern const char *Txt_of_PART_OF_A_TOTAL;
2019-09-16 00:15:40 +02:00
unsigned i;
2019-09-14 12:59:34 +02:00
unsigned BarWidth = 0;
/***** Begin container *****/
2019-10-24 00:04:40 +02:00
HTM_DIV_Begin ("class=\"MCH_RESULT\"");
2019-09-14 12:59:34 +02:00
/***** Draw bar with a with proportional to the number of clicks *****/
2019-12-12 09:14:26 +01:00
if (NumRespondersAns && NumRespondersQst)
BarWidth = (unsigned) ((((double) NumRespondersAns * (double) Mch_MAX_BAR_WIDTH) /
(double) NumRespondersQst) + 0.5);
2019-09-16 00:15:40 +02:00
/***** Bar proportional to number of users *****/
2019-10-23 19:05:05 +02:00
HTM_TABLE_BeginWide ();
HTM_TR_Begin ("class=\"MCH_RES_TR\"");
2019-09-16 00:15:40 +02:00
for (i = 0;
i < 100;
i++)
2019-10-07 17:36:41 +02:00
{
2019-10-23 19:05:05 +02:00
HTM_TD_Begin ("class=\"%s\"",
2019-10-21 21:36:31 +02:00
(i < BarWidth) ? (Correct ? "MCH_RES_CORRECT" :
"MCH_RES_WRONG") :
"MCH_RES_VOID");
2019-10-23 19:05:05 +02:00
HTM_TD_End ();
2019-10-07 17:36:41 +02:00
}
2019-10-23 19:05:05 +02:00
HTM_TR_End ();
HTM_TABLE_End ();
2019-09-14 12:59:34 +02:00
/***** Write the number of users *****/
2020-03-12 13:53:37 +01:00
if (NumRespondersAns && NumRespondersQst)
2019-12-30 18:08:31 +01:00
HTM_TxtF ("%u&nbsp;(%u%%&nbsp;%s&nbsp;%u)",
2020-03-12 13:53:37 +01:00
NumRespondersAns,
(unsigned) ((((double) NumRespondersAns * 100.0) / (double) NumRespondersQst) + 0.5),
Txt_of_PART_OF_A_TOTAL,NumRespondersQst);
2019-12-30 18:08:31 +01:00
else
2020-03-12 13:53:37 +01:00
HTM_NBSP ();
2019-09-16 00:15:40 +02:00
/***** End container *****/
2019-10-23 20:07:56 +02:00
HTM_DIV_End ();
2019-09-14 12:59:34 +02:00
}