diff --git a/Makefile b/Makefile index 078f850b..b66d7d87 100644 --- a/Makefile +++ b/Makefile @@ -51,9 +51,9 @@ OBJS = swad_account.o swad_account_database.o swad_action.o swad_admin.o \ swad_file_extension.o swad_file_MIME.o swad_firewall.o \ swad_firewall_database.o swad_follow.o swad_follow_database.o \ swad_form.o swad_forum.o swad_forum_database.o \ - swad_game.o swad_global.o swad_group.o swad_group_database.o \ - swad_help.o swad_hierarchy.o swad_hierarchy_config.o swad_holiday.o \ - swad_HTML.o \ + swad_game.o swad_game_database.o swad_global.o swad_group.o \ + swad_group_database.o swad_help.o swad_hierarchy.o \ + swad_hierarchy_config.o swad_holiday.o swad_HTML.o \ swad_icon.o swad_ID.o swad_indicator.o swad_info.o swad_institution.o \ swad_institution_config.o swad_institution_database.o \ swad_language.o swad_layout.o swad_link.o swad_log.o swad_logo.o \ diff --git a/swad_changelog.h b/swad_changelog.h index f8552e0b..d5f8f866 100644 --- a/swad_changelog.h +++ b/swad_changelog.h @@ -602,13 +602,14 @@ TODO: FIX BUG, URGENT! En las fechas como par TODO: En las encuestas, que los estudiantes no puedan ver los resultados hasta que no finalice el plazo. */ -#define Log_PLATFORM_VERSION "SWAD 21.5.3 (2021-09-16)" +#define Log_PLATFORM_VERSION "SWAD 21.6 (2021-09-18)" #define CSS_FILE "swad20.45.css" #define JS_FILE "swad20.69.1.js" /* TODO: Rename CENTRE to CENTER in help wiki. TODO: Rename ASSESSMENT.Announcements to ASSESSMENT.Calls_for_exams + Version 21.6: Sep 18, 2021 New module swad_game_database for database queries related to games. (315565 lines) Version 21.5.3: Sep 16, 2021 Queries moved to module swad_forum_database. (315431 lines) Version 21.5.2: Sep 16, 2021 Queries moved to module swad_forum_database. (315403 lines) Version 21.5.1: Sep 15, 2021 Queries moved to module swad_forum_database. (315348 lines) diff --git a/swad_game.c b/swad_game.c index 325cbe4b..20c39963 100644 --- a/swad_game.c +++ b/swad_game.c @@ -38,6 +38,7 @@ #include "swad_figure.h" #include "swad_form.h" #include "swad_game.h" +#include "swad_game_database.h" #include "swad_global.h" #include "swad_hierarchy_level.h" #include "swad_HTML.h" @@ -867,63 +868,16 @@ static Gam_Order_t Gam_GetParamOrder (void) void Gam_GetListGames (struct Gam_Games *Games,Gam_Order_t SelectedOrder) { - static const char *OrderBySubQuery[Gam_NUM_ORDERS] = - { - [Gam_ORDER_BY_START_DATE] = "StartTime DESC,EndTime DESC,gam_games.Title DESC", - [Gam_ORDER_BY_END_DATE ] = "EndTime DESC,StartTime DESC,gam_games.Title DESC", - [Gam_ORDER_BY_TITLE ] = "gam_games.Title", - }; MYSQL_RES *mysql_res; MYSQL_ROW row; - char *HiddenSubQuery; unsigned NumGame; /***** Free list of games *****/ if (Games->LstIsRead) Gam_FreeListGames (Games); - /***** Subquery: get hidden games depending on user's role *****/ - switch (Gbl.Usrs.Me.Role.Logged) - { - case Rol_STD: - if (asprintf (&HiddenSubQuery," AND gam_games.Hidden='N'") < 0) - Err_NotEnoughMemoryExit (); - break; - case Rol_NET: - case Rol_TCH: - case Rol_DEG_ADM: - case Rol_CTR_ADM: - case Rol_INS_ADM: - case Rol_SYS_ADM: - if (asprintf (&HiddenSubQuery,"%s","") < 0) - Err_NotEnoughMemoryExit (); - break; - default: - Err_WrongRoleExit (); - break; - } - /***** Get list of games from database *****/ - Games->Num = (unsigned) - DB_QuerySELECT (&mysql_res,"can not get games", - "SELECT gam_games.GamCod," // row[0] - "MIN(mch_matches.StartTime) AS StartTime," // row[1] - "MAX(mch_matches.EndTime) AS EndTime" // row[2] - " FROM gam_games" - " LEFT JOIN mch_matches" - " ON gam_games.GamCod=mch_matches.GamCod" - " WHERE gam_games.CrsCod=%ld" - "%s" - " GROUP BY gam_games.GamCod" - " ORDER BY %s", - Gbl.Hierarchy.Crs.CrsCod, - HiddenSubQuery, - OrderBySubQuery[SelectedOrder]); - - /***** Free allocated memory for subquery *****/ - free (HiddenSubQuery); - - if (Games->Num) // Games found... + if ((Games->Num = Gam_DB_GetListGames (&mysql_res,SelectedOrder))) // Games found... { /***** Create list of games *****/ if ((Games->Lst = malloc ((size_t) Games->Num * @@ -1026,21 +980,7 @@ void Gam_GetDataOfGameByCod (struct Gam_Game *Game) MYSQL_ROW row; /***** Get data of game from database *****/ - if (DB_QuerySELECT (&mysql_res,"can not get game data", - "SELECT gam_games.GamCod," // row[0] - "gam_games.CrsCod," // row[1] - "gam_games.Hidden," // row[2] - "gam_games.UsrCod," // row[3] - "gam_games.MaxGrade," // row[4] - "gam_games.Visibility," // row[5] - "gam_games.Title" // row[6] - " FROM gam_games" - " LEFT JOIN mch_matches" - " ON gam_games.GamCod=mch_matches.GamCod" - " WHERE gam_games.GamCod=%ld" - " AND gam_games.CrsCod='%ld'", // Extra check - Game->GamCod, - Gbl.Hierarchy.Crs.CrsCod)) // Game found... + if (Gam_DB_GetDataOfGameByCod (&mysql_res,Game->GamCod)) // Game found... { /* Get row */ row = mysql_fetch_row (mysql_res); @@ -1072,10 +1012,10 @@ void Gam_GetDataOfGameByCod (struct Gam_Game *Game) Game->NumQsts = Gam_DB_GetNumQstsGame (Game->GamCod); /* Get number of matches */ - Game->NumMchs = Mch_GetNumMchsInGame (Game->GamCod); + Game->NumMchs = Mch_DB_GetNumMchsInGame (Game->GamCod); /* Get number of unfinished matches */ - Game->NumUnfinishedMchs = Mch_GetNumUnfinishedMchsInGame (Game->GamCod); + Game->NumUnfinishedMchs = Mch_DB_GetNumUnfinishedMchsInGame (Game->GamCod); } else /* Initialize to empty game */ @@ -1087,20 +1027,14 @@ void Gam_GetDataOfGameByCod (struct Gam_Game *Game) if (Game->GamCod > 0) { /***** Get start and end times from database *****/ - if (DB_QuerySELECT (&mysql_res,"can not get game data", - "SELECT UNIX_TIMESTAMP(MIN(StartTime))," // row[0] - "UNIX_TIMESTAMP(MAX(EndTime))" // row[1] - " FROM mch_matches" - " WHERE GamCod=%ld", - Game->GamCod)) + if (Mch_DB_GetStartEndMatchesInGame (&mysql_res,Game->GamCod)) { /* Get row */ row = mysql_fetch_row (mysql_res); - /* Get start date (row[0] holds the start UTC time) */ + /* Get start date (row[0] holds the start UTC time) + and end date (row[1] holds the end UTC time) */ Game->TimeUTC[Dat_START_TIME] = Dat_GetUNIXTimeFromStr (row[0]); - - /* Get end date (row[1] holds the end UTC time) */ Game->TimeUTC[Dat_END_TIME ] = Dat_GetUNIXTimeFromStr (row[1]); } @@ -1715,21 +1649,6 @@ static void Gam_UpdateGame (struct Gam_Game *Game,const char *Txt) Ale_ShowAlert (Ale_SUCCESS,Txt_The_game_has_been_modified); } -/*****************************************************************************/ -/******************* Get number of questions of a game *********************/ -/*****************************************************************************/ - -unsigned Gam_DB_GetNumQstsGame (long GamCod) - { - /***** Get nuumber of questions in a game from database *****/ - return (unsigned) - DB_QueryCOUNT ("can not get number of questions of a game", - "SELECT COUNT(*)" - " FROM gam_questions" - " WHERE GamCod=%ld", - GamCod); - } - /*****************************************************************************/ /*************** Put a form to edit/create a question in game ****************/ /*****************************************************************************/ @@ -1889,47 +1808,6 @@ static unsigned Gam_DB_GetMaxQuestionIndexInGame (long GamCod) GamCod); } -/*****************************************************************************/ -/*********** Get previous question index to a given index in a game **********/ -/*****************************************************************************/ -// 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 - -unsigned Gam_DB_GetPrevQuestionIndexInGame (long GamCod,unsigned QstInd) - { - /***** Get previous question index in a game from database *****/ - // Although indexes are always continuous... - // ...this implementation works even with non continuous indexes - return DB_QuerySELECTUnsigned ("can not get previous question index", - "SELECT COALESCE(MAX(QstInd),0)" - " FROM gam_questions" - " WHERE GamCod=%ld" - " AND QstInd<%u", - GamCod, - QstInd); - } - -/*****************************************************************************/ -/************* Get next question index to a given index in a game ************/ -/*****************************************************************************/ -// 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 big number if no next question - -unsigned Gam_DB_GetNextQuestionIndexInGame (long GamCod,unsigned QstInd) - { - /***** Get next question index in a game from database *****/ - // Although indexes are always continuous... - // ...this implementation works even with non continuous indexes - return DB_QuerySELECTUnsigned ("can not get next question index", - "SELECT COALESCE(MIN(QstInd),%u)" - " FROM gam_questions" - " WHERE GamCod=%ld" - " AND QstInd>%u", - Gam_AFTER_LAST_QUESTION, // End of questions has been reached - GamCod, - QstInd); - } - /*****************************************************************************/ /************************ List the questions of a game ***********************/ /*****************************************************************************/ @@ -2614,249 +2492,6 @@ void Gam_RequestNewMatch (void) true); // Put form to start new match } -/*****************************************************************************/ -/********************* Get number of courses with games **********************/ -/*****************************************************************************/ -// Returns the number of courses with games in this location - -unsigned Gam_DB_GetNumCoursesWithGames (HieLvl_Level_t Scope) - { - /***** Get number of courses with games from database *****/ - switch (Scope) - { - case HieLvl_SYS: - return (unsigned) - DB_QueryCOUNT ("can not get number of courses with games", - "SELECT COUNT(DISTINCT CrsCod)" - " FROM gam_games"); - case HieLvl_CTY: - return (unsigned) - DB_QueryCOUNT ("can not get number of courses with games", - "SELECT COUNT(DISTINCT gam_games.CrsCod)" - " FROM ins_instits," - "ctr_centers," - "deg_degrees," - "crs_courses," - "gam_games" - " WHERE ins_instits.CtyCod=%ld" - " AND ins_instits.InsCod=ctr_centers.InsCod" - " AND ctr_centers.CtrCod=deg_degrees.CtrCod" - " AND deg_degrees.DegCod=crs_courses.DegCod" - " AND crs_courses.CrsCod=gam_games.CrsCod", - Gbl.Hierarchy.Ins.InsCod); - case HieLvl_INS: - return (unsigned) - DB_QueryCOUNT ("can not get number of courses with games", - "SELECT COUNT(DISTINCT gam_games.CrsCod)" - " FROM ctr_centers," - "deg_degrees," - "crs_courses," - "gam_games" - " WHERE ctr_centers.InsCod=%ld" - " AND ctr_centers.CtrCod=deg_degrees.CtrCod" - " AND deg_degrees.DegCod=crs_courses.DegCod" - " AND crs_courses.CrsCod=gam_games.CrsCod", - Gbl.Hierarchy.Ins.InsCod); - case HieLvl_CTR: - return (unsigned) - DB_QueryCOUNT ("can not get number of courses with games", - "SELECT COUNT(DISTINCT gam_games.CrsCod)" - " FROM deg_degrees," - "crs_courses," - "gam_games" - " WHERE deg_degrees.CtrCod=%ld" - " AND deg_degrees.DegCod=crs_courses.DegCod" - " AND crs_courses.CrsCod=gam_games.CrsCod", - Gbl.Hierarchy.Ctr.CtrCod); - case HieLvl_DEG: - return (unsigned) - DB_QueryCOUNT ("can not get number of courses with games", - "SELECT COUNT(DISTINCT gam_games.CrsCod)" - " FROM crs_courses," - "gam_games" - " WHERE crs_courses.DegCod=%ld" - " AND crs_courses.CrsCod=gam_games.CrsCod", - Gbl.Hierarchy.Deg.DegCod); - case HieLvl_CRS: - return (unsigned) - DB_QueryCOUNT ("can not get number of courses with games", - "SELECT COUNT(DISTINCT CrsCod)" - " FROM gam_games" - " WHERE CrsCod=%ld", - Gbl.Hierarchy.Crs.CrsCod); - default: - return 0; - } - } - -/*****************************************************************************/ -/**************************** Get number of games ****************************/ -/*****************************************************************************/ -// Returns the number of games in this location - -unsigned Gam_DB_GetNumGames (HieLvl_Level_t Scope) - { - /***** Get number of games from database *****/ - switch (Scope) - { - case HieLvl_SYS: - return (unsigned) - DB_QueryCOUNT ("can not get number of games", - "SELECT COUNT(*)" - " FROM gam_games"); - case HieLvl_CTY: - return (unsigned) - DB_QueryCOUNT ("can not get number of games", - "SELECT COUNT(*)" - " FROM ins_instits," - "ctr_centers," - "deg_degrees," - "crs_courses," - "gam_games" - " WHERE ins_instits.CtyCod=%ld" - " AND ins_instits.InsCod=ctr_centers.InsCod" - " AND ctr_centers.CtrCod=deg_degrees.CtrCod" - " AND deg_degrees.DegCod=crs_courses.DegCod" - " AND crs_courses.CrsCod=gam_games.CrsCod", - Gbl.Hierarchy.Cty.CtyCod); - case HieLvl_INS: - return (unsigned) - DB_QueryCOUNT ("can not get number of games", - "SELECT COUNT(*)" - " FROM ctr_centers," - "deg_degrees," - "crs_courses," - "gam_games" - " WHERE ctr_centers.InsCod=%ld" - " AND ctr_centers.CtrCod=deg_degrees.CtrCod" - " AND deg_degrees.DegCod=crs_courses.DegCod" - " AND crs_courses.CrsCod=gam_games.CrsCod", - Gbl.Hierarchy.Ins.InsCod); - case HieLvl_CTR: - return (unsigned) - DB_QueryCOUNT ("can not get number of games", - "SELECT COUNT(*)" - " FROM deg_degrees," - "crs_courses," - "gam_games" - " WHERE deg_degrees.CtrCod=%ld" - " AND deg_degrees.DegCod=crs_courses.DegCod" - " AND crs_courses.CrsCod=gam_games.CrsCod", - Gbl.Hierarchy.Ctr.CtrCod); - case HieLvl_DEG: - return (unsigned) - DB_QueryCOUNT ("can not get number of games", - "SELECT COUNT(*)" - " FROM crs_courses," - "gam_games" - " WHERE crs_courses.DegCod=%ld" - " AND crs_courses.CrsCod=gam_games.CrsCod", - Gbl.Hierarchy.Deg.DegCod); - case HieLvl_CRS: - return (unsigned) - DB_QueryCOUNT ("can not get number of games", - "SELECT COUNT(*)" - " FROM gam_games" - " WHERE CrsCod=%ld", - Gbl.Hierarchy.Crs.CrsCod); - default: - return 0; - } - } - -/*****************************************************************************/ -/***************** Get average number of questions per game ******************/ -/*****************************************************************************/ - -double Gam_DB_GetNumQstsPerGame (HieLvl_Level_t Scope) - { - /***** Get number of questions per game from database *****/ - switch (Scope) - { - case HieLvl_SYS: - return DB_QuerySELECTDouble ("can not get number of questions per game", - "SELECT AVG(NumQsts)" - " FROM (SELECT COUNT(gam_questions.QstCod) AS NumQsts" - " FROM gam_games," - "gam_questions" - " WHERE gam_games.GamCod=gam_questions.GamCod" - " GROUP BY gam_questions.GamCod) AS NumQstsTable"); - case HieLvl_CTY: - return DB_QuerySELECTDouble ("can not get number of questions per game", - "SELECT AVG(NumQsts)" - " FROM (SELECT COUNT(gam_questions.QstCod) AS NumQsts" - " FROM ins_instits," - "ctr_centers," - "deg_degrees," - "crs_courses," - "gam_games," - "gam_questions" - " WHERE ins_instits.CtyCod=%ld" - " AND ins_instits.InsCod=ctr_centers.InsCod" - " AND ctr_centers.CtrCod=deg_degrees.CtrCod" - " AND deg_degrees.DegCod=crs_courses.DegCod" - " AND crs_courses.CrsCod=gam_games.CrsCod" - " AND gam_games.GamCod=gam_questions.GamCod" - " GROUP BY gam_questions.GamCod) AS NumQstsTable", - Gbl.Hierarchy.Cty.CtyCod); - case HieLvl_INS: - return DB_QuerySELECTDouble ("can not get number of questions per game", - "SELECT AVG(NumQsts)" - " FROM (SELECT COUNT(gam_questions.QstCod) AS NumQsts" - " FROM ctr_centers," - "deg_degrees," - "crs_courses," - "gam_games," - "gam_questions" - " WHERE ctr_centers.InsCod=%ld" - " AND ctr_centers.CtrCod=deg_degrees.CtrCod" - " AND deg_degrees.DegCod=crs_courses.DegCod" - " AND crs_courses.CrsCod=gam_games.CrsCod" - " AND gam_games.GamCod=gam_questions.GamCod" - " GROUP BY gam_questions.GamCod) AS NumQstsTable", - Gbl.Hierarchy.Ins.InsCod); - case HieLvl_CTR: - return DB_QuerySELECTDouble ("can not get number of questions per game", - "SELECT AVG(NumQsts)" - " FROM (SELECT COUNT(gam_questions.QstCod) AS NumQsts" - " FROM deg_degrees," - "crs_courses," - "gam_games," - "gam_questions" - " WHERE deg_degrees.CtrCod=%ld" - " AND deg_degrees.DegCod=crs_courses.DegCod" - " AND crs_courses.CrsCod=gam_games.CrsCod" - " AND gam_games.GamCod=gam_questions.GamCod" - " GROUP BY gam_questions.GamCod) AS NumQstsTable", - Gbl.Hierarchy.Ctr.CtrCod); - case HieLvl_DEG: - return DB_QuerySELECTDouble ("can not get number of questions per game", - "SELECT AVG(NumQsts)" - " FROM (SELECT COUNT(gam_questions.QstCod) AS NumQsts" - " FROM crs_courses," - "gam_games," - "gam_questions" - " WHERE crs_courses.DegCod=%ld" - " AND crs_courses.CrsCod=gam_games.CrsCod" - " AND gam_games.GamCod=gam_questions.GamCod" - " GROUP BY gam_questions.GamCod) AS NumQstsTable", - Gbl.Hierarchy.Deg.DegCod); - case HieLvl_CRS: - return DB_QuerySELECTDouble ("can not get number of questions per game", - "SELECT AVG(NumQsts)" - " FROM (SELECT COUNT(gam_questions.QstCod) AS NumQsts" - " FROM gam_games," - "gam_questions" - " WHERE gam_games.Cod=%ld" - " AND gam_games.GamCod=gam_questions.GamCod" - " GROUP BY gam_questions.GamCod) AS NumQstsTable", - Gbl.Hierarchy.Crs.CrsCod); - default: - Err_WrongScopeExit (); - return 0.0; // Not reached - } - } - /*****************************************************************************/ /************************* Show test tags in a game **************************/ /*****************************************************************************/ diff --git a/swad_game.h b/swad_game.h index 5cb8e14b..cdb2d89d 100644 --- a/swad_game.h +++ b/swad_game.h @@ -143,16 +143,12 @@ void Gam_RequestCreatOrEditGame (void); void Gam_ReceiveFormGame (void); -unsigned Gam_DB_GetNumQstsGame (long GamCod); - void Gam_ReqSelectQstsToAddToGame (void); void Gam_ListQstsToAddToGame (void); void Gam_PutParamQstInd (unsigned QstInd); unsigned Gam_GetParamQstInd (void); long Gam_GetQstCodFromQstInd (long GamCod,unsigned QstInd); -unsigned Gam_DB_GetPrevQuestionIndexInGame (long GamCod,unsigned QstInd); -unsigned Gam_DB_GetNextQuestionIndexInGame (long GamCod,unsigned QstInd); void Gam_AddQstsToGame (void); @@ -165,10 +161,6 @@ void Gam_MoveDownQst (void); void Gam_PutButtonNewMatch (struct Gam_Games *Games,long GamCod); void Gam_RequestNewMatch (void); -unsigned Gam_DB_GetNumCoursesWithGames (HieLvl_Level_t Scope); -unsigned Gam_DB_GetNumGames (HieLvl_Level_t Scope); -double Gam_DB_GetNumQstsPerGame (HieLvl_Level_t Scope); - void Gam_ShowTstTagsPresentInAGame (long GamCod); void Gam_GetScoreRange (long GamCod,double *MinScore,double *MaxScore); diff --git a/swad_game_database.c b/swad_game_database.c new file mode 100644 index 00000000..7b4f0c5b --- /dev/null +++ b/swad_game_database.c @@ -0,0 +1,465 @@ +// swad_game_database.c: games using remote control, operations with database + +/* + SWAD (Shared Workspace At a Distance), + is a web platform developed at the University of Granada (Spain), + and used to support university teaching. + + This file is part of SWAD core. + Copyright (C) 1999-2021 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 ***********************************/ +/*****************************************************************************/ + +#define _GNU_SOURCE // For asprintf +// #include // For DBL_MAX +// #include // For PATH_MAX +// #include // For NULL +#include // For asprintf +// #include // For free +// #include // For string functions + +#include "swad_database.h" +#include "swad_error.h" +// #include "swad_figure.h" +// #include "swad_form.h" +#include "swad_game.h" +#include "swad_game_database.h" +#include "swad_global.h" +// #include "swad_hierarchy_level.h" +// #include "swad_HTML.h" +// #include "swad_match.h" +// #include "swad_match_result.h" +// #include "swad_pagination.h" +// #include "swad_role.h" +// #include "swad_test.h" +// #include "swad_test_visibility.h" + +/*****************************************************************************/ +/************** External global variables from others modules ****************/ +/*****************************************************************************/ + +extern struct Globals Gbl; + +/*****************************************************************************/ +/***************************** Private constants *****************************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/******************************* Private types *******************************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/***************************** Private variables *****************************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/***************************** Private prototypes ****************************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/************* Get list of all the games in the current course ***************/ +/*****************************************************************************/ + +unsigned Gam_DB_GetListGames (MYSQL_RES **mysql_res,Gam_Order_t SelectedOrder) + { + static const char *OrderBySubQuery[Gam_NUM_ORDERS] = + { + [Gam_ORDER_BY_START_DATE] = "StartTime DESC," + "EndTime DESC," + "gam_games.Title DESC", + [Gam_ORDER_BY_END_DATE ] = "EndTime DESC," + "StartTime DESC," + "gam_games.Title DESC", + [Gam_ORDER_BY_TITLE ] = "gam_games.Title", + }; + ; + char *HiddenSubQuery; + unsigned NumGames; + + /***** Subquery: get hidden games depending on user's role *****/ + switch (Gbl.Usrs.Me.Role.Logged) + { + case Rol_STD: + if (asprintf (&HiddenSubQuery," AND gam_games.Hidden='N'") < 0) + Err_NotEnoughMemoryExit (); + break; + case Rol_NET: + case Rol_TCH: + case Rol_DEG_ADM: + case Rol_CTR_ADM: + case Rol_INS_ADM: + case Rol_SYS_ADM: + if (asprintf (&HiddenSubQuery,"%s","") < 0) + Err_NotEnoughMemoryExit (); + break; + default: + Err_WrongRoleExit (); + break; + } + + /***** Get list of games from database *****/ + NumGames = (unsigned) + DB_QuerySELECT (mysql_res,"can not get games", + "SELECT gam_games.GamCod," // row[0] + "MIN(mch_matches.StartTime) AS StartTime," // row[1] + "MAX(mch_matches.EndTime) AS EndTime" // row[2] + " FROM gam_games" + " LEFT JOIN mch_matches" + " ON gam_games.GamCod=mch_matches.GamCod" + " WHERE gam_games.CrsCod=%ld" + "%s" + " GROUP BY gam_games.GamCod" + " ORDER BY %s", + Gbl.Hierarchy.Crs.CrsCod, + HiddenSubQuery, + OrderBySubQuery[SelectedOrder]); + + /***** Free allocated memory for subquery *****/ + free (HiddenSubQuery); + + return NumGames; + } + +/*****************************************************************************/ +/********************** Get game data using its code *************************/ +/*****************************************************************************/ + +unsigned Gam_DB_GetDataOfGameByCod (MYSQL_RES **mysql_res,long GamCod) + { + return (unsigned) + DB_QuerySELECT (mysql_res,"can not get game data", + "SELECT gam_games.GamCod," // row[0] + "gam_games.CrsCod," // row[1] + "gam_games.Hidden," // row[2] + "gam_games.UsrCod," // row[3] + "gam_games.MaxGrade," // row[4] + "gam_games.Visibility," // row[5] + "gam_games.Title" // row[6] + " FROM gam_games" + " LEFT JOIN mch_matches" + " ON gam_games.GamCod=mch_matches.GamCod" + " WHERE gam_games.GamCod=%ld" + " AND gam_games.CrsCod='%ld'", // Extra check + GamCod, + Gbl.Hierarchy.Crs.CrsCod); + } + +/*****************************************************************************/ +/******************* Get number of questions of a game *********************/ +/*****************************************************************************/ + +unsigned Gam_DB_GetNumQstsGame (long GamCod) + { + /***** Get nuumber of questions in a game from database *****/ + return (unsigned) + DB_QueryCOUNT ("can not get number of questions of a game", + "SELECT COUNT(*)" + " FROM gam_questions" + " WHERE GamCod=%ld", + GamCod); + } + +/*****************************************************************************/ +/*********** Get previous question index to a given index in a game **********/ +/*****************************************************************************/ +// 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 + +unsigned Gam_DB_GetPrevQuestionIndexInGame (long GamCod,unsigned QstInd) + { + /***** Get previous question index in a game from database *****/ + // Although indexes are always continuous... + // ...this implementation works even with non continuous indexes + return DB_QuerySELECTUnsigned ("can not get previous question index", + "SELECT COALESCE(MAX(QstInd),0)" + " FROM gam_questions" + " WHERE GamCod=%ld" + " AND QstInd<%u", + GamCod, + QstInd); + } + +/*****************************************************************************/ +/************* Get next question index to a given index in a game ************/ +/*****************************************************************************/ +// 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 big number if no next question + +unsigned Gam_DB_GetNextQuestionIndexInGame (long GamCod,unsigned QstInd) + { + /***** Get next question index in a game from database *****/ + // Although indexes are always continuous... + // ...this implementation works even with non continuous indexes + return DB_QuerySELECTUnsigned ("can not get next question index", + "SELECT COALESCE(MIN(QstInd),%u)" + " FROM gam_questions" + " WHERE GamCod=%ld" + " AND QstInd>%u", + Gam_AFTER_LAST_QUESTION, // End of questions has been reached + GamCod, + QstInd); + } + +/*****************************************************************************/ +/********************* Get number of courses with games **********************/ +/*****************************************************************************/ +// Returns the number of courses with games in this location + +unsigned Gam_DB_GetNumCoursesWithGames (HieLvl_Level_t Scope) + { + /***** Get number of courses with games from database *****/ + switch (Scope) + { + case HieLvl_SYS: + return (unsigned) + DB_QueryCOUNT ("can not get number of courses with games", + "SELECT COUNT(DISTINCT CrsCod)" + " FROM gam_games"); + case HieLvl_CTY: + return (unsigned) + DB_QueryCOUNT ("can not get number of courses with games", + "SELECT COUNT(DISTINCT gam_games.CrsCod)" + " FROM ins_instits," + "ctr_centers," + "deg_degrees," + "crs_courses," + "gam_games" + " WHERE ins_instits.CtyCod=%ld" + " AND ins_instits.InsCod=ctr_centers.InsCod" + " AND ctr_centers.CtrCod=deg_degrees.CtrCod" + " AND deg_degrees.DegCod=crs_courses.DegCod" + " AND crs_courses.CrsCod=gam_games.CrsCod", + Gbl.Hierarchy.Ins.InsCod); + case HieLvl_INS: + return (unsigned) + DB_QueryCOUNT ("can not get number of courses with games", + "SELECT COUNT(DISTINCT gam_games.CrsCod)" + " FROM ctr_centers," + "deg_degrees," + "crs_courses," + "gam_games" + " WHERE ctr_centers.InsCod=%ld" + " AND ctr_centers.CtrCod=deg_degrees.CtrCod" + " AND deg_degrees.DegCod=crs_courses.DegCod" + " AND crs_courses.CrsCod=gam_games.CrsCod", + Gbl.Hierarchy.Ins.InsCod); + case HieLvl_CTR: + return (unsigned) + DB_QueryCOUNT ("can not get number of courses with games", + "SELECT COUNT(DISTINCT gam_games.CrsCod)" + " FROM deg_degrees," + "crs_courses," + "gam_games" + " WHERE deg_degrees.CtrCod=%ld" + " AND deg_degrees.DegCod=crs_courses.DegCod" + " AND crs_courses.CrsCod=gam_games.CrsCod", + Gbl.Hierarchy.Ctr.CtrCod); + case HieLvl_DEG: + return (unsigned) + DB_QueryCOUNT ("can not get number of courses with games", + "SELECT COUNT(DISTINCT gam_games.CrsCod)" + " FROM crs_courses," + "gam_games" + " WHERE crs_courses.DegCod=%ld" + " AND crs_courses.CrsCod=gam_games.CrsCod", + Gbl.Hierarchy.Deg.DegCod); + case HieLvl_CRS: + return (unsigned) + DB_QueryCOUNT ("can not get number of courses with games", + "SELECT COUNT(DISTINCT CrsCod)" + " FROM gam_games" + " WHERE CrsCod=%ld", + Gbl.Hierarchy.Crs.CrsCod); + default: + return 0; + } + } + +/*****************************************************************************/ +/**************************** Get number of games ****************************/ +/*****************************************************************************/ +// Returns the number of games in this location + +unsigned Gam_DB_GetNumGames (HieLvl_Level_t Scope) + { + /***** Get number of games from database *****/ + switch (Scope) + { + case HieLvl_SYS: + return (unsigned) + DB_QueryCOUNT ("can not get number of games", + "SELECT COUNT(*)" + " FROM gam_games"); + case HieLvl_CTY: + return (unsigned) + DB_QueryCOUNT ("can not get number of games", + "SELECT COUNT(*)" + " FROM ins_instits," + "ctr_centers," + "deg_degrees," + "crs_courses," + "gam_games" + " WHERE ins_instits.CtyCod=%ld" + " AND ins_instits.InsCod=ctr_centers.InsCod" + " AND ctr_centers.CtrCod=deg_degrees.CtrCod" + " AND deg_degrees.DegCod=crs_courses.DegCod" + " AND crs_courses.CrsCod=gam_games.CrsCod", + Gbl.Hierarchy.Cty.CtyCod); + case HieLvl_INS: + return (unsigned) + DB_QueryCOUNT ("can not get number of games", + "SELECT COUNT(*)" + " FROM ctr_centers," + "deg_degrees," + "crs_courses," + "gam_games" + " WHERE ctr_centers.InsCod=%ld" + " AND ctr_centers.CtrCod=deg_degrees.CtrCod" + " AND deg_degrees.DegCod=crs_courses.DegCod" + " AND crs_courses.CrsCod=gam_games.CrsCod", + Gbl.Hierarchy.Ins.InsCod); + case HieLvl_CTR: + return (unsigned) + DB_QueryCOUNT ("can not get number of games", + "SELECT COUNT(*)" + " FROM deg_degrees," + "crs_courses," + "gam_games" + " WHERE deg_degrees.CtrCod=%ld" + " AND deg_degrees.DegCod=crs_courses.DegCod" + " AND crs_courses.CrsCod=gam_games.CrsCod", + Gbl.Hierarchy.Ctr.CtrCod); + case HieLvl_DEG: + return (unsigned) + DB_QueryCOUNT ("can not get number of games", + "SELECT COUNT(*)" + " FROM crs_courses," + "gam_games" + " WHERE crs_courses.DegCod=%ld" + " AND crs_courses.CrsCod=gam_games.CrsCod", + Gbl.Hierarchy.Deg.DegCod); + case HieLvl_CRS: + return (unsigned) + DB_QueryCOUNT ("can not get number of games", + "SELECT COUNT(*)" + " FROM gam_games" + " WHERE CrsCod=%ld", + Gbl.Hierarchy.Crs.CrsCod); + default: + return 0; + } + } + +/*****************************************************************************/ +/***************** Get average number of questions per game ******************/ +/*****************************************************************************/ + +double Gam_DB_GetNumQstsPerGame (HieLvl_Level_t Scope) + { + /***** Get number of questions per game from database *****/ + switch (Scope) + { + case HieLvl_SYS: + return + DB_QuerySELECTDouble ("can not get number of questions per game", + "SELECT AVG(NumQsts)" + " FROM (SELECT COUNT(gam_questions.QstCod) AS NumQsts" + " FROM gam_games," + "gam_questions" + " WHERE gam_games.GamCod=gam_questions.GamCod" + " GROUP BY gam_questions.GamCod) AS NumQstsTable"); + case HieLvl_CTY: + return + DB_QuerySELECTDouble ("can not get number of questions per game", + "SELECT AVG(NumQsts)" + " FROM (SELECT COUNT(gam_questions.QstCod) AS NumQsts" + " FROM ins_instits," + "ctr_centers," + "deg_degrees," + "crs_courses," + "gam_games," + "gam_questions" + " WHERE ins_instits.CtyCod=%ld" + " AND ins_instits.InsCod=ctr_centers.InsCod" + " AND ctr_centers.CtrCod=deg_degrees.CtrCod" + " AND deg_degrees.DegCod=crs_courses.DegCod" + " AND crs_courses.CrsCod=gam_games.CrsCod" + " AND gam_games.GamCod=gam_questions.GamCod" + " GROUP BY gam_questions.GamCod) AS NumQstsTable", + Gbl.Hierarchy.Cty.CtyCod); + case HieLvl_INS: + return + DB_QuerySELECTDouble ("can not get number of questions per game", + "SELECT AVG(NumQsts)" + " FROM (SELECT COUNT(gam_questions.QstCod) AS NumQsts" + " FROM ctr_centers," + "deg_degrees," + "crs_courses," + "gam_games," + "gam_questions" + " WHERE ctr_centers.InsCod=%ld" + " AND ctr_centers.CtrCod=deg_degrees.CtrCod" + " AND deg_degrees.DegCod=crs_courses.DegCod" + " AND crs_courses.CrsCod=gam_games.CrsCod" + " AND gam_games.GamCod=gam_questions.GamCod" + " GROUP BY gam_questions.GamCod) AS NumQstsTable", + Gbl.Hierarchy.Ins.InsCod); + case HieLvl_CTR: + return + DB_QuerySELECTDouble ("can not get number of questions per game", + "SELECT AVG(NumQsts)" + " FROM (SELECT COUNT(gam_questions.QstCod) AS NumQsts" + " FROM deg_degrees," + "crs_courses," + "gam_games," + "gam_questions" + " WHERE deg_degrees.CtrCod=%ld" + " AND deg_degrees.DegCod=crs_courses.DegCod" + " AND crs_courses.CrsCod=gam_games.CrsCod" + " AND gam_games.GamCod=gam_questions.GamCod" + " GROUP BY gam_questions.GamCod) AS NumQstsTable", + Gbl.Hierarchy.Ctr.CtrCod); + case HieLvl_DEG: + return + DB_QuerySELECTDouble ("can not get number of questions per game", + "SELECT AVG(NumQsts)" + " FROM (SELECT COUNT(gam_questions.QstCod) AS NumQsts" + " FROM crs_courses," + "gam_games," + "gam_questions" + " WHERE crs_courses.DegCod=%ld" + " AND crs_courses.CrsCod=gam_games.CrsCod" + " AND gam_games.GamCod=gam_questions.GamCod" + " GROUP BY gam_questions.GamCod) AS NumQstsTable", + Gbl.Hierarchy.Deg.DegCod); + case HieLvl_CRS: + return + DB_QuerySELECTDouble ("can not get number of questions per game", + "SELECT AVG(NumQsts)" + " FROM (SELECT COUNT(gam_questions.QstCod) AS NumQsts" + " FROM gam_games," + "gam_questions" + " WHERE gam_games.Cod=%ld" + " AND gam_games.GamCod=gam_questions.GamCod" + " GROUP BY gam_questions.GamCod) AS NumQstsTable", + Gbl.Hierarchy.Crs.CrsCod); + default: + Err_WrongScopeExit (); + return 0.0; // Not reached + } + } diff --git a/swad_game_database.h b/swad_game_database.h new file mode 100644 index 00000000..837fa8f3 --- /dev/null +++ b/swad_game_database.h @@ -0,0 +1,53 @@ +// swad_game_database.h: games using remote control, operations with database + +#ifndef _SWAD_GAM_DB +#define _SWAD_GAM_DB +/* + 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-2021 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_date.h" +#include "swad_hierarchy_level.h" +// #include "swad_scope.h" + +/*****************************************************************************/ +/************************** Public types and constants ***********************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/***************************** Public prototypes *****************************/ +/*****************************************************************************/ + +unsigned Gam_DB_GetListGames (MYSQL_RES **mysql_res,Gam_Order_t SelectedOrder); +unsigned Gam_DB_GetDataOfGameByCod (MYSQL_RES **mysql_res,long GamCod); +unsigned Gam_DB_GetNumQstsGame (long GamCod); + +unsigned Gam_DB_GetPrevQuestionIndexInGame (long GamCod,unsigned QstInd); +unsigned Gam_DB_GetNextQuestionIndexInGame (long GamCod,unsigned QstInd); + +unsigned Gam_DB_GetNumCoursesWithGames (HieLvl_Level_t Scope); +unsigned Gam_DB_GetNumGames (HieLvl_Level_t Scope); +double Gam_DB_GetNumQstsPerGame (HieLvl_Level_t Scope); + +#endif diff --git a/swad_match.c b/swad_match.c index 18187f02..f66facfa 100644 --- a/swad_match.c +++ b/swad_match.c @@ -37,6 +37,7 @@ #include "swad_error.h" #include "swad_form.h" #include "swad_game.h" +#include "swad_game_database.h" #include "swad_global.h" #include "swad_group_database.h" #include "swad_HTML.h" @@ -142,7 +143,7 @@ static void Mch_DB_RemoveMatchFromTable (long MchCod,const char *TableName); static void Mch_DB_RemoveMatchesInGameFromTable (long GamCod,const char *TableName); static void Mch_DB_RemoveMatchesInCourseFromTable (long CrsCod,const char *TableName); static void Mch_DB_RemoveMatchesMadeByUsrFromTable (long UsrCod,const char *TableName); -static void Mch_RemoveMatchesMadeByUsrInCrsFromTable (long UsrCod,long CrsCod,const char *TableName); +static void Mch_DB_RemoveMatchesMadeByUsrInCrsFromTable (long UsrCod,long CrsCod,const char *TableName); static void Mch_PutParamsPlay (void *MchCod); static void Mch_PutParamMchCod (long MchCod); @@ -234,17 +235,17 @@ static void Mch_PutBigButtonClose (void); static void Mch_ShowWaitImage (const char *Txt); -static void Mch_RemoveOldPlayers (void); -static void Mch_UpdateMatchAsBeingPlayed (long MchCod); -static void Mch_SetMatchAsNotBeingPlayed (long MchCod); -static bool Mch_GetIfMatchIsBeingPlayed (long MchCod); -static void Mch_GetNumPlayers (struct Mch_Match *Match); +static void Mch_DB_RemoveOldPlayers (void); +static void Mch_DB_UpdateMatchAsBeingPlayed (long MchCod); +static void Mch_DB_SetMatchAsNotBeingPlayed (long MchCod); +static bool Mch_DB_GetIfMatchIsBeingPlayed (long MchCod); +static void Mch_DB_GetNumPlayers (struct Mch_Match *Match); -static void Mch_UpdateMyAnswerToMatchQuestion (const struct Mch_Match *Match, - const struct Mch_UsrAnswer *UsrAnswer); -static void Mch_RemoveMyAnswerToMatchQuestion (const struct Mch_Match *Match); +static void Mch_DB_UpdateMyAnswerToMatchQuestion (const struct Mch_Match *Match, + const struct Mch_UsrAnswer *UsrAnswer); +static void Mch_DB_RemoveMyAnswerToMatchQuestion (const struct Mch_Match *Match); -static unsigned Mch_GetNumUsrsWhoHavePlayedMch (long MchCod); +static unsigned Mch_DB_GetNumUsrsWhoHavePlayedMch (long MchCod); /*****************************************************************************/ /*************** Set/Get match code of the match being played ****************/ @@ -837,7 +838,7 @@ static void Mch_ListOneOrMoreMatchesNumPlayers (const struct Mch_Match *Match) { /***** Number of players who have answered any question in the match ******/ HTM_TD_Begin ("class=\"DAT RT COLOR%u\"",Gbl.RowEvenOdd); - HTM_Unsigned (Mch_GetNumUsrsWhoHavePlayedMch (Match->MchCod)); + HTM_Unsigned (Mch_DB_GetNumUsrsWhoHavePlayedMch (Match->MchCod)); HTM_TD_End (); } @@ -1080,7 +1081,7 @@ static void Mch_GetMatchDataFromRow (MYSQL_RES *mysql_res, if (Match->Status.Showing == Mch_END) // Match over Match->Status.Playing = false; else // Match not over - Match->Status.Playing = Mch_GetIfMatchIsBeingPlayed (Match->MchCod); + Match->Status.Playing = Mch_DB_GetIfMatchIsBeingPlayed (Match->MchCod); } /*****************************************************************************/ @@ -1315,12 +1316,12 @@ static void Mch_DB_RemoveMatchesMadeByUsrFromTable (long UsrCod,const char *Tabl void Mch_RemoveMatchesMadeByUsrInCrs (long UsrCod,long CrsCod) { /***** Remove student from secondary tables *****/ - Mch_RemoveMatchesMadeByUsrInCrsFromTable (UsrCod,CrsCod,"mch_players"); - Mch_RemoveMatchesMadeByUsrInCrsFromTable (UsrCod,CrsCod,"mch_results"); - Mch_RemoveMatchesMadeByUsrInCrsFromTable (UsrCod,CrsCod,"mch_answers"); + Mch_DB_RemoveMatchesMadeByUsrInCrsFromTable (UsrCod,CrsCod,"mch_players"); + Mch_DB_RemoveMatchesMadeByUsrInCrsFromTable (UsrCod,CrsCod,"mch_results"); + Mch_DB_RemoveMatchesMadeByUsrInCrsFromTable (UsrCod,CrsCod,"mch_answers"); } -static void Mch_RemoveMatchesMadeByUsrInCrsFromTable (long UsrCod,long CrsCod,const char *TableName) +static void Mch_DB_RemoveMatchesMadeByUsrInCrsFromTable (long UsrCod,long CrsCod,const char *TableName) { /***** Remove matches in course from secondary table *****/ DB_QueryDELETE ("can not remove matches of a user from table", @@ -1727,7 +1728,7 @@ void Mch_ResumeMatch (void) /***** Remove old players. This function must be called by a teacher before getting match status. *****/ - Mch_RemoveOldPlayers (); + Mch_DB_RemoveOldPlayers (); /***** Get data of the match from database *****/ Match.MchCod = Mch_GetMchCodBeingPlayed (); @@ -2053,10 +2054,10 @@ static void Mch_UpdateMatchStatusInDB (const struct Mch_Match *Match) if (Match->Status.Playing) // Match is being played /* Update match as being played */ - Mch_UpdateMatchAsBeingPlayed (Match->MchCod); + Mch_DB_UpdateMatchAsBeingPlayed (Match->MchCod); else // Match is paused, not being played /* Update match as not being played */ - Mch_SetMatchAsNotBeingPlayed (Match->MchCod); + Mch_DB_SetMatchAsNotBeingPlayed (Match->MchCod); } /*****************************************************************************/ @@ -2179,7 +2180,7 @@ void Mch_PlayPauseMatch (void) /***** Remove old players. This function must be called by a teacher before getting match status. *****/ - Mch_RemoveOldPlayers (); + Mch_DB_RemoveOldPlayers (); /***** Get data of the match from database *****/ Match.MchCod = Mch_GetMchCodBeingPlayed (); @@ -2216,7 +2217,7 @@ void Mch_ChangeNumColsMch (void) /***** Remove old players. This function must be called by a teacher before getting match status. *****/ - Mch_RemoveOldPlayers (); + Mch_DB_RemoveOldPlayers (); /***** Get data of the match from database *****/ Match.MchCod = Mch_GetMchCodBeingPlayed (); @@ -2252,7 +2253,7 @@ void Mch_ToggleVisResultsMchQst (void) /***** Remove old players. This function must be called by a teacher before getting match status. *****/ - Mch_RemoveOldPlayers (); + Mch_DB_RemoveOldPlayers (); /***** Get data of the match from database *****/ Match.MchCod = Mch_GetMchCodBeingPlayed (); @@ -2287,7 +2288,7 @@ void Mch_BackMatch (void) /***** Remove old players. This function must be called by a teacher before getting match status. *****/ - Mch_RemoveOldPlayers (); + Mch_DB_RemoveOldPlayers (); /***** Get data of the match from database *****/ Match.MchCod = Mch_GetMchCodBeingPlayed (); @@ -2319,7 +2320,7 @@ void Mch_ForwardMatch (void) /***** Remove old players. This function must be called by a teacher before getting match status. *****/ - Mch_RemoveOldPlayers (); + Mch_DB_RemoveOldPlayers (); /***** Get data of the match from database *****/ Match.MchCod = Mch_GetMchCodBeingPlayed (); @@ -2507,7 +2508,7 @@ static void Mch_ShowMatchStatusForStd (struct Mch_Match *Match,Mch_Update_t Upda /********************** Get number of matches in a game **********************/ /*****************************************************************************/ -unsigned Mch_GetNumMchsInGame (long GamCod) +unsigned Mch_DB_GetNumMchsInGame (long GamCod) { /***** Trivial check *****/ if (GamCod < 0) // A non-existing game... @@ -2526,7 +2527,7 @@ unsigned Mch_GetNumMchsInGame (long GamCod) /*************** Get number of unfinished matches in a game ******************/ /*****************************************************************************/ -unsigned Mch_GetNumUnfinishedMchsInGame (long GamCod) +unsigned Mch_DB_GetNumUnfinishedMchsInGame (long GamCod) { /***** Trivial check *****/ if (GamCod < 0) // A non-existing game... @@ -2703,7 +2704,7 @@ static void Mch_WriteNumRespondersQst (struct Mch_Match *Match) HTM_Hyphen (); // Do not write number of responders break; default: - HTM_Unsigned (Mch_GetNumUsrsWhoAnsweredQst (Match->MchCod, + HTM_Unsigned (Mch_DB_GetNumUsrsWhoAnsweredQst (Match->MchCod, Match->Status.QstInd)); break; } @@ -2712,7 +2713,7 @@ static void Mch_WriteNumRespondersQst (struct Mch_Match *Match) if (Match->Status.Playing) // Match is being played { /* Get current number of players */ - Mch_GetNumPlayers (Match); + Mch_DB_GetNumPlayers (Match); /* Show current number of players */ HTM_TxtF ("/%u",Match->Status.NumPlayers); @@ -3363,7 +3364,7 @@ static void Mch_WriteChoiceAnsViewMatch (const struct Mch_Match *Match, unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]; // Indexes of all answers of this question /***** Get number of users who have answered this question from database *****/ - NumRespondersQst = Mch_GetNumUsrsWhoAnsweredQst (Match->MchCod,Match->Status.QstInd); + NumRespondersQst = Mch_DB_GetNumUsrsWhoAnsweredQst (Match->MchCod,Match->Status.QstInd); /***** Change format of answers text *****/ Tst_ChangeFormatAnswersText (Question); @@ -3406,7 +3407,7 @@ static void Mch_WriteChoiceAnsViewMatch (const struct Mch_Match *Match, if (ShowResult) { /* Get number of users who selected this answer */ - NumRespondersAns = Mch_GetNumUsrsWhoHaveChosenAns (Match->MchCod,Match->Status.QstInd,Indexes[NumOpt]); + NumRespondersAns = Mch_DB_GetNumUsrsWhoHaveChosenAns (Match->MchCod,Match->Status.QstInd,Indexes[NumOpt]); /* Draw proportional bar for this answer */ Mch_DrawBarNumUsrs (NumRespondersAns,NumRespondersQst, @@ -3811,7 +3812,7 @@ static void Mch_ShowWaitImage (const char *Txt) /**************************** Remove old players *****************************/ /*****************************************************************************/ -static void Mch_RemoveOldPlayers (void) +static void Mch_DB_RemoveOldPlayers (void) { /***** Delete matches not being played by teacher *****/ DB_QueryDELETE ("can not update matches as not being played", @@ -3830,7 +3831,7 @@ static void Mch_RemoveOldPlayers (void) /********************** Update match as being played *************************/ /*****************************************************************************/ -static void Mch_UpdateMatchAsBeingPlayed (long MchCod) +static void Mch_DB_UpdateMatchAsBeingPlayed (long MchCod) { /***** Insert match as being played *****/ DB_QueryREPLACE ("can not set match as being played", @@ -3845,7 +3846,7 @@ static void Mch_UpdateMatchAsBeingPlayed (long MchCod) /**************** Update match as paused, not being played *******************/ /*****************************************************************************/ -static void Mch_SetMatchAsNotBeingPlayed (long MchCod) +static void Mch_DB_SetMatchAsNotBeingPlayed (long MchCod) { /***** Delete all match players ******/ DB_QueryDELETE ("can not update match players", @@ -3864,7 +3865,7 @@ static void Mch_SetMatchAsNotBeingPlayed (long MchCod) /*********************** Get if match is being played ************************/ /*****************************************************************************/ -static bool Mch_GetIfMatchIsBeingPlayed (long MchCod) +static bool Mch_DB_GetIfMatchIsBeingPlayed (long MchCod) { /***** Get if a match is being played or not *****/ return (DB_QueryCOUNT ("can not get if match is being played", @@ -3878,7 +3879,7 @@ static bool Mch_GetIfMatchIsBeingPlayed (long MchCod) /*************************** Get number of players ***************************/ /*****************************************************************************/ -static void Mch_GetNumPlayers (struct Mch_Match *Match) +static void Mch_DB_GetNumPlayers (struct Mch_Match *Match) { /***** Get number of players who are playing a match *****/ Match->Status.NumPlayers = (unsigned) @@ -4000,7 +4001,7 @@ void Mch_RemoveMyQuestionAnswer (const struct Mch_Match *Match,unsigned QstInd) QstInd == Match->Status.QstInd) // Removing answer to the current question being played { /***** Remove my answer to this question *****/ - Mch_RemoveMyAnswerToMatchQuestion (Match); + Mch_DB_RemoveMyAnswerToMatchQuestion (Match); /***** Compute score and update my match result *****/ MchPrn_ComputeScoreAndUpdateMyMatchPrintInDB (Match->MchCod); @@ -4025,7 +4026,7 @@ void Mch_StartCountdown (void) /***** Remove old players. This function must be called by a teacher before getting match status. *****/ - Mch_RemoveOldPlayers (); + Mch_DB_RemoveOldPlayers (); /***** Get data of the match from database *****/ Match.MchCod = Mch_GetMchCodBeingPlayed (); @@ -4059,7 +4060,7 @@ void Mch_RefreshMatchTch (void) /***** Remove old players. This function must be called by a teacher before getting match status. *****/ - Mch_RemoveOldPlayers (); + Mch_DB_RemoveOldPlayers (); /***** Get data of the match from database *****/ Match.MchCod = Mch_GetMchCodBeingPlayed (); @@ -4248,7 +4249,7 @@ void Mch_StoreQuestionAnswer (const struct Mch_Match *Match,unsigned QstInd, UsrAnswer->AnsInd != PreviousUsrAnswer.AnsInd) { /***** Update my answer to this question *****/ - Mch_UpdateMyAnswerToMatchQuestion (Match,UsrAnswer); + Mch_DB_UpdateMyAnswerToMatchQuestion (Match,UsrAnswer); /***** Compute score and update my match result *****/ MchPrn_ComputeScoreAndUpdateMyMatchPrintInDB (Match->MchCod); @@ -4260,8 +4261,8 @@ void Mch_StoreQuestionAnswer (const struct Mch_Match *Match,unsigned QstInd, /******************** Update my answer to match question *********************/ /*****************************************************************************/ -static void Mch_UpdateMyAnswerToMatchQuestion (const struct Mch_Match *Match, - const struct Mch_UsrAnswer *UsrAnswer) +static void Mch_DB_UpdateMyAnswerToMatchQuestion (const struct Mch_Match *Match, + const struct Mch_UsrAnswer *UsrAnswer) { DB_QueryREPLACE ("can not register your answer to the match question", "REPLACE mch_answers" @@ -4279,7 +4280,7 @@ static void Mch_UpdateMyAnswerToMatchQuestion (const struct Mch_Match *Match, /******************* Remove my answer to match question **********************/ /*****************************************************************************/ -static void Mch_RemoveMyAnswerToMatchQuestion (const struct Mch_Match *Match) +static void Mch_DB_RemoveMyAnswerToMatchQuestion (const struct Mch_Match *Match) { DB_QueryDELETE ("can not remove your answer to the match question", "DELETE FROM mch_answers" @@ -4384,7 +4385,7 @@ void Mch_ComputeScore (struct MchPrn_Print *Print) /********** Get number of users who answered a question in a match ***********/ /*****************************************************************************/ -unsigned Mch_GetNumUsrsWhoAnsweredQst (long MchCod,unsigned QstInd) +unsigned Mch_DB_GetNumUsrsWhoAnsweredQst (long MchCod,unsigned QstInd) { /***** Get number of users who answered a question in a match from database *****/ @@ -4402,7 +4403,7 @@ unsigned Mch_GetNumUsrsWhoAnsweredQst (long MchCod,unsigned QstInd) /*** Get number of users who have chosen a given answer of a game question ***/ /*****************************************************************************/ -unsigned Mch_GetNumUsrsWhoHaveChosenAns (long MchCod,unsigned QstInd,unsigned AnsInd) +unsigned Mch_DB_GetNumUsrsWhoHaveChosenAns (long MchCod,unsigned QstInd,unsigned AnsInd) { /***** Get number of users who have chosen an answer of a question from database *****/ @@ -4422,7 +4423,7 @@ unsigned Mch_GetNumUsrsWhoHaveChosenAns (long MchCod,unsigned QstInd,unsigned An /************ Get number of users who have played a given match **************/ /*****************************************************************************/ -static unsigned Mch_GetNumUsrsWhoHavePlayedMch (long MchCod) +static unsigned Mch_DB_GetNumUsrsWhoHavePlayedMch (long MchCod) { /***** Get number of users who have played the match (users who have a result for this match, even blank result) @@ -4483,3 +4484,18 @@ void Mch_DrawBarNumUsrs (unsigned NumRespondersAns,unsigned NumRespondersQst,boo /***** End container *****/ HTM_DIV_End (); } + +/*****************************************************************************/ +/********* Get start of first match and end of last match in a game **********/ +/*****************************************************************************/ + +unsigned Mch_DB_GetStartEndMatchesInGame (MYSQL_RES **mysql_res,long GamCod) + { + return (unsigned) + DB_QuerySELECT (mysql_res,"can not get game data", + "SELECT UNIX_TIMESTAMP(MIN(StartTime))," // row[0] + "UNIX_TIMESTAMP(MAX(EndTime))" // row[1] + " FROM mch_matches" + " WHERE GamCod=%ld", + GamCod); + } diff --git a/swad_match.h b/swad_match.h index 000966e4..8d83da13 100644 --- a/swad_match.h +++ b/swad_match.h @@ -128,8 +128,8 @@ void Mch_ToggleVisResultsMchQst (void); void Mch_BackMatch (void); void Mch_ForwardMatch (void); -unsigned Mch_GetNumMchsInGame (long GamCod); -unsigned Mch_GetNumUnfinishedMchsInGame (long GamCod); +unsigned Mch_DB_GetNumMchsInGame (long GamCod); +unsigned Mch_DB_GetNumUnfinishedMchsInGame (long GamCod); bool Mch_CheckIfICanPlayThisMatchBasedOnGrps (const struct Mch_Match *Match); @@ -154,8 +154,10 @@ void Mch_GetMatchQuestionsFromDB (struct MchPrn_Print *Print); void Mch_ComputeScore (struct MchPrn_Print *Print); -unsigned Mch_GetNumUsrsWhoAnsweredQst (long MchCod,unsigned QstInd); -unsigned Mch_GetNumUsrsWhoHaveChosenAns (long MchCod,unsigned QstInd,unsigned AnsInd); +unsigned Mch_DB_GetNumUsrsWhoAnsweredQst (long MchCod,unsigned QstInd); +unsigned Mch_DB_GetNumUsrsWhoHaveChosenAns (long MchCod,unsigned QstInd,unsigned AnsInd); void Mch_DrawBarNumUsrs (unsigned NumRespondersAns,unsigned NumRespondersQst,bool Correct); +unsigned Mch_DB_GetStartEndMatchesInGame (MYSQL_RES **mysql_res,long GamCod); + #endif