Version 15.178

This commit is contained in:
Antonio Cañas Vargas 2016-04-04 21:51:21 +02:00
parent 2b59faa4ef
commit 0d9402c11f
6 changed files with 280 additions and 101 deletions

View File

@ -1087,6 +1087,7 @@ CREATE TABLE IF NOT EXISTS tst_answers (
AnsInd TINYINT NOT NULL, AnsInd TINYINT NOT NULL,
Answer TEXT NOT NULL, Answer TEXT NOT NULL,
Feedback TEXT NOT NULL, Feedback TEXT NOT NULL,
Image VARCHAR(43) NOT NULL,
Correct ENUM('N','Y') NOT NULL, Correct ENUM('N','Y') NOT NULL,
INDEX(QstCod)); INDEX(QstCod));
-- --
@ -1143,8 +1144,8 @@ CREATE TABLE IF NOT EXISTS tst_questions (
AnsType ENUM ('int','float','true_false','unique_choice','multiple_choice','text') NOT NULL, AnsType ENUM ('int','float','true_false','unique_choice','multiple_choice','text') NOT NULL,
Shuffle ENUM('N','Y') NOT NULL, Shuffle ENUM('N','Y') NOT NULL,
Stem TEXT NOT NULL, Stem TEXT NOT NULL,
Image CHAR(43) NOT NULL,
Feedback TEXT NOT NULL, Feedback TEXT NOT NULL,
Image VARCHAR(43) NOT NULL,
NumHits INT NOT NULL DEFAULT 0, NumHits INT NOT NULL DEFAULT 0,
NumHitsNotBlank INT NOT NULL DEFAULT 0, NumHitsNotBlank INT NOT NULL DEFAULT 0,
Score DOUBLE PRECISION NOT NULL DEFAULT 0, Score DOUBLE PRECISION NOT NULL DEFAULT 0,

View File

@ -108,7 +108,6 @@
// TODO: When page is refreshed in course works, prevent users to be duplicated // TODO: When page is refreshed in course works, prevent users to be duplicated
// TODO: Reply to all // TODO: Reply to all
// TODO: Hour in exam announcement should start at six a.m. // TODO: Hour in exam announcement should start at six a.m.
// TODO: Forum SWAD should be always named "SWAD"? // TODO: Forum SWAD should be always named "SWAD"?
// TODO: Enable chat for guests? // TODO: Enable chat for guests?
// TODO: Go to forum post (or at least to forum thread) from social timeline and notifications? // TODO: Go to forum post (or at least to forum thread) from social timeline and notifications?
@ -118,34 +117,38 @@
// TODO: FIX BUG: In results of search of students, no mark of confirmation is shown even if the student really has confirmed his/her registration in the course // TODO: FIX BUG: In results of search of students, no mark of confirmation is shown even if the student really has confirmed his/her registration in the course
// TODO: Insert "http://" to WWW when WWW does not start with "*://" // TODO: Insert "http://" to WWW when WWW does not start with "*://"
// TODO: Change links from external degrees to internal degrees in STATS > Degrees // TODO: Change links from external degrees to internal degrees in STATS > Degrees
// TODO: Icon to the left in list of forums is not correct when scope is system // TODO: Icon to the left in list of forums is not correct when scope is system
// TODO: Move info about number of files to bottom of file browsers // TODO: Move info about number of files to bottom of file browsers
// TODO: To avoid wrong email addresses, when a user fills his/her email address, check if the domain is in the white list of allowed domains. If not, ask for confirmation. // TODO: To avoid wrong email addresses, when a user fills his/her email address, check if the domain is in the white list of allowed domains. If not, ask for confirmation.
// TODO: Filtering email addresses --> an email address can not finish in "." // TODO: Filtering email addresses --> an email address can not finish in "."
// TODO: Upload an image in social posts, in test questions, in forum posts, in private messages, etc. // TODO: Upload an image in social posts, in test questions, in forum posts, in private messages, etc.
// TODO: Important!!!! E-mail should not be visible for not logged users // TODO: Important!!!! E-mail should not be visible for not logged users
// TODO: Do not show e-mails of administrators and teachers in lists openly // TODO: Do not show e-mails of administrators and teachers in lists openly
// TODO: Fix bug in marks reported by Francisco Ocaña // TODO: Fix bug in marks reported by Francisco Ocaña
// TODO: In Statistics > Degrees, show only degrees with students // TODO: In Statistics > Degrees, show only degrees with students
// TODO: Change layout of confirm / reject incription. Use green and red buttons
// TODO: Ask for confirmation when removing a test question? // TODO: Ask for confirmation when removing a test question
/*****************************************************************************/ /*****************************************************************************/
/****************************** Public constants *****************************/ /****************************** Public constants *****************************/
/*****************************************************************************/ /*****************************************************************************/
#define Log_PLATFORM_VERSION "SWAD 15.177 (2016-04-04)" #define Log_PLATFORM_VERSION "SWAD 15.178 (2016-04-04)"
#define CSS_FILE "swad15.175.10.css" #define CSS_FILE "swad15.175.10.css"
#define JS_FILE "swad15.131.3.js" #define JS_FILE "swad15.131.3.js"
// Number of lines (includes comments but not blank lines) has been got with the following command: // Number of lines (includes comments but not blank lines) has been got with the following command:
// nl swad*.c swad*.h css/swad*.css py/swad*.py js/swad*.js soap/swad*.h sql/swad*.sql | tail -1 // nl swad*.c swad*.h css/swad*.css py/swad*.py js/swad*.js soap/swad*.h sql/swad*.sql | tail -1
/* /*
Version 15.178: Apr 04, 2016 Code refactoring related to images in test questions. (198244 lines)
5 changes necessary in database:
ALTER TABLE tst_questions CHANGE COLUMN Image ImageOld CHAR(43) NOT NULL;
ALTER TABLE tst_questions ADD COLUMN Image VARCHAR(43) NOT NULL AFTER Feedback;
UPDATE tst_questions SET Image=ImageOld;
ALTER TABLE tst_questions DROP COLUMN ImageOld;
ALTER TABLE tst_answers ADD COLUMN Image VARCHAR(43) NOT NULL AFTER Feedback;
Version 15.177: Apr 04, 2016 Code refactoring related to images. (198083 lines) Version 15.177: Apr 04, 2016 Code refactoring related to images. (198083 lines)
Version 15.176: Apr 04, 2016 Code refactoring related to images. (198019 lines) Version 15.176: Apr 04, 2016 Code refactoring related to images. (198019 lines)
Version 15.175.11:Apr 04, 2016 Code refactoring related to image associated to a test question. Version 15.175.11:Apr 04, 2016 Code refactoring related to image associated to a test question.

View File

@ -2297,15 +2297,17 @@ mysql> DESCRIBE tst_answers;
| AnsInd | tinyint(4) | NO | | NULL | | | AnsInd | tinyint(4) | NO | | NULL | |
| Answer | text | NO | | NULL | | | Answer | text | NO | | NULL | |
| Feedback | text | NO | | NULL | | | Feedback | text | NO | | NULL | |
| Image | varchar(43) | NO | | NULL | |
| Correct | enum('N','Y') | NO | | NULL | | | Correct | enum('N','Y') | NO | | NULL | |
+----------+---------------+------+-----+---------+-------+ +----------+---------------+------+-----+---------+-------+
5 rows in set (0.00 sec) 6 rows in set (0.00 sec)
*/ */
DB_CreateTable ("CREATE TABLE IF NOT EXISTS tst_answers (" DB_CreateTable ("CREATE TABLE IF NOT EXISTS tst_answers ("
"QstCod INT NOT NULL," "QstCod INT NOT NULL,"
"AnsInd TINYINT NOT NULL," "AnsInd TINYINT NOT NULL,"
"Answer TEXT NOT NULL," "Answer TEXT NOT NULL,"
"Feedback TEXT NOT NULL," "Feedback TEXT NOT NULL,"
"Image VARCHAR(43) NOT NULL,"
"Correct ENUM('N','Y') NOT NULL," "Correct ENUM('N','Y') NOT NULL,"
"INDEX(QstCod))"); "INDEX(QstCod))");
@ -2418,13 +2420,13 @@ mysql> DESCRIBE tst_questions;
| AnsType | enum('int','float','true_false','unique_choice','multiple_choice','text') | NO | | NULL | | | AnsType | enum('int','float','true_false','unique_choice','multiple_choice','text') | NO | | NULL | |
| Shuffle | enum('N','Y') | NO | | NULL | | | Shuffle | enum('N','Y') | NO | | NULL | |
| Stem | text | NO | | NULL | | | Stem | text | NO | | NULL | |
| Image | char(43) | NO | | NULL | |
| Feedback | text | NO | | NULL | | | Feedback | text | NO | | NULL | |
| Image | varchar(43) | NO | | NULL | |
| NumHits | int(11) | NO | | 0 | | | NumHits | int(11) | NO | | 0 | |
| NumHitsNotBlank | int(11) | NO | | 0 | | | NumHitsNotBlank | int(11) | NO | | 0 | |
| Score | double | NO | | 0 | | | Score | double | NO | | 0 | |
+-----------------+---------------------------------------------------------------------------+------+-----+---------+----------------+ +-----------------+---------------------------------------------------------------------------+------+-----+---------+----------------+
11 rows in set (0.01 sec) 11 rows in set (0.00 sec)
*/ */
DB_CreateTable ("CREATE TABLE IF NOT EXISTS tst_questions (" DB_CreateTable ("CREATE TABLE IF NOT EXISTS tst_questions ("
"QstCod INT NOT NULL AUTO_INCREMENT," "QstCod INT NOT NULL AUTO_INCREMENT,"
@ -2433,8 +2435,8 @@ mysql> DESCRIBE tst_questions;
"AnsType ENUM ('int','float','true_false','unique_choice','multiple_choice','text') NOT NULL," "AnsType ENUM ('int','float','true_false','unique_choice','multiple_choice','text') NOT NULL,"
"Shuffle ENUM('N','Y') NOT NULL," "Shuffle ENUM('N','Y') NOT NULL,"
"Stem TEXT NOT NULL," "Stem TEXT NOT NULL,"
"Image CHAR(43) NOT NULL,"
"Feedback TEXT NOT NULL," "Feedback TEXT NOT NULL,"
"Image VARCHAR(43) NOT NULL,"
"NumHits INT NOT NULL DEFAULT 0," "NumHits INT NOT NULL DEFAULT 0,"
"NumHitsNotBlank INT NOT NULL DEFAULT 0," "NumHitsNotBlank INT NOT NULL DEFAULT 0,"
"Score DOUBLE PRECISION NOT NULL DEFAULT 0," "Score DOUBLE PRECISION NOT NULL DEFAULT 0,"

View File

@ -321,8 +321,9 @@ void Img_ShowImage (struct Image *Image,const char *ClassImg)
FileNameImgPriv); FileNameImgPriv);
/***** Show image *****/ /***** Show image *****/
fprintf (Gbl.F.Out,"<img src=\"%s\" alt=\"\" class=\"%s\"/>" fprintf (Gbl.F.Out,"<div>"
"<br />", "<img src=\"%s\" alt=\"\" class=\"%s\"/>"
"</div>",
URL,ClassImg); URL,ClassImg);
} }

