// 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_action.h" #include "swad_database.h" #include "swad_date.h" #include "swad_form.h" #include "swad_global.h" #include "swad_HTML.h" #include "swad_ID.h" #include "swad_match.h" #include "swad_match_result.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 McR_ShowUsrsMchResults (void); static void McR_ShowHeaderMchResults (Usr_MeOrOther_t MeOrOther); static void McR_ShowMchResults (Usr_MeOrOther_t MeOrOther); static void McR_ShowMchResultsSummaryRow (bool ShowSummaryResults, unsigned NumResults, unsigned NumTotalQsts, unsigned NumTotalQstsNotBlank, double TotalScoreOfAllResults); static void McR_GetMatchResultDataByMchCod (long MchCod,long UsrCod, time_t TimeUTC[Dat_NUM_START_END_TIME], unsigned *NumQsts, unsigned *NumQstsNotBlank, double *Score); static bool McR_CheckIfICanSeeMatchResult (long MchCod,long UsrCod); static bool McR_GetVisibilityMchResultFromDB (long MchCod); /*****************************************************************************/ /****************** Write a form to go to result of matches ******************/ /*****************************************************************************/ void McR_PutFormToViewMchResults (Act_Action_t Action) { extern const char *Txt_Results; /***** Contextual menu *****/ Mnu_ContextMenuBegin (); Lay_PutContextualLinkIconText (Action,NULL,NULL, "tasks.svg", Txt_Results); // View match results Mnu_ContextMenuEnd (); } /*****************************************************************************/ /****************** Select dates to show my matches results ******************/ /*****************************************************************************/ void McR_SelDatesToSeeMyMchResults (void) { extern const char *Hlp_ASSESSMENT_Games_results; extern const char *Txt_Results; extern const char *Txt_View_matches_results; /***** Begin form *****/ Frm_StartForm (ActSeeMyMchRes); /***** Begin 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 McR_ShowMyMchResults (void) { extern const char *Hlp_ASSESSMENT_Games_results; extern const char *Txt_Results; /***** Get starting and ending dates *****/ Dat_GetIniEndDatesFromForm (); /***** Begin 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 *****/ McR_ShowHeaderMchResults (Usr_ME); /***** List my matches results *****/ Tst_GetConfigTstFromDB (); // To get feedback type McR_ShowMchResults (Usr_ME); /***** End table and box *****/ Box_EndBoxTable (); } /*****************************************************************************/ /*********** Select users and dates to show their matches results ************/ /*****************************************************************************/ void McR_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; /***** Begin box *****/ Box_BoxBegin (NULL,Txt_Results,NULL, Hlp_ASSESSMENT_Games_results,Box_NOT_CLOSABLE); /***** Show form to select the groups *****/ Grp_ShowFormToSelectSeveralGroups (NULL, Grp_MY_GROUPS); /***** Start section with user list *****/ HTM_SECTION_Begin (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); /***** Begin form *****/ Frm_StartForm (ActSeeUsrMchRes); Grp_PutParamsCodGrps (); /***** Put list of users to select some of them *****/ HTM_TABLE_BeginCenterPadding (2); HTM_TR_Begin (NULL); HTM_TD_Begin ("class=\"%s RT\"",The_ClassFormInBox[Gbl.Prefs.Theme]); HTM_TxtF ("%s:",Txt_Users); HTM_TD_End (); HTM_TD_Begin ("colspan=\"2\" class=\"%s LT\"",The_ClassFormInBox[Gbl.Prefs.Theme]); HTM_TABLE_BeginPadding (2); Usr_ListUsersToSelect (Rol_TCH,&Gbl.Usrs.Selected); Usr_ListUsersToSelect (Rol_NET,&Gbl.Usrs.Selected); Usr_ListUsersToSelect (Rol_STD,&Gbl.Usrs.Selected); HTM_TABLE_End (); HTM_TD_End (); HTM_TR_End (); /***** Starting and ending dates in the search *****/ Dat_PutFormStartEndClientLocalDateTimesWithYesterdayToday (false); HTM_TABLE_End (); /***** 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 *****/ HTM_SECTION_End (); /***** End box *****/ Box_BoxEnd (); /***** 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_FreeListsSelectedEncryptedUsrsCods (&Gbl.Usrs.Selected); /***** Free memory for list of selected groups *****/ Grp_FreeListCodSelectedGrps (); } /*****************************************************************************/ /****************** Get users and show their matches results *****************/ /*****************************************************************************/ void McR_GetUsrsAndShowMchResults (void) { Usr_GetSelectedUsrsAndGoToAct (&Gbl.Usrs.Selected, McR_ShowUsrsMchResults, McR_SelUsrsToViewUsrsMchResults); } /*****************************************************************************/ /****************** Show matches results for several users *******************/ /*****************************************************************************/ static void McR_ShowUsrsMchResults (void) { extern const char *Hlp_ASSESSMENT_Games_results; extern const char *Txt_Results; const char *Ptr; /***** Get starting and ending dates *****/ Dat_GetIniEndDatesFromForm (); /***** Begin 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 *****/ McR_ShowHeaderMchResults (Usr_OTHER); /***** 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 *****/ McR_ShowMchResults (Usr_OTHER); } /***** End table and box *****/ Box_EndBoxTable (); } /*****************************************************************************/ /********************* Show header of my matches results *********************/ /*****************************************************************************/ static void McR_ShowHeaderMchResults (Usr_MeOrOther_t MeOrOther) { extern const char *Txt_User[Usr_NUM_SEXS]; extern const char *Txt_Match; 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; HTM_TR_Begin (NULL); HTM_TH (1,2,"CT",Txt_User[MeOrOther == Usr_ME ? Gbl.Usrs.Me.UsrDat.Sex : Usr_SEX_UNKNOWN]); HTM_TH (1,1,"LT",Txt_START_END_TIME[Dat_START_TIME]); HTM_TH (1,1,"LT",Txt_START_END_TIME[Dat_END_TIME]); HTM_TH (1,1,"LT",Txt_Match); HTM_TH (1,1,"RT",Txt_Questions); HTM_TH (1,1,"RT",Txt_Non_blank_BR_questions); HTM_TH (1,1,"RT",Txt_Total_BR_score); HTM_TH (1,1,"RT",Txt_Average_BR_score_BR_per_question_BR_from_0_to_1); HTM_TH_Begin (1,1,"RT"); HTM_Txt (Txt_Score); HTM_BR (); HTM_Txt (Txt_out_of_PART_OF_A_SCORE); HTM_BR (); HTM_Unsigned (Tst_SCORE_MAX); HTM_TH_End (); HTM_TH_Empty (1); HTM_TR_End (); } /*****************************************************************************/ /********* Show the matches results of a user in the current course **********/ /*****************************************************************************/ static void McR_ShowMchResults (Usr_MeOrOther_t MeOrOther) { 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; char *Id; struct Match Match; 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]; /***** 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 *****/ HTM_TR_Begin (NULL); 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 ((Match.MchCod = Str_ConvertStrCodToLongCod (row[0])) < 0) Lay_ShowErrorAndExit ("Wrong code of match."); Mch_GetDataOfMatchByCod (&Match); /* Show match result? */ ShowResultThisMatch = McR_CheckIfICanSeeMatchResult (Match.MchCod,UsrDat->UsrCod); ShowSummaryResults = ShowSummaryResults && ShowResultThisMatch; if (NumResult) HTM_TR_Begin (NULL); /* 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++; if (asprintf (&Id,"mch_time_%u_%u",(unsigned) StartEndTime,UniqueId) < 0) Lay_NotEnoughMemoryExit (); HTM_TD_Begin ("id =\"%s\" class=\"DAT LT COLOR%u\"", Id,Gbl.RowEvenOdd); Dat_WriteLocalDateHMSFromUTC (Id,TimeUTC[StartEndTime], Gbl.Prefs.DateFormat,Dat_SEPARATOR_BREAK, true,true,false,0x7); HTM_TD_End (); free (Id); } /* Write match title */ HTM_TD_Begin ("class=\"DAT LT COLOR%u\"",Gbl.RowEvenOdd); HTM_Txt (Match.Title); HTM_TD_End (); /* 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 */ HTM_TD_Begin ("class=\"DAT RT COLOR%u\"",Gbl.RowEvenOdd); HTM_Unsigned (NumQstsInThisResult); HTM_TD_End (); /* Write number of questions not blank */ HTM_TD_Begin ("class=\"DAT RT COLOR%u\"",Gbl.RowEvenOdd); HTM_Unsigned (NumQstsNotBlankInThisResult); HTM_TD_End (); /* Write score */ HTM_TD_Begin ("class=\"DAT RT COLOR%u\"",Gbl.RowEvenOdd); if (ShowResultThisMatch) HTM_Double (ScoreInThisResult); HTM_TD_End (); /* Write average score per question */ HTM_TD_Begin ("class=\"DAT RT COLOR%u\"",Gbl.RowEvenOdd); if (ShowResultThisMatch) HTM_Double (NumQstsInThisResult ? ScoreInThisResult / (double) NumQstsInThisResult : 0.0); HTM_TD_End (); /* Write score over Tst_SCORE_MAX */ HTM_TD_Begin ("class=\"DAT RT COLOR%u\"",Gbl.RowEvenOdd); if (ShowResultThisMatch) HTM_Double (NumQstsInThisResult ? ScoreInThisResult * Tst_SCORE_MAX / (double) NumQstsInThisResult : 0.0); HTM_TD_End (); /* Link to show this result */ HTM_TD_Begin ("class=\"RT COLOR%u\"",Gbl.RowEvenOdd); if (ShowResultThisMatch) { Gam_SetParamCurrentGamCod (Match.GamCod); // Used to pass parameter Mch_SetParamCurrentMchCod (Match.MchCod); // Used to pass parameter switch (MeOrOther) { case Usr_ME: Frm_StartForm (ActSeeOneMchResMe); Mch_PutParamsEdit (); break; case Usr_OTHER: Frm_StartForm (ActSeeOneMchResOth); Mch_PutParamsEdit (); Usr_PutParamOtherUsrCodEncrypted (); break; } Ico_PutIconLink ("tasks.svg",Txt_Match_result); Frm_EndForm (); } else Ico_PutIconOff ("eye-slash.svg",Txt_Hidden_result); HTM_TD_End (); HTM_TR_End (); } /***** Write totals for this user *****/ McR_ShowMchResultsSummaryRow (ShowSummaryResults, NumResults, NumTotalQsts,NumTotalQstsNotBlank, TotalScoreOfAllResults); } else { HTM_TD_ColouredEmpty (8); HTM_TR_End (); } /***** 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 McR_ShowMchResultsSummaryRow (bool ShowSummaryResults, unsigned NumResults, unsigned NumTotalQsts, unsigned NumTotalQstsNotBlank, double TotalScoreOfAllResults) { extern const char *Txt_Matches; /***** Start row *****/ HTM_TR_Begin (NULL); /***** Row title *****/ HTM_TD_Begin ("colspan=\"3\" class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd); HTM_TxtF ("%s: ",Txt_Matches); HTM_Unsigned (NumResults); HTM_TD_End (); /***** Write total number of questions *****/ HTM_TD_Begin ("class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd); if (NumResults) HTM_Unsigned (NumTotalQsts); HTM_TD_End (); /***** Write total number of questions not blank *****/ HTM_TD_Begin ("class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd); if (NumResults) HTM_Unsigned (NumTotalQstsNotBlank); HTM_TD_End (); /***** Write total score *****/ HTM_TD_Begin ("class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd); if (ShowSummaryResults) HTM_Double (TotalScoreOfAllResults); HTM_TD_End (); /***** Write average score per question *****/ HTM_TD_Begin ("class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd); if (ShowSummaryResults) HTM_Double (NumTotalQsts ? TotalScoreOfAllResults / (double) NumTotalQsts : 0.0); HTM_TD_End (); /***** Write score over Tst_SCORE_MAX *****/ HTM_TD_Begin ("class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd); if (ShowSummaryResults) HTM_Double (NumTotalQsts ? TotalScoreOfAllResults * Tst_SCORE_MAX / (double) NumTotalQsts : 0.0); HTM_TD_End (); /***** Last cell *****/ HTM_TD_Begin ("class=\"DAT_N_LINE_TOP COLOR%u\"",Gbl.RowEvenOdd); HTM_TD_End (); /***** End row *****/ HTM_TR_End (); } /*****************************************************************************/ /******************* Show one match result of another user *******************/ /*****************************************************************************/ void McR_ShowOneMchResult (void) { extern const char *Hlp_ASSESSMENT_Games_results; extern const char *Txt_Match_result; extern const char *Txt_The_user_does_not_exist; 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_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 Game Game; 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; char *Id; unsigned NumQsts; unsigned NumQstsNotBlank; double TotalScore; bool ShowPhoto; char PhotoURL[PATH_MAX + 1]; bool ItsMe; bool ICanPlayThisMatchBasedOnGrps; bool ICanViewResult; bool ICanViewScore; /***** Get and check parameters *****/ Mch_GetAndCheckParameters (&Game,&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 *****/ McR_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: ICanPlayThisMatchBasedOnGrps = Mch_CheckIfICanPlayThisMatchBasedOnGrps (Match.MchCod); ICanViewResult = ItsMe && ICanPlayThisMatchBasedOnGrps && 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 *****/ McR_GetMatchResultQuestionsFromDB (Match.MchCod,UsrDat->UsrCod, &NumQsts,&NumQstsNotBlank); /***** Begin box *****/ Box_BoxBegin (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); /***** Begin table *****/ HTM_TABLE_BeginWideMarginPadding (10); /***** Header row *****/ /* Get data of the user who answer the match */ if (!Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (UsrDat,Usr_DONT_GET_PREFS)) Lay_ShowErrorAndExit (Txt_The_user_does_not_exist); if (!Usr_CheckIfICanViewTst (UsrDat)) Lay_NoPermissionExit (); /* User */ HTM_TR_Begin (NULL); HTM_TD_Begin ("class=\"DAT_N RT\""); HTM_TxtF ("%s:",Txt_ROLES_SINGUL_Abc[UsrDat->Roles.InCurrentCrs.Role][UsrDat->Sex]); HTM_TD_End (); HTM_TD_Begin ("class=\"DAT LT\""); ID_WriteUsrIDs (UsrDat,NULL); HTM_TxtF (" %s",UsrDat->Surname1); if (UsrDat->Surname2[0]) HTM_TxtF (" %s",UsrDat->Surname2); if (UsrDat->FirstName[0]) HTM_TxtF (", %s",UsrDat->FirstName); HTM_BR (); ShowPhoto = Pho_ShowingUsrPhotoIsAllowed (UsrDat,PhotoURL); Pho_ShowUsrPhoto (UsrDat,ShowPhoto ? PhotoURL : NULL, "PHOTO45x60",Pho_ZOOM,false); HTM_TD_End (); HTM_TR_End (); /* 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++) { HTM_TR_Begin (NULL); HTM_TD_Begin ("class=\"DAT_N RT\""); HTM_TxtF ("%s:",Txt_START_END_TIME[StartEndTime]); HTM_TD_End (); if (asprintf (&Id,"match_%u",(unsigned) StartEndTime) < 0) Lay_NotEnoughMemoryExit (); HTM_TD_Begin ("id=\"%s\" class=\"DAT LT\"",Id); Dat_WriteLocalDateHMSFromUTC (Id,TimeUTC[StartEndTime], Gbl.Prefs.DateFormat,Dat_SEPARATOR_COMMA, true,true,true,0x7); HTM_TD_End (); free (Id); HTM_TR_End (); } /* Number of questions */ HTM_TR_Begin (NULL); HTM_TD_Begin ("class=\"DAT_N RT\""); HTM_TxtF ("%s:",Txt_Questions); HTM_TD_End (); HTM_TD_Begin ("class=\"DAT LT\""); HTM_TxtF ("%u (%u %s)",NumQsts,NumQstsNotBlank,Txt_non_blank_QUESTIONS); HTM_TD_End (); HTM_TR_End (); /* Score */ HTM_TR_Begin (NULL); HTM_TD_Begin ("class=\"DAT_N RT\""); HTM_TxtF ("%s:",Txt_Score); HTM_TD_End (); HTM_TD_Begin ("class=\"DAT LT\""); if (ICanViewScore) { HTM_Double (TotalScore); HTM_Txt (" ("); HTM_Double (NumQsts ? TotalScore * Tst_SCORE_MAX / (double) NumQsts : 0.0); } else HTM_Txt ("? (?"); // No feedback HTM_TxtF (" %s %u)",Txt_out_of_PART_OF_A_SCORE,Tst_SCORE_MAX); HTM_TD_End (); HTM_TR_End (); /* Tags present in this result */ HTM_TR_Begin (NULL); HTM_TD_Begin ("class=\"DAT_N RT\""); HTM_TxtF ("%s:",Txt_Tags); HTM_TD_End (); HTM_TD_Begin ("class=\"DAT LT\""); Gam_ShowTstTagsPresentInAGame (Match.GamCod); HTM_TD_End (); HTM_TR_End (); /***** Write answers and solutions *****/ Tst_ShowTestResult (UsrDat,NumQsts,TimeUTC[Dat_START_TIME]); /***** End table *****/ HTM_TABLE_End (); /***** Write total mark of match result *****/ if (ICanViewScore) Tst_ShowTstTotalMark (NumQsts,TotalScore); /***** End box *****/ Box_BoxEnd (); } else // I am not allowed to view this match result Lay_NoPermissionExit (); } /*****************************************************************************/ /************ Get the questions of a match result from database **************/ /*****************************************************************************/ void McR_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 McR_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 McR_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 = McR_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 McR_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; }