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,
Answer TEXT NOT NULL,
Feedback TEXT NOT NULL,
Image VARCHAR(43) NOT NULL,
Correct ENUM('N','Y') NOT NULL,
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,
Shuffle ENUM('N','Y') NOT NULL,
Stem TEXT NOT NULL,
Image CHAR(43) NOT NULL,
Feedback TEXT NOT NULL,
Image VARCHAR(43) NOT NULL,
NumHits INT NOT NULL DEFAULT 0,
NumHitsNotBlank INT 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: Reply to all
// TODO: Hour in exam announcement should start at six a.m.
// TODO: Forum SWAD should be always named "SWAD"?
// TODO: Enable chat for guests?
// 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: Insert "http://" to WWW when WWW does not start with "*://"
// 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: 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: 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: 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: Fix bug in marks reported by Francisco Ocaña
// 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 *****************************/
/*****************************************************************************/
#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 JS_FILE "swad15.131.3.js"
// 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
/*
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.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.

View File

@ -2297,15 +2297,17 @@ mysql> DESCRIBE tst_answers;
| AnsInd | tinyint(4) | NO | | NULL | |
| Answer | text | NO | | NULL | |
| Feedback | text | NO | | NULL | |
| Image | varchar(43) | 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 ("
"QstCod INT NOT NULL,"
"AnsInd TINYINT NOT NULL,"
"Answer TEXT NOT NULL,"
"Feedback TEXT NOT NULL,"
"Image VARCHAR(43) NOT NULL,"
"Correct ENUM('N','Y') NOT NULL,"
"INDEX(QstCod))");
@ -2418,13 +2420,13 @@ mysql> DESCRIBE tst_questions;
| AnsType | enum('int','float','true_false','unique_choice','multiple_choice','text') | NO | | NULL | |
| Shuffle | enum('N','Y') | NO | | NULL | |
| Stem | text | NO | | NULL | |
| Image | char(43) | NO | | NULL | |
| Feedback | text | NO | | NULL | |
| Image | varchar(43) | NO | | NULL | |
| NumHits | int(11) | NO | | 0 | |
| NumHitsNotBlank | int(11) | 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 ("
"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,"
"Shuffle ENUM('N','Y') NOT NULL,"
"Stem TEXT NOT NULL,"
"Image CHAR(43) NOT NULL,"
"Feedback TEXT NOT NULL,"
"Image VARCHAR(43) NOT NULL,"
"NumHits INT NOT NULL DEFAULT 0,"
"NumHitsNotBlank INT 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);
/***** Show image *****/
fprintf (Gbl.F.Out,"<img src=\"%s\" alt=\"\" class=\"%s\"/>"
"<br />",
fprintf (Gbl.F.Out,"<div>"
"<img src=\"%s\" alt=\"\" class=\"%s\"/>"
"</div>",
URL,ClassImg);
}

View File

