diff --git a/Makefile b/Makefile index 08ae6b73..9caa948e 100644 --- a/Makefile +++ b/Makefile @@ -63,8 +63,9 @@ OBJS = swad_account.o swad_account_database.o swad_action.o swad_admin.o \ swad_log.o swad_log_database.o swad_logo.o \ swad_MAC.o swad_mail.o swad_mail_database.o swad_main.o \ swad_maintenance.o swad_map.o swad_mark.o swad_mark_database.o \ - swad_match.o swad_match_print.o swad_match_result.o swad_media.o \ - swad_media_database.o swad_menu.o swad_message.o swad_MFU.o \ + swad_match.o swad_match_database.o swad_match_print.o \ + swad_match_result.o swad_media.o swad_media_database.o swad_menu.o \ + swad_message.o swad_MFU.o \ swad_network.o swad_nickname.o swad_notice.o swad_notification.o \ swad_notification_database.o swad_pagination.o swad_parameter.o \ swad_password.o swad_photo.o swad_place.o swad_plugin.o swad_privacy.o \ diff --git a/swad_changelog.h b/swad_changelog.h index 5c5ea9a1..d2a37243 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.15 (2021-09-24)" +#define Log_PLATFORM_VERSION "SWAD 21.16 (2021-09-24)" #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.16: Sep 24, 2021 New module swad_match_database for database queries related to game matches. (316995 lines) Version 21.15: Sep 24, 2021 New module swad_mark_database for database queries related to files of marks. (316792 lines) Version 21.14.1: Sep 23, 2021 Queries moved to module swad_mail_database. (316687 lines) Version 21.14: Sep 22, 2021 New module swad_mail_database for database queries related to mail domains. (316594 lines) diff --git a/swad_exam_database.c b/swad_exam_database.c index 84b0a79b..4846d4bb 100644 --- a/swad_exam_database.c +++ b/swad_exam_database.c @@ -1750,7 +1750,7 @@ void Exa_DB_RemoveAllGrpsOfType (long GrpTypCod) /******************** Remove one group from all sessions *********************/ /*****************************************************************************/ -void Exa_DB_RemoveGrpAssociatedToExamSess (long GrpCod) +void Exa_DB_RemoveGroup (long GrpCod) { /***** Remove group from all the sessions *****/ DB_QueryDELETE ("can not remove group" diff --git a/swad_exam_database.h b/swad_exam_database.h index 2876d28f..793f6ce7 100644 --- a/swad_exam_database.h +++ b/swad_exam_database.h @@ -135,7 +135,7 @@ void Exa_DB_RemoveAllGrpsFromExa (long ExaCod); void Exa_DB_RemoveAllGrpsFromCrs (long CrsCod); void Exa_DB_RemoveAllGrpsFromSes (long SesCod); void Exa_DB_RemoveAllGrpsOfType (long GrpTypCod); -void Exa_DB_RemoveGrpAssociatedToExamSess (long GrpCod); +void Exa_DB_RemoveGroup (long GrpCod); //---------------------------------- Prints ----------------------------------- long Exa_DB_CreatePrint (const struct ExaPrn_Print *Print); diff --git a/swad_game.c b/swad_game.c index 886e24d1..a676c62b 100644 --- a/swad_game.c +++ b/swad_game.c @@ -43,6 +43,7 @@ #include "swad_hierarchy_level.h" #include "swad_HTML.h" #include "swad_match.h" +#include "swad_match_database.h" #include "swad_match_result.h" #include "swad_pagination.h" #include "swad_role.h" @@ -1654,7 +1655,7 @@ static void Gam_ListGameQuestions (struct Gam_Games *Games,struct Gam_Game *Game bool ICanEditQuestions = Gam_CheckIfEditable (Game); /***** Get data of questions from database *****/ - NumQsts = Gam_DB_GetGameQuestions (&mysql_res,Game->GamCod); + NumQsts = Gam_DB_GetGameQuestionsBasic (&mysql_res,Game->GamCod); /***** Begin box *****/ Games->GamCod = Game->GamCod; @@ -1743,17 +1744,16 @@ static void Gam_ListOneOrMoreQuestionsForEdition (struct Gam_Games *Games, /***** Get question data *****/ row = mysql_fetch_row (mysql_res); /* - row[0] QstInd - row[1] QstCod + row[0] QstCod + row[1] QstInd */ + /* Get question code (row[0]) */ + Question.QstCod = Str_ConvertStrCodToLongCod (row[0]); - /* Get question index (row[0]) */ - QstInd = Str_ConvertStrToUnsigned (row[0]); + /* Get question index (row[1]) */ + QstInd = Str_ConvertStrToUnsigned (row[1]); snprintf (StrQstInd,sizeof (StrQstInd),"%u",QstInd); - /* Get question code (row[1]) */ - Question.QstCod = Str_ConvertStrCodToLongCod (row[1]); - /* Initialize context */ Games->GamCod = GamCod; Games->QstInd = QstInd; diff --git a/swad_game_database.c b/swad_game_database.c index 8f5fb010..9c239068 100644 --- a/swad_game_database.c +++ b/swad_game_database.c @@ -511,22 +511,43 @@ unsigned Gam_DB_GetNumQstsGame (long GamCod) " WHERE GamCod=%ld", GamCod); } + /*****************************************************************************/ /************************ Get the questions of a game ************************/ /*****************************************************************************/ -unsigned Gam_DB_GetGameQuestions (MYSQL_RES **mysql_res,long GamCod) +unsigned Gam_DB_GetGameQuestionsBasic (MYSQL_RES **mysql_res,long GamCod) { return (unsigned) DB_QuerySELECT (mysql_res,"can not get game questions", - "SELECT QstInd," // row[0] - "QstCod" // row[1] + "SELECT QstCod," // row[0] + "QstInd" // row[1] " FROM gam_questions" " WHERE GamCod=%ld" " ORDER BY QstInd", GamCod); } +/*****************************************************************************/ +/************************ Get the questions of a game ************************/ +/*****************************************************************************/ + +unsigned Gam_DB_GetGameQuestionsFull (MYSQL_RES **mysql_res,long GamCod) + { + return (unsigned) + DB_QuerySELECT (mysql_res,"can not get questions of a game", + "SELECT gam_questions.QstCod," // row[0] + "gam_questions.QstInd," // row[1] + "tst_questions.AnsType," // row[2] + "tst_questions.Shuffle" // row[3] + " FROM gam_questions," + "tst_questions" + " WHERE gam_questions.GamCod=%ld" + " AND gam_questions.QstCod=tst_questions.QstCod" + " ORDER BY gam_questions.QstInd", + GamCod); + } + /*****************************************************************************/ /************ Get question code given game and index of question *************/ /*****************************************************************************/ diff --git a/swad_game_database.h b/swad_game_database.h index d107eee7..e338926d 100644 --- a/swad_game_database.h +++ b/swad_game_database.h @@ -61,7 +61,8 @@ void Gam_DB_LockTable (void); void Gam_DB_UnlockTable (void); unsigned Gam_DB_GetNumQstsGame (long GamCod); -unsigned Gam_DB_GetGameQuestions (MYSQL_RES **mysql_res,long GamCod); +unsigned Gam_DB_GetGameQuestionsBasic (MYSQL_RES **mysql_res,long GamCod); +unsigned Gam_DB_GetGameQuestionsFull (MYSQL_RES **mysql_res,long GamCod); long Gam_DB_GetQstCodFromQstInd (long GamCod,unsigned QstInd); unsigned Gam_DB_GetQstIndFromQstCod (long GamCod,long QstCod); unsigned Gam_DB_GetMaxQuestionIndexInGame (long GamCod); diff --git a/swad_group.c b/swad_group.c index 608cf2e8..2f6ae397 100644 --- a/swad_group.c +++ b/swad_group.c @@ -44,7 +44,7 @@ #include "swad_group.h" #include "swad_group_database.h" #include "swad_HTML.h" -#include "swad_match.h" +#include "swad_match_database.h" #include "swad_notification.h" #include "swad_parameter.h" #include "swad_program.h" @@ -3563,7 +3563,7 @@ static void Grp_RemoveGroupCompletely (void) Mch_DB_RemoveGroup (GrpDat.GrpCod); /***** Remove this group from all exam sessions *****/ - Exa_DB_RemoveGrpAssociatedToExamSess (GrpDat.GrpCod); + Exa_DB_RemoveGroup (GrpDat.GrpCod); /***** Remove this group from all surveys *****/ Svy_DB_RemoveGroup (GrpDat.GrpCod); diff --git a/swad_match.c b/swad_match.c index f4589fec..6e10001f 100644 --- a/swad_match.c +++ b/swad_match.c @@ -42,6 +42,7 @@ #include "swad_group_database.h" #include "swad_HTML.h" #include "swad_match.h" +#include "swad_match_database.h" #include "swad_match_result.h" #include "swad_role.h" #include "swad_setting.h" @@ -82,18 +83,6 @@ typedef enum /***************************** Private constants *****************************/ /*****************************************************************************/ -const char *Mch_ShowingStringsDB[Mch_NUM_SHOWING] = - { - [Mch_START ] = "start", - [Mch_STEM ] = "stem", - [Mch_ANSWERS] = "answers", - [Mch_RESULTS] = "results", - [Mch_END ] = "end", - }; - -#define Mch_MAX_COLS 4 -#define Mch_NUM_COLS_DEFAULT 1 - /*****************************************************************************/ /***************************** Private variables *****************************/ /*****************************************************************************/ @@ -136,14 +125,8 @@ static void Mch_ListOneOrMoreMatchesResultTch (struct Gam_Games *Games, static void Mch_GetMatchDataFromRow (MYSQL_RES *mysql_res, struct Mch_Match *Match); -static Mch_Showing_t Mch_GetShowingFromStr (const char *Str); static void Mch_RemoveMatchFromAllTables (long MchCod); -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_DB_RemoveMatchesMadeByUsrInCrsFromTable (long UsrCod,long CrsCod,const char *TableName); static void Mch_PutParamsPlay (void *MchCod); static void Mch_PutParamMchCod (long MchCod); @@ -1060,7 +1043,7 @@ static void Mch_GetMatchDataFromRow (MYSQL_RES *mysql_res, Match->Status.QstCod = Str_ConvertStrCodToLongCod (row[7]); /* Get what to show (stem, answers, results) (row(8)) */ - Match->Status.Showing = Mch_GetShowingFromStr (row[8]); + Match->Status.Showing = Mch_DB_GetShowingFromStr (row[8]); /* Get countdown (row[9]) */ Match->Status.Countdown = Str_ConvertStrCodToLongCod (row[9]); @@ -1084,23 +1067,6 @@ static void Mch_GetMatchDataFromRow (MYSQL_RES *mysql_res, Match->Status.Playing = Mch_DB_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) ***************/ /*****************************************************************************/ @@ -1192,20 +1158,7 @@ static void Mch_RemoveMatchFromAllTables (long MchCod) Mch_DB_RemoveMatchFromTable (MchCod,"mch_indexes"); /***** Remove match from main table *****/ - DB_QueryDELETE ("can not remove match", - "DELETE FROM mch_matches" - " WHERE MchCod=%ld", - MchCod); - } - -static void Mch_DB_RemoveMatchFromTable (long MchCod,const char *TableName) - { - /***** Remove match from secondary table *****/ - DB_QueryDELETE ("can not remove match from table", - "DELETE FROM %s" - " WHERE MchCod=%ld", - TableName, - MchCod); + Mch_DB_RemoveMatchFromTable (MchCod,"mch_matches"); } /*****************************************************************************/ @@ -1215,34 +1168,16 @@ static void Mch_DB_RemoveMatchFromTable (long MchCod,const char *TableName) void Mch_RemoveMatchesInGameFromAllTables (long GamCod) { /***** Remove matches from secondary tables *****/ - Mch_DB_RemoveMatchesInGameFromTable (GamCod,"mch_players"); - Mch_DB_RemoveMatchesInGameFromTable (GamCod,"mch_playing"); - Mch_DB_RemoveMatchesInGameFromTable (GamCod,"mch_results"); - Mch_DB_RemoveMatchesInGameFromTable (GamCod,"mch_answers"); - Mch_DB_RemoveMatchesInGameFromTable (GamCod,"mch_times"); - Mch_DB_RemoveMatchesInGameFromTable (GamCod,"mch_groups"); - Mch_DB_RemoveMatchesInGameFromTable (GamCod,"mch_indexes"); + Mch_DB_RemoveMatchesInGameFromOtherTable (GamCod,"mch_players"); + Mch_DB_RemoveMatchesInGameFromOtherTable (GamCod,"mch_playing"); + Mch_DB_RemoveMatchesInGameFromOtherTable (GamCod,"mch_results"); + Mch_DB_RemoveMatchesInGameFromOtherTable (GamCod,"mch_answers"); + Mch_DB_RemoveMatchesInGameFromOtherTable (GamCod,"mch_times"); + Mch_DB_RemoveMatchesInGameFromOtherTable (GamCod,"mch_groups"); + Mch_DB_RemoveMatchesInGameFromOtherTable (GamCod,"mch_indexes"); - /***** Remove matches from main table *****/ - DB_QueryDELETE ("can not remove matches of a game", - "DELETE FROM mch_matches" - " WHERE GamCod=%ld", - GamCod); - } - -static void Mch_DB_RemoveMatchesInGameFromTable (long GamCod,const char *TableName) - { - /***** Remove matches in game from secondary table *****/ - DB_QueryDELETE ("can not remove matches of a game from table", - "DELETE FROM %s" - " USING mch_matches," - "%s" - " WHERE mch_matches.GamCod=%ld" - " AND mch_matches.MchCod=%s.MchCod", - TableName, - TableName, - GamCod, - TableName); + /***** Remove matches in game from main table *****/ + Mch_DB_RemoveMatchesInGameFromMainTable (GamCod); } /*****************************************************************************/ @@ -1252,39 +1187,16 @@ static void Mch_DB_RemoveMatchesInGameFromTable (long GamCod,const char *TableNa void Mch_RemoveMatchesInCourseFromAllTables (long CrsCod) { /***** Remove matches from secondary tables *****/ - Mch_DB_RemoveMatchesInCourseFromTable (CrsCod,"mch_players"); - Mch_DB_RemoveMatchesInCourseFromTable (CrsCod,"mch_playing"); - Mch_DB_RemoveMatchesInCourseFromTable (CrsCod,"mch_results"); - Mch_DB_RemoveMatchesInCourseFromTable (CrsCod,"mch_answers"); - Mch_DB_RemoveMatchesInCourseFromTable (CrsCod,"mch_times"); - Mch_DB_RemoveMatchesInCourseFromTable (CrsCod,"mch_groups"); - Mch_DB_RemoveMatchesInCourseFromTable (CrsCod,"mch_indexes"); + Mch_DB_RemoveMatchesInCrsFromOtherTable (CrsCod,"mch_players"); + Mch_DB_RemoveMatchesInCrsFromOtherTable (CrsCod,"mch_playing"); + Mch_DB_RemoveMatchesInCrsFromOtherTable (CrsCod,"mch_results"); + Mch_DB_RemoveMatchesInCrsFromOtherTable (CrsCod,"mch_answers"); + Mch_DB_RemoveMatchesInCrsFromOtherTable (CrsCod,"mch_times"); + Mch_DB_RemoveMatchesInCrsFromOtherTable (CrsCod,"mch_groups"); + Mch_DB_RemoveMatchesInCrsFromOtherTable (CrsCod,"mch_indexes"); - /***** Remove matches from main table *****/ - DB_QueryDELETE ("can not remove matches of a course", - "DELETE FROM mch_matches" - " USING gam_games," - "mch_matches" - " WHERE gam_games.CrsCod=%ld" - " AND gam_games.GamCod=mch_matches.GamCod", - CrsCod); - } - -static void Mch_DB_RemoveMatchesInCourseFromTable (long CrsCod,const char *TableName) - { - /***** Remove matches in course from secondary table *****/ - DB_QueryDELETE ("can not remove matches of a course from table", - "DELETE FROM %s" - " USING gam_games," - "mch_matches," - "%s" - " WHERE gam_games.CrsCod=%ld" - " AND gam_games.GamCod=mch_matches.GamCod" - " AND mch_matches.MchCod=%s.MchCod", - TableName, - TableName, - CrsCod, - TableName); + /***** Remove matches in course from main table *****/ + Mch_DB_RemoveMatchesInCrsFromMainTable (CrsCod); } /*****************************************************************************/ @@ -1299,16 +1211,6 @@ void Mch_RemoveMatchesMadeByUsrInAllCrss (long UsrCod) Mch_DB_RemoveMatchesMadeByUsrFromTable (UsrCod,"mch_answers"); } -static void Mch_DB_RemoveMatchesMadeByUsrFromTable (long UsrCod,const char *TableName) - { - /***** Remove matches in course from secondary table *****/ - DB_QueryDELETE ("can not remove matches of a user from table", - "DELETE FROM %s" - " WHERE UsrCod=%ld", - TableName, - UsrCod); - } - /*****************************************************************************/ /***************** Remove matches made by user in a course *******************/ /*****************************************************************************/ @@ -1321,26 +1223,6 @@ void Mch_RemoveMatchesMadeByUsrInCrs (long UsrCod,long CrsCod) Mch_DB_RemoveMatchesMadeByUsrInCrsFromTable (UsrCod,CrsCod,"mch_answers"); } -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", - "DELETE FROM %s" - " USING gam_games," - "mch_matches," - "%s" - " WHERE gam_games.CrsCod=%ld" - " AND gam_games.GamCod=mch_matches.GamCod" - " AND mch_matches.MchCod=%s.MchCod" - " AND %s.UsrCod=%ld", - TableName, - TableName, - CrsCod, - TableName, - TableName, - UsrCod); - } - /*****************************************************************************/ /************************ Edit a match (game instance) ***********************/ /*****************************************************************************/ @@ -1701,12 +1583,7 @@ void Mch_ChangeMatch (void) static void Mch_UpdateMatchTitleAndGrps (const struct Mch_Match *Match) { /***** Update match title into database *****/ - DB_QueryUPDATE ("can not update match", - "UPDATE mch_matches" - " SET Title='%s'" - " WHERE MchCod=%ld", - Match->Title, - Match->MchCod); + Mch_DB_UpdateMatchTitle (Match); /***** Update groups associated to the match *****/ Mch_DB_RemoveMatchFromTable (Match->MchCod,"mch_groups"); // Remove all groups associated to this match @@ -1756,30 +1633,7 @@ static long Mch_CreateMatch (long GamCod,char Title[Mch_MAX_BYTES_TITLE + 1]) long MchCod; /***** Insert this new match into database *****/ - MchCod = - DB_QueryINSERTandReturnCode ("can not create match", - "INSERT mch_matches" - " (GamCod,UsrCod,StartTime,EndTime,Title," - "QstInd,QstCod,Showing,Countdown," - "NumCols,ShowQstResults,ShowUsrResults)" - " VALUES" - " (%ld," // GamCod - "%ld," // UsrCod - "NOW()," // StartTime - "NOW()," // EndTime - "'%s'," // Title - "0," // QstInd: Match has not started, so not the first question yet - "-1," // QstCod: Non-existent question - "'%s'," // Showing: What is being shown - "-1," // Countdown: No countdown - "%u," // NumCols: Number of columns in answers - "'N'," // ShowQstResults: Don't show question results initially - "'N')", // ShowUsrResults: Don't show user results initially - GamCod, - Gbl.Usrs.Me.UsrDat.UsrCod, // Game creator - Title, - Mch_ShowingStringsDB[Mch_SHOWING_DEFAULT], - Mch_NUM_COLS_DEFAULT); + MchCod = Mch_DB_CreateMatch (GamCod,Title); /***** Create indexes for answers *****/ Mch_CreateIndexes (GamCod,MchCod); @@ -1809,18 +1663,7 @@ static void Mch_CreateIndexes (long GamCod,long MchCod) unsigned QstInd; /***** Get questions of the game *****/ - NumQsts = (unsigned) - DB_QuerySELECT (&mysql_res,"can not get questions of a game", - "SELECT gam_questions.QstCod," // row[0] - "gam_questions.QstInd," // row[1] - "tst_questions.AnsType," // row[2] - "tst_questions.Shuffle" // row[3] - " FROM gam_questions," - "tst_questions" - " WHERE gam_questions.GamCod=%ld" - " AND gam_questions.QstCod=tst_questions.QstCod" - " ORDER BY gam_questions.QstInd", - GamCod); + NumQsts = Gam_DB_GetGameQuestionsFull (&mysql_res,GamCod); /***** For each question in game... *****/ for (NumQst = 0; @@ -1883,16 +1726,8 @@ static void Mch_ReorderAnswer (long MchCod,unsigned QstInd, /***** Initialize list of answers to empty string *****/ StrAnswersOneQst[0] = '\0'; - /***** Get questions of the game *****/ - NumAnss = (unsigned) - DB_QuerySELECT (&mysql_res,"can not get questions of a game", - "SELECT AnsInd" // row[0] - " FROM tst_answers" - " WHERE QstCod=%ld" - " ORDER BY %s", - Question->QstCod, - Question->Answer.Shuffle ? "RAND()" : // Use RAND() because is really random; RAND(NOW()) repeats order - "AnsInd"); + /***** Get suffled/not-shuffled answers indexes of question *****/ + NumAnss = Tst_DB_GetShuffledAnswersIndexes (&mysql_res,Question); /***** For each answer in question... *****/ for (NumAns = 0; @@ -1915,14 +1750,7 @@ static void Mch_ReorderAnswer (long MchCod,unsigned QstInd, DB_FreeMySQLResult (&mysql_res); /***** Create entry for this question in table of match indexes *****/ - DB_QueryINSERT ("can not create match indexes", - "INSERT INTO mch_indexes" - " (MchCod,QstInd,Indexes)" - " VALUES" - " (%ld,%u,'%s')", - MchCod, - QstInd, - StrAnswersOneQst); + Mch_DB_CreateQstIndexes (MchCod,QstInd,StrAnswersOneQst); } /*****************************************************************************/ @@ -1935,14 +1763,7 @@ void Mch_GetIndexes (long MchCod,unsigned QstInd, char StrIndexesOneQst[Tst_MAX_BYTES_INDEXES_ONE_QST + 1]; /***** Get indexes for a question from database *****/ - DB_QuerySELECTString (StrIndexesOneQst,sizeof (StrIndexesOneQst) - 1, - "can not get data of a question", - "SELECT Indexes" // row[0] - " FROM mch_indexes" - " WHERE MchCod=%ld" - " AND QstInd=%u", - MchCod, - QstInd); + Mch_DB_GetIndexes (MchCod,QstInd,StrIndexesOneQst); if (!StrIndexesOneQst[0]) Err_ShowErrorAndExit ("No indexes found for a question."); @@ -1958,49 +1779,11 @@ 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 mch_groups" - " (MchCod,GrpCod)" - " VALUES" - " (%ld,%ld)", - MchCod, - Gbl.Crs.Grps.LstGrpsSel.GrpCods[NumGrpSel]); - } - -/*****************************************************************************/ -/********************* Remove one group from all matches *********************/ -/*****************************************************************************/ - -void Mch_DB_RemoveGroup (long GrpCod) - { - /***** Remove group from all the matches *****/ - DB_QueryDELETE ("can not remove group" - " from the associations between matches and groups", - "DELETE FROM mch_groups" - " WHERE GrpCod=%ld", - GrpCod); - } - -/*****************************************************************************/ -/***************** Remove groups of one type from all matches ****************/ -/*****************************************************************************/ - -void Mch_DB_RemoveGroupsOfType (long GrpTypCod) - { - /***** Remove group from all the matches *****/ - DB_QueryDELETE ("can not remove groups of a type" - " from the associations between matches and groups", - "DELETE FROM mch_groups" - " USING grp_groups," - "mch_groups" - " WHERE grp_groups.GrpTypCod=%ld" - " AND grp_groups.GrpCod=mch_groups.GrpCod", - GrpTypCod); + Mch_DB_AssociateGroupToMatch (MchCod, + Gbl.Crs.Grps.LstGrpsSel.GrpCods[NumGrpSel]); } /*****************************************************************************/ @@ -2009,49 +1792,10 @@ void Mch_DB_RemoveGroupsOfType (long GrpTypCod) static void Mch_UpdateMatchStatusInDB (const struct Mch_Match *Match) { - char *MchSubQuery; - - /***** Update end time only if match is currently being played *****/ - if (Match->Status.Playing) // Match is being played - { - if (asprintf (&MchSubQuery,"mch_matches.EndTime=NOW(),") < 0) - Err_NotEnoughMemoryExit (); - } - else // Match is paused, not being played - { - if (asprintf (&MchSubQuery,"%s","") < 0) - Err_NotEnoughMemoryExit (); - } - /***** Update match status in database *****/ - DB_QueryUPDATE ("can not update match being played", - "UPDATE mch_matches," - "gam_games" - " SET %s" - "mch_matches.QstInd=%u," - "mch_matches.QstCod=%ld," - "mch_matches.Showing='%s'," - "mch_matches.Countdown=%ld," - "mch_matches.NumCols=%u," - "mch_matches.ShowQstResults='%c'," - "mch_matches.ShowUsrResults='%c'" - " WHERE mch_matches.MchCod=%ld" - " AND mch_matches.GamCod=gam_games.GamCod" - " AND gam_games.CrsCod=%ld", // Extra check - MchSubQuery, - Match->Status.QstInd, - Match->Status.QstCod, - Mch_ShowingStringsDB[Match->Status.Showing], - Match->Status.Countdown, - Match->Status.NumCols, - Match->Status.ShowQstResults ? 'Y' : - 'N', - Match->Status.ShowUsrResults ? 'Y' : - 'N', - Match->MchCod, - Gbl.Hierarchy.Crs.CrsCod); - free (MchSubQuery); + Mch_DB_UpdateMatchStatus (Match); + /***** Update match as being/not-being played */ if (Match->Status.Playing) // Match is being played /* Update match as being played */ Mch_DB_UpdateMatchAsBeingPlayed (Match->MchCod); @@ -2070,17 +1814,7 @@ static void Mch_UpdateElapsedTimeInQuestion (const struct Mch_Match *Match) if (Match->Status.Playing && // Match is being played Match->Status.Showing != Mch_START && Match->Status.Showing != Mch_END) - DB_QueryINSERT ("can not update elapsed time in question", - "INSERT INTO mch_times" - " (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_MATCH_TCH, - Cfg_SECONDS_TO_REFRESH_MATCH_TCH); + Mch_DB_UpdateElapsedTimeInQuestion (Match->MchCod,Match->Status.QstInd); } /*****************************************************************************/ @@ -2375,11 +2109,11 @@ static void Mch_SetMatchStatusToPrevQst (struct Mch_Match *Match) { /***** Get index of the previous question *****/ Match->Status.QstInd = Gam_DB_GetPrevQuestionIndexInGame (Match->GamCod, - Match->Status.QstInd); + Match->Status.QstInd); if (Match->Status.QstInd) // Start of questions not reached { Match->Status.QstCod = Gam_DB_GetQstCodFromQstInd (Match->GamCod, - Match->Status.QstInd); + Match->Status.QstInd); Match->Status.Showing = Match->Status.ShowQstResults ? Mch_RESULTS : Mch_ANSWERS; } @@ -2438,13 +2172,13 @@ static void Mch_SetMatchStatusToNextQst (struct Mch_Match *Match) { /***** Get index of the next question *****/ Match->Status.QstInd = Gam_DB_GetNextQuestionIndexInGame (Match->GamCod, - Match->Status.QstInd); + Match->Status.QstInd); /***** Get question code *****/ if (Match->Status.QstInd < Gam_AFTER_LAST_QUESTION) // End of questions not reached { Match->Status.QstCod = Gam_DB_GetQstCodFromQstInd (Match->GamCod, - Match->Status.QstInd); + Match->Status.QstInd); Match->Status.Showing = Mch_STEM; } else // End of questions reached @@ -2504,46 +2238,6 @@ static void Mch_ShowMatchStatusForStd (struct Mch_Match *Match,Mch_Update_t Upda Mch_ShowRightColumnStd (Match,&UsrAnswer,Update); } -/*****************************************************************************/ -/********************** Get number of matches in a game **********************/ -/*****************************************************************************/ - -unsigned Mch_DB_GetNumMchsInGame (long GamCod) - { - /***** Trivial check *****/ - if (GamCod < 0) // A non-existing game... - return 0; // ...has no matches - - /***** Get number of matches in a game from database *****/ - return (unsigned) - DB_QueryCOUNT ("can not get number of matches of a game", - "SELECT COUNT(*)" - " FROM mch_matches" - " WHERE GamCod=%ld", - GamCod); - } - -/*****************************************************************************/ -/*************** Get number of unfinished matches in a game ******************/ -/*****************************************************************************/ - -unsigned Mch_DB_GetNumUnfinishedMchsInGame (long GamCod) - { - /***** Trivial check *****/ - if (GamCod < 0) // A non-existing game... - return 0; // ...has no matches - - /***** Get number of matches in a game from database *****/ - return (unsigned) - DB_QueryCOUNT ("can not get number of unfinished matches of a game", - "SELECT COUNT(*)" - " FROM mch_matches" - " WHERE GamCod=%ld" - " AND Showing<>'%s'", - GamCod, - Mch_ShowingStringsDB[Mch_END]); - } - /*****************************************************************************/ /************ Check if I belong to any of the groups of a match **************/ /*****************************************************************************/ @@ -2705,7 +2399,7 @@ static void Mch_WriteNumRespondersQst (struct Mch_Match *Match) break; default: HTM_Unsigned (Mch_DB_GetNumUsrsWhoAnsweredQst (Match->MchCod, - Match->Status.QstInd)); + Match->Status.QstInd)); break; } @@ -2905,7 +2599,7 @@ static void Mch_ShowLeftColumnStd (const struct Mch_Match *Match, /***** Write whether question is answered or not *****/ Mch_PutIfAnswered (Match,Answered); - if (Match->Status.Playing && // Match is being played + if (Match->Status.Playing && // Match is being played Match->Status.Showing == Mch_ANSWERS && // Teacher's screen is showing question answers Answered) // I have answered this question /***** Put icon to remove my answet *****/ @@ -2934,7 +2628,7 @@ static void Mch_ShowRightColumnStd (struct Mch_Match *Match, Mch_ShowMatchTitleStd (Match); /***** Bottom row *****/ - if (Match->Status.Playing) // Match is being played + if (Match->Status.Playing) // Match is being played { if (Match->Status.Showing == Mch_END) // Match over Mch_ShowWaitImage (Txt_Please_wait_); @@ -2956,7 +2650,7 @@ static void Mch_ShowRightColumnStd (struct Mch_Match *Match, HTM_DIV_End (); } } - else // Match is not being played + else // Match is not being played Mch_ShowWaitImage (Txt_Please_wait_); /***** End right container *****/ @@ -2979,7 +2673,7 @@ static void Mch_ShowNumQstInMch (const struct Mch_Match *Match) case Mch_START: // Not started HTM_Txt (Txt_MATCH_Start); break; - case Mch_END: // Match over + case Mch_END: // Match over HTM_Txt (Txt_MATCH_End); break; default: @@ -4516,21 +4210,3 @@ unsigned Mch_DB_GetStartEndMatchesInGame (MYSQL_RES **mysql_res,long GamCod) " WHERE GamCod=%ld", GamCod); } - -/*****************************************************************************/ -/********************** Remove answers of a game question ********************/ -/*****************************************************************************/ - -void Mch_DB_RemAnswersOfAQuestion (long GamCod,unsigned QstInd) - { - /***** Remove answers from all matches of this game *****/ - DB_QueryDELETE ("can not remove the answers of a question", - "DELETE FROM mch_answers" - " USING mch_matches," - "mch_answers" - " WHERE mch_matches.GamCod=%ld" // From all matches of this game... - " AND mch_matches.MchCod=mch_answers.MchCod" - " AND mch_answers.QstInd=%u", // ...remove only answers to this question - GamCod, - QstInd); - } diff --git a/swad_match.h b/swad_match.h index 9f2a8d88..20375416 100644 --- a/swad_match.h +++ b/swad_match.h @@ -52,6 +52,10 @@ typedef enum } Mch_Showing_t; #define Mch_SHOWING_DEFAULT Mch_START +/* Columns */ +#define Mch_MAX_COLS 4 +#define Mch_NUM_COLS_DEFAULT 1 + struct Mch_Match { long MchCod; @@ -119,18 +123,12 @@ void Mch_ResumeMatch (void); void Mch_GetIndexes (long MchCod,unsigned QstInd, unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]); -void Mch_DB_RemoveGroup (long GrpCod); -void Mch_DB_RemoveGroupsOfType (long GrpTypCod); - void Mch_PlayPauseMatch (void); void Mch_ChangeNumColsMch (void); void Mch_ToggleVisResultsMchQst (void); void Mch_BackMatch (void); void Mch_ForwardMatch (void); -unsigned Mch_DB_GetNumMchsInGame (long GamCod); -unsigned Mch_DB_GetNumUnfinishedMchsInGame (long GamCod); - bool Mch_CheckIfICanPlayThisMatchBasedOnGrps (const struct Mch_Match *Match); bool Mch_RegisterMeAsPlayerInMatch (struct Mch_Match *Match); @@ -161,6 +159,5 @@ void Mch_DrawBarNumUsrs (unsigned NumRespondersAns,unsigned NumRespondersQst,boo void Mch_DB_UpdateIndexesOfQstsGreaterThan (long GamCod,unsigned QstInd); unsigned Mch_DB_GetStartEndMatchesInGame (MYSQL_RES **mysql_res,long GamCod); -void Mch_DB_RemAnswersOfAQuestion (long GamCod,unsigned QstInd); #endif diff --git a/swad_match_database.c b/swad_match_database.c new file mode 100644 index 00000000..772384b1 --- /dev/null +++ b/swad_match_database.c @@ -0,0 +1,463 @@ +// swad_match_database.c: matches in games using remote control, operations woth 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 PATH_MAX +// #include // For NULL +#include // For asprintf +// #include // For free +#include // For string functions + +#include "swad_database.h" +// #include "swad_date.h" +#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" +// #include "swad_match.h" +#include "swad_match_database.h" +// #include "swad_match_result.h" +// #include "swad_role.h" +// #include "swad_setting.h" +// #include "swad_test.h" + +/*****************************************************************************/ +/************** External global variables from others modules ****************/ +/*****************************************************************************/ + +extern struct Globals Gbl; + +/*****************************************************************************/ +/***************************** Private constants *****************************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/******************************* Private types *******************************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/***************************** Private constants *****************************/ +/*****************************************************************************/ + +const char *Mch_ShowingStringsDB[Mch_NUM_SHOWING] = + { + [Mch_START ] = "start", + [Mch_STEM ] = "stem", + [Mch_ANSWERS] = "answers", + [Mch_RESULTS] = "results", + [Mch_END ] = "end", + }; + +/*****************************************************************************/ +/***************************** Private variables *****************************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/********************** Create a new match in a game *************************/ +/*****************************************************************************/ + +long Mch_DB_CreateMatch (long GamCod,char Title[Mch_MAX_BYTES_TITLE + 1]) + { + return + DB_QueryINSERTandReturnCode ("can not create match", + "INSERT mch_matches" + " (GamCod,UsrCod,StartTime,EndTime,Title," + "QstInd,QstCod,Showing,Countdown," + "NumCols,ShowQstResults,ShowUsrResults)" + " VALUES" + " (%ld," // GamCod + "%ld," // UsrCod + "NOW()," // StartTime + "NOW()," // EndTime + "'%s'," // Title + "0," // QstInd: Match has not started, so not the first question yet + "-1," // QstCod: Non-existent question + "'%s'," // Showing: What is being shown + "-1," // Countdown: No countdown + "%u," // NumCols: Number of columns in answers + "'N'," // ShowQstResults: Don't show question results initially + "'N')", // ShowUsrResults: Don't show user results initially + GamCod, + Gbl.Usrs.Me.UsrDat.UsrCod, // Game creator + Title, + Mch_ShowingStringsDB[Mch_SHOWING_DEFAULT], + Mch_NUM_COLS_DEFAULT); + } + +/*****************************************************************************/ +/******************** Update a game match being played ***********************/ +/*****************************************************************************/ + +void Mch_DB_UpdateMatchStatus (const struct Mch_Match *Match) + { + char *MchSubQuery; + + /***** Update end time only if match is currently being played *****/ + if (Match->Status.Playing) // Match is being played + { + if (asprintf (&MchSubQuery,"mch_matches.EndTime=NOW(),") < 0) + Err_NotEnoughMemoryExit (); + } + else // Match is paused, not being played + { + if (asprintf (&MchSubQuery,"%s","") < 0) + Err_NotEnoughMemoryExit (); + } + + /***** Update match status in database *****/ + DB_QueryUPDATE ("can not update match being played", + "UPDATE mch_matches," + "gam_games" + " SET %s" + "mch_matches.QstInd=%u," + "mch_matches.QstCod=%ld," + "mch_matches.Showing='%s'," + "mch_matches.Countdown=%ld," + "mch_matches.NumCols=%u," + "mch_matches.ShowQstResults='%c'," + "mch_matches.ShowUsrResults='%c'" + " WHERE mch_matches.MchCod=%ld" + " AND mch_matches.GamCod=gam_games.GamCod" + " AND gam_games.CrsCod=%ld", // Extra check + MchSubQuery, + Match->Status.QstInd, + Match->Status.QstCod, + Mch_ShowingStringsDB[Match->Status.Showing], + Match->Status.Countdown, + Match->Status.NumCols, + Match->Status.ShowQstResults ? 'Y' : + 'N', + Match->Status.ShowUsrResults ? 'Y' : + 'N', + Match->MchCod, + Gbl.Hierarchy.Crs.CrsCod); + free (MchSubQuery); + } + +/*****************************************************************************/ +/********************* Update title of an existing match *********************/ +/*****************************************************************************/ + +void Mch_DB_UpdateMatchTitle (const struct Mch_Match *Match) + { + DB_QueryUPDATE ("can not update match", + "UPDATE mch_matches" + " SET Title='%s'" + " WHERE MchCod=%ld", + Match->Title, + Match->MchCod); + } + +/*****************************************************************************/ +/****************** Get parameter with what is being shown *******************/ +/*****************************************************************************/ + +Mch_Showing_t Mch_DB_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; + } + +/*****************************************************************************/ +/********************** Get number of matches in a game **********************/ +/*****************************************************************************/ + +unsigned Mch_DB_GetNumMchsInGame (long GamCod) + { + /***** Trivial check *****/ + if (GamCod < 0) // A non-existing game... + return 0; // ...has no matches + + /***** Get number of matches in a game from database *****/ + return (unsigned) + DB_QueryCOUNT ("can not get number of matches of a game", + "SELECT COUNT(*)" + " FROM mch_matches" + " WHERE GamCod=%ld", + GamCod); + } + +/*****************************************************************************/ +/*************** Get number of unfinished matches in a game ******************/ +/*****************************************************************************/ + +unsigned Mch_DB_GetNumUnfinishedMchsInGame (long GamCod) + { + /***** Trivial check *****/ + if (GamCod < 0) // A non-existing game... + return 0; // ...has no matches + + /***** Get number of matches in a game from database *****/ + return (unsigned) + DB_QueryCOUNT ("can not get number of unfinished matches of a game", + "SELECT COUNT(*)" + " FROM mch_matches" + " WHERE GamCod=%ld" + " AND Showing<>'%s'", + GamCod, + Mch_ShowingStringsDB[Mch_END]); + } + +/*****************************************************************************/ +/************************* Remove match from table ***************************/ +/*****************************************************************************/ + +void Mch_DB_RemoveMatchFromTable (long MchCod,const char *TableName) + { + DB_QueryDELETE ("can not remove match from table", + "DELETE FROM %s" + " WHERE MchCod=%ld", + TableName, + MchCod); + } + +/*****************************************************************************/ +/****************** Remove matches in game from main table *******************/ +/*****************************************************************************/ + +void Mch_DB_RemoveMatchesInGameFromMainTable (long GamCod) + { + DB_QueryDELETE ("can not remove matches of a game", + "DELETE FROM mch_matches" + " WHERE GamCod=%ld", + GamCod); + } + +/*****************************************************************************/ +/***************** Remove matches in game from secondary table ***************/ +/*****************************************************************************/ + +void Mch_DB_RemoveMatchesInGameFromOtherTable (long GamCod,const char *TableName) + { + DB_QueryDELETE ("can not remove matches of a game from table", + "DELETE FROM %s" + " USING mch_matches," + "%s" + " WHERE mch_matches.GamCod=%ld" + " AND mch_matches.MchCod=%s.MchCod", + TableName, + TableName, + GamCod, + TableName); + } + +/*****************************************************************************/ +/***************** Remove matches in course from main table ******************/ +/*****************************************************************************/ + +void Mch_DB_RemoveMatchesInCrsFromMainTable (long CrsCod) + { + DB_QueryDELETE ("can not remove matches of a course", + "DELETE FROM mch_matches" + " USING gam_games," + "mch_matches" + " WHERE gam_games.CrsCod=%ld" + " AND gam_games.GamCod=mch_matches.GamCod", + CrsCod); + } + +/*****************************************************************************/ +/*************** Remove matches in course from secondary table ***************/ +/*****************************************************************************/ + +void Mch_DB_RemoveMatchesInCrsFromOtherTable (long CrsCod,const char *TableName) + { + DB_QueryDELETE ("can not remove matches of a course from table", + "DELETE FROM %s" + " USING gam_games," + "mch_matches," + "%s" + " WHERE gam_games.CrsCod=%ld" + " AND gam_games.GamCod=mch_matches.GamCod" + " AND mch_matches.MchCod=%s.MchCod", + TableName, + TableName, + CrsCod, + TableName); + } + +/*****************************************************************************/ +/****************** Remove matches made by a user from table *****************/ +/*****************************************************************************/ + +void Mch_DB_RemoveMatchesMadeByUsrFromTable (long UsrCod,const char *TableName) + { + DB_QueryDELETE ("can not remove matches of a user from table", + "DELETE FROM %s" + " WHERE UsrCod=%ld", + TableName, + UsrCod); + } + +/*****************************************************************************/ +/******* Remove matches made by a user in a course from secondary table ******/ +/*****************************************************************************/ + +void Mch_DB_RemoveMatchesMadeByUsrInCrsFromTable (long UsrCod,long CrsCod, + const char *TableName) + { + DB_QueryDELETE ("can not remove matches of a user from table", + "DELETE FROM %s" + " USING gam_games," + "mch_matches," + "%s" + " WHERE gam_games.CrsCod=%ld" + " AND gam_games.GamCod=mch_matches.GamCod" + " AND mch_matches.MchCod=%s.MchCod" + " AND %s.UsrCod=%ld", + TableName, + TableName, + CrsCod, + TableName, + TableName, + UsrCod); + } + +/*****************************************************************************/ +/******************** Create group associated to a match *********************/ +/*****************************************************************************/ + +void Mch_DB_AssociateGroupToMatch (long MchCod,long GrpCod) + { + DB_QueryINSERT ("can not associate a group to a match", + "INSERT INTO mch_groups" + " (MchCod,GrpCod)" + " VALUES" + " (%ld,%ld)", + MchCod, + GrpCod); + } + +/*****************************************************************************/ +/********************* Remove one group from all matches *********************/ +/*****************************************************************************/ + +void Mch_DB_RemoveGroup (long GrpCod) + { + DB_QueryDELETE ("can not remove group" + " from the associations between matches and groups", + "DELETE FROM mch_groups" + " WHERE GrpCod=%ld", + GrpCod); + } + +/*****************************************************************************/ +/***************** Remove groups of one type from all matches ****************/ +/*****************************************************************************/ + +void Mch_DB_RemoveGroupsOfType (long GrpTypCod) + { + DB_QueryDELETE ("can not remove groups of a type" + " from the associations between matches and groups", + "DELETE FROM mch_groups" + " USING grp_groups," + "mch_groups" + " WHERE grp_groups.GrpTypCod=%ld" + " AND grp_groups.GrpCod=mch_groups.GrpCod", + GrpTypCod); + } + +/*****************************************************************************/ +/******** Remove answers of a question from all matches of this game *********/ +/*****************************************************************************/ + +void Mch_DB_RemAnswersOfAQuestion (long GamCod,unsigned QstInd) + { + DB_QueryDELETE ("can not remove the answers of a question", + "DELETE FROM mch_answers" + " USING mch_matches," + "mch_answers" + " WHERE mch_matches.GamCod=%ld" // From all matches of this game... + " AND mch_matches.MchCod=mch_answers.MchCod" + " AND mch_answers.QstInd=%u", // ...remove only answers to this question + GamCod, + QstInd); + } + +/*****************************************************************************/ +/********* Create entry for this question in table of match indexes **********/ +/*****************************************************************************/ + +void Mch_DB_CreateQstIndexes (long MchCod,unsigned QstInd, + const char StrAnswersOneQst[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1]) + { + DB_QueryINSERT ("can not create indexes of a question", + "INSERT INTO mch_indexes" + " (MchCod,QstInd,Indexes)" + " VALUES" + " (%ld,%u,'%s')", + MchCod, + QstInd, + StrAnswersOneQst); + } + +/*****************************************************************************/ +/***************** Get indexes for a question from database ******************/ +/*****************************************************************************/ + +void Mch_DB_GetIndexes (long MchCod,unsigned QstInd, + char StrIndexesOneQst[Tst_MAX_BYTES_INDEXES_ONE_QST + 1]) + { + DB_QuerySELECTString (StrIndexesOneQst,Tst_MAX_BYTES_INDEXES_ONE_QST, + "can not get indexes of a question", + "SELECT Indexes" // row[0] + " FROM mch_indexes" + " WHERE MchCod=%ld" + " AND QstInd=%u", + MchCod, + QstInd); + } + +/*****************************************************************************/ +/********** Update elapsed time in current question (by a teacher) ***********/ +/*****************************************************************************/ + +void Mch_DB_UpdateElapsedTimeInQuestion (long MchCod,long QstInd) + { + DB_QueryINSERT ("can not update elapsed time in question", + "INSERT INTO mch_times" + " (MchCod,QstInd,ElapsedTime)" + " VALUES" + " (%ld,%u,SEC_TO_TIME(%u))" + " ON DUPLICATE KEY" + " UPDATE ElapsedTime=ADDTIME(ElapsedTime,SEC_TO_TIME(%u))", + MchCod, + QstInd, + Cfg_SECONDS_TO_REFRESH_MATCH_TCH, + Cfg_SECONDS_TO_REFRESH_MATCH_TCH); + } + diff --git a/swad_match_database.h b/swad_match_database.h new file mode 100644 index 00000000..4a64f274 --- /dev/null +++ b/swad_match_database.h @@ -0,0 +1,80 @@ +// swad_match_database.h: matches in games using remote control, operations woth database + +#ifndef _SWAD_MCH_DB +#define _SWAD_MCH_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_game.h" +#include "swad_match.h" +// #include "swad_match_print.h" +// #include "swad_scope.h" +// #include "swad_test.h" + +/*****************************************************************************/ +/************************** Public types and constants ***********************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/***************************** Public prototypes *****************************/ +/*****************************************************************************/ + +//------------------------------- Matches ------------------------------------- +long Mch_DB_CreateMatch (long GamCod,char Title[Mch_MAX_BYTES_TITLE + 1]); +void Mch_DB_UpdateMatchStatus (const struct Mch_Match *Match); +void Mch_DB_UpdateMatchTitle (const struct Mch_Match *Match); + +Mch_Showing_t Mch_DB_GetShowingFromStr (const char *Str); +unsigned Mch_DB_GetNumMchsInGame (long GamCod); +unsigned Mch_DB_GetNumUnfinishedMchsInGame (long GamCod); + +void Mch_DB_RemoveMatchFromTable (long MchCod,const char *TableName); +void Mch_DB_RemoveMatchesInGameFromMainTable (long GamCod); +void Mch_DB_RemoveMatchesInGameFromOtherTable (long GamCod,const char *TableName); +void Mch_DB_RemoveMatchesInCrsFromMainTable (long CrsCod); +void Mch_DB_RemoveMatchesInCrsFromOtherTable (long CrsCod,const char *TableName); +void Mch_DB_RemoveMatchesMadeByUsrFromTable (long UsrCod,const char *TableName); +void Mch_DB_RemoveMatchesMadeByUsrInCrsFromTable (long UsrCod,long CrsCod, + const char *TableName); + +//---------------------------------Groups ------------------------------------- +void Mch_DB_AssociateGroupToMatch (long MchCod,long GrpCod); + +void Mch_DB_RemoveGroup (long GrpCod); +void Mch_DB_RemoveGroupsOfType (long GrpTypCod); + +//-------------------------------- Answers ------------------------------------ +void Mch_DB_RemAnswersOfAQuestion (long GamCod,unsigned QstInd); + +//----------------------------- Answers indexes ------------------------------- +void Mch_DB_CreateQstIndexes (long MchCod,unsigned QstInd, + const char StrAnswersOneQst[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1]); +void Mch_DB_GetIndexes (long MchCod,unsigned QstInd, + char StrIndexesOneQst[Tst_MAX_BYTES_INDEXES_ONE_QST + 1]); + +//----------------------------- Elapsed times --------------------------------- +void Mch_DB_UpdateElapsedTimeInQuestion (long MchCod,long QstInd); + +#endif diff --git a/swad_test.c b/swad_test.c index de3b14ef..5dff5649 100644 --- a/swad_test.c +++ b/swad_test.c @@ -729,49 +729,49 @@ void Tst_ListQuestionForEdition (struct Tst_Question *Question, /***** Number of question and answer type (row[1]) *****/ HTM_TD_Begin ("class=\"RT COLOR%u\"",Gbl.RowEvenOdd); - Tst_WriteNumQst (QstInd,"BIG_INDEX"); - if (QuestionExists) - Tst_WriteAnswerType (Question->Answer.Type,"DAT_SMALL"); + Tst_WriteNumQst (QstInd,"BIG_INDEX"); + if (QuestionExists) + Tst_WriteAnswerType (Question->Answer.Type,"DAT_SMALL"); HTM_TD_End (); /***** Write question code *****/ HTM_TD_Begin ("class=\"DAT_SMALL CT COLOR%u\"",Gbl.RowEvenOdd); - HTM_TxtF ("%ld ",Question->QstCod); + HTM_TxtF ("%ld ",Question->QstCod); HTM_TD_End (); /***** Write the question tags *****/ HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd); - if (QuestionExists) - Tst_GetAndWriteTagsQst (Question->QstCod); + if (QuestionExists) + Tst_GetAndWriteTagsQst (Question->QstCod); HTM_TD_End (); /***** Write stem (row[3]) and media *****/ HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd); - HTM_ARTICLE_Begin (Anchor); - if (QuestionExists) - { - /* Write stem */ - Tst_WriteQstStem (Question->Stem,"TEST_TXT", - true); // Visible + HTM_ARTICLE_Begin (Anchor); + if (QuestionExists) + { + /* Write stem */ + Tst_WriteQstStem (Question->Stem,"TEST_TXT", + true); // Visible - /* Show media */ - Med_ShowMedia (&Question->Media, - "TEST_MED_EDIT_LIST_CONT", - "TEST_MED_EDIT_LIST"); + /* Show media */ + Med_ShowMedia (&Question->Media, + "TEST_MED_EDIT_LIST_CONT", + "TEST_MED_EDIT_LIST"); - /* Show feedback */ - Tst_WriteQstFeedback (Question->Feedback,"TEST_TXT_LIGHT"); + /* Show feedback */ + Tst_WriteQstFeedback (Question->Feedback,"TEST_TXT_LIGHT"); - /* Show answers */ - Tst_WriteAnswersBank (Question,"TEST_TXT","TEST_TXT_LIGHT"); - } - else - { - HTM_SPAN_Begin ("class=\"DAT_LIGHT\""); - HTM_Txt (Txt_Question_removed); - HTM_SPAN_End (); - } - HTM_ARTICLE_End (); + /* Show answers */ + Tst_WriteAnswersBank (Question,"TEST_TXT","TEST_TXT_LIGHT"); + } + else + { + HTM_SPAN_Begin ("class=\"DAT_LIGHT\""); + HTM_Txt (Txt_Question_removed); + HTM_SPAN_End (); + } + HTM_ARTICLE_End (); HTM_TD_End (); } @@ -2770,7 +2770,7 @@ static void Tst_WriteIntAnsBank (struct Tst_Question *Question, __attribute__((unused)) const char *ClassFeedback) { HTM_SPAN_Begin ("class=\"%s\"",ClassTxt); - HTM_TxtF ("(%ld)",Question->Answer.Integer); + HTM_TxtF ("(%ld)",Question->Answer.Integer); HTM_SPAN_End (); } @@ -2783,11 +2783,11 @@ static void Tst_WriteFltAnsBank (struct Tst_Question *Question, __attribute__((unused)) const char *ClassFeedback) { HTM_SPAN_Begin ("class=\"%s\"",ClassTxt); - HTM_Txt ("(["); - HTM_Double (Question->Answer.FloatingPoint[0]); - HTM_Txt ("; "); - HTM_Double (Question->Answer.FloatingPoint[1]); - HTM_Txt ("])"); + HTM_Txt ("(["); + HTM_Double (Question->Answer.FloatingPoint[0]); + HTM_Txt ("; "); + HTM_Double (Question->Answer.FloatingPoint[1]); + HTM_Txt ("])"); HTM_SPAN_End (); } @@ -2801,9 +2801,9 @@ static void Tst_WriteTF_AnsBank (struct Tst_Question *Question, { /***** Write answer *****/ HTM_SPAN_Begin ("class=\"%s\"",ClassTxt); - HTM_Txt ("("); - Tst_WriteAnsTF (Question->Answer.TF); - HTM_Txt (")"); + HTM_Txt ("("); + Tst_WriteAnsTF (Question->Answer.TF); + HTM_Txt (")"); HTM_SPAN_End (); } @@ -2825,42 +2825,42 @@ static void Tst_WriteChoAnsBank (struct Tst_Question *Question, Tst_ChangeFormatAnswersFeedback (Question); HTM_TABLE_BeginPadding (2); - for (NumOpt = 0; - NumOpt < Question->Answer.NumOptions; - NumOpt++) - { - HTM_TR_Begin (NULL); + for (NumOpt = 0; + NumOpt < Question->Answer.NumOptions; + NumOpt++) + { + HTM_TR_Begin (NULL); - /* Put an icon that indicates whether the answer is correct or wrong */ - HTM_TD_Begin ("class=\"BT%u\"",Gbl.RowEvenOdd); - if (Question->Answer.Options[NumOpt].Correct) - Ico_PutIcon ("check.svg",Txt_TST_Answer_given_by_the_teachers,"CONTEXT_ICO_16x16"); - HTM_TD_End (); + /* Put an icon that indicates whether the answer is correct or wrong */ + HTM_TD_Begin ("class=\"BT%u\"",Gbl.RowEvenOdd); + if (Question->Answer.Options[NumOpt].Correct) + Ico_PutIcon ("check.svg",Txt_TST_Answer_given_by_the_teachers,"CONTEXT_ICO_16x16"); + HTM_TD_End (); - /* Write the number of option */ - HTM_TD_Begin ("class=\"%s LT\"",ClassTxt); - HTM_TxtF ("%c) ",'a' + (char) NumOpt); - HTM_TD_End (); + /* Write the number of option */ + HTM_TD_Begin ("class=\"%s LT\"",ClassTxt); + HTM_TxtF ("%c) ",'a' + (char) NumOpt); + HTM_TD_End (); - HTM_TD_Begin ("class=\"LT\""); + HTM_TD_Begin ("class=\"LT\""); - /* Write the text of the answer and the media */ - HTM_DIV_Begin ("class=\"%s\"",ClassTxt); - HTM_Txt (Question->Answer.Options[NumOpt].Text); - Med_ShowMedia (&Question->Answer.Options[NumOpt].Media, - "TEST_MED_EDIT_LIST_CONT", - "TEST_MED_EDIT_LIST"); - HTM_DIV_End (); + /* Write the text of the answer and the media */ + HTM_DIV_Begin ("class=\"%s\"",ClassTxt); + HTM_Txt (Question->Answer.Options[NumOpt].Text); + Med_ShowMedia (&Question->Answer.Options[NumOpt].Media, + "TEST_MED_EDIT_LIST_CONT", + "TEST_MED_EDIT_LIST"); + HTM_DIV_End (); - /* Write the text of the feedback */ - HTM_DIV_Begin ("class=\"%s\"",ClassFeedback); - HTM_Txt (Question->Answer.Options[NumOpt].Feedback); - HTM_DIV_End (); + /* Write the text of the feedback */ + HTM_DIV_Begin ("class=\"%s\"",ClassFeedback); + HTM_Txt (Question->Answer.Options[NumOpt].Feedback); + HTM_DIV_End (); - HTM_TD_End (); + HTM_TD_End (); - HTM_TR_End (); - } + HTM_TR_End (); + } HTM_TABLE_End (); } @@ -5891,3 +5891,21 @@ static unsigned Tst_GetNumCoursesWithPluggableTstQuestions (HieLvl_Level_t Scope return 0; } } + +/*****************************************************************************/ +/*********** Get suffled/not-shuffled answers indexes of question ************/ +/*****************************************************************************/ + +unsigned Tst_DB_GetShuffledAnswersIndexes (MYSQL_RES **mysql_res, + const struct Tst_Question *Question) + { + return (unsigned) + DB_QuerySELECT (mysql_res,"can not get questions of a game", + "SELECT AnsInd" // row[0] + " FROM tst_answers" + " WHERE QstCod=%ld" + " ORDER BY %s", + Question->QstCod, + Question->Answer.Shuffle ? "RAND()" : // Use RAND() because is really random; RAND(NOW()) repeats order + "AnsInd"); + } diff --git a/swad_test.h b/swad_test.h index 84b7990d..45b2627e 100644 --- a/swad_test.h +++ b/swad_test.h @@ -187,4 +187,7 @@ void Tst_RemoveCrsTests (long CrsCod); void Tst_GetTestStats (Tst_AnswerType_t AnsType,struct Tst_Stats *Stats); +unsigned Tst_DB_GetShuffledAnswersIndexes (MYSQL_RES **mysql_res, + const struct Tst_Question *Question); + #endif