swad-core/swad_exam_result.c

1558 lines
50 KiB
C

// 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-2020 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 <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 calloc
#include <string.h> // For string functions
#include "swad_action.h"
#include "swad_database.h"
#include "swad_date.h"
#include "swad_exam.h"
#include "swad_exam_log.h"
#include "swad_exam_print.h"
#include "swad_exam_result.h"
#include "swad_exam_session.h"
#include "swad_exam_set.h"
#include "swad_exam_type.h"
#include "swad_form.h"
#include "swad_global.h"
#include "swad_HTML.h"
#include "swad_ID.h"
#include "swad_photo.h"
#include "swad_test_print.h"
#include "swad_test_visibility.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 ExaRes_ListMyResultsInCrs (struct Exa_Exams *Exams);
static void ExaRes_ListMyResultsInExa (struct Exa_Exams *Exams,long ExaCod);
static void ExaRes_ListMyResultsInSes (struct Exa_Exams *Exams,long SesCod);
static void ExaRes_PutFormToSelUsrsToViewResults (__attribute__((unused)) void *Args);
static void ExaRes_ShowAllResultsInSelectedExams (void *Exams);
static void ExaRes_ListAllResultsInSelectedExams (struct Exa_Exams *Exams);
static void ExaRes_ListAllResultsInExa (struct Exa_Exams *Exams,long ExaCod);
static void ExaRes_ListAllResultsInSes (struct Exa_Exams *Exams,long SesCod);
static void ExaRes_ShowResultsBegin (struct Exa_Exams *Exams,
const char *Title,bool ListExamsToSelect);
static void ExaRes_ShowResultsEnd (void);
static void ExaRes_ListExamsToSelect (struct Exa_Exams *Exams);
static void ExaRes_ShowHeaderResults (Usr_MeOrOther_t MeOrOther);
static void ExaRes_BuildExamsSelectedCommas (struct Exa_Exams *Exams,
char **ExamsSelectedCommas);
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,
unsigned NumTotalQsts,
unsigned NumTotalQstsNotBlank,
double TotalScoreOfAllResults,
double TotalGrade);
static void ExaRes_ShowExamResult (const struct Exa_Exam *Exam,
const struct ExaSes_Session *Session,
struct ExaPrn_Print *Print,
struct UsrData *UsrDat);
static bool ExaRes_CheckIfICanSeePrintResult (const struct Exa_Exam *Exam,
const struct ExaSes_Session *Session,
long UsrCod);
static bool ExaRes_CheckIfICanViewScore (bool ICanViewResult,unsigned Visibility);
static void ExaRes_ShowExamAnswers (struct UsrData *UsrDat,
struct ExaPrn_Print *Print,
unsigned Visibility);
static void ExaRes_WriteQstAndAnsExam (struct UsrData *UsrDat,
struct ExaPrn_Print *Print,
unsigned NumQst,
struct Tst_Question *Question,
unsigned Visibility);
/*****************************************************************************/
/*************************** Show my sessions results **************************/
/*****************************************************************************/
void ExaRes_ShowMyResultsInCrs (void)
{
extern const char *Txt_Results;
struct Exa_Exams Exams;
/***** Reset exams context *****/
Exa_ResetExams (&Exams);
/***** Get list of exams *****/
Exa_GetListExams (&Exams,Exa_ORDER_BY_TITLE);
Exa_GetListSelectedExaCods (&Exams);
/***** List my sessions results in the current course *****/
ExaRes_ShowResultsBegin (&Exams,Txt_Results,true); // List exams to select
ExaRes_ListMyResultsInCrs (&Exams);
ExaRes_ShowResultsEnd ();
/***** Free list of exams *****/
free (Exams.ExaCodsSelected);
Exa_FreeListExams (&Exams);
}
static void ExaRes_ListMyResultsInCrs (struct Exa_Exams *Exams)
{
char *ExamsSelectedCommas = NULL; // Initialized to avoid warning
/***** Table header *****/
ExaRes_ShowHeaderResults (Usr_ME);
/***** List my sessions results in the current course *****/
TstCfg_GetConfigFromDB (); // Get feedback type
ExaRes_BuildExamsSelectedCommas (Exams,&ExamsSelectedCommas);
ExaRes_ShowResults (Exams,Usr_ME,-1L,-1L,ExamsSelectedCommas);
free (ExamsSelectedCommas);
}
/*****************************************************************************/
/******************** Show my results in a given exam ************************/
/*****************************************************************************/
void ExaRes_ShowMyResultsInExa (void)
{
extern const char *Txt_Results_of_exam_X;
struct Exa_Exams Exams;
struct Exa_Exam Exam;
struct ExaSes_Session Session;
/***** Reset exams context *****/
Exa_ResetExams (&Exams);
Exa_ResetExam (&Exam);
ExaSes_ResetSession (&Session);
/***** Get parameters *****/
Exa_GetParams (&Exams);
if (Exams.ExaCod <= 0)
Lay_WrongExamExit ();
Exam.ExaCod = Exams.ExaCod;
/***** Get exam data from database *****/
Exa_GetDataOfExamByCod (&Exam);
Exams.ExaCod = Exam.ExaCod;
/***** Exam begin *****/
Exa_ShowOnlyOneExamBegin (&Exams,&Exam,&Session,
false); // Do not put form to start new session
/***** List my sessions results in exam *****/
ExaRes_ShowResultsBegin (&Exams,
Str_BuildStringStr (Txt_Results_of_exam_X,Exam.Title),
false); // Do not list exams to select
Str_FreeString ();
ExaRes_ListMyResultsInExa (&Exams,Exam.ExaCod);
ExaRes_ShowResultsEnd ();
/***** Exam end *****/
Exa_ShowOnlyOneExamEnd ();
}
static void ExaRes_ListMyResultsInExa (struct Exa_Exams *Exams,long ExaCod)
{
/***** Table header *****/
ExaRes_ShowHeaderResults (Usr_ME);
/***** List my sessions results in exam *****/
TstCfg_GetConfigFromDB (); // Get feedback type
ExaRes_ShowResults (Exams,Usr_ME,-1L,ExaCod,NULL);
}
/*****************************************************************************/
/****************** Show my exam results in a given session ******************/
/*****************************************************************************/
void ExaRes_ShowMyResultsInSes (void)
{
extern const char *Txt_Results_of_session_X;
struct Exa_Exams Exams;
struct Exa_Exam Exam;
struct ExaSes_Session Session;
/***** Reset exams context *****/
Exa_ResetExams (&Exams);
Exa_ResetExam (&Exam);
ExaSes_ResetSession (&Session);
/***** Get parameters *****/
Exa_GetParams (&Exams);
if (Exams.ExaCod <= 0)
Lay_WrongExamExit ();
Exam.ExaCod = Exams.ExaCod;
if ((Session.SesCod = ExaSes_GetParamSesCod ()) <= 0)
Lay_WrongExamSessionExit ();
Exa_GetDataOfExamByCod (&Exam);
Exams.ExaCod = Exam.ExaCod;
ExaSes_GetDataOfSessionByCod (&Session);
/***** Exam begin *****/
Exa_ShowOnlyOneExamBegin (&Exams,&Exam,&Session,
false); // Do not put form to start new session
/***** List my sessions results in session *****/
ExaRes_ShowResultsBegin (&Exams,Str_BuildStringStr (Txt_Results_of_session_X,Session.Title),
false); // Do not list exams to select
Str_FreeString ();
ExaRes_ListMyResultsInSes (&Exams,Session.SesCod);
ExaRes_ShowResultsEnd ();
/***** Exam end *****/
Exa_ShowOnlyOneExamEnd ();
}
static void ExaRes_ListMyResultsInSes (struct Exa_Exams *Exams,long SesCod)
{
/***** Table header *****/
ExaRes_ShowHeaderResults (Usr_ME);
/***** List my sessions results in exam *****/
TstCfg_GetConfigFromDB (); // Get feedback type
ExaRes_ShowResults (Exams,Usr_ME,SesCod,-1L,NULL);
}
/*****************************************************************************/
/**************** Select users to show their sessions results *****************/
/*****************************************************************************/
void ExaRes_SelUsrsToViewResults (void)
{
/***** Put form to select users *****/
ExaRes_PutFormToSelUsrsToViewResults (NULL);
}
static void ExaRes_PutFormToSelUsrsToViewResults (__attribute__((unused)) void *Args)
{
extern const char *Hlp_ASSESSMENT_Exams_results;
extern const char *Txt_Results;
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
}
/*****************************************************************************/
/****************** Get users and show their sessions results *****************/
/*****************************************************************************/
void ExaRes_ShowAllResultsInCrs (void)
{
struct Exa_Exams Exams;
/***** Reset exams context *****/
Exa_ResetExams (&Exams);
/***** Get users and show their sessions results *****/
Usr_GetSelectedUsrsAndGoToAct (&Gbl.Usrs.Selected,
ExaRes_ShowAllResultsInSelectedExams,&Exams,
ExaRes_PutFormToSelUsrsToViewResults,NULL);
}
/*****************************************************************************/
/****************** Show sessions results for several users *******************/
/*****************************************************************************/
static void ExaRes_ShowAllResultsInSelectedExams (void *Exams)
{
extern const char *Txt_Results;
if (!Exams)
return;
/***** Get list of exams *****/
Exa_GetListExams ((struct Exa_Exams *) Exams,Exa_ORDER_BY_TITLE);
Exa_GetListSelectedExaCods ((struct Exa_Exams *) Exams);
/***** List the sessions results of the selected users *****/
ExaRes_ShowResultsBegin ((struct Exa_Exams *) Exams,
Txt_Results,
true); // List exams to select
ExaRes_ListAllResultsInSelectedExams ((struct Exa_Exams *) Exams);
ExaRes_ShowResultsEnd ();
/***** Free list of exams *****/
free (((struct Exa_Exams *) Exams)->ExaCodsSelected);
Exa_FreeListExams ((struct Exa_Exams *) Exams);
}
static void ExaRes_ListAllResultsInSelectedExams (struct Exa_Exams *Exams)
{
char *ExamsSelectedCommas = NULL; // Initialized to avoid warning
const char *Ptr;
/***** Table head *****/
ExaRes_ShowHeaderResults (Usr_OTHER);
/***** List the sessions results of the selected users *****/
ExaRes_BuildExamsSelectedCommas (Exams,&ExamsSelectedCommas);
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))
if (Usr_CheckIfICanViewTstExaMchResult (&Gbl.Usrs.Other.UsrDat))
{
/***** Show sessions results *****/
Gbl.Usrs.Other.UsrDat.Accepted = Usr_CheckIfUsrHasAcceptedInCurrentCrs (&Gbl.Usrs.Other.UsrDat);
ExaRes_ShowResults (Exams,Usr_OTHER,-1L,-1L,ExamsSelectedCommas);
}
}
free (ExamsSelectedCommas);
}
/*****************************************************************************/
/*** Show sessions results of a exam for the users who answered in that exam **/
/*****************************************************************************/
void ExaRes_ShowAllResultsInExa (void)
{
extern const char *Txt_Results_of_exam_X;
struct Exa_Exams Exams;
struct Exa_Exam Exam;
struct ExaSes_Session Session;
/***** Reset exams context *****/
Exa_ResetExams (&Exams);
Exa_ResetExam (&Exam);
ExaSes_ResetSession (&Session);
/***** Get parameters *****/
Exa_GetParams (&Exams);
if (Exams.ExaCod <= 0)
Lay_WrongExamExit ();
Exam.ExaCod = Exams.ExaCod;
Exa_GetDataOfExamByCod (&Exam);
Exams.ExaCod = Exam.ExaCod;
/***** Exam begin *****/
Exa_ShowOnlyOneExamBegin (&Exams,&Exam,&Session,
false); // Do not put form to start new session
/***** List sessions results in exam *****/
ExaRes_ShowResultsBegin (&Exams,
Str_BuildStringStr (Txt_Results_of_exam_X,Exam.Title),
false); // Do not list exams to select
Str_FreeString ();
ExaRes_ListAllResultsInExa (&Exams,Exam.ExaCod);
ExaRes_ShowResultsEnd ();
/***** Exam end *****/
Exa_ShowOnlyOneExamEnd ();
}
static void ExaRes_ListAllResultsInExa (struct Exa_Exams *Exams,long ExaCod)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned long NumUsrs;
unsigned long NumUsr;
/***** Table head *****/
ExaRes_ShowHeaderResults (Usr_OTHER);
/***** Get all users who have answered any session question in this exam *****/
NumUsrs = DB_QuerySELECT (&mysql_res,"can not get users in exam",
"SELECT users.UsrCod FROM"
" (SELECT DISTINCT exa_prints.UsrCod AS UsrCod" // row[0]
" FROM exa_prints,exa_sessions,exa_exams"
" WHERE exa_sessions.ExaCod=%ld"
" AND exa_sessions.SesCod=exa_prints.SesCod"
" AND exa_sessions.ExaCod=exa_exams.ExaCod"
" AND exa_exams.CrsCod=%ld)" // Extra check
" AS users,usr_data"
" WHERE users.UsrCod=usr_data.UsrCod"
" ORDER BY usr_data.Surname1,"
"usr_data.Surname2,"
"usr_data.FirstName",
ExaCod,
Gbl.Hierarchy.Crs.CrsCod);
if (NumUsrs)
{
/***** List sessions results for each user *****/
for (NumUsr = 0;
NumUsr < NumUsrs;
NumUsr++)
{
row = mysql_fetch_row (mysql_res);
/* Get session code (row[0]) */
if ((Gbl.Usrs.Other.UsrDat.UsrCod = Str_ConvertStrCodToLongCod (row[0])) > 0)
if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat,Usr_DONT_GET_PREFS))
if (Usr_CheckIfICanViewTstExaMchResult (&Gbl.Usrs.Other.UsrDat))
{
/***** Show sessions results *****/
Gbl.Usrs.Other.UsrDat.Accepted = Usr_CheckIfUsrHasAcceptedInCurrentCrs (&Gbl.Usrs.Other.UsrDat);
ExaRes_ShowResults (Exams,Usr_OTHER,-1L,ExaCod,NULL);
}
}
}
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
/*****************************************************************************/
/** Show sessions results of a session for the users who answered in that session */
/*****************************************************************************/
void ExaRes_ShowAllResultsInSes (void)
{
extern const char *Txt_Results_of_session_X;
struct Exa_Exams Exams;
struct Exa_Exam Exam;
struct ExaSes_Session Session;
/***** Reset exams context *****/
Exa_ResetExams (&Exams);
Exa_ResetExam (&Exam);
ExaSes_ResetSession (&Session);
/***** Get parameters *****/
Exa_GetParams (&Exams);
if (Exams.ExaCod <= 0)
Lay_WrongExamExit ();
Exam.ExaCod = Exams.ExaCod;
if ((Session.SesCod = ExaSes_GetParamSesCod ()) <= 0)
Lay_WrongExamSessionExit ();
/***** Get exam data and session *****/
Exa_GetDataOfExamByCod (&Exam);
Exams.ExaCod = Exam.ExaCod;
ExaSes_GetDataOfSessionByCod (&Session);
/***** Exam begin *****/
Exa_ShowOnlyOneExamBegin (&Exams,&Exam,&Session,
false); // Do not put form to start new session
/***** List sessions results in session *****/
ExaRes_ShowResultsBegin (&Exams,
Str_BuildStringStr (Txt_Results_of_session_X,Session.Title),
false); // Do not list exams to select
Str_FreeString ();
ExaRes_ListAllResultsInSes (&Exams,Session.SesCod);
ExaRes_ShowResultsEnd ();
/***** Exam end *****/
Exa_ShowOnlyOneExamEnd ();
}
static void ExaRes_ListAllResultsInSes (struct Exa_Exams *Exams,long SesCod)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned long NumUsrs;
unsigned long NumUsr;
/***** Table head *****/
ExaRes_ShowHeaderResults (Usr_OTHER);
/***** Get all users who have answered any session question in this exam *****/
NumUsrs = DB_QuerySELECT (&mysql_res,"can not get users in session",
"SELECT users.UsrCod FROM"
" (SELECT exa_prints.UsrCod AS UsrCod" // row[0]
" FROM exa_prints,exa_sessions,exa_exams"
" WHERE exa_prints.SesCod=%ld"
" AND exa_prints.SesCod=exa_sessions.SesCod"
" AND exa_sessions.ExaCod=exa_exams.ExaCod"
" AND exa_exams.CrsCod=%ld)" // Extra check
" AS users,usr_data"
" WHERE users.UsrCod=usr_data.UsrCod"
" ORDER BY usr_data.Surname1,"
"usr_data.Surname2,"
"usr_data.FirstName",
SesCod,
Gbl.Hierarchy.Crs.CrsCod);
if (NumUsrs)
{
/***** List sessions results for each user *****/
for (NumUsr = 0;
NumUsr < NumUsrs;
NumUsr++)
{
row = mysql_fetch_row (mysql_res);
/* Get session code (row[0]) */
if ((Gbl.Usrs.Other.UsrDat.UsrCod = Str_ConvertStrCodToLongCod (row[0])) > 0)
if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat,Usr_DONT_GET_PREFS))
if (Usr_CheckIfICanViewTstExaMchResult (&Gbl.Usrs.Other.UsrDat))
{
/***** Show sessions results *****/
Gbl.Usrs.Other.UsrDat.Accepted = Usr_CheckIfUsrHasAcceptedInCurrentCrs (&Gbl.Usrs.Other.UsrDat);
ExaRes_ShowResults (Exams,Usr_OTHER,SesCod,-1L,NULL);
}
}
}
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
/*****************************************************************************/
/************************ Show results (begin / end) *************************/
/*****************************************************************************/
static void ExaRes_ShowResultsBegin (struct Exa_Exams *Exams,
const char *Title,bool ListExamsToSelect)
{
extern const char *Hlp_ASSESSMENT_Exams_results;
/***** Begin box *****/
HTM_SECTION_Begin (ExaRes_RESULTS_BOX_ID);
Box_BoxBegin ("100%",Title,
NULL,NULL,
Hlp_ASSESSMENT_Exams_results,Box_NOT_CLOSABLE);
/***** List exams to select *****/
if (ListExamsToSelect)
ExaRes_ListExamsToSelect (Exams);
/***** Begin session results table *****/
HTM_SECTION_Begin (ExaRes_RESULTS_TABLE_ID);
HTM_TABLE_BeginWidePadding (2);
}
static void ExaRes_ShowResultsEnd (void)
{
/***** End session results table *****/
HTM_TABLE_End ();
HTM_SECTION_End ();
/***** End box *****/
Box_BoxEnd ();
HTM_SECTION_End ();
}
/*****************************************************************************/
/********** Write list of those attendance sessions that have students *********/
/*****************************************************************************/
static void ExaRes_ListExamsToSelect (struct Exa_Exams *Exams)
{
extern const char *The_ClassFormLinkInBoxBold[The_NUM_THEMES];
extern const char *Txt_Exams;
extern const char *Txt_Exam;
extern const char *Txt_Update_results;
unsigned UniqueId;
unsigned NumExam;
struct Exa_Exam Exam;
/***** Reset exam *****/
Exa_ResetExam (&Exam);
/***** Begin box *****/
Box_BoxBegin (NULL,Txt_Exams,
NULL,NULL,
NULL,Box_CLOSABLE);
/***** Begin form to update the results
depending on the exams selected *****/
Frm_StartFormAnchor (Gbl.Action.Act,ExaRes_RESULTS_TABLE_ID);
Grp_PutParamsCodGrps ();
Usr_PutHiddenParSelectedUsrsCods (&Gbl.Usrs.Selected);
/***** Begin table *****/
HTM_TABLE_BeginWidePadding (2);
/***** Heading row *****/
HTM_TR_Begin (NULL);
HTM_TH (1,2,NULL,NULL);
HTM_TH (1,1,"LM",Txt_Exam);
HTM_TR_End ();
/***** List the sessions *****/
for (NumExam = 0, UniqueId = 1, Gbl.RowEvenOdd = 0;
NumExam < Exams->Num;
NumExam++, UniqueId++, Gbl.RowEvenOdd = 1 - Gbl.RowEvenOdd)
{
/* Get data of this exam */
Exam.ExaCod = Exams->Lst[NumExam].ExaCod;
Exa_GetDataOfExamByCod (&Exam);
Exams->ExaCod = Exam.ExaCod;
/* Write a row for this session */
HTM_TR_Begin (NULL);
HTM_TD_Begin ("class=\"DAT CT COLOR%u\"",Gbl.RowEvenOdd);
HTM_INPUT_CHECKBOX ("ExaCod",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=\"DAT RT COLOR%u\"",Gbl.RowEvenOdd);
HTM_LABEL_Begin ("for=\"Gam%u\"",NumExam);
HTM_TxtF ("%u:",NumExam + 1);
HTM_LABEL_End ();
HTM_TD_End ();
HTM_TD_Begin ("class=\"DAT LT COLOR%u\"",Gbl.RowEvenOdd);
HTM_Txt (Exam.Title);
HTM_TD_End ();
HTM_TR_End ();
}
/***** Put button to refresh *****/
HTM_TR_Begin (NULL);
HTM_TD_Begin ("colspan=\"3\" class=\"CM\"");
HTM_BUTTON_Animated_Begin (Txt_Update_results,
The_ClassFormLinkInBoxBold[Gbl.Prefs.Theme],
NULL);
Ico_PutCalculateIconWithText (Txt_Update_results);
HTM_BUTTON_End ();
HTM_TD_End ();
HTM_TR_End ();
/***** End table *****/
HTM_TABLE_End ();
/***** End form *****/
Frm_EndForm ();
/***** End box *****/
Box_BoxEnd ();
}
/*****************************************************************************/
/********************* Show header of my sessions results *********************/
/*****************************************************************************/
static void ExaRes_ShowHeaderResults (Usr_MeOrOther_t MeOrOther)
{
extern const char *Txt_User[Usr_NUM_SEXS];
extern const char *Txt_Session;
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_Score;
extern const char *Txt_Average_BR_score_BR_per_question_BR_from_0_to_1;
extern const char *Txt_Grade;
HTM_TR_Begin (NULL);
HTM_TH (1,2,"CT",Txt_User[MeOrOther == Usr_ME ? Gbl.Usrs.Me.UsrDat.Sex :
Usr_SEX_UNKNOWN]);
HTM_TH (1,1,"LT",Txt_START_END_TIME[Dat_START_TIME]);
HTM_TH (1,1,"LT",Txt_START_END_TIME[Dat_END_TIME ]);
HTM_TH (1,1,"LT",Txt_Session);
HTM_TH (1,1,"RT",Txt_Questions);
HTM_TH (1,1,"RT",Txt_Non_blank_BR_questions);
HTM_TH (1,1,"RT",Txt_Score);
HTM_TH (1,1,"RT",Txt_Average_BR_score_BR_per_question_BR_from_0_to_1);
HTM_TH (1,1,"RT",Txt_Grade);
HTM_TH_Empty (1);
HTM_TR_End ();
}
/*****************************************************************************/
/******* Build string with list of selected exams separated by commas ********/
/******* from list of selected exams ********/
/*****************************************************************************/
static void ExaRes_BuildExamsSelectedCommas (struct Exa_Exams *Exams,
char **ExamsSelectedCommas)
{
size_t MaxLength;
unsigned NumExam;
char LongStr[Cns_MAX_DECIMAL_DIGITS_LONG + 1];
/***** Allocate memory for subquery of exams selected *****/
MaxLength = (size_t) Exams->NumSelected * (Cns_MAX_DECIMAL_DIGITS_LONG + 1);
if ((*ExamsSelectedCommas = (char *) malloc (MaxLength + 1)) == NULL)
Lay_NotEnoughMemoryExit ();
/***** Build subquery with list of selected exams *****/
(*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);
}
}
/*****************************************************************************/
/********* Show the sessions results of a user in the current course *********/
/*****************************************************************************/
static void ExaRes_ShowResults (struct Exa_Exams *Exams,
Usr_MeOrOther_t MeOrOther,
long SesCod, // <= 0 ==> any
long ExaCod, // <= 0 ==> any
const char *ExamsSelectedCommas)
{
extern const char *Txt_Result;
char *SesSubQuery;
char *HidSubQuery;
char *ExaSubQuery;
MYSQL_RES *mysql_res;
MYSQL_ROW row;
struct UsrData *UsrDat;
bool ICanViewResult;
bool ICanViewScore;
unsigned NumResults;
unsigned NumResult;
static unsigned UniqueId = 0;
char *Id;
struct ExaSes_Session Session;
Dat_StartEndTime_t StartEndTime;
unsigned NumQstsInThisResult;
unsigned NumQstsNotBlankInThisResult;
unsigned NumTotalQsts = 0;
unsigned NumTotalQstsNotBlank = 0;
double ScoreInThisResult;
double TotalScoreOfAllResults = 0.0;
double MaxGrade;
double Grade;
double TotalGrade = 0.0;
unsigned Visibility;
time_t TimeUTC[Dat_NUM_START_END_TIME];
/***** Reset session *****/
ExaSes_ResetSession (&Session);
/***** Set user *****/
UsrDat = (MeOrOther == Usr_ME) ? &Gbl.Usrs.Me.UsrDat :
&Gbl.Usrs.Other.UsrDat;
/***** Build sessions subquery *****/
if (SesCod > 0) // One unique session
{
if (asprintf (&SesSubQuery," AND exa_prints.SesCod=%ld",SesCod) < 0)
Lay_NotEnoughMemoryExit ();
}
else // All sessions of selected exams
{
if (asprintf (&SesSubQuery,"%s","") < 0)
Lay_NotEnoughMemoryExit ();
}
/***** Subquery: get hidden sessions?
· A student will not be able to see their results in hidden sessions
· A teacher will be able to see results from other users
even in hidden sessions
*****/
switch (MeOrOther)
{
case Usr_ME: // A student watching her/his results
if (asprintf (&HidSubQuery," AND exa_sessions.Hidden='N'") < 0)
Lay_NotEnoughMemoryExit ();
break;
default: // A teacher/admin watching the results of other users
if (asprintf (&HidSubQuery,"%s","") < 0)
Lay_NotEnoughMemoryExit ();
break;
}
/***** Build exams subquery *****/
if (ExaCod > 0) // One unique exams
{
if (asprintf (&ExaSubQuery," AND exa_sessions.ExaCod=%ld",ExaCod) < 0)
Lay_NotEnoughMemoryExit ();
}
else if (ExamsSelectedCommas)
{
if (ExamsSelectedCommas[0]) // Selected exams
{
if (asprintf (&ExaSubQuery," AND exa_sessions.ExaCod IN (%s)",
ExamsSelectedCommas) < 0)
Lay_NotEnoughMemoryExit ();
}
else
{
if (asprintf (&ExaSubQuery,"%s","") < 0)
Lay_NotEnoughMemoryExit ();
}
}
else // All exams
{
if (asprintf (&ExaSubQuery,"%s","") < 0)
Lay_NotEnoughMemoryExit ();
}
/***** Make database query *****/
// Do not filter by groups, because a student who has changed groups
// must be able to access exams taken in other groups
NumResults =
(unsigned) DB_QuerySELECT (&mysql_res,"can not get sessions results",
"SELECT exa_prints.SesCod," // row[0]
"UNIX_TIMESTAMP(exa_prints.StartTime)," // row[1]
"UNIX_TIMESTAMP(exa_prints.EndTime)," // row[2]
"exa_prints.NumQsts," // row[3]
"exa_prints.NumQstsNotBlank," // row[4]
"exa_prints.Score," // row[5]
"exa_exams.MaxGrade," // row[6]
"exa_exams.Visibility" // row[7]
" FROM exa_prints,exa_sessions,exa_exams"
" WHERE exa_prints.UsrCod=%ld"
"%s" // Session subquery
" AND exa_prints.SesCod=exa_sessions.SesCod"
"%s" // Hidden sessions subquery
"%s" // Exams subquery
" AND exa_sessions.ExaCod=exa_exams.ExaCod"
" AND exa_exams.CrsCod=%ld" // Extra check
" ORDER BY exa_sessions.Title",
UsrDat->UsrCod,
SesSubQuery,
HidSubQuery,
ExaSubQuery,
Gbl.Hierarchy.Crs.CrsCod);
free (ExaSubQuery);
free (HidSubQuery);
free (SesSubQuery);
/***** Show user's data *****/
HTM_TR_Begin (NULL);
Usr_ShowTableCellWithUsrData (UsrDat,NumResults);
/***** Get and print sessions results *****/
if (NumResults)
{
for (NumResult = 0;
NumResult < NumResults;
NumResult++)
{
row = mysql_fetch_row (mysql_res);
/* Get session code (row[0]) */
if ((Session.SesCod = Str_ConvertStrCodToLongCod (row[0])) < 0)
Lay_ShowErrorAndExit ("Wrong code of session.");
ExaSes_GetDataOfSessionByCod (&Session);
/* Get visibility (row[7]) */
Visibility = TstVis_GetVisibilityFromStr (row[7]);
/* Show session result? */
ICanViewResult = true; // Filtering is already made in the query
ICanViewScore = ExaRes_CheckIfICanViewScore (ICanViewResult,Visibility);
if (NumResult)
HTM_TR_Begin (NULL);
/* Write start/end times (row[1], row[2] hold UTC start/end times) */
for (StartEndTime = (Dat_StartEndTime_t) 0;
StartEndTime <= (Dat_StartEndTime_t) (Dat_NUM_START_END_TIME - 1);
StartEndTime++)
{
TimeUTC[StartEndTime] = Dat_GetUNIXTimeFromStr (row[1 + StartEndTime]);
UniqueId++;
if (asprintf (&Id,"exa_res_time_%u_%u",(unsigned) StartEndTime,UniqueId) < 0)
Lay_NotEnoughMemoryExit ();
HTM_TD_Begin ("id =\"%s\" class=\"DAT LT COLOR%u\"",
Id,Gbl.RowEvenOdd);
Dat_WriteLocalDateHMSFromUTC (Id,TimeUTC[StartEndTime],
Gbl.Prefs.DateFormat,Dat_SEPARATOR_BREAK,
true,true,false,0x7);
HTM_TD_End ();
free (Id);
}
/* Write session title */
HTM_TD_Begin ("class=\"DAT LT COLOR%u\"",Gbl.RowEvenOdd);
HTM_Txt (Session.Title);
HTM_TD_End ();
if (ICanViewScore)
{
/* 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;
Str_SetDecimalPointToUS (); // To get the decimal point as a dot
/* Get score (row[5]) */
if (sscanf (row[5],"%lf",&ScoreInThisResult) != 1)
ScoreInThisResult = 0.0;
TotalScoreOfAllResults += ScoreInThisResult;
/* Get maximum grade (row[6]) */
if (sscanf (row[6],"%lf",&MaxGrade) != 1)
MaxGrade = 0.0;
Str_SetDecimalPointToLocal (); // Return to local system
}
/* Write number of questions */
HTM_TD_Begin ("class=\"DAT RT COLOR%u\"",Gbl.RowEvenOdd);
if (ICanViewScore)
HTM_Unsigned (NumQstsInThisResult);
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
/* Write number of questions not blank */
HTM_TD_Begin ("class=\"DAT RT COLOR%u\"",Gbl.RowEvenOdd);
if (ICanViewScore)
HTM_Unsigned (NumQstsNotBlankInThisResult);
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
/* Write score */
HTM_TD_Begin ("class=\"DAT RT COLOR%u\"",Gbl.RowEvenOdd);
if (ICanViewScore)
HTM_Double2Decimals (ScoreInThisResult);
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
/* Write average score per question */
HTM_TD_Begin ("class=\"DAT RT COLOR%u\"",Gbl.RowEvenOdd);
if (ICanViewScore)
HTM_Double2Decimals (NumQstsInThisResult ? ScoreInThisResult /
(double) NumQstsInThisResult :
0.0);
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
/* Write grade over maximum grade */
HTM_TD_Begin ("class=\"DAT RT COLOR%u\"",Gbl.RowEvenOdd);
if (ICanViewScore)
{
Grade = TstPrn_ComputeGrade (NumQstsInThisResult,ScoreInThisResult,MaxGrade);
TstPrn_ShowGrade (Grade,MaxGrade);
TotalGrade += Grade;
}
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
/* Link to show this result */
HTM_TD_Begin ("class=\"RT COLOR%u\"",Gbl.RowEvenOdd);
if (ICanViewResult)
{
Exams->ExaCod = Session.ExaCod;
Exams->SesCod = Session.SesCod;
switch (MeOrOther)
{
case Usr_ME:
Frm_StartForm (ActSeeOneExaResMe);
ExaSes_PutParamsEdit (Exams);
break;
case Usr_OTHER:
Frm_StartForm (ActSeeOneExaResOth);
ExaSes_PutParamsEdit (Exams);
Usr_PutParamOtherUsrCodEncrypted (Gbl.Usrs.Other.UsrDat.EncryptedUsrCod);
break;
}
Ico_PutIconLink ("tasks.svg",Txt_Result);
Frm_EndForm ();
}
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
HTM_TR_End ();
}
/***** Write totals for this user *****/
ExaRes_ShowResultsSummaryRow (NumResults,
NumTotalQsts,NumTotalQstsNotBlank,
TotalScoreOfAllResults,
TotalGrade);
}
else
{
HTM_TD_ColouredEmpty (9);
HTM_TR_End ();
}
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
Gbl.RowEvenOdd = 1 - Gbl.RowEvenOdd;
}
/*****************************************************************************/
/************** Show row with summary of user's sessions results **************/
/*****************************************************************************/
static void ExaRes_ShowResultsSummaryRow (unsigned NumResults,
unsigned NumTotalQsts,
unsigned NumTotalQstsNotBlank,
double TotalScoreOfAllResults,
double TotalGrade)
{
extern const char *Txt_Sessions;
/***** Start row *****/
HTM_TR_Begin (NULL);
/***** Row title *****/
HTM_TD_Begin ("colspan=\"3\" class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd);
HTM_TxtColonNBSP (Txt_Sessions);
HTM_Unsigned (NumResults);
HTM_TD_End ();
/***** Write total number of questions *****/
HTM_TD_Begin ("class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd);
if (NumResults)
HTM_Unsigned (NumTotalQsts);
HTM_TD_End ();
/***** Write total number of questions not blank *****/
HTM_TD_Begin ("class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd);
if (NumResults)
HTM_Unsigned (NumTotalQstsNotBlank);
HTM_TD_End ();
/***** Write total score *****/
HTM_TD_Begin ("class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd);
HTM_Double2Decimals (TotalScoreOfAllResults);
HTM_TD_End ();
/***** Write average score per question *****/
HTM_TD_Begin ("class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd);
HTM_Double2Decimals (NumTotalQsts ? TotalScoreOfAllResults /
(double) NumTotalQsts :
0.0);
HTM_TD_End ();
/***** Write total grade *****/
HTM_TD_Begin ("class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd);
HTM_Double2Decimals (TotalGrade);
HTM_TD_End ();
/***** Last cell *****/
HTM_TD_Begin ("class=\"DAT_N_LINE_TOP COLOR%u\"",Gbl.RowEvenOdd);
HTM_TD_End ();
/***** End row *****/
HTM_TR_End ();
}
/*****************************************************************************/
/*************************** Show one exam result ****************************/
/*****************************************************************************/
void ExaRes_ShowOneExaResult (void)
{
struct Exa_Exams Exams;
struct Exa_Exam Exam;
struct ExaSes_Session Session;
Usr_MeOrOther_t MeOrOther;
struct UsrData *UsrDat;
struct ExaPrn_Print Print;
/***** Reset exams context *****/
Exa_ResetExams (&Exams);
Exa_ResetExam (&Exam);
ExaSes_ResetSession (&Session);
/***** Get and check parameters *****/
ExaSes_GetAndCheckParameters (&Exams,&Exam,&Session);
/***** Pointer to user's data *****/
MeOrOther = (Gbl.Action.Act == ActSeeOneExaResMe ||
Gbl.Action.Act == ActEndExaPrn) ? 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 exam print data *****/
ExaPrn_ResetPrint (&Print);
Print.SesCod = Session.SesCod;
Print.UsrCod = UsrDat->UsrCod;
ExaPrn_GetDataOfPrintByCodAndUsrCod (&Print);
/***** Set log action and print code *****/
if (Gbl.Action.Act == ActEndExaPrn)
{
// 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 (&Exam,&Session));
}
/***** Get questions and user's answers of exam print from database *****/
ExaPrn_GetPrintQuestionsFromDB (&Print);
/***** Show exam result *****/
ExaRes_ShowExamResult (&Exam,&Session,&Print,UsrDat);
/***** Show exam log *****/
ExaLog_ShowExamLog (&Print);
}
/*****************************************************************************/
/***************************** Show exam result ******************************/
/*****************************************************************************/
static void ExaRes_ShowExamResult (const struct Exa_Exam *Exam,
const struct ExaSes_Session *Session,
struct ExaPrn_Print *Print,
struct UsrData *UsrDat)
{
extern const char *Hlp_ASSESSMENT_Exams_results;
extern const char *Txt_The_user_does_not_exist;
extern const char *Txt_ROLES_SINGUL_Abc[Rol_NUM_ROLES][Usr_NUM_SEXS];
extern const char *Txt_START_END_TIME[Dat_NUM_START_END_TIME];
extern const char *Txt_Questions;
extern const char *Txt_non_blank_QUESTIONS;
extern const char *Txt_Score;
extern const char *Txt_Grade;
bool ShowPhoto;
char PhotoURL[PATH_MAX + 1];
Dat_StartEndTime_t StartEndTime;
char *Id;
struct
{
bool Result;
bool Score;
} ICanView;
/***** Check if I can view this print result *****/
switch (Gbl.Usrs.Me.Role.Logged)
{
case Rol_STD:
// Depends on visibility of result for this session (eye icon)
ICanView.Result = ExaRes_CheckIfICanSeePrintResult (Exam,Session,UsrDat->UsrCod);
if (ICanView.Result)
// Depends on 5 visibility icons
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;
}
/***** Begin box *****/
Box_BoxBegin (NULL,Session->Title,
NULL,NULL,
Hlp_ASSESSMENT_Exams_results,Box_NOT_CLOSABLE);
Lay_WriteHeaderClassPhoto (false,false,
Gbl.Hierarchy.Ins.InsCod,
Gbl.Hierarchy.Deg.DegCod,
Gbl.Hierarchy.Crs.CrsCod);
/***** Begin table *****/
HTM_TABLE_BeginWideMarginPadding (10);
/***** Header row *****/
/* Get data of the user who answer the session */
if (!Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (UsrDat,Usr_DONT_GET_PREFS))
Lay_ShowErrorAndExit (Txt_The_user_does_not_exist);
if (!Usr_CheckIfICanViewTstExaMchResult (UsrDat))
Lay_NoPermissionExit ();
/* User */
HTM_TR_Begin (NULL);
HTM_TD_Begin ("class=\"DAT_N RT\"");
HTM_TxtF ("%s:",Txt_ROLES_SINGUL_Abc[UsrDat->Roles.InCurrentCrs.Role][UsrDat->Sex]);
HTM_TD_End ();
HTM_TD_Begin ("class=\"DAT LT\"");
ID_WriteUsrIDs (UsrDat,NULL);
HTM_TxtF ("&nbsp;%s",UsrDat->Surname1);
if (UsrDat->Surname2[0])
HTM_TxtF ("&nbsp;%s",UsrDat->Surname2);
if (UsrDat->FirstName[0])
HTM_TxtF (", %s",UsrDat->FirstName);
HTM_BR ();
ShowPhoto = Pho_ShowingUsrPhotoIsAllowed (UsrDat,PhotoURL);
Pho_ShowUsrPhoto (UsrDat,ShowPhoto ? PhotoURL :
NULL,
"PHOTO45x60",Pho_ZOOM,false);
HTM_TD_End ();
HTM_TR_End ();
/* Start/end time (for user in this session) */
for (StartEndTime = (Dat_StartEndTime_t) 0;
StartEndTime <= (Dat_StartEndTime_t) (Dat_NUM_START_END_TIME - 1);
StartEndTime++)
{
HTM_TR_Begin (NULL);
HTM_TD_Begin ("class=\"DAT_N RT\"");
HTM_TxtF ("%s:",Txt_START_END_TIME[StartEndTime]);
HTM_TD_End ();
if (asprintf (&Id,"match_%u",(unsigned) StartEndTime) < 0)
Lay_NotEnoughMemoryExit ();
HTM_TD_Begin ("id=\"%s\" class=\"DAT LT\"",Id);
Dat_WriteLocalDateHMSFromUTC (Id,Print->TimeUTC[StartEndTime],
Gbl.Prefs.DateFormat,Dat_SEPARATOR_COMMA,
true,true,true,0x7);
HTM_TD_End ();
free (Id);
HTM_TR_End ();
}
/* Number of questions */
HTM_TR_Begin (NULL);
HTM_TD_Begin ("class=\"DAT_N RT\"");
HTM_TxtF ("%s:",Txt_Questions);
HTM_TD_End ();
HTM_TD_Begin ("class=\"DAT LT\"");
if (ICanView.Result)
HTM_TxtF ("%u (%u %s)",
Print->NumQsts,
Print->NumQstsNotBlank,Txt_non_blank_QUESTIONS);
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
HTM_TR_End ();
/* Score */
HTM_TR_Begin (NULL);
HTM_TD_Begin ("class=\"DAT_N RT\"");
HTM_TxtF ("%s:",Txt_Score);
HTM_TD_End ();
HTM_TD_Begin ("class=\"DAT LT\"");
if (ICanView.Score)
HTM_Double2Decimals (Print->Score);
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
HTM_TR_End ();
/* Grade */
HTM_TR_Begin (NULL);
HTM_TD_Begin ("class=\"DAT_N RT\"");
HTM_TxtF ("%s:",Txt_Grade);
HTM_TD_End ();
HTM_TD_Begin ("class=\"DAT LT\"");
if (ICanView.Score)
TstPrn_ComputeAndShowGrade (Print->NumQsts,Print->Score,
Exam->MaxGrade);
else
Ico_PutIconNotVisible ();
HTM_TD_End ();
HTM_TR_End ();
/***** Write answers and solutions *****/
if (ICanView.Result)
ExaRes_ShowExamAnswers (UsrDat,Print,Exam->Visibility);
/***** End table *****/
HTM_TABLE_End ();
/***** Write total mark of session result *****/
if (ICanView.Score)
{
HTM_DIV_Begin ("class=\"DAT_N_BOLD CM\"");
HTM_TxtColonNBSP (Txt_Score);
HTM_Double2Decimals (Print->Score);
HTM_BR ();
HTM_TxtColonNBSP (Txt_Grade);
TstPrn_ComputeAndShowGrade (Print->NumQsts,Print->Score,
Exam->MaxGrade);
HTM_DIV_End ();
}
/***** End box *****/
Box_BoxEnd ();
}
/*****************************************************************************/
/********************** Get if I can see session result ************************/
/*****************************************************************************/
static bool ExaRes_CheckIfICanSeePrintResult (const struct Exa_Exam *Exam,
const struct ExaSes_Session *Session,
long UsrCod)
{
bool ItsMe;
switch (Gbl.Usrs.Me.Role.Logged)
{
case Rol_STD:
ItsMe = Usr_ItsMe (UsrCod);
if (ItsMe && // The result is mine
!Exam->Hidden && // The exam is visible
!Session->Hidden && // The session is visible
Session->ShowUsrResults) // The results of the session are visible to users
return ExaSes_CheckIfICanListThisSessionBasedOnGrps (Session->SesCod);
return false;
case Rol_NET:
case Rol_TCH:
case Rol_DEG_ADM:
case Rol_CTR_ADM:
case Rol_INS_ADM:
case Rol_SYS_ADM:
return true;
default:
return false;
}
}
/*****************************************************************************/
/********************** Get if I can see session result ************************/
/*****************************************************************************/
static bool ExaRes_CheckIfICanViewScore (bool ICanViewResult,unsigned Visibility)
{
switch (Gbl.Usrs.Me.Role.Logged)
{
case Rol_STD:
if (ICanViewResult)
return TstVis_IsVisibleTotalScore (Visibility);
return false;
break;
case Rol_NET:
case Rol_TCH:
case Rol_DEG_ADM:
case Rol_CTR_ADM:
case Rol_INS_ADM:
case Rol_SYS_ADM:
return true;
default:
return false;
}
}
/*****************************************************************************/
/************** Show user's and correct answers of a test exam ***************/
/*****************************************************************************/
static void ExaRes_ShowExamAnswers (struct UsrData *UsrDat,
struct ExaPrn_Print *Print,
unsigned Visibility)
{
unsigned NumQst;
struct Tst_Question Question;
for (NumQst = 0;
NumQst < Print->NumQsts;
NumQst++)
{
Gbl.RowEvenOdd = NumQst % 2;
/***** Create test question *****/
Tst_QstConstructor (&Question);
Question.QstCod = Print->PrintedQuestions[NumQst].QstCod;
/***** Get question data *****/
ExaSet_GetQstDataFromDB (&Question);
/***** Write questions and answers *****/
ExaRes_WriteQstAndAnsExam (UsrDat,Print,NumQst,&Question,Visibility);
/***** Destroy test question *****/
Tst_QstDestructor (&Question);
}
}
/*****************************************************************************/
/********** Write a row of a test, with one question and its answer **********/
/*****************************************************************************/
static void ExaRes_WriteQstAndAnsExam (struct UsrData *UsrDat,
struct ExaPrn_Print *Print,
unsigned NumQst,
struct Tst_Question *Question,
unsigned Visibility)
{
extern const char *Txt_Score;
bool ICanView[TstVis_NUM_ITEMS_VISIBILITY];
static char *ClassTxt[Tst_NUM_VALIDITIES] =
{
[Tst_INVALID_QUESTION] = "TEST_TXT_RED",
[Tst_VALID_QUESTION ] = "TEST_TXT",
};
static char *ClassFeedback[Tst_NUM_VALIDITIES] =
{
[Tst_INVALID_QUESTION] = "TEST_TXT_LIGHT_RED",
[Tst_VALID_QUESTION ] = "TEST_TXT_LIGHT",
};
/***** Check if I can view each part of the question *****/
switch (Gbl.Usrs.Me.Role.Logged)
{
case Rol_STD:
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);
break;
case Rol_NET:
case Rol_TCH:
case Rol_DEG_ADM:
case Rol_CTR_ADM:
case Rol_INS_ADM:
case Rol_SYS_ADM:
ICanView[TstVis_VISIBLE_QST_ANS_TXT ] =
ICanView[TstVis_VISIBLE_FEEDBACK_TXT ] =
ICanView[TstVis_VISIBLE_CORRECT_ANSWER] =
ICanView[TstVis_VISIBLE_EACH_QST_SCORE] = true;
break;
default:
ICanView[TstVis_VISIBLE_QST_ANS_TXT ] =
ICanView[TstVis_VISIBLE_FEEDBACK_TXT ] =
ICanView[TstVis_VISIBLE_CORRECT_ANSWER] =
ICanView[TstVis_VISIBLE_EACH_QST_SCORE] = false;
break;
}
/***** Begin row *****/
HTM_TR_Begin (NULL);
/***** Number of question and answer type *****/
HTM_TD_Begin ("class=\"RT COLOR%u\"",Gbl.RowEvenOdd);
Tst_WriteNumQst (NumQst + 1,"BIG_INDEX");
Tst_WriteAnswerType (Question->Answer.Type,"DAT_SMALL");
HTM_TD_End ();
/***** Stem, media and answers *****/
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
/* Stem */
Tst_WriteQstStem (Question->Stem,ClassTxt[Question->Validity],
ICanView[TstVis_VISIBLE_QST_ANS_TXT]);
/* Media */
if (ICanView[TstVis_VISIBLE_QST_ANS_TXT])
Med_ShowMedia (&Question->Media,
"TEST_MED_SHOW_CONT",
"TEST_MED_SHOW");
/* Answers */
ExaPrn_ComputeAnswerScore (&Print->PrintedQuestions[NumQst],Question);
TstPrn_WriteAnswersExam (UsrDat,&Print->PrintedQuestions[NumQst],Question,
ICanView,
ClassTxt[Question->Validity],
ClassFeedback[Question->Validity]);
/* Write score retrieved from database */
if (ICanView[TstVis_VISIBLE_EACH_QST_SCORE])
{
HTM_DIV_Begin ("class=\"DAT_SMALL LM\"");
HTM_TxtColonNBSP (Txt_Score);
HTM_SPAN_Begin ("class=\"%s\"",
Print->PrintedQuestions[NumQst].StrAnswers[0] ?
(Print->PrintedQuestions[NumQst].Score > 0 ? "ANS_OK" : // Correct/semicorrect
"ANS_BAD") : // Wrong
"ANS_0"); // Blank answer
HTM_Double2Decimals (Print->PrintedQuestions[NumQst].Score);
HTM_SPAN_End ();
HTM_DIV_End ();
}
/* Question feedback */
if (ICanView[TstVis_VISIBLE_FEEDBACK_TXT])
Tst_WriteQstFeedback (Question->Feedback,ClassFeedback[Question->Validity]);
HTM_TD_End ();
/***** End row *****/
HTM_TR_End ();
}