swad-core/swad_exam_print.c

1628 lines
60 KiB
C
Raw Normal View History

2020-05-17 02:28:30 +02:00
// swad_exam_print.c: exam prints (each copy of an exam in a session for a student)
2020-05-07 12:57:12 +02:00
/*
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.
2021-02-09 12:43:45 +01:00
Copyright (C) 1999-2021 Antonio Ca<EFBFBD>as Vargas
2020-05-07 12:57:12 +02:00
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************/
/********************************* Headers ***********************************/
/*****************************************************************************/
#define _GNU_SOURCE // For asprintf
#include <linux/limits.h> // For PATH_MAX
#include <stddef.h> // For NULL
#include <stdio.h> // For asprintf
#include <string.h> // For string functions
#include "swad_box.h"
#include "swad_database.h"
#include "swad_error.h"
2020-05-07 12:57:12 +02:00
#include "swad_exam.h"
2020-05-23 13:24:08 +02:00
#include "swad_exam_log.h"
2020-05-13 12:53:27 +02:00
#include "swad_exam_print.h"
2020-05-07 12:57:12 +02:00
#include "swad_exam_result.h"
2020-05-17 02:28:30 +02:00
#include "swad_exam_session.h"
2020-05-07 12:57:12 +02:00
#include "swad_exam_set.h"
#include "swad_exam_type.h"
2020-05-09 21:07:50 +02:00
#include "swad_form.h"
2020-05-07 12:57:12 +02:00
#include "swad_global.h"
2021-01-20 00:42:59 +01:00
#include "swad_ID.h"
#include "swad_photo.h"
2020-05-07 12:57:12 +02:00
/*****************************************************************************/
/************** External global variables from others modules ****************/
/*****************************************************************************/
extern struct Globals Gbl;
/*****************************************************************************/
/***************************** Private constants *****************************/
/*****************************************************************************/
/*****************************************************************************/
/******************************* Private types *******************************/
/*****************************************************************************/
/*****************************************************************************/
/***************************** Private variables *****************************/
/*****************************************************************************/
/*****************************************************************************/
/***************************** Private prototypes ****************************/
/*****************************************************************************/
2020-06-24 20:10:57 +02:00
static void ExaPrn_GetDataOfPrint (struct ExaPrn_Print *Print,
MYSQL_RES **mysql_res,
unsigned NumPrints);
2020-05-07 12:57:12 +02:00
2020-05-15 01:07:46 +02:00
static void ExaPrn_GetQuestionsForNewPrintFromDB (struct ExaPrn_Print *Print,long ExaCod);
2020-05-09 01:37:00 +02:00
static unsigned ExaPrn_GetSomeQstsFromSetToPrint (struct ExaPrn_Print *Print,
struct ExaSet_Set *Set,
unsigned *NumQstInPrint);
2020-05-13 00:28:32 +02:00
static void ExaPrn_GenerateChoiceIndexes (struct TstPrn_PrintedQuestion *PrintedQuestion,
bool Shuffle);
2020-05-10 14:03:40 +02:00
static void ExaPrn_CreatePrintInDB (struct ExaPrn_Print *Print);
2020-05-10 01:42:30 +02:00
2020-05-17 20:12:37 +02:00
static void ExaPrn_ShowExamPrintToFillIt (struct Exa_Exams *Exams,
const struct Exa_Exam *Exam,
2021-01-20 00:42:59 +01:00
struct ExaPrn_Print *Print);
2020-05-19 13:12:26 +02:00
static void ExaPrn_GetAndWriteDescription (long ExaCod);
2020-05-25 00:18:09 +02:00
static void ExaPrn_ShowTableWithQstsToFill (struct Exa_Exams *Exams,
const struct ExaPrn_Print *Print);
2020-05-17 20:12:37 +02:00
static void ExaPrn_WriteQstAndAnsToFill (const struct ExaPrn_Print *Print,
unsigned QstInd,
2020-06-17 02:31:42 +02:00
struct Tst_Question *Question);
2020-05-17 20:12:37 +02:00
static void ExaPrn_WriteAnswersToFill (const struct ExaPrn_Print *Print,
unsigned QstInd,
2020-06-17 02:31:42 +02:00
struct Tst_Question *Question);
2020-05-23 13:24:08 +02:00
2020-05-19 15:17:52 +02:00
//-----------------------------------------------------------------------------
2020-05-11 13:05:38 +02:00
static void ExaPrn_WriteIntAnsToFill (const struct ExaPrn_Print *Print,
unsigned QstInd,
2020-06-17 02:31:42 +02:00
__attribute__((unused)) struct Tst_Question *Question);
static void ExaPrn_WriteFltAnsToFill (const struct ExaPrn_Print *Print,
unsigned QstInd,
2020-06-17 02:31:42 +02:00
__attribute__((unused)) struct Tst_Question *Question);
2020-05-19 15:17:52 +02:00
static void ExaPrn_WriteTF_AnsToFill (const struct ExaPrn_Print *Print,
unsigned QstInd,
2020-06-17 02:31:42 +02:00
__attribute__((unused)) struct Tst_Question *Question);
2020-05-19 15:17:52 +02:00
static void ExaPrn_WriteChoAnsToFill (const struct ExaPrn_Print *Print,
unsigned QstInd,
2020-06-17 02:31:42 +02:00
struct Tst_Question *Question);
2020-05-19 15:17:52 +02:00
static void ExaPrn_WriteTxtAnsToFill (const struct ExaPrn_Print *Print,
unsigned QstInd,
2020-06-17 02:31:42 +02:00
__attribute__((unused)) struct Tst_Question *Question);
2020-05-19 15:17:52 +02:00
//-----------------------------------------------------------------------------
2020-05-23 13:24:08 +02:00
2020-05-19 15:17:52 +02:00
static void ExaPrn_WriteJSToUpdateExamPrint (const struct ExaPrn_Print *Print,
unsigned QstInd,
2020-05-19 15:17:52 +02:00
const char *Id,int NumOpt);
2020-05-09 21:07:50 +02:00
2020-05-23 13:24:08 +02:00
static void ExaPrn_GetAnswerFromForm (struct ExaPrn_Print *Print,unsigned QstInd);
2020-05-11 02:28:38 +02:00
static unsigned ExaPrn_GetParamQstInd (void);
static void ExaPrn_ComputeScoreAndStoreQuestionOfPrint (struct ExaPrn_Print *Print,
unsigned QstInd);
2020-05-13 12:53:27 +02:00
//-----------------------------------------------------------------------------
static void ExaPrn_GetCorrectAndComputeIntAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *Question);
static void ExaPrn_GetCorrectAndComputeFltAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *Question);
static void ExaPrn_GetCorrectAndComputeTF_AnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *Question);
static void ExaPrn_GetCorrectAndComputeChoAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *Question);
static void ExaPrn_GetCorrectAndComputeTxtAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *Question);
2020-05-23 13:24:08 +02:00
//-----------------------------------------------------------------------------
2020-05-13 12:53:27 +02:00
static void ExaPrn_GetCorrectIntAnswerFromDB (struct Tst_Question *Question);
static void ExaPrn_GetCorrectFltAnswerFromDB (struct Tst_Question *Question);
static void ExaPrn_GetCorrectTF_AnswerFromDB (struct Tst_Question *Question);
static void ExaPrn_GetCorrectChoAnswerFromDB (struct Tst_Question *Question);
static void ExaPrn_GetCorrectTxtAnswerFromDB (struct Tst_Question *Question);
//-----------------------------------------------------------------------------
2020-05-12 13:24:43 +02:00
static void ExaPrn_GetAnswerFromDB (struct ExaPrn_Print *Print,long QstCod,
char StrAnswers[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1]);
2020-05-11 02:28:38 +02:00
static void ExaPrn_StoreOneQstOfPrintInDB (const struct ExaPrn_Print *Print,
unsigned QstInd);
2020-05-11 02:28:38 +02:00
2020-05-14 01:41:20 +02:00
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);
2020-05-07 18:33:26 +02:00
/*****************************************************************************/
/**************************** Reset exam print *******************************/
/*****************************************************************************/
2020-05-16 02:04:36 +02:00
void ExaPrn_ResetPrint (struct ExaPrn_Print *Print)
2020-05-07 18:33:26 +02:00
{
2020-06-24 20:10:57 +02:00
Print->PrnCod = -1L;
2020-05-17 02:28:30 +02:00
Print->SesCod = -1L;
2020-05-10 01:42:30 +02:00
Print->UsrCod = -1L;
2020-05-07 18:33:26 +02:00
Print->TimeUTC[Dat_START_TIME] =
Print->TimeUTC[Dat_END_TIME ] = (time_t) 0;
Print->Sent = false; // After creating an exam print, it's not sent
2020-06-22 19:27:23 +02:00
Print->NumQsts.All =
Print->NumQsts.NotBlank =
Print->NumQsts.Valid.Correct =
Print->NumQsts.Valid.Wrong.Negative =
Print->NumQsts.Valid.Wrong.Zero =
Print->NumQsts.Valid.Wrong.Positive =
Print->NumQsts.Valid.Blank =
Print->NumQsts.Valid.Total = 0;
Print->Score.All =
Print->Score.Valid = 0.0;
2020-05-07 18:33:26 +02:00
}
2020-05-07 12:57:12 +02:00
/*****************************************************************************/
2020-05-17 02:28:30 +02:00
/********************** Show print of an exam in a session *******************/
2020-05-07 12:57:12 +02:00
/*****************************************************************************/
2020-05-09 21:07:50 +02:00
void ExaPrn_ShowExamPrint (void)
2020-05-07 12:57:12 +02:00
{
2020-05-17 20:12:37 +02:00
extern const char *Txt_You_dont_have_access_to_the_exam;
2020-05-07 12:57:12 +02:00
struct Exa_Exams Exams;
struct Exa_Exam Exam;
2020-05-17 02:28:30 +02:00
struct ExaSes_Session Session;
2020-05-07 12:57:12 +02:00
struct ExaPrn_Print Print;
/***** Reset exams context *****/
Exa_ResetExams (&Exams);
Exa_ResetExam (&Exam);
2020-05-17 02:28:30 +02:00
ExaSes_ResetSession (&Session);
2020-05-07 12:57:12 +02:00
/***** Get and check parameters *****/
2020-05-17 02:28:30 +02:00
ExaSes_GetAndCheckParameters (&Exams,&Exam,&Session);
2020-05-07 12:57:12 +02:00
2020-05-17 02:28:30 +02:00
/***** Check if I can access to this session *****/
2020-05-21 16:59:14 +02:00
if (ExaSes_CheckIfICanAnswerThisSession (&Exam,&Session))
2020-05-09 01:37:00 +02:00
{
2020-09-27 17:38:51 +02:00
/***** Set basic data of exam print *****/
2020-05-17 02:28:30 +02:00
Print.SesCod = Session.SesCod;
2020-05-15 01:07:46 +02:00
Print.UsrCod = Gbl.Usrs.Me.UsrDat.UsrCod;
2020-09-27 17:45:23 +02:00
/***** Get exam print data from database *****/
ExaPrn_GetDataOfPrintBySesCodAndUsrCod (&Print);
2021-01-20 00:42:59 +01:00
if (Print.PrnCod <= 0) // Exam print does not exists ==> create it
2020-05-15 01:07:46 +02:00
{
2020-09-27 17:45:23 +02:00
/***** Set again basic data of exam print *****/
Print.SesCod = Session.SesCod;
Print.UsrCod = Gbl.Usrs.Me.UsrDat.UsrCod;
2020-05-15 01:07:46 +02:00
/***** Get questions from database *****/
ExaPrn_GetQuestionsForNewPrintFromDB (&Print,Exam.ExaCod);
2020-06-18 20:06:17 +02:00
if (Print.NumQsts.All)
2020-05-23 15:53:53 +02:00
{
2020-05-23 19:08:59 +02:00
/***** Create new exam print in database *****/
2020-05-15 01:07:46 +02:00
ExaPrn_CreatePrintInDB (&Print);
2020-05-23 15:53:53 +02:00
/***** Set log print code and action *****/
ExaLog_SetPrnCod (Print.PrnCod);
ExaLog_SetAction (ExaLog_START_EXAM);
2020-05-23 19:08:59 +02:00
ExaLog_SetIfCanAnswer (true);
2020-05-23 15:53:53 +02:00
}
2020-09-27 17:38:51 +02:00
}
2021-01-20 00:42:59 +01:00
else // Exam print exists
2020-09-27 17:38:51 +02:00
{
/***** Get exam print data from database *****/
ExaPrn_GetDataOfPrintBySesCodAndUsrCod (&Print);
2020-05-23 15:53:53 +02:00
/***** Get questions and current user's answers from database *****/
ExaPrn_GetPrintQuestionsFromDB (&Print);
2020-05-15 01:07:46 +02:00
2020-05-23 15:53:53 +02:00
/***** Set log print code and action *****/
ExaLog_SetPrnCod (Print.PrnCod);
ExaLog_SetAction (ExaLog_RESUME_EXAM);
2020-05-23 19:08:59 +02:00
ExaLog_SetIfCanAnswer (true);
2020-05-23 15:53:53 +02:00
}
2020-05-22 20:10:45 +02:00
2020-05-15 01:07:46 +02:00
/***** Show test exam to be answered *****/
2020-05-17 20:12:37 +02:00
ExaPrn_ShowExamPrintToFillIt (&Exams,&Exam,&Print);
2020-05-09 01:37:00 +02:00
}
2020-05-23 15:53:53 +02:00
else // Session not open or accessible
/***** Show warning *****/
2020-05-17 20:12:37 +02:00
Ale_ShowAlert (Ale_INFO,Txt_You_dont_have_access_to_the_exam);
2020-05-07 12:57:12 +02:00
}
2020-06-24 20:10:57 +02:00
/*****************************************************************************/
/**************** Get data of an exam print using print code *****************/
/*****************************************************************************/
void ExaPrn_GetDataOfPrintByPrnCod (struct ExaPrn_Print *Print)
{
MYSQL_RES *mysql_res;
unsigned NumPrints;
2020-06-24 20:10:57 +02:00
/***** Make database query *****/
NumPrints = (unsigned)
DB_QuerySELECT (&mysql_res,"can not get data of an exam print",
"SELECT PrnCod," // row[0]
"SesCod," // row[1]
"UsrCod," // row[2]
"UNIX_TIMESTAMP(StartTime)," // row[3]
"UNIX_TIMESTAMP(EndTime)," // row[4]
"NumQsts," // row[5]
"NumQstsNotBlank," // row[6]
"Sent," // row[7]
"Score" // row[8]
" FROM exa_prints"
" WHERE PrnCod=%ld",
Print->PrnCod);
2020-06-24 20:10:57 +02:00
/***** Get data of print *****/
ExaPrn_GetDataOfPrint (Print,&mysql_res,NumPrints);
2020-06-24 20:10:57 +02:00
}
2020-05-09 21:07:50 +02:00
/*****************************************************************************/
2020-05-17 02:28:30 +02:00
/******** Get data of an exam print using session code and user code *********/
2020-05-09 21:07:50 +02:00
/*****************************************************************************/
2020-06-24 20:10:57 +02:00
void ExaPrn_GetDataOfPrintBySesCodAndUsrCod (struct ExaPrn_Print *Print)
2020-05-09 21:07:50 +02:00
{
2020-05-10 01:42:30 +02:00
MYSQL_RES *mysql_res;
unsigned NumPrints;
2020-05-10 01:42:30 +02:00
/***** Make database query *****/
NumPrints = (unsigned)
DB_QuerySELECT (&mysql_res,"can not get data of an exam print",
"SELECT PrnCod," // row[0]
"SesCod," // row[1]
"UsrCod," // row[2]
"UNIX_TIMESTAMP(StartTime)," // row[3]
"UNIX_TIMESTAMP(EndTime)," // row[4]
"NumQsts," // row[5]
"NumQstsNotBlank," // row[6]
"Sent," // row[7]
"Score" // row[8]
" FROM exa_prints"
" WHERE SesCod=%ld"
" AND UsrCod=%ld",
Print->SesCod,
Print->UsrCod);
2020-06-24 20:10:57 +02:00
/***** Get data of print *****/
ExaPrn_GetDataOfPrint (Print,&mysql_res,NumPrints);
2020-06-24 20:10:57 +02:00
}
/*****************************************************************************/
/************************* Get assignment data *******************************/
/*****************************************************************************/
static void ExaPrn_GetDataOfPrint (struct ExaPrn_Print *Print,
MYSQL_RES **mysql_res,
unsigned NumPrints)
2020-06-24 20:10:57 +02:00
{
MYSQL_ROW row;
if (NumPrints)
2020-05-10 01:42:30 +02:00
{
2020-06-24 20:10:57 +02:00
row = mysql_fetch_row (*mysql_res);
2020-05-10 01:42:30 +02:00
/* Get print code (row[0]) */
Print->PrnCod = Str_ConvertStrCodToLongCod (row[0]);
2020-06-24 20:10:57 +02:00
/* Get session code (row[1]) */
Print->SesCod = Str_ConvertStrCodToLongCod (row[1]);
2020-05-10 01:42:30 +02:00
2020-06-24 20:10:57 +02:00
/* Get user code (row[2]) */
Print->UsrCod = Str_ConvertStrCodToLongCod (row[2]);
/* Get date-time (row[3] and row[4] hold UTC date-time) */
Print->TimeUTC[Dat_START_TIME] = Dat_GetUNIXTimeFromStr (row[3]);
Print->TimeUTC[Dat_END_TIME ] = Dat_GetUNIXTimeFromStr (row[4]);
/* Get number of questions (row[5]) */
if (sscanf (row[5],"%u",&Print->NumQsts.All) != 1)
2020-06-18 20:06:17 +02:00
Print->NumQsts.All = 0;
2020-05-10 01:42:30 +02:00
2020-06-24 20:10:57 +02:00
/* Get number of questions not blank (row[6]) */
if (sscanf (row[6],"%u",&Print->NumQsts.NotBlank) != 1)
2020-06-18 20:06:17 +02:00
Print->NumQsts.NotBlank = 0;
2020-05-10 01:42:30 +02:00
2020-06-24 20:10:57 +02:00
/* Get if exam has been sent (row[7]) */
Print->Sent = (row[7][0] == 'Y');
2020-05-10 01:42:30 +02:00
2020-06-24 20:10:57 +02:00
/* Get score (row[8]) */
2020-05-10 01:42:30 +02:00
Str_SetDecimalPointToUS (); // To get the decimal point as a dot
2020-06-24 20:10:57 +02:00
if (sscanf (row[8],"%lf",&Print->Score.All) != 1)
2020-06-18 20:06:17 +02:00
Print->Score.All = 0.0;
2020-05-10 01:42:30 +02:00
Str_SetDecimalPointToLocal (); // Return to local system
}
else
2020-06-24 20:10:57 +02:00
ExaPrn_ResetPrint (Print);
2020-05-10 01:42:30 +02:00
/***** Free structure that stores the query result *****/
2020-06-24 20:10:57 +02:00
DB_FreeMySQLResult (mysql_res);
2020-05-09 21:07:50 +02:00
}
2020-05-07 12:57:12 +02:00
/*****************************************************************************/
/*********** Get questions for a new exam print from the database ************/
/*****************************************************************************/
2020-05-15 01:07:46 +02:00
static void ExaPrn_GetQuestionsForNewPrintFromDB (struct ExaPrn_Print *Print,long ExaCod)
2020-05-07 12:57:12 +02:00
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumSets;
unsigned NumSet;
struct ExaSet_Set Set;
2020-05-09 01:37:00 +02:00
unsigned NumQstsFromSet;
unsigned NumQstInPrint = 0;
2020-05-07 12:57:12 +02:00
/***** Get data of set of questions from database *****/
NumSets = (unsigned)
DB_QuerySELECT (&mysql_res,"can not get sets of questions",
"SELECT SetCod," // row[0]
"NumQstsToPrint," // row[1]
"Title" // row[2]
" FROM exa_sets"
" WHERE ExaCod=%ld"
" ORDER BY SetInd",
ExaCod);
2020-05-07 12:57:12 +02:00
2020-05-09 01:37:00 +02:00
/***** Get questions from all sets *****/
2020-06-18 20:06:17 +02:00
Print->NumQsts.All = 0;
2020-05-07 12:57:12 +02:00
if (NumSets)
2020-05-09 01:37:00 +02:00
/***** For each set in exam... *****/
2020-05-07 12:57:12 +02:00
for (NumSet = 0;
NumSet < NumSets;
NumSet++)
{
/***** Create set of questions *****/
ExaSet_ResetSet (&Set);
/***** Get set data *****/
row = mysql_fetch_row (mysql_res);
/*
row[0] SetCod
row[1] NumQstsToPrint
row[2] Title
*/
/* Get set code (row[0]) */
Set.SetCod = Str_ConvertStrCodToLongCod (row[0]);
/* Get set index (row[1]) */
Set.NumQstsToPrint = Str_ConvertStrToUnsigned (row[1]);
/* Get the title of the set (row[2]) */
Str_Copy (Set.Title,row[2],sizeof (Set.Title) - 1);
2020-05-07 12:57:12 +02:00
2020-05-07 18:33:26 +02:00
/***** Questions in this set *****/
2020-05-09 01:37:00 +02:00
NumQstsFromSet = ExaPrn_GetSomeQstsFromSetToPrint (Print,&Set,&NumQstInPrint);
2020-06-18 20:06:17 +02:00
Print->NumQsts.All += NumQstsFromSet;
2020-05-07 12:57:12 +02:00
}
2020-05-09 01:37:00 +02:00
/***** Check *****/
2020-06-18 20:06:17 +02:00
if (Print->NumQsts.All != NumQstInPrint)
Err_ShowErrorAndExit ("Wrong number of questions.");
2020-05-09 01:37:00 +02:00
2020-05-07 12:57:12 +02:00
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
2020-05-07 18:33:26 +02:00
/*****************************************************************************/
/************************ Show questions from a set **************************/
/*****************************************************************************/
2020-05-09 01:37:00 +02:00
static unsigned ExaPrn_GetSomeQstsFromSetToPrint (struct ExaPrn_Print *Print,
struct ExaSet_Set *Set,
unsigned *NumQstInPrint)
2020-05-07 18:33:26 +02:00
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
2020-05-09 01:37:00 +02:00
unsigned NumQstsInSet;
unsigned NumQstInSet;
2020-05-07 18:33:26 +02:00
Tst_AnswerType_t AnswerType;
bool Shuffle;
/***** Get questions from database *****/
2020-05-09 01:37:00 +02:00
NumQstsInSet = (unsigned)
DB_QuerySELECT (&mysql_res,"can not get questions from set",
"SELECT QstCod," // row[0]
"AnsType," // row[1]
"Shuffle" // row[2]
" FROM exa_set_questions"
" WHERE SetCod=%ld"
" ORDER BY RAND()" // Don't use RAND(NOW()) because the same ordering will be repeated across sets
" LIMIT %u",
Set->SetCod,
Set->NumQstsToPrint);
2020-05-07 18:33:26 +02:00
/***** Questions in this set *****/
2020-05-09 01:37:00 +02:00
for (NumQstInSet = 0;
NumQstInSet < NumQstsInSet;
NumQstInSet++, (*NumQstInPrint)++)
2020-05-07 18:33:26 +02:00
{
Gbl.RowEvenOdd = 1 - Gbl.RowEvenOdd;
/***** Get question data *****/
row = mysql_fetch_row (mysql_res);
/*
row[0] QstCod
row[1] AnsType
row[2] Shuffle
*/
/* Get question code (row[0]) */
2020-05-09 01:37:00 +02:00
Print->PrintedQuestions[*NumQstInPrint].QstCod = Str_ConvertStrCodToLongCod (row[0]);
2020-05-07 18:33:26 +02:00
2020-05-09 21:07:50 +02:00
/* Set set of questions */
Print->PrintedQuestions[*NumQstInPrint].SetCod = Set->SetCod;
2020-05-07 18:33:26 +02:00
/* Get answer type (row[1]) */
AnswerType = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[1]);
/* Get shuffle (row[2]) */
Shuffle = (row[2][0] == 'Y');
/* Set indexes of answers */
switch (AnswerType)
{
case Tst_ANS_INT:
case Tst_ANS_FLOAT:
case Tst_ANS_TRUE_FALSE:
case Tst_ANS_TEXT:
2020-05-09 01:37:00 +02:00
Print->PrintedQuestions[*NumQstInPrint].StrIndexes[0] = '\0';
2020-05-07 18:33:26 +02:00
break;
case Tst_ANS_UNIQUE_CHOICE:
case Tst_ANS_MULTIPLE_CHOICE:
/* If answer type is unique or multiple option,
generate indexes of answers depending on shuffle */
2020-05-13 00:28:32 +02:00
ExaPrn_GenerateChoiceIndexes (&Print->PrintedQuestions[*NumQstInPrint],Shuffle);
2020-05-07 18:33:26 +02:00
break;
default:
break;
}
/* Reset user's answers.
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. */
2020-05-09 01:37:00 +02:00
Print->PrintedQuestions[*NumQstInPrint].StrAnswers[0] = '\0';
2020-05-22 20:10:45 +02:00
/* Reset score of this question in print */
Print->PrintedQuestions[*NumQstInPrint].Score = 0.0;
2020-05-07 18:33:26 +02:00
}
2020-05-09 01:37:00 +02:00
return NumQstsInSet;
}
2020-05-13 00:28:32 +02:00
/*****************************************************************************/
/*************** Generate choice indexes depending on shuffle ****************/
/*****************************************************************************/
static void ExaPrn_GenerateChoiceIndexes (struct TstPrn_PrintedQuestion *PrintedQuestion,
bool Shuffle)
{
struct Tst_Question Question;
unsigned NumOpt;
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned Index;
bool ErrorInIndex;
char StrInd[1 + Cns_MAX_DECIMAL_DIGITS_UINT + 1];
/***** Create test question *****/
Tst_QstConstructor (&Question);
Question.QstCod = PrintedQuestion->QstCod;
/***** Get answers of question from database *****/
ExaSet_GetAnswersQst (&Question,&mysql_res,Shuffle);
/*
row[0] AnsInd
row[1] Answer
row[2] Feedback
row[3] MedCod
row[4] Correct
*/
for (NumOpt = 0;
NumOpt < Question.Answer.NumOptions;
NumOpt++)
{
/***** Get next answer *****/
row = mysql_fetch_row (mysql_res);
/***** Assign index (row[0]).
Index is 0,1,2,3... if no shuffle
or 1,3,0,2... (example) if shuffle *****/
ErrorInIndex = false;
if (sscanf (row[0],"%u",&Index) == 1)
{
if (Index >= Tst_MAX_OPTIONS_PER_QUESTION)
ErrorInIndex = true;
}
else
ErrorInIndex = true;
if (ErrorInIndex)
Err_WrongAnswerIndexExit ();
2020-05-13 00:28:32 +02:00
if (NumOpt == 0)
snprintf (StrInd,sizeof (StrInd),"%u",Index);
else
snprintf (StrInd,sizeof (StrInd),",%u",Index);
Str_Concat (PrintedQuestion->StrIndexes,StrInd,
sizeof (PrintedQuestion->StrIndexes) - 1);
2020-05-13 00:28:32 +02:00
}
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
/***** Destroy test question *****/
Tst_QstDestructor (&Question);
}
2020-05-09 01:37:00 +02:00
/*****************************************************************************/
/***************** Create new blank exam print in database *******************/
/*****************************************************************************/
2020-05-10 14:03:40 +02:00
static void ExaPrn_CreatePrintInDB (struct ExaPrn_Print *Print)
2020-05-09 01:37:00 +02:00
{
unsigned QstInd;
2020-05-22 20:10:45 +02:00
2020-05-09 01:37:00 +02:00
/***** Insert new exam print into table *****/
Print->PrnCod =
DB_QueryINSERTandReturnCode ("can not create new exam print",
"INSERT INTO exa_prints"
" (SesCod,UsrCod,StartTime,EndTime,"
"NumQsts,NumQstsNotBlank,Sent,Score)"
2020-05-09 01:37:00 +02:00
" VALUES"
" (%ld,%ld,NOW(),NOW(),"
"%u,0,'N',0)",
2020-05-17 02:28:30 +02:00
Print->SesCod,
2020-09-27 17:38:51 +02:00
Print->UsrCod,
2020-06-24 02:15:50 +02:00
Print->NumQsts.All);
2020-05-22 20:10:45 +02:00
/***** Store all questions (with blank answers)
of this exam print just generated in database *****/
for (QstInd = 0;
QstInd < Print->NumQsts.All;
QstInd++)
ExaPrn_StoreOneQstOfPrintInDB (Print,QstInd);
2020-05-09 01:37:00 +02:00
}
2020-05-10 01:42:30 +02:00
/*****************************************************************************/
/************* Get the questions of an exam print from database **************/
/*****************************************************************************/
2020-05-16 02:04:36 +02:00
void ExaPrn_GetPrintQuestionsFromDB (struct ExaPrn_Print *Print)
2020-05-10 01:42:30 +02:00
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned QstInd;
2020-05-10 01:42:30 +02:00
/***** Get questions of an exam print from database *****/
Print->NumQsts.All = (unsigned)
DB_QuerySELECT (&mysql_res,"can not get questions of an exam print",
"SELECT QstCod," // row[0]
"SetCod," // row[1]
"Score," // row[2]
"Indexes," // row[3]
"Answers" // row[4]
" FROM exa_print_questions"
" WHERE PrnCod=%ld"
" ORDER BY QstInd",
Print->PrnCod);
2020-05-10 01:42:30 +02:00
/***** Get questions *****/
2020-06-18 20:06:17 +02:00
if (Print->NumQsts.All <= ExaPrn_MAX_QUESTIONS_PER_EXAM_PRINT)
for (QstInd = 0;
QstInd < Print->NumQsts.All;
QstInd++)
2020-05-10 01:42:30 +02:00
{
row = mysql_fetch_row (mysql_res);
/* Get question code (row[0]) */
if ((Print->PrintedQuestions[QstInd].QstCod = Str_ConvertStrCodToLongCod (row[0])) < 0)
Err_WrongQuestionExit ();
2020-05-10 01:42:30 +02:00
/* Get set code (row[1]) */
if ((Print->PrintedQuestions[QstInd].SetCod = Str_ConvertStrCodToLongCod (row[1])) < 0)
Err_WrongSetExit ();
2020-05-10 01:42:30 +02:00
2020-05-12 02:45:03 +02:00
/* Get score (row[2]) */
2020-05-11 14:56:49 +02:00
Str_SetDecimalPointToUS (); // To get the decimal point as a dot
if (sscanf (row[2],"%lf",&Print->PrintedQuestions[QstInd].Score) != 1)
Err_ShowErrorAndExit ("Wrong question score.");
2020-05-11 14:56:49 +02:00
Str_SetDecimalPointToLocal (); // Return to local system
2020-05-12 02:45:03 +02:00
/* Get indexes for this question (row[3]) */
Str_Copy (Print->PrintedQuestions[QstInd].StrIndexes,row[3],
sizeof (Print->PrintedQuestions[QstInd].StrIndexes) - 1);
2020-05-10 01:42:30 +02:00
2020-05-12 02:45:03 +02:00
/* Get answers selected by user for this question (row[4]) */
Str_Copy (Print->PrintedQuestions[QstInd].StrAnswers,row[4],
sizeof (Print->PrintedQuestions[QstInd].StrAnswers) - 1);
2020-05-10 01:42:30 +02:00
}
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
2020-06-18 20:06:17 +02:00
if (Print->NumQsts.All > ExaPrn_MAX_QUESTIONS_PER_EXAM_PRINT)
Err_ShowErrorAndExit ("Too many questions.");
2020-05-10 01:42:30 +02:00
}
2020-05-09 21:07:50 +02:00
/*****************************************************************************/
2020-05-10 14:03:40 +02:00
/******************** Show an exam print to be answered **********************/
2020-05-09 21:07:50 +02:00
/*****************************************************************************/
2020-05-17 20:12:37 +02:00
static void ExaPrn_ShowExamPrintToFillIt (struct Exa_Exams *Exams,
const struct Exa_Exam *Exam,
2021-01-20 00:42:59 +01:00
struct ExaPrn_Print *Print)
2020-05-09 21:07:50 +02:00
{
2021-01-20 00:42:59 +01:00
extern const char *Hlp_ASSESSMENT_Exams_answer_exam;
2020-05-09 21:07:50 +02:00
/***** Begin box *****/
2020-05-17 20:12:37 +02:00
Box_BoxBegin (NULL,Exam->Title,
2020-05-09 21:07:50 +02:00
NULL,NULL,
2021-01-20 00:42:59 +01:00
Hlp_ASSESSMENT_Exams_answer_exam,Box_NOT_CLOSABLE);
2020-05-19 13:12:26 +02:00
/***** Heading *****/
2021-01-20 00:42:59 +01:00
/* Institution, degree and course */
2020-05-09 21:07:50 +02:00
Lay_WriteHeaderClassPhoto (false,false,
Gbl.Hierarchy.Ins.InsCod,
Gbl.Hierarchy.Deg.DegCod,
Gbl.Hierarchy.Crs.CrsCod);
2021-01-20 00:42:59 +01:00
/***** Show user and time *****/
/* Begin table */
HTM_TABLE_BeginWideMarginPadding (10);
/* User */
ExaRes_ShowExamResultUser (&Gbl.Usrs.Me.UsrDat);
/* End table */
HTM_TABLE_End ();
/* Exam description */
2020-05-19 13:12:26 +02:00
ExaPrn_GetAndWriteDescription (Exam->ExaCod);
2020-05-09 21:07:50 +02:00
2020-06-18 20:06:17 +02:00
if (Print->NumQsts.All)
2020-05-09 21:07:50 +02:00
{
2020-05-10 14:03:40 +02:00
/***** Show table with questions to answer *****/
2020-05-11 02:28:38 +02:00
HTM_DIV_Begin ("id=\"examprint\""); // Used for AJAX based refresh
2020-05-25 00:18:09 +02:00
ExaPrn_ShowTableWithQstsToFill (Exams,Print);
2020-05-11 02:28:38 +02:00
HTM_DIV_End (); // Used for AJAX based refresh
2020-05-10 14:03:40 +02:00
}
2020-05-09 21:07:50 +02:00
2020-05-10 14:03:40 +02:00
/***** End box *****/
Box_BoxEnd ();
}
2020-05-09 21:07:50 +02:00
2020-05-19 13:12:26 +02:00
/*****************************************************************************/
/********************* Write description in an exam print ********************/
/*****************************************************************************/
static void ExaPrn_GetAndWriteDescription (long ExaCod)
{
char Txt[Cns_MAX_BYTES_TEXT + 1];
/***** Get description from database *****/
Exa_GetExamTxtFromDB (ExaCod,Txt);
Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML, // Convert from HTML to rigorous HTML
Txt,Cns_MAX_BYTES_TEXT,false);
Str_InsertLinks (Txt,Cns_MAX_BYTES_TEXT,60); // Insert links
/***** Write description *****/
HTM_DIV_Begin ("class=\"EXA_PRN_DESC DAT_SMALL\"");
HTM_Txt (Txt);
HTM_DIV_End ();
}
2020-05-10 14:03:40 +02:00
/*****************************************************************************/
/********* Show the main part (table) of an exam print to be answered ********/
/*****************************************************************************/
2020-05-09 21:07:50 +02:00
2020-05-25 00:18:09 +02:00
static void ExaPrn_ShowTableWithQstsToFill (struct Exa_Exams *Exams,
const struct ExaPrn_Print *Print)
2020-05-10 14:03:40 +02:00
{
2020-05-25 00:18:09 +02:00
extern const char *Txt_I_have_finished;
unsigned QstInd;
2020-05-10 14:03:40 +02:00
struct Tst_Question Question;
2020-05-09 21:07:50 +02:00
2020-05-10 14:03:40 +02:00
/***** Begin table *****/
HTM_TABLE_BeginWideMarginPadding (10);
2020-05-09 21:07:50 +02:00
2020-05-10 14:03:40 +02:00
/***** Write one row for each question *****/
for (QstInd = 0;
QstInd < Print->NumQsts.All;
QstInd++)
2020-05-10 14:03:40 +02:00
{
/* Create test question */
Tst_QstConstructor (&Question);
Question.QstCod = Print->PrintedQuestions[QstInd].QstCod;
2020-05-10 14:03:40 +02:00
2020-05-15 17:08:16 +02:00
/* Get question from database */
2020-05-15 01:07:46 +02:00
ExaSet_GetQstDataFromDB (&Question);
2020-05-10 14:03:40 +02:00
/* Write question and answers */
ExaPrn_WriteQstAndAnsToFill (Print,QstInd,&Question);
2020-05-10 14:03:40 +02:00
/* Destroy test question */
Tst_QstDestructor (&Question);
2020-05-09 21:07:50 +02:00
}
2020-05-10 14:03:40 +02:00
/***** End table *****/
HTM_TABLE_End ();
2020-05-25 00:18:09 +02:00
/***** Form to end/close this exam print *****/
Frm_BeginFormId (ActEndExaPrn,"finished");
2020-05-25 00:18:09 +02:00
ExaSes_PutParamsEdit (Exams);
Btn_PutCreateButton (Txt_I_have_finished);
Frm_EndForm ();
2020-05-09 21:07:50 +02:00
}
2020-05-09 23:12:53 +02:00
/*****************************************************************************/
/********** Write a row of a test, with one question and its answer **********/
/*****************************************************************************/
2020-05-17 20:12:37 +02:00
static void ExaPrn_WriteQstAndAnsToFill (const struct ExaPrn_Print *Print,
unsigned QstInd,
2020-06-17 02:31:42 +02:00
struct Tst_Question *Question)
2020-05-09 23:12:53 +02:00
{
static struct ExaSet_Set CurrentSet =
{
.ExaCod = -1L,
.SetCod = -1L,
.SetInd = 0,
.NumQstsToPrint = 0,
.Title[0] = '\0'
};
if (Print->PrintedQuestions[QstInd].SetCod != CurrentSet.SetCod)
2020-05-09 23:12:53 +02:00
{
/***** Get data of this set *****/
CurrentSet.SetCod = Print->PrintedQuestions[QstInd].SetCod;
2020-05-09 23:12:53 +02:00
ExaSet_GetDataOfSetByCod (&CurrentSet);
/***** Title for this set *****/
HTM_TR_Begin (NULL);
2020-05-15 21:59:27 +02:00
HTM_TD_Begin ("colspan=\"2\" class=\"COLOR0\"");
2020-05-09 23:12:53 +02:00
ExaSet_WriteSetTitle (&CurrentSet);
HTM_TD_End ();
HTM_TR_End ();
}
/***** Begin row *****/
HTM_TR_Begin (NULL);
/***** Number of question and answer type *****/
2020-05-15 20:30:46 +02:00
HTM_TD_Begin ("class=\"RT\"");
Tst_WriteNumQst (QstInd + 1,"BIG_INDEX");
2020-06-17 20:20:16 +02:00
Tst_WriteAnswerType (Question->Answer.Type,"DAT_SMALL");
2020-05-09 23:12:53 +02:00
HTM_TD_End ();
/***** Stem, media and answers *****/
2020-05-15 20:30:46 +02:00
HTM_TD_Begin ("class=\"LT\"");
2020-05-09 23:12:53 +02:00
/* Stem */
2020-06-17 20:20:16 +02:00
Tst_WriteQstStem (Question->Stem,"TEST_TXT",true);
2020-05-09 23:12:53 +02:00
/* Media */
Med_ShowMedia (&Question->Media,
"TEST_MED_SHOW_CONT",
"TEST_MED_SHOW");
/* Answers */
Frm_BeginFormNoAction (); // Form that can not be submitted, to avoid enter key to send it
ExaPrn_WriteAnswersToFill (Print,QstInd,Question);
2020-05-12 02:45:03 +02:00
Frm_EndForm ();
2020-05-09 23:12:53 +02:00
HTM_TD_End ();
/***** End row *****/
HTM_TR_End ();
}
/*****************************************************************************/
/***************** Write answers of a question to fill them ******************/
/*****************************************************************************/
2020-05-17 20:12:37 +02:00
static void ExaPrn_WriteAnswersToFill (const struct ExaPrn_Print *Print,
unsigned QstInd,
2020-06-17 02:31:42 +02:00
struct Tst_Question *Question)
2020-05-09 23:12:53 +02:00
{
2020-06-17 02:31:42 +02:00
void (*ExaPrn_WriteAnsToFill[Tst_NUM_ANS_TYPES]) (const struct ExaPrn_Print *Print,
unsigned QstInd,
2020-06-17 02:31:42 +02:00
struct Tst_Question *Question) =
{
[Tst_ANS_INT ] = ExaPrn_WriteIntAnsToFill,
[Tst_ANS_FLOAT ] = ExaPrn_WriteFltAnsToFill,
[Tst_ANS_TRUE_FALSE ] = ExaPrn_WriteTF_AnsToFill,
[Tst_ANS_UNIQUE_CHOICE ] = ExaPrn_WriteChoAnsToFill,
[Tst_ANS_MULTIPLE_CHOICE] = ExaPrn_WriteChoAnsToFill,
[Tst_ANS_TEXT ] = ExaPrn_WriteTxtAnsToFill,
};
/***** Write answers *****/
ExaPrn_WriteAnsToFill[Question->Answer.Type] (Print,QstInd,Question);
2020-05-09 23:12:53 +02:00
}
/*****************************************************************************/
/****************** Write integer answer when seeing a test ******************/
/*****************************************************************************/
2020-05-11 13:05:38 +02:00
static void ExaPrn_WriteIntAnsToFill (const struct ExaPrn_Print *Print,
unsigned QstInd,
2020-06-17 02:31:42 +02:00
__attribute__((unused)) struct Tst_Question *Question)
2020-05-09 23:12:53 +02:00
{
2020-05-11 02:28:38 +02:00
char Id[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Ansxx...x"
2020-05-09 23:12:53 +02:00
/***** Write input field for the answer *****/
snprintf (Id,sizeof (Id),"Ans%010u",QstInd);
2020-05-12 02:45:03 +02:00
HTM_TxtF ("<input type=\"text\" id=\"%s\" name=\"Ans\""
2020-05-11 02:28:38 +02:00
" size=\"11\" maxlength=\"11\" value=\"%s\"",
Id,Print->PrintedQuestions[QstInd].StrAnswers);
ExaPrn_WriteJSToUpdateExamPrint (Print,QstInd,Id,-1);
2020-05-19 15:17:52 +02:00
HTM_Txt (" />");
2020-05-09 23:12:53 +02:00
}
/*****************************************************************************/
/****************** Write float answer when seeing a test ********************/
/*****************************************************************************/
2020-06-17 02:31:42 +02:00
static void ExaPrn_WriteFltAnsToFill (const struct ExaPrn_Print *Print,
unsigned QstInd,
2020-06-17 02:31:42 +02:00
__attribute__((unused)) struct Tst_Question *Question)
2020-05-09 23:12:53 +02:00
{
2020-05-11 13:05:38 +02:00
char Id[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Ansxx...x"
2020-05-09 23:12:53 +02:00
/***** Write input field for the answer *****/
snprintf (Id,sizeof (Id),"Ans%010u",QstInd);
2020-05-12 02:45:03 +02:00
HTM_TxtF ("<input type=\"text\" id=\"%s\" name=\"Ans\""
2020-05-11 13:05:38 +02:00
" size=\"11\" maxlength=\"%u\" value=\"%s\"",
2020-05-12 02:45:03 +02:00
Id,Tst_MAX_BYTES_FLOAT_ANSWER,
Print->PrintedQuestions[QstInd].StrAnswers);
ExaPrn_WriteJSToUpdateExamPrint (Print,QstInd,Id,-1);
2020-05-19 15:17:52 +02:00
HTM_Txt (" />");
2020-05-09 23:12:53 +02:00
}
/*****************************************************************************/
/************** Write false / true answer when seeing a test ****************/
/*****************************************************************************/
2020-05-19 15:17:52 +02:00
static void ExaPrn_WriteTF_AnsToFill (const struct ExaPrn_Print *Print,
unsigned QstInd,
2020-06-17 02:31:42 +02:00
__attribute__((unused)) struct Tst_Question *Question)
2020-05-09 23:12:53 +02:00
{
extern const char *Txt_TF_QST[2];
2020-05-11 13:23:42 +02:00
char Id[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Ansxx...x"
2020-05-09 23:12:53 +02:00
/***** 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. */
snprintf (Id,sizeof (Id),"Ans%010u",QstInd);
2020-05-12 02:45:03 +02:00
HTM_TxtF ("<select id=\"%s\" name=\"Ans\"",Id);
ExaPrn_WriteJSToUpdateExamPrint (Print,QstInd,Id,-1);
2020-05-19 15:17:52 +02:00
HTM_Txt (" />");
HTM_OPTION (HTM_Type_STRING,"" ,Print->PrintedQuestions[QstInd].StrAnswers[0] == '\0',false,"&nbsp;");
HTM_OPTION (HTM_Type_STRING,"T",Print->PrintedQuestions[QstInd].StrAnswers[0] == 'T' ,false,"%s",Txt_TF_QST[0]);
HTM_OPTION (HTM_Type_STRING,"F",Print->PrintedQuestions[QstInd].StrAnswers[0] == 'F' ,false,"%s",Txt_TF_QST[1]);
2020-05-11 13:23:42 +02:00
HTM_Txt ("</select>");
2020-05-09 23:12:53 +02:00
}
/*****************************************************************************/
/******** Write single or multiple choice answer when seeing a test **********/
/*****************************************************************************/
2020-05-19 15:17:52 +02:00
static void ExaPrn_WriteChoAnsToFill (const struct ExaPrn_Print *Print,
unsigned QstInd,
2020-06-17 02:31:42 +02:00
struct Tst_Question *Question)
2020-05-09 23:12:53 +02:00
{
unsigned NumOpt;
unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]; // Indexes of all answers of this question
bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION];
2020-05-11 14:11:15 +02:00
char Id[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Ansxx...x"
2020-05-09 23:12:53 +02:00
2020-06-17 02:31:42 +02:00
/***** Change format of answers text *****/
Tst_ChangeFormatAnswersText (Question);
2020-05-09 23:12:53 +02:00
/***** Get indexes for this question from string *****/
TstPrn_GetIndexesFromStr (Print->PrintedQuestions[QstInd].StrIndexes,Indexes);
2020-05-09 23:12:53 +02:00
/***** Get the user's answers for this question from string *****/
TstPrn_GetAnswersFromStr (Print->PrintedQuestions[QstInd].StrAnswers,UsrAnswers);
2020-05-09 23:12:53 +02:00
/***** Begin table *****/
HTM_TABLE_BeginPadding (2);
for (NumOpt = 0;
NumOpt < Question->Answer.NumOptions;
NumOpt++)
{
/***** Indexes are 0 1 2 3... if no shuffle
or 3 1 0 2... (example) if shuffle *****/
HTM_TR_Begin (NULL);
/***** Write selectors and letter of this option *****/
/* 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_TD_Begin ("class=\"LT\"");
snprintf (Id,sizeof (Id),"Ans%010u",QstInd);
2020-05-12 13:24:43 +02:00
HTM_TxtF ("<input type=\"%s\" id=\"%s_%u\" name=\"Ans\" value=\"%u\"%s",
Question->Answer.Type == Tst_ANS_UNIQUE_CHOICE ? "radio" :
"checkbox",
Id,NumOpt,Indexes[NumOpt],
UsrAnswers[Indexes[NumOpt]] ? " checked=\"checked\"" :
"");
ExaPrn_WriteJSToUpdateExamPrint (Print,QstInd,Id,(int) NumOpt);
2020-05-19 15:17:52 +02:00
HTM_Txt (" />");
2020-05-09 23:12:53 +02:00
HTM_TD_End ();
HTM_TD_Begin ("class=\"LT\"");
HTM_LABEL_Begin ("for=\"Ans%010u_%u\" class=\"TEST_TXT\"",QstInd,NumOpt);
2020-05-09 23:12:53 +02:00
HTM_TxtF ("%c)&nbsp;",'a' + (char) NumOpt);
HTM_LABEL_End ();
HTM_TD_End ();
/***** Write the option text *****/
HTM_TD_Begin ("class=\"LT\"");
HTM_LABEL_Begin ("for=\"Ans%010u_%u\" class=\"TEST_TXT\"",QstInd,NumOpt);
2020-05-09 23:12:53 +02:00
HTM_Txt (Question->Answer.Options[Indexes[NumOpt]].Text);
HTM_LABEL_End ();
Med_ShowMedia (&Question->Answer.Options[Indexes[NumOpt]].Media,
"TEST_MED_SHOW_CONT",
"TEST_MED_SHOW");
HTM_TD_End ();
HTM_TR_End ();
}
/***** End table *****/
HTM_TABLE_End ();
}
/*****************************************************************************/
/******************** Write text answer when seeing a test *******************/
/*****************************************************************************/
2020-05-19 15:17:52 +02:00
static void ExaPrn_WriteTxtAnsToFill (const struct ExaPrn_Print *Print,
unsigned QstInd,
2020-06-17 02:31:42 +02:00
__attribute__((unused)) struct Tst_Question *Question)
2020-05-09 23:12:53 +02:00
{
2020-05-11 13:05:38 +02:00
char Id[3 + Cns_MAX_DECIMAL_DIGITS_UINT + 1]; // "Ansxx...x"
2020-05-09 23:12:53 +02:00
/***** Write input field for the answer *****/
snprintf (Id,sizeof (Id),"Ans%010u",QstInd);
2020-05-12 02:45:03 +02:00
HTM_TxtF ("<input type=\"text\" id=\"%s\" name=\"Ans\""
2020-05-11 13:05:38 +02:00
" size=\"40\" maxlength=\"%u\" value=\"%s\"",
2020-05-12 02:45:03 +02:00
Id,Tst_MAX_CHARS_ANSWERS_ONE_QST,
Print->PrintedQuestions[QstInd].StrAnswers);
ExaPrn_WriteJSToUpdateExamPrint (Print,QstInd,Id,-1);
2020-05-25 00:18:09 +02:00
2020-05-19 15:17:52 +02:00
HTM_Txt (" />");
}
/*****************************************************************************/
/********************** Receive answer to an exam print **********************/
/*****************************************************************************/
static void ExaPrn_WriteJSToUpdateExamPrint (const struct ExaPrn_Print *Print,
unsigned QstInd,
2020-05-19 15:17:52 +02:00
const char *Id,int NumOpt)
{
if (NumOpt < 0)
HTM_TxtF (" onchange=\"updateExamPrint('examprint','%s','Ans',"
"'act=%ld&ses=%s&SesCod=%ld&QstInd=%u',%u);",
2020-05-19 15:17:52 +02:00
Id,
Act_GetActCod (ActAnsExaPrn),Gbl.Session.Id,Print->SesCod,QstInd,
2020-06-22 22:47:54 +02:00
(unsigned) Gbl.Prefs.Language);
2020-05-19 15:17:52 +02:00
else // NumOpt >= 0
HTM_TxtF (" onclick=\"updateExamPrint('examprint','%s_%d','Ans',"
"'act=%ld&ses=%s&SesCod=%ld&QstInd=%u',%u);",
2020-05-19 15:17:52 +02:00
Id,NumOpt,
Act_GetActCod (ActAnsExaPrn),Gbl.Session.Id,Print->SesCod,QstInd,
2020-06-22 22:47:54 +02:00
(unsigned) Gbl.Prefs.Language);
2020-05-19 15:17:52 +02:00
HTM_Txt (" return false;\""); // return false is necessary to not submit form
2020-05-09 23:12:53 +02:00
}
2020-05-09 21:07:50 +02:00
/*****************************************************************************/
/********************** Receive answer to an exam print **********************/
/*****************************************************************************/
void ExaPrn_ReceivePrintAnswer (void)
{
2020-05-17 20:12:37 +02:00
extern const char *Txt_You_dont_have_access_to_the_exam;
2020-05-25 00:18:09 +02:00
extern const char *Txt_Continue;
struct Exa_Exams Exams;
2020-05-23 19:08:59 +02:00
struct Exa_Exam Exam;
struct ExaSes_Session Session;
2020-05-11 02:28:38 +02:00
struct ExaPrn_Print Print;
2020-05-23 13:24:08 +02:00
unsigned QstInd;
2020-05-11 02:28:38 +02:00
2020-05-25 00:18:09 +02:00
/***** Reset exams context *****/
Exa_ResetExams (&Exams);
2020-05-23 19:08:59 +02:00
Exa_ResetExam (&Exam);
ExaSes_ResetSession (&Session);
2020-05-11 02:28:38 +02:00
2020-05-17 02:28:30 +02:00
/***** Get session code *****/
Print.SesCod = ExaSes_GetParamSesCod ();
2020-05-11 02:28:38 +02:00
2020-05-23 13:24:08 +02:00
/***** Get print data *****/
Print.UsrCod = Gbl.Usrs.Me.UsrDat.UsrCod;
2020-06-24 20:10:57 +02:00
ExaPrn_GetDataOfPrintBySesCodAndUsrCod (&Print);
2020-05-23 13:24:08 +02:00
if (Print.PrnCod <= 0)
Err_WrongExamExit ();
2020-05-11 02:28:38 +02:00
2020-05-23 19:08:59 +02:00
/***** Get session data *****/
Session.SesCod = Print.SesCod;
ExaSes_GetDataOfSessionByCod (&Session);
if (Session.SesCod <= 0)
Err_WrongExamExit ();
2020-05-25 00:18:09 +02:00
Exams.SesCod = Session.SesCod;
2020-05-23 19:08:59 +02:00
/***** Get exam data *****/
Exam.ExaCod = Session.ExaCod;
Exa_GetDataOfExamByCod (&Exam);
if (Exam.ExaCod <= 0)
Err_WrongExamExit ();
2020-05-23 19:08:59 +02:00
if (Exam.CrsCod != Gbl.Hierarchy.Crs.CrsCod)
Err_WrongExamExit ();
2020-05-25 00:18:09 +02:00
Exams.ExaCod = Exam.ExaCod;
2020-05-23 13:24:08 +02:00
/***** Get question index from form *****/
QstInd = ExaPrn_GetParamQstInd ();
2020-05-22 20:10:45 +02:00
2020-05-23 15:53:53 +02:00
/***** Set log print code, action and question index *****/
ExaLog_SetPrnCod (Print.PrnCod);
ExaLog_SetAction (ExaLog_ANSWER_QUESTION);
ExaLog_SetQstInd (QstInd);
2020-05-15 01:07:46 +02:00
2020-05-23 13:24:08 +02:00
/***** Check if session if visible and open *****/
2020-05-23 19:08:59 +02:00
if (ExaSes_CheckIfICanAnswerThisSession (&Exam,&Session))
2020-05-23 13:24:08 +02:00
{
2020-05-23 19:08:59 +02:00
/***** Set log open to true ****/
ExaLog_SetIfCanAnswer (true);
/***** Get questions and current user's answers of exam print from database *****/
ExaPrn_GetPrintQuestionsFromDB (&Print);
2020-05-23 15:53:53 +02:00
2020-05-15 01:07:46 +02:00
/***** Get answers from form to assess a test *****/
2020-05-23 13:24:08 +02:00
ExaPrn_GetAnswerFromForm (&Print,QstInd);
2020-05-11 02:28:38 +02:00
2020-05-15 01:07:46 +02:00
/***** Update answer in database *****/
/* Compute question score and store in database */
2020-05-23 13:24:08 +02:00
ExaPrn_ComputeScoreAndStoreQuestionOfPrint (&Print,QstInd);
2020-05-11 02:28:38 +02:00
2020-05-15 01:07:46 +02:00
/* Update exam print in database */
ExaPrn_GetNumQstsNotBlank (&Print);
ExaPrn_ComputeTotalScoreOfPrint (&Print);
ExaPrn_UpdatePrintInDB (&Print);
2020-05-11 02:28:38 +02:00
2020-05-15 01:07:46 +02:00
/***** Show table with questions to answer *****/
2020-05-25 00:18:09 +02:00
ExaPrn_ShowTableWithQstsToFill (&Exams,&Print);
2020-05-15 01:07:46 +02:00
}
2020-05-23 15:53:53 +02:00
else // Not accessible to answer
{
2020-05-23 19:08:59 +02:00
/***** Set log open to false ****/
ExaLog_SetIfCanAnswer (false);
2020-05-23 15:53:53 +02:00
/***** Show warning *****/
2020-05-17 20:12:37 +02:00
Ale_ShowAlert (Ale_INFO,Txt_You_dont_have_access_to_the_exam);
2020-05-25 00:18:09 +02:00
/***** Form to end/close this exam print *****/
Frm_BeginForm (ActEndExaPrn);
2020-05-25 00:18:09 +02:00
ExaSes_PutParamsEdit (&Exams);
Btn_PutCreateButton (Txt_Continue);
Frm_EndForm ();
2020-05-23 15:53:53 +02:00
}
2020-05-11 02:28:38 +02:00
}
/*****************************************************************************/
/******** Get questions and answers from form to assess an exam print ********/
/*****************************************************************************/
2020-05-23 13:24:08 +02:00
static void ExaPrn_GetAnswerFromForm (struct ExaPrn_Print *Print,unsigned QstInd)
2020-05-11 02:28:38 +02:00
{
/***** Get answers selected by user for this question *****/
2020-05-23 13:24:08 +02:00
Par_GetParToText ("Ans",Print->PrintedQuestions[QstInd].StrAnswers,
2020-05-12 02:45:03 +02:00
Tst_MAX_BYTES_ANSWERS_ONE_QST); /* If answer type == T/F ==> " ", "T", "F"; if choice ==> "0", "2",... */
2020-05-11 02:28:38 +02:00
}
/*****************************************************************************/
/********************* Get parameter with question index *********************/
2020-05-11 02:28:38 +02:00
/*****************************************************************************/
static unsigned ExaPrn_GetParamQstInd (void)
{
long QstInd;
2020-05-11 02:28:38 +02:00
if ((QstInd = Par_GetParToLong ("QstInd")) < 0)
Err_WrongQuestionIndexExit ();
2020-05-11 02:28:38 +02:00
return (unsigned) QstInd;
2020-05-11 02:28:38 +02:00
}
/*****************************************************************************/
/*********** Compute score of one question and store in database *************/
/*****************************************************************************/
static void ExaPrn_ComputeScoreAndStoreQuestionOfPrint (struct ExaPrn_Print *Print,
unsigned QstInd)
2020-05-11 02:28:38 +02:00
{
struct Tst_Question Question;
2020-05-12 13:24:43 +02:00
char CurrentStrAnswersInDB[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1]; // Answers selected by user
2020-05-11 02:28:38 +02:00
/***** Compute question score *****/
Tst_QstConstructor (&Question);
Question.QstCod = Print->PrintedQuestions[QstInd].QstCod;
2020-05-13 00:28:32 +02:00
Question.Answer.Type = ExaSet_GetQstAnswerTypeFromDB (Question.QstCod);
ExaPrn_ComputeAnswerScore (&Print->PrintedQuestions[QstInd],&Question);
2020-05-11 02:28:38 +02:00
Tst_QstDestructor (&Question);
2020-05-12 13:24:43 +02:00
/***** If type is unique choice and the option (radio button) is checked
==> uncheck it by deleting answer *****/
if (Question.Answer.Type == Tst_ANS_UNIQUE_CHOICE)
{
ExaPrn_GetAnswerFromDB (Print,Print->PrintedQuestions[QstInd].QstCod,
2020-05-12 13:24:43 +02:00
CurrentStrAnswersInDB);
if (!strcmp (Print->PrintedQuestions[QstInd].StrAnswers,CurrentStrAnswersInDB))
2020-05-22 20:10:45 +02:00
{
/* The answer just clicked by user
is the same as the last one checked and stored in database */
Print->PrintedQuestions[QstInd].StrAnswers[0] = '\0'; // Uncheck option
Print->PrintedQuestions[QstInd].Score = 0; // Clear question score
2020-05-22 20:10:45 +02:00
}
2020-05-12 13:24:43 +02:00
}
2020-05-11 02:28:38 +02:00
/***** Store test exam question in database *****/
ExaPrn_StoreOneQstOfPrintInDB (Print,
QstInd); // 0, 1, 2, 3...
2020-05-11 02:28:38 +02:00
}
2020-05-13 12:53:27 +02:00
/*****************************************************************************/
/************* Write answers of a question when assessing a test *************/
/*****************************************************************************/
void ExaPrn_ComputeAnswerScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *Question)
{
2020-05-13 13:13:03 +02:00
void (*ExaPrn_GetCorrectAndComputeAnsScore[Tst_NUM_ANS_TYPES]) (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *Question) =
{
[Tst_ANS_INT ] = ExaPrn_GetCorrectAndComputeIntAnsScore,
[Tst_ANS_FLOAT ] = ExaPrn_GetCorrectAndComputeFltAnsScore,
[Tst_ANS_TRUE_FALSE ] = ExaPrn_GetCorrectAndComputeTF_AnsScore,
[Tst_ANS_UNIQUE_CHOICE ] = ExaPrn_GetCorrectAndComputeChoAnsScore,
[Tst_ANS_MULTIPLE_CHOICE] = ExaPrn_GetCorrectAndComputeChoAnsScore,
[Tst_ANS_TEXT ] = ExaPrn_GetCorrectAndComputeTxtAnsScore,
};
/***** Get correct answer and compute answer score depending on type *****/
ExaPrn_GetCorrectAndComputeAnsScore[Question->Answer.Type] (PrintedQuestion,Question);
2020-05-13 12:53:27 +02:00
}
/*****************************************************************************/
/******* Get correct answer and compute score for each type of answer ********/
/*****************************************************************************/
static void ExaPrn_GetCorrectAndComputeIntAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *Question)
{
2020-05-13 13:37:15 +02:00
/***** Get the numerical value of the correct answer,
and compute score *****/
2020-05-13 12:53:27 +02:00
ExaPrn_GetCorrectIntAnswerFromDB (Question);
TstPrn_ComputeIntAnsScore (PrintedQuestion,Question);
}
static void ExaPrn_GetCorrectAndComputeFltAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *Question)
{
2020-05-13 13:37:15 +02:00
/***** Get the numerical value of the minimum and maximum correct answers,
and compute score *****/
2020-05-13 12:53:27 +02:00
ExaPrn_GetCorrectFltAnswerFromDB (Question);
TstPrn_ComputeFltAnsScore (PrintedQuestion,Question);
}
static void ExaPrn_GetCorrectAndComputeTF_AnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
2020-05-13 13:37:15 +02:00
struct Tst_Question *Question)
2020-05-13 12:53:27 +02:00
{
2020-05-13 13:37:15 +02:00
/***** Get answer true or false,
and compute score *****/
2020-05-13 12:53:27 +02:00
ExaPrn_GetCorrectTF_AnswerFromDB (Question);
TstPrn_ComputeTF_AnsScore (PrintedQuestion,Question);
}
static void ExaPrn_GetCorrectAndComputeChoAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
2020-05-13 13:37:15 +02:00
struct Tst_Question *Question)
2020-05-13 12:53:27 +02:00
{
2020-05-13 13:37:15 +02:00
/***** Get correct options of test question from database,
and compute score *****/
2020-05-13 12:53:27 +02:00
ExaPrn_GetCorrectChoAnswerFromDB (Question);
TstPrn_ComputeChoAnsScore (PrintedQuestion,Question);
}
static void ExaPrn_GetCorrectAndComputeTxtAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *Question)
{
2020-05-13 13:37:15 +02:00
/***** Get correct text answers for this question from database,
and compute score *****/
2020-05-13 12:53:27 +02:00
ExaPrn_GetCorrectTxtAnswerFromDB (Question);
TstPrn_ComputeTxtAnsScore (PrintedQuestion,Question);
}
/*****************************************************************************/
/***************** Get correct answer for each type of answer ****************/
/*****************************************************************************/
static void ExaPrn_GetCorrectIntAnswerFromDB (struct Tst_Question *Question)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
/***** Query database *****/
Question->Answer.NumOptions = (unsigned)
DB_QuerySELECT (&mysql_res,"can not get answers of a question",
"SELECT Answer" // row[0]
" FROM exa_set_answers"
" WHERE QstCod=%ld",
Question->QstCod);
2020-05-13 12:53:27 +02:00
/***** Check if number of rows is correct *****/
Tst_CheckIfNumberOfAnswersIsOne (Question);
/***** Get correct answer *****/
row = mysql_fetch_row (mysql_res);
if (sscanf (row[0],"%ld",&Question->Answer.Integer) != 1)
Err_WrongAnswerExit ();
2020-05-13 12:53:27 +02:00
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
static void ExaPrn_GetCorrectFltAnswerFromDB (struct Tst_Question *Question)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumOpt;
double Tmp;
/***** Query database *****/
Question->Answer.NumOptions = (unsigned)
DB_QuerySELECT (&mysql_res,"can not get answers of a question",
"SELECT Answer" // row[0]
" FROM exa_set_answers"
" WHERE QstCod=%ld",
Question->QstCod);
2020-05-13 12:53:27 +02:00
/***** Check if number of rows is correct *****/
if (Question->Answer.NumOptions != 2)
Err_WrongAnswerExit ();
2020-05-13 12:53:27 +02:00
/***** Get float range *****/
for (NumOpt = 0;
NumOpt < 2;
NumOpt++)
{
row = mysql_fetch_row (mysql_res);
Question->Answer.FloatingPoint[NumOpt] = Str_GetDoubleFromStr (row[0]);
}
if (Question->Answer.FloatingPoint[0] >
Question->Answer.FloatingPoint[1]) // The maximum and the minimum are swapped
{
/* Swap maximum and minimum */
Tmp = Question->Answer.FloatingPoint[0];
Question->Answer.FloatingPoint[0] = Question->Answer.FloatingPoint[1];
Question->Answer.FloatingPoint[1] = Tmp;
}
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
static void ExaPrn_GetCorrectTF_AnswerFromDB (struct Tst_Question *Question)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
/***** Query database *****/
Question->Answer.NumOptions = (unsigned)
DB_QuerySELECT (&mysql_res,"can not get answers of a question",
"SELECT Answer" // row[0]
" FROM exa_set_answers"
" WHERE QstCod=%ld",
Question->QstCod);
2020-05-13 12:53:27 +02:00
/***** Check if number of rows is correct *****/
Tst_CheckIfNumberOfAnswersIsOne (Question);
/***** Get answer *****/
row = mysql_fetch_row (mysql_res);
Question->Answer.TF = row[0][0];
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
static void ExaPrn_GetCorrectChoAnswerFromDB (struct Tst_Question *Question)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumOpt;
/***** Query database *****/
Question->Answer.NumOptions = (unsigned)
DB_QuerySELECT (&mysql_res,"can not get answers of a question",
"SELECT Correct" // row[0]
" FROM exa_set_answers"
" WHERE QstCod=%ld"
" ORDER BY AnsInd",
Question->QstCod);
2020-05-13 12:53:27 +02:00
for (NumOpt = 0;
NumOpt < Question->Answer.NumOptions;
NumOpt++)
{
/* Get next answer */
row = mysql_fetch_row (mysql_res);
/* Assign correctness (row[0]) of this answer (this option) */
Question->Answer.Options[NumOpt].Correct = (row[0][0] == 'Y');
}
/* Free structure that stores the query result */
DB_FreeMySQLResult (&mysql_res);
}
static void ExaPrn_GetCorrectTxtAnswerFromDB (struct Tst_Question *Question)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumOpt;
/***** Query database *****/
Question->Answer.NumOptions = (unsigned)
DB_QuerySELECT (&mysql_res,"can not get answers of a question",
"SELECT Answer" // row[0]
" FROM exa_set_answers"
" WHERE QstCod=%ld",
Question->QstCod);
2020-05-13 12:53:27 +02:00
/***** Get text and correctness of answers for this question from database (one row per answer) *****/
for (NumOpt = 0;
NumOpt < Question->Answer.NumOptions;
NumOpt++)
{
/***** Get next answer *****/
row = mysql_fetch_row (mysql_res);
/***** Allocate memory for text in this choice answer *****/
if (!Tst_AllocateTextChoiceAnswer (Question,NumOpt))
/* Abort on error */
Ale_ShowAlertsAndExit ();
2020-06-17 02:31:42 +02:00
/***** Copy answer text (row[0]) ******/
2020-05-13 12:53:27 +02:00
Str_Copy (Question->Answer.Options[NumOpt].Text,row[0],
Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
2020-05-13 12:53:27 +02:00
}
2020-06-17 02:31:42 +02:00
/***** Change format of answers text *****/
Tst_ChangeFormatAnswersText (Question);
2020-05-13 12:53:27 +02:00
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
2020-05-12 13:24:43 +02:00
/*****************************************************************************/
/************* Get the questions of an exam print from database **************/
/*****************************************************************************/
static void ExaPrn_GetAnswerFromDB (struct ExaPrn_Print *Print,long QstCod,
char StrAnswers[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1])
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
/***** Get questions of an exam print from database *****/
if (DB_QuerySELECT (&mysql_res,"can not get answer in an exam print",
"SELECT Answers" // row[0]
" FROM exa_print_questions"
" WHERE PrnCod=%ld"
" AND QstCod=%ld",
2020-05-12 13:24:43 +02:00
Print->PrnCod,QstCod))
{
row = mysql_fetch_row (mysql_res);
/* Get answers selected by user for this question (row[0]) */
Str_Copy (StrAnswers,row[0],strlen (StrAnswers) - 1);
2020-05-12 13:24:43 +02:00
}
else
StrAnswers[0] = '\0';
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
2020-05-11 02:28:38 +02:00
/*****************************************************************************/
/************* Store user's answers of an test exam into database ************/
/*****************************************************************************/
static void ExaPrn_StoreOneQstOfPrintInDB (const struct ExaPrn_Print *Print,
unsigned QstInd)
2020-05-11 02:28:38 +02:00
{
/***** Insert question and user's answers into database *****/
Str_SetDecimalPointToUS (); // To print the floating point as a dot
DB_QueryREPLACE ("can not update a question in an exam print",
"REPLACE INTO exa_print_questions"
" (PrnCod,QstCod,QstInd,SetCod,Score,Indexes,Answers)"
" VALUES"
" (%ld,%ld,%u,%ld,'%.15lg','%s','%s')",
Print->PrnCod,
Print->PrintedQuestions[QstInd].QstCod,
QstInd, // 0, 1, 2, 3...
Print->PrintedQuestions[QstInd].SetCod,
Print->PrintedQuestions[QstInd].Score,
Print->PrintedQuestions[QstInd].StrIndexes,
Print->PrintedQuestions[QstInd].StrAnswers);
2020-05-11 02:28:38 +02:00
Str_SetDecimalPointToLocal (); // Return to local system
}
2020-05-14 01:41:20 +02:00
/*****************************************************************************/
/************ Get number of questions not blank in an exam print *************/
/*****************************************************************************/
static void ExaPrn_GetNumQstsNotBlank (struct ExaPrn_Print *Print)
{
/***** Count number of questions not blank in exam print in database *****/
2020-06-18 20:06:17 +02:00
Print->NumQsts.NotBlank = (unsigned)
DB_QueryCOUNT ("can not get number of questions not blank",
"SELECT COUNT(*)"
" FROM exa_print_questions"
" WHERE PrnCod=%ld"
" AND Answers<>''",
Print->PrnCod);
2020-05-14 01:41:20 +02:00
}
/*****************************************************************************/
/***************** Compute score of questions of an exam print ***************/
/*****************************************************************************/
static void ExaPrn_ComputeTotalScoreOfPrint (struct ExaPrn_Print *Print)
{
/***** Compute total score of exam print *****/
Print->Score.All = DB_QuerySELECTDouble ("can not get score of exam print",
"SELECT SUM(Score)"
" FROM exa_print_questions"
" WHERE PrnCod=%ld",
Print->PrnCod);
2020-05-14 01:41:20 +02:00
}
2020-05-11 02:28:38 +02:00
/*****************************************************************************/
/********************** Update exam print in database ************************/
/*****************************************************************************/
static void ExaPrn_UpdatePrintInDB (const struct ExaPrn_Print *Print)
{
/***** Update exam print in database *****/
Str_SetDecimalPointToUS (); // To print the floating point as a dot
DB_QueryUPDATE ("can not update exam print",
"UPDATE exa_prints"
" SET EndTime=NOW(),"
"NumQstsNotBlank=%u,"
"Sent='%c',"
"Score='%.15lg'"
" WHERE PrnCod=%ld"
" AND SesCod=%ld"
" AND UsrCod=%ld", // Extra checks
2020-06-18 20:06:17 +02:00
Print->NumQsts.NotBlank,
2020-05-11 02:28:38 +02:00
Print->Sent ? 'Y' :
'N',
Print->Score,
Print->PrnCod,
Print->SesCod,
Gbl.Usrs.Me.UsrDat.UsrCod);
2020-05-11 02:28:38 +02:00
Str_SetDecimalPointToLocal (); // Return to local system
2020-05-09 21:07:50 +02:00
}
2020-05-10 15:23:11 +02:00
/*****************************************************************************/
2020-05-18 22:59:07 +02:00
/********************** Remove exam prints made by a user ********************/
2020-05-10 15:23:11 +02:00
/*****************************************************************************/
2020-05-18 22:59:07 +02:00
void ExaPrn_RemovePrintsMadeByUsrInAllCrss (long UsrCod)
2020-05-10 15:23:11 +02:00
{
2020-05-18 22:59:07 +02:00
/***** Remove exam prints questions for the given user *****/
DB_QueryDELETE ("can not remove exam prints made by a user",
"DELETE FROM exa_print_questions"
" USING exa_prints,"
"exa_print_questions"
2020-05-18 22:59:07 +02:00
" WHERE exa_prints.UsrCod=%ld"
" AND exa_prints.PrnCod=exa_print_questions.PrnCod",
2020-05-18 22:59:07 +02:00
UsrCod);
/***** Remove exam prints made by the given user *****/
DB_QueryDELETE ("can not remove exam prints made by a user",
"DELETE FROM exa_prints"
" WHERE UsrCod=%ld",
UsrCod);
2020-05-10 15:23:11 +02:00
}
2020-05-09 21:07:50 +02:00
/*****************************************************************************/
2020-05-18 22:59:07 +02:00
/*************** Remove exam prints made by a user in a course ***************/
2020-05-09 21:07:50 +02:00
/*****************************************************************************/
2020-05-18 22:59:07 +02:00
void ExaPrn_RemovePrintsMadeByUsrInCrs (long UsrCod,long CrsCod)
{
/***** Remove questions of exams prints made by the given user in the given course *****/
DB_QueryDELETE ("can not remove exams prints made by a user in a course",
"DELETE FROM exa_print_questions"
" USING exa_exams,"
"exa_sessions,"
"exa_prints,"
"exa_print_questions"
2020-05-20 02:59:49 +02:00
" WHERE exa_exams.CrsCod=%ld"
" AND exa_exams.ExaCod=exa_sessions.ExaCod"
" AND exa_sessions.SesCod=exa_prints.SesCod"
" AND exa_prints.UsrCod=%ld"
" AND exa_prints.PrnCod=exa_print_questions.PrnCod",
CrsCod,
UsrCod);
2020-05-18 22:59:07 +02:00
/***** Remove exams prints made by the given user in the given course *****/
DB_QueryDELETE ("can not remove exams prints made by a user in a course",
"DELETE FROM exa_prints"
" USING exa_exams,"
"exa_sessions,"
"exa_prints"
2020-05-20 02:59:49 +02:00
" WHERE exa_exams.CrsCod=%ld"
" AND exa_exams.ExaCod=exa_sessions.ExaCod"
" AND exa_sessions.SesCod=exa_prints.SesCod"
" AND exa_prints.UsrCod=%ld",
CrsCod,
UsrCod);
2020-05-18 22:59:07 +02:00
}
2020-05-09 21:07:50 +02:00
/*****************************************************************************/
2020-05-18 22:59:07 +02:00
/****************** Remove all exams prints made in a course *****************/
2020-05-09 21:07:50 +02:00
/*****************************************************************************/
2020-05-18 22:59:07 +02:00
void ExaPrn_RemoveCrsPrints (long CrsCod)
{
2020-05-20 02:59:49 +02:00
/***** Remove questions of exams prints made by the given user in the given course *****/
DB_QueryDELETE ("can not remove exams prints in a course",
2020-05-18 22:59:07 +02:00
"DELETE FROM exa_print_questions"
" USING exa_exams,"
"exa_sessions,"
"exa_prints,"
"exa_print_questions"
2020-05-20 02:59:49 +02:00
" WHERE exa_exams.CrsCod=%ld"
" AND exa_exams.ExaCod=exa_sessions.ExaCod"
" AND exa_sessions.SesCod=exa_prints.SesCod"
" AND exa_prints.PrnCod=exa_print_questions.PrnCod",
2020-05-18 22:59:07 +02:00
CrsCod);
2020-05-20 02:59:49 +02:00
/***** Remove exams prints made by the given user in the given course *****/
DB_QueryDELETE ("can not remove exams prints in a course",
"DELETE FROM exa_prints"
" USING exa_exams,"
"exa_sessions,"
"exa_prints"
2020-05-20 02:59:49 +02:00
" WHERE exa_exams.CrsCod=%ld"
" AND exa_exams.ExaCod=exa_sessions.ExaCod"
" AND exa_sessions.SesCod=exa_prints.SesCod",
2020-05-18 22:59:07 +02:00
CrsCod);
}