Version19.228

This commit is contained in:
acanas 2020-05-13 12:53:27 +02:00
parent 4368244ffa
commit 4377742d58
9 changed files with 585 additions and 282 deletions

View File

@ -548,10 +548,12 @@ enscript -2 --landscape --color --file-align=2 --highlight --line-numbers -o - *
En OpenSWAD: En OpenSWAD:
ps2pdf source.ps destination.pdf 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 CSS_FILE "swad19.217.css"
#define JS_FILE "swad19.223.js" #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) Version 19.227: May 13, 2020 Cache of linked images. (303586 lines)
1 change necessary in database: 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)); 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));

View File

@ -36,6 +36,7 @@
#include "swad_date.h" #include "swad_date.h"
#include "swad_exam.h" #include "swad_exam.h"
#include "swad_exam_event.h" #include "swad_exam_event.h"
#include "swad_exam_print.h"
#include "swad_exam_result.h" #include "swad_exam_result.h"
#include "swad_exam_set.h" #include "swad_exam_set.h"
#include "swad_exam_type.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; Question.Answer.Type = Tst_ANS_UNIQUE_CHOICE;
/***** Compute score for this answer ******/ /***** Compute score for this answer ******/
TstPrn_ComputeChoiceAnsScore (&Print->PrintedQuestions[NumQst],&Question); ExaPrn_ComputeAnswerScore (&Print->PrintedQuestions[NumQst],&Question);
/***** Update total score *****/ /***** Update total score *****/
Print->Score += Print->PrintedQuestions[NumQst].Score; Print->Score += Print->PrintedQuestions[NumQst].Score;

View File

