From 33b62e9a8218ccddde55f69ec27eeeb2668c68ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20Ca=C3=B1as=20Vargas?= Date: Sat, 28 Sep 2019 02:31:42 +0200 Subject: [PATCH] Version19.18 --- Makefile | 4 +- swad_action.c | 1 + swad_changelog.h | 3 +- swad_game.c | 11 +- swad_group.c | 1 + swad_match.c | 1055 +----------------------------------------- swad_match.h | 51 +- swad_match_result.c | 1076 +++++++++++++++++++++++++++++++++++++++++++ swad_match_result.h | 47 ++ 9 files changed, 1182 insertions(+), 1067 deletions(-) create mode 100644 swad_match_result.c create mode 100644 swad_match_result.h diff --git a/Makefile b/Makefile index 9d3349f51..7e1fdbb99 100644 --- a/Makefile +++ b/Makefile @@ -42,8 +42,8 @@ OBJS = swad_account.o swad_action.o swad_agenda.o swad_alert.o \ swad_help.o swad_hierarchy.o swad_holiday.o \ swad_icon.o swad_ID.o swad_indicator.o swad_info.o swad_institution.o \ swad_language.o swad_layout.o swad_link.o swad_logo.o \ - swad_mail.o swad_main.o swad_mark.o swad_match.o swad_media.o \ - swad_menu.o swad_message.o swad_MFU.o \ + swad_mail.o swad_main.o swad_mark.o swad_match.o swad_match_result.o \ + swad_media.o swad_menu.o swad_message.o swad_MFU.o \ swad_network.o swad_nickname.o swad_notice.o swad_notification.o \ swad_pagination.o swad_parameter.o swad_password.o swad_photo.o \ swad_place.o swad_plugin.o swad_privacy.o swad_profile.o swad_project.o \ diff --git a/swad_action.c b/swad_action.c index e3ebfd729..8309a6fc9 100644 --- a/swad_action.c +++ b/swad_action.c @@ -57,6 +57,7 @@ #include "swad_mail.h" #include "swad_mark.h" #include "swad_match.h" +#include "swad_match_result.h" #include "swad_MFU.h" #include "swad_network.h" #include "swad_nickname.h" diff --git a/swad_changelog.h b/swad_changelog.h index f75b73d25..f374b77b9 100644 --- a/swad_changelog.h +++ b/swad_changelog.h @@ -471,10 +471,11 @@ enscript -2 --landscape --color --file-align=2 --highlight --line-numbers -o - * En OpenSWAD: ps2pdf source.ps destination.pdf */ -#define Log_PLATFORM_VERSION "SWAD 19.17.3 (2019-09-28)" +#define Log_PLATFORM_VERSION "SWAD 19.18 (2019-09-28)" #define CSS_FILE "swad19.15.css" #define JS_FILE "swad19.15.js" /* + Version 19.18: Sep 28, 2019 New module swad_match_result for match results. (246540 lines) Version 19.17.3: Sep 28, 2019 Code refactoring removing matches and games. (246446 lines) Version 19.17.2: Sep 27, 2019 Code refactoring removing matches and games. (246448 lines) Version 19.17.1: Sep 27, 2019 Remove associations between matches and groups when removing groups. (246412 lines) diff --git a/swad_game.c b/swad_game.c index 2ece70408..0f1ea46ac 100644 --- a/swad_game.c +++ b/swad_game.c @@ -40,6 +40,7 @@ #include "swad_global.h" #include "swad_group.h" #include "swad_match.h" +#include "swad_match_result.h" #include "swad_pagination.h" #include "swad_parameter.h" #include "swad_role.h" @@ -724,7 +725,7 @@ void Gam_GetDataOfGameByCod (struct Game *Game) Game->NumQsts = Gam_GetNumQstsGame (Game->GamCod); /* Get number of matches */ - Game->NumMchs = Gam_GetNumMchsGame (Game->GamCod); + Game->NumMchs = Mch_GetNumMchsInGame (Game->GamCod); /* Can I view results of the game? Can I edit game? */ @@ -1770,12 +1771,6 @@ static void Gam_ListOneOrMoreQuestionsForEdition (long GamCod,unsigned NumQsts, /***** End table *****/ Tbl_EndTable (); - - /***** Button to add a new question *****/ - // Gam_PutButtonToAddNewQuestions (); - - /***** End box *****/ - // Box_EndBox (); } /*****************************************************************************/ @@ -2193,7 +2188,7 @@ static bool Gam_GetNumMchsGameAndCheckIfEditable (struct Game *Game) extern const char *Txt_You_can_not_edit_a_game_with_matches; /***** Check if game has matches *****/ - if ((Game->NumMchs = Gam_GetNumMchsGame (Game->GamCod))) + if ((Game->NumMchs = Mch_GetNumMchsInGame (Game->GamCod))) { Ale_ShowAlert (Ale_WARNING,Txt_You_can_not_edit_a_game_with_matches); return false; // It has matches ==> it's not editable diff --git a/swad_group.c b/swad_group.c index 23982652f..b1c1691b9 100644 --- a/swad_group.c +++ b/swad_group.c @@ -38,6 +38,7 @@ #include "swad_game.h" #include "swad_global.h" #include "swad_group.h" +#include "swad_match.h" #include "swad_notification.h" #include "swad_parameter.h" #include "swad_project.h" diff --git a/swad_match.c b/swad_match.c index 7a7822073..ea02f7856 100644 --- a/swad_match.c +++ b/swad_match.c @@ -41,6 +41,7 @@ #include "swad_group.h" #include "swad_ID.h" #include "swad_match.h" +#include "swad_match_result.h" #include "swad_pagination.h" #include "swad_parameter.h" #include "swad_role.h" @@ -70,42 +71,6 @@ extern struct Globals Gbl; /******************************* Private types *******************************/ /*****************************************************************************/ -#define Mch_NUM_SHOWING 4 -typedef enum - { - Mch_NOTHING, // Don't show anything - Mch_STEM, // Showing only the question stem - Mch_ANSWERS, // Showing the question stem and the answers - Mch_RESULTS, // Showing the results - } Mch_Showing_t; -#define Mch_SHOWING_DEFAULT Mch_NOTHING - -struct Match - { - long MchCod; - long GamCod; - long UsrCod; - time_t TimeUTC[Dat_NUM_START_END_TIME]; - char Title[Gam_MAX_BYTES_TITLE + 1]; - struct - { - unsigned QstInd; // 0 means that the game has not started. First question has index 1. - long QstCod; - time_t QstStartTimeUTC; - Mch_Showing_t Showing; // What is shown on teacher's screen - bool ShowQstResults; // Show global results of current question while playing - bool ShowUsrResults; // Show exam with results of all questions for the student - bool Playing; // Is being played now? - unsigned NumPlayers; - } Status; // Status related to match playing - }; - -struct Mch_UsrAnswer - { - int NumOpt; // < 0 ==> no answer selected - int AnsInd; // < 0 ==> no answer selected - }; - /*****************************************************************************/ /***************************** Private constants *****************************/ /*****************************************************************************/ @@ -144,8 +109,6 @@ long Mch_CurrentMchCod = -1L; // Used as parameter in contextual links /***************************** Private prototypes ****************************/ /*****************************************************************************/ -static void Mch_GetDataOfMatchByCod (struct Match *Match); - static void Mch_PutIconToPlayNewMatch (void); static void Mch_ListOneOrMoreMatches (struct Game *Game, @@ -162,9 +125,6 @@ static void Mch_ListOneOrMoreMatchesNumPlayers (const struct Match *Match); static void Mch_ListOneOrMoreMatchesStatus (const struct Match *Match,unsigned NumQsts); static void Mch_ListOneOrMoreMatchesResult (const struct Match *Match); -static bool Mch_CheckIfICanSeeMatchResult (long MchCod,long UsrCod); -static bool Mch_GetVisibilityMchResultFromDB (long MchCod); - static void Mch_GetMatchDataFromRow (MYSQL_RES *mysql_res, struct Match *Match); static Mch_Showing_t Mch_GetShowingFromStr (const char *Str); @@ -175,8 +135,6 @@ static void Mch_RemoveMatchesInGameFromTable (long GamCod,const char *TableName) static void Mch_RemoveMatchInCourseFromTable (long CrsCod,const char *TableName); static void Mch_PutParamCurrentMchCod (void); -static void Mch_PutParamMchCod (long MchCod); -static long Mch_GetParamMchCod (void); static void Mch_PutButtonNewMatch (long GamCod); @@ -241,29 +199,12 @@ static bool Mch_GetIfMatchIsBeingPlayed (long MchCod); static void Mch_RegisterMeAsPlayerInMatch (long MchCod); static void Mch_GetNumPlayers (struct Match *Match); -static void Mch_GetQstAnsFromDB (long MchCod,long UsrCod,unsigned QstInd, - struct Mch_UsrAnswer *UsrAnswer); static double Mch_ComputeScore (unsigned NumQsts); static unsigned Mch_GetNumUsrsWhoHaveChosenAns (long MchCod,unsigned QstInd,unsigned AnsInd); static unsigned Mch_GetNumUsrsWhoHaveAnswerMch (long MchCod); static void Mch_DrawBarNumUsrs (unsigned NumAnswerersAns,unsigned NumAnswerersQst,bool Correct); -static void Mch_ShowHeaderMchResults (void); -static void Mch_ShowMchResults (Usr_MeOrOther_t MeOrOther); -static void Mch_ShowMchResultsSummaryRow (bool ShowSummaryResults, - unsigned NumResults, - unsigned NumTotalQsts, - unsigned NumTotalQstsNotBlank, - double TotalScoreOfAllResults); -static void Mch_GetMatchResultQuestionsFromDB (long MchCod,long UsrCod, - unsigned *NumQsts,unsigned *NumQstsNotBlank); -static void Mch_GetMatchResultDataByMchCod (long MchCod,long UsrCod, - time_t TimeUTC[Dat_NUM_START_END_TIME], - unsigned *NumQsts, - unsigned *NumQstsNotBlank, - double *Score); - /*****************************************************************************/ /************************* List the matches of a game ************************/ /*****************************************************************************/ @@ -354,7 +295,7 @@ void Mch_ListMatches (struct Game *Game,bool PutFormNewMatch) /********************** Get match data using its code ************************/ /*****************************************************************************/ -static void Mch_GetDataOfMatchByCod (struct Match *Match) +void Mch_GetDataOfMatchByCod (struct Match *Match) { MYSQL_RES *mysql_res; unsigned long NumRows; @@ -825,76 +766,6 @@ void Mch_ToggleVisibilResultsMchUsr (void) false); // Do not put form to start new match } -/*****************************************************************************/ -/********************* Get if I can see match result ************************/ -/*****************************************************************************/ - -static bool Mch_CheckIfICanSeeMatchResult (long MchCod,long UsrCod) - { - bool ItsMe; - bool ShowResultThisMatch; - - switch (Gbl.Usrs.Me.Role.Logged) - { - case Rol_STD: - ItsMe = Usr_ItsMe (UsrCod); - if (ItsMe && Gbl.Test.Config.Feedback != Tst_FEEDBACK_NOTHING) - ShowResultThisMatch = Mch_GetVisibilityMchResultFromDB (MchCod); - else - ShowResultThisMatch = false; - break; - case Rol_NET: - case Rol_TCH: - case Rol_DEG_ADM: - case Rol_CTR_ADM: - case Rol_INS_ADM: - case Rol_SYS_ADM: - ShowResultThisMatch = true; - break; - default: - ShowResultThisMatch = false; - break; - } - - return ShowResultThisMatch; - } - -/*****************************************************************************/ -/********************* Get visibility of match result ************************/ -/*****************************************************************************/ - -static bool Mch_GetVisibilityMchResultFromDB (long MchCod) - { - MYSQL_RES *mysql_res; - MYSQL_ROW row; - unsigned long NumRows; - bool ShowUsrResults; - - /***** Get visibility of match result from database *****/ - NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get if show result", - "SELECT ShowUsrResults" // row[0] - " FROM mch_matches" - " WHERE MchCod=%ld" - " AND GamCod IN" // Extra check - " (SELECT GamCod FROM gam_games" - " WHERE CrsCod='%ld')", - MchCod, - Gbl.Hierarchy.Crs.CrsCod); - if (NumRows) // Match found... - { - /* Get whether to show user results or not (row(0)) */ - row = mysql_fetch_row (mysql_res); - ShowUsrResults = (row[0][0] == 'Y'); - } - else - ShowUsrResults = false; - - /***** Free structure that stores the query result *****/ - DB_FreeMySQLResult (&mysql_res); - - return ShowUsrResults; - } - /*****************************************************************************/ /******************** Get game data from a database row **********************/ /*****************************************************************************/ @@ -1186,7 +1057,7 @@ static void Mch_PutParamCurrentMchCod (void) /******************** Write parameter with code of match **********************/ /*****************************************************************************/ -static void Mch_PutParamMchCod (long MchCod) +void Mch_PutParamMchCod (long MchCod) { Par_PutHiddenParamLong ("MchCod",MchCod); } @@ -1195,7 +1066,7 @@ static void Mch_PutParamMchCod (long MchCod) /********************* Get parameter with code of match **********************/ /*****************************************************************************/ -static long Mch_GetParamMchCod (void) +long Mch_GetParamMchCod (void) { /***** Get code of match *****/ return Par_GetParToLong ("MchCod"); @@ -2073,7 +1944,7 @@ static void Mch_ShowMatchStatusForStd (struct Match *Match) /******************* Get number of questions of a game *********************/ /*****************************************************************************/ -unsigned Gam_GetNumMchsGame (long GamCod) +unsigned Mch_GetNumMchsInGame (long GamCod) { /***** Trivial check *****/ if (GamCod < 0) // A non-existing game... @@ -2813,8 +2684,8 @@ void Mch_RefreshMatchStd (void) /**** Receive previous question answer in a match question from database *****/ /*****************************************************************************/ -static void Mch_GetQstAnsFromDB (long MchCod,long UsrCod,unsigned QstInd, - struct Mch_UsrAnswer *UsrAnswer) +void Mch_GetQstAnsFromDB (long MchCod,long UsrCod,unsigned QstInd, + struct Mch_UsrAnswer *UsrAnswer) { MYSQL_RES *mysql_res; MYSQL_ROW row; @@ -3117,915 +2988,3 @@ static void Mch_DrawBarNumUsrs (unsigned NumAnswerersAns,unsigned NumAnswerersQs /***** End container *****/ fprintf (Gbl.F.Out,""); } - -/*****************************************************************************/ -/****************** Write a form to go to result of matches ******************/ -/*****************************************************************************/ - -void Mch_PutFormToViewMchResults (Act_Action_t Action) - { - extern const char *Txt_Results; - - fprintf (Gbl.F.Out,"
"); - Lay_PutContextualLinkIconText (Action,NULL,NULL, - "tasks.svg", - Txt_Results); - fprintf (Gbl.F.Out,"
"); - } - -/*****************************************************************************/ -/****************** Select dates to show my matches results ******************/ -/*****************************************************************************/ - -void Mch_SelDatesToSeeMyMchResults (void) - { - extern const char *Hlp_ASSESSMENT_Games_results; - extern const char *Txt_Results; - extern const char *Txt_View_matches_results; - - /***** Start form *****/ - Frm_StartForm (ActSeeMyMchRes); - - /***** Start box and table *****/ - Box_StartBoxTable (NULL,Txt_Results,NULL, - Hlp_ASSESSMENT_Games_results,Box_NOT_CLOSABLE,2); - Dat_PutFormStartEndClientLocalDateTimesWithYesterdayToday (false); - - /***** End table, send button and end box *****/ - Box_EndBoxTableWithButton (Btn_CONFIRM_BUTTON,Txt_View_matches_results); - - /***** End form *****/ - Frm_EndForm (); - } - -/*****************************************************************************/ -/*************************** Show my matches results *************************/ -/*****************************************************************************/ - -void Mch_ShowMyMchResults (void) - { - extern const char *Hlp_ASSESSMENT_Games_results; - extern const char *Txt_Results; - - /***** Get starting and ending dates *****/ - Dat_GetIniEndDatesFromForm (); - - /***** Start box and table *****/ - Box_StartBoxTable (NULL,Txt_Results,NULL, - Hlp_ASSESSMENT_Games_results,Box_NOT_CLOSABLE,2); - - /***** Header of the table with the list of users *****/ - Mch_ShowHeaderMchResults (); - - /***** List my matches results *****/ - Tst_GetConfigTstFromDB (); // To get feedback type - Mch_ShowMchResults (Usr_ME); - - /***** End table and box *****/ - Box_EndBoxTable (); - } - -/*****************************************************************************/ -/*********** Select users and dates to show their matches results ************/ -/*****************************************************************************/ - -void Mch_SelUsrsToViewUsrsMchResults (void) - { - extern const char *Hlp_ASSESSMENT_Games_results; - extern const char *The_ClassFormInBox[The_NUM_THEMES]; - extern const char *Txt_Results; - extern const char *Txt_Users; - extern const char *Txt_View_matches_results; - unsigned NumTotalUsrs; - - /***** Get and update type of list, - number of columns in class photo - and preference about viewing photos *****/ - Usr_GetAndUpdatePrefsAboutUsrList (); - - /***** Get groups to show ******/ - Grp_GetParCodsSeveralGrpsToShowUsrs (); - - /***** Get and order lists of users from this course *****/ - Usr_GetListUsrs (Hie_CRS,Rol_STD); - Usr_GetListUsrs (Hie_CRS,Rol_NET); - Usr_GetListUsrs (Hie_CRS,Rol_TCH); - NumTotalUsrs = Gbl.Usrs.LstUsrs[Rol_STD].NumUsrs + - Gbl.Usrs.LstUsrs[Rol_NET].NumUsrs + - Gbl.Usrs.LstUsrs[Rol_TCH].NumUsrs; - - /***** Start box *****/ - Box_StartBox (NULL,Txt_Results,NULL, - Hlp_ASSESSMENT_Games_results,Box_NOT_CLOSABLE); - - /***** Show form to select the groups *****/ - Grp_ShowFormToSelectSeveralGroups (NULL, - Grp_ONLY_MY_GROUPS); - - /***** Start section with user list *****/ - Lay_StartSection (Usr_USER_LIST_SECTION_ID); - - if (NumTotalUsrs) - { - if (Usr_GetIfShowBigList (NumTotalUsrs,NULL,NULL)) - { - /***** Form to select type of list used for select several users *****/ - Usr_ShowFormsToSelectUsrListType (NULL); - - /***** Start form *****/ - Frm_StartForm (ActSeeUsrMchRes); - Grp_PutParamsCodGrps (); - - /***** Put list of users to select some of them *****/ - Tbl_StartTableCenter (2); - fprintf (Gbl.F.Out,"" - "" - "%s:" - "" - "", - The_ClassFormInBox[Gbl.Prefs.Theme],Txt_Users, - The_ClassFormInBox[Gbl.Prefs.Theme]); - Tbl_StartTable (2); - Usr_ListUsersToSelect (Rol_TCH); - Usr_ListUsersToSelect (Rol_NET); - Usr_ListUsersToSelect (Rol_STD); - Tbl_EndTable (); - fprintf (Gbl.F.Out,"" - ""); - - /***** Starting and ending dates in the search *****/ - Dat_PutFormStartEndClientLocalDateTimesWithYesterdayToday (false); - - Tbl_EndTable (); - - /***** Send button *****/ - Btn_PutConfirmButton (Txt_View_matches_results); - - /***** End form *****/ - Frm_EndForm (); - } - } - else // NumTotalUsrs == 0 - /***** Show warning indicating no students found *****/ - Usr_ShowWarningNoUsersFound (Rol_UNK); - - /***** End section with user list *****/ - Lay_EndSection (); - - /***** End box *****/ - Box_EndBox (); - - /***** Free memory for users' list *****/ - Usr_FreeUsrsList (Rol_TCH); - Usr_FreeUsrsList (Rol_NET); - Usr_FreeUsrsList (Rol_STD); - - /***** Free memory used by list of selected users' codes *****/ - Usr_FreeListsSelectedUsrsCods (); - - /***** Free memory for list of selected groups *****/ - Grp_FreeListCodSelectedGrps (); - } - -/*****************************************************************************/ -/****************** Show matches results for several users *******************/ -/*****************************************************************************/ - -void Mch_ShowUsrsMchResults (void) - { - extern const char *Hlp_ASSESSMENT_Games_results; - extern const char *Txt_Results; - extern const char *Txt_You_must_select_one_ore_more_users; - const char *Ptr; - - /***** Get list of the selected users's IDs *****/ - Usr_GetListsSelectedUsrsCods (); - - /***** Get starting and ending dates *****/ - Dat_GetIniEndDatesFromForm (); - - /***** Check the number of users whose matches results will be shown *****/ - if (Usr_CountNumUsrsInListOfSelectedUsrs ()) // If some users are selected... - { - /***** Start box and table *****/ - Box_StartBoxTable (NULL,Txt_Results,NULL, - Hlp_ASSESSMENT_Games_results,Box_NOT_CLOSABLE,2); - - /***** Header of the table with the list of users *****/ - Mch_ShowHeaderMchResults (); - - /***** List the matches results of the selected users *****/ - Ptr = Gbl.Usrs.Selected.List[Rol_UNK]; - while (*Ptr) - { - Par_GetNextStrUntilSeparParamMult (&Ptr,Gbl.Usrs.Other.UsrDat.EncryptedUsrCod, - Cry_BYTES_ENCRYPTED_STR_SHA256_BASE64); - Usr_GetUsrCodFromEncryptedUsrCod (&Gbl.Usrs.Other.UsrDat); - if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat,Usr_DONT_GET_PREFS)) // Get of the database the data of the user - if (Usr_CheckIfICanViewMch (&Gbl.Usrs.Other.UsrDat)) - /***** Show matches results *****/ - Mch_ShowMchResults (Usr_OTHER); - } - - /***** End table and box *****/ - Box_EndBoxTable (); - } - else // If no users are selected... - { - // ...write warning alert - Ale_ShowAlert (Ale_WARNING,Txt_You_must_select_one_ore_more_users); - // ...and show again the form - Mch_SelUsrsToViewUsrsMchResults (); - } - - /***** Free memory used by list of selected users' codes *****/ - Usr_FreeListsSelectedUsrsCods (); - } - -/*****************************************************************************/ -/********************* Show header of my matches results *********************/ -/*****************************************************************************/ - -static void Mch_ShowHeaderMchResults (void) - { - extern const char *Txt_User[Usr_NUM_SEXS]; - extern const char *Txt_START_END_TIME[Dat_NUM_START_END_TIME]; - extern const char *Txt_Questions; - extern const char *Txt_Non_blank_BR_questions; - extern const char *Txt_Total_BR_score; - extern const char *Txt_Average_BR_score_BR_per_question_BR_from_0_to_1; - extern const char *Txt_Score; - extern const char *Txt_out_of_PART_OF_A_SCORE; - - fprintf (Gbl.F.Out,"" - "" - "%s" - "" - "" - "%s" - "" - "" - "%s" - "" - "" - "%s" - "" - "" - "%s" - "" - "" - "%s" - "" - "" - "%s" - "" - "" - "%s
%s
%u" - "" - "" - "", - Txt_User[Usr_SEX_UNKNOWN], - Txt_START_END_TIME[Dat_START_TIME], - Txt_START_END_TIME[Dat_END_TIME], - Txt_Questions, - Txt_Non_blank_BR_questions, - Txt_Total_BR_score, - Txt_Average_BR_score_BR_per_question_BR_from_0_to_1, - Txt_Score,Txt_out_of_PART_OF_A_SCORE,Tst_SCORE_MAX); - } - -/*****************************************************************************/ -/********* Show the matches results of a user in the current course **********/ -/*****************************************************************************/ - -static void Mch_ShowMchResults (Usr_MeOrOther_t MeOrOther) - { - extern const char *Txt_Today; - extern const char *Txt_Match_result; - extern const char *Txt_Hidden_result; - MYSQL_RES *mysql_res; - MYSQL_ROW row; - struct UsrData *UsrDat; - bool ShowResultThisMatch; - bool ShowSummaryResults = true; - unsigned NumResults; - unsigned NumResult; - static unsigned UniqueId = 0; - long MchCod; - Dat_StartEndTime_t StartEndTime; - unsigned NumQstsInThisResult; - unsigned NumQstsNotBlankInThisResult; - unsigned NumTotalQsts = 0; - unsigned NumTotalQstsNotBlank = 0; - double ScoreInThisResult; - double TotalScoreOfAllResults = 0.0; - time_t TimeUTC[Dat_NUM_START_END_TIME]; - char *ClassDat; - - /***** Set user *****/ - UsrDat = (MeOrOther == Usr_ME) ? &Gbl.Usrs.Me.UsrDat : - &Gbl.Usrs.Other.UsrDat; - - /***** Make database query *****/ - NumResults = - (unsigned) DB_QuerySELECT (&mysql_res,"can not get matches results of a user", - "SELECT mch_results.MchCod," // row[0] - "UNIX_TIMESTAMP(mch_results.StartTime)," // row[1] - "UNIX_TIMESTAMP(mch_results.EndTime)," // row[2] - "mch_results.NumQsts," // row[3] - "mch_results.NumQstsNotBlank," // row[4] - "mch_results.Score" // row[5] - " FROM mch_results,mch_matches,gam_games" - " WHERE mch_results.UsrCod=%ld" - " AND mch_results.MchCod=mch_matches.MchCod" - " AND mch_matches.GamCod=gam_games.GamCod" - " AND gam_games.CrsCod=%ld" // Extra check - " AND mch_matches.EndTime>=FROM_UNIXTIME(%ld)" - " AND mch_matches.StartTime<=FROM_UNIXTIME(%ld)" - " ORDER BY MchCod", - UsrDat->UsrCod, - Gbl.Hierarchy.Crs.CrsCod, - (long) Gbl.DateRange.TimeUTC[Dat_START_TIME], - (long) Gbl.DateRange.TimeUTC[Dat_END_TIME]); - - /***** Show user's data *****/ - fprintf (Gbl.F.Out,""); - Usr_ShowTableCellWithUsrData (UsrDat,NumResults); - - /***** Get and print matches results *****/ - if (NumResults) - { - for (NumResult = 0; - NumResult < NumResults; - NumResult++) - { - row = mysql_fetch_row (mysql_res); - - /* Get match code (row[0]) */ - if ((MchCod = Str_ConvertStrCodToLongCod (row[0])) < 0) - Lay_ShowErrorAndExit ("Wrong code of match."); - - /* Show match result? */ - ShowResultThisMatch = Mch_CheckIfICanSeeMatchResult (MchCod,UsrDat->UsrCod); - ShowSummaryResults = ShowSummaryResults && ShowResultThisMatch; - - if (NumResult) - fprintf (Gbl.F.Out,""); - - /* Write start/end times (row[1], row[2] hold UTC start/end times) */ - for (StartEndTime = (Dat_StartEndTime_t) 0; - StartEndTime <= (Dat_StartEndTime_t) (Dat_NUM_START_END_TIME - 1); - StartEndTime++) - { - TimeUTC[StartEndTime] = Dat_GetUNIXTimeFromStr (row[1 + StartEndTime]); - UniqueId++; - fprintf (Gbl.F.Out,"" - "" - "", - (unsigned) StartEndTime,UniqueId, - ClassDat,Gbl.RowEvenOdd, - (unsigned) StartEndTime,UniqueId, - (long) TimeUTC[StartEndTime], - (unsigned) Gbl.Prefs.DateFormat,Txt_Today); - } - - /* Get number of questions (row[3]) */ - if (sscanf (row[3],"%u",&NumQstsInThisResult) != 1) - NumQstsInThisResult = 0; - NumTotalQsts += NumQstsInThisResult; - - /* Get number of questions not blank (row[4]) */ - if (sscanf (row[4],"%u",&NumQstsNotBlankInThisResult) != 1) - NumQstsNotBlankInThisResult = 0; - NumTotalQstsNotBlank += NumQstsNotBlankInThisResult; - - if (ShowResultThisMatch) - { - /* Get score (row[5]) */ - Str_SetDecimalPointToUS (); // To get the decimal point as a dot - if (sscanf (row[5],"%lf",&ScoreInThisResult) != 1) - ScoreInThisResult = 0.0; - Str_SetDecimalPointToLocal (); // Return to local system - TotalScoreOfAllResults += ScoreInThisResult; - } - - /* Write number of questions */ - fprintf (Gbl.F.Out,"%u", - ClassDat,Gbl.RowEvenOdd,NumQstsInThisResult); - - /* Write number of questions not blank */ - fprintf (Gbl.F.Out,"%u", - ClassDat,Gbl.RowEvenOdd,NumQstsNotBlankInThisResult); - - /* Write score */ - fprintf (Gbl.F.Out,"", - ClassDat,Gbl.RowEvenOdd); - if (ShowResultThisMatch) - fprintf (Gbl.F.Out,"%.2lf", - ScoreInThisResult); - fprintf (Gbl.F.Out,""); - - /* Write average score per question */ - fprintf (Gbl.F.Out,"", - ClassDat,Gbl.RowEvenOdd); - if (ShowResultThisMatch) - fprintf (Gbl.F.Out,"%.2lf", - NumQstsInThisResult ? ScoreInThisResult / (double) NumQstsInThisResult : - 0.0); - fprintf (Gbl.F.Out,""); - - /* Write score over Tst_SCORE_MAX */ - fprintf (Gbl.F.Out,"", - ClassDat,Gbl.RowEvenOdd); - if (ShowResultThisMatch) - fprintf (Gbl.F.Out,"%.2lf", - NumQstsInThisResult ? ScoreInThisResult * Tst_SCORE_MAX / (double) NumQstsInThisResult : - 0.0); - fprintf (Gbl.F.Out,""); - - /* Link to show this result */ - fprintf (Gbl.F.Out,"", - Gbl.RowEvenOdd); - if (ShowResultThisMatch) - { - switch (MeOrOther) - { - case Usr_ME: - Frm_StartForm (ActSeeOneMchResMe); - Mch_PutParamMchCod (MchCod); - break; - case Usr_OTHER: - Frm_StartForm (ActSeeOneMchResOth); - Mch_PutParamMchCod (MchCod); - Usr_PutParamOtherUsrCodEncrypted (); - break; - } - Ico_PutIconLink ("tasks.svg",Txt_Match_result); - Frm_EndForm (); - } - else - Ico_PutIconOff ("eye-slash.svg",Txt_Hidden_result); - fprintf (Gbl.F.Out,""); - - fprintf (Gbl.F.Out,""); - } - - /***** Write totals for this user *****/ - Mch_ShowMchResultsSummaryRow (ShowSummaryResults, - NumResults, - NumTotalQsts,NumTotalQstsNotBlank, - TotalScoreOfAllResults); - } - else - { - Tbl_PutEmptyCells (8); - fprintf (Gbl.F.Out,""); - } - - /***** Free structure that stores the query result *****/ - DB_FreeMySQLResult (&mysql_res); - - Gbl.RowEvenOdd = 1 - Gbl.RowEvenOdd; - } - -/*****************************************************************************/ -/************** Show row with summary of user's matches results **************/ -/*****************************************************************************/ - -static void Mch_ShowMchResultsSummaryRow (bool ShowSummaryResults, - unsigned NumResults, - unsigned NumTotalQsts, - unsigned NumTotalQstsNotBlank, - double TotalScoreOfAllResults) - { - extern const char *Txt_Matches; - - /***** Start row *****/ - fprintf (Gbl.F.Out,""); - - /***** Row title *****/ - fprintf (Gbl.F.Out,"" - "%s: %u" - "", - Gbl.RowEvenOdd, - Txt_Matches,NumResults); - - /***** Write total number of questions *****/ - fprintf (Gbl.F.Out,"", - Gbl.RowEvenOdd); - if (NumResults) - fprintf (Gbl.F.Out,"%u",NumTotalQsts); - fprintf (Gbl.F.Out,""); - - /***** Write total number of questions not blank *****/ - fprintf (Gbl.F.Out,"", - Gbl.RowEvenOdd); - if (NumResults) - fprintf (Gbl.F.Out,"%u",NumTotalQstsNotBlank); - fprintf (Gbl.F.Out,""); - - /***** Write total score *****/ - fprintf (Gbl.F.Out,"", - Gbl.RowEvenOdd); - if (ShowSummaryResults) - fprintf (Gbl.F.Out,"%.2lf",TotalScoreOfAllResults); - fprintf (Gbl.F.Out,""); - - /***** Write average score per question *****/ - fprintf (Gbl.F.Out,"", - Gbl.RowEvenOdd); - if (ShowSummaryResults) - fprintf (Gbl.F.Out,"%.2lf", - NumTotalQsts ? TotalScoreOfAllResults / (double) NumTotalQsts : - 0.0); - fprintf (Gbl.F.Out,""); - - /***** Write score over Tst_SCORE_MAX *****/ - fprintf (Gbl.F.Out,"", - Gbl.RowEvenOdd); - if (ShowSummaryResults) - fprintf (Gbl.F.Out,"%.2lf", - NumTotalQsts ? TotalScoreOfAllResults * Tst_SCORE_MAX / - (double) NumTotalQsts : - 0.0); - fprintf (Gbl.F.Out,""); - - /***** Last cell *****/ - fprintf (Gbl.F.Out,"", - Gbl.RowEvenOdd); - - /***** End row *****/ - fprintf (Gbl.F.Out,""); - } - -/*****************************************************************************/ -/******************* Show one match result of another user *******************/ -/*****************************************************************************/ - -void Mch_ShowOneMchResult (void) - { - extern const char *Hlp_ASSESSMENT_Games_results; - extern const char *Txt_Match_result; - extern const char *Txt_ROLES_SINGUL_Abc[Rol_NUM_ROLES][Usr_NUM_SEXS]; - extern const char *Txt_START_END_TIME[Dat_NUM_START_END_TIME]; - extern const char *Txt_Today; - extern const char *Txt_Questions; - extern const char *Txt_non_blank_QUESTIONS; - extern const char *Txt_Score; - extern const char *Txt_out_of_PART_OF_A_SCORE; - extern const char *Txt_Tags; - struct Match Match; - Usr_MeOrOther_t MeOrOther; - struct UsrData *UsrDat; - time_t TimeUTC[Dat_NUM_START_END_TIME]; // Match result UTC date-time - Dat_StartEndTime_t StartEndTime; - unsigned NumQsts; - unsigned NumQstsNotBlank; - double TotalScore; - bool ShowPhoto; - char PhotoURL[PATH_MAX + 1]; - bool ItsMe; - bool ICanViewResult; - bool ICanViewScore; - - /***** Get match code *****/ - if ((Match.MchCod = Mch_GetParamMchCod ()) == -1L) - Lay_ShowErrorAndExit ("Code of match is missing."); - - /***** Get data of the match from database *****/ - Mch_GetDataOfMatchByCod (&Match); - - /***** Pointer to user's data *****/ - MeOrOther = (Gbl.Action.Act == ActSeeOneMchResMe) ? Usr_ME : - Usr_OTHER; - switch (MeOrOther) - { - case Usr_ME: - UsrDat = &Gbl.Usrs.Me.UsrDat; - break; - case Usr_OTHER: - default: - UsrDat = &Gbl.Usrs.Other.UsrDat; - Usr_GetParamOtherUsrCodEncrypted (UsrDat); - break; - } - - /***** Get match result data *****/ - Mch_GetMatchResultDataByMchCod (Match.MchCod,UsrDat->UsrCod, - TimeUTC, - &NumQsts, - &NumQstsNotBlank, - &TotalScore); - Gbl.Test.Config.Feedback = Tst_FEEDBACK_FULL_FEEDBACK; // Initialize feedback to maximum - - /***** Check if I can view this match result *****/ - ItsMe = Usr_ItsMe (UsrDat->UsrCod); - switch (Gbl.Usrs.Me.Role.Logged) - { - case Rol_STD: - switch (MeOrOther) - { - case Usr_ME: - ICanViewResult = ItsMe && Match.Status.ShowUsrResults; - if (ICanViewResult) - { - Tst_GetConfigTstFromDB (); // To get feedback type - ICanViewScore = Gbl.Test.Config.Feedback != Tst_FEEDBACK_NOTHING; - } - else - ICanViewScore = false; - break; - default: - ICanViewResult = - ICanViewScore = false; - break; - } - break; - case Rol_NET: - case Rol_TCH: - case Rol_DEG_ADM: - case Rol_CTR_ADM: - case Rol_INS_ADM: - switch (MeOrOther) - { - case Usr_ME: - ICanViewResult = - ICanViewScore = ItsMe; - break; - case Usr_OTHER: - ICanViewResult = - ICanViewScore = true; - break; - default: - ICanViewResult = - ICanViewScore = false; - break; - } - break; - case Rol_SYS_ADM: - ICanViewResult = - ICanViewScore = true; - break; - default: - ICanViewResult = - ICanViewScore = false; - break; - } - - if (ICanViewResult) // I am allowed to view this match result - { - /***** Get questions and user's answers of the match result from database *****/ - Mch_GetMatchResultQuestionsFromDB (Match.MchCod,UsrDat->UsrCod, - &NumQsts,&NumQstsNotBlank); - - /***** Start box *****/ - Box_StartBox (NULL,Txt_Match_result,NULL, - Hlp_ASSESSMENT_Games_results,Box_NOT_CLOSABLE); - Lay_WriteHeaderClassPhoto (false,false, - Gbl.Hierarchy.Ins.InsCod, - Gbl.Hierarchy.Deg.DegCod, - Gbl.Hierarchy.Crs.CrsCod); - - /***** Start table *****/ - Tbl_StartTableWideMargin (10); - - /***** Header row *****/ - /* Get data of the user who answer the match */ - if (!Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (UsrDat,Usr_DONT_GET_PREFS)) - Lay_ShowErrorAndExit ("User does not exists."); - if (!Usr_CheckIfICanViewTst (UsrDat)) - Lay_ShowErrorAndExit ("You can not view this match result."); - - /* User */ - fprintf (Gbl.F.Out,"" - "" - "%s:" - "" - "", - Txt_ROLES_SINGUL_Abc[UsrDat->Roles.InCurrentCrs.Role][UsrDat->Sex]); - ID_WriteUsrIDs (UsrDat,NULL); - fprintf (Gbl.F.Out," %s", - UsrDat->Surname1); - if (UsrDat->Surname2[0]) - fprintf (Gbl.F.Out," %s", - UsrDat->Surname2); - if (UsrDat->FirstName[0]) - fprintf (Gbl.F.Out,", %s", - UsrDat->FirstName); - fprintf (Gbl.F.Out,"
"); - ShowPhoto = Pho_ShowingUsrPhotoIsAllowed (UsrDat,PhotoURL); - Pho_ShowUsrPhoto (UsrDat,ShowPhoto ? PhotoURL : - NULL, - "PHOTO45x60",Pho_ZOOM,false); - fprintf (Gbl.F.Out,"" - ""); - - /* Start/end time (for user in this match) */ - for (StartEndTime = (Dat_StartEndTime_t) 0; - StartEndTime <= (Dat_StartEndTime_t) (Dat_NUM_START_END_TIME - 1); - StartEndTime++) - fprintf (Gbl.F.Out,"" - "" - "%s:" - "" - "" - "" - "" - "", - Txt_START_END_TIME[StartEndTime], - (unsigned) StartEndTime, - (unsigned) StartEndTime, - TimeUTC[StartEndTime], - (unsigned) Gbl.Prefs.DateFormat,Txt_Today); - - /* Number of questions */ - fprintf (Gbl.F.Out,"" - "" - "%s:" - "" - "" - "%u (%u %s)" - "" - "", - Txt_Questions, - NumQsts,NumQstsNotBlank,Txt_non_blank_QUESTIONS); - - /* Score */ - fprintf (Gbl.F.Out,"" - "" - "%s:" - "" - "", - Txt_Score); - if (ICanViewScore) - fprintf (Gbl.F.Out,"%.2lf (%.2lf", - TotalScore, - NumQsts ? TotalScore * Tst_SCORE_MAX / (double) NumQsts : - 0.0); - else - fprintf (Gbl.F.Out,"? (?"); // No feedback - fprintf (Gbl.F.Out," %s %u)" - "", - Txt_out_of_PART_OF_A_SCORE,Tst_SCORE_MAX); - - /* Tags present in this result */ - fprintf (Gbl.F.Out,"" - "" - "%s:" - "" - "", - Txt_Tags); - Gam_ShowTstTagsPresentInAGame (Match.GamCod); - fprintf (Gbl.F.Out,"" - ""); - - /***** Write answers and solutions *****/ - Tst_ShowTestResult (UsrDat,NumQsts,TimeUTC[Dat_START_TIME]); - - /***** End table *****/ - Tbl_EndTable (); - - /***** Write total mark of match result *****/ - if (ICanViewScore) - Tst_ShowTstTotalMark (NumQsts,TotalScore); - - /***** End box *****/ - Box_EndBox (); - } - else // I am not allowed to view this match result - Lay_ShowErrorAndExit ("You can not view this match result."); - } - -/*****************************************************************************/ -/************ Get the questions of a match result from database **************/ -/*****************************************************************************/ - -static void Mch_GetMatchResultQuestionsFromDB (long MchCod,long UsrCod, - unsigned *NumQsts,unsigned *NumQstsNotBlank) - { - MYSQL_RES *mysql_res; - MYSQL_ROW row; - unsigned NumQst; - long LongNum; - unsigned QstInd; - struct Mch_UsrAnswer UsrAnswer; - - /***** Get questions and answers of a match result *****/ - *NumQsts = (unsigned) - DB_QuerySELECT (&mysql_res,"can not get questions and answers" - " of a match result", - "SELECT gam_questions.QstCod," // row[0] - "gam_questions.QstInd," // row[1] - "mch_indexes.Indexes" // row[2] - " FROM mch_matches,gam_questions,mch_indexes" - " WHERE mch_matches.MchCod=%ld" - " AND mch_matches.GamCod=gam_questions.GamCod" - " AND mch_matches.MchCod=mch_indexes.MchCod" - " AND gam_questions.QstInd=mch_indexes.QstInd" - " ORDER BY gam_questions.QstInd", - MchCod); - for (NumQst = 0, *NumQstsNotBlank = 0; - NumQst < *NumQsts; - NumQst++) - { - row = mysql_fetch_row (mysql_res); - - /* Get question code (row[0]) */ - if ((Gbl.Test.QstCodes[NumQst] = Str_ConvertStrCodToLongCod (row[0])) < 0) - Lay_ShowErrorAndExit ("Wrong code of question."); - - /* Get question index (row[1]) */ - if ((LongNum = Str_ConvertStrCodToLongCod (row[1])) < 0) - Lay_ShowErrorAndExit ("Wrong code of question."); - QstInd = (unsigned) LongNum; - - /* Get indexes for this question (row[2]) */ - Str_Copy (Gbl.Test.StrIndexesOneQst[NumQst],row[2], - Tst_MAX_BYTES_INDEXES_ONE_QST); - - /* Get answers selected by user for this question */ - Mch_GetQstAnsFromDB (MchCod,UsrCod,QstInd,&UsrAnswer); - if (UsrAnswer.AnsInd >= 0) // UsrAnswer.AnsInd >= 0 ==> answer selected - { - snprintf (Gbl.Test.StrAnswersOneQst[NumQst],Tst_MAX_BYTES_ANSWERS_ONE_QST + 1, - "%d",UsrAnswer.AnsInd); - (*NumQstsNotBlank)++; - } - else // UsrAnswer.AnsInd < 0 ==> no answer selected - Gbl.Test.StrAnswersOneQst[NumQst][0] = '\0'; // Empty answer - - /* Replace each comma by a separator of multiple parameters */ - /* In database commas are used as separators instead of special chars */ - Par_ReplaceCommaBySeparatorMultiple (Gbl.Test.StrIndexesOneQst[NumQst]); - Par_ReplaceCommaBySeparatorMultiple (Gbl.Test.StrAnswersOneQst[NumQst]); - } - - /***** Free structure that stores the query result *****/ - DB_FreeMySQLResult (&mysql_res); - } - -/*****************************************************************************/ -/************* Get data of a match result using its match code ***************/ -/*****************************************************************************/ - -static void Mch_GetMatchResultDataByMchCod (long MchCod,long UsrCod, - time_t TimeUTC[Dat_NUM_START_END_TIME], - unsigned *NumQsts, - unsigned *NumQstsNotBlank, - double *Score) - { - MYSQL_RES *mysql_res; - MYSQL_ROW row; - Dat_StartEndTime_t StartEndTime; - - /***** Make database query *****/ - if (DB_QuerySELECT (&mysql_res,"can not get data" - " of a match result of a user", - "SELECT UNIX_TIMESTAMP(mch_results.StartTime)," // row[1] - "UNIX_TIMESTAMP(mch_results.EndTime)," // row[2] - "mch_results.NumQsts," // row[3] - "mch_results.NumQstsNotBlank," // row[4] - "mch_results.Score" // row[5] - " FROM mch_results,mch_matches,gam_games" - " WHERE mch_results.MchCod=%ld" - " AND mch_results.UsrCod=%ld" - " AND mch_results.MchCod=mch_matches.MchCod" - " AND mch_matches.GamCod=gam_games.GamCod" - " AND gam_games.CrsCod=%ld", // Extra check - MchCod,UsrCod, - Gbl.Hierarchy.Crs.CrsCod) == 1) - { - row = mysql_fetch_row (mysql_res); - - /* Get start time (row[0] and row[1] hold UTC date-times) */ - for (StartEndTime = (Dat_StartEndTime_t) 0; - StartEndTime <= (Dat_StartEndTime_t) (Dat_NUM_START_END_TIME - 1); - StartEndTime++) - TimeUTC[StartEndTime] = Dat_GetUNIXTimeFromStr (row[StartEndTime]); - - /* Get number of questions (row[2]) */ - if (sscanf (row[2],"%u",NumQsts) != 1) - *NumQsts = 0; - - /* Get number of questions not blank (row[3]) */ - if (sscanf (row[3],"%u",NumQstsNotBlank) != 1) - *NumQstsNotBlank = 0; - - /* Get score (row[4]) */ - Str_SetDecimalPointToUS (); // To get the decimal point as a dot - if (sscanf (row[4],"%lf",Score) != 1) - *Score = 0.0; - Str_SetDecimalPointToLocal (); // Return to local system - } - - /***** Free structure that stores the query result *****/ - DB_FreeMySQLResult (&mysql_res); - } diff --git a/swad_match.h b/swad_match.h index 9bd6918d4..efe2aaab7 100644 --- a/swad_match.h +++ b/swad_match.h @@ -37,11 +37,48 @@ #define Mch_AFTER_LAST_QUESTION ((unsigned)((1UL << 31) - 1)) // 2^31 - 1, don't change this number because it is used in database to indicate that a match is finished +#define Mch_NUM_SHOWING 4 +typedef enum + { + Mch_NOTHING, // Don't show anything + Mch_STEM, // Showing only the question stem + Mch_ANSWERS, // Showing the question stem and the answers + Mch_RESULTS, // Showing the results + } Mch_Showing_t; +#define Mch_SHOWING_DEFAULT Mch_NOTHING + +struct Match + { + long MchCod; + long GamCod; + long UsrCod; + time_t TimeUTC[Dat_NUM_START_END_TIME]; + char Title[Gam_MAX_BYTES_TITLE + 1]; + struct + { + unsigned QstInd; // 0 means that the game has not started. First question has index 1. + long QstCod; + time_t QstStartTimeUTC; + Mch_Showing_t Showing; // What is shown on teacher's screen + bool ShowQstResults; // Show global results of current question while playing + bool ShowUsrResults; // Show exam with results of all questions for the student + bool Playing; // Is being played now? + unsigned NumPlayers; + } Status; // Status related to match playing + }; + +struct Mch_UsrAnswer + { + int NumOpt; // < 0 ==> no answer selected + int AnsInd; // < 0 ==> no answer selected + }; + /*****************************************************************************/ /***************************** Public prototypes *****************************/ /*****************************************************************************/ void Mch_ListMatches (struct Game *Game,bool PutFormNewMatch); +void Mch_GetDataOfMatchByCod (struct Match *Match); void Mch_ToggleVisibilResultsMchUsr (void); @@ -51,6 +88,9 @@ void Mch_RemoveMatchTch (void); void Mch_RemoveMatchesInGameFromAllTables (long GamCod); void Mch_RemoveMatchInCourseFromAllTables (long CrsCod); +void Mch_PutParamMchCod (long MchCod); +long Mch_GetParamMchCod (void); + void Mch_CreateNewMatchTch (void); void Mch_RequestStartResumeMatchTch (void); void Mch_GetIndexes (long MchCod,unsigned QstInd, @@ -65,24 +105,19 @@ void Mch_ToggleVisibilResultsMchQst (void); void Mch_BackMatchTch (void); void Mch_ForwardMatchTch (void); -unsigned Gam_GetNumMchsGame (long GamCod); +unsigned Mch_GetNumMchsInGame (long GamCod); void Mch_GetMatchBeingPlayed (void); void Mch_ShowMatchToMeAsStd (void); void Mch_RefreshMatchTch (void); void Mch_RefreshMatchStd (void); +void Mch_GetQstAnsFromDB (long MchCod,long UsrCod,unsigned QstInd, + struct Mch_UsrAnswer *UsrAnswer); void Mch_ReceiveQstAnsFromStd (void); void Mch_GetAndDrawBarNumUsrsWhoHaveChosenAns (long MchCod,unsigned QstInd,unsigned AnsInd, unsigned NumAnswerersQst,bool Correct); unsigned Mch_GetNumUsrsWhoHaveAnswerQst (long MchCod,unsigned QstInd); -void Mch_PutFormToViewMchResults (Act_Action_t Action); -void Mch_SelDatesToSeeMyMchResults (void); -void Mch_ShowMyMchResults (void); -void Mch_SelUsrsToViewUsrsMchResults (void); -void Mch_ShowUsrsMchResults (void); -void Mch_ShowOneMchResult (void); - #endif diff --git a/swad_match_result.c b/swad_match_result.c new file mode 100644 index 000000000..2cdb0d5d2 --- /dev/null +++ b/swad_match_result.c @@ -0,0 +1,1076 @@ +// swad_match_result.c: matches results in games using remote control + +/* + SWAD (Shared Workspace At a Distance), + is a web platform developed at the University of Granada (Spain), + and used to support university teaching. + + This file is part of SWAD core. + Copyright (C) 1999-2019 Antonio Caņas Vargas + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +/*****************************************************************************/ +/********************************* Headers ***********************************/ +/*****************************************************************************/ + +#define _GNU_SOURCE // For asprintf +#include // For PATH_MAX +#include // For NULL +#include // For asprintf +#include // For calloc +#include // For string functions + +#include "swad_alert.h" +#include "swad_box.h" +#include "swad_database.h" +#include "swad_form.h" +#include "swad_game.h" +#include "swad_global.h" +#include "swad_group.h" +#include "swad_ID.h" +#include "swad_match.h" +#include "swad_match_result.h" +#include "swad_pagination.h" +#include "swad_parameter.h" +#include "swad_role.h" +#include "swad_setting.h" +#include "swad_table.h" +#include "swad_test.h" +#include "swad_user.h" + +/*****************************************************************************/ +/************** External global variables from others modules ****************/ +/*****************************************************************************/ + +extern struct Globals Gbl; + +/*****************************************************************************/ +/***************************** Private constants *****************************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/******************************* Private types *******************************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/***************************** Private constants *****************************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/***************************** Private variables *****************************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/***************************** Private prototypes ****************************/ +/*****************************************************************************/ + +static void Mch_ShowHeaderMchResults (void); +static void Mch_ShowMchResults (Usr_MeOrOther_t MeOrOther); +static void Mch_ShowMchResultsSummaryRow (bool ShowSummaryResults, + unsigned NumResults, + unsigned NumTotalQsts, + unsigned NumTotalQstsNotBlank, + double TotalScoreOfAllResults); +static void Mch_GetMatchResultDataByMchCod (long MchCod,long UsrCod, + time_t TimeUTC[Dat_NUM_START_END_TIME], + unsigned *NumQsts, + unsigned *NumQstsNotBlank, + double *Score); + +static bool Mch_CheckIfICanSeeMatchResult (long MchCod,long UsrCod); +static bool Mch_GetVisibilityMchResultFromDB (long MchCod); + +/*****************************************************************************/ +/****************** Write a form to go to result of matches ******************/ +/*****************************************************************************/ + +void Mch_PutFormToViewMchResults (Act_Action_t Action) + { + extern const char *Txt_Results; + + fprintf (Gbl.F.Out,"
"); + Lay_PutContextualLinkIconText (Action,NULL,NULL, + "tasks.svg", + Txt_Results); + fprintf (Gbl.F.Out,"
"); + } + +/*****************************************************************************/ +/****************** Select dates to show my matches results ******************/ +/*****************************************************************************/ + +void Mch_SelDatesToSeeMyMchResults (void) + { + extern const char *Hlp_ASSESSMENT_Games_results; + extern const char *Txt_Results; + extern const char *Txt_View_matches_results; + + /***** Start form *****/ + Frm_StartForm (ActSeeMyMchRes); + + /***** Start box and table *****/ + Box_StartBoxTable (NULL,Txt_Results,NULL, + Hlp_ASSESSMENT_Games_results,Box_NOT_CLOSABLE,2); + Dat_PutFormStartEndClientLocalDateTimesWithYesterdayToday (false); + + /***** End table, send button and end box *****/ + Box_EndBoxTableWithButton (Btn_CONFIRM_BUTTON,Txt_View_matches_results); + + /***** End form *****/ + Frm_EndForm (); + } + +/*****************************************************************************/ +/*************************** Show my matches results *************************/ +/*****************************************************************************/ + +void Mch_ShowMyMchResults (void) + { + extern const char *Hlp_ASSESSMENT_Games_results; + extern const char *Txt_Results; + + /***** Get starting and ending dates *****/ + Dat_GetIniEndDatesFromForm (); + + /***** Start box and table *****/ + Box_StartBoxTable (NULL,Txt_Results,NULL, + Hlp_ASSESSMENT_Games_results,Box_NOT_CLOSABLE,2); + + /***** Header of the table with the list of users *****/ + Mch_ShowHeaderMchResults (); + + /***** List my matches results *****/ + Tst_GetConfigTstFromDB (); // To get feedback type + Mch_ShowMchResults (Usr_ME); + + /***** End table and box *****/ + Box_EndBoxTable (); + } + +/*****************************************************************************/ +/*********** Select users and dates to show their matches results ************/ +/*****************************************************************************/ + +void Mch_SelUsrsToViewUsrsMchResults (void) + { + extern const char *Hlp_ASSESSMENT_Games_results; + extern const char *The_ClassFormInBox[The_NUM_THEMES]; + extern const char *Txt_Results; + extern const char *Txt_Users; + extern const char *Txt_View_matches_results; + unsigned NumTotalUsrs; + + /***** Get and update type of list, + number of columns in class photo + and preference about viewing photos *****/ + Usr_GetAndUpdatePrefsAboutUsrList (); + + /***** Get groups to show ******/ + Grp_GetParCodsSeveralGrpsToShowUsrs (); + + /***** Get and order lists of users from this course *****/ + Usr_GetListUsrs (Hie_CRS,Rol_STD); + Usr_GetListUsrs (Hie_CRS,Rol_NET); + Usr_GetListUsrs (Hie_CRS,Rol_TCH); + NumTotalUsrs = Gbl.Usrs.LstUsrs[Rol_STD].NumUsrs + + Gbl.Usrs.LstUsrs[Rol_NET].NumUsrs + + Gbl.Usrs.LstUsrs[Rol_TCH].NumUsrs; + + /***** Start box *****/ + Box_StartBox (NULL,Txt_Results,NULL, + Hlp_ASSESSMENT_Games_results,Box_NOT_CLOSABLE); + + /***** Show form to select the groups *****/ + Grp_ShowFormToSelectSeveralGroups (NULL, + Grp_ONLY_MY_GROUPS); + + /***** Start section with user list *****/ + Lay_StartSection (Usr_USER_LIST_SECTION_ID); + + if (NumTotalUsrs) + { + if (Usr_GetIfShowBigList (NumTotalUsrs,NULL,NULL)) + { + /***** Form to select type of list used for select several users *****/ + Usr_ShowFormsToSelectUsrListType (NULL); + + /***** Start form *****/ + Frm_StartForm (ActSeeUsrMchRes); + Grp_PutParamsCodGrps (); + + /***** Put list of users to select some of them *****/ + Tbl_StartTableCenter (2); + fprintf (Gbl.F.Out,"" + "" + "%s:" + "" + "", + The_ClassFormInBox[Gbl.Prefs.Theme],Txt_Users, + The_ClassFormInBox[Gbl.Prefs.Theme]); + Tbl_StartTable (2); + Usr_ListUsersToSelect (Rol_TCH); + Usr_ListUsersToSelect (Rol_NET); + Usr_ListUsersToSelect (Rol_STD); + Tbl_EndTable (); + fprintf (Gbl.F.Out,"" + ""); + + /***** Starting and ending dates in the search *****/ + Dat_PutFormStartEndClientLocalDateTimesWithYesterdayToday (false); + + Tbl_EndTable (); + + /***** Send button *****/ + Btn_PutConfirmButton (Txt_View_matches_results); + + /***** End form *****/ + Frm_EndForm (); + } + } + else // NumTotalUsrs == 0 + /***** Show warning indicating no students found *****/ + Usr_ShowWarningNoUsersFound (Rol_UNK); + + /***** End section with user list *****/ + Lay_EndSection (); + + /***** End box *****/ + Box_EndBox (); + + /***** Free memory for users' list *****/ + Usr_FreeUsrsList (Rol_TCH); + Usr_FreeUsrsList (Rol_NET); + Usr_FreeUsrsList (Rol_STD); + + /***** Free memory used by list of selected users' codes *****/ + Usr_FreeListsSelectedUsrsCods (); + + /***** Free memory for list of selected groups *****/ + Grp_FreeListCodSelectedGrps (); + } + +/*****************************************************************************/ +/****************** Show matches results for several users *******************/ +/*****************************************************************************/ + +void Mch_ShowUsrsMchResults (void) + { + extern const char *Hlp_ASSESSMENT_Games_results; + extern const char *Txt_Results; + extern const char *Txt_You_must_select_one_ore_more_users; + const char *Ptr; + + /***** Get list of the selected users's IDs *****/ + Usr_GetListsSelectedUsrsCods (); + + /***** Get starting and ending dates *****/ + Dat_GetIniEndDatesFromForm (); + + /***** Check the number of users whose matches results will be shown *****/ + if (Usr_CountNumUsrsInListOfSelectedUsrs ()) // If some users are selected... + { + /***** Start box and table *****/ + Box_StartBoxTable (NULL,Txt_Results,NULL, + Hlp_ASSESSMENT_Games_results,Box_NOT_CLOSABLE,2); + + /***** Header of the table with the list of users *****/ + Mch_ShowHeaderMchResults (); + + /***** List the matches results of the selected users *****/ + Ptr = Gbl.Usrs.Selected.List[Rol_UNK]; + while (*Ptr) + { + Par_GetNextStrUntilSeparParamMult (&Ptr,Gbl.Usrs.Other.UsrDat.EncryptedUsrCod, + Cry_BYTES_ENCRYPTED_STR_SHA256_BASE64); + Usr_GetUsrCodFromEncryptedUsrCod (&Gbl.Usrs.Other.UsrDat); + if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat,Usr_DONT_GET_PREFS)) // Get of the database the data of the user + if (Usr_CheckIfICanViewMch (&Gbl.Usrs.Other.UsrDat)) + /***** Show matches results *****/ + Mch_ShowMchResults (Usr_OTHER); + } + + /***** End table and box *****/ + Box_EndBoxTable (); + } + else // If no users are selected... + { + // ...write warning alert + Ale_ShowAlert (Ale_WARNING,Txt_You_must_select_one_ore_more_users); + // ...and show again the form + Mch_SelUsrsToViewUsrsMchResults (); + } + + /***** Free memory used by list of selected users' codes *****/ + Usr_FreeListsSelectedUsrsCods (); + } + +/*****************************************************************************/ +/********************* Show header of my matches results *********************/ +/*****************************************************************************/ + +static void Mch_ShowHeaderMchResults (void) + { + extern const char *Txt_User[Usr_NUM_SEXS]; + extern const char *Txt_START_END_TIME[Dat_NUM_START_END_TIME]; + extern const char *Txt_Questions; + extern const char *Txt_Non_blank_BR_questions; + extern const char *Txt_Total_BR_score; + extern const char *Txt_Average_BR_score_BR_per_question_BR_from_0_to_1; + extern const char *Txt_Score; + extern const char *Txt_out_of_PART_OF_A_SCORE; + + fprintf (Gbl.F.Out,"" + "" + "%s" + "" + "" + "%s" + "" + "" + "%s" + "" + "" + "%s" + "" + "" + "%s" + "" + "" + "%s" + "" + "" + "%s" + "" + "" + "%s
%s
%u" + "" + "" + "", + Txt_User[Usr_SEX_UNKNOWN], + Txt_START_END_TIME[Dat_START_TIME], + Txt_START_END_TIME[Dat_END_TIME], + Txt_Questions, + Txt_Non_blank_BR_questions, + Txt_Total_BR_score, + Txt_Average_BR_score_BR_per_question_BR_from_0_to_1, + Txt_Score,Txt_out_of_PART_OF_A_SCORE,Tst_SCORE_MAX); + } + +/*****************************************************************************/ +/********* Show the matches results of a user in the current course **********/ +/*****************************************************************************/ + +static void Mch_ShowMchResults (Usr_MeOrOther_t MeOrOther) + { + extern const char *Txt_Today; + extern const char *Txt_Match_result; + extern const char *Txt_Hidden_result; + MYSQL_RES *mysql_res; + MYSQL_ROW row; + struct UsrData *UsrDat; + bool ShowResultThisMatch; + bool ShowSummaryResults = true; + unsigned NumResults; + unsigned NumResult; + static unsigned UniqueId = 0; + long MchCod; + Dat_StartEndTime_t StartEndTime; + unsigned NumQstsInThisResult; + unsigned NumQstsNotBlankInThisResult; + unsigned NumTotalQsts = 0; + unsigned NumTotalQstsNotBlank = 0; + double ScoreInThisResult; + double TotalScoreOfAllResults = 0.0; + time_t TimeUTC[Dat_NUM_START_END_TIME]; + char *ClassDat; + + /***** Set user *****/ + UsrDat = (MeOrOther == Usr_ME) ? &Gbl.Usrs.Me.UsrDat : + &Gbl.Usrs.Other.UsrDat; + + /***** Make database query *****/ + NumResults = + (unsigned) DB_QuerySELECT (&mysql_res,"can not get matches results of a user", + "SELECT mch_results.MchCod," // row[0] + "UNIX_TIMESTAMP(mch_results.StartTime)," // row[1] + "UNIX_TIMESTAMP(mch_results.EndTime)," // row[2] + "mch_results.NumQsts," // row[3] + "mch_results.NumQstsNotBlank," // row[4] + "mch_results.Score" // row[5] + " FROM mch_results,mch_matches,gam_games" + " WHERE mch_results.UsrCod=%ld" + " AND mch_results.MchCod=mch_matches.MchCod" + " AND mch_matches.GamCod=gam_games.GamCod" + " AND gam_games.CrsCod=%ld" // Extra check + " AND mch_matches.EndTime>=FROM_UNIXTIME(%ld)" + " AND mch_matches.StartTime<=FROM_UNIXTIME(%ld)" + " ORDER BY MchCod", + UsrDat->UsrCod, + Gbl.Hierarchy.Crs.CrsCod, + (long) Gbl.DateRange.TimeUTC[Dat_START_TIME], + (long) Gbl.DateRange.TimeUTC[Dat_END_TIME]); + + /***** Show user's data *****/ + fprintf (Gbl.F.Out,""); + Usr_ShowTableCellWithUsrData (UsrDat,NumResults); + + /***** Get and print matches results *****/ + if (NumResults) + { + for (NumResult = 0; + NumResult < NumResults; + NumResult++) + { + row = mysql_fetch_row (mysql_res); + + /* Get match code (row[0]) */ + if ((MchCod = Str_ConvertStrCodToLongCod (row[0])) < 0) + Lay_ShowErrorAndExit ("Wrong code of match."); + + /* Show match result? */ + ShowResultThisMatch = Mch_CheckIfICanSeeMatchResult (MchCod,UsrDat->UsrCod); + ShowSummaryResults = ShowSummaryResults && ShowResultThisMatch; + + if (NumResult) + fprintf (Gbl.F.Out,""); + + /* Write start/end times (row[1], row[2] hold UTC start/end times) */ + for (StartEndTime = (Dat_StartEndTime_t) 0; + StartEndTime <= (Dat_StartEndTime_t) (Dat_NUM_START_END_TIME - 1); + StartEndTime++) + { + TimeUTC[StartEndTime] = Dat_GetUNIXTimeFromStr (row[1 + StartEndTime]); + UniqueId++; + fprintf (Gbl.F.Out,"" + "" + "", + (unsigned) StartEndTime,UniqueId, + ClassDat,Gbl.RowEvenOdd, + (unsigned) StartEndTime,UniqueId, + (long) TimeUTC[StartEndTime], + (unsigned) Gbl.Prefs.DateFormat,Txt_Today); + } + + /* Get number of questions (row[3]) */ + if (sscanf (row[3],"%u",&NumQstsInThisResult) != 1) + NumQstsInThisResult = 0; + NumTotalQsts += NumQstsInThisResult; + + /* Get number of questions not blank (row[4]) */ + if (sscanf (row[4],"%u",&NumQstsNotBlankInThisResult) != 1) + NumQstsNotBlankInThisResult = 0; + NumTotalQstsNotBlank += NumQstsNotBlankInThisResult; + + if (ShowResultThisMatch) + { + /* Get score (row[5]) */ + Str_SetDecimalPointToUS (); // To get the decimal point as a dot + if (sscanf (row[5],"%lf",&ScoreInThisResult) != 1) + ScoreInThisResult = 0.0; + Str_SetDecimalPointToLocal (); // Return to local system + TotalScoreOfAllResults += ScoreInThisResult; + } + + /* Write number of questions */ + fprintf (Gbl.F.Out,"%u", + ClassDat,Gbl.RowEvenOdd,NumQstsInThisResult); + + /* Write number of questions not blank */ + fprintf (Gbl.F.Out,"%u", + ClassDat,Gbl.RowEvenOdd,NumQstsNotBlankInThisResult); + + /* Write score */ + fprintf (Gbl.F.Out,"", + ClassDat,Gbl.RowEvenOdd); + if (ShowResultThisMatch) + fprintf (Gbl.F.Out,"%.2lf", + ScoreInThisResult); + fprintf (Gbl.F.Out,""); + + /* Write average score per question */ + fprintf (Gbl.F.Out,"", + ClassDat,Gbl.RowEvenOdd); + if (ShowResultThisMatch) + fprintf (Gbl.F.Out,"%.2lf", + NumQstsInThisResult ? ScoreInThisResult / (double) NumQstsInThisResult : + 0.0); + fprintf (Gbl.F.Out,""); + + /* Write score over Tst_SCORE_MAX */ + fprintf (Gbl.F.Out,"", + ClassDat,Gbl.RowEvenOdd); + if (ShowResultThisMatch) + fprintf (Gbl.F.Out,"%.2lf", + NumQstsInThisResult ? ScoreInThisResult * Tst_SCORE_MAX / (double) NumQstsInThisResult : + 0.0); + fprintf (Gbl.F.Out,""); + + /* Link to show this result */ + fprintf (Gbl.F.Out,"", + Gbl.RowEvenOdd); + if (ShowResultThisMatch) + { + switch (MeOrOther) + { + case Usr_ME: + Frm_StartForm (ActSeeOneMchResMe); + Mch_PutParamMchCod (MchCod); + break; + case Usr_OTHER: + Frm_StartForm (ActSeeOneMchResOth); + Mch_PutParamMchCod (MchCod); + Usr_PutParamOtherUsrCodEncrypted (); + break; + } + Ico_PutIconLink ("tasks.svg",Txt_Match_result); + Frm_EndForm (); + } + else + Ico_PutIconOff ("eye-slash.svg",Txt_Hidden_result); + fprintf (Gbl.F.Out,""); + + fprintf (Gbl.F.Out,""); + } + + /***** Write totals for this user *****/ + Mch_ShowMchResultsSummaryRow (ShowSummaryResults, + NumResults, + NumTotalQsts,NumTotalQstsNotBlank, + TotalScoreOfAllResults); + } + else + { + Tbl_PutEmptyCells (8); + fprintf (Gbl.F.Out,""); + } + + /***** Free structure that stores the query result *****/ + DB_FreeMySQLResult (&mysql_res); + + Gbl.RowEvenOdd = 1 - Gbl.RowEvenOdd; + } + +/*****************************************************************************/ +/************** Show row with summary of user's matches results **************/ +/*****************************************************************************/ + +static void Mch_ShowMchResultsSummaryRow (bool ShowSummaryResults, + unsigned NumResults, + unsigned NumTotalQsts, + unsigned NumTotalQstsNotBlank, + double TotalScoreOfAllResults) + { + extern const char *Txt_Matches; + + /***** Start row *****/ + fprintf (Gbl.F.Out,""); + + /***** Row title *****/ + fprintf (Gbl.F.Out,"" + "%s: %u" + "", + Gbl.RowEvenOdd, + Txt_Matches,NumResults); + + /***** Write total number of questions *****/ + fprintf (Gbl.F.Out,"", + Gbl.RowEvenOdd); + if (NumResults) + fprintf (Gbl.F.Out,"%u",NumTotalQsts); + fprintf (Gbl.F.Out,""); + + /***** Write total number of questions not blank *****/ + fprintf (Gbl.F.Out,"", + Gbl.RowEvenOdd); + if (NumResults) + fprintf (Gbl.F.Out,"%u",NumTotalQstsNotBlank); + fprintf (Gbl.F.Out,""); + + /***** Write total score *****/ + fprintf (Gbl.F.Out,"", + Gbl.RowEvenOdd); + if (ShowSummaryResults) + fprintf (Gbl.F.Out,"%.2lf",TotalScoreOfAllResults); + fprintf (Gbl.F.Out,""); + + /***** Write average score per question *****/ + fprintf (Gbl.F.Out,"", + Gbl.RowEvenOdd); + if (ShowSummaryResults) + fprintf (Gbl.F.Out,"%.2lf", + NumTotalQsts ? TotalScoreOfAllResults / (double) NumTotalQsts : + 0.0); + fprintf (Gbl.F.Out,""); + + /***** Write score over Tst_SCORE_MAX *****/ + fprintf (Gbl.F.Out,"", + Gbl.RowEvenOdd); + if (ShowSummaryResults) + fprintf (Gbl.F.Out,"%.2lf", + NumTotalQsts ? TotalScoreOfAllResults * Tst_SCORE_MAX / + (double) NumTotalQsts : + 0.0); + fprintf (Gbl.F.Out,""); + + /***** Last cell *****/ + fprintf (Gbl.F.Out,"", + Gbl.RowEvenOdd); + + /***** End row *****/ + fprintf (Gbl.F.Out,""); + } + +/*****************************************************************************/ +/******************* Show one match result of another user *******************/ +/*****************************************************************************/ + +void Mch_ShowOneMchResult (void) + { + extern const char *Hlp_ASSESSMENT_Games_results; + extern const char *Txt_Match_result; + extern const char *Txt_ROLES_SINGUL_Abc[Rol_NUM_ROLES][Usr_NUM_SEXS]; + extern const char *Txt_START_END_TIME[Dat_NUM_START_END_TIME]; + extern const char *Txt_Today; + extern const char *Txt_Questions; + extern const char *Txt_non_blank_QUESTIONS; + extern const char *Txt_Score; + extern const char *Txt_out_of_PART_OF_A_SCORE; + extern const char *Txt_Tags; + struct Match Match; + Usr_MeOrOther_t MeOrOther; + struct UsrData *UsrDat; + time_t TimeUTC[Dat_NUM_START_END_TIME]; // Match result UTC date-time + Dat_StartEndTime_t StartEndTime; + unsigned NumQsts; + unsigned NumQstsNotBlank; + double TotalScore; + bool ShowPhoto; + char PhotoURL[PATH_MAX + 1]; + bool ItsMe; + bool ICanViewResult; + bool ICanViewScore; + + /***** Get match code *****/ + if ((Match.MchCod = Mch_GetParamMchCod ()) == -1L) + Lay_ShowErrorAndExit ("Code of match is missing."); + + /***** Get data of the match from database *****/ + Mch_GetDataOfMatchByCod (&Match); + + /***** Pointer to user's data *****/ + MeOrOther = (Gbl.Action.Act == ActSeeOneMchResMe) ? Usr_ME : + Usr_OTHER; + switch (MeOrOther) + { + case Usr_ME: + UsrDat = &Gbl.Usrs.Me.UsrDat; + break; + case Usr_OTHER: + default: + UsrDat = &Gbl.Usrs.Other.UsrDat; + Usr_GetParamOtherUsrCodEncrypted (UsrDat); + break; + } + + /***** Get match result data *****/ + Mch_GetMatchResultDataByMchCod (Match.MchCod,UsrDat->UsrCod, + TimeUTC, + &NumQsts, + &NumQstsNotBlank, + &TotalScore); + Gbl.Test.Config.Feedback = Tst_FEEDBACK_FULL_FEEDBACK; // Initialize feedback to maximum + + /***** Check if I can view this match result *****/ + ItsMe = Usr_ItsMe (UsrDat->UsrCod); + switch (Gbl.Usrs.Me.Role.Logged) + { + case Rol_STD: + switch (MeOrOther) + { + case Usr_ME: + ICanViewResult = ItsMe && Match.Status.ShowUsrResults; + if (ICanViewResult) + { + Tst_GetConfigTstFromDB (); // To get feedback type + ICanViewScore = Gbl.Test.Config.Feedback != Tst_FEEDBACK_NOTHING; + } + else + ICanViewScore = false; + break; + default: + ICanViewResult = + ICanViewScore = false; + break; + } + break; + case Rol_NET: + case Rol_TCH: + case Rol_DEG_ADM: + case Rol_CTR_ADM: + case Rol_INS_ADM: + switch (MeOrOther) + { + case Usr_ME: + ICanViewResult = + ICanViewScore = ItsMe; + break; + case Usr_OTHER: + ICanViewResult = + ICanViewScore = true; + break; + default: + ICanViewResult = + ICanViewScore = false; + break; + } + break; + case Rol_SYS_ADM: + ICanViewResult = + ICanViewScore = true; + break; + default: + ICanViewResult = + ICanViewScore = false; + break; + } + + if (ICanViewResult) // I am allowed to view this match result + { + /***** Get questions and user's answers of the match result from database *****/ + Mch_GetMatchResultQuestionsFromDB (Match.MchCod,UsrDat->UsrCod, + &NumQsts,&NumQstsNotBlank); + + /***** Start box *****/ + Box_StartBox (NULL,Txt_Match_result,NULL, + Hlp_ASSESSMENT_Games_results,Box_NOT_CLOSABLE); + Lay_WriteHeaderClassPhoto (false,false, + Gbl.Hierarchy.Ins.InsCod, + Gbl.Hierarchy.Deg.DegCod, + Gbl.Hierarchy.Crs.CrsCod); + + /***** Start table *****/ + Tbl_StartTableWideMargin (10); + + /***** Header row *****/ + /* Get data of the user who answer the match */ + if (!Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (UsrDat,Usr_DONT_GET_PREFS)) + Lay_ShowErrorAndExit ("User does not exists."); + if (!Usr_CheckIfICanViewTst (UsrDat)) + Lay_ShowErrorAndExit ("You can not view this match result."); + + /* User */ + fprintf (Gbl.F.Out,"" + "" + "%s:" + "" + "", + Txt_ROLES_SINGUL_Abc[UsrDat->Roles.InCurrentCrs.Role][UsrDat->Sex]); + ID_WriteUsrIDs (UsrDat,NULL); + fprintf (Gbl.F.Out," %s", + UsrDat->Surname1); + if (UsrDat->Surname2[0]) + fprintf (Gbl.F.Out," %s", + UsrDat->Surname2); + if (UsrDat->FirstName[0]) + fprintf (Gbl.F.Out,", %s", + UsrDat->FirstName); + fprintf (Gbl.F.Out,"
"); + ShowPhoto = Pho_ShowingUsrPhotoIsAllowed (UsrDat,PhotoURL); + Pho_ShowUsrPhoto (UsrDat,ShowPhoto ? PhotoURL : + NULL, + "PHOTO45x60",Pho_ZOOM,false); + fprintf (Gbl.F.Out,"" + ""); + + /* Start/end time (for user in this match) */ + for (StartEndTime = (Dat_StartEndTime_t) 0; + StartEndTime <= (Dat_StartEndTime_t) (Dat_NUM_START_END_TIME - 1); + StartEndTime++) + fprintf (Gbl.F.Out,"" + "" + "%s:" + "" + "" + "" + "" + "", + Txt_START_END_TIME[StartEndTime], + (unsigned) StartEndTime, + (unsigned) StartEndTime, + TimeUTC[StartEndTime], + (unsigned) Gbl.Prefs.DateFormat,Txt_Today); + + /* Number of questions */ + fprintf (Gbl.F.Out,"" + "" + "%s:" + "" + "" + "%u (%u %s)" + "" + "", + Txt_Questions, + NumQsts,NumQstsNotBlank,Txt_non_blank_QUESTIONS); + + /* Score */ + fprintf (Gbl.F.Out,"" + "" + "%s:" + "" + "", + Txt_Score); + if (ICanViewScore) + fprintf (Gbl.F.Out,"%.2lf (%.2lf", + TotalScore, + NumQsts ? TotalScore * Tst_SCORE_MAX / (double) NumQsts : + 0.0); + else + fprintf (Gbl.F.Out,"? (?"); // No feedback + fprintf (Gbl.F.Out," %s %u)" + "", + Txt_out_of_PART_OF_A_SCORE,Tst_SCORE_MAX); + + /* Tags present in this result */ + fprintf (Gbl.F.Out,"" + "" + "%s:" + "" + "", + Txt_Tags); + Gam_ShowTstTagsPresentInAGame (Match.GamCod); + fprintf (Gbl.F.Out,"" + ""); + + /***** Write answers and solutions *****/ + Tst_ShowTestResult (UsrDat,NumQsts,TimeUTC[Dat_START_TIME]); + + /***** End table *****/ + Tbl_EndTable (); + + /***** Write total mark of match result *****/ + if (ICanViewScore) + Tst_ShowTstTotalMark (NumQsts,TotalScore); + + /***** End box *****/ + Box_EndBox (); + } + else // I am not allowed to view this match result + Lay_ShowErrorAndExit ("You can not view this match result."); + } + +/*****************************************************************************/ +/************ Get the questions of a match result from database **************/ +/*****************************************************************************/ + +void Mch_GetMatchResultQuestionsFromDB (long MchCod,long UsrCod, + unsigned *NumQsts,unsigned *NumQstsNotBlank) + { + MYSQL_RES *mysql_res; + MYSQL_ROW row; + unsigned NumQst; + long LongNum; + unsigned QstInd; + struct Mch_UsrAnswer UsrAnswer; + + /***** Get questions and answers of a match result *****/ + *NumQsts = (unsigned) + DB_QuerySELECT (&mysql_res,"can not get questions and answers" + " of a match result", + "SELECT gam_questions.QstCod," // row[0] + "gam_questions.QstInd," // row[1] + "mch_indexes.Indexes" // row[2] + " FROM mch_matches,gam_questions,mch_indexes" + " WHERE mch_matches.MchCod=%ld" + " AND mch_matches.GamCod=gam_questions.GamCod" + " AND mch_matches.MchCod=mch_indexes.MchCod" + " AND gam_questions.QstInd=mch_indexes.QstInd" + " ORDER BY gam_questions.QstInd", + MchCod); + for (NumQst = 0, *NumQstsNotBlank = 0; + NumQst < *NumQsts; + NumQst++) + { + row = mysql_fetch_row (mysql_res); + + /* Get question code (row[0]) */ + if ((Gbl.Test.QstCodes[NumQst] = Str_ConvertStrCodToLongCod (row[0])) < 0) + Lay_ShowErrorAndExit ("Wrong code of question."); + + /* Get question index (row[1]) */ + if ((LongNum = Str_ConvertStrCodToLongCod (row[1])) < 0) + Lay_ShowErrorAndExit ("Wrong code of question."); + QstInd = (unsigned) LongNum; + + /* Get indexes for this question (row[2]) */ + Str_Copy (Gbl.Test.StrIndexesOneQst[NumQst],row[2], + Tst_MAX_BYTES_INDEXES_ONE_QST); + + /* Get answers selected by user for this question */ + Mch_GetQstAnsFromDB (MchCod,UsrCod,QstInd,&UsrAnswer); + if (UsrAnswer.AnsInd >= 0) // UsrAnswer.AnsInd >= 0 ==> answer selected + { + snprintf (Gbl.Test.StrAnswersOneQst[NumQst],Tst_MAX_BYTES_ANSWERS_ONE_QST + 1, + "%d",UsrAnswer.AnsInd); + (*NumQstsNotBlank)++; + } + else // UsrAnswer.AnsInd < 0 ==> no answer selected + Gbl.Test.StrAnswersOneQst[NumQst][0] = '\0'; // Empty answer + + /* Replace each comma by a separator of multiple parameters */ + /* In database commas are used as separators instead of special chars */ + Par_ReplaceCommaBySeparatorMultiple (Gbl.Test.StrIndexesOneQst[NumQst]); + Par_ReplaceCommaBySeparatorMultiple (Gbl.Test.StrAnswersOneQst[NumQst]); + } + + /***** Free structure that stores the query result *****/ + DB_FreeMySQLResult (&mysql_res); + } + +/*****************************************************************************/ +/************* Get data of a match result using its match code ***************/ +/*****************************************************************************/ + +static void Mch_GetMatchResultDataByMchCod (long MchCod,long UsrCod, + time_t TimeUTC[Dat_NUM_START_END_TIME], + unsigned *NumQsts, + unsigned *NumQstsNotBlank, + double *Score) + { + MYSQL_RES *mysql_res; + MYSQL_ROW row; + Dat_StartEndTime_t StartEndTime; + + /***** Make database query *****/ + if (DB_QuerySELECT (&mysql_res,"can not get data" + " of a match result of a user", + "SELECT UNIX_TIMESTAMP(mch_results.StartTime)," // row[1] + "UNIX_TIMESTAMP(mch_results.EndTime)," // row[2] + "mch_results.NumQsts," // row[3] + "mch_results.NumQstsNotBlank," // row[4] + "mch_results.Score" // row[5] + " FROM mch_results,mch_matches,gam_games" + " WHERE mch_results.MchCod=%ld" + " AND mch_results.UsrCod=%ld" + " AND mch_results.MchCod=mch_matches.MchCod" + " AND mch_matches.GamCod=gam_games.GamCod" + " AND gam_games.CrsCod=%ld", // Extra check + MchCod,UsrCod, + Gbl.Hierarchy.Crs.CrsCod) == 1) + { + row = mysql_fetch_row (mysql_res); + + /* Get start time (row[0] and row[1] hold UTC date-times) */ + for (StartEndTime = (Dat_StartEndTime_t) 0; + StartEndTime <= (Dat_StartEndTime_t) (Dat_NUM_START_END_TIME - 1); + StartEndTime++) + TimeUTC[StartEndTime] = Dat_GetUNIXTimeFromStr (row[StartEndTime]); + + /* Get number of questions (row[2]) */ + if (sscanf (row[2],"%u",NumQsts) != 1) + *NumQsts = 0; + + /* Get number of questions not blank (row[3]) */ + if (sscanf (row[3],"%u",NumQstsNotBlank) != 1) + *NumQstsNotBlank = 0; + + /* Get score (row[4]) */ + Str_SetDecimalPointToUS (); // To get the decimal point as a dot + if (sscanf (row[4],"%lf",Score) != 1) + *Score = 0.0; + Str_SetDecimalPointToLocal (); // Return to local system + } + + /***** Free structure that stores the query result *****/ + DB_FreeMySQLResult (&mysql_res); + } + +/*****************************************************************************/ +/********************* Get if I can see match result ************************/ +/*****************************************************************************/ + +static bool Mch_CheckIfICanSeeMatchResult (long MchCod,long UsrCod) + { + bool ItsMe; + bool ShowResultThisMatch; + + switch (Gbl.Usrs.Me.Role.Logged) + { + case Rol_STD: + ItsMe = Usr_ItsMe (UsrCod); + if (ItsMe && Gbl.Test.Config.Feedback != Tst_FEEDBACK_NOTHING) + ShowResultThisMatch = Mch_GetVisibilityMchResultFromDB (MchCod); + else + ShowResultThisMatch = false; + break; + case Rol_NET: + case Rol_TCH: + case Rol_DEG_ADM: + case Rol_CTR_ADM: + case Rol_INS_ADM: + case Rol_SYS_ADM: + ShowResultThisMatch = true; + break; + default: + ShowResultThisMatch = false; + break; + } + + return ShowResultThisMatch; + } + +/*****************************************************************************/ +/********************* Get visibility of match result ************************/ +/*****************************************************************************/ + +static bool Mch_GetVisibilityMchResultFromDB (long MchCod) + { + MYSQL_RES *mysql_res; + MYSQL_ROW row; + unsigned long NumRows; + bool ShowUsrResults; + + /***** Get visibility of match result from database *****/ + NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get if show result", + "SELECT ShowUsrResults" // row[0] + " FROM mch_matches" + " WHERE MchCod=%ld" + " AND GamCod IN" // Extra check + " (SELECT GamCod FROM gam_games" + " WHERE CrsCod='%ld')", + MchCod, + Gbl.Hierarchy.Crs.CrsCod); + if (NumRows) // Match found... + { + /* Get whether to show user results or not (row(0)) */ + row = mysql_fetch_row (mysql_res); + ShowUsrResults = (row[0][0] == 'Y'); + } + else + ShowUsrResults = false; + + /***** Free structure that stores the query result *****/ + DB_FreeMySQLResult (&mysql_res); + + return ShowUsrResults; + } + diff --git a/swad_match_result.h b/swad_match_result.h new file mode 100644 index 000000000..5bb761c7a --- /dev/null +++ b/swad_match_result.h @@ -0,0 +1,47 @@ +// swad_match_result.h: matches results in games using remote control + +#ifndef _SWAD_MCR +#define _SWAD_MCR +/* + SWAD (Shared Workspace At a Distance in Spanish), + is a web platform developed at the University of Granada (Spain), + and used to support university teaching. + + This file is part of SWAD core. + Copyright (C) 1999-2019 Antonio Caņas Vargas + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +/*****************************************************************************/ +/********************************* Headers ***********************************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/************************** Public types and constants ***********************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/***************************** Public prototypes *****************************/ +/*****************************************************************************/ + +void Mch_PutFormToViewMchResults (Act_Action_t Action); +void Mch_SelDatesToSeeMyMchResults (void); +void Mch_ShowMyMchResults (void); +void Mch_SelUsrsToViewUsrsMchResults (void); +void Mch_ShowUsrsMchResults (void); +void Mch_ShowOneMchResult (void); +void Mch_GetMatchResultQuestionsFromDB (long MchCod,long UsrCod, + unsigned *NumQsts,unsigned *NumQstsNotBlank); + +#endif