diff --git a/Makefile b/Makefile
index ced82a0f..5ee496c4 100644
--- a/Makefile
+++ b/Makefile
@@ -35,7 +35,7 @@ OBJS = swad_account.o swad_action.o swad_agenda.o swad_alert.o \
swad_course_config.o swad_cryptography.o \
swad_database.o swad_date.o swad_degree.o swad_degree_config.o \
swad_degree_type.o swad_department.o swad_duplicate.o \
- swad_enrolment.o swad_exam.o swad_exam_announcement.o \
+ swad_enrolment.o swad_exam.o swad_exam_announcement.o swad_exam_log.o \
swad_exam_print.o swad_exam_result.o swad_exam_session.o \
swad_exam_set.o \
swad_figure.o swad_figure_cache.o swad_file.o swad_file_browser.o \
diff --git a/sql/cambios.sql b/sql/cambios.sql
index 1dc7efd7..1ef6ecb8 100644
--- a/sql/cambios.sql
+++ b/sql/cambios.sql
@@ -13363,4 +13363,15 @@ SELECT institutions.InsCod,institutions.ShortName,institutions.FullName,centres.
--------------------------------
\ No newline at end of file
+-------------------------------
+
+SELECT * FROM
+(SELECT PrnCod,Score AS S FROM exa_prints ORDER BY PrnCod) AS ep
+LEFT JOIN
+(SELECT PrnCod,SUM(Score) AS S FROM exa_print_questions GROUP BY PrnCod ORDER BY PrnCod) AS epq
+ON ep.PrnCod=epq.PrnCod
+WHERE ABS(ep.S-epq.S)<0.001;
+
+
+
+
diff --git a/sql/swad.sql b/sql/swad.sql
index a7ac0c5d..aa2ebdf6 100644
--- a/sql/swad.sql
+++ b/sql/swad.sql
@@ -484,6 +484,19 @@ CREATE TABLE IF NOT EXISTS exa_exams (
UNIQUE INDEX(ExaCod),
INDEX(CrsCod));
--
+-- Table exa_log: stores the access log to exam prints
+--
+CREATE TABLE IF NOT EXISTS exa_log (
+ LogCod INT NOT NULL,
+ PrnCod INT NOT NULL,
+ ActCod INT NOT NULL,
+ ClickTime DATETIME NOT NULL,
+ IP CHAR(15) NOT NULL,
+ SessionId CHAR(43) NOT NULL,
+ UNIQUE INDEX(LogCod),
+ INDEX(PrnCod,ClickTime),
+ INDEX(ClickTime));
+--
-- Table exa_print_questions: stores the questions and answers in exam prints made by users
--
CREATE TABLE IF NOT EXISTS exa_print_questions (
diff --git a/swad_action.c b/swad_action.c
index c3636d04..ca69a662 100644
--- a/swad_action.c
+++ b/swad_action.c
@@ -727,11 +727,12 @@ const struct Act_Actions Act_Actions[Act_NUM_ACTIONS] =
[ActSeeExaPrn ] = {1904,-1,TabUnk,ActSeeAllExa ,0x238,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,ExaPrn_ShowExamPrint ,NULL},
[ActAnsExaPrn ] = {1906,-1,TabUnk,ActSeeAllExa ,0x238,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_AJAX_NORMAL,NULL ,ExaPrn_ReceivePrintAnswer ,NULL},
+ [ActEndExaPrn ] = {1908,-1,TabUnk,ActSeeAllExa ,0x238,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,ExaRes_ShowOneExaResult ,NULL},
[ActSeeMyExaResCrs ] = {1867,-1,TabUnk,ActSeeAllExa ,0x208,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,ExaRes_ShowMyResultsInCrs ,NULL},
[ActSeeMyExaResExa ] = {1868,-1,TabUnk,ActSeeAllExa ,0x208,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,ExaRes_ShowMyResultsInExa ,NULL},
[ActSeeMyExaResSes ] = {1869,-1,TabUnk,ActSeeAllExa ,0x208,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,ExaRes_ShowMyResultsInSes ,NULL},
- [ActSeeOneExaResMe ] = {1870,-1,TabUnk,ActSeeAllExa ,0x238,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,ExaRes_ShowOneExaResult ,NULL},
+ [ActSeeOneExaResMe ] = {1870,-1,TabUnk,ActSeeAllExa ,0x208,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,ExaRes_ShowOneExaResult ,NULL},
[ActReqSeeUsrExaRes ] = {1871,-1,TabUnk,ActSeeAllExa ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,ExaRes_SelUsrsToViewResults ,NULL},
[ActSeeUsrExaResCrs ] = {1872,-1,TabUnk,ActSeeAllExa ,0x230,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,ExaRes_ShowAllResultsInCrs ,NULL},
@@ -3716,6 +3717,7 @@ Act_Action_t Act_FromActCodToAction[1 + Act_MAX_ACTION_COD] = // Do not reuse un
-1, // #1905 (obsolete action)
ActAnsExaPrn, // #1906
ActEdiTag, // #1907
+ ActEndExaPrn, // #1908
};
/*****************************************************************************/
diff --git a/swad_action.h b/swad_action.h
index aabfee0d..7d6e2cc4 100644
--- a/swad_action.h
+++ b/swad_action.h
@@ -64,7 +64,7 @@ typedef enum
typedef signed int Act_Action_t; // Must be a signed type, because -1 is used to indicate obsolete action
-#define Act_MAX_ACTION_COD 1907
+#define Act_MAX_ACTION_COD 1908
#define Act_MAX_OPTIONS_IN_MENU_PER_TAB 13
@@ -692,83 +692,84 @@ typedef signed int Act_Action_t; // Must be a signed type, because -1 is used to
#define ActSeeExaPrn (ActChgCrsTT1stDay + 163)
#define ActAnsExaPrn (ActChgCrsTT1stDay + 164)
+#define ActEndExaPrn (ActChgCrsTT1stDay + 165)
-#define ActSeeMyExaResCrs (ActChgCrsTT1stDay + 165)
-#define ActSeeMyExaResExa (ActChgCrsTT1stDay + 166)
-#define ActSeeMyExaResSes (ActChgCrsTT1stDay + 167)
-#define ActSeeOneExaResMe (ActChgCrsTT1stDay + 168)
-#define ActReqSeeUsrExaRes (ActChgCrsTT1stDay + 169)
-#define ActSeeUsrExaResCrs (ActChgCrsTT1stDay + 170)
-#define ActSeeUsrExaResExa (ActChgCrsTT1stDay + 171)
-#define ActSeeUsrExaResSes (ActChgCrsTT1stDay + 172)
-#define ActSeeOneExaResOth (ActChgCrsTT1stDay + 173)
-#define ActChgVisExaRes (ActChgCrsTT1stDay + 174)
+#define ActSeeMyExaResCrs (ActChgCrsTT1stDay + 166)
+#define ActSeeMyExaResExa (ActChgCrsTT1stDay + 167)
+#define ActSeeMyExaResSes (ActChgCrsTT1stDay + 168)
+#define ActSeeOneExaResMe (ActChgCrsTT1stDay + 169)
+#define ActReqSeeUsrExaRes (ActChgCrsTT1stDay + 170)
+#define ActSeeUsrExaResCrs (ActChgCrsTT1stDay + 171)
+#define ActSeeUsrExaResExa (ActChgCrsTT1stDay + 172)
+#define ActSeeUsrExaResSes (ActChgCrsTT1stDay + 173)
+#define ActSeeOneExaResOth (ActChgCrsTT1stDay + 174)
+#define ActChgVisExaRes (ActChgCrsTT1stDay + 175)
-#define ActSeeGam (ActChgCrsTT1stDay + 175)
-#define ActReqRemMch (ActChgCrsTT1stDay + 176)
-#define ActRemMch (ActChgCrsTT1stDay + 177)
-#define ActReqNewMch (ActChgCrsTT1stDay + 178)
-#define ActNewMch (ActChgCrsTT1stDay + 179)
-#define ActResMch (ActChgCrsTT1stDay + 180)
-#define ActBckMch (ActChgCrsTT1stDay + 181)
-#define ActPlyPauMch (ActChgCrsTT1stDay + 182)
-#define ActFwdMch (ActChgCrsTT1stDay + 183)
-#define ActChgNumColMch (ActChgCrsTT1stDay + 184)
-#define ActChgVisResMchQst (ActChgCrsTT1stDay + 185)
-#define ActMchCntDwn (ActChgCrsTT1stDay + 186)
-#define ActRefMchTch (ActChgCrsTT1stDay + 187)
+#define ActSeeGam (ActChgCrsTT1stDay + 176)
+#define ActReqRemMch (ActChgCrsTT1stDay + 177)
+#define ActRemMch (ActChgCrsTT1stDay + 178)
+#define ActReqNewMch (ActChgCrsTT1stDay + 179)
+#define ActNewMch (ActChgCrsTT1stDay + 180)
+#define ActResMch (ActChgCrsTT1stDay + 181)
+#define ActBckMch (ActChgCrsTT1stDay + 182)
+#define ActPlyPauMch (ActChgCrsTT1stDay + 183)
+#define ActFwdMch (ActChgCrsTT1stDay + 184)
+#define ActChgNumColMch (ActChgCrsTT1stDay + 185)
+#define ActChgVisResMchQst (ActChgCrsTT1stDay + 186)
+#define ActMchCntDwn (ActChgCrsTT1stDay + 187)
+#define ActRefMchTch (ActChgCrsTT1stDay + 188)
-#define ActJoiMch (ActChgCrsTT1stDay + 188)
-#define ActSeeMchAnsQstStd (ActChgCrsTT1stDay + 189)
-#define ActRemMchAnsQstStd (ActChgCrsTT1stDay + 190)
-#define ActAnsMchQstStd (ActChgCrsTT1stDay + 191)
-#define ActRefMchStd (ActChgCrsTT1stDay + 192)
+#define ActJoiMch (ActChgCrsTT1stDay + 189)
+#define ActSeeMchAnsQstStd (ActChgCrsTT1stDay + 190)
+#define ActRemMchAnsQstStd (ActChgCrsTT1stDay + 191)
+#define ActAnsMchQstStd (ActChgCrsTT1stDay + 192)
+#define ActRefMchStd (ActChgCrsTT1stDay + 193)
-#define ActSeeMyMchResCrs (ActChgCrsTT1stDay + 193)
-#define ActSeeMyMchResGam (ActChgCrsTT1stDay + 194)
-#define ActSeeMyMchResMch (ActChgCrsTT1stDay + 195)
-#define ActSeeOneMchResMe (ActChgCrsTT1stDay + 196)
+#define ActSeeMyMchResCrs (ActChgCrsTT1stDay + 194)
+#define ActSeeMyMchResGam (ActChgCrsTT1stDay + 195)
+#define ActSeeMyMchResMch (ActChgCrsTT1stDay + 196)
+#define ActSeeOneMchResMe (ActChgCrsTT1stDay + 197)
-#define ActReqSeeUsrMchRes (ActChgCrsTT1stDay + 197)
-#define ActSeeUsrMchResCrs (ActChgCrsTT1stDay + 198)
-#define ActSeeUsrMchResGam (ActChgCrsTT1stDay + 199)
-#define ActSeeUsrMchResMch (ActChgCrsTT1stDay + 200)
-#define ActSeeOneMchResOth (ActChgCrsTT1stDay + 201)
+#define ActReqSeeUsrMchRes (ActChgCrsTT1stDay + 198)
+#define ActSeeUsrMchResCrs (ActChgCrsTT1stDay + 199)
+#define ActSeeUsrMchResGam (ActChgCrsTT1stDay + 200)
+#define ActSeeUsrMchResMch (ActChgCrsTT1stDay + 201)
+#define ActSeeOneMchResOth (ActChgCrsTT1stDay + 202)
-#define ActChgVisResMchUsr (ActChgCrsTT1stDay + 202)
+#define ActChgVisResMchUsr (ActChgCrsTT1stDay + 203)
-#define ActFrmNewGam (ActChgCrsTT1stDay + 203)
-#define ActEdiOneGam (ActChgCrsTT1stDay + 204)
-#define ActNewGam (ActChgCrsTT1stDay + 205)
-#define ActChgGam (ActChgCrsTT1stDay + 206)
-#define ActReqRemGam (ActChgCrsTT1stDay + 207)
-#define ActRemGam (ActChgCrsTT1stDay + 208)
-#define ActHidGam (ActChgCrsTT1stDay + 209)
-#define ActShoGam (ActChgCrsTT1stDay + 210)
-#define ActAddOneGamQst (ActChgCrsTT1stDay + 211)
-#define ActGamLstTstQst (ActChgCrsTT1stDay + 212)
-#define ActAddTstQstToGam (ActChgCrsTT1stDay + 213)
-#define ActReqRemGamQst (ActChgCrsTT1stDay + 214)
-#define ActRemGamQst (ActChgCrsTT1stDay + 215)
-#define ActUp_GamQst (ActChgCrsTT1stDay + 216)
-#define ActDwnGamQst (ActChgCrsTT1stDay + 217)
+#define ActFrmNewGam (ActChgCrsTT1stDay + 204)
+#define ActEdiOneGam (ActChgCrsTT1stDay + 205)
+#define ActNewGam (ActChgCrsTT1stDay + 206)
+#define ActChgGam (ActChgCrsTT1stDay + 207)
+#define ActReqRemGam (ActChgCrsTT1stDay + 208)
+#define ActRemGam (ActChgCrsTT1stDay + 209)
+#define ActHidGam (ActChgCrsTT1stDay + 210)
+#define ActShoGam (ActChgCrsTT1stDay + 211)
+#define ActAddOneGamQst (ActChgCrsTT1stDay + 212)
+#define ActGamLstTstQst (ActChgCrsTT1stDay + 213)
+#define ActAddTstQstToGam (ActChgCrsTT1stDay + 214)
+#define ActReqRemGamQst (ActChgCrsTT1stDay + 215)
+#define ActRemGamQst (ActChgCrsTT1stDay + 216)
+#define ActUp_GamQst (ActChgCrsTT1stDay + 217)
+#define ActDwnGamQst (ActChgCrsTT1stDay + 218)
-#define ActSeeSvy (ActChgCrsTT1stDay + 218)
-#define ActAnsSvy (ActChgCrsTT1stDay + 219)
-#define ActFrmNewSvy (ActChgCrsTT1stDay + 220)
-#define ActEdiOneSvy (ActChgCrsTT1stDay + 221)
-#define ActNewSvy (ActChgCrsTT1stDay + 222)
-#define ActChgSvy (ActChgCrsTT1stDay + 223)
-#define ActReqRemSvy (ActChgCrsTT1stDay + 224)
-#define ActRemSvy (ActChgCrsTT1stDay + 225)
-#define ActReqRstSvy (ActChgCrsTT1stDay + 226)
-#define ActRstSvy (ActChgCrsTT1stDay + 227)
-#define ActHidSvy (ActChgCrsTT1stDay + 228)
-#define ActShoSvy (ActChgCrsTT1stDay + 229)
-#define ActEdiOneSvyQst (ActChgCrsTT1stDay + 230)
-#define ActRcvSvyQst (ActChgCrsTT1stDay + 231)
-#define ActReqRemSvyQst (ActChgCrsTT1stDay + 232)
-#define ActRemSvyQst (ActChgCrsTT1stDay + 233)
+#define ActSeeSvy (ActChgCrsTT1stDay + 219)
+#define ActAnsSvy (ActChgCrsTT1stDay + 220)
+#define ActFrmNewSvy (ActChgCrsTT1stDay + 221)
+#define ActEdiOneSvy (ActChgCrsTT1stDay + 222)
+#define ActNewSvy (ActChgCrsTT1stDay + 223)
+#define ActChgSvy (ActChgCrsTT1stDay + 224)
+#define ActReqRemSvy (ActChgCrsTT1stDay + 225)
+#define ActRemSvy (ActChgCrsTT1stDay + 226)
+#define ActReqRstSvy (ActChgCrsTT1stDay + 227)
+#define ActRstSvy (ActChgCrsTT1stDay + 228)
+#define ActHidSvy (ActChgCrsTT1stDay + 229)
+#define ActShoSvy (ActChgCrsTT1stDay + 230)
+#define ActEdiOneSvyQst (ActChgCrsTT1stDay + 231)
+#define ActRcvSvyQst (ActChgCrsTT1stDay + 232)
+#define ActReqRemSvyQst (ActChgCrsTT1stDay + 233)
+#define ActRemSvyQst (ActChgCrsTT1stDay + 234)
/*****************************************************************************/
/******************************** Files tab **********************************/
diff --git a/swad_changelog.h b/swad_changelog.h
index 5b903384..21ad3bfc 100644
--- a/swad_changelog.h
+++ b/swad_changelog.h
@@ -557,10 +557,16 @@ enscript -2 --landscape --color --file-align=2 --highlight --line-numbers -o - *
En OpenSWAD:
ps2pdf source.ps destination.pdf
*/
-#define Log_PLATFORM_VERSION "SWAD 19.239.9 (2020-05-21)"
+#define Log_PLATFORM_VERSION "SWAD 19.241 (2020-05-22)"
#define CSS_FILE "swad19.238.2.css"
#define JS_FILE "swad19.239.6.js"
/*
+ Version 19.241: May 22, 2020 Log in exams.
+ Bug fixing and code refactoring in tests and exams. (301712 lines)
+ 1 change necessary in database:
+CREATE TABLE IF NOT EXISTS exa_log (LogCod INT NOT NULL,PrnCod INT NOT NULL,ActCod INT NOT NULL,ClickTime DATETIME NOT NULL,IP CHAR(15) NOT NULL,SessionId CHAR(43) NOT NULL,UNIQUE INDEX(LogCod),INDEX(PrnCod,ClickTime),INDEX(ClickTime));
+
+ Version 19.240: May 21, 2020 Code refactoring in tests and exams. (301428 lines)
Version 19.239.9: May 21, 2020 Fixed issue in exam sessions: exam prints in sessions of hidden exams are no accesible. (301441 lines)
Version 19.239.8: May 21, 2020 Fixed issue in exam sessions: a student can not see hidden sessions. (301433 lines)
Version 19.239.7: May 21, 2020 Fixed bug in permissions to see exam results. Reported by Eduardo Ros Vidal. (301412 lines)
diff --git a/swad_database.c b/swad_database.c
index 2ad9f2b2..d214d892 100644
--- a/swad_database.c
+++ b/swad_database.c
@@ -1044,6 +1044,33 @@ mysql> DESCRIBE exa_exams;
"UNIQUE INDEX(ExaCod),"
"INDEX(CrsCod))");
+ /***** Table exa_log *****/
+/*
+mysql> DESCRIBE exa_log;
++-----------+----------+------+-----+---------+-------+
+| Field | Type | Null | Key | Default | Extra |
++-----------+----------+------+-----+---------+-------+
+| LogCod | int(11) | NO | PRI | NULL | |
+| PrnCod | int(11) | NO | MUL | NULL | |
+| ActCod | int(11) | NO | | NULL | |
+| ClickTime | datetime | NO | MUL | NULL | |
+| IP | char(15) | NO | | NULL | |
+| SessionId | char(43) | NO | | NULL | |
++-----------+----------+------+-----+---------+-------+
+6 rows in set (0.00 sec)
+*/
+// TODO: Change NtfCod and LogCod from INT to BIGINT in database tables.
+ DB_CreateTable ("CREATE TABLE IF NOT EXISTS exa_log ("
+ "LogCod INT NOT NULL,"
+ "PrnCod INT NOT NULL,"
+ "ActCod INT NOT NULL,"
+ "ClickTime DATETIME NOT NULL,"
+ "IP CHAR(15) NOT NULL," // Cns_MAX_BYTES_IP
+ "SessionId CHAR(43) NOT NULL," // Cns_BYTES_SESSION_ID
+ "UNIQUE INDEX(LogCod),"
+ "INDEX(PrnCod,ClickTime),"
+ "INDEX(ClickTime))");
+
/***** Table exa_print_questions *****/
/*
mysql> DESCRIBE exa_print_questions;
diff --git a/swad_exam_log.c b/swad_exam_log.c
new file mode 100644
index 00000000..1d12b823
--- /dev/null
+++ b/swad_exam_log.c
@@ -0,0 +1,99 @@
+// swad_exam_log.c: exam access log
+
+/*
+ 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_box.h"
+#include "swad_database.h"
+// #include "swad_exam.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"
+
+/*****************************************************************************/
+/************** External global variables from others modules ****************/
+/*****************************************************************************/
+
+extern struct Globals Gbl;
+
+/*****************************************************************************/
+/***************************** Private constants *****************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/******************************* Private types *******************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/***************************** Private variables *****************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/***************************** Private prototypes ****************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/**************************** Log access in database *************************/
+/*****************************************************************************/
+
+void ExaLog_LogAccess (long LogCod)
+ {
+ long PrnCod;
+
+ if (Gbl.Action.Act == ActAnsExaPrn || // Answer question
+ Gbl.Action.Act == ActSeeExaPrn || // Create/resume print exam
+ Gbl.Action.Act == ActEndExaPrn) // End print exam
+ {
+ PrnCod = ExaPrn_GetCurrentPrnCod ();
+
+ if (PrnCod > 0) // Only if exam print is accesible (visible, open...)
+ /***** Insert access into database *****/
+ /* Log access in exam log.
+ Redundant data (also present in log table) are stored for speed */
+ DB_QueryINSERT ("can not log exam access",
+ "INSERT INTO exa_log "
+ "(LogCod,PrnCod,ActCod,ClickTime,IP,SessionId)"
+ " VALUES "
+ "(%ld,%ld,%ld,NOW(),'%s','%s')",
+ LogCod,
+ PrnCod,
+ Act_GetActCod (Gbl.Action.Act), // Redundant, for speed
+ // NOW() Redundant, for speed
+ Gbl.IP, // Redundant, for speed
+ Gbl.Session.Id);
+ }
+ }
diff --git a/swad_exam_log.h b/swad_exam_log.h
new file mode 100644
index 00000000..ee8793a9
--- /dev/null
+++ b/swad_exam_log.h
@@ -0,0 +1,42 @@
+// swad_exam_log.h: exam access log
+
+#ifndef _SWAD_EXA_LOG
+#define _SWAD_EXA_LOG
+/*
+ SWAD (Shared Workspace At a Distance in Spanish),
+ is a web platform developed at the University of Granada (Spain),
+ and used to support university teaching.
+
+ This file is part of SWAD core.
+ Copyright (C) 1999-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 **********************************/
+/*****************************************************************************/
+
+
+/*****************************************************************************/
+/************************* Public types and constants ************************/
+/*****************************************************************************/
+
+
+/*****************************************************************************/
+/***************************** Public prototypes *****************************/
+/*****************************************************************************/
+
+void ExaLog_LogAccess (long LogCod);
+
+#endif
diff --git a/swad_exam_print.c b/swad_exam_print.c
index 460557a6..3e67677f 100644
--- a/swad_exam_print.c
+++ b/swad_exam_print.c
@@ -61,6 +61,8 @@ extern struct Globals Gbl;
/***************************** Private variables *****************************/
/*****************************************************************************/
+static long ExaPrn_CurrentPrnCod = -1L;
+
/*****************************************************************************/
/***************************** Private prototypes ****************************/
/*****************************************************************************/
@@ -107,7 +109,6 @@ static unsigned ExaPrn_GetAnswerFromForm (struct ExaPrn_Print *Print);
static unsigned ExaPrn_GetParamQstInd (void);
-static void ExaPrn_ComputeScoresAndStoreQuestionsOfPrint (struct ExaPrn_Print *Print);
static void ExaPrn_ComputeScoreAndStoreQuestionOfPrint (struct ExaPrn_Print *Print,
unsigned NumQst);
@@ -141,6 +142,20 @@ static void ExaPrn_GetNumQstsNotBlank (struct ExaPrn_Print *Print);
static void ExaPrn_ComputeTotalScoreOfPrint (struct ExaPrn_Print *Print);
static void ExaPrn_UpdatePrintInDB (const struct ExaPrn_Print *Print);
+/*****************************************************************************/
+/************* Set and get current exam print code (used in log) *************/
+/*****************************************************************************/
+
+void ExaPrn_SetCurrentPrnCod (long PrnCod)
+ {
+ ExaPrn_CurrentPrnCod = PrnCod;
+ }
+
+long ExaPrn_GetCurrentPrnCod (void)
+ {
+ return ExaPrn_CurrentPrnCod;
+ }
+
/*****************************************************************************/
/**************************** Reset exam print *******************************/
/*****************************************************************************/
@@ -201,13 +216,13 @@ void ExaPrn_ShowExamPrint (void)
ExaPrn_GetQuestionsForNewPrintFromDB (&Print,Exam.ExaCod);
if (Print.NumQsts)
- {
/***** Create/update new exam print in database *****/
ExaPrn_CreatePrintInDB (&Print);
- ExaPrn_ComputeScoresAndStoreQuestionsOfPrint (&Print);
- }
}
+ /***** Set current print code (to be used in log) *****/
+ ExaPrn_SetCurrentPrnCod (Print.PrnCod);
+
/***** Show test exam to be answered *****/
ExaPrn_ShowExamPrintToFillIt (&Exams,&Exam,&Print);
}
@@ -417,6 +432,9 @@ static unsigned ExaPrn_GetSomeQstsFromSetToPrint (struct ExaPrn_Print *Print,
If the user does not confirm the submission of their exam ==>
==> the exam may be half filled ==> the answers displayed will be those selected by the user. */
Print->PrintedQuestions[*NumQstInPrint].StrAnswers[0] = '\0';
+
+ /* Reset score of this question in print */
+ Print->PrintedQuestions[*NumQstInPrint].Score = 0.0;
}
return NumQstsInSet;
@@ -493,6 +511,8 @@ static void ExaPrn_GenerateChoiceIndexes (struct TstPrn_PrintedQuestion *Printed
static void ExaPrn_CreatePrintInDB (struct ExaPrn_Print *Print)
{
+ unsigned NumQst;
+
/***** Insert new exam print into table *****/
Print->PrnCod =
DB_QueryINSERTandReturnCode ("can not create new exam print",
@@ -503,6 +523,13 @@ static void ExaPrn_CreatePrintInDB (struct ExaPrn_Print *Print)
Print->SesCod,
Gbl.Usrs.Me.UsrDat.UsrCod,
Print->NumQsts);
+
+ /***** Store all questions (with blank answers)
+ of this exam print just generated in database *****/
+ for (NumQst = 0;
+ NumQst < Print->NumQsts;
+ NumQst++)
+ ExaPrn_StoreOneQstOfPrintInDB (Print,NumQst);
}
/*****************************************************************************/
@@ -599,7 +626,7 @@ static void ExaPrn_ShowExamPrintToFillIt (struct Exa_Exams *Exams,
HTM_DIV_End (); // Used for AJAX based refresh
/***** Form to end/close this exam print *****/
- Frm_StartForm (ActSeeOneExaResMe);
+ Frm_StartForm (ActEndExaPrn);
ExaSes_PutParamsEdit (Exams);
Btn_PutCreateButton (Txt_I_have_finished);
Frm_EndForm ();
@@ -968,6 +995,9 @@ void ExaPrn_ReceivePrintAnswer (void)
if (Print.PrnCod <= 0)
Lay_WrongExamExit ();
+ /***** Set current print code (to be used in log) *****/
+ ExaPrn_SetCurrentPrnCod (Print.PrnCod);
+
/***** Get questions and user's answers of exam print from database *****/
ExaPrn_GetPrintQuestionsFromDB (&Print);
@@ -1023,33 +1053,6 @@ static unsigned ExaPrn_GetParamQstInd (void)
return (unsigned) NumQst;
}
-/*****************************************************************************/
-/*********** Compute score of each question and store in database ************/
-/*****************************************************************************/
-
-static void ExaPrn_ComputeScoresAndStoreQuestionsOfPrint (struct ExaPrn_Print *Print)
- {
- unsigned NumQst;
-
- /***** Initialize total score *****/
- Print->Score = 0.0;
- Print->NumQstsNotBlank = 0;
-
- /***** Compute and store scores of all questions *****/
- for (NumQst = 0;
- NumQst < Print->NumQsts;
- NumQst++)
- {
- /* Compute question score and store in database */
- ExaPrn_ComputeScoreAndStoreQuestionOfPrint (Print,NumQst);
-
- /* Accumulate total score */
- Print->Score += Print->PrintedQuestions[NumQst].Score;
- if (Print->PrintedQuestions[NumQst].AnswerIsNotBlank)
- Print->NumQstsNotBlank++;
- }
- }
-
/*****************************************************************************/
/*********** Compute score of one question and store in database *************/
/*****************************************************************************/
@@ -1074,7 +1077,12 @@ static void ExaPrn_ComputeScoreAndStoreQuestionOfPrint (struct ExaPrn_Print *Pri
ExaPrn_GetAnswerFromDB (Print,Print->PrintedQuestions[NumQst].QstCod,
CurrentStrAnswersInDB);
if (!strcmp (Print->PrintedQuestions[NumQst].StrAnswers,CurrentStrAnswersInDB))
- Print->PrintedQuestions[NumQst].StrAnswers[0] = '\0';
+ {
+ /* The answer just clicked by user
+ is the same as the last one checked and stored in database */
+ Print->PrintedQuestions[NumQst].StrAnswers[0] = '\0'; // Uncheck option
+ Print->PrintedQuestions[NumQst].Score = 0; // Clear question score
+ }
}
/***** Store test exam question in database *****/
@@ -1395,7 +1403,9 @@ static void ExaPrn_ComputeTotalScoreOfPrint (struct ExaPrn_Print *Print)
/***** Compute total score of exam print *****/
if (DB_QuerySELECT (&mysql_res,"can not get score of exam print",
- "SELECT SUM(Score) FROM exa_print_questions WHERE PrnCod=%ld",
+ "SELECT SUM(Score)"
+ " FROM exa_print_questions"
+ " WHERE PrnCod=%ld",
Print->PrnCod))
{
/***** Get sum of individual scores (row[0]) *****/
diff --git a/swad_exam_print.h b/swad_exam_print.h
index f6770b53..c705eea2 100644
--- a/swad_exam_print.h
+++ b/swad_exam_print.h
@@ -53,6 +53,9 @@ struct ExaPrn_Print
/***************************** Public prototypes *****************************/
/*****************************************************************************/
+void ExaPrn_SetCurrentPrnCod (long PrnCod);
+long ExaPrn_GetCurrentPrnCod (void);
+
void ExaPrn_ResetPrint (struct ExaPrn_Print *Print);
void ExaPrn_ShowExamPrint (void);
diff --git a/swad_exam_result.c b/swad_exam_result.c
index 4d166590..6d9b2bc7 100644
--- a/swad_exam_result.c
+++ b/swad_exam_result.c
@@ -1110,8 +1110,9 @@ void ExaRes_ShowOneExaResult (void)
ExaSes_GetAndCheckParameters (&Exams,&Exam,&Session);
/***** Pointer to user's data *****/
- MeOrOther = (Gbl.Action.Act == ActSeeOneExaResMe) ? Usr_ME :
- Usr_OTHER;
+ MeOrOther = (Gbl.Action.Act == ActSeeOneExaResMe ||
+ Gbl.Action.Act == ActEndExaPrn) ? Usr_ME :
+ Usr_OTHER;
switch (MeOrOther)
{
case Usr_ME:
@@ -1130,12 +1131,18 @@ void ExaRes_ShowOneExaResult (void)
Print.UsrCod = UsrDat->UsrCod;
ExaPrn_GetPrintDataBySesCodAndUsrCod (&Print);
+ /***** Set current print code (to be used in log) *****/
+ ExaPrn_SetCurrentPrnCod (Print.PrnCod);
+
/***** 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)
ICanViewResult = ExaRes_CheckIfICanSeePrintResult (&Session,UsrDat->UsrCod);
+
if (ICanViewResult)
+ // Depends on 5 visibility icons
ICanViewScore = TstVis_IsVisibleTotalScore (Exam.Visibility);
else
ICanViewScore = false;
@@ -1393,7 +1400,35 @@ static void ExaRes_WriteQstAndAnsExam (struct UsrData *UsrDat,
unsigned Visibility)
{
extern const char *Txt_Score;
- bool IsVisibleQstAndAnsTxt = TstVis_IsVisibleQstAndAnsTxt (Visibility);
+ bool IsVisible[TstVis_NUM_ITEMS_VISIBILITY];
+
+ /***** Check if I can view each part of the question *****/
+ switch (Gbl.Usrs.Me.Role.Logged)
+ {
+ case Rol_STD:
+ IsVisible[TstVis_VISIBLE_QST_ANS_TXT ] = TstVis_IsVisibleQstAndAnsTxt (Visibility);
+ IsVisible[TstVis_VISIBLE_FEEDBACK_TXT ] = TstVis_IsVisibleFeedbackTxt (Visibility);
+ IsVisible[TstVis_VISIBLE_CORRECT_ANSWER] = TstVis_IsVisibleCorrectAns (Visibility);
+ IsVisible[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:
+ IsVisible[TstVis_VISIBLE_QST_ANS_TXT ] =
+ IsVisible[TstVis_VISIBLE_FEEDBACK_TXT ] =
+ IsVisible[TstVis_VISIBLE_CORRECT_ANSWER] =
+ IsVisible[TstVis_VISIBLE_EACH_QST_SCORE] = true;
+ break;
+ default:
+ IsVisible[TstVis_VISIBLE_QST_ANS_TXT ] =
+ IsVisible[TstVis_VISIBLE_FEEDBACK_TXT ] =
+ IsVisible[TstVis_VISIBLE_CORRECT_ANSWER] =
+ IsVisible[TstVis_VISIBLE_EACH_QST_SCORE] = false;
+ break;
+ }
/***** Begin row *****/
HTM_TR_Begin (NULL);
@@ -1408,20 +1443,21 @@ static void ExaRes_WriteQstAndAnsExam (struct UsrData *UsrDat,
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
/* Stem */
- Tst_WriteQstStem (Question->Stem,"TEST_EXA",IsVisibleQstAndAnsTxt);
+ Tst_WriteQstStem (Question->Stem,"TEST_EXA",IsVisible[TstVis_VISIBLE_QST_ANS_TXT]);
/* Media */
- if (IsVisibleQstAndAnsTxt)
+ if (IsVisible[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,Visibility);
+ TstPrn_WriteAnswersExam (UsrDat,&Print->PrintedQuestions[NumQst],Question,
+ IsVisible);
/* Write score retrieved from database */
- if (TstVis_IsVisibleEachQstScore (Visibility))
+ if (IsVisible[TstVis_VISIBLE_EACH_QST_SCORE])
{
HTM_DIV_Begin ("class=\"DAT_SMALL LM\"");
HTM_TxtColonNBSP (Txt_Score);
@@ -1436,7 +1472,7 @@ static void ExaRes_WriteQstAndAnsExam (struct UsrData *UsrDat,
}
/* Question feedback */
- if (TstVis_IsVisibleFeedbackTxt (Visibility))
+ if (IsVisible[TstVis_VISIBLE_FEEDBACK_TXT])
Tst_WriteQstFeedback (Question->Feedback,"TEST_EXA_LIGHT");
HTM_TD_End ();
diff --git a/swad_exam_set.c b/swad_exam_set.c
index f4c179d8..1cb84e23 100644
--- a/swad_exam_set.c
+++ b/swad_exam_set.c
@@ -1566,7 +1566,7 @@ static void ExaSet_ListQuestionForEdition (const struct Tst_Question *Question,
Tst_WriteQstFeedback (Question->Feedback,"TEST_EDI_LIGHT");
/* Show answers */
- Tst_WriteAnswersListing (Question);
+ Tst_WriteAnswersBank (Question);
HTM_ARTICLE_End ();
HTM_TD_End ();
diff --git a/swad_log.c b/swad_log.c
index d534dd27..0b2c8392 100644
--- a/swad_log.c
+++ b/swad_log.c
@@ -32,6 +32,7 @@
#include "swad_banner.h"
#include "swad_config.h"
#include "swad_database.h"
+#include "swad_exam_log.h"
#include "swad_global.h"
#include "swad_HTML.h"
#include "swad_log.h"
@@ -123,6 +124,9 @@ void Log_LogAccess (const char *Comments)
Gbl.TimeSendInMicroseconds,
Gbl.IP);
+ /* Log access while answering exam prints */
+ ExaLog_LogAccess (LogCod);
+
/* Log comments */
if (Comments)
{
diff --git a/swad_match_result.c b/swad_match_result.c
index 95f4fe42..7026cd2b 100644
--- a/swad_match_result.c
+++ b/swad_match_result.c
@@ -1181,8 +1181,11 @@ void MchRes_ShowOneMchResult (void)
switch (Gbl.Usrs.Me.Role.Logged)
{
case Rol_STD:
+ // Depends on visibility of result for this match (eye icon)
ICanViewResult = MchRes_CheckIfICanSeeMatchResult (&Match,UsrDat->UsrCod);
+
if (ICanViewResult)
+ // Depends on 5 visibility icons
ICanViewScore = TstVis_IsVisibleTotalScore (Game.Visibility);
else
ICanViewScore = false;
diff --git a/swad_test.c b/swad_test.c
index 186b8d66..3ed164fe 100644
--- a/swad_test.c
+++ b/swad_test.c
@@ -183,25 +183,32 @@ static void TstPrn_WriteAnswersToFill (const struct TstPrn_PrintedQuestion *Prin
unsigned NumQst,
const struct Tst_Question *Question);
-static void Tst_WriteIntAnsListing (const struct Tst_Question *Question);
-static void TstPrn_WriteIntAnsSeeing (const struct TstPrn_PrintedQuestion *PrintedQuestion,
- unsigned NumQst);
+//-----------------------------------------------------------------------------
-static void Tst_WriteFloatAnsEdit (const struct Tst_Question *Question);
-static void TstPrn_WriteFloatAnsSeeing (const struct TstPrn_PrintedQuestion *PrintedQuestion,
- unsigned NumQst);
+static void Tst_WriteIntAnsBank (const struct Tst_Question *Question);
+static void Tst_WriteFltAnsBank (const struct Tst_Question *Question);
+static void Tst_WriteTF_AnsBank (const struct Tst_Question *Question);
+static void Tst_WriteChoAnsBank (const struct Tst_Question *Question);
-static void Tst_WriteTFAnsListing (const struct Tst_Question *Question);
-static void TstPrn_WriteTFAnsSeeing (const struct TstPrn_PrintedQuestion *PrintedQuestion,
- unsigned NumQst);
+//-----------------------------------------------------------------------------
-static void Tst_WriteChoiceAnsListing (const struct Tst_Question *Question);
-static void TstPrn_WriteChoiceAnsSeeing (const struct TstPrn_PrintedQuestion *PrintedQuestion,
- unsigned NumQst,
- const struct Tst_Question *Question);
+static void TstPrn_WriteIntAnsToFill (const struct TstPrn_PrintedQuestion *PrintedQuestion,
+ unsigned NumQst,
+ __attribute__((unused)) const struct Tst_Question *Question);
+static void TstPrn_WriteFltAnsToFill (const struct TstPrn_PrintedQuestion *PrintedQuestion,
+ unsigned NumQst,
+ __attribute__((unused)) const struct Tst_Question *Question);
+static void TstPrn_WriteTF_AnsToFill (const struct TstPrn_PrintedQuestion *PrintedQuestion,
+ unsigned NumQst,
+ __attribute__((unused)) const struct Tst_Question *Question);
+static void TstPrn_WriteChoAnsToFill (const struct TstPrn_PrintedQuestion *PrintedQuestion,
+ unsigned NumQst,
+ const struct Tst_Question *Question);
+static void TstPrn_WriteTxtAnsToFill (const struct TstPrn_PrintedQuestion *PrintedQuestion,
+ unsigned NumQst,
+ __attribute__((unused)) const struct Tst_Question *Question);
-static void TstPrn_WriteTextAnsSeeing (const struct TstPrn_PrintedQuestion *PrintedQuestion,
- unsigned NumQst);
+//-----------------------------------------------------------------------------
static bool Tst_GetParamsTst (struct Tst_Test *Test,
Tst_ActionToDoWithQuestions_t ActionToDoWithQuestions);
@@ -932,7 +939,7 @@ void Tst_ListQuestionForEdition (const struct Tst_Question *Question,
Tst_WriteQstFeedback (Question->Feedback,"TEST_EDI_LIGHT");
/* Show answers */
- Tst_WriteAnswersListing (Question);
+ Tst_WriteAnswersBank (Question);
}
else
{
@@ -2541,7 +2548,7 @@ static void Tst_WriteQuestionListing (struct Tst_Test *Test,unsigned NumQst)
/* Feedback (row[4]) and answers */
Tst_WriteQstFeedback (Test->Question.Feedback,"TEST_EDI_LIGHT");
- Tst_WriteAnswersListing (&Test->Question);
+ Tst_WriteAnswersBank (&Test->Question);
HTM_TD_End ();
/* Number of times this question has been answered */
@@ -2820,7 +2827,7 @@ static void Tst_WriteQuestionRowForSelection (unsigned NumQst,
Tst_WriteQstFeedback (Question->Feedback,"TEST_EDI_LIGHT");
/* Write answers */
- Tst_WriteAnswersListing (Question);
+ Tst_WriteAnswersBank (Question);
HTM_TD_End ();
/***** End table row *****/
@@ -2881,28 +2888,20 @@ void Tst_GetAnswersQst (struct Tst_Question *Question,MYSQL_RES **mysql_res,
/**************** Get and write the answers of a test question ***************/
/*****************************************************************************/
-void Tst_WriteAnswersListing (const struct Tst_Question *Question)
+void Tst_WriteAnswersBank (const struct Tst_Question *Question)
{
+ void (*TstPrn_WriteAnsBank[Tst_NUM_ANS_TYPES]) (const struct Tst_Question *Question) =
+ {
+ [Tst_ANS_INT ] = Tst_WriteIntAnsBank,
+ [Tst_ANS_FLOAT ] = Tst_WriteFltAnsBank,
+ [Tst_ANS_TRUE_FALSE ] = Tst_WriteTF_AnsBank,
+ [Tst_ANS_UNIQUE_CHOICE ] = Tst_WriteChoAnsBank,
+ [Tst_ANS_MULTIPLE_CHOICE] = Tst_WriteChoAnsBank,
+ [Tst_ANS_TEXT ] = Tst_WriteChoAnsBank,
+ };
+
/***** Write answers *****/
- switch (Question->Answer.Type)
- {
- case Tst_ANS_INT:
- Tst_WriteIntAnsListing (Question);
- break;
- case Tst_ANS_FLOAT:
- Tst_WriteFloatAnsEdit (Question);
- break;
- case Tst_ANS_TRUE_FALSE:
- Tst_WriteTFAnsListing (Question);
- break;
- case Tst_ANS_UNIQUE_CHOICE:
- case Tst_ANS_MULTIPLE_CHOICE:
- case Tst_ANS_TEXT:
- Tst_WriteChoiceAnsListing (Question);
- break;
- default:
- break;
- }
+ TstPrn_WriteAnsBank[Question->Answer.Type] (Question);
}
/*****************************************************************************/
@@ -2913,28 +2912,20 @@ static void TstPrn_WriteAnswersToFill (const struct TstPrn_PrintedQuestion *Prin
unsigned NumQst,
const struct Tst_Question *Question)
{
- /***** Write answer depending on type *****/
- switch (Question->Answer.Type)
- {
- case Tst_ANS_INT:
- TstPrn_WriteIntAnsSeeing (PrintedQuestion,NumQst);
- break;
- case Tst_ANS_FLOAT:
- TstPrn_WriteFloatAnsSeeing (PrintedQuestion,NumQst);
- break;
- case Tst_ANS_TRUE_FALSE:
- TstPrn_WriteTFAnsSeeing (PrintedQuestion,NumQst);
- break;
- case Tst_ANS_UNIQUE_CHOICE:
- case Tst_ANS_MULTIPLE_CHOICE:
- TstPrn_WriteChoiceAnsSeeing (PrintedQuestion,NumQst,Question);
- break;
- case Tst_ANS_TEXT:
- TstPrn_WriteTextAnsSeeing (PrintedQuestion,NumQst);
- break;
- default:
- break;
- }
+ void (*TstPrn_WriteAnsBank[Tst_NUM_ANS_TYPES]) (const struct TstPrn_PrintedQuestion *PrintedQuestion,
+ unsigned NumQst,
+ const struct Tst_Question *Question) =
+ {
+ [Tst_ANS_INT ] = TstPrn_WriteIntAnsToFill,
+ [Tst_ANS_FLOAT ] = TstPrn_WriteFltAnsToFill,
+ [Tst_ANS_TRUE_FALSE ] = TstPrn_WriteTF_AnsToFill,
+ [Tst_ANS_UNIQUE_CHOICE ] = TstPrn_WriteChoAnsToFill,
+ [Tst_ANS_MULTIPLE_CHOICE] = TstPrn_WriteChoAnsToFill,
+ [Tst_ANS_TEXT ] = TstPrn_WriteTxtAnsToFill,
+ };
+
+ /***** Write answers *****/
+ TstPrn_WriteAnsBank[Question->Answer.Type] (PrintedQuestion,NumQst,Question);
}
/*****************************************************************************/
@@ -2955,36 +2946,18 @@ bool Tst_CheckIfQuestionIsValidForGame (long QstCod)
/****************** Write integer answer when editing a test *****************/
/*****************************************************************************/
-static void Tst_WriteIntAnsListing (const struct Tst_Question *Question)
+static void Tst_WriteIntAnsBank (const struct Tst_Question *Question)
{
HTM_SPAN_Begin ("class=\"TEST_EDI\"");
HTM_TxtF ("(%ld)",Question->Answer.Integer);
HTM_SPAN_End ();
}
-/*****************************************************************************/
-/****************** Write integer answer when seeing a test ******************/
-/*****************************************************************************/
-
-static void TstPrn_WriteIntAnsSeeing (const struct TstPrn_PrintedQuestion *PrintedQuestion,
- unsigned NumQst)
- {
- char StrAns[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Ansxx...x"
-
- /***** Write input field for the answer *****/
- snprintf (StrAns,sizeof (StrAns),
- "Ans%010u",
- NumQst);
- HTM_INPUT_TEXT (StrAns,11,PrintedQuestion->StrAnswers,
- HTM_DONT_SUBMIT_ON_CHANGE,
- "size=\"11\"");
- }
-
/*****************************************************************************/
/****************** Write float answer when editing a test *******************/
/*****************************************************************************/
-static void Tst_WriteFloatAnsEdit (const struct Tst_Question *Question)
+static void Tst_WriteFltAnsBank (const struct Tst_Question *Question)
{
HTM_SPAN_Begin ("class=\"TEST_EDI\"");
HTM_Txt ("([");
@@ -2995,29 +2968,11 @@ static void Tst_WriteFloatAnsEdit (const struct Tst_Question *Question)
HTM_SPAN_End ();
}
-/*****************************************************************************/
-/****************** Write float answer when seeing a test ********************/
-/*****************************************************************************/
-
-static void TstPrn_WriteFloatAnsSeeing (const struct TstPrn_PrintedQuestion *PrintedQuestion,
- unsigned NumQst)
- {
- char StrAns[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Ansxx...x"
-
- /***** Write input field for the answer *****/
- snprintf (StrAns,sizeof (StrAns),
- "Ans%010u",
- NumQst);
- HTM_INPUT_TEXT (StrAns,Tst_MAX_BYTES_FLOAT_ANSWER,PrintedQuestion->StrAnswers,
- HTM_DONT_SUBMIT_ON_CHANGE,
- "size=\"11\"");
- }
-
/*****************************************************************************/
/*********** Write false / true answer when listing test questions ***********/
/*****************************************************************************/
-static void Tst_WriteTFAnsListing (const struct Tst_Question *Question)
+static void Tst_WriteTF_AnsBank (const struct Tst_Question *Question)
{
/***** Write answer *****/
HTM_SPAN_Begin ("class=\"TEST_EDI\"");
@@ -3027,54 +2982,11 @@ static void Tst_WriteTFAnsListing (const struct Tst_Question *Question)
HTM_SPAN_End ();
}
-/*****************************************************************************/
-/************** Write false / true answer when seeing a test ****************/
-/*****************************************************************************/
-
-static void TstPrn_WriteTFAnsSeeing (const struct TstPrn_PrintedQuestion *PrintedQuestion,
- unsigned NumQst)
- {
- extern const char *Txt_TF_QST[2];
-
- /***** Write selector for the answer *****/
- /* Initially user has not answered the question ==> initially all the answers will be blank.
- If the user does not confirm the submission of their exam ==>
- ==> the exam may be half filled ==> the answers displayed will be those selected by the user. */
- HTM_SELECT_Begin (HTM_DONT_SUBMIT_ON_CHANGE,
- "name=\"Ans%010u\"",NumQst);
- HTM_OPTION (HTM_Type_STRING,"" ,PrintedQuestion->StrAnswers[0] == '\0',false," ");
- HTM_OPTION (HTM_Type_STRING,"T",PrintedQuestion->StrAnswers[0] == 'T' ,false,"%s",Txt_TF_QST[0]);
- HTM_OPTION (HTM_Type_STRING,"F",PrintedQuestion->StrAnswers[0] == 'F' ,false,"%s",Txt_TF_QST[1]);
- HTM_SELECT_End ();
- }
-
-/*****************************************************************************/
-/************** Write false / true answer when seeing a test *****************/
-/*****************************************************************************/
-
-void Tst_WriteAnsTF (char AnsTF)
- {
- extern const char *Txt_TF_QST[2];
-
- switch (AnsTF)
- {
- case 'T': // true
- HTM_Txt (Txt_TF_QST[0]);
- break;
- case 'F': // false
- HTM_Txt (Txt_TF_QST[1]);
- break;
- default: // no answer
- HTM_NBSP ();
- break;
- }
- }
-
/*****************************************************************************/
/**** Write single or multiple choice answer when listing test questions *****/
/*****************************************************************************/
-static void Tst_WriteChoiceAnsListing (const struct Tst_Question *Question)
+static void Tst_WriteChoAnsBank (const struct Tst_Question *Question)
{
extern const char *Txt_TST_Answer_given_by_the_teachers;
unsigned NumOpt;
@@ -3160,19 +3072,89 @@ static void Tst_WriteChoiceAnsListing (const struct Tst_Question *Question)
HTM_TABLE_End ();
}
+/*****************************************************************************/
+/****************** Write integer answer when seeing a test ******************/
+/*****************************************************************************/
+
+static void TstPrn_WriteIntAnsToFill (const struct TstPrn_PrintedQuestion *PrintedQuestion,
+ unsigned NumQst,
+ __attribute__((unused)) const struct Tst_Question *Question)
+ {
+ char StrAns[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Ansxx...x"
+
+ /***** Write input field for the answer *****/
+ snprintf (StrAns,sizeof (StrAns),
+ "Ans%010u",
+ NumQst);
+ HTM_INPUT_TEXT (StrAns,11,PrintedQuestion->StrAnswers,
+ HTM_DONT_SUBMIT_ON_CHANGE,
+ "size=\"11\"");
+ }
+
+/*****************************************************************************/
+/****************** Write float answer when seeing a test ********************/
+/*****************************************************************************/
+
+static void TstPrn_WriteFltAnsToFill (const struct TstPrn_PrintedQuestion *PrintedQuestion,
+ unsigned NumQst,
+ __attribute__((unused)) const struct Tst_Question *Question)
+ {
+ char StrAns[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Ansxx...x"
+
+ /***** Write input field for the answer *****/
+ snprintf (StrAns,sizeof (StrAns),
+ "Ans%010u",
+ NumQst);
+ HTM_INPUT_TEXT (StrAns,Tst_MAX_BYTES_FLOAT_ANSWER,PrintedQuestion->StrAnswers,
+ HTM_DONT_SUBMIT_ON_CHANGE,
+ "size=\"11\"");
+ }
+
+/*****************************************************************************/
+/************** Write false / true answer when seeing a test ****************/
+/*****************************************************************************/
+
+static void TstPrn_WriteTF_AnsToFill (const struct TstPrn_PrintedQuestion *PrintedQuestion,
+ unsigned NumQst,
+ __attribute__((unused)) const struct Tst_Question *Question)
+ {
+ extern const char *Txt_TF_QST[2];
+
+ /***** Write selector for the answer *****/
+ /* Initially user has not answered the question ==> initially all the answers will be blank.
+ If the user does not confirm the submission of their exam ==>
+ ==> the exam may be half filled ==> the answers displayed will be those selected by the user. */
+ HTM_SELECT_Begin (HTM_DONT_SUBMIT_ON_CHANGE,
+ "name=\"Ans%010u\"",NumQst);
+ HTM_OPTION (HTM_Type_STRING,"" ,PrintedQuestion->StrAnswers[0] == '\0',false," ");
+ HTM_OPTION (HTM_Type_STRING,"T",PrintedQuestion->StrAnswers[0] == 'T' ,false,"%s",Txt_TF_QST[0]);
+ HTM_OPTION (HTM_Type_STRING,"F",PrintedQuestion->StrAnswers[0] == 'F' ,false,"%s",Txt_TF_QST[1]);
+ HTM_SELECT_End ();
+ }
+
/*****************************************************************************/
/******** Write single or multiple choice answer when seeing a test **********/
/*****************************************************************************/
-static void TstPrn_WriteChoiceAnsSeeing (const struct TstPrn_PrintedQuestion *PrintedQuestion,
- unsigned NumQst,
- const struct Tst_Question *Question)
+static void TstPrn_WriteChoAnsToFill (const struct TstPrn_PrintedQuestion *PrintedQuestion,
+ unsigned NumQst,
+ const struct Tst_Question *Question)
{
unsigned NumOpt;
unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]; // Indexes of all answers of this question
bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION];
char StrAns[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Ansxx...x"
+ /***** Change format of answers text *****/
+ for (NumOpt = 0;
+ NumOpt < Question->Answer.NumOptions;
+ NumOpt++)
+ /* Convert answer text, that is in HTML, to rigorous HTML */
+ if (Question->Answer.Options[NumOpt].Text[0])
+ Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
+ Question->Answer.Options[NumOpt].Text,
+ Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false);
+
/***** Get indexes for this question from string *****/
TstPrn_GetIndexesFromStr (PrintedQuestion->StrIndexes,Indexes);
@@ -3241,6 +3223,47 @@ static void TstPrn_WriteChoiceAnsSeeing (const struct TstPrn_PrintedQuestion *Pr
HTM_TABLE_End ();
}
+/*****************************************************************************/
+/******************** Write text answer when seeing a test *******************/
+/*****************************************************************************/
+
+static void TstPrn_WriteTxtAnsToFill (const struct TstPrn_PrintedQuestion *PrintedQuestion,
+ unsigned NumQst,
+ __attribute__((unused)) const struct Tst_Question *Question)
+ {
+ char StrAns[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Ansxx...x"
+
+ /***** Write input field for the answer *****/
+ snprintf (StrAns,sizeof (StrAns),
+ "Ans%010u",
+ NumQst);
+ HTM_INPUT_TEXT (StrAns,Tst_MAX_CHARS_ANSWERS_ONE_QST,PrintedQuestion->StrAnswers,
+ HTM_DONT_SUBMIT_ON_CHANGE,
+ "size=\"40\"");
+ }
+
+/*****************************************************************************/
+/************** Write false / true answer when seeing a test *****************/
+/*****************************************************************************/
+
+void Tst_WriteAnsTF (char AnsTF)
+ {
+ extern const char *Txt_TF_QST[2];
+
+ switch (AnsTF)
+ {
+ case 'T': // true
+ HTM_Txt (Txt_TF_QST[0]);
+ break;
+ case 'F': // false
+ HTM_Txt (Txt_TF_QST[1]);
+ break;
+ default: // no answer
+ HTM_NBSP ();
+ break;
+ }
+ }
+
/*****************************************************************************/
/************************ Get choice answer from row *************************/
/*****************************************************************************/
@@ -3301,24 +3324,6 @@ void Tst_GetChoiceAns (struct Tst_Question *Question,MYSQL_RES *mysql_res)
}
}
-/*****************************************************************************/
-/******************** Write text answer when seeing a test *******************/
-/*****************************************************************************/
-
-static void TstPrn_WriteTextAnsSeeing (const struct TstPrn_PrintedQuestion *PrintedQuestion,
- unsigned NumQst)
- {
- char StrAns[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Ansxx...x"
-
- /***** Write input field for the answer *****/
- snprintf (StrAns,sizeof (StrAns),
- "Ans%010u",
- NumQst);
- HTM_INPUT_TEXT (StrAns,Tst_MAX_CHARS_ANSWERS_ONE_QST,PrintedQuestion->StrAnswers,
- HTM_DONT_SUBMIT_ON_CHANGE,
- "size=\"40\"");
- }
-
/*****************************************************************************/
/*************** Write parameter with the code of a question *****************/
/*****************************************************************************/
@@ -5604,8 +5609,8 @@ static void Tst_InsertAnswersIntoDB (struct Tst_Question *Question)
void Tst_UpdateQstScoreInDB (struct TstPrn_PrintedQuestion *PrintedQuestion)
{
/***** Update number of clicks and score of the question *****/
- Str_SetDecimalPointToUS (); // To print the floating point as a dot
- if (PrintedQuestion->AnswerIsNotBlank)
+ Str_SetDecimalPointToUS (); // To print the floating point as a dot
+ if (PrintedQuestion->StrAnswers[0]) // User's answer is not blank
DB_QueryUPDATE ("can not update the score of a question",
"UPDATE tst_questions"
" SET NumHits=NumHits+1,NumHitsNotBlank=NumHitsNotBlank+1,"
@@ -5613,7 +5618,7 @@ void Tst_UpdateQstScoreInDB (struct TstPrn_PrintedQuestion *PrintedQuestion)
" WHERE QstCod=%ld",
PrintedQuestion->Score,
PrintedQuestion->QstCod);
- else // The answer is blank
+ else // User's answer is blank
DB_QueryUPDATE ("can not update the score of a question",
"UPDATE tst_questions"
" SET NumHits=NumHits+1"
diff --git a/swad_test.h b/swad_test.h
index d2fb1b82..45494119 100644
--- a/swad_test.h
+++ b/swad_test.h
@@ -130,7 +130,7 @@ void Tst_WriteParamEditQst (const struct Tst_Test *Test);
unsigned Tst_GetNumAnswersQst (long QstCod);
void Tst_GetAnswersQst (struct Tst_Question *Question,MYSQL_RES **mysql_res,
bool Shuffle);
-void Tst_WriteAnswersListing (const struct Tst_Question *Question);
+void Tst_WriteAnswersBank (const struct Tst_Question *Question);
bool Tst_CheckIfQuestionIsValidForGame (long QstCod);
void Tst_WriteAnsTF (char AnsTF);
void Tst_GetChoiceAns (struct Tst_Question *Question,MYSQL_RES *mysql_res);
diff --git a/swad_test_print.c b/swad_test_print.c
index 9cdb8eb4..5373b445 100644
--- a/swad_test_print.c
+++ b/swad_test_print.c
@@ -105,23 +105,23 @@ static void TstPrn_GetCorrectTxtAnswerFromDB (struct Tst_Question *Question);
static void TstPrn_WriteIntAnsPrint (struct UsrData *UsrDat,
const struct TstPrn_PrintedQuestion *PrintedQuestion,
const struct Tst_Question *Question,
- unsigned Visibility);
+ bool IsVisible[TstVis_NUM_ITEMS_VISIBILITY]);
static void TstPrn_WriteFltAnsPrint (struct UsrData *UsrDat,
const struct TstPrn_PrintedQuestion *PrintedQuestion,
const struct Tst_Question *Question,
- unsigned Visibility);
+ bool IsVisible[TstVis_NUM_ITEMS_VISIBILITY]);
static void TstPrn_WriteTF_AnsPrint (struct UsrData *UsrDat,
const struct TstPrn_PrintedQuestion *PrintedQuestion,
const struct Tst_Question *Question,
- unsigned Visibility);
+ bool IsVisible[TstVis_NUM_ITEMS_VISIBILITY]);
static void TstPrn_WriteChoAnsPrint (struct UsrData *UsrDat,
const struct TstPrn_PrintedQuestion *PrintedQuestion,
const struct Tst_Question *Question,
- unsigned Visibility);
+ bool IsVisible[TstVis_NUM_ITEMS_VISIBILITY]);
static void TstPrn_WriteTxtAnsPrint (struct UsrData *UsrDat,
const struct TstPrn_PrintedQuestion *PrintedQuestion,
const struct Tst_Question *Question,
- unsigned Visibility);
+ bool IsVisible[TstVis_NUM_ITEMS_VISIBILITY]);
//-----------------------------------------------------------------------------
@@ -250,7 +250,7 @@ void TstPrn_ShowPrintAfterAssess (struct TstPrn_Print *Print)
/***** Compute total score *****/
Print->Score += Print->PrintedQuestions[NumQst].Score;
- if (Print->PrintedQuestions[NumQst].AnswerIsNotBlank)
+ if (Print->PrintedQuestions[NumQst].StrAnswers[0]) // User's answer is not blank
Print->NumQstsNotBlank++;
/***** Update the number of accesses and the score of this question *****/
@@ -280,7 +280,35 @@ static void TstPrn_WriteQstAndAnsExam (struct UsrData *UsrDat,
extern const char *Txt_Question_removed;
extern const char *Txt_Question_modified;
bool QuestionUneditedAfterExam = false;
- bool IsVisibleQstAndAnsTxt = TstVis_IsVisibleQstAndAnsTxt (Visibility);
+ bool IsVisible[TstVis_NUM_ITEMS_VISIBILITY];
+
+ /***** Check if I can view each part of the question *****/
+ switch (Gbl.Usrs.Me.Role.Logged)
+ {
+ case Rol_STD:
+ IsVisible[TstVis_VISIBLE_QST_ANS_TXT ] = TstVis_IsVisibleQstAndAnsTxt (Visibility);
+ IsVisible[TstVis_VISIBLE_FEEDBACK_TXT ] = TstVis_IsVisibleFeedbackTxt (Visibility);
+ IsVisible[TstVis_VISIBLE_CORRECT_ANSWER] = TstVis_IsVisibleCorrectAns (Visibility);
+ IsVisible[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:
+ IsVisible[TstVis_VISIBLE_QST_ANS_TXT ] =
+ IsVisible[TstVis_VISIBLE_FEEDBACK_TXT ] =
+ IsVisible[TstVis_VISIBLE_CORRECT_ANSWER] =
+ IsVisible[TstVis_VISIBLE_EACH_QST_SCORE] = true;
+ break;
+ default:
+ IsVisible[TstVis_VISIBLE_QST_ANS_TXT ] =
+ IsVisible[TstVis_VISIBLE_FEEDBACK_TXT ] =
+ IsVisible[TstVis_VISIBLE_CORRECT_ANSWER] =
+ IsVisible[TstVis_VISIBLE_EACH_QST_SCORE] = false;
+ break;
+ }
/***** If this question has been edited later than test time
==> don't show question ****/
@@ -306,17 +334,18 @@ static void TstPrn_WriteQstAndAnsExam (struct UsrData *UsrDat,
if (QuestionUneditedAfterExam)
{
/* Stem */
- Tst_WriteQstStem (Question->Stem,"TEST_EXA",IsVisibleQstAndAnsTxt);
+ Tst_WriteQstStem (Question->Stem,"TEST_EXA",IsVisible[TstVis_VISIBLE_QST_ANS_TXT]);
/* Media */
- if (IsVisibleQstAndAnsTxt)
+ if (IsVisible[TstVis_VISIBLE_QST_ANS_TXT])
Med_ShowMedia (&Question->Media,
"TEST_MED_SHOW_CONT",
"TEST_MED_SHOW");
/* Answers */
TstPrn_ComputeAnswerScore (&Print->PrintedQuestions[NumQst],Question);
- TstPrn_WriteAnswersExam (UsrDat,&Print->PrintedQuestions[NumQst],Question,Visibility);
+ TstPrn_WriteAnswersExam (UsrDat,&Print->PrintedQuestions[NumQst],Question,
+ IsVisible);
}
else
Ale_ShowAlert (Ale_WARNING,Txt_Question_modified);
@@ -325,7 +354,7 @@ static void TstPrn_WriteQstAndAnsExam (struct UsrData *UsrDat,
Ale_ShowAlert (Ale_WARNING,Txt_Question_removed);
/* Write score retrieved from database */
- if (TstVis_IsVisibleEachQstScore (Visibility))
+ if (IsVisible[TstVis_VISIBLE_EACH_QST_SCORE])
{
HTM_DIV_Begin ("class=\"DAT_SMALL LM\"");
HTM_TxtColonNBSP (Txt_Score);
@@ -341,7 +370,7 @@ static void TstPrn_WriteQstAndAnsExam (struct UsrData *UsrDat,
/* Question feedback */
if (QuestionUneditedAfterExam)
- if (TstVis_IsVisibleFeedbackTxt (Visibility))
+ if (IsVisible[TstVis_VISIBLE_FEEDBACK_TXT])
Tst_WriteQstFeedback (Question->Feedback,"TEST_EXA_LIGHT");
HTM_TD_End ();
@@ -382,7 +411,7 @@ void TstPrn_ComputeScoresAndStoreQuestionsOfPrint (struct TstPrn_Print *Print,
/* Accumulate total score */
Print->Score += Print->PrintedQuestions[NumQst].Score;
- if (Print->PrintedQuestions[NumQst].AnswerIsNotBlank)
+ if (Print->PrintedQuestions[NumQst].StrAnswers[0]) // User's answer is not blank
Print->NumQstsNotBlank++;
/* Update the number of hits and the score of this question in tests database */
@@ -632,22 +661,20 @@ void TstPrn_ComputeIntAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
{
long AnswerUsr;
- PrintedQuestion->Score = 0.0; // Default score for blank or wrong answer
- PrintedQuestion->AnswerIsNotBlank = (PrintedQuestion->StrAnswers[0] != '\0');
- if (PrintedQuestion->AnswerIsNotBlank) // If user has answered the answer
+ PrintedQuestion->Score = 0.0; // Default score for blank or wrong answer
+ if (PrintedQuestion->StrAnswers[0]) // If user has answered the answer
if (sscanf (PrintedQuestion->StrAnswers,"%ld",&AnswerUsr) == 1)
if (AnswerUsr == Question->Answer.Integer) // Correct answer
PrintedQuestion->Score = 1.0;
}
void TstPrn_ComputeFltAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
- const struct Tst_Question *Question)
+ const struct Tst_Question *Question)
{
double AnswerUsr;
- PrintedQuestion->Score = 0.0; // Default score for blank or wrong answer
- PrintedQuestion->AnswerIsNotBlank = (PrintedQuestion->StrAnswers[0] != '\0');
- if (PrintedQuestion->AnswerIsNotBlank) // If user has answered the answer
+ PrintedQuestion->Score = 0.0; // Default score for blank or wrong answer
+ if (PrintedQuestion->StrAnswers[0]) // If user has answered the answer
{
AnswerUsr = Str_GetDoubleFromStr (PrintedQuestion->StrAnswers);
@@ -661,8 +688,7 @@ void TstPrn_ComputeFltAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
void TstPrn_ComputeTF_AnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
const struct Tst_Question *Question)
{
- PrintedQuestion->AnswerIsNotBlank = (PrintedQuestion->StrAnswers[0] != '\0');
- if (PrintedQuestion->AnswerIsNotBlank) // User has selected T or F
+ if (PrintedQuestion->StrAnswers[0]) // If user has selected T or F
PrintedQuestion->Score = (PrintedQuestion->StrAnswers[0] == Question->Answer.TF) ? 1.0 : // Correct
-1.0; // Wrong
else
@@ -705,8 +731,7 @@ void TstPrn_ComputeChoAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
}
/* The answer is blank? */
- PrintedQuestion->AnswerIsNotBlank = NumAnsGood != 0 || NumAnsBad != 0;
- if (PrintedQuestion->AnswerIsNotBlank)
+ if (NumAnsGood || NumAnsBad) // If user has answered the answer
{
/* Compute the score */
if (Question->Answer.Type == Tst_ANS_UNIQUE_CHOICE)
@@ -748,8 +773,7 @@ void TstPrn_ComputeTxtAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
char TextAnsOK[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
PrintedQuestion->Score = 0.0; // Default score for blank or wrong answer
- PrintedQuestion->AnswerIsNotBlank = (PrintedQuestion->StrAnswers[0] != '\0');
- if (PrintedQuestion->AnswerIsNotBlank) // If user has answered the answer
+ if (PrintedQuestion->StrAnswers[0]) // If user has answered the answer
{
/* Filter the user answer */
Str_Copy (TextAnsUsr,PrintedQuestion->StrAnswers,
@@ -892,12 +916,12 @@ void TstPrn_ShowGrade (double Grade,double MaxGrade)
void TstPrn_WriteAnswersExam (struct UsrData *UsrDat,
const struct TstPrn_PrintedQuestion *PrintedQuestion,
const struct Tst_Question *Question,
- unsigned Visibility)
+ bool IsVisible[TstVis_NUM_ITEMS_VISIBILITY])
{
void (*TstPrn_WriteAnsExam[Tst_NUM_ANS_TYPES]) (struct UsrData *UsrDat,
const struct TstPrn_PrintedQuestion *PrintedQuestion,
const struct Tst_Question *Question,
- unsigned Visibility) =
+ bool IsVisible[TstVis_NUM_ITEMS_VISIBILITY]) =
{
[Tst_ANS_INT ] = TstPrn_WriteIntAnsPrint,
[Tst_ANS_FLOAT ] = TstPrn_WriteFltAnsPrint,
@@ -908,7 +932,8 @@ void TstPrn_WriteAnswersExam (struct UsrData *UsrDat,
};
/***** Get correct answer and compute answer score depending on type *****/
- TstPrn_WriteAnsExam[Question->Answer.Type] (UsrDat,PrintedQuestion,Question,Visibility);
+ TstPrn_WriteAnsExam[Question->Answer.Type] (UsrDat,PrintedQuestion,Question,
+ IsVisible);
}
/*****************************************************************************/
@@ -918,7 +943,7 @@ void TstPrn_WriteAnswersExam (struct UsrData *UsrDat,
static void TstPrn_WriteIntAnsPrint (struct UsrData *UsrDat,
const struct TstPrn_PrintedQuestion *PrintedQuestion,
const struct Tst_Question *Question,
- unsigned Visibility)
+ bool IsVisible[TstVis_NUM_ITEMS_VISIBILITY])
{
long IntAnswerUsr;
@@ -939,7 +964,7 @@ static void TstPrn_WriteIntAnsPrint (struct UsrData *UsrDat,
if (sscanf (PrintedQuestion->StrAnswers,"%ld",&IntAnswerUsr) == 1)
{
HTM_TD_Begin ("class=\"%s CM\"",
- TstVis_IsVisibleCorrectAns (Visibility) ?
+ IsVisible[TstVis_VISIBLE_CORRECT_ANSWER] ?
(IntAnswerUsr == Question->Answer.Integer ? "ANS_OK" :
"ANS_BAD") :
"ANS_0");
@@ -958,8 +983,8 @@ static void TstPrn_WriteIntAnsPrint (struct UsrData *UsrDat,
/***** Write the correct answer *****/
HTM_TD_Begin ("class=\"ANS_0 CM\"");
- if (TstVis_IsVisibleQstAndAnsTxt (Visibility) &&
- TstVis_IsVisibleCorrectAns (Visibility))
+ if (IsVisible[TstVis_VISIBLE_QST_ANS_TXT] &&
+ IsVisible[TstVis_VISIBLE_CORRECT_ANSWER])
HTM_Long (Question->Answer.Integer);
else
Ico_PutIconNotVisible ();
@@ -977,7 +1002,7 @@ static void TstPrn_WriteIntAnsPrint (struct UsrData *UsrDat,
static void TstPrn_WriteFltAnsPrint (struct UsrData *UsrDat,
const struct TstPrn_PrintedQuestion *PrintedQuestion,
const struct Tst_Question *Question,
- unsigned Visibility)
+ bool IsVisible[TstVis_NUM_ITEMS_VISIBILITY])
{
double FloatAnsUsr = 0.0;
@@ -999,7 +1024,7 @@ static void TstPrn_WriteFltAnsPrint (struct UsrData *UsrDat,
FloatAnsUsr = Str_GetDoubleFromStr (PrintedQuestion->StrAnswers);
// A bad formatted floating point answer will interpreted as 0.0
HTM_TD_Begin ("class=\"%s CM\"",
- TstVis_IsVisibleCorrectAns (Visibility) ?
+ IsVisible[TstVis_VISIBLE_CORRECT_ANSWER] ?
((FloatAnsUsr >= Question->Answer.FloatingPoint[0] &&
FloatAnsUsr <= Question->Answer.FloatingPoint[1]) ? "ANS_OK" :
"ANS_BAD") :
@@ -1012,8 +1037,8 @@ static void TstPrn_WriteFltAnsPrint (struct UsrData *UsrDat,
/***** Write the correct answer *****/
HTM_TD_Begin ("class=\"ANS_0 CM\"");
- if (TstVis_IsVisibleQstAndAnsTxt (Visibility) &&
- TstVis_IsVisibleCorrectAns (Visibility))
+ if (IsVisible[TstVis_VISIBLE_QST_ANS_TXT] &&
+ IsVisible[TstVis_VISIBLE_CORRECT_ANSWER])
{
HTM_Txt ("[");
HTM_Double (Question->Answer.FloatingPoint[0]);
@@ -1037,7 +1062,7 @@ static void TstPrn_WriteFltAnsPrint (struct UsrData *UsrDat,
static void TstPrn_WriteTF_AnsPrint (struct UsrData *UsrDat,
const struct TstPrn_PrintedQuestion *PrintedQuestion,
const struct Tst_Question *Question,
- unsigned Visibility)
+ bool IsVisible[TstVis_NUM_ITEMS_VISIBILITY])
{
char AnsTFUsr;
@@ -1057,7 +1082,7 @@ static void TstPrn_WriteTF_AnsPrint (struct UsrData *UsrDat,
/***** Write the user answer *****/
HTM_TD_Begin ("class=\"%s CM\"",
- TstVis_IsVisibleCorrectAns (Visibility) ?
+ IsVisible[TstVis_VISIBLE_CORRECT_ANSWER] ?
(AnsTFUsr == Question->Answer.TF ? "ANS_OK" :
"ANS_BAD") :
"ANS_0");
@@ -1066,8 +1091,8 @@ static void TstPrn_WriteTF_AnsPrint (struct UsrData *UsrDat,
/***** Write the correct answer *****/
HTM_TD_Begin ("class=\"ANS_0 CM\"");
- if (TstVis_IsVisibleQstAndAnsTxt (Visibility) &&
- TstVis_IsVisibleCorrectAns (Visibility))
+ if (IsVisible[TstVis_VISIBLE_QST_ANS_TXT] &&
+ IsVisible[TstVis_VISIBLE_CORRECT_ANSWER])
Tst_WriteAnsTF (Question->Answer.TF);
else
Ico_PutIconNotVisible ();
@@ -1085,7 +1110,7 @@ static void TstPrn_WriteTF_AnsPrint (struct UsrData *UsrDat,
static void TstPrn_WriteChoAnsPrint (struct UsrData *UsrDat,
const struct TstPrn_PrintedQuestion *PrintedQuestion,
const struct Tst_Question *Question,
- unsigned Visibility)
+ bool IsVisible[TstVis_NUM_ITEMS_VISIBILITY])
{
extern const char *Txt_TST_Answer_given_by_the_user;
extern const char *Txt_TST_Answer_given_by_the_teachers;
@@ -1098,6 +1123,26 @@ static void TstPrn_WriteChoAnsPrint (struct UsrData *UsrDat,
char *Str;
} Ans;
+ /***** Change format of answers text and feedback *****/
+ for (NumOpt = 0;
+ NumOpt < Question->Answer.NumOptions;
+ NumOpt++)
+ {
+ /* Convert answer text, that is in HTML, to rigorous HTML */
+ if (Question->Answer.Options[NumOpt].Text[0])
+ Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
+ Question->Answer.Options[NumOpt].Text,
+ Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false);
+
+ /* Convert answer feedback, that is in HTML, to rigorous HTML */
+ if (IsVisible[TstVis_VISIBLE_FEEDBACK_TXT])
+ if (Question->Answer.Options[NumOpt].Feedback)
+ if (Question->Answer.Options[NumOpt].Feedback[0])
+ Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
+ Question->Answer.Options[NumOpt].Feedback,
+ Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false);
+ }
+
/***** Get indexes for this question from string *****/
TstPrn_GetIndexesFromStr (PrintedQuestion->StrIndexes,Indexes);
@@ -1121,7 +1166,7 @@ static void TstPrn_WriteChoAnsPrint (struct UsrData *UsrDat,
/* Draw icon depending on user's answer */
if (UsrAnswers[Indexes[NumOpt]] == true) // This answer has been selected by the user
{
- if (TstVis_IsVisibleCorrectAns (Visibility))
+ if (IsVisible[TstVis_VISIBLE_CORRECT_ANSWER])
{
if (Question->Answer.Options[Indexes[NumOpt]].Correct)
{
@@ -1149,7 +1194,7 @@ static void TstPrn_WriteChoAnsPrint (struct UsrData *UsrDat,
HTM_TD_Empty (1);
/* Draw icon that indicates whether the answer is correct */
- if (TstVis_IsVisibleCorrectAns (Visibility))
+ if (IsVisible[TstVis_VISIBLE_CORRECT_ANSWER])
{
if (Question->Answer.Options[Indexes[NumOpt]].Correct)
{
@@ -1177,7 +1222,7 @@ static void TstPrn_WriteChoAnsPrint (struct UsrData *UsrDat,
HTM_TD_Begin ("class=\"LT\"");
HTM_DIV_Begin ("class=\"ANS_TXT\"");
- if (TstVis_IsVisibleQstAndAnsTxt (Visibility))
+ if (IsVisible[TstVis_VISIBLE_QST_ANS_TXT])
{
HTM_Txt (Question->Answer.Options[Indexes[NumOpt]].Text);
Med_ShowMedia (&Question->Answer.Options[Indexes[NumOpt]].Media,
@@ -1188,7 +1233,7 @@ static void TstPrn_WriteChoAnsPrint (struct UsrData *UsrDat,
Ico_PutIconNotVisible ();
HTM_DIV_End ();
- if (TstVis_IsVisibleCorrectAns (Visibility))
+ if (IsVisible[TstVis_VISIBLE_CORRECT_ANSWER])
if (Question->Answer.Options[Indexes[NumOpt]].Feedback)
if (Question->Answer.Options[Indexes[NumOpt]].Feedback[0])
{
@@ -1213,30 +1258,31 @@ static void TstPrn_WriteChoAnsPrint (struct UsrData *UsrDat,
static void TstPrn_WriteTxtAnsPrint (struct UsrData *UsrDat,
const struct TstPrn_PrintedQuestion *PrintedQuestion,
const struct Tst_Question *Question,
- unsigned Visibility)
+ bool IsVisible[TstVis_NUM_ITEMS_VISIBILITY])
{
unsigned NumOpt;
char TextAnsUsr[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
char TextAnsOK[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
bool Correct = false;
- /***** Get text and correctness of answers for this question from database (one row per answer) *****/
+ /***** Change format of answers text and feedback *****/
for (NumOpt = 0;
NumOpt < Question->Answer.NumOptions;
NumOpt++)
{
- /***** Convert answer text, that is in HTML, to rigorous HTML ******/
+ /* Convert answer text, that is in HTML, to rigorous HTML */
if (Question->Answer.Options[NumOpt].Text[0])
Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
Question->Answer.Options[NumOpt].Text,
Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false);
- /***** Convert answer feedback, that is in HTML, to rigorous HTML ******/
- if (TstVis_IsVisibleFeedbackTxt (Visibility))
- if (Question->Answer.Options[NumOpt].Feedback[0])
- Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
- Question->Answer.Options[NumOpt].Feedback,
- Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false);
+ /* Convert answer feedback, that is in HTML, to rigorous HTML */
+ if (IsVisible[TstVis_VISIBLE_FEEDBACK_TXT])
+ if (Question->Answer.Options[NumOpt].Feedback)
+ if (Question->Answer.Options[NumOpt].Feedback[0])
+ Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
+ Question->Answer.Options[NumOpt].Feedback,
+ Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false);
}
/***** Header with the title of each column *****/
@@ -1277,10 +1323,9 @@ static void TstPrn_WriteTxtAnsPrint (struct UsrData *UsrDat,
}
}
HTM_TD_Begin ("class=\"%s CT\"",
- TstVis_IsVisibleCorrectAns (Visibility) ?
- (Correct ? "ANS_OK" :
- "ANS_BAD") :
- "ANS_0");
+ IsVisible[TstVis_VISIBLE_CORRECT_ANSWER] ? (Correct ? "ANS_OK" :
+ "ANS_BAD") :
+ "ANS_0");
HTM_Txt (PrintedQuestion->StrAnswers);
}
else // If user has omitted the answer
@@ -1288,8 +1333,8 @@ static void TstPrn_WriteTxtAnsPrint (struct UsrData *UsrDat,
HTM_TD_End ();
/***** Write the correct answers *****/
- if (TstVis_IsVisibleQstAndAnsTxt (Visibility) &&
- TstVis_IsVisibleCorrectAns (Visibility))
+ if (IsVisible[TstVis_VISIBLE_QST_ANS_TXT] &&
+ IsVisible[TstVis_VISIBLE_CORRECT_ANSWER])
{
HTM_TD_Begin ("class=\"CT\"");
HTM_TABLE_BeginPadding (2);
@@ -1312,7 +1357,7 @@ static void TstPrn_WriteTxtAnsPrint (struct UsrData *UsrDat,
HTM_Txt (Question->Answer.Options[NumOpt].Text);
HTM_DIV_End ();
- if (TstVis_IsVisibleFeedbackTxt (Visibility))
+ if (IsVisible[TstVis_VISIBLE_FEEDBACK_TXT])
if (Question->Answer.Options[NumOpt].Feedback)
if (Question->Answer.Options[NumOpt].Feedback[0])
{
@@ -1735,7 +1780,7 @@ static void TstPrn_ShowUsrPrints (struct UsrData *UsrDat)
if (ICanView.Exam)
{
Frm_StartForm (Gbl.Action.Act == ActSeeMyTstResCrs ? ActSeeOneTstResMe :
- ActSeeOneTstResOth);
+ ActSeeOneTstResOth);
TstPrn_PutParamPrnCod (Print.PrnCod);
Ico_PutIconLink ("tasks.svg",Txt_View_test);
Frm_EndForm ();
@@ -2079,7 +2124,7 @@ void TstPrn_ShowOnePrint (void)
/***** Write answers and solutions *****/
TstPrn_ShowPrintAnswers (&Gbl.Usrs.Other.UsrDat,&Print,
- TstCfg_GetConfigVisibility ());
+ TstCfg_GetConfigVisibility ());
/***** End table *****/
HTM_TABLE_End ();
@@ -2138,8 +2183,8 @@ static void TstPrn_ShowTagsPresentInAPrint (long ResCod)
/*****************************************************************************/
void TstPrn_ShowPrintAnswers (struct UsrData *UsrDat,
- struct TstPrn_Print *Print,
- unsigned Visibility)
+ struct TstPrn_Print *Print,
+ unsigned Visibility)
{
unsigned NumQst;
struct Tst_Question Question;
diff --git a/swad_test_print.h b/swad_test_print.h
index 4fbb421f..c51a5953 100644
--- a/swad_test_print.h
+++ b/swad_test_print.h
@@ -29,6 +29,7 @@
#include "swad_test_config.h"
#include "swad_test_type.h"
+#include "swad_test_visibility.h"
#include "swad_user.h"
/*****************************************************************************/
@@ -46,7 +47,6 @@ struct TstPrn_PrintedQuestion
char StrIndexes[Tst_MAX_BYTES_INDEXES_ONE_QST + 1]; // 0 1 2 3, 3 0 2 1, etc.
char StrAnswers[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1]; // Answers selected by user
double Score; // Question score
- bool AnswerIsNotBlank; // Answer not blank?
};
struct TstPrn_Print
@@ -103,7 +103,7 @@ void TstPrn_ShowGrade (double Grade,double MaxGrade);
void TstPrn_WriteAnswersExam (struct UsrData *UsrDat,
const struct TstPrn_PrintedQuestion *PrintedQuestion,
const struct Tst_Question *Question,
- unsigned Visibility);
+ bool IsVisible[TstVis_NUM_ITEMS_VISIBILITY]);
void TstPrn_SelUsrsToViewUsrsPrints (void);
void TstPrn_SelDatesToSeeMyPrints (void);
diff --git a/swad_text_action.c b/swad_text_action.c
index 9b999eab..bf54c1e6 100644
--- a/swad_text_action.c
+++ b/swad_text_action.c
@@ -10904,9 +10904,9 @@ const char *Txt_Actions[Act_NUM_ACTIONS] =
#elif L==2 // de
"" // Need Übersetzung
#elif L==3 // en
- "See exam print"
+ "Start / resume exam"
#elif L==4 // es
- "Ver impresión de examen"
+ "Comenzar / reanudar examen"
#elif L==5 // fr
"" // Besoin de traduction
#elif L==6 // gn
@@ -10938,6 +10938,27 @@ const char *Txt_Actions[Act_NUM_ACTIONS] =
"" // Potrzebujesz tlumaczenie
#elif L==9 // pt
"" // Precisa de tradução
+#endif
+ ,
+ [ActEndExaPrn] =
+#if L==1 // ca
+ "" // Necessita traducció
+#elif L==2 // de
+ "" // Need Übersetzung
+#elif L==3 // en
+ "End exam"
+#elif L==4 // es
+ "Finalizar examen"
+#elif L==5 // fr
+ "" // Besoin de traduction
+#elif L==6 // gn
+ "" // Okoteve traducción
+#elif L==7 // it
+ "" // Bisogno di traduzione
+#elif L==8 // pl
+ "" // Potrzebujesz tlumaczenie
+#elif L==9 // pt
+ "" // Precisa de tradução
#endif
,
[ActSeeMyExaResCrs] =