Version19.241

This commit is contained in:
acanas 2020-05-22 20:10:45 +02:00
parent 86efca6412
commit c100ca9af4
20 changed files with 683 additions and 355 deletions

View File

@ -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 \

View File

@ -13363,4 +13363,15 @@ SELECT institutions.InsCod,institutions.ShortName,institutions.FullName,centres.
-------------------------------
-------------------------------
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;

View File

@ -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 (

View File

@ -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
};
/*****************************************************************************/

View File

@ -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 **********************************/

View File

@ -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)

View File

@ -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;

99
swad_exam_log.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************/
/********************************* Headers ***********************************/
/*****************************************************************************/
// #define _GNU_SOURCE // For asprintf
// #include <linux/limits.h> // For PATH_MAX
// #include <stddef.h> // For NULL
// #include <stdio.h> // For asprintf
// #include <stdlib.h> // For calloc
// #include <string.h> // For string functions
#include "swad_action.h"
// #include "swad_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);
}
}

42
swad_exam_log.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************/
/********************************** Headers **********************************/
/*****************************************************************************/
/*****************************************************************************/
/************************* Public types and constants ************************/
/*****************************************************************************/
/*****************************************************************************/
/***************************** Public prototypes *****************************/
/*****************************************************************************/
void ExaLog_LogAccess (long LogCod);
#endif

View File

@ -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]) *****/

View File

@ -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);

View File

@ -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 ();

View File

@ -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 ();

View File

@ -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)
{

View File

@ -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;

View File

@ -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,"&nbsp;");
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,"&nbsp;");
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"

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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&oacute;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] =