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,"
");
- }
-
-/*****************************************************************************/
-/****************** 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,"");
+ }
+
+/*****************************************************************************/
+/****************** 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