mirror of https://github.com/acanas/swad-core.git
Version19.228
This commit is contained in:
parent
4368244ffa
commit
4377742d58
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 **************/
|
||||
/*****************************************************************************/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
|
|
149
swad_media.c
149
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 ("<video src=\"%s\""
|
||||
HTM_TxtF ("<video src=\"%s/%s\""
|
||||
" preload=\"metadata\" controls=\"controls\""
|
||||
" class=\"%s\"",
|
||||
URL_Video,ClassMedia);
|
||||
URL,FileNameVideo,ClassMedia);
|
||||
if (Media->Title)
|
||||
if (Media->Title[0])
|
||||
HTM_TxtF (" title=\"%s\"",Media->Title);
|
||||
HTM_Txt (" lazyload=\"on\">" // Lazy load of the media
|
||||
"Your browser does not support HTML5 video."
|
||||
"</video>");
|
||||
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<br />"
|
||||
"MediaPriv[Med_SRC].FullPath = "%s"<br />"
|
||||
"MediaPriv[Med_DST].FullPath = "%s"",
|
||||
MediaPriv[Med_SRC].FullPath,
|
||||
MediaPriv[Med_DST].FullPath);
|
||||
|
||||
Fil_FastCopyOfFiles (MediaPriv[Med_SRC].FullPath,
|
||||
MediaPriv[Med_DST].FullPath);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
|
@ -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],
|
||||
|
|
Loading…
Reference in New Issue