View File

@ -216,11 +216,15 @@ static int Tst_CountNumTagsInList (void);
static int Tst_CountNumAnswerTypesInList (void); static int Tst_CountNumAnswerTypesInList (void);
static void Tst_PutFormEditOneQst (char *Stem,char *Feedback); static void Tst_PutFormEditOneQst (char *Stem,char *Feedback);
static void Tst_InitImagesOfQuestion (void);
static void Tst_GetQstDataFromDB (char *Stem,char *Feedback); static void Tst_GetQstDataFromDB (char *Stem,char *Feedback);
static void Tst_GetImageNameFromDB (unsigned NumOpt,char *ImageName); static void Tst_GetImageNameFromDB (unsigned NumOpt,char *ImageName);
static Tst_AnswerType_t Tst_ConvertFromUnsignedStrToAnsTyp (const char *UnsignedStr); static Tst_AnswerType_t Tst_ConvertFromUnsignedStrToAnsTyp (const char *UnsignedStr);
static void Tst_GetQstFromForm (char *Stem,char *Feedback); static void Tst_GetQstFromForm (char *Stem,char *Feedback);
static void Tst_MoveImagesToDefinitiveDirectories (void);
static long Tst_GetTagCodFromTagTxt (const char *TagTxt); static long Tst_GetTagCodFromTagTxt (const char *TagTxt);
static long Tst_CreateNewTag (long CrsCod,const char *TagTxt); static long Tst_CreateNewTag (long CrsCod,const char *TagTxt);
static void Tst_EnableOrDisableTag (long TagCod,bool TagHidden); static void Tst_EnableOrDisableTag (long TagCod,bool TagHidden);
@ -233,6 +237,10 @@ static void Tst_InsertAnswersIntoDB (void);
static void Tst_RemAnsFromQst (void); static void Tst_RemAnsFromQst (void);
static void Tst_RemTagsFromQst (void); static void Tst_RemTagsFromQst (void);
static void Tst_RemoveUnusedTagsFromCurrentCrs (void); static void Tst_RemoveUnusedTagsFromCurrentCrs (void);
static void Tst_RemoveImgFilesFromStemOfQsts (long CrsCod,long QstCod);
static void Tst_RemoveImgFilesFromAnsOfQsts (long CrsCod,long QstCod,unsigned AnsInd);
static void Tst_FreeTextChoiceAnswer (unsigned NumOpt); static void Tst_FreeTextChoiceAnswer (unsigned NumOpt);
static unsigned Tst_GetNumTstQuestions (Sco_Scope_t Scope,Tst_AnswerType_t AnsType,struct Tst_Stats *Stats); static unsigned Tst_GetNumTstQuestions (Sco_Scope_t Scope,Tst_AnswerType_t AnsType,struct Tst_Stats *Stats);
@ -2728,7 +2736,7 @@ static void Tst_ListOneOrMoreQuestionsToEdit (unsigned long NumRows,MYSQL_RES *m
} }
fprintf (Gbl.F.Out,"</td>"); fprintf (Gbl.F.Out,"</td>");
/* Write the stem (row[4]), the feedback (row[6]) and the answers */ /* Write the stem (row[4]), the image (row[5],vthe feedback (row[6]) and the answers */
fprintf (Gbl.F.Out,"<td class=\"LEFT_TOP COLOR%u\">", fprintf (Gbl.F.Out,"<td class=\"LEFT_TOP COLOR%u\">",
Gbl.RowEvenOdd); Gbl.RowEvenOdd);
Tst_WriteQstStem (row[4],"TEST_EDI"); Tst_WriteQstStem (row[4],"TEST_EDI");
@ -2834,7 +2842,7 @@ unsigned Tst_GetAnswersQst (long QstCod,MYSQL_RES **mysql_res,bool Shuffle)
unsigned long NumRows; unsigned long NumRows;
/***** Get answers of a question from database *****/ /***** Get answers of a question from database *****/
sprintf (Query,"SELECT AnsInd,Answer,Correct,Feedback" sprintf (Query,"SELECT AnsInd,Answer,Correct,Feedback,Image"
" FROM tst_answers" " FROM tst_answers"
" WHERE QstCod='%ld' ORDER BY %s", " WHERE QstCod='%ld' ORDER BY %s",
QstCod, QstCod,
@ -2929,9 +2937,18 @@ static void Tst_WriteAnswersOfAQstEdit (long QstCod)
Feedback,LengthFeedback,false); Feedback,LengthFeedback,false);
} }
/* Copy image */
if (row[4][0])
{
Gbl.Test.Answer.Options[NumOpt].Image.Status = Img_NAME_STORED_IN_DB;
strncpy (Gbl.Test.Answer.Options[NumOpt].Image.Name,row[4],Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64);
Gbl.Test.Answer.Options[NumOpt].Image.Name[Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64] = '\0';
}
/* Put an icon that indicates whether the answer is correct or wrong */ /* Put an icon that indicates whether the answer is correct or wrong */
fprintf (Gbl.F.Out,"<tr>" fprintf (Gbl.F.Out,"<tr>"
"<td class=\"BT%u\">",Gbl.RowEvenOdd); "<td class=\"BT%u\">",
Gbl.RowEvenOdd);
if (Str_ConvertToUpperLetter (row[2][0]) == 'Y') if (Str_ConvertToUpperLetter (row[2][0]) == 'Y')
fprintf (Gbl.F.Out,"<img src=\"%s/ok_on16x16.gif\"" fprintf (Gbl.F.Out,"<img src=\"%s/ok_on16x16.gif\""
" alt=\"%s\" title=\"%s\"" " alt=\"%s\" title=\"%s\""
@ -2947,17 +2964,21 @@ static void Tst_WriteAnswersOfAQstEdit (long QstCod)
"</td>", "</td>",
'a' + (char) NumOpt); 'a' + (char) NumOpt);
/* Write the text of the answer */ /* Write the text of the answer and the image (row[4]) */
fprintf (Gbl.F.Out,"<td class=\"TEST_EDI LEFT_TOP\">" fprintf (Gbl.F.Out,"<td class=\"LEFT_TOP\">"
"%s" "<div class=\"TEST_EDI\">"
"</td>", "%s",
Answer); Answer);
if (Gbl.Test.Answer.Options[NumOpt].Image.Name[0])
Img_ShowImage (&Gbl.Test.Answer.Options[NumOpt].Image,"TEST_IMG_EDIT_LIST");
fprintf (Gbl.F.Out,"</div>");
/* Write the text of the feedback */ /* Write the text of the feedback */
fprintf (Gbl.F.Out,"<td class=\"TEST_EDI_LIGHT LEFT_TOP\">"); fprintf (Gbl.F.Out,"<div class=\"TEST_EDI_LIGHT\">");
if (LengthFeedback) if (LengthFeedback)
fprintf (Gbl.F.Out,"%s",Feedback); fprintf (Gbl.F.Out,"%s",Feedback);
fprintf (Gbl.F.Out,"</td>" fprintf (Gbl.F.Out,"</div>"
"</td>"
"</tr>"); "</tr>");
/* Free memory allocated for the answer and the feedback */ /* Free memory allocated for the answer and the feedback */
@ -3212,6 +3233,14 @@ static void Tst_WriteChoiceAnsSeeExam (unsigned NumQst,long QstCod,bool Shuffle)
Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML, Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
Gbl.Test.Answer.Options[NumOpt].Text,Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false); Gbl.Test.Answer.Options[NumOpt].Text,Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false);
/***** Copy image *****/
if (row[4][0])
{
Gbl.Test.Answer.Options[NumOpt].Image.Status = Img_NAME_STORED_IN_DB;
strncpy (Gbl.Test.Answer.Options[NumOpt].Image.Name,row[4],Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64);
Gbl.Test.Answer.Options[NumOpt].Image.Name[Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64] = '\0';
}
/***** Write selectors and letter of this option *****/ /***** Write selectors and letter of this option *****/
fprintf (Gbl.F.Out,"<tr>" fprintf (Gbl.F.Out,"<tr>"
"<td class=\"LEFT_TOP\">"); "<td class=\"LEFT_TOP\">");
@ -3231,10 +3260,12 @@ static void Tst_WriteChoiceAnsSeeExam (unsigned NumQst,long QstCod,bool Shuffle)
/***** Write the option text *****/ /***** Write the option text *****/
fprintf (Gbl.F.Out,"<td class=\"TEST_EXA LEFT_TOP\">" fprintf (Gbl.F.Out,"<td class=\"TEST_EXA LEFT_TOP\">"
"%s" "%s",
"</td>"
"</tr>",
Gbl.Test.Answer.Options[NumOpt].Text); Gbl.Test.Answer.Options[NumOpt].Text);
if (Gbl.Test.Answer.Options[NumOpt].Image.Name[0])
Img_ShowImage (&Gbl.Test.Answer.Options[NumOpt].Image,"TEST_IMG_SHOW");
fprintf (Gbl.F.Out,"</td>"
"</tr>");
} }
/***** End of table *****/ /***** End of table *****/
@ -3275,6 +3306,7 @@ static void Tst_WriteChoiceAnsAssessExam (unsigned NumQst,MYSQL_RES *mysql_res,
row[1] Answer row[1] Answer
row[2] Correct row[2] Correct
row[3] Feedback row[3] Feedback
row[4] Image
*/ */
for (NumOpt = 0; for (NumOpt = 0;
NumOpt < Gbl.Test.Answer.NumOptions; NumOpt < Gbl.Test.Answer.NumOptions;
@ -3306,6 +3338,14 @@ static void Tst_WriteChoiceAnsAssessExam (unsigned NumQst,MYSQL_RES *mysql_res,
Gbl.Test.Answer.Options[NumOpt].Feedback,Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false); Gbl.Test.Answer.Options[NumOpt].Feedback,Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false);
} }
/***** Copy image *****/
if (row[4][0])
{
Gbl.Test.Answer.Options[NumOpt].Image.Status = Img_NAME_STORED_IN_DB;
strncpy (Gbl.Test.Answer.Options[NumOpt].Image.Name,row[4],Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64);
Gbl.Test.Answer.Options[NumOpt].Image.Name[Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64] = '\0';
}
/***** Assign correctness (row[2]) of this answer (this option) *****/ /***** Assign correctness (row[2]) of this answer (this option) *****/
Gbl.Test.Answer.Options[NumOpt].Correct = (Str_ConvertToUpperLetter (row[2][0]) == 'Y'); Gbl.Test.Answer.Options[NumOpt].Correct = (Str_ConvertToUpperLetter (row[2][0]) == 'Y');
} }
@ -3394,9 +3434,11 @@ static void Tst_WriteChoiceAnsAssessExam (unsigned NumQst,MYSQL_RES *mysql_res,
/* Answer text and feedback */ /* Answer text and feedback */
fprintf (Gbl.F.Out,"<td class=\"LEFT_TOP\">" fprintf (Gbl.F.Out,"<td class=\"LEFT_TOP\">"
"<div class=\"TEST_EXA\">" "<div class=\"TEST_EXA\">"
"%s" "%s",
"</div>",
Gbl.Test.Answer.Options[Indexes[NumOpt]].Text); Gbl.Test.Answer.Options[Indexes[NumOpt]].Text);
if (Gbl.Test.Answer.Options[Indexes[NumOpt]].Image.Name[0])
Img_ShowImage (&Gbl.Test.Answer.Options[Indexes[NumOpt]].Image,"TEST_IMG_SHOW");
fprintf (Gbl.F.Out,"</div>");
if (Gbl.Test.Config.FeedbackType == Tst_FEEDBACK_FULL_FEEDBACK) if (Gbl.Test.Config.FeedbackType == Tst_FEEDBACK_FULL_FEEDBACK)
if (Gbl.Test.Answer.Options[Indexes[NumOpt]].Feedback) if (Gbl.Test.Answer.Options[Indexes[NumOpt]].Feedback)
if (Gbl.Test.Answer.Options[Indexes[NumOpt]].Feedback[0]) if (Gbl.Test.Answer.Options[Indexes[NumOpt]].Feedback[0])
@ -4601,9 +4643,6 @@ void Tst_InitQst (void)
Gbl.Test.Stem.Length = 0; Gbl.Test.Stem.Length = 0;
Gbl.Test.Feedback.Text = NULL; Gbl.Test.Feedback.Text = NULL;
Gbl.Test.Feedback.Length = 0; Gbl.Test.Feedback.Length = 0;
Gbl.Test.Image.Action = Img_ACTION_NO_IMAGE;
Gbl.Test.Image.Status = Img_FILE_NONE;
Gbl.Test.Image.Name[0] = '\0';
Gbl.Test.Shuffle = false; Gbl.Test.Shuffle = false;
Gbl.Test.AnswerType = Tst_ANS_UNIQUE_CHOICE; Gbl.Test.AnswerType = Tst_ANS_UNIQUE_CHOICE;
Gbl.Test.Answer.NumOptions = 0; Gbl.Test.Answer.NumOptions = 0;
@ -4615,13 +4654,33 @@ void Tst_InitQst (void)
Gbl.Test.Answer.Options[NumOpt].Correct = false; Gbl.Test.Answer.Options[NumOpt].Correct = false;
Gbl.Test.Answer.Options[NumOpt].Text = NULL; Gbl.Test.Answer.Options[NumOpt].Text = NULL;
Gbl.Test.Answer.Options[NumOpt].Feedback = NULL; Gbl.Test.Answer.Options[NumOpt].Feedback = NULL;
Gbl.Test.Answer.Options[NumOpt].Image.Action = Img_ACTION_NO_IMAGE;
Gbl.Test.Answer.Options[NumOpt].Image.Status = Img_FILE_NONE;
Gbl.Test.Answer.Options[NumOpt].Image.Name[0] = '\0';
} }
Gbl.Test.Answer.Integer = 0; Gbl.Test.Answer.Integer = 0;
Gbl.Test.Answer.FloatingPoint[0] = Gbl.Test.Answer.FloatingPoint[0] =
Gbl.Test.Answer.FloatingPoint[1] = 0.0; Gbl.Test.Answer.FloatingPoint[1] = 0.0;
Tst_InitImagesOfQuestion ();
}
/*****************************************************************************/
/***************** Initialize images of a question to zero *******************/
/*****************************************************************************/
static void Tst_InitImagesOfQuestion (void)
{
unsigned NumOpt;
Gbl.Test.Image.Action = Img_ACTION_NO_IMAGE;
Gbl.Test.Image.Status = Img_FILE_NONE;
Gbl.Test.Image.Name[0] = '\0';
for (NumOpt = 0;
NumOpt < Tst_MAX_OPTIONS_PER_QUESTION;
NumOpt++)
{
Gbl.Test.Answer.Options[NumOpt].Image.Action = Img_ACTION_NO_IMAGE;
Gbl.Test.Answer.Options[NumOpt].Image.Status = Img_FILE_NONE;
Gbl.Test.Answer.Options[NumOpt].Image.Name[0] = '\0';
}
} }
/*****************************************************************************/ /*****************************************************************************/
@ -4736,6 +4795,14 @@ static void Tst_GetQstDataFromDB (char *Stem,char *Feedback)
Gbl.Test.Answer.Options[NumOpt].Feedback[Tst_MAX_BYTES_ANSWER_OR_FEEDBACK] = '\0'; Gbl.Test.Answer.Options[NumOpt].Feedback[Tst_MAX_BYTES_ANSWER_OR_FEEDBACK] = '\0';
} }
/* Copy image */
if (row[4][0])
{
Gbl.Test.Answer.Options[NumOpt].Image.Status = Img_NAME_STORED_IN_DB;
strncpy (Gbl.Test.Answer.Options[NumOpt].Image.Name,row[4],Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64);
Gbl.Test.Answer.Options[NumOpt].Image.Name[Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64] = '\0';
}
Gbl.Test.Answer.Options[NumOpt].Correct = (Str_ConvertToUpperLetter (row[2][0]) == 'Y'); Gbl.Test.Answer.Options[NumOpt].Correct = (Str_ConvertToUpperLetter (row[2][0]) == 'Y');
break; break;
default: default:
@ -4840,17 +4907,8 @@ void Tst_ReceiveQst (void)
/***** Make sure that tags, text and answer are not empty *****/ /***** Make sure that tags, text and answer are not empty *****/
if (Tst_CheckIfQstFormatIsCorrectAndCountNumOptions ()) if (Tst_CheckIfQstFormatIsCorrectAndCountNumOptions ())
{ {
if (Gbl.Test.Image.Action != Img_ACTION_KEEP_IMAGE) // Don't keep the current image /***** Move images to definitive directories *****/
/* Remove possible file with the old image Tst_MoveImagesToDefinitiveDirectories ();
(the new image file is already processed
and moved to the definitive directory) */
Tst_RemoveImageFilesFromQstsInCrs (Gbl.CurrentCrs.Crs.CrsCod,Gbl.Test.QstCod);
if ((Gbl.Test.Image.Action == Img_ACTION_NEW_IMAGE || // Upload new image
Gbl.Test.Image.Action == Img_ACTION_CHANGE_IMAGE) && // Replace existing image by new image
Gbl.Test.Image.Status == Img_FILE_PROCESSED) // The new image received has been processed
/* Move processed image to definitive directory */
Img_MoveImageToDefinitiveDirectory (&Gbl.Test.Image);
/***** Insert or update question, tags and answer in the database *****/ /***** Insert or update question, tags and answer in the database *****/
Tst_InsertOrUpdateQstTagsAnsIntoDB (); Tst_InsertOrUpdateQstTagsAnsIntoDB ();
@ -4860,11 +4918,8 @@ void Tst_ReceiveQst (void)
} }
else // Question is wrong else // Question is wrong
{ {
/***** Whether an image has been received or not, /***** Whether images has been received or not, reset images *****/
reset status and image *****/ Tst_InitImagesOfQuestion ();
Gbl.Test.Image.Action = Img_ACTION_NO_IMAGE;
Gbl.Test.Image.Status = Img_FILE_NONE;
Gbl.Test.Image.Name[0] = '\0';
/***** Put form to edit question again *****/ /***** Put form to edit question again *****/
Tst_PutFormEditOneQst (Stem,Feedback); Tst_PutFormEditOneQst (Stem,Feedback);
@ -5242,6 +5297,51 @@ bool Tst_CheckIfQstFormatIsCorrectAndCountNumOptions (void)
return true; // Question format without errors return true; // Question format without errors
} }
/*****************************************************************************/
/* Move images associates to a test question to their definitive directories */
/*****************************************************************************/
static void Tst_MoveImagesToDefinitiveDirectories (void)
{
unsigned NumOpt;
/****** Move image associated to question stem *****/
if (Gbl.Test.Image.Action != Img_ACTION_KEEP_IMAGE) // Don't keep the current image
/* Remove possible file with the old image
(the new image file is already processed
and moved to the definitive directory) */
Tst_RemoveImgFilesFromStemOfQsts (Gbl.CurrentCrs.Crs.CrsCod,
Gbl.Test.QstCod);
if ((Gbl.Test.Image.Action == Img_ACTION_NEW_IMAGE || // Upload new image
Gbl.Test.Image.Action == Img_ACTION_CHANGE_IMAGE) && // Replace existing image by new image
Gbl.Test.Image.Status == Img_FILE_PROCESSED) // The new image received has been processed
/* Move processed image to definitive directory */
Img_MoveImageToDefinitiveDirectory (&Gbl.Test.Image);
/****** Move images associated to answers *****/
if (Gbl.Test.AnswerType == Tst_ANS_UNIQUE_CHOICE ||
Gbl.Test.AnswerType == Tst_ANS_MULTIPLE_CHOICE)
for (NumOpt = 0;
NumOpt < Gbl.Test.Answer.NumOptions;
NumOpt++)
{
if (Gbl.Test.Answer.Options[NumOpt].Image.Action != Img_ACTION_KEEP_IMAGE) // Don't keep the current image
/* Remove possible file with the old image
(the new image file is already processed
and moved to the definitive directory) */
Tst_RemoveImgFilesFromAnsOfQsts (Gbl.CurrentCrs.Crs.CrsCod,
Gbl.Test.QstCod,
NumOpt);
if ((Gbl.Test.Answer.Options[NumOpt].Image.Action == Img_ACTION_NEW_IMAGE || // Upload new image
Gbl.Test.Answer.Options[NumOpt].Image.Action == Img_ACTION_CHANGE_IMAGE) && // Replace existing image by new image
Gbl.Test.Answer.Options[NumOpt].Image.Status == Img_FILE_PROCESSED) // The new image received has been processed
/* Move processed image to definitive directory */
Img_MoveImageToDefinitiveDirectory (&Gbl.Test.Answer.Options[NumOpt].Image);
}
}
/*****************************************************************************/ /*****************************************************************************/
/******************** Get a integer number from a string *********************/ /******************** Get a integer number from a string *********************/
/*****************************************************************************/ /*****************************************************************************/
@ -5384,8 +5484,12 @@ void Tst_RemoveQst (void)
Par_GetParToText ("OnlyThisQst",YN,1); Par_GetParToText ("OnlyThisQst",YN,1);
EditingOnlyThisQst = (Str_ConvertToUpperLetter (YN[0]) == 'Y'); EditingOnlyThisQst = (Str_ConvertToUpperLetter (YN[0]) == 'Y');
/***** Remove image associated to question *****/ /***** Remove images associated to question *****/
Tst_RemoveImageFilesFromQstsInCrs (Gbl.CurrentCrs.Crs.CrsCod,Gbl.Test.QstCod); Tst_RemoveImgFilesFromAnsOfQsts (Gbl.CurrentCrs.Crs.CrsCod,
Gbl.Test.QstCod,
Tst_MAX_OPTIONS_PER_QUESTION); // All answers
Tst_RemoveImgFilesFromStemOfQsts (Gbl.CurrentCrs.Crs.CrsCod,
Gbl.Test.QstCod);
/***** Remove the question from all the tables *****/ /***** Remove the question from all the tables *****/
/* Remove answers and tags from this test question */ /* Remove answers and tags from this test question */
@ -5539,11 +5643,11 @@ static void Tst_InsertOrUpdateQstIntoDB (void)
Gbl.Test.Image.Name, Gbl.Test.Image.Name,
Gbl.Test.Feedback.Text ? Gbl.Test.Feedback.Text : "", Gbl.Test.Feedback.Text ? Gbl.Test.Feedback.Text : "",
Gbl.Test.QstCod,Gbl.CurrentCrs.Crs.CrsCod); Gbl.Test.QstCod,Gbl.CurrentCrs.Crs.CrsCod);
DB_QueryUPDATE (Query,"can not update question");
/* Update image status */ /* Update image status */
if (Gbl.Test.Image.Name[0]) if (Gbl.Test.Image.Name[0])
Gbl.Test.Image.Status = Img_NAME_STORED_IN_DB; Gbl.Test.Image.Status = Img_NAME_STORED_IN_DB;
DB_QueryUPDATE (Query,"can not update question");
/* Remove answers and tags from this test question */ /* Remove answers and tags from this test question */
Tst_RemAnsFromQst (); Tst_RemAnsFromQst ();
@ -5554,47 +5658,6 @@ static void Tst_InsertOrUpdateQstIntoDB (void)
free ((void *) Query); free ((void *) Query);
} }
/*****************************************************************************/
/******** Remove one or more image files associated to test questions ********/
/*****************************************************************************/
// Use question code <= 0 to remove all images associated to questions in course
void Tst_RemoveImageFilesFromQstsInCrs (long CrsCod,
long QstCod) // <= 0 ==> all questions in course
{
char Query[256];
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumImages;
unsigned NumImg;
/***** Get names of images associated to test questions from database *****/
if (QstCod > 0) // Only one question
sprintf (Query,"SELECT Image FROM tst_questions"
" WHERE QstCod='%ld' AND CrsCod='%ld'",
QstCod,CrsCod);
else // All questions in the course
sprintf (Query,"SELECT Image FROM tst_questions"
" WHERE CrsCod='%ld'",
CrsCod);
NumImages = (unsigned) DB_QuerySELECT (Query,&mysql_res,"can not get test images");
/***** Go over result removing image files *****/
for (NumImg = 0;
NumImg < NumImages;
NumImg++)
{
/***** Get image name (row[0]) *****/
row = mysql_fetch_row (mysql_res);
/***** Remove image file *****/
Img_RemoveImageFile (row[0]);
}
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
/*****************************************************************************/ /*****************************************************************************/
/*********************** Insert tags in the tags table ***********************/ /*********************** Insert tags in the tags table ***********************/
/*****************************************************************************/ /*****************************************************************************/
@ -5680,14 +5743,20 @@ static void Tst_InsertAnswersIntoDB (void)
NumOpt++) NumOpt++)
if (Gbl.Test.Answer.Options[NumOpt].Text[0]) if (Gbl.Test.Answer.Options[NumOpt].Text[0])
{ {
sprintf (Query,"INSERT INTO tst_answers (QstCod,AnsInd,Answer,Feedback,Correct)" sprintf (Query,"INSERT INTO tst_answers"
" VALUES (%ld,%u,'%s','%s','%c')", " (QstCod,AnsInd,Answer,Feedback,Image,Correct)"
" VALUES (%ld,%u,'%s','%s','%s','%c')",
Gbl.Test.QstCod,NumOpt, Gbl.Test.QstCod,NumOpt,
Gbl.Test.Answer.Options[NumOpt].Text, Gbl.Test.Answer.Options[NumOpt].Text,
Gbl.Test.Answer.Options[NumOpt].Feedback, Gbl.Test.Answer.Options[NumOpt].Feedback,
Gbl.Test.Answer.Options[NumOpt].Image.Name,
Gbl.Test.Answer.Options[NumOpt].Correct ? 'Y' : Gbl.Test.Answer.Options[NumOpt].Correct ? 'Y' :
'N'); 'N');
DB_QueryINSERT (Query,"can not create answer"); DB_QueryINSERT (Query,"can not create answer");
/* Update image status */
if (Gbl.Test.Answer.Options[NumOpt].Image.Name[0])
Gbl.Test.Answer.Options[NumOpt].Image.Status = Img_NAME_STORED_IN_DB;
} }
break; break;
default: default:
@ -5742,6 +5811,108 @@ static void Tst_RemoveUnusedTagsFromCurrentCrs (void)
DB_QueryDELETE (Query,"can not remove unused tags"); DB_QueryDELETE (Query,"can not remove unused tags");
} }
/*****************************************************************************/
/**** Remove one or more image files associated to stems of test questions ***/
/*****************************************************************************/
// Use question code <= 0 to remove all images associated to stems of questions in course
static void Tst_RemoveImgFilesFromStemOfQsts (long CrsCod,
long QstCod) // <= 0 ==> all questions in course
{
char Query[256];
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumImages;
unsigned NumImg;
/***** Get names of images associated to stems of test questions from database *****/
if (QstCod > 0) // Only one question
sprintf (Query,"SELECT Image FROM tst_questions"
" WHERE QstCod='%ld' AND CrsCod='%ld'",
QstCod,CrsCod);
else // All questions in the course
sprintf (Query,"SELECT Image FROM tst_questions"
" WHERE CrsCod='%ld'",
CrsCod);
NumImages = (unsigned) DB_QuerySELECT (Query,&mysql_res,"can not get test images");
/***** Go over result removing image files *****/
for (NumImg = 0;
NumImg < NumImages;
NumImg++)
{
/***** Get image name (row[0]) *****/
row = mysql_fetch_row (mysql_res);
/***** Remove image file *****/
Img_RemoveImageFile (row[0]);
}
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
/*****************************************************************************/
/** Remove one or more image files associated to answers of test questions ***/
/*****************************************************************************/
// Use question code <= 0 to remove all images associated to answers of questions in course
// Use AnsInd == Tst_MAX_OPTIONS_PER_QUESTION to remove all images associated to answers of a question
static void Tst_RemoveImgFilesFromAnsOfQsts (long CrsCod,
long QstCod, // <= 0 ==> all questions in course
unsigned AnsInd) // == Tst_MAX_OPTIONS_PER_QUESTION ==> all answers of a question
{
char Query[512];
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumImages;
unsigned NumImg;
/***** Get names of images associated to answers of test questions from database *****/
if (QstCod > 0) // Only one question
{
if (AnsInd < Tst_MAX_OPTIONS_PER_QUESTION) // Only one answer
sprintf (Query,"SELECT tst_answers.Image"
" FROM tst_questions,tst_answers"
" WHERE tst_questions.CrsCod='%ld'" // Extra check
" AND tst_questions.QstCod='%ld'" // Extra check
" AND tst_questions.QstCod=tst_answers.QstCod"
" AND tst_answers.QstCod='%ld'"
" AND tst_answers.AnsInd='%u'",
CrsCod,QstCod,QstCod,AnsInd);
else // All answers of a question
sprintf (Query,"SELECT tst_answers.Image"
" FROM tst_questions,tst_answers"
" WHERE tst_questions.CrsCod='%ld'" // Extra check
" AND tst_questions.QstCod='%ld'" // Extra check
" AND tst_questions.QstCod=tst_answers.QstCod"
" AND tst_answers.QstCod='%ld'",
CrsCod,QstCod,QstCod);
}
else // All answers of all questions in the course
sprintf (Query,"SELECT tst_answers.Image"
" FROM tst_questions,tst_answers"
" WHERE tst_questions.CrsCod='%ld'"
" AND tst_questions.QstCod=tst_answers.QstCod",
CrsCod);
NumImages = (unsigned) DB_QuerySELECT (Query,&mysql_res,"can not get test images");
/***** Go over result removing image files *****/
for (NumImg = 0;
NumImg < NumImages;
NumImg++)
{
/***** Get image name (row[0]) *****/
row = mysql_fetch_row (mysql_res);
/***** Remove image file *****/
Img_RemoveImageFile (row[0]);
}
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
/*****************************************************************************/ /*****************************************************************************/
/******************* Allocate memory for a choice answer *********************/ /******************* Allocate memory for a choice answer *********************/
/*****************************************************************************/ /*****************************************************************************/
@ -7361,8 +7532,11 @@ void Tst_RemoveCrsTests (long CrsCod)
/***** Remove files with images associated /***** Remove files with images associated
to test questions in the course *****/ to test questions in the course *****/
Tst_RemoveImageFilesFromQstsInCrs (CrsCod, Tst_RemoveImgFilesFromAnsOfQsts (CrsCod,
-1L); // All questions in the course -1L, // All answers of questions in the course
Tst_MAX_OPTIONS_PER_QUESTION); // does not matter
Tst_RemoveImgFilesFromStemOfQsts (CrsCod,
-1L); // All questions in the course
/***** Remove test questions in the course *****/ /***** Remove test questions in the course *****/
sprintf (Query,"DELETE FROM tst_questions WHERE CrsCod='%ld'", sprintf (Query,"DELETE FROM tst_questions WHERE CrsCod='%ld'",

View File

@ -151,8 +151,6 @@ void Tst_RemoveQst (void);
void Tst_ChangeShuffleQst (void); void Tst_ChangeShuffleQst (void);
void Tst_InsertOrUpdateQstTagsAnsIntoDB (void); void Tst_InsertOrUpdateQstTagsAnsIntoDB (void);
void Tst_RemoveImageFilesFromQstsInCrs (long CrsCod,long QstCod);
int Tst_AllocateTextChoiceAnswer (unsigned NumOpt); int Tst_AllocateTextChoiceAnswer (unsigned NumOpt);
void Tst_FreeTextChoiceAnswers (void); void Tst_FreeTextChoiceAnswers (void);
void Tst_FreeTagsList (void); void Tst_FreeTagsList (void);