@ -216,11 +216,15 @@ static int Tst_CountNumTagsInList (void);
static int Tst_CountNumAnswerTypesInList (void);
static void Tst_PutFormEditOneQst (char *Stem,char *Feedback);
static void Tst_InitImagesOfQuestion (void);
static void Tst_GetQstDataFromDB (char *Stem,char *Feedback);
static void Tst_GetImageNameFromDB (unsigned NumOpt,char *ImageName);
static Tst_AnswerType_t Tst_ConvertFromUnsignedStrToAnsTyp (const char *UnsignedStr);
static void Tst_GetQstFromForm (char *Stem,char *Feedback);
static void Tst_MoveImagesToDefinitiveDirectories (void);
static long Tst_GetTagCodFromTagTxt (const char *TagTxt);
static long Tst_CreateNewTag (long CrsCod,const char *TagTxt);
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_RemTagsFromQst (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 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>");
/* 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\">",
Gbl.RowEvenOdd);
Tst_WriteQstStem (row[4],"TEST_EDI");
@ -2834,7 +2842,7 @@ unsigned Tst_GetAnswersQst (long QstCod,MYSQL_RES **mysql_res,bool Shuffle)
unsigned long NumRows;
/***** 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"
" WHERE QstCod='%ld' ORDER BY %s",
QstCod,
@ -2929,9 +2937,18 @@ static void Tst_WriteAnswersOfAQstEdit (long QstCod)
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 */
fprintf (Gbl.F.Out,"<tr>"
"<td class=\"BT%u\">",Gbl.RowEvenOdd);
"<td class=\"BT%u\">",
Gbl.RowEvenOdd);
if (Str_ConvertToUpperLetter (row[2][0]) == 'Y')
fprintf (Gbl.F.Out,"<img src=\"%s/ok_on16x16.gif\""
" alt=\"%s\" title=\"%s\""
@ -2947,17 +2964,21 @@ static void Tst_WriteAnswersOfAQstEdit (long QstCod)
"</td>",
'a' + (char) NumOpt);
/* Write the text of the answer */
fprintf (Gbl.F.Out,"<td class=\"TEST_EDI LEFT_TOP\">"
"%s"
"</td>",
/* Write the text of the answer and the image (row[4]) */
fprintf (Gbl.F.Out,"<td class=\"LEFT_TOP\">"
"<div class=\"TEST_EDI\">"
"%s",
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 */
fprintf (Gbl.F.Out,"<td class=\"TEST_EDI_LIGHT LEFT_TOP\">");
fprintf (Gbl.F.Out,"<div class=\"TEST_EDI_LIGHT\">");
if (LengthFeedback)
fprintf (Gbl.F.Out,"%s",Feedback);
fprintf (Gbl.F.Out,"</td>"
fprintf (Gbl.F.Out,"</div>"
"</td>"
"</tr>");
/* 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,
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 *****/
fprintf (Gbl.F.Out,"<tr>"
"<td class=\"LEFT_TOP\">");
@ -3231,10 +3260,12 @@ static void Tst_WriteChoiceAnsSeeExam (unsigned NumQst,long QstCod,bool Shuffle)
/***** Write the option text *****/
fprintf (Gbl.F.Out,"<td class=\"TEST_EXA LEFT_TOP\">"
"%s"
"</td>"
"</tr>",
"%s",
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 *****/
@ -3275,6 +3306,7 @@ static void Tst_WriteChoiceAnsAssessExam (unsigned NumQst,MYSQL_RES *mysql_res,
row[1] Answer
row[2] Correct
row[3] Feedback
row[4] Image
*/
for (NumOpt = 0;
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);
}
/***** 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) *****/
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 */
fprintf (Gbl.F.Out,"<td class=\"LEFT_TOP\">"
"<div class=\"TEST_EXA\">"
"%s"
"</div>",
"%s",
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.Answer.Options[Indexes[NumOpt]].Feedback)
if (Gbl.Test.Answer.Options[Indexes[NumOpt]].Feedback[0])
@ -4601,9 +4643,6 @@ void Tst_InitQst (void)
Gbl.Test.Stem.Length = 0;
Gbl.Test.Feedback.Text = NULL;
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.AnswerType = Tst_ANS_UNIQUE_CHOICE;
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].Text = 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.FloatingPoint[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';
}
/* 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');
break;
default:
@ -4840,17 +4907,8 @@ void Tst_ReceiveQst (void)
/***** Make sure that tags, text and answer are not empty *****/
if (Tst_CheckIfQstFormatIsCorrectAndCountNumOptions ())
{
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_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);
/***** Move images to definitive directories *****/
Tst_MoveImagesToDefinitiveDirectories ();
/***** Insert or update question, tags and answer in the database *****/
Tst_InsertOrUpdateQstTagsAnsIntoDB ();
@ -4860,11 +4918,8 @@ void Tst_ReceiveQst (void)
}
else // Question is wrong
{
/***** Whether an image has been received or not,
reset status and image *****/
Gbl.Test.Image.Action = Img_ACTION_NO_IMAGE;
Gbl.Test.Image.Status = Img_FILE_NONE;
Gbl.Test.Image.Name[0] = '\0';
/***** Whether images has been received or not, reset images *****/
Tst_InitImagesOfQuestion ();
/***** Put form to edit question again *****/
Tst_PutFormEditOneQst (Stem,Feedback);
@ -5242,6 +5297,51 @@ bool Tst_CheckIfQstFormatIsCorrectAndCountNumOptions (void)
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 *********************/
/*****************************************************************************/
@ -5384,8 +5484,12 @@ void Tst_RemoveQst (void)
Par_GetParToText ("OnlyThisQst",YN,1);
EditingOnlyThisQst = (Str_ConvertToUpperLetter (YN[0]) == 'Y');
/***** Remove image associated to question *****/
Tst_RemoveImageFilesFromQstsInCrs (Gbl.CurrentCrs.Crs.CrsCod,Gbl.Test.QstCod);
/***** Remove images associated to question *****/
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 answers and tags from this test question */
@ -5539,11 +5643,11 @@ static void Tst_InsertOrUpdateQstIntoDB (void)
Gbl.Test.Image.Name,
Gbl.Test.Feedback.Text ? Gbl.Test.Feedback.Text : "",
Gbl.Test.QstCod,Gbl.CurrentCrs.Crs.CrsCod);
DB_QueryUPDATE (Query,"can not update question");
/* Update image status */
if (Gbl.Test.Image.Name[0])
Gbl.Test.Image.Status = Img_NAME_STORED_IN_DB;
DB_QueryUPDATE (Query,"can not update question");
/* Remove answers and tags from this test question */
Tst_RemAnsFromQst ();
@ -5554,47 +5658,6 @@ static void Tst_InsertOrUpdateQstIntoDB (void)
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 ***********************/
/*****************************************************************************/
@ -5680,14 +5743,20 @@ static void Tst_InsertAnswersIntoDB (void)
NumOpt++)
if (Gbl.Test.Answer.Options[NumOpt].Text[0])
{
sprintf (Query,"INSERT INTO tst_answers (QstCod,AnsInd,Answer,Feedback,Correct)"
" VALUES (%ld,%u,'%s','%s','%c')",
sprintf (Query,"INSERT INTO tst_answers"
" (QstCod,AnsInd,Answer,Feedback,Image,Correct)"
" VALUES (%ld,%u,'%s','%s','%s','%c')",
Gbl.Test.QstCod,NumOpt,
Gbl.Test.Answer.Options[NumOpt].Text,
Gbl.Test.Answer.Options[NumOpt].Feedback,
Gbl.Test.Answer.Options[NumOpt].Image.Name,
Gbl.Test.Answer.Options[NumOpt].Correct ? 'Y' :
'N');
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;
default:
@ -5742,6 +5811,108 @@ static void Tst_RemoveUnusedTagsFromCurrentCrs (void)
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 *********************/
/*****************************************************************************/
@ -7361,8 +7532,11 @@ void Tst_RemoveCrsTests (long CrsCod)
/***** Remove files with images associated
to test questions in the course *****/
Tst_RemoveImageFilesFromQstsInCrs (CrsCod,
-1L); // All questions in the course
Tst_RemoveImgFilesFromAnsOfQsts (CrsCod,
-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 *****/
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_InsertOrUpdateQstTagsAnsIntoDB (void);
void Tst_RemoveImageFilesFromQstsInCrs (long CrsCod,long QstCod);
int Tst_AllocateTextChoiceAnswer (unsigned NumOpt);
void Tst_FreeTextChoiceAnswers (void);
void Tst_FreeTagsList (void);