// 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 . */ /*****************************************************************************/ /********************************* Headers ***********************************/ /*****************************************************************************/ #define _GNU_SOURCE // For asprintf #include // For PATH_MAX #include // For NULL #include // For asprintf #include // For calloc #include // For string functions #include "swad_action.h" #include "swad_database.h" #include "swad_date.h" #include "swad_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 (" %s",UsrDat->Surname1); if (UsrDat->Surname2[0]) HTM_TxtF (" %s",UsrDat->Surname2); if (UsrDat->FirstName[0]) HTM_TxtF (", %s",UsrDat->FirstName); HTM_BR (); ShowPhoto = Pho_ShowingUsrPhotoIsAllowed (UsrDat,PhotoURL); Pho_ShowUsrPhoto (UsrDat,ShowPhoto ? PhotoURL : NULL, "PHOTO45x60",Pho_ZOOM,false); HTM_TD_End (); HTM_TR_End (); /* Start/end time (for user in this 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 (); }