@ -36,6 +36,7 @@
#include "swad_database.h" #include "swad_database.h"
#include "swad_exam.h" #include "swad_exam.h"
#include "swad_exam_event.h" #include "swad_exam_event.h"
#include "swad_exam_print.h"
#include "swad_exam_result.h" #include "swad_exam_result.h"
#include "swad_exam_set.h" #include "swad_exam_set.h"
#include "swad_exam_type.h" #include "swad_exam_type.h"
@ -129,6 +130,28 @@ static void ExaPrn_ComputeScoresAndStoreQuestionsOfPrint (struct ExaPrn_Print *P
bool UpdateQstScore); bool UpdateQstScore);
static void ExaPrn_ComputeScoreAndStoreQuestionOfPrint (struct ExaPrn_Print *Print, static void ExaPrn_ComputeScoreAndStoreQuestionOfPrint (struct ExaPrn_Print *Print,
unsigned NumQst); 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, static void ExaPrn_GetAnswerFromDB (struct ExaPrn_Print *Print,long QstCod,
char StrAnswers[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1]); char StrAnswers[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1]);
static void ExaPrn_StoreOneQstOfPrintInDB (const struct ExaPrn_Print *Print, static void ExaPrn_StoreOneQstOfPrintInDB (const struct ExaPrn_Print *Print,
@ -1047,7 +1070,7 @@ static void ExaPrn_ComputeScoreAndStoreQuestionOfPrint (struct ExaPrn_Print *Pri
Tst_QstConstructor (&Question); Tst_QstConstructor (&Question);
Question.QstCod = Print->PrintedQuestions[NumQst].QstCod; Question.QstCod = Print->PrintedQuestions[NumQst].QstCod;
Question.Answer.Type = ExaSet_GetQstAnswerTypeFromDB (Question.QstCod); Question.Answer.Type = ExaSet_GetQstAnswerTypeFromDB (Question.QstCod);
TstPrn_ComputeAnswerScore (&Print->PrintedQuestions[NumQst],&Question); ExaPrn_ComputeAnswerScore (&Print->PrintedQuestions[NumQst],&Question);
Tst_QstDestructor (&Question); Tst_QstDestructor (&Question);
/***** If type is unique choice and the option (radio button) is checked /***** 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... 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 **************/ /************* Get the questions of an exam print from database **************/
/*****************************************************************************/ /*****************************************************************************/

View File

@ -39,6 +39,9 @@ void ExaPrn_ShowExamPrint (void);
void ExaPrn_ReceivePrintAnswer (void); void ExaPrn_ReceivePrintAnswer (void);
void ExaPrn_ComputeAnswerScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *Question);
void ExaPrn_EndPrintAnswer (void); void ExaPrn_EndPrintAnswer (void);
#endif #endif

View File

@ -1743,7 +1743,6 @@ static void ExaSet_CopyQstFromBankToExamSet (struct ExaSet_Set *Set,long QstCod)
{ {
/***** Clone media *****/ /***** Clone media *****/
CloneMedCod = Med_CloneMedia (&Question.Media); CloneMedCod = Med_CloneMedia (&Question.Media);
Ale_ShowAlert (Ale_INFO,"DEBUG: CloneMedCod = %ld",CloneMedCod);
/***** Insert question in table of questions *****/ /***** Insert question in table of questions *****/
QstCodInSet = DB_QueryINSERTandReturnCode ("can not add question to set", QstCodInSet = DB_QueryINSERTandReturnCode ("can not add question to set",

View File

@ -4015,7 +4015,7 @@ static void Mch_ComputeScore (struct TstPrn_Print *Print)
Question.Answer.Type = Tst_ANS_UNIQUE_CHOICE; Question.Answer.Type = Tst_ANS_UNIQUE_CHOICE;
/***** Compute score for this answer ******/ /***** Compute score for this answer ******/
TstPrn_ComputeChoiceAnsScore (&Print->PrintedQuestions[NumQst],&Question); TstPrn_ComputeAnswerScore (&Print->PrintedQuestions[NumQst],&Question);
/***** Update total score *****/ /***** Update total score *****/
Print->Score += Print->PrintedQuestions[NumQst].Score; Print->Score += Print->PrintedQuestions[NumQst].Score;

View File

@ -1512,48 +1512,51 @@ static void Med_ShowJPG (const struct Media *Media,
const char *ClassMedia) const char *ClassMedia)
{ {
extern const char *Txt_File_not_found; extern const char *Txt_File_not_found;
char FileNameMedia[NAME_MAX + 1]; char FileNameJPG[NAME_MAX + 1];
char TmpPubDir[PATH_MAX + 1]; char TmpPubDir[PATH_MAX + 1];
char *FullPathMediaPriv; char *FullPathJPGPriv;
char *URL; char *URL;
bool Cached;
/***** Build private path to JPG *****/ /***** Build private path to JPG *****/
snprintf (FileNameMedia,sizeof (FileNameMedia), snprintf (FileNameJPG,sizeof (FileNameJPG),
"%s.%s", "%s.%s",
Media->Name,Med_Extensions[Med_JPG]); Media->Name,Med_Extensions[Med_JPG]);
if (asprintf (&FullPathMediaPriv,"%s/%s", if (asprintf (&FullPathJPGPriv,"%s/%s",
PathMedPriv,FileNameMedia) < 0) PathMedPriv,FileNameJPG) < 0)
Lay_NotEnoughMemoryExit (); Lay_NotEnoughMemoryExit ();
/***** Check if private media file exists *****/ /***** Check if private media file exists *****/
if (Fil_CheckIfPathExists (FullPathMediaPriv)) if (Fil_CheckIfPathExists (FullPathJPGPriv))
{ {
/***** Get cached public link to private file *****/ /***** 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 /***** Create symbolic link from temporary public directory to private file
in order to gain access to it for showing/downloading *****/ in order to gain access to it for showing/downloading *****/
Brw_CreateDirDownloadTmp (); Brw_CreateDirDownloadTmp ();
Brw_CreateTmpPublicLinkToPrivateFile (FullPathMediaPriv,FileNameMedia); Brw_CreateTmpPublicLinkToPrivateFile (FullPathJPGPriv,FileNameJPG);
snprintf (TmpPubDir,sizeof (TmpPubDir), snprintf (TmpPubDir,sizeof (TmpPubDir),
"%s/%s", "%s/%s",
Gbl.FileBrowser.TmpPubDir.L,Gbl.FileBrowser.TmpPubDir.R); Gbl.FileBrowser.TmpPubDir.L,Gbl.FileBrowser.TmpPubDir.R);
Ses_AddPublicDirToCache (FullPathMediaPriv,TmpPubDir); Ses_AddPublicDirToCache (FullPathJPGPriv,TmpPubDir);
} }
/***** Show media *****/ /***** Show media *****/
if (asprintf (&URL,"%s/%s", if (asprintf (&URL,"%s/%s",
Cfg_URL_FILE_BROWSER_TMP_PUBLIC,TmpPubDir) < 0) Cfg_URL_FILE_BROWSER_TMP_PUBLIC,TmpPubDir) < 0)
Lay_NotEnoughMemoryExit (); Lay_NotEnoughMemoryExit ();
HTM_IMG (URL,FileNameMedia,Media->Title, HTM_IMG (URL,FileNameJPG,Media->Title,
"class=\"%s\" lazyload=\"on\"",ClassMedia); // Lazy load of the media "class=\"%s\" lazyload=\"on\"",ClassMedia); // Lazy load of the media
free (URL); free (URL);
} }
else else
HTM_Txt (Txt_File_not_found); 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) const char *ClassMedia)
{ {
extern const char *Txt_File_not_found; 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 *FullPathGIFPriv;
char *FullPathPNGPriv; char *FullPathPNGPriv;
char *URL; char *URL;
char *URL_GIF; char *URL_GIF;
char *URL_PNG; char *URL_PNG;
bool Cached;
/***** Build private path to animated GIF image *****/ /***** Build private path to animated GIF image *****/
snprintf (FileNameMedia,sizeof (FileNameMedia), snprintf (FileNameGIF,sizeof (FileNameGIF),
"%s.%s", "%s.%s",
Media->Name,Med_Extensions[Med_GIF]); Media->Name,Med_Extensions[Med_GIF]);
if (asprintf (&FullPathGIFPriv,"%s/%s", // The animated GIF image 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 (); Lay_NotEnoughMemoryExit ();
/***** Check if private media file exists *****/ /***** Check if private media file exists *****/
if (Fil_CheckIfPathExists (FullPathGIFPriv)) // The animated GIF image if (Fil_CheckIfPathExists (FullPathGIFPriv)) // The animated GIF image
{ {
/***** Create symbolic link from temporary public directory to private file /***** Get cached public link to private file *****/
in order to gain access to it for showing/downloading *****/ Cached = Ses_GetPublicDirFromCache (FullPathGIFPriv,TmpPubDir);
Brw_CreateDirDownloadTmp ();
Brw_CreateTmpPublicLinkToPrivateFile (FullPathGIFPriv,FileNameMedia);
/***** Create URL pointing to symbolic link *****/ if (!Cached)
if (asprintf (&URL,"%s/%s/%s", {
Cfg_URL_FILE_BROWSER_TMP_PUBLIC, /***** Create symbolic link from temporary public directory to private file
Gbl.FileBrowser.TmpPubDir.L, in order to gain access to it for showing/downloading *****/
Gbl.FileBrowser.TmpPubDir.R) < 0) Brw_CreateDirDownloadTmp ();
Lay_NotEnoughMemoryExit (); Brw_CreateTmpPublicLinkToPrivateFile (FullPathGIFPriv,FileNameGIF);
if (asprintf (&URL_GIF,"%s/%s",URL,FileNameMedia) < 0) Brw_CreateTmpPublicLinkToPrivateFile (FullPathPNGPriv,FileNamePNG);
Lay_NotEnoughMemoryExit ();
/***** Build private path to static PNG image *****/ snprintf (TmpPubDir,sizeof (TmpPubDir),
snprintf (FileNameMedia,sizeof (FileNameMedia), "%s/%s",
"%s.png", Gbl.FileBrowser.TmpPubDir.L,Gbl.FileBrowser.TmpPubDir.R);
Media->Name); Ses_AddPublicDirToCache (FullPathGIFPriv,TmpPubDir);
if (asprintf (&FullPathPNGPriv,"%s/%s", }
PathMedPriv,FileNameMedia) < 0)
/***** Create URLs pointing to symbolic links *****/
if (asprintf (&URL,"%s/%s",
Cfg_URL_FILE_BROWSER_TMP_PUBLIC,TmpPubDir) < 0)
Lay_NotEnoughMemoryExit (); 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 (); Lay_NotEnoughMemoryExit ();
/***** Check if private media file exists *****/ /***** Check if private media file exists *****/
if (Fil_CheckIfPathExists (FullPathPNGPriv)) // The static PNG image 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 *****/ /***** Show static PNG and animated GIF *****/
HTM_DIV_Begin ("class=\"MED_PLAY\"" HTM_DIV_Begin ("class=\"MED_PLAY\""
" onmouseover=\"toggleOnGIF(this,'%s');\"" " onmouseover=\"toggleOnGIF(this,'%s');\""
@ -1623,7 +1634,7 @@ static void Med_ShowGIF (const struct Media *Media,
URL_PNG); URL_PNG);
/* Image */ /* Image */
HTM_IMG (URL,FileNameMedia,Media->Title, HTM_IMG (URL,FileNamePNG,Media->Title,
"class=\"%s\" lazyload=\"on\"",ClassMedia); // Lazy load of the media "class=\"%s\" lazyload=\"on\"",ClassMedia); // Lazy load of the media
/* Overlay with GIF label */ /* Overlay with GIF label */
@ -1636,8 +1647,6 @@ static void Med_ShowGIF (const struct Media *Media,
else else
HTM_Txt (Txt_File_not_found); HTM_Txt (Txt_File_not_found);
free (FullPathPNGPriv);
/***** Free URLs *****/ /***** Free URLs *****/
free (URL_PNG); free (URL_PNG);
free (URL_GIF); free (URL_GIF);
@ -1646,6 +1655,7 @@ static void Med_ShowGIF (const struct Media *Media,
else else
HTM_Txt (Txt_File_not_found); HTM_Txt (Txt_File_not_found);
free (FullPathPNGPriv);
free (FullPathGIFPriv); free (FullPathGIFPriv);
} }
@ -1658,50 +1668,61 @@ static void Med_ShowVideo (const struct Media *Media,
const char *ClassMedia) const char *ClassMedia)
{ {
extern const char *Txt_File_not_found; extern const char *Txt_File_not_found;
char FileNameMediaPriv[NAME_MAX + 1]; char FileNameVideo[NAME_MAX + 1];
char *FullPathMediaPriv; char TmpPubDir[PATH_MAX + 1];
char URL_Video[PATH_MAX + 1]; char *FullPathVideoPriv;
char *URL;
bool Cached;
/***** Build private path to video *****/ /***** Build private path to video *****/
snprintf (FileNameMediaPriv,sizeof (FileNameMediaPriv), snprintf (FileNameVideo,sizeof (FileNameVideo),
"%s.%s", "%s.%s",
Media->Name,Med_Extensions[Media->Type]); Media->Name,Med_Extensions[Media->Type]);
if (asprintf (&FullPathMediaPriv,"%s/%s", if (asprintf (&FullPathVideoPriv,"%s/%s",
PathMedPriv,FileNameMediaPriv) < 0) PathMedPriv,FileNameVideo) < 0)
Lay_NotEnoughMemoryExit (); Lay_NotEnoughMemoryExit ();
/***** Check if private media file exists *****/ /***** Check if private media file exists *****/
if (Fil_CheckIfPathExists (FullPathMediaPriv)) if (Fil_CheckIfPathExists (FullPathVideoPriv))
{ {
/***** Create symbolic link from temporary public directory to private file /***** Get cached public link to private file *****/
in order to gain access to it for showing/downloading *****/ Cached = Ses_GetPublicDirFromCache (FullPathVideoPriv,TmpPubDir);
Brw_CreateDirDownloadTmp ();
Brw_CreateTmpPublicLinkToPrivateFile (FullPathMediaPriv,FileNameMediaPriv); 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 *****/ /***** Create URL pointing to symbolic link *****/
snprintf (URL_Video,sizeof (URL_Video), if (asprintf (&URL,"%s/%s",
"%s/%s/%s/%s", Cfg_URL_FILE_BROWSER_TMP_PUBLIC,TmpPubDir) < 0)
Cfg_URL_FILE_BROWSER_TMP_PUBLIC, Lay_NotEnoughMemoryExit ();
Gbl.FileBrowser.TmpPubDir.L,
Gbl.FileBrowser.TmpPubDir.R,
FileNameMediaPriv);
/***** Show media *****/ /***** Show media *****/
HTM_TxtF ("<video src=\"%s\"" HTM_TxtF ("<video src=\"%s/%s\""
" preload=\"metadata\" controls=\"controls\"" " preload=\"metadata\" controls=\"controls\""
" class=\"%s\"", " class=\"%s\"",
URL_Video,ClassMedia); URL,FileNameVideo,ClassMedia);
if (Media->Title) if (Media->Title)
if (Media->Title[0]) if (Media->Title[0])
HTM_TxtF (" title=\"%s\"",Media->Title); HTM_TxtF (" title=\"%s\"",Media->Title);
HTM_Txt (" lazyload=\"on\">" // Lazy load of the media HTM_Txt (" lazyload=\"on\">" // Lazy load of the media
"Your browser does not support HTML5 video." "Your browser does not support HTML5 video."
"</video>"); "</video>");
free (URL);
} }
else else
HTM_Txt (Txt_File_not_found); 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]); MediaPriv[Med_DST].Path,MediaDst.Name,Med_Extensions[MediaSrc->Type]);
/* Copy file */ /* Copy file */
Ale_ShowAlert (Ale_INFO,"DEBUG<br />"
"MediaPriv[Med_SRC].FullPath = &quot;%s&quot<br />"
"MediaPriv[Med_DST].FullPath = &quot;%s&quot",
MediaPriv[Med_SRC].FullPath,
MediaPriv[Med_DST].FullPath);
Fil_FastCopyOfFiles (MediaPriv[Med_SRC].FullPath, Fil_FastCopyOfFiles (MediaPriv[Med_SRC].FullPath,
MediaPriv[Med_DST].FullPath); MediaPriv[Med_DST].FullPath);

View File

@ -78,24 +78,27 @@ static void TstPrn_WriteQstAndAnsExam (struct UsrData *UsrDat,
struct Tst_Question *Question, struct Tst_Question *Question,
bool QuestionExists, bool QuestionExists,
unsigned Visibility); 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 static void TstPrn_GetCorrectAndComputeIntAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
bool UsrAnswers[Tst_MAX_OPTIONS_PER_QUESTION]); struct Tst_Question *Question);
static void TstPrn_ComputeTextAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion, static void TstPrn_GetCorrectAndComputeFltAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *Question); struct Tst_Question *Question);
static void TstPrn_GetCorrectTextAnswerFromDB (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, static void TstPrn_WriteAnswersExam (struct UsrData *UsrDat,
const struct TstPrn_Print *Print, 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, void TstPrn_ComputeAnswerScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
@ -404,47 +407,79 @@ void TstPrn_ComputeAnswerScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
switch (Question->Answer.Type) switch (Question->Answer.Type)
{ {
case Tst_ANS_INT: case Tst_ANS_INT:
TstPrn_ComputeIntAnsScore (PrintedQuestion,Question); TstPrn_GetCorrectAndComputeIntAnsScore (PrintedQuestion,Question); break;
break;
case Tst_ANS_FLOAT: case Tst_ANS_FLOAT:
TstPrn_ComputeFloatAnsScore (PrintedQuestion,Question); TstPrn_GetCorrectAndComputeFltAnsScore (PrintedQuestion,Question); break;
break;
case Tst_ANS_TRUE_FALSE: case Tst_ANS_TRUE_FALSE:
TstPrn_ComputeTFAnsScore (PrintedQuestion,Question); TstPrn_GetCorrectAndComputeTF_AnsScore (PrintedQuestion,Question); break;
break;
case Tst_ANS_UNIQUE_CHOICE: case Tst_ANS_UNIQUE_CHOICE:
case Tst_ANS_MULTIPLE_CHOICE: case Tst_ANS_MULTIPLE_CHOICE:
TstPrn_ComputeChoiceAnsScore (PrintedQuestion,Question); TstPrn_GetCorrectAndComputeChoAnsScore (PrintedQuestion,Question); break;
break;
case Tst_ANS_TEXT: case Tst_ANS_TEXT:
TstPrn_ComputeTextAnsScore (PrintedQuestion,Question); TstPrn_GetCorrectAndComputeTxtAnsScore (PrintedQuestion,Question); break;
break;
default: default:
break; 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, static void TstPrn_GetCorrectAndComputeIntAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *Question) struct Tst_Question *Question)
{ {
long AnswerUsr;
/***** Get the numerical value of the correct answer *****/ /***** Get the numerical value of the correct answer *****/
TstPrn_GetCorrectIntAnswerFromDB (Question); TstPrn_GetCorrectIntAnswerFromDB (Question);
/***** Compute score *****/ /***** Compute score *****/
PrintedQuestion->Score = 0.0; // Default score for blank or wrong answer TstPrn_ComputeIntAnsScore (PrintedQuestion,Question);
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;
} }
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) static void TstPrn_GetCorrectIntAnswerFromDB (struct Tst_Question *Question)
{ {
MYSQL_RES *mysql_res; MYSQL_RES *mysql_res;
@ -470,33 +505,7 @@ static void TstPrn_GetCorrectIntAnswerFromDB (struct Tst_Question *Question)
DB_FreeMySQLResult (&mysql_res); DB_FreeMySQLResult (&mysql_res);
} }
/*****************************************************************************/ static void TstPrn_GetCorrectFltAnswerFromDB (struct Tst_Question *Question)
/***************** 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)
{ {
MYSQL_RES *mysql_res; MYSQL_RES *mysql_res;
MYSQL_ROW row; MYSQL_ROW row;
@ -536,26 +545,7 @@ static void TstPrn_GetCorrectFloatAnswerFromDB (struct Tst_Question *Question)
DB_FreeMySQLResult (&mysql_res); DB_FreeMySQLResult (&mysql_res);
} }
/*****************************************************************************/ static void TstPrn_GetCorrectTF_AnswerFromDB (struct Tst_Question *Question)
/************** 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)
{ {
MYSQL_RES *mysql_res; MYSQL_RES *mysql_res;
MYSQL_ROW row; MYSQL_ROW row;
@ -579,30 +569,7 @@ static void TstPrn_GetCorrectTFAnswerFromDB (struct Tst_Question *Question)
DB_FreeMySQLResult (&mysql_res); DB_FreeMySQLResult (&mysql_res);
} }
/*****************************************************************************/ static void TstPrn_GetCorrectChoAnswerFromDB (struct Tst_Question *Question)
/************ 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)
{ {
MYSQL_RES *mysql_res; MYSQL_RES *mysql_res;
MYSQL_ROW row; MYSQL_ROW row;
@ -631,88 +598,108 @@ static void TstPrn_GetCorrectChoiceAnswerFromDB (struct Tst_Question *Question)
DB_FreeMySQLResult (&mysql_res); DB_FreeMySQLResult (&mysql_res);
} }
/*****************************************************************************/ static void TstPrn_GetCorrectTxtAnswerFromDB (struct Tst_Question *Question)
/********************* 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])
{ {
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumOpt; unsigned NumOpt;
const char *Ptr;
char StrOneIndex[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
/***** Get indexes from string *****/ /***** Query database *****/
for (NumOpt = 0, Ptr = StrIndexesOneQst; Question->Answer.NumOptions =
NumOpt < Tst_MAX_OPTIONS_PER_QUESTION && *Ptr; (unsigned) DB_QuerySELECT (&mysql_res,"can not get answers of a question",
NumOpt++) "SELECT Answer" // row[0]
{ " FROM tst_answers"
Par_GetNextStrUntilComma (&Ptr,StrOneIndex,Cns_MAX_DECIMAL_DIGITS_UINT); " WHERE QstCod=%ld",
Question->QstCod);
if (sscanf (StrOneIndex,"%u",&(Indexes[NumOpt])) != 1) /***** Get text and correctness of answers for this question from database (one row per answer) *****/
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 *****/
for (NumOpt = 0; for (NumOpt = 0;
NumOpt < Tst_MAX_OPTIONS_PER_QUESTION; NumOpt < Question->Answer.NumOptions;
NumOpt++)
UsrAnswers[NumOpt] = false;
/***** Set selected answers to true *****/
for (NumOpt = 0, Ptr = StrAnswersOneQst;
NumOpt < Tst_MAX_OPTIONS_PER_QUESTION && *Ptr;
NumOpt++) 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) /***** Allocate memory for text in this choice answer *****/
Lay_ShowErrorAndExit ("Bad user's answer."); if (!Tst_AllocateTextChoiceAnswer (Question,NumOpt))
/* Abort on error */
Ale_ShowAlertsAndExit ();
if (AnsUsr >= Tst_MAX_OPTIONS_PER_QUESTION) /***** Copy answer text (row[0]) and convert it, that is in HTML, to rigorous HTML ******/
Lay_ShowErrorAndExit ("Bad user's answer."); Str_Copy (Question->Answer.Options[NumOpt].Text,row[0],
Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
UsrAnswers[AnsUsr] = true; 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, void TstPrn_ComputeIntAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
const struct Tst_Question *Question, 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])
{ {
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 NumOpt;
unsigned NumOptTotInQst = 0; unsigned NumOptTotInQst = 0;
unsigned NumOptCorrInQst = 0; unsigned NumOptCorrInQst = 0;
unsigned NumAnsGood = 0; unsigned NumAnsGood = 0;
unsigned NumAnsBad = 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 *****/ /***** Compute the total score of this question *****/
for (NumOpt = 0; for (NumOpt = 0;
NumOpt < Question->Answer.NumOptions; NumOpt < Question->Answer.NumOptions;
@ -767,21 +754,13 @@ static void TstPrn_ComputeScoreQst (struct TstPrn_PrintedQuestion *PrintedQuesti
PrintedQuestion->Score = 0.0; PrintedQuestion->Score = 0.0;
} }
/*****************************************************************************/ void TstPrn_ComputeTxtAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
/********************* Compute score for text answer *************************/ const struct Tst_Question *Question)
/*****************************************************************************/
static void TstPrn_ComputeTextAnsScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *Question)
{ {
unsigned NumOpt; unsigned NumOpt;
char TextAnsUsr[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1]; char TextAnsUsr[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1];
char TextAnsOK[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->Score = 0.0; // Default score for blank or wrong answer
PrintedQuestion->AnswerIsNotBlank = (PrintedQuestion->StrAnswers[0] != '\0'); PrintedQuestion->AnswerIsNotBlank = (PrintedQuestion->StrAnswers[0] != '\0');
if (PrintedQuestion->AnswerIsNotBlank) // If user has answered the answer 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; unsigned NumOpt;
const char *Ptr;
char StrOneIndex[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
/***** Query database *****/ /***** Get indexes from string *****/
Question->Answer.NumOptions = for (NumOpt = 0, Ptr = StrIndexesOneQst;
(unsigned) DB_QuerySELECT (&mysql_res,"can not get answers of a question", NumOpt < Tst_MAX_OPTIONS_PER_QUESTION && *Ptr;
"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;
NumOpt++) NumOpt++)
{ {
/***** Get next answer *****/ Par_GetNextStrUntilComma (&Ptr,StrOneIndex,Cns_MAX_DECIMAL_DIGITS_UINT);
row = mysql_fetch_row (mysql_res);
/***** Allocate memory for text in this choice answer *****/ if (sscanf (StrOneIndex,"%u",&(Indexes[NumOpt])) != 1)
if (!Tst_AllocateTextChoiceAnswer (Question,NumOpt)) Lay_ShowErrorAndExit ("Wrong index of answer.");
/* Abort on error */
Ale_ShowAlertsAndExit ();
/***** Copy answer text (row[0]) and convert it, that is in HTML, to rigorous HTML ******/ if (Indexes[NumOpt] >= Tst_MAX_OPTIONS_PER_QUESTION)
Str_Copy (Question->Answer.Options[NumOpt].Text,row[0], Lay_ShowErrorAndExit ("Wrong index of answer.");
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 *****/ /***** Initialize remaining to 0 *****/
DB_FreeMySQLResult (&mysql_res); 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;
}
} }
/*****************************************************************************/ /*****************************************************************************/

View File

@ -76,8 +76,20 @@ void TstPrn_ComputeScoresAndStoreQuestionsOfPrint (struct TstPrn_Print *Print,
bool UpdateQstScore); bool UpdateQstScore);
void TstPrn_ComputeAnswerScore (struct TstPrn_PrintedQuestion *PrintedQuestion, void TstPrn_ComputeAnswerScore (struct TstPrn_PrintedQuestion *PrintedQuestion,
struct Tst_Question *Question); 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. 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]); unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]);
void TstPrn_GetAnswersFromStr (const char StrAnswersOneQst[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1], void TstPrn_GetAnswersFromStr (const char StrAnswersOneQst[Tst_MAX_BYTES_ANSWERS_ONE_QST + 1],