diff --git a/swad_changelog.h b/swad_changelog.h
index 020b5720..964b5304 100644
--- a/swad_changelog.h
+++ b/swad_changelog.h
@@ -548,10 +548,12 @@ enscript -2 --landscape --color --file-align=2 --highlight --line-numbers -o - *
En OpenSWAD:
ps2pdf source.ps destination.pdf
*/
-#define Log_PLATFORM_VERSION "SWAD 19.227 (2020-05-13)"
+#define Log_PLATFORM_VERSION "SWAD 19.228 (2020-05-13)"
#define CSS_FILE "swad19.217.css"
#define JS_FILE "swad19.223.js"
/*
+ Version 19.228: May 13, 2020 Code refactoring and bug fixing in exam prints. (303837 lines)
+ Version 19.227.1: May 13, 2020 Cache of linked GIFs and videos. (303612 lines)
Version 19.227: May 13, 2020 Cache of linked images. (303586 lines)
1 change necessary in database:
CREATE TABLE IF NOT EXISTS file_cache (SessionId CHAR(43) NOT NULL,PrivPath TEXT COLLATE latin1_bin NOT NULL,TmpPubDir TEXT COLLATE latin1_bin NOT NULL,INDEX(SessionId));
diff --git a/swad_exam_event.c b/swad_exam_event.c
index 460c4810..b6a8bb08 100644
--- a/swad_exam_event.c
+++ b/swad_exam_event.c
@@ -36,6 +36,7 @@
#include "swad_date.h"
#include "swad_exam.h"
#include "swad_exam_event.h"
+#include "swad_exam_print.h"
#include "swad_exam_result.h"
#include "swad_exam_set.h"
#include "swad_exam_type.h"
@@ -4210,7 +4211,7 @@ static void ExaEvt_ComputeScore (struct TstPrn_Print *Print)
Question.Answer.Type = Tst_ANS_UNIQUE_CHOICE;
/***** Compute score for this answer ******/
- TstPrn_ComputeChoiceAnsScore (&Print->PrintedQuestions[NumQst],&Question);
+ ExaPrn_ComputeAnswerScore (&Print->PrintedQuestions[NumQst],&Question);
/***** Update total score *****/
Print->Score += Print->PrintedQuestions[NumQst].Score;
diff --git a/swad_exam_print.c b/swad_exam_print.c
index af58992c..1b3a48c2 100644
--- a/swad_exam_print.c
+++ b/swad_exam_print.c
@@ -36,6 +36,7 @@
#include "swad_database.h"
#include "swad_exam.h"
#include "swad_exam_event.h"
+#include "swad_exam_print.h"
#include "swad_exam_result.h"
#include "swad_exam_set.h"
#include "swad_exam_type.h"
@@ -129,6 +130,28 @@ static void ExaPrn_ComputeScoresAndStoreQuestionsOfPrint (struct ExaPrn_Print *P
bool UpdateQstScore);
static void ExaPrn_ComputeScoreAndStoreQuestionOfPrint (struct ExaPrn_Print *Print,
unsigned NumQst);
+
+//-----------------------------------------------------------------------------
+
+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);
+
+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);
+
+//-----------------------------------------------------------------------------
+
static void ExaPrn_GetAnswerFromDB (struct ExaPrn_Print *Print,long QstCod,
char StrAnswers[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1]);
static void ExaPrn_StoreOneQstOfPrintInDB (const struct ExaPrn_Print *Print,
@@ -1047,7 +1070,7 @@ static void ExaPrn_ComputeScoreAndStoreQuestionOfPrint (struct ExaPrn_Print *Pri
Tst_QstConstructor (&Question);
Question.QstCod = Print->PrintedQuestions[NumQst].QstCod;
Question.Answer.Type = ExaSet_GetQstAnswerTypeFromDB (Question.QstCod);
- TstPrn_ComputeAnswerScore (&Print->PrintedQuestions[NumQst],&Question);
+ ExaPrn_ComputeAnswerScore (&Print->PrintedQuestions[NumQst],&Question);
Tst_QstDestructor (&Question);
/***** If type is unique choice and the option (radio button) is checked
@@ -1065,6 +1088,247 @@ static void ExaPrn_ComputeScoreAndStoreQuestionOfPrint (struct ExaPrn_Print *Pri
NumQst); // 0, 1, 2, 3...
}
+/*****************************************************************************/
+/************* Write answers of a question when assessing a test *************/
+/*****************************************************************************/
+
+void ExaPrn_ComputeAnswerScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
+ struct Tst_Question *Question)
+ {
+ /***** Write answer depending on type *****/
+ switch (Question->Answer.Type)
+ {
+ case Tst_ANS_INT:
+ ExaPrn_GetCorrectAndComputeIntAnsScore (PrintedQuestion,Question); break;
+ case Tst_ANS_FLOAT:
+ ExaPrn_GetCorrectAndComputeFltAnsScore (PrintedQuestion,Question); break;
+ case Tst_ANS_TRUE_FALSE:
+ ExaPrn_GetCorrectAndComputeTF_AnsScore (PrintedQuestion,Question); break;
+ case Tst_ANS_UNIQUE_CHOICE:
+ case Tst_ANS_MULTIPLE_CHOICE:
+ ExaPrn_GetCorrectAndComputeChoAnsScore (PrintedQuestion,Question); break;
+ case Tst_ANS_TEXT:
+ ExaPrn_GetCorrectAndComputeTxtAnsScore (PrintedQuestion,Question); break;
+ default:
+ break;
+ }
+ }
+
+/*****************************************************************************/
+/******* Get correct answer and compute score for each type of answer ********/
+/*****************************************************************************/
+
+static void ExaPrn_GetCorrectAndComputeIntAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
+ struct Tst_Question *Question)
+ {
+ /***** Get the numerical value of the correct answer *****/
+ ExaPrn_GetCorrectIntAnswerFromDB (Question);
+
+ /***** Compute score *****/
+ TstPrn_ComputeIntAnsScore (PrintedQuestion,Question);
+ }
+
+static void ExaPrn_GetCorrectAndComputeFltAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
+ struct Tst_Question *Question)
+ {
+ /***** Get the numerical value of the minimum and maximum correct answers *****/
+ ExaPrn_GetCorrectFltAnswerFromDB (Question);
+
+ /***** Compute score *****/
+ TstPrn_ComputeFltAnsScore (PrintedQuestion,Question);
+ }
+
+static void ExaPrn_GetCorrectAndComputeTF_AnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
+ struct Tst_Question *Question)
+ {
+ /***** Get answer true or false *****/
+ ExaPrn_GetCorrectTF_AnswerFromDB (Question);
+
+ /***** Compute score *****/
+ TstPrn_ComputeTF_AnsScore (PrintedQuestion,Question);
+ }
+
+static void ExaPrn_GetCorrectAndComputeChoAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
+ struct Tst_Question *Question)
+ {
+ /***** Get correct options of test question from database *****/
+ ExaPrn_GetCorrectChoAnswerFromDB (Question);
+
+ /***** Compute the total score of this question *****/
+ TstPrn_ComputeChoAnsScore (PrintedQuestion,Question);
+ }
+
+static void ExaPrn_GetCorrectAndComputeTxtAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
+ struct Tst_Question *Question)
+ {
+ /***** Get correct answers for this question from database *****/
+ ExaPrn_GetCorrectTxtAnswerFromDB (Question);
+
+ /***** Compute score *****/
+ 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);
+
+ /***** 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)
+ Lay_ShowErrorAndExit ("Wrong integer answer.");
+
+ /***** 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);
+
+ /***** Check if number of rows is correct *****/
+ if (Question->Answer.NumOptions != 2)
+ Lay_ShowErrorAndExit ("Wrong float range.");
+
+ /***** 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);
+
+ /***** 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);
+ 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);
+
+ /***** 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 ();
+
+ /***** Copy answer text (row[0]) and convert it, that is in HTML, to rigorous HTML ******/
+ Str_Copy (Question->Answer.Options[NumOpt].Text,row[0],
+ Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
+ Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
+ Question->Answer.Options[NumOpt].Text,
+ Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false);
+ }
+
+ /***** Free structure that stores the query result *****/
+ DB_FreeMySQLResult (&mysql_res);
+ }
+
/*****************************************************************************/
/************* Get the questions of an exam print from database **************/
/*****************************************************************************/
diff --git a/swad_exam_print.h b/swad_exam_print.h
index ba614899..5a5f8cee 100644
--- a/swad_exam_print.h
+++ b/swad_exam_print.h
@@ -39,6 +39,9 @@ void ExaPrn_ShowExamPrint (void);
void ExaPrn_ReceivePrintAnswer (void);
+void ExaPrn_ComputeAnswerScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
+ struct Tst_Question *Question);
+
void ExaPrn_EndPrintAnswer (void);
#endif
diff --git a/swad_exam_set.c b/swad_exam_set.c
index 831531e5..326c2030 100644
--- a/swad_exam_set.c
+++ b/swad_exam_set.c
@@ -1743,7 +1743,6 @@ static void ExaSet_CopyQstFromBankToExamSet (struct ExaSet_Set *Set,long QstCod)
{
/***** Clone media *****/
CloneMedCod = Med_CloneMedia (&Question.Media);
- Ale_ShowAlert (Ale_INFO,"DEBUG: CloneMedCod = %ld",CloneMedCod);
/***** Insert question in table of questions *****/
QstCodInSet = DB_QueryINSERTandReturnCode ("can not add question to set",
diff --git a/swad_match.c b/swad_match.c
index bfb59abd..877c18da 100644
--- a/swad_match.c
+++ b/swad_match.c
@@ -4015,7 +4015,7 @@ static void Mch_ComputeScore (struct TstPrn_Print *Print)
Question.Answer.Type = Tst_ANS_UNIQUE_CHOICE;
/***** Compute score for this answer ******/
- TstPrn_ComputeChoiceAnsScore (&Print->PrintedQuestions[NumQst],&Question);
+ TstPrn_ComputeAnswerScore (&Print->PrintedQuestions[NumQst],&Question);
/***** Update total score *****/
Print->Score += Print->PrintedQuestions[NumQst].Score;
diff --git a/swad_media.c b/swad_media.c
index c78a1f4b..3363b412 100644
--- a/swad_media.c
+++ b/swad_media.c
@@ -1512,48 +1512,51 @@ static void Med_ShowJPG (const struct Media *Media,
const char *ClassMedia)
{
extern const char *Txt_File_not_found;
- char FileNameMedia[NAME_MAX + 1];
+ char FileNameJPG[NAME_MAX + 1];
char TmpPubDir[PATH_MAX + 1];
- char *FullPathMediaPriv;
+ char *FullPathJPGPriv;
char *URL;
+ bool Cached;
/***** Build private path to JPG *****/
- snprintf (FileNameMedia,sizeof (FileNameMedia),
+ snprintf (FileNameJPG,sizeof (FileNameJPG),
"%s.%s",
Media->Name,Med_Extensions[Med_JPG]);
- if (asprintf (&FullPathMediaPriv,"%s/%s",
- PathMedPriv,FileNameMedia) < 0)
+ if (asprintf (&FullPathJPGPriv,"%s/%s",
+ PathMedPriv,FileNameJPG) < 0)
Lay_NotEnoughMemoryExit ();
/***** Check if private media file exists *****/
- if (Fil_CheckIfPathExists (FullPathMediaPriv))
+ if (Fil_CheckIfPathExists (FullPathJPGPriv))
{
/***** Get cached public link to private file *****/
- if (!Ses_GetPublicDirFromCache (FullPathMediaPriv,TmpPubDir))
+ Cached = Ses_GetPublicDirFromCache (FullPathJPGPriv,TmpPubDir);
+
+ if (!Cached)
{
/***** Create symbolic link from temporary public directory to private file
in order to gain access to it for showing/downloading *****/
Brw_CreateDirDownloadTmp ();
- Brw_CreateTmpPublicLinkToPrivateFile (FullPathMediaPriv,FileNameMedia);
+ Brw_CreateTmpPublicLinkToPrivateFile (FullPathJPGPriv,FileNameJPG);
snprintf (TmpPubDir,sizeof (TmpPubDir),
"%s/%s",
Gbl.FileBrowser.TmpPubDir.L,Gbl.FileBrowser.TmpPubDir.R);
- Ses_AddPublicDirToCache (FullPathMediaPriv,TmpPubDir);
+ Ses_AddPublicDirToCache (FullPathJPGPriv,TmpPubDir);
}
/***** Show media *****/
if (asprintf (&URL,"%s/%s",
Cfg_URL_FILE_BROWSER_TMP_PUBLIC,TmpPubDir) < 0)
Lay_NotEnoughMemoryExit ();
- HTM_IMG (URL,FileNameMedia,Media->Title,
+ HTM_IMG (URL,FileNameJPG,Media->Title,
"class=\"%s\" lazyload=\"on\"",ClassMedia); // Lazy load of the media
free (URL);
}
else
HTM_Txt (Txt_File_not_found);
- free (FullPathMediaPriv);
+ free (FullPathJPGPriv);
}
/*****************************************************************************/
@@ -1565,56 +1568,64 @@ static void Med_ShowGIF (const struct Media *Media,
const char *ClassMedia)
{
extern const char *Txt_File_not_found;
- char FileNameMedia[NAME_MAX + 1];
+ char FileNameGIF[NAME_MAX + 1];
+ char FileNamePNG[NAME_MAX + 1];
+ char TmpPubDir[PATH_MAX + 1];
char *FullPathGIFPriv;
char *FullPathPNGPriv;
char *URL;
char *URL_GIF;
char *URL_PNG;
+ bool Cached;
/***** Build private path to animated GIF image *****/
- snprintf (FileNameMedia,sizeof (FileNameMedia),
+ snprintf (FileNameGIF,sizeof (FileNameGIF),
"%s.%s",
Media->Name,Med_Extensions[Med_GIF]);
if (asprintf (&FullPathGIFPriv,"%s/%s", // The animated GIF image
- PathMedPriv,FileNameMedia) < 0)
+ PathMedPriv,FileNameGIF) < 0)
+ Lay_NotEnoughMemoryExit ();
+
+ /***** Build private path to static PNG image *****/
+ snprintf (FileNamePNG,sizeof (FileNamePNG),
+ "%s.png",
+ Media->Name);
+ if (asprintf (&FullPathPNGPriv,"%s/%s",
+ PathMedPriv,FileNamePNG) < 0)
Lay_NotEnoughMemoryExit ();
/***** Check if private media file exists *****/
if (Fil_CheckIfPathExists (FullPathGIFPriv)) // The animated GIF image
{
- /***** Create symbolic link from temporary public directory to private file
- in order to gain access to it for showing/downloading *****/
- Brw_CreateDirDownloadTmp ();
- Brw_CreateTmpPublicLinkToPrivateFile (FullPathGIFPriv,FileNameMedia);
+ /***** Get cached public link to private file *****/
+ Cached = Ses_GetPublicDirFromCache (FullPathGIFPriv,TmpPubDir);
- /***** Create URL pointing to symbolic link *****/
- if (asprintf (&URL,"%s/%s/%s",
- Cfg_URL_FILE_BROWSER_TMP_PUBLIC,
- Gbl.FileBrowser.TmpPubDir.L,
- Gbl.FileBrowser.TmpPubDir.R) < 0)
- Lay_NotEnoughMemoryExit ();
- if (asprintf (&URL_GIF,"%s/%s",URL,FileNameMedia) < 0)
- Lay_NotEnoughMemoryExit ();
+ if (!Cached)
+ {
+ /***** Create symbolic link from temporary public directory to private file
+ in order to gain access to it for showing/downloading *****/
+ Brw_CreateDirDownloadTmp ();
+ Brw_CreateTmpPublicLinkToPrivateFile (FullPathGIFPriv,FileNameGIF);
+ Brw_CreateTmpPublicLinkToPrivateFile (FullPathPNGPriv,FileNamePNG);
- /***** Build private path to static PNG image *****/
- snprintf (FileNameMedia,sizeof (FileNameMedia),
- "%s.png",
- Media->Name);
- if (asprintf (&FullPathPNGPriv,"%s/%s",
- PathMedPriv,FileNameMedia) < 0)
+ snprintf (TmpPubDir,sizeof (TmpPubDir),
+ "%s/%s",
+ Gbl.FileBrowser.TmpPubDir.L,Gbl.FileBrowser.TmpPubDir.R);
+ Ses_AddPublicDirToCache (FullPathGIFPriv,TmpPubDir);
+ }
+
+ /***** Create URLs pointing to symbolic links *****/
+ if (asprintf (&URL,"%s/%s",
+ Cfg_URL_FILE_BROWSER_TMP_PUBLIC,TmpPubDir) < 0)
Lay_NotEnoughMemoryExit ();
- if (asprintf (&URL_PNG,"%s/%s",URL,FileNameMedia) < 0) // The static PNG image
+ if (asprintf (&URL_GIF,"%s/%s",URL,FileNameGIF) < 0)
+ Lay_NotEnoughMemoryExit ();
+ if (asprintf (&URL_PNG,"%s/%s",URL,FileNamePNG) < 0) // The static PNG image
Lay_NotEnoughMemoryExit ();
/***** Check if private media file exists *****/
if (Fil_CheckIfPathExists (FullPathPNGPriv)) // The static PNG image
{
- /***** Create symbolic link from temporary public directory to private file
- in order to gain access to it for showing/downloading *****/
- Brw_CreateDirDownloadTmp ();
- Brw_CreateTmpPublicLinkToPrivateFile (FullPathPNGPriv,FileNameMedia);
-
/***** Show static PNG and animated GIF *****/
HTM_DIV_Begin ("class=\"MED_PLAY\""
" onmouseover=\"toggleOnGIF(this,'%s');\""
@@ -1623,7 +1634,7 @@ static void Med_ShowGIF (const struct Media *Media,
URL_PNG);
/* Image */
- HTM_IMG (URL,FileNameMedia,Media->Title,
+ HTM_IMG (URL,FileNamePNG,Media->Title,
"class=\"%s\" lazyload=\"on\"",ClassMedia); // Lazy load of the media
/* Overlay with GIF label */
@@ -1636,8 +1647,6 @@ static void Med_ShowGIF (const struct Media *Media,
else
HTM_Txt (Txt_File_not_found);
- free (FullPathPNGPriv);
-
/***** Free URLs *****/
free (URL_PNG);
free (URL_GIF);
@@ -1646,6 +1655,7 @@ static void Med_ShowGIF (const struct Media *Media,
else
HTM_Txt (Txt_File_not_found);
+ free (FullPathPNGPriv);
free (FullPathGIFPriv);
}
@@ -1658,50 +1668,61 @@ static void Med_ShowVideo (const struct Media *Media,
const char *ClassMedia)
{
extern const char *Txt_File_not_found;
- char FileNameMediaPriv[NAME_MAX + 1];
- char *FullPathMediaPriv;
- char URL_Video[PATH_MAX + 1];
+ char FileNameVideo[NAME_MAX + 1];
+ char TmpPubDir[PATH_MAX + 1];
+ char *FullPathVideoPriv;
+ char *URL;
+ bool Cached;
/***** Build private path to video *****/
- snprintf (FileNameMediaPriv,sizeof (FileNameMediaPriv),
+ snprintf (FileNameVideo,sizeof (FileNameVideo),
"%s.%s",
Media->Name,Med_Extensions[Media->Type]);
- if (asprintf (&FullPathMediaPriv,"%s/%s",
- PathMedPriv,FileNameMediaPriv) < 0)
+ if (asprintf (&FullPathVideoPriv,"%s/%s",
+ PathMedPriv,FileNameVideo) < 0)
Lay_NotEnoughMemoryExit ();
/***** Check if private media file exists *****/
- if (Fil_CheckIfPathExists (FullPathMediaPriv))
+ if (Fil_CheckIfPathExists (FullPathVideoPriv))
{
- /***** Create symbolic link from temporary public directory to private file
- in order to gain access to it for showing/downloading *****/
- Brw_CreateDirDownloadTmp ();
- Brw_CreateTmpPublicLinkToPrivateFile (FullPathMediaPriv,FileNameMediaPriv);
+ /***** Get cached public link to private file *****/
+ Cached = Ses_GetPublicDirFromCache (FullPathVideoPriv,TmpPubDir);
+
+ if (!Cached)
+ {
+ /***** Create symbolic link from temporary public directory to private file
+ in order to gain access to it for showing/downloading *****/
+ Brw_CreateDirDownloadTmp ();
+ Brw_CreateTmpPublicLinkToPrivateFile (FullPathVideoPriv,FileNameVideo);
+
+ snprintf (TmpPubDir,sizeof (TmpPubDir),
+ "%s/%s",
+ Gbl.FileBrowser.TmpPubDir.L,Gbl.FileBrowser.TmpPubDir.R);
+ Ses_AddPublicDirToCache (FullPathVideoPriv,TmpPubDir);
+ }
/***** Create URL pointing to symbolic link *****/
- snprintf (URL_Video,sizeof (URL_Video),
- "%s/%s/%s/%s",
- Cfg_URL_FILE_BROWSER_TMP_PUBLIC,
- Gbl.FileBrowser.TmpPubDir.L,
- Gbl.FileBrowser.TmpPubDir.R,
- FileNameMediaPriv);
+ if (asprintf (&URL,"%s/%s",
+ Cfg_URL_FILE_BROWSER_TMP_PUBLIC,TmpPubDir) < 0)
+ Lay_NotEnoughMemoryExit ();
/***** Show media *****/
- HTM_TxtF ("");
+ free (URL);
}
else
HTM_Txt (Txt_File_not_found);
- free (FullPathMediaPriv);
+ free (FullPathVideoPriv);
}
/*****************************************************************************/
@@ -1905,12 +1926,6 @@ long Med_CloneMedia (const struct Media *MediaSrc)
MediaPriv[Med_DST].Path,MediaDst.Name,Med_Extensions[MediaSrc->Type]);
/* Copy file */
- Ale_ShowAlert (Ale_INFO,"DEBUG
"
- "MediaPriv[Med_SRC].FullPath = "%s"
"
- "MediaPriv[Med_DST].FullPath = "%s"",
- MediaPriv[Med_SRC].FullPath,
- MediaPriv[Med_DST].FullPath);
-
Fil_FastCopyOfFiles (MediaPriv[Med_SRC].FullPath,
MediaPriv[Med_DST].FullPath);
diff --git a/swad_test_print.c b/swad_test_print.c
index f2394a0e..efc9c9d7 100644
--- a/swad_test_print.c
+++ b/swad_test_print.c
@@ -78,24 +78,27 @@ static void TstPrn_WriteQstAndAnsExam (struct UsrData *UsrDat,
struct Tst_Question *Question,
bool QuestionExists,
unsigned Visibility);
-static void TstPrn_ComputeIntAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
- struct Tst_Question *Question);
-static void TstPrn_GetCorrectIntAnswerFromDB (struct Tst_Question *Question);
-static void TstPrn_ComputeFloatAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
- struct Tst_Question *Question);
-static void TstPrn_GetCorrectFloatAnswerFromDB (struct Tst_Question *Question);
-static void TstPrn_ComputeTFAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
- struct Tst_Question *Question);
-static void TstPrn_GetCorrectTFAnswerFromDB (struct Tst_Question *Question);
-static void TstPrn_GetCorrectChoiceAnswerFromDB (struct Tst_Question *Question);
-static void TstPrn_ComputeScoreQst (struct TstPrn_PrintedQuestion *PrintedQuestion,
- const struct Tst_Question *Question,
- unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION], // Indexes of all answers of this question
- bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION]);
-static void TstPrn_ComputeTextAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
- struct Tst_Question *Question);
-static void TstPrn_GetCorrectTextAnswerFromDB (struct Tst_Question *Question);
+//-----------------------------------------------------------------------------
+
+static void TstPrn_GetCorrectAndComputeIntAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
+ struct Tst_Question *Question);
+static void TstPrn_GetCorrectAndComputeFltAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
+ struct Tst_Question *Question);
+static void TstPrn_GetCorrectAndComputeTF_AnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
+ struct Tst_Question *Question);
+static void TstPrn_GetCorrectAndComputeChoAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
+ struct Tst_Question *Question);
+static void TstPrn_GetCorrectAndComputeTxtAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
+ struct Tst_Question *Question);
+
+static void TstPrn_GetCorrectIntAnswerFromDB (struct Tst_Question *Question);
+static void TstPrn_GetCorrectFltAnswerFromDB (struct Tst_Question *Question);
+static void TstPrn_GetCorrectTF_AnswerFromDB (struct Tst_Question *Question);
+static void TstPrn_GetCorrectChoAnswerFromDB (struct Tst_Question *Question);
+static void TstPrn_GetCorrectTxtAnswerFromDB (struct Tst_Question *Question);
+
+//-----------------------------------------------------------------------------
static void TstPrn_WriteAnswersExam (struct UsrData *UsrDat,
const struct TstPrn_Print *Print,
@@ -394,7 +397,7 @@ void TstPrn_ComputeScoresAndStoreQuestionsOfPrint (struct TstPrn_Print *Print,
}
/*****************************************************************************/
-/************* Write answers of a question when assessing a test *************/
+/******************* Get correct answer and compute score ********************/
/*****************************************************************************/
void TstPrn_ComputeAnswerScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
@@ -404,47 +407,79 @@ void TstPrn_ComputeAnswerScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
switch (Question->Answer.Type)
{
case Tst_ANS_INT:
- TstPrn_ComputeIntAnsScore (PrintedQuestion,Question);
- break;
+ TstPrn_GetCorrectAndComputeIntAnsScore (PrintedQuestion,Question); break;
case Tst_ANS_FLOAT:
- TstPrn_ComputeFloatAnsScore (PrintedQuestion,Question);
- break;
+ TstPrn_GetCorrectAndComputeFltAnsScore (PrintedQuestion,Question); break;
case Tst_ANS_TRUE_FALSE:
- TstPrn_ComputeTFAnsScore (PrintedQuestion,Question);
- break;
+ TstPrn_GetCorrectAndComputeTF_AnsScore (PrintedQuestion,Question); break;
case Tst_ANS_UNIQUE_CHOICE:
case Tst_ANS_MULTIPLE_CHOICE:
- TstPrn_ComputeChoiceAnsScore (PrintedQuestion,Question);
- break;
+ TstPrn_GetCorrectAndComputeChoAnsScore (PrintedQuestion,Question); break;
case Tst_ANS_TEXT:
- TstPrn_ComputeTextAnsScore (PrintedQuestion,Question);
- break;
+ TstPrn_GetCorrectAndComputeTxtAnsScore (PrintedQuestion,Question); break;
default:
break;
}
}
/*****************************************************************************/
-/**************** Write integer answer when assessing a test *****************/
+/******* Get correct answer and compute score for each type of answer ********/
/*****************************************************************************/
-static void TstPrn_ComputeIntAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
- struct Tst_Question *Question)
+static void TstPrn_GetCorrectAndComputeIntAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
+ struct Tst_Question *Question)
{
- long AnswerUsr;
-
/***** Get the numerical value of the correct answer *****/
TstPrn_GetCorrectIntAnswerFromDB (Question);
/***** Compute score *****/
- 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 (sscanf (PrintedQuestion->StrAnswers,"%ld",&AnswerUsr) == 1)
- if (AnswerUsr == Question->Answer.Integer) // Correct answer
- PrintedQuestion->Score = 1.0;
+ TstPrn_ComputeIntAnsScore (PrintedQuestion,Question);
}
+static void TstPrn_GetCorrectAndComputeFltAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
+ struct Tst_Question *Question)
+ {
+ /***** Get the numerical value of the minimum and maximum correct answers *****/
+ TstPrn_GetCorrectFltAnswerFromDB (Question);
+
+ /***** Compute score *****/
+ TstPrn_ComputeFltAnsScore (PrintedQuestion,Question);
+ }
+
+static void TstPrn_GetCorrectAndComputeTF_AnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
+ struct Tst_Question *Question)
+ {
+ /***** Get answer true or false *****/
+ TstPrn_GetCorrectTF_AnswerFromDB (Question);
+
+ /***** Compute score *****/
+ TstPrn_ComputeTF_AnsScore (PrintedQuestion,Question);
+ }
+
+static void TstPrn_GetCorrectAndComputeChoAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
+ struct Tst_Question *Question)
+ {
+ /***** Get correct options of test question from database *****/
+ TstPrn_GetCorrectChoAnswerFromDB (Question);
+
+ /***** Compute the total score of this question *****/
+ TstPrn_ComputeChoAnsScore (PrintedQuestion,Question);
+ }
+
+static void TstPrn_GetCorrectAndComputeTxtAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
+ struct Tst_Question *Question)
+ {
+ /***** Get correct answers for this question from database *****/
+ TstPrn_GetCorrectTxtAnswerFromDB (Question);
+
+ /***** Compute score *****/
+ TstPrn_ComputeTxtAnsScore (PrintedQuestion,Question);
+ }
+
+/*****************************************************************************/
+/**************** Get correct answer for each type of answer *****************/
+/*****************************************************************************/
+
static void TstPrn_GetCorrectIntAnswerFromDB (struct Tst_Question *Question)
{
MYSQL_RES *mysql_res;
@@ -470,33 +505,7 @@ static void TstPrn_GetCorrectIntAnswerFromDB (struct Tst_Question *Question)
DB_FreeMySQLResult (&mysql_res);
}
-/*****************************************************************************/
-/***************** Write float answer when assessing a test ******************/
-/*****************************************************************************/
-
-static void TstPrn_ComputeFloatAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
- struct Tst_Question *Question)
- {
- double AnswerUsr;
-
- /***** Get the numerical value of the minimum and maximum correct answers *****/
- TstPrn_GetCorrectFloatAnswerFromDB (Question);
-
- /***** Compute score *****/
- 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
- {
- AnswerUsr = Str_GetDoubleFromStr (PrintedQuestion->StrAnswers);
-
- // A bad formatted floating point answer will interpreted as 0.0
- PrintedQuestion->Score = (AnswerUsr >= Question->Answer.FloatingPoint[0] &&
- AnswerUsr <= Question->Answer.FloatingPoint[1]) ? 1.0 : // If correct (inside the interval)
- 0.0; // If wrong (outside the interval)
- }
- }
-
-static void TstPrn_GetCorrectFloatAnswerFromDB (struct Tst_Question *Question)
+static void TstPrn_GetCorrectFltAnswerFromDB (struct Tst_Question *Question)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
@@ -536,26 +545,7 @@ static void TstPrn_GetCorrectFloatAnswerFromDB (struct Tst_Question *Question)
DB_FreeMySQLResult (&mysql_res);
}
-/*****************************************************************************/
-/************** Write false / true answer when assessing a test **************/
-/*****************************************************************************/
-
-static void TstPrn_ComputeTFAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
- struct Tst_Question *Question)
- {
- /***** Get answer true or false *****/
- TstPrn_GetCorrectTFAnswerFromDB (Question);
-
- /***** Compute score *****/
- PrintedQuestion->AnswerIsNotBlank = (PrintedQuestion->StrAnswers[0] != '\0');
- if (PrintedQuestion->AnswerIsNotBlank) // User has selected T or F
- PrintedQuestion->Score = (PrintedQuestion->StrAnswers[0] == Question->Answer.TF) ? 1.0 : // Correct
- -1.0; // Wrong
- else
- PrintedQuestion->Score = 0.0;
- }
-
-static void TstPrn_GetCorrectTFAnswerFromDB (struct Tst_Question *Question)
+static void TstPrn_GetCorrectTF_AnswerFromDB (struct Tst_Question *Question)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
@@ -579,30 +569,7 @@ static void TstPrn_GetCorrectTFAnswerFromDB (struct Tst_Question *Question)
DB_FreeMySQLResult (&mysql_res);
}
-/*****************************************************************************/
-/************ Compute score for single or multiple choice answer *************/
-/*****************************************************************************/
-
-void TstPrn_ComputeChoiceAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
- struct Tst_Question *Question)
- {
- unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]; // Indexes of all answers of this question
- bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION];
-
- /***** Get correct options of test question from database *****/
- TstPrn_GetCorrectChoiceAnswerFromDB (Question);
-
- /***** Get indexes for this question from string *****/
- TstPrn_GetIndexesFromStr (PrintedQuestion->StrIndexes,Indexes);
-
- /***** Get the user's answers for this question from string *****/
- TstPrn_GetAnswersFromStr (PrintedQuestion->StrAnswers,UsrAnswers);
-
- /***** Compute the total score of this question *****/
- TstPrn_ComputeScoreQst (PrintedQuestion,Question,Indexes,UsrAnswers);
- }
-
-static void TstPrn_GetCorrectChoiceAnswerFromDB (struct Tst_Question *Question)
+static void TstPrn_GetCorrectChoAnswerFromDB (struct Tst_Question *Question)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
@@ -631,88 +598,108 @@ static void TstPrn_GetCorrectChoiceAnswerFromDB (struct Tst_Question *Question)
DB_FreeMySQLResult (&mysql_res);
}
-/*****************************************************************************/
-/********************* Get vector of indexes from string *********************/
-/*****************************************************************************/
-
-void TstPrn_GetIndexesFromStr (const char StrIndexesOneQst[Tst_MAX_BYTES_INDEXES_ONE_QST + 1], // 0 1 2 3, 3 0 2 1, etc.
- unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION])
+static void TstPrn_GetCorrectTxtAnswerFromDB (struct Tst_Question *Question)
{
+ MYSQL_RES *mysql_res;
+ MYSQL_ROW row;
unsigned NumOpt;
- const char *Ptr;
- char StrOneIndex[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
- /***** Get indexes from string *****/
- for (NumOpt = 0, Ptr = StrIndexesOneQst;
- NumOpt < Tst_MAX_OPTIONS_PER_QUESTION && *Ptr;
- NumOpt++)
- {
- Par_GetNextStrUntilComma (&Ptr,StrOneIndex,Cns_MAX_DECIMAL_DIGITS_UINT);
+ /***** Query database *****/
+ Question->Answer.NumOptions =
+ (unsigned) DB_QuerySELECT (&mysql_res,"can not get answers of a question",
+ "SELECT Answer" // row[0]
+ " FROM tst_answers"
+ " WHERE QstCod=%ld",
+ Question->QstCod);
- if (sscanf (StrOneIndex,"%u",&(Indexes[NumOpt])) != 1)
- Lay_ShowErrorAndExit ("Wrong index of answer.");
-
- if (Indexes[NumOpt] >= Tst_MAX_OPTIONS_PER_QUESTION)
- Lay_ShowErrorAndExit ("Wrong index of answer.");
- }
-
- /***** Initialize remaining to 0 *****/
- for (;
- NumOpt < Tst_MAX_OPTIONS_PER_QUESTION;
- NumOpt++)
- Indexes[NumOpt] = 0;
- }
-
-/*****************************************************************************/
-/****************** Get vector of user's answers from string *****************/
-/*****************************************************************************/
-
-void TstPrn_GetAnswersFromStr (const char StrAnswersOneQst[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1],
- bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION])
- {
- unsigned NumOpt;
- const char *Ptr;
- char StrOneAnswer[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
- unsigned AnsUsr;
-
- /***** Initialize all answers to false *****/
+ /***** Get text and correctness of answers for this question from database (one row per answer) *****/
for (NumOpt = 0;
- NumOpt < Tst_MAX_OPTIONS_PER_QUESTION;
- NumOpt++)
- UsrAnswers[NumOpt] = false;
-
- /***** Set selected answers to true *****/
- for (NumOpt = 0, Ptr = StrAnswersOneQst;
- NumOpt < Tst_MAX_OPTIONS_PER_QUESTION && *Ptr;
+ NumOpt < Question->Answer.NumOptions;
NumOpt++)
{
- Par_GetNextStrUntilComma (&Ptr,StrOneAnswer,Cns_MAX_DECIMAL_DIGITS_UINT);
+ /***** Get next answer *****/
+ row = mysql_fetch_row (mysql_res);
- if (sscanf (StrOneAnswer,"%u",&AnsUsr) != 1)
- Lay_ShowErrorAndExit ("Bad user's answer.");
+ /***** Allocate memory for text in this choice answer *****/
+ if (!Tst_AllocateTextChoiceAnswer (Question,NumOpt))
+ /* Abort on error */
+ Ale_ShowAlertsAndExit ();
- if (AnsUsr >= Tst_MAX_OPTIONS_PER_QUESTION)
- Lay_ShowErrorAndExit ("Bad user's answer.");
-
- UsrAnswers[AnsUsr] = true;
+ /***** Copy answer text (row[0]) and convert it, that is in HTML, to rigorous HTML ******/
+ Str_Copy (Question->Answer.Options[NumOpt].Text,row[0],
+ Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
+ Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
+ Question->Answer.Options[NumOpt].Text,
+ Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false);
}
+
+ /***** Free structure that stores the query result *****/
+ DB_FreeMySQLResult (&mysql_res);
}
/*****************************************************************************/
-/*********************** Compute the score of a question *********************/
+/************** Compute answer score for each type of answer *****************/
/*****************************************************************************/
-static void TstPrn_ComputeScoreQst (struct TstPrn_PrintedQuestion *PrintedQuestion,
- const struct Tst_Question *Question,
- unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION], // Indexes of all answers of this question
- bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION])
+void TstPrn_ComputeIntAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
+ const struct Tst_Question *Question)
{
+ 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
+ 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)
+ {
+ 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
+ {
+ AnswerUsr = Str_GetDoubleFromStr (PrintedQuestion->StrAnswers);
+
+ // A bad formatted floating point answer will interpreted as 0.0
+ PrintedQuestion->Score = (AnswerUsr >= Question->Answer.FloatingPoint[0] &&
+ AnswerUsr <= Question->Answer.FloatingPoint[1]) ? 1.0 : // If correct (inside the interval)
+ 0.0; // If wrong (outside the interval)
+ }
+ }
+
+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
+ PrintedQuestion->Score = (PrintedQuestion->StrAnswers[0] == Question->Answer.TF) ? 1.0 : // Correct
+ -1.0; // Wrong
+ else
+ PrintedQuestion->Score = 0.0;
+ }
+
+void TstPrn_ComputeChoAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
+ const struct Tst_Question *Question)
+ {
+ unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]; // Indexes of all answers of this question
+ bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION];
unsigned NumOpt;
unsigned NumOptTotInQst = 0;
unsigned NumOptCorrInQst = 0;
unsigned NumAnsGood = 0;
unsigned NumAnsBad = 0;
+ /***** Get indexes for this question from string *****/
+ TstPrn_GetIndexesFromStr (PrintedQuestion->StrIndexes,Indexes);
+
+ /***** Get the user's answers for this question from string *****/
+ TstPrn_GetAnswersFromStr (PrintedQuestion->StrAnswers,UsrAnswers);
+
/***** Compute the total score of this question *****/
for (NumOpt = 0;
NumOpt < Question->Answer.NumOptions;
@@ -767,21 +754,13 @@ static void TstPrn_ComputeScoreQst (struct TstPrn_PrintedQuestion *PrintedQuesti
PrintedQuestion->Score = 0.0;
}
-/*****************************************************************************/
-/********************* Compute score for text answer *************************/
-/*****************************************************************************/
-
-static void TstPrn_ComputeTextAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
- struct Tst_Question *Question)
+void TstPrn_ComputeTxtAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
+ const struct Tst_Question *Question)
{
unsigned NumOpt;
char TextAnsUsr[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
char TextAnsOK[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
- /***** Get correct answers for this question from database *****/
- TstPrn_GetCorrectTextAnswerFromDB (Question);
-
- /***** Compute score *****/
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
@@ -811,43 +790,71 @@ static void TstPrn_ComputeTextAnsScore (struct TstPrn_PrintedQuestion *PrintedQu
}
}
-static void TstPrn_GetCorrectTextAnswerFromDB (struct Tst_Question *Question)
+/*****************************************************************************/
+/********** Get vector of unsigned indexes from string with indexes **********/
+/*****************************************************************************/
+
+void TstPrn_GetIndexesFromStr (const char StrIndexesOneQst[Tst_MAX_BYTES_INDEXES_ONE_QST + 1], // 0 1 2 3, 3 0 2 1, etc.
+ unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION])
{
- MYSQL_RES *mysql_res;
- MYSQL_ROW row;
unsigned NumOpt;
+ const char *Ptr;
+ char StrOneIndex[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
- /***** Query database *****/
- Question->Answer.NumOptions =
- (unsigned) DB_QuerySELECT (&mysql_res,"can not get answers of a question",
- "SELECT Answer" // row[0]
- " FROM tst_answers"
- " WHERE QstCod=%ld",
- Question->QstCod);
-
- /***** Get text and correctness of answers for this question from database (one row per answer) *****/
- for (NumOpt = 0;
- NumOpt < Question->Answer.NumOptions;
+ /***** Get indexes from string *****/
+ for (NumOpt = 0, Ptr = StrIndexesOneQst;
+ NumOpt < Tst_MAX_OPTIONS_PER_QUESTION && *Ptr;
NumOpt++)
{
- /***** Get next answer *****/
- row = mysql_fetch_row (mysql_res);
+ Par_GetNextStrUntilComma (&Ptr,StrOneIndex,Cns_MAX_DECIMAL_DIGITS_UINT);
- /***** Allocate memory for text in this choice answer *****/
- if (!Tst_AllocateTextChoiceAnswer (Question,NumOpt))
- /* Abort on error */
- Ale_ShowAlertsAndExit ();
+ if (sscanf (StrOneIndex,"%u",&(Indexes[NumOpt])) != 1)
+ Lay_ShowErrorAndExit ("Wrong index of answer.");
- /***** Copy answer text (row[0]) and convert it, that is in HTML, to rigorous HTML ******/
- Str_Copy (Question->Answer.Options[NumOpt].Text,row[0],
- Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
- Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
- Question->Answer.Options[NumOpt].Text,
- Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false);
+ if (Indexes[NumOpt] >= Tst_MAX_OPTIONS_PER_QUESTION)
+ Lay_ShowErrorAndExit ("Wrong index of answer.");
}
- /***** Free structure that stores the query result *****/
- DB_FreeMySQLResult (&mysql_res);
+ /***** Initialize remaining to 0 *****/
+ for (;
+ NumOpt < Tst_MAX_OPTIONS_PER_QUESTION;
+ NumOpt++)
+ Indexes[NumOpt] = 0;
+ }
+
+/*****************************************************************************/
+/************ Get vector of bool answers from string with answers ************/
+/*****************************************************************************/
+
+void TstPrn_GetAnswersFromStr (const char StrAnswersOneQst[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1],
+ bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION])
+ {
+ unsigned NumOpt;
+ const char *Ptr;
+ char StrOneAnswer[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
+ unsigned AnsUsr;
+
+ /***** Initialize all answers to false *****/
+ for (NumOpt = 0;
+ NumOpt < Tst_MAX_OPTIONS_PER_QUESTION;
+ NumOpt++)
+ UsrAnswers[NumOpt] = false;
+
+ /***** Set selected answers to true *****/
+ for (NumOpt = 0, Ptr = StrAnswersOneQst;
+ NumOpt < Tst_MAX_OPTIONS_PER_QUESTION && *Ptr;
+ NumOpt++)
+ {
+ Par_GetNextStrUntilComma (&Ptr,StrOneAnswer,Cns_MAX_DECIMAL_DIGITS_UINT);
+
+ if (sscanf (StrOneAnswer,"%u",&AnsUsr) != 1)
+ Lay_ShowErrorAndExit ("Bad user's answer.");
+
+ if (AnsUsr >= Tst_MAX_OPTIONS_PER_QUESTION)
+ Lay_ShowErrorAndExit ("Bad user's answer.");
+
+ UsrAnswers[AnsUsr] = true;
+ }
}
/*****************************************************************************/
diff --git a/swad_test_print.h b/swad_test_print.h
index fa1fd916..86ed5e4d 100644
--- a/swad_test_print.h
+++ b/swad_test_print.h
@@ -76,8 +76,20 @@ void TstPrn_ComputeScoresAndStoreQuestionsOfPrint (struct TstPrn_Print *Print,
bool UpdateQstScore);
void TstPrn_ComputeAnswerScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *Question);
-void TstPrn_ComputeChoiceAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
- struct Tst_Question *Question);
+
+//-----------------------------------------------------------------------------
+void TstPrn_ComputeIntAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
+ const struct Tst_Question *Question);
+void TstPrn_ComputeFltAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
+ const struct Tst_Question *Question);
+void TstPrn_ComputeTF_AnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
+ const struct Tst_Question *Question);
+void TstPrn_ComputeChoAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
+ const struct Tst_Question *Question);
+void TstPrn_ComputeTxtAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
+ const struct Tst_Question *Question);
+//-----------------------------------------------------------------------------
+
void TstPrn_GetIndexesFromStr (const char StrIndexesOneQst[Tst_MAX_BYTES_INDEXES_ONE_QST + 1], // 0 1 2 3, 3 0 2 1, etc.
unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]);
void TstPrn_GetAnswersFromStr (const char StrAnswersOneQst[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1],