swad-core/swad_exam_result.c

1926 lines
65 KiB
C
Raw Normal View History

2020-04-20 01:26:46 +02:00
// swad_exam_result.c: exams results
/*
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-2024 Antonio Ca<EFBFBD>as Vargas
2020-04-20 01:26:46 +02:00
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 <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************/
/********************************* Headers ***********************************/
/*****************************************************************************/
#define _GNU_SOURCE // For asprintf
#include <linux/limits.h> // For PATH_MAX
#include <stddef.h> // For NULL
#include <stdio.h> // For asprintf
#include <stdlib.h> // For free
2020-04-20 01:26:46 +02:00
#include <string.h> // For string functions
#include "swad_action.h"
#include "swad_action_list.h"
#include "swad_box.h"
2020-04-20 01:26:46 +02:00
#include "swad_database.h"
#include "swad_date.h"
#include "swad_error.h"
2020-04-20 01:26:46 +02:00
#include "swad_exam.h"
#include "swad_exam_database.h"
2020-05-23 13:24:08 +02:00
#include "swad_exam_log.h"
2020-05-16 02:04:36 +02:00
#include "swad_exam_print.h"
2020-04-20 01:26:46 +02:00
#include "swad_exam_result.h"
2020-05-17 02:28:30 +02:00
#include "swad_exam_session.h"
2020-05-16 02:04:36 +02:00
#include "swad_exam_set.h"
2020-05-05 21:49:00 +02:00
#include "swad_exam_type.h"
2020-04-20 01:26:46 +02:00
#include "swad_form.h"
#include "swad_global.h"
#include "swad_HTML.h"
#include "swad_ID.h"
#include "swad_parameter.h"
#include "swad_parameter_code.h"
2020-04-20 01:26:46 +02:00
#include "swad_photo.h"
2020-05-16 02:04:36 +02:00
#include "swad_test_print.h"
2020-04-20 01:26:46 +02:00
#include "swad_test_visibility.h"
#include "swad_user.h"
/*****************************************************************************/
/************** External global variables from others modules ****************/
/*****************************************************************************/
extern struct Globals Gbl;
/*****************************************************************************/
/******************************* Private types *******************************/
/*****************************************************************************/
2020-06-19 12:47:51 +02:00
struct ExaRes_ICanView
{
bool Result;
bool Score;
};
2020-04-20 01:26:46 +02:00
/*****************************************************************************/
/***************************** Private prototypes ****************************/
/*****************************************************************************/
2020-05-18 02:38:51 +02:00
static void ExaRes_ListMyResultsInCrs (struct Exa_Exams *Exams);
static void ExaRes_ListMyResultsInExa (struct Exa_Exams *Exams);
static void ExaRes_ListMyResultsInSes (struct Exa_Exams *Exams);
2020-05-18 02:38:51 +02:00
2020-05-18 22:59:07 +02:00
static void ExaRes_PutFormToSelUsrsToViewResults (__attribute__((unused)) void *Args);
2020-05-18 02:38:51 +02:00
static void ExaRes_ShowAllResultsInSelectedExams (void *Exams);
static void ExaRes_ListAllResultsInSelectedExams (struct Exa_Exams *Exams);
static void ExaRes_ListAllResultsInExa (struct Exa_Exams *Exams);
2020-05-18 02:38:51 +02:00
static void ExaRes_ListAllResultsInSes (struct Exa_Exams *Exams,long SesCod);
2020-04-20 01:26:46 +02:00
static void ExaRes_ShowResultsBegin (struct Exa_Exams *Exams,
2020-04-22 03:15:04 +02:00
const char *Title,bool ListExamsToSelect);
2020-04-20 01:26:46 +02:00
static void ExaRes_ShowResultsEnd (void);
static void ExaRes_ListExamsToSelect (struct Exa_Exams *Exams);
2020-05-18 02:38:51 +02:00
static void ExaRes_ShowHeaderResults (Usr_MeOrOther_t MeOrOther);
2020-04-20 01:26:46 +02:00
static void ExaRes_BuildExamsSelectedCommas (struct Exa_Exams *Exams,
char **ExamsSelectedCommas);
2020-05-18 02:38:51 +02:00
static void ExaRes_ShowResults (struct Exa_Exams *Exams,
Usr_MeOrOther_t MeOrOther,
long SesCod, // <= 0 ==> any
long ExaCod, // <= 0 ==> any
const char *ExamsSelectedCommas);
static void ExaRes_ShowResultsSummaryRow (unsigned NumResults,
2020-06-18 20:06:17 +02:00
const struct ExaPrn_NumQuestions *NumTotalQsts,
const struct ExaPrn_Score *TotalScore,
2020-05-18 02:38:51 +02:00
double TotalGrade);
2020-04-20 01:26:46 +02:00
2020-05-23 19:08:59 +02:00
static void ExaRes_ShowExamResult (const struct Exa_Exam *Exam,
const struct ExaSes_Session *Session,
struct ExaPrn_Print *Print,
struct Usr_Data *UsrDat,
2021-01-27 22:40:04 +01:00
struct ExaRes_ICanView *ICanView,
unsigned Visibility);
2020-05-23 19:08:59 +02:00
2021-01-27 22:40:04 +01:00
static void ExaRes_CheckIfICanViewResult (const struct Exa_Exam *Exam,
const struct ExaSes_Session *Session,
long UsrCod,
struct ExaRes_ICanView *ICanView);
2020-04-20 01:26:46 +02:00
2020-06-18 20:06:17 +02:00
static void ExaRes_ComputeValidPrintScore (struct ExaPrn_Print *Print);
2021-01-20 00:42:59 +01:00
static void ExaRes_ShowExamResultTime (struct ExaPrn_Print *Print);
static void ExaRes_ShowExamResultNumQsts (struct ExaPrn_Print *Print,
const struct ExaRes_ICanView *ICanView);
static void ExaRes_ShowExamResultNumAnss (struct ExaPrn_Print *Print,
const struct ExaRes_ICanView *ICanView);
static void ExaRes_ShowExamResultScore (struct ExaPrn_Print *Print,
const struct ExaRes_ICanView *ICanView);
static void ExaRes_ShowExamResultGrade (const struct Exa_Exam *Exam,
struct ExaPrn_Print *Print,
const struct ExaRes_ICanView *ICanView);
static void ExaRes_ShowExamAnswers (struct Usr_Data *UsrDat,
2020-05-16 02:04:36 +02:00
struct ExaPrn_Print *Print,
unsigned Visibility);
static void ExaRes_WriteQstAndAnsExam (struct Usr_Data *UsrDat,
2020-05-16 02:04:36 +02:00
struct ExaPrn_Print *Print,
unsigned QstInd,
struct Qst_Question *Question,
2020-05-16 02:04:36 +02:00
unsigned Visibility);
2020-04-20 01:26:46 +02:00
/*****************************************************************************/
2020-05-17 02:28:30 +02:00
/*************************** Show my sessions results **************************/
2020-04-20 01:26:46 +02:00
/*****************************************************************************/
2020-05-18 02:38:51 +02:00
void ExaRes_ShowMyResultsInCrs (void)
2020-04-20 01:26:46 +02:00
{
extern const char *Txt_Results;
struct Exa_Exams Exams;
2020-04-23 23:09:28 +02:00
/***** Reset exams context *****/
2020-04-22 03:15:04 +02:00
Exa_ResetExams (&Exams);
2020-04-20 01:26:46 +02:00
2020-04-22 03:15:04 +02:00
/***** Get list of exams *****/
Exa_GetListExams (&Exams,Exa_ORDER_BY_TITLE);
Exa_GetListSelectedExaCods (&Exams);
2020-04-20 01:26:46 +02:00
2020-05-17 02:28:30 +02:00
/***** List my sessions results in the current course *****/
2020-04-22 03:15:04 +02:00
ExaRes_ShowResultsBegin (&Exams,Txt_Results,true); // List exams to select
2020-05-18 02:38:51 +02:00
ExaRes_ListMyResultsInCrs (&Exams);
2020-04-20 01:26:46 +02:00
ExaRes_ShowResultsEnd ();
2020-04-22 03:15:04 +02:00
/***** Free list of exams *****/
free (Exams.ExaCodsSelected);
Exa_FreeListExams (&Exams);
2020-04-20 01:26:46 +02:00
}
2020-05-18 02:38:51 +02:00
static void ExaRes_ListMyResultsInCrs (struct Exa_Exams *Exams)
2020-04-20 01:26:46 +02:00
{
char *ExamsSelectedCommas = NULL; // Initialized to avoid warning
/***** Table header *****/
2020-05-18 02:38:51 +02:00
ExaRes_ShowHeaderResults (Usr_ME);
2020-04-20 01:26:46 +02:00
2020-05-17 02:28:30 +02:00
/***** List my sessions results in the current course *****/
TstCfg_GetConfig (); // Get feedback type
2020-04-20 01:26:46 +02:00
ExaRes_BuildExamsSelectedCommas (Exams,&ExamsSelectedCommas);
2020-05-18 02:38:51 +02:00
ExaRes_ShowResults (Exams,Usr_ME,-1L,-1L,ExamsSelectedCommas);
2020-04-20 01:26:46 +02:00
free (ExamsSelectedCommas);
}
/*****************************************************************************/
2020-05-18 02:38:51 +02:00
/******************** Show my results in a given exam ************************/
2020-04-20 01:26:46 +02:00
/*****************************************************************************/
2020-05-18 02:38:51 +02:00
void ExaRes_ShowMyResultsInExa (void)
2020-04-20 01:26:46 +02:00
{
2020-05-14 02:01:14 +02:00
extern const char *Txt_Results_of_exam_X;
2020-04-20 01:26:46 +02:00
struct Exa_Exams Exams;
char *Title;
2020-04-20 01:26:46 +02:00
2020-04-23 23:09:28 +02:00
/***** Reset exams context *****/
2020-04-22 03:15:04 +02:00
Exa_ResetExams (&Exams);
Exa_ResetExam (&Exams.Exam);
2020-04-23 23:09:28 +02:00
2020-04-20 01:26:46 +02:00
/***** Get parameters *****/
Exa_GetPars (&Exams,Exa_CHECK_EXA_COD);
2020-04-25 01:36:53 +02:00
2020-04-26 01:54:27 +02:00
/***** Get exam data from database *****/
Exa_GetExamDataByCod (&Exams.Exam);
2020-04-20 01:26:46 +02:00
/***** Exam begin *****/
Exa_ShowOnlyOneExamBegin (&Exams,Frm_DONT_PUT_FORM);
2020-04-20 01:26:46 +02:00
/***** List my sessions results in exam *****/
if (asprintf (&Title,Txt_Results_of_exam_X,Exams.Exam.Title) < 0)
Err_NotEnoughMemoryExit ();
ExaRes_ShowResultsBegin (&Exams,Title,false); // Do not list exams to select
free (Title);
ExaRes_ListMyResultsInExa (&Exams);
ExaRes_ShowResultsEnd ();
2020-04-20 01:26:46 +02:00
/***** Exam end *****/
2020-04-22 03:15:04 +02:00
Exa_ShowOnlyOneExamEnd ();
2020-04-20 01:26:46 +02:00
}
static void ExaRes_ListMyResultsInExa (struct Exa_Exams *Exams)
2020-04-20 01:26:46 +02:00
{
/***** Table header *****/
2020-05-18 02:38:51 +02:00
ExaRes_ShowHeaderResults (Usr_ME);
2020-04-20 01:26:46 +02:00
2020-05-17 02:28:30 +02:00
/***** List my sessions results in exam *****/
TstCfg_GetConfig (); // Get feedback type
ExaRes_ShowResults (Exams,Usr_ME,-1L,Exams->Exam.ExaCod,NULL);
2020-04-20 01:26:46 +02:00
}
/*****************************************************************************/
2020-05-17 20:12:37 +02:00
/****************** Show my exam results in a given session ******************/
2020-04-20 01:26:46 +02:00
/*****************************************************************************/
2020-05-18 02:38:51 +02:00
void ExaRes_ShowMyResultsInSes (void)
2020-04-20 01:26:46 +02:00
{
2020-05-17 02:28:30 +02:00
extern const char *Txt_Results_of_session_X;
2020-04-20 01:26:46 +02:00
struct Exa_Exams Exams;
2020-05-17 02:28:30 +02:00
struct ExaSes_Session Session;
char *Title;
2020-04-20 01:26:46 +02:00
2020-04-23 23:09:28 +02:00
/***** Reset exams context *****/
2020-04-22 03:15:04 +02:00
Exa_ResetExams (&Exams);
Exa_ResetExam (&Exams.Exam);
2020-05-17 02:28:30 +02:00
ExaSes_ResetSession (&Session);
2020-04-23 23:09:28 +02:00
2020-04-20 01:26:46 +02:00
/***** Get parameters *****/
Exa_GetPars (&Exams,Exa_CHECK_EXA_COD);
Exams.SesCod = Session.SesCod = ParCod_GetAndCheckPar (ParCod_Ses);
Exa_GetExamDataByCod (&Exams.Exam);
ExaSes_GetSessionDataByCod (&Session);
2020-04-20 01:26:46 +02:00
/***** Exam begin *****/
Exa_ShowOnlyOneExamBegin (&Exams,Frm_DONT_PUT_FORM);
/***** List my sessions results in session *****/
if (asprintf (&Title,Txt_Results_of_session_X,Session.Title) < 0)
Err_NotEnoughMemoryExit ();
ExaRes_ShowResultsBegin (&Exams,Title,false); // Do not list exams to select
free (Title);
ExaRes_ListMyResultsInSes (&Exams);
ExaRes_ShowResultsEnd ();
2020-04-20 01:26:46 +02:00
/***** Exam end *****/
2020-04-22 03:15:04 +02:00
Exa_ShowOnlyOneExamEnd ();
2020-04-20 01:26:46 +02:00
}
static void ExaRes_ListMyResultsInSes (struct Exa_Exams *Exams)
2020-04-20 01:26:46 +02:00
{
/***** Table header *****/
2020-05-18 02:38:51 +02:00
ExaRes_ShowHeaderResults (Usr_ME);
2020-04-20 01:26:46 +02:00
2020-05-17 02:28:30 +02:00
/***** List my sessions results in exam *****/
TstCfg_GetConfig (); // Get feedback type
ExaRes_ShowResults (Exams,Usr_ME,Exams->SesCod,-1L,NULL);
2020-05-18 02:38:51 +02:00
}
/*****************************************************************************/
/**************** Select users to show their sessions results *****************/
/*****************************************************************************/
void ExaRes_SelUsrsToViewResults (void)
{
/***** Put form to select users *****/
2020-05-18 22:59:07 +02:00
ExaRes_PutFormToSelUsrsToViewResults (NULL);
2020-05-18 02:38:51 +02:00
}
2020-05-18 22:59:07 +02:00
static void ExaRes_PutFormToSelUsrsToViewResults (__attribute__((unused)) void *Args)
2020-05-18 02:38:51 +02:00
{
extern const char *Hlp_ASSESSMENT_Exams_results;
extern const char *Txt_Results;
2020-05-18 22:59:07 +02:00
extern const char *Txt_View_results;
Usr_PutFormToSelectUsrsToGoToAct (&Gbl.Usrs.Selected,
ActSeeUsrExaResCrs,
NULL,NULL,
Txt_Results,
Hlp_ASSESSMENT_Exams_results,
Txt_View_results,
false); // Do not put form with date range
2020-04-20 01:26:46 +02:00
}
/*****************************************************************************/
2020-05-17 02:28:30 +02:00
/****************** Get users and show their sessions results *****************/
2020-04-20 01:26:46 +02:00
/*****************************************************************************/
2020-05-18 02:38:51 +02:00
void ExaRes_ShowAllResultsInCrs (void)
2020-04-20 01:26:46 +02:00
{
struct Exa_Exams Exams;
2020-04-23 23:09:28 +02:00
/***** Reset exams context *****/
2020-04-22 03:15:04 +02:00
Exa_ResetExams (&Exams);
2020-04-20 01:26:46 +02:00
2020-05-17 02:28:30 +02:00
/***** Get users and show their sessions results *****/
2020-04-20 01:26:46 +02:00
Usr_GetSelectedUsrsAndGoToAct (&Gbl.Usrs.Selected,
2020-05-18 02:38:51 +02:00
ExaRes_ShowAllResultsInSelectedExams,&Exams,
2020-05-18 22:59:07 +02:00
ExaRes_PutFormToSelUsrsToViewResults,NULL);
2020-04-20 01:26:46 +02:00
}
/*****************************************************************************/
2020-05-17 02:28:30 +02:00
/****************** Show sessions results for several users *******************/
2020-04-20 01:26:46 +02:00
/*****************************************************************************/
2020-05-18 02:38:51 +02:00
static void ExaRes_ShowAllResultsInSelectedExams (void *Exams)
2020-04-20 01:26:46 +02:00
{
extern const char *Txt_Results;
if (!Exams)
return;
2020-04-22 03:15:04 +02:00
/***** Get list of exams *****/
Exa_GetListExams ((struct Exa_Exams *) Exams,Exa_ORDER_BY_TITLE);
Exa_GetListSelectedExaCods ((struct Exa_Exams *) Exams);
2020-04-20 01:26:46 +02:00
2020-05-17 02:28:30 +02:00
/***** List the sessions results of the selected users *****/
2020-04-20 01:26:46 +02:00
ExaRes_ShowResultsBegin ((struct Exa_Exams *) Exams,
Txt_Results,
2020-04-22 03:15:04 +02:00
true); // List exams to select
2020-05-18 02:38:51 +02:00
ExaRes_ListAllResultsInSelectedExams ((struct Exa_Exams *) Exams);
2020-04-20 01:26:46 +02:00
ExaRes_ShowResultsEnd ();
2020-04-22 03:15:04 +02:00
/***** Free list of exams *****/
free (((struct Exa_Exams *) Exams)->ExaCodsSelected);
Exa_FreeListExams ((struct Exa_Exams *) Exams);
2020-04-20 01:26:46 +02:00
}
2020-05-18 02:38:51 +02:00
static void ExaRes_ListAllResultsInSelectedExams (struct Exa_Exams *Exams)
2020-04-20 01:26:46 +02:00
{
char *ExamsSelectedCommas = NULL; // Initialized to avoid warning
const char *Ptr;
/***** Table head *****/
2020-05-18 02:38:51 +02:00
ExaRes_ShowHeaderResults (Usr_OTHER);
2020-04-20 01:26:46 +02:00
2020-05-17 02:28:30 +02:00
/***** List the sessions results of the selected users *****/
2020-04-20 01:26:46 +02:00
ExaRes_BuildExamsSelectedCommas (Exams,&ExamsSelectedCommas);
Ptr = Gbl.Usrs.Selected.List[Rol_UNK];
while (*Ptr)
{
Par_GetNextStrUntilSeparParMult (&Ptr,Gbl.Usrs.Other.UsrDat.EnUsrCod,
2020-04-20 01:26:46 +02:00
Cry_BYTES_ENCRYPTED_STR_SHA256_BASE64);
Usr_GetUsrCodFromEncryptedUsrCod (&Gbl.Usrs.Other.UsrDat);
if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat,
Usr_DONT_GET_PREFS,
Usr_DONT_GET_ROLE_IN_CRS))
2020-04-22 03:15:04 +02:00
if (Usr_CheckIfICanViewTstExaMchResult (&Gbl.Usrs.Other.UsrDat))
2020-04-20 01:26:46 +02:00
{
2020-05-17 02:28:30 +02:00
/***** Show sessions results *****/
Gbl.Usrs.Other.UsrDat.Accepted = Enr_CheckIfUsrHasAcceptedInCurrentCrs (&Gbl.Usrs.Other.UsrDat);
2020-05-18 02:38:51 +02:00
ExaRes_ShowResults (Exams,Usr_OTHER,-1L,-1L,ExamsSelectedCommas);
2020-04-20 01:26:46 +02:00
}
}
free (ExamsSelectedCommas);
}
/*****************************************************************************/
2020-05-17 02:28:30 +02:00
/*** Show sessions results of a exam for the users who answered in that exam **/
2020-04-20 01:26:46 +02:00
/*****************************************************************************/
2020-05-18 02:38:51 +02:00
void ExaRes_ShowAllResultsInExa (void)
2020-04-20 01:26:46 +02:00
{
2020-05-14 02:01:14 +02:00
extern const char *Txt_Results_of_exam_X;
2020-04-20 01:26:46 +02:00
struct Exa_Exams Exams;
2020-05-17 02:28:30 +02:00
struct ExaSes_Session Session;
char *Title;
2020-04-20 01:26:46 +02:00
2020-04-23 23:09:28 +02:00
/***** Reset exams context *****/
2020-04-22 03:15:04 +02:00
Exa_ResetExams (&Exams);
Exa_ResetExam (&Exams.Exam);
2020-05-17 02:28:30 +02:00
ExaSes_ResetSession (&Session);
2020-04-23 23:09:28 +02:00
2020-04-20 01:26:46 +02:00
/***** Get parameters *****/
Exa_GetPars (&Exams,Exa_CHECK_EXA_COD);
Exa_GetExamDataByCod (&Exams.Exam);
2020-04-20 01:26:46 +02:00
/***** Exam begin *****/
Exa_ShowOnlyOneExamBegin (&Exams,Frm_DONT_PUT_FORM);
/***** List sessions results in exam *****/
if (asprintf (&Title,Txt_Results_of_exam_X,Exams.Exam.Title) < 0)
Err_NotEnoughMemoryExit ();
ExaRes_ShowResultsBegin (&Exams,Title,false); // Do not list exams to select
free (Title);
ExaRes_ListAllResultsInExa (&Exams);
ExaRes_ShowResultsEnd ();
2020-04-20 01:26:46 +02:00
/***** Exam end *****/
2020-04-22 03:15:04 +02:00
Exa_ShowOnlyOneExamEnd ();
2020-04-20 01:26:46 +02:00
}
static void ExaRes_ListAllResultsInExa (struct Exa_Exams *Exams)
2020-04-20 01:26:46 +02:00
{
MYSQL_RES *mysql_res;
unsigned NumUsrs;
unsigned NumUsr;
2020-04-20 01:26:46 +02:00
/***** Table head *****/
2020-05-18 02:38:51 +02:00
ExaRes_ShowHeaderResults (Usr_OTHER);
2020-04-20 01:26:46 +02:00
2020-05-17 02:28:30 +02:00
/***** Get all users who have answered any session question in this exam *****/
NumUsrs = Exa_DB_GetAllUsrsWhoHaveMadeExam (&mysql_res,Exams->Exam.ExaCod);
/***** List sessions results for each user *****/
for (NumUsr = 0;
NumUsr < NumUsrs;
NumUsr++)
/* Get session code */
if ((Gbl.Usrs.Other.UsrDat.UsrCod = DB_GetNextCode (mysql_res)) > 0)
if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat,
Usr_DONT_GET_PREFS,
Usr_DONT_GET_ROLE_IN_CRS))
if (Usr_CheckIfICanViewTstExaMchResult (&Gbl.Usrs.Other.UsrDat))
{
/***** Show sessions results *****/
Gbl.Usrs.Other.UsrDat.Accepted = Enr_CheckIfUsrHasAcceptedInCurrentCrs (&Gbl.Usrs.Other.UsrDat);
ExaRes_ShowResults (Exams,Usr_OTHER,-1L,Exams->Exam.ExaCod,NULL);
}
2020-04-20 01:26:46 +02:00
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
/*****************************************************************************/
2020-05-17 02:28:30 +02:00
/** Show sessions results of a session for the users who answered in that session */
2020-04-20 01:26:46 +02:00
/*****************************************************************************/
2020-05-18 02:38:51 +02:00
void ExaRes_ShowAllResultsInSes (void)
2020-04-20 01:26:46 +02:00
{
2020-05-17 02:28:30 +02:00
extern const char *Txt_Results_of_session_X;
2020-04-20 01:26:46 +02:00
struct Exa_Exams Exams;
2020-05-17 02:28:30 +02:00
struct ExaSes_Session Session;
char *Title;
2020-04-20 01:26:46 +02:00
2020-04-23 23:09:28 +02:00
/***** Reset exams context *****/
2020-04-22 03:15:04 +02:00
Exa_ResetExams (&Exams);
Exa_ResetExam (&Exams.Exam);
2020-05-17 02:28:30 +02:00
ExaSes_ResetSession (&Session);
2020-04-23 23:09:28 +02:00
2020-04-20 01:26:46 +02:00
/***** Get parameters *****/
Exa_GetPars (&Exams,Exa_CHECK_EXA_COD);
Exams.SesCod = Session.SesCod = ParCod_GetAndCheckPar (ParCod_Ses);
2020-04-25 01:36:53 +02:00
2020-05-17 02:28:30 +02:00
/***** Get exam data and session *****/
Exa_GetExamDataByCod (&Exams.Exam);
ExaSes_GetSessionDataByCod (&Session);
2020-04-20 01:26:46 +02:00
/***** Exam begin *****/
Exa_ShowOnlyOneExamBegin (&Exams,Frm_DONT_PUT_FORM);
/***** List sessions results in session *****/
if (asprintf (&Title,Txt_Results_of_session_X,Session.Title) < 0)
Err_NotEnoughMemoryExit ();
ExaRes_ShowResultsBegin (&Exams,Title,false); // Do not list exams to select
free (Title);
ExaRes_ListAllResultsInSes (&Exams,Session.SesCod);
ExaRes_ShowResultsEnd ();
2020-04-20 01:26:46 +02:00
/***** Exam end *****/
2020-04-22 03:15:04 +02:00
Exa_ShowOnlyOneExamEnd ();
2020-04-20 01:26:46 +02:00
}
2020-05-18 02:38:51 +02:00
static void ExaRes_ListAllResultsInSes (struct Exa_Exams *Exams,long SesCod)
2020-04-20 01:26:46 +02:00
{
MYSQL_RES *mysql_res;
unsigned NumUsrs;
unsigned NumUsr;
2020-04-20 01:26:46 +02:00
/***** Table head *****/
2020-05-18 02:38:51 +02:00
ExaRes_ShowHeaderResults (Usr_OTHER);
2020-04-20 01:26:46 +02:00
2020-05-17 02:28:30 +02:00
/***** Get all users who have answered any session question in this exam *****/
NumUsrs = Exa_DB_GetAllUsrsWhoHaveMadeSession (&mysql_res,SesCod);
/***** List sessions results for each user *****/
for (NumUsr = 0;
NumUsr < NumUsrs;
NumUsr++)
/* Get session code (row[0]) */
if ((Gbl.Usrs.Other.UsrDat.UsrCod = DB_GetNextCode (mysql_res)) > 0)
if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat,
Usr_DONT_GET_PREFS,
Usr_DONT_GET_ROLE_IN_CRS))
if (Usr_CheckIfICanViewTstExaMchResult (&Gbl.Usrs.Other.UsrDat))
{
/***** Show sessions results *****/
Gbl.Usrs.Other.UsrDat.Accepted = Enr_CheckIfUsrHasAcceptedInCurrentCrs (&Gbl.Usrs.Other.UsrDat);
ExaRes_ShowResults (Exams,Usr_OTHER,SesCod,-1L,NULL);
}
2020-04-20 01:26:46 +02:00
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
/*****************************************************************************/
/************************ Show results (begin / end) *************************/
/*****************************************************************************/
static void ExaRes_ShowResultsBegin (struct Exa_Exams *Exams,
2020-04-22 03:15:04 +02:00
const char *Title,bool ListExamsToSelect)
2020-04-20 01:26:46 +02:00
{
2020-04-22 03:15:04 +02:00
extern const char *Hlp_ASSESSMENT_Exams_results;
2020-04-20 01:26:46 +02:00
/***** Begin section *****/
2020-04-20 01:26:46 +02:00
HTM_SECTION_Begin (ExaRes_RESULTS_BOX_ID);
/***** Begin box *****/
Box_BoxBegin (Title,NULL,NULL,
Hlp_ASSESSMENT_Exams_results,Box_NOT_CLOSABLE);
/***** List exams to select *****/
if (ListExamsToSelect)
ExaRes_ListExamsToSelect (Exams);
/***** Begin session results section *****/
HTM_SECTION_Begin (ExaRes_RESULTS_TABLE_ID);
2020-04-20 01:26:46 +02:00
/* Begin session results table */
HTM_TABLE_Begin ("TBL_SCROLL");
2020-04-20 01:26:46 +02:00
}
static void ExaRes_ShowResultsEnd (void)
{
/* End session results table */
HTM_TABLE_End ();
2020-04-20 01:26:46 +02:00
/***** End session results section *****/
HTM_SECTION_End ();
/***** End box *****/
Box_BoxEnd ();
/***** End section *****/
2020-04-20 01:26:46 +02:00
HTM_SECTION_End ();
}
/*****************************************************************************/
2020-05-17 02:28:30 +02:00
/********** Write list of those attendance sessions that have students *********/
2020-04-20 01:26:46 +02:00
/*****************************************************************************/
static void ExaRes_ListExamsToSelect (struct Exa_Exams *Exams)
{
extern const char *Par_CodeStr[];
2020-04-22 03:15:04 +02:00
extern const char *Txt_Exams;
extern const char *Txt_Exam;
2020-04-20 01:26:46 +02:00
extern const char *Txt_Update_results;
unsigned UniqueId;
unsigned NumExam;
2020-04-23 23:09:28 +02:00
/***** Reset exam *****/
Exa_ResetExam (&Exams->Exam);
2020-04-23 23:09:28 +02:00
2020-04-20 01:26:46 +02:00
/***** Begin box *****/
Box_BoxBegin (Txt_Exams,NULL,NULL,NULL,Box_CLOSABLE);
2020-04-20 01:26:46 +02:00
/***** Begin form to update the results
depending on the exams selected *****/
Frm_BeginFormAnchor (Gbl.Action.Act,ExaRes_RESULTS_TABLE_ID);
Grp_PutParsCodGrps ();
Usr_PutParSelectedUsrsCods (&Gbl.Usrs.Selected);
2020-04-20 01:26:46 +02:00
/***** Begin table *****/
HTM_TABLE_BeginWidePadding (2);
2020-04-20 01:26:46 +02:00
/***** Heading row *****/
HTM_TR_Begin (NULL);
HTM_TH_Empty (2);
HTM_TH (Txt_Exam,HTM_HEAD_LEFT);
HTM_TR_End ();
/***** List the sessions *****/
for (NumExam = 0, UniqueId = 1, The_ResetRowColor ();
NumExam < Exams->Num;
NumExam++, UniqueId++, The_ChangeRowColor ())
{
/* Get data of this exam */
Exams->Exam.ExaCod = Exams->Lst[NumExam].ExaCod;
Exa_GetExamDataByCod (&Exams->Exam);
/* Write a row for this session */
HTM_TR_Begin (NULL);
HTM_TD_Begin ("class=\"CT DAT_%s %s\"",
The_GetSuffix (),
The_GetColorRows ());
HTM_INPUT_CHECKBOX (Par_CodeStr[ParCod_Exa],HTM_DONT_SUBMIT_ON_CHANGE,
"id=\"Gam%u\" value=\"%ld\"%s",
NumExam,Exams->Lst[NumExam].ExaCod,
Exams->Lst[NumExam].Selected ? " checked=\"checked\"" :
"");
HTM_TD_End ();
HTM_TD_Begin ("class=\"RT DAT_%s %s\"",
The_GetSuffix (),
The_GetColorRows ());
HTM_LABEL_Begin ("for=\"Gam%u\"",NumExam);
HTM_UnsignedColon (NumExam + 1);
HTM_LABEL_End ();
HTM_TD_End ();
HTM_TD_Begin ("class=\"LT DAT_%s %s\"",
The_GetSuffix (),
The_GetColorRows ());
HTM_Txt (Exams->Exam.Title);
HTM_TD_End ();
HTM_TR_End ();
}
2020-04-20 01:26:46 +02:00
/***** End table *****/
HTM_TABLE_End ();
2020-04-20 01:26:46 +02:00
/***** Put link to refresh *****/
Lay_WriteLinkToUpdate (Txt_Update_results,NULL);
2020-04-20 01:26:46 +02:00
/***** End form *****/
Frm_EndForm ();
2020-04-20 01:26:46 +02:00
/***** End box *****/
Box_BoxEnd ();
}
/*****************************************************************************/
2020-05-17 02:28:30 +02:00
/********************* Show header of my sessions results *********************/
2020-04-20 01:26:46 +02:00
/*****************************************************************************/
2020-05-18 02:38:51 +02:00
static void ExaRes_ShowHeaderResults (Usr_MeOrOther_t MeOrOther)
2020-04-20 01:26:46 +02:00
{
extern const char *Txt_User[Usr_NUM_SEXS];
2020-05-17 02:28:30 +02:00
extern const char *Txt_Session;
2020-04-20 01:26:46 +02:00
extern const char *Txt_START_END_TIME[Dat_NUM_START_END_TIME];
extern const char *Txt_Questions;
2020-06-22 19:27:23 +02:00
extern const char *Txt_Valid_answers;
2020-04-20 01:26:46 +02:00
extern const char *Txt_Score;
extern const char *Txt_Grade;
2020-06-22 19:27:23 +02:00
extern const char *Txt_total;
extern const char *Txt_QUESTIONS_valid;
extern const char *Txt_QUESTIONS_invalid;
2020-06-23 20:28:33 +02:00
extern const char *Txt_ANSWERS_correct;
extern const char *Txt_ANSWERS_wrong;
extern const char *Txt_ANSWERS_blank;
2020-06-22 19:27:23 +02:00
extern const char *Txt_average;
2020-04-20 01:26:46 +02:00
2020-06-22 19:27:23 +02:00
/***** First row *****/
2020-04-20 01:26:46 +02:00
HTM_TR_Begin (NULL);
HTM_TH_Span (Txt_User[MeOrOther == Usr_ME ?
Gbl.Usrs.Me.UsrDat.Sex :
Usr_SEX_UNKNOWN ],HTM_HEAD_CENTER,3,2,"LINE_BOTTOM");
HTM_TH_Span (Txt_START_END_TIME[Dat_STR_TIME] ,HTM_HEAD_LEFT ,3,1,"LINE_BOTTOM");
HTM_TH_Span (Txt_START_END_TIME[Dat_END_TIME] ,HTM_HEAD_LEFT ,3,1,"LINE_BOTTOM");
HTM_TH_Span (Txt_Session ,HTM_HEAD_LEFT ,3,1,"LINE_BOTTOM");
HTM_TH_Span (Txt_Questions ,HTM_HEAD_CENTER,1,3,"LINE_LEFT");
HTM_TH_Span (Txt_Valid_answers ,HTM_HEAD_CENTER,1,5,"LINE_LEFT");
HTM_TH_Span (Txt_Score ,HTM_HEAD_CENTER,1,2,"LINE_LEFT");
HTM_TH_Span (Txt_Grade ,HTM_HEAD_RIGHT ,3,1,"LINE_BOTTOM LINE_LEFT");
HTM_TH_Span (NULL ,HTM_HEAD_CENTER,3,1,"LINE_BOTTOM LINE_LEFT");
2020-06-22 19:27:23 +02:00
HTM_TR_End ();
/***** Second row *****/
HTM_TR_Begin (NULL);
HTM_TH_Span (Txt_total ,HTM_HEAD_RIGHT ,2,1,"LINE_BOTTOM LINE_LEFT");
HTM_TH_Span (Txt_QUESTIONS_valid ,HTM_HEAD_RIGHT ,2,1,"LINE_BOTTOM");
HTM_TH_Span (Txt_QUESTIONS_invalid ,HTM_HEAD_RIGHT ,2,1,"LINE_BOTTOM");
HTM_TH_Span (Txt_ANSWERS_correct ,HTM_HEAD_RIGHT ,1,1,"LINE_LEFT");
HTM_TH_Span (Txt_ANSWERS_wrong ,HTM_HEAD_CENTER,1,3,NULL);
HTM_TH_Span (Txt_ANSWERS_blank ,HTM_HEAD_RIGHT ,1,1,NULL);
HTM_TH_Span (Txt_total ,HTM_HEAD_RIGHT ,1,1,"LINE_LEFT");
HTM_TH_Span (Txt_average ,HTM_HEAD_RIGHT ,1,1,NULL);
2020-06-22 19:27:23 +02:00
HTM_TR_End ();
/***** Third row *****/
HTM_TR_Begin (NULL);
HTM_TH_Span ("{<em>p<sub>i</sub></em>=1}" ,HTM_HEAD_RIGHT ,1,1,"LINE_BOTTOM LINE_LEFT");
HTM_TH_Span ("{-1&le;<em>p<sub>i</sub></em>&lt;0}",HTM_HEAD_RIGHT ,1,1,"LINE_BOTTOM");
HTM_TH_Span ("{<em>p<sub>i</sub></em>=0}" ,HTM_HEAD_RIGHT ,1,1,"LINE_BOTTOM");
HTM_TH_Span ("{0&lt;<em>p<sub>i</sub></em>&lt;1}" ,HTM_HEAD_RIGHT ,1,1,"LINE_BOTTOM");
HTM_TH_Span ("{<em>p<sub>i</sub></em>=0}" ,HTM_HEAD_RIGHT ,1,1,"LINE_BOTTOM");
HTM_TH_Span ("<em>&Sigma;p<sub>i</sub></em>" ,HTM_HEAD_RIGHT ,1,1,"LINE_BOTTOM LINE_LEFT");
HTM_TH_Span ("-1&le;"
"<em style=\"text-decoration:overline;\">p</em>"
"&le;1" ,HTM_HEAD_RIGHT ,1,1,"LINE_BOTTOM");
2020-04-20 01:26:46 +02:00
HTM_TR_End ();
}
/*****************************************************************************/
2020-04-22 03:15:04 +02:00
/******* Build string with list of selected exams separated by commas ********/
/******* from list of selected exams ********/
2020-04-20 01:26:46 +02:00
/*****************************************************************************/
static void ExaRes_BuildExamsSelectedCommas (struct Exa_Exams *Exams,
char **ExamsSelectedCommas)
{
size_t MaxLength;
unsigned NumExam;
char LongStr[Cns_MAX_DECIMAL_DIGITS_LONG + 1];
2020-04-22 03:15:04 +02:00
/***** Allocate memory for subquery of exams selected *****/
2020-04-20 01:26:46 +02:00
MaxLength = (size_t) Exams->NumSelected * (Cns_MAX_DECIMAL_DIGITS_LONG + 1);
if ((*ExamsSelectedCommas = malloc (MaxLength + 1)) == NULL)
Err_NotEnoughMemoryExit ();
2020-04-20 01:26:46 +02:00
2020-04-22 03:15:04 +02:00
/***** Build subquery with list of selected exams *****/
2020-04-20 01:26:46 +02:00
(*ExamsSelectedCommas)[0] = '\0';
for (NumExam = 0;
NumExam < Exams->Num;
NumExam++)
if (Exams->Lst[NumExam].Selected)
{
sprintf (LongStr,"%ld",Exams->Lst[NumExam].ExaCod);
if ((*ExamsSelectedCommas)[0])
Str_Concat (*ExamsSelectedCommas,",",MaxLength);
Str_Concat (*ExamsSelectedCommas,LongStr,MaxLength);
}
}
/*****************************************************************************/
2020-05-17 02:28:30 +02:00
/********* Show the sessions results of a user in the current course *********/
2020-04-20 01:26:46 +02:00
/*****************************************************************************/
2020-05-18 02:38:51 +02:00
static void ExaRes_ShowResults (struct Exa_Exams *Exams,
Usr_MeOrOther_t MeOrOther,
long SesCod, // <= 0 ==> any
long ExaCod, // <= 0 ==> any
const char *ExamsSelectedCommas)
2020-04-20 01:26:46 +02:00
{
MYSQL_RES *mysql_res;
struct Usr_Data *UsrDat;
2020-04-20 01:26:46 +02:00
unsigned NumResults;
unsigned NumResult;
2020-06-19 12:47:51 +02:00
struct ExaRes_ICanView ICanView;
2020-04-20 01:26:46 +02:00
static unsigned UniqueId = 0;
char *Id;
2020-06-18 02:06:02 +02:00
struct ExaPrn_Print Print;
2020-05-17 02:28:30 +02:00
struct ExaSes_Session Session;
2020-06-19 12:47:51 +02:00
struct Exa_Exam Exam;
2020-04-20 01:26:46 +02:00
Dat_StartEndTime_t StartEndTime;
2020-06-22 19:27:23 +02:00
unsigned NumQstsInvalid;
2020-06-18 20:06:17 +02:00
struct ExaPrn_NumQuestions NumTotalQsts;
struct ExaPrn_Score TotalScore;
2020-04-20 01:26:46 +02:00
double Grade;
double TotalGrade = 0.0;
2020-04-23 23:09:28 +02:00
2020-06-18 20:06:17 +02:00
/***** Reset total number of questions and total score *****/
2020-06-22 19:27:23 +02:00
NumTotalQsts.All =
NumTotalQsts.NotBlank =
NumTotalQsts.Valid.Correct =
NumTotalQsts.Valid.Wrong.Negative =
NumTotalQsts.Valid.Wrong.Zero =
NumTotalQsts.Valid.Wrong.Positive =
NumTotalQsts.Valid.Blank =
NumTotalQsts.Valid.Total = 0;
TotalScore.All =
TotalScore.Valid = 0.0;
2020-06-18 20:06:17 +02:00
2020-04-20 01:26:46 +02:00
/***** Make database query *****/
2020-05-29 12:40:07 +02:00
// Do not filter by groups, because a student who has changed groups
// must be able to access exams taken in other groups
NumResults = Exa_DB_GetResults (&mysql_res,MeOrOther,SesCod,ExaCod,ExamsSelectedCommas);
/***** Set user *****/
UsrDat = (MeOrOther == Usr_ME) ? &Gbl.Usrs.Me.UsrDat :
&Gbl.Usrs.Other.UsrDat;
2020-04-20 01:26:46 +02:00
/***** Show user's data *****/
HTM_TR_Begin (NULL);
Usr_ShowTableCellWithUsrData (UsrDat,NumResults);
2020-04-20 01:26:46 +02:00
/***** Get and print sessions results *****/
if (NumResults)
2020-04-20 01:26:46 +02:00
{
for (NumResult = 0;
NumResult < NumResults;
NumResult++)
{
/* Get print code (row[0]) */
if ((Print.PrnCod = DB_GetNextCode (mysql_res)) <= 0)
Err_WrongExamExit ();
2020-04-20 01:26:46 +02:00
/* Get print data */
ExaPrn_GetPrintDataByPrnCod (&Print);
2020-04-20 01:26:46 +02:00
/* Get data of session and exam */
Session.SesCod = Print.SesCod;
ExaSes_GetSessionDataByCod (&Session);
Exam.ExaCod = Session.ExaCod;
Exa_GetExamDataByCod (&Exam);
2020-04-20 01:26:46 +02:00
/* Check if I can view this print result and its score */
ExaRes_CheckIfICanViewResult (&Exam,&Session,UsrDat->UsrCod,&ICanView);
2020-04-20 01:26:46 +02:00
if (NumResult)
HTM_TR_Begin (NULL);
2020-04-20 01:26:46 +02:00
/* Write start/end times */
for (StartEndTime = (Dat_StartEndTime_t) 0;
StartEndTime <= (Dat_StartEndTime_t) (Dat_NUM_START_END_TIME - 1);
StartEndTime++)
{
UniqueId++;
if (asprintf (&Id,"exa_res_time_%u_%u",(unsigned) StartEndTime,UniqueId) < 0)
Err_NotEnoughMemoryExit ();
HTM_TD_Begin ("id =\"%s\" class=\"LT DAT_%s %s\"",
Id,The_GetSuffix (),
The_GetColorRows ());
Dat_WriteLocalDateHMSFromUTC (Id,Print.TimeUTC[StartEndTime],
Gbl.Prefs.DateFormat,Dat_SEPARATOR_BREAK,
true,true,false,0x7);
HTM_TD_End ();
free (Id);
}
2020-06-18 02:06:02 +02:00
/* Write session title */
HTM_TD_Begin ("class=\"LT DAT_%s %s\"",
The_GetSuffix (),
The_GetColorRows ());
HTM_Txt (Session.Title);
HTM_TD_End ();
2020-06-22 19:27:23 +02:00
/* Get and accumulate questions and score */
if (ICanView.Score)
{
/* Get questions and user's answers of exam print from database */
ExaPrn_GetPrintQuestionsFromDB (&Print);
NumTotalQsts.All += Print.NumQsts.All;
/* Compute score taking into account only valid questions */
ExaRes_ComputeValidPrintScore (&Print);
NumTotalQsts.Valid.Correct += Print.NumQsts.Valid.Correct;
NumTotalQsts.Valid.Wrong.Negative += Print.NumQsts.Valid.Wrong.Negative;
NumTotalQsts.Valid.Wrong.Zero += Print.NumQsts.Valid.Wrong.Zero;
NumTotalQsts.Valid.Wrong.Positive += Print.NumQsts.Valid.Wrong.Positive;
NumTotalQsts.Valid.Blank += Print.NumQsts.Valid.Blank;
NumTotalQsts.Valid.Total += Print.NumQsts.Valid.Total;
TotalScore.Valid += Print.Score.Valid;
}
2020-04-20 01:26:46 +02:00
/* Write total number of questions */
HTM_TD_Begin ("class=\"RT DAT_%s LINE_LEFT %s\"",
The_GetSuffix (),
The_GetColorRows ());
if (ICanView.Score)
HTM_Unsigned (Print.NumQsts.All);
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
2020-04-20 01:26:46 +02:00
/* Valid questions */
HTM_TD_Begin ("class=\"RT DAT_GREEN_%s %s\"",
The_GetSuffix (),
The_GetColorRows ());
if (ICanView.Score)
{
if (Print.NumQsts.Valid.Total)
HTM_Unsigned (Print.NumQsts.Valid.Total);
else
HTM_Light0 ();
}
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
2020-06-22 19:27:23 +02:00
/* Invalid questions */
HTM_TD_Begin ("class=\"RT DAT_RED_%s %s\"",
The_GetSuffix (),
The_GetColorRows ());
if (ICanView.Score)
{
NumQstsInvalid = Print.NumQsts.All - Print.NumQsts.Valid.Total;
if (NumQstsInvalid)
HTM_Unsigned (NumQstsInvalid);
else
HTM_Light0 ();
}
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
2020-06-22 19:27:23 +02:00
/* Write number of correct questions */
HTM_TD_Begin ("class=\"RT DAT_%s LINE_LEFT %s\"",
The_GetSuffix (),
The_GetColorRows ());
if (ICanView.Score)
{
if (Print.NumQsts.Valid.Correct)
HTM_Unsigned (Print.NumQsts.Valid.Correct);
else
HTM_Light0 ();
}
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
2020-04-20 01:26:46 +02:00
/* Write number of wrong questions */
HTM_TD_Begin ("class=\"RT DAT_%s %s\"",
The_GetSuffix (),
The_GetColorRows ());
if (ICanView.Score)
{
if (Print.NumQsts.Valid.Wrong.Negative)
HTM_Unsigned (Print.NumQsts.Valid.Wrong.Negative);
else
HTM_Light0 ();
}
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
2020-06-22 19:27:23 +02:00
HTM_TD_Begin ("class=\"RT DAT_%s %s\"",
The_GetSuffix (),
The_GetColorRows ());
if (ICanView.Score)
{
if (Print.NumQsts.Valid.Wrong.Zero)
HTM_Unsigned (Print.NumQsts.Valid.Wrong.Zero);
else
HTM_Light0 ();
}
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
2020-06-22 19:27:23 +02:00
HTM_TD_Begin ("class=\"RT DAT_%s %s\"",
The_GetSuffix (),
The_GetColorRows ());
if (ICanView.Score)
{
if (Print.NumQsts.Valid.Wrong.Positive)
HTM_Unsigned (Print.NumQsts.Valid.Wrong.Positive);
else
HTM_Light0 ();
}
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
2020-04-20 01:26:46 +02:00
/* Write number of blank questions */
HTM_TD_Begin ("class=\"RT DAT_%s %s\"",
The_GetSuffix (),
The_GetColorRows ());
if (ICanView.Score)
{
if (Print.NumQsts.Valid.Blank)
HTM_Unsigned (Print.NumQsts.Valid.Blank);
else
HTM_Light0 ();
}
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
2020-04-20 01:26:46 +02:00
/* Write score valid (taking into account only valid questions) */
HTM_TD_Begin ("class=\"RT DAT_%s LINE_LEFT %s\"",
The_GetSuffix (),
The_GetColorRows ());
if (ICanView.Score)
{
HTM_Double2Decimals (Print.Score.Valid);
HTM_Txt ("/");
HTM_Unsigned (Print.NumQsts.Valid.Total);
}
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
2020-04-20 01:26:46 +02:00
/* Write average score per question (taking into account only valid questions) */
HTM_TD_Begin ("class=\"RT DAT_%s %s\"",
The_GetSuffix (),
The_GetColorRows ());
if (ICanView.Score)
HTM_Double2Decimals (Print.NumQsts.Valid.Total ? Print.Score.Valid /
(double) Print.NumQsts.Valid.Total :
0.0);
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
/* Write grade over maximum grade (taking into account only valid questions) */
HTM_TD_Begin ("class=\"RT DAT_%s LINE_LEFT %s\"",
The_GetSuffix (),
The_GetColorRows ());
if (ICanView.Score)
{
Grade = TstPrn_ComputeGrade (Print.NumQsts.Valid.Total,Print.Score.Valid,Exam.MaxGrade);
TstPrn_ShowGrade (Grade,Exam.MaxGrade);
TotalGrade += Grade;
}
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
2020-04-20 01:26:46 +02:00
/* Link to show this result */
HTM_TD_Begin ("class=\"RT LINE_LEFT %s\"",
The_GetColorRows ());
if (ICanView.Result)
{
Exams->Exam.ExaCod = Session.ExaCod;
Exams->SesCod = Session.SesCod;
switch (MeOrOther)
{
case Usr_ME:
Frm_BeginForm (ActSeeOneExaResMe);
ExaSes_PutParsEdit (Exams);
Ico_PutIconLink ("tasks.svg",Ico_BLACK,ActSeeOneExaResMe);
break;
case Usr_OTHER:
Frm_BeginForm (ActSeeOneExaResOth);
ExaSes_PutParsEdit (Exams);
Usr_PutParOtherUsrCodEncrypted (Gbl.Usrs.Other.UsrDat.EnUsrCod);
Ico_PutIconLink ("tasks.svg",Ico_BLACK,ActSeeOneExaResOth);
break;
}
Frm_EndForm ();
}
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
2020-04-20 01:26:46 +02:00
HTM_TR_End ();
}
2020-06-22 20:26:34 +02:00
/***** Write totals for this user *****/
HTM_TR_Begin (NULL);
ExaRes_ShowResultsSummaryRow (NumResults,&NumTotalQsts,&TotalScore,TotalGrade);
}
else
{
/* Columns for dates and title */
HTM_TD_Begin ("colspan=\"3\" class=\"LINE_BOTTOM %s\"",
The_GetColorRows ());
HTM_TD_End ();
2020-06-22 20:26:34 +02:00
/* Columns for questions */
HTM_TD_Begin ("colspan=\"3\" class=\"LINE_BOTTOM LINE_LEFT %s\"",
The_GetColorRows ());
HTM_TD_End ();
2020-06-22 20:26:34 +02:00
/* Columns for answers */
HTM_TD_Begin ("colspan=\"5\" class=\"LINE_BOTTOM LINE_LEFT %s\"",
The_GetColorRows ());
HTM_TD_End ();
2020-06-22 20:26:34 +02:00
/* Columns for score */
HTM_TD_Begin ("colspan=\"2\" class=\"LINE_BOTTOM LINE_LEFT %s\"",
The_GetColorRows ());
HTM_TD_End ();
2020-06-22 20:26:34 +02:00
/* Column for grade */
HTM_TD_Begin ("class=\"LINE_BOTTOM LINE_LEFT %s\"",
The_GetColorRows ());
HTM_TD_End ();
2020-06-22 20:26:34 +02:00
/* Column for link to show the result */
HTM_TD_Begin ("class=\"LINE_BOTTOM LINE_LEFT %s\"",
The_GetColorRows ());
HTM_TD_End ();
}
/***** End last row *****/
HTM_TR_End ();
2020-04-20 01:26:46 +02:00
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
The_ChangeRowColor ();
2020-04-20 01:26:46 +02:00
}
/*****************************************************************************/
2020-06-18 20:06:17 +02:00
/**************** Show row with summary of user's exam results ***************/
2020-04-20 01:26:46 +02:00
/*****************************************************************************/
2020-05-18 02:38:51 +02:00
static void ExaRes_ShowResultsSummaryRow (unsigned NumResults,
2020-06-18 20:06:17 +02:00
const struct ExaPrn_NumQuestions *NumTotalQsts,
const struct ExaPrn_Score *TotalScore,
2020-05-18 02:38:51 +02:00
double TotalGrade)
2020-04-20 01:26:46 +02:00
{
2020-05-17 02:28:30 +02:00
extern const char *Txt_Sessions;
2020-06-22 19:27:23 +02:00
unsigned NumTotalQstsInvalid;
2020-04-20 01:26:46 +02:00
/***** Row title *****/
HTM_TD_Begin ("colspan=\"3\" class=\"RM DAT_STRONG_%s LINE_TOP LINE_BOTTOM %s\"",
The_GetSuffix (),
The_GetColorRows ());
HTM_TxtColonNBSP (Txt_Sessions);
HTM_Unsigned (NumResults);
HTM_TD_End ();
2020-06-22 19:27:23 +02:00
/***** Write total number of questions *****/
HTM_TD_Begin ("class=\"RM DAT_STRONG_%s LINE_TOP LINE_BOTTOM LINE_LEFT %s\"",
The_GetSuffix (),
The_GetColorRows ());
HTM_Unsigned (NumTotalQsts->All);
HTM_TD_End ();
2020-04-20 01:26:46 +02:00
/***** Write total number of valid questions *****/
HTM_TD_Begin ("class=\"RT DAT_GREEN_%s LINE_TOP LINE_BOTTOM %s\"",
The_GetSuffix (),
The_GetColorRows ());
if (NumTotalQsts->Valid.Total)
HTM_Unsigned (NumTotalQsts->Valid.Total);
else
HTM_Light0 ();
HTM_TD_End ();
/***** Write total number of invalid questions *****/
HTM_TD_Begin ("class=\"RT DAT_RED_%s LINE_TOP LINE_BOTTOM %s\"",
The_GetSuffix (),
The_GetColorRows ());
NumTotalQstsInvalid = NumTotalQsts->All - NumTotalQsts->Valid.Total;
if (NumTotalQstsInvalid)
HTM_Unsigned (NumTotalQstsInvalid);
else
HTM_Light0 ();
HTM_TD_End ();
/***** Write number of correct questions *****/
HTM_TD_Begin ("class=\"RT DAT_STRONG_%s LINE_TOP LINE_BOTTOM LINE_LEFT %s\"",
The_GetSuffix (),
The_GetColorRows ());
if (NumTotalQsts->Valid.Correct)
HTM_Unsigned (NumTotalQsts->Valid.Correct);
else
HTM_Light0 ();
HTM_TD_End ();
/***** Write number of wrong questions *****/
HTM_TD_Begin ("class=\"RT DAT_STRONG_%s LINE_TOP LINE_BOTTOM %s\"",
The_GetSuffix (),
The_GetColorRows ());
if (NumTotalQsts->Valid.Wrong.Negative)
HTM_Unsigned (NumTotalQsts->Valid.Wrong.Negative);
else
HTM_Light0 ();
HTM_TD_End ();
HTM_TD_Begin ("class=\"RT DAT_STRONG_%s LINE_TOP LINE_BOTTOM %s\"",
The_GetSuffix (),
The_GetColorRows ());
if (NumTotalQsts->Valid.Wrong.Zero)
HTM_Unsigned (NumTotalQsts->Valid.Wrong.Zero);
else
HTM_Light0 ();
HTM_TD_End ();
HTM_TD_Begin ("class=\"RT DAT_STRONG_%s LINE_TOP LINE_BOTTOM %s\"",
The_GetSuffix (),
The_GetColorRows ());
if (NumTotalQsts->Valid.Wrong.Positive)
HTM_Unsigned (NumTotalQsts->Valid.Wrong.Positive);
else
HTM_Light0 ();
HTM_TD_End ();
/***** Write number of blank questions *****/
HTM_TD_Begin ("class=\"RT DAT_STRONG_%s LINE_TOP LINE_BOTTOM %s\"",
The_GetSuffix (),
The_GetColorRows ());
if (NumTotalQsts->Valid.Blank)
HTM_Unsigned (NumTotalQsts->Valid.Blank);
else
HTM_Light0 ();
HTM_TD_End ();
/***** Write total valid score *****/
HTM_TD_Begin ("class=\"RM DAT_STRONG_%s LINE_TOP LINE_BOTTOM LINE_LEFT %s\"",
The_GetSuffix (),
The_GetColorRows ());
HTM_Double2Decimals (TotalScore->Valid);
HTM_Txt ("/");
HTM_Unsigned (NumTotalQsts->Valid.Total);
HTM_TD_End ();
/***** Write average valid score per valid question *****/
HTM_TD_Begin ("class=\"RM DAT_STRONG_%s LINE_TOP LINE_BOTTOM %s\"",
The_GetSuffix (),
The_GetColorRows ());
HTM_Double2Decimals (NumTotalQsts->Valid.Total ? TotalScore->Valid /
(double) NumTotalQsts->Valid.Total :
0.0);
HTM_TD_End ();
/***** Write total grade *****/
HTM_TD_Begin ("class=\"RM DAT_STRONG_%s LINE_TOP LINE_BOTTOM LINE_LEFT %s\"",
The_GetSuffix (),
The_GetColorRows ());
HTM_Double2Decimals (TotalGrade);
HTM_TD_End ();
/***** Last cell *****/
HTM_TD_Begin ("class=\"DAT_STRONG_%s LINE_TOP LINE_BOTTOM LINE_LEFT %s\"",
The_GetSuffix (),
The_GetColorRows ());
HTM_TD_End ();
2020-04-20 01:26:46 +02:00
}
2021-01-27 19:37:16 +01:00
/*****************************************************************************/
/************** Show one exam result after finish answering it ***************/
/*****************************************************************************/
void ExaRes_ShowExaResultAfterFinish (void)
{
struct Exa_Exams Exams;
struct ExaSes_Session Session;
struct ExaPrn_Print Print;
2021-01-27 22:40:04 +01:00
struct ExaRes_ICanView ICanView =
{
.Result = true, // I have just finish answering, so show result...
.Score = false, // ...but not score
};
unsigned Visibility = 1 << TstVis_VISIBLE_QST_ANS_TXT; // Show only questions and answers text
2021-01-27 19:37:16 +01:00
/***** Reset exams context *****/
Exa_ResetExams (&Exams);
Exa_ResetExam (&Exams.Exam);
2021-01-27 19:37:16 +01:00
ExaSes_ResetSession (&Session);
/***** Get and check parameters *****/
ExaSes_GetAndCheckPars (&Exams,&Session);
2021-01-27 19:37:16 +01:00
/***** Get exam print data *****/
Print.SesCod = Session.SesCod;
Print.UsrCod = Gbl.Usrs.Me.UsrDat.UsrCod;
ExaPrn_GetPrintDataBySesCodAndUsrCod (&Print);
2021-01-27 19:37:16 +01:00
/***** Set log action and print code *****/
// The user has clicked on the "I have finished" button in an exam print
ExaLog_SetAction (ExaLog_FINISH_EXAM);
ExaLog_SetPrnCod (Print.PrnCod);
ExaLog_SetIfCanAnswer (ExaSes_CheckIfICanAnswerThisSession (&Exams.Exam,&Session));
2021-01-27 19:37:16 +01:00
/***** Get questions and user's answers of exam print from database *****/
ExaPrn_GetPrintQuestionsFromDB (&Print);
/***** Show exam result *****/
ExaRes_ShowExamResult (&Exams.Exam,&Session,&Print,
2021-01-27 22:40:04 +01:00
&Gbl.Usrs.Me.UsrDat,&ICanView,Visibility);
2021-01-27 19:37:16 +01:00
}
2020-04-20 01:26:46 +02:00
/*****************************************************************************/
2020-05-23 19:08:59 +02:00
/*************************** Show one exam result ****************************/
2020-04-20 01:26:46 +02:00
/*****************************************************************************/
void ExaRes_ShowOneExaResult (void)
{
struct Exa_Exams Exams;
2020-05-17 02:28:30 +02:00
struct ExaSes_Session Session;
2020-04-20 01:26:46 +02:00
Usr_MeOrOther_t MeOrOther;
struct Usr_Data *UsrDat;
2020-05-16 02:04:36 +02:00
struct ExaPrn_Print Print;
2021-01-27 22:40:04 +01:00
struct ExaRes_ICanView ICanView;
2020-04-20 01:26:46 +02:00
2020-04-23 23:09:28 +02:00
/***** Reset exams context *****/
2020-04-22 03:15:04 +02:00
Exa_ResetExams (&Exams);
Exa_ResetExam (&Exams.Exam);
2020-05-17 02:28:30 +02:00
ExaSes_ResetSession (&Session);
2020-04-23 23:09:28 +02:00
2020-04-20 01:26:46 +02:00
/***** Get and check parameters *****/
ExaSes_GetAndCheckPars (&Exams,&Session);
2020-04-20 01:26:46 +02:00
/***** Pointer to user's data *****/
2021-01-27 19:37:16 +01:00
MeOrOther = (Gbl.Action.Act == ActSeeOneExaResMe) ? Usr_ME :
Usr_OTHER;
2020-04-20 01:26:46 +02:00
switch (MeOrOther)
{
case Usr_ME:
UsrDat = &Gbl.Usrs.Me.UsrDat;
break;
case Usr_OTHER:
default:
UsrDat = &Gbl.Usrs.Other.UsrDat;
Usr_GetParOtherUsrCodEncrypted (UsrDat);
2020-04-20 01:26:46 +02:00
break;
}
2020-05-23 19:08:59 +02:00
/***** Get exam print data *****/
2020-05-17 02:28:30 +02:00
Print.SesCod = Session.SesCod;
2020-05-16 02:04:36 +02:00
Print.UsrCod = UsrDat->UsrCod;
ExaPrn_GetPrintDataBySesCodAndUsrCod (&Print);
2020-06-22 19:27:23 +02:00
2020-05-23 19:08:59 +02:00
/***** Get questions and user's answers of exam print from database *****/
ExaPrn_GetPrintQuestionsFromDB (&Print);
2021-01-27 22:40:04 +01:00
/***** Check if I can view this print result and its score *****/
ExaRes_CheckIfICanViewResult (&Exams.Exam,&Session,UsrDat->UsrCod,&ICanView);
2021-01-27 22:40:04 +01:00
2020-05-23 19:08:59 +02:00
/***** Show exam result *****/
ExaRes_ShowExamResult (&Exams.Exam,&Session,&Print,
UsrDat,&ICanView,Exams.Exam.Visibility);
2020-05-23 19:08:59 +02:00
/***** Show exam log *****/
2021-01-20 00:42:59 +01:00
switch (Gbl.Usrs.Me.Role.Logged)
{
case Rol_NET:
case Rol_TCH:
case Rol_DEG_ADM:
case Rol_CTR_ADM:
case Rol_INS_ADM:
case Rol_SYS_ADM:
ExaLog_ShowExamLog (&Print);
break;
default: // Other users can not see log
return;
}
2020-05-23 19:08:59 +02:00
}
/*****************************************************************************/
2020-05-23 20:51:51 +02:00
/***************************** Show exam result ******************************/
2020-05-23 19:08:59 +02:00
/*****************************************************************************/
static void ExaRes_ShowExamResult (const struct Exa_Exam *Exam,
const struct ExaSes_Session *Session,
struct ExaPrn_Print *Print,
struct Usr_Data *UsrDat,
2021-01-27 22:40:04 +01:00
struct ExaRes_ICanView *ICanView,
unsigned Visibility)
2020-05-23 19:08:59 +02:00
{
extern const char *Hlp_ASSESSMENT_Exams_results;
2020-04-20 01:26:46 +02:00
2020-06-17 22:19:57 +02:00
/***** Compute score taking into account only valid questions *****/
2021-01-27 22:40:04 +01:00
ExaRes_ComputeValidPrintScore (Print);
2020-06-17 22:19:57 +02:00
2020-05-18 02:15:10 +02:00
/***** Begin box *****/
Box_BoxBegin (Session->Title,NULL,NULL,
2020-05-18 02:15:10 +02:00
Hlp_ASSESSMENT_Exams_results,Box_NOT_CLOSABLE);
2020-04-20 01:26:46 +02:00
/***** Header *****/
Lay_WriteHeaderClassPhoto (Vie_VIEW);
2020-04-20 01:26:46 +02:00
/***** Check user data *****/
/* Get data of the user who answered the exam print */
if (!Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (UsrDat,
Usr_DONT_GET_PREFS,
Usr_DONT_GET_ROLE_IN_CRS))
Err_WrongUserExit ();
if (!Usr_CheckIfICanViewTstExaMchResult (UsrDat))
Err_NoPermissionExit ();
2021-01-20 00:42:59 +01:00
/***** Begin table *****/
HTM_TABLE_BeginWideMarginPadding (10);
2021-01-20 00:42:59 +01:00
/* User */
ExaRes_ShowExamResultUser (UsrDat);
2021-01-20 00:42:59 +01:00
/* Start/end time (for user in this exam print) */
ExaRes_ShowExamResultTime (Print);
2021-01-20 00:42:59 +01:00
/* Number of questions */
ExaRes_ShowExamResultNumQsts (Print,ICanView);
2021-01-20 00:42:59 +01:00
/* Number of answers */
ExaRes_ShowExamResultNumAnss (Print,ICanView);
2021-01-20 00:42:59 +01:00
/* Score */
ExaRes_ShowExamResultScore (Print,ICanView);
2021-01-20 00:42:59 +01:00
/* Grade */
ExaRes_ShowExamResultGrade (Exam,Print,ICanView);
2021-01-20 00:42:59 +01:00
/* Answers and solutions */
if (ICanView->Result)
ExaRes_ShowExamAnswers (UsrDat,Print,Visibility);
/***** End table *****/
HTM_TABLE_End ();
2021-01-20 00:42:59 +01:00
/***** End box *****/
Box_BoxEnd ();
}
/*****************************************************************************/
2021-01-27 22:40:04 +01:00
/********************* Get if I can view print result ************************/
2021-01-20 00:42:59 +01:00
/*****************************************************************************/
2021-01-27 22:40:04 +01:00
static void ExaRes_CheckIfICanViewResult (const struct Exa_Exam *Exam,
const struct ExaSes_Session *Session,
long UsrCod,
struct ExaRes_ICanView *ICanView)
2021-01-20 00:42:59 +01:00
{
/***** Check if I can view print result and score *****/
switch (Gbl.Usrs.Me.Role.Logged)
{
case Rol_STD:
// Depends on visibility of exam, session and result (eye icons)
ICanView->Result = (Usr_ItsMe (UsrCod) == Usr_ME && // The result is mine
Exam->HiddenOrVisible == HidVis_VISIBLE && // The exam is visible
Session->HiddenOrVisible == HidVis_VISIBLE && // The session is visible
Session->ShowUsrResults); // The results of the session are visible to users
2021-01-20 00:42:59 +01:00
// Whether I belong or not to groups of session is not checked here...
// ...because I should be able to see old exams made in old groups to which I belonged
if (ICanView->Result)
// Depends on 5 visibility icons associated to exam
ICanView->Score = TstVis_IsVisibleTotalScore (Exam->Visibility);
else
ICanView->Score = false;
break;
case Rol_NET:
case Rol_TCH:
case Rol_DEG_ADM:
case Rol_CTR_ADM:
case Rol_INS_ADM:
case Rol_SYS_ADM:
ICanView->Result =
ICanView->Score = true;
break;
default:
ICanView->Result =
ICanView->Score = false;
break;
}
}
/*****************************************************************************/
/****** Compute total score of exam print counting only valid questions ******/
/*****************************************************************************/
static void ExaRes_ComputeValidPrintScore (struct ExaPrn_Print *Print)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned QstInd;
struct Qst_Question Question;
2021-01-20 00:42:59 +01:00
bool QuestionExists;
/***** Initialize score valid *****/
Print->NumQsts.Valid.Correct =
Print->NumQsts.Valid.Wrong.Negative =
Print->NumQsts.Valid.Wrong.Zero =
Print->NumQsts.Valid.Wrong.Positive =
Print->NumQsts.Valid.Blank =
Print->NumQsts.Valid.Total = 0;
Print->Score.Valid = 0.0;
for (QstInd = 0;
QstInd < Print->NumQsts.All;
QstInd++)
2021-01-20 00:42:59 +01:00
{
/***** Copy question code *****/
Question.QstCod = Print->PrintedQuestions[QstInd].QstCod;
2021-01-20 00:42:59 +01:00
/***** Get validity and answer type from database *****/
if ((QuestionExists = (Exa_DB_GetValidityAndAnswerType (&mysql_res,Question.QstCod) != 0)))
2021-01-20 00:42:59 +01:00
{
row = mysql_fetch_row (mysql_res);
/* Get whether the question is invalid (row[0]) */
Question.Validity = (row[0][0] == 'Y') ? Qst_INVALID_QUESTION :
Qst_VALID_QUESTION;
2021-01-20 00:42:59 +01:00
/* Get the type of answer (row[1]) */
Question.Answer.Type = Qst_ConvertFromStrAnsTypDBToAnsTyp (row[1]);
2021-01-20 00:42:59 +01:00
}
/* Free structure that stores the query result */
DB_FreeMySQLResult (&mysql_res);
/***** Compute answer score *****/
if (QuestionExists)
if (Question.Validity == Qst_VALID_QUESTION)
2021-01-20 00:42:59 +01:00
{
ExaPrn_ComputeAnswerScore (&Print->PrintedQuestions[QstInd],&Question);
switch (Print->PrintedQuestions[QstInd].AnswerIsCorrect)
2021-01-20 00:42:59 +01:00
{
case TstPrn_ANSWER_IS_CORRECT:
Print->NumQsts.Valid.Correct++;
break;
case TstPrn_ANSWER_IS_WRONG_NEGATIVE:
Print->NumQsts.Valid.Wrong.Negative++;
break;
case TstPrn_ANSWER_IS_WRONG_ZERO:
Print->NumQsts.Valid.Wrong.Zero++;
break;
case TstPrn_ANSWER_IS_WRONG_POSITIVE:
Print->NumQsts.Valid.Wrong.Positive++;
break;
case TstPrn_ANSWER_IS_BLANK:
Print->NumQsts.Valid.Blank++;
break;
}
Print->NumQsts.Valid.Total++;
Print->Score.Valid += Print->PrintedQuestions[QstInd].Score;
2021-01-20 00:42:59 +01:00
}
}
}
/*****************************************************************************/
/************************ Show user row in exam result ***********************/
/*****************************************************************************/
void ExaRes_ShowExamResultUser (struct Usr_Data *UsrDat)
2021-01-20 00:42:59 +01:00
{
extern const char *Txt_ROLES_SINGUL_Abc[Rol_NUM_ROLES][Usr_NUM_SEXS];
static const char *ClassPhoto[PhoSha_NUM_SHAPES] =
{
[PhoSha_SHAPE_CIRCLE ] = "PHOTOC45x60",
[PhoSha_SHAPE_ELLIPSE ] = "PHOTOE45x60",
[PhoSha_SHAPE_OVAL ] = "PHOTOO45x60",
[PhoSha_SHAPE_RECTANGLE] = "PHOTOR45x60",
};
2021-01-20 00:42:59 +01:00
/***** Row begin *****/
2020-05-18 02:15:10 +02:00
HTM_TR_Begin (NULL);
2020-04-20 01:26:46 +02:00
/***** Label *****/
HTM_TD_Begin ("class=\"RT DAT_STRONG_%s\"",The_GetSuffix ());
HTM_TxtColon (Txt_ROLES_SINGUL_Abc[UsrDat->Roles.InCurrentCrs][UsrDat->Sex]);
HTM_TD_End ();
2020-04-20 01:26:46 +02:00
/***** User's data *****/
HTM_TD_Begin ("class=\"LB DAT_%s\"",The_GetSuffix ());
ID_WriteUsrIDs (UsrDat,NULL);
HTM_NBSPTxt (UsrDat->Surname1);
if (UsrDat->Surname2[0])
HTM_SPTxt (UsrDat->Surname2);
if (UsrDat->FrstName[0])
{
HTM_Comma ();
HTM_SPTxt (UsrDat->FrstName);
}
HTM_BR ();
Pho_ShowUsrPhotoIfAllowed (UsrDat,
ClassPhoto[Gbl.Prefs.PhotoShape],Pho_ZOOM);
HTM_TD_End ();
2020-04-20 01:26:46 +02:00
2021-01-20 00:42:59 +01:00
/***** Row end *****/
2020-05-18 02:15:10 +02:00
HTM_TR_End ();
2021-01-20 00:42:59 +01:00
}
/*****************************************************************************/
/********************* Show start/end time in exam print *********************/
/*****************************************************************************/
static void ExaRes_ShowExamResultTime (struct ExaPrn_Print *Print)
{
extern const char *Txt_START_END_TIME[Dat_NUM_START_END_TIME];
Dat_StartEndTime_t StartEndTime;
char *Id;
2020-04-20 01:26:46 +02:00
2020-05-18 02:15:10 +02:00
for (StartEndTime = (Dat_StartEndTime_t) 0;
StartEndTime <= (Dat_StartEndTime_t) (Dat_NUM_START_END_TIME - 1);
StartEndTime++)
{
2021-01-20 00:42:59 +01:00
/***** Row begin *****/
2020-04-20 01:26:46 +02:00
HTM_TR_Begin (NULL);
/***** Label *****/
HTM_TD_Begin ("class=\"RT DAT_STRONG_%s\"",
The_GetSuffix ());
HTM_TxtColon (Txt_START_END_TIME[StartEndTime]);
HTM_TD_End ();
2020-04-20 01:26:46 +02:00
/***** Time *****/
if (asprintf (&Id,"match_%u",(unsigned) StartEndTime) < 0)
Err_NotEnoughMemoryExit ();
HTM_TD_Begin ("id=\"%s\" class=\"LB DAT_%s\"",
Id,The_GetSuffix ());
Dat_WriteLocalDateHMSFromUTC (Id,Print->TimeUTC[StartEndTime],
Gbl.Prefs.DateFormat,Dat_SEPARATOR_COMMA,
true,true,true,0x7);
HTM_TD_End ();
free (Id);
2020-04-20 01:26:46 +02:00
2021-01-20 00:42:59 +01:00
/***** Row end *****/
2020-04-20 01:26:46 +02:00
HTM_TR_End ();
2020-05-18 02:15:10 +02:00
}
2021-01-20 00:42:59 +01:00
}
2020-04-20 01:26:46 +02:00
2021-01-20 00:42:59 +01:00
/*****************************************************************************/
/******************* Show number of questions in exam print ******************/
/*****************************************************************************/
static void ExaRes_ShowExamResultNumQsts (struct ExaPrn_Print *Print,
const struct ExaRes_ICanView *ICanView)
{
extern const char *Txt_Questions;
extern const char *Txt_QUESTIONS_valid;
extern const char *Txt_QUESTIONS_invalid;
/***** Row begin *****/
2020-05-18 02:15:10 +02:00
HTM_TR_Begin (NULL);
2020-04-20 01:26:46 +02:00
/***** Label *****/
HTM_TD_Begin ("class=\"RT DAT_STRONG_%s\"",The_GetSuffix ());
HTM_TxtColon (Txt_Questions);
HTM_TD_End ();
2020-04-20 01:26:46 +02:00
/***** Number of questions *****/
HTM_TD_Begin ("class=\"LB DAT_%s\"",The_GetSuffix ());
if (ICanView->Result)
{
HTM_TxtF ("%u",Print->NumQsts.All);
if (Print->NumQsts.All != Print->NumQsts.Valid.Total)
{
HTM_Txt (" (");
2020-06-18 20:06:17 +02:00
/* Valid questions */
HTM_SPAN_Begin ("class=\"DAT_GREEN_%s\"",The_GetSuffix ());
HTM_TxtColonNBSP (Txt_QUESTIONS_valid);
HTM_Unsigned (Print->NumQsts.Valid.Total);
HTM_SPAN_End ();
2020-06-18 20:06:17 +02:00
HTM_TxtF ("; ");
2020-06-18 20:06:17 +02:00
/* Invalid questions */
HTM_SPAN_Begin ("class=\"DAT_RED_%s\"",The_GetSuffix ());
HTM_TxtColonNBSP (Txt_QUESTIONS_invalid);
HTM_Unsigned (Print->NumQsts.All - Print->NumQsts.Valid.Total);
HTM_SPAN_End ();
2020-06-18 20:06:17 +02:00
HTM_Txt (")");
}
}
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
2020-04-20 01:26:46 +02:00
2021-01-20 00:42:59 +01:00
/***** Row end *****/
2020-05-18 02:15:10 +02:00
HTM_TR_End ();
2021-01-20 00:42:59 +01:00
}
2020-04-20 01:26:46 +02:00
2021-01-20 00:42:59 +01:00
/*****************************************************************************/
/******************** Show number of answers in exam print *******************/
/*****************************************************************************/
static void ExaRes_ShowExamResultNumAnss (struct ExaPrn_Print *Print,
const struct ExaRes_ICanView *ICanView)
{
extern const char *Txt_Valid_answers;
extern const char *Txt_ANSWERS_correct;
extern const char *Txt_ANSWERS_wrong;
extern const char *Txt_ANSWERS_blank;
/***** Row begin *****/
2020-06-22 19:27:23 +02:00
HTM_TR_Begin (NULL);
/***** Label *****/
HTM_TD_Begin ("class=\"RT DAT_STRONG_%s\"",The_GetSuffix ());
HTM_TxtColon (Txt_Valid_answers);
HTM_TD_End ();
/***** Number of answers *****/
HTM_TD_Begin ("class=\"LB DAT_%s\"",The_GetSuffix ());
if (ICanView->Score)
HTM_TxtF ("%s(<em>p<sub>i</sub></em>=1):&nbsp;%u; "
"%s(-1&le;<em>p<sub>i</sub></em>&lt;0):&nbsp;%u; "
"%s(<em>p<sub>i</sub></em>=0):&nbsp;%u; "
"%s(0&lt;<em>p<sub>i</sub></em>&lt;1):&nbsp;%u; "
"%s(<em>p<sub>i</sub></em>=0):&nbsp;%u",
Txt_ANSWERS_correct,Print->NumQsts.Valid.Correct,
Txt_ANSWERS_wrong ,Print->NumQsts.Valid.Wrong.Negative,
Txt_ANSWERS_wrong ,Print->NumQsts.Valid.Wrong.Zero,
Txt_ANSWERS_wrong ,Print->NumQsts.Valid.Wrong.Positive,
Txt_ANSWERS_blank ,Print->NumQsts.Valid.Blank);
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
2020-06-22 19:27:23 +02:00
2021-01-20 00:42:59 +01:00
/***** Row end *****/
2020-06-22 19:27:23 +02:00
HTM_TR_End ();
2021-01-20 00:42:59 +01:00
}
2020-06-22 19:27:23 +02:00
2021-01-20 00:42:59 +01:00
/*****************************************************************************/
/************************** Show score in exam print *************************/
/*****************************************************************************/
static void ExaRes_ShowExamResultScore (struct ExaPrn_Print *Print,
const struct ExaRes_ICanView *ICanView)
{
extern const char *Txt_Score;
extern const char *Txt_valid_score;
/***** Row begin *****/
2020-05-18 02:15:10 +02:00
HTM_TR_Begin (NULL);
2020-04-20 01:26:46 +02:00
/***** Label *****/
HTM_TD_Begin ("class=\"RT DAT_STRONG_%s\"",The_GetSuffix ());
HTM_TxtColon (Txt_Score);
HTM_TD_End ();
2020-04-20 01:26:46 +02:00
/***** Score *****/
HTM_TD_Begin ("class=\"LB DAT_%s\"",The_GetSuffix ());
if (ICanView->Score)
{
/* Score counting all questions */
if (Print->NumQsts.All == Print->NumQsts.Valid.Total)
HTM_STRONG_Begin ();
HTM_Double2Decimals (Print->Score.All);
HTM_Txt ("/");
HTM_Unsigned (Print->NumQsts.All);
if (Print->NumQsts.All == Print->NumQsts.Valid.Total)
HTM_STRONG_End ();
/* Scoure counting only valid questions */
if (Print->NumQsts.All != Print->NumQsts.Valid.Total)
{
HTM_Txt ("; ");
HTM_TxtColonNBSP (Txt_valid_score);
HTM_STRONG_Begin ();
HTM_Double2Decimals (Print->Score.Valid);
HTM_Txt ("/");
HTM_Unsigned (Print->NumQsts.Valid.Total);
HTM_STRONG_End ();
}
}
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
2020-04-20 01:26:46 +02:00
2021-01-20 00:42:59 +01:00
/***** Row end *****/
2020-05-18 02:15:10 +02:00
HTM_TR_End ();
2021-01-20 00:42:59 +01:00
}
2020-04-20 01:26:46 +02:00
2021-01-20 00:42:59 +01:00
/*****************************************************************************/
/************************** Show grade in exam print *************************/
/*****************************************************************************/
static void ExaRes_ShowExamResultGrade (const struct Exa_Exam *Exam,
struct ExaPrn_Print *Print,
const struct ExaRes_ICanView *ICanView)
{
extern const char *Txt_Grade;
extern const char *Txt_valid_grade;
/***** Row begin *****/
2020-05-18 02:15:10 +02:00
HTM_TR_Begin (NULL);
2020-04-20 01:26:46 +02:00
/***** Label *****/
HTM_TD_Begin ("class=\"RT DAT_STRONG_%s\"",The_GetSuffix ());
HTM_TxtColon (Txt_Grade);
HTM_TD_End ();
2020-04-20 01:26:46 +02:00
/***** Grade *****/
HTM_TD_Begin ("class=\"LB DAT_%s\"",The_GetSuffix ());
if (ICanView->Score)
{
/* Grade counting all questions */
if (Print->NumQsts.All == Print->NumQsts.Valid.Total)
HTM_STRONG_Begin ();
TstPrn_ComputeAndShowGrade (Print->NumQsts.All,Print->Score.All,Exam->MaxGrade);
if (Print->NumQsts.All == Print->NumQsts.Valid.Total)
HTM_STRONG_End ();
/* Grade counting only valid questions */
if (Print->NumQsts.All != Print->NumQsts.Valid.Total)
{
HTM_Txt ("; ");
HTM_TxtColonNBSP (Txt_valid_grade);
HTM_STRONG_Begin ();
TstPrn_ComputeAndShowGrade (Print->NumQsts.Valid.Total,Print->Score.Valid,Exam->MaxGrade);
HTM_STRONG_End ();
}
}
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
2020-05-18 02:15:10 +02:00
2021-01-20 00:42:59 +01:00
/***** Row end *****/
2020-05-18 02:15:10 +02:00
HTM_TR_End ();
2020-06-17 22:19:57 +02:00
}
2020-05-16 02:04:36 +02:00
/*****************************************************************************/
/**************** Show user's and correct answers of a test ******************/
2020-05-16 02:04:36 +02:00
/*****************************************************************************/
static void ExaRes_ShowExamAnswers (struct Usr_Data *UsrDat,
2020-05-16 02:04:36 +02:00
struct ExaPrn_Print *Print,
unsigned Visibility)
{
unsigned QstInd;
struct Qst_Question Question;
2020-05-16 02:04:36 +02:00
for (QstInd = 0, The_ResetRowColor ();
QstInd < Print->NumQsts.All;
QstInd++, The_ChangeRowColor ())
2020-05-16 02:04:36 +02:00
{
/***** Create test question *****/
Qst_QstConstructor (&Question);
Question.QstCod = Print->PrintedQuestions[QstInd].QstCod;
2020-05-16 02:04:36 +02:00
/***** Get question data *****/
ExaSet_GetQstDataFromDB (&Question);
/***** Write questions and answers *****/
ExaRes_WriteQstAndAnsExam (UsrDat,Print,QstInd,&Question,Visibility);
2020-05-16 02:04:36 +02:00
/***** Destroy test question *****/
Qst_QstDestructor (&Question);
2020-05-16 02:04:36 +02:00
}
}
/*****************************************************************************/
/********** Write a row of a test, with one question and its answer **********/
/*****************************************************************************/
static void ExaRes_WriteQstAndAnsExam (struct Usr_Data *UsrDat,
2020-05-16 02:04:36 +02:00
struct ExaPrn_Print *Print,
unsigned QstInd,
struct Qst_Question *Question,
2020-05-16 02:04:36 +02:00
unsigned Visibility)
{
extern const char *Txt_Score;
2020-06-18 20:06:17 +02:00
extern const char *Txt_Invalid_question;
2020-05-23 19:08:59 +02:00
bool ICanView[TstVis_NUM_ITEMS_VISIBILITY];
static const char *ClassNumQst[Qst_NUM_VALIDITIES] =
2020-06-17 22:19:57 +02:00
{
[Qst_INVALID_QUESTION] = "BIG_INDEX_RED",
[Qst_VALID_QUESTION ] = "BIG_INDEX",
2020-06-17 22:19:57 +02:00
};
static const char *ClassAnswerType[Qst_NUM_VALIDITIES] =
2020-06-17 22:19:57 +02:00
{
[Qst_INVALID_QUESTION] = "DAT_SMALL_RED",
[Qst_VALID_QUESTION ] = "DAT_SMALL",
2020-06-17 22:19:57 +02:00
};
static const char *ClassTxt[Qst_NUM_VALIDITIES] =
2020-06-17 20:20:16 +02:00
{
[Qst_INVALID_QUESTION] = "Qst_TXT_RED",
[Qst_VALID_QUESTION ] = "Qst_TXT",
2020-06-17 20:20:16 +02:00
};
static const char *ClassFeedback[Qst_NUM_VALIDITIES] =
2020-06-17 20:20:16 +02:00
{
[Qst_INVALID_QUESTION] = "Qst_TXT_LIGHT_RED",
[Qst_VALID_QUESTION ] = "Qst_TXT_LIGHT",
2020-06-17 20:20:16 +02:00
};
2020-05-22 20:10:45 +02:00
/***** Check if I can view each part of the question *****/
switch (Gbl.Usrs.Me.Role.Logged)
{
case Rol_STD:
2020-05-23 19:08:59 +02:00
ICanView[TstVis_VISIBLE_QST_ANS_TXT ] = TstVis_IsVisibleQstAndAnsTxt (Visibility);
ICanView[TstVis_VISIBLE_FEEDBACK_TXT ] = TstVis_IsVisibleFeedbackTxt (Visibility);
ICanView[TstVis_VISIBLE_CORRECT_ANSWER] = TstVis_IsVisibleCorrectAns (Visibility);
ICanView[TstVis_VISIBLE_EACH_QST_SCORE] = TstVis_IsVisibleEachQstScore (Visibility);
2020-05-22 20:10:45 +02:00
break;
case Rol_NET:
case Rol_TCH:
case Rol_DEG_ADM:
case Rol_CTR_ADM:
case Rol_INS_ADM:
case Rol_SYS_ADM:
2020-05-23 19:08:59 +02:00
ICanView[TstVis_VISIBLE_QST_ANS_TXT ] =
ICanView[TstVis_VISIBLE_FEEDBACK_TXT ] =
ICanView[TstVis_VISIBLE_CORRECT_ANSWER] =
ICanView[TstVis_VISIBLE_EACH_QST_SCORE] = true;
2020-05-22 20:10:45 +02:00
break;
default:
2020-05-23 19:08:59 +02:00
ICanView[TstVis_VISIBLE_QST_ANS_TXT ] =
ICanView[TstVis_VISIBLE_FEEDBACK_TXT ] =
ICanView[TstVis_VISIBLE_CORRECT_ANSWER] =
ICanView[TstVis_VISIBLE_EACH_QST_SCORE] = false;
2020-05-22 20:10:45 +02:00
break;
}
2020-05-16 02:04:36 +02:00
/***** Begin row *****/
HTM_TR_Begin (NULL);
/***** Number of question and answer type *****/
HTM_TD_Begin ("class=\"RT %s\"",The_GetColorRows ());
Lay_WriteIndex (QstInd + 1,ClassNumQst[Question->Validity]);
Qst_WriteAnswerType (Question->Answer.Type,
ClassAnswerType[Question->Validity]);
HTM_TD_End ();
/***** Stem, media and answers *****/
HTM_TD_Begin ("class=\"LT %s\"",The_GetColorRows ());
/* Stem */
Qst_WriteQstStem (Question->Stem,ClassTxt[Question->Validity],
ICanView[TstVis_VISIBLE_QST_ANS_TXT]);
2020-05-16 02:04:36 +02:00
/* Media */
if (ICanView[TstVis_VISIBLE_QST_ANS_TXT])
Med_ShowMedia (&Question->Media,
"Tst_MED_SHOW_CONT",
"Tst_MED_SHOW");
2020-05-16 02:04:36 +02:00
/* Answers */
ExaPrn_ComputeAnswerScore (&Print->PrintedQuestions[QstInd],Question);
TstPrn_WriteAnswersExam (UsrDat,&Print->PrintedQuestions[QstInd],Question,
ICanView,
ClassTxt[Question->Validity],
ClassFeedback[Question->Validity]);
/* Write score retrieved from database */
if (ICanView[TstVis_VISIBLE_EACH_QST_SCORE])
{
HTM_DIV_Begin ("class=\"LM DAT_SMALL_%s\"",
The_GetSuffix ());
HTM_TxtColonNBSP (Txt_Score);
HTM_SPAN_Begin ("class=\"%s_%s\"",
Print->PrintedQuestions[QstInd].StrAnswers[0] ?
(Print->PrintedQuestions[QstInd].Score > 0 ? "Qst_ANS_OK" : // Correct
"Qst_ANS_BAD") : // Wrong
"Qst_ANS_0", // Blank answer
The_GetSuffix ());
HTM_Double2Decimals (Print->PrintedQuestions[QstInd].Score);
if (Question->Validity == Qst_INVALID_QUESTION)
HTM_TxtF (" (%s)",Txt_Invalid_question);
HTM_SPAN_End ();
HTM_DIV_End ();
}
/* Question feedback */
if (ICanView[TstVis_VISIBLE_FEEDBACK_TXT])
Qst_WriteQstFeedback (Question->Feedback,
ClassFeedback[Question->Validity]);
HTM_TD_End ();
2020-05-16 02:04:36 +02:00
/***** End row *****/
HTM_TR_End ();
}