Version 15.184

This commit is contained in:
Antonio Cañas Vargas 2016-04-06 19:26:09 +02:00
parent 72c3301506
commit 58ccf23b80
8 changed files with 282 additions and 294 deletions

View File

@ -1743,12 +1743,12 @@ a:hover img.CENTRE_PHOTO_SHOW
.TEST_EXA_LIGHT {color:#A0A0A0; font-size:12pt;}
.TEST_EDI {color:#404040; font-size:12pt;}
.TEST_EDI_LIGHT {color:#A0A0A0; font-size:12pt;}
.TEST_IMG_SHOW_STEM {width:600px; margin:10px 0;}
.TEST_IMG_SHOW_ANS {width:450px; margin:10px 0;}
.TEST_IMG_EDIT_LIST_STEM {width:300px; margin:5px 0;}
.TEST_IMG_EDIT_LIST_ANS {width:225px; margin:5px 0;}
.TEST_IMG_EDIT_ONE_STEM {width:600px;}
.TEST_IMG_EDIT_ONE_ANS {width:450px;}
.TEST_IMG_SHOW_STEM {width:600px; border-radius:4px; margin:10px 0;}
.TEST_IMG_SHOW_ANS {width:450px; border-radius:4px; margin:10px 0;}
.TEST_IMG_EDIT_LIST_STEM {width:300px; border-radius:2px; margin:5px 0;}
.TEST_IMG_EDIT_LIST_ANS {width:225px; border-radius:2px; margin:5px 0;}
.TEST_IMG_EDIT_ONE_STEM {width:600px; border-radius:4px;}
.TEST_IMG_EDIT_ONE_ANS {width:450px; border-radius:4px;}
/******************************* Time table **********************************/
#timetable

View File

@ -132,13 +132,15 @@
/****************************** Public constants *****************************/
/*****************************************************************************/
#define Log_PLATFORM_VERSION "SWAD 15.183.1 (2016-04-06)"
#define Log_PLATFORM_VERSION "SWAD 15.184 (2016-04-06)"
#define CSS_FILE "swad15.178.2.css"
#define JS_FILE "swad15.178.2.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.184: Apr 06, 2016 Code refactoring in tests and images.
Fixed bugs in test questions. (198682 lines)
Version 15.183.1: Apr 06, 2016 Fix bug in feedback of test question. (198706 lines)
Version 15.183: Apr 06, 2016 Change in length of title/attribution of images. (198704 lines)
2 changes necessary in database:

View File

@ -71,45 +71,47 @@ static void Img_ProcessImage (const char *FileNameImgOriginal,
/*************************** Reset image title *******************************/
/*****************************************************************************/
void Img_ResetImageTitle (struct Image *Image)
void Img_FreeImageTitle (struct Image *Image)
{
// Image->Title must be initialized to NULL
if (Image->Title)
{
free ((void *) Image->Title);
Image->Title = NULL;
Image->Title = NULL;
}
}
/*****************************************************************************/
/********* Get image name and title from strings and copy to struct **********/
/****** Get image name and title from a query result and copy to struct ******/
/*****************************************************************************/
void Img_GetImageNameAndTitle (const char *Name,const char *Title,
struct Image *Image)
void Img_GetImageNameAndTitleFromRow (const char *Name,const char *Title,
struct Image *Image)
{
size_t Length;
Img_ResetImageTitle (Image);
/***** Copy image name to struct *****/
strncpy (Image->Name,Name,Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64);
Image->Name[Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64] = '\0';
if (Name[0])
/***** Set status of image file *****/
Image->Status = Image->Name[0] ? Img_NAME_STORED_IN_DB :
Img_FILE_NONE;
/***** Copy image title to struct *****/
Img_FreeImageTitle (Image);
if (Title[0])
{
strncpy (Image->Name,Name,Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64);
Image->Name[Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64] = '\0';
/* Get and limit length of the title */
Length = strlen (Title);
if (Length > Img_MAX_BYTES_TITLE)
Length = Img_MAX_BYTES_TITLE;
if (Image->Name[0]) // There is an image
if (Title[0])
{
/* Get and limit length of the title */
Length = strlen (Title);
if (Length > Img_MAX_BYTES_TITLE)
Length = Img_MAX_BYTES_TITLE;
if ((Image->Title = (char *) malloc (Length+1)) == NULL)
Lay_ShowErrorAndExit ("Error allocating memory for image title.");
strncpy (Image->Title,Title,Length);
Image->Title[Length] = '\0';
}
if ((Image->Title = (char *) malloc (Length+1)) == NULL)
Lay_ShowErrorAndExit ("Error allocating memory for image title.");
strncpy (Image->Title,Title,Length);
Image->Title[Length] = '\0';
}
else // No image in this question
Image->Name[0] = '\0';
}
/*****************************************************************************/
@ -117,50 +119,60 @@ void Img_GetImageNameAndTitle (const char *Name,const char *Title,
/*****************************************************************************/
void Img_GetImageFromForm (unsigned NumOpt,struct Image *Image,
void (*GetImageNameFromDB) (unsigned NumOpt,struct Image *Image),
void (*GetImageFromDB) (unsigned NumOpt,struct Image *Image),
const char *ParamAction,const char *ParamFile,const char *ParamTitle,
unsigned Width,unsigned Height,unsigned Quality)
{
char Title[Img_MAX_BYTES_TITLE+1];
size_t Length;
Image->Action = Img_GetImageActionFromForm (ParamAction);
Image->Status = Img_FILE_NONE;
Img_FreeImageTitle (Image); // Reset to NULL
switch (Image->Action)
{
case Img_ACTION_NO_IMAGE: // Do not use image (remove current image if exists)
/***** Reset image name *****/
Image->Name[0] = '\0';
Img_ResetImageTitle (Image);
break;
case Img_ACTION_KEEP_IMAGE: // Keep current image unchanged
/***** Get image name *****/
GetImageNameFromDB (NumOpt,Image);
if (Image->Name[0])
Image->Status = Img_NAME_STORED_IN_DB;
GetImageFromDB (NumOpt,Image);
break;
case Img_ACTION_NEW_IMAGE: // Upload new image
/***** Get new image (if present ==> process and create temporary file) *****/
Img_GetAndProcessImageFileFromForm (Image,ParamFile,ParamTitle,
Img_GetAndProcessImageFileFromForm (Image,ParamFile,
Width,Height,Quality);
if (Image->Status != Img_FILE_PROCESSED) // No new image received-processed successfully
{
/* Reset image name */
Image->Status = Img_FILE_NONE;
Image->Name[0] = '\0';
Img_ResetImageTitle (Image);
}
break;
case Img_ACTION_CHANGE_IMAGE: // Replace old image by new image
/***** Get new image (if present ==> process and create temporary file) *****/
Img_GetAndProcessImageFileFromForm (Image,ParamFile,ParamTitle,
Img_GetAndProcessImageFileFromForm (Image,ParamFile,
Width,Height,Quality);
if (Image->Status != Img_FILE_PROCESSED) // No new image received-processed successfully
{
/* Get image name */
GetImageNameFromDB (NumOpt,Image);
Image->Status = (Image->Name[0] ? Img_NAME_STORED_IN_DB :
Img_FILE_NONE);
}
GetImageFromDB (NumOpt,Image);
break;
}
/***** Get image title from form *****/
Par_GetParToHTML (ParamTitle,Title,Img_MAX_BYTES_TITLE);
if ((Length = strlen (Title)) > 0) // If title comming from the form is not empty
{
/* Overwrite current title (empty or coming from database)
with the title coming from the form */
Img_FreeImageTitle (Image);
if ((Image->Title = (char *) malloc (Length + 1)) == NULL)
Lay_ShowErrorAndExit ("Error allocating memory for image title.");
strncpy (Image->Title,Title,Length);
Image->Title[Length] = '\0';
}
}
/*****************************************************************************/
@ -183,12 +195,9 @@ Img_Action_t Img_GetImageActionFromForm (const char *ParamAction)
/*****************************************************************************/
/**************************** Get image from form ****************************/
/*****************************************************************************/
// Return true if image is created
void Img_GetAndProcessImageFileFromForm (struct Image *Image,
const char *ParamFile,const char *ParamTitle,
unsigned Width,unsigned Height,
unsigned Quality)
void Img_GetAndProcessImageFileFromForm (struct Image *Image,const char *ParamFile,
unsigned Width,unsigned Height,unsigned Quality)
{
struct Param *Param;
char FileNameImgSrc[PATH_MAX+1];
@ -199,8 +208,6 @@ void Img_GetAndProcessImageFileFromForm (struct Image *Image,
char FileNameImgOrig[PATH_MAX+1]; // Full name of original uploaded file
char FileNameImgTmp[PATH_MAX+1]; // Full name of temporary processed file
bool WrongType = false;
char Title[Img_MAX_BYTES_TITLE+1];
size_t Length;
/***** Rest image file status *****/
Image->Status = Img_FILE_NONE;
@ -265,17 +272,6 @@ void Img_GetAndProcessImageFileFromForm (struct Image *Image,
/***** Remove temporary original file *****/
unlink (FileNameImgOrig);
/***** Get image title from form *****/
Par_GetParToHTML (ParamTitle,Title,Img_MAX_BYTES_TITLE); // TODO: Create a function to get only the length of a parameter
Length = strlen (Title);
if (Length > 0)
{
if ((Image->Title = (char *) malloc (Length+1)) == NULL)
Lay_ShowErrorAndExit ("Error allocating memory for image title.");
strncpy (Image->Title,Title,Length);
Image->Title[Length] = '\0';
}
}
}

View File

@ -94,19 +94,17 @@ struct Image
/***************************** Public prototypes *****************************/
/*****************************************************************************/
void Img_ResetImageTitle (struct Image *Image);
void Img_GetImageNameAndTitle (const char *Name,const char *Title,
struct Image *Image);
void Img_FreeImageTitle (struct Image *Image);
void Img_GetImageNameAndTitleFromRow (const char *Name,const char *Title,
struct Image *Image);
void Img_GetImageFromForm (unsigned NumOpt,struct Image *Image,
void (*GetImageNameFromDB) (unsigned NumOpt,struct Image *Image),
void (*GetImageFromDB) (unsigned NumOpt,struct Image *Image),
const char *ParamAction,const char *ParamFile,const char *ParamTitle,
unsigned Width,unsigned Height,unsigned Quality);
Img_Action_t Img_GetImageActionFromForm (const char *ParamAction);
void Img_GetAndProcessImageFileFromForm (struct Image *Image,
const char *ParamFile,const char *ParamTitle,
unsigned Width,unsigned Height,
unsigned Quality);
void Img_GetAndProcessImageFileFromForm (struct Image *Image,const char *ParamFile,
unsigned Width,unsigned Height,unsigned Quality);
void Img_MoveImageToDefinitiveDirectory (struct Image *Image);
void Img_ShowImage (struct Image *Image,const char *ClassImg);

View File

@ -2527,7 +2527,7 @@ static unsigned Svy_GetAnswersQst (long QstCod,MYSQL_RES **mysql_res)
/***** Count number of rows of result *****/
if (NumRows == 0)
Lay_ShowErrorAndExit ("Error when getting answers of a question.");
Lay_ShowAlert (Lay_ERROR,"Error when getting answers of a question.");
return (unsigned) NumRows;
}
@ -3024,64 +3024,67 @@ static void Svy_WriteAnswersOfAQst (struct Survey *Svy,struct SurveyQuestion *Sv
NumAnswers = Svy_GetAnswersQst (SvyQst->QstCod,&mysql_res); // Result: AnsInd,NumUsrs,Answer
/***** Write the answers *****/
fprintf (Gbl.F.Out,"<table class=\"CELLS_PAD_5\" style=\"width:100%%;\">");
for (NumAns = 0;
NumAns < NumAnswers;
NumAns++)
if (NumAnswers)
{
row = mysql_fetch_row (mysql_res);
fprintf (Gbl.F.Out,"<table class=\"CELLS_PAD_5\" style=\"width:100%%;\">");
for (NumAns = 0;
NumAns < NumAnswers;
NumAns++)
{
row = mysql_fetch_row (mysql_res);
/* Get number of users who have marked this answer (row[1]) */
if (sscanf (row[1],"%u",&NumUsrsThisAnswer) != 1)
Lay_ShowErrorAndExit ("Error when getting number of users who have marked an answer.");
/* Get number of users who have marked this answer (row[1]) */
if (sscanf (row[1],"%u",&NumUsrsThisAnswer) != 1)
Lay_ShowErrorAndExit ("Error when getting number of users who have marked an answer.");
/* Convert the answer (row[2]), that is in HTML, to rigorous HTML */
AnsLength = strlen (row[2]) * Str_MAX_LENGTH_SPEC_CHAR_HTML;
if ((Answer = malloc (AnsLength+1)) == NULL)
Lay_ShowErrorAndExit ("Not enough memory to store answer.");
strcpy (Answer,row[2]);
Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
Answer,AnsLength,false);
/* Convert the answer (row[2]), that is in HTML, to rigorous HTML */
AnsLength = strlen (row[2]) * Str_MAX_LENGTH_SPEC_CHAR_HTML;
if ((Answer = malloc (AnsLength+1)) == NULL)
Lay_ShowErrorAndExit ("Not enough memory to store answer.");
strcpy (Answer,row[2]);
Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
Answer,AnsLength,false);
/* Selectors and label with the letter of the answer */
fprintf (Gbl.F.Out,"<tr>");
/* Selectors and label with the letter of the answer */
fprintf (Gbl.F.Out,"<tr>");
if (PutFormAnswerSurvey)
{
/* Write selector to choice this answer */
fprintf (Gbl.F.Out,"<td class=\"LEFT_TOP\">"
"<input type=\"");
if (SvyQst->AnswerType == Svy_ANS_UNIQUE_CHOICE)
fprintf (Gbl.F.Out,"radio\""
" onclick=\"selectUnselectRadio(this,this.form.Ans%010u,%u)\"",
(unsigned) SvyQst->QstCod,NumAnswers);
else // SvyQst->AnswerType == Svy_ANS_MULTIPLE_CHOICE
fprintf (Gbl.F.Out,"checkbox\"");
fprintf (Gbl.F.Out," name=\"Ans%010u\" value=\"%u\" />"
"</td>",
(unsigned) SvyQst->QstCod,NumAns);
}
if (PutFormAnswerSurvey)
{
/* Write selector to choice this answer */
fprintf (Gbl.F.Out,"<td class=\"LEFT_TOP\">"
"<input type=\"");
if (SvyQst->AnswerType == Svy_ANS_UNIQUE_CHOICE)
fprintf (Gbl.F.Out,"radio\""
" onclick=\"selectUnselectRadio(this,this.form.Ans%010u,%u)\"",
(unsigned) SvyQst->QstCod,NumAnswers);
else // SvyQst->AnswerType == Svy_ANS_MULTIPLE_CHOICE
fprintf (Gbl.F.Out,"checkbox\"");
fprintf (Gbl.F.Out," name=\"Ans%010u\" value=\"%u\" />"
"</td>",
(unsigned) SvyQst->QstCod,NumAns);
}
/* Write the number of option */
fprintf (Gbl.F.Out,"<td class=\"DAT LEFT_TOP\" style=\"width:50px;\">"
"%u)"
"</td>",
NumAns + 1);
/* Write the number of option */
fprintf (Gbl.F.Out,"<td class=\"DAT LEFT_TOP\" style=\"width:50px;\">"
"%u)"
"</td>",
NumAns + 1);
/* Write the text of the answer */
fprintf (Gbl.F.Out,"<td class=\"TEST_EDI LEFT_TOP\">%s</td>",
Answer);
/* Write the text of the answer */
fprintf (Gbl.F.Out,"<td class=\"TEST_EDI LEFT_TOP\">%s</td>",
Answer);
/* Show stats of this answer */
if (Svy->Status.ICanViewResults)
Svy_DrawBarNumUsrs (NumUsrsThisAnswer,Svy->NumUsrs);
/* Show stats of this answer */
if (Svy->Status.ICanViewResults)
Svy_DrawBarNumUsrs (NumUsrsThisAnswer,Svy->NumUsrs);
fprintf (Gbl.F.Out,"</tr>");
fprintf (Gbl.F.Out,"</tr>");
/* Free memory allocated for the answer */
free ((void *) Answer);
/* Free memory allocated for the answer */
free ((void *) Answer);
}
fprintf (Gbl.F.Out,"</table>");
}
fprintf (Gbl.F.Out,"</table>");
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);

View File

@ -219,6 +219,9 @@ static int Tst_CountNumTagsInList (void);
static int Tst_CountNumAnswerTypesInList (void);
static void Tst_PutFormEditOneQst (char *Stem,char *Feedback);
static void Tst_FreeTextChoiceAnswers (void);
static void Tst_FreeTextChoiceAnswer (unsigned NumOpt);
static void Tst_InitImagesOfQuestion (void);
static void Tst_FreeImagesOfQuestion (void);
@ -236,7 +239,7 @@ static void Tst_EnableOrDisableTag (long TagCod,bool TagHidden);
static void Tst_PutIconToRemoveOneQst (void);
static void Tst_PutParamsRemoveOneQst (void);
static bool Tst_GetQstCod (void);
static long Tst_GetQstCod (void);
static void Tst_InsertOrUpdateQstIntoDB (void);
static void Tst_InsertTagsIntoDB (void);
@ -249,8 +252,6 @@ 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);
static unsigned Tst_GetNumCoursesWithTstQuestions (Sco_Scope_t Scope,Tst_AnswerType_t AnsType);
static unsigned Tst_GetNumCoursesWithPluggableTstQuestions (Sco_Scope_t Scope,Tst_AnswerType_t AnsType);
@ -981,6 +982,10 @@ static void Tst_WriteQstAndAnsExam (unsigned NumQst,long QstCod,MYSQL_ROW row,
row[10] Score
*/
/***** Create test question *****/
Tst_QstConstructor ();
Gbl.Test.QstCod = QstCod;
/***** Write number of question *****/
fprintf (Gbl.F.Out,"<tr>"
"<td class=\"RIGHT_TOP COLOR%u\">"
@ -999,12 +1004,9 @@ static void Tst_WriteQstAndAnsExam (unsigned NumQst,long QstCod,MYSQL_ROW row,
fprintf (Gbl.F.Out,"<td class=\"LEFT_TOP COLOR%u\">",
Gbl.RowEvenOdd);
Tst_WriteQstStem (row[4],"TEST_EXA");
if (row[6][0])
{
Gbl.Test.Image.Status = Img_NAME_STORED_IN_DB;
Img_GetImageNameAndTitle (row[6],row[7],&Gbl.Test.Image);
Img_ShowImage (&Gbl.Test.Image,"TEST_IMG_SHOW_STEM");
}
Img_GetImageNameAndTitleFromRow (row[6],row[7],&Gbl.Test.Image);
Img_ShowImage (&Gbl.Test.Image,"TEST_IMG_SHOW_STEM");
if (Gbl.Action.Act == ActSeeTst)
Tst_WriteAnswersOfAQstSeeExam (NumQst,QstCod,(Str_ConvertToUpperLetter (row[3][0]) == 'Y'));
else // Assessing exam / Viewing old exam
@ -1018,9 +1020,8 @@ static void Tst_WriteQstAndAnsExam (unsigned NumQst,long QstCod,MYSQL_ROW row,
fprintf (Gbl.F.Out,"</td>"
"</tr>");
/***** Free answers and images of this test question *****/
Tst_FreeTextChoiceAnswers ();
Tst_FreeImagesOfQuestion ();
/***** Destroy test question *****/
Tst_QstDestructor ();
}
/*****************************************************************************/
@ -2615,7 +2616,6 @@ static void Tst_ListOneOrMoreQuestionsToEdit (unsigned long NumRows,MYSQL_RES *m
Tst_QuestionsOrder_t Order;
unsigned long NumRow;
MYSQL_ROW row;
long QstCod;
unsigned UniqueId;
time_t TimeUTC;
unsigned long NumHitsThisQst;
@ -2706,8 +2706,11 @@ static void Tst_ListOneOrMoreQuestionsToEdit (unsigned long NumRows,MYSQL_RES *m
row[ 9] NumHitsNotBlank
row[10] Score
*/
/***** Create test question *****/
Tst_QstConstructor ();
/* row[0] holds the code of the question */
if ((QstCod = Str_ConvertStrCodToLongCod (row[0])) < 0)
if ((Gbl.Test.QstCod = Str_ConvertStrCodToLongCod (row[0])) < 0)
Lay_ShowErrorAndExit ("Wrong code of question.");
/* Write icon to remove the question */
@ -2716,7 +2719,7 @@ static void Tst_ListOneOrMoreQuestionsToEdit (unsigned long NumRows,MYSQL_RES *m
Act_FormStart (ActReqRemTstQst);
Sta_WriteParamsDatesSeeAccesses ();
Tst_WriteParamEditQst ();
Par_PutHiddenParamLong ("QstCod",QstCod);
Par_PutHiddenParamLong ("QstCod",Gbl.Test.QstCod);
if (NumRows == 1)
Par_PutHiddenParamChar ("OnlyThisQst",'Y'); // If there are only one row, don't list again after removing
Lay_PutIconRemove ();
@ -2726,7 +2729,7 @@ static void Tst_ListOneOrMoreQuestionsToEdit (unsigned long NumRows,MYSQL_RES *m
/* Write icon to edit the question */
fprintf (Gbl.F.Out,"<td class=\"BT%u\">",Gbl.RowEvenOdd);
Act_FormStart (ActEdiOneTstQst);
Par_PutHiddenParamLong ("QstCod",QstCod);
Par_PutHiddenParamLong ("QstCod",Gbl.Test.QstCod);
fprintf (Gbl.F.Out,"<input type=\"image\" src=\"%s/edit64x64.png\""
" alt=\"%s\" title=\"%s\""
" class=\"ICON20x20\" />",
@ -2746,7 +2749,7 @@ static void Tst_ListOneOrMoreQuestionsToEdit (unsigned long NumRows,MYSQL_RES *m
fprintf (Gbl.F.Out,"<td class=\"DAT_SMALL CENTER_TOP COLOR%u\">"
"%ld&nbsp;"
"</td>",
Gbl.RowEvenOdd,QstCod);
Gbl.RowEvenOdd,Gbl.Test.QstCod);
/* Write the date (row[1] has the UTC date-time) */
TimeUTC = Dat_GetUNIXTimeFromStr (row[1]);
@ -2762,7 +2765,7 @@ static void Tst_ListOneOrMoreQuestionsToEdit (unsigned long NumRows,MYSQL_RES *m
/* Write the question tags */
fprintf (Gbl.F.Out,"<td class=\"LEFT_TOP COLOR%u\">",
Gbl.RowEvenOdd);
Tst_GetAndWriteTagsQst (QstCod);
Tst_GetAndWriteTagsQst (Gbl.Test.QstCod);
fprintf (Gbl.F.Out,"</td>");
/* Write the question type (row[2]) */
@ -2780,7 +2783,7 @@ static void Tst_ListOneOrMoreQuestionsToEdit (unsigned long NumRows,MYSQL_RES *m
Gbl.Test.AnswerType == Tst_ANS_MULTIPLE_CHOICE)
{
Act_FormStart (ActShfTstQst);
Par_PutHiddenParamLong ("QstCod",QstCod);
Par_PutHiddenParamLong ("QstCod",Gbl.Test.QstCod);
Sta_WriteParamsDatesSeeAccesses ();
Tst_WriteParamEditQst ();
if (NumRows == 1)
@ -2800,20 +2803,12 @@ static void Tst_ListOneOrMoreQuestionsToEdit (unsigned long NumRows,MYSQL_RES *m
fprintf (Gbl.F.Out,"<td class=\"LEFT_TOP COLOR%u\">",
Gbl.RowEvenOdd);
Tst_WriteQstStem (row[4],"TEST_EDI");
if (row[6][0])
{
Gbl.Test.Image.Status = Img_NAME_STORED_IN_DB;
Img_GetImageNameAndTitle (row[6],row[7],&Gbl.Test.Image);
Img_ShowImage (&Gbl.Test.Image,"TEST_IMG_EDIT_LIST_STEM");
}
Img_GetImageNameAndTitleFromRow (row[6],row[7],&Gbl.Test.Image);
Img_ShowImage (&Gbl.Test.Image,"TEST_IMG_EDIT_LIST_STEM");
Tst_WriteQstFeedback (row[5],"TEST_EDI_LIGHT");
Tst_WriteAnswersOfAQstEdit (QstCod);
Tst_WriteAnswersOfAQstEdit (Gbl.Test.QstCod);
fprintf (Gbl.F.Out,"</td>");
/* Free answers and images of this test question */
Tst_FreeTextChoiceAnswers ();
Tst_FreeImagesOfQuestion ();
/* Get number of hits
(number of times that the question has been answered,
including blank answers) (row[8]) */
@ -2865,6 +2860,9 @@ static void Tst_ListOneOrMoreQuestionsToEdit (unsigned long NumRows,MYSQL_RES *m
fprintf (Gbl.F.Out,"N.A.");
fprintf (Gbl.F.Out,"</td>"
"</tr>");
/***** Destroy test question *****/
Tst_QstDestructor ();
}
/***** End table *****/
@ -2901,7 +2899,7 @@ void Tst_WriteParamEditQst (void)
unsigned Tst_GetAnswersQst (long QstCod,MYSQL_RES **mysql_res,bool Shuffle)
{
char Query[256];
char Query[512];
unsigned long NumRows;
/***** Get answers of a question from database *****/
@ -2911,7 +2909,7 @@ unsigned Tst_GetAnswersQst (long QstCod,MYSQL_RES **mysql_res,bool Shuffle)
Shuffle ? "RAND(NOW())" :
"AnsInd");
if (!(NumRows = DB_QuerySELECT (Query,mysql_res,"can not get answers of a question")))
Lay_ShowErrorAndExit ("Error when getting answers of a question.");
Lay_ShowAlert (Lay_ERROR,"Error when getting answers of a question.");
return (unsigned) NumRows;
}
@ -3007,11 +3005,7 @@ static void Tst_WriteAnswersOfAQstEdit (long QstCod)
}
/* Copy image */
if (row[3][0])
{
Gbl.Test.Answer.Options[NumOpt].Image.Status = Img_NAME_STORED_IN_DB;
Img_GetImageNameAndTitle (row[3],row[4],&Gbl.Test.Answer.Options[NumOpt].Image);
}
Img_GetImageNameAndTitleFromRow (row[3],row[4],&Gbl.Test.Answer.Options[NumOpt].Image);
/* Put an icon that indicates whether the answer is correct or wrong */
fprintf (Gbl.F.Out,"<tr>"
@ -3037,8 +3031,7 @@ static void Tst_WriteAnswersOfAQstEdit (long QstCod)
"<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_ANS");
Img_ShowImage (&Gbl.Test.Answer.Options[NumOpt].Image,"TEST_IMG_EDIT_LIST_ANS");
fprintf (Gbl.F.Out,"</div>");
/* Write the text of the feedback */
@ -3106,6 +3099,10 @@ static void Tst_WriteAnswersOfAQstAssessExam (unsigned NumQst,long QstCod,
{
MYSQL_RES *mysql_res;
/***** Create test question *****/
Tst_QstConstructor ();
Gbl.Test.QstCod = QstCod;
/***** Get answers of a question from database *****/
Gbl.Test.Answer.NumOptions = Tst_GetAnswersQst (QstCod,&mysql_res,false); // Result: AnsInd,Answer,Correct
/*
@ -3141,6 +3138,9 @@ static void Tst_WriteAnswersOfAQstAssessExam (unsigned NumQst,long QstCod,
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
/***** Destroy test question *****/
Tst_QstDestructor ();
}
/*****************************************************************************/
@ -3281,8 +3281,9 @@ static void Tst_WriteChoiceAnsSeeExam (unsigned NumQst,long QstCod,bool Shuffle)
bool ErrorInIndex = false;
char ParamName[3+6+1];
/***** Start of table *****/
fprintf (Gbl.F.Out,"<table class=\"CELLS_PAD_2\">");
/***** Create test question *****/
Tst_QstConstructor ();
Gbl.Test.QstCod = QstCod;
/***** Get answers of a question from database *****/
Gbl.Test.Answer.NumOptions = Tst_GetAnswersQst (QstCod,&mysql_res,Shuffle); // Result: AnsInd,Answer,Correct
@ -3294,6 +3295,10 @@ static void Tst_WriteChoiceAnsSeeExam (unsigned NumQst,long QstCod,bool Shuffle)
row[ 4] ImageTitle
row[ 5] Correct
*/
/***** Start of table *****/
fprintf (Gbl.F.Out,"<table class=\"CELLS_PAD_2\">");
for (NumOpt = 0;
NumOpt < Gbl.Test.Answer.NumOptions;
NumOpt++)
@ -3324,11 +3329,7 @@ static void Tst_WriteChoiceAnsSeeExam (unsigned NumQst,long QstCod,bool Shuffle)
Gbl.Test.Answer.Options[NumOpt].Text,Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false);
/***** Copy image *****/
if (row[3][0])
{
Gbl.Test.Answer.Options[NumOpt].Image.Status = Img_NAME_STORED_IN_DB;
Img_GetImageNameAndTitle (row[3],row[4],&Gbl.Test.Answer.Options[NumOpt].Image);
}
Img_GetImageNameAndTitleFromRow (row[3],row[4],&Gbl.Test.Answer.Options[NumOpt].Image);
/***** Write selectors and letter of this option *****/
fprintf (Gbl.F.Out,"<tr>"
@ -3351,8 +3352,7 @@ static void Tst_WriteChoiceAnsSeeExam (unsigned NumQst,long QstCod,bool Shuffle)
fprintf (Gbl.F.Out,"<td class=\"TEST_EXA LEFT_TOP\">"
"%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_ANS");
Img_ShowImage (&Gbl.Test.Answer.Options[NumOpt].Image,"TEST_IMG_SHOW_ANS");
fprintf (Gbl.F.Out,"</td>"
"</tr>");
}
@ -3360,12 +3360,11 @@ static void Tst_WriteChoiceAnsSeeExam (unsigned NumQst,long QstCod,bool Shuffle)
/***** End of table *****/
fprintf (Gbl.F.Out,"</table>");
/***** Free answers and images of this test question *****/
Tst_FreeTextChoiceAnswers ();
Tst_FreeImagesOfQuestion ();
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
/***** Destroy test question *****/
Tst_QstDestructor ();
}
/*****************************************************************************/
@ -3430,11 +3429,7 @@ static void Tst_WriteChoiceAnsAssessExam (unsigned NumQst,MYSQL_RES *mysql_res,
}
/***** Copy image *****/
if (row[3][0])
{
Gbl.Test.Answer.Options[NumOpt].Image.Status = Img_NAME_STORED_IN_DB;
Img_GetImageNameAndTitle (row[3],row[4],&Gbl.Test.Answer.Options[NumOpt].Image);
}
Img_GetImageNameAndTitleFromRow (row[3],row[4],&Gbl.Test.Answer.Options[NumOpt].Image);
/***** Assign correctness (row[5]) of this answer (this option) *****/
Gbl.Test.Answer.Options[NumOpt].Correct = (Str_ConvertToUpperLetter (row[5][0]) == 'Y');
@ -3526,8 +3521,7 @@ static void Tst_WriteChoiceAnsAssessExam (unsigned NumQst,MYSQL_RES *mysql_res,
"<div class=\"TEST_EXA\">"
"%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_ANS");
Img_ShowImage (&Gbl.Test.Answer.Options[Indexes[NumOpt]].Image,"TEST_IMG_SHOW_ANS");
fprintf (Gbl.F.Out,"</div>");
if (Gbl.Test.Config.FeedbackType == Tst_FEEDBACK_FULL_FEEDBACK)
if (Gbl.Test.Answer.Options[Indexes[NumOpt]].Feedback)
@ -3606,10 +3600,6 @@ static void Tst_WriteChoiceAnsAssessExam (unsigned NumQst,MYSQL_RES *mysql_res,
/***** End of table *****/
fprintf (Gbl.F.Out,"</table>");
/***** Free answers and images of this test question *****/
Tst_FreeTextChoiceAnswers ();
Tst_FreeImagesOfQuestion ();
}
/*****************************************************************************/
@ -3791,9 +3781,6 @@ static void Tst_WriteTextAnsAssessExam (unsigned NumQst,MYSQL_RES *mysql_res,
}
fprintf (Gbl.F.Out,"</table>");
/***** Free answers *****/
Tst_FreeTextChoiceAnswers ();
}
/*****************************************************************************/
@ -4351,8 +4338,18 @@ void Tst_ShowFormEditOneQst (void)
char Stem[Cns_MAX_BYTES_TEXT+1];
char Feedback[Cns_MAX_BYTES_TEXT+1];
/***** Create test question *****/
Tst_QstConstructor ();
Gbl.Test.QstCod = Tst_GetQstCod ();
Stem[0] = Feedback[0] = '\0';
if (Gbl.Test.QstCod > 0) // If question already exists in the database
Tst_GetQstDataFromDB (Stem,Feedback);
/***** Put form to edit question *****/
Tst_PutFormEditOneQst (Stem,Feedback);
/***** Destroy test question *****/
Tst_QstDestructor ();
}
/*****************************************************************************/
@ -4398,16 +4395,6 @@ static void Tst_PutFormEditOneQst (char *Stem,char *Feedback)
char ParamFile[32];
char ParamTitle[32];
/***** If no receiving the question, but editing a new or existing question
==> init or edit data of question *****/
if (Gbl.Action.Act == ActEdiOneTstQst)
{
Tst_InitQst ();
if (Tst_GetQstCod ()) // If parameter QstCod received ==>
// ==> question already exists in the database
Tst_GetQstDataFromDB (Stem,Feedback);
}
/***** Start frame *****/
if (Gbl.Test.QstCod > 0) // The question already has assigned a code
{
@ -4737,21 +4724,18 @@ static void Tst_PutFormEditOneQst (char *Stem,char *Feedback)
/***** End frame *****/
Lay_EndRoundFrame ();
/***** Free memory for answers *****/
Tst_FreeTextChoiceAnswers ();
}
/*****************************************************************************/
/********************* Initialize a new question to zero *********************/
/*****************************************************************************/
void Tst_InitQst (void)
void Tst_QstConstructor (void)
{
unsigned NumOpt;
unsigned NumTag;
Gbl.Test.QstCod = -1;
Gbl.Test.QstCod = -1L;
for (NumTag = 0;
NumTag < Tst_MAX_TAGS_PER_QUESTION;
NumTag++)
@ -4781,6 +4765,74 @@ void Tst_InitQst (void)
Tst_InitImagesOfQuestion ();
}
/*****************************************************************************/
/***************** Free memory allocated for test question *******************/
/*****************************************************************************/
void Tst_QstDestructor (void)
{
Tst_FreeTextChoiceAnswers ();
Tst_FreeImagesOfQuestion ();
}
/*****************************************************************************/
/******************* Allocate memory for a choice answer *********************/
/*****************************************************************************/
int Tst_AllocateTextChoiceAnswer (unsigned NumOpt)
{
Tst_FreeTextChoiceAnswer (NumOpt);
if ((Gbl.Test.Answer.Options[NumOpt].Text =
malloc (Tst_MAX_BYTES_ANSWER_OR_FEEDBACK + 1)) == NULL)
{
sprintf (Gbl.Message,"Not enough memory to store answer.");
return 0;
}
if ((Gbl.Test.Answer.Options[NumOpt].Feedback =
malloc (Tst_MAX_BYTES_ANSWER_OR_FEEDBACK + 1)) == NULL)
{
sprintf (Gbl.Message,"Not enough memory to store feedback.");
return 0;
}
Gbl.Test.Answer.Options[NumOpt].Text[0] =
Gbl.Test.Answer.Options[NumOpt].Feedback[0] = '\0';
return 1;
}
/*****************************************************************************/
/******************** Free memory of all choice answers **********************/
/*****************************************************************************/
static void Tst_FreeTextChoiceAnswers (void)
{
unsigned NumOpt;
for (NumOpt = 0;
NumOpt < Tst_MAX_OPTIONS_PER_QUESTION;
NumOpt++)
Tst_FreeTextChoiceAnswer (NumOpt);
}
/*****************************************************************************/
/********************** Free memory of a choice answer ***********************/
/*****************************************************************************/
static void Tst_FreeTextChoiceAnswer (unsigned NumOpt)
{
if (Gbl.Test.Answer.Options[NumOpt].Text)
{
free ((void *) Gbl.Test.Answer.Options[NumOpt].Text);
Gbl.Test.Answer.Options[NumOpt].Text = NULL;
}
if (Gbl.Test.Answer.Options[NumOpt].Feedback)
{
free ((void *) Gbl.Test.Answer.Options[NumOpt].Feedback);
Gbl.Test.Answer.Options[NumOpt].Feedback = NULL;
}
}
/*****************************************************************************/
/***************** Initialize images of a question to zero *******************/
/*****************************************************************************/
@ -4812,11 +4864,11 @@ static void Tst_FreeImagesOfQuestion (void)
{
unsigned NumOpt;
Img_ResetImageTitle (&Gbl.Test.Image);
Img_FreeImageTitle (&Gbl.Test.Image);
for (NumOpt = 0;
NumOpt < Tst_MAX_OPTIONS_PER_QUESTION;
NumOpt++)
Img_ResetImageTitle (&Gbl.Test.Answer.Options[NumOpt].Image);
Img_FreeImageTitle (&Gbl.Test.Answer.Options[NumOpt].Image);
}
/*****************************************************************************/
@ -4869,13 +4921,7 @@ static void Tst_GetQstDataFromDB (char *Stem,char *Feedback)
}
/* Get the image name of the question from the database (row[4]) */
if (row[4][0])
{
Gbl.Test.Image.Status = Img_NAME_STORED_IN_DB;
Img_GetImageNameAndTitle (row[4],row[5],&Gbl.Test.Image);
}
else
Gbl.Test.Image.Status = Img_FILE_NONE;
Img_GetImageNameAndTitleFromRow (row[4],row[5],&Gbl.Test.Image);
/* Free structure that stores the query result */
DB_FreeMySQLResult (&mysql_res);
@ -4946,11 +4992,7 @@ static void Tst_GetQstDataFromDB (char *Stem,char *Feedback)
}
/* Copy image */
if (row[3][0])
{
Gbl.Test.Answer.Options[NumOpt].Image.Status = Img_NAME_STORED_IN_DB;
Img_GetImageNameAndTitle (row[3],row[4],&Gbl.Test.Answer.Options[NumOpt].Image);
}
Img_GetImageNameAndTitleFromRow (row[3],row[4],&Gbl.Test.Answer.Options[NumOpt].Image);
Gbl.Test.Answer.Options[NumOpt].Correct = (Str_ConvertToUpperLetter (row[5][0]) == 'Y');
break;
@ -4991,7 +5033,7 @@ static void Tst_GetImageFromDB (unsigned NumOpt,struct Image *Image)
row = mysql_fetch_row (mysql_res);
/***** Get the image name (row[0]) *****/
Img_GetImageNameAndTitle (row[0],row[1],Image);
Img_GetImageNameAndTitleFromRow (row[0],row[1],Image);
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
@ -5040,11 +5082,11 @@ void Tst_ReceiveQst (void)
char Stem[Cns_MAX_BYTES_TEXT+1];
char Feedback[Cns_MAX_BYTES_TEXT+1];
/***** Initialize new question to zero *****/
Tst_InitQst ();
Stem[0] = Feedback[0] = '\0';
/***** Create test question *****/
Tst_QstConstructor ();
/***** Get parameters of the question from form *****/
Stem[0] = Feedback[0] = '\0';
Tst_GetQstFromForm (Stem,Feedback);
/***** Make sure that tags, text and answer are not empty *****/
@ -5068,8 +5110,8 @@ void Tst_ReceiveQst (void)
Tst_PutFormEditOneQst (Stem,Feedback);
}
/***** Free answers *****/
Tst_FreeTextChoiceAnswers ();
/***** Destroy test question *****/
Tst_QstDestructor ();
}
/*****************************************************************************/
@ -5094,7 +5136,7 @@ static void Tst_GetQstFromForm (char *Stem,char *Feedback)
char ParamTitle[32];
/***** Get question code *****/
Tst_GetQstCod ();
Gbl.Test.QstCod = Tst_GetQstCod ();
/***** Get answer type *****/
Par_GetParToText ("AnswerType",UnsignedStr,10);
@ -5645,7 +5687,8 @@ void Tst_RequestRemoveQst (void)
/***** Get main parameters from form *****/
/* Get the question code */
if (!Tst_GetQstCod ())
Gbl.Test.QstCod = Tst_GetQstCod ();
if (Gbl.Test.QstCod <= 0)
Lay_ShowErrorAndExit ("Wrong code of question.");
/* Get a parameter that indicates whether it's necessary
@ -5697,7 +5740,8 @@ void Tst_RemoveQst (void)
bool EditingOnlyThisQst;
/***** Get the question code *****/
if (!Tst_GetQstCod ())
Gbl.Test.QstCod = Tst_GetQstCod ();
if (Gbl.Test.QstCod <= 0)
Lay_ShowErrorAndExit ("Wrong code of question.");
/***** Get a parameter that indicates whether it's necessary
@ -5749,7 +5793,8 @@ void Tst_ChangeShuffleQst (void)
bool Shuffle;
/***** Get the question code *****/
if (!Tst_GetQstCod ())
Gbl.Test.QstCod = Tst_GetQstCod ();
if (Gbl.Test.QstCod <= 0)
Lay_ShowErrorAndExit ("Wrong code of question.");
/***** Get a parameter that indicates whether it's necessary to continue listing the rest of questions ******/
@ -5787,14 +5832,12 @@ void Tst_ChangeShuffleQst (void)
/************ Get the parameter with the code of a test question *************/
/*****************************************************************************/
static bool Tst_GetQstCod (void)
static long Tst_GetQstCod (void)
{
char LongStr[1+10+1];
Par_GetParToText ("QstCod",LongStr,1+10);
if ((Gbl.Test.QstCod = Str_ConvertStrCodToLongCod (LongStr)) < 0)
return false;
return true;
return Str_ConvertStrCodToLongCod (LongStr);
}
/*****************************************************************************/
@ -5825,9 +5868,10 @@ static void Tst_InsertOrUpdateQstIntoDB (void)
char *Query;
/***** Allocate space for query *****/
if ((Query = malloc (256 +
if ((Query = malloc (512 +
Gbl.Test.Stem.Length +
Gbl.Test.Feedback.Length)) == NULL)
Gbl.Test.Feedback.Length +
Img_MAX_BYTES_TITLE)) == NULL)
Lay_ShowErrorAndExit ("Not enough memory to store database query.");
if (Gbl.Test.QstCod < 0) // It's a new question
@ -6144,64 +6188,6 @@ static void Tst_RemoveImgFilesFromAnsOfQsts (long CrsCod,
DB_FreeMySQLResult (&mysql_res);
}
/*****************************************************************************/
/******************* Allocate memory for a choice answer *********************/
/*****************************************************************************/
int Tst_AllocateTextChoiceAnswer (unsigned NumOpt)
{
Tst_FreeTextChoiceAnswer (NumOpt);
if ((Gbl.Test.Answer.Options[NumOpt].Text =
malloc (Tst_MAX_BYTES_ANSWER_OR_FEEDBACK + 1)) == NULL)
{
sprintf (Gbl.Message,"Not enough memory to store answer.");
return 0;
}
if ((Gbl.Test.Answer.Options[NumOpt].Feedback =
malloc (Tst_MAX_BYTES_ANSWER_OR_FEEDBACK + 1)) == NULL)
{
sprintf (Gbl.Message,"Not enough memory to store feedback.");
return 0;
}
Gbl.Test.Answer.Options[NumOpt].Text[0] =
Gbl.Test.Answer.Options[NumOpt].Feedback[0] = '\0';
return 1;
}
/*****************************************************************************/
/******************** Free memory of all choice answers **********************/
/*****************************************************************************/
void Tst_FreeTextChoiceAnswers (void)
{
unsigned NumOpt;
for (NumOpt = 0;
NumOpt < Tst_MAX_OPTIONS_PER_QUESTION;
NumOpt++)
Tst_FreeTextChoiceAnswer (NumOpt);
}
/*****************************************************************************/
/********************** Free memory of a choice answer ***********************/
/*****************************************************************************/
static void Tst_FreeTextChoiceAnswer (unsigned NumOpt)
{
if (Gbl.Test.Answer.Options[NumOpt].Text)
{
free ((void *) Gbl.Test.Answer.Options[NumOpt].Text);
Gbl.Test.Answer.Options[NumOpt].Text = NULL;
}
if (Gbl.Test.Answer.Options[NumOpt].Feedback)
{
free ((void *) Gbl.Test.Answer.Options[NumOpt].Feedback);
Gbl.Test.Answer.Options[NumOpt].Feedback = NULL;
}
}
/*****************************************************************************/
/*********************** Get stats about test questions **********************/
/*****************************************************************************/

View File

@ -141,7 +141,12 @@ void Tst_GetConfigFromRow (MYSQL_ROW row);
bool Tst_CheckIfCourseHaveTestsAndPluggableIsUnknown (void);
void Tst_ReceiveConfigTst (void);
void Tst_ShowFormEditOneQst (void);
void Tst_InitQst (void);
void Tst_QstConstructor (void);
void Tst_QstDestructor (void);
int Tst_AllocateTextChoiceAnswer (unsigned NumOpt);
Tst_AnswerType_t Tst_ConvertFromStrAnsTypDBToAnsTyp (const char *StrAnsTypeBD);
void Tst_ReceiveQst (void);
bool Tst_CheckIfQstFormatIsCorrectAndCountNumOptions (void);
@ -152,8 +157,6 @@ void Tst_RemoveQst (void);
void Tst_ChangeShuffleQst (void);
void Tst_InsertOrUpdateQstTagsAnsIntoDB (void);
int Tst_AllocateTextChoiceAnswer (unsigned NumOpt);
void Tst_FreeTextChoiceAnswers (void);
void Tst_FreeTagsList (void);
void Tst_GetTestStats (Tst_AnswerType_t AnsType,struct Tst_Stats *Stats);

View File

@ -533,8 +533,8 @@ static void TsI_ImportQuestionsFromXMLBuffer (const char *XMLBuffer)
{
if (!strcmp (QuestionElem->TagName,"question"))
{
/***** Initialize new question to zero *****/
Tst_InitQst ();
/***** Create test question *****/
Tst_QstConstructor ();
/* Get type of questions (in mandatory attribute "type") */
AnswerTypeFound = false;
@ -653,8 +653,8 @@ static void TsI_ImportQuestionsFromXMLBuffer (const char *XMLBuffer)
}
}
/***** Free answers *****/
Tst_FreeTextChoiceAnswers ();
/***** Destroy test question *****/
Tst_QstDestructor ();
}
}