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