diff --git a/Makefile b/Makefile
index 3af99ffd..9d3349f5 100644
--- a/Makefile
+++ b/Makefile
@@ -42,8 +42,8 @@ OBJS = swad_account.o swad_action.o swad_agenda.o swad_alert.o \
swad_help.o swad_hierarchy.o swad_holiday.o \
swad_icon.o swad_ID.o swad_indicator.o swad_info.o swad_institution.o \
swad_language.o swad_layout.o swad_link.o swad_logo.o \
- swad_mail.o swad_main.o swad_mark.o swad_media.o swad_menu.o \
- swad_message.o swad_MFU.o \
+ swad_mail.o swad_main.o swad_mark.o swad_match.o swad_media.o \
+ swad_menu.o swad_message.o swad_MFU.o \
swad_network.o swad_nickname.o swad_notice.o swad_notification.o \
swad_pagination.o swad_parameter.o swad_password.o swad_photo.o \
swad_place.o swad_plugin.o swad_privacy.o swad_profile.o swad_project.o \
diff --git a/swad_action.c b/swad_action.c
index 8be1911a..927c7fb3 100644
--- a/swad_action.c
+++ b/swad_action.c
@@ -56,6 +56,7 @@
#include "swad_language.h"
#include "swad_mail.h"
#include "swad_mark.h"
+#include "swad_match.h"
#include "swad_MFU.h"
#include "swad_network.h"
#include "swad_nickname.h"
@@ -2152,22 +2153,22 @@ struct Act_Actions Act_Actions[Act_NUM_ACTIONS] =
/* ActSeeGam */{1650,-1,TabUnk,ActSeeAllGam ,0x238,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Gam_SeeOneGame ,NULL},
- /* ActReqRemMchTch */{1783,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Gam_RequestRemoveMatchTch ,NULL},
- /* ActRemMchTch */{1784,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Gam_RemoveMatchTch ,NULL},
+ /* ActReqRemMchTch */{1783,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Mch_RequestRemoveMatchTch ,NULL},
+ /* ActRemMchTch */{1784,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Mch_RemoveMatchTch ,NULL},
/* ActReqNewMchTch */{1670,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Gam_RequestNewMatchTch ,NULL},
- /* ActNewMchTch */{1671,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_NEW_TAB,Gam_CreateNewMatchTch ,Gam_RequestStartResumeMatchTch ,NULL},
- /* ActResMchTch */{1785,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_NEW_TAB,Gam_GetMatchBeingPlayed ,Gam_RequestStartResumeMatchTch ,NULL},
- /* ActPauMchTch */{1791,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_2ND_TAB,Gam_GetMatchBeingPlayed ,Gam_PauseMatchTch ,NULL},
- /* ActPlyMchTch */{1789,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_2ND_TAB,Gam_GetMatchBeingPlayed ,Gam_ResumeMatchTch ,NULL},
- /* ActBckMchTch */{1790,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_2ND_TAB,Gam_GetMatchBeingPlayed ,Gam_BackMatchTch ,NULL},
- /* ActFwdMchTch */{1672,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_2ND_TAB,Gam_GetMatchBeingPlayed ,Gam_ForwardMatchTch ,NULL},
- /* ActShoResMchTch */{1795,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_2ND_TAB,Gam_GetMatchBeingPlayed ,Gam_ShowResultsQstMatchTch ,NULL},
- /* ActRefMchTch */{1788,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_AJAX_RFRESH,Gam_GetMatchBeingPlayed ,Gam_RefreshMatchTch ,NULL},
- /* ActShoMchTch */{1786,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Gam_ShowFinishedMatchResults ,NULL},
+ /* ActNewMchTch */{1671,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_NEW_TAB,Mch_CreateNewMatchTch ,Mch_RequestStartResumeMatchTch ,NULL},
+ /* ActResMchTch */{1785,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_NEW_TAB,Mch_GetMatchBeingPlayed ,Mch_RequestStartResumeMatchTch ,NULL},
+ /* ActPauMchTch */{1791,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_2ND_TAB,Mch_GetMatchBeingPlayed ,Mch_PauseMatchTch ,NULL},
+ /* ActPlyMchTch */{1789,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_2ND_TAB,Mch_GetMatchBeingPlayed ,Mch_ResumeMatchTch ,NULL},
+ /* ActBckMchTch */{1790,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_2ND_TAB,Mch_GetMatchBeingPlayed ,Mch_BackMatchTch ,NULL},
+ /* ActFwdMchTch */{1672,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_2ND_TAB,Mch_GetMatchBeingPlayed ,Mch_ForwardMatchTch ,NULL},
+ /* ActShoResMchTch */{1795,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_2ND_TAB,Mch_GetMatchBeingPlayed ,Mch_ShowResultsQstMatchTch ,NULL},
+ /* ActRefMchTch */{1788,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_AJAX_RFRESH,Mch_GetMatchBeingPlayed ,Mch_RefreshMatchTch ,NULL},
+ /* ActShoMchTch */{1786,-1,TabUnk,ActSeeAllGam ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Mch_ShowFinishedMatchResults ,NULL},
- /* ActPlyMchStd */{1780,-1,TabUnk,ActSeeAllGam ,0x008, 0, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_NEW_TAB,Gam_GetMatchBeingPlayed ,Gam_ShowMatchToMeAsStd ,NULL},
- /* ActRefMchStd */{1782,-1,TabUnk,ActSeeAllGam ,0x008, 0, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_AJAX_RFRESH,Gam_GetMatchBeingPlayed ,Gam_RefreshMatchStd ,NULL},
- /* ActAnsMchQstStd */{1651,-1,TabUnk,ActSeeAllGam ,0x238,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_2ND_TAB,Gam_GetMatchBeingPlayed ,Gam_ReceiveQstAnsFromStd ,NULL},
+ /* ActPlyMchStd */{1780,-1,TabUnk,ActSeeAllGam ,0x008, 0, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_NEW_TAB,Mch_GetMatchBeingPlayed ,Mch_ShowMatchToMeAsStd ,NULL},
+ /* ActRefMchStd */{1782,-1,TabUnk,ActSeeAllGam ,0x008, 0, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_AJAX_RFRESH,Mch_GetMatchBeingPlayed ,Mch_RefreshMatchStd ,NULL},
+ /* ActAnsMchQstStd */{1651,-1,TabUnk,ActSeeAllGam ,0x238,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_2ND_TAB,Mch_GetMatchBeingPlayed ,Mch_ReceiveQstAnsFromStd ,NULL},
/* ActFrmNewGam */{1652,-1,TabUnk,ActSeeAllGam ,0x238,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Gam_RequestCreatOrEditGame ,NULL},
/* ActEdiOneGam */{1653,-1,TabUnk,ActSeeAllGam ,0x238,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Gam_RequestCreatOrEditGame ,NULL},
diff --git a/swad_changelog.h b/swad_changelog.h
index fff2727e..46436e84 100644
--- a/swad_changelog.h
+++ b/swad_changelog.h
@@ -460,10 +460,11 @@ En OpenSWAD:
ps2pdf source.ps destination.pdf
*/
-#define Log_PLATFORM_VERSION "SWAD 19.0.1 (2019-09-13)"
+#define Log_PLATFORM_VERSION "SWAD 19.1 (2019-09-14)"
#define CSS_FILE "swad18.138.css"
#define JS_FILE "swad18.130.2.js"
/*
+ Version 19.1: Sep 14, 2019 Game module splitted into game and match modules. (244572 lines)
Version 19.0.1: Sep 13, 2019 Show results of questions in matches. (244583 lines)
Version 19.0: Sep 12, 2019 Changes in behaviour of matches. (244585 lines)
3 changes necessary in database:
diff --git a/swad_game.c b/swad_game.c
index f8acced8..eab96be0 100644
--- a/swad_game.c
+++ b/swad_game.c
@@ -39,6 +39,7 @@
#include "swad_game.h"
#include "swad_global.h"
#include "swad_group.h"
+#include "swad_match.h"
#include "swad_pagination.h"
#include "swad_parameter.h"
#include "swad_role.h"
@@ -72,62 +73,15 @@ const char *Gam_StrAnswerTypesDB[Gam_NUM_ANS_TYPES] =
#define Gam_MAX_SELECTED_QUESTIONS 1000
#define Gam_MAX_BYTES_LIST_SELECTED_QUESTIONS (Gam_MAX_SELECTED_QUESTIONS * (1 + 10 + 1))
-#define Gam_NEW_MATCH_SECTION_ID "new_match"
-
-#define Gam_AFTER_LAST_QUESTION ((unsigned)((1UL << 31) - 1)) // 2^31 - 1, don't change this number because it is used in database to indicate that a match is finished
-
-#define Gam_ICON_CLOSE "fas fa-times"
-#define Gam_ICON_PLAY "fas fa-play"
-#define Gam_ICON_PAUSE "fas fa-pause"
-#define Gam_ICON_PREVIOUS "fas fa-step-backward"
-#define Gam_ICON_NEXT "fas fa-step-forward"
-#define Gam_ICON_RESULTS "fas fa-chart-bar"
-
/*****************************************************************************/
/******************************* Private types *******************************/
/*****************************************************************************/
-#define Gam_NUM_SHOWING 4
-typedef enum
- {
- Gam_WORDING, // Showing only the question wording
- Gam_ANSWERS, // Showing the question wording and the answers
- Gam_REQUEST, // Requesting confirmation to show the results
- Gam_RESULTS, // Showing the results
- } Gam_Showing_t;
-#define Gam_SHOWING_DEFAULT Gam_WORDING
-const char *Gam_ShowingStringsDB[Gam_NUM_SHOWING] =
- {
- "wording",
- "answers",
- "request",
- "results",
- };
-
-struct Match
- {
- long MchCod;
- long GamCod;
- long UsrCod;
- time_t TimeUTC[2];
- char Title[Gam_MAX_BYTES_TITLE + 1];
- struct
- {
- unsigned QstInd; // 0 means that the game has not started. First question has index 1.
- long QstCod;
- time_t QstStartTimeUTC;
- Gam_Showing_t Showing;
- bool BeingPlayed;
- unsigned NumPlayers;
- } Status;
- };
-
/*****************************************************************************/
/***************************** Private variables *****************************/
/*****************************************************************************/
long Gam_CurrentGamCod = -1L; // Used as parameter in contextual links
-long Gam_CurrentMchCod = -1L; // Used as parameter in contextual links
unsigned Gam_CurrentQstInd = 0; // Used as parameter in contextual links
/*****************************************************************************/
@@ -140,46 +94,23 @@ static void Gam_PutIconsListGames (void);
static void Gam_PutIconToCreateNewGame (void);
static void Gam_PutButtonToCreateNewGame (void);
static void Gam_PutParamsToCreateNewGame (void);
-static void Gam_ShowOneGame (long GamCod,
- bool ShowOnlyThisGame,
- bool ListGameQuestions,
- bool PutFormNewMatch);
static void Gam_WriteAuthor (struct Game *Game);
static void Gam_PutFormsToRemEditOneGame (const struct Game *Game,
const char *Anchor);
-static void Gam_PutParams (void);
-static void Gam_PutParamCurrentMchCod (void);
static void Gam_PutHiddenParamOrder (void);
static void Gam_GetParamOrder (void);
static void Gam_GetGameTxtFromDB (long GamCod,char Txt[Cns_MAX_BYTES_TEXT + 1]);
-static void Gam_PutParamMatchCod (long MchCod);
-static long Gam_GetParamMatchCod (void);
-
static bool Gam_CheckIfSimilarGameExists (struct Game *Game);
-static void Gam_ShowLstGrpsToCreateMatch (void);
static void Gam_CreateGame (struct Game *Game,const char *Txt);
static void Gam_UpdateGame (struct Game *Game,const char *Txt);
-static void Gam_CreateGrps (long MchCod);
-static void Gam_GetAndWriteNamesOfGrpsAssociatedToMatch (struct Match *Match);
-static bool Gam_CheckIfIPlayThisMatchBasedOnGrps (long GamCod);
-static unsigned Gam_GetNumQstsGame (long GamCod);
-static void Gam_PutParamQstInd (unsigned QstInd);
-static unsigned Gam_GetParamQstInd (void);
-static void Gam_PutParamAnswer (unsigned AnsInd);
-static unsigned Gam_GetParamAnswer (void);
-static unsigned Gam_GetQstIndFromStr (const char *UnsignedStr);
-static Gam_Showing_t Gam_GetShowingFromStr (const char *Str);
static void Gam_RemAnswersOfAQuestion (long GamCod,unsigned QstInd);
-static long Gam_GetQstCodFromQstInd (long GamCod,unsigned QstInd);
static unsigned Gam_GetMaxQuestionIndexInGame (long GamCod);
-static unsigned Gam_GetPrevQuestionIndexInGame (long GamCod,unsigned QstInd);
-static unsigned Gam_GetNextQuestionIndexInGame (long GamCod,unsigned QstInd);
static void Gam_ListGameQuestions (struct Game *Game);
static void Gam_ListOneOrMoreQuestionsForEdition (long GamCod,unsigned NumQsts,
MYSQL_RES *mysql_res);
@@ -190,68 +121,11 @@ static void Gam_AllocateListSelectedQuestions (void);
static void Gam_FreeListsSelectedQuestions (void);
static unsigned Gam_CountNumQuestionsInList (void);
-static unsigned Gam_GetNumAnswerers (struct Match *Match);
-static unsigned Gam_GetNumUsrsWhoAnswered (long MchCod,unsigned QstInd,unsigned AnsInd);
-static void Gam_DrawBarNumUsrs (unsigned NumUsrs,unsigned MaxUsrs);
-
static void Gam_PutParamsOneQst (void);
static void Gam_ExchangeQuestions (long GamCod,
unsigned QstIndTop,unsigned QstIndBottom);
-static void Gam_ListMatches (struct Game *Game,bool PutFormNewMatch);
-static void Gam_PutIconToPlayNewMatch (void);
-static void Gam_ListOneOrMoreMatches (struct Game *Game,
- unsigned NumMatches,
- MYSQL_RES *mysql_res);
-static void Gam_GetMatchDataFromRow (MYSQL_RES *mysql_res,
- struct Match *Match);
-static void Gam_PutButtonNewMatch (long GamCod);
-
-static void Gam_PutFormNewMatch (struct Game *Game);
-
-static long Gam_CreateMatch (long GamCod,char Title[Gam_MAX_BYTES_TITLE + 1]);
-static void Gam_UpdateMatchStatusInDB (struct Match *Match);
-
-static void Gam_UpdateElapsedTimeInQuestion (struct Match *Match);
-static void Gam_GetElapsedTimeInQuestion (struct Match *Match,
- struct Time *Time);
-static void Gam_GetElapsedTimeInMatch (struct Match *Match,
- struct Time *Time);
-static void Gam_GetElapsedTime (unsigned NumRows,MYSQL_RES *mysql_res,
- struct Time *Time);
-
-static void Gam_SetMatchStatusToPrev (struct Match *Match);
-static void Gam_SetMatchStatusToNext (struct Match *Match);
-static void Gam_ShowMatchStatusForTch (struct Match *Match);
-static void Gam_ShowMatchStatusForStd (struct Match *Match);
-static void Gam_ShowLeftColumnTch (struct Match *Match);
-static void Gam_ShowLeftColumnStd (struct Match *Match);
-static void Gam_PutMatchControlButtons (struct Match *Match);
-// static void Gam_PutCheckboxResult (struct Match *Match);
-static void Gam_ShowNumQstInGame (struct Match *Match);
-static void Gam_ShowNumPlayers (struct Match *Match);
-static void Gam_ShowMatchTitle (struct Match *Match);
-static void Gam_ShowQuestionAndAnswersTch (struct Match *Match);
-static void Gam_ShowQuestionAndAnswersStd (struct Match *Match);
-
-static void Gam_PutBigButton (Act_Action_t NextAction,long MchCod,
- const char *Icon,const char *Txt);
-static void Gam_PutBigButtonOff (const char *Icon);
-static void Gam_PutBigButtonClose (void);
-
-static void Gam_ShowWaitImage (const char *Txt);
-
-static void Gam_RemoveOldPlayers (void);
-static void Gam_UpdateMatchAsBeingPlayed (long MchCod);
-static void Gam_SetMatchAsNotBeingPlayed (long MchCod);
-static bool Gam_GetIfMatchIsBeingPlayed (long MchCod);
-static void Gam_RegisterMeAsPlayerInMatch (long MchCod);
-static void Gam_GetNumPlayers (struct Match *Match);
-
-static void Gam_ShowMatchStatusForStd (struct Match *Match);
-static int Gam_GetQstAnsFromDB (long MchCod,unsigned QstInd);
-
/*****************************************************************************/
/*************************** List all the games ******************************/
/*****************************************************************************/
@@ -467,10 +341,10 @@ void Gam_SeeOneGame (void)
/******************************* Show one game *******************************/
/*****************************************************************************/
-static void Gam_ShowOneGame (long GamCod,
- bool ShowOnlyThisGame,
- bool ListGameQuestions,
- bool PutFormNewMatch)
+void Gam_ShowOneGame (long GamCod,
+ bool ShowOnlyThisGame,
+ bool ListGameQuestions,
+ bool PutFormNewMatch)
{
extern const char *Hlp_ASSESSMENT_Games;
extern const char *Txt_Game;
@@ -516,7 +390,7 @@ static void Gam_ShowOneGame (long GamCod,
if (ShowOnlyThisGame)
/* Icon to start a new match */
- Lay_PutContextualLinkOnlyIcon (ActReqNewMchTch,Gam_NEW_MATCH_SECTION_ID,
+ Lay_PutContextualLinkOnlyIcon (ActReqNewMchTch,Mch_NEW_MATCH_SECTION_ID,
Gam_PutParams,
"play.svg",
Txt_New_match);
@@ -630,7 +504,7 @@ static void Gam_ShowOneGame (long GamCod,
if (ShowOnlyThisGame)
{
/***** List matches *****/
- Gam_ListMatches (&Game,PutFormNewMatch);
+ Mch_ListMatches (&Game,PutFormNewMatch);
/***** Write questions of this game *****/
if (ListGameQuestions)
@@ -690,7 +564,7 @@ static void Gam_PutFormsToRemEditOneGame (const struct Game *Game,
/******************** Params used to edit/play a game ************************/
/*****************************************************************************/
-static void Gam_PutParams (void)
+void Gam_PutParams (void)
{
if (Gam_CurrentGamCod > 0)
Gam_PutParamGameCod (Gam_CurrentGamCod);
@@ -699,12 +573,6 @@ static void Gam_PutParams (void)
Pag_PutHiddenParamPagNum (Pag_GAMES,Gbl.Games.CurrentPage);
}
-static void Gam_PutParamCurrentMchCod (void)
- {
- if (Gam_CurrentMchCod > 0)
- Gam_PutParamMatchCod (Gam_CurrentMchCod);
- }
-
/*****************************************************************************/
/****** Put a hidden parameter with the type of order in list of games *******/
/*****************************************************************************/
@@ -975,25 +843,6 @@ long Gam_GetParamGameCod (void)
return Par_GetParToLong ("GamCod");
}
-/*****************************************************************************/
-/******************** Write parameter with code of match **********************/
-/*****************************************************************************/
-
-static void Gam_PutParamMatchCod (long MchCod)
- {
- Par_PutHiddenParamLong ("MchCod",MchCod);
- }
-
-/*****************************************************************************/
-/********************* Get parameter with code of match **********************/
-/*****************************************************************************/
-
-static long Gam_GetParamMatchCod (void)
- {
- /***** Get code of match *****/
- return Par_GetParToLong ("MchCod");
- }
-
/*****************************************************************************/
/*************** Ask for confirmation of removing of a game ******************/
/*****************************************************************************/
@@ -1332,66 +1181,6 @@ void Gam_RequestCreatOrEditGame (void)
Gam_ListGameQuestions (&Game);
}
-/*****************************************************************************/
-/***************** Show list of groups to create a new match *****************/
-/*****************************************************************************/
-
-static void Gam_ShowLstGrpsToCreateMatch (void)
- {
- 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)
- {
- /***** Start box and table *****/
- fprintf (Gbl.F.Out,"
"
- "
"
- "%s:"
- "
"
- "
",
- The_ClassFormInBox[Gbl.Prefs.Theme],
- Txt_Groups);
- Box_StartBoxTable ("95%",NULL,NULL,
- NULL,Box_NOT_CLOSABLE,0);
-
- /***** First row: checkbox to select the whole course *****/
- fprintf (Gbl.F.Out,"
"
- "
"
- ""
- "
"
- "
",
- Txt_The_whole_course,Gbl.Hierarchy.Crs.ShrtName);
-
- /***** 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)
- Grp_ListGrpsToEditAsgAttSvyMch (&Gbl.Crs.Grps.GrpTypes.LstGrpTypes[NumGrpTyp],
- -1L, // -1 means "New match"
- Grp_MATCH);
-
- /***** End table and box *****/
- Box_EndBoxTable ();
- fprintf (Gbl.F.Out,""
- "");
- }
-
- /***** Free list of groups types and groups in this course *****/
- Grp_FreeListGrpTypesAndGrps ();
- }
-
/*****************************************************************************/
/********************* Receive form to create a new game *******************/
/*****************************************************************************/
@@ -1548,91 +1337,6 @@ void Gam_RemoveGroupsOfType (long GrpTypCod)
GrpTypCod);
}
-/*****************************************************************************/
-/******************* Create groups associated to a match *********************/
-/*****************************************************************************/
-
-static void Gam_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",
- "INSERT INTO gam_grp"
- " (MchCod,GrpCod)"
- " VALUES"
- " (%ld,%ld)",
- MchCod,Gbl.Crs.Grps.LstGrpsSel.GrpCods[NumGrpSel]);
- }
-
-/*****************************************************************************/
-/************* Get and write the names of the groups of a match **************/
-/*****************************************************************************/
-
-static void Gam_GetAndWriteNamesOfGrpsAssociatedToMatch (struct Match *Match)
- {
- 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 long NumRow;
- unsigned long NumRows;
-
- /***** Get groups associated to a match from database *****/
- NumRows = DB_QuerySELECT (&mysql_res,"can not get groups of a match",
- "SELECT crs_grp_types.GrpTypName,crs_grp.GrpName"
- " FROM gam_grp,crs_grp,crs_grp_types"
- " WHERE gam_grp.MchCod=%ld"
- " AND gam_grp.GrpCod=crs_grp.GrpCod"
- " AND crs_grp.GrpTypCod=crs_grp_types.GrpTypCod"
- " ORDER BY crs_grp_types.GrpTypName,crs_grp.GrpName",
- Match->MchCod);
-
- /***** Write heading *****/
- fprintf (Gbl.F.Out,"
%s: ",
- NumRows == 1 ? Txt_Group :
- Txt_Groups);
-
- /***** Write groups *****/
- if (NumRows) // Groups found...
- {
- /* Get and write the group types and names */
- for (NumRow = 0;
- NumRow < NumRows;
- NumRow++)
- {
- /* Get next group */
- row = mysql_fetch_row (mysql_res);
-
- /* Write group type name and group name */
- fprintf (Gbl.F.Out,"%s %s",row[0],row[1]);
-
- if (NumRows >= 2)
- {
- if (NumRow == NumRows-2)
- fprintf (Gbl.F.Out," %s ",Txt_and);
- if (NumRows >= 3)
- if (NumRow < NumRows-2)
- fprintf (Gbl.F.Out,", ");
- }
- }
- }
- else
- fprintf (Gbl.F.Out,"%s %s",
- Txt_The_whole_course,Gbl.Hierarchy.Crs.ShrtName);
-
- fprintf (Gbl.F.Out,"
");
-
- /***** Free structure that stores the query result *****/
- DB_FreeMySQLResult (&mysql_res);
- }
-
/*****************************************************************************/
/************* Remove all the games of a place on the hierarchy **************/
/************* (country, institution, centre, degree or course) **************/
@@ -1674,28 +1378,11 @@ void Gam_RemoveGames (Hie_Level_t Scope,long Cod)
Sco_GetDBStrFromScope (Scope),Cod);
}
-/*****************************************************************************/
-/************ Check if I belong to any of the groups of a match **************/
-/*****************************************************************************/
-
-static bool Gam_CheckIfIPlayThisMatchBasedOnGrps (long MchCod)
- {
- /***** Get if I can play a match from database *****/
- return (DB_QueryCOUNT ("can not check if I can play a match",
- "SELECT COUNT(*) FROM gam_matches"
- " WHERE MchCod=%ld"
- " AND (MchCod NOT IN (SELECT MchCod FROM gam_grp) OR"
- " MchCod IN (SELECT gam_grp.MchCod FROM gam_grp,crs_grp_usr"
- " WHERE crs_grp_usr.UsrCod=%ld"
- " AND gam_grp.GrpCod=crs_grp_usr.GrpCod))",
- MchCod,Gbl.Usrs.Me.UsrDat.UsrCod) != 0);
- }
-
/*****************************************************************************/
/******************* Get number of questions of a game *********************/
/*****************************************************************************/
-static unsigned Gam_GetNumQstsGame (long GamCod)
+unsigned Gam_GetNumQstsGame (long GamCod)
{
/***** Get nuumber of questions in a game from database *****/
return
@@ -1736,7 +1423,7 @@ void Gam_RequestNewQuestion (void)
/****************** Write parameter with index of question *******************/
/*****************************************************************************/
-static void Gam_PutParamQstInd (unsigned QstInd)
+void Gam_PutParamQstInd (unsigned QstInd)
{
Par_PutHiddenParamUnsigned ("QstInd",QstInd);
}
@@ -1745,7 +1432,7 @@ static void Gam_PutParamQstInd (unsigned QstInd)
/******************* Get parameter with index of question ********************/
/*****************************************************************************/
-static unsigned Gam_GetParamQstInd (void)
+unsigned Gam_GetParamQstInd (void)
{
long LongNum;
@@ -1756,35 +1443,11 @@ static unsigned Gam_GetParamQstInd (void)
return (unsigned) LongNum;
}
-/*****************************************************************************/
-/******************* Write parameter with student's answer *******************/
-/*****************************************************************************/
-
-static void Gam_PutParamAnswer (unsigned AnsInd)
- {
- Par_PutHiddenParamUnsigned ("Ans",AnsInd);
- }
-
-/*****************************************************************************/
-/******************* Get parameter with student's answer *********************/
-/*****************************************************************************/
-
-static unsigned Gam_GetParamAnswer (void)
- {
- long LongNum;
-
- LongNum = Par_GetParToLong ("Ans");
- if (LongNum < 0)
- Lay_ShowErrorAndExit ("Wrong answer index.");
-
- return (unsigned) LongNum;
- }
-
/*****************************************************************************/
/******************* Get parameter with index of question ********************/
/*****************************************************************************/
-static unsigned Gam_GetQstIndFromStr (const char *UnsignedStr)
+unsigned Gam_GetQstIndFromStr (const char *UnsignedStr)
{
long LongNum;
@@ -1793,23 +1456,6 @@ static unsigned Gam_GetQstIndFromStr (const char *UnsignedStr)
0;
}
-/*****************************************************************************/
-/****************** Get parameter with what is being shown *******************/
-/*****************************************************************************/
-
-static Gam_Showing_t Gam_GetShowingFromStr (const char *Str)
- {
- Gam_Showing_t Showing;
-
- for (Showing = (Gam_Showing_t) 0;
- Showing <= (Gam_Showing_t) (Gam_NUM_SHOWING - 1);
- Showing++)
- if (!strcmp (Str,Gam_ShowingStringsDB[Showing]))
- return Showing;
-
- return (Gam_Showing_t) Gam_SHOWING_DEFAULT;
- }
-
/*****************************************************************************/
/********************** Remove answers of a game question ********************/
/*****************************************************************************/
@@ -1827,7 +1473,7 @@ static void Gam_RemAnswersOfAQuestion (long GamCod,unsigned QstInd)
/************ Get question code given game and index of question *************/
/*****************************************************************************/
-static long Gam_GetQstCodFromQstInd (long GamCod,unsigned QstInd)
+long Gam_GetQstCodFromQstInd (long GamCod,unsigned QstInd)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
@@ -1884,7 +1530,7 @@ static unsigned Gam_GetMaxQuestionIndexInGame (long GamCod)
// Input question index can be 1, 2, 3... n-1
// Return question index will be 1, 2, 3... n if previous question exists, or 0 if no previous question
-static unsigned Gam_GetPrevQuestionIndexInGame (long GamCod,unsigned QstInd)
+unsigned Gam_GetPrevQuestionIndexInGame (long GamCod,unsigned QstInd)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
@@ -1917,11 +1563,11 @@ static unsigned Gam_GetPrevQuestionIndexInGame (long GamCod,unsigned QstInd)
// Input question index can be 0, 1, 2, 3... n-1
// Return question index will be 1, 2, 3... n if next question exists, or 0 if no next question
-static unsigned Gam_GetNextQuestionIndexInGame (long GamCod,unsigned QstInd)
+unsigned Gam_GetNextQuestionIndexInGame (long GamCod,unsigned QstInd)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
- unsigned NextQstInd = Gam_AFTER_LAST_QUESTION; // End of questions has been reached
+ unsigned NextQstInd = Mch_AFTER_LAST_QUESTION; // End of questions has been reached
/***** Get next question index in a game from database *****/
// Although indexes are always continuous...
@@ -2340,95 +1986,6 @@ static unsigned Gam_CountNumQuestionsInList (void)
return NumQuestions;
}
-/*****************************************************************************/
-/*** Get number of users who selected this answer and draw proportional bar **/
-/*****************************************************************************/
-
-void Gam_GetAndDrawBarNumUsrsWhoAnswered (long MchCod,unsigned QstInd,unsigned AnsInd,unsigned NumUsrs)
- {
- unsigned NumUsrsThisAnswer;
-
- /***** Get number of users who selected this answer *****/
- NumUsrsThisAnswer = Gam_GetNumUsrsWhoAnswered (MchCod,QstInd,AnsInd);
-
- /***** Show stats of this answer *****/
- Gam_DrawBarNumUsrs (NumUsrsThisAnswer,NumUsrs);
- }
-
-/*****************************************************************************/
-/***** Get number of users who have answered current question in a match *****/
-/*****************************************************************************/
-
-static unsigned Gam_GetNumAnswerers (struct Match *Match)
- {
- /***** Get number of users who have answered the current question in a match from database *****/
- return
- (unsigned) DB_QueryCOUNT ("can not get number of questions of a game",
- "SELECT COUNT(*) FROM gam_answers"
- " WHERE MchCod=%ld AND QstInd=%u",
- Match->MchCod,Match->Status.QstInd);
- }
-
-/*****************************************************************************/
-/**** Get number of users who selected a given answer of a game question *****/
-/*****************************************************************************/
-
-static unsigned Gam_GetNumUsrsWhoAnswered (long MchCod,unsigned QstInd,unsigned AnsInd)
- {
- /***** 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 answered",
- "SELECT COUNT(*)"
- " FROM gam_answers"
- " WHERE MchCod=%ld"
- " AND QstInd=%u"
- " AND AnsInd=%u",
- MchCod,QstInd,AnsInd);
- }
-
-/*****************************************************************************/
-/***************** Draw a bar with the percentage of answers *****************/
-/*****************************************************************************/
-
-#define Gam_MAX_BAR_WIDTH 125
-
-static void Gam_DrawBarNumUsrs (unsigned NumUsrs,unsigned MaxUsrs)
- {
- extern const char *Txt_of_PART_OF_A_TOTAL;
- unsigned BarWidth = 0;
-
- /***** String with the number of users *****/
- if (MaxUsrs)
- snprintf (Gbl.Title,sizeof (Gbl.Title),
- "%u (%u%% %s %u)",
- NumUsrs,
- (unsigned) ((((float) NumUsrs * 100.0) / (float) MaxUsrs) + 0.5),
- Txt_of_PART_OF_A_TOTAL,MaxUsrs);
- else
- snprintf (Gbl.Title,sizeof (Gbl.Title),
- "0 (0%% %s %u)",
- Txt_of_PART_OF_A_TOTAL,MaxUsrs);
-
- /***** Draw bar with a with proportional to the number of clicks *****/
- if (NumUsrs && MaxUsrs)
- BarWidth = (unsigned) ((((float) NumUsrs * (float) Gam_MAX_BAR_WIDTH) /
- (float) MaxUsrs) + 0.5);
- if (BarWidth < 2)
- BarWidth = 2;
- fprintf (Gbl.F.Out,""
- " ",
- Cfg_URL_ICON_PUBLIC,
- Gbl.Title,
- Gbl.Title,
- BarWidth);
-
- /***** Write the number of users *****/
- fprintf (Gbl.F.Out,"%s",Gbl.Title);
- }
-
/*****************************************************************************/
/**************** Put parameter to move/remove one question ******************/
/*****************************************************************************/
@@ -2673,522 +2230,7 @@ static void Gam_ExchangeQuestions (long GamCod,
}
/*****************************************************************************/
-/************************* List the matches of a game ************************/
-/*****************************************************************************/
-
-static void Gam_ListMatches (struct Game *Game,bool PutFormNewMatch)
- {
- 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 */
- if (Gbl.Crs.Grps.WhichGrps == Grp_ONLY_MY_GROUPS)
- {
- if (asprintf (&SubQuery," AND"
- "(MchCod NOT IN"
- " (SELECT MchCod FROM gam_grp)"
- " OR"
- " MchCod IN"
- " (SELECT gam_grp.MchCod"
- " FROM gam_grp,crs_grp_usr"
- " WHERE crs_grp_usr.UsrCod=%ld"
- " AND gam_grp.GrpCod=crs_grp_usr.GrpCod))",
- 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]
- "UNIX_TIMESTAMP(QstStartTime)," // row[8]
- "Showing" // row[9]
- " FROM gam_matches"
- " WHERE GamCod=%ld%s"
- " ORDER BY MchCod",
- Game->GamCod,
- SubQuery);
-
- /* Free allocated memory for subquery */
- free ((void *) SubQuery);
-
- /***** Start box *****/
- Gam_CurrentGamCod = Game->GamCod;
- Box_StartBox (NULL,Txt_Matches,Gam_PutIconToPlayNewMatch,
- Hlp_ASSESSMENT_Games_matches,Box_NOT_CLOSABLE);
-
- if (NumMatches)
- /***** Show the table with the matches *****/
- Gam_ListOneOrMoreMatches (Game,NumMatches,mysql_res);
-
- /***** 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)
- Gam_PutFormNewMatch (Game); // Form to fill in data and start playing a new match
- else
- Gam_PutButtonNewMatch (Game->GamCod); // Button to create a new match
- break;
- default:
- break;
- }
-
- /***** End box *****/
- Box_EndBox ();
- }
-
-/*****************************************************************************/
-/********************** Get match data using its code ************************/
-/*****************************************************************************/
-
-void Gam_GetDataOfMatchByCod (struct Match *Match)
- {
- MYSQL_RES *mysql_res;
- unsigned long NumRows;
-
- /***** Get data of match from database *****/
- 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]
- "UNIX_TIMESTAMP(QstStartTime)," // row[8]
- "Showing" // row[9]
- " FROM gam_matches"
- " WHERE MchCod=%ld"
- " AND GamCod IN" // Extra check
- " (SELECT GamCod FROM games"
- " WHERE CrsCod='%ld')",
- Match->MchCod,
- Gbl.Hierarchy.Crs.CrsCod);
- if (NumRows) // Match found...
- /***** Get match data from row *****/
- Gam_GetMatchDataFromRow (mysql_res,Match);
- else
- {
- /* Initialize to empty match */
- Match->MchCod = -1L;
- Match->GamCod = -1L;
- Match->UsrCod = -1L;
- Match->TimeUTC[Gam_START_TIME] =
- Match->TimeUTC[Gam_END_TIME ] = (time_t) 0;
- Match->Title[0] = '\0';
- Match->Status.QstInd = 0;
- Match->Status.QstCod = -1L;
- Match->Status.QstStartTimeUTC = (time_t) 0;
- Match->Status.Showing = Gam_WORDING;
- Match->Status.BeingPlayed = false;
- }
-
- /***** Free structure that stores the query result *****/
- DB_FreeMySQLResult (&mysql_res);
- }
-
-/*****************************************************************************/
-/***************** Put icon to add a new questions to game *******************/
-/*****************************************************************************/
-
-static void Gam_PutIconToPlayNewMatch (void)
- {
- extern const char *Txt_New_match;
-
- /***** Put form to create a new question *****/
- Ico_PutContextualIconToAdd (ActReqNewMchTch,Gam_NEW_MATCH_SECTION_ID,Gam_PutParams,
- Txt_New_match);
- }
-
-/*****************************************************************************/
-/*********************** List game matches for edition ***********************/
-/*****************************************************************************/
-
-static void Gam_ListOneOrMoreMatches (struct Game *Game,
- unsigned NumMatches,
- MYSQL_RES *mysql_res)
- {
- extern const char *Txt_No_INDEX;
- 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;
- extern const char *Txt_Status;
- extern const char *Txt_Play;
- extern const char *Txt_Resume;
- extern const char *Txt_Today;
- unsigned NumMatch;
- unsigned UniqueId;
- struct Match Match;
-
- /***** Write the heading *****/
- Tbl_StartTableWideMargin (2);
- fprintf (Gbl.F.Out,"
",
- UniqueId,Match.TimeUTC[Gam_END_TIME],
- (unsigned) Gbl.Prefs.DateFormat,Txt_Today);
-
- /***** Title and groups *****/
- fprintf (Gbl.F.Out,"
",Gbl.RowEvenOdd);
-
- /* Title */
- fprintf (Gbl.F.Out,"%s",Match.Title);
-
- /* Groups whose students can answer this match */
- if (Gbl.Crs.Grps.NumGrps)
- Gam_GetAndWriteNamesOfGrpsAssociatedToMatch (&Match);
-
- fprintf (Gbl.F.Out,"
");
-
- /***** Match status ******/
- fprintf (Gbl.F.Out,"
",Gbl.RowEvenOdd);
-
- if (Match.Status.QstInd < Gam_AFTER_LAST_QUESTION) // Unfinished match
- /* Current question index / total of questions */
- fprintf (Gbl.F.Out,"
%u/%u
",
- Match.Status.QstInd,Game->NumQsts);
-
- switch (Gbl.Usrs.Me.Role.Logged)
- {
- case Rol_STD:
- /* Icon to play as student */
- Gam_CurrentMchCod = Match.MchCod;
- Lay_PutContextualLinkOnlyIcon (ActPlyMchStd,NULL,
- Gam_PutParamCurrentMchCod,
- Match.Status.QstInd < Gam_AFTER_LAST_QUESTION ? "play.svg" :
- "flag-checkered.svg",
- Txt_Play);
- break;
- case Rol_NET:
- case Rol_TCH:
- case Rol_DEG_ADM:
- case Rol_CTR_ADM:
- case Rol_INS_ADM:
- case Rol_SYS_ADM:
- /* Icon to resume */
- Gam_CurrentMchCod = Match.MchCod;
- Lay_PutContextualLinkOnlyIcon (ActResMchTch,NULL,
- Gam_PutParamCurrentMchCod,
- Match.Status.QstInd < Gam_AFTER_LAST_QUESTION ? "play.svg" :
- "flag-checkered.svg",
- Txt_Resume);
- break;
- default:
- break;
- }
-
- fprintf (Gbl.F.Out,"
");
-
- fprintf (Gbl.F.Out,"
");
- }
-
- /***** End table *****/
- Tbl_EndTable ();
- }
-
-/*****************************************************************************/
-/******************** Get game data from a database row **********************/
-/*****************************************************************************/
-
-static void Gam_GetMatchDataFromRow (MYSQL_RES *mysql_res,
- struct Match *Match)
- {
- MYSQL_ROW row;
-
- /***** Get match data *****/
- row = mysql_fetch_row (mysql_res);
- /*
- row[0] MchCod
- row[1] GamCod
- row[2] UsrCod
- row[3] UNIX_TIMESTAMP(StartTime)
- row[4] UNIX_TIMESTAMP(EndTime)
- row[5] Title
- */
- /***** Get match data *****/
- /* Code of the match (row[0]) */
- if ((Match->MchCod = Str_ConvertStrCodToLongCod (row[0])) <= 0)
- Lay_ShowErrorAndExit ("Wrong code of match.");
-
- /* Code of the game (row[1]) */
- if ((Match->GamCod = Str_ConvertStrCodToLongCod (row[1])) <= 0)
- Lay_ShowErrorAndExit ("Wrong code of game.");
-
- /* Get match teacher (row[2]) */
- Match->UsrCod = Str_ConvertStrCodToLongCod (row[2]);
-
- /* Get start date (row[3] holds the start UTC time) */
- Match->TimeUTC[Gam_START_TIME] = Dat_GetUNIXTimeFromStr (row[3]);
-
- /* Get end date (row[4] holds the end UTC time) */
- Match->TimeUTC[Gam_END_TIME ] = Dat_GetUNIXTimeFromStr (row[4]);
-
- /* Get the title of the game (row[5]) */
- if (row[5])
- Str_Copy (Match->Title,row[5],
- Gam_MAX_BYTES_TITLE);
- else
- Match->Title[0] = '\0';
-
- /***** Get current match status *****/
- /*
- row[6] QstInd
- row[7] QstCod
- row[8] UNIX_TIMESTAMP(QstStartTime)
- row[9] Showing
- */
- /* Current question index (row[6]) */
- Match->Status.QstInd = Gam_GetQstIndFromStr (row[6]);
-
- /* Current question code (row[7]) */
- Match->Status.QstCod = Str_ConvertStrCodToLongCod (row[7]);
-
- /* Get question start date (row[8] holds the start UTC time) */
- Match->Status.QstStartTimeUTC = Dat_GetUNIXTimeFromStr (row[8]);
-
- /* Get what to show (stem, answers, results) (row(9)) */
- Match->Status.Showing = Gam_GetShowingFromStr (row[9]);
-
- /***** Get whether the match is being played or not *****/
- if (Match->Status.QstInd >= Gam_AFTER_LAST_QUESTION) // Finished
- Match->Status.BeingPlayed = false;
- else // Unfinished
- Match->Status.BeingPlayed = Gam_GetIfMatchIsBeingPlayed (Match->MchCod);
- }
-
-/*****************************************************************************/
-/************** Request the removal of a match (game instance) ***************/
-/*****************************************************************************/
-
-void Gam_RequestRemoveMatchTch (void)
- {
- extern const char *Txt_Do_you_really_want_to_remove_the_match_X;
- extern const char *Txt_Remove_match;
- struct Match Match;
-
- /***** Get parameters *****/
- /* Get match code */
- if ((Match.MchCod = Gam_GetParamMatchCod ()) == -1L)
- Lay_ShowErrorAndExit ("Code of match is missing.");
-
- /***** Get data of the match from database *****/
- Gam_GetDataOfMatchByCod (&Match);
-
- /***** Show question and button to remove question *****/
- Gam_CurrentMchCod = Match.MchCod;
- Ale_ShowAlertAndButton (ActRemMchTch,NULL,NULL,Gam_PutParamCurrentMchCod,
- Btn_REMOVE_BUTTON,Txt_Remove_match,
- Ale_QUESTION,Txt_Do_you_really_want_to_remove_the_match_X,
- Match.Title);
-
- /***** Show current game *****/
- Gam_ShowOneGame (Match.GamCod,
- true, // Show only this game
- false, // Do not list game questions
- false); // Do not put form to start new match
- }
-
-/*****************************************************************************/
-/********************** Remove a match (game instance) ***********************/
-/*****************************************************************************/
-
-void Gam_RemoveMatchTch (void)
- {
- extern const char *Txt_Match_X_removed;
- struct Match Match;
-
- /***** Get parameters *****/
- /* Get match code */
- if ((Match.MchCod = Gam_GetParamMatchCod ()) == -1L)
- Lay_ShowErrorAndExit ("Code of match is missing.");
-
- /***** Get data of the match from database *****/
- Gam_GetDataOfMatchByCod (&Match);
-
- /***** Remove the match from all the tables *****/
- /* Remove match players */
- DB_QueryDELETE ("can not remove match players",
- "DELETE FROM gam_players"
- " USING gam_players,gam_matches,games"
- " WHERE gam_players.MchCod=%ld"
- " AND gam_players.MchCod=gam_matches.MchCod"
- " AND gam_matches.GamCod=games.GamCod"
- " AND games.CrsCod=%ld", // Extra check
- Match.MchCod,Gbl.Hierarchy.Crs.CrsCod);
-
- /* Remove match from list of matches being played */
- DB_QueryDELETE ("can not remove match from matches being played",
- "DELETE FROM gam_mch_being_played"
- " USING gam_mch_being_played,gam_matches,games"
- " WHERE gam_mch_being_played.MchCod=%ld"
- " AND gam_mch_being_played.MchCod=gam_matches.MchCod"
- " AND gam_matches.GamCod=games.GamCod"
- " AND games.CrsCod=%ld", // Extra check
- Match.MchCod,Gbl.Hierarchy.Crs.CrsCod);
-
- /* Remove students' answers to match */
- DB_QueryDELETE ("can not remove students' answers associated to a match",
- "DELETE FROM gam_answers"
- " USING gam_answers,gam_matches,games"
- " WHERE gam_answers.MchCod=%ld"
- " AND gam_answers.MchCod=gam_matches.MchCod"
- " AND gam_matches.GamCod=games.GamCod"
- " AND games.CrsCod=%ld", // Extra check
- Match.MchCod,Gbl.Hierarchy.Crs.CrsCod);
-
- /* Remove groups associated to the match */
- DB_QueryDELETE ("can not remove the groups associated to a match",
- "DELETE FROM gam_grp"
- " USING gam_grp,gam_matches,games"
- " WHERE gam_grp.MchCod=%ld"
- " AND gam_grp.MchCod=gam_matches.MchCod"
- " AND gam_matches.GamCod=games.GamCod"
- " AND games.CrsCod=%ld", // Extra check
- Match.MchCod,Gbl.Hierarchy.Crs.CrsCod);
-
- /* Remove the match itself */
- DB_QueryDELETE ("can not remove a match",
- "DELETE FROM gam_matches"
- " USING gam_matches,games"
- " WHERE gam_matches.MchCod=%ld"
- " AND gam_matches.GamCod=games.GamCod"
- " AND games.CrsCod=%ld", // Extra check
- Match.MchCod,Gbl.Hierarchy.Crs.CrsCod);
- if (!mysql_affected_rows (&Gbl.mysql))
- Lay_ShowErrorAndExit ("The match to be removed does not exist.");
-
- /***** Write message *****/
- Ale_ShowAlert (Ale_SUCCESS,Txt_Match_X_removed,
- Match.Title);
-
- /***** Show current game *****/
- Gam_ShowOneGame (Match.GamCod,
- true, // Show only this game
- false, // Do not list game questions
- false); // Do not put form to start new match
- }
-
-/*****************************************************************************/
-/********************* Put button to create a new match **********************/
-/*****************************************************************************/
-
-static void Gam_PutButtonNewMatch (long GamCod)
- {
- extern const char *Txt_New_match;
-
- Frm_StartFormAnchor (ActReqNewMchTch,Gam_NEW_MATCH_SECTION_ID);
- Gam_PutParamGameCod (GamCod);
- Btn_PutConfirmButton (Txt_New_match);
- Frm_EndForm ();
- }
-
-/*****************************************************************************/
-/******************* Start playing a game as a teacher ***********************/
+/************* Request the creation of a new match as a teacher **************/
/*****************************************************************************/
void Gam_RequestNewMatchTch (void)
@@ -3211,1405 +2253,6 @@ void Gam_RequestNewMatchTch (void)
true); // Put form to start new match
}
-/*****************************************************************************/
-/****** Put a big button to play match (start a new match) as a teacher ******/
-/*****************************************************************************/
-
-static void Gam_PutFormNewMatch (struct Game *Game)
- {
- extern const char *Hlp_ASSESSMENT_Games_new_match;
- extern const char *The_ClassFormInBox[The_NUM_THEMES];
- extern const char *Txt_New_match;
- extern const char *Txt_Title;
- extern const char *Txt_Play;
-
- /***** Start section for a new match *****/
- Lay_StartSection (Gam_NEW_MATCH_SECTION_ID);
-
- /***** Start form *****/
- Frm_StartForm (ActNewMchTch);
- Gam_PutParamGameCod (Game->GamCod);
- Gam_PutParamQstInd (0); // Start by first question in game
-
- /***** Start box and table *****/
- Box_StartBoxTable (NULL,Txt_New_match,NULL,
- Hlp_ASSESSMENT_Games_new_match,Box_NOT_CLOSABLE,2);
-
- /***** Match title *****/
- fprintf (Gbl.F.Out,"
"
- "
"
- ""
- "
"
- "
"
- ""
- "
"
- "
",
- The_ClassFormInBox[Gbl.Prefs.Theme],
- Txt_Title,
- Gam_MAX_CHARS_TITLE,Game->Title);
-
- /***** Groups *****/
- Gam_ShowLstGrpsToCreateMatch ();
-
- /***** End table *****/
- Tbl_EndTable ();
-
- /***** Put icon with link *****/
- Frm_LinkFormSubmit (Txt_Play,NULL,NULL);
- fprintf (Gbl.F.Out,"",
- Cfg_URL_ICON_PUBLIC,Txt_Play,Txt_Play);
- fprintf (Gbl.F.Out,"");
-
- /***** End box *****/
- Box_EndBox ();
-
- /***** End form *****/
- Frm_EndForm ();
-
- /***** End section for a new match *****/
- Lay_EndSection ();
- }
-
-/*****************************************************************************/
-/********************* Create a new match (by a teacher) *********************/
-/*****************************************************************************/
-
-void Gam_CreateNewMatchTch (void)
- {
- long GamCod;
- char Title[Gam_MAX_BYTES_TITLE + 1];
-
- /***** Get form parameters *****/
- /* Get match code */
- if ((GamCod = Gam_GetParamGameCod ()) == -1L)
- Lay_ShowErrorAndExit ("Code of game is missing.");
-
- /* Get match title */
- Par_GetParToText ("Title",Title,Gam_MAX_BYTES_TITLE);
-
- /* Get groups for this games */
- Grp_GetParCodsSeveralGrps ();
-
- /***** Create a new match *****/
- Gbl.Games.MchCodBeingPlayed = Gam_CreateMatch (GamCod,Title);
-
- /***** Free memory for list of selected groups *****/
- Grp_FreeListCodSelectedGrps ();
- }
-
-/*****************************************************************************/
-/******* Show button to actually start / resume a match (by a teacher) *******/
-/*****************************************************************************/
-
-void Gam_RequestStartResumeMatchTch (void)
- {
- struct Match Match;
-
- /***** Remove old players.
- This function must be called before getting match status. *****/
- Gam_RemoveOldPlayers ();
-
- /***** Get data of the match from database *****/
- Match.MchCod = Gbl.Games.MchCodBeingPlayed;
- Gam_GetDataOfMatchByCod (&Match);
-
- /***** Update match status in database *****/
- Gam_UpdateMatchStatusInDB (&Match);
-
- /***** Show current match status *****/
- fprintf (Gbl.F.Out,"
");
- }
-
-/*****************************************************************************/
-/********************** Create a new match in a game *************************/
-/*****************************************************************************/
-
-static long Gam_CreateMatch (long GamCod,char Title[Gam_MAX_BYTES_TITLE + 1])
- {
- long MchCod;
-
- /***** Insert this new match into database *****/
- MchCod = DB_QueryINSERTandReturnCode ("can not create match",
- "INSERT gam_matches"
- " (GamCod,UsrCod,StartTime,EndTime,Title,ShowResults,"
- "QstInd,QstCod,QstStartTime,Showing)"
- " VALUES"
- " (%ld," // GamCod
- "%ld," // UsrCod
- "NOW()," // StartTime
- "NOW()," // EndTime
- "'%s'," // Title
- "'N'," // ShowResults: Don't show results initially
- "0," // QstInd: Match has not started, so not the first question yet
- "-1," // QstCod: Non-existent question
- "NOW()," // QstStartTime
- "'%s'", // What is being shown
- GamCod,
- Gbl.Usrs.Me.UsrDat.UsrCod, // Game creator
- Title,
- Gam_ShowingStringsDB[Gam_SHOWING_DEFAULT]);
-
- /***** Create groups associated to the match *****/
- if (Gbl.Crs.Grps.LstGrpsSel.NumGrps)
- Gam_CreateGrps (MchCod);
-
- return MchCod;
- }
-
-/*****************************************************************************/
-/***************** Insert/update a game match being played *******************/
-/*****************************************************************************/
-
-static void Gam_UpdateMatchStatusInDB (struct Match *Match)
- {
- /***** Update match status in database *****/
- DB_QueryUPDATE ("can not update match being played",
- "UPDATE gam_matches,games"
- " SET gam_matches.EndTime=NOW(),"
- "gam_matches.QstInd=%u,"
- "gam_matches.QstCod=%ld,"
- "gam_matches.QstStartTime=NOW(),"
- "gam_matches.Showing='%s'"
- " WHERE gam_matches.MchCod=%ld"
- " AND gam_matches.GamCod=games.GamCod"
- " AND games.CrsCod=%ld", // Extra check
- Match->Status.QstInd,Match->Status.QstCod,
- Gam_ShowingStringsDB[Match->Status.Showing],
- Match->MchCod,Gbl.Hierarchy.Crs.CrsCod);
-
- if (Match->Status.BeingPlayed)
- /* Update match as being played */
- Gam_UpdateMatchAsBeingPlayed (Match->MchCod);
- else
- /* Update match as not being played */
- Gam_SetMatchAsNotBeingPlayed (Match->MchCod);
- }
-
-/*****************************************************************************/
-/********** Update elapsed time in current question (by a teacher) ***********/
-/*****************************************************************************/
-
-static void Gam_UpdateElapsedTimeInQuestion (struct Match *Match)
- {
- /***** Update elapsed time in current question in database *****/
- if (Match->Status.BeingPlayed &&
- Match->Status.QstInd > 0 &&
- Match->Status.QstInd < Gam_AFTER_LAST_QUESTION)
- DB_QueryINSERT ("can not update elapsed time in question",
- "INSERT INTO gam_time (MchCod,QstInd,ElapsedTime)"
- " VALUES (%ld,%u,SEC_TO_TIME(%u))"
- " ON DUPLICATE KEY"
- " UPDATE ElapsedTime=ADDTIME(ElapsedTime,SEC_TO_TIME(%u))",
- Match->MchCod,Match->Status.QstInd,
- Cfg_SECONDS_TO_REFRESH_GAME,
- Cfg_SECONDS_TO_REFRESH_GAME);
- }
-
-/*****************************************************************************/
-/******************* Get elapsed time in a match question ********************/
-/*****************************************************************************/
-
-static void Gam_GetElapsedTimeInQuestion (struct Match *Match,
- struct Time *Time)
- {
- MYSQL_RES *mysql_res;
- unsigned NumRows;
-
- /***** Query database *****/
- NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get elapsed time",
- "SELECT ElapsedTime"
- " FROM gam_time"
- " WHERE MchCod=%ld AND QstInd=%u",
- Match->MchCod,Match->Status.QstInd);
-
- /***** Get elapsed time from query result *****/
- Gam_GetElapsedTime (NumRows,mysql_res,Time);
-
- /***** Free structure that stores the query result *****/
- DB_FreeMySQLResult (&mysql_res);
- }
-
-/*****************************************************************************/
-/*********************** Get elapsed time in a match *************************/
-/*****************************************************************************/
-
-static void Gam_GetElapsedTimeInMatch (struct Match *Match,
- 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)))"
- " FROM gam_time WHERE MchCod=%ld",
- Match->MchCod);
-
- /***** Get elapsed time from query result *****/
- Gam_GetElapsedTime (NumRows,mysql_res,Time);
-
- /***** Free structure that stores the query result *****/
- DB_FreeMySQLResult (&mysql_res);
- }
-
-/*****************************************************************************/
-/*********************** Get elapsed time in a match *************************/
-/*****************************************************************************/
-
-static void Gam_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)
- ElapsedTimeGotFromDB = true;
- }
-
- /***** Initialize time to default value (0) *****/
- if (!ElapsedTimeGotFromDB)
- Time->Hour =
- Time->Minute =
- Time->Second = 0;
- }
-
-/*****************************************************************************/
-/********************* Pause current match (by a teacher) ********************/
-/*****************************************************************************/
-
-void Gam_PauseMatchTch (void)
- {
- struct Match Match;
-
- /***** Remove old players.
- This function must be called before getting match status. *****/
- Gam_RemoveOldPlayers ();
-
- /***** Get data of the match from database *****/
- Match.MchCod = Gbl.Games.MchCodBeingPlayed;
- Gam_GetDataOfMatchByCod (&Match);
-
- /***** Update status *****/
- Match.Status.BeingPlayed = false; // Resume match
-
- /***** Update match status in database *****/
- Gam_UpdateMatchStatusInDB (&Match);
-
- /***** Show current match status *****/
- fprintf (Gbl.F.Out,"
");
- }
-
-/*****************************************************************************/
-/** Show current match status (current question, answers...) (by a teacher) **/
-/*****************************************************************************/
-
-void Gam_ResumeMatchTch (void)
- {
- struct Match Match;
-
- /***** Remove old players.
- This function must be called before getting match status. *****/
- Gam_RemoveOldPlayers ();
-
- /***** Get data of the match from database *****/
- Match.MchCod = Gbl.Games.MchCodBeingPlayed;
- Gam_GetDataOfMatchByCod (&Match);
-
- /***** If not yet finished, update status *****/
- if (Match.Status.QstInd < Gam_AFTER_LAST_QUESTION) // Unfinished
- {
- if (Match.Status.QstInd == 0) // Match has been created, but it has not started
- Gam_SetMatchStatusToNext (&Match);
- Match.Status.BeingPlayed = true; // Start/resume match
- }
-
- /***** Update match status in database *****/
- Gam_UpdateMatchStatusInDB (&Match);
-
- /***** Show current match status *****/
- fprintf (Gbl.F.Out,"
");
- }
-
-/*****************************************************************************/
-/** Show stem, hiding answers, of current question in a match (by a teacher) */
-/*****************************************************************************/
-/*
-void Gam_ShowStemQstMatchTch (void)
- {
- struct Match Match;
-
- ***** Remove old players.
- This function must be called before getting match status. *****
- Gam_RemoveOldPlayers ();
-
- ***** Get data of the match from database *****
- Match.MchCod = Gbl.Games.MchCodBeingPlayed;
- Gam_GetDataOfMatchByCod (&Match);
-
- ***** Update status *****
- Match.Status.Showing = Gam_WORDING; // Show only the stem
-
- ***** Update match status in database *****
- Gam_UpdateMatchStatusInDB (&Match);
-
- ***** Show current match status *****
- fprintf (Gbl.F.Out,"
");
- }
-*/
-/*****************************************************************************/
-/**** Show stem and answers of current question in a match (by a teacher) ****/
-/*****************************************************************************/
-/*
-void Gam_ShowAnssQstMatchTch (void)
- {
- struct Match Match;
-
- ***** Remove old players.
- This function must be called before getting match status. *****
- Gam_RemoveOldPlayers ();
-
- ***** Get data of the match from database *****
- Match.MchCod = Gbl.Games.MchCodBeingPlayed;
- Gam_GetDataOfMatchByCod (&Match);
-
- ***** Update status *****
- Match.Status.Showing = Gam_ANSWERS; // Show answers
-
- ***** Update match status in database *****
- Gam_UpdateMatchStatusInDB (&Match);
-
- ***** Show current match status *****
- fprintf (Gbl.F.Out,"
");
- }
-*/
-/*****************************************************************************/
-/******** Show results of current question in a match (by a teacher) *********/
-/*****************************************************************************/
-
-void Gam_ShowResultsQstMatchTch (void)
- {
- struct Match Match;
-
- /***** Remove old players.
- This function must be called before getting match status. *****/
- Gam_RemoveOldPlayers ();
-
- /***** Get data of the match from database *****/
- Match.MchCod = Gbl.Games.MchCodBeingPlayed;
- Gam_GetDataOfMatchByCod (&Match);
-
- /***** Update status *****/
- Match.Status.Showing = Gam_RESULTS; // Show results
-
- /***** Update match status in database *****/
- Gam_UpdateMatchStatusInDB (&Match);
-
- /***** Show current match status *****/
- fprintf (Gbl.F.Out,"
");
- }
-
-/*****************************************************************************/
-/************* Show previous question in a match (by a teacher) **************/
-/*****************************************************************************/
-
-void Gam_BackMatchTch (void)
- {
- struct Match Match;
-
- /***** Remove old players.
- This function must be called before getting match status. *****/
- Gam_RemoveOldPlayers ();
-
- /***** Get data of the match from database *****/
- Match.MchCod = Gbl.Games.MchCodBeingPlayed;
- Gam_GetDataOfMatchByCod (&Match);
-
- /***** Update status *****/
- Gam_SetMatchStatusToPrev (&Match);
-
- /***** Update match status in database *****/
- Gam_UpdateMatchStatusInDB (&Match);
-
- /***** Show current match status *****/
- fprintf (Gbl.F.Out,"
");
- }
-
-/*****************************************************************************/
-/*************** Show next question in a match (by a teacher) ****************/
-/*****************************************************************************/
-
-void Gam_ForwardMatchTch (void)
- {
- struct Match Match;
-
- /***** Remove old players.
- This function must be called before getting match status. *****/
- Gam_RemoveOldPlayers ();
-
- /***** Get data of the match from database *****/
- Match.MchCod = Gbl.Games.MchCodBeingPlayed;
- Gam_GetDataOfMatchByCod (&Match);
-
- /***** Update status *****/
- Gam_SetMatchStatusToNext (&Match);
-
- /***** Update match status in database *****/
- Gam_UpdateMatchStatusInDB (&Match);
-
- /***** Show current match status *****/
- fprintf (Gbl.F.Out,"
");
- }
-
-/*****************************************************************************/
-/************** Show current question in a match (by a teacher) **************/
-/*****************************************************************************/
-/*
-void Gam_CurrQstMatchTch (void)
- {
- struct Match Match;
-
- ***** Remove old players.
- This function must be called before getting match status. *****
- Gam_RemoveOldPlayers ();
-
- ***** Get data of the match from database *****
- Match.MchCod = Gbl.Games.MchCodBeingPlayed;
- Gam_GetDataOfMatchByCod (&Match);
-
- ***** Toggle display of results *****
- Match.Status.ShowResults = !Match.Status.ShowResults;
-
- ***** Update match status in database *****
- Gam_UpdateMatchStatusInDB (&Match);
-
- ***** Show current match status *****
- fprintf (Gbl.F.Out,"
");
- }
-*/
-/*****************************************************************************/
-/************** Set match status to previous (backward) status ***************/
-/*****************************************************************************/
-
-static void Gam_SetMatchStatusToPrev (struct Match *Match)
- {
- /***** What to show *****/
- switch (Match->Status.Showing)
- {
- case Gam_WORDING:
- Match->Status.Showing = Gam_REQUEST;
-
- /***** Get index of the previous question *****/
- Match->Status.QstInd = Gam_GetPrevQuestionIndexInGame (Match->GamCod,
- Match->Status.QstInd);
- if (Match->Status.QstInd == 0) // Start of questions has been reached
- {
- Match->Status.QstCod = -1L; // No previous questions
- Match->Status.BeingPlayed = false; // Match is not being played
- }
- else
- Match->Status.QstCod = Gam_GetQstCodFromQstInd (Match->GamCod,
- Match->Status.QstInd);
- break;
- case Gam_ANSWERS:
- Match->Status.Showing = Gam_WORDING;
- break;
- case Gam_REQUEST:
- Match->Status.Showing = Gam_ANSWERS;
- break;
- case Gam_RESULTS:
- Match->Status.Showing = Gam_REQUEST;
- break;
- }
- }
-
-/*****************************************************************************/
-/**************** Set match status to next (forward) status ******************/
-/*****************************************************************************/
-
-static void Gam_SetMatchStatusToNext (struct Match *Match)
- {
- /***** What to show *****/
- switch (Match->Status.Showing)
- {
- case Gam_WORDING:
- Match->Status.Showing = Gam_ANSWERS;
- break;
- case Gam_ANSWERS:
- Match->Status.Showing = Gam_REQUEST;
- break;
- case Gam_REQUEST:
- case Gam_RESULTS:
- Match->Status.Showing = Gam_WORDING;
-
- /***** Get index of the next question *****/
- Match->Status.QstInd = Gam_GetNextQuestionIndexInGame (Match->GamCod,
- Match->Status.QstInd);
- if (Match->Status.QstInd < Gam_AFTER_LAST_QUESTION) // Unfinished
- Match->Status.QstCod = Gam_GetQstCodFromQstInd (Match->GamCod,
- Match->Status.QstInd);
- else // Finished
- {
- Match->Status.QstCod = -1L; // No more questions
- Match->Status.BeingPlayed = false;
- }
- break;
- }
- }
-
-/*****************************************************************************/
-/******* Show current match status (number, question, answers, button) *******/
-/*****************************************************************************/
-
-static void Gam_ShowMatchStatusForTch (struct Match *Match)
- {
- /***** Get current number of players *****/
- Gam_GetNumPlayers (Match);
-
- /***** Left column *****/
- Gam_ShowLeftColumnTch (Match);
-
- /***** Right column *****/
- /* Start right container */
- fprintf (Gbl.F.Out,"
");
-
- /* Top row: match title */
- Gam_ShowMatchTitle (Match);
-
- /* Bottom row: current question and possible answers */
- Gam_ShowQuestionAndAnswersTch (Match);
-
- /* End right container */
- fprintf (Gbl.F.Out,"
");
- }
-
-/*****************************************************************************/
-/************ Show current question being played for a student ***************/
-/*****************************************************************************/
-
-static void Gam_ShowMatchStatusForStd (struct Match *Match)
- {
- extern const char *Txt_Please_wait_;
- bool IBelongToGroups;
-
- /***** Do I belong to valid groups to play this match? *****/
- IBelongToGroups = Gbl.Usrs.Me.IBelongToCurrentCrs &&
- Gam_CheckIfIPlayThisMatchBasedOnGrps (Match->MchCod);
- if (!IBelongToGroups)
- Lay_ShowErrorAndExit ("You can not play this match!");
-
- /***** Get current number of players *****/
- Gam_GetNumPlayers (Match);
-
- /***** Left column *****/
- Gam_ShowLeftColumnStd (Match);
-
- /***** Right column *****/
- /* Start right container */
- fprintf (Gbl.F.Out,"
");
-
- /***** Update players ******/
- Gam_RegisterMeAsPlayerInMatch (Match->MchCod);
-
- if (Match->Status.BeingPlayed)
- /* Show current question and possible answers */
- Gam_ShowQuestionAndAnswersStd (Match);
- else // Not being played
- Gam_ShowWaitImage (Txt_Please_wait_);
-
- fprintf (Gbl.F.Out,"
");
- }
-
- /* End right container */
- fprintf (Gbl.F.Out,"
");
- }
-
-/*****************************************************************************/
-/******** Show left botton column when playing a match (as a teacher) ********/
-/*****************************************************************************/
-
-static void Gam_ShowLeftColumnTch (struct Match *Match)
- {
- extern const char *Txt_MATCH_respond;
- struct Time Time;
- unsigned NumAnswerers;
-
- /***** Start left container *****/
- fprintf (Gbl.F.Out,"
");
-
- /***** Top *****/
- fprintf (Gbl.F.Out,"
");
-
- /* Write elapsed time in match */
- Gam_GetElapsedTimeInMatch (Match,&Time);
- Dat_WriteHoursMinutesSeconds (&Time);
-
- fprintf (Gbl.F.Out,"
");
-
- /***** Write number of question *****/
- Gam_ShowNumQstInGame (Match);
-
- /***** Write elapsed time in question *****/
- fprintf (Gbl.F.Out,"
");
-
- /***** Buttons *****/
- Gam_PutMatchControlButtons (Match);
-
- /***** Checkbox to show results *****/
- // Gam_PutCheckboxResult (Match);
-
- /***** Number of players *****/
- Gam_ShowNumPlayers (Match);
-
- /***** Number of users who have answered *****/
- if (Match->Status.BeingPlayed)
- {
- NumAnswerers = Gam_GetNumAnswerers (Match);
- fprintf (Gbl.F.Out,"
"
- "%s "
- "%u/%u"
- "
",
- Txt_MATCH_respond,
- NumAnswerers,Match->Status.NumPlayers);
- }
-
- /***** End left container *****/
- fprintf (Gbl.F.Out,"
");
- }
-
-/*****************************************************************************/
-/******** Show left botton column when playing a match (as a student) ********/
-/*****************************************************************************/
-
-static void Gam_ShowLeftColumnStd (struct Match *Match)
- {
- /***** Start left container *****/
- fprintf (Gbl.F.Out,"
");
-
- /***** Top *****/
- fprintf (Gbl.F.Out,"");
-
- /***** Write number of question *****/
- Gam_ShowNumQstInGame (Match);
-
- /***** End left container *****/
- fprintf (Gbl.F.Out,"
");
- }
-
-/*****************************************************************************/
-/********************** Put buttons to control a match ***********************/
-/*****************************************************************************/
-
-static void Gam_PutMatchControlButtons (struct Match *Match)
- {
- 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 *****/
- fprintf (Gbl.F.Out,"
");
-
- /***** Left button *****/
- fprintf (Gbl.F.Out,"
");
- if (Match->Status.QstInd == 0)
- /* Put button to close browser tab */
- Gam_PutBigButtonClose ();
- else
- /* Put button to go back */
- Gam_PutBigButton (ActBckMchTch,Match->MchCod,
- Gam_ICON_PREVIOUS,Txt_Go_back);
- fprintf (Gbl.F.Out,"
");
-
- /***** Center button *****/
- fprintf (Gbl.F.Out,"
");
- if (Match->Status.BeingPlayed) // Being played
- /* Put button to pause match */
- Gam_PutBigButton (ActPauMchTch,
- Match->MchCod,
- Gam_ICON_PAUSE,Txt_Pause);
- else // Paused
- {
- if (Match->Status.QstInd < Gam_AFTER_LAST_QUESTION) // Not finished
- /* Put button to play match */
- Gam_PutBigButton (ActPlyMchTch,
- Match->MchCod,
- Gam_ICON_PLAY,Match->Status.QstInd == 0 ? Txt_Start :
- Txt_Resume);
- else // Finished
- /* Put disabled button to play match */
- Gam_PutBigButtonOff (Gam_ICON_PLAY);
- }
- fprintf (Gbl.F.Out,"
");
-
- /***** Right button *****/
- fprintf (Gbl.F.Out,"
");
- if (Match->Status.QstInd >= Gam_AFTER_LAST_QUESTION) // Finished
- /* Put button to close browser tab */
- Gam_PutBigButtonClose ();
- else
- /* Put button to show answers */
- Gam_PutBigButton (ActFwdMchTch,Match->MchCod,
- Gam_ICON_NEXT,Txt_Go_forward);
- fprintf (Gbl.F.Out,"
");
- }
-
-/*****************************************************************************/
-/********************* Show number of question in game ***********************/
-/*****************************************************************************/
-
-static void Gam_ShowNumQstInGame (struct Match *Match)
- {
- extern const char *Txt_MATCH_Start;
- extern const char *Txt_MATCH_End;
- unsigned NumQsts = Gam_GetNumQstsGame (Match->GamCod);
-
- fprintf (Gbl.F.Out,"
");
- if (Match->Status.QstInd == 0) // Not started
- fprintf (Gbl.F.Out,"%s",Txt_MATCH_Start);
- else if (Match->Status.QstInd >= Gam_AFTER_LAST_QUESTION) // Finished
- fprintf (Gbl.F.Out,"%s",Txt_MATCH_End);
- else
- fprintf (Gbl.F.Out,"%u/%u",Match->Status.QstInd,NumQsts);
- fprintf (Gbl.F.Out,"
");
- }
-
-/*****************************************************************************/
-/************************** Show number of players ***************************/
-/*****************************************************************************/
-
-static void Gam_ShowNumPlayers (struct Match *Match)
- {
- extern const char *Txt_Players;
-
- fprintf (Gbl.F.Out,"
"
- "%s "
- "%u"
- "
",
- Txt_Players,Match->Status.NumPlayers);
- }
-
-/*****************************************************************************/
-/***************************** Show match title ******************************/
-/*****************************************************************************/
-
-static void Gam_ShowMatchTitle (struct Match *Match)
- {
- /***** Match title *****/
- fprintf (Gbl.F.Out,"
%s
",Match->Title);
- }
-
-/*****************************************************************************/
-/***** Show question and its answers when playing a match (as a teacher) *****/
-/*****************************************************************************/
-
-static void Gam_ShowQuestionAndAnswersTch (struct Match *Match)
- {
- extern const char *Txt_MATCH_Paused;
- extern const char *Txt_View_results;
- MYSQL_RES *mysql_res;
- MYSQL_ROW row;
-
- /***** Trivial check: question index should be correct *****/
- if (Match->Status.QstInd == 0 ||
- Match->Status.QstInd >= Gam_AFTER_LAST_QUESTION)
- return;
-
- /***** Get data of question from database *****/
- if (!DB_QuerySELECT (&mysql_res,"can not get data of a question",
- "SELECT AnsType," // row[0]
- "Stem," // row[1]
- "MedCod" // row[2]
- " FROM tst_questions"
- " WHERE QstCod=%ld",
- Match->Status.QstCod))
- Ale_ShowAlert (Ale_ERROR,"Question doesn't exist.");
- row = mysql_fetch_row (mysql_res);
-
- /***** Show question *****/
- /* Get answer type (row[0]) */
- Gbl.Test.AnswerType = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[0]);
- // TODO: Check that answer type is correct (unique choice)
-
- fprintf (Gbl.F.Out,"
");
-
- /* Write stem (row[1]) */
- Tst_WriteQstStem (row[1],"MATCH_TCH_QST");
-
- /* Get media (row[2]) */
- Gbl.Test.Media.MedCod = Str_ConvertStrCodToLongCod (row[2]);
- Med_GetMediaDataByCod (&Gbl.Test.Media);
-
- /* Show media */
- Med_ShowMedia (&Gbl.Test.Media,
- "TEST_MED_EDIT_LIST_STEM_CONTAINER",
- "TEST_MED_EDIT_LIST_STEM");
-
- /* Write answers? */
- switch (Match->Status.Showing)
- {
- case Gam_WORDING:
- /* Don't write anything */
- break;
- case Gam_ANSWERS:
- if (Match->Status.BeingPlayed)
- /* Write answers */
- Tst_WriteAnswersMatchResult (Match->MchCod,
- Match->Status.QstInd,
- Match->Status.QstCod,
- "MATCH_TCH_QST",false); // Don't show result
- else // Not being played
- Gam_ShowWaitImage (Txt_MATCH_Paused);
- break;
- case Gam_REQUEST:
- /* Write button to request viewing results */
- Gam_PutBigButton (ActShoResMchTch,Match->MchCod,
- Gam_ICON_RESULTS,Txt_View_results);
- break;
- case Gam_RESULTS:
- /* Write answers with results */
- Tst_WriteAnswersMatchResult (Match->MchCod,
- Match->Status.QstInd,
- Match->Status.QstCod,
- "MATCH_TCH_QST",true); // Show result
- break;
- }
-
- fprintf (Gbl.F.Out,"
");
- }
-
-/*****************************************************************************/
-/************************ Put button to show results *************************/
-/*****************************************************************************/
-/*
-static void Gam_PutCheckboxResult (struct Match *Match)
- {
- extern const char *Txt_View_results;
-
- ***** Start container *****
- fprintf (Gbl.F.Out,"
");
-
- ***** Start form *****
- Frm_StartForm (ActChgShoResMchTch);
- Gam_PutParamMatchCod (Match->MchCod); // Current match being played
-
- ***** Put icon with link *****
- * Submitting onmousedown instead of default onclick
- is necessary in order to be fast
- and not lose clicks due to refresh *
- fprintf (Gbl.F.Out,"
");
- }
-*/
-
-/*****************************************************************************/
-/***** Show question and its answers when playing a match (as a student) *****/
-/*****************************************************************************/
-
-static void Gam_ShowQuestionAndAnswersStd (struct Match *Match)
- {
- bool Shuffle = false; // TODO: Read shuffle from question
- MYSQL_RES *mysql_res;
- MYSQL_ROW row;
- int StdAnsInd;
- unsigned NumOptions;
- unsigned NumOpt;
- unsigned Index;
- bool ErrorInIndex = false;
-
- /***** Show question *****/
- /* Write buttons for answers? */
- if (Match->Status.Showing == Gam_ANSWERS)
- {
- if (Tst_CheckIfQuestionIsValidForGame (Match->Status.QstCod))
- {
- /***** Get student's answer to this question
- (<0 ==> no answer) *****/
- StdAnsInd = Gam_GetQstAnsFromDB (Match->MchCod,
- Match->Status.QstInd);
-
- /***** Get number of options in this question *****/
- NumOptions = Tst_GetNumAnswersQst (Match->Status.QstCod);
-
- /***** Get answers of question from database *****/
- Shuffle = false;
- NumOptions = Tst_GetAnswersQst (Match->Status.QstCod,&mysql_res,Shuffle);
- /*
- row[0] AnsInd
- row[1] Answer
- row[2] Feedback
- row[3] MedCod
- row[4] Correct
- */
-
- /***** Start table *****/
- Tbl_StartTableWide (8);
-
- for (NumOpt = 0;
- NumOpt < NumOptions;
- NumOpt++)
- {
- /***** Get next answer *****/
- row = mysql_fetch_row (mysql_res);
-
- /***** Assign index (row[0]).
- Index is 0,1,2,3... if no shuffle
- or 1,3,0,2... (example) if shuffle *****/
- if (sscanf (row[0],"%u",&Index) == 1)
- {
- if (Index >= Tst_MAX_OPTIONS_PER_QUESTION)
- ErrorInIndex = true;
- }
- else
- ErrorInIndex = true;
- if (ErrorInIndex)
- Lay_ShowErrorAndExit ("Wrong index of answer when showing a test.");
-
- /***** Start row *****/
- // if (NumOpt % 2 == 0)
- fprintf (Gbl.F.Out,"
");
-
- /***** Write letter for this option *****/
- /* Start table cell */
- fprintf (Gbl.F.Out,"
");
-
- /* Form with button.
- Sumitting onmousedown instead of default onclick
- is necessary in order to be fast
- and not lose clicks due to refresh */
- Frm_StartForm (ActAnsMchQstStd);
- Gam_PutParamMatchCod (Match->MchCod); // Current match being played
- Gam_PutParamQstInd (Match->Status.QstInd); // Current question index shown
- Gam_PutParamAnswer (Index); // Index for this option
- fprintf (Gbl.F.Out,"",
- 'A' + (char) NumOpt,
- 'a' + (char) NumOpt);
- Frm_EndForm ();
-
- /* End table cell */
- fprintf (Gbl.F.Out,"
");
-
- /***** End row *****/
- // if (NumOpt % 2 == 1)
- fprintf (Gbl.F.Out,"
");
- }
-
- /***** End table *****/
- Tbl_EndTable ();
- }
- else
- Ale_ShowAlert (Ale_ERROR,"Type of answer not valid in a game.");
- }
- }
-
-/*****************************************************************************/
-/*********************** Put a big button to do action ***********************/
-/*****************************************************************************/
-
-static void Gam_PutBigButton (Act_Action_t NextAction,long MchCod,
- const char *Icon,const char *Txt)
- {
- /***** Start form *****/
- Frm_StartForm (NextAction);
- Gam_PutParamMatchCod (MchCod);
-
- /***** Put icon with link *****/
- /* Submitting onmousedown instead of default onclick
- is necessary in order to be fast
- and not lose clicks due to refresh */
- fprintf (Gbl.F.Out,"
",
- Icon);
- }
-
-static void Gam_PutBigButtonClose (void)
- {
- extern const char *Txt_Close;
-
- /***** Put icon with link *****/
- /* onmousedown instead of default onclick
- is necessary in order to be fast
- and not lose clicks due to refresh */
- fprintf (Gbl.F.Out,"
",
- Cfg_URL_ICON_PUBLIC,
- Txt,
- Txt);
- }
-
-/*****************************************************************************/
-/**************************** Remove old players *****************************/
-/*****************************************************************************/
-
-static void Gam_RemoveOldPlayers (void)
- {
- /***** Delete matches not being played *****/
- DB_QueryDELETE ("can not update matches as not being played",
- "DELETE FROM gam_mch_being_played"
- " WHERE TSStatus.NumPlayers = (unsigned) DB_QueryCOUNT ("can not get number of players",
- "SELECT COUNT(*) FROM gam_players"
- " WHERE MchCod=%ld",
- Match->MchCod);
- }
-
-/*****************************************************************************/
-/******************* Show the results of a finished match ********************/
-/*****************************************************************************/
-
-void Gam_ShowFinishedMatchResults (void)
- {
- Ale_ShowAlert (Ale_INFO,"To be implemented...");
- }
-
-/*****************************************************************************/
-/********************** Get code of match being played ***********************/
-/*****************************************************************************/
-
-void Gam_GetMatchBeingPlayed (void)
- {
- /***** Get match code ****/
- if ((Gbl.Games.MchCodBeingPlayed = Gam_GetParamMatchCod ()) == -1L)
- Lay_ShowErrorAndExit ("Code of match is missing.");
- }
-
-/*****************************************************************************/
-/********* Show game being played to me as student in a new window ***********/
-/*****************************************************************************/
-
-void Gam_ShowMatchToMeAsStd (void)
- {
- struct Match Match;
-
- /***** Remove old players.
- This function must be called before getting match status. *****/
- Gam_RemoveOldPlayers ();
-
- /***** Get data of the match from database *****/
- Match.MchCod = Gbl.Games.MchCodBeingPlayed;
- Gam_GetDataOfMatchByCod (&Match);
-
- /***** Show current match status *****/
- fprintf (Gbl.F.Out,"
");
- }
-
-/*****************************************************************************/
-/****************** Refresh match for a teacher via AJAX *********************/
-/*****************************************************************************/
-
-void Gam_RefreshMatchTch (void)
- {
- struct Match Match;
-
- if (!Gbl.Session.IsOpen) // If session has been closed, do not write anything
- return;
-
- /***** Remove old players.
- This function must be called before getting match status. *****/
- Gam_RemoveOldPlayers ();
-
- /***** Get data of the match from database *****/
- Match.MchCod = Gbl.Games.MchCodBeingPlayed;
- Gam_GetDataOfMatchByCod (&Match);
-
- /***** Update match status in database *****/
- Gam_UpdateMatchStatusInDB (&Match);
-
- /***** Update elapsed time in this question *****/
- Gam_UpdateElapsedTimeInQuestion (&Match);
-
- /***** Show current match status *****/
- Gam_ShowMatchStatusForTch (&Match);
- }
-
-/*****************************************************************************/
-/*************** Refresh current game for a student via AJAX *****************/
-/*****************************************************************************/
-
-void Gam_RefreshMatchStd (void)
- {
- struct Match Match;
-
- if (!Gbl.Session.IsOpen) // If session has been closed, do not write anything
- return;
-
- /***** Remove old players.
- This function must be called before getting match status. *****/
- Gam_RemoveOldPlayers ();
-
- /***** Get data of the match from database *****/
- Match.MchCod = Gbl.Games.MchCodBeingPlayed;
- Gam_GetDataOfMatchByCod (&Match);
-
- /***** Show current match status *****/
- Gam_ShowMatchStatusForStd (&Match);
- }
-
-/*****************************************************************************/
-/**** Receive previous question answer in a match question from database *****/
-/*****************************************************************************/
-
-static int Gam_GetQstAnsFromDB (long MchCod,unsigned QstInd)
- {
- MYSQL_RES *mysql_res;
- MYSQL_ROW row;
- unsigned NumRows;
- int StdAnsInd = -1; // <0 ==> no answer selected
-
- /***** Get student's answer *****/
- NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get student's answer to a match question",
- "SELECT AnsInd FROM gam_answers"
- " WHERE MchCod=%ld AND UsrCod=%ld AND QstInd=%u",
- MchCod,
- Gbl.Usrs.Me.UsrDat.UsrCod,
- QstInd);
- if (NumRows) // Answer found...
- {
- /***** Get answer index *****/
- row = mysql_fetch_row (mysql_res);
- if (sscanf (row[0],"%d",&StdAnsInd) != 1)
- Lay_ShowErrorAndExit ("Error when getting student's answer to a match question.");
- }
-
- /***** Free structure that stores the query result *****/
- DB_FreeMySQLResult (&mysql_res);
-
- return StdAnsInd;
- }
-
-/*****************************************************************************/
-/********* Receive question answer from student when playing a match *********/
-/*****************************************************************************/
-
-void Gam_ReceiveQstAnsFromStd (void)
- {
- struct Match Match;
- unsigned QstInd;
- unsigned StdAnsInd;
- int PreviousStdAnsInd;
-
- /***** Remove old players.
- This function must be called before getting match status. *****/
- Gam_RemoveOldPlayers ();
-
- /***** Get data of the match from database *****/
- Match.MchCod = Gbl.Games.MchCodBeingPlayed;
- Gam_GetDataOfMatchByCod (&Match);
-
- /***** Get question index from form *****/
- QstInd = Gam_GetParamQstInd ();
-
- /***** Check that question index is the current one being played *****/
- if (QstInd == Match.Status.QstInd) // Receiving an answer
- // to the current question being played
- {
- /***** Get answer index *****/
- /*-------+--------------+
- | Button | Answer index |
- +--------+--------------+
- | a | 0 |
- | b | 1 |
- | c | 2 |
- | d | 3 |
- | ... | ... |
- +--------+-------------*/
- StdAnsInd = Gam_GetParamAnswer ();
-
- /***** Get previous student's answer to this question
- (<0 ==> no answer) *****/
- PreviousStdAnsInd = Gam_GetQstAnsFromDB (Match.MchCod,QstInd);
-
- /***** Store student's answer *****/
- if (PreviousStdAnsInd == (int) StdAnsInd)
- DB_QueryDELETE ("can not register your answer to the match question",
- "DELETE FROM gam_answers"
- " WHERE MchCod=%ld AND UsrCod=%ld AND QstInd=%u",
- Match.MchCod,Gbl.Usrs.Me.UsrDat.UsrCod,QstInd);
- else
- DB_QueryREPLACE ("can not register your answer to the match question",
- "REPLACE gam_answers"
- " (MchCod,UsrCod,QstInd,AnsInd)"
- " VALUES"
- " (%ld,%ld,%u,%u)",
- Match.MchCod,Gbl.Usrs.Me.UsrDat.UsrCod,QstInd,StdAnsInd);
- }
-
- /***** Show current match status *****/
- fprintf (Gbl.F.Out,"
",
+ UniqueId,Match.TimeUTC[Gam_END_TIME],
+ (unsigned) Gbl.Prefs.DateFormat,Txt_Today);
+
+ /***** Title and groups *****/
+ fprintf (Gbl.F.Out,"
",Gbl.RowEvenOdd);
+
+ /* Title */
+ fprintf (Gbl.F.Out,"%s",Match.Title);
+
+ /* Groups whose students can answer this match */
+ if (Gbl.Crs.Grps.NumGrps)
+ Mch_GetAndWriteNamesOfGrpsAssociatedToMatch (&Match);
+
+ fprintf (Gbl.F.Out,"
");
+
+ /***** Match status ******/
+ fprintf (Gbl.F.Out,"
",Gbl.RowEvenOdd);
+
+ if (Match.Status.QstInd < Mch_AFTER_LAST_QUESTION) // Unfinished match
+ /* Current question index / total of questions */
+ fprintf (Gbl.F.Out,"
%u/%u
",
+ Match.Status.QstInd,Game->NumQsts);
+
+ switch (Gbl.Usrs.Me.Role.Logged)
+ {
+ case Rol_STD:
+ /* Icon to play as student */
+ Mch_CurrentMchCod = Match.MchCod;
+ Lay_PutContextualLinkOnlyIcon (ActPlyMchStd,NULL,
+ Mch_PutParamCurrentMchCod,
+ Match.Status.QstInd < Mch_AFTER_LAST_QUESTION ? "play.svg" :
+ "flag-checkered.svg",
+ Txt_Play);
+ break;
+ case Rol_NET:
+ case Rol_TCH:
+ case Rol_DEG_ADM:
+ case Rol_CTR_ADM:
+ case Rol_INS_ADM:
+ case Rol_SYS_ADM:
+ /* Icon to resume */
+ Mch_CurrentMchCod = Match.MchCod;
+ Lay_PutContextualLinkOnlyIcon (ActResMchTch,NULL,
+ Mch_PutParamCurrentMchCod,
+ Match.Status.QstInd < Mch_AFTER_LAST_QUESTION ? "play.svg" :
+ "flag-checkered.svg",
+ Txt_Resume);
+ break;
+ default:
+ break;
+ }
+
+ fprintf (Gbl.F.Out,"
");
+
+ fprintf (Gbl.F.Out,"
");
+ }
+
+ /***** End table *****/
+ Tbl_EndTable ();
+ }
+
+/*****************************************************************************/
+/************* Get and write the names of the groups of a match **************/
+/*****************************************************************************/
+
+static void Mch_GetAndWriteNamesOfGrpsAssociatedToMatch (struct Match *Match)
+ {
+ 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 long NumRow;
+ unsigned long NumRows;
+
+ /***** Get groups associated to a match from database *****/
+ NumRows = DB_QuerySELECT (&mysql_res,"can not get groups of a match",
+ "SELECT crs_grp_types.GrpTypName,crs_grp.GrpName"
+ " FROM gam_grp,crs_grp,crs_grp_types"
+ " WHERE gam_grp.MchCod=%ld"
+ " AND gam_grp.GrpCod=crs_grp.GrpCod"
+ " AND crs_grp.GrpTypCod=crs_grp_types.GrpTypCod"
+ " ORDER BY crs_grp_types.GrpTypName,crs_grp.GrpName",
+ Match->MchCod);
+
+ /***** Write heading *****/
+ fprintf (Gbl.F.Out,"
%s: ",
+ NumRows == 1 ? Txt_Group :
+ Txt_Groups);
+
+ /***** Write groups *****/
+ if (NumRows) // Groups found...
+ {
+ /* Get and write the group types and names */
+ for (NumRow = 0;
+ NumRow < NumRows;
+ NumRow++)
+ {
+ /* Get next group */
+ row = mysql_fetch_row (mysql_res);
+
+ /* Write group type name and group name */
+ fprintf (Gbl.F.Out,"%s %s",row[0],row[1]);
+
+ if (NumRows >= 2)
+ {
+ if (NumRow == NumRows-2)
+ fprintf (Gbl.F.Out," %s ",Txt_and);
+ if (NumRows >= 3)
+ if (NumRow < NumRows-2)
+ fprintf (Gbl.F.Out,", ");
+ }
+ }
+ }
+ else
+ fprintf (Gbl.F.Out,"%s %s",
+ Txt_The_whole_course,Gbl.Hierarchy.Crs.ShrtName);
+
+ fprintf (Gbl.F.Out,"
");
+
+ /***** Free structure that stores the query result *****/
+ DB_FreeMySQLResult (&mysql_res);
+ }
+
+/*****************************************************************************/
+/******************** Get game data from a database row **********************/
+/*****************************************************************************/
+
+static void Mch_GetMatchDataFromRow (MYSQL_RES *mysql_res,
+ struct Match *Match)
+ {
+ MYSQL_ROW row;
+
+ /***** Get match data *****/
+ row = mysql_fetch_row (mysql_res);
+ /*
+ row[0] MchCod
+ row[1] GamCod
+ row[2] UsrCod
+ row[3] UNIX_TIMESTAMP(StartTime)
+ row[4] UNIX_TIMESTAMP(EndTime)
+ row[5] Title
+ */
+ /***** Get match data *****/
+ /* Code of the match (row[0]) */
+ if ((Match->MchCod = Str_ConvertStrCodToLongCod (row[0])) <= 0)
+ Lay_ShowErrorAndExit ("Wrong code of match.");
+
+ /* Code of the game (row[1]) */
+ if ((Match->GamCod = Str_ConvertStrCodToLongCod (row[1])) <= 0)
+ Lay_ShowErrorAndExit ("Wrong code of game.");
+
+ /* Get match teacher (row[2]) */
+ Match->UsrCod = Str_ConvertStrCodToLongCod (row[2]);
+
+ /* Get start date (row[3] holds the start UTC time) */
+ Match->TimeUTC[Gam_START_TIME] = Dat_GetUNIXTimeFromStr (row[3]);
+
+ /* Get end date (row[4] holds the end UTC time) */
+ Match->TimeUTC[Gam_END_TIME ] = Dat_GetUNIXTimeFromStr (row[4]);
+
+ /* Get the title of the game (row[5]) */
+ if (row[5])
+ Str_Copy (Match->Title,row[5],
+ Gam_MAX_BYTES_TITLE);
+ else
+ Match->Title[0] = '\0';
+
+ /***** Get current match status *****/
+ /*
+ row[6] QstInd
+ row[7] QstCod
+ row[8] UNIX_TIMESTAMP(QstStartTime)
+ row[9] Showing
+ */
+ /* Current question index (row[6]) */
+ Match->Status.QstInd = Gam_GetQstIndFromStr (row[6]);
+
+ /* Current question code (row[7]) */
+ Match->Status.QstCod = Str_ConvertStrCodToLongCod (row[7]);
+
+ /* Get question start date (row[8] holds the start UTC time) */
+ Match->Status.QstStartTimeUTC = Dat_GetUNIXTimeFromStr (row[8]);
+
+ /* Get what to show (stem, answers, results) (row(9)) */
+ Match->Status.Showing = Mch_GetShowingFromStr (row[9]);
+
+ /***** Get whether the match is being played or not *****/
+ if (Match->Status.QstInd >= Mch_AFTER_LAST_QUESTION) // Finished
+ Match->Status.BeingPlayed = false;
+ else // Unfinished
+ Match->Status.BeingPlayed = Mch_GetIfMatchIsBeingPlayed (Match->MchCod);
+ }
+
+/*****************************************************************************/
+/****************** Get parameter with what is being shown *******************/
+/*****************************************************************************/
+
+static Mch_Showing_t Mch_GetShowingFromStr (const char *Str)
+ {
+ Mch_Showing_t Showing;
+
+ for (Showing = (Mch_Showing_t) 0;
+ 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) ***************/
+/*****************************************************************************/
+
+void Mch_RequestRemoveMatchTch (void)
+ {
+ extern const char *Txt_Do_you_really_want_to_remove_the_match_X;
+ extern const char *Txt_Remove_match;
+ struct Match Match;
+
+ /***** Get parameters *****/
+ /* Get match code */
+ if ((Match.MchCod = Mch_GetParamMatchCod ()) == -1L)
+ Lay_ShowErrorAndExit ("Code of match is missing.");
+
+ /***** Get data of the match from database *****/
+ Mch_GetDataOfMatchByCod (&Match);
+
+ /***** Show question and button to remove question *****/
+ Mch_CurrentMchCod = Match.MchCod;
+ Ale_ShowAlertAndButton (ActRemMchTch,NULL,NULL,Mch_PutParamCurrentMchCod,
+ Btn_REMOVE_BUTTON,Txt_Remove_match,
+ Ale_QUESTION,Txt_Do_you_really_want_to_remove_the_match_X,
+ Match.Title);
+
+ /***** Show current game *****/
+ Gam_ShowOneGame (Match.GamCod,
+ true, // Show only this game
+ false, // Do not list game questions
+ false); // Do not put form to start new match
+ }
+
+/*****************************************************************************/
+/********************** Remove a match (game instance) ***********************/
+/*****************************************************************************/
+
+void Mch_RemoveMatchTch (void)
+ {
+ extern const char *Txt_Match_X_removed;
+ struct Match Match;
+
+ /***** Get parameters *****/
+ /* Get match code */
+ if ((Match.MchCod = Mch_GetParamMatchCod ()) == -1L)
+ Lay_ShowErrorAndExit ("Code of match is missing.");
+
+ /***** Get data of the match from database *****/
+ Mch_GetDataOfMatchByCod (&Match);
+
+ /***** Remove the match from all the tables *****/
+ /* Remove match players */
+ DB_QueryDELETE ("can not remove match players",
+ "DELETE FROM gam_players"
+ " USING gam_players,gam_matches,games"
+ " WHERE gam_players.MchCod=%ld"
+ " AND gam_players.MchCod=gam_matches.MchCod"
+ " AND gam_matches.GamCod=games.GamCod"
+ " AND games.CrsCod=%ld", // Extra check
+ Match.MchCod,Gbl.Hierarchy.Crs.CrsCod);
+
+ /* Remove match from list of matches being played */
+ DB_QueryDELETE ("can not remove match from matches being played",
+ "DELETE FROM gam_mch_being_played"
+ " USING gam_mch_being_played,gam_matches,games"
+ " WHERE gam_mch_being_played.MchCod=%ld"
+ " AND gam_mch_being_played.MchCod=gam_matches.MchCod"
+ " AND gam_matches.GamCod=games.GamCod"
+ " AND games.CrsCod=%ld", // Extra check
+ Match.MchCod,Gbl.Hierarchy.Crs.CrsCod);
+
+ /* Remove students' answers to match */
+ DB_QueryDELETE ("can not remove students' answers associated to a match",
+ "DELETE FROM gam_answers"
+ " USING gam_answers,gam_matches,games"
+ " WHERE gam_answers.MchCod=%ld"
+ " AND gam_answers.MchCod=gam_matches.MchCod"
+ " AND gam_matches.GamCod=games.GamCod"
+ " AND games.CrsCod=%ld", // Extra check
+ Match.MchCod,Gbl.Hierarchy.Crs.CrsCod);
+
+ /* Remove groups associated to the match */
+ DB_QueryDELETE ("can not remove the groups associated to a match",
+ "DELETE FROM gam_grp"
+ " USING gam_grp,gam_matches,games"
+ " WHERE gam_grp.MchCod=%ld"
+ " AND gam_grp.MchCod=gam_matches.MchCod"
+ " AND gam_matches.GamCod=games.GamCod"
+ " AND games.CrsCod=%ld", // Extra check
+ Match.MchCod,Gbl.Hierarchy.Crs.CrsCod);
+
+ /* Remove the match itself */
+ DB_QueryDELETE ("can not remove a match",
+ "DELETE FROM gam_matches"
+ " USING gam_matches,games"
+ " WHERE gam_matches.MchCod=%ld"
+ " AND gam_matches.GamCod=games.GamCod"
+ " AND games.CrsCod=%ld", // Extra check
+ Match.MchCod,Gbl.Hierarchy.Crs.CrsCod);
+ if (!mysql_affected_rows (&Gbl.mysql))
+ Lay_ShowErrorAndExit ("The match to be removed does not exist.");
+
+ /***** Write message *****/
+ Ale_ShowAlert (Ale_SUCCESS,Txt_Match_X_removed,
+ Match.Title);
+
+ /***** Show current game *****/
+ Gam_ShowOneGame (Match.GamCod,
+ true, // Show only this game
+ false, // Do not list game questions
+ false); // Do not put form to start new match
+ }
+
+/*****************************************************************************/
+/***************** Put parameter with current match code *********************/
+/*****************************************************************************/
+
+static void Mch_PutParamCurrentMchCod (void)
+ {
+ if (Mch_CurrentMchCod > 0)
+ Mch_PutParamMatchCod (Mch_CurrentMchCod);
+ }
+
+/*****************************************************************************/
+/******************** Write parameter with code of match **********************/
+/*****************************************************************************/
+
+static void Mch_PutParamMatchCod (long MchCod)
+ {
+ Par_PutHiddenParamLong ("MchCod",MchCod);
+ }
+
+/*****************************************************************************/
+/********************* Get parameter with code of match **********************/
+/*****************************************************************************/
+
+static long Mch_GetParamMatchCod (void)
+ {
+ /***** Get code of match *****/
+ return Par_GetParToLong ("MchCod");
+ }
+
+/*****************************************************************************/
+/********************* Put button to create a new match **********************/
+/*****************************************************************************/
+
+static void Mch_PutButtonNewMatch (long GamCod)
+ {
+ extern const char *Txt_New_match;
+
+ Frm_StartFormAnchor (ActReqNewMchTch,Mch_NEW_MATCH_SECTION_ID);
+ Gam_PutParamGameCod (GamCod);
+ Btn_PutConfirmButton (Txt_New_match);
+ Frm_EndForm ();
+ }
+
+/*****************************************************************************/
+/****** Put a big button to play match (start a new match) as a teacher ******/
+/*****************************************************************************/
+
+static void Mch_PutFormNewMatch (struct Game *Game)
+ {
+ extern const char *Hlp_ASSESSMENT_Games_new_match;
+ extern const char *The_ClassFormInBox[The_NUM_THEMES];
+ extern const char *Txt_New_match;
+ extern const char *Txt_Title;
+ extern const char *Txt_Play;
+
+ /***** Start section for a new match *****/
+ Lay_StartSection (Mch_NEW_MATCH_SECTION_ID);
+
+ /***** Start form *****/
+ Frm_StartForm (ActNewMchTch);
+ Gam_PutParamGameCod (Game->GamCod);
+ Gam_PutParamQstInd (0); // Start by first question in game
+
+ /***** Start box and table *****/
+ Box_StartBoxTable (NULL,Txt_New_match,NULL,
+ Hlp_ASSESSMENT_Games_new_match,Box_NOT_CLOSABLE,2);
+
+ /***** Match title *****/
+ fprintf (Gbl.F.Out,"
"
+ "
"
+ ""
+ "
"
+ "
"
+ ""
+ "
"
+ "
",
+ The_ClassFormInBox[Gbl.Prefs.Theme],
+ Txt_Title,
+ Gam_MAX_CHARS_TITLE,Game->Title);
+
+ /***** Groups *****/
+ Mch_ShowLstGrpsToCreateMatch ();
+
+ /***** End table *****/
+ Tbl_EndTable ();
+
+ /***** Put icon with link *****/
+ Frm_LinkFormSubmit (Txt_Play,NULL,NULL);
+ fprintf (Gbl.F.Out,"",
+ Cfg_URL_ICON_PUBLIC,Txt_Play,Txt_Play);
+ fprintf (Gbl.F.Out,"");
+
+ /***** End box *****/
+ Box_EndBox ();
+
+ /***** End form *****/
+ Frm_EndForm ();
+
+ /***** End section for a new match *****/
+ Lay_EndSection ();
+ }
+
+/*****************************************************************************/
+/***************** Show list of groups to create a new match *****************/
+/*****************************************************************************/
+
+static void Mch_ShowLstGrpsToCreateMatch (void)
+ {
+ 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)
+ {
+ /***** Start box and table *****/
+ fprintf (Gbl.F.Out,"
"
+ "
"
+ "%s:"
+ "
"
+ "
",
+ The_ClassFormInBox[Gbl.Prefs.Theme],
+ Txt_Groups);
+ Box_StartBoxTable ("95%",NULL,NULL,
+ NULL,Box_NOT_CLOSABLE,0);
+
+ /***** First row: checkbox to select the whole course *****/
+ fprintf (Gbl.F.Out,"
"
+ "
"
+ ""
+ "
"
+ "
",
+ Txt_The_whole_course,Gbl.Hierarchy.Crs.ShrtName);
+
+ /***** 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)
+ Grp_ListGrpsToEditAsgAttSvyMch (&Gbl.Crs.Grps.GrpTypes.LstGrpTypes[NumGrpTyp],
+ -1L, // -1 means "New match"
+ Grp_MATCH);
+
+ /***** End table and box *****/
+ Box_EndBoxTable ();
+ fprintf (Gbl.F.Out,""
+ "");
+ }
+
+ /***** Free list of groups types and groups in this course *****/
+ Grp_FreeListGrpTypesAndGrps ();
+ }
+
+/*****************************************************************************/
+/********************* Create a new match (by a teacher) *********************/
+/*****************************************************************************/
+
+void Mch_CreateNewMatchTch (void)
+ {
+ long GamCod;
+ char Title[Gam_MAX_BYTES_TITLE + 1];
+
+ /***** Get form parameters *****/
+ /* Get match code */
+ if ((GamCod = Gam_GetParamGameCod ()) == -1L)
+ Lay_ShowErrorAndExit ("Code of game is missing.");
+
+ /* Get match title */
+ Par_GetParToText ("Title",Title,Gam_MAX_BYTES_TITLE);
+
+ /* Get groups for this games */
+ Grp_GetParCodsSeveralGrps ();
+
+ /***** Create a new match *****/
+ Gbl.Games.MchCodBeingPlayed = Mch_CreateMatch (GamCod,Title);
+
+ /***** Free memory for list of selected groups *****/
+ Grp_FreeListCodSelectedGrps ();
+ }
+
+/*****************************************************************************/
+/******* Show button to actually start / resume a match (by a teacher) *******/
+/*****************************************************************************/
+
+void Mch_RequestStartResumeMatchTch (void)
+ {
+ struct Match Match;
+
+ /***** Remove old players.
+ This function must be called before getting match status. *****/
+ Mch_RemoveOldPlayers ();
+
+ /***** Get data of the match from database *****/
+ Match.MchCod = Gbl.Games.MchCodBeingPlayed;
+ Mch_GetDataOfMatchByCod (&Match);
+
+ /***** Update match status in database *****/
+ Mch_UpdateMatchStatusInDB (&Match);
+
+ /***** Show current match status *****/
+ fprintf (Gbl.F.Out,"
");
+ }
+
+/*****************************************************************************/
+/********************** Create a new match in a game *************************/
+/*****************************************************************************/
+
+static long Mch_CreateMatch (long GamCod,char Title[Gam_MAX_BYTES_TITLE + 1])
+ {
+ long MchCod;
+
+ /***** Insert this new match into database *****/
+ MchCod = DB_QueryINSERTandReturnCode ("can not create match",
+ "INSERT gam_matches"
+ " (GamCod,UsrCod,StartTime,EndTime,Title,ShowResults,"
+ "QstInd,QstCod,QstStartTime,Showing)"
+ " VALUES"
+ " (%ld," // GamCod
+ "%ld," // UsrCod
+ "NOW()," // StartTime
+ "NOW()," // EndTime
+ "'%s'," // Title
+ "'N'," // ShowResults: Don't show results initially
+ "0," // QstInd: Match has not started, so not the first question yet
+ "-1," // QstCod: Non-existent question
+ "NOW()," // QstStartTime
+ "'%s'", // What is being shown
+ GamCod,
+ Gbl.Usrs.Me.UsrDat.UsrCod, // Game creator
+ Title,
+ Mch_ShowingStringsDB[Mch_SHOWING_DEFAULT]);
+
+ /***** Create groups associated to the match *****/
+ if (Gbl.Crs.Grps.LstGrpsSel.NumGrps)
+ Mch_CreateGrps (MchCod);
+
+ return MchCod;
+ }
+
+/*****************************************************************************/
+/******************* 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",
+ "INSERT INTO gam_grp"
+ " (MchCod,GrpCod)"
+ " VALUES"
+ " (%ld,%ld)",
+ MchCod,Gbl.Crs.Grps.LstGrpsSel.GrpCods[NumGrpSel]);
+ }
+
+/*****************************************************************************/
+/***************** Insert/update a game match being played *******************/
+/*****************************************************************************/
+
+static void Mch_UpdateMatchStatusInDB (struct Match *Match)
+ {
+ /***** Update match status in database *****/
+ DB_QueryUPDATE ("can not update match being played",
+ "UPDATE gam_matches,games"
+ " SET gam_matches.EndTime=NOW(),"
+ "gam_matches.QstInd=%u,"
+ "gam_matches.QstCod=%ld,"
+ "gam_matches.QstStartTime=NOW(),"
+ "gam_matches.Showing='%s'"
+ " WHERE gam_matches.MchCod=%ld"
+ " AND gam_matches.GamCod=games.GamCod"
+ " AND games.CrsCod=%ld", // Extra check
+ Match->Status.QstInd,Match->Status.QstCod,
+ Mch_ShowingStringsDB[Match->Status.Showing],
+ Match->MchCod,Gbl.Hierarchy.Crs.CrsCod);
+
+ if (Match->Status.BeingPlayed)
+ /* Update match as being played */
+ Mch_UpdateMatchAsBeingPlayed (Match->MchCod);
+ else
+ /* Update match as not being played */
+ Mch_SetMatchAsNotBeingPlayed (Match->MchCod);
+ }
+
+/*****************************************************************************/
+/********** Update elapsed time in current question (by a teacher) ***********/
+/*****************************************************************************/
+
+static void Mch_UpdateElapsedTimeInQuestion (struct Match *Match)
+ {
+ /***** Update elapsed time in current question in database *****/
+ if (Match->Status.BeingPlayed &&
+ Match->Status.QstInd > 0 &&
+ Match->Status.QstInd < Mch_AFTER_LAST_QUESTION)
+ DB_QueryINSERT ("can not update elapsed time in question",
+ "INSERT INTO gam_time (MchCod,QstInd,ElapsedTime)"
+ " VALUES (%ld,%u,SEC_TO_TIME(%u))"
+ " ON DUPLICATE KEY"
+ " UPDATE ElapsedTime=ADDTIME(ElapsedTime,SEC_TO_TIME(%u))",
+ Match->MchCod,Match->Status.QstInd,
+ Cfg_SECONDS_TO_REFRESH_GAME,
+ Cfg_SECONDS_TO_REFRESH_GAME);
+ }
+
+/*****************************************************************************/
+/******************* Get elapsed time in a match question ********************/
+/*****************************************************************************/
+
+static void Mch_GetElapsedTimeInQuestion (struct Match *Match,
+ struct Time *Time)
+ {
+ MYSQL_RES *mysql_res;
+ unsigned NumRows;
+
+ /***** Query database *****/
+ NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get elapsed time",
+ "SELECT ElapsedTime"
+ " FROM gam_time"
+ " WHERE MchCod=%ld AND QstInd=%u",
+ Match->MchCod,Match->Status.QstInd);
+
+ /***** 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_GetElapsedTimeInMatch (struct Match *Match,
+ 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)))"
+ " FROM gam_time WHERE MchCod=%ld",
+ Match->MchCod);
+
+ /***** 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)
+ ElapsedTimeGotFromDB = true;
+ }
+
+ /***** Initialize time to default value (0) *****/
+ if (!ElapsedTimeGotFromDB)
+ Time->Hour =
+ Time->Minute =
+ Time->Second = 0;
+ }
+
+/*****************************************************************************/
+/********************* Pause current match (by a teacher) ********************/
+/*****************************************************************************/
+
+void Mch_PauseMatchTch (void)
+ {
+ struct Match Match;
+
+ /***** Remove old players.
+ This function must be called before getting match status. *****/
+ Mch_RemoveOldPlayers ();
+
+ /***** Get data of the match from database *****/
+ Match.MchCod = Gbl.Games.MchCodBeingPlayed;
+ Mch_GetDataOfMatchByCod (&Match);
+
+ /***** Update status *****/
+ Match.Status.BeingPlayed = false; // Resume match
+
+ /***** Update match status in database *****/
+ Mch_UpdateMatchStatusInDB (&Match);
+
+ /***** Show current match status *****/
+ fprintf (Gbl.F.Out,"
");
+ }
+
+/*****************************************************************************/
+/** Show current match status (current question, answers...) (by a teacher) **/
+/*****************************************************************************/
+
+void Mch_ResumeMatchTch (void)
+ {
+ struct Match Match;
+
+ /***** Remove old players.
+ This function must be called before getting match status. *****/
+ Mch_RemoveOldPlayers ();
+
+ /***** Get data of the match from database *****/
+ Match.MchCod = Gbl.Games.MchCodBeingPlayed;
+ Mch_GetDataOfMatchByCod (&Match);
+
+ /***** If not yet finished, update status *****/
+ if (Match.Status.QstInd < Mch_AFTER_LAST_QUESTION) // Unfinished
+ {
+ if (Match.Status.QstInd == 0) // Match has been created, but it has not started
+ Mch_SetMatchStatusToNext (&Match);
+ Match.Status.BeingPlayed = true; // Start/resume match
+ }
+
+ /***** Update match status in database *****/
+ Mch_UpdateMatchStatusInDB (&Match);
+
+ /***** Show current match status *****/
+ fprintf (Gbl.F.Out,"
");
+ }
+
+/*****************************************************************************/
+/******** Show results of current question in a match (by a teacher) *********/
+/*****************************************************************************/
+
+void Mch_ShowResultsQstMatchTch (void)
+ {
+ struct Match Match;
+
+ /***** Remove old players.
+ This function must be called before getting match status. *****/
+ Mch_RemoveOldPlayers ();
+
+ /***** Get data of the match from database *****/
+ Match.MchCod = Gbl.Games.MchCodBeingPlayed;
+ Mch_GetDataOfMatchByCod (&Match);
+
+ /***** Update status *****/
+ Match.Status.Showing = Mch_RESULTS; // Show results
+
+ /***** Update match status in database *****/
+ Mch_UpdateMatchStatusInDB (&Match);
+
+ /***** Show current match status *****/
+ fprintf (Gbl.F.Out,"
");
+ }
+
+/*****************************************************************************/
+/************* Show previous question in a match (by a teacher) **************/
+/*****************************************************************************/
+
+void Mch_BackMatchTch (void)
+ {
+ struct Match Match;
+
+ /***** Remove old players.
+ This function must be called before getting match status. *****/
+ Mch_RemoveOldPlayers ();
+
+ /***** Get data of the match from database *****/
+ Match.MchCod = Gbl.Games.MchCodBeingPlayed;
+ Mch_GetDataOfMatchByCod (&Match);
+
+ /***** Update status *****/
+ Mch_SetMatchStatusToPrev (&Match);
+
+ /***** Update match status in database *****/
+ Mch_UpdateMatchStatusInDB (&Match);
+
+ /***** Show current match status *****/
+ fprintf (Gbl.F.Out,"
");
+ }
+
+/*****************************************************************************/
+/*************** Show next question in a match (by a teacher) ****************/
+/*****************************************************************************/
+
+void Mch_ForwardMatchTch (void)
+ {
+ struct Match Match;
+
+ /***** Remove old players.
+ This function must be called before getting match status. *****/
+ Mch_RemoveOldPlayers ();
+
+ /***** Get data of the match from database *****/
+ Match.MchCod = Gbl.Games.MchCodBeingPlayed;
+ Mch_GetDataOfMatchByCod (&Match);
+
+ /***** Update status *****/
+ Mch_SetMatchStatusToNext (&Match);
+
+ /***** Update match status in database *****/
+ Mch_UpdateMatchStatusInDB (&Match);
+
+ /***** Show current match status *****/
+ fprintf (Gbl.F.Out,"
");
+ }
+
+/*****************************************************************************/
+/************** Set match status to previous (backward) status ***************/
+/*****************************************************************************/
+
+static void Mch_SetMatchStatusToPrev (struct Match *Match)
+ {
+ /***** What to show *****/
+ switch (Match->Status.Showing)
+ {
+ case Mch_WORDING:
+ Match->Status.Showing = Mch_REQUEST;
+
+ /***** Get index of the previous question *****/
+ Match->Status.QstInd = Gam_GetPrevQuestionIndexInGame (Match->GamCod,
+ Match->Status.QstInd);
+ if (Match->Status.QstInd == 0) // Start of questions has been reached
+ {
+ Match->Status.QstCod = -1L; // No previous questions
+ Match->Status.BeingPlayed = false; // Match is not being played
+ }
+ else
+ Match->Status.QstCod = Gam_GetQstCodFromQstInd (Match->GamCod,
+ Match->Status.QstInd);
+ break;
+ case Mch_ANSWERS:
+ Match->Status.Showing = Mch_WORDING;
+ break;
+ case Mch_REQUEST:
+ Match->Status.Showing = Mch_ANSWERS;
+ break;
+ case Mch_RESULTS:
+ Match->Status.Showing = Mch_REQUEST;
+ break;
+ }
+ }
+
+/*****************************************************************************/
+/**************** Set match status to next (forward) status ******************/
+/*****************************************************************************/
+
+static void Mch_SetMatchStatusToNext (struct Match *Match)
+ {
+ /***** What to show *****/
+ switch (Match->Status.Showing)
+ {
+ case Mch_WORDING:
+ Match->Status.Showing = Mch_ANSWERS;
+ break;
+ case Mch_ANSWERS:
+ Match->Status.Showing = Mch_REQUEST;
+ break;
+ case Mch_REQUEST:
+ case Mch_RESULTS:
+ Match->Status.Showing = Mch_WORDING;
+
+ /***** Get index of the next question *****/
+ Match->Status.QstInd = Gam_GetNextQuestionIndexInGame (Match->GamCod,
+ Match->Status.QstInd);
+ if (Match->Status.QstInd < Mch_AFTER_LAST_QUESTION) // Unfinished
+ Match->Status.QstCod = Gam_GetQstCodFromQstInd (Match->GamCod,
+ Match->Status.QstInd);
+ else // Finished
+ {
+ Match->Status.QstCod = -1L; // No more questions
+ Match->Status.BeingPlayed = false;
+ }
+ break;
+ }
+ }
+
+/*****************************************************************************/
+/******* Show current match status (number, question, answers, button) *******/
+/*****************************************************************************/
+
+static void Mch_ShowMatchStatusForTch (struct Match *Match)
+ {
+ /***** Get current number of players *****/
+ Mch_GetNumPlayers (Match);
+
+ /***** Left column *****/
+ Mch_ShowLeftColumnTch (Match);
+
+ /***** Right column *****/
+ /* Start right container */
+ fprintf (Gbl.F.Out,"
");
+
+ /* Top row: match title */
+ Mch_ShowMatchTitle (Match);
+
+ /* Bottom row: current question and possible answers */
+ Mch_ShowQuestionAndAnswersTch (Match);
+
+ /* End right container */
+ fprintf (Gbl.F.Out,"
");
+ }
+
+/*****************************************************************************/
+/************ Show current question being played for a student ***************/
+/*****************************************************************************/
+
+static void Mch_ShowMatchStatusForStd (struct Match *Match)
+ {
+ extern const char *Txt_Please_wait_;
+ bool IBelongToGroups;
+
+ /***** Do I belong to valid groups to play this match? *****/
+ IBelongToGroups = Gbl.Usrs.Me.IBelongToCurrentCrs &&
+ Mch_CheckIfIPlayThisMatchBasedOnGrps (Match->MchCod);
+ if (!IBelongToGroups)
+ Lay_ShowErrorAndExit ("You can not play this match!");
+
+ /***** Get current number of players *****/
+ Mch_GetNumPlayers (Match);
+
+ /***** Left column *****/
+ Mch_ShowLeftColumnStd (Match);
+
+ /***** Right column *****/
+ /* Start right container */
+ fprintf (Gbl.F.Out,"
");
+
+ /***** Update players ******/
+ Mch_RegisterMeAsPlayerInMatch (Match->MchCod);
+
+ if (Match->Status.BeingPlayed)
+ /* Show current question and possible answers */
+ Mch_ShowQuestionAndAnswersStd (Match);
+ else // Not being played
+ Mch_ShowWaitImage (Txt_Please_wait_);
+
+ fprintf (Gbl.F.Out,"
");
+ }
+
+ /* End right container */
+ fprintf (Gbl.F.Out,"
");
+ }
+
+/*****************************************************************************/
+/************ Check if I belong to any of the groups of a match **************/
+/*****************************************************************************/
+
+static bool Mch_CheckIfIPlayThisMatchBasedOnGrps (long MchCod)
+ {
+ /***** Get if I can play a match from database *****/
+ return (DB_QueryCOUNT ("can not check if I can play a match",
+ "SELECT COUNT(*) FROM gam_matches"
+ " WHERE MchCod=%ld"
+ " AND (MchCod NOT IN (SELECT MchCod FROM gam_grp) OR"
+ " MchCod IN (SELECT gam_grp.MchCod FROM gam_grp,crs_grp_usr"
+ " WHERE crs_grp_usr.UsrCod=%ld"
+ " AND gam_grp.GrpCod=crs_grp_usr.GrpCod))",
+ MchCod,Gbl.Usrs.Me.UsrDat.UsrCod) != 0);
+ }
+
+/*****************************************************************************/
+/******** Show left botton column when playing a match (as a teacher) ********/
+/*****************************************************************************/
+
+static void Mch_ShowLeftColumnTch (struct Match *Match)
+ {
+ extern const char *Txt_MATCH_respond;
+ struct Time Time;
+ unsigned NumAnswerers;
+
+ /***** Start left container *****/
+ fprintf (Gbl.F.Out,"
");
+
+ /***** Top *****/
+ fprintf (Gbl.F.Out,"
");
+
+ /* Write elapsed time in match */
+ Mch_GetElapsedTimeInMatch (Match,&Time);
+ Dat_WriteHoursMinutesSeconds (&Time);
+
+ fprintf (Gbl.F.Out,"
");
+
+ /***** Write number of question *****/
+ Mch_ShowNumQstInMatch (Match);
+
+ /***** Write elapsed time in question *****/
+ fprintf (Gbl.F.Out,"
");
+
+ /***** Buttons *****/
+ Mch_PutMatchControlButtons (Match);
+
+ /***** Number of players *****/
+ Mch_ShowNumPlayers (Match);
+
+ /***** Number of users who have answered *****/
+ if (Match->Status.BeingPlayed)
+ {
+ NumAnswerers = Mch_GetNumAnswerers (Match);
+ fprintf (Gbl.F.Out,"
"
+ "%s "
+ "%u/%u"
+ "
",
+ Txt_MATCH_respond,
+ NumAnswerers,Match->Status.NumPlayers);
+ }
+
+ /***** End left container *****/
+ fprintf (Gbl.F.Out,"
");
+ }
+
+/*****************************************************************************/
+/******** Show left botton column when playing a match (as a student) ********/
+/*****************************************************************************/
+
+static void Mch_ShowLeftColumnStd (struct Match *Match)
+ {
+ /***** Start left container *****/
+ fprintf (Gbl.F.Out,"
");
+
+ /***** Top *****/
+ fprintf (Gbl.F.Out,"");
+
+ /***** Write number of question *****/
+ Mch_ShowNumQstInMatch (Match);
+
+ /***** End left container *****/
+ fprintf (Gbl.F.Out,"
");
+ }
+
+/*****************************************************************************/
+/********************** Put buttons to control a match ***********************/
+/*****************************************************************************/
+
+static void Mch_PutMatchControlButtons (struct Match *Match)
+ {
+ 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 *****/
+ fprintf (Gbl.F.Out,"
");
+
+ /***** Left button *****/
+ fprintf (Gbl.F.Out,"
");
+ if (Match->Status.QstInd == 0)
+ /* Put button to close browser tab */
+ Mch_PutBigButtonClose ();
+ else
+ /* Put button to go back */
+ Mch_PutBigButton (ActBckMchTch,Match->MchCod,
+ Mch_ICON_PREVIOUS,Txt_Go_back);
+ fprintf (Gbl.F.Out,"
");
+
+ /***** Center button *****/
+ fprintf (Gbl.F.Out,"
");
+ if (Match->Status.BeingPlayed) // Being played
+ /* Put button to pause match */
+ Mch_PutBigButton (ActPauMchTch,
+ Match->MchCod,
+ Mch_ICON_PAUSE,Txt_Pause);
+ else // Paused
+ {
+ if (Match->Status.QstInd < Mch_AFTER_LAST_QUESTION) // Not finished
+ /* Put button to play match */
+ Mch_PutBigButton (ActPlyMchTch,
+ Match->MchCod,
+ Mch_ICON_PLAY,Match->Status.QstInd == 0 ? Txt_Start :
+ Txt_Resume);
+ else // Finished
+ /* Put disabled button to play match */
+ Mch_PutBigButtonOff (Mch_ICON_PLAY);
+ }
+ fprintf (Gbl.F.Out,"
");
+
+ /***** Right button *****/
+ fprintf (Gbl.F.Out,"
");
+ if (Match->Status.QstInd >= Mch_AFTER_LAST_QUESTION) // Finished
+ /* Put button to close browser tab */
+ Mch_PutBigButtonClose ();
+ else
+ /* Put button to show answers */
+ Mch_PutBigButton (ActFwdMchTch,Match->MchCod,
+ Mch_ICON_NEXT,Txt_Go_forward);
+ fprintf (Gbl.F.Out,"
");
+ }
+
+/*****************************************************************************/
+/********************* Show number of question in game ***********************/
+/*****************************************************************************/
+
+static void Mch_ShowNumQstInMatch (struct Match *Match)
+ {
+ extern const char *Txt_MATCH_Start;
+ extern const char *Txt_MATCH_End;
+ unsigned NumQsts = Gam_GetNumQstsGame (Match->GamCod);
+
+ fprintf (Gbl.F.Out,"
");
+ if (Match->Status.QstInd == 0) // Not started
+ fprintf (Gbl.F.Out,"%s",Txt_MATCH_Start);
+ else if (Match->Status.QstInd >= Mch_AFTER_LAST_QUESTION) // Finished
+ fprintf (Gbl.F.Out,"%s",Txt_MATCH_End);
+ else
+ fprintf (Gbl.F.Out,"%u/%u",Match->Status.QstInd,NumQsts);
+ fprintf (Gbl.F.Out,"
");
+ }
+
+/*****************************************************************************/
+/************************** Show number of players ***************************/
+/*****************************************************************************/
+
+static void Mch_ShowNumPlayers (struct Match *Match)
+ {
+ extern const char *Txt_Players;
+
+ fprintf (Gbl.F.Out,"
"
+ "%s "
+ "%u"
+ "
",
+ Txt_Players,Match->Status.NumPlayers);
+ }
+
+/*****************************************************************************/
+/***************************** Show match title ******************************/
+/*****************************************************************************/
+
+static void Mch_ShowMatchTitle (struct Match *Match)
+ {
+ /***** Match title *****/
+ fprintf (Gbl.F.Out,"
%s
",Match->Title);
+ }
+
+/*****************************************************************************/
+/***** Show question and its answers when playing a match (as a teacher) *****/
+/*****************************************************************************/
+
+static void Mch_ShowQuestionAndAnswersTch (struct Match *Match)
+ {
+ extern const char *Txt_MATCH_Paused;
+ extern const char *Txt_View_results;
+ MYSQL_RES *mysql_res;
+ MYSQL_ROW row;
+
+ /***** Trivial check: question index should be correct *****/
+ if (Match->Status.QstInd == 0 ||
+ Match->Status.QstInd >= Mch_AFTER_LAST_QUESTION)
+ return;
+
+ /***** Get data of question from database *****/
+ if (!DB_QuerySELECT (&mysql_res,"can not get data of a question",
+ "SELECT AnsType," // row[0]
+ "Stem," // row[1]
+ "MedCod" // row[2]
+ " FROM tst_questions"
+ " WHERE QstCod=%ld",
+ Match->Status.QstCod))
+ Ale_ShowAlert (Ale_ERROR,"Question doesn't exist.");
+ row = mysql_fetch_row (mysql_res);
+
+ /***** Show question *****/
+ /* Get answer type (row[0]) */
+ Gbl.Test.AnswerType = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[0]);
+ // TODO: Check that answer type is correct (unique choice)
+
+ fprintf (Gbl.F.Out,"
");
+
+ /* Write stem (row[1]) */
+ Tst_WriteQstStem (row[1],"MATCH_TCH_QST");
+
+ /* Get media (row[2]) */
+ Gbl.Test.Media.MedCod = Str_ConvertStrCodToLongCod (row[2]);
+ Med_GetMediaDataByCod (&Gbl.Test.Media);
+
+ /* Show media */
+ Med_ShowMedia (&Gbl.Test.Media,
+ "TEST_MED_EDIT_LIST_STEM_CONTAINER",
+ "TEST_MED_EDIT_LIST_STEM");
+
+ /* Write answers? */
+ switch (Match->Status.Showing)
+ {
+ case Mch_WORDING:
+ /* Don't write anything */
+ break;
+ case Mch_ANSWERS:
+ if (Match->Status.BeingPlayed)
+ /* Write answers */
+ Tst_WriteAnswersMatchResult (Match->MchCod,
+ Match->Status.QstInd,
+ Match->Status.QstCod,
+ "MATCH_TCH_QST",false); // Don't show result
+ else // Not being played
+ Mch_ShowWaitImage (Txt_MATCH_Paused);
+ break;
+ case Mch_REQUEST:
+ /* Write button to request viewing results */
+ Mch_PutBigButton (ActShoResMchTch,Match->MchCod,
+ Mch_ICON_RESULTS,Txt_View_results);
+ break;
+ case Mch_RESULTS:
+ /* Write answers with results */
+ Tst_WriteAnswersMatchResult (Match->MchCod,
+ Match->Status.QstInd,
+ Match->Status.QstCod,
+ "MATCH_TCH_QST",true); // Show result
+ break;
+ }
+
+ fprintf (Gbl.F.Out,"
");
+ }
+
+/*****************************************************************************/
+/***** Show question and its answers when playing a match (as a student) *****/
+/*****************************************************************************/
+
+static void Mch_ShowQuestionAndAnswersStd (struct Match *Match)
+ {
+ bool Shuffle = false; // TODO: Read shuffle from question
+ MYSQL_RES *mysql_res;
+ MYSQL_ROW row;
+ int StdAnsInd;
+ unsigned NumOptions;
+ unsigned NumOpt;
+ unsigned Index;
+ bool ErrorInIndex = false;
+
+ /***** Show question *****/
+ /* Write buttons for answers? */
+ if (Match->Status.Showing == Mch_ANSWERS)
+ {
+ if (Tst_CheckIfQuestionIsValidForGame (Match->Status.QstCod))
+ {
+ /***** Get student's answer to this question
+ (<0 ==> no answer) *****/
+ StdAnsInd = Mch_GetQstAnsFromDB (Match->MchCod,
+ Match->Status.QstInd);
+
+ /***** Get number of options in this question *****/
+ NumOptions = Tst_GetNumAnswersQst (Match->Status.QstCod);
+
+ /***** Get answers of question from database *****/
+ Shuffle = false;
+ NumOptions = Tst_GetAnswersQst (Match->Status.QstCod,&mysql_res,Shuffle);
+ /*
+ row[0] AnsInd
+ row[1] Answer
+ row[2] Feedback
+ row[3] MedCod
+ row[4] Correct
+ */
+
+ /***** Start table *****/
+ Tbl_StartTableWide (8);
+
+ for (NumOpt = 0;
+ NumOpt < NumOptions;
+ NumOpt++)
+ {
+ /***** Get next answer *****/
+ row = mysql_fetch_row (mysql_res);
+
+ /***** Assign index (row[0]).
+ Index is 0,1,2,3... if no shuffle
+ or 1,3,0,2... (example) if shuffle *****/
+ if (sscanf (row[0],"%u",&Index) == 1)
+ {
+ if (Index >= Tst_MAX_OPTIONS_PER_QUESTION)
+ ErrorInIndex = true;
+ }
+ else
+ ErrorInIndex = true;
+ if (ErrorInIndex)
+ Lay_ShowErrorAndExit ("Wrong index of answer when showing a test.");
+
+ /***** Start row *****/
+ // if (NumOpt % 2 == 0)
+ fprintf (Gbl.F.Out,"
");
+
+ /***** Write letter for this option *****/
+ /* Start table cell */
+ fprintf (Gbl.F.Out,"
");
+
+ /* Form with button.
+ Sumitting onmousedown instead of default onclick
+ is necessary in order to be fast
+ and not lose clicks due to refresh */
+ Frm_StartForm (ActAnsMchQstStd);
+ Mch_PutParamMatchCod (Match->MchCod); // Current match being played
+ Gam_PutParamQstInd (Match->Status.QstInd); // Current question index shown
+ Mch_PutParamAnswer (Index); // Index for this option
+ fprintf (Gbl.F.Out,"",
+ 'A' + (char) NumOpt,
+ 'a' + (char) NumOpt);
+ Frm_EndForm ();
+
+ /* End table cell */
+ fprintf (Gbl.F.Out,"
");
+
+ /***** End row *****/
+ // if (NumOpt % 2 == 1)
+ fprintf (Gbl.F.Out,"
");
+ }
+
+ /***** End table *****/
+ Tbl_EndTable ();
+ }
+ else
+ Ale_ShowAlert (Ale_ERROR,"Type of answer not valid in a game.");
+ }
+ }
+
+
+/*****************************************************************************/
+/******************* Write parameter with student's answer *******************/
+/*****************************************************************************/
+
+static void Mch_PutParamAnswer (unsigned AnsInd)
+ {
+ Par_PutHiddenParamUnsigned ("Ans",AnsInd);
+ }
+
+/*****************************************************************************/
+/******************* Get parameter with student's answer *********************/
+/*****************************************************************************/
+
+static unsigned Mch_GetParamAnswer (void)
+ {
+ long LongNum;
+
+ LongNum = Par_GetParToLong ("Ans");
+ if (LongNum < 0)
+ Lay_ShowErrorAndExit ("Wrong answer index.");
+
+ return (unsigned) LongNum;
+ }
+
+/*****************************************************************************/
+/*********************** Put a big button to do action ***********************/
+/*****************************************************************************/
+
+static void Mch_PutBigButton (Act_Action_t NextAction,long MchCod,
+ const char *Icon,const char *Txt)
+ {
+ /***** Start form *****/
+ Frm_StartForm (NextAction);
+ Mch_PutParamMatchCod (MchCod);
+
+ /***** Put icon with link *****/
+ /* Submitting onmousedown instead of default onclick
+ is necessary in order to be fast
+ and not lose clicks due to refresh */
+ fprintf (Gbl.F.Out,"
",
+ Icon);
+ }
+
+static void Mch_PutBigButtonClose (void)
+ {
+ extern const char *Txt_Close;
+
+ /***** Put icon with link *****/
+ /* onmousedown instead of default onclick
+ is necessary in order to be fast
+ and not lose clicks due to refresh */
+ fprintf (Gbl.F.Out,"
",
+ Cfg_URL_ICON_PUBLIC,
+ Txt,
+ Txt);
+ }
+
+/*****************************************************************************/
+/**************************** Remove old players *****************************/
+/*****************************************************************************/
+
+static void Mch_RemoveOldPlayers (void)
+ {
+ /***** Delete matches not being played *****/
+ DB_QueryDELETE ("can not update matches as not being played",
+ "DELETE FROM gam_mch_being_played"
+ " WHERE TSStatus.NumPlayers = (unsigned) DB_QueryCOUNT ("can not get number of players",
+ "SELECT COUNT(*) FROM gam_players"
+ " WHERE MchCod=%ld",
+ Match->MchCod);
+ }
+
+/*****************************************************************************/
+/******************* Show the results of a finished match ********************/
+/*****************************************************************************/
+
+void Mch_ShowFinishedMatchResults (void)
+ {
+ Ale_ShowAlert (Ale_INFO,"To be implemented...");
+ }
+
+/*****************************************************************************/
+/********************** Get code of match being played ***********************/
+/*****************************************************************************/
+
+void Mch_GetMatchBeingPlayed (void)
+ {
+ /***** Get match code ****/
+ if ((Gbl.Games.MchCodBeingPlayed = Mch_GetParamMatchCod ()) == -1L)
+ Lay_ShowErrorAndExit ("Code of match is missing.");
+ }
+
+/*****************************************************************************/
+/********* Show game being played to me as student in a new window ***********/
+/*****************************************************************************/
+
+void Mch_ShowMatchToMeAsStd (void)
+ {
+ struct Match Match;
+
+ /***** Remove old players.
+ This function must be called before getting match status. *****/
+ Mch_RemoveOldPlayers ();
+
+ /***** Get data of the match from database *****/
+ Match.MchCod = Gbl.Games.MchCodBeingPlayed;
+ Mch_GetDataOfMatchByCod (&Match);
+
+ /***** Show current match status *****/
+ fprintf (Gbl.F.Out,"
");
+ }
+
+/*****************************************************************************/
+/****************** Refresh match for a teacher via AJAX *********************/
+/*****************************************************************************/
+
+void Mch_RefreshMatchTch (void)
+ {
+ struct Match Match;
+
+ if (!Gbl.Session.IsOpen) // If session has been closed, do not write anything
+ return;
+
+ /***** Remove old players.
+ This function must be called before getting match status. *****/
+ Mch_RemoveOldPlayers ();
+
+ /***** Get data of the match from database *****/
+ Match.MchCod = Gbl.Games.MchCodBeingPlayed;
+ Mch_GetDataOfMatchByCod (&Match);
+
+ /***** Update match status in database *****/
+ Mch_UpdateMatchStatusInDB (&Match);
+
+ /***** Update elapsed time in this question *****/
+ Mch_UpdateElapsedTimeInQuestion (&Match);
+
+ /***** Show current match status *****/
+ Mch_ShowMatchStatusForTch (&Match);
+ }
+
+/*****************************************************************************/
+/*************** Refresh current game for a student via AJAX *****************/
+/*****************************************************************************/
+
+void Mch_RefreshMatchStd (void)
+ {
+ struct Match Match;
+
+ if (!Gbl.Session.IsOpen) // If session has been closed, do not write anything
+ return;
+
+ /***** Remove old players.
+ This function must be called before getting match status. *****/
+ Mch_RemoveOldPlayers ();
+
+ /***** Get data of the match from database *****/
+ Match.MchCod = Gbl.Games.MchCodBeingPlayed;
+ Mch_GetDataOfMatchByCod (&Match);
+
+ /***** Show current match status *****/
+ Mch_ShowMatchStatusForStd (&Match);
+ }
+
+/*****************************************************************************/
+/**** Receive previous question answer in a match question from database *****/
+/*****************************************************************************/
+
+static int Mch_GetQstAnsFromDB (long MchCod,unsigned QstInd)
+ {
+ MYSQL_RES *mysql_res;
+ MYSQL_ROW row;
+ unsigned NumRows;
+ int StdAnsInd = -1; // <0 ==> no answer selected
+
+ /***** Get student's answer *****/
+ NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get student's answer to a match question",
+ "SELECT AnsInd FROM gam_answers"
+ " WHERE MchCod=%ld AND UsrCod=%ld AND QstInd=%u",
+ MchCod,
+ Gbl.Usrs.Me.UsrDat.UsrCod,
+ QstInd);
+ if (NumRows) // Answer found...
+ {
+ /***** Get answer index *****/
+ row = mysql_fetch_row (mysql_res);
+ if (sscanf (row[0],"%d",&StdAnsInd) != 1)
+ Lay_ShowErrorAndExit ("Error when getting student's answer to a match question.");
+ }
+
+ /***** Free structure that stores the query result *****/
+ DB_FreeMySQLResult (&mysql_res);
+
+ return StdAnsInd;
+ }
+
+/*****************************************************************************/
+/********* Receive question answer from student when playing a match *********/
+/*****************************************************************************/
+
+void Mch_ReceiveQstAnsFromStd (void)
+ {
+ struct Match Match;
+ unsigned QstInd;
+ unsigned StdAnsInd;
+ int PreviousStdAnsInd;
+
+ /***** Remove old players.
+ This function must be called before getting match status. *****/
+ Mch_RemoveOldPlayers ();
+
+ /***** Get data of the match from database *****/
+ Match.MchCod = Gbl.Games.MchCodBeingPlayed;
+ Mch_GetDataOfMatchByCod (&Match);
+
+ /***** Get question index from form *****/
+ QstInd = Gam_GetParamQstInd ();
+
+ /***** Check that question index is the current one being played *****/
+ if (QstInd == Match.Status.QstInd) // Receiving an answer
+ // to the current question being played
+ {
+ /***** Get answer index *****/
+ /*-------+--------------+
+ | Button | Answer index |
+ +--------+--------------+
+ | a | 0 |
+ | b | 1 |
+ | c | 2 |
+ | d | 3 |
+ | ... | ... |
+ +--------+-------------*/
+ StdAnsInd = Mch_GetParamAnswer ();
+
+ /***** Get previous student's answer to this question
+ (<0 ==> no answer) *****/
+ PreviousStdAnsInd = Mch_GetQstAnsFromDB (Match.MchCod,QstInd);
+
+ /***** Store student's answer *****/
+ if (PreviousStdAnsInd == (int) StdAnsInd)
+ DB_QueryDELETE ("can not register your answer to the match question",
+ "DELETE FROM gam_answers"
+ " WHERE MchCod=%ld AND UsrCod=%ld AND QstInd=%u",
+ Match.MchCod,Gbl.Usrs.Me.UsrDat.UsrCod,QstInd);
+ else
+ DB_QueryREPLACE ("can not register your answer to the match question",
+ "REPLACE gam_answers"
+ " (MchCod,UsrCod,QstInd,AnsInd)"
+ " VALUES"
+ " (%ld,%ld,%u,%u)",
+ Match.MchCod,Gbl.Usrs.Me.UsrDat.UsrCod,QstInd,StdAnsInd);
+ }
+
+ /***** Show current match status *****/
+ fprintf (Gbl.F.Out,"
");
+ }
+
+/*****************************************************************************/
+/***** Get number of users who have answered current question in a match *****/
+/*****************************************************************************/
+
+static unsigned Mch_GetNumAnswerers (struct Match *Match)
+ {
+ /***** Get number of users who have answered the current question in a match from database *****/
+ return
+ (unsigned) DB_QueryCOUNT ("can not get number of questions of a game",
+ "SELECT COUNT(*) FROM gam_answers"
+ " WHERE MchCod=%ld AND QstInd=%u",
+ Match->MchCod,
+ Match->Status.QstInd);
+ }
+
+/*****************************************************************************/
+/*** Get number of users who selected this answer and draw proportional bar **/
+/*****************************************************************************/
+
+void Mch_GetAndDrawBarNumUsrsWhoAnswered (long MchCod,unsigned QstInd,unsigned AnsInd,unsigned NumUsrs)
+ {
+ unsigned NumUsrsThisAnswer;
+
+ /***** Get number of users who selected this answer *****/
+ NumUsrsThisAnswer = Mch_GetNumUsrsWhoAnswered (MchCod,QstInd,AnsInd);
+
+ /***** Show stats of this answer *****/
+ Mch_DrawBarNumUsrs (NumUsrsThisAnswer,NumUsrs);
+ }
+
+/*****************************************************************************/
+/**** Get number of users who selected a given answer of a game question *****/
+/*****************************************************************************/
+
+static unsigned Mch_GetNumUsrsWhoAnswered (long MchCod,unsigned QstInd,unsigned AnsInd)
+ {
+ /***** 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 answered",
+ "SELECT COUNT(*)"
+ " FROM gam_answers"
+ " WHERE MchCod=%ld"
+ " AND QstInd=%u"
+ " AND AnsInd=%u",
+ MchCod,QstInd,AnsInd);
+ }
+
+/*****************************************************************************/
+/***************** Draw a bar with the percentage of answers *****************/
+/*****************************************************************************/
+
+#define Gam_MAX_BAR_WIDTH 125
+
+static void Mch_DrawBarNumUsrs (unsigned NumUsrs,unsigned MaxUsrs)
+ {
+ extern const char *Txt_of_PART_OF_A_TOTAL;
+ unsigned BarWidth = 0;
+
+ /***** String with the number of users *****/
+ if (MaxUsrs)
+ snprintf (Gbl.Title,sizeof (Gbl.Title),
+ "%u (%u%% %s %u)",
+ NumUsrs,
+ (unsigned) ((((float) NumUsrs * 100.0) / (float) MaxUsrs) + 0.5),
+ Txt_of_PART_OF_A_TOTAL,MaxUsrs);
+ else
+ snprintf (Gbl.Title,sizeof (Gbl.Title),
+ "0 (0%% %s %u)",
+ Txt_of_PART_OF_A_TOTAL,MaxUsrs);
+
+ /***** Draw bar with a with proportional to the number of clicks *****/
+ if (NumUsrs && MaxUsrs)
+ BarWidth = (unsigned) ((((float) NumUsrs * (float) Gam_MAX_BAR_WIDTH) /
+ (float) MaxUsrs) + 0.5);
+ if (BarWidth < 2)
+ BarWidth = 2;
+ fprintf (Gbl.F.Out,""
+ " ",
+ Cfg_URL_ICON_PUBLIC,
+ Gbl.Title,
+ Gbl.Title,
+ BarWidth);
+
+ /***** Write the number of users *****/
+ fprintf (Gbl.F.Out,"%s",Gbl.Title);
+ }
diff --git a/swad_match.h b/swad_match.h
new file mode 100644
index 00000000..d37f08ff
--- /dev/null
+++ b/swad_match.h
@@ -0,0 +1,68 @@
+// swad_match.h: matches in games using remote control
+
+#ifndef _SWAD_MCH
+#define _SWAD_MCH
+/*
+ SWAD (Shared Workspace At a Distance in Spanish),
+ is a web platform developed at the University of Granada (Spain),
+ and used to support university teaching.
+
+ This file is part of SWAD core.
+ Copyright (C) 1999-2019 Antonio Caņas Vargas
+
+ 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 .
+*/
+/*****************************************************************************/
+/********************************* Headers ***********************************/
+/*****************************************************************************/
+
+#include "swad_scope.h"
+
+/*****************************************************************************/
+/************************** Public types and constants ***********************/
+/*****************************************************************************/
+
+#define Mch_NEW_MATCH_SECTION_ID "new_match"
+
+#define Mch_AFTER_LAST_QUESTION ((unsigned)((1UL << 31) - 1)) // 2^31 - 1, don't change this number because it is used in database to indicate that a match is finished
+
+/*****************************************************************************/
+/***************************** Public prototypes *****************************/
+/*****************************************************************************/
+
+void Mch_ListMatches (struct Game *Game,bool PutFormNewMatch);
+
+void Mch_RequestRemoveMatchTch (void);
+void Mch_RemoveMatchTch (void);
+
+void Mch_CreateNewMatchTch (void);
+void Mch_RequestStartResumeMatchTch (void);
+void Mch_PauseMatchTch (void);
+void Mch_ResumeMatchTch (void);
+void Mch_ShowResultsQstMatchTch (void);
+void Mch_BackMatchTch (void);
+void Mch_ForwardMatchTch (void);
+
+void Mch_ShowFinishedMatchResults (void);
+
+void Mch_GetMatchBeingPlayed (void);
+void Mch_ShowMatchToMeAsStd (void);
+void Mch_RefreshMatchTch (void);
+void Mch_RefreshMatchStd (void);
+
+void Mch_ReceiveQstAnsFromStd (void);
+
+void Mch_GetAndDrawBarNumUsrsWhoAnswered (long MchCod,unsigned QstInd,unsigned AnsInd,unsigned NumUsrs);
+
+#endif
diff --git a/swad_test.c b/swad_test.c
index 5a4571b4..8e9de988 100644
--- a/swad_test.c
+++ b/swad_test.c
@@ -43,6 +43,7 @@
#include "swad_global.h"
#include "swad_ID.h"
#include "swad_language.h"
+#include "swad_match.h"
#include "swad_media.h"
#include "swad_parameter.h"
#include "swad_table.h"
@@ -4142,7 +4143,7 @@ static void Tst_WriteChoiceAnsViewGame (long MchCod,unsigned QstInd,long QstCod,
"
");
/* Get number of users who selected this answer
and draw proportional bar */
- Gam_GetAndDrawBarNumUsrsWhoAnswered (MchCod,QstInd,AnsInd,
+ Mch_GetAndDrawBarNumUsrsWhoAnswered (MchCod,QstInd,AnsInd,
0); // TODO: NumUsrs
fprintf (Gbl.F.Out,"